UE1 - UT Problem with Replication

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
Ugh.

Just when you think you have replication figured out, something else comes and bites you in the ass!

I'm having troubles spawning a mutator clientside. I don't see what I'm doing wrong! I have followed the Wiki's instructions and set bAlwaysRelevant and bNetTemporary to True, as well as RemoteRole to ROLE_SimulatedProxy. It simply won't work! I had this problem with my JetBoots mutator and somehow it randomly fixed itself. I still don't know what happened there... Here's the wiki link anyway. http://wiki.beyondunreal.com/Legacy:Linked_List/Existing_Lists_In_Unreal_Tournament#HUD_Mutators_.3D

For the record, the mutator I'm working on is a simple one, which fixes the purple UDamage HUD problem. Here's the code:

PHP:
//=============================================================================
// UDamageFixMut.
//=============================================================================
class UDamageFixMut extends Mutator;

replication
{
    unreliable if( Role==ROLE_Authority && Level.NetMode!=NM_Standalone )
        ReplicateDamageScaling;
}

function PostBeginPlay()
{
    log("Entered PostBeginPlay");
    SetTimer(1,True);
    if ( NextMutator != None )
        NextMutator.PostBeginPlay();
}

function AddMutator( Mutator M )
{
    if ( M != Self )
    Super.AddMutator(M);
}

function Timer()
{
    local Pawn P;

    log("Entered Timer");
    for( P = Level.PawnList; P != None; P = P.NextPawn )
    {
        log("Entered loop");
        if( P.IsA('Bot') || P.IsA('PlayerPawn') )
        {
            log("Pawn Condition Passed");
            ReplicateDamageScaling(P, P.DamageScaling);
        }
    }
}

simulated function ReplicateDamageScaling( Pawn P, float Scaling )
{
    log("Entered ReplicateDamageScaling");
    P.DamageScaling = Scaling;
}

DefaultProperties
{
     Role=ROLE_Authority
     bAlwaysRelevant=True
}
Any help on the matter would be greatly appreciated :)

P.S.
I removed bNetTemporary=True because my JetBoots mutator spawns clientside fine without it.
 
Last edited:

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
Mutators are not replicated by default. You need to give your mutator a RemoteRole other than their default ROLE_None and make it bAlwaysRelevant.

Also, function replication is not broadcast and only works if the actor is being owned by a client. Mutators are never owned by anyone.

And finally: Only follow the mutator chain when the implementation in the base class does so. Ideally you don't call NextMutator.Whatever(), but let Super.Whatever() handle the mutator chain.

[edit]
See UnrealWiki: Useful Mutator Functions for an example that makes a mutator available on the client (HUD mutator)
 
Last edited:

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
Firstly, thanks for the instant reply. Coming from Australia, that's among the fastest replies I've ever received!

Mutators are not replicated by default. You need to give your mutator a RemoteRole other than their default ROLE_None and make it bAlwaysRelevant.
That's obviously what I've done, like I said in my post. But you probably knew that and said it just for the sake of archives. ;)

Also, function replication is not broadcast and only works if the actor is being owned by a client. Mutators are never owned by anyone.
So you're saying I need to spawn another actor from the mutator and let that actor do all the client stuff? Define "owned". :confused: And, what you're saying about mutators is not consistent with my JetBoots mutator, which spawned clientside fine.

Oh - on that note, here it is! http://www.box.net/shared/b13cqgaw4c I finally got the JetBoots to the stage of a playable beta.

And finally: Only follow the mutator chain when the implementation in the base class does so. Ideally you don't call NextMutator.Whatever(), but let Super.Whatever() handle the mutator chain.
Sound advice.
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
Actually you didn't set the RemoteRole of your Mutator. In your posted defaults you have "Role=ROLE_Authority", which is redundant, because that's the default and MUST NOT be changed in defaults. Ever!
Depending on whether your mutator should use Tick/Timer events or state code clientsidely, you set RemoteRole=ROLE_DumbProxy or ROLE_SimulatedProxy.

