sega 500 more replication jeff “ezeikeil” giles [email protected] jgiles

46
Sega 500 More replication Jeff “Ezeikeil” Giles [email protected]

Upload: clara-ward

Post on 30-Dec-2015

216 views

Category:

Documents


0 download

TRANSCRIPT

Sega 500

More replication

Jeff “Ezeikeil” [email protected]://gamestudies.cdis.org/~jgiles

More Replication

Yes…that’s the plan for today.

Replication in UT is so important that it really is worth taking another class to look at another example.

Rolling back the clock

I figure I’d have some fun with this lesson and reuse some code that you are already familiar with.

We are going to create the network functionality for the King of the Hill Gametype we made in lesson 9.

As you recall…

The rules were fairly straight forward. When a player was killed for the first time, a special damage doubler would be spawned.

This doubler would be carried by the player until he was killed. At which time it get dropped for the next player to pickup.

As you recall…

Only the player with the special doubler was able to score and there could only ever be one in play.

And as you no doubt recall…

When we did the network test, it fail miserably.

The reason was that the client and server for the EzeUdam would go out of sync… in effect, being in 2 different locations, with only the one on the server being valid.

However, it worked fine in single player.

Now at this point…

It would be a good idea to refer back to lesson 9 and look at the code intend to modify.

It boils down to three classes: Gametype EzeUdam Message class

Gametype

Was essentially our rule of play with most of the work happening in the Killed function.

The Killed function did the spawning of the EzeUDam and broadcasting of messages to the player.

EzeGameMSG

Was simply the localized message class which is used for two reasons.

1. Formatting and placement of messages of that class is super easy.

2. Network optimization. As opposed to sending a string, it sends an index into this class… much more efficient.

EzeUDam

Essentially the pickup which enabled the player Pawns UDamage parameter.

The only real difference between ours an the UDamagePack was that ours broadcasted messages as well

EzeUDam

And spawned navigation information so that the bots would take after it…

Other than that, no real significant differences.

Making replication

Now, working with these classes, things replicate differently then they did with the KRock.

And this cause me much grief. I spend a couple of hours just trying to figure out why the client wouldn’t replicate properly.

It came down to the Gametype

Now as I said yesterday, and I’ll say it again…Clients don’t have gametypes.

And Because it was in the Gametype that I was updating and tracking changes.

This really caused me some puzzlement in trying to figure out how to track who had the EzeUdam, not to mention it’s location.

But as it turns out…

Once I got my head around it, it wasn’t that big of a deal.

I just had to remember that the clients don’t need the game info class because the server is the authority on what goes on in that level…It’s word is law.

Thus…

The client never makes any decisions for itself. The server makes all the decisions and then tells the clients.

Hence, most of the replication conditions are based on whether or not I am the server. If I am, replicate the condition. If not…skip it.

Getting started

In the Gametype, there are 2 variables that the clients have to be aware of

These are who has the EzeUDam and the EzeUdam itself.

var pawn HasUDam;var EzeUDam dam;

The Gametype

The server will use these to track who has the Udamage and if it can be pickup (not out of play).

Hence our replication statement :

reliable if(role==ROLE_AUTHORITY) HasUDam, dam, snd;

The Gametype

If I am the server, these variables are reliable and I can safely send them to the client.

reliable if(role==ROLE_AUTHORITY) HasUDam, dam, snd;

The Gametype

Which brings us down to the Killed function.

Now there is not a lot of anything new in this class…mostly some gameplay tweaks which we will come back to. Right now, we only care about the replication…Which in this case, is related to how the EzeUDam is flung into the air.

The Gametype

Which occurs here:

