Add object inventory helpers to region object manager

This commit is contained in:
Salad Dais
2025-06-15 17:44:03 +00:00
parent d7092e7733
commit a652779cc5
4 changed files with 43 additions and 8 deletions

View File

@@ -226,7 +226,7 @@ class InventoryModel(InventoryBase):
return model
@classmethod
def from_llsd(cls, llsd_val: List[Dict], flavor: str = "legacy") -> InventoryModel:
def from_llsd(cls, llsd_val: List[Dict], flavor: str = "legacy") -> Self:
model = cls()
for obj_dict in llsd_val:
obj = None
@@ -565,7 +565,7 @@ class InventoryCategory(InventoryContainerBase):
)
@classmethod
def from_llsd(cls, inv_dict: Dict, flavor: str = "legacy"):
def from_llsd(cls, inv_dict: Dict, flavor: str = "legacy") -> Self:
if flavor == "ais" and "type" not in inv_dict:
inv_dict = inv_dict.copy()
inv_dict["type"] = AssetType.CATEGORY
@@ -691,7 +691,7 @@ class InventoryItem(InventoryNodeBase):
return val
@classmethod
def from_llsd(cls, inv_dict: Dict, flavor: str = "legacy"):
def from_llsd(cls, inv_dict: Dict, flavor: str = "legacy") -> Self:
if flavor == "ais" and "linked_id" in inv_dict:
# Links get represented differently than other items for whatever reason.
# This is incredibly annoying, under *NIX there's nothing really special about symlinks.

View File

@@ -174,7 +174,7 @@ class SchemaBase(abc.ABC):
return fields_dict
@classmethod
def from_str(cls, text: str):
def from_str(cls, text: str) -> Self:
return cls.from_reader(StringIO(text))
@classmethod
@@ -183,11 +183,11 @@ class SchemaBase(abc.ABC):
pass
@classmethod
def from_bytes(cls, data: bytes):
def from_bytes(cls, data: bytes) -> Self:
return cls.from_str(data.decode("utf8"))
@classmethod
def from_llsd(cls, inv_dict: Dict, flavor: str = "legacy"):
def from_llsd(cls, inv_dict: Dict, flavor: str = "legacy") -> Self:
fields = cls._get_fields_dict(llsd_flavor=flavor)
obj_dict = {}
try:
@@ -262,5 +262,5 @@ class SchemaBase(abc.ABC):
pass
@classmethod
def _obj_from_dict(cls, obj_dict: Dict):
def _obj_from_dict(cls, obj_dict: Dict) -> Self:
return cls(**obj_dict) # type: ignore

View File

@@ -15,6 +15,7 @@ from typing import *
from hippolyzer.lib.base.datatypes import UUID, Vector3
from hippolyzer.lib.base.helpers import proxify
from hippolyzer.lib.base.inventory import InventoryItem, InventoryModel
from hippolyzer.lib.base.message.message import Block, Message
from hippolyzer.lib.base.message.message_handler import MessageHandler
from hippolyzer.lib.base.message.msgtypes import PacketFlags
@@ -27,7 +28,7 @@ from hippolyzer.lib.base.objects import (
)
from hippolyzer.lib.base.settings import Settings
from hippolyzer.lib.client.namecache import NameCache, NameCacheEntry
from hippolyzer.lib.base.templates import PCode, ObjectStateSerializer
from hippolyzer.lib.base.templates import PCode, ObjectStateSerializer, XferFilePath
from hippolyzer.lib.base import llsd
if TYPE_CHECKING:
@@ -217,6 +218,38 @@ class ClientObjectManager:
for entry in entries:
self.state.materials[UUID(bytes=entry["ID"])] = entry["Material"]
async def request_object_inv(self, obj: Object) -> List[InventoryItem]:
if "RequestTaskInventory" in self._region.cap_urls:
return await self.request_object_inv_via_cap(obj)
else:
return await self.request_object_inv_via_xfer(obj)
async def request_object_inv_via_cap(self, obj: Object) -> List[InventoryItem]:
async with self._region.caps_client.get("RequestTaskInventory", params={"task_id": obj.FullID}) as resp:
resp.raise_for_status()
return [InventoryItem.from_llsd(x) for x in (await resp.read_llsd())["contents"]]
async def request_object_inv_via_xfer(self, obj: Object) -> List[InventoryItem]:
session = self._region.session()
with self._region.message_handler.subscribe_async(
('ReplyTaskInventory',), predicate=lambda x: x["InventoryData"]["TaskID"] == obj.FullID
) as get_msg:
await self._region.circuit.send_reliable(Message(
'RequestTaskInventory',
# If no session is passed in we'll use the active session when the coro was created
Block('AgentData', AgentID=session.agent_id, SessionID=session.id),
Block('InventoryData', LocalID=obj.LocalID),
))
inv_message = await asyncio.wait_for(get_msg(), timeout=5.0)
# Xfer doesn't need to be immediately awaited, multiple signals can be waited on.
xfer = await self._region.xfer_manager.request(
file_name=inv_message["InventoryData"]["Filename"], file_path=XferFilePath.CACHE)
inv_model = InventoryModel.from_bytes(xfer.reassemble_chunks())
return list(inv_model.all_items)
class ObjectEvent:
__slots__ = ("object", "updated", "update_type")

View File

@@ -17,6 +17,7 @@ from hippolyzer.lib.base.message.message_handler import MessageHandler
from hippolyzer.lib.base.network.caps_client import CapsClient
from hippolyzer.lib.base.network.transport import ADDR_TUPLE
from hippolyzer.lib.base.objects import handle_to_global_pos
from hippolyzer.lib.base.xfer_manager import XferManager
from hippolyzer.lib.client.object_manager import ClientObjectManager, ClientWorldObjectManager
@@ -27,6 +28,7 @@ class BaseClientRegion(ConnectionHolder, abc.ABC):
# Actually a weakref
session: Callable[[], BaseClientSession]
objects: ClientObjectManager
xfer_manager: XferManager
caps_client: CapsClient
cap_urls: multidict.MultiDict[str]
circuit_addr: ADDR_TUPLE