Tutorial by: Tlatoani


This is a tutorial to explain how to use the WebSocket features added in MundoSK 1.8.

All names of syntaxes that are capitalized and in italics like New WebSocket are the names of the syntaxes as used in the documentation, so you can know where to find them in MundoSK’s documentation.

What are WebSockets?

WebSockets are a way to transmit information between a server and a client, one of which (or both) will now be your Minecraft server. A websocket object represents one endpoint in such a connection.

Writing the Template(s)

Most likely the first step in writing your websocket system will be to write a template/templates for a websocket client and/or server. Depending on how you plan to use websockets, you may need to write both of these, only one of these, or both but on different servers (if you have multiple servers). The relevant documentation here is WebSocket Client Template and WebSocket Server Template.

The basic structure of a websocket client template is like this:

websocket client %string%:
    on handshake: #Called when the server sends a response handshake (before on open) Cannot be used to send messages
        #Code
    on open: #Called when the connection initially opens
        #Code
    on message: #Called when the server sends a message
        #Code
    on error: #Called when an error occurs for some reason
        #Code
    on close: #Called when the connection is closing
        #Code

The specified string is used as an identifier for this particular client template to be used when creating a new websocket using this template.

The most important ones here are on open, on message, on error, and on close. You only really need on handshake for more advanced functionality.

The basic structure of a websocket server template is like this:

websocket server %string%:
    on start: #Called when this websocket server is initially starting
        #Code
    on stop: #Called when this websocket server is stopping
        #Code
    on handshake: #Called when a client sends a request handshake (before on open for this particular connection) Cannot be used to send messages
        #Code
    on open: #Called when a connection initially opens
        #Code
    on message: #Called when a client sends a message
        #Code
    on error: #Called when an error occurs for some reason
        #Code
    on close: #Called when a connection is closing
        #Code

Like above, the specified string is used as an identifier for this particular server template to be used when starting a websocket server using this template.

In addition to the important ones above, here we also have on start and on stop. Also note that the four events that are shared with websocket client are now specified to certain clients, as the server will be connected to multiple clients most likely, and thus events like on open, on handshake, and on close will be called multiple times. One last thing to note here is that on handshake has a slightly different meaning than in websocket client, as it is called before the server sends a response handshake. This will be elaborated on more in the Handshake section of this tutorial.

Event-Values and Event-Specific Expressions in the Templates

Of course, those events would be mostly useless if we didn’t have any information about what was happening, so we need to next talk about event-values and event-specific expressions.

The most important event-value you will need is event-websocket. This is present in all of the events in websocket client as well as their analogues in websocket server (not on start or on stop). This represents the local endpoint of the websocket connection and can be used to perform actions such as sending messages and closing the connection which will be elaborated on further in the Doing Things section.

Next, event-string, which is present in the on message event, represents the message that was received.

event-throwable is present in the on error event and represents the error that caused the event to be called. This can be dealt with using MundoSK’s Throwable syntaxes to perform actions such as printing the stack trace of the error.

event-string is also present in the on close event and here represents the reason for closing.

event-number is present in the on close event and represents the closing code.

event-boolean is present in the on close event and represents whether the closing was initiated remotely (true) or locally (false).

[websocket] request [handshake] is the request handshake sent by the client to the server, and is present in the on handshake event, and for the server, in the on open event.

Similarly, [websocket] response [handshake] is the response handshake sent by the server to the client, and is present in the on handshake event, and for the client, in the on open event.

The above handshake expressions are grouped together in the documentation as Handshake Request/Response/New, and will be elaborated more on in the Handshake section of this tutorial.

Now, for some event-values and event-specific expression used only in websocket server:

all websockets is present in all events of websocket server and represents a list of all websockets that are endpoints for connections currently maintained by the websocket server. This is known in the documentation as All WebSockets of Server.

websocket [server] port is also present in all events of websocket server and represents the port on which the websocket server is open. This is known in the documentation as WebSocket Server Port.

[websocket] request [handshake] is (accepted|refused) is present in the on handshake event of websocket server and represents whether the client’s request was accepted or refused. This is known in the documentation as Request is Accepted and will be elaborated more on in the Handshake section of this tutorial.

Doing Things

Now that we know how to write our templates, it’s time to start doing things (as suggested by the name of this section).

The first syntax we’re going to explain here is the New WebSocket expression. The syntax is like this:

[new] websocket %string% connected to uri %string% [with (handshake|http) headers %-handshake%]

This returns a new websocket using the client template with the specified id that immediately connects to the server at the specified URI. URIs are used to identify servers/things in general on the internet (all URLs are URIs as well, at least by certain definitions of URI and URL). An example of a URI you could use to connect to a websocket server is wss://localhost:28000 (note that the syntax requires a string, so you must surround the URI in quotes). This URI would be used to connect to a websocket server on the same host as the client at the port 28000. The URI you need to use may differ in format depending on your situation. Finally, the optional specified handshake in the syntax is used to specify additional HTTP headers. This will be elaborated more on in the Handshake section.

The above expression is extremely important as you must use it to initiate a connection, and thus if you are trying to create a client connection to any websocket server the use of this expression is required.

