Connecting to DCSS Webtiles
DCSS Webtiles uses websocket to communicate between the client and the DCSS server.
Connecting to DCSS webtiles through websocket
You can connect to the websocket of the DCSS webtiles using tungstenite for Rust or websockets for Python. The example below assumes a DCSS webtiles on ‘localhost:8080’. Running it will provide you some ZLIB compressed data.
For Rust:
// URL to DCSS Webtiles
let url = "ws://localhost:8080/socket";
// Parse with url crate
let parsed_url = url::Url::parse(url).unwrap();
// Connect with tungstenite crate
let (mut socket, _response) = tungstenite::connect(parsed_url).unwrap();
// Read the socket until something is received
let mut compressed_msg = socket.read().unwrap().into_data();
For Python:
import asyncio
import websockets
# URL to DCSS Webtiles
= "ws://localhost:8080/socket"
ws_link
async def connect():
# Connect with websockets
= await websockets.connect(ws_link)
connection
# Read the socket
= await connection.recv()
received
return received
= asyncio.run(connect()) compressed_msg
The compressed_msg
variable in the example above will likely contain [170, 86, 202, 45, 78, 47, 86, 178, 138, 174, 6, 49, 148, 172, 20, 148, 10, 50, 243, 210, 149, 106, 99, 107, 1, 0]
for Rust or b'\xaaV\xca-N/V\xb2\x8a\xae\x061\x94\xac\x14\x94\n2\xf3\xd2\x95jck\x01\x00'
for Python.
Decoding the received data
Now that data has been received, it can be decoded. Before decoding the data, the 4 bytes removed by DCSS (prior to sending to the client) must be re-added ([0u8, 0, 255, 255]
for Rust and b"\x00\x00\xFF\xFF"
for Python). Once added, the data can be decoded with flate2 for Rust and zlib for Python. The the data can be turned into a json object with serde_json for Rust and json for Python.
For Rust:
// Append removed bytes
.append(&mut vec![0u8, 0, 255, 255]);
compressed_msg
// Create decompressor (https://github.com/rust-lang/flate2-rs/issues/312)
let wbits = 15; // Windows bits fixed (goes to -15 in flate2 because of zlib_header = false)
let mut decompressor = flate2::Decompress::new_with_window_bits(false, wbits);
let bufsize = 1024 * 1024; // Needs a buffer size to work (1mb)
let mut decompressed_bytes = Vec::with_capacity(bufsize);
// Use the decompressor to decompress the data
decompressor.decompress_vec(
&compressed_msg[..],
&mut decompressed_bytes,
flate2::FlushDecompress::Sync,
).unwrap();
// Convert to str
let json_str = std::str::from_utf8(&decompressed_bytes).unwrap();
// Convert to json
let json_data: serde_json::Value = serde_json::from_str(json_str).unwrap();
For Python:
import zlib
import json
# Data from connection example
= compressed_msg + bytearray(b"\x00\x00\xFF\xFF")
full_compressed_data
# Decompress with zlib
= zlib.decompressobj(-zlib.MAX_WBITS)
_decompressobj = _decompressobj.decompress(full_compressed_data)
decompressed_data
# Jsonify the data
= json.loads(decompressed_data) json_data
The json_data
variable in the example above will likely contain {'msgs': [{'msg': 'ping'}]}
as a serde_json Value
object for Rust and as a json
object for Python.
Sending data to DCSS webtiles
Sending data to DCSS is simpler than receiving it, but the correct objects must be sent. The example below connects to the webtiles and sends a login command for a user and a play command for DCSS. This assumes that a user called Username
, that has a password equal to Password
, exists on localhost:8080
. It also assumes a game id of dcss-web-trunk
is available.
For Rust:
// URL to DCSS Webtiles
let url = "ws://localhost:8080/socket";
// Parse with url crate
let parsed_url = url::Url::parse(url).unwrap();
// Connect with tungstenite crate
let (mut socket, _response) = tungstenite::connect(parsed_url).unwrap();
// Data packets to be sent
let message1 = serde_json::json!({"msg": "login", "username": "Username", "password": "Password"});
let message2 = serde_json::json!({"msg": "play", "game_id": "dcss-web-trunk"});
// Send messages
.send(tungstenite::Message::Text(message1.to_string())).unwrap();
socket.send(tungstenite::Message::Text(message2.to_string())).unwrap();
socket
// Wait for 5 seconds
std::thread::sleep(std::time::Duration::from_millis(5000));
For Python:
import asyncio
import websockets
import json
import time
= "ws://localhost:8080/socket"
ws_link = {"msg": "login", "username": "Username", "password": "Password"}
message1 = {'msg': 'play', 'game_id': 'dcss-web-trunk'}
message2
async def connect_and_send():
# Connect
= await websockets.connect(ws_link)
connection
# Send both messages
await connection.send(json.dumps(message1))
await connection.send(json.dumps(message2))
5)
time.sleep(
asyncio.run(connect_and_send())
If you watch on http://localhost:8080/#lobby, you will see for 5 seconds the user named Username
playing a game of dcss-web-trunk
.
See what can be sent and what will be received on the next page.
Connecting to a public DCSS Webtiles server
It is possible to send and receive commands (e.g. play the game) at an incredibly fast pace, but full speed should be restricted to local games only (e.g. running DCSS Webtiles server on your local computer or a server you own). When connecting to a public server, limit your play speed to a maximum of one sent command per 100 milliseconds. Follow any other restrictions from the server (e.g. no bot chat interaction).
Websocket Secure (wss) connection
DCSS Webtiles can have secure connections enabled, especially public servers. You can tell if a link is secured if the websocket link starts with “wss” instead of “ws”. In Rust, no change in code is necessary, but the native-tls-vendored
feature flag must be enabled for tungstenite: tungstenite = { version = "0.21", features = ["native-tls-vendored"] }
. In python, the websockets.connect(ws_link)
code must become websockets.connect(ws_link, ssl = True)
.