Big update! Projectile sync, latency and weapon fix

This commit is contained in:
sausage
2022-05-25 10:09:59 +08:00
parent 7854a47569
commit f4e9767184
25 changed files with 546 additions and 501 deletions

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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);
}
}
}
}
}

View File

@ -10,5 +10,7 @@ namespace RageCoop.Client
{
public bool IsMine { get; set; }
public Projectile Projectile { get; set; }
public SyncedPed Owner { get; set; }
}
}

View File

@ -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 = (() =>