Skip to content

Instantly share code, notes, and snippets.

@johan-boule
Last active January 21, 2026 00:30
Show Gist options
  • Select an option

  • Save johan-boule/33016ff71a6aa1b34395b97fbbcebf38 to your computer and use it in GitHub Desktop.

Select an option

Save johan-boule/33016ff71a6aa1b34395b97fbbcebf38 to your computer and use it in GitHub Desktop.
Warzone 2100 Lobby server
#! /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()
@johan-boule
Copy link
Author

To use it, one has to edit his local WZ config file, and change the entry masterserver_name=
e.g. masterserver_name=lobby.warzone2100.retropaganda.info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment