Index of /fun/project-91

      Name                    Last modified      Size  Description
Parent Directory -
Disomod
A Massively Multiplayer Online Distributed Modular Diorama Role-Playing Game
============================================================================

CROSS SERVER COMMUNICATION
--------------------------

URLS

Servers are described to each other by URLs, whose scheme is
"disomod", whose hostname component is the hostname or IP of the
target server and whose port component is the port of that server, and
whose path component is a key to be interpreted by the server.

These URLs are used in two places: privately, between two server
administrators, to describe how two realms connect; and to define
callbacks for magic spells and other special effects.

Here is an example of a disomod URL used to transfer creatures to a
particular location with UUID "203ddf2a-67e1-459c-a310-22fe136bf114".
The last part of the URL is a key provided only to one particular
other server so that that server can transfer creatures to this
location, without letting other servers guess the URL.

   disomod://realm.example.net:10001/203ddf2a-67e1-459c-a310-22fe136bf114/82758275237583572385853

Here is an example of a disomod URL used for a callback. Since all it
does is modify the creature's stats, there is no need for a key.

   disomod://diorama.example.org:55550/rejuvenation-spell


PROTOCOL

When a creature is to be transfered using a disomod URL, there are
four possible outcomes:

 - The connection can fail before the transfer takes place, in which
   case nothing happens.

 - The connection can fail after the point where the other server may
   have received the creature, in which case the sending server has to
   reattempt to send the creature in an idempotent manner.

 - The other server can receive the creature and keep it, possibly to
   send back later as a separate transfer.

 - The other server can receive the creature and send it back in the
   same connection.

Disomod connections use a UTF-8 line-orientated protocol.

The first step is the handshake, which establishes that both servers
are Disomod servers.

When connecting to a Disomod server, and when receiving a connection,
disomod servers must send the following handshake to the other server,
and must wait to receive the same handshake. It is followed by a
single U+000A character.

   DISOMOD

Then, the sending server must send a line containing the path part of
the URL, unescaped (as UTF-8, both for the unescaping and the
transmission, so the URL encoding can actually be treated as opaque
binary). This must be followed by a line containing a unique ID
representing the transaction.

The server must respond with either a single line reading READY or
must close the connection, if the path is not a recognised one.

If the server closes the connection, then the sending server must
treat this as a rejection and handle it accordingly. (Maybe the
dungeon path is blocked, maybe the magic potion was a dud...)

If the server responds with READY, the sending server must then send
the creature JSON blob, and wait for the server's response.

The server must read the creature blob, and decide what to do with it
based on the path provided earlier. If the path is one for a transfer
(e.g. an entrance to the realm, or a teleportation spell), but the
transaction ID indicates that the transfer has already happened, the
server must respond with three lines TRANSFER, TRANSFERRED, and END.

In this case, the sending server would consider this a successful
transfer, and send the two lines READY and END as described below.

Otherwise, if the path is one for a transfer, the server must respond
with the line TRANSFER. The sending server should respond to this by
sending READY. At this point, the sending server has permanently
commited the creature to being sent, and if the connection dies before
the server responds with TRANSFERRED, then the sending server must try
again, with the same transaction ID, considering any response from the
server other than the four lines DISOMOD, READY, TRANSFER, TRANSFERRED
(optionally followed by the fifth line END) as being a failure of some
kind and trying again. Upon receiving READY, the server must save the
creature locally and then send back the lines TRANSFERRED and END.
Upon receiving TRANSFERRED, the sending server must consider the
transfer successful and must send the line END.

OTherwise, the path is one for an in-place mutation. The server must
send the line MUTATE. The sending server, upon receiving this, must
send the line READY and read the creature back. The server, upon
receiving the READY line, must send back the JSON blob representing
the creature after approriate mutation, and then must send the END
string. When the sending server has received the creature, it must
equally send the END string. If the connection dies at any point after
the sending server has received the MUTATE line, it can redo the
attempt, sending the original creature again (such transformation are
considered idempotent).

If the connection dies before the sending server sends its second
READY, then it can redo the attempt from the top.

Once a server receives END from the other side, if it has finished
sending everything (including its own END) then it should close the
connection.

Sample flows for a transfer server and a mutate server (the sending
server is the same in both cases; blank lines represent no data being
sent -- there are no actual blank lines in the protocol):

   Sending server    Transfer Server   Mutate Server  
   DISOMOD           DISOMOD           DISOMOD        
   path...             
   transaction ID
   READY
                     READY             READY          
   {creature}
                     TRANSFER          MUTATE
   READY
                     TRANSFERRED       MUTATED
                                       {creature}
   END               END               END


CREATURE BLOBS
--------------

The following object types exist:

 - creatures
 - items
 - locations
 - race
 + log entries:
   - diary
   - battle

objects are JSON strings with a "type" property whose value is a string which is one of the above.

