sega 500 an introduction to replication jeff “ezeikeil” giles [email protected] jgiles

79
Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles [email protected]

Upload: alexander-harris

Post on 04-Jan-2016

217 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Sega 500

An Introduction to

Replication

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

Page 2: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Today

Is about one of the core elements that make UT2003 what it is. The multiplayer experience.

In another word, Replication.

Page 3: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Replication, so what is it?

In a nutshell, Replication in UT is about how information gets sent across the wire.

As well as to whom and who from.

It’s how UT shares reality with all the players connected to that game.

Page 4: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Replication

A dictionary definition:

rep·li·cate To duplicate, copy, reproduce, or repeat. Biology To reproduce or make an exact copy

or copies of (genetic material, a cell, or an organism).

Page 5: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

And there you have

A very simple introduction to object replication between client and server.

Page 6: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

UT’s Defintion

Unreal views the general problem of "coordinating a reasonable approximation of a shared reality between the server and clients" as a problem of "replication".  That is, a problem of determining a set of data and commands that flow between the client and server in order to achieve that approximate shared reality.

Page 7: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

An important fact…

about UT, it is a client server architecture which allow players to join a game which is currently in progress.

Meaning one server, and zero or more client machines.

Page 8: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Why is this important?

The Server Is The Man.

the server is authoritative about the gameplay flow, the server's game state can always be regarded as the one true game state. 

Page 9: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

One true game

The version of the game state on client machines should always be regarded as an approximation subject to many different kinds of deviations from the server's game state.

Page 10: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The players

Actors that exist on the client machine should be considered proxies because they are a temporary, approximate representation of an object rather than the object itself.

Page 11: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Three Types of Replication

The network code is based on three primitive, low-level replication operations for communicating information about the game state between the the server and clients.

Page 12: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Actor replication

