ugBASIC User Manual

DOJO support

Available on: c64 c64reu coco coco3

The ugBASIC language is equipped with primitives capable of communicating with an external server, called DOJO, which is a system for creating virtual "game rooms" online. This guide contains information and tutorials to make the best use of this feature, as well as a description of the DOJO protocol

Preparation Services Message Ports

Preparatory activities

In order to use the DOJO protocol features, it is necessary to connect the retrocomputer to a server that implements the protocol. The ugBASIC language provides several servers that can be used, and the procedure to link to it is different, depending on the target.

TRS-80 Color Computer 1/2/3

All coco and coco3 emulators support the Becker port. To enable this port on XRoar, you need to use the -cart-becker command line option. Moreover, you need to give a specific TCP port and address to use. On that address a server that implements the DOJO protocol should listen for TCP/IP connections from the machine the emulator runs on. They make that connection every time it starts.

In this example, we use the ugBASIC's server:
xroar -machine coco -cart-becker -becker-ip dojo.ugbasic.iwashere.eu -becker-port 50666 dojo_example_01.bin
xroar -machine coco3p -cart-becker -becker-ip dojo.ugbasic.iwashere.eu -becker-port 50666 dojo_example_01.bin

Commodore 64

All c64 emulators support the RS-232 port by using the ACIA1 chipset. In order to test the operation on a PC, you need a modem connected to the RS-232 port or its emulation. Emulators allow you to connect the RS-232 port to a TCP port, and then communicate with a "server" that acts as a modem (unless there is a modem integrated into the emulator). From empirical tests carried out on current software, there are some problems (or actual "bugs") that prevent the correct operation of some of these configurations "out of the box".

At the moment the following configuration has been successfully tested:

  • vice - VICE 3.8 emulator, recompiled a specific fix (), Windows binaries available here (20 MB):
    Patch for VICE 3.8

    --- a/src/rs232drv/rsuser.c
    +++ b/src/rs232drv/rsuser.c
    @@ -243,10 +243,10 @@ static int set_up_device(int val, void *param)

         rsuser_device = val;

    -    if (fd >= 0) {
    -        rs232drv_close(fd);
    -        fd = rs232drv_open(rsuser_device);
    -    }
    +    // if (fd >= 0) {
    +    // rs232drv_close(fd);
    +    // fd = rs232drv_open(rsuser_device);
    +    // }
        return 0;
    }

    @@ -457,9 +457,9 @@ static void rsuser_setup(void)
        clk_start_tx = 0;
        clk_start_bit = 0;
        dsr_cnt = 0;
    -    if (fd < 0 && rsuser_enabled) {
    -        fd = rs232drv_open(rsuser_device);
    -    }
    +    // if (fd < 0 && rsuser_enabled) {
    +    // fd = rs232drv_open(rsuser_device);
    +    // }
        alarm_set(rsuser_alarm, maincpu_clk + char_clk_ticks / 10);
    }

  • tcpser - IP232 modem, recompiled under cygwin with a specific fix (), Windows binaries available here (130 KB).
    Patch for TCPSER

    --- a/src/bridge.c
    +++ b/src/bridge.c
    @@ +236,7 -236,6 @@
            } else {
                LOG(LOG_INFO, "Link has gone down");
                writePipe(cfg->wp[0][1], MSG_LE_DOWN);
    +            new_status = -1;
            }
        }

It is therefore necessary to recompile the executables with these fixes, and run the following command lines:

tcpser -v 25233 -p 6400 -s 1200
x64sc -default -acia1 -rsdev1 "127.0.0.1:25233" -rsdev1baud "1200" -rsuserbaud "1200" -userportdevice "2" -myaciadev "0"

Note that the tcpser must be restarted each time the emulator is closed after connected to the DOJO server.

Available services

The DOJO protocol provides a set of commands that are implemented as statements and functions on ugBASIC, and serve to model a set of services:

  • DIRECT LINK - allows you to read and write one or more bytes from the communication channel;
  • MESSAGE PORTS - allows you to create ports where to send and receive messages.

DIRECT LINK

If you choose to use this mode, you have access to a direct two-way communication channel to the DOJO server. This type of connection can be useful if you want to interact directly with the protocol elements. In this case, the available instructions are as follows:

  • = PING() - return TRUE if the connection is alive;
  • = RECEIVE() - receive a byte from the communication channel, and it will wait indefinitely until the next byte arrives;
  • SEND - send a byte into the communication channel, and it will wait indefinitely until sent;
  • = READY() - return TRUE if a byte to receive is present.

MESSAGE PORTS

