Server side prop control

This commit is contained in:
Sardelka
2022-07-02 17:14:56 +08:00
parent 335ea2ca38
commit d8ac486984
22 changed files with 392 additions and 112 deletions

View File

@ -65,7 +65,6 @@ namespace RageCoop.Client
{ {
return; return;
} }
if (!_gameLoaded) if (!_gameLoaded)
{ {
GTA.UI.Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true); GTA.UI.Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true);

View File

@ -10,7 +10,6 @@ namespace RageCoop.Client
{ {
static DownloadManager() static DownloadManager()
{ {
Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) => Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) =>
{ {
var fr = new Packets.FileTransferRequest(); var fr = new Packets.FileTransferRequest();
@ -40,7 +39,7 @@ namespace RageCoop.Client
{ {
try try
{ {
Main.Resources.Load(DownloadFolder); Main.Resources.Load(ResourceFolder);
return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.Loaded }; return new Packets.FileTransferResponse() { ID=0, Response=FileResponse.Loaded };
} }
catch(Exception ex) catch(Exception ex)
@ -52,7 +51,7 @@ namespace RageCoop.Client
} }
}); });
} }
public static string DownloadFolder { public static string ResourceFolder {
get { get {
return Path.Combine(Main.Settings.DataDirectory,"Resources", Main.Settings.LastServerAddress.Replace(":", ".")); return Path.Combine(Main.Settings.DataDirectory,"Resources", Main.Settings.LastServerAddress.Replace(":", "."));
} }
@ -60,13 +59,13 @@ namespace RageCoop.Client
private static readonly Dictionary<int, DownloadFile> InProgressDownloads = new Dictionary<int, DownloadFile>(); private static readonly Dictionary<int, DownloadFile> InProgressDownloads = new Dictionary<int, DownloadFile>();
public static bool AddFile(int id, string name, long length) public static bool AddFile(int id, string name, long length)
{ {
Main.Logger.Debug($"Downloading file to {DownloadFolder}\\{name} , id:{id}"); Main.Logger.Debug($"Downloading file to {ResourceFolder}\\{name} , id:{id}");
if (!Directory.Exists(DownloadFolder)) 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}"); Main.Logger.Debug($"File already exists! canceling download:{name}");
return false; return false;
@ -84,7 +83,7 @@ namespace RageCoop.Client
FileID = id, FileID = id,
FileName = name, FileName = name,
FileLength = length, 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; return true;
@ -159,6 +158,10 @@ namespace RageCoop.Client
} }
InProgressDownloads.Clear(); InProgressDownloads.Clear();
} }
foreach (var zip in Directory.GetDirectories(ResourceFolder, "*.zip"))
{
File.Delete(zip);
}
} }
} }

View File

@ -59,7 +59,7 @@ namespace RageCoop.Client
public static void LoadAll() public static void LoadAll()
{ {
string downloadFolder = DownloadManager.DownloadFolder; string downloadFolder = DownloadManager.ResourceFolder;
if (!Directory.Exists(downloadFolder)) if (!Directory.Exists(downloadFolder))
{ {

View File

@ -3,6 +3,7 @@ using Lidgren.Network;
using RageCoop.Core; using RageCoop.Core;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using System.IO;
namespace RageCoop.Client namespace RageCoop.Client
{ {
@ -69,27 +70,34 @@ namespace RageCoop.Client
EntityPool.AddPlayer(); EntityPool.AddPlayer();
Task.Run(() => Task.Run(() =>
{ {
Client = new NetClient(config); try
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()
{ {
PedID = Main.LocalPlayerID, DownloadManager.Cleanup();
Username =username, Client = new NetClient(config);
ModVersion = Main.CurrentVersion, Client.Start();
PassHashEncrypted=Security.Encrypt(password.GetHash()) Main.QueueAction(() => { GTA.UI.Notification.Show($"~y~Trying to connect..."); });
}; Security.Regen();
GetServerPublicKey(address);
Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted,out handshake.AesIVCrypted);
handshake.Pack(outgoingMessage); // Send HandshakePacket
Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage); 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);
}
}); });
} }

View File

@ -328,7 +328,7 @@ namespace RageCoop.Client
{ {
EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID)); EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID));
} }
if (v.IsMine) { return; } if (v.IsLocal) { return; }
v.ID= packet.ID; v.ID= packet.ID;
v.Position=packet.Position; v.Position=packet.Position;
v.Quaternion=packet.Quaternion; v.Quaternion=packet.Quaternion;
@ -343,7 +343,7 @@ namespace RageCoop.Client
private static void VehicleStateSync(Packets.VehicleStateSync packet) private static void VehicleStateSync(Packets.VehicleStateSync packet)
{ {
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID); SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null||v.IsMine) { return; } if (v==null||v.IsLocal) { return; }
v.ID= packet.ID; v.ID= packet.ID;
v.OwnerID= packet.OwnerID; v.OwnerID= packet.OwnerID;
v.DamageModel=packet.DamageModel; v.DamageModel=packet.DamageModel;

