1. Welcome to skUnity!

    Welcome to skUnity! This is a forum where members of the Skript community can communicate and interact. Skript Resource Creators can post their Resources for all to see and use.

    If you haven't done so already, feel free to join our official Discord server to expand your level of interaction with the comminuty!

    Now, what are you waiting for? Join the community now!

Dismiss Notice
This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

MundoSK WebSockets

Discussion in 'Tutorials' started by Tlatoani, Jan 14, 2018.

  1. Tlatoani

    Addon Developer

    Joined:
    Jan 24, 2017
    Messages:
    151
    Likes Received:
    89
    Medals:
    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:
    Code (Skript):
    1. websocket client %string%:
    2.    on handshake: #Called when the server sends a response handshake (before on open) Cannot be used to send messages
    3.        #Code
    4.    on open: #Called when the connection initially opens
    5.        #Code
    6.    on message: #Called when the server sends a message
    7.        #Code
    8.    on error: #Called when an error occurs for some reason
    9.        #Code
    10.    on close: #Called when the connection is closing
    11.        #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:
    Code (Skript):
    1. websocket server %string%:
    2.    on start: #Called when this websocket server is initially starting
    3.        #Code
    4.    on stop: #Called when this websocket server is stopping
    5.        #Code
    6.    on handshake: #Called when a client sends a request handshake (before on open for this particular connection) Cannot be used to send messages
    7.        #Code
    8.    on open: #Called when a connection initially opens
    9.        #Code
    10.    on message: #Called when a client sends a message
    11.        #Code
    12.    on error: #Called when an error occurs for some reason
    13.        #Code
    14.    on close: #Called when a connection is closing
    15.        #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,
    Code (Skript):
    1. event-string
    , which is present in the `on message` event, represents the message that was received.

    Code (Skript):
    1. 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.

    Code (Skript):
    1. event-string
    is also present in the `on close` event and here represents the reason for closing.

    Code (Skript):
    1. event-number
    is present in the `on close` event and represents the closing code.

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

    Code (Skript):
    1. [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,
    Code (Skript):
    1. [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`:

    Code (Skript):
    1. 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.

    Code (Skript):
    1. 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.

    Code (Skript):
    1. [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 [I]Request is Accepted[/I] and will be elaborated more on in the Handshake section of this tutorial.
    2.  
    3. [B]Doing Things[/B]
    4.  
    5. Now that we know how to write our templates, it’s time to start doing things (as suggested by the name of this section).
    6.  
    7. The first syntax we’re going to explain here is the [I]New WebSocket [/I]expression. The syntax is like this:
    8. [code=Skript][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:
    Code (Skript):
    1. 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:
    Code (Skript):
    1. 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):
    Code (Skript):
    1. start websocket server %string% at port %number%
    2. 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:
    Code (Skript):
    1. 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
    2. connecting #When a websocket is trying to connect to a server
    3. open #When a websocket is connected to a server. This is the only time you should send messages through the websocket
    4. closing #When a websocket is in the process of closing its connection (or having its connection closed by the other end of the connection)
    5. 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:
    Code (Skript):
    1. [the] websocket state of %websocket%
    2. %websocket%[s] websocket state
    Alternatively, you can directly compare a websocket to a websocketstate, for example:
    Code (Skript):
    1. if {_websocket} is connected:
    2.    websocket send “Yay!” through {_websocket}
    More Useful Expressions

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

    Host of WebSocket

    Code (Skript):
    1. [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
    Code (Skript):
    1. [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
    Code (Skript):
    1. [the] websocket id of %websocket%
    2. %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
    Code (Skript):
    1. 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
    Code (Skript):
    1. [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
    Code (Skript):
    1. [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:
    Code (Skript):
    1. [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
    Code (Skript):
    1. 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:

    Code (Skript):
    1. [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.

    Code (Skript):
    1. [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.

    Code (Skript):
    1. [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
    Code (Skript):
    1. 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

    Code (Skript):
    1. [the] [handshake] http status of %handshake%
    2. %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

    Code (Skript):
    1. [the] [handshake] http status message of %handshake%
    2. %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

    Code (Skript):
    1. [the] [handshake] resource descriptor of %handshake%
    2. %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:
    Code (Skript):
    1. websocket server "global chat":
    2.   on handshake:
    3.     if header "Password" of request isn't "verysecurepassword":
    4.       set request is refused to true
    5.   on message:
    6.     broadcast "%event-string%"
    7.     websocket send event-string through all websockets
    8.   on error:
    9.     send "Error occurred on the GlobalChat websocket server" to console
    10.     print stack trace of event-throwable
    11.  
    12. function sendGlobalMessage(player: string, msg: string):
    13.   set {_message} to "<%{_player}%>: %{_msg}%"
    14.   broadcast {_message}
    15.   websocket send {_message} through all websockets of server at port 25000
    16.  
    17. on server start:
    18.   set {GlobalChatEnabled} to false
    19.  
    20. command /enableglobalchat:
    21.   trigger:
    22.     if {GlobalChatEnabled}:
    23.       send "&2GlobalChat is already enabled!"
    24.       stop trigger
    25.     set {GlobalChatEnabled} to true
    26.     start websocket server "global chat" at port 25000
    27.     send "&2GlobalChat has been enabled!"
    28.  
    29. on chat:
    30.   if {GlobalChatEnabled}:
    31.     cancel event
    32.     sendGlobalMessage("%player%", message)
    Other Script
    Code (Skript):
    1. websocket client "global chat":
    2.   on message:
    3.     broadcast "%event-string%"
    4.   on error:
    5.     send "Error occurred on the GlobalChat websocket client" to console
    6.     print stack trace of event-throwable
    7.  
    8. function sendGlobalMessage(player: string, msg: string):
    9.   set {_message} to "<%{_player}%>: %{_msg}%"
    10.   websocket send {_message} through {GlobalChatWebSocket}
    11.  
    12. on server start:
    13.   set {GlobalChatEnabled} to false
    14.  
    15. command /enableglobalchat:
    16.   trigger:
    17.     if {GlobalChatEnabled}:
    18.       send "&2GlobalChat is already enabled!"
    19.       stop trigger
    20.     set {GlobalChatEnabled} to true
    21.     set {_headers} to new handshake
    22.     set header "Password" of {_headers} to "verysecurepassword"
    23.     set {GlobalChatWebSocket} to new websocket "global chat" connected to uri "wss://localhost:25000" with http headers {_headers}
    24.     send "&2GlobalChat has been enabled!"
    25.  
    26. on chat:
    27.   if {GlobalChatEnabled}:
    28.     cancel event
    29.     sendGlobalMessage("%player%", message)
    Questions? Comments? Suggestions or improvements? Comment below!
     
    • Like Like x 4
    • Informative Informative x 1
    • Friendly Friendly x 1
  2. Spartan9802

    Spartan9802 Member

    Joined:
    Jan 26, 2017
    Messages:
    158
    Likes Received:
    8
    Hello,
    Could you add an example to communicate from php to the server?
     
  3. PNB

    PNB Member

    Joined:
    Jan 27, 2017
    Messages:
    22
    Likes Received:
    3
    Very informative. Thanks for this great tutorial!
     
    • Like Like x 1
Loading...