Skip to content

Instantly share code, notes, and snippets.

@EncodeTheCode
Created June 29, 2026 16:08
Show Gist options
  • Select an option

  • Save EncodeTheCode/fc261a1cc7e308b88688dcb9ecfd0993 to your computer and use it in GitHub Desktop.

Select an option

Save EncodeTheCode/fc261a1cc7e308b88688dcb9ecfd0993 to your computer and use it in GitHub Desktop.
import asyncio
import websockets
import json
import time
# =========================
# CONFIG
# =========================
PORT = 4445
TICK_RATE = 20
TIMEOUT = 5.0
# =========================
# PLAYER CLASS
# =========================
class Player:
def __init__(self, ws):
self.ws = ws
self.user_id = None
self.x = self.y = self.z = 0.0
self.px = self.py = self.pz = 0.0
self.health = 100
self.armor = 0
self.weapon_id = 0
self.primary_ammo = 30
self.secondary_ammo = 90
self.walking = False
self.running = False
self.ping = 0.0
self.last_seen = time.time()
self.authenticated = False
def serialize(self):
return {
"user_id": self.user_id,
"x": self.x,
"y": self.y,
"z": self.z,
"px": self.px,
"py": self.py,
"pz": self.pz,
"health": self.health,
"armor": self.armor,
"weapon_id": self.weapon_id,
"primary_ammo": self.primary_ammo,
"secondary_ammo": self.secondary_ammo,
"walking": self.walking,
"running": self.running,
"ping": round(self.ping, 3)
}
# =========================
# GAME SERVER
# =========================
class GameServer:
def __init__(self):
self.players = {} # websocket -> Player
# -------------------------
# AUTH
# -------------------------
async def handle_auth(self, player, msg):
player.user_id = msg.get("user_id")
token = msg.get("token")
if player.user_id and len(token) > 5:
player.authenticated = True
await player.ws.send(json.dumps({
"type": "auth_ok",
"user_id": player.user_id
}))
# -------------------------
# HANDLE MESSAGE
# -------------------------
async def handle_message(self, ws, msg):
player = self.players.get(ws)
if not player:
player = Player(ws)
self.players[ws] = player
player.last_seen = time.time()
mtype = msg.get("type")
# AUTH FIRST
if not player.authenticated:
if mtype == "auth":
await self.handle_auth(player, msg)
return
# ---------------------
# MOVEMENT
# ---------------------
if mtype == "update":
player.px, player.py, player.pz = player.x, player.y, player.z
player.x += float(msg.get("dx", 0))
player.y += float(msg.get("dy", 0))
player.z += float(msg.get("dz", 0))
player.walking = msg.get("walking", False)
player.running = msg.get("running", False)
# ---------------------
# COMBAT
# ---------------------
elif mtype == "combat":
player.weapon_id = msg.get("weapon_id", player.weapon_id)
player.primary_ammo = msg.get("primary_ammo", player.primary_ammo)
player.secondary_ammo = msg.get("secondary_ammo", player.secondary_ammo)
# ---------------------
# PING
# ---------------------
elif mtype == "ping":
client_time = msg.get("t", time.time())
player.ping = time.time() - client_time
# -------------------------
# WORLD STATE
# -------------------------
def world(self):
return {
"type": "world",
"players": {
id(p.ws): p.serialize()
for p in self.players.values()
}
}
# -------------------------
# CLEANUP
# -------------------------
def cleanup(self):
now = time.time()
dead = []
for ws, p in self.players.items():
if now - p.last_seen > TIMEOUT:
dead.append(ws)
for ws in dead:
del self.players[ws]
# -------------------------
# BROADCAST LOOP
# -------------------------
async def broadcast(self):
interval = 1 / TICK_RATE
while True:
self.cleanup()
packet = json.dumps(self.world())
dead = []
for ws, player in self.players.items():
try:
await ws.send(packet)
except:
dead.append(ws)
for ws in dead:
self.players.pop(ws, None)
await asyncio.sleep(interval)
# =========================
# SERVER ENTRY
# =========================
server = GameServer()
async def handler(ws):
try:
async for message in ws:
msg = json.loads(message)
await server.handle_message(ws, msg)
except:
pass
finally:
server.players.pop(ws, None)
async def main():
async with websockets.serve(handler, "0.0.0.0", PORT):
print(f"WebSocket server running on ws://0.0.0.0:{PORT}")
await server.broadcast()
asyncio.run(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment