I am currently working on a side project where I try to figure out how to use custom worlds when doing multiplayer games in DOTS. The goal is to see if my studio can make a larger transition to DOTS with our lidgren based multiplayer framework.
The problem I am having is that the systems in my custom worlds (Server and Client) doesnt get updated automatically. It feels like I have missed some crucial detail but I just can’t find any information on how to properly use custom worlds. The way I solved it temporarily is to use disable auto creation on all systems and manually store a list of them in a monobehaviour that will loop over them in Update() and call the Update function for all systems.
Here is how I setup up the ServerWorld:
public void Start()
{
ServerWorld = new World("ServerWorld");
m_Systems = WorldCreator.FindAndCreateSystemsFromAssemblies(ServerWorld, "Server", "Shared");
World.Active = null;
ScriptBehaviourUpdateOrder.UpdatePlayerLoop(ServerWorld);
}
The function CreateWorldAndSystemsFromAssemblies will iterate through the assemblies Server and Shared and find all classes that derive ComponentSystemBase and call CreateSystem on them. I then return the systems and store them in a list as I described earlier.
So my first question is, How do I get the systems in my custom worlds to Update?
I also wonder if there is a way to control the tickrate of Worlds? I want the server world to run in 5 ticks per second and right now im handling it manually and just skip Updates until its time for a tick but that feels pretty ugly.
I looked through the DefaultWorldInitialization.cs class in Unity.Entities.Hybrid/Injection and figured out what I was missing. I never added the systems to the update list using AddSystemToUpdateList for all system groups. This is how I got it working:
foreach (var type in systems)
{
var groups = type.GetCustomAttributes(typeof(UpdateInGroupAttribute), true);
if (groups.Length == 0)
{
simulationSystemGroup.AddSystemToUpdateList(ServerWorld.GetOrCreateSystem(type) as ComponentSystemBase);
}
foreach (var g in groups)
{
var group = g as UpdateInGroupAttribute;
if (group == null) continue;
var groupMgr = ServerWorld.GetOrCreateSystem(group.GroupType);
var groupSys = groupMgr as ComponentSystemGroup;
if (groupSys != null)
{
groupSys.AddSystemToUpdateList(ServerWorld.GetOrCreateSystem(type) as ComponentSystemBase);
}
}
}
However I now stumbled upon a new problem related to ScriptBehaviourUpdateOrder.UpdatePlayerLoop() with multiple worlds. It seems like the call to UpdatePlayerLoop for my client world overrides the server world call meaning I can only have one active world? The entity debugger shows no systems at all for my server world. Does anyone know how to solve this? Or is it the wrong approach to have separate worlds for client and server?
But you may find it not flexible enough and want to roll your own world initialization code (as do I). To fix your ScriptBehaviourUpdateOrder.UpdatePlayerLoop(), see here:
And here’s how to do the reflection and swap Update to FixedUpdate (if you want):