The server identifies the set of "relevant" actors for each client (actors which are either visible to the client or are likely to somehow affect the client's view or movement instantaneously), and tells the client to create and maintain a "replicated" copy of that actor.  While the server always has the authoritative version of that actor, at any time many clients might have approximate, replicated versions of that actor.

Page 13: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Variable replication

Actor variables that describe aspects of the game state which are important to clients can be "replicated".  That is, whenever the value of the variable changes on the server side, the server sends the client the updated value.

Page 14: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Function call replication

A function that is called on the server in a network game can be routed to the remote client rather than executed locally. Alternatively, a function called on the client side may be routed to the server, rather than called locally.

Page 15: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

For example

You can hear other players gunshots because the server is replicating the ClientHearSound function to you.  The ClientHearSound function is called for a PlayerPawn whenever that PlayerPawn hears a sound.

Page 16: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The replication statement

In UnrealScript, every class can have one replication statement.  

The replication statement contains one or more replication definitions.  

Each replication definition consist of a replication condition (a statement that evaluates to True or False), and a list of one or more functions and variables to which the condition applies.

Page 17: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The replication statement

may only refer to variables defined in that class, and functions defined first in that class (that is, it cannot apply to functions defined in a superclass but overridden in that class.

Page 18: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The replication statement

Therefore, if the Actor class contains a variable DrawType, then you know where to look for its replication condition: it can only reside there in the Actor class.

Page 19: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The replication statement

Essentially determines what gets sent across the wire, or what functions and values a relevant depending on the role of that class in a network game.

Each replication statement reads like a function, but definitions begins with "reliable if (condition)" or "unreliable if (condition)"

Page 20: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

An example.

From Xpawnreplication{ // client to server reliable if ( Role < ROLE_Authority )

ServerDoCombo, ServerRequestRules, AdminMenu, ServerRequestPlayerInfo, ServerSpecViewGoal;

unreliable if( Role < ROLE_Authority )L33tPhrase;

reliable if ( Role == ROLE_Authority) ClientReceiveRule, ClientReceiveCombo;}

Page 21: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

So what’s going on?

Unreliable: Functions replicated with the "unreliable"

keyword are not guaranteed to reach the other party and, if they do reach the other party, they may be received out-of-order.

The only things which can prevent an unreliable function from being received are network packet-loss, and bandwidth saturation.

Page 22: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

So what’s going on?

Reliable: The replicated function will get through in the

proper order.

Variables are always reliable Variables are always guaranteed to reach the

other party eventually, even under packet-loss and bandwidth saturation condition.

Page 23: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

A note on Packet loss

In a LAN game, we guesstimate that unreliable data is received successfully approximately 99% of the time.  However, in the course of a game, hundreds of thousands of things are replicated, so you can be sure that some unreliable data will be lost.

Page 24: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

A note on Packet loss

In a typical low quality 28.8K ISP connection, unreliable data is generally received 90%-95% of the time.  In other words, it is very frequently lost.

Page 25: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

And what’s “ROLE_Authority”?

ROLE_Authority is part of the ENetRole enum defined in actor.

enum ENetRole { ROLE_None, // Means the actor is not relevant in network play. ROLE_DumbProxy, // A dumb proxy. ROLE_SimulatedProxy, // A simulated proxy. ROLE_AutonomousProxy, // An autonomous proxy. ROLE_Authority, // The one authoritative version of the actor. };

Page 26: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

What’s “ROLE_Authority”?

And it’s intended for use with two variables, Role and RemoteRole.

These variables describe how much control the local and remote machines, respectively, have over the actor:

Page 27: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Role==ROLE_DumbProxy

The actor is a temporary, approximate proxy which should not simulate any physics at all.  On the client, dumb proxies just sit around and are only moved or updated when the server replicates a new location, rotation, or animation information.

This situation is only seen in network clients, never for network servers or single-player games.

Page 28: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Role==ROLE_SimulatedProxy

The actor is a temporary, approximate proxy which should simulate physics and animation.  On the client, simulated proxies carry out their basic physics (linear or gravitationally-influenced movement and collision), but they don't make any high-level movement decisions.  They just go.

This situation is only seen in network clients, never for network servers or single-player games.

Page 29: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Role==ROLE_AutonomousProxy

The actor is the local player. Autonomous proxies have special logic built in for client-side prediction (rather than simulation) of movement.

This situation is only seen in network clients, never for network servers or single-player games.

Page 30: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Role==ROLE_Authority

This machine has absolute, authoritative control over the actor.

This is the case for all actors in single-player games.

This is the case for all actors on a server. On a client, this is the case for actors that were

locally spawned by the client, such as gratuitous special effects which are done client-side in order to reduce bandwidth usage.

Page 31: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

On roles Actor’s play

On the server side, all actors have Role==ROLE_Authority, and RemoteRole set to one of the proxy types. 

On the client side, the Role and RemoteRole are always the exactly reversed relative to the server's value. 

Page 32: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Core replication variables

To get your actors to replicate properly there is a rather long list of special replication variables to know about.

Here, I’m only gone to address the most important of them:

Page 33: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

NetPriority

Each actor has a floating point variable called NetPriority.  The higher the number, the more bandwidth that actor receives relative to others.  An actor with a priority of 2.0 will be updated exactly twice as frequently as an actor with priority 1.0.  

The only thing that matters with priorities is their ratio.

Page 34: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

NetPriority

Since 3.0 is the highest on the list, there is no real difference between giving your actor a NetPriority of 4.0 and 4000.0, since they will both be placed at the top of the list.

Page 35: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

bNetInitial

true if this is the first time this actor is being replicated across the network. Useful for variables that differ from the defaultproperties, yet will not change over the life of the actor.

Page 36: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Some others…

bNetOwner True if the player we are replicating to owns

this actor directly.

bAlwaysRelevant Always keep the actor relevant to network

play.

Page 37: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Determining relevance

An Unreal level can be huge, and at any time a player can only see a small fraction of the actors in that level.  Most of the other actors in the level aren't visible, aren't audible, and have no significant effect on the player.

Page 38: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Determining relevance

The set of actors that a server deems are visible to or capable of affecting a client are deemed the relevant set of actors  for that client.

This is a significant bandwidth optimization in Unreal's network code is that the server only tells clients about actors in that client's relevant set.

Page 39: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Determining relevance

The basic rules are:

1) The actor belongs to the ZoneInfo class, then it is relevant.

2) If the actor has static=true or bNoDelete=true, then it is relevant.

3) If the actor is owned by the player (Owner==Player), then it is relevant.

