Merge pull request #36 from EntenKoeniq/main

0.9.0
This commit is contained in:
Nick-I. A 2021-12-08 04:14:58 +01:00 committed by GitHub
commit 857b1b7622
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 41350 additions and 792 deletions

View File

@ -4,17 +4,38 @@ using System.Linq;
namespace CoopClient namespace CoopClient
{ {
public static class Interface /// <summary>
/// ?
/// </summary>
public static class COOPAPI
{ {
#region DELEGATES #region DELEGATES
/// <summary>
/// ?
/// </summary>
public delegate void ConnectEvent(bool connected, long fromId, string reason = null); public delegate void ConnectEvent(bool connected, long fromId, string reason = null);
/// <summary>
/// ?
/// </summary>
public delegate void ChatMessage(string from, string message, CancelEventArgs args); public delegate void ChatMessage(string from, string message, CancelEventArgs args);
/// <summary>
/// ?
/// </summary>
public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes); public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes);
#endregion #endregion
#region EVENTS #region EVENTS
/// <summary>
/// ?
/// </summary>
public static event ConnectEvent OnConnection; public static event ConnectEvent OnConnection;
/// <summary>
/// ?
/// </summary>
public static event ChatMessage OnChatMessage; public static event ChatMessage OnChatMessage;
/// <summary>
/// ?
/// </summary>
public static event ModEvent OnModPacketReceived; public static event ModEvent OnModPacketReceived;
internal static void Connected() internal static void Connected()
@ -50,31 +71,55 @@ namespace CoopClient
} }
#endregion #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); Main.MainChat.AddMessage(from, message);
} }
/// <summary>
/// ?
/// </summary>
public static void Connect(string serverAddress) public static void Connect(string serverAddress)
{ {
Main.MainNetworking.DisConnectFromServer(serverAddress); Main.MainNetworking.DisConnectFromServer(serverAddress);
} }
/// <summary>
/// ?
/// </summary>
public static void Disconnect() public static void Disconnect()
{ {
Main.MainNetworking.DisConnectFromServer(null); Main.MainNetworking.DisConnectFromServer(null);
} }
/// <summary>
/// ?
/// </summary>
public static bool IsOnServer() public static bool IsOnServer()
{ {
return Main.MainNetworking.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() public static long GetLocalID()
{ {
return Main.LocalClientID; 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() public static Dictionary<long, int?> GetAllPlayers()
{ {
Dictionary<long, int?> result = new Dictionary<long, int?>(); Dictionary<long, int?> result = new Dictionary<long, int?>();
@ -88,11 +133,22 @@ namespace CoopClient
return result; 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() public static bool IsMenuVisible()
{ {
#if NON_INTERACTIVE #if NON_INTERACTIVE
@ -102,33 +158,76 @@ namespace CoopClient
#endif #endif
} }
/// <summary>
/// ?
/// </summary>
public static bool IsChatFocused() public static bool IsChatFocused()
{ {
return Main.MainChat.Focused; return Main.MainChat.Focused;
} }
/// <summary>
/// ?
/// </summary>
public static bool IsPlayerListVisible() public static bool IsPlayerListVisible()
{ {
return Util.GetTickCount64() - PlayerList.Pressed < 5000; return Util.GetTickCount64() - PlayerList.Pressed < 5000;
} }
/// <summary>
/// ?
/// </summary>
public static string GetCurrentVersion() public static string GetCurrentVersion()
{ {
return Main.CurrentVersion; 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) public static void SendDataToAll(string mod, byte customID, byte[] bytes)
{ {
Main.MainNetworking.SendModData(0, mod, customID, bytes); Main.MainNetworking.SendModData(0, mod, customID, bytes);
} }
// Send bytes to target /// <summary>
public static void SendDataToPlayer(long target, string mod, byte customID, byte[] bytes) /// 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) public static void Configure(string playerName, bool shareNpcsWithPlayers, int streamedNpcs, bool debug = false)
{ {
Main.MainSettings.Username = playerName; Main.MainSettings.Username = playerName;

View File

@ -8,14 +8,17 @@ using GTA.Native;
namespace CoopClient namespace CoopClient
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class Chat public class Chat
{ {
private readonly Scaleform MainScaleForm; private readonly Scaleform MainScaleForm;
public string CurrentInput { get; set; } internal string CurrentInput { get; set; }
private bool CurrentFocused { get; set; } private bool CurrentFocused { get; set; }
public bool Focused internal bool Focused
{ {
get { return CurrentFocused; } get { return CurrentFocused; }
set set
@ -55,23 +58,26 @@ namespace CoopClient
} }
} }
/// <summary>
/// Don't use it!
/// </summary>
public Chat() public Chat()
{ {
MainScaleForm = new Scaleform("multiplayer_chat"); MainScaleForm = new Scaleform("multiplayer_chat");
} }
public void Init() internal void Init()
{ {
MainScaleForm.CallFunction("SET_FOCUS", 2, 2, "ALL"); MainScaleForm.CallFunction("SET_FOCUS", 2, 2, "ALL");
MainScaleForm.CallFunction("SET_FOCUS", 1, 2, "ALL"); MainScaleForm.CallFunction("SET_FOCUS", 1, 2, "ALL");
} }
public void Clear() internal void Clear()
{ {
MainScaleForm.CallFunction("RESET"); MainScaleForm.CallFunction("RESET");
} }
public void Tick() internal void Tick()
{ {
if ((Util.GetTickCount64() - LastMessageTime) > 15000 && !Focused && !Hidden) if ((Util.GetTickCount64() - LastMessageTime) > 15000 && !Focused && !Hidden)
{ {
@ -91,14 +97,14 @@ namespace CoopClient
Function.Call(Hash.DISABLE_ALL_CONTROL_ACTIONS, 0); 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); MainScaleForm.CallFunction("ADD_MESSAGE", sender + ":", msg);
LastMessageTime = Util.GetTickCount64(); LastMessageTime = Util.GetTickCount64();
Hidden = false; Hidden = false;
} }
public void OnKeyDown(Keys key) internal void OnKeyDown(Keys key)
{ {
if (key == Keys.Escape) if (key == Keys.Escape)
{ {
@ -151,12 +157,12 @@ namespace CoopClient
} }
[DllImport("user32.dll")] [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)] [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)]
StringBuilder receivingBuffer, StringBuilder receivingBuffer,
int bufferSize, uint flags, IntPtr kblayout); 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); StringBuilder buf = new StringBuilder(256);
byte[] keyboardState = new byte[256]; byte[] keyboardState = new byte[256];

View File

@ -24,6 +24,8 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<DocumentationFile>bin\Debug\CoopClient.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@ -33,18 +35,26 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<DocumentationFile>bin\Release\CoopClient.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="LemonUI.SHVDN3"> <Reference Include="LemonUI.SHVDN3, Version=1.5.1.0, Culture=neutral, processorArchitecture=AMD64">
<HintPath>..\Libs\Release\LemonUI.SHVDN3.dll</HintPath> <SpecificVersion>False</SpecificVersion>
<HintPath>..\Libs\Release\scripts\LemonUI.SHVDN3.dll</HintPath>
</Reference> </Reference>
<Reference Include="Lidgren.Network"> <Reference Include="Lidgren.Network, Version=2012.1.7.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\Libs\Release\Lidgren.Network.dll</HintPath> <SpecificVersion>False</SpecificVersion>
<HintPath>..\Libs\Release\scripts\Lidgren.Network.dll</HintPath>
</Reference> </Reference>
<Reference Include="protobuf-net, Version=2.4.0.0, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\protobuf-net.2.4.6\lib\net40\protobuf-net.dll</HintPath> <SpecificVersion>False</SpecificVersion>
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
</Reference> </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> <SpecificVersion>False</SpecificVersion>
<HintPath>..\Libs\Release\ScriptHookVDotNet3.dll</HintPath> <HintPath>..\Libs\Release\ScriptHookVDotNet3.dll</HintPath>
</Reference> </Reference>
@ -65,13 +75,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Chat.cs" /> <Compile Include="Chat.cs" />
<Compile Include="Interface.cs" /> <Compile Include="COOPAPI.cs" />
<Compile Include="Entities\EntitiesNpc.cs" /> <Compile Include="Entities\EntitiesNpc.cs" />
<Compile Include="Entities\EntitiesPed.cs" /> <Compile Include="Entities\EntitiesPed.cs" />
<Compile Include="Entities\EntitiesPlayer.cs" /> <Compile Include="Entities\EntitiesPlayer.cs" />
<Compile Include="Entities\EntitiesThread.cs" /> <Compile Include="Entities\EntitiesThread.cs" />
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="Menus\MenusMain.cs" /> <Compile Include="Menus\MenusMain.cs" />
<Compile Include="Menus\Sub\Servers.cs" />
<Compile Include="Menus\Sub\Settings.cs" /> <Compile Include="Menus\Sub\Settings.cs" />
<Compile Include="Networking.cs" /> <Compile Include="Networking.cs" />
<Compile Include="Packets.cs" /> <Compile Include="Packets.cs" />
@ -81,8 +92,5 @@
<Compile Include="Settings.cs" /> <Compile Include="Settings.cs" />
<Compile Include="Util.cs" /> <Compile Include="Util.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -1,5 +1,8 @@
namespace CoopClient.Entities namespace CoopClient.Entities
{ {
/// <summary>
/// ?
/// </summary>
public class EntitiesNpc : EntitiesPed public class EntitiesNpc : EntitiesPed
{ {
//public int LastUpdateReceived { get; set; } //public int LastUpdateReceived { get; set; }

View File

@ -9,58 +9,157 @@ using GTA.Math;
using LemonUI.Elements; using LemonUI.Elements;
namespace CoopClient namespace CoopClient.Entities
{ {
/// <summary>
/// ?
/// </summary>
public class EntitiesPed public class EntitiesPed
{ {
private bool AllDataAvailable = false; private bool AllDataAvailable = false;
/// <summary>
/// ?
/// </summary>
public bool LastSyncWasFull { get; set; } = false; public bool LastSyncWasFull { get; set; } = false;
/// <summary>
/// ?
/// </summary>
public ulong LastUpdateReceived { get; set; } public ulong LastUpdateReceived { get; set; }
/// <summary>
/// ?
/// </summary>
public float Latency { get; set; } public float Latency { get; set; }
/// <summary>
/// ?
/// </summary>
public Ped Character { get; set; } public Ped Character { get; set; }
/// <summary>
/// ?
/// </summary>
public int Health { get; set; } public int Health { get; set; }
private int LastModelHash = 0; private int LastModelHash = 0;
/// <summary>
/// ?
/// </summary>
public int ModelHash { get; set; } public int ModelHash { get; set; }
private Dictionary<int, int> LastProps = new Dictionary<int, int>(); private Dictionary<int, int> LastProps = new Dictionary<int, int>();
/// <summary>
/// ?
/// </summary>
public Dictionary<int, int> Props { get; set; } public Dictionary<int, int> Props { get; set; }
/// <summary>
/// ?
/// </summary>
public Vector3 Position { get; set; } public Vector3 Position { get; set; }
#region -- ON FOOT -- #region -- ON FOOT --
/// <summary>
/// ?
/// </summary>
public Vector3 Rotation { get; set; } public Vector3 Rotation { get; set; }
/// <summary>
/// ?
/// </summary>
public Vector3 Velocity { get; set; } public Vector3 Velocity { get; set; }
/// <summary>
/// ?
/// </summary>
public byte Speed { get; set; } public byte Speed { get; set; }
private bool LastIsJumping = false; private bool LastIsJumping = false;
/// <summary>
/// ?
/// </summary>
public bool IsJumping { get; set; } public bool IsJumping { get; set; }
/// <summary>
/// ?
/// </summary>
public bool IsRagdoll { get; set; } public bool IsRagdoll { get; set; }
/// <summary>
/// ?
/// </summary>
public bool IsOnFire { get; set; } public bool IsOnFire { get; set; }
/// <summary>
/// ?
/// </summary>
public Vector3 AimCoords { get; set; } public Vector3 AimCoords { get; set; }
/// <summary>
/// ?
/// </summary>
public bool IsAiming { get; set; } public bool IsAiming { get; set; }
/// <summary>
/// ?
/// </summary>
public bool IsShooting { get; set; } public bool IsShooting { get; set; }
/// <summary>
/// ?
/// </summary>
public bool IsReloading { get; set; } public bool IsReloading { get; set; }
/// <summary>
/// ?
/// </summary>
public int CurrentWeaponHash { get; set; } public int CurrentWeaponHash { get; set; }
#endregion #endregion
/// <summary>
/// ?
/// </summary>
public Blip PedBlip; public Blip PedBlip;
#region -- IN VEHICLE -- #region -- IN VEHICLE --
private ulong VehicleStopTime { get; set; } private ulong VehicleStopTime { get; set; }
/// <summary>
/// ?
/// </summary>
public bool IsInVehicle { get; set; } public bool IsInVehicle { get; set; }
/// <summary>
/// ?
/// </summary>
public int VehicleModelHash { get; set; } public int VehicleModelHash { get; set; }
private int[] LastVehicleColors = new int[] { 0, 0 }; private int[] LastVehicleColors = new int[] { 0, 0 };
/// <summary>
/// ?
/// </summary>
public int[] VehicleColors { get; set; } public int[] VehicleColors { get; set; }
private Dictionary<int, int> LastVehicleMods = new Dictionary<int, int>(); private Dictionary<int, int> LastVehicleMods = new Dictionary<int, int>();
/// <summary>
/// ?
/// </summary>
public Dictionary<int, int> VehicleMods { get; set; } public Dictionary<int, int> VehicleMods { get; set; }
/// <summary>
/// ?
/// </summary>
public bool VehicleDead { get; set; } public bool VehicleDead { get; set; }
/// <summary>
/// ?
/// </summary>
public float VehicleEngineHealth { get; set; } public float VehicleEngineHealth { get; set; }
/// <summary>
/// ?
/// </summary>
public int VehicleSeatIndex { get; set; } public int VehicleSeatIndex { get; set; }
/// <summary>
/// ?
/// </summary>
public Vehicle MainVehicle { get; set; } public Vehicle MainVehicle { get; set; }
/// <summary>
/// ?
/// </summary>
public Vector3 VehiclePosition { get; set; } public Vector3 VehiclePosition { get; set; }
/// <summary>
/// ?
/// </summary>
public Quaternion VehicleRotation { get; set; } public Quaternion VehicleRotation { get; set; }
/// <summary>
/// ?
/// </summary>
public Vector3 VehicleVelocity { get; set; } public Vector3 VehicleVelocity { get; set; }
private float LastVehicleSpeed { get; set; } private float LastVehicleSpeed { get; set; }
private float CurrentVehicleSpeed { get; set; } private float CurrentVehicleSpeed { get; set; }
/// <summary>
/// ?
/// </summary>
public float VehicleSpeed public float VehicleSpeed
{ {
set set
@ -69,19 +168,44 @@ namespace CoopClient
CurrentVehicleSpeed = value; CurrentVehicleSpeed = value;
} }
} }
/// <summary>
/// ?
/// </summary>
public float VehicleSteeringAngle { get; set; } public float VehicleSteeringAngle { get; set; }
private int LastVehicleAim;
/// <summary>
/// ?
/// </summary>
public bool VehIsEngineRunning { get; set; } public bool VehIsEngineRunning { get; set; }
/// <summary>
/// ?
/// </summary>
public float VehRPM { get; set; } public float VehRPM { get; set; }
/// <summary>
/// ?
/// </summary>
public bool VehAreLightsOn { get; set; } public bool VehAreLightsOn { get; set; }
/// <summary>
/// ?
/// </summary>
public bool VehAreHighBeamsOn { get; set; } public bool VehAreHighBeamsOn { get; set; }
/// <summary>
/// ?
/// </summary>
public bool VehIsSireneActive { get; set; } public bool VehIsSireneActive { get; set; }
private VehicleDoors[] LastVehDoors; private VehicleDoors[] LastVehDoors;
/// <summary>
/// ?
/// </summary>
public VehicleDoors[] VehDoors { get; set; } public VehicleDoors[] VehDoors { get; set; }
private int LastVehTires; private int LastVehTires;
/// <summary>
/// ?
/// </summary>
public int VehTires { get; set; } public int VehTires { get; set; }
#endregion #endregion
public void DisplayLocally(string username) internal void DisplayLocally(string username)
{ {
/* /*
* username: string * username: string
@ -297,6 +421,11 @@ namespace CoopClient
} }
#region -- VEHICLE SYNC -- #region -- VEHICLE SYNC --
if (MainVehicle.GetResponsiblePedHandle() != Character.Handle)
{
return;
}
if (VehicleColors != null && VehicleColors != LastVehicleColors) if (VehicleColors != null && VehicleColors != LastVehicleColors)
{ {
Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, VehicleColors[0], VehicleColors[1]); Function.Call(Hash.SET_VEHICLE_COLOURS, MainVehicle, VehicleColors[0], VehicleColors[1]);
@ -322,11 +451,6 @@ namespace CoopClient
} }
else else
{ {
if (MainVehicle.GetResponsiblePedHandle() != Character.Handle)
{
return;
}
if (VehicleMods != null && VehicleMods != LastVehicleMods) if (VehicleMods != null && VehicleMods != LastVehicleMods)
{ {
Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0); Function.Call(Hash.SET_VEHICLE_MOD_KIT, MainVehicle, 0);
@ -437,12 +561,22 @@ namespace CoopClient
LastVehTires = VehTires; 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) 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 // Good enough for now, but we need to create a better sync

View File

@ -1,8 +1,17 @@
namespace CoopClient.Entities namespace CoopClient.Entities
{ {
/// <summary>
/// ?
/// </summary>
public class EntitiesPlayer : EntitiesPed public class EntitiesPlayer : EntitiesPed
{ {
/// <summary>
/// ?
/// </summary>
public string SocialClubName { get; set; } public string SocialClubName { get; set; }
/// <summary>
/// ?
/// </summary>
public string Username { get; set; } = "Player"; public string Username { get; set; } = "Player";
} }
} }

View File

@ -6,12 +6,18 @@ using GTA;
namespace CoopClient.Entities namespace CoopClient.Entities
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class EntitiesThread : Script public class EntitiesThread : Script
{ {
/// <summary>
/// Don't use it!
/// </summary>
public EntitiesThread() public EntitiesThread()
{ {
Tick += OnTick; Tick += OnTick;
Interval = 1000 / 60; Interval = Util.GetGameMs<int>();
} }
private void OnTick(object sender, EventArgs e) private void OnTick(object sender, EventArgs e)

View File

@ -12,31 +12,49 @@ using GTA.Native;
namespace CoopClient namespace CoopClient
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class Main : Script public class Main : Script
{ {
public static RelationshipGroup RelationshipGroup; internal static RelationshipGroup RelationshipGroup;
private bool GameLoaded = false; 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; internal static bool ShareNpcsWithPlayers = false;
public static bool DisableTraffic = false; internal static bool DisableTraffic = false;
public static bool NpcsAllowed = false; internal static bool NpcsAllowed = false;
private static bool IsGoingToCar = false; private static bool IsGoingToCar = false;
/// <summary>
/// Don't use it!
/// </summary>
public static Settings MainSettings = Util.ReadSettings(); public static Settings MainSettings = Util.ReadSettings();
/// <summary>
/// Don't use it!
/// </summary>
public static Networking MainNetworking = new Networking(); public static Networking MainNetworking = new Networking();
#if !NON_INTERACTIVE #if !NON_INTERACTIVE
/// <summary>
/// Don't use it!
/// </summary>
public static MenusMain MainMenu = new MenusMain(); public static MenusMain MainMenu = new MenusMain();
#endif #endif
/// <summary>
/// Don't use it!
/// </summary>
public static Chat MainChat = new Chat(); public static Chat MainChat = new Chat();
public static long LocalClientID = 0; internal static long LocalClientID = 0;
public static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>(); internal static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
public static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>(); internal static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
/// <summary>
/// Don't use it!
/// </summary>
public Main() public Main()
{ {
Function.Call((Hash)0x0888C3502DBBEEF5); // _LOAD_MP_DLC_MAPS Function.Call((Hash)0x0888C3502DBBEEF5); // _LOAD_MP_DLC_MAPS
@ -62,6 +80,8 @@ namespace CoopClient
{ {
RelationshipGroup = World.AddRelationshipGroup("SYNCPED"); RelationshipGroup = World.AddRelationshipGroup("SYNCPED");
Game.Player.Character.RelationshipGroup = RelationshipGroup; 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 #if !NON_INTERACTIVE
@ -104,7 +124,7 @@ namespace CoopClient
} }
#endif #endif
if ((Util.GetTickCount64() - LastDataSend) < (1000 / 60)) if ((Util.GetTickCount64() - LastDataSend) < Util.GetGameMs<ulong>())
{ {
return; return;
} }
@ -182,7 +202,7 @@ namespace CoopClient
} }
#endif #endif
public static void CleanUp() internal static void CleanUp()
{ {
MainChat.Clear(); MainChat.Clear();
@ -203,24 +223,13 @@ namespace CoopClient
Npc.Value.Character?.Delete(); Npc.Value.Character?.Delete();
} }
Npcs.Clear(); 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 #if DEBUG
private ulong ArtificialLagCounter; private ulong ArtificialLagCounter;
public static EntitiesPlayer DebugSyncPed; internal static EntitiesPlayer DebugSyncPed;
public static ulong LastFullDebugSync = 0; internal static ulong LastFullDebugSync = 0;
public static bool UseDebug = false; internal static bool UseDebug = false;
private void Debug() private void Debug()
{ {
@ -231,12 +240,12 @@ namespace CoopClient
DebugSyncPed = Players[0]; DebugSyncPed = Players[0];
} }
if ((Util.GetTickCount64() - ArtificialLagCounter) < 157) if ((Util.GetTickCount64() - ArtificialLagCounter) < 56)
{ {
return; return;
} }
bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 1500; bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 500;
if (fullSync) if (fullSync)
{ {
@ -273,6 +282,7 @@ namespace CoopClient
DebugSyncPed.VehicleVelocity = veh.Velocity; DebugSyncPed.VehicleVelocity = veh.Velocity;
DebugSyncPed.VehicleSpeed = veh.Speed; DebugSyncPed.VehicleSpeed = veh.Speed;
DebugSyncPed.VehicleSteeringAngle = veh.SteeringAngle; DebugSyncPed.VehicleSteeringAngle = veh.SteeringAngle;
DebugSyncPed.AimCoords = veh.IsTurretSeat((int)player.SeatIndex) ? Util.GetVehicleAimCoords() : new GTA.Math.Vector3();
DebugSyncPed.VehicleColors = new int[] { primaryColor, secondaryColor }; DebugSyncPed.VehicleColors = new int[] { primaryColor, secondaryColor };
DebugSyncPed.VehicleMods = veh.Mods.GetVehicleMods(); DebugSyncPed.VehicleMods = veh.Mods.GetVehicleMods();
DebugSyncPed.VehDoors = veh.Doors.GetVehicleDoors(); DebugSyncPed.VehDoors = veh.Doors.GetVehicleDoors();

View File

@ -5,17 +5,27 @@ using LemonUI.Menus;
namespace CoopClient.Menus namespace CoopClient.Menus
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class MenusMain 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, UseMouse = false,
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
}; };
#region SUB #region SUB
/// <summary>
/// Don't use it!
/// </summary>
public Sub.Settings SubSettings = new Sub.Settings(); public Sub.Settings SubSettings = new Sub.Settings();
/// <summary>
/// Don't use it!
/// </summary>
public Sub.Servers ServerList = new Sub.Servers();
#endregion #endregion
#region ITEMS #region ITEMS
@ -28,12 +38,17 @@ namespace CoopClient.Menus
Main.CurrentVersion.Replace("_", ".")) { LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") }; Main.CurrentVersion.Replace("_", ".")) { LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") };
#endregion #endregion
/// <summary>
/// Don't use it!
/// </summary>
public MenusMain() public MenusMain()
{ {
UsernameItem.Activated += UsernameActivated; UsernameItem.Activated += UsernameActivated;
ServerIpItem.Activated += ServerIpActivated; ServerIpItem.Activated += ServerIpActivated;
ServerConnectItem.Activated += (sender, item) => { Main.MainNetworking.DisConnectFromServer(Main.MainSettings.LastServerAddress); }; ServerConnectItem.Activated += (sender, item) => { Main.MainNetworking.DisConnectFromServer(Main.MainSettings.LastServerAddress); };
MainMenu.AddSubMenu(ServerList.MainMenu);
MainMenu.Add(UsernameItem); MainMenu.Add(UsernameItem);
MainMenu.Add(ServerIpItem); MainMenu.Add(ServerIpItem);
MainMenu.Add(ServerConnectItem); MainMenu.Add(ServerConnectItem);
@ -42,11 +57,12 @@ namespace CoopClient.Menus
MainMenu.Add(AboutItem); MainMenu.Add(AboutItem);
MenuPool.Add(ServerList.MainMenu);
MenuPool.Add(MainMenu); MenuPool.Add(MainMenu);
MenuPool.Add(SubSettings.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); string newUsername = Game.GetUserInput(WindowTitle.EnterMessage20, UsernameItem.AltTitle, 20);
if (!string.IsNullOrWhiteSpace(newUsername)) 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); string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);
if (!string.IsNullOrWhiteSpace(newServerIp) && newServerIp.Contains(":")) 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[0].Enabled = false;
MainMenu.Items[1].Enabled = false; MainMenu.Items[1].Enabled = false;
MainMenu.Items[2].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[3].Enabled = true;
MainMenu.Items[2].Title = "Disconnect"; MainMenu.Items[3].Title = "Disconnect";
SubSettings.MainMenu.Items[1].Enabled = !Main.DisableTraffic && Main.NpcsAllowed; SubSettings.MainMenu.Items[1].Enabled = !Main.DisableTraffic && Main.NpcsAllowed;
MainMenu.Visible = false; MainMenu.Visible = false;
ServerList.MainMenu.Visible = false;
MenuPool.RefreshAll(); MenuPool.RefreshAll();
} }
public void DisconnectedMenuSetting() internal void DisconnectedMenuSetting()
{ {
MainMenu.Items[0].Enabled = true; MainMenu.Items[0].Enabled = true;
MainMenu.Items[1].Enabled = true; MainMenu.Items[1].Enabled = true;
MainMenu.Items[2].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; SubSettings.MainMenu.Items[1].Enabled = false;
MenuPool.RefreshAll(); MenuPool.RefreshAll();

111
Client/Menus/Sub/Servers.cs Normal file
View 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}");
}
};
}
}
}
}

View File

@ -2,9 +2,12 @@
namespace CoopClient.Menus.Sub namespace CoopClient.Menus.Sub
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class Settings 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, UseMouse = false,
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left 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); private readonly NativeCheckboxItem ShowNetworkInfo = new NativeCheckboxItem("Show Network Info", Main.MainNetworking.ShowNetworkInfo);
#endif #endif
/// <summary>
/// Don't use it!
/// </summary>
public Settings() public Settings()
{ {
DisableTraffic.CheckboxChanged += DisableTrafficCheckboxChanged; DisableTraffic.CheckboxChanged += DisableTrafficCheckboxChanged;
@ -40,7 +46,7 @@ namespace CoopClient.Menus.Sub
#endif #endif
} }
public void DisableTrafficCheckboxChanged(object a, System.EventArgs b) internal void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{ {
Main.DisableTraffic = DisableTraffic.Checked; 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; Main.MainSettings.StreamedNpc = StreamedNpcsItem.Value;
Util.SaveSettings(); Util.SaveSettings();
StreamedNpcsItem.Title = string.Format("Streamed Npcs ({0})", Main.MainSettings.StreamedNpc); 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 #if !NON_INTERACTIVE
Main.MainMenu.MainMenu.Alignment = FlipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left; Main.MainMenu.MainMenu.Alignment = FlipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left;
@ -78,7 +84,7 @@ namespace CoopClient.Menus.Sub
} }
#if DEBUG #if DEBUG
public void UseDebugCheckboxChanged(object a, System.EventArgs b) internal void UseDebugCheckboxChanged(object a, System.EventArgs b)
{ {
Main.UseDebug = UseDebugItem.Checked; 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; Main.MainNetworking.ShowNetworkInfo = ShowNetworkInfo.Checked;

View File

@ -10,17 +10,20 @@ using GTA.Native;
namespace CoopClient namespace CoopClient
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class Networking public class Networking
{ {
public NetClient Client; internal NetClient Client;
public float Latency; internal float Latency;
public bool ShowNetworkInfo = false; internal bool ShowNetworkInfo = false;
public int BytesReceived = 0; internal int BytesReceived = 0;
public int BytesSend = 0; internal int BytesSend = 0;
public void DisConnectFromServer(string address) internal void DisConnectFromServer(string address)
{ {
if (IsOnServer()) if (IsOnServer())
{ {
@ -44,9 +47,16 @@ namespace CoopClient
Client.Start(); Client.Start();
string[] ip = address.Split(':'); string[] ip = new string[2];
if(ip.Length != 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"); throw new Exception("Malformed URL");
} }
@ -66,12 +76,12 @@ namespace CoopClient
} }
} }
public bool IsOnServer() internal bool IsOnServer()
{ {
return Client?.ConnectionStatus == NetConnectionStatus.Connected; return Client?.ConnectionStatus == NetConnectionStatus.Connected;
} }
public void ReceiveMessages() internal void ReceiveMessages()
{ {
if (Client == null) if (Client == null)
{ {
@ -114,12 +124,6 @@ namespace CoopClient
Main.LocalClientID = handshakePacket.ID; Main.LocalClientID = handshakePacket.ID;
Main.NpcsAllowed = handshakePacket.NpcsAllowed; 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(); Main.MainChat.Init();
// Send player connect packet // Send player connect packet
@ -137,7 +141,7 @@ namespace CoopClient
Main.MainMenu.ConnectedMenuSetting(); Main.MainMenu.ConnectedMenuSetting();
#endif #endif
Interface.Connected(); COOPAPI.Connected();
GTA.UI.Notification.Show("~g~Connected!"); GTA.UI.Notification.Show("~g~Connected!");
} }
break; break;
@ -153,11 +157,12 @@ namespace CoopClient
} }
Main.CleanUp(); Main.CleanUp();
#if !NON_INTERACTIVE #if !NON_INTERACTIVE
Main.MainMenu.DisconnectedMenuSetting(); Main.MainMenu.DisconnectedMenuSetting();
#endif #endif
Interface.Disconnected(reason); COOPAPI.Disconnected(reason);
GTA.UI.Notification.Show("~r~Disconnected: " + reason); GTA.UI.Notification.Show("~r~Disconnected: " + reason);
break; break;
} }
@ -219,7 +224,7 @@ namespace CoopClient
packet.NetIncomingMessageToPacket(message); packet.NetIncomingMessageToPacket(message);
ChatMessagePacket chatMessagePacket = (ChatMessagePacket)packet; 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); Main.MainChat.AddMessage(chatMessagePacket.Username, chatMessagePacket.Message);
} }
@ -233,7 +238,7 @@ namespace CoopClient
packet = new ModPacket(); packet = new ModPacket();
packet.NetIncomingMessageToPacket(message); packet.NetIncomingMessageToPacket(message);
ModPacket modPacket = (ModPacket)packet; 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;
} }
break; break;
@ -244,6 +249,9 @@ namespace CoopClient
case NetIncomingMessageType.ErrorMessage: case NetIncomingMessageType.ErrorMessage:
case NetIncomingMessageType.WarningMessage: case NetIncomingMessageType.WarningMessage:
case NetIncomingMessageType.VerboseDebugMessage: case NetIncomingMessageType.VerboseDebugMessage:
#if DEBUG
// TODO?
#endif
break; break;
default: default:
break; break;
@ -265,7 +273,7 @@ namespace CoopClient
}; };
Main.Players.Add(packet.ID, player); Main.Players.Add(packet.ID, player);
Interface.Connected(packet.ID); COOPAPI.Connected(packet.ID);
} }
private void PlayerDisconnect(PlayerDisconnectPacket packet) private void PlayerDisconnect(PlayerDisconnectPacket packet)
@ -281,7 +289,7 @@ namespace CoopClient
player.PedBlip?.Delete(); player.PedBlip?.Delete();
Interface.Disconnected(packet.ID); COOPAPI.Disconnected(packet.ID);
Main.Players.Remove(packet.ID); Main.Players.Remove(packet.ID);
} }
} }
@ -334,6 +342,7 @@ namespace CoopClient
player.VehicleVelocity = packet.VehVelocity.ToVector(); player.VehicleVelocity = packet.VehVelocity.ToVector();
player.VehicleSpeed = packet.VehSpeed; player.VehicleSpeed = packet.VehSpeed;
player.VehicleSteeringAngle = packet.VehSteeringAngle; player.VehicleSteeringAngle = packet.VehSteeringAngle;
player.AimCoords = packet.VehAimCoords.ToVector();
player.VehicleColors = packet.VehColors; player.VehicleColors = packet.VehColors;
player.VehicleMods = packet.VehMods; player.VehicleMods = packet.VehMods;
player.VehDoors = packet.VehDoors; player.VehDoors = packet.VehDoors;
@ -590,7 +599,7 @@ namespace CoopClient
#region -- SEND -- #region -- SEND --
private ulong LastPlayerFullSync = 0; private ulong LastPlayerFullSync = 0;
public void SendPlayerData() internal void SendPlayerData()
{ {
Ped player = Game.Player.Character; Ped player = Game.Player.Character;
@ -660,6 +669,7 @@ namespace CoopClient
VehVelocity = vehVelocity, VehVelocity = vehVelocity,
VehSpeed = vehSpeed, VehSpeed = vehSpeed,
VehSteeringAngle = vehSteeringAngle, VehSteeringAngle = vehSteeringAngle,
VehAimCoords = veh.IsTurretSeat((int)player.SeatIndex) ? Util.GetVehicleAimCoords().ToLVector() : new LVector3(),
VehColors = new int[] { primaryColor, secondaryColor }, VehColors = new int[] { primaryColor, secondaryColor },
VehMods = vehMods, VehMods = vehMods,
VehDoors = vehDoors, VehDoors = vehDoors,
@ -762,7 +772,7 @@ namespace CoopClient
#endif #endif
} }
public void SendNpcData(Ped npc) internal void SendNpcData(Ped npc)
{ {
NetOutgoingMessage outgoingMessage = Client.CreateMessage(); NetOutgoingMessage outgoingMessage = Client.CreateMessage();
@ -859,7 +869,7 @@ namespace CoopClient
#endif #endif
} }
public void SendChatMessage(string message) internal void SendChatMessage(string message)
{ {
NetOutgoingMessage outgoingMessage = Client.CreateMessage(); NetOutgoingMessage outgoingMessage = Client.CreateMessage();
new ChatMessagePacket() new ChatMessagePacket()
@ -878,7 +888,7 @@ namespace CoopClient
#endif #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(); NetOutgoingMessage outgoingMessage = Client.CreateMessage();
new ModPacket() new ModPacket()

View File

@ -1,10 +1,10 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using Lidgren.Network; using Lidgren.Network;
using ProtoBuf; using ProtoBuf;
using Newtonsoft.Json;
using GTA.Math; using GTA.Math;
@ -156,10 +156,16 @@ namespace CoopClient
IsDead = 1 << 5 IsDead = 1 << 5
} }
/// <summary>
/// ?
/// </summary>
[ProtoContract] [ProtoContract]
public struct VehicleDoors public struct VehicleDoors
{ {
#region CLIENT-ONLY #region CLIENT-ONLY
/// <summary>
/// ?
/// </summary>
public VehicleDoors(float angleRatio = 0f, bool broken = false, bool open = false, bool fullyOpen = false) public VehicleDoors(float angleRatio = 0f, bool broken = false, bool open = false, bool fullyOpen = false)
{ {
AngleRatio = angleRatio; AngleRatio = angleRatio;
@ -169,27 +175,39 @@ namespace CoopClient
} }
#endregion #endregion
/// <summary>
/// ?
/// </summary>
[ProtoMember(1)] [ProtoMember(1)]
public float AngleRatio { get; set; } public float AngleRatio { get; set; }
/// <summary>
/// ?
/// </summary>
[ProtoMember(2)] [ProtoMember(2)]
public bool Broken { get; set; } public bool Broken { get; set; }
/// <summary>
/// ?
/// </summary>
[ProtoMember(3)] [ProtoMember(3)]
public bool Open { get; set; } public bool Open { get; set; }
/// <summary>
/// ?
/// </summary>
[ProtoMember(4)] [ProtoMember(4)]
public bool FullyOpen { get; set; } public bool FullyOpen { get; set; }
} }
#endregion #endregion
public interface IPacket interface IPacket
{ {
void PacketToNetOutGoingMessage(NetOutgoingMessage message); void PacketToNetOutGoingMessage(NetOutgoingMessage message);
void NetIncomingMessageToPacket(NetIncomingMessage message); void NetIncomingMessageToPacket(NetIncomingMessage message);
} }
public abstract class Packet : IPacket abstract class Packet : IPacket
{ {
public abstract void PacketToNetOutGoingMessage(NetOutgoingMessage message); public abstract void PacketToNetOutGoingMessage(NetOutgoingMessage message);
public abstract void NetIncomingMessageToPacket(NetIncomingMessage message); public abstract void NetIncomingMessageToPacket(NetIncomingMessage message);
@ -454,18 +472,21 @@ namespace CoopClient
public float VehSteeringAngle { get; set; } public float VehSteeringAngle { get; set; }
[ProtoMember(13)] [ProtoMember(13)]
public int[] VehColors { get; set; } public LVector3 VehAimCoords { get; set; }
[ProtoMember(14)] [ProtoMember(14)]
public Dictionary<int, int> VehMods { get; set; } public int[] VehColors { get; set; }
[ProtoMember(15)] [ProtoMember(15)]
public VehicleDoors[] VehDoors { get; set; } public Dictionary<int, int> VehMods { get; set; }
[ProtoMember(16)] [ProtoMember(16)]
public int VehTires { get; set; } public VehicleDoors[] VehDoors { get; set; }
[ProtoMember(17)] [ProtoMember(17)]
public int VehTires { get; set; }
[ProtoMember(18)]
public byte? Flag { get; set; } = 0; public byte? Flag { get; set; } = 0;
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message) public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
@ -496,6 +517,7 @@ namespace CoopClient
VehVelocity = data.VehVelocity; VehVelocity = data.VehVelocity;
VehSpeed = data.VehSpeed; VehSpeed = data.VehSpeed;
VehSteeringAngle = data.VehSteeringAngle; VehSteeringAngle = data.VehSteeringAngle;
VehAimCoords = data.VehAimCoords;
VehColors = data.VehColors; VehColors = data.VehColors;
VehMods = data.VehMods; VehMods = data.VehMods;
VehDoors = data.VehDoors; VehDoors = data.VehDoors;
@ -910,8 +932,14 @@ namespace CoopClient
} }
#endregion #endregion
/// <summary>
/// ?
/// </summary>
public static class CoopSerializer public static class CoopSerializer
{ {
/// <summary>
/// ?
/// </summary>
public static byte[] CSerialize(this object obj) public static byte[] CSerialize(this object obj)
{ {
if (obj == null) if (obj == null)
@ -919,14 +947,13 @@ namespace CoopClient
return null; return null;
} }
BinaryFormatter bf = new BinaryFormatter(); string jsonString = JsonConvert.SerializeObject(obj);
using (MemoryStream ms = new MemoryStream()) return System.Text.Encoding.UTF8.GetBytes(jsonString);
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
} }
/// <summary>
/// ?
/// </summary>
public static T CDeserialize<T>(this byte[] bytes) where T : class public static T CDeserialize<T>(this byte[] bytes) where T : class
{ {
if (bytes == null) if (bytes == null)
@ -934,14 +961,8 @@ namespace CoopClient
return null; return null;
} }
using (MemoryStream memStream = new MemoryStream()) var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
{ return JsonConvert.DeserializeObject<T>(jsonString);
BinaryFormatter binForm = new BinaryFormatter();
memStream.Write(bytes, 0, bytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
T obj = (T)binForm.Deserialize(memStream);
return obj;
}
} }
internal static T Deserialize<T>(this byte[] data) where T : new() internal static T Deserialize<T>(this byte[] data) where T : new()

View File

@ -8,12 +8,18 @@ using GTA.Native;
namespace CoopClient namespace CoopClient
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class PlayerList : Script public class PlayerList : Script
{ {
private readonly Scaleform MainScaleform = new Scaleform("mp_mm_card_freemode"); private readonly Scaleform MainScaleform = new Scaleform("mp_mm_card_freemode");
private ulong LastUpdate = Util.GetTickCount64(); 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() public PlayerList()
{ {
Init(); Init();

View File

@ -1,10 +1,29 @@
namespace CoopClient namespace CoopClient
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class Settings public class Settings
{ {
/// <summary>
/// Don't use it!
/// </summary>
public string Username { get; set; } = "Player"; public string Username { get; set; } = "Player";
/// <summary>
/// Don't use it!
/// </summary>
public string LastServerAddress { get; set; } = "127.0.0.1:4499"; 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; public bool FlipMenu { get; set; } = false;
/// <summary>
/// Don't use it!
/// </summary>
public int StreamedNpc { get; set; } = 10; public int StreamedNpc { get; set; } = 10;
} }
} }

View File

@ -2,9 +2,7 @@
using System.IO; using System.IO;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using static System.Runtime.InteropServices.Marshal;
using GTA; using GTA;
using GTA.Native; using GTA.Native;
@ -17,60 +15,29 @@ namespace CoopClient
#region -- POINTER -- #region -- POINTER --
private static int SteeringAngleOffset { get; set; } 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() 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"); address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
GetEntityAddressFunc = GetDelegateForFunctionPointer<GetHandleAddressFuncDelegate>(new IntPtr(*(int*)(address + 1) + address + 5)); if (address != IntPtr.Zero)
address = FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
if (address != null)
{ {
SteeringAngleOffset = *(int*)(address + 6) + 8; SteeringAngleOffset = *(int*)(address + 6) + 8;
} }
address = FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
if (address != null) if (address != IntPtr.Zero)
{ {
for (int i = 0; i < 6; i++) 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) if (address == IntPtr.Zero || SteeringAngleOffset == 0)
{ {
return; return;
@ -80,6 +47,11 @@ namespace CoopClient
} }
#endregion #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) public static Model ModelRequest(this int hash)
{ {
Model model = new Model(hash); Model model = new Model(hash);
@ -167,6 +139,15 @@ namespace CoopClient
return aimOrShoot ? (isNpc ? GetLastWeaponImpact(ped) : RaycastEverything(new Vector2(0, 0))) : new Vector3(); 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) public static byte? GetVehicleFlags(this Vehicle veh, bool fullSync)
{ {
byte? flags = 0; byte? flags = 0;
@ -365,6 +346,38 @@ namespace CoopClient
return coord.GetResult<Vector3>(); 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) public static double DegToRad(double deg)
{ {
return deg * Math.PI / 180.0; return deg * Math.PI / 180.0;

View File

@ -6,14 +6,20 @@ using GTA.Native;
namespace CoopClient namespace CoopClient
{ {
/// <summary>
/// Don't use it!
/// </summary>
public class WorldThread : Script public class WorldThread : Script
{ {
private static bool LastDisableTraffic = false; private static bool LastDisableTraffic = false;
/// <summary>
/// Don't use it!
/// </summary>
public WorldThread() public WorldThread()
{ {
Tick += OnTick; Tick += OnTick;
Interval = 1000 / 60; Interval = Util.GetGameMs<int>();
Aborted += (sender, e) => Aborted += (sender, e) =>
{ {
if (LastDisableTraffic) 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) if (Game.IsLoading)
{ {

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="protobuf-net" version="2.4.6" targetFramework="net48" />
</packages>

View File

@ -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

View File

@ -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()));
}
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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>

View File

@ -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;
}
}
}
}

View File

@ -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")]

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@ -11,14 +11,7 @@
| ⚠️ The original GTACoOp can be found [HERE](https://gtacoop.com/) | | ⚠️ The original GTACoOp can be found [HERE](https://gtacoop.com/) |
| --- | | --- |
Get the latest client to test out the new features before we release them: http://gtacoopr.000webhostapp.com/
<br/>
[**Download**](https://entenkoeniq.de/client.zip)
<br/>
<br/>
Server:
<br/>
**168.119.29.103:4499**
# 📋 Requirements # 📋 Requirements
- Visual Studio 2022 - Visual Studio 2022
@ -26,7 +19,7 @@ Server:
- .NET Framework 4.8 - .NET Framework 4.8
# 📚 Libraries # 📚 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) - [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/tree/1342949cd19eaa4115990793311681ea1568054c)
- Lidgren Network Custom (***PRIVATE***) - Lidgren Network Custom (***PRIVATE***)
- - No new features (only improvements) - - No new features (only improvements)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using Lidgren.Network; using Lidgren.Network;
@ -43,14 +44,21 @@ namespace CoopServer
} }
#endregion #endregion
#region FUNCTIONS
public void Kick(string[] reason) 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") public void SendChatMessage(string message, string from = "Server")
{
try
{ {
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID); NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
if (userConnection == null)
{
return;
}
ChatMessagePacket packet = new() ChatMessagePacket packet = new()
{ {
@ -62,10 +70,21 @@ namespace CoopServer
packet.PacketToNetOutGoingMessage(outgoingMessage); packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0); 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) public void SendNativeCall(ulong hash, params object[] args)
{
try
{ {
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID); NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
if (userConnection == null)
{
return;
}
List<NativeArgument> arguments = Util.ParseNativeArguments(args); List<NativeArgument> arguments = Util.ParseNativeArguments(args);
if (arguments == null) if (arguments == null)
@ -83,15 +102,26 @@ namespace CoopServer
packet.PacketToNetOutGoingMessage(outgoingMessage); packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0); 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 SendModPacket(string mod, byte customID, byte[] bytes) public void SendModPacket(string mod, byte customID, byte[] bytes)
{
try
{ {
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID); NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID);
if (userConnection == null)
{
return;
}
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new ModPacket() new ModPacket()
{ {
ID = -1, ID = 0,
Target = 0, Target = 0,
Mod = mod, Mod = mod,
CustomPacketID = customID, CustomPacketID = customID,
@ -100,5 +130,11 @@ namespace CoopServer
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0); Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
Server.MainNetServer.FlushSendQueue(); Server.MainNetServer.FlushSendQueue();
} }
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
#endregion
} }
} }

View File

@ -8,13 +8,15 @@
<RepositoryUrl>https://github.com/GTACOOP-R/GTACoop-R</RepositoryUrl> <RepositoryUrl>https://github.com/GTACOOP-R/GTACoop-R</RepositoryUrl>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="2.4.6" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Lidgren.Network"> <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> </Reference>
</ItemGroup> </ItemGroup>

View File

@ -1,10 +1,10 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using Lidgren.Network; using Lidgren.Network;
using ProtoBuf; using ProtoBuf;
using Newtonsoft.Json;
namespace CoopServer namespace CoopServer
{ {
@ -387,18 +387,21 @@ namespace CoopServer
public float VehSteeringAngle { get; set; } public float VehSteeringAngle { get; set; }
[ProtoMember(13)] [ProtoMember(13)]
public int[] VehColors { get; set; } public LVector3 VehAimCoords { get; set; }
[ProtoMember(14)] [ProtoMember(14)]
public Dictionary<int, int> VehMods { get; set; } public int[] VehColors { get; set; }
[ProtoMember(15)] [ProtoMember(15)]
public VehicleDoors[] VehDoors { get; set; } public Dictionary<int, int> VehMods { get; set; }
[ProtoMember(16)] [ProtoMember(16)]
public int VehTires { get; set; } public VehicleDoors[] VehDoors { get; set; }
[ProtoMember(17)] [ProtoMember(17)]
public int VehTires { get; set; }
[ProtoMember(18)]
public byte? Flag { get; set; } = 0; public byte? Flag { get; set; } = 0;
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message) public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
@ -429,6 +432,7 @@ namespace CoopServer
VehVelocity = data.VehVelocity; VehVelocity = data.VehVelocity;
VehSpeed = data.VehSpeed; VehSpeed = data.VehSpeed;
VehSteeringAngle = data.VehSteeringAngle; VehSteeringAngle = data.VehSteeringAngle;
VehAimCoords = data.VehAimCoords;
VehColors = data.VehColors; VehColors = data.VehColors;
VehMods = data.VehMods; VehMods = data.VehMods;
VehDoors = data.VehDoors; VehDoors = data.VehDoors;
@ -852,10 +856,8 @@ namespace CoopServer
return null; return null;
} }
BinaryFormatter bf = new BinaryFormatter(); string jsonString = JsonConvert.SerializeObject(obj);
using MemoryStream ms = new MemoryStream(); return System.Text.Encoding.UTF8.GetBytes(jsonString);
bf.Serialize(ms, obj);
return ms.ToArray();
} }
public static T CDeserialize<T>(this byte[] bytes) where T : class public static T CDeserialize<T>(this byte[] bytes) where T : class
@ -865,12 +867,8 @@ namespace CoopServer
return null; return null;
} }
using MemoryStream memStream = new MemoryStream(); var jsonString = System.Text.Encoding.UTF8.GetString(bytes);
BinaryFormatter binForm = new BinaryFormatter(); return JsonConvert.DeserializeObject<T>(jsonString);
memStream.Write(bytes, 0, bytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
T obj = (T)binForm.Deserialize(memStream);
return obj;
} }
internal static T Deserialize<T>(this byte[] data) where T : new() internal static T Deserialize<T>(this byte[] data) where T : new()

View File

@ -8,11 +8,7 @@
private LVector3 CurrentPosition { get; set; } private LVector3 CurrentPosition { get; set; }
public LVector3 Position public LVector3 Position
{ {
get get => CurrentPosition;
{
return CurrentPosition;
}
set set
{ {
LastPosition = CurrentPosition; 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) public bool IsInRangeOf(LVector3 position, float distance)
{ {

View File

@ -4,14 +4,23 @@ using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Reflection; using System.Reflection;
using System.IO; using System.IO;
using System.Net.Http;
using Newtonsoft.Json;
using Lidgren.Network; using Lidgren.Network;
using System.Text;
namespace CoopServer 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"); public static readonly Settings MainSettings = Util.Read<Settings>("CoopSettings.xml");
private readonly Blocklist MainBlocklist = Util.Read<Blocklist>("Blocklist.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)) if (!string.IsNullOrEmpty(MainSettings.Resource))
{ {
try try
@ -283,37 +372,37 @@ namespace CoopServer
{ {
packet = new ModPacket(); packet = new ModPacket();
packet.NetIncomingMessageToPacket(message); packet.NetIncomingMessageToPacket(message);
ModPacket modPacket = (ModPacket)packet; 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)) // Was canceled
}
else if (modPacket.Target != -1)
{ {
break;
}
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
modPacket.PacketToNetOutGoingMessage(outgoingMessage); modPacket.PacketToNetOutGoingMessage(outgoingMessage);
if (modPacket.Target != 0) if (modPacket.Target != 0)
{ {
NetConnection target = MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == modPacket.Target); NetConnection target = MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == modPacket.Target);
if (target == null) if (target.Equals(default(Client)))
{ {
Logging.Error($"[ModPacket] target \"{modPacket.Target}\" not found!"); Logging.Error($"[ModPacket] target \"{modPacket.Target}\" not found!");
return;
} }
else
{
// Send back to target // Send back to target
MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, 0); MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, 0);
} }
}
else else
{ {
// Send back to all players // Send back to all players
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0); MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
} }
} }
}
catch (Exception e) catch (Exception e)
{ {
message.SenderConnection.Disconnect(e.Message); message.SenderConnection.Disconnect(e.Message);
@ -331,7 +420,7 @@ namespace CoopServer
break; break;
case NetIncomingMessageType.ConnectionLatencyUpdated: case NetIncomingMessageType.ConnectionLatencyUpdated:
Client client = Clients.FirstOrDefault(x => x.ID == message.SenderConnection.RemoteUniqueIdentifier); Client client = Clients.FirstOrDefault(x => x.ID == message.SenderConnection.RemoteUniqueIdentifier);
if (client != default) if (!client.Equals(default(Client)))
{ {
client.Latency = message.ReadFloat(); client.Latency = message.ReadFloat();
} }
@ -420,9 +509,13 @@ namespace CoopServer
long localID = local.RemoteUniqueIdentifier; long localID = local.RemoteUniqueIdentifier;
Client tmpClient;
// Add the player to Players // Add the player to Players
lock (Clients)
{
Clients.Add( Clients.Add(
new Client() tmpClient = new Client()
{ {
ID = localID, ID = localID,
Player = new() Player = new()
@ -432,6 +525,7 @@ namespace CoopServer
} }
} }
); );
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
@ -447,47 +541,51 @@ namespace CoopServer
// Accept the connection and send back a new handshake packet with the connection ID // Accept the connection and send back a new handshake packet with the connection ID
local.Approve(outgoingMessage); 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 // 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) 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)) if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
{ {
SendChatMessage(new ChatMessagePacket() { Username = "Server", Message = MainSettings.WelcomeMessage }, new List<NetConnection>() { local }); 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; List<NetConnection> clients;
if ((clients = Util.FilterAllLocal(local)).Count == 0) if ((clients = Util.FilterAllLocal(local)).Count > 0)
{ {
return;
}
// Send all players to local // Send all players to local
clients.ForEach(targetPlayer => clients.ForEach(targetPlayer =>
{ {
long targetPlayerID = targetPlayer.RemoteUniqueIdentifier; 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(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new PlayerConnectPacket() new PlayerConnectPacket()
{ {
ID = targetPlayerID, ID = targetPlayerID,
SocialClubName = targetEntity.Player.SocialClubName, SocialClubName = targetClient.Player.SocialClubName,
Username = targetEntity.Player.Username Username = targetClient.Player.Username
}.PacketToNetOutGoingMessage(outgoingMessage); }.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0); MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0);
}
}); });
// Send local to all players // Send local to all players
Client localClient = Clients.First(x => x.ID == packet.ID);
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new PlayerConnectPacket() new PlayerConnectPacket()
{ {
@ -498,14 +596,15 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0); MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
} }
if (MainResource != null)
{
MainResource.InvokePlayerConnected(localClient);
}
}
// Send all players a message that someone has left the server // Send all players a message that someone has left the server
private static void SendPlayerDisconnectPacket(PlayerDisconnectPacket packet) private static void SendPlayerDisconnectPacket(PlayerDisconnectPacket packet)
{ {
if (MainResource != null)
{
MainResource.InvokePlayerDisconnected(Clients.Find(x => x.ID == packet.ID));
}
List<NetConnection> clients; List<NetConnection> clients;
if ((clients = Util.FilterAllLocal(packet.ID)).Count > 0) if ((clients = Util.FilterAllLocal(packet.ID)).Count > 0)
{ {
@ -514,16 +613,40 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0); 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) private static void FullSyncPlayer(FullSyncPlayerPacket packet)
{ {
Client client = Clients.First(x => x.ID == packet.Extra.ID); Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position; 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 playerPacket = packet.Extra;
playerPacket.Latency = client.Latency; playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket; packet.Extra = playerPacket;
@ -545,15 +668,30 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0); MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
}); });
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
} }
private static void FullSyncPlayerVeh(FullSyncPlayerVehPacket packet) private static void FullSyncPlayerVeh(FullSyncPlayerVehPacket packet)
{ {
Client client = Clients.First(x => x.ID == packet.Extra.ID); Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position; 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 playerPacket = packet.Extra;
playerPacket.Latency = client.Latency; playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket; packet.Extra = playerPacket;
@ -575,15 +713,30 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0); MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
}); });
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
} }
private static void LightSyncPlayer(LightSyncPlayerPacket packet) private static void LightSyncPlayer(LightSyncPlayerPacket packet)
{ {
Client client = Clients.First(x => x.ID == packet.Extra.ID); Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position; 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 playerPacket = packet.Extra;
playerPacket.Latency = client.Latency; playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket; packet.Extra = playerPacket;
@ -605,15 +758,30 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0); MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
}); });
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
} }
private static void LightSyncPlayerVeh(LightSyncPlayerVehPacket packet) private static void LightSyncPlayerVeh(LightSyncPlayerVehPacket packet)
{ {
Client client = Clients.First(x => x.ID == packet.Extra.ID); Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position; 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 playerPacket = packet.Extra;
playerPacket.Latency = client.Latency; playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket; packet.Extra = playerPacket;
@ -635,6 +803,11 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0); MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
}); });
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
} }
// Send a message to targets or all players // Send a message to targets or all players
@ -644,19 +817,40 @@ namespace CoopServer
if (MainResource != null) if (MainResource != null)
{ {
if (packet.Message.StartsWith("/")) if (packet.Message.StartsWith('/'))
{ {
string[] cmdArgs = packet.Message.Split(" "); string[] cmdArgs = packet.Message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1); string cmdName = cmdArgs[0].Remove(0, 1);
if (Commands.Any(x => x.Key.Name == cmdName)) if (Commands.Any(x => x.Key.Name == cmdName))
{ {
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
CommandContext ctx = new() CommandContext ctx = new()
{ {
Client = Clients.First(x => x.Player.Username == packet.Username), Client = Clients.FirstOrDefault(x => x.Player.Username == packet.Username),
Args = cmdArgs.Skip(1).ToArray() Args = argsWithoutCmd
}; };
KeyValuePair<Command, Action<CommandContext>> command = Commands.First(x => x.Key.Name == cmdName); 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); command.Value.Invoke(ctx);
} }
else else
@ -723,9 +917,20 @@ namespace CoopServer
} }
#endregion #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) public static void RegisterCommand(string name, Action<CommandContext> callback)
{ {
Command command = new() { Name = name }; Command command = new(name);
if (Commands.ContainsKey(command)) if (Commands.ContainsKey(command))
{ {
@ -737,13 +942,13 @@ namespace CoopServer
public static void RegisterCommands<T>() 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) 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));
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
@ -9,22 +10,25 @@ using Lidgren.Network;
namespace CoopServer namespace CoopServer
{ {
public abstract class ServerScript
{
public API API { get; } = new();
}
internal class Resource internal class Resource
{ {
private static Thread _mainThread; private static Thread _mainThread;
private static bool _hasToStop = false; private static bool _hasToStop = false;
private static Queue<Action> _actionQueue; private static Queue _actionQueue;
private static TaskFactory _factory;
private static ServerScript _script; private static ServerScript _script;
public Resource(ServerScript script) public Resource(ServerScript script)
{ {
_factory = new(); _actionQueue = Queue.Synchronized(new Queue());
_actionQueue = new();
_mainThread = new(ThreadLoop) { IsBackground = true }; _mainThread = new(ThreadLoop) { IsBackground = true };
_mainThread.Start(); _mainThread.Start();
lock (_actionQueue) lock (_actionQueue.SyncRoot)
{ {
_actionQueue.Enqueue(() => _actionQueue.Enqueue(() =>
{ {
@ -36,88 +40,120 @@ namespace CoopServer
private void ThreadLoop() private void ThreadLoop()
{ {
do while (!_hasToStop)
{ {
if (_actionQueue.Count != 0) Queue localQueue;
lock (_actionQueue.SyncRoot)
{ {
lock (_actionQueue) localQueue = new(_actionQueue);
{ _actionQueue.Clear();
_factory.StartNew(() => _actionQueue.Dequeue()?.Invoke());
} }
while (localQueue.Count > 0)
{
(localQueue.Dequeue() as Action)?.Invoke();
} }
// 16 milliseconds to sleep to reduce CPU usage // 16 milliseconds to sleep to reduce CPU usage
Thread.Sleep(1000 / 60); Thread.Sleep(1000 / 60);
} while (_hasToStop); }
} }
public bool InvokeModPacketReceived(long from, long target, string mod, byte customID, byte[] bytes) 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)); Task<bool> task = new(() => _script.API.InvokeModPacketReceived(from, target, mod, customID, bytes));
shutdownTask.Start(); task.Start();
shutdownTask.Wait(5000); 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) 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) 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) public bool InvokeChatMessage(string username, string message)
{ {
Task<bool> shutdownTask = new(() => _script.API.InvokeChatMessage(username, message)); Task<bool> task = new(() => _script.API.InvokeChatMessage(username, message));
shutdownTask.Start(); task.Start();
shutdownTask.Wait(5000); task.Wait(5000);
return shutdownTask.Result; return task.Result;
} }
public void InvokePlayerPositionUpdate(PlayerData playerData) 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 void InvokePlayerUpdate(Client client)
{ {
public API API { get; } = new(); 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 public class API
{ {
#region DELEGATES #region DELEGATES
public delegate void EmptyEvent();
public delegate void ChatEvent(string username, string message, CancelEventArgs cancel); public delegate void ChatEvent(string username, string message, CancelEventArgs cancel);
public delegate void PlayerEvent(Client client); public delegate void PlayerEvent(Client client);
public delegate void ModEvent(long from, long target, string mod, byte customID, byte[] bytes, CancelEventArgs args); public delegate void ModEvent(long from, long target, string mod, byte customID, byte[] bytes, CancelEventArgs args);
#endregion #endregion
#region EVENTS #region EVENTS
public event EventHandler OnStart; public event EmptyEvent OnStart;
public event ChatEvent OnChatMessage; public event ChatEvent OnChatMessage;
public event PlayerEvent OnPlayerHandshake;
public event PlayerEvent OnPlayerConnected; public event PlayerEvent OnPlayerConnected;
public event PlayerEvent OnPlayerDisconnected; public event PlayerEvent OnPlayerDisconnected;
public event PlayerEvent OnPlayerUpdate;
public event PlayerEvent OnPlayerHealthUpdate;
public event PlayerEvent OnPlayerPositionUpdate; public event PlayerEvent OnPlayerPositionUpdate;
public event ModEvent OnModPacketReceived; public event ModEvent OnModPacketReceived;
internal void InvokeStart() internal void InvokeStart()
{ {
OnStart?.Invoke(this, EventArgs.Empty); OnStart?.Invoke();
}
internal void InvokePlayerHandshake(Client client)
{
OnPlayerHandshake?.Invoke(client);
} }
internal void InvokePlayerConnected(Client client) internal void InvokePlayerConnected(Client client)
@ -130,6 +166,16 @@ namespace CoopServer
OnPlayerDisconnected?.Invoke(client); 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) internal bool InvokeChatMessage(string username, string message)
{ {
CancelEventArgs args = new(false); CancelEventArgs args = new(false);
@ -152,11 +198,13 @@ namespace CoopServer
#region FUNCTIONS #region FUNCTIONS
public static void SendModPacketToAll(string mod, byte customID, byte[] bytes) public static void SendModPacketToAll(string mod, byte customID, byte[] bytes)
{
try
{ {
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new ModPacket() new ModPacket()
{ {
ID = -1, ID = 0,
Target = 0, Target = 0,
Mod = mod, Mod = mod,
CustomPacketID = customID, CustomPacketID = customID,
@ -165,8 +213,15 @@ namespace CoopServer
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0); Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
Server.MainNetServer.FlushSendQueue(); 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) public static void SendNativeCallToAll(ulong hash, params object[] args)
{
try
{ {
if (Server.MainNetServer.ConnectionsCount == 0) if (Server.MainNetServer.ConnectionsCount == 0)
{ {
@ -189,6 +244,11 @@ namespace CoopServer
packet.PacketToNetOutGoingMessage(outgoingMessage); packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0); Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
} }
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
public static List<long> GetAllConnections() public static List<long> GetAllConnections()
{ {
@ -211,27 +271,37 @@ namespace CoopServer
public static Client GetClientByUsername(string username) 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") public static void SendChatMessageToAll(string message, string username = "Server")
{
try
{ {
if (Server.MainNetServer.ConnectionsCount == 0) if (Server.MainNetServer.ConnectionsCount == 0)
{ {
return; return;
} }
ChatMessagePacket packet = new() NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new ChatMessagePacket()
{ {
Username = username, Username = username,
Message = message Message = message
}; }.PacketToNetOutGoingMessage(outgoingMessage);
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0); Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
} }
catch (Exception e)
{
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) public static void RegisterCommand(string name, Action<CommandContext> callback)
{ {
Server.RegisterCommand(name, callback); Server.RegisterCommand(name, callback);
@ -244,20 +314,19 @@ namespace CoopServer
#endregion #endregion
} }
public class Command
{
public string Name { get; set; }
}
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method)]
public class CommandAttribute : Attribute public class Command : Attribute
{ {
/// <summary> /// <summary>
/// Sets name of the command /// Sets name of the command
/// </summary> /// </summary>
public string Name { get; set; } 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; Name = name;
} }

View File

@ -11,6 +11,8 @@
public bool NpcsAllowed { get; set; } = true; public bool NpcsAllowed { get; set; } = true;
public bool ModsAllowed { get; set; } = false; public bool ModsAllowed { get; set; } = false;
public bool UPnP { get; set; } = true; 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; public bool DebugMode { get; set; } = false;
} }
} }

View File

@ -8,7 +8,7 @@ using Lidgren.Network;
namespace CoopServer namespace CoopServer
{ {
class Util internal class Util
{ {
public static List<NativeArgument> ParseNativeArguments(params object[] args) public static List<NativeArgument> ParseNativeArguments(params object[] args)
{ {
@ -50,13 +50,13 @@ namespace CoopServer
public static NetConnection GetConnectionByUsername(string username) public static NetConnection GetConnectionByUsername(string username)
{ {
long clientID; Client client = Server.Clients.FirstOrDefault(x => x.Player.Username == username);
if ((clientID = Server.Clients.FirstOrDefault(x => x.Player.Username == username).ID) == default) if (client.Equals(default(Client)))
{ {
return null; 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 // 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 ... // Return a list of players within range of ...
public static List<NetConnection> GetAllInRange(LVector3 position, float range) 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 // Return a list of players within range of ... but not the local one
public static List<NetConnection> GetAllInRange(LVector3 position, float range, NetConnection local) 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() public static T Read<T>(string file) where T : new()