Marbles2 - 23-Nov 2020 [devlog/marbles2]
For the last few weeks I've had a mostly working version of the game logic for Marbles2 on the Spectrum Next. One of goals I wanted in place was to be able to share high scores and to get remote high scores from an online service.
There's a number problems presented with this. First of all, to share a high score requires some kind of validation. Since the game will be in NextBASIC this means there can't be some hidden encryption key that I can verify when it's been sent to me. So the next option is to send the sequence of game play and verify it on the server for the score.
This then means that high scores can be verified and attributed properly. However, the bigger problem is: internet (or even web) + spectrum = mixed bag.
To say that internet connectivity from the Spectrum is tricky is doing the headache an injustice. There's a lot of async communication between multiple modules before it gets from the client to the server and back. I'm being exposed to the entire stack that I normally don't think two moments about during web development.
Ideally I want the Spectrum to perform a POST request to my server. Most servers I can launch quickly are forced in to using TLS over HTTP - i.e. HTTPS - and the Spectrum's modules doesn't have anything written that (I know of that) can deal with encryption - let alone doing it from the NextBASIC end, not only will it be tremendously slow, but it'll likely hose the memory, let alone actually complete.
Then there's the POST over the web. I'd rather use the web because it's a common communication method, and one that I understand well, but there's an overhead in talking HTTP and doing proper POSTs. I need the Spectrum to do the bare minimum from NextBASIC partly for speed, but partly to reduce the potential for errors.
In the end, I decided to write a simple custom socket server that could receive comma separate values (16 bit values) and once it had an empty line, it would assume the message had ended and close out the connection (though it'll eventually send a yay/nay on whether the values were good).
After a few weeks of stolen hours here and there, I've finally got something that works … some of the time?!
The process is this:
- Clear communication channels
- Test if the Spectrum can talk to the driver that talks to UART
- If it can't, install the drivers
- Then attempt to initialise the drivers - all three of these steps can error but in general are fairly stable
- Attempt to connect via TCP to the server - this can fail - a lot, so I try this 3 times before bailing
- Then, if we have a connection, read a memory bank and send chunks of 63 values 16 bit values at a time (as strings) then finally close out the connection
At this stage I had a lot of problems that were all intermittent. It turned out (I think) that my local test environment was too quick - i.e. the server was responding and closing before the Spectrum had a chances to switch from writing the socket to reading - and it seemed like when it tried to read (the acknowledgement message) the connection had close already and the Spectrum would throw another error (or… something 🤷♀).
It was also mega important that the response from the server socket was formed "correctly" so that the Spectrum knew when the message ended. This isn't a great surprise but it definitely took me a while to get the full combination of things in the right place.
My server needed to close out using this:
socket.write("ACK\r\n", "utf-8", reportErrors)
Without the line feed things would go weirdly wrong.
So now I've got code that can, most of the time, send the contents of a memory bank to a remote server. The remote server will likely be running on AWS as I need to expose a custom port - though I'd probably just shunt that data across to a new service that I can run under something like Vercel or Netlify so I can marry it up to some kind of database and a web presence (especially since I've got the domain).
So next the game needs some UI elements. Not fun bits of code at all, but necessary.
Originally published on Remy Sharp's b:log