LoveLogicAI commited on
Commit
151124f
·
verified ·
1 Parent(s): e6b25de
Files changed (1) hide show
  1. xros_server.py +104 -0
xros_server.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """xros_server.py — WebSocket server exposing the shared world-state API.
2
+
3
+ Runs a single WorldState and lets any number of clients read and mutate it
4
+ over a JSON-over-WebSocket protocol. This is the "world as a system service"
5
+ primitive from DiOS: apps consume one shared world model instead of each
6
+ reconstructing their own.
7
+
8
+ Protocol (JSON messages, one object per line):
9
+
10
+ client -> server:
11
+ {"op": "snapshot"} -> full current world
12
+ {"op": "spawn", "type": "...", "pose": {...}, "properties": {...}}
13
+ {"op": "move", "id": "...", "pose": {...}}
14
+ {"op": "remove", "id": "..."}
15
+
16
+ server -> client (after any mutation, to every connected client):
17
+ {"op": "spawn", "object": {...}}
18
+ {"op": "move", "object": {...}}
19
+ {"op": "remove", "id": "..."}
20
+
21
+ Run: python xros_server.py --host 127.0.0.1 --port 8765
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import asyncio
28
+ import json
29
+
30
+ import websockets
31
+
32
+ from xros_world import Pose, WorldState
33
+ from xros_shell import SpatialShell
34
+
35
+
36
+ async def handle(ws, world: WorldState, shell: SpatialShell) -> None:
37
+ """One connection = one app. Subscribe it to the event bus, then serve it."""
38
+ queue = world.subscribe()
39
+ # Forward world events to this client.
40
+ forward = asyncio.create_task(_forward(queue, ws))
41
+ try:
42
+ async for raw in ws:
43
+ try:
44
+ msg = json.loads(raw)
45
+ except json.JSONDecodeError:
46
+ await ws.send(json.dumps({"op": "error", "error": "bad json"}))
47
+ continue
48
+ await _dispatch(ws, world, shell, msg)
49
+ finally:
50
+ forward.cancel()
51
+ world.unsubscribe(queue)
52
+
53
+
54
+ async def _forward(queue: asyncio.Queue, ws) -> None:
55
+ while True:
56
+ event = await queue.get()
57
+ await ws.send(json.dumps(event))
58
+
59
+
60
+ async def _dispatch(ws, world: WorldState, shell: SpatialShell, msg: dict) -> None:
61
+ op = msg.get("op")
62
+ try:
63
+ if op == "command":
64
+ output = await shell.execute(msg.get("text", ""))
65
+ await ws.send(json.dumps({"op": "output", "text": output}))
66
+ elif op == "snapshot":
67
+ await ws.send(json.dumps({"op": "snapshot", "objects": await world.snapshot()}))
68
+ elif op == "spawn":
69
+ pose = Pose(**msg.get("pose", {})) if msg.get("pose") else Pose()
70
+ obj = await world.spawn(
71
+ type=msg.get("type", "entity"),
72
+ pose=pose,
73
+ properties=msg.get("properties"),
74
+ owner=msg.get("owner"),
75
+ )
76
+ await ws.send(json.dumps({"op": "ack", "id": obj.id}))
77
+ elif op == "move":
78
+ obj = await world.move(msg["id"], Pose(**msg["pose"]))
79
+ await ws.send(json.dumps({"op": "ack", "id": obj.id}))
80
+ elif op == "remove":
81
+ await world.remove(msg["id"])
82
+ await ws.send(json.dumps({"op": "ack", "id": msg["id"]}))
83
+ else:
84
+ await ws.send(json.dumps({"op": "error", "error": f"unknown op {op!r}"}))
85
+ except KeyError as e:
86
+ await ws.send(json.dumps({"op": "error", "error": f"unknown object {e}"}))
87
+ except (TypeError, ValueError) as e:
88
+ await ws.send(json.dumps({"op": "error", "error": str(e)}))
89
+
90
+
91
+ async def main(host: str, port: int) -> None:
92
+ world = WorldState()
93
+ shell = SpatialShell(world)
94
+ async with websockets.serve(lambda ws: handle(ws, world, shell), host, port):
95
+ print(f"XROS spatial shell listening on ws://{host}:{port}")
96
+ await asyncio.Future() # run forever
97
+
98
+
99
+ if __name__ == "__main__":
100
+ p = argparse.ArgumentParser()
101
+ p.add_argument("--host", default="127.0.0.1")
102
+ p.add_argument("--port", type=int, default=8765)
103
+ args = p.parse_args()
104
+ asyncio.run(main(args.host, args.port))