if(HasUDam == none && dam == none){ dam=spawn(class'EzeUDam',,,KilledPawn.location);

//toss the UDamage in to the air dam.Toss(); snd=none;} Does the flinging

The Gametype

Now this is a very important difference to make note of.

In the previouve version of the code, we just applied the velocity to the EzeUDam here. We can no longer do that.

We have to go into the EzeUdam itself and call the Toss function.

Why?

Well, it comes down to a matter of replication and we need to access a simulated function to ensure that it get run on the right machine.

Now, the Toss function itself is not simulated, but the one it calls, is.

EzeUDam

function Toss(){

bOnlyReplicateHidden = false;

//if I am the server, tell the clients the velocity if(Role==ROLE_Authority) { ClientToss(); return; }}

If I am the server call the Clienttoss. Otherwise, donothing

EzeUDam

And the ClientToss function is was does the actual flinging.

simulated function ClientToss(){ Velocity= 500*VRand(); Velocity.Z= RandRange(500,800); SetPhysics(PHYS_Falling);}

EzeUDam

Notice that the ClientToss is simulated where the Plan-Jane Toss is not.

By making the actual fling method simulated, it get replicated over the wire. I was not able to get this to run by simply calling the ClientToss from the Gametype.

EzeUDam

Which brings us to EzeUdam’s replication statement.

Not much, If I am the server, trust in the client toss function for replication.

reliable if(role==ROLE_AUTHORITY) ClientToss;

EzeUDam

And before we go any further with this class, we should have a look at the Defaults.

bStatic=falsebNoDelete=falsebHidden=false

No to surprising

EzeUDam

if true, only replicate actor if bNetDirty is true - useful if no C++ changed attributes (such as physics) bOnlyDirtyReplication only used with bAlwaysRelevant actors.

bOnlyDirtyReplication=falsebAlwaysRelevant = truebReplicateMovement=trueNetUpdateFrequency=100.000000NetPriority=2.000000bNetInitialRotation=True

EzeUDam

Still not any newness:

But do notice that I’m using DumbProxy.

If I were using some other replication property, the server would try to replicate its rotation as well…Very ugly.

RemoteRole=ROLE_DumbProxybUpdateSimulatedPosition=True

EzeUDam

And finally, we also update the gametype…if we are the server….from inside the touch function:

if(Role== ROLE_Authority){ EzeGame(level.Game).HasUDam=P; EzeGame(level.Game).PlayClientSeekSound();}

EzeUDam

Update the player who has the EzeUdam with whoever just picked it up.

if(Role== ROLE_Authority){ EzeGame(level.Game).HasUDam=P; EzeGame(level.Game).PlayClientSeekSound();}

EzeUDam

And have the server make some noise.

if(Role== ROLE_Authority){ EzeGame(level.Game).HasUDam=P; EzeGame(level.Game).PlayClientSeekSound();}

Gametype

Now, for the PlayClientSeekSound function I’m reaching into my bag of tricj on this one…

What I plan to do is play a sound on the client side only…excluding the player who has the EzeUdam.

Gametype

And because of where we are calling this from, we know that we are the server…

Hence, we have the gametype… Hence, we have access to all the players

in the game… Hence, we can manipulate any one (or all)

of them. Remember, the server makes the rules.

Gametype

So how am I going to pull this off you ask?

Well, being the server, we have the gametype, which has a list of all the controllers in play…

Just going to iterated over all those…

Gametype

simulated function PlayClientSeekSound(){ local controller c; snd=sound'XEffects.LightningSound';

For ( C=Level.ControllerList; C!=None; C=C.NextController ) { if(c.IsA('Playercontroller') && c.Pawn != HasUDam) Playercontroller(c).ClientPlaySound(snd); }}

ClientPlaySound

Which allows me to introduce a very interesting function. ClientPlaySound.

The function will only play sound on the client side…no other players will be able to hear it.

ClientPlaySound

Which, in of itself, is really cool…not to mention handy.

But even better, go over to playercontroller and look at how its implemented… Not how you might expect.

ClientPlaySoundsimulated function ClientPlaySound(sound ASound, optional bool

bVolumeControl, optional float inAtten, optional ESoundSlot slot )

{ local float atten;

atten = 0.9; if( bVolumeControl ) atten = FClamp(inAtten,0,1);

if ( ViewTarget != None )ViewTarget.PlaySound(ASound, slot, atten,,,,false);

}

ClientPlaySound

Now, most of this dosen’t rate to highly on the “YIPPEE” scale of things…except for the last line…

Where it’s using the viewport to play the sound.

ClientPlaySound

ViewTarget? What the heck is that?

<taps monitor> We are using the clients machine to play a

“none specific” sound to the client player. More exactly, we are playing the sound

through the clients player, kind of like the announcers voice.

ClientPlaySound

What this results in is a sound that only plays for that players who do not have the EzeUdam, giving a heads up that its been picked up.

As for the sound, well I found a really pleasing thunder clap effect .

Another effect…

And just to furthur make the player stand out, back in the ouch function of the EzeUdam class I added.

This is the derez effect material seen when you die…what it does for us:

p.OverlayMaterial=FinalBlend'DeRez.DeRezFinalBody';

Another effect…

A green techno ghost

And this should

Leave use some time today for poking at it and playing with the code…

Replication is a rather opaque thing, and the only way to get good at it, is to practice.

A wrap…

I hope today’s lesson helps to clear up the relation between client and server in UT and further illustrates how they talk to each other.