View File

@ -1,7 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using GTA.Native; using GTA.Native;
using GTA.Math;
using GTA;
using RageCoop.Core;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using System.Linq;
namespace RageCoop.Client.Scripting namespace RageCoop.Client.Scripting
{ {
@ -11,6 +15,8 @@ namespace RageCoop.Client.Scripting
{ {
API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn,SetAutoRespawn); API.RegisterCustomEventHandler(CustomEvents.SetAutoRespawn,SetAutoRespawn);
API.RegisterCustomEventHandler(CustomEvents.NativeCall,NativeCall); 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.OnPedDeleted+=(s,p) => { API.SendCustomEvent(CustomEvents.OnPedDeleted,p.ID); };
API.Events.OnVehicleDeleted+=(s, p) => { API.SendCustomEvent(CustomEvents.OnVehicleDeleted, 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]; 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) private void NativeCall(CustomEventReceivedArgs e)
{ {
List<InputArgument> arguments = new List<InputArgument>(); List<InputArgument> arguments = new List<InputArgument>();

View File

@ -149,6 +149,7 @@ namespace RageCoop.Client.Scripting
} }
} }
LoadedResources.Add(r); LoadedResources.Add(r);
file.Close();
} }
private bool LoadScriptsFromAssembly(ResourceFile file, string path, ClientResource resource, bool shadowCopy = true) private bool LoadScriptsFromAssembly(ResourceFile file, string path, ClientResource resource, bool shadowCopy = true)
{ {

View File

@ -13,17 +13,18 @@ namespace RageCoop.Client
/// </summary> /// </summary>
public abstract class SyncedEntity public abstract class SyncedEntity
{ {
/// <summary> /// <summary>
/// Indicates whether the current player is responsible for syncing this entity. /// Indicates whether the current player is responsible for syncing this entity.
/// </summary> /// </summary>
public bool IsMine public bool IsLocal
{ {
get get
{ {
return OwnerID==Main.LocalPlayerID; return OwnerID==Main.LocalPlayerID;
} }
} }
/// <summary> /// <summary>
/// Network ID for this entity /// Network ID for this entity
/// </summary> /// </summary>
@ -39,13 +40,14 @@ namespace RageCoop.Client
{ {
get get
{ {
return Main.Ticked-LastSynced>200; return Main.Ticked-LastSynced>200 && ID!=0;
} }
} }
internal bool IsReady internal bool IsReady
{ {
get {return !(LastSynced==0||LastStateSynced==0);} get {return (LastSynced>0||LastStateSynced==0);}
} }
internal bool IsInvincible { get; set; } = false;
internal bool NeedUpdate internal bool NeedUpdate
{ {
get { return LastSynced>LastUpdated; } get { return LastSynced>LastUpdated; }
@ -65,6 +67,12 @@ namespace RageCoop.Client
public ulong LastUpdated { get; set; } = 0; public ulong LastUpdated { get; set; } = 0;
#endregion #endregion
/// <summary>
///
/// </summary>
internal protected bool _lastFrozen=false;
internal bool IsFrozen { get; set; } = false;
internal int ModelHash { get; set; }
internal Vector3 Position { get; set; } internal Vector3 Position { get; set; }
internal Vector3 Rotation { get; set; } internal Vector3 Rotation { get; set; }
internal Quaternion Quaternion { get; set; } internal Quaternion Quaternion { get; set; }

View File

@ -68,10 +68,6 @@ namespace RageCoop.Client
private bool _lastRagdoll=false; private bool _lastRagdoll=false;
private ulong _lastRagdollTime=0; private ulong _lastRagdollTime=0;
private bool _lastInCover = false; private bool _lastInCover = false;
internal int ModelHash
{
get;set;
}
private byte[] _lastClothes = null; private byte[] _lastClothes = null;
internal byte[] Clothes { get; set; } internal byte[] Clothes { get; set; }
@ -284,6 +280,7 @@ namespace RageCoop.Client
MainPed.IsInvincible=true; MainPed.IsInvincible=true;
} }
if (IsInvincible) { MainPed.IsInvincible=true; }
// Add to EntityPool so this Character can be accessed by handle. // Add to EntityPool so this Character can be accessed by handle.
EntityPool.Add(this); EntityPool.Add(this);
@ -325,6 +322,11 @@ namespace RageCoop.Client
private void DisplayOnFoot() private void DisplayOnFoot()
{ {
if (IsFrozen != _lastFrozen)
{
MainPed.SetFrozen(IsFrozen);
_lastFrozen=IsFrozen;
}
if (IsInParachuteFreeFall) if (IsInParachuteFreeFall)
{ {
MainPed.PositionNoOffset = Vector3.Lerp(MainPed.Position, Position + Velocity, 0.5f); MainPed.PositionNoOffset = Vector3.Lerp(MainPed.Position, Position + Velocity, 0.5f);

View File

@ -37,7 +37,7 @@ namespace RageCoop.Client
IsValid=false; IsValid=false;
} }
ShooterID=shooter.ID; ShooterID=shooter.ID;
IsMine=shooter.IsMine; IsMine=shooter.IsLocal;
} }
} }

View File

@ -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
{
/// <summary>
/// Synchronized prop, mostly owned by server
/// </summary>
public class SyncedProp : SyncedEntity
{
internal SyncedProp(int id)
{
ID= id;
}
/// <summary>
/// The real entity
/// </summary>
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;
}
}
}

View File

@ -89,7 +89,6 @@ namespace RageCoop.Client
internal VehicleRoofState RoofState { get; set; } internal VehicleRoofState RoofState { get; set; }
internal bool SireneActive { get; set; } internal bool SireneActive { get; set; }
internal VehicleDamageModel DamageModel { get; set; } internal VehicleDamageModel DamageModel { get; set; }
internal int ModelHash { get; set; }
internal byte[] Colors { get; set; } internal byte[] Colors { get; set; }
internal Dictionary<int, int> Mods { get; set; } internal Dictionary<int, int> Mods { get; set; }
internal bool IsDead { get; set; } internal bool IsDead { get; set; }
@ -133,7 +132,12 @@ namespace RageCoop.Client
{ {
MainVehicle.BrakePower=BrakePower; 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); MainVehicle.Velocity = Velocity+5*(Position+Velocity*SyncParameters.PositioinPredictionDefault - MainVehicle.Position);
if (IsFlipped) if (IsFlipped)
@ -378,6 +382,7 @@ namespace RageCoop.Client
{ {
MainVehicle.RoofState=RoofState; MainVehicle.RoofState=RoofState;
} }
if (IsInvincible) { MainVehicle.IsInvincible=true; }
vehicleModel.MarkAsNoLongerNeeded(); vehicleModel.MarkAsNoLongerNeeded();
} }
#region -- PEDALING -- #region -- PEDALING --

