system-designer-rs
is a puzzle game that combines system design and programming, built in Rust and the Bevy game engine.
Install Rust, then execute from your terminal:
cargo run
- Levels: Compose and implement a system that meets the level requirements in order to pass.
- Sandbox: Experiement with building your own systems, with all available System Components.
All System simulations start at the Clients in the System. Clients each have a list of HTTP requests that they send to dedicated Servers sequentially - a Client's next request is sent when it receives a response for the previous request. When all requests for all Clients have completed, the simulation finishes.
Clients may only be connected to Servers.
Name
: An optional human readable name for this Client.Requests
: A list of Requests that this Client will send sequentially during system simulation - see below.
Each Request is configured as follows:
URL
: This must match a Hostname of a connected Server. The request will be sent to that Server, if it is connected.Path
: The path of the request. If the Server has an endpoint handler whose path pattern matches this path (in addition to the request HTTP method), that endpoint handler will handle the request.Method
: The HTTP method of the request. If the Server has an endpoint handler whose method matches (in addition to the request path), that endpoint handler will handle the request.Body
: The JSON payload for the request.
Requests are not configurable by the player for Levels mode, but they are for the Sandbox mode.
Servers may be connected to Clients, other Servers, and Databases. Servers handle requests from Clients and other Servers, and may perform operations on Databases.
Servers are configured as follows:
Name
: An optional human readable name for this Server.Hostname
: The hostname of this Server.Endpoints
:Path
: The path for the endpoint. This supports path patterns, so the for the endpoint path/users/:id
and incoming request path/users/1
, the string value"1"
will be bound and accessible viarequest.params.id
within the request handler.Method
: The HTTP method that this handler expects.Request
handler: Code that executes when the endpoint receives a request. See below for details.
Endpoint handlers must be implemented in JavaScript code. Generator functions are used to model "asynchronous" method calls (i.e. any method call that interacts with another Server/Database component), so they must be used with the yield
keyword. Such methods are denoted with function*
below.
The default function signature for an endpoint handler must be preserved, and you must return a Response
object using the reponse
builder function. Implement your logic for the endpoint within this function:
const requestHandler = function* () {
// Implement me!
return response(200, "Ok");
};
The following objects and functions are globally accessible within an endpoint handler function.
type Value = null | boolean | number | string | Value[] | Map<string, Value>;
type Request = {
url: string;
path: string;
method: string;
params: Map<string, Value>;
};
type Response = {
status: number;
data: Value;
};
type Document = Map<string, Value>;
Note that the value undefined
is not supported as a value to pass between system components, but it is safe to use within a request handler function itself.
The Request
object that triggered execution of the endpoint handler.
const id = parseInt(request.params.id);
A class instance that implements the following methods to interact with connected Databases.
function* save(dbName: string, doc: Document): Document
function* findOne(dbName: string, id: number): Document | null
function* findAll(dbName: string): Document[]
function* contains(dbName: string, id: number): boolean
function* delete(dbName: string, id: number): void
// Insert a new document:
const savedDoc = yield db.save("db1", { foo: "bar" });
// Specify the id to overwrite an existing document:
const overwrittenDoc = yield db.save("db1", { id: 0, qux: {} });
// Find a document by id:
const doc = yield db.findOne("db1", 5);
A class instance that implements the following methods to send requests to other connected Servers.
function* get(url: string, path: string): Response
const res = yield http.get("server-1.com", "/users/1");
A function to construct a response from the endpoint handler.
function response(status: number, body: Value): Response
return response(200, "Ok");
A NoSQL/document database that stores a single collection of JSON objects. All documents stored in a Database have a id: number
field, but this field is not required when saving a new document. If it is present, it must be of type number
.
Databases are configured as follows:
Name
: An optional human readable name for this Database.Hostname
: The hostname of this Database.
The Documents stored in the Database during a system simulation will be visible in the Inspector sidebar when the Database is selected.
- Add a new component to your system from the LHS sidebar.
- Left click a component in your system to bring up the RHS Inspector sidebar to configure the component, or view its state during a system simulation.
- Left click drag a component to move it around.
- Right click drag a component and release over a different component to form a connection.
- Right click a connection to remove it.
- Start the system simulation by pressing "Execute" on the LHS sidebar. Select a Client to view the responses it receives during the simulation.