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
{
public static class Interface
/// <summary>
/// ?
/// </summary>
public static class COOPAPI
{
#region DELEGATES
/// <summary>
/// ?
/// </summary>
public delegate void ConnectEvent(bool connected, long fromId, string reason = null);
/// <summary>
/// ?
/// </summary>
public delegate void ChatMessage(string from, string message, CancelEventArgs args);
/// <summary>
/// ?
/// </summary>
public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes);
#endregion
#region EVENTS
/// <summary>
/// ?
/// </summary>
public static event ConnectEvent OnConnection;
/// <summary>
/// ?
/// </summary>
public static event ChatMessage OnChatMessage;
/// <summary>
/// ?
/// </summary>
public static event ModEvent OnModPacketReceived;
internal static void Connected()
@ -50,31 +71,55 @@ namespace CoopClient
}
#endregion
public static void SendChatMessage(string from, string message)
/// <summary>
/// Send a local chat message to this player
/// </summary>
/// <param name="from">Username of the player who sent this message</param>
/// <param name="message">The player's message</param>
public static void LocalChatMessage(string from, string message)
{
Main.MainChat.AddMessage(from, message);
}
/// <summary>
/// ?
/// </summary>
public static void Connect(string serverAddress)
{
Main.MainNetworking.DisConnectFromServer(serverAddress);
}
/// <summary>
/// ?
/// </summary>
public static void Disconnect()
{
Main.MainNetworking.DisConnectFromServer(null);
}
/// <summary>
/// ?
/// </summary>
public static bool IsOnServer()
{
return Main.MainNetworking.IsOnServer();
}
/// <summary>
/// Get the local ID from this Lidgren network client when connected to a server
/// </summary>
/// <returns>long</returns>
public static long GetLocalID()
{
return Main.LocalClientID;
}
/// <summary>
/// Get all connected player's as a Dictionary.
/// Key = Lidgren-Network client ID
/// Value = Character handle or null
/// </summary>
/// <returns>Dictionary(long, int)</returns>
public static Dictionary<long, int?> GetAllPlayers()
{
Dictionary<long, int?> result = new Dictionary<long, int?>();
@ -88,11 +133,22 @@ namespace CoopClient
return result;
}
public static Entities.EntitiesPlayer GetPlayer(long playerId)
/// <summary>
/// Get a player using their Lidgren Network Client ID
/// </summary>
/// <param name="lnID">Lidgren-Network client ID</param>
/// <returns>Entities.EntitiesPlayer</returns>
public static Entities.EntitiesPlayer GetPlayer(long lnID)
{
return Main.Players.ContainsKey(playerId) ? Main.Players[playerId] : null;
lock (Main.Players)
{
return Main.Players.ContainsKey(lnID) ? Main.Players[lnID] : null;
}
}
/// <summary>
/// ?
/// </summary>
public static bool IsMenuVisible()
{
#if NON_INTERACTIVE
@ -102,33 +158,76 @@ namespace CoopClient
#endif
}
/// <summary>
/// ?
/// </summary>
public static bool IsChatFocused()
{
return Main.MainChat.Focused;
}
/// <summary>
/// ?
/// </summary>
public static bool IsPlayerListVisible()
{
return Util.GetTickCount64() - PlayerList.Pressed < 5000;
}
/// <summary>
/// ?
/// </summary>
public static string GetCurrentVersion()
{
return Main.CurrentVersion;
}
// Send bytes to all players
/// <summary>
/// Send any data (bytes) to the server
/// </summary>
/// <param name="mod">The name of this modification (script)</param>
/// <param name="customID">The ID to know what the data is</param>
/// <param name="bytes">Your class, structure or whatever in bytes</param>
public static void SendDataToServer(string mod, byte customID, byte[] bytes)
{
Main.MainNetworking.SendModData(-1, mod, customID, bytes);
}
/// <summary>
/// Send any data (bytes) to the all player
/// </summary>
/// <param name="mod">The name of this modification (script)</param>
/// <param name="customID">The ID to know what the data is</param>
/// <param name="bytes">Your class, structure or whatever in bytes</param>
public static void SendDataToAll(string mod, byte customID, byte[] bytes)
{
Main.MainNetworking.SendModData(0, mod, customID, bytes);
}
// Send bytes to target
public static void SendDataToPlayer(long target, string mod, byte customID, byte[] bytes)
/// <summary>
/// Send any data (bytes) to a player
/// </summary>
/// <param name="lnID">The Lidgren Network Client ID that receives the data</param>
/// <param name="mod">The name of this modification (script)</param>
/// <param name="customID">The ID to know what the data is</param>
/// <param name="bytes">Your class, structure or whatever in bytes</param>
public static void SendDataToPlayer(long lnID, string mod, byte customID, byte[] bytes)
{
Main.MainNetworking.SendModData(target, mod, customID, bytes);
Main.MainNetworking.SendModData(lnID, mod, customID, bytes);
}
/// <summary>
/// Get that player's local username that has been set
/// </summary>
/// <returns>string</returns>
public static string GetLocalUsername()
{
return Main.MainSettings.Username;
}
/// <summary>
/// ?
/// </summary>
public static void Configure(string playerName, bool shareNpcsWithPlayers, int streamedNpcs, bool debug = false)
{
Main.MainSettings.Username = playerName;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,31 +12,49 @@ using GTA.Native;
namespace CoopClient
{
/// <summary>
/// Don't use it!
/// </summary>
public class Main : Script
{
public static RelationshipGroup RelationshipGroup;
internal static RelationshipGroup RelationshipGroup;
private bool GameLoaded = false;
public static readonly string CurrentVersion = "V0_8_0_1";
internal static readonly string CurrentVersion = "V0_9_0";
public static bool ShareNpcsWithPlayers = false;
public static bool DisableTraffic = false;
public static bool NpcsAllowed = false;
internal static bool ShareNpcsWithPlayers = false;
internal static bool DisableTraffic = false;
internal static bool NpcsAllowed = false;
private static bool IsGoingToCar = false;
/// <summary>
/// Don't use it!
/// </summary>
public static Settings MainSettings = Util.ReadSettings();
/// <summary>
/// Don't use it!
/// </summary>
public static Networking MainNetworking = new Networking();
#if !NON_INTERACTIVE
/// <summary>
/// Don't use it!
/// </summary>
public static MenusMain MainMenu = new MenusMain();
#endif
/// <summary>
/// Don't use it!
/// </summary>
public static Chat MainChat = new Chat();
public static long LocalClientID = 0;
public static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
public static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
internal static long LocalClientID = 0;
internal static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
internal static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
/// <summary>
/// Don't use it!
/// </summary>
public Main()
{
Function.Call((Hash)0x0888C3502DBBEEF5); // _LOAD_MP_DLC_MAPS
@ -62,6 +80,8 @@ namespace CoopClient
{
RelationshipGroup = World.AddRelationshipGroup("SYNCPED");
Game.Player.Character.RelationshipGroup = RelationshipGroup;
GTA.UI.Notification.Show(GTA.UI.NotificationIcon.AllPlayersConf, "GTACOOP:R", "Welcome!", "Press ~g~F9~s~ to open the menu.");
}
#if !NON_INTERACTIVE
@ -104,7 +124,7 @@ namespace CoopClient
}
#endif
if ((Util.GetTickCount64() - LastDataSend) < (1000 / 60))
if ((Util.GetTickCount64() - LastDataSend) < Util.GetGameMs<ulong>())
{
return;
}
@ -182,7 +202,7 @@ namespace CoopClient
}
#endif
public static void CleanUp()
internal static void CleanUp()
{
MainChat.Clear();
@ -203,24 +223,13 @@ namespace CoopClient
Npc.Value.Character?.Delete();
}
Npcs.Clear();
foreach (Ped entity in World.GetAllPeds().Where(p => p.Handle != Game.Player.Character.Handle))
{
entity.Kill();
entity.Delete();
}
foreach (Vehicle veh in World.GetAllVehicles().Where(v => v.Handle != Game.Player.Character.CurrentVehicle?.Handle))
{
veh.Delete();
}
}
#if DEBUG
private ulong ArtificialLagCounter;
public static EntitiesPlayer DebugSyncPed;
public static ulong LastFullDebugSync = 0;
public static bool UseDebug = false;
internal static EntitiesPlayer DebugSyncPed;
internal static ulong LastFullDebugSync = 0;
internal static bool UseDebug = false;
private void Debug()
{
@ -231,12 +240,12 @@ namespace CoopClient
DebugSyncPed = Players[0];
}
if ((Util.GetTickCount64() - ArtificialLagCounter) < 157)
if ((Util.GetTickCount64() - ArtificialLagCounter) < 56)
{
return;
}
bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 1500;
bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 500;
if (fullSync)
{
@ -273,6 +282,7 @@ namespace CoopClient
DebugSyncPed.VehicleVelocity = veh.Velocity;
DebugSyncPed.VehicleSpeed = veh.Speed;
DebugSyncPed.VehicleSteeringAngle = veh.SteeringAngle;
DebugSyncPed.AimCoords = veh.IsTurretSeat((int)player.SeatIndex) ? Util.GetVehicleAimCoords() : new GTA.Math.Vector3();
DebugSyncPed.VehicleColors = new int[] { primaryColor, secondaryColor };
DebugSyncPed.VehicleMods = veh.Mods.GetVehicleMods();
DebugSyncPed.VehDoors = veh.Doors.GetVehicleDoors();

View File

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

111
Client/Menus/Sub/Servers.cs Normal file
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
{
/// <summary>
/// Don't use it!
/// </summary>
public class Settings
{
public NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "Settings", "Go to the settings")
internal NativeMenu MainMenu = new NativeMenu("GTACOOP:R", "Settings", "Go to the settings")
{
UseMouse = false,
Alignment = Main.MainSettings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
@ -19,6 +22,9 @@ namespace CoopClient.Menus.Sub
private readonly NativeCheckboxItem ShowNetworkInfo = new NativeCheckboxItem("Show Network Info", Main.MainNetworking.ShowNetworkInfo);
#endif
/// <summary>
/// Don't use it!
/// </summary>
public Settings()
{
DisableTraffic.CheckboxChanged += DisableTrafficCheckboxChanged;
@ -40,7 +46,7 @@ namespace CoopClient.Menus.Sub
#endif
}
public void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
internal void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{
Main.DisableTraffic = DisableTraffic.Checked;
@ -59,14 +65,14 @@ namespace CoopClient.Menus.Sub
}
}
public void StreamedNpcsValueChanged(object a, System.EventArgs b)
internal void StreamedNpcsValueChanged(object a, System.EventArgs b)
{
Main.MainSettings.StreamedNpc = StreamedNpcsItem.Value;
Util.SaveSettings();
StreamedNpcsItem.Title = string.Format("Streamed Npcs ({0})", Main.MainSettings.StreamedNpc);
}
public void FlipMenuCheckboxChanged(object a, System.EventArgs b)
internal void FlipMenuCheckboxChanged(object a, System.EventArgs b)
{
#if !NON_INTERACTIVE
Main.MainMenu.MainMenu.Alignment = FlipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left;
@ -78,7 +84,7 @@ namespace CoopClient.Menus.Sub
}
#if DEBUG
public void UseDebugCheckboxChanged(object a, System.EventArgs b)
internal void UseDebugCheckboxChanged(object a, System.EventArgs b)
{
Main.UseDebug = UseDebugItem.Checked;
@ -96,7 +102,7 @@ namespace CoopClient.Menus.Sub
}
}
public void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
internal void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
{
Main.MainNetworking.ShowNetworkInfo = ShowNetworkInfo.Checked;

View File

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

View File

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

View File

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

View File

@ -1,10 +1,29 @@
namespace CoopClient
{
/// <summary>
/// Don't use it!
/// </summary>
public class Settings
{
/// <summary>
/// Don't use it!
/// </summary>
public string Username { get; set; } = "Player";
/// <summary>
/// Don't use it!
/// </summary>
public string LastServerAddress { get; set; } = "127.0.0.1:4499";
/// <summary>
/// Don't use it!
/// </summary>
public string MasterServer { get; set; } = "http://gtacoopr.000webhostapp.com/servers.php";
/// <summary>
/// Don't use it!
/// </summary>
public bool FlipMenu { get; set; } = false;
/// <summary>
/// Don't use it!
/// </summary>
public int StreamedNpc { get; set; } = 10;
}
}

View File

@ -2,9 +2,7 @@
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using static System.Runtime.InteropServices.Marshal;
using GTA;
using GTA.Native;
@ -17,60 +15,29 @@ namespace CoopClient
#region -- POINTER --
private static int SteeringAngleOffset { get; set; }
delegate ulong GetHandleAddressFuncDelegate(int handle);
static GetHandleAddressFuncDelegate GetEntityAddressFunc;
static unsafe byte* FindPattern(string pattern, string mask)
{
ProcessModule module = Process.GetCurrentProcess().MainModule;
ulong address = (ulong)module.BaseAddress.ToInt64();
ulong endAddress = address + (ulong)module.ModuleMemorySize;
for (; address < endAddress; address++)
{
for (int i = 0; i < pattern.Length; i++)
{
if (mask[i] != '?' && ((byte*)address)[i] != pattern[i])
{
break;
}
else if (i + 1 == pattern.Length)
{
return (byte*)address;
}
}
}
return null;
}
public static unsafe void NativeMemory()
{
byte* address;
IntPtr address;
address = FindPattern("\xE8\x00\x00\x00\x00\x48\x8B\xD8\x48\x85\xC0\x74\x2E\x48\x83\x3D", "x????xxxxxxxxxxx");
GetEntityAddressFunc = GetDelegateForFunctionPointer<GetHandleAddressFuncDelegate>(new IntPtr(*(int*)(address + 1) + address + 5));
address = FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
if (address != null)
address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
if (address != IntPtr.Zero)
{
SteeringAngleOffset = *(int*)(address + 6) + 8;
}
address = FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
if (address != null)
address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
if (address != IntPtr.Zero)
{
for (int i = 0; i < 6; i++)
{
*(byte*)((IntPtr)address + i).ToPointer() = 0x90;
*(byte*)(address + i).ToPointer() = 0x90;
}
}
}
public static unsafe void CustomSteeringAngle(this int handle, float value)
public static unsafe void CustomSteeringAngle(this Vehicle veh, float value)
{
IntPtr address = new IntPtr((long)GetEntityAddressFunc(handle));
IntPtr address = new IntPtr((long)veh.MemoryAddress);
if (address == IntPtr.Zero || SteeringAngleOffset == 0)
{
return;
@ -80,6 +47,11 @@ namespace CoopClient
}
#endregion
public static T GetGameMs<T>() where T : IConvertible
{
return (T)Convert.ChangeType(1f / (Game.FPS > 60f ? 60f : Game.FPS) * 1000f, typeof(T));
}
public static Model ModelRequest(this int hash)
{
Model model = new Model(hash);
@ -167,6 +139,15 @@ namespace CoopClient
return aimOrShoot ? (isNpc ? GetLastWeaponImpact(ped) : RaycastEverything(new Vector2(0, 0))) : new Vector3();
}
/// <summary>
/// Only works for players NOT NPCs
/// </summary>
/// <returns>Vector3</returns>
public static Vector3 GetVehicleAimCoords()
{
return RaycastEverything(new Vector2(0, 0));
}
public static byte? GetVehicleFlags(this Vehicle veh, bool fullSync)
{
byte? flags = 0;
@ -365,6 +346,38 @@ namespace CoopClient
return coord.GetResult<Vector3>();
}
public static bool IsTurretSeat(this Vehicle veh, int seat)
{
if (!Function.Call<bool>(Hash.DOES_VEHICLE_HAVE_WEAPONS, veh.Handle))
{
return false;
}
switch (seat)
{
case -1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Rhino
|| (VehicleHash)veh.Model.Hash == VehicleHash.Khanjari
|| (VehicleHash)veh.Model.Hash == VehicleHash.FireTruck;
case 1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical3
|| (VehicleHash)veh.Model.Hash == VehicleHash.HalfTrack; // Not sure
case 2:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2;
case 3:
return (VehicleHash)veh.Model.Hash == VehicleHash.Limo2;
case 7:
return (VehicleHash)veh.Model.Hash == VehicleHash.Insurgent;
}
return false;
}
public static double DegToRad(double deg)
{
return deg * Math.PI / 180.0;

View File

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

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/) |
| --- |
Get the latest client to test out the new features before we release them:
<br/>
[**Download**](https://entenkoeniq.de/client.zip)
<br/>
<br/>
Server:
<br/>
**168.119.29.103:4499**
http://gtacoopr.000webhostapp.com/
# 📋 Requirements
- Visual Studio 2022
@ -26,7 +19,7 @@ Server:
- .NET Framework 4.8
# 📚 Libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.3.2)
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/tree/ca82751b7cc8bdf2203d361b27fe0d1aa895eb55)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/tree/1342949cd19eaa4115990793311681ea1568054c)
- Lidgren Network Custom (***PRIVATE***)
- - No new features (only improvements)

View File

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

View File

@ -8,13 +8,15 @@
<RepositoryUrl>https://github.com/GTACOOP-R/GTACoop-R</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="2.4.6" />
</ItemGroup>
<ItemGroup>
<Reference Include="Lidgren.Network">
<HintPath>..\Libs\Release\Lidgren.Network.dll</HintPath>
<HintPath>..\Libs\Release\scripts\Lidgren.Network.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="protobuf-net">
<HintPath>..\Libs\Release\scripts\protobuf-net.dll</HintPath>
</Reference>
</ItemGroup>

View File

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

View File

@ -8,11 +8,7 @@
private LVector3 CurrentPosition { get; set; }
public LVector3 Position
{
get
{
return CurrentPosition;
}
get => CurrentPosition;
set
{
LastPosition = CurrentPosition;
@ -24,6 +20,20 @@
}
}
}
private int CurrentHealth { get; set; }
public int Health
{
get => CurrentHealth;
set
{
if (CurrentHealth != value && Server.MainResource != null)
{
Server.MainResource.InvokePlayerHealthUpdate(this);
}
CurrentHealth = value;
}
}
public bool IsInRangeOf(LVector3 position, float distance)
{

View File

@ -4,14 +4,23 @@ using System.Collections.Generic;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Net.Http;
using Newtonsoft.Json;
using Lidgren.Network;
using System.Text;
namespace CoopServer
{
class Server
internal class IpInfo
{
private static readonly string CompatibleVersion = "V0_8_0_1";
public string Country { get; set; }
}
internal class Server
{
private static readonly string CompatibleVersion = "V0_9_0";
public static readonly Settings MainSettings = Util.Read<Settings>("CoopSettings.xml");
private readonly Blocklist MainBlocklist = Util.Read<Blocklist>("Blocklist.xml");
@ -62,6 +71,86 @@ namespace CoopServer
}
}
if (MainSettings.AnnounceSelf)
{
Logging.Info("Announcing to master server...");
#region -- MASTERSERVER --
new Thread(async () =>
{
try
{
HttpClient httpClient = new();
IpInfo info;
try
{
string data = await httpClient.GetStringAsync("https://ipinfo.io/json");
info = JsonConvert.DeserializeObject<IpInfo>(data);
}
catch
{
info = new() { Country = "?" };
}
bool responseError = false;
while (!responseError)
{
string msg =
"{ " +
"\"port\": \"" + MainSettings.ServerPort + "\", " +
"\"name\": \"" + MainSettings.ServerName + "\", " +
"\"version\": \"" + CompatibleVersion.Replace("_", ".") + "\", " +
"\"players\": \"" + MainNetServer.ConnectionsCount + "\", " +
"\"maxPlayers\": \"" + MainSettings.MaxPlayers + "\", " +
"\"allowlist\": \"" + MainSettings.Allowlist + "\", " +
"\"mods\": \"" + MainSettings.ModsAllowed + "\", " +
"\"npcs\": \"" + MainSettings.NpcsAllowed + "\", " +
"\"country\": \"" + info.Country + "\"" +
" }";
HttpResponseMessage response = null;
string responseContent = "";
try
{
response = await httpClient.PostAsync(MainSettings.MasterServer, new StringContent(msg, Encoding.UTF8, "application/json"));
if (response == null)
{
Logging.Error("MasterServer: Something went wrong!");
continue;
}
responseContent = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
Logging.Error(ex.Message);
continue;
}
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
Logging.Error($"MasterServer: {response.StatusCode}");
responseError = true;
}
else
{
// Sleep for 12.5s
Thread.Sleep(12500);
}
}
}
catch (Exception ex)
{
Logging.Error($"MasterServer: {ex.Message}");
}
}).Start();
#endregion
}
if (!string.IsNullOrEmpty(MainSettings.Resource))
{
try
@ -283,35 +372,35 @@ namespace CoopServer
{
packet = new ModPacket();
packet.NetIncomingMessageToPacket(message);
ModPacket modPacket = (ModPacket)packet;
if (MainResource != null)
if (MainResource != null &&
MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes))
{
if (MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes))
{
break;
}
// Was canceled
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
modPacket.PacketToNetOutGoingMessage(outgoingMessage);
if (modPacket.Target != 0)
else if (modPacket.Target != -1)
{
NetConnection target = MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == modPacket.Target);
if (target == null)
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
modPacket.PacketToNetOutGoingMessage(outgoingMessage);
if (modPacket.Target != 0)
{
Logging.Error($"[ModPacket] target \"{modPacket.Target}\" not found!");
return;
NetConnection target = MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == modPacket.Target);
if (target.Equals(default(Client)))
{
Logging.Error($"[ModPacket] target \"{modPacket.Target}\" not found!");
}
else
{
// Send back to target
MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, 0);
}
}
else
{
// Send back to all players
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
}
// Send back to target
MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, 0);
}
else
{
// Send back to all players
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0);
}
}
catch (Exception e)
@ -331,7 +420,7 @@ namespace CoopServer
break;
case NetIncomingMessageType.ConnectionLatencyUpdated:
Client client = Clients.FirstOrDefault(x => x.ID == message.SenderConnection.RemoteUniqueIdentifier);
if (client != default)
if (!client.Equals(default(Client)))
{
client.Latency = message.ReadFloat();
}
@ -420,18 +509,23 @@ namespace CoopServer
long localID = local.RemoteUniqueIdentifier;
Client tmpClient;
// Add the player to Players
Clients.Add(
new Client()
{
ID = localID,
Player = new()
lock (Clients)
{
Clients.Add(
tmpClient = new Client()
{
SocialClubName = packet.SocialClubName,
Username = packet.Username
ID = localID,
Player = new()
{
SocialClubName = packet.SocialClubName,
Username = packet.Username
}
}
}
);
);
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
@ -447,65 +541,70 @@ namespace CoopServer
// Accept the connection and send back a new handshake packet with the connection ID
local.Approve(outgoingMessage);
if (MainResource != null)
{
MainResource.InvokePlayerHandshake(tmpClient);
}
}
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
private static void SendPlayerConnectPacket(NetConnection local, PlayerConnectPacket packet)
{
Client localClient = Clients.FirstOrDefault(x => x.ID == packet.ID);
if (localClient.Equals(default(Client)))
{
local.Disconnect("No data found!");
return;
}
if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
{
SendChatMessage(new ChatMessagePacket() { Username = "Server", Message = MainSettings.WelcomeMessage }, new List<NetConnection>() { local });
}
if (MainResource != null)
{
MainResource.InvokePlayerConnected(Clients.Find(x => x.ID == packet.ID));
}
List<NetConnection> clients;
if ((clients = Util.FilterAllLocal(local)).Count == 0)
if ((clients = Util.FilterAllLocal(local)).Count > 0)
{
return;
}
// Send all players to local
clients.ForEach(targetPlayer =>
{
long targetPlayerID = targetPlayer.RemoteUniqueIdentifier;
// Send all players to local
clients.ForEach(targetPlayer =>
{
long targetPlayerID = targetPlayer.RemoteUniqueIdentifier;
Client targetEntity = Clients.First(x => x.ID == targetPlayerID);
Client targetClient = Clients.FirstOrDefault(x => x.ID == targetPlayerID);
if (!targetClient.Equals(default(Client)))
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new PlayerConnectPacket()
{
ID = targetPlayerID,
SocialClubName = targetClient.Player.SocialClubName,
Username = targetClient.Player.Username
}.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0);
}
});
// Send local to all players
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new PlayerConnectPacket()
{
ID = targetPlayerID,
SocialClubName = targetEntity.Player.SocialClubName,
Username = targetEntity.Player.Username
ID = packet.ID,
SocialClubName = localClient.Player.SocialClubName,
Username = localClient.Player.Username
}.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0);
});
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
}
// Send local to all players
Client localClient = Clients.First(x => x.ID == packet.ID);
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new PlayerConnectPacket()
if (MainResource != null)
{
ID = packet.ID,
SocialClubName = localClient.Player.SocialClubName,
Username = localClient.Player.Username
}.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
MainResource.InvokePlayerConnected(localClient);
}
}
// Send all players a message that someone has left the server
private static void SendPlayerDisconnectPacket(PlayerDisconnectPacket packet)
{
if (MainResource != null)
{
MainResource.InvokePlayerDisconnected(Clients.Find(x => x.ID == packet.ID));
}
List<NetConnection> clients;
if ((clients = Util.FilterAllLocal(packet.ID)).Count > 0)
{
@ -514,16 +613,40 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
}
Clients.Remove(Clients.Find(x => x.ID == packet.ID));
Client localClient = Clients.FirstOrDefault(x => x.ID == packet.ID);
if (localClient.Equals(default(Client)))
{
return;
}
if (MainResource != null)
{
MainResource.InvokePlayerDisconnected(localClient);
}
lock (Clients)
{
Clients.Remove(localClient);
}
}
private static void FullSyncPlayer(FullSyncPlayerPacket packet)
{
Client client = Clients.First(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position;
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
if (tmpClient.Equals(default(Client)))
{
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
if (localConn != null)
{
localConn.Disconnect("No data found!");
}
return;
}
tmpClient.Player.Position = packet.Extra.Position;
tmpClient.Player.Health = packet.Extra.Health;
PlayerPacket playerPacket = packet.Extra;
playerPacket.Latency = client.Latency;
playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket;
@ -545,15 +668,30 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
});
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
}
private static void FullSyncPlayerVeh(FullSyncPlayerVehPacket packet)
{
Client client = Clients.First(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position;
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
if (tmpClient.Equals(default(Client)))
{
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
if (localConn != null)
{
localConn.Disconnect("No data found!");
}
return;
}
tmpClient.Player.Position = packet.Extra.Position;
tmpClient.Player.Health = packet.Extra.Health;
PlayerPacket playerPacket = packet.Extra;
playerPacket.Latency = client.Latency;
playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket;
@ -575,15 +713,30 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
});
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
}
private static void LightSyncPlayer(LightSyncPlayerPacket packet)
{
Client client = Clients.First(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position;
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
if (tmpClient.Equals(default(Client)))
{
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
if (localConn != null)
{
localConn.Disconnect("No data found!");
}
return;
}
tmpClient.Player.Position = packet.Extra.Position;
tmpClient.Player.Health = packet.Extra.Health;
PlayerPacket playerPacket = packet.Extra;
playerPacket.Latency = client.Latency;
playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket;
@ -605,15 +758,30 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
});
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
}
private static void LightSyncPlayerVeh(LightSyncPlayerVehPacket packet)
{
Client client = Clients.First(x => x.ID == packet.Extra.ID);
client.Player.Position = packet.Extra.Position;
Client tmpClient = Clients.FirstOrDefault(x => x.ID == packet.Extra.ID);
if (tmpClient.Equals(default(Client)))
{
NetConnection localConn = MainNetServer.Connections.Find(x => packet.Extra.ID == x.RemoteUniqueIdentifier);
if (localConn != null)
{
localConn.Disconnect("No data found!");
}
return;
}
tmpClient.Player.Position = packet.Extra.Position;
tmpClient.Player.Health = packet.Extra.Health;
PlayerPacket playerPacket = packet.Extra;
playerPacket.Latency = client.Latency;
playerPacket.Latency = tmpClient.Latency;
packet.Extra = playerPacket;
@ -635,6 +803,11 @@ namespace CoopServer
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, 0);
});
if (MainResource != null)
{
MainResource.InvokePlayerUpdate(tmpClient);
}
}
// Send a message to targets or all players
@ -644,19 +817,40 @@ namespace CoopServer
if (MainResource != null)
{
if (packet.Message.StartsWith("/"))
if (packet.Message.StartsWith('/'))
{
string[] cmdArgs = packet.Message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1);
if (Commands.Any(x => x.Key.Name == cmdName))
{
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
CommandContext ctx = new()
{
Client = Clients.First(x => x.Player.Username == packet.Username),
Args = cmdArgs.Skip(1).ToArray()
Client = Clients.FirstOrDefault(x => x.Player.Username == packet.Username),
Args = argsWithoutCmd
};
KeyValuePair<Command, Action<CommandContext>> command = Commands.First(x => x.Key.Name == cmdName);
if (command.Key.Usage != null && command.Key.ArgsLength != argsWithoutCmd.Length)
{
NetConnection userConnection = Util.GetConnectionByUsername(packet.Username);
if (userConnection == default)
{
return;
}
outgoingMessage = MainNetServer.CreateMessage();
new ChatMessagePacket()
{
Username = "Server",
Message = command.Key.Usage
}.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0);
return;
}
command.Value.Invoke(ctx);
}
else
@ -678,7 +872,7 @@ namespace CoopServer
return;
}
if (MainResource.InvokeChatMessage(packet.Username, packet.Message))
{
return;
@ -723,9 +917,20 @@ namespace CoopServer
}
#endregion
public static void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
{
Command command = new(name) { Usage = usage, ArgsLength = argsLength };
if (Commands.ContainsKey(command))
{
throw new Exception("Command \"" + command.Name + "\" was already been registered!");
}
Commands.Add(command, callback);
}
public static void RegisterCommand(string name, Action<CommandContext> callback)
{
Command command = new() { Name = name };
Command command = new(name);
if (Commands.ContainsKey(command))
{
@ -737,13 +942,13 @@ namespace CoopServer
public static void RegisterCommands<T>()
{
IEnumerable<MethodInfo> commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(CommandAttribute), false).Any());
IEnumerable<MethodInfo> commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(Command), false).Any());
foreach (MethodInfo method in commands)
{
CommandAttribute attribute = method.GetCustomAttribute<CommandAttribute>(true);
Command attribute = method.GetCustomAttribute<Command>(true);
RegisterCommand(attribute.Name, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
}
}
}