View File

@ -37,6 +37,8 @@ namespace RageCoop.Client
private static Dictionary<int, SyncedProjectile> ID_Projectiles = new Dictionary<int, SyncedProjectile>(); private static Dictionary<int, SyncedProjectile> ID_Projectiles = new Dictionary<int, SyncedProjectile>();
private static Dictionary<int, SyncedProjectile> Handle_Projectiles = new Dictionary<int, SyncedProjectile>(); private static Dictionary<int, SyncedProjectile> Handle_Projectiles = new Dictionary<int, SyncedProjectile>();
public static object PropsLock=new object();
public static Dictionary<int,SyncedProp> ServerProps=new Dictionary<int,SyncedProp>();
public static void Cleanup(bool keepPlayer=true,bool keepMine=true) public static void Cleanup(bool keepPlayer=true,bool keepMine=true)
{ {
@ -66,6 +68,12 @@ namespace RageCoop.Client
} }
ID_Projectiles.Clear(); ID_Projectiles.Clear();
Handle_Projectiles.Clear(); Handle_Projectiles.Clear();
foreach(var p in ServerProps.Values)
{
p?.MainProp?.Delete();
}
ServerProps.Clear();
} }
#region PEDS #region PEDS
@ -139,7 +147,7 @@ namespace RageCoop.Client
{ {
Handle_Peds.Add(c.MainPed.Handle, c); Handle_Peds.Add(c.MainPed.Handle, c);
} }
if (c.IsMine) if (c.IsLocal)
{ {
API.Events.InvokePedSpawned(c); API.Events.InvokePedSpawned(c);
} }
@ -165,7 +173,7 @@ namespace RageCoop.Client
c.PedBlip?.Delete(); c.PedBlip?.Delete();
c.ParachuteProp?.Delete(); c.ParachuteProp?.Delete();
ID_Peds.Remove(id); ID_Peds.Remove(id);
if (c.IsMine) if (c.IsLocal)
{ {
API.Events.InvokePedDeleted(c); API.Events.InvokePedDeleted(c);
} }
@ -205,7 +213,7 @@ namespace RageCoop.Client
{ {
Handle_Vehicles.Add(v.MainVehicle.Handle, v); Handle_Vehicles.Add(v.MainVehicle.Handle, v);
} }
if (v.IsMine) if (v.IsLocal)
{ {
API.Events.InvokeVehicleSpawned(v); API.Events.InvokeVehicleSpawned(v);
} }
@ -228,7 +236,7 @@ namespace RageCoop.Client
veh.Delete(); veh.Delete();
} }
ID_Vehicles.Remove(id); 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 // Outgoing sync
if (c.IsMine) if (c.IsLocal)
{ {
#if BENCHMARK #if BENCHMARK
var start = PerfCounter2.ElapsedTicks; var start = PerfCounter2.ElapsedTicks;
@ -495,7 +503,7 @@ namespace RageCoop.Client
} }
// Outgoing sync // Outgoing sync
if (v.IsMine) if (v.IsLocal)
{ {
SyncEvents.Check(v); SyncEvents.Check(v);
@ -521,6 +529,14 @@ namespace RageCoop.Client
} }
} }
lock (PropsLock)
{
foreach (var p in ServerProps.Values)
{
p.Update();
}
}
#if BENCHMARK #if BENCHMARK
Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks; Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks;
#endif #endif

