I'm in the process of making a simple game using re-frame (and thus react and reagent), but I'm stuck at one point. In
Not every app has lots of logical states, but many do, and if you are implementing one of them, then formally recognising it and using a technique like state charts will help greatly in getting a clean design and a nice datamodel.
Now, what I'm wondering is whether or not I should implement my game as a state machine explicitly, such as https://github.com/ztellman/automat, or if that is just to be implicit in the handlers and db?
Let me elaborate.
In my game, the following "macro states" are possible (not implemented this way yet):
- not connected
- connected to server, not to game (in the "choose game" area)
- connected to game
- created game
The transitions then would be
1->1 if pressing "connect", but getting a network error or using a bad username
1->2 if pressing "connect", and everything ok
2->1 if pressing "connect to game", and network error
2->2 if pressing "connect to game", and game not available
2->3 if pressing "connect to game", and everything ok
2->1 if pressing "create game", and network error
2->2 if pressing "create game", with some error in creating game
2->4 if pressing "create game", everything ok
4->1 if i get any network error
4->2 if i cancel my game
4->3 if someone joins my game
Now, much of this will be done over http, and thus asynchronously. To make sure that you won't press "connect" twice i added a boolean "connecting to server" (and then one for every http request), and then i get something like:
(register-handler :connect-to-server (fn [db _]) (if (not (:connecting-to-server db)) (do (POST ... #(dispatch [:connecting-to-server-succeeded ...] #(dispatch [:connecting-to-server-failed] ) (assoc db :connecting-to-server true)) db))) (register-handler :connecting-to-server-succeeded ... (-> db (assoc :connecting-to-server false) (assoc :connected-to-server true) ... )) (register-handler :connecting-to-server-failed ... (assoc db :connecting-to-server false) ... ) (register-sub :can-press-connect (fn [db _] (reaction (not :connecting-to-server)))
So basically, when I'm connecting to the server, nothing else can be done.
This will turn into a huge amount of states, and it seems like a lot of boilerplate code which I'm going to mess up somewhere. Naturally, I can abstract some of it away, but I'm guessing there's already a good idea of how to deal with this. So my questions:
Am I reinventing some bad, old school approach on top of reagent, and should do it in some other way?
Is re-frame perhaps even the wrong approach with so much network io?
If the idea is ok, would it be fruitful to do this explicitly via a state machine to automatically tell whether or not a button can be pressed?
(A better way, long term, is likely websockets, but this is a learning experience.)