Zombie pathing is already cheap and runs off the main thread. I did fix a bug last week with only 1 path per frame being sent to the threading system and since it typically only takes 1 frame to make a path, it was a bottleneck for say 100 zeds making paths each second. A fairly simple change now has it requesting up to 8 paths per frame of which the threading system can provide about 6. This tested well with around 150 zeds on a server spread across 6 players.
Ohhh? What I noticed was your animation rate versus physics updates and transforms are all done OOP. This can be spread out to help the runtime load. Making this multi thread is very difficult with the way it is coded however I was able to separate phases for players in specific so far. I took over the gmupdate from the unity update tick and separated some of the work. Specifically the entity update tick. Your thread pool is set too high. Way too high. If most of the game is single threaded, then having a thread pool do that will toss the threads into the pool waiting for work that almost never shows up. This can cause IO handling problems. This is my own opinion but the pool should be slightly larger than the work orientation within the code. If the game is only coded to use 2 cores in general, then 1200 threads in a pool will be an issue.
I also like to run work simultaneous along side the main, rather than isolated to only threads. This keeps a better work flow and the data has a better potential to be closer in the data stack when passed off to the other threads by the main which then runs part of the operation.
I am isolating the Entity.OnUpdate from the EnityAlive.OnUpdateLive which contains the heaviest calculations. Both can be multi threaded by dividing the work evenly with the players being split among them. If the Entity.OnUpdate runs at the same time as EnityAlive.OnUpdateLive, then the overlaps can cause issues. Isolating them into phases prevent this and I highly recommend it for all of the entity. I run the Entity.OnUpdate phase multi threaded, then the live section multi threaded. There are a few things you have to pull out of this process and run isolated to avoid overlaps with multiple players updated simultaneous.
updateCurrentBlockPosAndValue and checkForTeleportOutOfTraderArea. Ideally these would be inside the position update phase. The buffs, inventory, bag, stealth and updateprefab sections need to be isolated, plus those must be done single thread. If some of these can be written for thread safety, it can all go multi thread while keeping your OOP process. This would avoid a total rewrite of the game while getting a giant advantage out of the multi thread.
Here is how I mapped the isolation phases
Positions > Chunks > Buffs-Inventory-Bag-Stealth-Prefab > Entity.OnUpdate > EntityAlive.OnUpdateLive > One offs that must avoid overlaps like CheckSleeperTriggers
4x 1x 1x 4x 4x 1x
1x = single thread
4x = four threads
Not sure if I measured this improper but your runtime with a single player in game on the dedicated servers was around 0.05-0.06 which is honestly atrocious. The unity update tick rate is 0.01. 5 runtimes missed from a single player. I hope I measured it wrong. After taking over the gmupdate and doing some tweaks for dedicated and taking over the entity update phase entirely, I have it down to below 0.01 with one player in game. Actually it was closer to 0.001.
I can go into a bunch of other things if you are interested.
Due to the runtime you are averaging and how much faster I can run the update, I can run multi runtime ticks in the same time frame, so I split the work across them. It actually was running so fast at first when I left it running every tick, everything was moving around like the flash. Chickens were red blurs. Breaking the chicken sound barrier. Beak neck speeds. Now I am updating across ten ticks. Zombies are in very nice animation flow while keeping their tracking more accurate. I also split them across four ticks. I update the players twice to keep them in better animation, network and update state with others around them.
You can reduce the updates across multiple ticks while keeping your OOP but splitting it to phases to allow multi threading too
You can also use flat buffers for the entity update phase which helps avoid some of the garbage collection.
I did some work on block collapse to make it rapid and look like it crumbles.
Chunk sync to unity has four ticks which dramatically helps in with loading large volumes and high player counts.
I run the entire gmupdate every tick like normal, it is just the entities update and chunk sync to unity that can be spread out across the ticks/frames
Tick 1 and 6 update players. Tick 2 3 4 5 is for non players and single thread for now(Yes it can be multi threaded but more difficult)
Tick 7 8 9 10 is for chunk sync to unity.
Keep in mind, I do that in 0.1 where the normal game right now is updating one player in 0.05-0.06. I do what I can to keep the runtimes under 0.01 to match the unity tick rate. The closer to that you can keep it, the better the overall fps.
We were able to achieve 300 zombies and 70 fps on a high end server with multi threading. There are huge race conditions and overlaps that make non players extremely hard to multi thread.
You have a few sections using static lists inside of the class which is the only thing stopping multi thread. To avoid the garbage you need more lists allocated upfront or allocate them at runtime if not worried about the gc.
The AI is, well I will be honest its a bit ugly. It must be done single thread currently. I can achieve multi if I alter those static lists, however it hits a duplicate path id error and then spams the console. I suspect it is an issue in the library you guys are using for the pathing algo but I could be wrong. The good thing is its fast. Spreading the work load across more runtime ticks allows the ai to keep up despite being single thread and the changes you guys have made for a21 is a big deal. That single path check instead of eight will help a lot too. I am avoiding touching ai since you guys are updating it but I will let you know if I find anything