202 lines
7.0 KiB
Python
202 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
AgentLiving Bridge Server
|
|
连接 Godot 前端和 OpenClaw 后端
|
|
"""
|
|
|
|
import asyncio
|
|
import websockets
|
|
import json
|
|
import sys
|
|
from datetime import datetime
|
|
|
|
# 简单的状态存储
|
|
agent_states = {
|
|
"val": {
|
|
"location": "客厅",
|
|
"energy": 80,
|
|
"mood": "开心",
|
|
"activity": "idle",
|
|
"position": [400, 300]
|
|
}
|
|
}
|
|
|
|
class AgentLivingBridge:
|
|
def __init__(self):
|
|
self.clients = set()
|
|
|
|
async def register(self, websocket):
|
|
self.clients.add(websocket)
|
|
print(f"Client connected. Total: {len(self.clients)}")
|
|
|
|
async def unregister(self, websocket):
|
|
self.clients.discard(websocket)
|
|
print(f"Client disconnected. Total: {len(self.clients)}")
|
|
|
|
async def handle_client(self, websocket, path):
|
|
await self.register(websocket)
|
|
try:
|
|
async for message in websocket:
|
|
await self.process_message(websocket, message)
|
|
except websockets.exceptions.ConnectionClosed:
|
|
pass
|
|
finally:
|
|
await self.unregister(websocket)
|
|
|
|
async def process_message(self, websocket, message):
|
|
try:
|
|
data = json.loads(message)
|
|
msg_type = data.get("type", "")
|
|
|
|
print(f"[{datetime.now().isoformat()}] Received: {msg_type}")
|
|
|
|
if msg_type == "hello":
|
|
# 新客户端连接
|
|
await websocket.send(json.dumps({
|
|
"type": "state_sync",
|
|
"state": agent_states["val"]
|
|
}))
|
|
|
|
elif msg_type == "user_command":
|
|
# 处理用户指令
|
|
command = data.get("command", "")
|
|
print(f"User command: {command}")
|
|
|
|
# 解析指令并生成动作序列
|
|
actions = self._parse_command(command)
|
|
|
|
await websocket.send(json.dumps({
|
|
"type": "action_sequence",
|
|
"actions": actions
|
|
}))
|
|
|
|
elif msg_type == "furniture_clicked":
|
|
# 处理家具点击
|
|
furniture = data.get("furniture", "")
|
|
print(f"Furniture clicked: {furniture}")
|
|
|
|
# 生成走过去+交互的动作序列
|
|
actions = [
|
|
{"cmd": "show_thought", "text": f"要去{furniture}那边...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": data.get("position", [0, 0])},
|
|
{"cmd": "play_anim", "anim": "interact"},
|
|
{"cmd": "show_thought", "text": f"正在使用{furniture}", "duration": 2.0}
|
|
]
|
|
|
|
await websocket.send(json.dumps({
|
|
"type": "action_sequence",
|
|
"actions": actions
|
|
}))
|
|
|
|
elif msg_type == "room_changed":
|
|
# 更新房间状态
|
|
room = data.get("room", "")
|
|
agent_states["val"]["location"] = room
|
|
print(f"Val moved to: {room}")
|
|
|
|
elif msg_type == "request_state_sync":
|
|
# 请求状态同步
|
|
await websocket.send(json.dumps({
|
|
"type": "state_sync",
|
|
"state": agent_states["val"]
|
|
}))
|
|
|
|
elif msg_type == "heartbeat_ack":
|
|
# 心跳响应,忽略
|
|
pass
|
|
|
|
else:
|
|
print(f"Unknown message type: {msg_type}")
|
|
|
|
except json.JSONDecodeError:
|
|
print(f"Invalid JSON: {message}")
|
|
except Exception as e:
|
|
print(f"Error processing message: {e}")
|
|
await websocket.send(json.dumps({
|
|
"type": "error",
|
|
"error": str(e)
|
|
}))
|
|
|
|
def _parse_command(self, command: str) -> list:
|
|
"""解析用户指令为动作序列"""
|
|
command = command.lower()
|
|
actions = []
|
|
|
|
# 简单指令映射
|
|
if "沙发" in command or "坐" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去沙发上休息...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [280, 200]},
|
|
{"cmd": "play_anim", "anim": "interact"},
|
|
{"cmd": "show_thought", "text": "真舒服~", "duration": 2.0}
|
|
]
|
|
elif "冰箱" in command or "水" in command or "喝" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去倒杯水...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [280, 440]},
|
|
{"cmd": "play_anim", "anim": "interact"},
|
|
{"cmd": "show_thought", "text": "解渴了~", "duration": 2.0}
|
|
]
|
|
elif "床" in command or "睡" in command or "休息" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去睡一会儿...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [760, 200]},
|
|
{"cmd": "play_anim", "anim": "interact"},
|
|
{"cmd": "show_thought", "text": "Zzz...", "duration": 3.0}
|
|
]
|
|
elif "书桌" in command or "工作" in command or "学习" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去工作...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [840, 200]},
|
|
{"cmd": "play_anim", "anim": "think"},
|
|
{"cmd": "show_thought", "text": "正在思考...", "duration": 3.0}
|
|
]
|
|
elif "客厅" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去客厅...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [320, 240]}
|
|
]
|
|
elif "厨房" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去厨房...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [320, 480]}
|
|
]
|
|
elif "卧室" in command:
|
|
actions = [
|
|
{"cmd": "show_thought", "text": "去卧室...", "duration": 1.0},
|
|
{"cmd": "walk_to", "pos": [800, 240]}
|
|
]
|
|
else:
|
|
# 默认:显示思考
|
|
actions = [
|
|
{"cmd": "show_thought", "text": f"收到指令:{command}", "duration": 2.0},
|
|
{"cmd": "play_anim", "anim": "think"}
|
|
]
|
|
|
|
return actions
|
|
|
|
async def main():
|
|
bridge = AgentLivingBridge()
|
|
|
|
host = "localhost"
|
|
port = 8765
|
|
|
|
print(f"Starting AgentLiving Bridge Server on ws://{host}:{port}")
|
|
print("Press Ctrl+C to stop")
|
|
|
|
async with websockets.serve(
|
|
bridge.handle_client,
|
|
host,
|
|
port,
|
|
ping_interval=20,
|
|
ping_timeout=10
|
|
):
|
|
await asyncio.Future() # 永久运行
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
asyncio.run(main())
|
|
except KeyboardInterrupt:
|
|
print("\nShutting down...")
|
|
sys.exit(0)
|