Big update! Projectile sync, latency and weapon fix
This commit is contained in:
@ -24,15 +24,7 @@ namespace RageCoop.Client
|
||||
/// <param name="p"></param>
|
||||
public SyncedPed(Ped p)
|
||||
{
|
||||
while((ID==0) || (EntityPool.GetPedByID(ID)!=null))
|
||||
{
|
||||
byte[] rngBytes = new byte[4];
|
||||
|
||||
RandomNumberGenerator.Create().GetBytes(rngBytes);
|
||||
|
||||
// Convert the bytes into an integer
|
||||
ID = BitConverter.ToInt32(rngBytes,0);
|
||||
}
|
||||
ID=EntityPool.RequestNewID();
|
||||
p.CanWrithe=false;
|
||||
p.IsOnlyDamagedByPlayer=false;
|
||||
MainPed=p;
|
||||
@ -143,7 +135,8 @@ namespace RageCoop.Client
|
||||
{
|
||||
SetClothes();
|
||||
}
|
||||
|
||||
|
||||
CheckCurrentWeapon();
|
||||
}
|
||||
|
||||
|
||||
@ -500,7 +493,6 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
CheckCurrentWeapon();
|
||||
|
||||
if (IsReloading)
|
||||
{
|
||||
@ -576,27 +568,25 @@ namespace RageCoop.Client
|
||||
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents))
|
||||
{
|
||||
MainPed.Weapons.RemoveAll();
|
||||
|
||||
_lastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
|
||||
|
||||
if (CurrentWeaponHash != (uint)WeaponHash.Unarmed)
|
||||
{
|
||||
if (WeaponComponents == null || WeaponComponents.Count == 0)
|
||||
if (WeaponComponents != null && WeaponComponents.Count != 0)
|
||||
{
|
||||
MainPed.Weapons.Give((WeaponHash)CurrentWeaponHash, 0, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
|
||||
|
||||
foreach (KeyValuePair<uint, bool> comp in WeaponComponents)
|
||||
{
|
||||
|
||||
if (comp.Value)
|
||||
{
|
||||
Function.Call(Hash.GIVE_WEAPON_COMPONENT_TO_WEAPON_OBJECT, _lastWeaponObj, comp.Key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _lastWeaponObj, MainPed.Handle);
|
||||
}
|
||||
Function.Call(Hash.GIVE_WEAPON_OBJECT_TO_PED, _lastWeaponObj, MainPed.Handle);
|
||||
|
||||
}
|
||||
|
||||
_lastWeaponComponents = WeaponComponents;
|
||||
|
@ -7,74 +7,56 @@ using GTA;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal class SyncedProjectile:SyncedEntity
|
||||
internal class SyncedProjectile : SyncedEntity
|
||||
{
|
||||
public SyncedProjectile(Projectile p)
|
||||
{
|
||||
ID=EntityPool.RequestNewID();
|
||||
IsMine=true;
|
||||
MainProjectile = p;
|
||||
ShooterID=p.Owner.GetSyncEntity().ID;
|
||||
}
|
||||
public SyncedProjectile(int id)
|
||||
{
|
||||
ID= id;
|
||||
IsMine=false;
|
||||
}
|
||||
public new bool IsMine { get; private set; }
|
||||
public bool Exploded { get; set; } = false;
|
||||
public Projectile MainProjectile { get; set; }
|
||||
public int ShooterID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invalid property for projectile.
|
||||
/// </summary>
|
||||
private new int OwnerID{ set { } }
|
||||
public WeaponHash Hash { get; set; }
|
||||
private WeaponAsset Asset { get; set; }
|
||||
private bool _creatingProjectile{ get;set; }=false;
|
||||
private ulong _projectileShotTime { get;set; }
|
||||
public override void Update()
|
||||
{
|
||||
// Check if all data avalible
|
||||
if (!IsReady) { return; }
|
||||
|
||||
// Skip update if no new sync message has arrived.
|
||||
if (!NeedUpdate)
|
||||
{ return; }
|
||||
|
||||
if (_creatingProjectile) { return; }
|
||||
if (!NeedUpdate){ return; }
|
||||
|
||||
if (MainProjectile == null || !MainProjectile.Exists())
|
||||
{
|
||||
CreateProjectile();
|
||||
return;
|
||||
}
|
||||
MainProjectile.Position=Position+Velocity*Networking.Latency;
|
||||
MainProjectile.PositionNoOffset=Position+Velocity*Networking.Latency;
|
||||
MainProjectile.Velocity=Velocity;
|
||||
MainProjectile.Rotation=Rotation;
|
||||
if (Exploded)
|
||||
{
|
||||
if (Exploded)
|
||||
{
|
||||
if(MainProjectile != null && MainProjectile.Exists())
|
||||
{
|
||||
MainProjectile.Explode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
LastUpdated=Main.Ticked;
|
||||
}
|
||||
|
||||
private void CreateProjectile()
|
||||
{
|
||||
Asset=new WeaponAsset(Hash);
|
||||
if (!Asset.IsLoaded) { Asset.Request(); }
|
||||
World.ShootBullet(Position,Position+Velocity,EntityPool.GetPedByID(ShooterID)?.MainPed,Asset,0,Velocity.Length());
|
||||
_projectileShotTime=Main.Ticked;
|
||||
_creatingProjectile=true;
|
||||
|
||||
EventHandler<ProjectileShotEventArgs> checker = null;
|
||||
checker= (sender, e) =>
|
||||
{
|
||||
if (Main.Ticked<=_projectileShotTime+1)
|
||||
{
|
||||
if (e.Projectile.WeaponHash==Hash)
|
||||
{
|
||||
MainProjectile=e.Projectile;
|
||||
_creatingProjectile=false;
|
||||
SyncEvents.OnProjectileShot-=checker;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_creatingProjectile=false;
|
||||
SyncEvents.OnProjectileShot-=checker;
|
||||
}
|
||||
};
|
||||
SyncEvents.OnProjectileShot+=checker;
|
||||
World.ShootBullet(Position,Position+Velocity,EntityPool.GetPedByID(ShooterID)?.MainPed,Asset,0);
|
||||
var ps = World.GetAllProjectiles();
|
||||
MainProjectile=ps[ps.Length-1];
|
||||
EntityPool.Add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,15 +22,8 @@ namespace RageCoop.Client
|
||||
/// <param name="p"></param>
|
||||
public SyncedVehicle(Vehicle v)
|
||||
{
|
||||
while ((ID==0) || EntityPool.Exists(ID))
|
||||
{
|
||||
byte[] rngBytes = new byte[4];
|
||||
|
||||
RandomNumberGenerator.Create().GetBytes(rngBytes);
|
||||
|
||||
// Convert the bytes into an integer
|
||||
ID = BitConverter.ToInt32(rngBytes, 0);
|
||||
}
|
||||
ID=EntityPool.RequestNewID();
|
||||
MainVehicle=v;
|
||||
MainVehicle.CanPretendOccupants=false;
|
||||
OwnerID=Main.LocalPlayerID;
|
||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
@ -26,6 +27,11 @@ namespace RageCoop.Client
|
||||
private static Dictionary<int, SyncedVehicle> ID_Vehicles = new Dictionary<int, SyncedVehicle>();
|
||||
private static Dictionary<int, SyncedVehicle> Handle_Vehicles = new Dictionary<int, SyncedVehicle>();
|
||||
|
||||
public static object ProjectilesLock = new object();
|
||||
private static Dictionary<int, SyncedProjectile> ID_Projectiles = new Dictionary<int, SyncedProjectile>();
|
||||
private static Dictionary<int, SyncedProjectile> Handle_Projectiles = new Dictionary<int, SyncedProjectile>();
|
||||
|
||||
|
||||
public static void Cleanup(bool keepPlayer=true,bool keepMine=true)
|
||||
{
|
||||
foreach(int id in new List<int>(ID_Peds.Keys))
|
||||
@ -44,6 +50,9 @@ namespace RageCoop.Client
|
||||
}
|
||||
ID_Vehicles.Clear();
|
||||
Handle_Vehicles.Clear();
|
||||
|
||||
ID_Projectiles.Clear();
|
||||
Handle_Projectiles.Clear();
|
||||
}
|
||||
public static SyncedPed GetPedByID(int id)
|
||||
{
|
||||
@ -193,22 +202,111 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static bool Exists(int id)
|
||||
public static SyncedProjectile GetProjectileByID(int id)
|
||||
{
|
||||
return ID_Peds.ContainsKey(id) || ID_Vehicles.ContainsKey(id);
|
||||
return ID_Projectiles.ContainsKey(id) ? ID_Projectiles[id] : null;
|
||||
}
|
||||
public static void Add(SyncedProjectile p)
|
||||
{
|
||||
if (ID_Projectiles.ContainsKey(p.ID))
|
||||
{
|
||||
ID_Projectiles[p.ID]=p;
|
||||
}
|
||||
else
|
||||
{
|
||||
ID_Projectiles.Add(p.ID, p);
|
||||
}
|
||||
if (p.MainProjectile==null) { return; }
|
||||
if (Handle_Projectiles.ContainsKey(p.MainProjectile.Handle))
|
||||
{
|
||||
Handle_Projectiles[p.MainProjectile.Handle]=p;
|
||||
}
|
||||
else
|
||||
{
|
||||
Handle_Projectiles.Add(p.MainProjectile.Handle, p);
|
||||
}
|
||||
}
|
||||
public static void RemoveProjectile(int id, string reason)
|
||||
{
|
||||
if (ID_Projectiles.ContainsKey(id))
|
||||
{
|
||||
SyncedProjectile sp = ID_Projectiles[id];
|
||||
var p = sp.MainProjectile;
|
||||
if (p!=null)
|
||||
{
|
||||
if (Handle_Projectiles.ContainsKey(p.Handle))
|
||||
{
|
||||
Handle_Projectiles.Remove(p.Handle);
|
||||
}
|
||||
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
|
||||
p.Explode();
|
||||
}
|
||||
ID_Projectiles.Remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool PedExists(int id)
|
||||
{
|
||||
return ID_Peds.ContainsKey(id);
|
||||
}
|
||||
public static bool VehicleExists(int id)
|
||||
{
|
||||
return ID_Vehicles.ContainsKey(id);
|
||||
}
|
||||
public static bool ProjectileExists(int id)
|
||||
{
|
||||
return ID_Projectiles.ContainsKey(id);
|
||||
}
|
||||
public static void DoSync()
|
||||
{
|
||||
PerfCounter.Restart();
|
||||
SyncEvents.CheckProjectiles();
|
||||
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
|
||||
var allPeds = World.GetAllPeds();
|
||||
var allVehicles=World.GetAllVehicles();
|
||||
var allProjectiles=World.GetAllProjectiles();
|
||||
|
||||
if (Main.Ticked%100==0) { if (allVehicles.Length>50) { SetBudget(0); } else { SetBudget(1); } }
|
||||
Debug.TimeStamps[TimeStamp.GetAllEntities]=PerfCounter.ElapsedTicks;
|
||||
lock (ProjectilesLock)
|
||||
{
|
||||
|
||||
lock (EntityPool.PedsLock)
|
||||
foreach (Projectile p in allProjectiles)
|
||||
{
|
||||
if (!Handle_Projectiles.ContainsKey(p.Handle))
|
||||
{
|
||||
Add(new SyncedProjectile(p));
|
||||
Main.Logger.Debug($"Projectile shot: {p.Handle}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SyncedProjectile p in ID_Projectiles.Values.ToArray())
|
||||
{
|
||||
|
||||
// Outgoing sync
|
||||
if (p.IsMine)
|
||||
{
|
||||
if (p.MainProjectile.AttachedEntity==null)
|
||||
{
|
||||
Networking.SendProjectile(p);
|
||||
}
|
||||
}
|
||||
else // Incoming sync
|
||||
{
|
||||
if (p.Exploded || p.IsOutOfSync)
|
||||
{
|
||||
RemoveProjectile(p.ID, "OutOfSync | Exploded");
|
||||
}
|
||||
else
|
||||
{
|
||||
p.Update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
lock (PedsLock)
|
||||
{
|
||||
EntityPool.AddPlayer();
|
||||
|
||||
@ -255,16 +353,10 @@ namespace RageCoop.Client
|
||||
}
|
||||
else // Incoming sync
|
||||
{
|
||||
|
||||
|
||||
c.Update();
|
||||
if (c.IsOutOfSync)
|
||||
{
|
||||
try
|
||||
{
|
||||
EntityPool.RemovePed(c.ID,"OutOfSync");
|
||||
}
|
||||
catch { }
|
||||
RemovePed(c.ID, "OutOfSync");
|
||||
}
|
||||
|
||||
}
|
||||
@ -272,13 +364,12 @@ namespace RageCoop.Client
|
||||
Debug.TimeStamps[TimeStamp.PedTotal]=PerfCounter.ElapsedTicks;
|
||||
|
||||
}
|
||||
lock (EntityPool.VehiclesLock)
|
||||
lock (VehiclesLock)
|
||||
{
|
||||
|
||||
foreach (Vehicle veh in allVehicles)
|
||||
{
|
||||
SyncedVehicle v = EntityPool.GetVehicleByHandle(veh.Handle);
|
||||
if (v==null)
|
||||
if (!Handle_Vehicles.ContainsKey(veh.Handle))
|
||||
{
|
||||
Main.Logger.Debug($"Creating SyncEntity for vehicle, handle:{veh.Handle}");
|
||||
|
||||
@ -318,11 +409,7 @@ namespace RageCoop.Client
|
||||
v.Update();
|
||||
if (v.IsOutOfSync)
|
||||
{
|
||||
try
|
||||
{
|
||||
EntityPool.RemoveVehicle(v.ID, "OutOfSync");
|
||||
}
|
||||
catch { }
|
||||
RemoveVehicle(v.ID, "OutOfSync");
|
||||
}
|
||||
|
||||
}
|
||||
@ -335,6 +422,8 @@ namespace RageCoop.Client
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void RemoveAllFromPlayer(int playerPedId)
|
||||
{
|
||||
foreach(SyncedPed p in ID_Peds.Values.ToArray())
|
||||
@ -352,6 +441,24 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int RequestNewID()
|
||||
{
|
||||
int ID=0;
|
||||
while ((ID==0)
|
||||
|| ID_Peds.ContainsKey(ID)
|
||||
|| ID_Vehicles.ContainsKey(ID)
|
||||
|| ID_Projectiles.ContainsKey(ID))
|
||||
{
|
||||
byte[] rngBytes = new byte[4];
|
||||
|
||||
RandomNumberGenerator.Create().GetBytes(rngBytes);
|
||||
|
||||
// Convert the bytes into an integer
|
||||
ID = BitConverter.ToInt32(rngBytes, 0);
|
||||
}
|
||||
return ID;
|
||||
}
|
||||
private static void SetBudget(int b)
|
||||
{
|
||||
Function.Call(Hash.SET_PED_POPULATION_BUDGET, b); // 0 - 3
|
||||
@ -366,5 +473,29 @@ namespace RageCoop.Client
|
||||
s+="\nHandle_Vehicles: "+Handle_Vehicles.Count;
|
||||
return s;
|
||||
}
|
||||
public static class ThreadSafe
|
||||
{
|
||||
public static void Add(SyncedVehicle v)
|
||||
{
|
||||
lock (EntityPool.VehiclesLock)
|
||||
{
|
||||
EntityPool.Add(v);
|
||||
}
|
||||
}
|
||||
public static void Add(SyncedPed p)
|
||||
{
|
||||
lock (EntityPool.PedsLock)
|
||||
{
|
||||
EntityPool.Add(p);
|
||||
}
|
||||
}
|
||||
public static void Add(SyncedProjectile sp)
|
||||
{
|
||||
lock (EntityPool.ProjectilesLock)
|
||||
{
|
||||
EntityPool.Add(sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,5 +10,7 @@ namespace RageCoop.Client
|
||||
{
|
||||
public bool IsMine { get; set; }
|
||||
public Projectile Projectile { get; set; }
|
||||
|
||||
public SyncedPed Owner { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -12,17 +12,7 @@ using System.Threading;
|
||||
namespace RageCoop.Client {
|
||||
internal static class SyncEvents
|
||||
{
|
||||
public static event EventHandler<ProjectileShotEventArgs> OnProjectileShot;
|
||||
|
||||
static SyncEvents() {
|
||||
OnProjectileShot+=(s, e) =>
|
||||
{
|
||||
if (e.IsMine)
|
||||
{
|
||||
TriggerProjectileShot(e.Projectile);
|
||||
}
|
||||
};
|
||||
}
|
||||
#region TRIGGER
|
||||
public static void TriggerPedKilled(SyncedPed victim)
|
||||
{
|
||||
@ -69,6 +59,8 @@ namespace RageCoop.Client {
|
||||
|
||||
public static void TriggerBulletShot(uint hash,SyncedPed owner,Vector3 impactPosition)
|
||||
{
|
||||
Main.Logger.Trace($"bullet shot:{(WeaponHash)hash}");
|
||||
|
||||
// Minigun, not working for some reason
|
||||
if (hash==(uint)WeaponHash.Minigun)
|
||||
{
|
||||
@ -100,17 +92,6 @@ namespace RageCoop.Client {
|
||||
|
||||
}
|
||||
|
||||
public static void TriggerProjectileShot(Projectile p)
|
||||
{
|
||||
var pp = p.Owner;
|
||||
if (pp.IsShooting)
|
||||
{
|
||||
|
||||
var start = p.Position;
|
||||
var end = start+p.Velocity;
|
||||
Networking.SendBulletShot(start, end, (uint)p.WeaponHash, pp.GetSyncEntity().ID);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region HANDLE
|
||||
@ -135,8 +116,16 @@ namespace RageCoop.Client {
|
||||
public static void HandleEnteredVehicle(int pedId, int vehId, VehicleSeat seat)
|
||||
{
|
||||
var v = EntityPool.GetVehicleByID(vehId);
|
||||
if (v==null) { return; }
|
||||
EntityPool.GetPedByID(pedId)?.MainPed?.SetIntoVehicle(v.MainVehicle, seat);
|
||||
var p = EntityPool.GetPedByID(pedId)?.MainPed;
|
||||
if (v==null||p==null) { return; }
|
||||
if (!v.MainVehicle.IsSeatFree(seat))
|
||||
{
|
||||
if (v.MainVehicle.GetPedOnSeat(seat)!=p)
|
||||
{
|
||||
v.MainVehicle.GetPedOnSeat(seat).Task.WarpOutOfVehicle(v.MainVehicle);
|
||||
}
|
||||
}
|
||||
p.SetIntoVehicle(v.MainVehicle, seat);
|
||||
}
|
||||
public static void HandleOwnerChanged(Packets.OwnerChanged p)
|
||||
{
|
||||
@ -269,46 +258,11 @@ namespace RageCoop.Client {
|
||||
*/
|
||||
#region CHECK EVENTS
|
||||
|
||||
static bool _projectileShot = false;
|
||||
static Projectile[] lastProjectiles=new Projectile[0];
|
||||
public static void CheckProjectiles()
|
||||
{
|
||||
Projectile[] projectiles = World.GetAllProjectiles();
|
||||
|
||||
_projectileShot=false;
|
||||
foreach (Projectile p in projectiles)
|
||||
{
|
||||
var owner = p.Owner?.GetSyncEntity();
|
||||
if (!lastProjectiles.Contains(p))
|
||||
{
|
||||
if((owner!=null) && owner.IsMine)
|
||||
{
|
||||
_projectileShot=true;
|
||||
OnProjectileShot?.Invoke(null, new ProjectileShotEventArgs()
|
||||
{
|
||||
Projectile=p,
|
||||
IsMine=true
|
||||
}) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnProjectileShot?.Invoke(null, new ProjectileShotEventArgs()
|
||||
{
|
||||
Projectile=p,
|
||||
IsMine=false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lastProjectiles=projectiles;
|
||||
}
|
||||
|
||||
public static void Check(SyncedPed c)
|
||||
{
|
||||
Ped subject = c.MainPed;
|
||||
if (subject.IsShooting && !_projectileShot)
|
||||
if (subject.IsShooting && !subject.IsUsingProjectileWeapon())
|
||||
{
|
||||
int i = 0;
|
||||
Func<bool> getBulletImpact = (() =>
|
||||
|
Reference in New Issue
Block a user