Unet - NetworkServer.Spawn() correct usage

Hello,
I am working on an RTS game: I have a gamelord class (attached to its own object) that is supposed to handle the status of the game, then i have a playerController class that handles player actions and is attached on the player prefab, which gets fed to the network manager and spawned correctly when a new client connects to the game. The idea would be to centralize all gameplay actions into the gamelord and separate all the local stuff that needs to happen to other classes.

The gamelord is “unique” in the sense that it’s meant to hold the same information on each client, most of its variables are syncvars, and all of its functions are supposed to be things that should happen on the server and then echo on every client, so stuff like creating a unit or building , keeping track of winning/losing conditions, etc.

playerControllers on the other hand handle the local player’s input and then translate that input into game actions by calling the appropriate functions on the gamelord. So if the player builds something, the playerController class would do something like “gamelord.Build(something)” and then the gamelord would actually instantiate and spawn that something.

So, in code, here is what i am trying to do

on the playerController :

[Command]public void CmdCreateBuildingHere(Vector3 pos,GameObject what)
    {        
       //some other non-related code
        gameLord.SpawnBuilding(playerId, pos, what);
    }

then, on the gamelord

 [Server]
    public void SpawnBuilding(string owner,Vector3 where,GameObject what)
    {
        GameObject newBuilding = (GameObject) GameObject.Instantiate(what,where,Quaternion.identity);
        RTSGamePiece newbuildingCtrl = newBuilding.GetComponent<RTSGamePiece>();
        newbuildingCtrl.owner = owner;        
        NetworkServer.Spawn(newBuilding);
    }

This approach does not work, as it behaves correctly only on the host, while if the client builds something, it won’t show on the server.

On the other hand, moving the SpawnBuilding() lines of code directly into the first function and forgetting about the gamelord does make it work, but architecturally it’s not what i meant to do, and it seems less logical to give more weight to the player object in an RTS where the important objects are not the player objects (like in FPSes).

All the objects that i want to spawn are already registered in the netmanager (i did it manually via the editor, the prefab for the netmanager already contains all the prefabs it needs in the “spawn info” section), so that should not be the cause.

My question is:
Considering the constraints of Unet (some of which i am aware, such as commands only working on playerObjs or prefab registering, and many of which i’m not) ,is there a way to do this in the way i wanted it to?

Specifically:in the client, have the playerController class calling a function in the gamelord, and then on the server have the gamelord actually instantiating the object and spawning it across the network.

Should i rethink my approach? Am i misusing Commands and the [Server] attribute? Is there something about NetworkServer.Spawn that i am missing?

Thank you for your time,
Dario.

Here’s an example.

@Command
function CmdSpawnOnNetwork()
{
	var myPattern = myGun.bulletPattern;
	var projectileObject = Instantiate(myGun.bulletType, Vector3(transform.position.x, transform.position.y, transform.position.z), transform.rotation * Quaternion.Euler(0, 180, 0));
	NetworkServer.Spawn(projectileObject);
	projectileObject.AddComponent(SwerveShot); 
}

Notice its @Command ([Command] -C#) not @Server.

@Server specifies a function to run on server only.

The Command attribute is to invoke a method on a server from a client.

Spawn your object, hold its reference, use that reference to perform actions on that object. You seem to have done all this.

Just change [Server] to [Command] and make that one your Cmd function. If you want to call a function on a client only use [Client]

@Client
public function ShootButtonPress() :void
{
	CmdSpawnOnNetwork();
}

on a function or

if(isClient)

within one.

I don’t think you can pass a GameObject as an argument to a Command function. You can only pass in simple arguments, NetworkIdentity’s, and NetworkBehaviors to Command’s/ClientRpc’s. Also, I’m assuming this is a prefab you are passing in, so I don’t think that NetworkBehavior’s or NetworkIdentity’s will work on it since they wouldn’t have an instance id set by the server.

Instead, I would have some sort of helper function that can change the prefab reference to a index of some sort. Here’s an example:

[Client]
public void CreateBuildingHere(Vector3 pos, GameObject prefabObject)
{
    int prefabIndex = NetworkManager.singleton.spawnPrefabs.IndexOf(prefabObject);
    CmdCreateBuildingHere(pos, prefabIndex);
}

[Command]
private void CmdCreateBuildingHere(Vector3 pos, int prefabIndex)
{
    //some other non-related code
    GameObject prefabToSpawn = NetworkManager.singleton.spawnPrefabs[prefabIndex];
    gameLord.SpawnBuilding(playerId, pos, prefabToSpawn);
}

Keep in mind, that this example may have problems if you are dynamically registering prefabs to your NetworkManager. You may want to have your own prefab indexing system instead of using the list of spawnPrefabs.

To @grandmapuckett I saw that you had already check the documentation but in order to have it as an answer here i give the following.

For anyone still having a similar problem although it has been answered already in a lot of other places.

From Unet Documentation : Remote Actions

Commands

Commands are sent from player objects on the client to player objects on the server. For security, Commands can only be sent from YOUR player object, so you cannot control the objects of other players.

The problem is that this will work for a client which is a host also, but not for a remote client. I think unity should warn about this and not let us think that is permitted.

To @sharat

Arguments to Remote Actions

  • GameObject with a NetworkIdentity component attached