Managing Nested State

I’m working on a pretty hefty project, but haven’t ever used Elixir before - I’m more familiar with Javascript so I might say ‘Object’ when I really mean ‘Map’, etc. You could say I’m diving into the deep end with this one.

It’s basically a game server built in Phoenix for running sci-fi space simulations in a physical location. It’s similar to Artemis. An instance of a simulation is called a ‘flight’. Flights have at least one ‘simulator,’ which represents a single space ship, as well as any data which is global to the flight simulation, such as the location of NPC ships, etc. Each simulator has ‘stations’, which are the crew bridge positions; ‘systems’, which represent the different systems on the simulator such as engines, weapons, and communications.

Flights would be created from template objects (probably JSON stored in a persisted database), and would instantiate processes for the simulators, stations, and other objects in a supervisor tree structure. For speed and reliability, I would want all data for currently running flights to be stored in Elixir and not saved to a database. However, I want to have the ability to serialize all of the data for a flight and create a snapshot which can be saved back to the database and reinstantiated at a future time

There are a couple of things I want to be able to do with this:

  • Connect clients and have them access information from these processes using Phoenix channels.

  • Have processes which update simulator data automatically and push any updates to any connected clients using Phoenix Channels. This would be used for steadily increasing engine heat for example.

  • Be able to save and restore snapshots at any given time.

  • Bonus feature, but not necessarily needed: store a log of all operations that happen to a flight or its children, providing the ability to rewind the state.

What I just described sounds an awful lot like a Redux store, with the data nested together in a single store. From what I’ve read, implementing Redux isn’t an especially hard thing to do, especially in functional programming languages like Elixir. It would involve having a global state process which any of my other processes could easily access and address and apply Redux-like actions to reducers to return the final state. My question is: is this a good solution to my problem?

Another solution I’ve considered is storing state in the child processes themselves for themselves and creating a mechanism for serializing the data into a single object. It seems a little hack-isa, but I don’t know what ‘best practice’ is for this kind of thing.

Anyone have any thoughts, suggestions, or resources they could point me to? If not, I’ll try implementing the Redux solution.

2 Likes

Maybe using a state machine process per socket for the spaceship specific data? Like a gen_statem process? http://erlang.org/~raimo/doc-8.0/doc/design_principles/statem.html

You would hidrate the state on init and regularly persist it (either every N operations or with a timeout). Would that work?

As for global state for the whole flight, that has to be a separate process you interact with, which may also be modeled as a state machine.

In terms of serializing data, as long as you can constrain the data types in the state to something that can be serialized to JSON, you should be fine. Bear in mind that any erlang term (like a pid) is a valid map key/value, so you would need to be careful with those.

2 Likes

On a higher level, I had this saved in my bookmarks (but I skimmed it at the time, so it may not fit your use case) https://gameserverarchitecture.com/2015/12/pattern-responsibility-oriented-game-server/

2 Likes

@cloud8421 I’ll look into those docs and that link. Thanks for the help!

Using something like https://hex.pm/packages/fsm to handle the state machine code allows pretty instant serialization (as the fsm is stored as data instead of as a process, so you can serialize it in and out as you wish).

EDIT: Also found https://hex.pm/packages/exfsm but not taken a hard look at it yet.

1 Like