View File

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

View File

@ -11,6 +11,8 @@
public bool NpcsAllowed { get; set; } = true;
public bool ModsAllowed { get; set; } = false;
public bool UPnP { get; set; } = true;
public bool AnnounceSelf { get; set; } = true;
public string MasterServer { get; set; } = "http://gtacoopr.000webhostapp.com/servers.php";
public bool DebugMode { get; set; } = false;
}
}

View File

@ -8,7 +8,7 @@ using Lidgren.Network;
namespace CoopServer
{
class Util
internal class Util
{
public static List<NativeArgument> ParseNativeArguments(params object[] args)
{
@ -50,13 +50,13 @@ namespace CoopServer
public static NetConnection GetConnectionByUsername(string username)
{
long clientID;
if ((clientID = Server.Clients.FirstOrDefault(x => x.Player.Username == username).ID) == default)
Client client = Server.Clients.FirstOrDefault(x => x.Player.Username == username);
if (client.Equals(default(Client)))
{
return null;
}
return Server.MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == clientID);
return Server.MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == client.ID);
}
// Return a list of all connections but not the local connection
@ -72,12 +72,20 @@ namespace CoopServer
// Return a list of players within range of ...
public static List<NetConnection> GetAllInRange(LVector3 position, float range)
{
return new(Server.MainNetServer.Connections.FindAll(e => Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier).Player.IsInRangeOf(position, range)));
return new(Server.MainNetServer.Connections.FindAll(e =>
{
Client client = Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier);
return !client.Equals(default(Client)) && client.Player.IsInRangeOf(position, range);
}));
}
// Return a list of players within range of ... but not the local one
public static List<NetConnection> GetAllInRange(LVector3 position, float range, NetConnection local)
{
return new(Server.MainNetServer.Connections.Where(e => e != local && Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier).Player.IsInRangeOf(position, range)));
return new(Server.MainNetServer.Connections.Where(e =>
{
Client client = Server.Clients.First(x => x.ID == e.RemoteUniqueIdentifier);
return e != local && !client.Equals(default(Client)) && client.Player.IsInRangeOf(position, range);
}));
}
public static T Read<T>(string file) where T : new()