In this mode, a mechanism for managing ports and messages is set up above the communication channel, which are elements of asynchronous communication between programs. They are very useful for allowing the management of multiplayer games. The instructions that are available in this mode are:

  • = PING() - return TRUE if the connection is alive;
  • = LOGIN(...) - it allows the client to be identified at the server;
  • = SUCCESS(...) - it allows to understand if the last command was successfully executed;
  • = CREATE PORT(...) - it allows to create a (receiving) port;
  • = FIND PORT(...) - it allows to find out a (sending) port;
  • PUT MESSAGE ... - it allows to send a message to a port;
  • = PEEK MESSAGE(...) - it allows to check if a message is available;
  • = GET MESSAGE(...) - it allows to retrieve any available message;
  • DESTROY PORT - it allows to destroy the receiving port;

MESSAGE PORTS FOR BEGINNERS

IDENTIFY YOURSELF


Message ports work on the assumption that they can uniquely identify the program that wants to communicate with others. In reality, it is not so much the program that wants to communicate but the user of the program. For this very reason, the first operation to be performed is precisely that of "logging in". The syntax is very simple:

= LOGIN( username, password )

Note that on DOJO servers there is no different process between registration and access to the systems: this is because, in fact, it is unlikely that the user's situation has persisted on the retrocomputers. It follows that, at the first attempt to access, there will also be contextual registration of the user; after the first access, each subsequent access must have the same credentials, and there is no way to change them, at least with this version of the DOJO protocol.

The LOGIN function return a sessionId, To be used on other commands / functions. To verify that the operation has taken place, the SUCCESS() function must be used. If SUCCESS(...) returns a TRUE result, then the login process was successful; if it returns a FALSE result, then the process must be considered failed for some reason (the username may have already been used, or the credentials are incorrect, and so on).

OPEN (AT LEAST) ONE (RECEIVING) PORT


A message port can be thought of as a mailbox. When someone (a program or a user that uses a program) sends a message to a port, it is as if we were asking a postal system to deliver a letter to the mailbox (of another program). The postal service guarantees that the message will be delivered, but of course it cannot guarantee when this will happen. Therefore, the order in which messages arrive may not be the same as the order in which they are sent.

To create a message port where receive messages, simply use the CREATE PORT(...) function, whose syntax is as follows:

= CREATE PORT( sessionId, application )

The sessionId parameter is the same one that returned the call to LOGIN(...), and identifies the working session at that moment. The application parameter represents, instead, the name of the application or functionality to which you want to associate this message port.


The message port (mailbox) is owned by the user but also by each individual application or application functionality. Therefore, it is possible to define more than one mailbox (i.e., a port) for a single user, as long as each of them is identified by a different application code.

FIND OUT THE (SENDING) PORT


To send a message, we need to know the address of that message port. In DOJO protocol jargon, a message box is identified by a unique identifier, called "port id". So the first useful operation is to find out the port id to use to send the message. This is exactly the purpose of = FIND PORT(...) function.

The syntax follows:

port = FIND PORT( sessionId, username, application )

So basically three pieces of information are needed to identify that mailbox. First of all, you need an identifier that allows the user (sender) to be recognized by the DOJO server (sessionId). To get it, you need to identify yourself, and you can do that with the procedure explained in the previous chapter. Secondly, you need to know the identifier of the user who owns that mailbox (username). Finally, it is essential to know the name of the application on whose behalf we are looking for the mailbox (application).


As we mentioned before, it is possible to have multiple ports assigned to a user. However, it is also true that each port is marked with an application identifier (or, if we want, an application functionality identifier). The consequence is that the DOJO protocol indirectly creates communication networks whose nodes belong to the same application. This is the basis for sharing servers by programs that are also very different from each other.

PUT A MESSAGE TO A PORT

To put a message we can use the PUT MESSAGE statement, with the following syntax:

PUT MESSAGE portId, message

This is a statement, not a function. It follows that we cannot know if the statement was successful. This does not really represent a limitation, for the simple fact that the statement will always be successful: if the program on the other side of the port is not available, for some reason, the message will be preserved by the DOJO server, and it will be delivered at the first available moment.


WAIT FOR A MESSAGE

Once you have created a port and sent a message, you may wait for a message to arrive to you. As explained in the previous chapters, the port is accessible to anyone who knows its identifier (perhaps because they have searched for it). Therefore, many messages may arrive or none at all. The program that wants to wait for the arrival can use the PEEK MESSAGE command. The syntax is as follows:

= PEEK MESSAGE( portId )

If it returns FALSE, it means that the message port is empty; on the contrary, if it returns TRUE, it is possible to fetch the message using the GET MESSAGE command. The syntax is:

= GET MESSAGE( portId )


Note that the message port behaves like a queue, that is, the first message that is inserted is the first that is retrieved. Therefore, if a program sends three messages in a row to the attention of the port of another, the receiving program will receive the three messages in the same order.


Obviously, if several programs do the same thing, the messages will be interspersed but, in the end, they will be in the same order.

Any problem?

If you have found a problem, if you think there is a bug or, more simply, you would like something to be improved, write a topic on the official forum, or open an issue on GitHub.

Thank you!