SFJake wrote on Jul 31
st, 2015 at 7:52pm:
It does work perfectly fine if they have bNetTemporary False, but not if its true. I'm not sure why I can't seem to get that.
AFAIR, bNetTemporary = true makes the client-side projectile completely independent from the original server-side projectile immediately after its initialization. That is, there is no any synchronization between them, unless you explicitly provide such a synchronization somehow. After detaching it's even not possible to replicate a reference to the server-side projectile to the client in order to check if the client-side projectile and its prototype are supposed to be the same object.
This is an ugly optimization whose side-effects are often observable even without such mods - sometimes client-side behavior radically differs from server-side behavior.
I know only two possible solutions:
1) add synchronization actors for all projectiles - difficult and costly method;
2) try to apply nearly the same actions on both server and client sides.
The second solution gives a very rough approximation, but I would use it nevertheless, considering the fact that prediction with bNetTemporary is very far from perfect anyway. I hardly get the rationale behind some of your implementation choices such as checks for Instigator, so I'll demonstrate the idea on a different example:
class ProjectilePusher expands Info;
simulated function PostBeginPlay()
{
if (Level.NetMode == NM_Client)
PushProjectiles(Level, Location);
}
static function bool PushProjectiles(LevelInfo Level, vector Pos)
{
const Radius = 750;
const PushFactor = 1.0;
local Projectile P;
local vector Offset, OldVelocity;
local float Speed;
local bool bPushed;
foreach Level.RadiusActors(class'Projectile', P, Radius, Pos)
if (P.Role != ROLE_DumbProxy)
{
Offset = P.Location - Pos;
OldVelocity = P.Velocity;
Speed = VSize(P.Velocity);
P.Velocity += PushFactor * (Radius - VSize(Offset)) * Normal(Offset);
P.Velocity = Normal(P.Velocity) * Speed;
if (VSize(P.Velocity) > 0)
{
P.Acceleration = Normal(P.Velocity) * VSize(P.Acceleration);
P.SetRotation(rotator(P.Velocity));
}
else
P.Velocity = OldVelocity;
bPushed = true;
}
return bPushed;
}
static function Apply(LevelInfo Level, vector Pos)
{
if (PushProjectiles(Level, Pos) && Level.NetMode != NM_Standalone) // push projectiles server-side
Level.Spawn(class'ProjectilePusher',,, Pos); // then do the same client-side
}
defaultproperties
{
bAlwaysRelevant=True
bNetTemporary=True
LifeSpan=1
RemoteRole=ROLE_SimulatedProxy
}
// somewhere in the inventory class...
function Timer()
{
if (Owner != none)
class'ProjectilePusher'.static.Apply(Level, Owner.Location);
}