Add addon for testing object manager conformance against viewer
Closes #18
This commit is contained in:
111
addon_examples/object_management_validator.py
Normal file
111
addon_examples/object_management_validator.py
Normal file
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
Check object manager state against region ViewerObject cache
|
||||
|
||||
Can't look at every object we've tracked and every object in VOCache
|
||||
and report mismatches due to weird VOCache cache eviction criteria and certain
|
||||
cacheable objects not being added to the VOCache.
|
||||
|
||||
Off the top of my head, animesh objects get explicit KillObjects at extreme
|
||||
view distances same as avatars, but will still be present in the cache even
|
||||
though they will not be in gObjectList.
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import *
|
||||
|
||||
from hippolyzer.lib.base.objects import normalize_object_update_compressed_data
|
||||
from hippolyzer.lib.base.templates import ObjectUpdateFlags, PCode
|
||||
from hippolyzer.lib.proxy.addon_utils import BaseAddon, GlobalProperty
|
||||
from hippolyzer.lib.base.message.message import Message
|
||||
from hippolyzer.lib.proxy.addons import AddonManager
|
||||
from hippolyzer.lib.proxy.region import ProxiedRegion
|
||||
from hippolyzer.lib.proxy.sessions import SessionManager, Session
|
||||
from hippolyzer.lib.proxy.vocache import is_valid_vocache_dir, RegionViewerObjectCacheChain
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ObjectManagementValidator(BaseAddon):
|
||||
base_cache_path: Optional[str] = GlobalProperty(None)
|
||||
orig_auto_request: Optional[bool] = GlobalProperty(None)
|
||||
|
||||
def handle_init(self, session_manager: SessionManager):
|
||||
if self.orig_auto_request is None:
|
||||
self.orig_auto_request = session_manager.settings.ALLOW_AUTO_REQUEST_OBJECTS
|
||||
session_manager.settings.ALLOW_AUTO_REQUEST_OBJECTS = False
|
||||
|
||||
async def _choose_cache_path():
|
||||
while not self.base_cache_path:
|
||||
cache_dir = await AddonManager.UI.open_dir("Choose the base cache directory")
|
||||
if not cache_dir:
|
||||
return
|
||||
if not is_valid_vocache_dir(cache_dir):
|
||||
continue
|
||||
self.base_cache_path = cache_dir
|
||||
|
||||
if not self.base_cache_path:
|
||||
self._schedule_task(_choose_cache_path(), session_scoped=False)
|
||||
|
||||
def handle_unload(self, session_manager: SessionManager):
|
||||
session_manager.settings.ALLOW_AUTO_REQUEST_OBJECTS = self.orig_auto_request
|
||||
|
||||
def handle_session_init(self, session: Session):
|
||||
# Use only the specified cache path for the vocache
|
||||
session.cache_dir = self.base_cache_path
|
||||
|
||||
def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message):
|
||||
if message.name != "DisableSimulator":
|
||||
return
|
||||
# Send it off to the client without handling it normally,
|
||||
# we need to defer region teardown in the proxy
|
||||
region.circuit.send(message)
|
||||
self._schedule_task(self._check_cache_before_region_teardown(region))
|
||||
return True
|
||||
|
||||
async def _check_cache_before_region_teardown(self, region: ProxiedRegion):
|
||||
await asyncio.sleep(0.5)
|
||||
print("Ok, checking cache differences")
|
||||
try:
|
||||
# Index will have been rewritten, so re-read it.
|
||||
region_cache_chain = RegionViewerObjectCacheChain.for_region(
|
||||
handle=region.handle,
|
||||
cache_id=region.cache_id,
|
||||
cache_dir=self.base_cache_path
|
||||
)
|
||||
if not region_cache_chain.region_caches:
|
||||
print(f"no caches for {region!r}?")
|
||||
return
|
||||
all_full_ids = set()
|
||||
for obj in region.objects.all_objects:
|
||||
cacheable = True
|
||||
orig_obj = obj
|
||||
# Walk along the ancestry checking for things that would make the tree non-cacheable
|
||||
while obj is not None:
|
||||
if obj.UpdateFlags & ObjectUpdateFlags.TEMPORARY_ON_REZ:
|
||||
cacheable = False
|
||||
if obj.PCode == PCode.AVATAR:
|
||||
cacheable = False
|
||||
obj = obj.Parent
|
||||
if cacheable:
|
||||
all_full_ids.add(orig_obj.FullID)
|
||||
|
||||
for key in all_full_ids:
|
||||
obj = region.objects.lookup_fullid(key)
|
||||
cached_data = region_cache_chain.lookup_object_data(obj.LocalID, obj.CRC)
|
||||
if not cached_data:
|
||||
continue
|
||||
orig_dict = obj.to_dict()
|
||||
parsed_data = normalize_object_update_compressed_data(cached_data)
|
||||
updated = obj.update_properties(parsed_data)
|
||||
# Can't compare this yet
|
||||
updated -= {"TextureEntry"}
|
||||
if updated:
|
||||
print(key)
|
||||
for attr in updated:
|
||||
print("\t", attr, orig_dict[attr], parsed_data[attr])
|
||||
finally:
|
||||
# Ok to teardown region in the proxy now
|
||||
region.mark_dead()
|
||||
|
||||
|
||||
addons = [ObjectManagementValidator()]
|
||||
Reference in New Issue
Block a user