Hello!
I’m completely new in Nakama and I’m currently creating a Real-time Strategy game (like Travian). To better understand what the context is:
Gameplay is slow-paced/transactional.
Building upgrades and troop marches can take minutes or hours to complete.
Small latency is not a primary concern.
World state must be persistent and survive server restarts.
I’ve read through the documentation regarding Authoritative Matches but as far as I understood, managing a persistent world map seems risky due to RAM volatility and could result resource-heavy.
I was wondering if this pattern is a good to go, or if there is a better way of doing it:
All game state (building upgrades, troop marches) are stored in the database.
Player triggers actions via RPCs (e.g. StartMarch), which saves the start_time and end_time on the database.
Inside the InitModule, I launch a goroutine wiith a ticker that runs every 1 second, this will query the DB for events where end_time >= now, it then processes the logic (result of the action), and updates the DB accordingly sending a notification/update to players.
Additionally:
If I plan to have multiple kingdoms (servers/realms), would you recommend spawning a separate Goroutine/ticker for each kingdom ID to process their specific queries in parallel?
For the ticker query, is it better to use a custom SQL table, or is the Nakama Storage Engine’s Listing feature performant enough for frequent polling?
For the type of game you’re building, I believe this may be the best approach.
The StorageList API was designed to be efficient, but some thought has to be put into how you model your data in the StorageEngine to make sure the existing APIs serve your needs. Avoid creating tables if possible, we discourage the use custom tables.
If you need to lookup storage objects based on their content, make sure you setup indices using Storage Search. The indices have a max entry cap, so you should be mindful of what objects get indexed, but it may allow you to do offline matchmaking or just quickly lookup specific objects - however remember that if you have more Storage Objects than the size of the index, you may only be getting a subset back. You may be able to hold all of them in the Index, but it depends on your memory constraints.
I’d create a dedicated collection for pending tasks where you’d put all these tasks that need processing, then go through them in the background goroutine and maybe use a pool of workers to handle the required updates, deleting the pending tasks (storage objects) and etc.
If I plan to have multiple kingdoms (servers/realms), would you recommend spawning a separate Goroutine/ticker for each kingdom ID to process their specific queries in parallel?
If these are isolated (e.g.: collection1: “k1:buildings”, collection2: “k2:buildings”), so concurrent writes to the same objects can’t be issued from different workers, then yes a background goroutine per kingdom should be ok.
The important part to keep in mind is that ideally, you’d batch all Storage Engine operations as much as possible within the same StorageWrite operation (or using multiUpdate - which guarantees all writes/deletes happen transactionally).