Builder Server
With the builder gaining momentum, it is rapidly going to outgrow its current single user and offline structure (see #14).
Moving forward with the builder towards a general tool requires the introduction of a server. To bring it in line with iMD-VR, we would also like to introduce multiplayer.
The simplest incarnation of the builder server is a very typical RPC server. Some example services:
- Perform minimisation / optimisation routine, given the current structure and some options. This would be nice so we could support OB/Terachem/QML/Amber/OpenMM etc etc.
- “Dial-a-molecule” - services/methods for fetching/generating fragments, e.g.:
- Generate protein structures: helix, coiled coil, beta sheet, loops.
- Compute the orbitals / bond order of the current state of the molecule.
- Generate nanostructures.
- Fetch, clean and serve drugs/pdbs (as currently implemented locally).
- Export. Save to some remote location (e.g. on Quest).
These would be easy to implement, they could all just produce FrameData which the builder client could consume.
Supporting multiplayer makes all of this more complicated. In such a situation, there is presumably some core service providing a stream of FrameData to clients which represents edits to the shared system. Keeping things simple, let’s assume for now only one client can edit the system at a time. Thus, we can use the updated multiplayer protocol to acquire a lock on the shared resource, the “system”. The client then publishes a stream of their edits to the system. Like the Isambard-VR prototype, the local client will be free to make all the updates it wants asynchronously from their publication, to avoid lag.
The resulting protocol is something like this (using minimisation and a helix as an example):
//These are imported from the narupa.multiplayer
// Acquire a lock for a player on a specified resource.
rpc AcquireResourceLock(AcquireLockRequest) returns (ResourceRequestResponse) {}
// Release a player's lock on a specified resource.
rpc ReleaseResourceLock(ReleaseLockRequest) returns (ResourceRequestResponse) {}
// Set a resource's value.
rpc SetResourceValue(SetResourceValueRequest) returns (ResourceRequestResponse) {}
//These are imported from narupa.trajectory
/* Subscribe to a continuous updating source of frames. The client gets the latest available frame at the time of transmission. */
rpc SubscribeLatestFrames (GetFrameRequest) returns (stream GetFrameResponse);
//These are some suggested builder protocols
rpc PublishEdit(stream BuilderEdit) returns (PublishEditResponse) {}
//Note that the GenerateHelixResponse will not include the resulting frame, that will be produced by
//SubscribeLatestFrames.
rpc GenerateHelix(GenerateHelixRequest) returns (GenerateHelixResponse) {}
rpc Minimize(MinimizeRequest) returns (MinimizeResponse) {}
message BuilderEdit {
// ID of player
string player_id = 1;
// Publication
FrameData edit = 2;
}
//Sketch of the message for minimisation. Note that we do not need to transmit the current state,
//as the server will have it.
message MinimizeRequest {
// ID of player making request.
string player_id = 1;
// Options
Struct options = 2;
}
This structure is pleasing as it reuses much of our core Narupa structure, and it's easy to extend with new features.