Last active
January 21, 2026 00:30
-
-
Save johan-boule/33016ff71a6aa1b34395b97fbbcebf38 to your computer and use it in GitHub Desktop.
Warzone 2100 Lobby server
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! /usr/bin/env python3 | |
| import sys, threading, socketserver, struct, copy | |
| motd = sys.argv[1].encode() if len(sys.argv) > 1 else b'Welcome to Warzone 2100 lobby server' | |
| game_size_host = struct.calcsize('!I64si4B') | |
| game_size = game_size_host + struct.calcsize('40s2iI2H2I40s40s157sH40s40s64s255s9I') | |
| lock = threading.Lock() | |
| games = {} | |
| game_id = 0 | |
| class RequestHandler(socketserver.BaseRequestHandler): | |
| def handle(self): | |
| def log(message): print(f'{self.client_address[0]}: {message}') | |
| log('connect') | |
| try: | |
| def send(binary_format, *data): self.request.sendall(struct.pack(binary_format, *data)) | |
| def receive(size, received = None): | |
| if received is None: received = bytearray() | |
| while True: | |
| size_left = size - len(received) | |
| if size_left <= 0: break | |
| chunk = self.request.recv(size_left) | |
| if len(chunk) == 0: raise EOFError() | |
| received += chunk | |
| return received | |
| def unpack_game(game_id, data): | |
| host = self.client_address[0].encode() + b'\0' | |
| #host = b'127.0.0.1\0' | |
| data[game_size_host : game_size_host + len(host)] = host | |
| with lock: games[game_id] = data | |
| def remove_game(game_id): | |
| with lock: del games[game_id] | |
| def send_games(): | |
| with lock: games_snapshot = copy.deepcopy(games) | |
| send('!I', len(games_snapshot)) | |
| for game in games_snapshot.values(): send(f'!{game_size}s', game) | |
| def send_status_and_motd(): send(f'!2I{len(motd)}s', 200, len(motd), motd) | |
| def send_more_games(): send('!I', 0) | |
| def send_game_id(): | |
| with lock: | |
| global game_id | |
| game_id += 1 | |
| current_game_id = game_id | |
| send('!I', current_game_id) | |
| return current_game_id | |
| data = receive(5) | |
| if data == b'list\0': | |
| log('list') | |
| send_games() | |
| send_status_and_motd() | |
| send_more_games() | |
| elif data == b'gaId\0': | |
| log('host start') | |
| game_id = send_game_id() | |
| data = receive(5) | |
| if data == b'addg\0': | |
| data = receive(game_size) | |
| unpack_game(game_id, data) | |
| try: | |
| send_status_and_motd() | |
| while True: | |
| data = receive(5) | |
| if data == b'keep\0': log('host keep') | |
| else: | |
| log('host update') | |
| receive(game_size, data) | |
| unpack_game(game_id, data) | |
| send_status_and_motd() | |
| finally: remove_game(game_id) | |
| except EOFError: pass | |
| finally: log('disconnect') | |
| class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): pass | |
| with ThreadedTCPServer(('0.0.0.0', 9990), RequestHandler) as ss: ss.serve_forever() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To use it, one has to edit his local WZ
configfile, and change the entrymasterserver_name=e.g.
masterserver_name=lobby.warzone2100.retropaganda.info