commit
857b1b7622
@ -4,17 +4,38 @@ using System.Linq;
|
||||
|
||||
namespace CoopClient
|
||||
{
|
||||
public static class Interface
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static class COOPAPI
|
||||
{
|
||||
#region DELEGATES
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public delegate void ConnectEvent(bool connected, long fromId, string reason = null);
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public delegate void ChatMessage(string from, string message, CancelEventArgs args);
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes);
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static event ConnectEvent OnConnection;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static event ChatMessage OnChatMessage;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static event ModEvent OnModPacketReceived;
|
||||
|
||||
internal static void Connected()
|
||||
@ -50,31 +71,55 @@ namespace CoopClient
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static void SendChatMessage(string from, string message)
|
||||
/// <summary>
|
||||
/// Send a local chat message to this player
|
||||
/// </summary>
|
||||
/// <param name="from">Username of the player who sent this message</param>
|
||||
/// <param name="message">The player's message</param>
|
||||
public static void LocalChatMessage(string from, string message)
|
||||
{
|
||||
Main.MainChat.AddMessage(from, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static void Connect(string serverAddress)
|
||||
{
|
||||
Main.MainNetworking.DisConnectFromServer(serverAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static void Disconnect()
|
||||
{
|
||||
Main.MainNetworking.DisConnectFromServer(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static bool IsOnServer()
|
||||
{
|
||||
return Main.MainNetworking.IsOnServer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the local ID from this Lidgren network client when connected to a server
|
||||
/// </summary>
|
||||
/// <returns>long</returns>
|
||||
public static long GetLocalID()
|
||||
{
|
||||
return Main.LocalClientID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all connected player's as a Dictionary.
|
||||
/// Key = Lidgren-Network client ID
|
||||
/// Value = Character handle or null
|
||||
/// </summary>
|
||||
/// <returns>Dictionary(long, int)</returns>
|
||||
public static Dictionary<long, int?> GetAllPlayers()
|
||||
{
|
||||
Dictionary<long, int?> result = new Dictionary<long, int?>();
|
||||
@ -88,11 +133,22 @@ namespace CoopClient
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Entities.EntitiesPlayer GetPlayer(long playerId)
|
||||
/// <summary>
|
||||
/// Get a player using their Lidgren Network Client ID
|
||||
/// </summary>
|
||||
/// <param name="lnID">Lidgren-Network client ID</param>
|
||||
/// <returns>Entities.EntitiesPlayer</returns>
|
||||
public static Entities.EntitiesPlayer GetPlayer(long lnID)
|
||||
{
|
||||
return Main.Players.ContainsKey(playerId) ? Main.Players[playerId] : null;
|
||||
lock (Main.Players)
|
||||
{
|
||||
return Main.Players.ContainsKey(lnID) ? Main.Players[lnID] : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static bool IsMenuVisible()
|
||||
{
|
||||
#if NON_INTERACTIVE
|
||||
@ -102,33 +158,76 @@ namespace CoopClient
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static bool IsChatFocused()
|
||||
{
|
||||
return Main.MainChat.Focused;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static bool IsPlayerListVisible()
|
||||
{
|
||||
return Util.GetTickCount64() - PlayerList.Pressed < 5000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static string GetCurrentVersion()
|
||||
{
|
||||
return Main.CurrentVersion;
|
||||
}
|
||||
|
||||
// Send bytes to all players
|
||||
/// <summary>
|
||||
/// Send any data (bytes) to the server
|
||||
/// </summary>
|
||||
/// <param name="mod">The name of this modification (script)</param>
|
||||
/// <param name="customID">The ID to know what the data is</param>
|
||||
/// <param name="bytes">Your class, structure or whatever in bytes</param>
|
||||
public static void SendDataToServer(string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
Main.MainNetworking.SendModData(-1, mod, customID, bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send any data (bytes) to the all player
|
||||
/// </summary>
|
||||
/// <param name="mod">The name of this modification (script)</param>
|
||||
/// <param name="customID">The ID to know what the data is</param>
|
||||
/// <param name="bytes">Your class, structure or whatever in bytes</param>
|
||||
public static void SendDataToAll(string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
Main.MainNetworking.SendModData(0, mod, customID, bytes);
|
||||
}
|
||||
|
||||
// Send bytes to target
|
||||
public static void SendDataToPlayer(long target, string mod, byte customID, byte[] bytes)
|
||||
/// <summary>
|
||||
/// Send any data (bytes) to a player
|
||||
/// </summary>
|
||||
/// <param name="lnID">The Lidgren Network Client ID that receives the data</param>
|
||||
/// <param name="mod">The name of this modification (script)</param>
|
||||
/// <param name="customID">The ID to know what the data is</param>
|
||||
/// <param name="bytes">Your class, structure or whatever in bytes</param>
|
||||
public static void SendDataToPlayer(long lnID, string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
Main.MainNetworking.SendModData(target, mod, customID, bytes);
|
||||
Main.MainNetworking.SendModData(lnID, mod, customID, bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get that player's local username that has been set
|
||||
/// </summary>
|
||||
/// <returns>string</returns>
|
||||
public static string GetLocalUsername()
|
||||
{
|
||||
return Main.MainSettings.Username;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static void Configure(string playerName, bool shareNpcsWithPlayers, int streamedNpcs, bool debug = false)
|
||||
{
|
||||
Main.MainSettings.Username = playerName;
|
@ -8,14 +8,17 @@ using GTA.Native;
|
||||
|
||||
namespace CoopClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Chat
|
||||
{
|
||||
private readonly Scaleform MainScaleForm;
|
||||
|
||||
public string CurrentInput { get; set; }
|
||||
internal string CurrentInput { get; set; }
|
||||
|
||||
private bool CurrentFocused { get; set; }
|
||||
public bool Focused
|
||||
internal bool Focused
|
||||
{
|
||||
get { return CurrentFocused; }
|
||||
set
|
||||
@ -55,23 +58,26 @@ namespace CoopClient
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Chat()
|
||||
{
|
||||
MainScaleForm = new Scaleform("multiplayer_chat");
|
||||
}
|
||||
|
||||
public void Init()
|
||||
internal void Init()
|
||||
{
|
||||
MainScaleForm.CallFunction("SET_FOCUS", 2, 2, "ALL");
|
||||
MainScaleForm.CallFunction("SET_FOCUS", 1, 2, "ALL");
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
internal void Clear()
|
||||
{
|
||||
MainScaleForm.CallFunction("RESET");
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
internal void Tick()
|
||||
{
|
||||
if ((Util.GetTickCount64() - LastMessageTime) > 15000 && !Focused && !Hidden)
|
||||
{
|
||||
@ -91,14 +97,14 @@ namespace CoopClient
|
||||
Function.Call(Hash.DISABLE_ALL_CONTROL_ACTIONS, 0);
|
||||
}
|
||||
|
||||
public void AddMessage(string sender, string msg)
|
||||
internal void AddMessage(string sender, string msg)
|
||||
{
|
||||
MainScaleForm.CallFunction("ADD_MESSAGE", sender + ":", msg);
|
||||
LastMessageTime = Util.GetTickCount64();
|
||||
Hidden = false;
|
||||
}
|
||||
|
||||
public void OnKeyDown(Keys key)
|
||||
internal void OnKeyDown(Keys key)
|
||||
{
|
||||
if (key == Keys.Escape)
|
||||
{
|
||||
@ -151,12 +157,12 @@ namespace CoopClient
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int ToUnicodeEx(uint virtualKeyCode, uint scanCode, byte[] keyboardState,
|
||||
internal static extern int ToUnicodeEx(uint virtualKeyCode, uint scanCode, byte[] keyboardState,
|
||||
[Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
|
||||
StringBuilder receivingBuffer,
|
||||
int bufferSize, uint flags, IntPtr kblayout);
|
||||
|
||||
public static string GetCharFromKey(Keys key, bool shift, bool altGr)
|
||||
internal static string GetCharFromKey(Keys key, bool shift, bool altGr)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
byte[] keyboardState = new byte[256];
|
||||
|
@ -24,6 +24,8 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<DocumentationFile>bin\Debug\CoopClient.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@ -33,18 +35,26 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<DocumentationFile>bin\Release\CoopClient.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="LemonUI.SHVDN3">
|
||||
<HintPath>..\Libs\Release\LemonUI.SHVDN3.dll</HintPath>
|
||||
<Reference Include="LemonUI.SHVDN3, Version=1.5.1.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\LemonUI.SHVDN3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\Libs\Release\Lidgren.Network.dll</HintPath>
|
||||
<Reference Include="Lidgren.Network, Version=2012.1.7.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net, Version=2.4.0.0, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\protobuf-net.2.4.6\lib\net40\protobuf-net.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3, Version=3.1.0.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<Reference Include="protobuf-net">
|
||||
<HintPath>..\Libs\Release\scripts\protobuf-net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3, Version=3.3.2.0, Culture=neutral, processorArchitecture=AMD64">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Libs\Release\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
@ -65,13 +75,14 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Chat.cs" />
|
||||
<Compile Include="Interface.cs" />
|
||||
<Compile Include="COOPAPI.cs" />
|
||||
<Compile Include="Entities\EntitiesNpc.cs" />
|
||||
<Compile Include="Entities\EntitiesPed.cs" />
|
||||
<Compile Include="Entities\EntitiesPlayer.cs" />
|
||||
<Compile Include="Entities\EntitiesThread.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Menus\MenusMain.cs" />
|
||||
<Compile Include="Menus\Sub\Servers.cs" />
|
||||
<Compile Include="Menus\Sub\Settings.cs" />
|
||||
<Compile Include="Networking.cs" />
|
||||
<Compile Include="Packets.cs" />
|
||||
@ -81,8 +92,5 @@
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -1,5 +1,8 @@
|
||||
namespace CoopClient.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public class EntitiesNpc : EntitiesPed
|
||||
{
|
||||
//public int LastUpdateReceived { get; set; }
|
||||
|
@ -9,58 +9,157 @@ using GTA.Math;
|
||||
|
||||
using LemonUI.Elements;
|
||||
|
||||
namespace CoopClient
|
||||
namespace CoopClient.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public class EntitiesPed
|
||||
{
|
||||
private bool AllDataAvailable = false;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool LastSyncWasFull { get; set; } = false;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public ulong LastUpdateReceived { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public float Latency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Ped Character { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int Health { get; set; }
|
||||
private int LastModelHash = 0;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int ModelHash { get; set; }
|
||||
private Dictionary<int, int> LastProps = new Dictionary<int, int>();
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Dictionary<int, int> Props { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
#region -- ON FOOT --
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vector3 Rotation { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vector3 Velocity { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public byte Speed { get; set; }
|
||||
private bool LastIsJumping = false;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsJumping { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsRagdoll { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsOnFire { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vector3 AimCoords { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsAiming { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsShooting { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsReloading { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int CurrentWeaponHash { get; set; }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Blip PedBlip;
|
||||
|
||||
#region -- IN VEHICLE --
|
||||
private ulong VehicleStopTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool IsInVehicle { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int VehicleModelHash { get; set; }
|
||||
private int[] LastVehicleColors = new int[] { 0, 0 };
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int[] VehicleColors { get; set; }
|
||||
private Dictionary<int, int> LastVehicleMods = new Dictionary<int, int>();
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Dictionary<int, int> VehicleMods { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool VehicleDead { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public float VehicleEngineHealth { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int VehicleSeatIndex { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vehicle MainVehicle { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vector3 VehiclePosition { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Quaternion VehicleRotation { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public Vector3 VehicleVelocity { get; set; }
|
||||
private float LastVehicleSpeed { get; set; }
|
||||
private float CurrentVehicleSpeed { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public float VehicleSpeed
|
||||
{
|
||||
set
|
||||
@ -69,19 +168,44 @@ namespace CoopClient
|
||||
CurrentVehicleSpeed = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public float VehicleSteeringAngle { get; set; }
|
||||
private int LastVehicleAim;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool VehIsEngineRunning { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public float VehRPM { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool VehAreLightsOn { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool VehAreHighBeamsOn { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public bool VehIsSireneActive { get; set; }
|
||||
private VehicleDoors[] LastVehDoors;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public VehicleDoors[] VehDoors { get; set; }
|
||||
private int LastVehTires;
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public int VehTires { get; set; }
|
||||
#endregion
|
||||
|
||||
public void DisplayLocally(string username)
|
||||
internal void DisplayLocally(string username)
|
||||
{
|
||||
/*
|
||||
* username: string
|
||||
@ -297,6 +421,11 @@ namespace CoopClient
|
||||
}
|
||||
|
||||
#region -- VEHICLE SYNC --
|
||||
if (MainVehicle.GetResponsiblePedHandle() != Character.Handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (VehicleColors != null && VehicleColors != LastVehicleColors)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, VehicleColors[0], VehicleColors[1]);
|
||||
@ -322,11 +451,6 @@ namespace CoopClient
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MainVehicle.GetResponsiblePedHandle() != Character.Handle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (VehicleMods != null && VehicleMods != LastVehicleMods)
|
||||
{
|
||||
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
|
||||
@ -437,12 +561,22 @@ namespace CoopClient
|
||||
|
||||
LastVehTires = VehTires;
|
||||
}
|
||||
|
||||
if (AimCoords != default)
|
||||
{
|
||||
int gameTime = Game.GameTime;
|
||||
if (gameTime - LastVehicleAim > 30)
|
||||
{
|
||||
Function.Call(Hash.TASK_VEHICLE_AIM_AT_COORD, Character.Handle, AimCoords.X, AimCoords.Y, AimCoords.Z);
|
||||
LastVehicleAim = gameTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (VehicleSteeringAngle != MainVehicle.SteeringAngle)
|
||||
{
|
||||
MainVehicle.Handle.CustomSteeringAngle((float)(Math.PI / 180) * VehicleSteeringAngle);
|
||||
MainVehicle.CustomSteeringAngle((float)(Math.PI / 180) * VehicleSteeringAngle);
|
||||
}
|
||||
|
||||
// Good enough for now, but we need to create a better sync
|
||||
|
@ -1,8 +1,17 @@
|
||||
namespace CoopClient.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public class EntitiesPlayer : EntitiesPed
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public string SocialClubName { get; set; }
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public string Username { get; set; } = "Player";
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,18 @@ using GTA;
|
||||
|
||||
namespace CoopClient.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class EntitiesThread : Script
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public EntitiesThread()
|
||||
{
|
||||
Tick += OnTick;
|
||||
Interval = 1000 / 60;
|
||||
Interval = Util.GetGameMs<int>();
|
||||
}
|
||||
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
|
@ -12,31 +12,49 @@ using GTA.Native;
|
||||
|
||||
namespace CoopClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Main : Script
|
||||
{
|
||||
public static RelationshipGroup RelationshipGroup;
|
||||
internal static RelationshipGroup RelationshipGroup;
|
||||
|
||||
private bool GameLoaded = false;
|
||||
|
||||
public static readonly string CurrentVersion = "V0_8_0_1";
|
||||
internal static readonly string CurrentVersion = "V0_9_0";
|
||||
|
||||
public static bool ShareNpcsWithPlayers = false;
|
||||
public static bool DisableTraffic = false;
|
||||
public static bool NpcsAllowed = false;
|
||||
internal static bool ShareNpcsWithPlayers = false;
|
||||
internal static bool DisableTraffic = false;
|
||||
internal static bool NpcsAllowed = false;
|
||||
private static bool IsGoingToCar = false;
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public static Settings MainSettings = Util.ReadSettings();
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public static Networking MainNetworking = new Networking();
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public static MenusMain MainMenu = new MenusMain();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public static Chat MainChat = new Chat();
|
||||
|
||||
public static long LocalClientID = 0;
|
||||
public static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
|
||||
public static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
|
||||
internal static long LocalClientID = 0;
|
||||
internal static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
|
||||
internal static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Main()
|
||||
{
|
||||
Function.Call((Hash)0x0888C3502DBBEEF5); // _LOAD_MP_DLC_MAPS
|
||||
@ -62,6 +80,8 @@ namespace CoopClient
|
||||
{
|
||||
RelationshipGroup = World.AddRelationshipGroup("SYNCPED");
|
||||
Game.Player.Character.RelationshipGroup = RelationshipGroup;
|
||||
|
||||
GTA.UI.Notification.Show(GTA.UI.NotificationIcon.AllPlayersConf, "GTACOOP:R", "Welcome!", "Press ~g~F9~s~ to open the menu.");
|
||||
}
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
@ -104,7 +124,7 @@ namespace CoopClient
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((Util.GetTickCount64() - LastDataSend) < (1000 / 60))
|
||||
if ((Util.GetTickCount64() - LastDataSend) < Util.GetGameMs<ulong>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -182,7 +202,7 @@ namespace CoopClient
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void CleanUp()
|
||||
internal static void CleanUp()
|
||||
{
|
||||
MainChat.Clear();
|
||||
|
||||
@ -203,24 +223,13 @@ namespace CoopClient
|
||||
Npc.Value.Character?.Delete();
|
||||
}
|
||||
Npcs.Clear();
|
||||
|
||||
foreach (Ped entity in World.GetAllPeds().Where(p => p.Handle != Game.Player.Character.Handle))
|
||||
{
|
||||
entity.Kill();
|
||||
entity.Delete();
|
||||
}
|
||||
|
||||
foreach (Vehicle veh in World.GetAllVehicles().Where(v => v.Handle != Game.Player.Character.CurrentVehicle?.Handle))
|
||||
{
|
||||
veh.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private ulong ArtificialLagCounter;
|
||||
public static EntitiesPlayer DebugSyncPed;
|
||||
public static ulong LastFullDebugSync = 0;
|
||||
public static bool UseDebug = false;
|
||||
internal static EntitiesPlayer DebugSyncPed;
|
||||
internal static ulong LastFullDebugSync = 0;
|
||||
internal static bool UseDebug = false;
|
||||
|
||||
private void Debug()
|
||||
{
|
||||
@ -231,12 +240,12 @@ namespace CoopClient
|
||||
DebugSyncPed = Players[0];
|
||||
}
|
||||
|
||||
if ((Util.GetTickCount64() - ArtificialLagCounter) < 157)
|
||||
if ((Util.GetTickCount64() - ArtificialLagCounter) < 56)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 1500;
|
||||
bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 500;
|
||||
|
||||
if (fullSync)
|
||||
{
|
||||
@ -273,6 +282,7 @@ namespace CoopClient
|
||||
DebugSyncPed.VehicleVelocity = veh.Velocity;
|
||||
DebugSyncPed.VehicleSpeed = veh.Speed;
|
||||
DebugSyncPed.VehicleSteeringAngle = veh.SteeringAngle;
|
||||
DebugSyncPed.AimCoords = veh.IsTurretSeat((int)player.SeatIndex) ? Util.GetVehicleAimCoords() : new GTA.Math.Vector3();
|
||||
DebugSyncPed.VehicleColors = new int[] { primaryColor, secondaryColor };
|
||||
DebugSyncPed.VehicleMods = veh.Mods.GetVehicleMods();
|
||||
DebugSyncPed.VehDoors = veh.Doors.GetVehicleDoors();
|
||||
|
@ -5,17 +5,27 @@ using LemonUI.Menus;
|
||||
|
||||
namespace CoopClient.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class MenusMain
|
||||
{
|
||||
public ObjectPool MenuPool = new ObjectPool();
|
||||
internal ObjectPool MenuPool = new ObjectPool();
|
||||
|
||||
public NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "MAIN")
|
||||
internal NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "MAIN")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
|
||||
};
|
||||
#region SUB
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Sub.Settings SubSettings = new Sub.Settings();
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Sub.Servers ServerList = new Sub.Servers();
|
||||
#endregion
|
||||
|
||||
#region ITEMS
|
||||
@ -28,12 +38,17 @@ namespace CoopClient.Menus
|
||||
Main.CurrentVersion.Replace("_", ".")) { LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") };
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public MenusMain()
|
||||
{
|
||||
UsernameItem.Activated += UsernameActivated;
|
||||
ServerIpItem.Activated += ServerIpActivated;
|
||||
ServerConnectItem.Activated += (sender, item) => { Main.MainNetworking.DisConnectFromServer(Main.MainSettings.LastServerAddress); };
|
||||
|
||||
MainMenu.AddSubMenu(ServerList.MainMenu);
|
||||
|
||||
MainMenu.Add(UsernameItem);
|
||||
MainMenu.Add(ServerIpItem);
|
||||
MainMenu.Add(ServerConnectItem);
|
||||
@ -42,11 +57,12 @@ namespace CoopClient.Menus
|
||||
|
||||
MainMenu.Add(AboutItem);
|
||||
|
||||
MenuPool.Add(ServerList.MainMenu);
|
||||
MenuPool.Add(MainMenu);
|
||||
MenuPool.Add(SubSettings.MainMenu);
|
||||
}
|
||||
|
||||
public void UsernameActivated(object a, System.EventArgs b)
|
||||
internal void UsernameActivated(object a, System.EventArgs b)
|
||||
{
|
||||
string newUsername = Game.GetUserInput(WindowTitle.EnterMessage20, UsernameItem.AltTitle, 20);
|
||||
if (!string.IsNullOrWhiteSpace(newUsername))
|
||||
@ -59,7 +75,7 @@ namespace CoopClient.Menus
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerIpActivated(object a, System.EventArgs b)
|
||||
internal void ServerIpActivated(object a, System.EventArgs b)
|
||||
{
|
||||
string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);
|
||||
if (!string.IsNullOrWhiteSpace(newServerIp) && newServerIp.Contains(":"))
|
||||
@ -72,29 +88,32 @@ namespace CoopClient.Menus
|
||||
}
|
||||
}
|
||||
|
||||
public void InitiateConnectionMenuSetting()
|
||||
internal void InitiateConnectionMenuSetting()
|
||||
{
|
||||
MainMenu.Items[0].Enabled = false;
|
||||
MainMenu.Items[1].Enabled = false;
|
||||
MainMenu.Items[2].Enabled = false;
|
||||
MainMenu.Items[3].Enabled = false;
|
||||
}
|
||||
|
||||
public void ConnectedMenuSetting()
|
||||
internal void ConnectedMenuSetting()
|
||||
{
|
||||
MainMenu.Items[2].Enabled = true;
|
||||
MainMenu.Items[2].Title = "Disconnect";
|
||||
MainMenu.Items[3].Enabled = true;
|
||||
MainMenu.Items[3].Title = "Disconnect";
|
||||
SubSettings.MainMenu.Items[1].Enabled = !Main.DisableTraffic && Main.NpcsAllowed;
|
||||
|
||||
MainMenu.Visible = false;
|
||||
ServerList.MainMenu.Visible = false;
|
||||
MenuPool.RefreshAll();
|
||||
}
|
||||
|
||||
public void DisconnectedMenuSetting()
|
||||
internal void DisconnectedMenuSetting()
|
||||
{
|
||||
MainMenu.Items[0].Enabled = true;
|
||||
MainMenu.Items[1].Enabled = true;
|
||||
MainMenu.Items[2].Enabled = true;
|
||||
MainMenu.Items[2].Title = "Connect";
|
||||
MainMenu.Items[3].Enabled = true;
|
||||
MainMenu.Items[3].Title = "Connect";
|
||||
SubSettings.MainMenu.Items[1].Enabled = false;
|
||||
|
||||
MenuPool.RefreshAll();
|
||||
|
111
Client/Menus/Sub/Servers.cs
Normal file
111
Client/Menus/Sub/Servers.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using LemonUI.Menus;
|
||||
|
||||
namespace CoopClient.Menus.Sub
|
||||
{
|
||||
internal class ServerListClass
|
||||
{
|
||||
[JsonProperty("ip")]
|
||||
public string IP { get; set; }
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
[JsonProperty("players")]
|
||||
public int Players { get; set; }
|
||||
[JsonProperty("maxPlayers")]
|
||||
public int MaxPlayers { get; set; }
|
||||
[JsonProperty("allowlist")]
|
||||
public bool AllowList { get; set; }
|
||||
[JsonProperty("mods")]
|
||||
public bool Mods { get; set; }
|
||||
[JsonProperty("npcs")]
|
||||
public bool NPCs { get; set; }
|
||||
[JsonProperty("country")]
|
||||
public string Country { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Servers
|
||||
{
|
||||
internal NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "Servers", "Go to the server list")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
|
||||
};
|
||||
internal NativeItem ResultItem = null;
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Servers()
|
||||
{
|
||||
MainMenu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) =>
|
||||
{
|
||||
MainMenu.Add(ResultItem = new NativeItem("Loading..."));
|
||||
GetAllServer();
|
||||
};
|
||||
MainMenu.Closed += (object sender, EventArgs e) =>
|
||||
{
|
||||
for (int i = 0; i < MainMenu.Items.Count; i++)
|
||||
{
|
||||
MainMenu.Remove(MainMenu.Items[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
private void GetAllServer()
|
||||
{
|
||||
List<ServerListClass> serverList = null;
|
||||
try
|
||||
{
|
||||
WebClient client = new WebClient();
|
||||
string data = client.DownloadString(Main.MainSettings.MasterServer);
|
||||
serverList = JsonConvert.DeserializeObject<List<ServerListClass>>(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResultItem.Title = "Download failed!";
|
||||
ResultItem.Description = ex.Message; // You have to use any key to see this message
|
||||
}
|
||||
|
||||
if (serverList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ResultItem != null)
|
||||
{
|
||||
MainMenu.Remove(MainMenu.Items[0]);
|
||||
ResultItem = null;
|
||||
}
|
||||
|
||||
foreach (ServerListClass server in serverList)
|
||||
{
|
||||
NativeItem tmpItem = null;
|
||||
MainMenu.Add(tmpItem = new NativeItem($"[{server.Country}] {server.Name}", $"~b~{server.IP}~s~~n~Mods = {server.Mods}~n~NPCs = {server.NPCs}") { AltTitle = $"[{server.Players}/{server.MaxPlayers}][{(server.AllowList ? "~r~X~s~" : "~g~O~s~")}]"});
|
||||
tmpItem.Activated += (object sender, EventArgs e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
MainMenu.Visible = false;
|
||||
Main.MainMenu.MainMenu.Visible = true;
|
||||
|
||||
Main.MainNetworking.DisConnectFromServer(server.IP);
|
||||
|
||||
Main.MainSettings.LastServerAddress = server.IP;
|
||||
Util.SaveSettings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GTA.UI.Notification.Show($"~r~{ex.Message}");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,12 @@
|
||||
|
||||
namespace CoopClient.Menus.Sub
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Settings
|
||||
{
|
||||
public NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "Settings", "Go to the settings")
|
||||
internal NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "Settings", "Go to the settings")
|
||||
{
|
||||
UseMouse = false,
|
||||
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
|
||||
@ -19,6 +22,9 @@ namespace CoopClient.Menus.Sub
|
||||
private readonly NativeCheckboxItem ShowNetworkInfo = new NativeCheckboxItem("Show Network Info", Main.MainNetworking.ShowNetworkInfo);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public Settings()
|
||||
{
|
||||
DisableTraffic.CheckboxChanged += DisableTrafficCheckboxChanged;
|
||||
@ -40,7 +46,7 @@ namespace CoopClient.Menus.Sub
|
||||
#endif
|
||||
}
|
||||
|
||||
public void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
|
||||
internal void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
|
||||
{
|
||||
Main.DisableTraffic = DisableTraffic.Checked;
|
||||
|
||||
@ -59,14 +65,14 @@ namespace CoopClient.Menus.Sub
|
||||
}
|
||||
}
|
||||
|
||||
public void StreamedNpcsValueChanged(object a, System.EventArgs b)
|
||||
internal void StreamedNpcsValueChanged(object a, System.EventArgs b)
|
||||
{
|
||||
Main.MainSettings.StreamedNpc = StreamedNpcsItem.Value;
|
||||
Util.SaveSettings();
|
||||
StreamedNpcsItem.Title = string.Format("Streamed Npcs ({0})", Main.MainSettings.StreamedNpc);
|
||||
}
|
||||
|
||||
public void FlipMenuCheckboxChanged(object a, System.EventArgs b)
|
||||
internal void FlipMenuCheckboxChanged(object a, System.EventArgs b)
|
||||
{
|
||||
#if !NON_INTERACTIVE
|
||||
Main.MainMenu.MainMenu.Alignment = FlipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left;
|
||||
@ -78,7 +84,7 @@ namespace CoopClient.Menus.Sub
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public void UseDebugCheckboxChanged(object a, System.EventArgs b)
|
||||
internal void UseDebugCheckboxChanged(object a, System.EventArgs b)
|
||||
{
|
||||
Main.UseDebug = UseDebugItem.Checked;
|
||||
|
||||
@ -96,7 +102,7 @@ namespace CoopClient.Menus.Sub
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
|
||||
internal void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
|
||||
{
|
||||
Main.MainNetworking.ShowNetworkInfo = ShowNetworkInfo.Checked;
|
||||
|
||||
|
@ -10,17 +10,20 @@ using GTA.Native;
|
||||
|
||||
namespace CoopClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Networking
|
||||
{
|
||||
public NetClient Client;
|
||||
public float Latency;
|
||||
internal NetClient Client;
|
||||
internal float Latency;
|
||||
|
||||
public bool ShowNetworkInfo = false;
|
||||
internal bool ShowNetworkInfo = false;
|
||||
|
||||
public int BytesReceived = 0;
|
||||
public int BytesSend = 0;
|
||||
internal int BytesReceived = 0;
|
||||
internal int BytesSend = 0;
|
||||
|
||||
public void DisConnectFromServer(string address)
|
||||
internal void DisConnectFromServer(string address)
|
||||
{
|
||||
if (IsOnServer())
|
||||
{
|
||||
@ -44,9 +47,16 @@ namespace CoopClient
|
||||
|
||||
Client.Start();
|
||||
|
||||
string[] ip = address.Split(':');
|
||||
|
||||
if(ip.Length != 2)
|
||||
string[] ip = new string[2];
|
||||
|
||||
int idx = address.LastIndexOf(':');
|
||||
if (idx != -1)
|
||||
{
|
||||
ip[0] = address.Substring(0, idx);
|
||||
ip[1] = address.Substring(idx + 1);
|
||||
}
|
||||
|
||||
if (ip.Length != 2)
|
||||
{
|
||||
throw new Exception("Malformed URL");
|
||||
}
|
||||
@ -66,12 +76,12 @@ namespace CoopClient
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsOnServer()
|
||||
internal bool IsOnServer()
|
||||
{
|
||||
return Client?.ConnectionStatus == NetConnectionStatus.Connected;
|
||||
}
|
||||
|
||||
public void ReceiveMessages()
|
||||
internal void ReceiveMessages()
|
||||
{
|
||||
if (Client == null)
|
||||
{
|
||||
@ -114,12 +124,6 @@ namespace CoopClient
|
||||
Main.LocalClientID = handshakePacket.ID;
|
||||
Main.NpcsAllowed = handshakePacket.NpcsAllowed;
|
||||
|
||||
Main.CleanUp();
|
||||
|
||||
Function.Call(Hash.SET_GARBAGE_TRUCKS, 0);
|
||||
Function.Call(Hash.SET_RANDOM_BOATS, 0);
|
||||
Function.Call(Hash.SET_RANDOM_TRAINS, 0);
|
||||
|
||||
Main.MainChat.Init();
|
||||
|
||||
// Send player connect packet
|
||||
@ -137,7 +141,7 @@ namespace CoopClient
|
||||
Main.MainMenu.ConnectedMenuSetting();
|
||||
#endif
|
||||
|
||||
Interface.Connected();
|
||||
COOPAPI.Connected();
|
||||
GTA.UI.Notification.Show("~g~Connected!");
|
||||
}
|
||||
break;
|
||||
@ -153,11 +157,12 @@ namespace CoopClient
|
||||
}
|
||||
|
||||
Main.CleanUp();
|
||||
|
||||
#if !NON_INTERACTIVE
|
||||
Main.MainMenu.DisconnectedMenuSetting();
|
||||
#endif
|
||||
|
||||
Interface.Disconnected(reason);
|
||||
COOPAPI.Disconnected(reason);
|
||||
GTA.UI.Notification.Show("~r~Disconnected: " + reason);
|
||||
break;
|
||||
}
|
||||
@ -219,7 +224,7 @@ namespace CoopClient
|
||||
packet.NetIncomingMessageToPacket(message);
|
||||
|
||||
ChatMessagePacket chatMessagePacket = (ChatMessagePacket)packet;
|
||||
if (!Interface.ChatMessageReceived(chatMessagePacket.Username, chatMessagePacket.Message))
|
||||
if (!COOPAPI.ChatMessageReceived(chatMessagePacket.Username, chatMessagePacket.Message))
|
||||
{
|
||||
Main.MainChat.AddMessage(chatMessagePacket.Username, chatMessagePacket.Message);
|
||||
}
|
||||
@ -233,7 +238,7 @@ namespace CoopClient
|
||||
packet = new ModPacket();
|
||||
packet.NetIncomingMessageToPacket(message);
|
||||
ModPacket modPacket = (ModPacket)packet;
|
||||
Interface.ModPacketReceived(modPacket.ID, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes);
|
||||
COOPAPI.ModPacketReceived(modPacket.ID, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -244,6 +249,9 @@ namespace CoopClient
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
#if DEBUG
|
||||
// TODO?
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -265,7 +273,7 @@ namespace CoopClient
|
||||
};
|
||||
|
||||
Main.Players.Add(packet.ID, player);
|
||||
Interface.Connected(packet.ID);
|
||||
COOPAPI.Connected(packet.ID);
|
||||
}
|
||||
|
||||
private void PlayerDisconnect(PlayerDisconnectPacket packet)
|
||||
@ -281,7 +289,7 @@ namespace CoopClient
|
||||
|
||||
player.PedBlip?.Delete();
|
||||
|
||||
Interface.Disconnected(packet.ID);
|
||||
COOPAPI.Disconnected(packet.ID);
|
||||
Main.Players.Remove(packet.ID);
|
||||
}
|
||||
}
|
||||
@ -334,6 +342,7 @@ namespace CoopClient
|
||||
player.VehicleVelocity = packet.VehVelocity.ToVector();
|
||||
player.VehicleSpeed = packet.VehSpeed;
|
||||
player.VehicleSteeringAngle = packet.VehSteeringAngle;
|
||||
player.AimCoords = packet.VehAimCoords.ToVector();
|
||||
player.VehicleColors = packet.VehColors;
|
||||
player.VehicleMods = packet.VehMods;
|
||||
player.VehDoors = packet.VehDoors;
|
||||
@ -590,7 +599,7 @@ namespace CoopClient
|
||||
|
||||
#region -- SEND --
|
||||
private ulong LastPlayerFullSync = 0;
|
||||
public void SendPlayerData()
|
||||
internal void SendPlayerData()
|
||||
{
|
||||
Ped player = Game.Player.Character;
|
||||
|
||||
@ -660,6 +669,7 @@ namespace CoopClient
|
||||
VehVelocity = vehVelocity,
|
||||
VehSpeed = vehSpeed,
|
||||
VehSteeringAngle = vehSteeringAngle,
|
||||
VehAimCoords = veh.IsTurretSeat((int)player.SeatIndex) ? Util.GetVehicleAimCoords().ToLVector() : new LVector3(),
|
||||
VehColors = new int[] { primaryColor, secondaryColor },
|
||||
VehMods = vehMods,
|
||||
VehDoors = vehDoors,
|
||||
@ -762,7 +772,7 @@ namespace CoopClient
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SendNpcData(Ped npc)
|
||||
internal void SendNpcData(Ped npc)
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
|
||||
@ -859,7 +869,7 @@ namespace CoopClient
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SendChatMessage(string message)
|
||||
internal void SendChatMessage(string message)
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
new ChatMessagePacket()
|
||||
@ -878,7 +888,7 @@ namespace CoopClient
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SendModData(long target, string mod, byte customID, byte[] bytes)
|
||||
internal void SendModData(long target, string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
new ModPacket()
|
||||
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using Lidgren.Network;
|
||||
using ProtoBuf;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using GTA.Math;
|
||||
|
||||
@ -156,10 +156,16 @@ namespace CoopClient
|
||||
IsDead = 1 << 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
[ProtoContract]
|
||||
public struct VehicleDoors
|
||||
{
|
||||
#region CLIENT-ONLY
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public VehicleDoors(float angleRatio = 0f, bool broken = false, bool open = false, bool fullyOpen = false)
|
||||
{
|
||||
AngleRatio = angleRatio;
|
||||
@ -169,27 +175,39 @@ namespace CoopClient
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
[ProtoMember(1)]
|
||||
public float AngleRatio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public bool Broken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public bool Open { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
[ProtoMember(4)]
|
||||
public bool FullyOpen { get; set; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
public interface IPacket
|
||||
interface IPacket
|
||||
{
|
||||
void PacketToNetOutGoingMessage(NetOutgoingMessage message);
|
||||
void NetIncomingMessageToPacket(NetIncomingMessage message);
|
||||
}
|
||||
|
||||
public abstract class Packet : IPacket
|
||||
abstract class Packet : IPacket
|
||||
{
|
||||
public abstract void PacketToNetOutGoingMessage(NetOutgoingMessage message);
|
||||
public abstract void NetIncomingMessageToPacket(NetIncomingMessage message);
|
||||
@ -454,18 +472,21 @@ namespace CoopClient
|
||||
public float VehSteeringAngle { get; set; }
|
||||
|
||||
[ProtoMember(13)]
|
||||
public int[] VehColors { get; set; }
|
||||
public LVector3 VehAimCoords { get; set; }
|
||||
|
||||
[ProtoMember(14)]
|
||||
public Dictionary<int, int> VehMods { get; set; }
|
||||
public int[] VehColors { get; set; }
|
||||
|
||||
[ProtoMember(15)]
|
||||
public VehicleDoors[] VehDoors { get; set; }
|
||||
public Dictionary<int, int> VehMods { get; set; }
|
||||
|
||||
[ProtoMember(16)]
|
||||
public int VehTires { get; set; }
|
||||
public VehicleDoors[] VehDoors { get; set; }
|
||||
|
||||
[ProtoMember(17)]
|
||||
public int VehTires { get; set; }
|
||||
|
||||
[ProtoMember(18)]
|
||||
public byte? Flag { get; set; } = 0;
|
||||
|
||||
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
|
||||
@ -496,6 +517,7 @@ namespace CoopClient
|
||||
VehVelocity = data.VehVelocity;
|
||||
VehSpeed = data.VehSpeed;
|
||||
VehSteeringAngle = data.VehSteeringAngle;
|
||||
VehAimCoords = data.VehAimCoords;
|
||||
VehColors = data.VehColors;
|
||||
VehMods = data.VehMods;
|
||||
VehDoors = data.VehDoors;
|
||||
@ -910,8 +932,14 @@ namespace CoopClient
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static class CoopSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static byte[] CSerialize(this object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
@ -919,14 +947,13 @@ namespace CoopClient
|
||||
return null;
|
||||
}
|
||||
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
bf.Serialize(ms, obj);
|
||||
return ms.ToArray();
|
||||
}
|
||||
string jsonString = JsonConvert.SerializeObject(obj);
|
||||
return System.Text.Encoding.UTF8.GetBytes(jsonString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ?
|
||||
/// </summary>
|
||||
public static T CDeserialize<T>(this byte[] bytes) where T : class
|
||||
{
|
||||
if (bytes == null)
|
||||
@ -934,14 +961,8 @@ namespace CoopClient
|
||||
return null;
|
||||
}
|
||||
|
||||
using (MemoryStream memStream = new MemoryStream())
|
||||
{
|
||||
BinaryFormatter binForm = new BinaryFormatter();
|
||||
memStream.Write(bytes, 0, bytes.Length);
|
||||
memStream.Seek(0, SeekOrigin.Begin);
|
||||
T obj = (T)binForm.Deserialize(memStream);
|
||||
return obj;
|
||||
}
|
||||
var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
return JsonConvert.DeserializeObject<T>(jsonString);
|
||||
}
|
||||
|
||||
internal static T Deserialize<T>(this byte[] data) where T : new()
|
||||
|
@ -8,12 +8,18 @@ using GTA.Native;
|
||||
|
||||
namespace CoopClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class PlayerList : Script
|
||||
{
|
||||
private readonly Scaleform MainScaleform = new Scaleform("mp_mm_card_freemode");
|
||||
private ulong LastUpdate = Util.GetTickCount64();
|
||||
public static ulong Pressed { get; set; }
|
||||
internal static ulong Pressed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public PlayerList()
|
||||
{
|
||||
Init();
|
||||
|
@ -1,10 +1,29 @@
|
||||
namespace CoopClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public string Username { get; set; } = "Player";
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public string LastServerAddress { get; set; } = "127.0.0.1:4499";
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public string MasterServer { get; set; } = "http://gtacoopr.000webhostapp.com/servers.php";
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public bool FlipMenu { get; set; } = false;
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public int StreamedNpc { get; set; } = 10;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,7 @@
|
||||
using System.IO;
|
||||
using System.Xml.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Runtime.InteropServices.Marshal;
|
||||
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
@ -17,60 +15,29 @@ namespace CoopClient
|
||||
#region -- POINTER --
|
||||
private static int SteeringAngleOffset { get; set; }
|
||||
|
||||
delegate ulong GetHandleAddressFuncDelegate(int handle);
|
||||
static GetHandleAddressFuncDelegate GetEntityAddressFunc;
|
||||
|
||||
static unsafe byte* FindPattern(string pattern, string mask)
|
||||
{
|
||||
ProcessModule module = Process.GetCurrentProcess().MainModule;
|
||||
|
||||
ulong address = (ulong)module.BaseAddress.ToInt64();
|
||||
ulong endAddress = address + (ulong)module.ModuleMemorySize;
|
||||
|
||||
for (; address < endAddress; address++)
|
||||
{
|
||||
for (int i = 0; i < pattern.Length; i++)
|
||||
{
|
||||
if (mask[i] != '?' && ((byte*)address)[i] != pattern[i])
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (i + 1 == pattern.Length)
|
||||
{
|
||||
return (byte*)address;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static unsafe void NativeMemory()
|
||||
{
|
||||
byte* address;
|
||||
IntPtr address;
|
||||
|
||||
address = FindPattern("\xE8\x00\x00\x00\x00\x48\x8B\xD8\x48\x85\xC0\x74\x2E\x48\x83\x3D", "x????xxxxxxxxxxx");
|
||||
GetEntityAddressFunc = GetDelegateForFunctionPointer<GetHandleAddressFuncDelegate>(new IntPtr(*(int*)(address + 1) + address + 5));
|
||||
|
||||
address = FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
|
||||
if (address != null)
|
||||
address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
|
||||
if (address != IntPtr.Zero)
|
||||
{
|
||||
SteeringAngleOffset = *(int*)(address + 6) + 8;
|
||||
}
|
||||
|
||||
address = FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
|
||||
if (address != null)
|
||||
address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
|
||||
if (address != IntPtr.Zero)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
*(byte*)((IntPtr)address + i).ToPointer() = 0x90;
|
||||
*(byte*)(address + i).ToPointer() = 0x90;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void CustomSteeringAngle(this int handle, float value)
|
||||
public static unsafe void CustomSteeringAngle(this Vehicle veh, float value)
|
||||
{
|
||||
IntPtr address = new IntPtr((long)GetEntityAddressFunc(handle));
|
||||
IntPtr address = new IntPtr((long)veh.MemoryAddress);
|
||||
if (address == IntPtr.Zero || SteeringAngleOffset == 0)
|
||||
{
|
||||
return;
|
||||
@ -80,6 +47,11 @@ namespace CoopClient
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static T GetGameMs<T>() where T : IConvertible
|
||||
{
|
||||
return (T)Convert.ChangeType(1f / (Game.FPS > 60f ? 60f : Game.FPS) * 1000f, typeof(T));
|
||||
}
|
||||
|
||||
public static Model ModelRequest(this int hash)
|
||||
{
|
||||
Model model = new Model(hash);
|
||||
@ -167,6 +139,15 @@ namespace CoopClient
|
||||
return aimOrShoot ? (isNpc ? GetLastWeaponImpact(ped) : RaycastEverything(new Vector2(0, 0))) : new Vector3();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Only works for players NOT NPCs
|
||||
/// </summary>
|
||||
/// <returns>Vector3</returns>
|
||||
public static Vector3 GetVehicleAimCoords()
|
||||
{
|
||||
return RaycastEverything(new Vector2(0, 0));
|
||||
}
|
||||
|
||||
public static byte? GetVehicleFlags(this Vehicle veh, bool fullSync)
|
||||
{
|
||||
byte? flags = 0;
|
||||
@ -365,6 +346,38 @@ namespace CoopClient
|
||||
return coord.GetResult<Vector3>();
|
||||
}
|
||||
|
||||
public static bool IsTurretSeat(this Vehicle veh, int seat)
|
||||
{
|
||||
if (!Function.Call<bool>(Hash.DOES_VEHICLE_HAVE_WEAPONS, veh.Handle))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (seat)
|
||||
{
|
||||
case -1:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Rhino
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Khanjari
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.FireTruck;
|
||||
case 1:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical2
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical3
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.HalfTrack; // Not sure
|
||||
case 2:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|
||||
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2;
|
||||
case 3:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Limo2;
|
||||
case 7:
|
||||
return (VehicleHash)veh.Model.Hash == VehicleHash.Insurgent;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static double DegToRad(double deg)
|
||||
{
|
||||
return deg * Math.PI / 180.0;
|
||||
|
@ -6,14 +6,20 @@ using GTA.Native;
|
||||
|
||||
namespace CoopClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public class WorldThread : Script
|
||||
{
|
||||
private static bool LastDisableTraffic = false;
|
||||
|
||||
/// <summary>
|
||||
/// Don't use it!
|
||||
/// </summary>
|
||||
public WorldThread()
|
||||
{
|
||||
Tick += OnTick;
|
||||
Interval = 1000 / 60;
|
||||
Interval = Util.GetGameMs<int>();
|
||||
Aborted += (sender, e) =>
|
||||
{
|
||||
if (LastDisableTraffic)
|
||||
@ -23,7 +29,7 @@ namespace CoopClient
|
||||
};
|
||||
}
|
||||
|
||||
public static void OnTick(object sender, EventArgs e)
|
||||
internal static void OnTick(object sender, EventArgs e)
|
||||
{
|
||||
if (Game.IsLoading)
|
||||
{
|
||||
|
@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="protobuf-net" version="2.4.6" targetFramework="net48" />
|
||||
</packages>
|
@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31605.320
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FirstGameMode", "FirstGameMode\FirstGameMode.csproj", "{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{212B1A61-0C03-4B0E-A53C-2CC6B667E0DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3C329584-BE48-469B-85D8-FD24F47BD033}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -1,52 +0,0 @@
|
||||
using CoopServer;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FirstGameMode
|
||||
{
|
||||
class Commands
|
||||
{
|
||||
[Command("set")]
|
||||
public static void TimeCommand(CommandContext ctx)
|
||||
{
|
||||
if (ctx.Args.Length < 1)
|
||||
{
|
||||
ctx.Client.SendChatMessage("Please use \"/set <OPTION> ...\"");
|
||||
return;
|
||||
}
|
||||
else if (ctx.Args.Length < 2)
|
||||
{
|
||||
ctx.Client.SendChatMessage($"Please use \"/set {ctx.Args[0]} ...\"");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ctx.Args[0])
|
||||
{
|
||||
case "time":
|
||||
int hours, minutes, seconds;
|
||||
|
||||
if (ctx.Args.Length < 4)
|
||||
{
|
||||
ctx.Client.SendChatMessage("Please use \"/set time <HOURS> <MINUTES> <SECONDS>\"");
|
||||
return;
|
||||
}
|
||||
else if (!int.TryParse(ctx.Args[1], out hours) || !int.TryParse(ctx.Args[2], out minutes) || !int.TryParse(ctx.Args[3], out seconds))
|
||||
{
|
||||
ctx.Client.SendChatMessage($"Please use \"/set time <NUMBER> <NUMBER> <NUMBER>\"");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Client.SendNativeCall(0x47C3B5848C3E45D8, hours, minutes, seconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[Command("inrange")]
|
||||
public static void InRangeCommand(CommandContext ctx)
|
||||
{
|
||||
List<string> list = new();
|
||||
API.GetAllClients().FindAll(x => x.Player.IsInRangeOf(ctx.Client.Player.Position, 15f)).ForEach(x => list.Add(x.Player.Username));
|
||||
|
||||
ctx.Client.SendChatMessage(string.Join(", ", list.ToArray()));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="CoopServer">
|
||||
<HintPath>..\..\Server\bin\Debug\net6.0\CoopServer.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Timers;
|
||||
using System.Linq;
|
||||
|
||||
using CoopServer;
|
||||
|
||||
namespace FirstGameMode
|
||||
{
|
||||
[Serializable]
|
||||
public class SetPlayerTime
|
||||
{
|
||||
public int Hours { get; set; }
|
||||
public int Minutes { get; set; }
|
||||
public int Seconds { get; set; }
|
||||
}
|
||||
|
||||
public class Main : ServerScript
|
||||
{
|
||||
private static readonly Timer RunningSinceTimer = new() { Interval = 1000 };
|
||||
private static int RunningSince = 0;
|
||||
|
||||
public Main()
|
||||
{
|
||||
RunningSinceTimer.Start();
|
||||
RunningSinceTimer.Elapsed += new ElapsedEventHandler((sender, e) => RunningSince += 1);
|
||||
|
||||
API.OnPlayerConnected += OnPlayerConnected;
|
||||
API.OnPlayerDisconnected += OnPlayerDisconnected;
|
||||
API.OnChatMessage += OnChatMessage;
|
||||
API.OnModPacketReceived += OnModPacketReceived;
|
||||
|
||||
API.RegisterCommand("running", RunningCommand);
|
||||
API.RegisterCommands<Commands>();
|
||||
}
|
||||
|
||||
private void OnModPacketReceived(long from, long target, string mod, byte customID, byte[] bytes, CancelEventArgs args)
|
||||
{
|
||||
if (mod != "FirstScript" || customID != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
args.Cancel = true;
|
||||
|
||||
// Get data from bytes
|
||||
SetPlayerTime setPlayerTime = bytes.CDeserialize<SetPlayerTime>();
|
||||
|
||||
// Find the client by 'from' and send the time back as a nativecall
|
||||
//API.GetAllClients().Find(x => x.ID == from).SendNativeCall(0x47C3B5848C3E45D8, setPlayerTime.Hours, setPlayerTime.Minutes, setPlayerTime.Seconds);
|
||||
|
||||
// Find the client by 'target' and send the time back as a nativecall
|
||||
Client targetClient = API.GetAllClients().FirstOrDefault(x => x.ID == target);
|
||||
|
||||
targetClient.SendChatMessage($"New modpacket nativecall from \"{API.GetAllClients().FirstOrDefault(x => x.ID == from)?.Player.Username}\"");
|
||||
targetClient.SendNativeCall(0x47C3B5848C3E45D8, setPlayerTime.Hours, setPlayerTime.Minutes, setPlayerTime.Seconds);
|
||||
}
|
||||
|
||||
public static void RunningCommand(CommandContext ctx)
|
||||
{
|
||||
ctx.Client.SendChatMessage("Server has been running for: " + RunningSince + " seconds!");
|
||||
}
|
||||
|
||||
public static void OnPlayerConnected(Client client)
|
||||
{
|
||||
API.SendChatMessageToAll("Player " + client.Player.Username + " connected!");
|
||||
}
|
||||
|
||||
public static void OnPlayerDisconnected(Client client)
|
||||
{
|
||||
API.SendChatMessageToAll("Player " + client.Player.Username + " disconnected!");
|
||||
}
|
||||
|
||||
public static void OnChatMessage(string username, string message, CancelEventArgs e)
|
||||
{
|
||||
e.Cancel = true;
|
||||
|
||||
if (message.StartsWith("EASTEREGG"))
|
||||
{
|
||||
Client client;
|
||||
if ((client = API.GetClientByUsername(username)) != null)
|
||||
{
|
||||
client.SendChatMessage("You found the EASTEREGG! *-*");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
API.SendChatMessageToAll(message, username);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31717.71
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FirstScript", "FirstScript\FirstScript.csproj", "{9524435E-24F3-4BDB-B7AA-351A00C2D209}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9524435E-24F3-4BDB-B7AA-351A00C2D209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9524435E-24F3-4BDB-B7AA-351A00C2D209}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9524435E-24F3-4BDB-B7AA-351A00C2D209}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9524435E-24F3-4BDB-B7AA-351A00C2D209}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {398E3E84-30E0-408E-89E5-91CB0F001E14}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{9524435E-24F3-4BDB-B7AA-351A00C2D209}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>FirstScript</RootNamespace>
|
||||
<AssemblyName>FirstScript</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CoopClient">
|
||||
<HintPath>..\..\Client\bin\Debug\CoopClient.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\..\Libs\Release\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -1,76 +0,0 @@
|
||||
using System;
|
||||
|
||||
using GTA;
|
||||
|
||||
namespace FirstScript
|
||||
{
|
||||
[Serializable]
|
||||
public class TestPacketClass
|
||||
{
|
||||
public int A { get; set; }
|
||||
public int B { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SetPlayerTimePacket
|
||||
{
|
||||
public int Hours { get; set; }
|
||||
public int Minutes { get; set; }
|
||||
public int Seconds { get; set; }
|
||||
}
|
||||
|
||||
public class Main : Script
|
||||
{
|
||||
public Main()
|
||||
{
|
||||
Tick += OnTick;
|
||||
Interval += 3000;
|
||||
|
||||
CoopClient.Interface.OnModPacketReceived += OnModPacketReceived;
|
||||
CoopClient.Interface.OnConnection += OnConnection;
|
||||
}
|
||||
|
||||
private void OnConnection(bool connected, string reason = null)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
CoopClient.Interface.SendChatMessage("Mod", "Welcome!");
|
||||
}
|
||||
else
|
||||
{
|
||||
GTA.UI.Notification.Show("~r~Mod~s~: ~b~C(°-°)D");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTick(object sender, EventArgs e)
|
||||
{
|
||||
if (!CoopClient.Interface.IsOnServer())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CoopClient.Interface.SendDataToAll("FirstScript", 0, CoopClient.CoopSerializer.CSerialize(new TestPacketClass() { A = 5, B = 15 }));
|
||||
CoopClient.Interface.SendDataToPlayer(CoopClient.Interface.GetLocalID(), "FirstScript", 1, CoopClient.CoopSerializer.CSerialize(new SetPlayerTimePacket() { Hours = 1, Minutes = 2, Seconds = 3 }));
|
||||
}
|
||||
|
||||
private void OnModPacketReceived(long from, string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
if (mod != "FirstScript")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (customID)
|
||||
{
|
||||
case 0:
|
||||
TestPacketClass testPacketClass = CoopClient.CoopSerializer.CDeserialize<TestPacketClass>(bytes);
|
||||
|
||||
GTA.UI.Notification.Show($"ModPacket(0)({from}): A[{testPacketClass.A}] B[{testPacketClass.B}]");
|
||||
break;
|
||||
default:
|
||||
GTA.UI.Notification.Show($"ModPacket({from}): ~r~Unknown customID!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||
// die einer Assembly zugeordnet sind.
|
||||
[assembly: AssemblyTitle("FirstScript")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("FirstScript")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
||||
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
[assembly: Guid("9524435e-24f3-4bdb-b7aa-351a00c2d209")]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||
// indem Sie "*" wie unten gezeigt eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
BIN
Libs/Release/ScriptHookVDotNet.asi
Normal file
BIN
Libs/Release/ScriptHookVDotNet.asi
Normal file
Binary file not shown.
BIN
Libs/Release/ScriptHookVDotNet2.dll
Normal file
BIN
Libs/Release/ScriptHookVDotNet2.dll
Normal file
Binary file not shown.
16221
Libs/Release/ScriptHookVDotNet2.xml
Normal file
16221
Libs/Release/ScriptHookVDotNet2.xml
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
23904
Libs/Release/ScriptHookVDotNet3.xml
Normal file
23904
Libs/Release/ScriptHookVDotNet3.xml
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Libs/Release/scripts/Newtonsoft.Json.dll
Normal file
BIN
Libs/Release/scripts/Newtonsoft.Json.dll
Normal file
Binary file not shown.
BIN
Libs/Release/scripts/protobuf-net.dll
Normal file
BIN
Libs/Release/scripts/protobuf-net.dll
Normal file
Binary file not shown.
11
README.md
11
README.md
@ -11,14 +11,7 @@
|
||||
| ⚠️ The original GTACoOp can be found [HERE](https://gtacoop.com/) |
|
||||
| --- |
|
||||
|
||||
Get the latest client to test out the new features before we release them:
|
||||
<br/>
|
||||
[**Download**](https://entenkoeniq.de/client.zip)
|
||||
<br/>
|
||||
<br/>
|
||||
Server:
|
||||
<br/>
|
||||
**168.119.29.103:4499**
|
||||
http://gtacoopr.000webhostapp.com/
|
||||
|
||||
# 📋 Requirements
|
||||
- Visual Studio 2022
|
||||
@ -26,7 +19,7 @@ Server:
|
||||
- .NET Framework 4.8
|
||||
|
||||
# 📚 Libraries
|
||||
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.3.2)
|
||||
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/tree/ca82751b7cc8bdf2203d361b27fe0d1aa895eb55)
|
||||
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/tree/1342949cd19eaa4115990793311681ea1568054c)
|
||||
- Lidgren Network Custom (***PRIVATE***)
|
||||
- - No new features (only improvements)
|
||||
|
110
Server/Client.cs
110
Server/Client.cs
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
@ -43,62 +44,97 @@ namespace CoopServer
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region FUNCTIONS
|
||||
public void Kick(string[] reason)
|
||||
{
|
||||
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID).Disconnect(string.Join(" ", reason));
|
||||
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID)?.Disconnect(string.Join(" ", reason));
|
||||
}
|
||||
|
||||
public void SendChatMessage(string message, string from = "Server")
|
||||
{
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
|
||||
|
||||
ChatMessagePacket packet = new()
|
||||
try
|
||||
{
|
||||
Username = from,
|
||||
Message = message
|
||||
};
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
|
||||
if (userConnection == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
ChatMessagePacket packet = new()
|
||||
{
|
||||
Username = from,
|
||||
Message = message
|
||||
};
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
|
||||
public void SendNativeCall(ulong hash, params object[] args)
|
||||
{
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
|
||||
|
||||
List<NativeArgument> arguments = Util.ParseNativeArguments(args);
|
||||
if (arguments == null)
|
||||
try
|
||||
{
|
||||
return;
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
|
||||
if (userConnection == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<NativeArgument> arguments = Util.ParseNativeArguments(args);
|
||||
if (arguments == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NativeCallPacket packet = new()
|
||||
{
|
||||
Hash = hash,
|
||||
Args = arguments
|
||||
};
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
NativeCallPacket packet = new()
|
||||
catch (Exception e)
|
||||
{
|
||||
Hash = hash,
|
||||
Args = arguments
|
||||
};
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
|
||||
public void SendModPacket(string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new ModPacket()
|
||||
try
|
||||
{
|
||||
ID = -1,
|
||||
Target = 0,
|
||||
Mod = mod,
|
||||
CustomPacketID = customID,
|
||||
Bytes = bytes
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
Server.MainNetServer.FlushSendQueue();
|
||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
|
||||
if (userConnection == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new ModPacket()
|
||||
{
|
||||
ID = 0,
|
||||
Target = 0,
|
||||
Mod = mod,
|
||||
CustomPacketID = customID,
|
||||
Bytes = bytes
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
Server.MainNetServer.FlushSendQueue();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,15 @@
|
||||
<RepositoryUrl>https://github.com/GTACOOP-R/GTACoop-R</RepositoryUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="protobuf-net" Version="2.4.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Lidgren.Network">
|
||||
<HintPath>..\Libs\Release\Lidgren.Network.dll</HintPath>
|
||||
<HintPath>..\Libs\Release\scripts\Lidgren.Network.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net">
|
||||
<HintPath>..\Libs\Release\scripts\protobuf-net.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using Lidgren.Network;
|
||||
using ProtoBuf;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CoopServer
|
||||
{
|
||||
@ -387,18 +387,21 @@ namespace CoopServer
|
||||
public float VehSteeringAngle { get; set; }
|
||||
|
||||
[ProtoMember(13)]
|
||||
public int[] VehColors { get; set; }
|
||||
public LVector3 VehAimCoords { get; set; }
|
||||
|
||||
[ProtoMember(14)]
|
||||
public Dictionary<int, int> VehMods { get; set; }
|
||||
public int[] VehColors { get; set; }
|
||||
|
||||
[ProtoMember(15)]
|
||||
public VehicleDoors[] VehDoors { get; set; }
|
||||
public Dictionary<int, int> VehMods { get; set; }
|
||||
|
||||
[ProtoMember(16)]
|
||||
public int VehTires { get; set; }
|
||||
public VehicleDoors[] VehDoors { get; set; }
|
||||
|
||||
[ProtoMember(17)]
|
||||
public int VehTires { get; set; }
|
||||
|
||||
[ProtoMember(18)]
|
||||
public byte? Flag { get; set; } = 0;
|
||||
|
||||
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
|
||||
@ -429,6 +432,7 @@ namespace CoopServer
|
||||
VehVelocity = data.VehVelocity;
|
||||
VehSpeed = data.VehSpeed;
|
||||
VehSteeringAngle = data.VehSteeringAngle;
|
||||
VehAimCoords = data.VehAimCoords;
|
||||
VehColors = data.VehColors;
|
||||
VehMods = data.VehMods;
|
||||
VehDoors = data.VehDoors;
|
||||
@ -852,10 +856,8 @@ namespace CoopServer
|
||||
return null;
|
||||
}
|
||||
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
using MemoryStream ms = new MemoryStream();
|
||||
bf.Serialize(ms, obj);
|
||||
return ms.ToArray();
|
||||
string jsonString = JsonConvert.SerializeObject(obj);
|
||||
return System.Text.Encoding.UTF8.GetBytes(jsonString);
|
||||
}
|
||||
|
||||
public static T CDeserialize<T>(this byte[] bytes) where T : class
|
||||
@ -865,12 +867,8 @@ namespace CoopServer
|
||||
return null;
|
||||
}
|
||||
|
||||
using MemoryStream memStream = new MemoryStream();
|
||||
BinaryFormatter binForm = new BinaryFormatter();
|
||||
memStream.Write(bytes, 0, bytes.Length);
|
||||
memStream.Seek(0, SeekOrigin.Begin);
|
||||
T obj = (T)binForm.Deserialize(memStream);
|
||||
return obj;
|
||||
var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
|
||||
return JsonConvert.DeserializeObject<T>(jsonString);
|
||||
}
|
||||
|
||||
internal static T Deserialize<T>(this byte[] data) where T : new()
|
||||
|
@ -8,11 +8,7 @@
|
||||
private LVector3 CurrentPosition { get; set; }
|
||||
public LVector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return CurrentPosition;
|
||||
}
|
||||
|
||||
get => CurrentPosition;
|
||||
set
|
||||
{
|
||||
LastPosition = CurrentPosition;
|
||||
@ -24,6 +20,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
private int CurrentHealth { get; set; }
|
||||
public int Health
|
||||
{
|
||||
get => CurrentHealth;
|
||||
set
|
||||
{
|
||||
if (CurrentHealth != value && Server.MainResource != null)
|
||||
{
|
||||
Server.MainResource.InvokePlayerHealthUpdate(this);
|
||||
}
|
||||
|
||||
CurrentHealth = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInRangeOf(LVector3 position, float distance)
|
||||
{
|
||||
|
385
Server/Server.cs
385
Server/Server.cs
@ -4,14 +4,23 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Lidgren.Network;
|
||||
using System.Text;
|
||||
|
||||
namespace CoopServer
|
||||
{
|
||||
class Server
|
||||
internal class IpInfo
|
||||
{
|
||||
private static readonly string CompatibleVersion = "V0_8_0_1";
|
||||
public string Country { get; set; }
|
||||
}
|
||||
|
||||
internal class Server
|
||||
{
|
||||
private static readonly string CompatibleVersion = "V0_9_0";
|
||||
|
||||
public static readonly Settings MainSettings = Util.Read<Settings>("CoopSettings.xml");
|
||||
private readonly Blocklist MainBlocklist = Util.Read<Blocklist>("Blocklist.xml");
|
||||
@ -62,6 +71,86 @@ namespace CoopServer
|
||||
}
|
||||
}
|
||||
|
||||
if (MainSettings.AnnounceSelf)
|
||||
{
|
||||
Logging.Info("Announcing to master server...");
|
||||
|
||||
#region -- MASTERSERVER --
|
||||
new Thread(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpClient httpClient = new();
|
||||
|
||||
IpInfo info;
|
||||
|
||||
try
|
||||
{
|
||||
string data = await httpClient.GetStringAsync("https://ipinfo.io/json");
|
||||
|
||||
info = JsonConvert.DeserializeObject<IpInfo>(data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
info = new() { Country = "?" };
|
||||
}
|
||||
|
||||
bool responseError = false;
|
||||
|
||||
while (!responseError)
|
||||
{
|
||||
string msg =
|
||||
"{ " +
|
||||
"\"port\": \"" + MainSettings.ServerPort + "\", " +
|
||||
"\"name\": \"" + MainSettings.ServerName + "\", " +
|
||||
"\"version\": \"" + CompatibleVersion.Replace("_", ".") + "\", " +
|
||||
"\"players\": \"" + MainNetServer.ConnectionsCount + "\", " +
|
||||
"\"maxPlayers\": \"" + MainSettings.MaxPlayers + "\", " +
|
||||
"\"allowlist\": \"" + MainSettings.Allowlist + "\", " +
|
||||
"\"mods\": \"" + MainSettings.ModsAllowed + "\", " +
|
||||
"\"npcs\": \"" + MainSettings.NpcsAllowed + "\", " +
|
||||
"\"country\": \"" + info.Country + "\"" +
|
||||
" }";
|
||||
|
||||
HttpResponseMessage response = null;
|
||||
string responseContent = "";
|
||||
try
|
||||
{
|
||||
response = await httpClient.PostAsync(MainSettings.MasterServer, new StringContent(msg, Encoding.UTF8, "application/json"));
|
||||
if (response == null)
|
||||
{
|
||||
Logging.Error("MasterServer: Something went wrong!");
|
||||
continue;
|
||||
}
|
||||
|
||||
responseContent = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Error(ex.Message);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||
{
|
||||
Logging.Error($"MasterServer: {response.StatusCode}");
|
||||
responseError = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sleep for 12.5s
|
||||
Thread.Sleep(12500);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Error($"MasterServer: {ex.Message}");
|
||||
}
|
||||
}).Start();
|
||||
#endregion
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(MainSettings.Resource))
|
||||
{
|
||||
try
|
||||
@ -283,35 +372,35 @@ namespace CoopServer
|
||||
{
|
||||
packet = new ModPacket();
|
||||
packet.NetIncomingMessageToPacket(message);
|
||||
|
||||
ModPacket modPacket = (ModPacket)packet;
|
||||
if (MainResource != null)
|
||||
if (MainResource != null &&
|
||||
MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes))
|
||||
{
|
||||
if (MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes))
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Was canceled
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
modPacket.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
|
||||
if (modPacket.Target != 0)
|
||||
else if (modPacket.Target != -1)
|
||||
{
|
||||
NetConnection target = MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == modPacket.Target);
|
||||
if (target == null)
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
modPacket.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
|
||||
if (modPacket.Target != 0)
|
||||
{
|
||||
Logging.Error($"[ModPacket] target \"{modPacket.Target}\" not found!");
|
||||
return;
|
||||
NetConnection target = MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == modPacket.Target);
|
||||
if (target.Equals(default(Client)))
|
||||
{
|
||||
Logging.Error($"[ModPacket] target \"{modPacket.Target}\" not found!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send back to target
|
||||
MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send back to all players
|
||||
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
// Send back to target
|
||||
MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send back to all players
|
||||
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -331,7 +420,7 @@ namespace CoopServer
|
||||
break;
|
||||
case NetIncomingMessageType.ConnectionLatencyUpdated:
|
||||
Client client = Clients.FirstOrDefault(x => x.ID == message.SenderConnection.RemoteUniqueIdentifier);
|
||||
if (client != default)
|
||||
if (!client.Equals(default(Client)))
|
||||
{
|
||||
client.Latency = message.ReadFloat();
|
||||
}
|
||||
@ -420,18 +509,23 @@ namespace CoopServer
|
||||
|
||||
long localID = local.RemoteUniqueIdentifier;
|
||||
|
||||
Client tmpClient;
|
||||
|
||||
// Add the player to Players
|
||||
Clients.Add(
|
||||
new Client()
|
||||
{
|
||||
ID = localID,
|
||||
Player = new()
|
||||
lock (Clients)
|
||||
{
|
||||
Clients.Add(
|
||||
tmpClient = new Client()
|
||||
{
|
||||
SocialClubName = packet.SocialClubName,
|
||||
Username = packet.Username
|
||||
ID = localID,
|
||||
Player = new()
|
||||
{
|
||||
SocialClubName = packet.SocialClubName,
|
||||
Username = packet.Username
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
|
||||
@ -447,65 +541,70 @@ namespace CoopServer
|
||||
|
||||
// Accept the connection and send back a new handshake packet with the connection ID
|
||||
local.Approve(outgoingMessage);
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerHandshake(tmpClient);
|
||||
}
|
||||
}
|
||||
|
||||
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
|
||||
private static void SendPlayerConnectPacket(NetConnection local, PlayerConnectPacket packet)
|
||||
{
|
||||
Client localClient = Clients.FirstOrDefault(x => x.ID == packet.ID);
|
||||
if (localClient.Equals(default(Client)))
|
||||
{
|
||||
local.Disconnect("No data found!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
|
||||
{
|
||||
SendChatMessage(new ChatMessagePacket() { Username = "Server", Message = MainSettings.WelcomeMessage }, new List<NetConnection>() { local });
|
||||
}
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerConnected(Clients.Find(x => x.ID == packet.ID));
|
||||
}
|
||||
|
||||
List<NetConnection> clients;
|
||||
if ((clients = Util.FilterAllLocal(local)).Count == 0)
|
||||
if ((clients = Util.FilterAllLocal(local)).Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Send all players to local
|
||||
clients.ForEach(targetPlayer =>
|
||||
{
|
||||
long targetPlayerID = targetPlayer.RemoteUniqueIdentifier;
|
||||
|
||||
// Send all players to local
|
||||
clients.ForEach(targetPlayer =>
|
||||
{
|
||||
long targetPlayerID = targetPlayer.RemoteUniqueIdentifier;
|
||||
|
||||
Client targetEntity = Clients.First(x => x.ID == targetPlayerID);
|
||||
Client targetClient = Clients.FirstOrDefault(x => x.ID == targetPlayerID);
|
||||
if (!targetClient.Equals(default(Client)))
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
new PlayerConnectPacket()
|
||||
{
|
||||
ID = targetPlayerID,
|
||||
SocialClubName = targetClient.Player.SocialClubName,
|
||||
Username = targetClient.Player.Username
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Send local to all players
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
new PlayerConnectPacket()
|
||||
{
|
||||
ID = targetPlayerID,
|
||||
SocialClubName = targetEntity.Player.SocialClubName,
|
||||
Username = targetEntity.Player.Username
|
||||
ID = packet.ID,
|
||||
SocialClubName = localClient.Player.SocialClubName,
|
||||
Username = localClient.Player.Username
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
});
|
||||
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
// Send local to all players
|
||||
Client localClient = Clients.First(x => x.ID == packet.ID);
|
||||
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
new PlayerConnectPacket()
|
||||
if (MainResource != null)
|
||||
{
|
||||
ID = packet.ID,
|
||||
SocialClubName = localClient.Player.SocialClubName,
|
||||
Username = localClient.Player.Username
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
MainResource.InvokePlayerConnected(localClient);
|
||||
}
|
||||
}
|
||||
|
||||
// Send all players a message that someone has left the server
|
||||
private static void SendPlayerDisconnectPacket(PlayerDisconnectPacket packet)
|
||||
{
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerDisconnected(Clients.Find(x => x.ID == packet.ID));
|
||||
}
|
||||
|
||||
List<NetConnection> clients;
|
||||
if ((clients = Util.FilterAllLocal(packet.ID)).Count > 0)
|
||||
{
|
||||
@ -514,16 +613,40 @@ namespace CoopServer
|
||||
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
Clients.Remove(Clients.Find(x => x.ID == packet.ID));
|
||||
Client localClient = Clients.FirstOrDefault(x => x.ID == packet.ID);
|
||||
if (localClient.Equals(default(Client)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerDisconnected(localClient);
|
||||
}
|
||||
|
||||
lock (Clients)
|
||||
{
|
||||
Clients.Remove(localClient);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FullSyncPlayer(FullSyncPlayerPacket packet)
|
||||
{
|
||||
Client client = Clients.First(x => x.ID == packet.Extra.ID);
|
||||
client.Player.Position = packet.Extra.Position;
|
||||
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
|
||||
if (tmpClient.Equals(default(Client)))
|
||||
{
|
||||
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
|
||||
if (localConn != null)
|
||||
{
|
||||
localConn.Disconnect("No data found!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
tmpClient.Player.Position = packet.Extra.Position;
|
||||
tmpClient.Player.Health = packet.Extra.Health;
|
||||
|
||||
PlayerPacket playerPacket = packet.Extra;
|
||||
playerPacket.Latency = client.Latency;
|
||||
playerPacket.Latency = tmpClient.Latency;
|
||||
|
||||
packet.Extra = playerPacket;
|
||||
|
||||
@ -545,15 +668,30 @@ namespace CoopServer
|
||||
|
||||
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
|
||||
});
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerUpdate(tmpClient);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FullSyncPlayerVeh(FullSyncPlayerVehPacket packet)
|
||||
{
|
||||
Client client = Clients.First(x => x.ID == packet.Extra.ID);
|
||||
client.Player.Position = packet.Extra.Position;
|
||||
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
|
||||
if (tmpClient.Equals(default(Client)))
|
||||
{
|
||||
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
|
||||
if (localConn != null)
|
||||
{
|
||||
localConn.Disconnect("No data found!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
tmpClient.Player.Position = packet.Extra.Position;
|
||||
tmpClient.Player.Health = packet.Extra.Health;
|
||||
|
||||
PlayerPacket playerPacket = packet.Extra;
|
||||
playerPacket.Latency = client.Latency;
|
||||
playerPacket.Latency = tmpClient.Latency;
|
||||
|
||||
packet.Extra = playerPacket;
|
||||
|
||||
@ -575,15 +713,30 @@ namespace CoopServer
|
||||
|
||||
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
|
||||
});
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerUpdate(tmpClient);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LightSyncPlayer(LightSyncPlayerPacket packet)
|
||||
{
|
||||
Client client = Clients.First(x => x.ID == packet.Extra.ID);
|
||||
client.Player.Position = packet.Extra.Position;
|
||||
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
|
||||
if (tmpClient.Equals(default(Client)))
|
||||
{
|
||||
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
|
||||
if (localConn != null)
|
||||
{
|
||||
localConn.Disconnect("No data found!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
tmpClient.Player.Position = packet.Extra.Position;
|
||||
tmpClient.Player.Health = packet.Extra.Health;
|
||||
|
||||
PlayerPacket playerPacket = packet.Extra;
|
||||
playerPacket.Latency = client.Latency;
|
||||
playerPacket.Latency = tmpClient.Latency;
|
||||
|
||||
packet.Extra = playerPacket;
|
||||
|
||||
@ -605,15 +758,30 @@ namespace CoopServer
|
||||
|
||||
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
|
||||
});
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerUpdate(tmpClient);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LightSyncPlayerVeh(LightSyncPlayerVehPacket packet)
|
||||
{
|
||||
Client client = Clients.First(x => x.ID == packet.Extra.ID);
|
||||
client.Player.Position = packet.Extra.Position;
|
||||
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
|
||||
if (tmpClient.Equals(default(Client)))
|
||||
{
|
||||
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
|
||||
if (localConn != null)
|
||||
{
|
||||
localConn.Disconnect("No data found!");
|
||||
}
|
||||
return;
|
||||
}
|
||||
tmpClient.Player.Position = packet.Extra.Position;
|
||||
tmpClient.Player.Health = packet.Extra.Health;
|
||||
|
||||
PlayerPacket playerPacket = packet.Extra;
|
||||
playerPacket.Latency = client.Latency;
|
||||
playerPacket.Latency = tmpClient.Latency;
|
||||
|
||||
packet.Extra = playerPacket;
|
||||
|
||||
@ -635,6 +803,11 @@ namespace CoopServer
|
||||
|
||||
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
|
||||
});
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
MainResource.InvokePlayerUpdate(tmpClient);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a message to targets or all players
|
||||
@ -644,19 +817,40 @@ namespace CoopServer
|
||||
|
||||
if (MainResource != null)
|
||||
{
|
||||
if (packet.Message.StartsWith("/"))
|
||||
if (packet.Message.StartsWith('/'))
|
||||
{
|
||||
string[] cmdArgs = packet.Message.Split(" ");
|
||||
string cmdName = cmdArgs[0].Remove(0, 1);
|
||||
if (Commands.Any(x => x.Key.Name == cmdName))
|
||||
{
|
||||
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
|
||||
|
||||
CommandContext ctx = new()
|
||||
{
|
||||
Client = Clients.First(x => x.Player.Username == packet.Username),
|
||||
Args = cmdArgs.Skip(1).ToArray()
|
||||
Client = Clients.FirstOrDefault(x => x.Player.Username == packet.Username),
|
||||
Args = argsWithoutCmd
|
||||
};
|
||||
|
||||
KeyValuePair<Command, Action<CommandContext>> command = Commands.First(x => x.Key.Name == cmdName);
|
||||
|
||||
if (command.Key.Usage != null && command.Key.ArgsLength != argsWithoutCmd.Length)
|
||||
{
|
||||
NetConnection userConnection = Util.GetConnectionByUsername(packet.Username);
|
||||
if (userConnection == default)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
outgoingMessage = MainNetServer.CreateMessage();
|
||||
new ChatMessagePacket()
|
||||
{
|
||||
Username = "Server",
|
||||
Message = command.Key.Usage
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
command.Value.Invoke(ctx);
|
||||
}
|
||||
else
|
||||
@ -678,7 +872,7 @@ namespace CoopServer
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (MainResource.InvokeChatMessage(packet.Username, packet.Message))
|
||||
{
|
||||
return;
|
||||
@ -723,9 +917,20 @@ namespace CoopServer
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
|
||||
{
|
||||
Command command = new(name) { Usage = usage, ArgsLength = argsLength };
|
||||
|
||||
if (Commands.ContainsKey(command))
|
||||
{
|
||||
throw new Exception("Command \"" + command.Name + "\" was already been registered!");
|
||||
}
|
||||
|
||||
Commands.Add(command, callback);
|
||||
}
|
||||
public static void RegisterCommand(string name, Action<CommandContext> callback)
|
||||
{
|
||||
Command command = new() { Name = name };
|
||||
Command command = new(name);
|
||||
|
||||
if (Commands.ContainsKey(command))
|
||||
{
|
||||
@ -737,13 +942,13 @@ namespace CoopServer
|
||||
|
||||
public static void RegisterCommands<T>()
|
||||
{
|
||||
IEnumerable<MethodInfo> commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(CommandAttribute), false).Any());
|
||||
IEnumerable<MethodInfo> commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(Command), false).Any());
|
||||
|
||||
foreach (MethodInfo method in commands)
|
||||
{
|
||||
CommandAttribute attribute = method.GetCustomAttribute<CommandAttribute>(true);
|
||||
Command attribute = method.GetCustomAttribute<Command>(true);
|
||||
|
||||
RegisterCommand(attribute.Name, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
|
||||
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@ -9,22 +10,25 @@ using Lidgren.Network;
|
||||
|
||||
namespace CoopServer
|
||||
{
|
||||
public abstract class ServerScript
|
||||
{
|
||||
public API API { get; } = new();
|
||||
}
|
||||
|
||||
internal class Resource
|
||||
{
|
||||
private static Thread _mainThread;
|
||||
private static bool _hasToStop = false;
|
||||
private static Queue<Action> _actionQueue;
|
||||
private static TaskFactory _factory;
|
||||
private static Queue _actionQueue;
|
||||
private static ServerScript _script;
|
||||
|
||||
public Resource(ServerScript script)
|
||||
{
|
||||
_factory = new();
|
||||
_actionQueue = new();
|
||||
_actionQueue = Queue.Synchronized(new Queue());
|
||||
_mainThread = new(ThreadLoop) { IsBackground = true };
|
||||
_mainThread.Start();
|
||||
|
||||
lock (_actionQueue)
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(() =>
|
||||
{
|
||||
@ -36,88 +40,120 @@ namespace CoopServer
|
||||
|
||||
private void ThreadLoop()
|
||||
{
|
||||
do
|
||||
while (!_hasToStop)
|
||||
{
|
||||
if (_actionQueue.Count != 0)
|
||||
Queue localQueue;
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
lock (_actionQueue)
|
||||
{
|
||||
_factory.StartNew(() => _actionQueue.Dequeue()?.Invoke());
|
||||
}
|
||||
localQueue = new(_actionQueue);
|
||||
_actionQueue.Clear();
|
||||
}
|
||||
|
||||
while (localQueue.Count > 0)
|
||||
{
|
||||
(localQueue.Dequeue() as Action)?.Invoke();
|
||||
}
|
||||
|
||||
// 16 milliseconds to sleep to reduce CPU usage
|
||||
Thread.Sleep(1000 / 60);
|
||||
} while (_hasToStop);
|
||||
}
|
||||
}
|
||||
|
||||
public bool InvokeModPacketReceived(long from, long target, string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
Task<bool> shutdownTask = new(() => _script.API.InvokeModPacketReceived(from, target, mod, customID, bytes));
|
||||
shutdownTask.Start();
|
||||
shutdownTask.Wait(5000);
|
||||
Task<bool> task = new(() => _script.API.InvokeModPacketReceived(from, target, mod, customID, bytes));
|
||||
task.Start();
|
||||
task.Wait(5000);
|
||||
|
||||
return shutdownTask.Result;
|
||||
return task.Result;
|
||||
}
|
||||
|
||||
public void InvokePlayerHandshake(Client client)
|
||||
{
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerConnected(client)));
|
||||
}
|
||||
}
|
||||
|
||||
public void InvokePlayerConnected(Client client)
|
||||
{
|
||||
lock (_actionQueue)
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(() => _script.API.InvokePlayerConnected(client));
|
||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerConnected(client)));
|
||||
}
|
||||
}
|
||||
|
||||
public void InvokePlayerDisconnected(Client client)
|
||||
{
|
||||
lock (_actionQueue)
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(() => _script.API.InvokePlayerDisconnected(client));
|
||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerDisconnected(client)));
|
||||
}
|
||||
}
|
||||
|
||||
public bool InvokeChatMessage(string username, string message)
|
||||
{
|
||||
Task<bool> shutdownTask = new(() => _script.API.InvokeChatMessage(username, message));
|
||||
shutdownTask.Start();
|
||||
shutdownTask.Wait(5000);
|
||||
Task<bool> task = new(() => _script.API.InvokeChatMessage(username, message));
|
||||
task.Start();
|
||||
task.Wait(5000);
|
||||
|
||||
return shutdownTask.Result;
|
||||
return task.Result;
|
||||
}
|
||||
|
||||
public void InvokePlayerPositionUpdate(PlayerData playerData)
|
||||
{
|
||||
lock (_actionQueue)
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(() => _script.API.InvokePlayerPositionUpdate(playerData));
|
||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerPositionUpdate(playerData)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ServerScript
|
||||
{
|
||||
public API API { get; } = new();
|
||||
public void InvokePlayerUpdate(Client client)
|
||||
{
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerUpdate(client)));
|
||||
}
|
||||
}
|
||||
|
||||
public void InvokePlayerHealthUpdate(PlayerData playerData)
|
||||
{
|
||||
lock (_actionQueue.SyncRoot)
|
||||
{
|
||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerHealthUpdate(playerData)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class API
|
||||
{
|
||||
#region DELEGATES
|
||||
public delegate void EmptyEvent();
|
||||
public delegate void ChatEvent(string username, string message, CancelEventArgs cancel);
|
||||
public delegate void PlayerEvent(Client client);
|
||||
public delegate void ModEvent(long from, long target, string mod, byte customID, byte[] bytes, CancelEventArgs args);
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
public event EventHandler OnStart;
|
||||
public event EmptyEvent OnStart;
|
||||
public event ChatEvent OnChatMessage;
|
||||
public event PlayerEvent OnPlayerHandshake;
|
||||
public event PlayerEvent OnPlayerConnected;
|
||||
public event PlayerEvent OnPlayerDisconnected;
|
||||
public event PlayerEvent OnPlayerUpdate;
|
||||
public event PlayerEvent OnPlayerHealthUpdate;
|
||||
public event PlayerEvent OnPlayerPositionUpdate;
|
||||
public event ModEvent OnModPacketReceived;
|
||||
|
||||
internal void InvokeStart()
|
||||
{
|
||||
OnStart?.Invoke(this, EventArgs.Empty);
|
||||
OnStart?.Invoke();
|
||||
}
|
||||
|
||||
internal void InvokePlayerHandshake(Client client)
|
||||
{
|
||||
OnPlayerHandshake?.Invoke(client);
|
||||
}
|
||||
|
||||
internal void InvokePlayerConnected(Client client)
|
||||
@ -130,6 +166,16 @@ namespace CoopServer
|
||||
OnPlayerDisconnected?.Invoke(client);
|
||||
}
|
||||
|
||||
internal void InvokePlayerUpdate(Client client)
|
||||
{
|
||||
OnPlayerUpdate?.Invoke(client);
|
||||
}
|
||||
|
||||
internal void InvokePlayerHealthUpdate(PlayerData playerData)
|
||||
{
|
||||
OnPlayerHealthUpdate?.Invoke(Server.Clients.First(x => x.Player.Username == playerData.Username));
|
||||
}
|
||||
|
||||
internal bool InvokeChatMessage(string username, string message)
|
||||
{
|
||||
CancelEventArgs args = new(false);
|
||||
@ -153,41 +199,55 @@ namespace CoopServer
|
||||
#region FUNCTIONS
|
||||
public static void SendModPacketToAll(string mod, byte customID, byte[] bytes)
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new ModPacket()
|
||||
try
|
||||
{
|
||||
ID = -1,
|
||||
Target = 0,
|
||||
Mod = mod,
|
||||
CustomPacketID = customID,
|
||||
Bytes = bytes
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
Server.MainNetServer.FlushSendQueue();
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new ModPacket()
|
||||
{
|
||||
ID = 0,
|
||||
Target = 0,
|
||||
Mod = mod,
|
||||
CustomPacketID = customID,
|
||||
Bytes = bytes
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
Server.MainNetServer.FlushSendQueue();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendNativeCallToAll(ulong hash, params object[] args)
|
||||
{
|
||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||
try
|
||||
{
|
||||
return;
|
||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<NativeArgument> arguments;
|
||||
if ((arguments = Util.ParseNativeArguments(args)) == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NativeCallPacket packet = new()
|
||||
{
|
||||
Hash = hash,
|
||||
Args = arguments
|
||||
};
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
List<NativeArgument> arguments;
|
||||
if ((arguments = Util.ParseNativeArguments(args)) == null)
|
||||
catch (Exception e)
|
||||
{
|
||||
return;
|
||||
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
|
||||
NativeCallPacket packet = new()
|
||||
{
|
||||
Hash = hash,
|
||||
Args = arguments
|
||||
};
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
public static List<long> GetAllConnections()
|
||||
@ -211,27 +271,37 @@ namespace CoopServer
|
||||
|
||||
public static Client GetClientByUsername(string username)
|
||||
{
|
||||
return Server.Clients.FirstOrDefault(x => x.Player.Username == username);
|
||||
Client client = Server.Clients.FirstOrDefault(x => x.Player.Username == username);
|
||||
return client.Equals(default(Client)) ? null : client;
|
||||
}
|
||||
|
||||
public static void SendChatMessageToAll(string message, string username = "Server")
|
||||
{
|
||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||
try
|
||||
{
|
||||
return;
|
||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
new ChatMessagePacket()
|
||||
{
|
||||
Username = username,
|
||||
Message = message
|
||||
}.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
ChatMessagePacket packet = new()
|
||||
catch (Exception e)
|
||||
{
|
||||
Username = username,
|
||||
Message = message
|
||||
};
|
||||
|
||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||
packet.PacketToNetOutGoingMessage(outgoingMessage);
|
||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||
}
|
||||
}
|
||||
|
||||
public static void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
|
||||
{
|
||||
Server.RegisterCommand(name, usage, argsLength, callback);
|
||||
}
|
||||
public static void RegisterCommand(string name, Action<CommandContext> callback)
|
||||
{
|
||||
Server.RegisterCommand(name, callback);
|
||||
@ -244,20 +314,19 @@ namespace CoopServer
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class Command
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class CommandAttribute : Attribute
|
||||
public class Command : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets name of the command
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public CommandAttribute(string name)
|
||||
public string Usage { get; set; }
|
||||
|
||||
public short ArgsLength { get; set; }
|
||||
|
||||
public Command(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
public bool NpcsAllowed { get; set; } = true;
|
||||
public bool ModsAllowed { get; set; } = false;
|
||||
public bool UPnP { get; set; } = true;
|
||||
public bool AnnounceSelf { get; set; } = true;
|
||||
public string MasterServer { get; set; } = "http://gtacoopr.000webhostapp.com/servers.php";
|
||||
public bool DebugMode { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using Lidgren.Network;
|
||||
|
||||
namespace CoopServer
|
||||
{
|
||||
class Util
|
||||
internal class Util
|
||||
{
|
||||
public static List<NativeArgument> ParseNativeArguments(params object[] args)
|
||||
{
|
||||
@ -50,13 +50,13 @@ namespace CoopServer
|
||||
|
||||
public static NetConnection GetConnectionByUsername(string username)
|
||||
{
|
||||
long clientID;
|
||||
if ((clientID = Server.Clients.FirstOrDefault(x => x.Player.Username == username).ID) == default)
|
||||
Client client = Server.Clients.FirstOrDefault(x => x.Player.Username == username);
|
||||
if (client.Equals(default(Client)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Server.MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == clientID);
|
||||
return Server.MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == client.ID);
|
||||
}
|
||||
|
||||
// Return a list of all connections but not the local connection
|
||||
@ -72,12 +72,20 @@ namespace CoopServer
|
||||
// Return a list of players within range of ...
|
||||
public static List<NetConnection> GetAllInRange(LVector3 position, float range)
|
||||
{
|
||||
return new(Server.MainNetServer.Connections.FindAll(e => Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier).Player.IsInRangeOf(position, range)));
|
||||
return new(Server.MainNetServer.Connections.FindAll(e =>
|
||||
{
|
||||
Client client = Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier);
|
||||
return !client.Equals(default(Client)) && client.Player.IsInRangeOf(position, range);
|
||||
}));
|
||||
}
|
||||
// Return a list of players within range of ... but not the local one
|
||||
public static List<NetConnection> GetAllInRange(LVector3 position, float range, NetConnection local)
|
||||
{
|
||||
return new(Server.MainNetServer.Connections.Where(e => e != local && Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier).Player.IsInRangeOf(position, range)));
|
||||
return new(Server.MainNetServer.Connections.Where(e =>
|
||||
{
|
||||
Client client = Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier);
|
||||
return e != local && !client.Equals(default(Client)) && client.Player.IsInRangeOf(position, range);
|
||||
}));
|
||||
}
|
||||
|
||||
public static T Read<T>(string file) where T : new()
|
||||
|
Loading…
x
Reference in New Issue
Block a user