diff --git a/RageCoop.Client/Scripting/BaseScript.cs b/RageCoop.Client/Scripting/BaseScript.cs index d21544e..b910ecd 100644 --- a/RageCoop.Client/Scripting/BaseScript.cs +++ b/RageCoop.Client/Scripting/BaseScript.cs @@ -20,9 +20,46 @@ namespace RageCoop.Client.Scripting API.RegisterCustomEventHandler(CustomEvents.DeleteEntity, DeleteEntity); API.RegisterCustomEventHandler(CustomEvents.SetDisplayNameTag, SetNameTag); API.RegisterCustomEventHandler(CustomEvents.SetEntity, SetEntity); + API.RegisterCustomEventHandler(CustomEvents.ServerBlipSync, ServerBlipSync); + API.RegisterCustomEventHandler(CustomEvents.DeleteServerBlip, DeleteServerBlip); API.Events.OnPedDeleted+=(s,p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted,p.ID); }; API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, p.ID); }; + } + private void DeleteServerBlip(CustomEventReceivedArgs e) + { + if (EntityPool.ServerBlips.TryGetValue((int)e.Args[0], out var blip)) + { + EntityPool.ServerBlips.Remove((int)e.Args[0]); + API.QueueAction(()=>{ + blip?.Delete(); + }); + } + } + + private void ServerBlipSync(CustomEventReceivedArgs obj) + { + int id= (int)obj.Args[0]; + var sprite=(BlipSprite)(short)obj.Args[1]; + var color = (BlipColor)(byte)obj.Args[2]; + var scale=(Vector2)obj.Args[3]; + var pos=(Vector3)obj.Args[4]; + int rot= (int)obj.Args[5]; + Blip blip; + API.QueueAction(() => + { + Main.Logger.Debug($"{sprite},{color},{scale},{pos},{rot}"); + if (!EntityPool.ServerBlips.TryGetValue(id, out blip)) + { + EntityPool.ServerBlips.Add(id, blip=World.CreateBlip(pos)); + } + blip.Sprite = sprite; + blip.Color = color; + blip.ScaleX = scale.X; + blip.ScaleY = scale.Y; + blip.Position = pos; + blip.Rotation = rot; + }); } private void SetEntity(CustomEventReceivedArgs obj) diff --git a/RageCoop.Client/Sync/Entities/SyncedPed.cs b/RageCoop.Client/Sync/Entities/SyncedPed.cs index d0e3b4c..5e833d8 100644 --- a/RageCoop.Client/Sync/Entities/SyncedPed.cs +++ b/RageCoop.Client/Sync/Entities/SyncedPed.cs @@ -93,12 +93,32 @@ namespace RageCoop.Client } RenderNameTag(); } + + // Check if all data avalible + if (!IsReady) { return; } + + // Skip update if no new sync message has arrived. + if (!NeedUpdate) + { + return; + } + + + bool characterExist = (MainPed != null) && MainPed.Exists(); + + if (!characterExist) + { + CreateCharacter(); + return; + } + + if (((byte)BlipColor==255) && (PedBlip!=null)) { PedBlip.Delete(); PedBlip=null; } - else if(PedBlip==null) + else if (PedBlip==null) { PedBlip=MainPed.AddBlip(); if (IsPlayer) @@ -119,24 +139,6 @@ namespace RageCoop.Client PedBlip.Color=BlipColor; } - // Check if all data avalible - if (!IsReady) { return; } - - // Skip update if no new sync message has arrived. - if (!NeedUpdate) - { - return; - } - - - bool characterExist = (MainPed != null) && MainPed.Exists(); - - if (!characterExist) - { - CreateCharacter(); - return; - } - // Need to update state diff --git a/RageCoop.Client/Sync/EntityPool.cs b/RageCoop.Client/Sync/EntityPool.cs index e150622..291450b 100644 --- a/RageCoop.Client/Sync/EntityPool.cs +++ b/RageCoop.Client/Sync/EntityPool.cs @@ -40,6 +40,10 @@ namespace RageCoop.Client public static object PropsLock=new object(); public static Dictionary ServerProps=new Dictionary(); + public static object BlipsLock = new object(); + public static Dictionary ServerBlips = new Dictionary(); + + public static void Cleanup(bool keepPlayer=true,bool keepMine=true) { foreach(int id in new List(ID_Peds.Keys)) @@ -74,6 +78,15 @@ namespace RageCoop.Client p?.MainProp?.Delete(); } ServerProps.Clear(); + + foreach(var b in ServerBlips.Values) + { + if (b.Exists()) + { + b.Delete(); + } + } + ServerBlips.Clear(); } #region PEDS diff --git a/RageCoop.Core/BitReader.cs b/RageCoop.Core/BitReader.cs index e9d7e65..13dcea3 100644 --- a/RageCoop.Core/BitReader.cs +++ b/RageCoop.Core/BitReader.cs @@ -123,6 +123,14 @@ namespace RageCoop.Core Z = ReadFloat() }; } + public Vector2 ReadVector2() + { + return new Vector2() + { + X = ReadFloat(), + Y = ReadFloat() + }; + } public Quaternion ReadQuaternion() { return new Quaternion() diff --git a/RageCoop.Core/CoreUtils.cs b/RageCoop.Core/CoreUtils.cs index 39876d0..3347cbe 100644 --- a/RageCoop.Core/CoreUtils.cs +++ b/RageCoop.Core/CoreUtils.cs @@ -46,6 +46,8 @@ namespace RageCoop.Core return (0x12, ((Quaternion)obj).GetBytes()); case GTA.Model _: return (0x13, BitConverter.GetBytes((GTA.Model)obj)); + case Vector2 _: + return (0x14, ((Vector2)obj).GetBytes()); case Tuple _: var tup = (Tuple)obj; return (tup.Item1, tup.Item2); @@ -129,6 +131,13 @@ namespace RageCoop.Core // 12 bytes return new List() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y), BitConverter.GetBytes(vec.Z) }.Join(4); } + + public static byte[] GetBytes(this Vector2 vec) + { + // 8 bytes + return new List() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y) }.Join(4); + } + /// /// /// diff --git a/RageCoop.Core/Packets/CustomEvent.cs b/RageCoop.Core/Packets/CustomEvent.cs index 81c6670..8e710ec 100644 --- a/RageCoop.Core/Packets/CustomEvent.cs +++ b/RageCoop.Core/Packets/CustomEvent.cs @@ -79,6 +79,8 @@ namespace RageCoop.Core Args.Add(reader.ReadQuaternion()); break; case 0x13: Args.Add((GTA.Model)reader.ReadInt()); break; + case 0x14: + Args.Add(reader.ReadVector2()); break; default: if (_resolve==null) { diff --git a/RageCoop.Core/Scripting/CustomEvents.cs b/RageCoop.Core/Scripting/CustomEvents.cs index 18b0241..6296dd8 100644 --- a/RageCoop.Core/Scripting/CustomEvents.cs +++ b/RageCoop.Core/Scripting/CustomEvents.cs @@ -21,9 +21,11 @@ namespace RageCoop.Core.Scripting 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 ServerBlipSync = Hash("RageCoop.ServerBlipSync"); internal static readonly int SetEntity = Hash("RageCoop.SetEntity"); internal static readonly int DeleteServerProp = Hash("RageCoop.DeleteServerProp"); internal static readonly int DeleteEntity = Hash("RageCoop.DeleteEntity"); + internal static readonly int DeleteServerBlip = Hash("RageCoop.DeleteServerBlip"); /// /// Get a Int32 hash of a string. /// diff --git a/RageCoop.Server/Scripting/BaseScript.cs b/RageCoop.Server/Scripting/BaseScript.cs index 45664fe..455c13f 100644 --- a/RageCoop.Server/Scripting/BaseScript.cs +++ b/RageCoop.Server/Scripting/BaseScript.cs @@ -44,6 +44,13 @@ namespace RageCoop.Server.Scripting API.SendCustomEvent(CustomEvents.ServerPropSync, new() { obj.ID, obj.Model ,obj.Position,obj.Rotation },clients); } } + public void SendServerBlipsTo(List objects, List clients = null) + { + foreach (var obj in objects) + { + API.SendCustomEvent(CustomEvents.ServerBlipSync, new() { obj.ID, (short)obj.Sprite, (byte)obj.Color, obj.Scale,obj.Position,obj.Rotation }, clients); + } + } void NativeResponse(CustomEventReceivedArgs e) { try diff --git a/RageCoop.Server/Server.cs b/RageCoop.Server/Server.cs index 43703ff..a461742 100644 --- a/RageCoop.Server/Server.cs +++ b/RageCoop.Server/Server.cs @@ -637,7 +637,10 @@ namespace RageCoop.Server // Send all props to this player BaseScript.SendServerPropsTo( new(Entities.ServerProps.Values), new() { newClient}); - + + // Send all blips to this player + BaseScript.SendServerBlipsTo(new(Entities.Blips.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 65b4265..d98edf7 100644 --- a/RageCoop.Server/ServerEntities.cs +++ b/RageCoop.Server/ServerEntities.cs @@ -25,6 +25,7 @@ namespace RageCoop.Server internal Dictionary Peds { get; set; } = new(); internal Dictionary Vehicles { get; set; } = new(); internal Dictionary ServerProps { get; set; }=new(); + internal Dictionary Blips { get; set; } = new(); /// /// Get a by it's id @@ -76,6 +77,24 @@ namespace RageCoop.Server return null; } } + + /// + /// Get a by it's id. + /// + /// + /// + public ServerBlip GetBlipByID(int id) + { + if (Blips.TryGetValue(id, out var obj)) + { + return obj; + } + else + { + return null; + } + } + /// /// Create a static prop owned by server. /// @@ -85,18 +104,38 @@ namespace RageCoop.Server /// public ServerProp CreateProp(Model model,Vector3 pos,Vector3 rot) { - int id = RequestID(); + int id = RequestNetworkID(); ServerProp prop; ServerProps.Add(id,prop=new ServerProp(Server) { ID=id, Model=model, - Position=pos, - Rotation=rot + _pos=pos, + _rot=rot }); + prop.Update(); return prop; } + /// + /// Create a static owned by server. + /// + /// + /// + /// + public ServerBlip CreateBlip(Vector3 pos,int rotation) + { + var b = new ServerBlip(Server) + { + ID=RequestNetworkID(), + Position=pos, + Rotation=rotation + }; + Blips.Add(b.ID,b); + b.Update(); + return b; + } + /// /// Get all peds on this server /// @@ -116,13 +155,22 @@ namespace RageCoop.Server } /// - /// Get all static objects owned by server + /// Get all static prop objects owned by server /// /// public ServerProp[] GetAllProps() { return ServerProps.Values.ToArray(); - } + } + + /// + /// Get all static objects owned by server + /// + /// + public ServerBlip[] GetAllBlips() + { + return Blips.Values.ToArray(); + } /// /// Not thread safe @@ -196,6 +244,17 @@ namespace RageCoop.Server // Server.Logger?.Trace($"Removing vehicle:{id}"); if (Vehicles.ContainsKey(id)) { Vehicles.Remove(id); } } + + internal void RemoveProp(int id) + { + // Server.Logger?.Trace($"Removing vehicle:{id}"); + if (ServerProps.ContainsKey(id)) { ServerProps.Remove(id); } + } + internal void RemoveServerBlip(int id) + { + // Server.Logger?.Trace($"Removing vehicle:{id}"); + if (Blips.ContainsKey(id)) { Blips.Remove(id); } + } internal void RemovePed(int id) { // Server.Logger?.Trace($"Removing ped:{id}"); @@ -213,13 +272,14 @@ namespace RageCoop.Server Peds.Add(ped.ID, ped); } } - internal int RequestID() + internal int RequestNetworkID() { int ID = 0; while ((ID==0) || ServerProps.ContainsKey(ID) || Peds.ContainsKey(ID) - || Vehicles.ContainsKey(ID)) + || Vehicles.ContainsKey(ID) + || Blips.ContainsKey(ID)) { byte[] rngBytes = new byte[4]; diff --git a/RageCoop.Server/ServerObject.cs b/RageCoop.Server/ServerObject.cs index 6b01424..8510c36 100644 --- a/RageCoop.Server/ServerObject.cs +++ b/RageCoop.Server/ServerObject.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using GTA; using GTA.Native; @@ -17,6 +18,8 @@ namespace RageCoop.Server /// public abstract class ServerObject { + internal ServerObject() { } + /// /// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side. /// @@ -79,7 +82,7 @@ namespace RageCoop.Server internal Vector3 _rot; /// - /// Send updated information to clients + /// Send updated information to clients, would be called automatically. /// public virtual void Update() { Owner.SendCustomEvent(CustomEvents.SetEntity, Handle, Position, Rotation); @@ -130,13 +133,14 @@ namespace RageCoop.Server public override void Delete() { Server.API.SendCustomEvent(CustomEvents.DeleteServerProp, new() { ID }); + Server.Entities.RemoveProp(ID); } /// - /// Send updated information to clients + /// Send updated information to clients, would be called automatically. /// public override void Update() { @@ -188,12 +192,102 @@ namespace RageCoop.Server /// public Quaternion Quaternion { get; internal set; } } - internal class ServerBlip - { - internal ServerBlip() - { + /// + /// A static blip owned by server. + /// + public class ServerBlip + { + private readonly Server Server; + internal ServerBlip(Server server) + { + Server = server; } + /// + /// Network ID (not handle!) + /// + public int ID { get; internal set; } + + + internal BlipColor _color; + /// + /// Color of this blip + /// + public BlipColor Color { + get { return _color; } + set { _color=value; Update(); } + } + + internal BlipSprite _sprite; + /// + /// Sprite of this blip + /// + public BlipSprite Sprite { + get { return _sprite; } + set { _sprite=value; Update();} + } + + internal Vector2 _scale=new(1f,1f); + /// + /// Scale of this blip + /// + public Vector2 Scale + { + get { return _scale; } + set { _scale=value;Update(); } + } + + internal Vector3 _pos = new(); + /// + /// Position of this blip + /// + public Vector3 Position + { + get { return _pos; } + set { _pos=value; Update(); } + } + + internal int _rot; + /// + /// Scale of this blip + /// + public int Rotation + { + get { return _rot; } + set { _rot=value; Update(); } + } + + /// + /// Delete this blip + /// + public void Delete() + { + Server.API.SendCustomEvent(CustomEvents.DeleteServerBlip, new() { ID }); + Server.Entities.RemoveServerBlip(ID); + } + + private bool _bouncing=false; + internal void Update() + { + // 5ms debounce + if (!_bouncing) + { + _bouncing=true; + Task.Run(() => + { + Thread.Sleep(5); + DoUpdate(); + _bouncing=false; + }); + } + } + private void DoUpdate() + { + Server.Logger?.Debug("bee"); + // Serve-side blip + Server.BaseScript.SendServerBlipsTo(new() { this }); + + } } }