#undef DEBUG
using GTA;
using Newtonsoft.Json;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace RageCoop.Client.Scripting
{
///
///
///
public class CustomEventReceivedArgs : EventArgs
{
///
/// The event hash
///
public int Hash { get; set; }
///
/// Supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string, Vector3, Quaternion
///
public object[] Args { get; set; }
}
///
/// Client configuration, this will conflict with server-side config.
///
public class ClientConfig
{
///
/// Get or set local player's username, set won't be effective if already connected to a server.
///
public string Username
{
get => Main.Settings.Username;
set
{
if (Networking.IsOnServer || string.IsNullOrEmpty(value))
{
return;
}
Main.Settings.Username = value;
}
}
///
/// Enable automatic respawn for this player.
///
public bool EnableAutoRespawn { get; set; } = true;
///
/// Get or set player's blip color
///
public BlipColor BlipColor { get; set; } = BlipColor.White;
///
/// Get or set player's blip sprite
///
public BlipSprite BlipSprite { get; set; } = BlipSprite.Standard;
///
/// Get or set scale of player's blip
///
public float BlipScale { get; set; } = 1;
}
///
/// Base events for RageCoop
///
public class ClientEvents
{
internal Dictionary>> CustomEventHandlers = new Dictionary>>();
#region DELEGATES
///
///
///
public delegate void EmptyEvent();
///
///
///
///
///
public delegate void CustomEvent(int hash, List args);
#endregion
///
/// The local player is dead
///
public event EmptyEvent OnPlayerDied;
///
/// A local vehicle is spawned
///
public event EventHandler OnVehicleSpawned;
///
/// A local vehicle is deleted
///
public event EventHandler OnVehicleDeleted;
///
/// A local ped is spawned
///
public event EventHandler OnPedSpawned;
///
/// A local ped is deleted
///
public event EventHandler OnPedDeleted;
#region INVOKE
internal void InvokeVehicleSpawned(SyncedVehicle v) { OnVehicleSpawned?.Invoke(null, v); }
internal void InvokeVehicleDeleted(SyncedVehicle v) { OnVehicleDeleted?.Invoke(null, v); }
internal void InvokePedSpawned(SyncedPed p) { OnPedSpawned?.Invoke(null, p); }
internal void InvokePedDeleted(SyncedPed p) { OnPedDeleted?.Invoke(null, p); }
internal void InvokePlayerDied() { OnPlayerDied?.Invoke(); }
internal void InvokeCustomEventReceived(Packets.CustomEvent p)
{
var args = new CustomEventReceivedArgs() { Hash = p.Hash, Args = p.Args };
// Main.Logger.Debug($"CustomEvent:\n"+args.Args.DumpWithType());
if (CustomEventHandlers.TryGetValue(p.Hash, out List> handlers))
{
handlers.ForEach((x) => { x.Invoke(args); });
}
}
#endregion
}
///
/// Provides vital functionality to interact with RAGECOOP
///
public class API : MarshalByRefObject
{
static API Instance;
private API() { }
///
/// Get an instance to bridge data between domains
///
///
///
public static API GetInstance()
{
if (Instance != null) { return Instance; }
if (Util.IsPrimaryDomain)
{
Instance = new API();
}
else
{
Instance = AppDomain.CurrentDomain.GetData("RageCoop.Client.API") as API;
}
return Instance;
}
public ClientEvents Events = new ClientEvents();
public ClientConfig Config = new ClientConfig();
#region PROPERTIES
///
/// Get the local player's ID
///
/// PlayerID
public int LocalPlayerID => Main.LocalPlayerID;
///
/// Check if player is connected to a server
///
public bool IsOnServer => Networking.IsOnServer;
///
/// Get an that the player is currently connected to, or null if not connected to the server
///
public System.Net.IPEndPoint ServerEndPoint => Networking.IsOnServer ? Networking.ServerConnection?.RemoteEndPoint : null;
///
/// Check if a RAGECOOP menu is visible
///
public bool IsMenuVisible => Menus.CoopMenu.MenuPool.AreAnyVisible;
///
/// Check if the RAGECOOP chat is visible
///
public bool IsChatFocused => Main.MainChat.Focused;
///
/// Check if the RAGECOOP list of players is visible
///
public bool IsPlayerListVisible => Util.GetTickCount64() - PlayerList.Pressed < 5000;
///
/// Get the version of RAGECOOP
///
public Version CurrentVersion => Main.Version;
///
/// Get a that RAGECOOP is currently using.
///
///
public Logger Logger => Main.Logger;
///
/// Get all players indexed by their ID
///
public Dictionary Players => new Dictionary(PlayerList.Players);
#endregion
#region FUNCTIONS
///
/// Connect to a server
///
/// Address of the server, e.g. 127.0.0.1:4499
/// When a connection is active or being established
public void Connect(string address)
{
if (Networking.IsOnServer || Networking.IsConnecting)
{
throw new InvalidOperationException("Cannot connect to server when another connection is active");
}
Networking.ToggleConnection(address);
}
///
/// Disconnect from current server or cancel the connection attempt.
///
public void Disconnect()
{
if (Networking.IsOnServer || Networking.IsConnecting)
{
Networking.ToggleConnection(null);
}
}
///
/// List all servers from master server address
///
///
public List ListServers()
{
return JsonConvert.DeserializeObject>(HttpHelper.DownloadString(Main.Settings.MasterServer));
}
///
/// Send a local chat message to this player
///
/// Name of the sender
/// The player's message
public void LocalChatMessage(string from, string message)
{
Main.MainChat.AddMessage(from, message);
}
///
/// Send a chat message or command to server/other players
///
///
public void SendChatMessage(string message)
{
Networking.SendChatMessage(message);
}
///
/// Queue an action to be executed on next tick.
///
///
public void QueueAction(Action a)
{
WorldThread.QueueAction(a);
}
///
/// Queue an action to be executed on next tick, allowing you to call scripting API from another thread.
///
/// An to be executed with a return value indicating whether it can be removed after execution.
public void QueueAction(Func a)
{
WorldThread.QueueAction(a);
}
///
/// Send an event and data to the server.
///
/// An unique identifier of the event
/// The objects conataing your data, see for a list of supported types
public void SendCustomEvent(CustomEventHash eventHash, params object[] args)
{
Networking.Peer.SendTo(new Packets.CustomEvent()
{
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
///
/// Send an event and data to the server
///
///
/// An unique identifier of the event
/// The objects conataing your data, see for a list of supported types
public void SendCustomEvent(CustomEventFlags flags, CustomEventHash eventHash, params object[] args)
{
Networking.Peer.SendTo(new Packets.CustomEvent(flags)
{
Args = args,
Hash = eventHash
}, Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
///
/// Register an handler to the specifed event hash, one event can have multiple handlers. This will be invoked from backgound thread, use in the handler to dispatch code to script thread.
///
/// An unique identifier of the event, you can hash your event name with
/// An handler to be invoked when the event is received from the server.
public void RegisterCustomEventHandler(CustomEventHash hash, Action handler)
{
lock (Events.CustomEventHandlers)
{
if (!Events.CustomEventHandlers.TryGetValue(hash, out List> handlers))
{
Events.CustomEventHandlers.Add(hash, handlers = new List>());
}
handlers.Add(handler);
}
}
///
///
///
///
public void RequestSharedFile(string name, Action callback)
{
EventHandler handler = (s, e) =>
{
if (e.EndsWith(name))
{
callback(e);
}
};
DownloadManager.DownloadCompleted += handler;
Networking.GetResponse(new Packets.FileTransferRequest()
{
Name = name,
},
(p) =>
{
if (p.Response != FileResponse.Loaded)
{
DownloadManager.DownloadCompleted -= handler;
throw new ArgumentException("Requested file was not found on the server: " + name);
}
});
}
#endregion
}
}