4) If the actor is a Weapon and is owned by a visible actor, then it is relevant.

Page 40: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Determining relevance

5) If the actor is hidden (bHidden=true) and it doesn't collide (bBlockPlayers=false) and it doesn't have an ambient sound (AmbientSound==None) then the actor is not relevant.

6) If the actor is visible according to a line-of-sight check between the actor's Location and the player's Location, then it is relevant.

7) If the actor was visible less than 2 to 10 seconds ago (the exact number varies because of some performance optimizations), then it is relevant.

Page 41: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Simulated functions

Declares that a function may execute on the client-side when an actor is either a simulated proxy or an autonomous proxy. All functions that are both native and final are automatically simulated as well.

Page 42: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

For more reading

And a whole lot more specific information, read these:

http://unreal.epicgames.com/Network.htm

http://www.planetunreal.com/pipeline/tutorials/replication.html

Page 43: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

And now for our example…

For today, I wanted to do something rather simple, and that was allow the players to throw some rocks at each other.

But not just any rocks, Karma rocks that replicate their positions over the network.

Page 44: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

And keeping with the cheesy art skills, I used the editor to create a simple static mesh for the KRocks. Complete with Karma primitives…

Page 45: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The plan

Is to have the player spawn one of these whenever they fire their weapon.

Then they can shoot them around some and play a bit of “gun hockey” with them.

But too keep the network bandwidth reasonable, I’ve also created a cap on the number that players can spawn.

Page 46: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The plan

I’ve also created a few debug methods so that was can see what going on, either on the HUD or in the log.

Page 47: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Getting started…

For convenience, I created a gametype for testing the replication as the easies way to intercept the fire command from the player is through the controller, but we’ll talk about that in a second.

Page 48: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

There is really nothing new going on here, just some defaults so that we can use the new controller and HUD.

class ERepGame extends xDeathMatch;

DefaultProperties{ HUDType="Ezerep.EHUD" CountDown=0 PlayerControllerClassName="EzeRep.Epc"}

Page 49: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

Now, keep in mind that only the server has a gametype object. The clients do not.

Thus any attempt to access information in the gametype via Level.game will access none.

Page 50: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

Which brings me to the one function in the gametype.

This function was intended to spit out information to the HUD or log based on the role of the player. However, due to the a for mention of the gamtypes existence, this function will not *properly*.

simulated function LogRole()

Page 51: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

However, the logic is sound. Therefore, left in for reference purposes, just don’t try to access it from the client.

This function will work fine if it’s copied into other classes.

Page 52: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

That being said…simulated function LogRole(){ if(role==ROLE_Authority) { log("--->We are Server"); BroadcastLocalizedMessage(class'RepMsg', 0); } if(Role<ROLE_Authority) { log("--->We are Client"); BroadcastLocalizedMessage(class'RepMsg', 1); }}

Page 53: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

This is a very simple debug method which, based on our role, determines what function to execute.

if(role==ROLE_Authority){ log("--->We are Server"); BroadcastLocalizedMessage(class'RepMsg', 0);}

If we are the server, do this

Page 54: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The Gametype

All we do is splash some info to the log or screen, based on the role we play.

if(Role<ROLE_Authority){ log("--->We are Client"); BroadcastLocalizedMessage(class'RepMsg', 1);}

If we are a client, do this

Page 55: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The RepMsg

This is simply a local message handler to display info to the HUD.

Nothing of interest here, just some debug strings.

class RepMsg extends LocalMessage;

Page 56: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

The KRock

Now this class likely isn’t going to to be what you’d expect…

What we end up doing here is setting up a bunch of variables. No replication work…

Page 57: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

Starting with the physics…

//physics and collisionDrawType=ST_StaticmeshStaticMesh=staticmesh'ErepTest.box'

bCollideWorld=truebCollideActors=true

CollisionRadius=8Collisionheight=8

Page 58: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

And The Karma Params//KarmaPhysics=PHYS_KarmaBegin Object Class=KarmaParams Name=KParams0

KAngularDamping=0KLinearDamping=0bHighDetailOnly=FalsebClientOnly=FalsebKDoubleTickRate=TrueName="KParams0"

