I’m trying to get MapMagic2 terrain working properly with a FishNet multiplayer setup. MM2 creates procedural terrain - it handles the math etc but uses Unity terrain. They aren’t prefabs or anything - it’s dynamically created terrain game objects.
However, FishNet doesn’t seem to allow spawning them (via ServerManager.Spawn). I was hoping to only generate it on the server but it seems like I’d need to generate the terrain on every client.
The problem is, the world objects spawned in by MM2 definitely need to be spawned on the server as I need players to be able to interact and remove (despawn) them.
If the tool generates terrain procedurally I assume there is a seed value that can be used to guarantee that is reproducible? Can’t you then just share that seed via a network variable or RPC and have it generated everywhere else?
I can but wanted to avoid that because I need to generate the terrain on the server anyway:
I need to spawn the world objects on the server since they’re NetworkObjects - they can be removed by players so I need to synchronize that across clients. This can only be done on the server, so the server needs to also generate the terrain
The player spawner needs the terrain to be complete before it can spawn a player for the first time, as it needs to know the terrain height
Both world objects and players have to wait to be spawned until the terrain is done otherwise those affected by gravity will fall through. If I have to generate terrain on both client/server I also need to add a complex system to force everything else to wait until the terrain exists on both ends
It sounds as though you can use a seed to generate the terrain you are just facing an issue with the objects spawning before the terrain completes, right?
If I am correct, the solution should be pretty easy using observer conditions. There’s a number of ways to do this but the easiest with conditions is to probably make a custom observer condition and use that.
The condition could be very simple, such as PlayerLoadedTerrainCondition.
Add this condition to your world objects, or any objects you want to follow this workflow.
private static HashSet<NetworkConnect> _addedConns = new();
public static void AddLoadedScene(NetworkConnection c)
{
if (_addedConns.Add(c))
InstanceFinder.ServerManager.Objects.RebuildObservers(c);
}
public static void RemoveLoadedScene(NetworkConnection c)
{
if (_addedConns.Remove(c))
InstanceFinder.ServerManager.Objects.RebuildObservers(c);
}
public override bool ConditionMet(NetworkConnection connection, bool currentlyAdded, out bool notProcessed)
{
notProcessed = false;
return _addedConns.Contains(connection);
}
public override ObserverConditionType GetConditionType() => ObserverConditionType.Normal;
Be sure to hook into when a client disconnects on the server using
public event Action<NetworkConnection, RemoteConnectionStateArgs> OnRemoteConnectionState;
to remove clients from the observer condition, and remove manually when they are changing scenes.
Add a script to clients that calls a RPC to the server when they load the terrain. In that RPC call the AddLoadedScene method.
This code is rough, going from memory. Hope that helps!
Although unfortunately I discover more problems with every step.
After getting feedback in the FishNet discord I decided to revert to having clients generate their own terrain, since it’s not possible to spawn non-prefab/scene objects via FishNet.
However I’ve discovered that MM spawns world objects in a tree of other objects which also are dynamic (this aren’t networked) - this means fishnet can’t spawn them in the proper hierarchy because it’s unaware of several objects, so they spawn in the scene.
This causes only one “tile”'s objects to show up, while all others remain empty.