Working on puzzles together

This is something that’s been mentioned before, but I think this would be a really useful feature, especially for helping new players. I’ve been thinking about how this could be implemented and have come up with some ideas. Before I dive into the technical stuff, I’d like to do a brief overview of what this would probably include and ask for ideas.

This would allow multiple players to work on the same puzzle. A user can create a link for the puzzle. When a user mutates a base, that change will be reflected on everyone else who is working on the puzzle’s screen. The users would share a common undo/redo stack (I would think), as well as the same marked bases. Presumably, there would be some sort of moderation (to prevent people from joining, messing up the puzzle and leaving), but there are limits to how sophisticated such a system can be. If anyone has ideas, please let me know.

Technical details Now for the technical details. Given that WebRTC isn't quite ready yet, I think it would be best just to use WebSockets. It would be necessary to have a server for this - this makes it much easier to have multiple people (i.e. more than 2) work on the puzzle and also makes it easier to save the puzzle while nobody's working on it.

I’ve set up a basic Node client and server over WebSockets and so far it’s gone relatively smoothly. I’m taking pretty heavy inspiration from IRC for this (given that I’ve spent quite a bit of time working on the chat). The basic structure I’ve used has each client connected to the server. When a user mutates a base or makes a change, the client will send information containing the new sequence/marks. The server then relays this information to the other clients and makes an API call to store the data.

There would need to be some work on the backend to accommodate this. I’m imagining 4 main hooks, plus a couple of others for the website.

Main

  • Create a link - would take a parameter for the puzzle id to create the shared puzzle from, the sequence to start it off with (probably optional), and whether it should be invite-only. Would return the generated puzzle id.
  • Remove a link - would take a parameter for the id and return whether it was successfully removed
  • Update data - would take a parameter for the type of data to update (e.g. sequence, marks) and return whether the operation was successful
  • Fetch data - would take a parameter for the id and return the puzzle id to load, the sequence, and the marks. If we wanted to, we could include a user id parameter and keep track of who has worked on puzzles.

Website

  • Fetch a list of public puzzles (not set to invite-only) - probably would function similarly to the existing call for all puzzles
  • If we kept track of which puzzles a user has worked on (or vice versa), which puzzles the current user has worked on and which non-invite-only puzzles any user has worked on.

It might not be a bad idea to automatically open a chat channel for each shared puzzle room for discussion about the puzzle - I can add a chat hook for this if we decide to do this. The benefits of this approach are it keeps track of online users (including whether they are away) and it keeps #help tidy.

This would be a really useful feature I’d like to see implemented. Hopefully, I can work on it once I find the time (after chat code review and reworking scripts). I’d love to hear some feedback on this idea. Thanks for reading!

Totally agree, this would be great. Below are some specific thoughts on what you’ve outlined

  • Why do you say that WebRTC isn’t ready? Browser support seems fine. AFAIK, it’s actively being used in real-time webapps. Peer to peer is really the most appropriate option for this case.
  • Honestly I wouldn’t suggest persisting stuff on the backend - this is ephemeral by nature, or at least only saved locally (as autosaves already are). Just keep it in memory, or maybe use Redis/Elasticache.
  • As far as invites, I think this could be handled well enough by the protocol itself. A new peer/client can send a request to join, and data wouldn’t start being sent to it until the host (or anyone given relevant privileges) approves. In the case of p2p, the hosts designated with permissions would inform the other peers when a new peer has been added and should be looped in on the updates. Of course this doesn’t prevent a rogue peer from simply forwarding the data itself, but this is more of an issue of trusting the user joining - similar issue to, say, trusting someone to not copy-paste a PM. And of course, the host would not accept messages specifying a new host to be added from anyone except those who it has agreed has permission (or been told about such peers by peers it has given permission to, and etc down the rabbit hole), and joining peers would similarly verify such requests against the internal permission list it maintains just like the host.
  • As far as the actual synchronization, it’s probably a good idea to look into CRDTs:
  • If you do go down the chat room approach, probably a good idea to have an autogenerated channel name (ie, eternasync-<guid> or something), and then set it to invite only