View File

@ -174,6 +174,10 @@ namespace RageCoop.Client
Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle); 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) public static SyncedPed GetSyncEntity(this Ped p)
{ {

View File

@ -111,7 +111,7 @@ namespace RageCoop.Client
foreach (Ped ped in World.GetAllPeds()) foreach (Ped ped in World.GetAllPeds())
{ {
SyncedPed c = EntityPool.GetPedByHandle(ped.Handle); 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; } if (ped.Handle==Game.Player.Character.Handle) { continue; }
@ -131,7 +131,7 @@ namespace RageCoop.Client
// Don't delete player's vehicle // Don't delete player's vehicle
continue; 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"); Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");

View File

@ -39,7 +39,16 @@ namespace RageCoop.Core
case bool _: case bool _:
return (0x09, BitConverter.GetBytes((bool)obj)); return (0x09, BitConverter.GetBytes((bool)obj));
case string _: 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<byte, byte[]> _:
var tup = (Tuple<byte, byte[]>)obj;
return (tup.Item1, tup.Item2);
default: default:
return (0x0, null); return (0x0, null);
} }
@ -115,7 +124,21 @@ namespace RageCoop.Core
{ {
return Encoding.UTF8.GetString(data); return Encoding.UTF8.GetString(data);
} }
public static byte[] GetBytes(this Vector3 vec)
{
// 12 bytes
return new List<byte[]>() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y), BitConverter.GetBytes(vec.Z) }.Join(4);
}
/// <summary>
///
/// </summary>
/// <param name="qua"></param>
/// <returns>An array of bytes with length 16</returns>
public static byte[] GetBytes(this Quaternion qua)
{
// 16 bytes
return new List<byte[]>() { 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) public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
{ {
@ -223,10 +246,10 @@ namespace RageCoop.Core
return memoryStream.ToArray(); return memoryStream.ToArray();
} }
} }
public static byte[] Join(this List<byte[]> arrays) public static byte[] Join(this List<byte[]> arrays,int lengthPerArray=-1)
{ {
if (arrays.Count==1) { return arrays[0]; } 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; int writeIdx = 0;
foreach (var byteArr in arrays) foreach (var byteArr in arrays)
{ {

View File

@ -121,16 +121,35 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="message"></param>
/// <param name="error"></param>
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();
}
}
/// <summary>
///
/// </summary>
/// <param name="ex"></param> /// <param name="ex"></param>
public void Error(Exception ex) public void Error(Exception ex)
{ {
if (LogLevel>4) { return; } if (LogLevel>4) { return; }
lock (Buffer) lock (Buffer)
{ {
string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.ToString(), Name); string msg = string.Format("[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.Message, Name);
// msg += string.Format("\r\n[{0}][{2}] [ERR] {1}", Date(), "\r\n"+ex.StackTrace, Process.GetCurrentProcess().Id);
Buffer+=msg+"\r\n"; Buffer+=msg+"\r\n";
Trace(ex.ToString());
} }
if (FlushImmediately) if (FlushImmediately)
{ {

View File

@ -49,35 +49,31 @@ namespace RageCoop.Core
switch (type) switch (type)
{ {
case 0x01: case 0x01:
Args.Add(reader.ReadByte()); Args.Add(reader.ReadByte()); break;
break;
case 0x02: case 0x02:
Args.Add(reader.ReadShort()); Args.Add(reader.ReadShort()); break;
break;
case 0x03: case 0x03:
Args.Add(reader.ReadUShort()); Args.Add(reader.ReadUShort()); break;
break;
case 0x04: case 0x04:
Args.Add(reader.ReadInt()); Args.Add(reader.ReadInt()); break;
break;
case 0x05: case 0x05:
Args.Add(reader.ReadUInt()); Args.Add(reader.ReadUInt()); break;
break;
case 0x06: case 0x06:
Args.Add(reader.ReadLong()); Args.Add(reader.ReadLong()); break;
break;
case 0x07: case 0x07:
Args.Add(reader.ReadULong()); Args.Add(reader.ReadULong()); break;
break;
case 0x08: case 0x08:
Args.Add(reader.ReadFloat()); Args.Add(reader.ReadFloat()); break;
break;
case 0x09: case 0x09:
Args.Add(reader.ReadBool()); Args.Add(reader.ReadBool()); break;
break;
case 0x10: case 0x10:
Args.Add(reader.ReadString()); Args.Add(reader.ReadString()); break;
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: default:
throw new InvalidOperationException($"Unexpected type:{type}\r\n{array.Dump()}"); throw new InvalidOperationException($"Unexpected type:{type}\r\n{array.Dump()}");

View File

@ -19,6 +19,9 @@ namespace RageCoop.Core.Scripting
internal static readonly int NativeCall = Hash("RageCoop.NativeCall"); internal static readonly int NativeCall = Hash("RageCoop.NativeCall");
internal static readonly int NativeResponse = Hash("RageCoop.NativeResponse"); internal static readonly int NativeResponse = Hash("RageCoop.NativeResponse");
internal static readonly int AllResourcesSent = Hash("RageCoop.AllResourcesSent"); internal static readonly int AllResourcesSent = Hash("RageCoop.AllResourcesSent");
internal static readonly int ServerPropSync = Hash("RageCoop.ServerPropSync");
internal static readonly int DeleteServerProp = Hash("RageCoop.DeleteServerProp");
/// <summary> /// <summary>
/// Get a Int32 hash of a string. /// Get a Int32 hash of a string.
/// </summary> /// </summary>

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using RageCoop.Core.Scripting; using RageCoop.Core.Scripting;
using RageCoop.Core;
namespace RageCoop.Server.Scripting namespace RageCoop.Server.Scripting
{ {
@ -28,6 +29,13 @@ namespace RageCoop.Server.Scripting
{ {
c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle ); c.SendCustomEvent(CustomEvents.SetAutoRespawn, toggle );
} }
public void SendServerObjectsTo(List<ServerProp> objects,List<Client> 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) void NativeResponse(CustomEventReceivedArgs e)
{ {
try try

View File

@ -637,6 +637,10 @@ namespace RageCoop.Server
}.Pack(outgoingMessage); }.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, newClient.Connection, NetDeliveryMethod.ReliableOrdered, 0); 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 // Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection); var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0) if (cons.Count!=0)

View File

@ -4,12 +4,62 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using RageCoop.Core; using RageCoop.Core;
using RageCoop.Core.Scripting;
using System.Security.Cryptography;
using GTA.Math; using GTA.Math;
using GTA; using GTA;
namespace RageCoop.Server namespace RageCoop.Server
{ {
/// <summary>
/// Represents an prop owned by server.
/// </summary>
public class ServerProp
{
private Server Server;
internal ServerProp(Server server)
{
Server= server;
}
/// <summary>
/// Delete this prop
/// </summary>
public void Delete()
{
Server.API.SendCustomEvent(CustomEvents.DeleteServerProp, new() { ID });
}
/// <summary>
/// Network ID of this object.
/// </summary>
public int ID { get; internal set; }
/// <summary>
/// The object's model
/// </summary>
public Model Model { get; internal set; }
/// <summary>
/// Gets or sets this object's position
/// </summary>
public Vector3 Position
{
get { return _pos; }
set { _pos=value; Server.BaseScript.SendServerObjectsTo(new() { this }); }
}
private Vector3 _pos;
/// <summary>
/// Gets or sets this object's rotation
/// </summary>
public Vector3 Rotation
{
get { return _rot; }
set { _rot=value; Server.BaseScript.SendServerObjectsTo(new() { this }); }
}
private Vector3 _rot;
}
/// <summary> /// <summary>
/// Represents a ped from a client /// Represents a ped from a client
/// </summary> /// </summary>
@ -76,36 +126,6 @@ namespace RageCoop.Server
/// </summary> /// </summary>
public Quaternion Quaternion { get; internal set; } public Quaternion Quaternion { get; internal set; }
} }
/// <summary>
/// Represents an object owned by server.
/// </summary>
public class ServerObject
{
internal ServerObject()
{
}
/// <summary>
/// The object's model
/// </summary>
public Model Model { get; internal set; }
/// <summary>
/// Gets or sets this object's position
/// </summary>
public Vector3 Position { get;set; }
/// <summary>
/// Gets or sets this object's quaternion
/// </summary>
public Quaternion Quaternion { get; set; }
/// <summary>
/// Whether this object is invincible
/// </summary>
public bool IsInvincible { get; set; }
}
/// <summary> /// <summary>
/// Manipulate entities from the server /// Manipulate entities from the server
@ -119,8 +139,79 @@ namespace RageCoop.Server
} }
internal Dictionary<int, ServerPed> Peds { get; set; } = new(); internal Dictionary<int, ServerPed> Peds { get; set; } = new();
internal Dictionary<int, ServerVehicle> Vehicles { get; set; } = new(); internal Dictionary<int, ServerVehicle> Vehicles { get; set; } = new();
internal Dictionary<int,ServerObject> ServerObjects { get; set; }=new(); internal Dictionary<int,ServerProp> ServerProps { get; set; }=new();
/// <summary>
/// Get a <see cref="ServerPed"/> by it's id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public ServerPed GetPedByID(int id)
{
if(Peds.TryGetValue(id,out var ped))
{
return ped;
}
else
{
return null;
}
}
/// <summary>
/// Get a <see cref="ServerVehicle"/> by it's id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public ServerVehicle GetVehicleByID(int id)
{
if (Vehicles.TryGetValue(id, out var veh))
{
return veh;
}
else
{
return null;
}
}
/// <summary>
/// Get a <see cref="ServerProp"/> owned by server from it's ID.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public ServerProp GetPropByID(int id)
{
if (ServerProps.TryGetValue(id, out var obj))
{
return obj;
}
else
{
return null;
}
}
/// <summary>
/// Create a static prop owned by server.
/// </summary>
/// <param name="model"></param>
/// <param name="pos"></param>
/// <param name="rot"></param>
/// <returns></returns>
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;
}
/// <summary> /// <summary>
/// Get all peds on this server /// Get all peds on this server
/// </summary> /// </summary>
@ -143,9 +234,9 @@ namespace RageCoop.Server
/// Get all static objects owned by server /// Get all static objects owned by server
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public ServerObject[] GetAllObjects() public ServerProp[] GetAllObjects()
{ {
return ServerObjects.Values.ToArray(); return ServerProps.Values.ToArray();
} }
/// <summary> /// <summary>
@ -212,7 +303,7 @@ namespace RageCoop.Server
} }
} }
Server.QueueJob(() => 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) internal void RemoveVehicle(int id)
{ {
@ -236,5 +327,22 @@ namespace RageCoop.Server
Peds.Add(ped.ID, ped); 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;
}
} }
} }