using System; using System.Threading; using System.Threading.Tasks; using GTA; using GTA.Math; using GTA.Native; using RageCoop.Core; using RageCoop.Core.Scripting; using static RageCoop.Core.Scripting.CustomEvents; namespace RageCoop.Server.Scripting; /// /// Server-side object controller /// public abstract class ServerObject { /// /// Server that this object belongs to /// internal readonly Server Server; internal Vector3 _pos; internal Vector3 _rot; internal ServerObject(Server server) { Server = server; } /// /// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side. /// public Tuple Handle => new(GetTypeByte(), BitConverter.GetBytes(ID)); /// /// The client that owns this object, null if it's owned by server. /// public Client Owner { get; internal set; } /// /// 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 virtual Vector3 Position { get => _pos; set { _pos = value; Owner.SendNativeCall(Hash.SET_ENTITY_COORDS_NO_OFFSET, Handle, value.X, value.Y, value.Z, 1, 1, 1); } } /// /// Gets or sets this object's rotation /// public virtual Vector3 Rotation { get => _rot; set { _rot = value; Owner.SendNativeCall(Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z, 2, 1); } } private byte GetTypeByte() { switch (this) { case ServerProp _: return T_ID_PROP; case ServerPed _: return T_ID_PED; case ServerVehicle _: return T_ID_VEH; default: throw new NotImplementedException(); } } /// /// Send updated information to clients, would be called automatically. /// /// /// Delete this object /// public virtual void Delete() { Owner?.SendCustomEvent(CustomEventFlags.Queued, CustomEvents.DeleteEntity, Handle); } /// /// Freeze this object, will throw an exception if it's a ServerProp. /// /// /// public virtual void Freeze(bool toggle) { if (GetTypeByte() == 50) throw new InvalidOperationException("Can't freeze or unfreeze static server object"); Owner.SendNativeCall(Hash.FREEZE_ENTITY_POSITION, Handle, toggle); } } /// /// Represents an prop owned by server. /// public class ServerProp : ServerObject { internal ServerProp(Server server) : base(server) { } /// /// Gets or sets this object's position /// public override Vector3 Position { get => _pos; set { _pos = value; Server.API.SendNativeCall(null, Hash.SET_ENTITY_COORDS_NO_OFFSET, Handle, value.X, value.Y, value.Z, 1, 1, 1); } } /// /// Gets or sets this object's rotation /// public override Vector3 Rotation { get => _rot; set { _rot = value; Server.API.SendNativeCall(null, Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z, 2, 1); } } /// /// Delete this prop /// public override void Delete() { Server.API.SendCustomEvent(CustomEventFlags.Queued, null, CustomEvents.DeleteServerProp, ID); Server.API.Entities.RemoveProp(ID); } /// /// Send updated information to clients, would be called automatically. /// internal void Update() { Server.API.Server.BaseScript.SendServerPropsTo(new List { this }); } } /// /// Represents a ped from a client /// public class ServerPed : ServerObject { internal bool _isInvincible; internal ServerPed(Server server) : base(server) { } /// /// Get the ped's last vehicle /// public ServerVehicle LastVehicle { get; internal set; } /// /// Get the attached to this ped. /// public PedBlip AttachedBlip { get; internal set; } /// /// Health /// public int Health { get; internal set; } /// /// Get or set whether this ped is invincible /// public bool IsInvincible { get => _isInvincible; set => Owner.SendNativeCall(Hash.SET_ENTITY_INVINCIBLE, Handle, value); } /// /// Attach a blip to this ped. /// /// public PedBlip AddBlip() { AttachedBlip = new PedBlip(this); AttachedBlip.Update(); return AttachedBlip; } } /// /// Represents a vehicle from a client /// public class ServerVehicle : ServerObject { internal Quaternion _quat; internal ServerVehicle(Server server) : base(server) { } /// /// Gets or sets vehicle rotation /// public override Vector3 Rotation { get => _quat.ToEulerAngles().ToDegree(); set => Owner.SendNativeCall(Hash.SET_ENTITY_ROTATION, Handle, value.X, value.Y, value.Z); } /// /// Get this vehicle's quaternion /// public Quaternion Quaternion { get => _quat; set { _quat = value; Owner.SendNativeCall(Hash.SET_ENTITY_QUATERNION, Handle, value.X, value.Y, value.Z, value.W); } } } /// /// A static blip owned by server. /// public class ServerBlip { private readonly Server Server; private bool _bouncing; internal BlipColor _color; internal string _name = "Beeeeeee"; internal Vector3 _pos; internal int _rot; internal float _scale = 1; internal BlipSprite _sprite = BlipSprite.Standard; internal ServerBlip(Server server) { Server = server; } /// /// Pass this as an argument in CustomEvent or NativeCall to convert this object to handle at client side. /// public Tuple Handle => new(T_ID_BLIP, BitConverter.GetBytes(ID)); /// /// Network ID (not handle!) /// public int ID { get; internal set; } /// /// Color of this blip /// public BlipColor Color { get => _color; set { _color = value; Update(); } } /// /// Sprite of this blip /// public BlipSprite Sprite { get => _sprite; set { _sprite = value; Update(); } } /// /// Scale of this blip /// public float Scale { get => _scale; set { _scale = value; Update(); } } /// /// Position of this blip /// public Vector3 Position { get => _pos; set { _pos = value; Update(); } } /// /// Rotation of this blip /// public int Rotation { get => _rot; set { _rot = value; Update(); } } /// /// Name of this blip /// public string Name { get => _name; set { _name = value; Update(); } } /// /// Delete this blip /// public void Delete() { Server.API.SendCustomEvent(CustomEventFlags.Queued, null, CustomEvents.DeleteServerBlip, ID); Server.Entities.RemoveServerBlip(ID); } 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 List { this }); } } /// /// Represent a blip attached to ped. /// public class PedBlip { private bool _bouncing; internal BlipColor _color; internal float _scale = 1; internal BlipSprite _sprite = BlipSprite.Standard; internal PedBlip(ServerPed ped) { Ped = ped; } /// /// Get the that this blip attached to. /// public ServerPed Ped { get; internal set; } /// /// Color of this blip /// public BlipColor Color { get => _color; set { _color = value; Update(); } } /// /// Sprite of this blip /// public BlipSprite Sprite { get => _sprite; set { _sprite = value; Update(); } } /// /// Scale of this blip /// public float Scale { get => _scale; set { _scale = value; Update(); } } internal void Update() { // 5ms debounce if (!_bouncing) { _bouncing = true; Task.Run(() => { Thread.Sleep(5); DoUpdate(); _bouncing = false; }); } } private void DoUpdate() { Ped.Owner.SendCustomEvent(CustomEventFlags.Queued, CustomEvents.UpdatePedBlip, Ped.Handle, (byte)Color, (ushort)Sprite, Scale); } }