0

I'm implementing an online turn-based game, where game state changes are driven by client actions sent to the server. Valid client actions are generated and validated by a values method, which takes the game state as an argument and returns the possible values for that action.

Some of these actions might lead to a state where a player is required to perform other actions as part of it, disallowing all other actions which could otherwise be made.

What I'm trying to achieve is the possibility of these sequences being interrupted and resumed at any time, like in a case of server outage. The state is serialized and stored after each action, so all the information necessary to resume the sequence has to be stored in it.

Two approaches I have in mind:

  1. Have the first step of the sequence set some kind of "flag" in the state that subsequent actions could look for in their values function. This would make it kind of verbose, because everything has to be declared explicitly:

    actionA1 = (state, ...args) => {
        //some code here...
    
        return {
           ...state,
           waitFor: actionA2
        }
    }
    
    actionA2 = {
        handler: (state, ...args) => {
            //actionA2 code here...
            //some more code here...
        },
        values: (state, player) => {
            if(state.waitFor == actionA2)
                //return values for actionA2
            return []
        }
    }
    
  2. Declare this sequence of actions implicitly and have the game engine handle the resuming (basically doing number 1 automagically behind the scenes):

    actionA1 = (state, ...args) => {
        //some code here...
    
        /*
        suspend execution until a valid actionA2 is performed,
        but somehow this can be resumed just from reloading
        the serialized state, meaning that on a server restart
        the code above would not be executed
        */
    
        await actionA2
    
        //some more code here...
    }
    
    actionA2 = {
        handler: (state, ...args) => {
            //actionA2 code here...
        },
        values: (state, player) => {
            /*
            no need to explicitly check any conditions
            as this action can only be performed as part of actionA1
            */
    
            //return values for actionA2
        }
    }
    

I prefer option 2, but I'm not sure how I would go about implementing it in a way that doesn't feel "hacky". Maybe this is just a question of confidence :P

I'm trying to go for a functional approach here, so no mutations of objects, and all effects depend solely on the game state. Also, I'm developing this in JavaScript.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.