diff --git a/Client/Interface.cs b/Client/COOPAPI.cs similarity index 50% rename from Client/Interface.cs rename to Client/COOPAPI.cs index 8f3914b..eaa30f6 100644 --- a/Client/Interface.cs +++ b/Client/COOPAPI.cs @@ -4,17 +4,38 @@ using System.Linq; namespace CoopClient { - public static class Interface + /// + /// ? + /// + public static class COOPAPI { #region DELEGATES + /// + /// ? + /// public delegate void ConnectEvent(bool connected, long fromId, string reason = null); + /// + /// ? + /// public delegate void ChatMessage(string from, string message, CancelEventArgs args); + /// + /// ? + /// public delegate void ModEvent(long from, string mod, byte customID, byte[] bytes); #endregion #region EVENTS + /// + /// ? + /// public static event ConnectEvent OnConnection; + /// + /// ? + /// public static event ChatMessage OnChatMessage; + /// + /// ? + /// public static event ModEvent OnModPacketReceived; internal static void Connected() @@ -50,31 +71,55 @@ namespace CoopClient } #endregion - public static void SendChatMessage(string from, string message) + /// + /// Send a local chat message to this player + /// + /// Username of the player who sent this message + /// The player's message + public static void LocalChatMessage(string from, string message) { Main.MainChat.AddMessage(from, message); } + /// + /// ? + /// public static void Connect(string serverAddress) { Main.MainNetworking.DisConnectFromServer(serverAddress); } + /// + /// ? + /// public static void Disconnect() { Main.MainNetworking.DisConnectFromServer(null); } + /// + /// ? + /// public static bool IsOnServer() { return Main.MainNetworking.IsOnServer(); } + /// + /// Get the local ID from this Lidgren network client when connected to a server + /// + /// long public static long GetLocalID() { return Main.LocalClientID; } + /// + /// Get all connected player's as a Dictionary. + /// Key = Lidgren-Network client ID + /// Value = Character handle or null + /// + /// Dictionary(long, int) public static Dictionary GetAllPlayers() { Dictionary result = new Dictionary(); @@ -88,11 +133,22 @@ namespace CoopClient return result; } - public static Entities.EntitiesPlayer GetPlayer(long playerId) + /// + /// Get a player using their Lidgren Network Client ID + /// + /// Lidgren-Network client ID + /// Entities.EntitiesPlayer + 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; + } } + /// + /// ? + /// public static bool IsMenuVisible() { #if NON_INTERACTIVE @@ -102,33 +158,76 @@ namespace CoopClient #endif } + /// + /// ? + /// public static bool IsChatFocused() { return Main.MainChat.Focused; } + /// + /// ? + /// public static bool IsPlayerListVisible() { return Util.GetTickCount64() - PlayerList.Pressed < 5000; } + /// + /// ? + /// public static string GetCurrentVersion() { return Main.CurrentVersion; } - // Send bytes to all players + /// + /// Send any data (bytes) to the server + /// + /// The name of this modification (script) + /// The ID to know what the data is + /// Your class, structure or whatever in bytes + public static void SendDataToServer(string mod, byte customID, byte[] bytes) + { + Main.MainNetworking.SendModData(-1, mod, customID, bytes); + } + + /// + /// Send any data (bytes) to the all player + /// + /// The name of this modification (script) + /// The ID to know what the data is + /// Your class, structure or whatever in bytes 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) + /// + /// Send any data (bytes) to a player + /// + /// The Lidgren Network Client ID that receives the data + /// The name of this modification (script) + /// The ID to know what the data is + /// Your class, structure or whatever in bytes + 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); } + /// + /// Get that player's local username that has been set + /// + /// string + public static string GetLocalUsername() + { + return Main.MainSettings.Username; + } + + /// + /// ? + /// public static void Configure(string playerName, bool shareNpcsWithPlayers, int streamedNpcs, bool debug = false) { Main.MainSettings.Username = playerName; diff --git a/Client/Chat.cs b/Client/Chat.cs index 30c422f..4975ca3 100644 --- a/Client/Chat.cs +++ b/Client/Chat.cs @@ -8,14 +8,17 @@ using GTA.Native; namespace CoopClient { + /// + /// Don't use it! + /// 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 } } + /// + /// Don't use it! + /// 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]; diff --git a/Client/CoopClient.csproj b/Client/CoopClient.csproj index 1053733..dcd0cec 100644 --- a/Client/CoopClient.csproj +++ b/Client/CoopClient.csproj @@ -24,6 +24,8 @@ 4 x64 true + Auto + bin\Debug\CoopClient.xml pdbonly @@ -33,18 +35,26 @@ prompt 4 true + Auto + bin\Release\CoopClient.xml - - ..\Libs\Release\LemonUI.SHVDN3.dll + + False + ..\Libs\Release\scripts\LemonUI.SHVDN3.dll - - ..\Libs\Release\Lidgren.Network.dll + + False + ..\Libs\Release\scripts\Lidgren.Network.dll - - ..\packages\protobuf-net.2.4.6\lib\net40\protobuf-net.dll + + False + ..\Libs\Release\scripts\Newtonsoft.Json.dll - + + ..\Libs\Release\scripts\protobuf-net.dll + + False ..\Libs\Release\ScriptHookVDotNet3.dll @@ -65,13 +75,14 @@ - + + @@ -81,8 +92,5 @@ - - - \ No newline at end of file diff --git a/Client/Entities/EntitiesNPC.cs b/Client/Entities/EntitiesNPC.cs index a7a5f14..f230a83 100644 --- a/Client/Entities/EntitiesNPC.cs +++ b/Client/Entities/EntitiesNPC.cs @@ -1,5 +1,8 @@ namespace CoopClient.Entities { + /// + /// ? + /// public class EntitiesNpc : EntitiesPed { //public int LastUpdateReceived { get; set; } diff --git a/Client/Entities/EntitiesPed.cs b/Client/Entities/EntitiesPed.cs index da27a74..7ee034e 100644 --- a/Client/Entities/EntitiesPed.cs +++ b/Client/Entities/EntitiesPed.cs @@ -9,58 +9,157 @@ using GTA.Math; using LemonUI.Elements; -namespace CoopClient +namespace CoopClient.Entities { + /// + /// ? + /// public class EntitiesPed { private bool AllDataAvailable = false; + /// + /// ? + /// public bool LastSyncWasFull { get; set; } = false; + /// + /// ? + /// public ulong LastUpdateReceived { get; set; } + /// + /// ? + /// public float Latency { get; set; } + /// + /// ? + /// public Ped Character { get; set; } + /// + /// ? + /// public int Health { get; set; } private int LastModelHash = 0; + /// + /// ? + /// public int ModelHash { get; set; } private Dictionary LastProps = new Dictionary(); + /// + /// ? + /// public Dictionary Props { get; set; } + /// + /// ? + /// public Vector3 Position { get; set; } #region -- ON FOOT -- + /// + /// ? + /// public Vector3 Rotation { get; set; } + /// + /// ? + /// public Vector3 Velocity { get; set; } + /// + /// ? + /// public byte Speed { get; set; } private bool LastIsJumping = false; + /// + /// ? + /// public bool IsJumping { get; set; } + /// + /// ? + /// public bool IsRagdoll { get; set; } + /// + /// ? + /// public bool IsOnFire { get; set; } + /// + /// ? + /// public Vector3 AimCoords { get; set; } + /// + /// ? + /// public bool IsAiming { get; set; } + /// + /// ? + /// public bool IsShooting { get; set; } + /// + /// ? + /// public bool IsReloading { get; set; } + /// + /// ? + /// public int CurrentWeaponHash { get; set; } #endregion + /// + /// ? + /// public Blip PedBlip; #region -- IN VEHICLE -- private ulong VehicleStopTime { get; set; } + /// + /// ? + /// public bool IsInVehicle { get; set; } + /// + /// ? + /// public int VehicleModelHash { get; set; } private int[] LastVehicleColors = new int[] { 0, 0 }; + /// + /// ? + /// public int[] VehicleColors { get; set; } private Dictionary LastVehicleMods = new Dictionary(); + /// + /// ? + /// public Dictionary VehicleMods { get; set; } + /// + /// ? + /// public bool VehicleDead { get; set; } + /// + /// ? + /// public float VehicleEngineHealth { get; set; } + /// + /// ? + /// public int VehicleSeatIndex { get; set; } + /// + /// ? + /// public Vehicle MainVehicle { get; set; } + /// + /// ? + /// public Vector3 VehiclePosition { get; set; } + /// + /// ? + /// public Quaternion VehicleRotation { get; set; } + /// + /// ? + /// public Vector3 VehicleVelocity { get; set; } private float LastVehicleSpeed { get; set; } private float CurrentVehicleSpeed { get; set; } + /// + /// ? + /// public float VehicleSpeed { set @@ -69,19 +168,44 @@ namespace CoopClient CurrentVehicleSpeed = value; } } + /// + /// ? + /// public float VehicleSteeringAngle { get; set; } + private int LastVehicleAim; + /// + /// ? + /// public bool VehIsEngineRunning { get; set; } + /// + /// ? + /// public float VehRPM { get; set; } + /// + /// ? + /// public bool VehAreLightsOn { get; set; } + /// + /// ? + /// public bool VehAreHighBeamsOn { get; set; } + /// + /// ? + /// public bool VehIsSireneActive { get; set; } private VehicleDoors[] LastVehDoors; + /// + /// ? + /// public VehicleDoors[] VehDoors { get; set; } private int LastVehTires; + /// + /// ? + /// 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 diff --git a/Client/Entities/EntitiesPlayer.cs b/Client/Entities/EntitiesPlayer.cs index d520161..add3c74 100644 --- a/Client/Entities/EntitiesPlayer.cs +++ b/Client/Entities/EntitiesPlayer.cs @@ -1,8 +1,17 @@ namespace CoopClient.Entities { + /// + /// ? + /// public class EntitiesPlayer : EntitiesPed { + /// + /// ? + /// public string SocialClubName { get; set; } + /// + /// ? + /// public string Username { get; set; } = "Player"; } } diff --git a/Client/Entities/EntitiesThread.cs b/Client/Entities/EntitiesThread.cs index c6b4f42..387b6ab 100644 --- a/Client/Entities/EntitiesThread.cs +++ b/Client/Entities/EntitiesThread.cs @@ -6,12 +6,18 @@ using GTA; namespace CoopClient.Entities { + /// + /// Don't use it! + /// public class EntitiesThread : Script { + /// + /// Don't use it! + /// public EntitiesThread() { Tick += OnTick; - Interval = 1000 / 60; + Interval = Util.GetGameMs(); } private void OnTick(object sender, EventArgs e) diff --git a/Client/Main.cs b/Client/Main.cs index 4cb419c..81d7d27 100644 --- a/Client/Main.cs +++ b/Client/Main.cs @@ -12,31 +12,49 @@ using GTA.Native; namespace CoopClient { + /// + /// Don't use it! + /// 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; + /// + /// Don't use it! + /// public static Settings MainSettings = Util.ReadSettings(); + /// + /// Don't use it! + /// public static Networking MainNetworking = new Networking(); #if !NON_INTERACTIVE + /// + /// Don't use it! + /// public static MenusMain MainMenu = new MenusMain(); #endif + /// + /// Don't use it! + /// public static Chat MainChat = new Chat(); - public static long LocalClientID = 0; - public static readonly Dictionary Players = new Dictionary(); - public static readonly Dictionary Npcs = new Dictionary(); + internal static long LocalClientID = 0; + internal static readonly Dictionary Players = new Dictionary(); + internal static readonly Dictionary Npcs = new Dictionary(); + /// + /// Don't use it! + /// 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()) { 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(); diff --git a/Client/Menus/MenusMain.cs b/Client/Menus/MenusMain.cs index f2442a9..61e4527 100644 --- a/Client/Menus/MenusMain.cs +++ b/Client/Menus/MenusMain.cs @@ -5,17 +5,27 @@ using LemonUI.Menus; namespace CoopClient.Menus { + /// + /// Don't use it! + /// 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 + /// + /// Don't use it! + /// public Sub.Settings SubSettings = new Sub.Settings(); + /// + /// Don't use it! + /// + 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 + /// + /// Don't use it! + /// 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(); diff --git a/Client/Menus/Sub/Servers.cs b/Client/Menus/Sub/Servers.cs new file mode 100644 index 0000000..1bc4e26 --- /dev/null +++ b/Client/Menus/Sub/Servers.cs @@ -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; } + } + + /// + /// Don't use it! + /// + 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; + + /// + /// Don't use it! + /// + 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 serverList = null; + try + { + WebClient client = new WebClient(); + string data = client.DownloadString(Main.MainSettings.MasterServer); + serverList = JsonConvert.DeserializeObject>(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}"); + } + }; + } + } + } +} diff --git a/Client/Menus/Sub/Settings.cs b/Client/Menus/Sub/Settings.cs index abb8134..4ef99f0 100644 --- a/Client/Menus/Sub/Settings.cs +++ b/Client/Menus/Sub/Settings.cs @@ -2,9 +2,12 @@ namespace CoopClient.Menus.Sub { + /// + /// Don't use it! + /// 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 + /// + /// Don't use it! + /// 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; diff --git a/Client/Networking.cs b/Client/Networking.cs index a59de80..5cb82fc 100644 --- a/Client/Networking.cs +++ b/Client/Networking.cs @@ -10,17 +10,20 @@ using GTA.Native; namespace CoopClient { + /// + /// Don't use it! + /// 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() diff --git a/Client/Packets.cs b/Client/Packets.cs index 1588dd5..ce45898 100644 --- a/Client/Packets.cs +++ b/Client/Packets.cs @@ -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 } + /// + /// ? + /// [ProtoContract] public struct VehicleDoors { #region CLIENT-ONLY + /// + /// ? + /// public VehicleDoors(float angleRatio = 0f, bool broken = false, bool open = false, bool fullyOpen = false) { AngleRatio = angleRatio; @@ -169,27 +175,39 @@ namespace CoopClient } #endregion + /// + /// ? + /// [ProtoMember(1)] public float AngleRatio { get; set; } + /// + /// ? + /// [ProtoMember(2)] public bool Broken { get; set; } + /// + /// ? + /// [ProtoMember(3)] public bool Open { get; set; } + /// + /// ? + /// [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 VehMods { get; set; } + public int[] VehColors { get; set; } [ProtoMember(15)] - public VehicleDoors[] VehDoors { get; set; } + public Dictionary 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 + /// + /// ? + /// public static class CoopSerializer { + /// + /// ? + /// 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); } + /// + /// ? + /// public static T CDeserialize(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(jsonString); } internal static T Deserialize(this byte[] data) where T : new() diff --git a/Client/PlayerList.cs b/Client/PlayerList.cs index eed62d5..7b02f1c 100644 --- a/Client/PlayerList.cs +++ b/Client/PlayerList.cs @@ -8,12 +8,18 @@ using GTA.Native; namespace CoopClient { + /// + /// Don't use it! + /// 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; } + /// + /// Don't use it! + /// public PlayerList() { Init(); diff --git a/Client/Settings.cs b/Client/Settings.cs index 6ea215f..d56cb91 100644 --- a/Client/Settings.cs +++ b/Client/Settings.cs @@ -1,10 +1,29 @@ namespace CoopClient { + /// + /// Don't use it! + /// public class Settings { + /// + /// Don't use it! + /// public string Username { get; set; } = "Player"; + /// + /// Don't use it! + /// public string LastServerAddress { get; set; } = "127.0.0.1:4499"; + /// + /// Don't use it! + /// + public string MasterServer { get; set; } = "http://gtacoopr.000webhostapp.com/servers.php"; + /// + /// Don't use it! + /// public bool FlipMenu { get; set; } = false; + /// + /// Don't use it! + /// public int StreamedNpc { get; set; } = 10; } } diff --git a/Client/Util.cs b/Client/Util.cs index 0718042..fc3ecbb 100644 --- a/Client/Util.cs +++ b/Client/Util.cs @@ -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(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() 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(); } + /// + /// Only works for players NOT NPCs + /// + /// Vector3 + 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(); } + public static bool IsTurretSeat(this Vehicle veh, int seat) + { + if (!Function.Call(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; diff --git a/Client/WorldThread.cs b/Client/WorldThread.cs index 8a5096a..feddba1 100644 --- a/Client/WorldThread.cs +++ b/Client/WorldThread.cs @@ -6,14 +6,20 @@ using GTA.Native; namespace CoopClient { + /// + /// Don't use it! + /// public class WorldThread : Script { private static bool LastDisableTraffic = false; + /// + /// Don't use it! + /// public WorldThread() { Tick += OnTick; - Interval = 1000 / 60; + Interval = Util.GetGameMs(); 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) { diff --git a/Client/packages.config b/Client/packages.config deleted file mode 100644 index bd81797..0000000 --- a/Client/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/FirstGameMod/FirstGameMode.sln b/FirstGameMod/FirstGameMode.sln deleted file mode 100644 index ce30111..0000000 --- a/FirstGameMod/FirstGameMode.sln +++ /dev/null @@ -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 diff --git a/FirstGameMod/FirstGameMode/Commands.cs b/FirstGameMod/FirstGameMode/Commands.cs deleted file mode 100644 index 03fd7fc..0000000 --- a/FirstGameMod/FirstGameMode/Commands.cs +++ /dev/null @@ -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