Looking for feedback on my logic and rough implementation. Any and all comments are appreciated!
// CLIENT CLIENT STUFF
// VERSION 3.0
// Greg Wilson - gswilson@shaw.ca
// Concept: Multiplayer gametype, similar to BF:1942 in that players can jump into 'tanks' and then enter 'turrets'. Once a player is in the turret
// his mouse movements control the turret's yaw and pitch.
// The turretPawn extends from Pawn and the turretController extends from PlayerController.
// Problem: We need clients to see other clients correct turret rotations. This logic doesn't appear to solve the problem when implemented. The clients still
// cannot see any server or other client turret rotations. Any suggestions or improvements are greatly appreciated.
// Logic:
// Every tick, the PlayerTick() function is called. It calls the PlayerMove() function ( which is implemented in various states).
// PlayerMove() figures out the rotation changes the player has requested and then calls turretServerOnlyProcessMove()
// for single player or server Player and turretReplicateMove() if it’s a network client player.
// Server initiated moves: (by player on the server box)
//
// turretServerOnlyProcessMove() calls turretProcessMove() which updates the servers rotation and then calls
// turretClientAdjustPosition() on the clients to do the same.
//
// turretProcessMove() updates the bone rotation of the turret based on the players input. Both client & server run this
// independently.
//
// turretClientAdjustPosition() is executed on the client. The client sets its rotation to the servers version.
//
// Client initiated moves: (by the player on a client box)
//
// turretReplicateMove() calls turretProcessMove() and then executes the move on the server by calling the replicated
// function turretServerMove() and passing in the new rotation value.
//
// turretServerMove() is executed on the server only. It calls turretProcessMove() which updates the server to the clients
// rotation
//
// Client Client Updates: ??
//
// the controller populates each 'player initiated move' into 'theRotation' variable of the pawn - mid tick(). Because variables are
// replicated at the end of the tick(), it will be a tick behind, but should theoretically cause all clients have their 'theRotation'
// variables synced with the server values and at the start of the next tick, the pawn's themselves will update their bone rotations
// using 'theRotation' value and a SetBoneRotation() call in their tick() function.
//
// A Local function means a function that is only changing data on its own box. They can be run on client or server boxes by themselves.
// Some local functions may call client or server specific functions in addition to performing a duty on its own box.
//
// From Unreal Networking Architecture Page: http://unreal.epicgames.com/Network.htm
// Variable replication occurs only after a tick completes. Therefore, if in the duration of a tick, a variable changes to a new value,
// and then it changes back to its original value, then that variable will not be replicated. Thus, clients only hear about the state of
// the server's actor's variables after its tick completes; the state of the variables during the tick is invisible to the client.
// There is some confusion as to what the simulated keyword does. One document says it needs to be used in order for any function to be run on the client,
// others say it needs to be used if the server wants to call a client only function on a client.
// When you take a look at 'UnrealWiki: Simulated Functions' you will see that functions don't need to be simulated if the actor's Role is
// ROLE_AutonomousProxy or ROLE_Authority.
// Based on this, it leads me to believe that any function called by a client on the server, needs to be simulated?? (to be safe)
// Uncertainties:
// can clients run non simulated functions on themeselves? i.e.: is my explanation of 'a local function' correct.
// do client only functions, called BY the server (via replication statement) need to be simulated? // if we're dealing with a playercontroller and Pawn
// do server only functions, called BY the client (via replication statement) need to be simulated? // if we're dealing with a playercontroller and Pawn
// what keywords does the TurretPawn need to have set?
// bAlwaysRelevant=true, bGameRelevant=true, bStatic=false, bNoDelete=true, bNetRelevant=true, bReplicateMovement=true
// NetUpdateFrequency=1.0, NetPriority=3.0, RemoteRole=ROLE_SimulatedProxy, bReplicateAnimations=true, ??
// example:
// contains some pseudo code.
// CLIENT CLIENT STUFF
// VERSION 3.0
// Greg Wilson - gswilson@shaw.ca
// Concept: Multiplayer gametype, similar to BF:1942 in that players can jump into 'tanks' and then enter 'turrets'. Once a player is in the turret
// his mouse movements control the turret's yaw and pitch.
// The turretPawn extends from Pawn and the turretController extends from PlayerController.
// Problem: We need clients to see other clients correct turret rotations. This logic doesn't appear to solve the problem when implemented. The clients still
// cannot see any server or other client turret rotations. Any suggestions or improvements are greatly appreciated.
// Logic:
// Every tick, the PlayerTick() function is called. It calls the PlayerMove() function ( which is implemented in various states).
// PlayerMove() figures out the rotation changes the player has requested and then calls turretServerOnlyProcessMove()
// for single player or server Player and turretReplicateMove() if it’s a network client player.
// Server initiated moves: (by player on the server box)
//
// turretServerOnlyProcessMove() calls turretProcessMove() which updates the servers rotation and then calls
// turretClientAdjustPosition() on the clients to do the same.
//
// turretProcessMove() updates the bone rotation of the turret based on the players input. Both client & server run this
// independently.
//
// turretClientAdjustPosition() is executed on the client. The client sets its rotation to the servers version.
//
// Client initiated moves: (by the player on a client box)
//
// turretReplicateMove() calls turretProcessMove() and then executes the move on the server by calling the replicated
// function turretServerMove() and passing in the new rotation value.
//
// turretServerMove() is executed on the server only. It calls turretProcessMove() which updates the server to the clients
// rotation
//
// Client Client Updates: ??
//
// the controller populates each 'player initiated move' into 'theRotation' variable of the pawn - mid tick(). Because variables are
// replicated at the end of the tick(), it will be a tick behind, but should theoretically cause all clients have their 'theRotation'
// variables synced with the server values and at the start of the next tick, the pawn's themselves will update their bone rotations
// using 'theRotation' value and a SetBoneRotation() call in their tick() function.
//
// A Local function means a function that is only changing data on its own box. They can be run on client or server boxes by themselves.
// Some local functions may call client or server specific functions in addition to performing a duty on its own box.
//
// From Unreal Networking Architecture Page: http://unreal.epicgames.com/Network.htm
// Variable replication occurs only after a tick completes. Therefore, if in the duration of a tick, a variable changes to a new value,
// and then it changes back to its original value, then that variable will not be replicated. Thus, clients only hear about the state of
// the server's actor's variables after its tick completes; the state of the variables during the tick is invisible to the client.
// There is some confusion as to what the simulated keyword does. One document says it needs to be used in order for any function to be run on the client,
// others say it needs to be used if the server wants to call a client only function on a client.
// When you take a look at 'UnrealWiki: Simulated Functions' you will see that functions don't need to be simulated if the actor's Role is
// ROLE_AutonomousProxy or ROLE_Authority.
// Based on this, it leads me to believe that any function called by a client on the server, needs to be simulated?? (to be safe)
// Uncertainties:
// can clients run non simulated functions on themeselves? i.e.: is my explanation of 'a local function' correct.
// do client only functions, called BY the server (via replication statement) need to be simulated? // if we're dealing with a playercontroller and Pawn
// do server only functions, called BY the client (via replication statement) need to be simulated? // if we're dealing with a playercontroller and Pawn
// what keywords does the TurretPawn need to have set?
// bAlwaysRelevant=true, bGameRelevant=true, bStatic=false, bNoDelete=true, bNetRelevant=true, bReplicateMovement=true
// NetUpdateFrequency=1.0, NetPriority=3.0, RemoteRole=ROLE_SimulatedProxy, bReplicateAnimations=true, ??
// example:
// contains some pseudo code.
Code:
class TurretController extends PlayerController; replication { if role < role_authority // sync the servers version of client initiated moves. turretServerMove(); if role == role_authority // sync clients version of server initiated moves. turretClientAdjustPosition() } // PlayerMove() is called via playerTick() a local, non simulated, non replicated function // Both clients and server call this themselves. // at this point, all the possesing etc... is done and the player is controlling the bone rotation of a turret with his mouse. // state controlTurret // does this state need to be simulated? { function playerMove() // does this function need to be simulated? { newRotation = player input; // psuedo code. if ( Role < ROLE_Authority ) turretReplicateMove(newRotation); // a player has initiated a move on a client. execute this move and replicate it else { // a player has initiated a move from the server, move him. turretServerOnlyProcessMove(newRotation);// this function calls another function on the client, updating it to the servers 'state' } } } // local, non simulated function. It updates the actual values from playerMove function turretProcessMove(newRotation) { // this function can run on any box, it simply updates the rotation on itself. pawn.SetBoneRotation( newRotation ); // rotate bones here. this is psuedo code. } // local, non simulated function. a client only function. function turretReplicateMove(newRotation) // a local function, dont let the name fool you. its not really replicated ( // but it's calling turretServerMove, which IS replicated. turretProcessMove(newRotation) // carry out the move from input by player on a client. turretServerMove(newRotation); // carry out this move on the server too } // this function is called on the server, by a client. using the < role_authority in replication statement. function turretServerMove(newRotation) // replicated, non simulated function { turretProcessMove(newRotation); // sync's up the server to the clients move. // v3.0 additions TurretPawn(pawn).theRotation = newRotation; // for use in pawns tick(), each clients rotation is saved on the pawn, mid tick() } // simulated function. this server only function calls a client only function. Simulated functions can only be called by // other simulated functions simulated function turretServerOnlyProcessMove(newRotation) { turretProcessMove(newRotation); // carry out the move from input player on server. turretClientAdjustPosition(newRotation); // do same move on client version of the server player. TurretPawn(pawn).theRotation = newRotation; // v3.0 additions. makes sure the server updates too } // simulated function so it can be called on a client, by the server. simulated function turretClientAdjustPosition(newRotation) { pawn.SetBoneRotation( newRotation ); // same as turretProcessMove, but client specific. this is psuedo code. } //v3.0 additions //this is the pawn class that's controlled by the turretController. class turretPawn extends Pawn; var rotator theRotation; replication { if(role == role_authority ) // broadcast servers versions of rotations a the end of each tick. theRotation; // would eventually incorporate bNetDirty into this to improve performance. } // at the start of each tick, every box will update its bone rotations from the last ticks data. function Tick(float deltaTime) { SetBoneRotation(theRotation); // this is psuedo code. super.Tick(deltaTime) } defaultProperties { bAlwaysRelevant = true bGameRelevant = true }
Comment