""" Debugger for detecting when animations within an object get started or stopped Useful for tracking down animation sequence-related bugs within your LSL scripts, or debugging automatic animation stopping behavior in the viewer. If an animation unexpectedly stops and nobody requested it be stopped, it's a potential viewer bug (or priority issue). If an animation unexpectedly stops and the viewer requested it be stopped, it's also a potential viewer bug. If an animation unexpectedly stops and only the server requested it be stopped, it's a potential script / server bug. """ from typing import * from hippolyzer.lib.base.message.message import Message from hippolyzer.lib.base.network.transport import Direction from hippolyzer.lib.base.objects import Object from hippolyzer.lib.base.templates import AssetType from hippolyzer.lib.proxy.addon_utils import BaseAddon, SessionProperty from hippolyzer.lib.proxy.region import ProxiedRegion from hippolyzer.lib.proxy.sessions import Session from hippolyzer.lib.base.datatypes import UUID from hippolyzer.lib.proxy.commands import handle_command from hippolyzer.lib.proxy.addon_utils import show_message class AnimTrackerAddon(BaseAddon): should_track_anims: bool = SessionProperty(False) anims_lookup: Dict[UUID, str] = SessionProperty(dict) last_tracker_anims: Set[UUID] = SessionProperty(set) def _format_anim_diffs(self, started_anims: Set[UUID], stopped_anims: Set[UUID]): added_strs = [f"+{self.anims_lookup[x]!r}" for x in started_anims] removed_strs = [f"-{self.anims_lookup[x]!r}" for x in stopped_anims] return ", ".join(removed_strs + added_strs) @handle_command() async def track_anims(self, session: Session, region: ProxiedRegion): """Track when animations within this object get started or stopped""" if self.should_track_anims: self.last_tracker_anims.clear() self.anims_lookup.clear() selected = region.objects.lookup_localid(session.selected.object_local) if not selected: return self.should_track_anims = True object_items = await region.objects.request_object_inv(selected) anims: Dict[UUID, str] = {} for item in object_items: if item.type != AssetType.ANIMATION: continue anims[item.true_asset_id] = item.name self.anims_lookup = anims @handle_command() async def stop_tracking_anims(self, _session: Session, _region: ProxiedRegion): """Stop reporting differences""" if self.should_track_anims: self.should_track_anims = False self.last_tracker_anims.clear() self.anims_lookup.clear() def handle_lludp_message(self, session: Session, region: ProxiedRegion, message: Message): if not self.should_track_anims: return if message.name != "AgentAnimation" or message.direction != Direction.OUT: # AgentAnimation is the message the viewer uses to request manually starting or stopping animations. # We don't care about other messages, we're just interested in distinguishing cases where the viewer # specifically requested something vs something being done by the server on its own. return av = session.objects.lookup_avatar(session.agent_id) if not av or not av.Object: print("Somehow didn't know about our own av object?") return current_anims = set([x for x in av.Object.Animations if x in self.anims_lookup]) started_anims: Set[UUID] = set() stopped_anims: Set[UUID] = set() for block in message["AnimationList"]: anim_id = block["AnimID"] if anim_id not in self.anims_lookup: continue start_anim = block["StartAnim"] already_started = anim_id in current_anims if start_anim == already_started: # No change continue if start_anim: started_anims.add(anim_id) else: stopped_anims.add(anim_id) if started_anims or stopped_anims: show_message("Viewer Requested Anims: " + self._format_anim_diffs(started_anims, stopped_anims)) def handle_object_updated(self, session: Session, region: ProxiedRegion, obj: Object, updated_props: Set[str], msg: Optional[Message]): if not self.should_track_anims: return if obj.FullID != session.agent_id: return if "Animations" not in updated_props: return current_anims = set([x for x in obj.Animations if x in self.anims_lookup]) started_anims = current_anims - self.last_tracker_anims stopped_anims = self.last_tracker_anims - current_anims self.last_tracker_anims.clear() self.last_tracker_anims.update(current_anims) if started_anims or stopped_anims: show_message("Anim Diffs: " + self._format_anim_diffs(started_anims, stopped_anims)) addons = [AnimTrackerAddon()]