using System; using System.Linq; using System.Windows.Forms; using System.Collections.Generic; using System.Drawing; using CoopClient.Entities.Player; using CoopClient.Entities.NPC; using CoopClient.Menus; using GTA; using GTA.Native; namespace CoopClient { /// /// Don't use it! /// public class Main : Script { internal static RelationshipGroup RelationshipGroup; private bool GameLoaded = false; internal static readonly string CurrentVersion = "V1_3_0"; internal static bool ShareNPCsWithPlayers = false; internal static bool DisableTraffic = false; internal static bool NPCsAllowed = false; private static bool IsGoingToCar = false; internal static Settings MainSettings = null; internal static Networking MainNetworking = null; #if !NON_INTERACTIVE internal static MenusMain MainMenu = null; #endif internal static Chat MainChat = null; internal static long LocalNetHandle = 0; internal static Dictionary Players = null; internal static Dictionary NPCs = null; internal static Dictionary NPCsVehicles = null; /// /// Don't use it! /// public Main() { // Required for some synchronization! if (Game.Version < GameVersion.v1_0_1290_1_Steam) { Tick += (object sender, EventArgs e) => { if (Game.IsLoading) { return; } if (!GameLoaded) { GTA.UI.Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true); GameLoaded = true; } }; return; } MainSettings = Util.ReadSettings(); MainNetworking = new Networking(); #if !NON_INTERACTIVE MainMenu = new MenusMain(); #endif MainChat = new Chat(); Players = new Dictionary(); NPCs = new Dictionary(); NPCsVehicles = new Dictionary(); Tick += OnTick; KeyDown += OnKeyDown; Aborted += (object sender, EventArgs e) => CleanUp(); Util.NativeMemory(); } #if DEBUG private ulong LastDebugData; private int DebugBytesSend; private int DebugBytesReceived; #endif private void OnTick(object sender, EventArgs e) { if (Game.IsLoading) { return; } else if (!GameLoaded && (GameLoaded = true)) { RelationshipGroup = World.AddRelationshipGroup("SYNCPED"); Game.Player.Character.RelationshipGroup.SetRelationshipBetweenGroups(RelationshipGroup, Relationship.Neutral, true); #if !NON_INTERACTIVE GTA.UI.Notification.Show(GTA.UI.NotificationIcon.AllPlayersConf, "GTACOOP:R", "Welcome!", "Press ~g~F9~s~ to open the menu."); #endif } #if !NON_INTERACTIVE MainMenu.MenuPool.Process(); #endif MainNetworking.ReceiveMessages(); if (IsGoingToCar && Game.Player.Character.IsInVehicle()) { IsGoingToCar = false; } if (!MainNetworking.IsOnServer()) { return; } #if DEBUG if (MainNetworking.ShowNetworkInfo) { ulong time = Util.GetTickCount64(); if (time - LastDebugData > 1000) { LastDebugData = time; DebugBytesReceived = MainNetworking.BytesReceived; MainNetworking.BytesReceived = 0; DebugBytesSend = MainNetworking.BytesSend; MainNetworking.BytesSend = 0; } new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0), $"L: {MainNetworking.Latency * 1000:N0}ms", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw(); new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {Lidgren.Network.NetUtility.ToHumanReadable(DebugBytesReceived)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw(); new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {Lidgren.Network.NetUtility.ToHumanReadable(DebugBytesSend)}/s", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw(); } #endif MainChat.Tick(); #if DEBUG if (UseDebug) { Debug(); } #endif // Display all players foreach (KeyValuePair player in Players) { player.Value.DisplayLocally(player.Value.Username); } MainNetworking.SendPlayerData(); } #if !NON_INTERACTIVE private void OnKeyDown(object sender, KeyEventArgs e) { if (MainChat.Focused) { MainChat.OnKeyDown(e.KeyCode); return; } switch (e.KeyCode) { case Keys.F9: if (MainMenu.MenuPool.AreAnyVisible) { MainMenu.MainMenu.Visible = false; MainMenu.SubSettings.MainMenu.Visible = false; } else { MainMenu.MainMenu.Visible = true; } break; case Keys.G: if (IsGoingToCar) { Game.Player.Character.Task.ClearAll(); IsGoingToCar = false; } else if (!Game.Player.Character.IsInVehicle()) { Vehicle veh = World.GetNearbyVehicles(Game.Player.Character, 5f).FirstOrDefault(); if (veh != null) { for (int i = 0; i < veh.PassengerCapacity; i++) { if (veh.IsSeatFree((VehicleSeat)i)) { Game.Player.Character.Task.EnterVehicle(veh, (VehicleSeat)i); IsGoingToCar = true; break; } } } } break; default: if (Game.IsControlJustPressed(GTA.Control.MultiplayerInfo)) { if (MainNetworking.IsOnServer()) { ulong currentTimestamp = Util.GetTickCount64(); PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp; } return; } else if (Game.IsControlJustPressed(GTA.Control.MpTextChatAll)) { if (MainNetworking.IsOnServer()) { MainChat.Focused = true; } return; } break; } } #else private void OnKeyDown(object sender, KeyEventArgs e) { if (MainChat.Focused) { MainChat.OnKeyDown(e.KeyCode); return; } if (Game.IsControlJustPressed(GTA.Control.MultiplayerInfo)) { if (MainNetworking.IsOnServer()) { ulong currentTimestamp = Util.GetTickCount64(); PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp; } return; } else if (Game.IsControlJustPressed(GTA.Control.MpTextChatAll)) { if (MainNetworking.IsOnServer()) { MainChat.Focused = true; } return; } } #endif internal static void CleanUp() { MainChat.Clear(); foreach (KeyValuePair player in Players) { player.Value.Character?.AttachedBlip?.Delete(); player.Value.Character?.CurrentVehicle?.Delete(); player.Value.Character?.Kill(); player.Value.Character?.Delete(); player.Value.PedBlip?.Delete(); player.Value.ParachuteProp?.Delete(); } Players.Clear(); foreach (KeyValuePair npc in NPCs) { npc.Value.Character?.CurrentVehicle?.Delete(); npc.Value.Character?.Kill(); npc.Value.Character?.Delete(); } NPCs.Clear(); NPCsVehicles.Clear(); } internal static readonly Dictionary CheckNativeHash = new Dictionary() { { 0xD49F9B0955C367DE, 1 }, { 0xEF29A16337FACADB, 1 }, { 0xB4AC7D0CF06BFE8F, 1 }, { 0x9B62392B474F44A0, 1 }, { 0x7DD959874C1FD534, 1 }, { 0xAF35D0D2583051B0, 2 }, { 0x63C6CCA8E68AE8C8, 2 }, { 0x509D5878EB39E842, 3 }, { 0x9A294B2138ABB884, 3 }, { 0x46818D79B1F7499A, 4 }, { 0x5CDE92C702A8FCE7, 4 }, { 0xBE339365C863BD36, 4 }, { 0x5A039BB0BCA604B6, 4 }, { 0x0134F0835AB6BFCB, 5 } }; internal static Dictionary ServerItems = new Dictionary(); internal static void CleanUpWorld() { if (ServerItems.Count == 0) { return; } lock (ServerItems) { foreach (KeyValuePair item in ServerItems) { try { switch (item.Value) { case 1: World.GetAllEntities().FirstOrDefault(x => x.Handle == item.Key)?.Delete(); break; case 2: World.GetAllVehicles().FirstOrDefault(x => x.Handle == item.Key)?.Delete(); break; case 3: World.GetAllProps().FirstOrDefault(x => x.Handle == item.Key)?.Delete(); break; case 4: Blip blip = new Blip(item.Key); if (blip.Exists()) { blip.Delete(); } break; case 5: Checkpoint checkpoint = new Checkpoint(item.Key); if (checkpoint.Exists()) { checkpoint.Delete(); } break; } } catch { GTA.UI.Notification.Show($"CleanUpWorld(): ~r~Item {item.Value} cannot be deleted!"); } } ServerItems.Clear(); } } #if DEBUG private ulong ArtificialLagCounter; internal static EntitiesPlayer DebugSyncPed; internal static ulong LastFullDebugSync = 0; internal static bool UseDebug = false; private void Debug() { Ped player = Game.Player.Character; if (!Players.ContainsKey(0)) { Players.Add(0, new EntitiesPlayer() { Username = "DebugPlayer" }); DebugSyncPed = Players[0]; } if ((Util.GetTickCount64() - ArtificialLagCounter) < 274) { return; } bool fullSync = (Util.GetTickCount64() - LastFullDebugSync) > 500; if (fullSync) { DebugSyncPed.ModelHash = player.Model.Hash; DebugSyncPed.Clothes = player.GetPedClothes(); } DebugSyncPed.Health = player.Health; DebugSyncPed.Position = player.Position; ushort? flags; if (player.IsInVehicle()) { Vehicle veh = player.CurrentVehicle; veh.Opacity = 75; flags = player.GetVehicleFlags(veh); byte secondaryColor; byte primaryColor; unsafe { Function.Call(Hash.GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor); } DebugSyncPed.VehicleModelHash = veh.Model.Hash; DebugSyncPed.VehicleSeatIndex = (short)player.SeatIndex; DebugSyncPed.Position = veh.Position; DebugSyncPed.VehicleRotation = veh.Quaternion; DebugSyncPed.VehicleEngineHealth = veh.EngineHealth; DebugSyncPed.VehRPM = veh.CurrentRPM; 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 byte[] { primaryColor, secondaryColor }; DebugSyncPed.VehicleMods = veh.Mods.GetVehicleMods(); DebugSyncPed.VehDamageModel = veh.GetVehicleDamageModel(); DebugSyncPed.LastSyncWasFull = true; DebugSyncPed.IsInVehicle = true; DebugSyncPed.VehIsEngineRunning = (flags.Value & (ushort)VehicleDataFlags.IsEngineRunning) > 0; DebugSyncPed.VehAreLightsOn = (flags.Value & (ushort)VehicleDataFlags.AreLightsOn) > 0; DebugSyncPed.VehAreBrakeLightsOn = (flags.Value & (ushort)VehicleDataFlags.AreBrakeLightsOn) > 0; DebugSyncPed.VehAreHighBeamsOn = (flags.Value & (ushort)VehicleDataFlags.AreHighBeamsOn) > 0; DebugSyncPed.VehIsSireneActive = (flags.Value & (ushort)VehicleDataFlags.IsSirenActive) > 0; DebugSyncPed.VehicleDead = (flags.Value & (ushort)VehicleDataFlags.IsDead) > 0; DebugSyncPed.IsHornActive = (flags.Value & (ushort)VehicleDataFlags.IsHornActive) > 0; DebugSyncPed.Transformed = (flags.Value & (ushort)VehicleDataFlags.IsTransformed) > 0; DebugSyncPed.VehRoofOpened = (flags.Value & (ushort)VehicleDataFlags.RoofOpened) > 0; DebugSyncPed.VehLandingGear = veh.IsPlane ? (byte)veh.LandingGearState : (byte)0; if (DebugSyncPed.MainVehicle != null && DebugSyncPed.MainVehicle.Exists() && player.IsInVehicle()) { Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, DebugSyncPed.MainVehicle.Handle, veh.Handle, false); Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, veh.Handle, DebugSyncPed.MainVehicle.Handle, false); } } else { flags = player.GetPedFlags(true); DebugSyncPed.Rotation = player.Rotation; DebugSyncPed.Velocity = player.Velocity; DebugSyncPed.Speed = player.GetPedSpeed(); DebugSyncPed.AimCoords = player.GetPedAimCoords(false); DebugSyncPed.CurrentWeaponHash = (uint)player.Weapons.Current.Hash; DebugSyncPed.WeaponComponents = player.Weapons.Current.GetWeaponComponents(); DebugSyncPed.LastSyncWasFull = true; DebugSyncPed.IsAiming = (flags.Value & (ushort)PedDataFlags.IsAiming) > 0; DebugSyncPed.IsShooting = (flags.Value & (ushort)PedDataFlags.IsShooting) > 0; DebugSyncPed.IsReloading = (flags.Value & (ushort)PedDataFlags.IsReloading) > 0; DebugSyncPed.IsJumping = (flags.Value & (ushort)PedDataFlags.IsJumping) > 0; DebugSyncPed.IsRagdoll = (flags.Value & (ushort)PedDataFlags.IsRagdoll) > 0; DebugSyncPed.IsOnFire = (flags.Value & (ushort)PedDataFlags.IsOnFire) > 0; DebugSyncPed.IsInParachuteFreeFall = (flags.Value & (ushort)PedDataFlags.IsInParachuteFreeFall) > 0; DebugSyncPed.IsParachuteOpen = (flags.Value & (ushort)PedDataFlags.IsParachuteOpen) > 0; DebugSyncPed.IsOnLadder = (flags.Value & (ushort)PedDataFlags.IsOnLadder) > 0; DebugSyncPed.IsVaulting = (flags.Value & (ushort)PedDataFlags.IsVaulting) > 0; DebugSyncPed.IsInVehicle = false; if (DebugSyncPed.Character != null && DebugSyncPed.Character.Exists()) { Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, DebugSyncPed.Character.Handle, player.Handle, false); Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, player.Handle, DebugSyncPed.Character.Handle, false); } } ulong currentTimestamp = Util.GetTickCount64(); DebugSyncPed.LastUpdateReceived = currentTimestamp; DebugSyncPed.Latency = (currentTimestamp - ArtificialLagCounter) / 1000f; ArtificialLagCounter = currentTimestamp; if (fullSync) { LastFullDebugSync = currentTimestamp; } } #endif } }