Definition "Owned by client":
Take the linked list created by the Actor.Owner property starting at the actor whose property "owned by client" you want to check. You can iterate over that list like you do over the linked list of a Pawn's Inventory or the Mutators list. If at any point while iterating over that "Owner" list you find a PlayerPawn (for later UE generations: PlayerController) that belongs to a client you can stop there and have found the client owning the actor.

Function replication on an actor can only ever reach the client found by this method. Since mutators are never owned by any other actor, you can't replicate functions in a mutator.
For your purpose I recommend spawning a separate actor for each Pawn e.g. in Mutator.ModifyPlayer(), which is owned by that Pawn. Before you spawn that actor, make sure the Pawn doesn't already have an instance of that actor. The type of actor to spawn depends on whether you want the value to be available on all clients or just on the client of the Pawn. Server to all clients replication is best done through a ReplicationInfo subclass, while replication to owning client is probably best done through an Inventory.
 

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
Oops! OK I wasn't thinking. To be honest, I actually copied the code in from UEd and added the DefaultProperties manually here. So, that "Role=ROLE_Authority" is actually "RemoteRole=ROLE_SimulatedProxy". I know that's right, because I changed that default property via it's default properties window in UEd, and we all know that uncategorised variables, of which Role is one, are not accessible through that method.

So you'll have to excuse that blunder of mine - it was a force of habit. You have no idea how many times I've typed "Role == ROLE_Authority" over the last week :p

Now I have an idea of what I could do. Call AddInventory() from within the mutator to give an inventory to each Bot/Player in the pawn list. The Inventory will be the actor that executes the above code. That should work!
 
Last edited:

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
OK, now this is really frustrating. The mutator spawns the inventory and sets the owner as the player via GiveTo(), but it doesn't spawn the inventory clientside! *What* do I need to do to spawn the inventory clientside? What am I missing?

PHP:
//=============================================================================
// UDamageFixMut.
//=============================================================================
class UDamageFixMut extends Mutator;

function AddMutator( Mutator M )
{
    if ( M != Self )
    Super.AddMutator(M);
}

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{
    local inventory inv;

    log("Entered CheckReplacement");
    if ( Other.bIsPawn && Pawn(Other).bIsPlayer )
    {
        log("Pawn condition passed");
        log("v v Inventory spawned v v");
        inv = Spawn(class'UDamageFixInv');
        log("^ ^ Inventory spawned ^ ^");
        if( inv != None )
        {
            inv.bHeldItem = true;
            inv.RespawnTime = 0.0;
            inv.GiveTo(Pawn(Other));
            inv.Activate();
            log("Inventory given to player");
        }
    }
    return true;
}

function ModifyPlayer(Pawn Other)
{
    local Inventory Inv;

    log("Entered ModifyPlayer");
    Inv = Other.FindInventoryType(class'UDamageFixInv');
    if ( Inv == None )
    {
        log("Inv doesn't exist");
        log("v v Inventory spawned v v");
        inv = Spawn(class'UDamageFixInv');
        log("^ ^ Inventory spawned ^ ^");
        if( inv != None )
        {
            inv.bHeldItem = true;
            inv.RespawnTime = 0.0;
            inv.GiveTo(Other);
            inv.Activate();
            log("Inventory given to player");
        }
    }
    Super.ModifyPlayer(Other);
}

defaultproperties
{
}

PHP:
//=============================================================================
// UDamageFixInv.
//=============================================================================
class UDamageFixInv expands TournamentPickup;

replication
{
    unreliable if( Role==ROLE_Authority && Level.NetMode!=NM_Standalone )
        ReplicateDamageScaling;
}

function BecomeItem()
{
    Super.BecomeItem();
    SetTimer(1,True);
}

function Timer()
{
    log("Entered Timer. Owner is "$Owner);
    if( Pawn(Owner) != None )
        ReplicateDamageScaling(Pawn(Owner), Pawn(Owner).DamageScaling);
}

