2 - We have achieved multiplayer

Unity Netcode for Game Objects (NGO) is amazing. I can see why so many people create asset-flip slop games for some goofy multiplayer concept like "X simulator" or some parkour game with a mess of props to climb up. I built a copy for my friend to test multiplayer with and it just worked pretty much out of the box. I had to iterate a few times on broken animations, crashing, etc. but the physics and the netcode are solid.

One key detail that I've been thinking about is how I'm going to synchronize players who are in different scenes. I've already decided on a P2P, client-authoritative model (i.e. the host doesn't validate or reconcile the client's network data). I don't really care about people using cheated characters or hacking down the line - I'm going to rely on lobby hosts to decide what they will and will not put up with in their lobbies (I'm assuming based on the structure of the game, 90% or more of the players will want to play in private lobbies anyway). I decided that for the purposes of zone synchronization, I'm just going to store critical information in a ZoneInfo class (stuff like which enemies have been killed, is there loot on the ground, etc). This is a lightweight file that the host can use to sync up other players after a scene has been altered without having to keep the scene loaded for the host all the time. I also am going to use a "Zone Leader" system where a client temporarily acts as a host for a specific zone's enemies.

The first player to enter a zone where the host isn't present becomes a Zone Leader for that zone. If the Zone Leader leaves, leadership passes to the next client in the zone (lowest ClientId or similar metric). Finally, if the host enters the zone, the zone leader transfers to the host. I'll give an example to show why a system like this is useful.

Host: killing goblins in the forest

Player A and B: killing skeletons in the lowlands

In the regular host-client model, if Player A hits a skeleton, that data has to get sent to the host, then the host needs to send that data to Player B's client. If Player A is instead a zone leader, then this eliminates a redundant network call. In the case of many players in many zones, we would reduce the burden on the host significantly (imagine a scenario with 16 players across 8 zones). This is all probably unnecessary premature optimization, but I'd rather have a tight optimization plan which I can slowly loosen as permissable then have a sloppy optimization plan and worry about it years from now. I boxed myself into a corner when I developed Warlock: Age of Entropy by relying on libtcod for python as the basis of the engine; I want to have flexibility down the line in this project.

Anyways here's a picture of me and my friend in the game together: Cool picture

Next I might work on adding a new scene to the game (and also polishing the starting hub - the shadows are too dark and the town is too small imo). A lot of logic for my premature optimization is based on scene loading/unloading, and it might be time to start getting those systems set up. I also need an automated testing strategy.