header image

Back in the saddle

I’ve been slacking a lot lately. Let’s blame Diablo 3, The Binding of Isaac, work and summer. I hate summer. It’s too hot for anything when I’m not in the office with nice air conditioning. Makes staying late so tempting…

Anyway, I’m back coding Heimdall. Right now the focus is on finishing the first prototype. What features are needed?

  • Basic account functionality: creating accounts, account persistence [done]
  • Client-gateway authentication and basic communication: logging in, selecting realm, creating characters [done]
  • Basic game server functionality: entity system with persistence, communication with gateway (player authentication, character info), client communication (receiving input, basic movement simulation, sending world updates) [mostly done, working on movement and updates]
  • Basic client-client interaction: clients should be able to move and see each other [to-do]
  • Graphical client prototype: basic UI, placeholder world rendering (just to see characters) [UI done, rendering to-do]

As you can see, most of the back-end infrastructure is in at least somewhat working state. I’m implementing movement now – it’s the first real Entity System in the engine so a huge step forward. Also movement is kind of needed for anything to work 😉

At first I was thinking to implement “discrete” movement (like in UO for example). My world is tile-based, so why not only allow players to move by specified amount in compass directions? Well, it’s tempting and quite easy to implement, but I feel I can do better. Discrete movement has some big disadvantages:

  • Limited player freedom, of course. I’d like to allow free movement, even if it makes collision detection harder.
  • Speed hacking! With discrete steps the only real metric of ‘speed’ is rate of movement commands, and this can be easily exploited (as in UO ;)) to gain speed advantage. You could of course implement some countermeasures and rate limits, but that’s not so easy (and costs time).
  • It’s hard to implement variable character speed (see above).

So what I’m doing? Well, my approach is to have more action-gamey movement. Each movable entity has a Location and Velocity components (basically 3d vectors). Game server will simulate basic kinematics, which is not hard with (mostly) constant speed values. Of course clients need to be kept in sync, which is not always easy, but with mostly 2D movement it should be OK. Because of network and processing latency I probably can’t afford to have clients as just “dumb terminals” displaying game world, they need to perform limited simulation themselves.

Basic information flow is like this:

  • Client logs in and selects character (character enters game world).
  • Server assigns character entity to the client connection.
  • Server constructs a “world state update” packet containing initial information about all entities in client’s “interaction radius” (I call it “client frustum”, frustum is mostly used in context of client rendering but for me it means “all objects that client should know about”). This initial packet contains list of entities with components needed by the client (like names, locations, velocities etc, no internal potentially sensitive server state is sent of course).
  • Client deserializes the update packet and initializes its own entity system with the received data. That’s the base for client-side simulation and rendering.
  • Player initiates character movement in a specific direction. GUI sends the ‘move’ message to the “world” object, and it in turn constructs a “movement request” packet containing direction and velocity. This requires that client knows about “potential speed” of its character, but is needed if we don’t want to wait for server acknowledgment before locally starting to move.
  • Client applies the velocity change to the character and uses the new state for rendering and collision detection.
  • Server receives the movement packet and validates it. If everything is OK it applies velocity changes to its entity state. No acknowledgement is immediately sent.
  • Server keeps track of each client’s frustum. If a new entity enters this region or a “visible state change” occurs for some entity, another “state update” packet is created and sent, but this time it’s a “delta” packet (containing only components that changed since last time). Because client frustum is slightly larger than rendering area it allows for smoother appearance of new objects that just “arrived”.
  • Client applies any state updates to its own entity system.
  • Collision detection is both client- and server-side. Server sends update messages on collision, because entity’s velocity changes. If client and server world data match, they both detect collision at the same time and will remain in sync (of course lag compensation, client-side interpolation etc is still needed, but that’s another topic). If the client has invalid data (like hacked world files) it will desync, but server will still have correct state.

Once the movement and world updates work properly it will be time to implement simple rendering and the prototype will be complete.

~ by omeg on July 6, 2012.

entity systems, game development, Heimdall

Leave a Reply