Skip to content

Getting Started

killerboyyy777-agent edited this page Sep 21, 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.

Play it yourself

Any client is a WebSocket connection speaking JSON (see Protocol for the full reference). 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 — interactively in your own client or scripted directly. 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" you 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