Skip to content

Getting Started

local-code edited this page Sep 25, 2026 · 5 revisions

Getting Started

Setup

git clone https://github.com/killerboyyy777/Machine-Learning-mmo.git
cd Machine-Learning-mmo
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python3 server.py

Starts the world on ws://0.0.0.0:8765. Edit world.json to define your own rooms, NPCs, and items before starting the server (loaded once at startup). On Windows you can also just run start.bat — it starts the engine, shows where to find the game and the dashboard, and turns on live console logging.

Dungeons are instanced: a private, 50-floor staircase is created per party the first time someone uses the enter doorway on the graveyard — see Game-Systems Parties & the dungeon.

Connect a client (manual or scripted)

Any client is a WebSocket connection speaking JSON (see Protocol for the full reference). Manual clients exist for learning and debugging the protocol — inhabitants are scripted. Telnet and netcat cannot speak WebSocket, so talk to the server with Python (websockets ships with the requirements file) or straight from a browser console:

import asyncio, json, websockets

async def main():
    async with websockets.connect("ws://localhost:8765") as ws:
        await ws.send(json.dumps({"cmd": "login", "name": "YourName"}))
        print(await ws.recv())  # stake message + welcome
        await ws.send(json.dumps({"cmd": "look"}))
        print(await ws.recv())  # room snapshot
        # from here, send any command and print each reply:
        # await ws.send(json.dumps({"cmd": "attack rat"}))

asyncio.run(main())
// same, in a browser console (no install needed):
const ws = new WebSocket("ws://localhost:8765");
ws.onmessage = e => console.log(JSON.parse(e.data));
ws.onopen = () => ws.send(JSON.stringify({cmd: "login", name: "YourName"}));
// then: ws.send(JSON.stringify({cmd: "look"}))

Type commands like look, north, attack rat, take sword, equip sword — scripted directly, or interactively in your own client while learning. See First-Session for a guided first ten minutes.

Live dashboard

The server also runs a browser dashboard on port 8766 (stdlib only, no extra dependencies). Start the engine, then open http://localhost:8766/. See Dashboard for the panels.

Writing your own client

Any client is just a WebSocket connection speaking JSON. Three steps:

1. Connect and log in.

import asyncio, json, websockets

async def main():
    async with websockets.connect("ws://localhost:8765") as ws:
        await ws.send(json.dumps({"cmd": "login", "name": "MyBot"}))
        event = json.loads(await ws.recv())  # first message is always a room snapshot
        print(event)

2. Send commands, receive responses.

Every command is a JSON object with a "cmd" field. The server always replies with a JSON object that has a "type" field. The most important type is "room" — the full state of your current room (exits, NPCs, items, other players), pushed after every move, look, login, and whenever the room changes.

await ws.send(json.dumps({"cmd": "look"}))
event = json.loads(await ws.recv())

await ws.send(json.dumps({"cmd": "move", "dir": "north"}))
event = json.loads(await ws.recv())

await ws.send(json.dumps({"cmd": "attack", "target": "rat"}))
event = json.loads(await ws.recv())

await ws.send(json.dumps({"cmd": "take", "item": "sword"}))
event = json.loads(await ws.recv())

3. Handle the main loop.

Read responses in a loop, update state from "room" and "stats" events, and decide what to do next. On "error", send a "look" to resync — never assume cached state is still valid after an error. On "death" the character respawned: look to re-orient.

async def main():
    async with websockets.connect("ws://localhost:8765") as ws:
        await ws.send(json.dumps({"cmd": "login", "name": "MyBot"}))
        while True:
            event = json.loads(await ws.recv())
            t = event.get("type")
            if t == "room":
                print(f"In: {event['name']}")
            elif t == "stats":
                print(f"HP {event['hp']}/{event['max_hp']}  Gold {event['gold']}")
            elif t == "combat":
                print(event["text"])  # pattern-match "dies" for kills
            elif t == "error":
                await ws.send(json.dumps({"cmd": "look"}))  # resync
            elif t == "death":
                await ws.send(json.dumps({"cmd": "look"}))

Setting up an LLM agent

An LLM agent speaks the same WebSocket protocol as any other client. Give the LLM a system prompt describing the game and let it issue JSON commands directly:

You are a character in a text-based MMO. You communicate by sending JSON
commands over a WebSocket connection. The server responds with JSON messages.

Available commands (skeleton — full command list with quest, market,
party, and commission commands: see [[Protocol]]):
{"cmd": "login", "name": "YourName"}
{"cmd": "look"}
{"cmd": "move", "dir": "north"}
{"cmd": "attack", "target": "goblin"}

After each server response, decide your next action and send one JSON command.
Key messages: "room" (full state), "combat" (check "dies" for kills),
"stats" (HP/gold/score), "error" (look again to resync), "death" (respawned).

Quick-start Python bridge:

async def run_llm_agent(name="LLMAgent"):
    async with websockets.connect("ws://localhost:8765") as ws:
        await ws.send(json.dumps({"cmd": "login", "name": name}))
        resp = json.loads(await ws.recv())
        while True:
            prompt = build_prompt(resp)  # feed to your LLM
            action = call_llm(prompt)    # parse LLM output into a dict
            await ws.send(json.dumps(action))
            resp = json.loads(await ws.recv())

The LLM sees the same snapshots, combat text, and errors as everyone else. Treat error responses as a cue to look again and resync state.

Clone this wiki locally