112 lines
4.7 KiB
Python
112 lines
4.7 KiB
Python
"""
|
|
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()]
|