Looking at the replication glossary (http://udn.epicgames.com/Three/Repli...ents/functions).
- bOnlyRelevantToOwner - This actor is only relevant to its owner.
- bReplicateInstigator - Replicate the instigator variable to the client.
- bOnlyDirtyReplication - Only replicate this actor if bNetDirty is true. This is only used if bAlwaysRelevant is true.
- RemoteRole - The role for the actor on the remote run time.
Right, so right from the start we know that Weapons are only ever relevant to the owner. We also know that weapons always replicate the instigator variable to the client. We also know that this Weapon is not only replicated when bNetDirty is true, since bOnlyDirtyReplication is false. And we know that the remote role controls the role on the remote machine. Ok, so we've now got more information about things we don't know. What's relevancy, what's the instigator variable? What's it mean by bNetDirty? What is role?
So a UDN search for relevancy reveals this documentation (http://udn.epicgames.com/Three/Repli...Relevancy.html). Oh right! So relevancy indicates that some Actors are important to some clients, and if they aren't relevant then they aren't sent to the other clients! So, based that we know that this actor is only relevant to its owner ... we need to know what the Owner is. So searching through code we find...
Code:
var const Actor Owner; // Owner actor.
Right, so it's an Actor reference to another actor. So who owns a Weapon? So let's search in UTGame, and how weapons are given to players. Searching for any one of the UT Weapon classes, we find a function in UTCheatManager...
Code:
/* AllWeapons
Give player all available weapons
*/
exec function AllWeapons()
{
if( (WorldInfo.NetMode!=NM_Standalone) || (Pawn == None) )
return;
GiveWeapon("UTGame.UTWeap_LinkGun");
GiveWeapon("UTGameContent.UTWeap_RocketLauncher_Content");
GiveWeapon("UTGameContent.UTWeap_ShockRifle");
GiveWeapon("UTGame.UTWeap_Physicsgun");
}
So what the heck does GiveWeapon() do? Searching through the code base, we find this function in CheatManager...
Code:
/**
* Give a specified weapon to the Pawn.
* If weapon is not carried by player, then it is created.
* Weapon given is returned as the function's return parmater.
*/
exec function Weapon GiveWeapon( String WeaponClassStr )
{
Local Weapon Weap;
local class<Weapon> WeaponClass;
WeaponClass = class<Weapon>(DynamicLoadObject(WeaponClassStr, class'Class'));
Weap = Weapon(Pawn.FindInventoryType(WeaponClass));
if( Weap != None )
{
return Weap;
}
return Weapon(Pawn.CreateInventory( WeaponClass ));
}
Right! So GiveWeapon() asks the Pawn to CreateInventory... so let's find that.
Code:
event final Inventory CreateInventory( class<Inventory> NewInvClass, optional bool bDoNotActivate )
{
if ( InvManager != None )
return InvManager.CreateInventory( NewInvClass, bDoNotActivate );
return None;
}
Right, so Pawn.CreateInventory() just calls InvManager.CreateInventory(). So what is InvManager?
Code:
var repnotify InventoryManager InvManager;
So we see that it's a reference to an InventoryManager. Ok, so what does CreateInventory() do within InventoryManager.
Code:
/**
* Spawns a new Inventory actor of NewInventoryItemClass type, and adds it to the Inventory Manager.
* @param NewInventoryItemClass Class of inventory item to spawn and add.
* @return Inventory actor, None if couldn't be spawned.
*/
simulated function Inventory CreateInventory(class<Inventory> NewInventoryItemClass, optional bool bDoNotActivate)
{
local Inventory Inv;
if( NewInventoryItemClass != None )
{
inv = Spawn(NewInventoryItemClass, Owner);
if( inv != None )
{
if( !AddInventory(Inv, bDoNotActivate) )
{
`warn("InventoryManager::CreateInventory - Couldn't Add newly created inventory" @ Inv);
Inv.Destroy();
Inv = None;
}
}
else
{
`warn("InventoryManager::CreateInventory - Couldn't spawn inventory" @ NewInventoryItemClass);
}
}
return Inv;
}
Right, so this class spawns the inventory and adds it to itself. Aha, we see it using Owner! So let's look up the definition of Spawn to see why Owner is used there.
Code:
/** Spawn an actor. Returns an actor of the specified class, not
* of class Actor (this is hardcoded in the compiler). Returns None
* if the actor could not be spawned (if that happens, there will be a log warning indicating why)
* Defaults to spawning at the spawner's location.
*
* @note: ActorTemplate is sent for replicated actors and therefore its properties will also be applied
* at initial creation on the client. However, because of this, ActorTemplate must be a static resource
* (an actor archetype, default object, or a bStatic/bNoDelete actor in a level package)
* or the spawned Actor cannot be replicated
*/
native noexport final function coerce actor Spawn
(
class<actor> SpawnClass,
optional actor SpawnOwner,
optional name SpawnTag,
optional vector SpawnLocation,
optional rotator SpawnRotation,
optional Actor ActorTemplate,
optional bool bNoCollisionFail
);
Ahh right! So when spawning an actor, you can define its owner, right from the start. So when weapons are spawned, they have their Owner set to the same Actor that owns the InventoryManager. So who owns the InventoryManager? So let's find where InvManager is spawned.
Code:
// Spawn Inventory Container
if (Role == ROLE_Authority && InvManager == None && InventoryManagerClass != None)
{
InvManager = Spawn(InventoryManagerClass, Self);
if ( InvManager == None )
`log("Warning! Couldn't spawn InventoryManager" @ InventoryManagerClass @ "for" @ Self @ GetHumanReadableName() );
else
InvManager.SetupFor( Self );
}
Ahh right, so the InventoryManager is owned by the Pawn that spawned it. Is the pawn owned by anyone else?
Code:
function PossessedBy(Controller C, bool bVehicleTransition)
{
Controller = C;
NetPriority = 3;
NetUpdateFrequency = 100;
bForceNetUpdate = TRUE;
if ( C.PlayerReplicationInfo != None )
{
PlayerReplicationInfo = C.PlayerReplicationInfo;
}
UpdateControllerOnPossess(bVehicleTransition);
SetOwner(Controller); // for network replication
Eyeheight = BaseEyeHeight;
if ( C.IsA('PlayerController') )
{
if ( WorldInfo.NetMode != NM_Standalone )
{
RemoteRole = ROLE_AutonomousProxy;
}
// inform client of current weapon
if( Weapon != None )
{
Weapon.ClientWeaponSet(FALSE);
}
}
else
{
RemoteRole = Default.RemoteRole;
}
//Update the AIController cache
if (Weapon != None)
{
Weapon.CacheAIController();
}
}
Aha! So the Pawn is owned by the Controller that possess it in the end.
So after all that, it is deduced that the Weapon is only relevant to the Owner which is either a PlayerController or AIController. If the Weapon is only relevant to the Owner (as defined by bOnlyRelevantToOwner); and since relevancy determines who receives information... then that means that Weapon is only ever replicated to a single PlayerController or AIController.
If no replication occurs then this would explain why other clients wouldn't get each other Weapons. So if the Weapon doesn't exist on the client, does this mean that the Weapon function replication wouldn't get called?
According to (http://udn.epicgames.com/Three/Funct...nction%20calls),
On the client, every function has to originate from a 'source'. A source can be a server-client replicated function, an exec function, or an event. Events, while not covered previously, are quite simple. An event is any function that is called from native code. Events happen both on the server and on the client. The server does not directly tell the client to execute the event, but the client knows to execute the event based upon the game state (colliding actors, hitting a wall, a timer event, ticking of the game state, etc). If that event is simulated, then it can also serve as a 'source' of client side function calls. A source can call any function it wishes, assuming it meets the client side execution conditions, which can then call any...and so on.
Right, so every function has to originate from a source. If there is no corresponding actor, there can't be a server-client replicated function, exec function or an event because there simply isn't an instance of that Actor on the client.
Bookmarks