PDA

View Full Version : Many Mutators not working online. Need official help



KewlAzMe
11-28-2007, 02:04 PM
This doesn't seem to be getting much attention yet but I'm seeing a growing trend that is just going to get worse until we get someone with veteran skills to help us understand what we are missing in our mutators to allow them to work online.

When I connect to a server running these mutators I see the client downloading the mutator but when the game starts... the mutators don't take effect. I've added LogInternal to some of my debug versions and I put a checkpoint on either side of the changing value.. both checkpoints show up in the log.. but don't actually affect the player.

I've checked some other mutators that allegedly work online and I've gone as far as to add the same function calls (with nothing to be executed) hoping that one of the function calls would kick it into gear.

Here are a few small mutators that don't seem to be working online. Is there anything evident to anyone as to why these wouldn't work online?

Multijump: (by KewlAzMe)


class MultiJump extends UTMutator Config(Game);


var() config int iMaxNumJumps;
var() config int iMaxJumpBoost;

function ModifyPlayer(Pawn P)
{
if ( UTPawn(P) != None )
{
// Increase the number of times a player can jump in mid air
UTPawn(P).MaxMultiJump = iMaxNumJumps;
UTPawn(P).MultiJumpRemaining = iMaxNumJumps;

// Also increase a bit the amount they jump each time
UTPawn(P).MultiJumpBoost = iMaxJumpBoost;

}
Super.ModifyPlayer(P);
}

defaultproperties
{
bExportMenuData=False
GroupNames(0)="MULTIJUMP"
Begin Object Name=Sprite ObjName=Sprite Archetype=SpriteComponent'UTGame.Default__UTMutato r:Sprite'
ObjectArchetype=SpriteComponent'UTGame.Default__UT Mutator:Sprite'
End Object
Components(0)=Sprite
Name="Default__MultiJump"
ObjectArchetype=UTMutator'UTGame.Default__UTMutato r'
}



Aircontrol: (by MrCobra)



class AirControl extends UTMutator
config(AirControl);

var() config float fAirControl;

function ModifyPlayer(Pawn P)
{
if ( UTPawn(P) != None )
{
UTPawn(P).AirControl = fAirControl;
}
Super.ModifyPlayer(P);
}

defaultproperties
{
fAirControl=1.500000
GroupNames(0)="AIRCONTROL"
Begin Object Name=Sprite ObjName=Sprite Archetype=SpriteComponent'UTGame.Default__UTMutato r:Sprite'
ObjectArchetype=SpriteComponent'UTGame.Default__UT Mutator:Sprite'
End Object
Components(0)=Sprite
Name="Default__AirControl"
ObjectArchetype=UTMutator'UTGame.Default__UTMutato r'
}


MultiDodgeJump: (by Eliot)

File: MutMultiDodgeJump.uc:


// Coded by Eliot @ 2007-2008.
Class MutMultiDodgeJump Extends UTMutator;

Function bool CheckReplacement( Actor Other )
{
if( Other.IsA('UTPlayerController') )
Spawn( Class'MultiDodgeJumpInfo', Other );
return Super.CheckReplacement(Other);
}

defaultproperties
{
bExportMenuData=False
GroupNames(0)="DodgeJump"
Begin Object Name=Sprite ObjName=Sprite Archetype=SpriteComponent'UTGame.Default__UTMutato r:Sprite'
ObjectArchetype=SpriteComponent'UTGame.Default__UT Mutator:Sprite'
End Object
Components(0)=Sprite
Name="Default__MutMultiDodgeJump"
ObjectArchetype=UTMutator'UTGame.Default__UTMutato r'
}


File: MultiDodgeJumpInfo.uc:


Class MultiDodgeJumpInfo Extends Info;

