Files
pymetaverse/metaverse/viewer/agent.py
2025-08-19 09:04:58 -04:00

184 lines
6.4 KiB
Python

import asyncio
import struct
from ..eventtarget import EventTarget
from .simulator import Simulator
from . import messages
import logging
logger = logging.getLogger(__name__)
sHandle = struct.Struct("<II")
sIP = struct.Struct("<BBBB")
class Agent(EventTarget):
def __init__(self):
super().__init__()
self.username = (None, None)
self.agentId = None
self.sessionId = None
self.secureSessionId = None
self.circuitCode = None
self.simulator = None
self.simulators = []
self.messageTemplate = messages.getDefaultTemplate()
async def addSimulator(self, handle, host, circuit, caps = None, parent = False):
logger.debug(f"Connecting to {host} with circuit {circuit}")
sim = Simulator(self)
await sim.connect(host, circuit)
self.simulators.append(sim)
if caps:
await sim.fetchCapabilities(caps)
if parent:
logger.debug(f"Setting parent simulator to {sim}")
self.simulator = sim
sim.on("message", self.handleMessage)
sim.on("event", self.handleEvent)
return sim
def removeSimulator(self, simulator):
logger.debug(f"Removing simulator {simulator.name} with address {simulator.host}")
if simulator == self.simulator:
logger.debug(f"Parent simulator {simulator} removed!")
self.simulator = None
if simulator in self.simulators:
self.simulators.remove(simulator)
simulator.close()
def send(self, msg, reliable):
if self.simulator:
self.simulator.send(msg, reliable)
async def handleMessage(self, sim, msg):
if msg.name == "DisableSimulator":
logger.debug(f"Disabling simulator {sim}")
self.removeSimulator(sim)
elif msg.name == "LogoutReply":
logger.debug(f"LogoutReply from {sim}")
self.removeSimulator(sim)
await self.fire("logout")
elif msg.name == "KickUser":
logger.debug(f"KickUser from {sim}")
self.removeSimulator(sim)
await self.fire("kicked")
await self.fire("logout")
await self.fire("message", sim, msg)
async def handleEvent(self, sim, name, body):
logger.debug(f"EventQueue \"{name}\" from {sim}")
if name == "EnableSimulator":
simulatorInfo = body["SimulatorInfo"][0]
handle = struct.unpack("<II", simulatorInfo["Handle"])
host = "{}.{}.{}.{}".format(*sIP.unpack(simulatorInfo["IP"]))
await self.addSimulator(
handle,
(host, simulatorInfo["Port"]),
self.circuitCode
)
elif name == "TeleportFinish":
info = body["Info"][0]
handle = struct.unpack("<II", info["RegionHandle"])
host = "{}.{}.{}.{}".format(*sIP.unpack(info["SimIP"]))
await self.addSimulator(
handle,
(host, info["SimPort"]),
self.circuitCode,
info["SeedCapability"],
True
)
msg = self.messageTemplate.getMessage("CompleteAgentMovement")
msg.AgentData.AgentID = self.agentId
msg.AgentData.SessionID = self.sessionId
msg.AgentData.CircuitCode = self.circuitCode
self.send(msg, True)
elif name == "CrossedRegion":
CrossedRegion = body["CrossedRegion"][0]
handle = struct.unpack("<II", CrossedRegion["RegionData"][0]["RegionHandle"])
host = "{}.{}.{}.{}".format(*sIP.unpack(CrossedRegion["RegionData"][0]["SimIP"]))
await self.addSimulator(
handle,
(host, CrossedRegion["RegionData"][0]["SimPort"]),
self.circuitCode,
CrossedRegion["RegionData"][0]["SeedCapability"],
True
)
msg = self.messageTemplate.getMessage("CompleteAgentMovement")
msg.AgentData.AgentID = self.agentId
msg.AgentData.SessionID = self.sessionId
msg.AgentData.CircuitCode = self.circuitCode
self.send(msg, True)
elif name == "EstablishAgentCommunication":
host = body["sim-ip-and-port"].split(":", 1)
host = (host[0], int(host[1]))
for simulator in self.simulators:
if simulator.host == host:
await simulator.fetchCapabilities(body["seed-capability"])
break
else:
logger.warning(f"Received EstablishAgentCommunication for unknown host {host}")
await self.fire("event", sim, name, body)
async def login(self, login):
if login["login"] == "false":
raise ValueError("Invalid login handle")
self.username = (login["first_name"], login["last_name"])
self.agentId = login["agent_id"]
self.sessionId = login["session_id"]
self.secureSessionId = login["secure_session_id"]
self.circuitCode = login["circuit_code"]
await self.addSimulator(
(login["region_x"], login["region_y"]),
(login["sim_ip"], login["sim_port"]),
self.circuitCode,
login["seed_capability"],
True
)
msg = self.messageTemplate.getMessage("CompleteAgentMovement")
msg.AgentData.AgentID = self.agentId
msg.AgentData.SessionID = self.sessionId
msg.AgentData.CircuitCode = self.circuitCode
self.send(msg, True)
def logout(self):
logger.info(f"Logging out")
msg = self.messageTemplate.getMessage("LogoutRequest")
msg.AgentData.AgentID = self.agentId
msg.AgentData.SessionID = self.sessionId
self.send(msg, True)
async def run(self):
while True:
try:
# Remove simulators if they fail ping check
for simulator in self.simulators:
if not await simulator.ping():
self.removeSimulator(simulator)
if not self.simulator:
break
await asyncio.sleep(0.1)
except asyncio.exceptions.CancelledError as e:
# Attempt to gracefully logout
self.logout()
raise e