Advertisement
KillaMaaki

RTS Demo Explanation

Jan 9th, 2017
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.15 KB | None | 0 0
  1. So this is a dumb little test of mine to try and implement a "proper" online-capable RTS in Unity.
  2.  
  3. One of the major challenges of any multiplayer RTS is just how many units there can be. Using classic state sync with that many units will bring ANYONE's internet crashing down. There just isn't the bandwidth for it.
  4.  
  5. So the favored solution is, instead, to make sure everyone's simulation runs in absolute lock-step with each other. The only thing sent between player machines is commands. That's it. Even AI players can be simulated 100% clientside with no need for a central server, as long as they are deterministic.
  6.  
  7. Of course, this presents a challenge: how do you make a game deterministic? It's not enough just to make sure your random number generator is seeded properly - another big source of non-determinism is using floating-point values anywhere in your simulation code. Floats are not deterministic by any means, and can quickly allow errors to pile up and cause simulations to diverge with disastrous results.
  8.  
  9. The solution to this is to not used floats, and opt for integers. Integers are 100% deterministic. While integers don't generally represent fractional values, which are pretty important in games, fixed-point notation essentially allocates different parts of the integer towards whole and fractional values. However, a whole new set of math functions must be written to deal with them, and this can be a time consuming process. But it's totally necessary for making a simulation work in lockstep.
  10.  
  11. The idea in this demo was to produce an example codebase which made use of fixed-point math, with a design goal of giving the Unity side (or whichever client was used for the simulation code - technically, I could also have written a custom MonoGame/XNA client as well) as little access as possible to the internal sim code. This is for safety, primarily - if the client code can directly access the simulation, it's easier to accidentally let the client modify something that it shouldn't touch, which increases the chances of some client code causing a hard-to-debug desync. In fact, the client doesn't even get direct access to the entities in the simulation. Instead, in this case, it gets callbacks for when an entity is created and destroyed, which is passed a *handle* to the entity, a thin class that internally references the actual entity and exposes *just* enough information for the client code to be able to display the entity to the player, with no possible way to access the actual entity itself (again, for safety).
  12.  
  13. While interaction isn't yet implemented, the design of the system will boil down to allowing the client to generate a Command. A Command is made of several SubCommands, and each SubCommand basically stores the handle of an entity it will operate on, an action, and the handle of the target of the action. For example, a SubCommand may be a MoveCommand which stores the handle of a soldier and the world position to move the soldier to. When commanding multiple units, each unit gets a separate SubCommand.
  14. The Command itself stores a timestamp, which is the frame on which it should execute. This command is queued by the client and executed by the sim. In singleplayer it just executes the command on the given frame number. However, multiplayer works a bit differently.
  15. In multiplayer, we actually store a separate buffer for every player in the game. When one player sends out a command, everyone else has to respond with an empty command (a "NOP", for "no-operation"). When the sim core reaches a command in the buffer, it checks everyone else for a NOP on the same frame. If everyone has responded properly, the sim core will have a NOP from every player. However, if one or more player's NOP responses for that frame are missing, the sim core must pause and wait. Additionally, while waiting for other players to catch up, it will send a message telling all clients to increment a delay value, which is the value used to schedule commands to execute. The idea is to reduce time spent waiting as much as possible, at the expense of increasing command delays. So commands will be scheduled farther and farther into the future until the sim core no longer has to wait for players.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement