Closed kjvalencik closed 9 months ago
PoC running at http://tintin.valencik.com/
Added support for reading Cloudflare's CF-Connecting-IP
header. This makes it really easy to put TLS in front of it. Passwords are finally sent encrypted instead of plaintext! 😅
I've got this running on my local server will Cloudflare Tunnel.
Why
Currently, playing RotS requires a native app (e.g., telnet, JMC) and can't be played from a browser. This is because it expects raw TCP connections and browsers cannot make these connections.
Some players cannot install apps or may prefer to play from a browser.
What
If we naively put a WebSocket proxy in front of RotS, it would break multi-player connection because all source addresses would be the proxy. This PR includes proxying functionality that resolves this issue and is broken up into a few changes.
-p
flagI added a
-p
flag to the main rots binary. When this flag is set it expects to be proxied with a special protocol. In this protocol, the very first 4-bytes are the IPv4 address of the source IP. The RotS loop continues on as normal, using this as the source IP instead of getting it directly from the connection.For simplicity (avoiding a state machine), this was added to the synchronous part of the loop, but since the proxy will flush these bytes immediately, we can rely on this not causing lag.
Proxy
I added a game proxy written in Rust using the high performance tokio I/O framework. At it's simplest, it:
This allows TCP conditions to continue operating as they do today.
WebSocket
In the last commit, I extended the proxy to include a WebSocket listener. This listener will perform a WS handshake and will then start proxying bytes back and forth (just like the plain TCP proxy), but encapsulating in WS frames.
This provides two different ports that RotS can be connected:
Alternatives
Omit the IP header on the connection
This is nice because it doesn't require proxying telnet connections, but means we need a completely different way to handle multi-playing and other types of abuse. IMO, proxying lets us make changes more incrementally.
WebSockets directly in main process
Handling websockets in C is quite a lot of work. The most realistic way would be to create a shared library (probably written in Rust) that exposes a small, specially crafted C API. However, this would still require a refactor in RotS to better abstract network code.
This would be nice because we drop the proxy and the proxy header, but it adds risk and build complexity to get it running. I prefer the proxy approach because it minimizes the changes to the codebase if the WebSocket experiment proves to not be super useful.
Running
This requires having Rust installed.
Testing
I did some simple testing and I detected no lag and low resource consumption (thanks Rust!). I was able to connect both from tintin and a web browser with the correct IP address showing in the RotS logs.
You can perform a quick test by opening a blank browser tab and opening the console:
Follow-ups
There are a few web based mud clients, but they tend to be game specific. We could adapt one, but it would also be cool to have a RotS specific one where it understands the game output and can have built-in parsers for things like mapping and a stats window.
To prove out the concept, I wrote a very simple client that can be opened locally.