End ObjectKParams=KarmaParams'KParams0'

Page 59: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

And you can play with some of these params to change the Karma properties…but I leave that to you. I’m just using the defaults in the sample code, so it’s really slippery for the KRock’s

The only function to note is the tick function which calls KWake, so the KRock can never “sleep”.

Page 60: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

And finally, the network parameters.

These are so that the object is deemed not “fixed in place”. And Karma can updated it’s position and send it over the wire…eg it’s deemed relevant.

bStatic=false //req'dbNoDelete=false //req'dbHidden=false

Page 61: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

More to guarantee the objects relevance to the client…

Btw: I likely don’t need the bGameRelevant. But when I wrote up the lesson, I was no where near a network to test out its absence.

bGameRelevant=TruebAlwaysRelevant=True //req'dbNetRelevant=true //req'd

Page 62: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

We update the movment oof the objet so they stay in sync, and change the update frequency to reflect how frequely this object is updated by the server.

bReplicateMovement=trueNetUpdateFrequency=10.000000NetPriority=2.500000bNetInitialRotation=True

Page 63: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

As found in the actor class NetUpdateFrequency reflects how many seconds between net updates.

This should reflect the importance of your object.

E.g. if the NetPriority is high (important) I would expect the NetUpdateFrequency to be low (more frequent).

Page 64: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Krock

And lastly, change the remote role so that the client can anticipate where the Krock will be…

RemoteRole=ROLE_SimulatedProxybUpdateSimulatedPosition=True

Page 65: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

Now, it’s actually the player controller that does the work in this case.

This controller is really responsible for two actions, spawning the KRock through the fire command, and count the number spawned.

Page 66: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

Thus the fire function:exec function Fire( optional float F ){ local vector l; local Krock t;

if(ctr >= 5) return; else ctr++;

Simple counting

Page 67: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

Change what function gets called if we are not the server!

if(role < Role_AUTHORITY){ clientFire(f); return;}

Page 68: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

Then Spawn the Krock

l=pawn.location; l.z+=64; super.Fire(f); t= Spawn(class'Krock',pawn,,l); t.Velocity=50*vector(pawn.Rotation);

Page 69: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

But his is the interesting bit

The client must call this function so that the server know that a new KRock has been spawned…otherwise it’s only client side!

if(role < Role_AUTHORITY){ clientFire(f); return;}

Page 70: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

Which brings us to:simulated function clientFire( optional float F ){ local vector l; local Krock t;

l=pawn.location; l.z+=64; super.Fire(f); t= Spawn(class'Krock',pawn,,l); t.Velocity=50* vector(pawn.Rotation);}

Which does fundamentallythe same thing…

Page 71: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

But the function itself is SIMULATED.

Meaning that it can be called by a remote machine. This is how the server knows to spawn the KRock.

Page 72: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

And lastly, we need a replication statement.

replication{ reliable if(role < Role_AUTHORITY) clientfire; reliable if(role == Role_AUTHORITY) ctr;}

Page 73: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

Epc: the player controller

Without the replication statement, even if the rest of the code is fine, we will not be able to see KRocks spawned on client machines.

Page 74: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

On an interesting note:

One this is up and running, take a look in the client machines and the servers log files.

In the sample code I left in some log entries in the Epc.

Page 75: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

On an interesting note:

In the fire function:

ClientFire function:

log("Server----Fire"@t);

log("Client----FIre"@t);

Page 76: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

On an interesting note:

The client machine, shows no entries at all with regards to the client fire log...none…nada…zip.

However, the server machine show entries from both client and server.

Page 77: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

On an interesting note:

This means that it is the server that is in fact doing the spawning Krock and execution of the of the simulated function, which gets sent back to the client machine.

Page 78: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

And what we should have

Is a bunch of KRocks spinning around the level that get properly replictated on both client and server.

Page 79: Sega 500 An Introduction to Replication Jeff “Ezeikeil” Giles jgiles@artschool.com jgiles

For fun

Try changing the parent class of Krock to one of the projectiles, or even replace the spawning of the KRock with a projectile.

Then remove the replication statement. Notice the changes this incurs.