{
 type: "creature",
 uuid: "65382f30-8195-4734-8941-c0a65f586c59";
 race: {
  type: "race",
  uuid: "c5ded26a-3561-4c8e-a07c-d3f4312d5e94",
  name: "Goblin",
 },
 name: "Frank",
 alignment: "Evil", // "Good", "Evil", or anything else; battles are fought between groups of creatures from two alignments
 home: {
  type: "location",
  uuid: "cc0e1b95-656b-4c18-a94d-020cca5ff7d9",
  name: "Londor",
  description: ["A small ", {
   type: "race",
   uuid: "c5ded26a-3561-4c8e-a07c-d3f4312d5e94",
   name: "Goblin",
  }, " settlement surrounded by forest and flanked on the east by large mountains." ],
 },
 log: [
  {
   type: "diary",
   date: "2010-02-02T12:12:01Z",
   description: [
    "Born in a cave in ",
    {
     type: "location",
     uuid: "cc0e1b95-656b-4c18-a94d-020cca5ff7d9",
     name: "Londor",
     description: ["A small ", {
      type: "race",
      uuid: "c5ded26a-3561-4c8e-a07c-d3f4312d5e94",
      name: "Goblin",
     }, " settlement surrounded by forest and flanked on the east by large mountains." ],
    },
    ", Frank quickly turned to a life of crime.",
   ],
  },
  {
   type: "battle",
   date: "2010-02-03T01:00:00Z",
   location: {
    type: "location",
    uuid: "cc0e1b95-656b-4c18-a94d-020cca5ff7d9",
    name: "Londor",
    description: ["A small ", {
     type: "race",
     uuid: "c5ded26a-3561-4c8e-a07c-d3f4312d5e94",
     name: "Goblin",
    }, " settlement surrounded by forest and flanked on the east by large mountains." ],
   },
   participants: [ // other than self, obviously; also, this only includes relevant battle information (i.e. not "log", "orders")
    { // state is as at start of battle
     type: "creature"
     uuid: "58f34ac8-a9a3-4440-bb0c-8fd6b97a6201",
     race: {
      type: "race",
      uuid: "8eb3f9ea-f839-4968-ae46-b576869fb844",
      name: "Human",
     },
     name: "George",
     alignment: "Lawful",
     home: {
      type: "location",
      uuid: "fca50eb0-0da5-4a31-856e-96865508856f",
      name: "Frontier Town",
     }
     hands: 2, // sum of 'hands' of 
     inventory: {
      head: null,
      torso: null,
      hands: null, // for gloves, not weapons
      feet: null,
      neck: null,
      finger: null,
      finger: null,
      back: {
       type: "item",
       slot: "back", // items can only be equipped in slots with the given name, or put inside objects with sufficient room; default none
       name: "Leather Backpack",
       size: 5, // counts against 'contents' ('capacity' slots); default 1
       capacity: 5, // sum of 'size' of items that can be placed inside 'contents' of this one; default 0
       contents: [
        {
         type: "item",
         slot: "gems",
         name: "Emerald",
         // size defaults to 1
         // capacity defaults to 0; contents is n/a
         // hands is n/a
         // sockets defaults to 0; gems is n/a
        },
        {
         type: "item",
         slot: "gems",
         name: "Emerald",
         // size defaults to 1
         // capacity defaults to 0; contents is n/a
         // hands is n/a
         // sockets defaults to 0; gems is n/a
        },
       ],
       // hands is n/a
       // sockets defaults to 0; gems is n/a
       // stats all default to 0
      },
      vehicle: null,
      weapon: [
       {
        type: "item",
        slot: "weapon",
        name: "Long Sword",
        size: 4,
        // capacity defaults to 0; contents is n/a
        hands: 2, // counts against 'weapon' ('hands' slots); only relevant for 'weapon' items
        sockets: 3, // max number of 'gem' items that can be placed inside 'gems' of this one; default 0
        gems: [],
        stats: { attack: { blunt: 2, }, }, // everything else defaults to 0
       },
      ],
     },
     stats: {
      hpmax: 10,
      hp: 10,
      manamax: 10,
      mana: 10,
      points: 0,
      attack: { // hp damage per round
       blunt: 1,
       sharp: 0,
       fire: 0,
       ice: 0,
      },
      defense: { // hp damage safely soaked per round
       blunt: 1,
       sharp: 1,
       fire: 0,
       ice: 0,
      },
     },
    },
   ],
  },
 ],
 inventory: {
  head: null,
  torso: null,
  hands: null,
  feet: null,
  neck: null,
  finger: null,
  finger: null,
  horn: null,
  tail: null,
  weapon: [],
 }
}

Doing battles:

// TODO
// potions
// spells
// notifications
//  - invoke a request to a given provider when something happens
//  - e.g. when disarmed, so that the home base can say how to rearm
//  - callbacks can't be something that happens frequently (once per battle is too often)
// orders:
//  - where to go
//  - when to abort and return
//  - which spells and potions to use
//  - how to decide what to equip, etc
// knowledge
//  - the map, including things to do at those places
//  - rumours, with a date, and a report count
// items having stuff they're made out of, including mass
// strength, a maximum total carryiable mass