Simulated Function Tick( float dt )
{
local UTPawn Pawn;

if( Owner == None )
Destroy();

Pawn = UTPawn(UTPlayerController(Owner).Pawn);
if( Pawn != None && Pawn.bCanDoubleJump )
{
if( Pawn.bDodging )
{
Pawn.bReadyToDoubleJump = True;
Pawn.bDodging = False;
if( UTPlayerController(Owner).DoubleClickDir == DCLICK_Active )
{
Pawn.CurrentDir = DCLICK_Done;
UTPlayerController(Owner).DoubleClickDir = DCLICK_Done;
UTPlayerController(Owner).ClearDoubleClick();
Pawn.MultiJumpRemaining = Pawn.MaxMultiJump;
}
}
}
}

defaultproperties
{
Begin Object Name=Sprite ObjName=Sprite Archetype=SpriteComponent'Engine.Default__Info:Spr ite'
ObjectArchetype=SpriteComponent'Engine.Default__In fo:Sprite'
End Object
Components(0)=Sprite
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=True
Name="Default__MultiDodgeJumpInfo"
ObjectArchetype=Info'Engine.Default__Info'
}

Tonester
11-28-2007, 02:20 PM
Without looking at the code, I can probably say this has everything to do with Replication. This is one of the things I have always struggled with and all new MOD authors struggle with. Stuff will work fine on a local server/client, but if that new functionality is not being replicated to all clients, then clients will not be able to see/do what others are doing.

KewlAzMe
11-28-2007, 02:22 PM
Without looking at the code, I can probably say this has everything to do with Replication. This is one of the things I have always struggled with and all new MOD authors struggle with. Stuff will work fine on a local server/client, but if that new functionality is not being replicated to all clients, then clients will not be able to see/do what others are doing.

Well the first two are simple ports of the way UT2004 did things.. the mutator should be executing on the client pc.. But it is likely that UT3 could have changed something.. we need to know what that something is.

Mysterial
11-28-2007, 02:24 PM
The first two don't work because they are only changing values on the server and those properties aren't replicated. You have to write something that will also change the values on the client.

The third looks like it should work, since it should be performing its effect on both sides, but it's hard to know what might be wrong without debugging it. I don't think DoubleClickDir is set on the server, so maybe


if( UTPlayerController(Owner).DoubleClickDir == DCLICK_Active )
{
Pawn.CurrentDir = DCLICK_Done;
UTPlayerController(Owner).DoubleClickDir = DCLICK_Done;
UTPlayerController(Owner).ClearDoubleClick();
Pawn.MultiJumpRemaining = Pawn.MaxMultiJump;
}

isn't executing on the server, but needs to.

KewlAzMe
11-28-2007, 02:30 PM
Well the Bighead Mutator that comes with UT3 is:


// Copyright 1998-2007 Epic Games, Inc. All Rights Reserved.
class UTMutator_BigHead extends UTMutator;

/* called by GameInfo.RestartPlayer()
change the players jumpz, etc. here
*/
function ModifyPlayer(Pawn P)
{
if ( UTPawn(P) != None )
{
UTPawn(P).SetBigHead();
}
Super.ModifyPlayer(P);
}

defaultproperties
{
GroupNames(0)="BIGHEAD"
Begin Object Name=Sprite ObjName=Sprite Archetype=SpriteComponent'UTGame.Default__UTMutato r:Sprite'
ObjectArchetype=SpriteComponent'UTGame.Default__UT Mutator:Sprite'
End Object
Components(0)=Sprite
Name="Default__UTMutator_BigHead"
ObjectArchetype=UTMutator'UTGame.Default__UTMutato r'
}


Isn't that the same thing as the first two? Its a simple script. I haven't tested the Bighead mutator but I was under the impression that only the slow kills was offline only. Kinda dumb to release a bighead mutator that only works offline.

