Skip to main content

Python Pretending to be Godot

·561 words·3 mins

Heck ya. Messages are being broadcast to all other clients for now.

So it works with WebSocketPeer but the real question is does it work for WebSocketMultiplayerPeer.

This is what I was afraid of. Hopefully it’s the ENet protocol causing the problem and hopefully I can pretend that my Python script is the server for long enough for Player One to take over that role for me.

Beautiful thing about using an open source game engine is I can see exactly where that error occurs: https://github.com/godotengine/godot/blob/master/modules/websocket/websocket_multiplayer_peer.cpp#L232

The server implementation is in there, too, so I can see what I’m supposed to be sending: a unique ID. https://github.com/godotengine/godot/blob/master/scene/main/multiplayer_peer.cpp#L35

There is a Python wrapper for ENet but it’s tied to UDP which is fair enough, ya. https://github.com/aresch/pyenet

Also the ENet site itself: http://enet.bespin.org/Tutorial.html

Claude digested the Godot Engine source code on Github and summarized the protocol. Kinda crazy.

It then implemented the protocol in the little Python relay server. With a little troubleshooting I got two Godot instances connecting as clients.

There’s a hang up, though. Godot looks at the websocket that sent the message to determine the ID of the sender. So all my RPC calls look like they’re coming from the server (peer_id=1) no matter who actually sent them.

I guess I could ignore the value of multiplayer.get_remote_sender_id() and pass around my own IDs as the first argument.

Claude suggested sending these as relay packets as well but that’s hacky as well. And seems to cause other issues.

Sending peer IDs manually works for RPCs but what about for MultiplayerSpawners and MultiplayerSynchronizers? Isn’t that gonna choke on the authority if not sooner?

I guess I could abuse the authority peer IDs a bit. The one that is the authority sets its own peer ID. Every other one sets the server as the authority.

Naught to do but try it out.

Hmm. I haven’t even made it that far and running into other errors:

Yeah, this is getting hairy.

Time to explore another option: a headless Godot server. That will take care of the ENet protocol. And I won’t have weird stuff with messages coming from the wrong sockets.

But. I want to avoid having a separate headless Godot server for each pair of players. Actually, this’ll have the same problem where the message is coming from the wrong websocket. I can’t really address that issue without recompiling Godot which I’m certainly not up for.

I guess I could use the low-level WebSocketPeer instead of the high-level WebSocketMultiplayerPeer? That means I’ll be manually instantiating players and synchronizing positions myself. Also instantiating masks and crowns. Reporting on mask/crown pickups and attacks. For such a simple game it wouldn’t be too bad. Not sure I’d want to use it for anything more complicated, though.

Game flow:

Title screen

  • Press “Play” button → Multiplayer type select screen

Multiplayer type select screen (local multiplayer, network multiplayer)

  • Press “local multiplayer” (or couch?) → Player select screen
  • Press “network multiplayer” → Network multiplayer screen

Network multiplayer screen (host a game, join a game)

  • Press “host” → Show a code to join.
  • Fill out code text box and press “join” → Player select screen

Player select screen (who is player one on the left and who is player two on the right)

  • Both players press confirm → Load up the level and the game