The second syntax that will be important for using websockets is the WebSocket Send effect. The syntax is like this:

websocket send %strings% [through %-websockets%]

This does essentially what you would expect: sends the specified strings as messages through the specified websockets, or through event-websocket if no websockets are specified. Note that although you can send multiple messages in one use of this effect, each message sent will trigger its own on message event on the receiving end. Thus, if you want to send a lot of information together that would be hard to convey in some simple message, you might put the data you want to send in a jsonobject and then send the string form of that jsonobject, so that the receiving end can then convert the string back to a jsonobject and more easily read that information.

Another important syntax is the Close WebSocket effect. The syntax is like this:

close websocket %websocket% [with message %-string%]

This closes the connection of the specified websocket. Optionally, you can add a closing message to send. This is the reason described above for the on close event.

For websocket servers, mainly the effects you need to know just deal with starting and stopping a websocket server (Start WebSocket Server and Stop WebSocket Server):

start websocket server %string% at port %number%
stop websocket server at port %number%

For starting the server, the specified string is the id of the websocket server template that you would like to use. In both syntaxes, you need to specify a port on which the server is or should be. When you are starting the server, the port must be available - not being used by any other kind of server (ex. you can’t use the same port your Minecraft server is using, or the port another websocket server is using).

WebSocket State

A useful concept to have in mind with websockets is their connection state. In MundoSK, this is represented using a type known as websocketstate (WebSocket State). The possible states that a websocket can be in are:

not yet connected #When a websocket isn’t yet connected. You are unlikely to encounter this state using MundoSK as websockets here start connecting immediately upon creation
connecting #When a websocket is trying to connect to a server
open #When a websocket is connected to a server. This is the only time you should send messages through the websocket
closing #When a websocket is in the process of closing its connection (or having its connection closed by the other end of the connection)
closed #When a websocket’s connection has ended. The websocket is now mostly useless

You can get the connection state of a websocket using the Connection State of WebSocket expression:

[the] websocket state of %websocket%
%websocket%’[s] websocket state

Alternatively, you can directly compare a websocket to a websocketstate, for example:

if {_websocket} is connected:
    websocket send “Yay!” through {_websocket}

More Useful Expressions

Here are some more expressions you may find useful for dealing with websockets:

Host of WebSocket

[the] local host of %websocket% 
%websocket%'[s] local host  
[the] remote host of %websocket%    
%websocket%'[s] remote host 
[the] external host of %websocket%  
%websocket%'[s] external host

This is either the host which the websocket is on locally, or the host of the other endpoint of the connection.

Port of WebSocket

[the] local port of %websocket% 
%websocket%'[s] local port  
[the] remote port of %websocket%    
%websocket%'[s] remote port 
[the] external port of %websocket%  
%websocket%'[s] external port

This is either the port which the websocket is on locally, or the port of the other endpoint of the connection.

ID of WebSocket Client

[the] websocket id of %websocket%
%websocket%’[s] websocket id

This is the id of the websocket client template being used by the specified websocket. If the specified websocket is from a websocket server on this end, this will not be set.

ID of WebSocket Server

id of websocket server at port %number%

This is the id of the websocket server template being used by the websocket server open at the specified port.

Dealing with Handshakes

This is the section where I’ll be explaining everything relating to handshakes that I left unexplained in order to fully cover it here. Handshakes are the packets of data initially exchanged by websocket clients and servers when starting a connection. MundoSK has a handshake type (Handshake) which represents this data.

Before proceeding, it’s critical to be aware of the part of handshake data that you will be primarily interacting with: HTTP headers. An HTTP header is essentially a name - value pair with the name being unique within the handshake and both the name and the value being strings. Handshakes will contain multiple HTTP headers containing information required for a websocket connection to be initiated, and you may add additional headers in order to convey information that you need to during the initial connection for your specific setup. The expressions you need to manipulate these headers are below:

HTTP Header of Handshake

[handshake] [http] header %string% of %handshake%

This is used to get (and set) the value of the header with the specified name in the specified handshake. Note that if a header with a certain name is not contained in a handshake, it will be said to have a value of ”” rather than not being set.

HTTP Header Names of Handshake

[all] [handshake] [http] header names of %handshake%

This is just used to get all of the header names that are contained within the specified handshake.

One way you might want to use handshakes is to specify additional HTTP headers that you want, as a client, to be contained in the request handshake that will be sent to the server you are connecting to. To do this, let’s go back to the syntax for the New WebSocket expression:

[new] websocket %string% connected to uri %string% [with (handshake|http) headers %-handshake%]

As you can see, the syntax allows you to specify a handshake. This handshake is used to specify additional HTTP headers that you want included in the request handshake. In order to create a handshake that you can use to specify HTTP headers, you need to use the syntax new [websocket] handshake (grouped in the documentation with other expressions for handshakes as Handshake Request/Response/New). Note that the only information that will be used from the handshake you specify in the New WebSocket expression is its headers; when the client is sending its request, it creates a new handshake and adds the headers from the handshake you specify to that new handshake, while other information is added through other methods and sources.