simulated function ReplicateDamageScaling( Pawn P, float Scaling )
{
    log("Entered ReplicateDamageScaling");
    P.DamageScaling = Scaling;
}

defaultproperties
{
     RemoteRole=ROLE_SimulatedProxy
     bAlwaysRelevant=True
     bNetTemporary=True
}
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
Don't make it bNetTemporary, that would cut off the connection right after the initial replication data arrived at the client and later updates, including replicated function calls, will not reach the client.
Also, I recommend not using CheckReplacement() to add the Inventory. ModifyPlayer() is enough already.
 
Last edited:

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
Don't make it bNetTemporary, that would cut off the connection right after the initial replication data arrived at the client and later updates, including replicated function calls, will not reach the client.
Also, I recommend not using CheckReplacement() to add the Inventory. ModifyPlayer() is enough already.
OK, cool. Although, I actually derived the both CheckReplacement code and the ModifyPlayer code from UT's Stealth mutator. So the question lies, if it's unnecessary, why did the professionals put it in there? But 'cause you said so, I'll take it out. And what about spawning ClientSide? It didn't happen with or without bNetTemporary.
 
Last edited:

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
Being owned by the replication target client is one way to make an actor relevant. Others include the target player being able to see the actor or hear its AmbientSound. If an actor has no AmbientSound and is bHidden, then that's not possible.


BTW: Use
Code:
 tags, not [php] tags, please.[/size]
 

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
I have another question about replication.

I'm running a small DM server. When I join a game, I see that my PlayerPawn's bNetOwner is false. This means that it won't replicate its JumpZ to the client, which is necessary for one of the mutators on the server. Keeping in mind it's a constant variable, how can I make it be set to true?
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
bNetOwner is a status variable. On the server it will be set only during replication to the owning network client and cleared during replication to any other client. This is nothing you can change and there really is no reason to do so. JumpZ is only required serversidely and on the owning client to autonomously simulate movement correctly. All other clients don't use JumpZ directly but receive the actual velocity change via replication.
 

XxDangerxX

New Member
Jan 20, 2008
128
0
0
NSW, Australia
www.myspace.com
It's playable if the client JumpZ is a bit off - you won't notice it - but it's 0! This means that as soon as I jump, I land. I land hard. I make the sound effect that you get from a high fall (without taking fall damage). I have to rely on the replicated Location and Velocity to get air. And they have a delay, due to ping. Once they are replicated, my view then snaps to match the replicated values. This is simply not acceptable, I'd say, for most online players. It'll put them off/distract them. I do have a way I can work around, but it's somewhat of a cheap workaround, and I only want to use it if I have to.

Are you saying that there is simply no way I can make the server give the Pawn bNetOwner?
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
JumpZ is only required serversidely and on the owning client to autonomously simulate movement correctly. All other clients don't use JumpZ directly but receive the actual velocity change via replication.

This sounds interesting. When you say autonomous do you mean that the movement is handled separately by the client and server ?
 

Wormbo

Administrator
Staff member
Jun 4, 2001
5,913
36
48
Germany
www.koehler-homepage.de
Yes, it always is. The player's local copy of his own Pawn is always simulated autonomously and only receives updates from the server once in a while to make sure things stay in sync. If player movement wasn't simulated autonomously, but only be based on replication, movement would feel entirely wrong to the player because his input would have ot travel to the server and back to the client, resulting movement to lag behind input depending on ping.
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
Right, and that would explain why a player will "warp" backwards online if UT detects the cpuspeed incorrectly. I also suppose this is what made zeroping possible.
 

War_Master

Member
May 27, 2005
702
0
16
ya, zp works in the autonomous-ClientSide function from the weapon that links it to... sorry... cant tell actually. But yeah, it works that way.
 

Zur

surrealistic mad cow
Jul 8, 2002
11,708
8
38
48
how ZP works isn't exactly secret ...

Tell us more. I can sort of figure it out (calculation based on ping) but I can't confirm if I'm right since the source can't be exported.