Welcome to the official documentation for the CloudLink Suite!
Please keep the comments clean. Information pretaining to the extension and the server can be found here.
Note that these docs contain stability levels. You can view the information about stability levels here:
Stable
3 (Stable)
2.5 (Stable/Subject to Change)
2 (Subject to Change)
Experimental
1 (Experimental)
Draft/Not Usable
0 (Draft)
Deprecated
-1 (Not Recommended)
-2 (Deprecated)
Stability: 3 (Stable)
To use CloudLink, you will need to load it in a Scratch Mod. You can use one of the following to load CloudLink:
• TurboWarp
• SheepTester's E羊icques
• Ogadaki's Adacraft
Open the Editor, Click on the "Extensions" button (it looks like this): and click on "CloudLink".
Stability: 0 (Draft)
Learn more on how to correctly start a CloudLink connection.
Stability: 3 (Stable)
This block allows you to connect to any server IP. By default, it will allow you to connect to CloudLink running on your computer's localhost on port 3000 (see the server section for more details).
You can put in any server address that starts with wss://
.
(For security purposes, web browsers don't allow you to use server URLs that start withws://
, as these connections aren't secure. The only exception to this rule is the localhost/127.0.0.1.)
This block allows you to connect to a publically accessible server. The list is only updated when the extension is loaded. When the extension is loaded, a fetch request is sent and will download a string of JSON from GitHub, containing an array of all the available servers to use. From there, you can simply select a server through the menu button on the block, and run the block to connect.
To implement a user-friendly server selector, use the Server List reporter, the JSON parser block, and the Connect to IP block.
This block is intended for testing/development purposes only and shouldn't be used in production. Server list subject to change.
This block terminates the connection to the server. Should be self-explanitory.
This block sets your username on the link. This will only work when you are connected to a server.
The block only allows the username to be set when the input meets the following criteria:
The username block can only be used once per connection. To change your username, you must disconnect from the link and reconnect.
This block sends general-purpose packets to everyone (including you) connected on the link.
The block supports the following datatypes:
The packet will be sent under the following criteria:
Like the "Send (A name)" block, this block allows you to send general-purpose packets to specific usernames.
The packet supports all the same datatypes as it's counterpart, and only allows the packet to be send under the following criteria:
Like the "Send (Apple)" block, it allows you to send general-purpose packets to everyone (including you) on the link, however, this data is sent as a custom variable.
By specifying a name and setting a value, you can send an infinite amount of custom variables.
Like the "Send (Apple)" block, it supports all the same datatypes.
The packet will be sent, under the following criteria:
On the receiving end, if the variable doesn't exist, it will be created and it's value will be written. Variables will survive as long as the connection is established and running. All variable data will be destroyed when the connection is lost or the "Disconnect" block is used.
Like the "Send (Apple) to (A name)" block, it allows you to send general-purpose packets to specific usernames, however, this data is sent as a custom variable.
By specifying a name and setting a value, you can send an infinite amount of custom variables.
Like the "Send Var (Apple) with Data (Banana)" block, it supports all the same datatypes.
The packet will be sent, under the following criteria:
This block resets the "Got New [Global/Private/Direct/Status Code] Data?" reporter's value to False.
You can use this block to signify that a new Global/Private stream packet has been handled and that the code should wait for a new packet.
This block resets the "Got New [Global/Private] var (Apple) Data?" reporter's value to False.
You can use this block to signify that a new Global/Private variable packet has been handled and that the code should wait for a new packet.
This block is a custom command block, which allows you to interact with CloudLink to a greater degree than before. You can write your own commands for CloudLink Server and use this block to interface with those commands. See more information on how to use this block.
Stability: 3 (Stable)
This reporter returns the output of the global data stream. It is automatically updated whenever someone on the link uses the "Send (Apple)" block.
This reporter returns the output of your private data stream. It is automatically updated whenever someone on the link uses the "Send (Apple) to (A name)" block, and that the username has been set to yours.
This reporter returns the output of the direct data stream between the client and CloudLink server. It is automatically updated whenever the server sends information directly to the client.
This reporter returns the state of the extension as well as the state of the connection. It returns a int. value in this list:
Value | Description |
---|---|
0 | Extension started |
1 | Connecting to server |
2 | Connected |
3 | Disconnected |
This reporter returns the output of the current status code of the server. The output is updated when CloudLink server handles a request. You can view an entire list of possible codes here.
This reporter returns the current list of all connected users (Any username that has % at the beginning/end will be filtered out). The output is a string array, separated by a semicolon (;).
Example:
A name; Another name; apple; banana; MikeDEV; TheLongestUsername;
This reporter returns the currently set username for your client. It's value is set when the "Set (A name) as username" is used.
This reporter returns the current version number of the extension.
This reporter returns the current version number of the server.
This block returns a string of JSON containing the available public servers to use. This block is recommended for implementing a server selector. Block will be set immediately when the extension is loaded and the latest server list data has been fetched.
This block returns the output of the variable within the specified data stream. It is automatically updated whenever someone on the link uses the "Send Var (Apple) with Data (Banana)" (Global) or the "Send Var (Apple) to (A name) with Data (Banana)" (Private) block.
This block returns the output of parsed JSON.
Stability: 3 (Stable)
This boolean returns true if the link state is connected (2), and returns false otherwise.
You can use this block to syncronize connections.
This boolean returns true if the username has been set on the server and has been relayed to all clients.
You can use this block to syncronize connections.
This boolean returns true if there is new data present on the Global/Private/Direct/Status Code data streams. It's value(s) are reset when the "Reset Got New [Global/Private/Direct/Status Code] data" block is used.
You can use this block to wait for new packets and perform functions when a new Global/Private stream packet is received.
This boolean returns true if there is new data present on the global/private variables. It's value(s) are reset when the "Reset Got New [Global/Private] var (Apple) data" block is used.
You can use this block to wait for new packets and perform functions when a new Global/Private variable packet is received.
This boolean returns true if the specified username exists on the link.
You can use this block to check if a server is online or that a certain user is connected.
Stability: 2.5 (Stable/Subject to change)
CloudLink can be downloaded and imported as a python module. To install CloudLink, you can:
py -m pip install cloudlink (on Windows)
python3.9 -m pip install cloudlink (on Linux/Unix)
Simply download cloudlink.py and place it into your development environment's directory or into the directory where you'll be using it.
CloudLink has 2 dependencies, which are websocket-server
and websocket-client
. Install these libraries to get CloudLink to work.
To instanciate CloudLink in your project, simply add the following lines of code:
from cloudlink import CloudLink
cl = CloudLink()
From here, CloudLink will be accessable as part of the "cl" object. From there, you can do whatever you need CloudLink to do.
If you are exposing CloudLink to the internet, a good suggestion is to use a reputable tunneling or reverse proxy service. Also be sure to install CloudLink into a virtual environment or on a up-to-date machine!
For details on supported services, please see this GitHub Issue.
At the very end of your code, add cl.server()
. This will start running CloudLink in server mode, which creates a new websocket server on ws://127.0.0.1:3000/
.
from cloudlink import CloudLink
cl = CloudLink()
cl.server()
DO NOT INCLUDE ANY CODE AFTER STARTING THE CLOUDLINK SERVER, AS IT WILL NOT RUN. CLOUDLINK SERVER WILL BLOCK ANY OTHER CODE FROM RUNNING.
…or, you can just run CloudLink in another thread. But that's some otherworldly stuff there.
You can specify the port number CloudLink will host on by specifying the optional "port" parameter.
cl.server(port = 42069)
Like the "port" parameter, you can also specify the IP address CloudLink will run on using the optional "ip" parameter.
cl.server(ip = "0.0.0.0")
Like with CloudLink server, you can run the module as a client, which will emulate the likes of the Scratch extension. As you can probably tell, simply add cl.client()
to run as a client.
from cloudlink import CloudLink
cl = CloudLink()
cl.client()
By default, leaving this function with no extra parameters will connect to CloudLink on the Localhost on port 3000, or ws://127.0.0.1:3000/
.
You can specify the server IP to connect to by adding the "ip" parameter.
cl.client(wss://cloudlink-sample-server.mikedev101.repl.co/)
The server IP must be formatted using the following:
(ws or wss)://(subdomain).(hostname).(top level domain):(port number (if needed))/
It is not recommended to connect to an insecure websocket server (ws://). Ideally, you should connect to a secure websocket server (wss://).
Both CloudLink Client and CloudLink server can have callback bindings.
The on_packet
binding will function differently in CloudLink Server than CloudLink Client.
If running as a server, CloudLink will only run your function if the server gets a direct
command.
See CloudLink Protocol Reference for more details about commands.
Notice that while running the above scripts, nothing really happens in the console. That's because nothing else is happening! If you want to get CloudLink to do things, you'll need to bind some callbacks.
Binding callbacks is simple, as all you need to do is run:
cl.callback("(CALLBACK ID)", (A FUNCTION IN YOUR CODE))
from cloudlink import CloudLink
cl = CloudLink()
def got_a_message_for_you(message):
print(message)
cl.callback("on_packet", got_a_message_for_you)
cl.client()
As mentioned above, do NOT run any other code after starting the server or client, as it will not run.
Add your cl.callback()
function(s) BEFORE calling cl.server()
or cl.client()
.
CloudLink is designed to be as resilient and stable as possible, so even tiny exceptions will get caught, which might be problematic when you are debugging your code.
If you are running into code troubles and can't figure out why, you can pass along the "debug" parameter while instanciating CloudLink.
from cloudlink import CloudLink
cl = CloudLink(debug = true)
From here, CloudLink will start spitting out everything that occurs when you run a server or start a client.
It is recommended to enable the debug
parameter in a production server environment for info if you encounter server problems.
Stability: 2.5 (Stable/Subject to change)
Method | Description | Takes | Returns | Supported modes |
---|---|---|---|---|
server() |
Runs CloudLink in server mode. | (Optional) ip: Str. Type, Specifies the IP to bind to run CloudLink server on. (Optional) port: Int. Type, Specifies the port # to listen to. | None | None |
client() |
Runs CloudLink in client mode. | (Optional) ip: Str. Type, specifies the Server IP to connect to, not specifying this parameter will default to ws://127.0.0.1:3000/ |
None | None |
stop() |
Shuts down the server or disconnects the client. | (Optional) abrupt: Bool. Type, if true the server will forcefully terminate, leaving false or not specifying this parameter will default to graceful shutdown, this parameter only works if running in Server mode. | None | Client and Server |
callback() |
Binds a function for callbacks. | callback_id: Str. Type, the ID of the callback to bind to. function: Function Type, the function to run code when this callback is used | Depends on function. See Callback IDs for more info. | Client and Server |
sendPacket() |
Sends JSON packets. | msg: Dict type, see CLPv3 table for more info. | None | Client and Server |
setMOTD() |
Sends JSON packets. | enable: Bool type, can be set to true to enable Message-Of-The-Day, false to disable. motd: Str. type, the message to broadcast as the Message-Of-The-Day | None | Server only |
getUsernames() |
Returns the current username list. | None | List | Client and Server |
getIPofUsername() |
Returns the IP address of a username. | user: Str. Type, the username of the client to get the IP address from. | Str | Server only |
getIPofObject() |
Returns the IP address of a client using it's memory object instead of a username. | obj: Dict. Type, the memory address reference to a websocket client. | Str | Server only |
trustedAccess() |
Enables or disables Trusted Access on the server. | enable: Bool. Type, turns on/off the feature. keys: List Type, the list of valid keys, only supports strings. | None | Server only |
untrust() |
Manually untrusts a client. | obj: Dict. Type, the memory address reference to a websocket client. | None | Server only |
loadIPBlocklist() |
Loads a list of blocked IPs. | blist: List Type, a list containing strings of IP addresses to block. | None | Server only |
blockIP() |
Blocks an IP address. | ip: Str. Type, the IP address to block. | None | Server only |
unblockIP() |
Unblocks an IP address. | ip: Str. Type, the IP address to unblock. | None | Server only |
getIPBlocklist() |
Gets the current IP blocklist. | None | List | Server only |
Callback ID | Returns | Description |
---|---|---|
on_packet |
Message (JSON, String, Number) | Returns data when a new message is received. If the packet is JSON, thecmd parameter is the direct command, and the client does not have a username set up, it will return with the client's dict. memory object instead for the origin . |
on_error |
Error (String, Client Only), None (Server) | Returns error info if an error occurs. |
on_connect |
Client (Object, Server only), None (Client) | Runs code when the server gets a new user connected, or the client has connected to the server. |
on_close |
Client (Dict, Server only), None (Client) | Runs code when the server detects a disconnect or when the client closes the connection. |
Stability: 2.5 (Stable/Subject to change)
This is an example of a proper CloudLink connection setup.
{
"cmd": "ulist",
"val": "something; another boring name; foobar;"
}
{
"cmd": "gmsg",
"val": "Hello, world!"
}
{
"cmd": "direct",
"val": {"cmd": "type", "val": "(scratch/py/js)"}
}
{
"cmd": "setid",
"val": "(Username)"
}
{
"cmd": "direct",
"val": {"cmd": "ip", "val": "(IP ADDRESS)"}
}
{
"cmd": "direct",
"val": {"cmd": "type", "val": "(scratch/py/js)"}
}
I:112 | Trusted Access enabled
. The server will be listening for only direct
packets or gmsg
packets. Any other packet will return with E:115 | Refused
.{
"cmd": "statuscode",
"val": "I:112 | Trusted Access enabled"
}
{
"cmd": "direct",
"val": "(KEY)"
}
{
"cmd": "gmsg",
"val": "(KEY)"
}
I:100 | OK
and send over the username list and current "Global Data" value.{
"cmd": "ulist",
"val": "something; another boring name; foobar;"
}
{
"cmd": "gmsg",
"val": "Hello, world!"
}
{
"cmd": "setid",
"val": "(Username)"
}
Ever since CloudLink 0.1.6, a feature called UPL (Uniform Packet Layout) was introduced to make life easier for developers.
Before UPL, you had to stringify JSON if you had nested JSON because of the limitations of Scratch. However, starting with 0.1.7, UPL has been improved so that anyone with basic knowledge of JSON and websockets can interface with a CloudLink server.
The basic specifications of CloudLink UPL are as follows:
Some things to note:
val
cannot be larger than 1000 Characters (1KB) in size. Packets with val
larger than 1KB will be rejected by the server. If a packet specifies name
, it must not be larger than 100 characters.id
or origin
will likely end in being overridden by the server.As part of CloudLink's UPL (Uniform Packet Layout), sending packets is as simple as formatting some JSON to the following:
{
"cmd": "(Command)",
"val": (Payload),
"id": "(Username)"
}
This command will return as the following on the other end (if it's a private data command):
{
"cmd": "(Command)",
"val": (Payload),
"origin": "(Packet origin, username)"
}
If the packet is not a private data command, it will not include the "origin" parameter.
{
"cmd": "(Command)",
"val": (Payload),
}
Global stream variable packets can be sent/will return with the following:
{
"cmd": "gvar",
"val": (Payload),
"name": "(Variable name)"
}
Private stream variable packets can be sent with the following:
{
"cmd": "pvar",
"val": (Payload),
"name": "(Variable name)"
}
and will return with:
{
"cmd": "pvar",
"val": (Payload),
"name": "(Variable name)",
"origin": "(Packet origin, username)"
}
Because of a security explot found in past versions of CloudLink, specifying the origin
parameter manually will be overriden by the server.
Parameter | Description | Datatypes |
---|---|---|
cmd | Packet's command/function. See Commands Reference (CloudLink Only) for more info. | String |
val | Packet/variable data. | String, Int, Float, Dict/JSON |
id | Packet recipient's username, dependent on Packet command, see Commands Reference (CloudLink Only) for more info. | String |
origin | Packet origin's username, automatically added by server and cannot be specified manually, depends upon the existence/requirement of the "id" parameter. | String |
name | Variable name (Only used for gvar and pvar ) |
String |
Command (cmd ) |
Description | Takes (sending val ) |
Gives (receiving val ) |
Requires id parameter? (Yes -> Returns with origin parameter) |
Sends from |
---|---|---|---|---|---|
gmsg |
Global data stream | Dict/JSON, String, Float, Int. | Dict/JSON, String, Float, Int. | No | Client or Server |
pmsg |
Private data stream | Dict/JSON, String, Float, Int. | Dict/JSON, String, Float, Int. | Yes | Client or Server |
gvar |
Global data stream variable | Dict/JSON, String, Float, Int. | Dict/JSON, String, Float, Int. | No | Client or Server |
pvar |
Private data stream variable | Dict/JSON, String, Float, Int. | Dict/JSON, String, Float, Int. | Yes | Client or Server |
ulist |
Username list | Not applicable | String (Turbowarp, split with semicolons (; ) to convert to list) or List (Python) |
No | Server only |
direct |
Direct server-to-client and client-to-server communications, see Direct COMMs Reference | Dict/JSON, String, Float, Int. | Dict/JSON, String, Float, Int. | Yes | Client or Server |
setid |
Sets username for client and enables private/direct COMMs | String | Not Applicable | Not Applicable | Client only |
statuscode |
Returns server state info when any packet is handled (Kinda like HTTP status codes). See Status Codes for more info. | Not Applicable | String | Not Applicable | Server only |
If you have set a callback for on_packet
in CloudLink Server, it will only listen for packets that uses the direct
command. This reference below will provide more details on how to use this feature.
When the client connects to the server, it will send (at most 2) packets with the direct
command. Both will look something like this:
{
"cmd": "direct",
"val": {"cmd": "vers", "val": "(Version string, ex. '0.1.7')"}
}
The first packet is the version number reporter. This tells the client what version of CloudLink Server it has. This can be used to check if a client or server is out of date.
{
"cmd": "direct",
"val": {"cmd": "motd", "val": "(Message-Of-The-Day, ex. 'Hello, world!')"}
}
The second packet will return the Message-Of-The-Day (if the server admin chooses to enable it). This is just for fun, and does nothing important.
This feature allows you to use custom command handlers in CloudLink server. This assumes you know what you're doing.
{
"cmd": "direct",
"val": {"cmd": "(CUSTOM COMMAND)", "val": (PAYLOAD)}
}
The second packet will return the Message-Of-The-Day (if the server admin chooses to enable it). This is just for fun, and does nothing important.
Clients need to specify it's type. This helps with managing packets to help prevent hiccups. When the CloudLink Scratch Client connects, it will send over this JSON to the server:
{
"cmd": "direct",
"val": {"cmd": "type", "val": "scratch"}
}
…which tells the server that it will need to stringfify any nested JSON it finds. As for the Python client, it will send over:
{
"cmd": "direct",
"val": {"cmd": "type", "val": "py"}
}
…which tells the server that it doesn't need to stringfify any nested JSON it finds.
If you use CloudLink In a Browser, it must send over the following:
{
"cmd": "direct",
"val": {"cmd": "type", "val": "js"}
}
…which also tells the server that it doesn't need to stringfify any nested JSON it finds.
You can specify a custom client type by simply changing the nested val
's value. But if you use a custom client type, be warned that it will assume that your client can interpret nested JSON without stringification. Scratch cannot process nested JSON well, so this feature is to prevent hiccupps.
You can send custom data directly to the server using the following JSON:
{
"cmd": "direct",
"val": (Payload)
}
and you can write custom commands with this feature.
When a client sends a packet with the direct
command, it will return the following output to the on_packet
callback function:
{
"val": (Payload),
"origin": (Packet origin's username or dict. memory object)
}
You can use this feature to write custom command handlers to further expand CloudLink's functionality.
For example, you can write a custom command for ducks
.
{
"val": {"cmd": "ducks", "val": "quack"},
"origin": (Packet origin's username or dict. memory object)
}
from there, you can simply read back the values of on_packet
and it will return with:
{
"cmd": "ducks",
"val": "quack",
"origin": (Packet origin's username or dict. memory object)
}
This reference will only be for the statuscode
command. This table will provide more details on the state of the CloudLink Server.
(Type):(Code) | (Description)
Type | Code | Description |
---|---|---|
Letter (E for Error, I for Info) |
3-Digit Int. (ex. 123 ), describes the info/error in machine-readable format |
String, describes the info/error in a human-readable format |
Code |
Type |
Description |
---|---|---|
I:000 |
Test |
A test info code for debugging functions. |
I:100 |
OK |
The server handed the request correctly. |
E:101 |
Syntax |
The packet was not formatted correctly (Bad JSON?) or has missing parameters. |
E:102 |
Datatype |
The packet contained a parameter that was not the correct datatype. |
E:103 |
IDNotFound |
The client attempted to send/route a packet to an ID that currently doesn't exist. |
E:104 |
InternalServerError |
The server suffered an internal error. |
E:105 |
Loop |
While handing the request, the server predicted a loop would occur and has stopped the request. |
E:106 |
RateLimit |
The server is ratelimiting the request because the request has been used too many times too quicky. |
E:107 |
TooLarge |
The packet val or name is too large to safely use. |
E:108 |
BrokenPipe |
The server suffered a broken pipe error internally. |
E:109 |
EmptyPacket |
The server got an empty packet, which is basically useless. |
E:110 |
IDConflict |
The server refused to set the client's ID as it would conflict with an existing ID. |
E:111 |
IDSet |
The server refused to set the client's ID as it has already been set. |
I:112 |
TAEnabled |
The server has TA enabled, and is requesting the client to send a key. |
E:113 |
TAInvalid |
The server refused the key as it was invalid. |
E:114 |
TAExpired |
The server refused the key as it has expired. |
E:115 |
Refused |
The server refused to handle the packet. This can happen if the server has blocked your IP address or you attempted to send a packet while not authorized on a Trusted Access server. |
E:116 |
IDRequired |
The server cannot process the command without a username set for the client. |
E:117 |
TATrustLost |
The server has lost trust in the user. |
E:118 |
Invalid |
The command was invalid for the custom commands feature. |
E:119 |
Blocked |
The server has blocked the user's IP address and will refuse any packets it receives. |
E:120 |
IPRequired |
The server requires the client's IP address to gain trust. |