A distributed world
Foreword
This is about gleeps1, a game I'm working on. Here's a quick overview of the idea:
- Built with and for Gleam2
- How you play it is heavily inspired by Screeps3
- The gameplay loop will be similar to The Settlers IV4
- And the (visual) vibe will hopefully end up Stardew Valley5 esque
That being said; this is my first blog post, if you wanna call it that.
In it I'll go over one of the major components of how the game works: The game world and how user interactions work in it.
State of things in screeps
One of the gripes I have with screeps is that you can't really rely on the returned values of the different actions. And you need to consult the documentation carefully to make sure what you are doing will actually work as expected. Thus there is a whole section6 about how different actions interact with each other. From blocking each other with a somewhat convoluted priority chart, to overriding previous actions with repeat calls.
I'm pretty sure I get how they got there, and why it's done the way it is. However since gleeps runs on the BEAM I think I'll be able to improve on this.
What I'm hoping to achieve here is, having returned values be reliable. As in, if gleeps.move returns Ok(Nil), that action will be executed.
Chunked world model
My current plan is to have actors that manage a certain area (chunk) of the world.
- I will refer to them as 'chunk-actor' here.
User code needs to send a message to the chunk-actor when doing pretty much anything in a given area. 7 This will, naturally, be handled by the gleeps library.
The chunk-actor(s) keep track of any changes that have been accepted, and consider those when handling further requests. This should also encourage users to do their actions as quickly as possible, since in some cases, another may get there first.
Now, what if I want to do something across a chunk border? In those cases, the chunk actor will need to ask its neighboring chunk first.8 To avoid blocking other requests during that time the chunk-actor needs to do a few things.
- Send a message to the neighboring chunk
- Start a timer, in case the neighbor doesn't respond (in time)
- Store the request and the timer
And then once the neighbor, or timer, finish it will respond to the user.
At the end of a tick, all the chunk-actors need to send a list of what has changed (or the full new state, unsure rn) to the orchestrator9, which will then write all of it to the database in one transaction. Primarily to avoid the headache of having to deal with many write operations and some of them potentially failing.
How large a chunk is yet to be determined. Ideally I'd like to be able to tweak the chunk size at runtime. I.e. set the size to something else and recreate all of them. So I can figure out what size performs best when it comes to response times and overhead with tick times.
That is it for now.
Feel free to reach out if you have thoughts, questions or just want to talk about this or related things.
You can find a short list of my socials at the top of this page or the full one on the main page.
Happy pride month :3
-
There may be different variants of actions, one wich waits for the response and one that is fire and forget. I'm not sure about this yet, since it would push users to write unsafe (and not very gleam-y) code for the sake of getting more done in a tick. This is an undesireable outcome IMO. Since ideally this could be a way to get people into trying and (hopefully) loving gleam. Which I think is best done by having the game library provide one way to do things with strong guarantees.↩︎
-
This does mean that there is a higher time cost associated with cross-chunk actions, but I think this should be okay. I'll have to benchmark it once its fully implemented.↩︎
-
The 'main' process that orchestrates (creative naming, I know) the game tick by tick. A normal tick looks like this: Start the tick and wait for user code to finish. End the tick and wait for chunk-actors to finish. Persist changes and start over again.↩︎