Re: Game world and implementation

So one thing I have to say right away, each major “Region” of a Planet (like a Big Island or so) will have its own World, simply for management Reasons, and so that Servers can do things like Clustering properly (I plan to test that with Raspberry Pi’s by the way).

This means a lot of Stuff regarding Region Boundaries, since when you cross them you need to be moved smoothly from one World to another, you need a good transition especially with the Physics (that’s why Islands is gonna be the first non-debug Worldgeneration Type, because you can mostly ignore that Issue for swimming people).

And I did talk a lot about this problem with @OvermindDL1 , and he said it is “easy to share a database between servers”, whatever that will mean for the transition between Regions.

1 Like

Do you mean you want to shard each region onto a different server?

2 Likes

Yep, like having 4 Raspberry Pis run one World as 4 Clustered Servers. That kinda thing.

1 Like

Okay. I don’t think sharding is easy but I also know very little about it. Amethyst’s entity index and component storage tables are not exposed and it’s not implemented as a proper SQL-style database (the the tables are private Vecs allocated on the heap), so you will have to write database management and sharding yourself.

Setting up Shards

I’m assuming you would have all the servers hooked up over LAN. The number of servers and their machine identifiers (MAC addresses, local IP addresses, what have you) can be read from config files.

The thing is, regions (and Worlds in general) are going to be created on the fly. Somehow the software has to decide, on the fly, which server will create each new World. I don’t know how to make that decision, maybe you can base it on some sort of runtime profiling or maybe there’s a way to let the server operator configure it. I don’t know.

I can think of a way to implement the sharding once it is decided where it will go. Wherever above I wrote Option<World>, replace that with Option<Shard>. Shard would be a struct that contains an identifier for, and owned TCP Stream/Listener to communicate with, the server that will contain the new World.

Each method for spec’s World will be wrapped by Shard to send an appropriate EXEC message down the channel (see the next section). I think Rust allows you to go from strings to function calls but if not an enum or static array of closures can match procedure strings from EXEC messages to functions.

For their part, all secondary servers will run the server executable with either a command-line argument or a config file option that puts them in shard mode. In this mode they do not run a full server, but instead listen for messages on a network channel.

Server-Server Interface (within a cluster or between threads on the same server)

AFAIK you have two options,

  • write your own shared memory model
  • write your own messaging model

Even the Rust book says shared memory sucks, so that leaves messaging. The good news is that the Rust standard library has TCP tools, and Specs has built-in serialization (for the purpose of saving/loading games).

There’s only a few messages you really have to implement. Those are, in SQL terms,

  • DELETE: When a system wants to remove data from another World
  • EXEC: When a system wants to call a procedure on another World
  • INSERT: When a system wants to add data to another World
  • SELECT: When a system requires data from another World
  • UPDATE: When a system wants to update data on another World

Client-Server Interface

As far as the client->server interface goes, you have to decide on frontend or backend access.

  • frontend access: client->frontend->shard
  • backend access: client->shard

I think for simplicity’s sake you should use a single point of access (frontend) so the client only uses one interface regardless of server clustering. You could implement both and let the server operator decide, but that seems like a waste of dev time.

2 Likes