diff --git a/RageCoop.Client/Main.cs b/RageCoop.Client/Main.cs index fc85030..f55b159 100644 --- a/RageCoop.Client/Main.cs +++ b/RageCoop.Client/Main.cs @@ -65,7 +65,6 @@ namespace RageCoop.Client { return; } - if (!_gameLoaded) { GTA.UI.Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true); diff --git a/RageCoop.Client/Networking/DownloadManager.cs b/RageCoop.Client/Networking/DownloadManager.cs index 527716f..fddb025 100644 --- a/RageCoop.Client/Networking/DownloadManager.cs +++ b/RageCoop.Client/Networking/DownloadManager.cs @@ -10,7 +10,6 @@ namespace RageCoop.Client { static DownloadManager() { - Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) => { var fr = new Packets.FileTransferRequest(); @@ -40,7 +39,7 @@ namespace RageCoop.Client { try { - Main.Resources.Load(DownloadFolder); + Main.Resources.Load(ResourceFolder); return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.Loaded }; } catch(Exception ex) @@ -52,7 +51,7 @@ namespace RageCoop.Client } }); } - public static string DownloadFolder { + public static string ResourceFolder { get { return Path.Combine(Main.Settings.DataDirectory,"Resources", Main.Settings.LastServerAddress.Replace(":", ".")); } @@ -60,13 +59,13 @@ namespace RageCoop.Client private static readonly Dictionary InProgressDownloads = new Dictionary(); public static bool AddFile(int id, string name, long length) { - Main.Logger.Debug($"Downloading file to {DownloadFolder}\\{name} , id:{id}"); - if (!Directory.Exists(DownloadFolder)) + Main.Logger.Debug($"Downloading file to {ResourceFolder}\\{name} , id:{id}"); + if (!Directory.Exists(ResourceFolder)) { - Directory.CreateDirectory(DownloadFolder); + Directory.CreateDirectory(ResourceFolder); } - if (FileAlreadyExists(DownloadFolder, name, length)) + if (FileAlreadyExists(ResourceFolder, name, length)) { Main.Logger.Debug($"File already exists! canceling download:{name}"); return false; @@ -84,7 +83,7 @@ namespace RageCoop.Client FileID = id, FileName = name, FileLength = length, - Stream = new FileStream($"{DownloadFolder}\\{name}", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite) + Stream = new FileStream($"{ResourceFolder}\\{name}", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite) }); } return true; @@ -159,6 +158,10 @@ namespace RageCoop.Client } InProgressDownloads.Clear(); } + foreach (var zip in Directory.GetDirectories(ResourceFolder, "*.zip")) + { + File.Delete(zip); + } } } diff --git a/RageCoop.Client/Networking/MapLoader.cs b/RageCoop.Client/Networking/MapLoader.cs index d2d4507..62767d0 100644 --- a/RageCoop.Client/Networking/MapLoader.cs +++ b/RageCoop.Client/Networking/MapLoader.cs @@ -59,7 +59,7 @@ namespace RageCoop.Client public static void LoadAll() { - string downloadFolder = DownloadManager.DownloadFolder; + string downloadFolder = DownloadManager.ResourceFolder; if (!Directory.Exists(downloadFolder)) { diff --git a/RageCoop.Client/Networking/Networking.cs b/RageCoop.Client/Networking/Networking.cs index 720fa9e..5a12b5e 100644 --- a/RageCoop.Client/Networking/Networking.cs +++ b/RageCoop.Client/Networking/Networking.cs @@ -3,6 +3,7 @@ using Lidgren.Network; using RageCoop.Core; using System.Threading.Tasks; using System.Threading; +using System.IO; namespace RageCoop.Client { @@ -69,27 +70,34 @@ namespace RageCoop.Client EntityPool.AddPlayer(); Task.Run(() => { - Client = new NetClient(config); - Client.Start(); - Main.QueueAction(() => { GTA.UI.Notification.Show($"~y~Trying to connect..."); }); - DownloadManager.Cleanup(); - Security.Regen(); - GetServerPublicKey(address); - - // Send HandshakePacket - NetOutgoingMessage outgoingMessage = Client.CreateMessage(); - var handshake=new Packets.Handshake() + try { - PedID = Main.LocalPlayerID, - Username =username, - ModVersion = Main.CurrentVersion, - PassHashEncrypted=Security.Encrypt(password.GetHash()) - }; - - Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted,out handshake.AesIVCrypted); - handshake.Pack(outgoingMessage); - Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage); - + DownloadManager.Cleanup(); + Client = new NetClient(config); + Client.Start(); + Main.QueueAction(() => { GTA.UI.Notification.Show($"~y~Trying to connect..."); }); + Security.Regen(); + GetServerPublicKey(address); + + // Send HandshakePacket + NetOutgoingMessage outgoingMessage = Client.CreateMessage(); + var handshake = new Packets.Handshake() + { + PedID = Main.LocalPlayerID, + Username =username, + ModVersion = Main.CurrentVersion, + PassHashEncrypted=Security.Encrypt(password.GetHash()) + }; + + Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted, out handshake.AesIVCrypted); + handshake.Pack(outgoingMessage); + Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage); + + } + catch(Exception ex) + { + Main.Logger.Error("Cannot connect to server", ex); + } }); } diff --git a/RageCoop.Client/Networking/Receive.cs b/RageCoop.Client/Networking/Receive.cs index 1ef650d..dba5186 100644 --- a/RageCoop.Client/Networking/Receive.cs +++ b/RageCoop.Client/Networking/Receive.cs @@ -328,7 +328,7 @@ namespace RageCoop.Client { EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID)); } - if (v.IsMine) { return; } + if (v.IsLocal) { return; } v.ID= packet.ID; v.Position=packet.Position; v.Quaternion=packet.Quaternion; @@ -343,7 +343,7 @@ namespace RageCoop.Client private static void VehicleStateSync(Packets.VehicleStateSync packet) { SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID); - if (v==null||v.IsMine) { return; } + if (v==null||v.IsLocal) { return; } v.ID= packet.ID; v.OwnerID= packet.OwnerID; v.DamageModel=packet.DamageModel; diff --git a/RageCoop.Client/Scripting/BaseScript.cs b/RageCoop.Client/Scripting/BaseScript.cs index 93a34cf..d9afbdc 100644 --- a/RageCoop.Client/Scripting/BaseScript.cs +++ b/RageCoop.Client/Scripting/BaseScript.cs @@ -1,7 +1,11 @@ using System; using System.Collections.Generic; using GTA.Native; +using GTA.Math; +using GTA; +using RageCoop.Core; using RageCoop.Core.Scripting; +using System.Linq; namespace RageCoop.Client.Scripting { @@ -11,6 +15,8 @@ namespace RageCoop.Client.Scripting { API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn,SetAutoRespawn); API.RegisterCustomEventHandler(CustomEvents.NativeCall,NativeCall); + API.RegisterCustomEventHandler(CustomEvents.ServerPropSync, ServerObjectSync); + API.RegisterCustomEventHandler(CustomEvents.DeleteServerProp, DeleteServerProp); API.Events.OnPedDeleted+=(s,p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted,p.ID); }; API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); }; @@ -23,6 +29,29 @@ namespace RageCoop.Client.Scripting { API.Config.EnableAutoRespawn=(bool)args.Args[0]; } + private void DeleteServerProp(CustomEventReceivedArgs e) + { + var id = (int)e.Args[0]; + if (EntityPool.ServerProps.TryGetValue(id, out var prop)) + { + EntityPool.ServerProps.Remove(id); + API.QueueAction(() => { prop?.MainProp?.Delete(); }); + } + } + private void ServerObjectSync(CustomEventReceivedArgs e) + { + SyncedProp prop; + var id = (int)e.Args[0]; + if (!EntityPool.ServerProps.TryGetValue(id,out prop)) + { + EntityPool.ServerProps.Add(id,prop=new SyncedProp(id)); + } + prop.LastSynced=Main.Ticked+1; + prop.ModelHash= (Model)e.Args[1]; + prop.Position=(Vector3)e.Args[2]; + prop.Rotation=(Vector3)e.Args[3]; + Main.Logger.Trace($"{Main.Ticked},{(VehicleHash)prop.ModelHash},{prop.Position},{prop.Rotation}"); + } private void NativeCall(CustomEventReceivedArgs e) { List arguments = new List(); diff --git a/RageCoop.Client/Scripting/Resources.cs b/RageCoop.Client/Scripting/Resources.cs index 1ec7820..a831830 100644 --- a/RageCoop.Client/Scripting/Resources.cs +++ b/RageCoop.Client/Scripting/Resources.cs @@ -149,6 +149,7 @@ namespace RageCoop.Client.Scripting } } LoadedResources.Add(r); + file.Close(); } private bool LoadScriptsFromAssembly(ResourceFile file, string path, ClientResource resource, bool shadowCopy = true) { diff --git a/RageCoop.Client/Sync/Entities/SyncedEntity.cs b/RageCoop.Client/Sync/Entities/SyncedEntity.cs index 8577558..e4bc77a 100644 --- a/RageCoop.Client/Sync/Entities/SyncedEntity.cs +++ b/RageCoop.Client/Sync/Entities/SyncedEntity.cs @@ -13,17 +13,18 @@ namespace RageCoop.Client /// public abstract class SyncedEntity { - + /// /// Indicates whether the current player is responsible for syncing this entity. /// - public bool IsMine + public bool IsLocal { get { return OwnerID==Main.LocalPlayerID; } } + /// /// Network ID for this entity /// @@ -39,13 +40,14 @@ namespace RageCoop.Client { get { - return Main.Ticked-LastSynced>200; + return Main.Ticked-LastSynced>200 && ID!=0; } } internal bool IsReady { - get {return !(LastSynced==0||LastStateSynced==0);} + get {return (LastSynced>0||LastStateSynced==0);} } + internal bool IsInvincible { get; set; } = false; internal bool NeedUpdate { get { return LastSynced>LastUpdated; } @@ -65,6 +67,12 @@ namespace RageCoop.Client public ulong LastUpdated { get; set; } = 0; #endregion + /// + /// + /// + internal protected bool _lastFrozen=false; + internal bool IsFrozen { get; set; } = false; + internal int ModelHash { get; set; } internal Vector3 Position { get; set; } internal Vector3 Rotation { get; set; } internal Quaternion Quaternion { get; set; } diff --git a/RageCoop.Client/Sync/Entities/SyncedPed.cs b/RageCoop.Client/Sync/Entities/SyncedPed.cs index 6a94478..7302a69 100644 --- a/RageCoop.Client/Sync/Entities/SyncedPed.cs +++ b/RageCoop.Client/Sync/Entities/SyncedPed.cs @@ -68,10 +68,6 @@ namespace RageCoop.Client private bool _lastRagdoll=false; private ulong _lastRagdollTime=0; private bool _lastInCover = false; - internal int ModelHash - { - get;set; - } private byte[] _lastClothes = null; internal byte[] Clothes { get; set; } @@ -284,6 +280,7 @@ namespace RageCoop.Client MainPed.IsInvincible=true; } + if (IsInvincible) { MainPed.IsInvincible=true; } // Add to EntityPool so this Character can be accessed by handle. EntityPool.Add(this); @@ -325,6 +322,11 @@ namespace RageCoop.Client private void DisplayOnFoot() { + if (IsFrozen != _lastFrozen) + { + MainPed.SetFrozen(IsFrozen); + _lastFrozen=IsFrozen; + } if (IsInParachuteFreeFall) { MainPed.PositionNoOffset = Vector3.Lerp(MainPed.Position, Position + Velocity, 0.5f); diff --git a/RageCoop.Client/Sync/Entities/SyncedProjectile.cs b/RageCoop.Client/Sync/Entities/SyncedProjectile.cs index 44b326f..d0412e3 100644 --- a/RageCoop.Client/Sync/Entities/SyncedProjectile.cs +++ b/RageCoop.Client/Sync/Entities/SyncedProjectile.cs @@ -37,7 +37,7 @@ namespace RageCoop.Client IsValid=false; } ShooterID=shooter.ID; - IsMine=shooter.IsMine; + IsMine=shooter.IsLocal; } } diff --git a/RageCoop.Client/Sync/Entities/SyncedProp.cs b/RageCoop.Client/Sync/Entities/SyncedProp.cs new file mode 100644 index 0000000..c3b1b2f --- /dev/null +++ b/RageCoop.Client/Sync/Entities/SyncedProp.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using GTA.Native; +using GTA; + +namespace RageCoop.Client +{ + /// + /// Synchronized prop, mostly owned by server + /// + public class SyncedProp : SyncedEntity + { + internal SyncedProp(int id) + { + ID= id; + } + /// + /// The real entity + /// + public Prop MainProp { get; set; } + internal new int OwnerID { get + { + // alwayse owned by server + return 0; + } } + internal override void Update() + { + + if (!NeedUpdate) { return; } + if (MainProp== null || !MainProp.Exists()) + { + MainProp=World.CreateProp(ModelHash,Position,Rotation,false,false); + MainProp.IsInvincible=true; + } + MainProp.Position=Position; + MainProp.Rotation=Rotation; + MainProp.SetFrozen(true); + LastUpdated=Main.Ticked; + } + } +} diff --git a/RageCoop.Client/Sync/Entities/SyncedVehicle.cs b/RageCoop.Client/Sync/Entities/SyncedVehicle.cs index c12a2bc..37625e6 100644 --- a/RageCoop.Client/Sync/Entities/SyncedVehicle.cs +++ b/RageCoop.Client/Sync/Entities/SyncedVehicle.cs @@ -89,7 +89,6 @@ namespace RageCoop.Client internal VehicleRoofState RoofState { get; set; } internal bool SireneActive { get; set; } internal VehicleDamageModel DamageModel { get; set; } - internal int ModelHash { get; set; } internal byte[] Colors { get; set; } internal Dictionary Mods { get; set; } internal bool IsDead { get; set; } @@ -133,7 +132,12 @@ namespace RageCoop.Client { MainVehicle.BrakePower=BrakePower; } - if (MainVehicle.Position.DistanceTo(Position)<5) + if (IsFrozen != _lastFrozen) + { + MainVehicle.SetFrozen(IsFrozen); + _lastFrozen=IsFrozen; + } + else if (MainVehicle.Position.DistanceTo(Position)<5) { MainVehicle.Velocity = Velocity+5*(Position+Velocity*SyncParameters.PositioinPredictionDefault - MainVehicle.Position); if (IsFlipped) @@ -378,6 +382,7 @@ namespace RageCoop.Client { MainVehicle.RoofState=RoofState; } + if (IsInvincible) { MainVehicle.IsInvincible=true; } vehicleModel.MarkAsNoLongerNeeded(); } #region -- PEDALING -- diff --git a/RageCoop.Client/Sync/EntityPool.cs b/RageCoop.Client/Sync/EntityPool.cs index a79bb12..a94aa15 100644 --- a/RageCoop.Client/Sync/EntityPool.cs +++ b/RageCoop.Client/Sync/EntityPool.cs @@ -37,6 +37,8 @@ namespace RageCoop.Client private static Dictionary ID_Projectiles = new Dictionary(); private static Dictionary Handle_Projectiles = new Dictionary(); + public static object PropsLock=new object(); + public static Dictionary ServerProps=new Dictionary(); public static void Cleanup(bool keepPlayer=true,bool keepMine=true) { @@ -66,6 +68,12 @@ namespace RageCoop.Client } ID_Projectiles.Clear(); Handle_Projectiles.Clear(); + + foreach(var p in ServerProps.Values) + { + p?.MainProp?.Delete(); + } + ServerProps.Clear(); } #region PEDS @@ -139,7 +147,7 @@ namespace RageCoop.Client { Handle_Peds.Add(c.MainPed.Handle, c); } - if (c.IsMine) + if (c.IsLocal) { API.Events.InvokePedSpawned(c); } @@ -165,7 +173,7 @@ namespace RageCoop.Client c.PedBlip?.Delete(); c.ParachuteProp?.Delete(); ID_Peds.Remove(id); - if (c.IsMine) + if (c.IsLocal) { API.Events.InvokePedDeleted(c); } @@ -205,7 +213,7 @@ namespace RageCoop.Client { Handle_Vehicles.Add(v.MainVehicle.Handle, v); } - if (v.IsMine) + if (v.IsLocal) { API.Events.InvokeVehicleSpawned(v); } @@ -228,7 +236,7 @@ namespace RageCoop.Client veh.Delete(); } ID_Vehicles.Remove(id); - if (v.IsMine) { API.Events.InvokeVehicleDeleted(v); } + if (v.IsLocal) { API.Events.InvokeVehicleDeleted(v); } } } @@ -417,7 +425,7 @@ namespace RageCoop.Client } // Outgoing sync - if (c.IsMine) + if (c.IsLocal) { #if BENCHMARK var start = PerfCounter2.ElapsedTicks; @@ -495,7 +503,7 @@ namespace RageCoop.Client } // Outgoing sync - if (v.IsMine) + if (v.IsLocal) { SyncEvents.Check(v); @@ -521,6 +529,14 @@ namespace RageCoop.Client } } + + lock (PropsLock) + { + foreach (var p in ServerProps.Values) + { + p.Update(); + } + } #if BENCHMARK Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks; #endif diff --git a/RageCoop.Client/Util/Util.cs b/RageCoop.Client/Util/Util.cs index 1777a1c..0b745b3 100644 --- a/RageCoop.Client/Util/Util.cs +++ b/RageCoop.Client/Util/Util.cs @@ -174,6 +174,10 @@ namespace RageCoop.Client Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle); } } + public static void SetFrozen(this Entity e,bool toggle) + { + Function.Call(Hash.FREEZE_ENTITY_POSITION, e, toggle); + } public static SyncedPed GetSyncEntity(this Ped p) { diff --git a/RageCoop.Client/WorldThread.cs b/RageCoop.Client/WorldThread.cs index bf19308..b244937 100644 --- a/RageCoop.Client/WorldThread.cs +++ b/RageCoop.Client/WorldThread.cs @@ -111,7 +111,7 @@ namespace RageCoop.Client foreach (Ped ped in World.GetAllPeds()) { SyncedPed c = EntityPool.GetPedByHandle(ped.Handle); - if ((c==null) || (c.IsMine && (ped.Handle!=Game.Player.Character.Handle)&&ped.PopulationType!=EntityPopulationType.Mission)) + if ((c==null) || (c.IsLocal && (ped.Handle!=Game.Player.Character.Handle)&&ped.PopulationType!=EntityPopulationType.Mission)) { if (ped.Handle==Game.Player.Character.Handle) { continue; } @@ -131,7 +131,7 @@ namespace RageCoop.Client // Don't delete player's vehicle continue; } - if((v== null) || (v.IsMine&&veh.PopulationType!=EntityPopulationType.Mission)) + if((v== null) || (v.IsLocal&&veh.PopulationType!=EntityPopulationType.Mission)) { Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic"); diff --git a/RageCoop.Core/CoreUtils.cs b/RageCoop.Core/CoreUtils.cs index 0ee47bd..39876d0 100644 --- a/RageCoop.Core/CoreUtils.cs +++ b/RageCoop.Core/CoreUtils.cs @@ -39,7 +39,16 @@ namespace RageCoop.Core case bool _: return (0x09, BitConverter.GetBytes((bool)obj)); case string _: - return (0x10, (obj as string).GetBytesWithLength()); + return (0x10, ((string)obj).GetBytesWithLength()); + case Vector3 _: + return (0x11,((Vector3)obj).GetBytes()); + case Quaternion _: + return (0x12, ((Quaternion)obj).GetBytes()); + case GTA.Model _: + return (0x13, BitConverter.GetBytes((GTA.Model)obj)); + case Tuple _: + var tup = (Tuple)obj; + return (tup.Item1, tup.Item2); default: return (0x0, null); } @@ -115,7 +124,21 @@ namespace RageCoop.Core { return Encoding.UTF8.GetString(data); } - + public static byte[] GetBytes(this Vector3 vec) + { + // 12 bytes + return new List() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y), BitConverter.GetBytes(vec.Z) }.Join(4); + } + /// + /// + /// + /// + /// An array of bytes with length 16 + public static byte[] GetBytes(this Quaternion qua) + { + // 16 bytes + return new List() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4); + } public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag) { @@ -223,10 +246,10 @@ namespace RageCoop.Core return memoryStream.ToArray(); } } - public static byte[] Join(this List arrays) + public static byte[] Join(this List arrays,int lengthPerArray=-1) { if (arrays.Count==1) { return arrays[0]; } - var output = new byte[arrays.Sum(arr => arr.Length)]; + var output = lengthPerArray== -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count*lengthPerArray]; int writeIdx = 0; foreach (var byteArr in arrays) { diff --git a/RageCoop.Core/Logger.cs b/RageCoop.Core/Logger.cs index 006d0aa..322dd03 100644 --- a/RageCoop.Core/Logger.cs +++ b/RageCoop.Core/Logger.cs @@ -121,16 +121,35 @@ namespace RageCoop.Core /// /// /// + /// + /// + public void Error(string message, Exception error) + { + if (LogLevel>4) { return; } + lock (Buffer) + { + string msg = string.Format("[{0}][{2}] [ERR] {1}:{3}", Date(), message, Name,error.Message); + Buffer+=msg+"\r\n"; + Trace(error.ToString()); + + } + if (FlushImmediately) + { + Flush(); + } + } + /// + /// + /// /// public void Error(Exception ex) { if (LogLevel>4) { return; } lock (Buffer) { - string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.ToString(), Name); - // msg += string.Format("\r\n[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.StackTrace, Process.GetCurrentProcess().Id); - + string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.Message, Name); Buffer+=msg+"\r\n"; + Trace(ex.ToString()); } if (FlushImmediately) { diff --git a/RageCoop.Core/Packets/CustomEvent.cs b/RageCoop.Core/Packets/CustomEvent.cs index 3e01027..88fe3ae 100644 --- a/RageCoop.Core/Packets/CustomEvent.cs +++ b/RageCoop.Core/Packets/CustomEvent.cs @@ -49,35 +49,31 @@ namespace RageCoop.Core switch (type) { case 0x01: - Args.Add(reader.ReadByte()); - break; + Args.Add(reader.ReadByte()); break; case 0x02: - Args.Add(reader.ReadShort()); - break; + Args.Add(reader.ReadShort()); break; case 0x03: - Args.Add(reader.ReadUShort()); - break; + Args.Add(reader.ReadUShort()); break; case 0x04: - Args.Add(reader.ReadInt()); - break; + Args.Add(reader.ReadInt()); break; case 0x05: - Args.Add(reader.ReadUInt()); - break; + Args.Add(reader.ReadUInt()); break; case 0x06: - Args.Add(reader.ReadLong()); - break; + Args.Add(reader.ReadLong()); break; case 0x07: - Args.Add(reader.ReadULong()); - break; + Args.Add(reader.ReadULong()); break; case 0x08: - Args.Add(reader.ReadFloat()); - break; + Args.Add(reader.ReadFloat()); break; case 0x09: - Args.Add(reader.ReadBool()); - break; + Args.Add(reader.ReadBool()); break; case 0x10: - Args.Add(reader.ReadString()); - break; + Args.Add(reader.ReadString()); break; + case 0x11: + Args.Add(reader.ReadVector3()); break; + case 0x12: + Args.Add(reader.ReadQuaternion()); break; + case 0x13: + Args.Add((GTA.Model)reader.ReadInt()); break; default: throw new InvalidOperationException($"Unexpected type:{type}\r\n{array.Dump()}"); diff --git a/RageCoop.Core/Scripting/CustomEvents.cs b/RageCoop.Core/Scripting/CustomEvents.cs index f78e45b..8776ec7 100644 --- a/RageCoop.Core/Scripting/CustomEvents.cs +++ b/RageCoop.Core/Scripting/CustomEvents.cs @@ -19,6 +19,9 @@ namespace RageCoop.Core.Scripting internal static readonly int NativeCall = Hash("RageCoop.NativeCall"); internal static readonly int NativeResponse = Hash("RageCoop.NativeResponse"); internal static readonly int AllResourcesSent = Hash("RageCoop.AllResourcesSent"); + internal static readonly int ServerPropSync = Hash("RageCoop.ServerPropSync"); + internal static readonly int DeleteServerProp = Hash("RageCoop.DeleteServerProp"); + /// /// Get a Int32 hash of a string. /// diff --git a/RageCoop.Server/Scripting/BaseScript.cs b/RageCoop.Server/Scripting/BaseScript.cs index cd46632..d08fc91 100644 --- a/RageCoop.Server/Scripting/BaseScript.cs +++ b/RageCoop.Server/Scripting/BaseScript.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using RageCoop.Core.Scripting; +using RageCoop.Core; namespace RageCoop.Server.Scripting { @@ -28,6 +29,13 @@ namespace RageCoop.Server.Scripting { c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle ); } + public void SendServerObjectsTo(List objects,List clients=null) + { + foreach(var obj in objects) + { + API.SendCustomEvent(CustomEvents.ServerPropSync, new() { obj.ID, obj.Model ,obj.Position,obj.Rotation },clients); + } + } void NativeResponse(CustomEventReceivedArgs e) { try diff --git a/RageCoop.Server/Server.cs b/RageCoop.Server/Server.cs index 1f231c5..78fac0f 100644 --- a/RageCoop.Server/Server.cs +++ b/RageCoop.Server/Server.cs @@ -637,6 +637,10 @@ namespace RageCoop.Server }.Pack(outgoingMessage); MainNetServer.SendMessage(outgoingMessage, newClient.Connection, NetDeliveryMethod.ReliableOrdered, 0); }); + + // Send all props to this player + BaseScript.SendServerObjectsTo( new(Entities.ServerProps.Values), new() { newClient}); + // Send new client to all players var cons = MainNetServer.Connections.Exclude(newClient.Connection); if (cons.Count!=0) diff --git a/RageCoop.Server/ServerEntities.cs b/RageCoop.Server/ServerEntities.cs index 6187a24..239bc30 100644 --- a/RageCoop.Server/ServerEntities.cs +++ b/RageCoop.Server/ServerEntities.cs @@ -4,12 +4,62 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using RageCoop.Core; +using RageCoop.Core.Scripting; +using System.Security.Cryptography; using GTA.Math; using GTA; namespace RageCoop.Server { + /// + /// Represents an prop owned by server. + /// + public class ServerProp + { + private Server Server; + internal ServerProp(Server server) + { + Server= server; + } + /// + /// Delete this prop + /// + public void Delete() + { + Server.API.SendCustomEvent(CustomEvents.DeleteServerProp, new() { ID }); + } + + /// + /// Network ID of this object. + /// + public int ID { get; internal set; } + + /// + /// The object's model + /// + public Model Model { get; internal set; } + + /// + /// Gets or sets this object's position + /// + public Vector3 Position + { + get { return _pos; } + set { _pos=value; Server.BaseScript.SendServerObjectsTo(new() { this }); } + } + private Vector3 _pos; + + /// + /// Gets or sets this object's rotation + /// + public Vector3 Rotation + { + get { return _rot; } + set { _rot=value; Server.BaseScript.SendServerObjectsTo(new() { this }); } + } + private Vector3 _rot; + } /// /// Represents a ped from a client /// @@ -76,36 +126,6 @@ namespace RageCoop.Server /// public Quaternion Quaternion { get; internal set; } } - - /// - /// Represents an object owned by server. - /// - public class ServerObject - { - internal ServerObject() - { - - } - /// - /// The object's model - /// - public Model Model { get; internal set; } - - /// - /// Gets or sets this object's position - /// - public Vector3 Position { get;set; } - - /// - /// Gets or sets this object's quaternion - /// - public Quaternion Quaternion { get; set; } - - /// - /// Whether this object is invincible - /// - public bool IsInvincible { get; set; } - } /// /// Manipulate entities from the server @@ -119,8 +139,79 @@ namespace RageCoop.Server } internal Dictionary Peds { get; set; } = new(); internal Dictionary Vehicles { get; set; } = new(); - internal Dictionary ServerObjects { get; set; }=new(); + internal Dictionary ServerProps { get; set; }=new(); + /// + /// Get a by it's id + /// + /// + /// + public ServerPed GetPedByID(int id) + { + if(Peds.TryGetValue(id,out var ped)) + { + return ped; + } + else + { + return null; + } + } + + /// + /// Get a by it's id + /// + /// + /// + public ServerVehicle GetVehicleByID(int id) + { + if (Vehicles.TryGetValue(id, out var veh)) + { + return veh; + } + else + { + return null; + } + } + + /// + /// Get a owned by server from it's ID. + /// + /// + /// + public ServerProp GetPropByID(int id) + { + if (ServerProps.TryGetValue(id, out var obj)) + { + return obj; + } + else + { + return null; + } + } + /// + /// Create a static prop owned by server. + /// + /// + /// + /// + /// + public ServerProp CreateProp(Model model,Vector3 pos,Vector3 rot) + { + int id = RequestID(); + ServerProp prop; + ServerProps.Add(id,prop=new ServerProp(Server) + { + ID=id, + Model=model, + Position=pos, + Rotation=rot + }); + return prop; + } + /// /// Get all peds on this server /// @@ -143,9 +234,9 @@ namespace RageCoop.Server /// Get all static objects owned by server /// /// - public ServerObject[] GetAllObjects() + public ServerProp[] GetAllObjects() { - return ServerObjects.Values.ToArray(); + return ServerProps.Values.ToArray(); } /// @@ -212,7 +303,7 @@ namespace RageCoop.Server } } Server.QueueJob(() => - Server.Logger?.Trace("Remaining entities: "+(Peds.Count+Vehicles.Count))); + Server.Logger?.Trace("Remaining entities: "+(Peds.Count+Vehicles.Count+ServerProps.Count))); } internal void RemoveVehicle(int id) { @@ -236,5 +327,22 @@ namespace RageCoop.Server Peds.Add(ped.ID, ped); } } + internal int RequestID() + { + int ID = 0; + while ((ID==0) + || ServerProps.ContainsKey(ID) + || Peds.ContainsKey(ID) + || Vehicles.ContainsKey(ID)) + { + byte[] rngBytes = new byte[4]; + + RandomNumberGenerator.Create().GetBytes(rngBytes); + + // Convert the bytes into an integer + ID = BitConverter.ToInt32(rngBytes, 0); + } + return ID; + } } }