![CLOUDLINK 4 BANNER](https://user-images.githubusercontent.com/12957745/188282246-a221e66a-5d8a-4516-9ae2-79212b745d91.png) ##### CL4 banner(s) made by [@zedthehedgehog](https://github.com/zedthehedgehog) # Cloudlink 4.0 Cloudlink is a free and open-source, websocket-powered API optimized for Scratch 3.0. Cloudlink comes with several powerful utilities and features: * **Fast messages between clients and servers** - Typical responses within 10ms! * **No more race conditions** - Queued messages guarantee that you will never miss a message. * **Large data limits** - Send up to 1,000 characters per message! * **Proven reliability** - Extensively tested and utilized in [The Meower Project](https://github.com/meower-media-co/)! * **Custom links/rooms** for high-speed multiplayer or intelligent live updates! * **A powerful alternative to Scratch Cloud Variables** - Cloudlink performs everything that Scratch's cloud variables can - And a lot more. **_Cloudlink Server also supports the Cloud Variable protocol._** * **The CloudLink Suite** - enhances functionality of your Scratch projects by adding cross-project file storage (CloudDisk), universal in-game currency (CloudCoin), and smarter user identification (CloudAccount) *Coming soon!* # In a hurry? * ### I just want to try out Cloudlink. [**Why not give it a try?**](#Quickstart) * ### I want to learn how to use Cloudlink in my new Scratch project. How can I do that? [**Here you go!**](#Scratch-Client) * ### I'm making something Cloudlink-powered in Python. Where do I go? [**Look no further!**](#Python-Client--Server) * ### I want to make my own Cloudlink server. What should I do? [**Take a look at this!**](#Server) * ### I would like to make my own port of Cloudlink to x language, is there some documentation on Cloudlink's protocol? [**Yes indeed!**](#Cloudlink-Protocol-v4-CLPv4) * ### I want to suggest a feature / report a bug or vulnerability. [**Please visit the Github Repo to make suggestions/reports.**](https://github.com/MikeDev101/cloudlink/issues) * ### I need older documentation! Where can I find it? See https://hackmd.io/@MikeDEV/SywNTBqRD * ### Quick links to load the extension - [TurboWarp](https://turbowarp.org/editor?extension=https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) - [SheepTester's E 羊 icques](https://sheeptester.github.io/scratch-gui/?url=https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) - [Ogadaki's Adacraft - NOT UPDATED AS OF 10.3.2022](https://adacraft.org/studio/) - [Ogadaki's Adacraft (Beta) - NOT UPDATED AS OF 10.3.2022](https://beta.adacraft.org/studio/) * ### Quick links for old stuff #### [Discussion Forum (Archive)](https://scratch.mit.edu/discuss/topic/398473) #### [Cloudlink JS "CLJS"](https://github.com/williamhorning/archives/tree/main/cloudlink) # Stability levels ::::success **Stable** 3 (Stable) 2.5 (Stable/Subject to Change) 2 (Subject to Change) :::: ::::warning **Experimental** 1 (Experimental) :::: ::::info **Draft/Not Usable** 0 (Draft) :::: ::::danger **Deprecated** -1 (Not Recommended) -2 (Deprecated) :::: # What's new in Cloudlink 4? ::::success **Stable** 3 (Stable) :::: CloudLink 4 brings new features, enhancements, and security fixes. Here's a brief overview of all new features: * **Scratch's cloud variable protocol support** - You can now natively send/receive global variables using Scratch's native cloud variable interface! There's no limit on how many variables you can use. You can use letters, numbers, symbols, even JSON! You can also have a (theoretically) unlimited number of users connected to a project, *and you get a very generous 1,000 character limit.* ::::info This feature is supported on Cloudlink versions >0.1.9.x. To use this feature, you will need to look into your Scratch mod's documentation regarding custom cloud variable servers. In most instances, you should have a URL option to specify a cloud variable host, which will use the same URL formatting as Cloudlink. :::: * **Multicasting** - You can send packets to more than one client at a time. * **Enhanced user ID System** - Adds support for multiple identical usernames on a single server. * **Rooms** - Allows clients to communicate without interfering with the parent Cloudlink server. This opens up the possibility of many mini-servers for projects like MMOs, Chats, and other high-speed networking projects. * **Message Listeners** - Expecting a result for a request? Attach a listener, and Cloudlink will take care of the rest. No need to use complex message handling code. Sending a message with a listener gurantees your client will get a response. * **Packet Queue** - Have slow message handling code? Cloudlink's Scratch Extension got you covered. With a built-in message queue, you can manage many messages in bursts and seamlessly manage more without extra project bloat. * **Custom command loader** - Simply create a class storing your custom commands, and Cloudlink will take care of the rest. ::::info This feature is exclusive to Cloudlink's Python Server/Client implementations *(As of 10.3.2022)*. If you want to develop custom commands for your own Cloudlink implementation, [**make sure to conform to the Cloudlink Protocol.**](https://https://hackmd.io/M8uS_IgOQZmzxwWJlL-miw#Cloudlink-Protocol-v4-CLPv4) :::: * **Command disabler** - Disable certain Cloudlink commands for more security-focused applications. * **Improved IP management system** - Cloudlink can identify clients more effectively by reading IP addresses directly from the system, or through HTTP headers (By default, configured for Cloudflare). If there's a bad client on a known malicious IP address, Cloudlink rejects all connections. ::::warning By default, Cloudlink compares client IPs against a list of strings. In some implemetations, servers might disable this feature, or modify it so that only malicious IPs/Proxies/VPNs get blocked. If you do not wish for your IP address to be detected/blocked by a server host, you should not use Cloudlink. :::: ::::danger **Beware suspicious / private projects!** It's the internet, if something can go wrong, it will. Use some common sense: Don't click links you don't trust, and don't connect to servers you don't know / aren't familar with. **You should demand to see the project's source code!** :::: * **Reject mode** - If you need to put the server in a private mode, you can set Reject mode to deny any new clients from connecting. ::::danger If you restart a server with reject mode enabled, you will **NOT** be able to connect. When you enable reject mode, **make sure to stay connected**, or you will **NOT** be able to reconnect without manually diabling reject mode! By default, Cloudlink leaves reject mode disabled. If your custom implementation enables reject mode on startup, **I won't be able to help you regain access!** :::: * **Sandboxed/Unsandboxed support** - The Scratch extension supports being loaded in a sandboxed or unsandboxed environment. ::::info If you want to run Cloudlink unsandboxed, consider using a code injector extension for your browser! :::: # Quickstart ::::success **Stable** 3 (Stable) :::: You can try Cloudlink without needing to host your own server. There are several publically-hosted Cloudlink instances available, which can be found in [serverlist.json](https://github.com/MikeDev101/cloudlink/blob/master/serverlist.json) or through the Server List block. Cloudlink was originally created for Scratch 3.0. You can view the latest version of Cloudlink in any of these Scratch editors: - [TurboWarp](https://turbowarp.org/editor?extension=https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) - [SheepTester's E 羊 icques](https://sheeptester.github.io/scratch-gui/?url=https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) As of 10.3.2022, these mods have not been updated to support Cloudlink 4: - [Ogadaki's Adacraft](https://adacraft.org/studio/) - [Ogadaki's Adacraft (Beta)](https://beta.adacraft.org/studio/) Cloudlink is also available as a Python module, which comes bundled with the Cloudlink Server. [There is even a browser friendly version of Cloudlink available as CLJS, but it has been discontinued.](https://github.com/williamhorning/archives/tree/main/cloudlink) # Scratch Client ::::info **Draft/Not Usable** 0 (Draft) :::: ## Getting started To use the Cloudlink Extension, you will need to have a basic understanding of how JSON and Scratch works. I'm not here to lecture you on how those work. Google is your friend. ### Loading Cloudlink (Sandboxed) Before you can use Cloudlink, you will need to load the extension. The Cloudlink Extension supports sandboxed and unsandboxed importing. By default, these extensions run Cloudlink in a sandboxed environment: - [TurboWarp](https://turbowarp.org/editor?extension=https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) - [SheepTester's E 羊 icques](https://sheeptester.github.io/scratch-gui/?url=https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) As of 10.3.2022, these mods have not been updated to support Cloudlink 4: - [Ogadaki's Adacraft](https://adacraft.org/studio/) - [Ogadaki's Adacraft (Beta)](https://beta.adacraft.org/studio/) **But be advised: Loading Cloudlink in a sandboxed environment will harm performance.** > Web Workers cannot access the globals in the window object (instead, they have a global self object, which is much more limited), so you cannot use them for things like gamepad access. > > Sandboxed extensions do not have access to the Scratch runtime object. > > Sandboxed extensions are much slower. > > JavaScript console error messages for sandboxed extensions are more cryptic in Chrome. ([samq64, Scratch Forums](https://scratch.mit.edu/discuss/topic/528839/)) ...which is why it's recommended to load Cloudlink in an unsandboxed environment. ### Loading Cloudlink (Unsandboxed) To load Cloudlink in an unsandboxed environment, you will need to: 1. Get the "Code Injector" extension for your Browser ([Chrome](https://chrome.google.com/webstore/detail/code-injector/edkcmfocepnifkbnbkmlcmegedeikdeb?hl=en), [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codeinjector/), [Edge](https://microsoftedge.microsoft.com/addons/detail/code-injector/kgmlfocfgenookigofalapefagndnlnc)) 2. Select all and copy the [extension source code](https://mikedev101.github.io/cloudlink/S4-0-nosuite.js) 3. Open the Code Injector extension menu: ![example icon](https://i.imgur.com/ADorgMF.png) 4. Click on "Add rule" ![add rule button](https://i.imgur.com/DvQdbJh.png) 5. Click on "Current host" ![current host](https://i.imgur.com/GRkRr7j.png) 6. Paste the extension code into the JS box, and then click "Save" ![example](https://i.imgur.com/Ryzj6xS.png) 7. Refresh your Scratch Mod and Cloudlink should be there! In the blocks menu in the editor, you will see a white and green icon for Cloudlink. Clicking on that will bring you to all the Cloudlink blocks. ## It's loaded, now what? To use Cloudlink, you will also need a server! If you feel ambitious, [see how to make a server of your own!](#Server) If you don't have the time/resources to run a server, you can simply connect to any of the freely hosted, public servers: ![cl4test](https://i.imgur.com/NPBn9FC.png) ![tnixserver](https://i.imgur.com/ZalR6Op.png) You can connect to any of the servers in the Sever list block using: ![example-public](https://i.imgur.com/vRPqc6Z.png) or, you can use: ![example-public2](https://i.imgur.com/1FE9QxB.png) Index 0 of the Server list will connect to your localhost (`ws://127.0.0.1:3000/`), which requires running a server. ### Tips, tricks, and warnings There are many ways to interact with Cloudlink, but here are some tips/tricks to properly use Cloudlink: ::::danger The Cloudlink Extension only supports **one server connection at a time.** I am not going to change this. If you need to manage multiple servers, consider using the Python Client, ~~or wait for Cloudlink Relay.~~ :::: ::::danger **To keep your client/server(s) from catching fire, it is not recommended you send messages in a forever loop without a delay.** ![dontdothis](https://i.imgur.com/yxOG5Mo.png) ![dothis1](https://i.imgur.com/PZqsxja.png) ![dothis2](https://i.imgur.com/GxEf9Q4.png) :::: ::::warning **It is not advised to run Cloudlink blocks in clones, or in more than one sprite at a time.** This *can* work, but *it can lead to unpredictable behavior.* Cloudlink is thread-safe, however, in the event that you need to update future code, having all of your Cloudlink code separate from your project will make your life a lot easier in the long-term. :::: ::::info A single Cloudlink server can support over 1000 clients, however, you should keep your usercount under 500 to keep CPU and RAM usage low. This also helps with latency. Making connections to the server will slow down significantly when you have an absurd amount of clients connected, but in most instances the server will handle it. There have been many modifications to Cloudlink to optimize this, Server >=0.1.9.x officially supports over 1000 clients at a time! If you have further suggestions on how to improve Cloudlink, [**share your feedback here!**](https://github.com/MikeDev101/cloudlink/issues) :::: ::::info It is recommended that your Scratch mod have the search feature, or the middle-click "add blocks" menu. Search feature: ![search prompt](https://i.imgur.com/03ZqQdt.png) Middle-Click menu: ![middleclick menu](https://i.imgur.com/DwMgpEM.png) :::: ## Script examples You are free to use any of these examples to make your own Cloudlink project! This is only a small collection of example scripts. If you want to learn what each individual block does, see [All blocks.](#All-blocks) ### Managing connections To make a connection: ![example-connections](https://i.imgur.com/ltTUyBl.png) To handle disconnects: ![example-disconnects](https://i.imgur.com/oWCSRTE.png) ### Setting a name ![example-setname](https://i.imgur.com/e4jRw5R.png) ### Linking to rooms ![example-links](https://i.imgur.com/R7fjyLk.png) ## All blocks TODO # Python Client / Server CloudLink 4.0's Python client / server implementation share the same fundamental code structure, parameters, datatypes, and methods. However, note the following between the two: * Server only supports asyncio. * Client supports asyncio or non-asycio modes. * Clients and servers support events, however events types are different between the two. * Unless stated otherwise, methods and argument formatting are shared between the Server and Client. ## Making a client ### Setup ```python! from cloudlink import cloudlink # Somewhere before you start the client, you define custom methods / events if __name__ == "__main__": cl = cloudlink() # Instanciate the parent cloudlink object # Initialize a new client object, assumes async mode by default. client = cl.client(async_client=True, logs=True) # Runs the client. Assumes the server is on localhost:3000 by default. client.run("ws://127.0.0.1:3000/") ``` ## Making many clients ### Setup ```python! from cloudlink import cloudlink # Somewhere before you start the client, you define custom methods / events if __name__ == "__main__": cl = cloudlink() # Instanciate the parent cloudlink object # Initialize a new multi client object, assumes async mode by default. multi_client = cl.client(async_client=True, logs=True) ... # Creates a new client. # Will return a client object that behaves exactly like a single client. # Add spawn() to a for loop to spawn x many clients. client = multi_client.spawn("Name", "ws://127.0.0.1:3000/") ... # Runs all clients. # run() is NOT blocking, you will need to manually keep clients alive. multi_client.run() # Shuts down all clients. multi_client.stop() ``` ## Making a server ### Basic setup The most basic server setup is outlined below. ```python! from cloudlink import cloudlink # Somewhere before you start the server, you define custom methods / events if __name__ == "__main__": cl = cloudlink() # Instanciate the parent cloudlink object server = cl.server(logs=True) # Initialize a new server object # Do configuration here server.run( ip = "localhost", # Defaults to 127.0.0.1, use 0.0.0.0 to host on all interfaces port = 3000 # Defaults to port 3000 ) # server.run() is blocking, # no further code will execute unless a CTRL+C, SIGTERM, or SIGKILL is detected. ``` ### Custom methods / callbacks / events Example custom method for server ````python! class myExampleMethods: def __init__(self, parent): self.parent = parent async def example_command(self, client, message, listener): """ * self - Required for classes to work. * client - Recipient client object. Use to read/write attributes and state. * message - Dictionary. Contains the method's request contents. Conforms to UPLv2.1 and CLPv4. * listener - String. Defaults to None if no listener is detected. """ pass ... # Instanciate your custom methods and load methods into Cloudlink example = myExampleMethods(server) # Load a single type of custom commands server.load_custom_methods(example) # Load many types of custom commands server.load_custom_methods([ foo, bar, ... ]) # Start the server server.run() ```` Example custom callback method for client/server ````python! async def example_callback(client, message, listener): # Same structure and arguments as a custom method. pass ... # Bind a event to that custom method client.bind_callback_method(client.cl_methods.gmsg, example_callback) # Same example, but for a server server.bind_callback(server.cl_methods.gmsg, example_callback) ```` Example events for client/server ````python! async def example_event(client): pass ... # Bind a method to a event client.bind_event(client.events.on_connect, example_event) # Same example, but for a server server.bind_event(server.events.on_connect, example_event) ```` ## Base public API #### `cloudlink()` Parent method that initializes the library. Returns with `cloudlink` object that is used to initialize servers or clients. ### Attributes * `cloudlink.version` - Version string. * `cloudlink.supporter` - Supporter module. See [Supporter public API](#Supporter-public-API) for details. ### Methods #### `cloudlink.server()` Initializes a server object. Returns with `cloudlink.server` object. Reference the [Server public API](#Server-public-API) for usage. #### `cloudlink.client()` Initializes a singlular client object. Returns with `cloudlink.client` object. Reference the [Client public API](#Client-public-API) for usage. #### `cloudlink.multi_client()` Initializes a multi client object. Returns with `cloudlink.multi_client` object. Reference the [Multi Client public API](#Multi-Client-public-API) for usage. ## Supporter public API ### Attributes #### Protocol types - Internal use only * `cloudlink.supporter.proto_unset` - Default protocol when a client has not performed a handshake. * `cloudlink.supporter.proto_cloudlink`- Protocol type used when a client performs a Cloudlink-formatted handshake or method. * `cloudlink.supporter.proto_scratch_cloud` - Protocol type used when a client performs a Scratch protocol request. #### Message quirks - Server and client ##### `cloudlink.supporter.quirk_embed_val` Default message quirk. Messages sent using this quirk will simply place the message payload in the val argument of the Cloudlink protocol. Example: sending `{"test": True}` will create message: ```json! {"cmd": (command), "val": {"test": True}} ``` ##### `cloudlink.supporter.quirk_update_msg` Special message quirk. Messages sent using this argument will perform a dictionary update() onto the message payload. Example: sending `{"test": True}` will create message: ```json! {"cmd": (command), "test": True} ``` #### Case codes * `cloudlink.supporter.valid` - Processing a request was successful. * `cloudlink.supporter.invalid` - Request was invalid. * `cloudlink.supporter.missing_key` - Validation check failed: Missing dictionary key(s). * `cloudlink.supporter.not_a_dict` - Validation check failed: Input was not a dictionary. * `cloudlink.supporter.unknown_method` - Method was unknown or invalid. * `cloudlink.supporter.unknown_protocol` - Server could not interpret the protocol being used by the client. * `cloudlink.supporter.username_set` - Client's username is currently set. * `cloudlink.supporter.username_not_set` - Client's username is currently not set. * `cloudlink.supporter.disabled_method` - Method was disabled. * `cloudlink.supporter.too_large` - Request payload was too large. #### Scratch error codes * `cloudlink.supporter.connection_error` - Generic error used by the Scratch protocol. * `cloudlink.supporter.username_error` - Username was blocked or a username conflict has occurred with the Scratch protocol. * `cloudlink.supporter.overloaded` - Currently a placeholder, but would be returned if the server was overloaded using the Scratch protocol. * `cloudlink.supporter.unavailable` - Currently a placeholder, but would be returned if the server was unavailable using the Scratch protocol. * `cloudlink.supporter.refused_security` - Error used by the Scratch protocol if a scratch client accidentally sends a Scratcher's login credentials to the server. #### `cloudlink.supporter.codes` Implementation of Info/Error codes as outlined in the CLPv4 protocol. This attribute is a dictionary storing Error codes with tuples as values. #### `cloudlink.supporter.keydefaults` This is a dictionary storing argument names and supported datatypes. Each value is a list containing valid datatypes. ### Methods #### `cloudlink.supporter.validate(keys, payload, optional, sizes)` An all-in-one solution for input size and datatype validation. It's recommended to pair this method with a case switch. Return values: * `cloudlink.supporter.valid` - Data validation passed. * `cloudlink.supporter.invalid` - Data validation failed. * `cloudlink.supporter.missing_key` - Payload was missing an argument. * `cloudlink.supporter.too_large` - Payload argument exceeded limits specified in sizes argument. * `cloudlinks.supporter.not_a_dict` - Key and/or payload argument(s) were not a dictionary. Argument details: * keys - Required - Dictionary. Use a key value from `cloudlink.supporter.keydefaults` or implementa custom key to check for payload validity. * payload - Required - Dictionary. This is the data to validate. * optional - Not required - List/Set. Any key passed in optional will not throw `cloudlink.supporter.missing_key` if said key is not present. If the key is present, data validation will continue. * sizes - Not required - Dictionary. Keys with interger values define the maximum permitted size of a specific payload argument. Example usage: ```python! # Example of a valid payload cloudlink.supporter.validate( keys={"cmd": str, "val": [str, dict, int, bool, float], "listener": [str, int]}, payload={"cmd": "gmsg", "val": "test"}, optional=["listener"], sizes={} ) # -> cloudlink.supporter.valid # Example of an invalid payload - cmd is not a string, and val is unsupported cloudlink.supporter.validate( keys={"cmd": str, "val": [str, dict, int, bool, float], "listener": [str, int]}, payload={"cmd": 3282, "val": (5, 3, 1)}, optional=["listener"], sizes={"cmd": 10} ) # -> cloudlink.supporter.invalid # cmd is valid, but val is not - The alphabet is larger than 10 characters cloudlink.supporter.validate( keys={"cmd": str, "val": [str, dict, int, bool, float], "listener": [str, int]}, payload={"cmd": "gmsg", "val": "abcdefghijklmnopqrstuvwxyz"}, optional=["listener"], sizes={"val": 10} ) # -> cloudlink.supporter.too_large ``` #### `cloudlink.supporter.full_stack()` Used when exceptions occur. Prints out the full traceback of the exception. #### `cloudlink.supporter.is_json(json_str)` Checks if a string is valid JSON. Returns a boolean. #### `cloudlink.supporter.get_client_ip(client)` Resolves the client object's IP address. Returns a string. Assumes the client object is valid. #### `cloudlink.supporter.generate_statuscode(code)` Using a dictionary key from `cloudlink.supporter.codes` as code will return a string, generating a human-readable CLPv4 error code. #### `cloudlink.supporter.detect_listener(message)` Checks if the `listener` key is present within the message. Automatically validates input. Returns the value of `listener` if present, returns `None` otherwise. #### `cloudlink.supporter.disable_methods(functions)` Disables methods. Functions is a list/set with string values. #### `cloudlink.supporter.load_custom_methods(_class)` Passing a instanciated class into this function will load it's methods into Cloudlink Client/Server. Automatically ignores private methods. If your class contains the attribute `importer_ignore_functions`, a list containing string values, each string value must have the same label as a method in your class. Each specified method in this list will be ignored for importing. #### `cloudlink.supporter.log(event, force)` Displays a message in the console. Event is a string, force is boolean. If logs are enabled, messages will display in the console. If disabled, the console will be silent, unless if force is set to true. #### `cloudlink.supporter.timestamp()` Returns a timestamp using month/day/year hour:minute.second formatting. ## Server public API ### Attributes #### `cloudlink.server.enable_logs` Boolean. Enables/disables console logs. #### `cloudlink.server.id_counter` Interger. Should not be modified by other code. #### `cloudlink.server.ip_blocklist` List. Add/remove string entries storing IP addresses to block access to the server. Requires having `cloudlink.server.check_ip_addresses` enabled to work. #### `cloudlink.server.reject_clients` Boolean. Enables/disables reject mode. If true, the server will refuse future client connections. #### `cloudlink.server.enable_scratch_support` Boolean. Defaults to true. Enables/disables support for the Scratch cloud variable protocol. #### `cloudlink.server.enable_scratch_support` Boolean. Defaults to true. Enables/disables support for the Scratch cloud variable protocol. #### `cloudlink.server.check_ip_addresses` Boolean. Defaults to false. If enabled, client IP addresses will be resolved and registered. Enables support for IP blocking. #### `cloudlink.server.enable_motd` Boolean. Defaults to false. If enabled, the server will report a Message-Of-The-Day using whatever value stored in `cloudlink.server.motd_message`. #### `cloudlink.server.motd_message` String. Message to report to clients upon successful handshake. Requires having `cloudlink.server.enable_motd` set to true. #### Aliases from `cloudlink.supporter`: * `cloudlink.server.log`: Alias of `cloudlink.supporter.log` * `cloudlink.server.validate`: Alias of `cloudlink.supporter.validate` * `cloudlink.server.load_custom_methods`: Alias of `cloudlink.supporter.load_custom_methods` * `cloudlink.server.disable_methods`: Alias of `cloudlink.supporter.disable_methods` * `cloudlink.server.is_json`: Alias of `cloudlink.supporter.is_json` * `cloudlink.server.get_client_ip`: Alias of `cloudlink.supporter.get_client_ip` * `cloudlink.server.detect_listener`: Alias of `cloudlink.supporter.detect_listener` * `cloudlink.server.generate_statuscode`: Alias of `cloudlink.supporter.generate_statuscode` ### Methods Sample text ## Multi Client public API ### Attributes Sample text ### Methods Sample text ## Client public API ### Attributes Sample text ### Methods Sample text # Cloudlink Protocol v4 (CLPv4) ::::success **Stable** 2.5 (Stable/Subject to Change) :::: This section outlines information to Cloudlink's protocol, formatting, commands, status codes, and more. ## Notes ### Enhanced/removed/obsolete functionality in CL4 ::::success **Stable** 3 (Stable) :::: #### Enhanced * *(Python server exclusive)* **Callback system** - Most known implementations of Cloudlink's Python server use the callback system. In combination with the new custom command loader, the callback system has been extended to support callbacks for all loaded commands, and supports firing more than one method per callback. #### Removed * **Client-reported IP addresses** - This functionality is known to be spoofed and has been removed in favor of system or HTTP-header provided IP addresses. #### Obsolete * **UPLv1 *(Uniform Packet Layout version 1)*** - Sending custom commands through the Direct command has been deprecated. Custom commands will now follow the same format as standard CLP (Cloudlink Protocol) commands. * **String-only usernames** - This is reimplemented as as backwards compatibility feature. CloudLink 4 will now use JSON objects to reference clients. If there are more than one client(s) with the same username, CloudLink 4 will ask that your username selection to be more specific - Use a JSON object or a client ID instead! ### UPL *(Uniform Packet Layout)* v2.1 ::::success **Stable** 3 (Stable) :::: UPL is a format standard introduced in Cloudlink 3.0, which defined how Cloudlink packets should be formatted and handled in custom reimplemenations. **This format standard is specified in Message format below.** UPLv2.1 retains full compatibility with UPLv2, as it is an extension. UPLv2 retains some compatibility with UPL1. Server version >= 0.1.9.x use UPLv2.1, versions <=0.1.8.x use UPLv2. All server versions >=0.1.8.x support UPLv1, however any server version older than 0.1.7.x only support UPLv1. In extremely ancient cases, historical server versions do not use UPL at all. ::::warning Messages using UPLv2.1/v2 will **NOT** process correctly on a sever using UPLv1. **It is advised to not connect/use a server running Cloudlink older than 0.1.8.x.** :::: ### Difference between UPLv2.1 and UPLv2 / UPLv1 UPLv2.1 extends the functionality of UPLv2 by implemeting the `code_id` key and modifying the functionality of the `ulist` method. #### Adding the `code_id` key Servers <= 0.1.8.x returns the `val` key when returning a response. ```json { "cmd": "statuscode" "val": (str: Status code) } ``` Servers <0.1.9.x and >0.1.8.x returns only the `code` key when returning a response. Some implementations also use the `val` key to return extra payload information. ```json { "cmd": "statuscode" "code": (str: Status code), "val": (str, int, bool, object, float: Payload) } ``` Servers >=0.1.9.x return both `code` and `code_id` keys. UPLv2.1 also makes the `val` key standard, if a response is required to return a payload. ```json { "cmd": "statuscode" "code": (str: Status code), "code_id": (int: Status code), "val": (str, int, bool, object, float: Payload) } ``` #### Modifying the functionality of `ulist` Servers <=0.1.8.x or UPLv2/v1 returns only a JSON array for `ulist` responses. ```json { "cmd": "ulist" "val": ["a", "b", "c"] } ``` UPLv2.1 changes the functionality of `ulist` to use a JSON object, storing a `method` key storing a string value, and a `value` key storing a JSON user object. ```json { "cmd": "ulist" "val": { "method": (str: Userlist method), "val": (array or object: User(s)) } } ``` ## Message format All Cloudlink requests/responses are JSON-encoded text frames. Each request requires a `cmd` key to specify methods. Most commands require a `val` key containing a payload, but some will function without it. ::::info To make a listener, simply specify the `listener` key with a string or a number. Responses will return with the same `listener` key and value. :::: ::::info When specifying specific rooms to broadcast, specify the `rooms` key and set it's value to a string, number, or an array of strings/numbers. Rooms that you have linked to will be selected. **Rooms that have not been joined will be ignored.** :::: The `code` and `code_id` keys are attached to server responses. `code` returns a string with a human-readable error/info message, and `code_id` returns an interger with a machine-readable error/info code. ```json { "cmd": (string: Packet command), "name": (string or number: Variable name), "val": (str, int, bool, json object, float: Payload), "id": (str, int, json object, or array: Recipient(s)), "rooms": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID), "code": (string: Status code), "code_id": (int: Status code) } ``` ## Standard CLPv4 methods ::::success **Stable** 3 (Stable) :::: These methods specify a standard implementation of Cloudlink 4 / UPLv2.1. ::::danger If you are porting Cloudlink to a different programming language, **properly implemementing these methods is a requirement** *(With the exception of `handshake`, unless if you want to port [Scratch's cloud variable protocol...](https://github.com/TurboWarp/cloud-server/blob/master/doc/protocol.md)* :::: ### Handshake (`handshake`) This method is used in servers version >=0.1.9.x to identify the Cloudlink protocol. This method requires only the `cmd` key. The `listener` key is optional. ::::info Servers <0.1.9.x do not handle this request, but will automatically send it's responses upon connection (excluding a statuscode). :::: Cloudlink's Scratch and Python Clients will automatically process this method. Upon successful handling, the server will return various messages as well as a `OK` statuscode response. If a `listener` key is specified, the server will return the `OK` statuscode response with the `listener` key. **Request** ```json { "cmd": "handshake", "listener": (str, int, float: Packet's listener ID) } ``` **Responses** 1. Server version ```json { "cmd": "server_version", "val": (str: Server's version string) } ``` 2. Detected IP address of client ```json { "cmd": "client_ip", "val": (str: Client's detected IP address) } ``` 3. Message-Of-The-Day (MOTD) *(Must be enabled in the server)* ```json { "cmd": "motd", "val": (str: Friendly MOTD) } ``` 4. Current global message ```json { "cmd": "gmsg", "val": (str, int, bool, json object, float: Payload), "rooms": "default" } ``` 5. The current userlist in the `default` room ```json { "cmd": "ulist" "val": { "method": "set", "val": (array or object: User(s)) }, "rooms": "default" } ``` ::::info After you connect to the server and use `handshake`, future ulist messages will use either `add`, `set`, or `delete` methods. :::: 5. Status code `OK` ```json { "cmd": "statuscode" "code": "I:100 | OK", "code_id": 100, "listener": (str, int, float: Packet's listener ID) } ``` ### Global messages (`gmsg`) Global messages broadcast a payload to all clients connected in a room / series of rooms. `cmd` and `val` keys are required, the `rooms` key and the `listener` key are optional. Upon successful handling, the server will return a message identical to the request. If a `listener` key is specified, the server will return a message containing the `listener` key only to the request's origin. All clients in the room(s) will receive an identical message excluding the `listener` key. **Request/response** ```json { "cmd": "gmsg" "val": (str, int, bool, json object, float: Payload) "rooms": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID) } ``` ### Private messages (`pmsg`) Private messages broadcast a payload to a specific client / series of specific clients connected in a room / series of rooms. ::::warning This method requires you to set your username first. Use`setid`. :::: `cmd`,`val`, and `id` keys are required, the `rooms` key and the `listener` key are optional. Upon successful handling, the server will return a message with the `statuscode` method as well as a response code. See [**Status Codes**](#Status-Codes) for possible response codes. If a `listener` key is specified, the server will return the statuscode with the `listener` key for the client to process. Recipients of the message will receive an identical message excluding the `id` and `listener` keys. **Request** ```json { "cmd": "pmsg", "val": (str, int, bool, json object, float: Payload), "id": (str, int, json object, or array: Recipient(s)), "rooms": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID) } ``` **Response** ```json { "cmd": "statuscode", "code": (str: Status code), "code_id": (int: Status code) } ``` ### Global variables (`gvar`) Global messages broadcast variables with a value to all clients connected in a room / series of rooms. ::::success In Cloudlink >=0.1.9.x, the built-in Scratch cloud variable protocol translator supports cross-platform communication. As long as a Cloudlink client and a Scratch client are connected and linked to the same room/Project ID, global variable / cloud variable values will be synchronized. :::: ::::warning The Scratch client must be listening to the exact same variable names as your Cloudlink client. Otherwise, you will see messages in the inspect console regarding "server set a cloud variable that does not exist". :::: `cmd`, `name`, and`val` keys are required, the `rooms` key and the `listener` key are optional. Upon successful handling, the server will return a message identical to the request. If a `listener` key is specified, the server will return a message containing the `listener` key only to the request's origin. Recipients of the message will receive an identical message excluding the `listener` key. **Request/response** ```json { "cmd": "gvar", "name": (string or number: Variable name), "val": (str, int, bool, json object, float: Payload), "rooms": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID) } ``` ### Private variables (`pvar`) Global messages broadcast variables with a value to a specific client / series of specific clients connected in a room / series of rooms. ::::warning This method requires you to set your username first. Use`setid`. :::: ::::warning In Cloudlink >=0.1.9.x, the built-in Scratch cloud variable protocol translator supports cross-platform communication, but this is only supported for global variables. Scratch does not support private cloud variables. :::: `cmd`,`name`, `val`, and `id` keys are required, the `rooms` key and the `listener` key are optional. Upon successful handling, the server will return a message with the `statuscode` method as well as a response code. See [**Status Codes**](#Status-Codes) for possible response codes. If a `listener` key is specified, the server will return the statuscode with the `listener` key for the client to process. Recipients of the message will receive an identical message excluding `id` and `listener` keys. **Request** ```json { "cmd": "pvar", "name": (string or number: Variable name), "val": (str, int, bool, json object, float: Payload), "id": (str, int, json object, or array: Recipient(s)), "rooms": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID) } ``` **Response** ```json { "cmd": "statuscode", "code": (str: Status code), "code_id": (int: Status code) } ``` ### Set username (`setid`) This method allows sending messages to other clients using `pmsg` or `pvar`, as well as enable linking/unlinking to/from rooms. `cmd` and`val` keys are required, the `listener` key is optional. Upon successful handling, the server will update the userlist, and return a message with the `statuscode` method as well as a response code. It will also return your user object. See [**Status Codes**](#Status-Codes) for possible response codes. If a `listener` key is specified, the server will return the statuscode with the `listener` key for the client to process. **Request** ```json { "cmd": "setid", "val": (string or number: Client's username), "listener": (str, int, float: Packet's listener ID) } ``` **Responses** 1. Updating the userlist ```json { "cmd": "ulist" "val": { "method": "set", "val": (array or object: User(s)) }, "rooms": "default" } ``` ::::info After you connect to the server, use `handshake` and `setid`, future ulist messages will use either `add` or `delete` methods. Linking/unlinking to rooms will use `set`. :::: 2. Status code ```json { "cmd": "statuscode", "val": (object: User object), "code": (str: Status code), "code_id": (int: Status code), "listener": (str, int, float: Packet's listener ID) } ``` ### Linking to rooms (`link`) This method allows connecting to separate rooms, as well as linking to Scratch project IDs. ::::success In Cloudlink >=0.1.9.x, the built-in Scratch cloud variable protocol translator supports cross-platform communication. To allow Scratch's cloud variables to interact with Cloudlink's global variables, you only need to link to a project ID the scratch project is listening to. See documentation for your Scratch mod for further details regarding Project IDs. :::: ::::warning This method requires you to set your username first. Use`setid`. :::: ::::info You can use this method multiple times to link to more rooms. :::: `cmd` and`val` keys are required, the `listener` key is optional. Upon successful handling, the server will update the userlist, and return a message with the `statuscode` method as well as a response code. See [**Status Codes**](#Status-Codes) for possible response codes. If a `listener` key is specified, the server will return the statuscode with the `listener` key for the client to process. **Request** ```json { "cmd": "link", "val": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID) } ``` **Responses** 1. Updating the userlist - This will be broadcasted for each room linked. ```json { "cmd": "ulist" "val": { "method": "set", "val": (array or object: User(s)) }, "rooms": (string: Room ID) } ``` ::::info After you connect to the server, use `handshake`,`setid`, and `link`, future ulist messages will use either `add` or `delete` methods. :::: 2. Status code ```json { "cmd": "statuscode", "code": (str: Status code), "code_id": (int: Status code), "listener": (str, int, float: Packet's listener ID) } ``` ### Unlinking rooms (`unlink`) This method allows disconnecting from rooms. If you specify `val`, you can select specific rooms to disconnect from, or you can disconnect from all rooms and reset to the `default` room by not specifying `val`. ::::warning This method requires you to set your username first. Use`setid`. :::: ::::warning This method requires you to be linked to a room / rooms. :::: The`cmd` key is required, the `val` and `listener` keys are optional. Upon successful handling, the server will update the userlist, and return a message with the `statuscode` method as well as a response code. See [**Status Codes**](#Status-Codes) for possible response codes. If a `listener` key is specified, the server will return the statuscode with the `listener` key for the client to process. **Request** ```json { "cmd": "link", "val": (str, int, float, or array: Room(s)), "listener": (str, int, float: Packet's listener ID) } ``` **Responses** 1. Removing you from the exising userlists - This is broadcasted to the rooms you have left. ```json { "cmd": "ulist" "val": { "method": "delete", "val": (object: User object) }, "rooms": (string: Room ID) } ``` 2. Updating the userlist - This will be broadcasted to all members in each room you have been disconencted from ```json { "cmd": "ulist" "val": { "method": "add", "val": (array or object: User(s)) }, "rooms": (string: Room ID) } ``` 3. Updating your userlist - This will be broadcasted only to you. ```json { "cmd": "ulist" "val": { "method": "set", "val": (array or object: User(s)) }, "rooms": (string: Room ID) } ``` 4. Status code ```json { "cmd": "statuscode", "code": (str: Status code), "code_id": (int: Status code), "listener": (str, int, float: Packet's listener ID) } ``` ## Direct commands (`direct`) This method is programmer-defined. A stock implementation of Cloudlink Server will route messages over direct to other clients, having very similar functionality to `pmsg`. Unlike `pmsg`, `direct` does not support multicasting, nor does it honor rooms. If you specify`val` in a `direct` request and the receipient's user object is valid, the server will route the message directly to the client. Because of this, the server will not return a statuscode. If the recipient's user object is invalid, the server will return a statuscode `IDNotFound`. If `val` is not specified in the request, one of the following will occur: 1. **Legacy command handling** - The server will detect if the request uses CLPv3 commands or UPLv2/v1 formatting. If it does, it will convert the request to CLPv4/UPLv2.1 and handle it accordingly. **Please note that responses will be in CLPv4 / UPLv2.1 format.** Most requests will remain functional, but some instances will require reprogramming. 2. **Custom functionality** - The server will fire a callback, which will perform programmer-defined functionality. By default, this does nothing. Depending upon your specific implementation, please visit that implementation's documentation pages for further info regarding usage of the `direct` command. ## Status Codes ::::success **Stable** 2.5 (Stable/Subject to Change) :::: `(Type):(Code) | (Description)` | Type | Code | Description | | -------- | -------- | -------- | | Character (`E` for Error, `I` for Info)| Integer (ex. `123`), describes the info/error in machine-readable format, included in the `code_id` key | String, describes the info/error in a human-readable format | | `Code` | `Type` | Description | | -------- | -------- | -------- | | `I:0` | `Test` | Generic status code for debugging. | | `I:100` | `OK` | Generic response for successful requests. | | `E:101` | `Syntax` | Returned when a JSON formatting error has occurred, or there are missing keys in the JSON body. | | `E:102` | `DataType` | Returned when a parameter is using an invalid data type. | | `E:103` | `IDNotFound` | Selection of usernames, Client IDs, or JSON Objects returned no results. Server couldn't send packet. | | `E:104` | `IDNotSpecific` | Selection of a string-based username returned more than one result. Specify a Client ID or a JSON Object instead. | | `E:105` | `InternalServerError` | The server suffered an internal error. | | `E:106` | `EmptyPacket` | Zero bytes were received. | | `E:107` | `IDSet` | Username was already set. | | `E:108` | `Refused` | The server refused to handle the packet. | | `E:109` | `Invalid` | Invalid command. | | `E:110` | `Disabled` | Command was disabled. | | `E:111` | `IDRequired` | This command requires setting a username with `setid` before it can be used. | | `E:112` | `IDConflict` | **Currently reserved.** |