Search public documentation:
UActorChannelis created on the connection, which causes an actor of the appropriate class to be spawned on the client, with the defaultproperties of the actor. If the server set variables immediately after the spawning of the actor, (before the relevancy checks), then assuming those variables meet the replication statements, they will be sent to the client as an addendum to the actor's data. Since replication is not done until the end of the current tick, you have a lot of time in which to set the variables for their initial replication with the actor itself. These variables that are set on the actor will be received on the other end, and the other end will then call PostNetBeginPlay (since PostBeginPlay is called before the actor's variables are read off the network, into memory.) In deciding what vars to replicate initially, the server will send any variables that differ from the defaultproperties. If the server has a different idea of the defaultproperties of an actor versus the client, then the server will only replicate the variable if it differs from the server's version. (By the same token, if a variable is changed in client-side code, the server will not know about it, and so the server will not replicate changed data.) You might think that changing an actor's defaultproperties on both the client and the server might solve the problem, but if an actor goes un-relevant, than simulated functions cannot be called on the actor, and when the actor becomes relevant again, the defaultproperties on the server and the client will be different. In general, don't change the defaultproperties of a variable unless the variable has no networking properties. As an example of the bad things that can happen, an Unreal Tournament mod had set both the
Default.Meshto the playerclass's mesh that the player had chosen. Players however, go in and out of scope, to save on network bandwidth. Normally they are only relevant when you can see them, and for about three seconds once they disappear from view. Let's say the player changed their playerclass at that point, after they had gone out of relevancy. Or say they changed their class at the start of the game while the round has not started and they are invisible, and thus not relevant. When they do become relevant to the client again, (say they turned a corner or the round just started and they are spawned and made visible,) the
Mesh == Default.Mesh, and so the
Meshis never replicated. The skin however, was still replicated normally. To clients in the game, they saw the player as the original mesh, with a inappropriate skin that was intended for another model. Strange behavior that can easily be avoided by treading lightly on the default variables for network variables. In this case, the
Default.Meshwas not needed for everything, and removing that assignment caused everything to work fine.
nativereplicationkeyword in their class definition. These classes ignore the script conditions and replicate properties based on the C++ GetOptimizedRepList() function instead. However, the UnrealScript? replication block is still required to list all properties that could be replicated so the compiler can mark them as such, which is required for the properties to be sent correctly.
bNetInitialvariable to replicate something when the actor is first relevant, and then rely upon simulated functions executing on both the server and client to change the value from then on. The variable
bNetInitial, and it's friends, is discussed next.
- 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.
- true if the player we are replicating to owns this actor directly.
- Actor is currently relevant. Only valid server side.
- bDemoRecording bClientDemoRecording bRepClientDemo bClientDemoNetFunc bDemoOwner
- used for demo recording purposes.
reliable if ( Role < ROLE_Authority ) Password, bReadyToPlay;This here shows where your Password is sent to the server, when trying to join passworded servers, or to log in as an admin to that server. The server needs your password in order to validate it, and this code sends it to the server. Let's evaluate this check both on the server and on the client.
- On the server,
Role == ROLE_Authority. Looking at the conditional, it can easily be shown to be false, and so the Password is not replicated server-side. This means that the server does not send the Password to the other end of the connection.
- Since on the client, the
ROLE_Authority(remember it's only Authority on the server itself), this replication check evaluations to true on the client. And since variable replication only works in your current PlayerPawn, it will be sent to the server for yourself alone. (It makes sense not to send other's passwords, doesn't it? ;)
bReadyToPlay, which is used in Tournament style games, where everyone has to click to start the game. Note that this is only sent for yourself, since the client can only replicate variables to the server if they are part of it's own playerpawn actor.
unreliable if( Role == ROLE_Authority ) Owner, Role, RemoteRole;This ensures that the owner of an object is always replicated to the client. Remember that the owner's variables themselves are not replicated, (because there is no recursive replication,) but the actor itself is. This means that simple comparisons
if (PlayerPawn?is replicated to the client. Remember that the unreliable versus reliable makes no difference in UT, as they are both treated equivilently. They may have had an effect at some point during the Unreal 1 days, but they no longer play a factor in development. Don't let that confuse you. Replicating
= Owner)will work fine because the reference to the =Owner
RemoteRolemay seem strange at first, since they should be reversed on the client. However, there's native code that causes that switch to be made. On the client, it will ensure that those two variables are reversed, so that
ROLE_Authority. You may wonder why they are even replicated in the first place. While the client never has any direct use for these variables in the code, there is one easy-to-overlook place where they are vitally important: Replication Statements. When the client is evaluating whether it needs to replicate a certain function call to the server, or a playerpawn's variable to the server, the client needs accurate copies of what
RemoteRoleare. Without those, it would be unable to make the necessary decisions on what to send to the server. And since the server checks the validity of the replication data sent to the server with
Role/RemoteRole, the client needs the latest copy in order to successfully replicate that data.
unreliable if( bNetOwner && Role == ROLE_Authority ) bNetOwner, Inventory;Inventory is the head of the linked list that lists the entire Inventory for the Actor (usually only utilized with
Pawn). We want the inventory to be replicated to the client, so that he knows what Inventory he has, to display on his HUD, and stuff (makes sense, right?). Simply replicating the head of a linked list (where each object points to the next in the list) is not enough however. Each actor must itself be relevant (satisfied by the criterion that it must be owned by the current playerpawn), and each link itself must be replicated. And since Inventory subclass Actor somewhere in the chain, Inventory also has a replicated Inventory variable. And that's how you are able to see your Inventory client-side. And since I don't need to know the full Inventory list of what every other player in the game has (the current weapon and the shieldbelt effect are transferred via other means), the Inventory will only be replicated if the client is the owner. The other variable in the above replication statement is bNetOwner. Since this is set natively on both the client and server, this variable does not need to be replicated. It could be argued that it adds a tiny amount to bandwidth, but its effect is probably negligible. Perhaps it will be removed in a future patch.
unreliable if( DrawType == DT_Mesh && Role == ROLE_Authority ) Mesh, PrePivot, bMeshEnviroMap, Skin, MultiSkins, Fatness, AmbientGlow, ScaleGlow, bUnlit;Here we see that all these mesh-specific variables are ONLY replicated if this actor is currently being displayed as a mesh. If it's a sprite, or a brush, or even no drawtype at all, there is no reason to send these variables. Now let's get into some of the more complex variable replication statements...
unreliable if( RemoteRole == ROLE_SimulatedProxy ) Base;Here we see a slightly more complex replication statement, that does not involve
Role == ROLE_Authority. Here we see that the current
Base(set via SetBase) is only replicated if the actor is set to be a simulated proxy. If you set the actor to a
DumbProxy, you'll see no
Basechange clientside, and instead will get the jerky
Locationupdates that the server sends to you, (more on this later). So if you are using SetBase, make sure it's a simulated proxy, or if it's not a simulated proxy, use something other than SetBase. Or if you need both SetBase and a simulated proxy, the Base will not be replicated for you. You'll have to work out some other mechanism to do that, probably a simulated function that sets the base, so it is run on the client, (more on this later, too.) You might still have troubles when the actor gets its simulated function called while the actor was not relevant. This results in the simulated function never being called clientside, and then when the actor does finally become relevant, it's as if that function was never called (and the base isn't set), which will result in those things affectionately referred to as 'bugs.' You can make the actor being attached (and what it's being attached to)
bAlwaysRelevantso that the simulated functions will always be called, but that might be a bit too harsh on network bandwidth.
unreliable if( RemoteRole == ROLE_SimulatedProxy && Physics == PHYS_Rotating && bNetInitial ) bFixedRotationDir, bRotateToDesired, RotationRate, DesiredRotation;This one is still relatively easy, but it's getting more complex. Here we have similar logic to what was seen above, with the server only sending these variables to the client if the client is a simulated proxy. However, since all these variables apply ONLY to
PHYS_Rotating, there is no need to replicate these variables to the client if the client is not using
PHYS_Rotating. And finally, a very important clause at the end is the
bNetInitialone. This states that these variables will only be replicated to the client when it is being replicated for the first time.
unreliable if( bSimFall || (RemoteRole == ROLE_SimulatedProxy && bNetInitial && !bSimulatedPawn) ) Physics, Acceleration, bBounce;Here we have some of the interesting variables replicated. We see that if the
bSimFallis set to true, it will replicate the
Physicsto the client. This is used when tossing weapons from your inventory. When they are tossed, they require a
PHYS_Nonewhile sitting in your Inventory to
PHYS_Fallingas they fly through the air. They then get set back to
PHYS_Nonewhen they land on the ground. All these
Physicschanges are accomplished by setting
bSimFallat the key points during the TournamentWeapon's life. It is set to true when it is thrown from the inventory, to capture the change, and left on until it hits the ground, where it changes
Physicsagain. After it finally hits and has its physics reset,
bSimFallis set to false so no further
Physicschanges occur. Looking at the second half of the statement, we see that the
Physicsis replicated ONLY if it's a simulated proxy, it's the first time this actor is being replicated over the internet, AND it's not a simulated pawn. The
bSimulatedPawnvariable is set to true if it's a Pawn with a
ROLE_SimulatedProxy(who would have thought? :) This means any changes to the
Physicsof a simulated proxy non-pawn actor before it is replicated will be replicated to the client. The
Physicsare never replicated for pawns. Rather, the code for their physics is hard-wired into the code to cause them to be pushed to the ground. Basically, when a pawn jumps, the server sets his vertical velocity so that he travels upwards. That Velocity is then replicated to the client (described below). The client then checks if the pawn is a player (eg: a bot or a playerpawn), that they are currently unable to fly (set via the Pawn's
bCanFlyvariable), and that they are not in a water zone (since that involves different gravities and physics). So in fact, the
Physicsfor a pawn is never replicated to the client, it only appears to do so. If you are attempting to create alternate physics with the playerpawn, you will need to ensure that you set
bCanFlyto true so that the native code does not enforce the falling Physics upon the player when he's on the wall or in the air. You must then implement the alternate means of transportation yourself.
unreliable if( !bCarriedItem && (bNetInitial || bSimulatedPawn || RemoteRole < ROLE_SimulatedProxy) && Role == ROLE_Authority ) Location;Here we see another important variable,
Location, and it's replication. We see that it is not replicated for carried items. This is useful in the case of
Inventory. When a player is carrying it, there's no reason its location should be replicated, since it isn't used for anything. replicating a player's entire inventory would be quite the strain on network bandwidth. Let's look at the next section of the statement. The location is replicated if this is the first time the actor is being replicated (assuming the other parts of the conditional are true). This is very useful, since all pawns will be spawned in different locations when you enter the game, and rockets will need their start locations set when they spawn out of a rocket launcher, etc. The location is also sent if this is a
bSimulatedPawn. This helps correct any errors that may occur in replicating pawns. Since a pawn can change direction, and a client may not always know about it (due to lag, etc), this location resetting is the only way in which the locational updates can be 'corrected'. Note that this is not used to correct your own location when you experience lag. When that happens, you are an
AutonomousProxy, and functions specific to PlayerPawn handle that (namely
ClientAdjustPosition). For example,
PHYS_Walkingis not like
PHYS_Projectile, where the position can be predicted with great accuracy.
PHYS_Walkingjust means "keep them attached to the ground", basically, The client's only updates (without location) would have been their velocity, which can easily lead to errors in the player's movements over any significant amount of time. This location is used as a correcting factor, making sure the client's view does not stray too far from where he expects the player to be, while at the same time keeping the velocity's replication so he can be predicted inbetween server updates. The other option for allowing the
Locationto replicate is that of where the
RemoteRoleis less than a
ROLE_SimulatedProxy, namely, a
ROLE_DumbProxy, since the
ROLE_Noneprevents relevancy in the first place.
DumbProxiesget periodic updates from the server every few ticks, and would create a jumpy effect in netplay. It was the cause of a jerky puck in the hockey mod I attempted a while ago, although I knew nowhere near enough to fix it at the time. A
SimulatedProxywould not get sent
Locationupdates, since it would be predicted through the use of the
Velocityand the current Physics. Since
DumbProxiesaren't simulated client-side to achieve smoothness, they only get the
Locationupdates. I'm going to clear this all up after the examples of the various replicated variables.
unreliable if( !bCarriedItem && (DrawType == DT_Mesh || DrawType == DT_Brush) && (bNetInitial || bSimulatedPawn || RemoteRole < ROLE_SimulatedProxy) && Role == ROLE_Authority ) Rotation;Here we see another important variable,
Rotation. This also has the same
!bCarriedItemclause, for the same reasons as described above. The rotation is also only replicated if this actor is a mesh or a brush. Since sprites always face the player, replicating their rotation is useless. The
bNetInitialalso causes the rotation to be replicated the first time through, (again, assuming if the other conditions are true,) since rockets need to be facing in the right direction when it has them flying through the air. Their velocity determines their direction of movement, but the direction they are facing which is equally important, is determined by their rotation. The rotation is replicated if they are a simulated pawn (every playerpawn and bot in the level except your own self). This is so you can see what direction the other people are facing. The
ViewRotation(which determines where the client is looking), is sent to the server via ServerMove, where it is translated into a rotation. it is this rotation that is then replicated to the clients. And finally, if it is a
DumbProxy(the only valid one less than
SimulatedProxy), it also gets the rotation updates. Rotation updates do not need any interpolating between updates like location does. Location lag is much more noticeable when the player is moving quickly, but you never really notice
Rotationlag. Besides, there is no real way to predict or forecast rotation. It depends entirely upon the other user's mouse.
unreliable if( bSimFall || ((RemoteRole == ROLE_SimulatedProxy && (bNetInitial || bSimulatedPawn)) || bIsMover) ) Velocity;Here's one of the last important variables with a complex replication statement. The velocity is replicated if it has
bSimFallset, (used when throwing weapons from your inventory). The
PHYS_Fallingalone isn't enough. It needs to know exactly what it's initial velocity is when it is launched from the pawn. And no,
bNetInitialwill not work here, since it's not the first time it's being replicated. It's existed as a weapon for quite some time. It's just for the initial velocity of when it's being thrown from the player that we want updates. Moving along, we see that
Velocitiesreplicated if it the first time on the replication channel, for newly spawned rockets, or for grenades, or shock projectiles, etc.
SimulatedProxiesalso get their velocities replicated if they are a simulated pawn, as all the various pawns need their velocities replicated so that they can be predicated locally. And finally, movers get their velocities replicated, so that the client can accurately predict the mover's movement locally. In the early Unreal 1 days, mover's velocity was not replicated, and so the client got periodic update locations because it was a
DumbProxy. This resulted in very jumpy movers in netplay, something that was fixed for Unreal 224+.
unreliable if( DrawType == DT_Mesh && ((RemoteRole <= ROLE_SimulatedProxy && (!bNetOwner || !bClientAnim)) || bDemoRecording) ) AnimSequence, SimAnim, AnimMinRate, bAnimNotify;There are a few more variables here, all of which relate to animation. These variables are only replicated if it is currently being drawn as a Mesh. If we are recording a demo, then the animations are always sent. Otherwise, it checks the somewhat confusing conditional. If the actor is a
DumbProxy, and the player is not the owner of this object, and
bClientAnimis not set, then it replicates the animation variables. In the case of weapons, you see those animations clientside, and so the animation variables do not need to be replicated from the server. That is because all TournamentWeapons have the
bClientAnimset to true, indicating that their animations are handled clientside. If the animations are not handled clientside, and the actor is a dumb proxy, then it will send them from the server. Simulated proxies, which include pawns and rockets and other projectiles, are all simulated proxies, and so they do not receive animations from the server. Instead, the client animates them with its client-side prediction. In the case of pawns, this is handled internally in the engine (for another tutorial ;), and you need not worry about it.