BattleMode
11-28-2007, 02:34 PM
All settings of my BattleMOD mutator (http://www.onsrpg.com/ut3mods.php) seem to work fine even if the mutator is on the server. All source code is included GPL V3 so feel free to look at how it works.

The only (very serious) BUG so far seems to be that UPK files are auto downloaded but not executed by the UT3 client (see also http://utforums.epicgames.com/showthread.php?t=588343). I am still hoping I am making some sort of stupid mistake here, but whatever I try the client simply refuses to load the UPK after it has downloaded it. If the UPK is pre-installed on the client it all works fine, but this is not really a workable solution. In case of BattleMOD the UPK is used to create custom UI Scenes to alter the scoreboard (add things like PPH). I am also working on RPG for UT3 so things like this are important to allow all kinds of user interface enhancements. I hope someone finds a solution for this.

KewlAzMe
11-28-2007, 02:37 PM
I actually have DL'd your mut to check it out but haven't looked at it yet. There is the MutTranlocatorPlus1.1 that apparently works online and there is nothing there that I see added for any other replication, unless some function call is also causing the client to update behind the scenes.

Mysterial
11-28-2007, 03:16 PM
Isn't that the same thing as the first two? Its a simple script. I haven't tested the Bighead mutator but I was under the impression that only the slow kills was offline only. Kinda dumb to release a bighead mutator that only works offline.

HeadScale is replicated on UTPawn - that's how it gets to the client. All properties are not inherently replicated. There's some good replication documents on UnrealWiki for getting a better understanding of how that stuff works.

Pfhoenix
11-28-2007, 04:32 PM
Also : Unless UT3 has changed this, mutators do not execute on clients unless specifically setup to be replicated to the client (check their RemoteRole, as well as making functions simulated so that they execute on the client).

KewlAzMe
11-28-2007, 04:46 PM
Well they must have changed something if the near exact copy of UT2004's quadjump doesn't work now.

UT2004 QuadJump:


class MutQuadJump extends Mutator;

function ModifyPlayer(Pawn Other)
{
local xPawn x;
x = xPawn(Other);

if(x != None)
{
// Increase the number of times a player can jump in mid air
x.MaxMultiJump = 3;
x.MultiJumpRemaining = 3;

// Also increase a bit the amount they jump each time
x.MultiJumpBoost = 50;
}

Super.ModifyPlayer(Other);
}



UT3 QuadJump


class QuadJump extends UTMutator;

function ModifyPlayer(Pawn P)
{
if ( UTPawn(P) != None )
{
// Increase the number of times a player can jump in mid air
UTPawn(P).MaxMultiJump = 3;
UTPawn(P).MultiJumpRemaining = 3;

// Also increase a bit the amount they jump each time
UTPawn(P).MultiJumpBoost = 50;

}
Super.ModifyPlayer(P);
}


The only different is the:
local xPawn x;
x = xPawn(Other);

would adding that be enough? or is xPawn client side but UTpawn not client side?

I assume UTPawn is the UT3 equivalent of xPawn.

Mysterial
11-28-2007, 04:56 PM
What changed is that we don't replicate those things for you anymore.

kisk
11-28-2007, 05:04 PM
If you look at my source in TranslocatorPlus, you'll see I do have a lot of simulated functions for my weap/proj classes. This was required as the parents' functions were simulated, and the fact that a weapon would by default be replicated. My mut does not use ModifyPlayer() or CheckReplacement() either.

}DA{Moneyshot
11-28-2007, 05:05 PM
ugh. I have been looking @ replication stuff for the last hour or so, but I guess I just don't have a good handle on uscript because I'm just not getting it.

}DA{Moneyshot
11-28-2007, 07:47 PM
I think I figured it out. I was wanting to change the aircontrol so what I ended up doing is following someone else's lead and subclassing UTPawn and adding a reliable function to set the value. I think this will do it for me.

KewlAzMe
11-28-2007, 09:26 PM
Anyone out there care to take the multijump or Aircontrol code and make it work online to show us an example of how to do it? I think if we can start small, it would be helpful to others to better understand what needs to happen.

}DA{Moneyshot
11-28-2007, 09:46 PM
I think this will do it for the aircontrol. It is what I have in my mutator.

LGIPawn.uc

class LGIPawn extends UTPawn;

reliable client function SetAirControl(float LGIAirControl)
{
AirControl = LGIAirControl;
}

defaultproperties
{
}

LGIMutator.uc (snippet)

simulated function ModifyPlayer(Pawn Other)
{
local UTPawn P;
local LGIPawn LGIP;

P = UTPawn(Other);
if(P != None)
{
LGIP = LGIPawn(P);
if(LGIP != None)
{
LGIP.SetAirControl(LGIAirControl);
}
}

Super.ModifyPlayer(Other);
}

Right now I'm trying to figure out the ini configuration system so I can put my mutator out as beta.

}DA{Moneyshot
11-30-2007, 09:03 AM
I'm still struggling with this. Mysterial would it be possible for you to post a small example of what one would do to replicate out one of the unreplicated settings from utpawn?

Eliot
11-30-2007, 09:16 AM
the best thing you could do instead of making custom pawn is spawning an info class for every client and let modifyplayer call a function in there that replicates it to client and executes it on server too.

and @ mysterial.

that part doubleclickdir is only set client side and only needs to be changed clientside so i don't think thats the problem.

the problem there is that owner is not replicated anymore to client which makes the client just destroys it but i fixed this but it still isn't working.

heres the updated code



Class DodgeJumpInfo Extends Info;

var bool bCanMultiDodge;
var MutDodgeJump Mutator;
var UTPlayerController PC;

Replication
{
if( bNetDirty && (Role == ROLE_Authority) && bNetInitial )
bCanMultiDodge;
}

Simulated Function Tick( float dt )
{
local UTPawn Pawn;
local bool bCanDoubleJump;

if( PC == None )
{
ForEach LocalPlayerControllers( Class'UTPlayerController', PC )
break;

if( PC == None ) // if still none destroy (happens if player left server).
{
LogInternal( "Destroying myself!", Name );
Destroy();
return;
}
}

if( WorldInfo.NetMode != NM_Client )
{
if( bCanMultiDodge != Mutator.bAllowMultiDodge )
bCanMultiDodge = True;
}

Pawn = UTPawn(PC.Pawn);
if( Pawn != None )
{
if( Pawn.bDodging || PC.DoubleClickDir == DCLICK_Active )
{
LogInternal( "Dodging!", Name );
bCanDoubleJump = Pawn.bCanDoubleJump;
if( bCanDoubleJump )
{
LogInternal( "Jump Enabled!", Name );
Pawn.bReadyToDoubleJump = True;
Pawn.bDodging = False;
}

if( bCanMultiDodge )
{
Pawn.CurrentDir = DCLICK_Done;
PC.DoubleClickDir = DCLICK_Done;
PC.ClearDoubleClick();
if( UTPlayerInput(PC.PlayerInput) != None )
UTPlayerInput(PC.PlayerInput).ForcedDoubleClick = DCLICK_Done;

if( bCanDoubleJump ) // Only allow extra jumps if player can doublejump (this way certain maps or w/e else can turn this off).
ServerResetJumps( Pawn );
}
}
}
}

// No idea if this would be still needed in ut3 but i don't wanna test 999x times to figure out :).
reliable Server Protected Simulated Function ServerResetJumps( UTPawn Pawn )
{
Pawn.MultiJumpRemaining = Pawn.MaxMultiJump;
}

DefaultProperties
{
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=True
}


Mutator var is set by checkreplacement and only used by server so don't look there :).

Mysterial
11-30-2007, 09:45 AM
Seems like:


if( PC == None )
{
ForEach LocalPlayerControllers( Class'UTPlayerController', PC )
break;

if( PC == None ) // if still none destroy (happens if player left server).
{
LogInternal( "Destroying myself!", Name );
Destroy();
return;
}
}


would cause it to immediately destroy itself on a dedicated server.

BTW, Owner replicates fine, although it could be delayed by a bit. You can't destroy actors you're not authoritative on, so the Destroy() call in the old code was almost certainly doing nothing.

Eliot
11-30-2007, 09:57 AM
indeed lol well thx for making that clear, heres an update of it this time i let server send the pc owner.



Class DodgeJumpInfo Extends Info;

var bool bCanMultiDodge;
var MutDodgeJump Mutator;
var UTPlayerController PC;

Replication
{
if( bNetDirty && (Role == ROLE_Authority) && bNetInitial )
bCanMultiDodge, PC;
}

Simulated Function Tick( float dt )
{
local UTPawn Pawn;
local bool bCanDoubleJump;

if( WorldInfo.NetMode != NM_Client )
{
if( PC == None ); // Send owner to client.
PC = UTPlayerController(Owner);

if( PC == None ) // Still none clean up...
{
LogInternal( "Destroying myself!", Name );
Destroy();
return;
}

if( bCanMultiDodge != Mutator.bAllowMultiDodge )
bCanMultiDodge = True;
}

if( PC != None )
{
Pawn = UTPawn(PC.Pawn);
if( Pawn != None )
{
if( Pawn.bDodging || PC.DoubleClickDir == DCLICK_Active )
{
LogInternal( "Dodging!", Name );
bCanDoubleJump = Pawn.bCanDoubleJump;
if( bCanDoubleJump )
{
LogInternal( "Jump Enabled!", Name );
Pawn.bReadyToDoubleJump = True;
Pawn.bDodging = False;
}

if( bCanMultiDodge )
{
// Clear any Direction moves.
Pawn.CurrentDir = DCLICK_Done; // Server
PC.DoubleClickDir = DCLICK_Done; // Client
PC.ClearDoubleClick(); // Client
if( UTPlayerInput(PC.PlayerInput) != None )
UTPlayerInput(PC.PlayerInput).ForcedDoubleClick = DCLICK_Done;

if( bCanDoubleJump ) // Only allow extra jumps if player can doublejump (this way certain maps or w/e else can turn this off).
ServerResetJumps( Pawn );
}
}
}
}
}

// No idea if this would be still needed in ut3 but i don't wanna test 999x times to figure out :).
reliable Server Protected Simulated Function ServerResetJumps( UTPawn Pawn )
{
Pawn.MultiJumpRemaining = Pawn.MaxMultiJump;
}

DefaultProperties
{
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=True
}

}DA{Moneyshot
11-30-2007, 11:19 AM
I think this will be quite helpful. Thanks guys.

dickbird
11-30-2007, 11:58 AM
I can maybe understand a simple example of this stuff, but I don't have a lan or anything to test things on so it is kind of hard to know if I have any of it right, really. Sorry to be a bother.

But anyway, looking at that BigHead mutator, I'm guessing they have the modifyplayer routine, an entry in that replication statement in pawn.uc for headscale, and then headscale is declared as type repnotify whatever, and then there's an event routine that kicks off when a new value of headscale comes across.

And it's like that because: The initial call to the mutator's copy of modifyplayer calls setbighead, which flips on a switch to tell the server that bighead's active. So it starts varying headscale according to kills. And headscale gets replicated. And the event procedure gets kicked off on clients when headscale comes across to them, so their copy of headscale gets updated with the new value, along with the size of the pawn's head. Their copy of the switch bkillsaffectheadsize or whatever is NOT replicated, so they don't do any monkeying around with it, they just use the value as given to them (like they always do, it being set initially to 1 on every machine anyhow.) The server knows how to vary it because it knows whenever anybody gets killed...

Which is all really convenient because the code for most of this
is already on every machine everywhere, running the 'mutator' on the server just switches it on.

um... right?

So... if I want to, say, totally ruin the game by letting flag carriers use all the vehicles, like this for example;



class VFlags extends UTMutator;

function bool CheckReplacement(Actor Other)
{
local UTVehicle Vehicle;

Vehicle = UTVehicle(Other);
if (Vehicle != None)
{
Vehicle.bCanCarryFlag = TRUE;
}

return true;
}


defaultproperties
{
Name="VFlags"
GroupNames(0)="VFlags"
}


... Would I actually have to replicate anything?

I mean, if the server loads and runs the mutator on the client anyway (ummm... that's how it works, right...?), then the only change being made is already there- a boolean has been flipped to TRUE,
and it just stays there ever after. No further communication necessary about it...?


... Wheras... with that aircontrol, multijump thing... you've got a couple numbers that are coming from like a setup panel or something, to configure the whole thing, So the server has to pass those along to all the clients after the fact of mere installation, so they're all running the same page... so replication is required there...?

Am I anywhere near the realm of reality here?

Thanks

Pfhoenix
11-30-2007, 12:38 PM
Not accurate - Mutators are not, by default, replicated to clients. You can make them replicate to clients by setting their RemoteRole=ROLE_SimulatedProxy in defaultproperties, but then you still wouldn't get the Mutator events/callbacks from the gametype, since it is never on the client to begin with. The only reason to have a mutator be replicated to clients is if you need to setup client-side specific things, like a HUD mutator, for example.

dickbird
11-30-2007, 03:50 PM
Ok, I can see where assuming CheckReplacement would somehow automatically be run on the client is inconsistent with the guesswork I was making about how Bighead works.

So are you saying that for such a simple example as this VFlag thing, I couldn't get away with just setting this RemoteRole=ROLE_SimulatedProxy default property and making CheckReplacement a simulated function?

That'd probably be about the next thing I'd want to try. It compiles and runs ok locally that way, anyway. After that I guess those examples Eliot put up might suggest a few things.

Not sure how practical it is to try and figure this out without actually having any way to test things out on a network, but I would like to get the idea.

}DA{Moneyshot
11-30-2007, 05:54 PM
indeed lol well thx for making that clear, heres an update of it this time i let server send the pc owner.


Eliot,

The above code was not getting me the dodge jump when connecting to a server. In order to do the dodge jump I had to change the following line in Tick:


bCanDoubleJump = CanMultiJump(Pawn);


and add the below function:


reliable Server Protected Simulated function bool CanMultiJump(UTPawn Pawn)
{
return ( Pawn.MaxMultiJump > 0 );
}


I'm not quite sure why, but bCanDoubleJump is always false on the client with out this. I couldn't go directly at the MaxMultiJump variable either. *shrugs* I would love to hear an explanation from some one that knows more than me.

dickbird
12-01-2007, 07:45 AM
Ok, so I scraped together a second computer capable of running ut3... barely... (though surprisingly well I think) and hooked it up to the router.

I used it to host a game of VCTF-Suspense, using the VFlags mutator I listed above, without modification.

On the main computer, I erased the .u and .ini files for Vflags, then started it up and joined the game, over LAN.

And the mutator worked just fine, exactly the way it's supposed to, on the client machine.

What am I missing here? Is there something else I need to clear off the client machine to make it a fair test?

I'd really love to think such a mutator is just too simple to require this replication malarkey at all.

... Oh wait, just looked at the .uc file again, and apparently yesterday sometime I did edit in that RemoteRole and made the function simulated, so that's probably why it works now. Guess I should check that, but it's kind of late here, do it tomorrow.

dickbird
12-02-2007, 11:30 AM
Huh. So anyway I took the RemoteRole property out, and changed the function back to a regular old non-simulated function... and both those stupid muts run just fine on that, on both server and client, when only the server has a copy of the mut.

So I guess, as far as I can tell, replication isn't necessary at all in muts this simple.

Well this is all really dumb and elementary I know, but I am glad I took the trouble to get set up to answer the question myself. Can't recommend that course too highly.