Another important tool for dealing with handshakes is the on handshake event used in websocket client and websocket server templates. Although you won’t necessarily need this even if you are dealing with handshakes, they may be of use in certain situations.

As mentioned previously, on handshake has a slightly different meaning and behavior in websocket server and websocket client. Specifically, in websocket client, the on handshake event is called after the server’s response handshake has been received, which in turn means that the client has already sent the initial handshake request. However, in websocket server, the on handshake event is called when the client’s request handshake is initially received, meaning it is before the server has sent its response handshake, further meaning that here you have the ability to add information, specifically HTTP headers, to the response handshake before it is sent (whereas adding information to the request handshake on the client is done in the New WebSocket expression), and you can also choose to refuse the client’s request completely depending on the content of the request handshake. This makes the on handshake event a lot more useful in websocket server than in websocket client.

The expressions you may need for the on handshake event are listed below:

[websocket] request [handshake]

This is the request handshake sent by the client to the server. It is present in the on handshake event for both the client and the server, and also in the on open event for the server. This could be used to contain authentication information such as a password that is checked by the server in its on handshake event when deciding whether to accept or refuse the request.

[websocket] response [handshake]

This is the response handshake sent by the server to the client. It is present in the on handshake event for both the client and the server, and in the on open event for the client. In the on handshake event, this has not yet been sent and thus can be modified in order to specify information that should be sent to the client in the handshake.

The above expressions are grouped together in the documentation with the expression for creating a new handshake as Handshake Request/Response/New.

[websocket] request [handshake] is (accepted|refused)

This is known in the documentation as Request is Accepted. It is present only in the on handshake event of the server, and represents whether the client’s request is accepted or refused. This can be set, most likely according to information contained in the request handshake, in order to accept or refuse the client’s attempt to connect.

Additional Information Contained within Handshakes

Content of Handshake

handshake content of %handshake%

This is for a byte array of content within the specified handshake, represented in Skript as a list of numbers.

The following two expressions are only used for handshakes sent by the server (response) and will not be set for other handshakes.

HTTP Status of Handshake

[the] [handshake] http status of %handshake%
%handshake%’[s] [handshake] http status

This is for the HTTP status of the specified handshake, and is a number (specifically, a short).

HTTP Status Message of Handshake

[the] [handshake] http status message of %handshake%
%handshake%’[s] [handshake] http status message

This is for the HTTP status message of the specified handshake.

The following expression is only used for handshakes sent by the client (request) and will not be set for other handshakes.

Resource Descriptor of Handshake

[the] [handshake] resource descriptor of %handshake%
%handshake%’[s] [handshake] resource descriptor

This is for the resource descriptor of the specified handshake.

Inter-Server Chat Example

Finally, I want to demonstrate the use and power of websockets through an example. This is based off of an old example used for MundoSK’s older socket features, and allows players on different servers of yours to chat with each other.

There are two different scripts that make up this example: one is the hub script, used on a single server that acts as the hub for your global chat. The other is the other server script, used on all of your other servers. You enable the global chat by first doing the /enableglobalchat command on your hub server, then doing it on your other servers.

Hub Script:

websocket server "global chat":
  on handshake:
    if header "Password" of request isn't "verysecurepassword":
      set request is refused to true
  on message:
    broadcast "%event-string%"
    websocket send event-string through all websockets
  on error:
    send "Error occurred on the GlobalChat websocket server" to console
    print stack trace of event-throwable

function sendGlobalMessage(player: string, msg: string):
  set {_message} to "<%{_player}%>: %{_msg}%"
  broadcast {_message}
  websocket send {_message} through all websockets of server at port 25000

on server start:
  set {GlobalChatEnabled} to false

command /enableglobalchat:
  trigger:
    if {GlobalChatEnabled}:
      send "&2GlobalChat is already enabled!"
      stop trigger
    set {GlobalChatEnabled} to true
    start websocket server "global chat" at port 25000
    send "&2GlobalChat has been enabled!"

on chat:
  if {GlobalChatEnabled}:
    cancel event
    sendGlobalMessage("%player%", message)

Other Script

websocket client "global chat":
  on message:
    broadcast "%event-string%"
  on error:
    send "Error occurred on the GlobalChat websocket client" to console
    print stack trace of event-throwable

function sendGlobalMessage(player: string, msg: string):
  set {_message} to "<%{_player}%>: %{_msg}%"
  websocket send {_message} through {GlobalChatWebSocket}

on server start:
  set {GlobalChatEnabled} to false

command /enableglobalchat:
  trigger:
    if {GlobalChatEnabled}:
      send "&2GlobalChat is already enabled!"
      stop trigger
    set {GlobalChatEnabled} to true
    set {_headers} to new handshake
    set header "Password" of {_headers} to "verysecurepassword"
    set {GlobalChatWebSocket} to new websocket "global chat" connected to uri "wss://localhost:25000" with http headers {_headers}
    send "&2GlobalChat has been enabled!"

on chat:
  if {GlobalChatEnabled}:
    cancel event
    sendGlobalMessage("%player%", message)

Did you find Tlatoani's tutorial helpful?


You must be logged in to comment