optimistic updates
soooooooooooooo optimisticUpdates are implemented and now I'm getting definitely a feeling of boilerplate and losing some easiness in my offboarding story, that took way more work than it should have and I introduced some bugs due to me having to restructure functions and naming being hard...
17 Replies
@cyremur hey can we hop in a thread with this
So all my stuff now looks like this:
sure sorry for putting so much stuff in general
Do you have a ton of these, or mostly this one that you pass arbitrary game actions to?
I have a few small thoughts but curious what your thoughts on this are
re this pattern, I could imagine a
db.mustGet
or db.getOrThrow
and in my own code I write a
sort of thingso I basically have
-initializeGame
-startGame
-executeAbility
-playCard
-passTurn
first two are mostly for matchmaking, last three carry the most of the gameplay
that was the level of abstraction that felt reasonable with redux
one answer I was considering is basically doing one-level-higher and have just a getGame query and mutateGame mutation and then pass everything as parameters and select subfunctions and all that fun
another thing I was considering was higher level functions that handle the loadState / saveState at the start and end of optimistic updates
neither here nor there but I did this the other day with a class, no fewer lines of code but as long as it's a single object that only makes sense to modify together might as well slap some behavior on it
so I can do something like
and then localMutate basically returns the optimistcUpdate handler that loads state, executes actionOne with args on state, and then saves state to localStore
what was the scope of the class?
I made my Game object a class, it has class methods
.fromRecord()
and toRecord()
but once it was a class instance I could call methods on it like playTurn
it just lasted for a single mutationyeah I kinda like stuff like
entity.attack(entity)
and putting stuff like that on the actual game objects
that's how we learned SWE couple years ago with all the OOP patternssomewhat because it helped me unit test that logic all separately from my DB stuff
I kinda started like that and then got a headache over synchronizing class objects with functions over web and instead of writing a transport layer with serialization I just gave up and made everything plain data and functions
now I just have a big json in the middle and a bunch of functions that represent gameactions that move json values around
and while I would do
over
anytime
it's not enough of a pain point that I want to deal with classes and serialization just for some syntactic sugar and autocomplete support
Once you find patterns you like you might choose to abstract them with "middleware" (just helper functions that run every time) — we don't provide anything ORM-y but a wrapper that passes you a game object or even adds a method to
db
is an optionI forgot for a moment that I implemented attacks of opportunities and panicked that I broke movement with optimistic updates when my creature vanished... game log says it was fine though... phew
honestly, the biggest piece of "middleware" are
executeAbility
and manifestCard
they take so many args that they're basically half a game engine
the thing that I would really consider "middleware" though is minification of the gamestate
on client side I work with a 50kb json that has like 60 full copies of playing cards in the state which is compressed to 10kb by swapping out the full rich card jsons with just cardIdsI mention these because you mention boilerplate before, things like https://github.com/get-convex/convex-helpers/blob/main/convex/lib/withUser.ts mostly from @ian are slick for boilerplate reduction once you figure out which parts of your app you want to be looking at and which you don't
thanks, will definitely have another look
the main feels bad for me was having the same load-action-save implemented in both the convex function serverside and the optimistic update client side... obviously not the general case that the optimistic update has all the code and info to execute 100% of the change independently so hard to complain
this does look like a really cool repo
totally, that makes sense
ok but I'm somewhat optimistic that all the infra changes are just kinda done now and I can go back to game logic and asset work
only thing left is auth and account management
Really cool stuff. I think Ian mentioned it but I would scope down mutable stuff via Immer, and avoid deep cloning. Calling the same logic from optimistic update and server-side sounds totally reasonable - but for games you might do that a lot more than for a typical "app".
It might be interesting for you to eventually split up the game state (if possible). Convex gives you guarantees like "consistent view" of all your queries, that should enable this.