Display templated EQ messages as templated messages
This makes them less annoying to read, and allows us to use subfield serializers to pretty-print their contents.
This commit is contained in:
@@ -29,7 +29,10 @@ from hippolyzer.lib.base.message.msgtypes import MsgType
|
||||
|
||||
PACKER = Callable[[Any], bytes]
|
||||
UNPACKER = Callable[[bytes], Any]
|
||||
LLSD_PACKER = Callable[[Any], Any]
|
||||
LLSD_UNPACKER = Callable[[Any], Any]
|
||||
SPEC = Tuple[UNPACKER, PACKER]
|
||||
LLSD_SPEC = Tuple[LLSD_UNPACKER, LLSD_PACKER]
|
||||
|
||||
|
||||
def _pack_string(pack_string):
|
||||
@@ -64,6 +67,21 @@ def _make_tuplecoord_spec(typ: Type[TupleCoord], struct_fmt: str,
|
||||
return lambda x: typ(*struct_obj.unpack(x)), _packer
|
||||
|
||||
|
||||
def _make_llsd_tuplecoord_spec(typ: Type[TupleCoord], needed_elems: Optional[int] = None):
|
||||
if needed_elems is None:
|
||||
# Number of elems needed matches the number in the coord type
|
||||
def _packer(x):
|
||||
return list(x)
|
||||
else:
|
||||
# Special case, we only want to pack some of the components.
|
||||
# Mostly for Quaternion since we don't actually need to send W.
|
||||
def _packer(x):
|
||||
if isinstance(x, TupleCoord):
|
||||
x = x.data()
|
||||
return list(x.data(needed_elems))
|
||||
return lambda x: typ(*x), _packer
|
||||
|
||||
|
||||
def _unpack_specs(cls):
|
||||
cls.UNPACKERS = {k: v[0] for (k, v) in cls.SPECS.items()}
|
||||
cls.PACKERS = {k: v[1] for (k, v) in cls.SPECS.items()}
|
||||
@@ -110,10 +128,15 @@ class TemplateDataPacker:
|
||||
class LLSDDataPacker(TemplateDataPacker):
|
||||
# Some template var types aren't directly representable in LLSD, so they
|
||||
# get encoded to binary fields.
|
||||
SPECS = {
|
||||
SPECS: Dict[MsgType, LLSD_SPEC] = {
|
||||
MsgType.MVT_IP_ADDR: (socket.inet_ntoa, socket.inet_aton),
|
||||
# LLSD ints are technically bound to S32 range.
|
||||
MsgType.MVT_U32: _make_struct_spec('!I'),
|
||||
MsgType.MVT_U64: _make_struct_spec('!Q'),
|
||||
MsgType.MVT_S64: _make_struct_spec('!q'),
|
||||
# These are arrays in LLSD, we need to turn them into coords.
|
||||
MsgType.MVT_LLVector3: _make_llsd_tuplecoord_spec(Vector3),
|
||||
MsgType.MVT_LLVector3d: _make_llsd_tuplecoord_spec(Vector3),
|
||||
MsgType.MVT_LLVector4: _make_llsd_tuplecoord_spec(Vector4),
|
||||
MsgType.MVT_LLQuaternion: _make_llsd_tuplecoord_spec(Quaternion, needed_elems=3)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ class PacketFlags(enum.IntFlag):
|
||||
RELIABLE = 0x40
|
||||
RESENT = 0x20
|
||||
ACK = 0x10
|
||||
# Not a real flag, just used for display.
|
||||
EQ = 1 << 10
|
||||
|
||||
|
||||
# frequency for messages
|
||||
|
||||
@@ -1952,7 +1952,7 @@ class AvatarPropertiesFlags(IntFlag):
|
||||
|
||||
|
||||
@se.flag_field_serializer("AvatarGroupsReply", "GroupData", "GroupPowers")
|
||||
@se.flag_field_serializer("AvatarGroupDataUpdate", "GroupData", "GroupPowers")
|
||||
@se.flag_field_serializer("AgentGroupDataUpdate", "GroupData", "GroupPowers")
|
||||
@se.flag_field_serializer("AgentDataUpdate", "AgentData", "GroupPowers")
|
||||
@se.flag_field_serializer("GroupProfileReply", "GroupData", "PowersMask")
|
||||
@se.flag_field_serializer("GroupRoleDataReply", "RoleData", "Powers")
|
||||
@@ -2134,6 +2134,43 @@ class ScriptPermissions(IntFlag):
|
||||
CHANGE_ENVIRONMENT = 1 << 18
|
||||
|
||||
|
||||
@se.flag_field_serializer("ParcelProperties", "ParcelData", "ParcelFlags")
|
||||
class ParcelFlags(IntFlag):
|
||||
ALLOW_FLY = 1 << 0 # Can start flying
|
||||
ALLOW_OTHER_SCRIPTS = 1 << 1 # Scripts by others can run.
|
||||
FOR_SALE = 1 << 2 # Can buy this land
|
||||
FOR_SALE_OBJECTS = 1 << 7 # Can buy all objects on this land
|
||||
ALLOW_LANDMARK = 1 << 3 # Always true/deprecated
|
||||
ALLOW_TERRAFORM = 1 << 4
|
||||
ALLOW_DAMAGE = 1 << 5
|
||||
CREATE_OBJECTS = 1 << 6
|
||||
# 7 is moved above
|
||||
USE_ACCESS_GROUP = 1 << 8
|
||||
USE_ACCESS_LIST = 1 << 9
|
||||
USE_BAN_LIST = 1 << 10
|
||||
USE_PASS_LIST = 1 << 11
|
||||
SHOW_DIRECTORY = 1 << 12
|
||||
ALLOW_DEED_TO_GROUP = 1 << 13
|
||||
CONTRIBUTE_WITH_DEED = 1 << 14
|
||||
SOUND_LOCAL = 1 << 15 # Hear sounds in this parcel only
|
||||
SELL_PARCEL_OBJECTS = 1 << 16 # Objects on land are included as part of the land when the land is sold
|
||||
ALLOW_PUBLISH = 1 << 17 # Allow publishing of parcel information on the web
|
||||
MATURE_PUBLISH = 1 << 18 # The information on this parcel is mature
|
||||
URL_WEB_PAGE = 1 << 19 # The "media URL" is an HTML page
|
||||
URL_RAW_HTML = 1 << 20 # The "media URL" is a raw HTML string like <H1>Foo</H1>
|
||||
RESTRICT_PUSHOBJECT = 1 << 21 # Restrict push object to either on agent or on scripts owned by parcel owner
|
||||
DENY_ANONYMOUS = 1 << 22 # Deny all non identified/transacted accounts
|
||||
# DENY_IDENTIFIED = 1 << 23 # Deny identified accounts
|
||||
# DENY_TRANSACTED = 1 << 24 # Deny identified accounts
|
||||
ALLOW_GROUP_SCRIPTS = 1 << 25 # Allow scripts owned by group
|
||||
CREATE_GROUP_OBJECTS = 1 << 26 # Allow object creation by group members or objects
|
||||
ALLOW_ALL_OBJECT_ENTRY = 1 << 27 # Allow all objects to enter a parcel
|
||||
ALLOW_GROUP_OBJECT_ENTRY = 1 << 28 # Only allow group (and owner) objects to enter the parcel
|
||||
ALLOW_VOICE_CHAT = 1 << 29 # Allow residents to use voice chat on this parcel
|
||||
USE_ESTATE_VOICE_CHAN = 1 << 30
|
||||
DENY_AGEUNVERIFIED = 1 << 31 # Prevent residents who aren't age-verified
|
||||
|
||||
|
||||
@se.enum_field_serializer("UpdateMuteListEntry", "MuteData", "MuteType")
|
||||
class MuteType(IntEnum):
|
||||
BY_NAME = 0
|
||||
@@ -2193,6 +2230,7 @@ class CreationDateSerializer(se.AdapterSubfieldSerializer):
|
||||
|
||||
|
||||
@se.subfield_serializer("MeanCollisionAlert", "MeanCollision", "Time")
|
||||
@se.subfield_serializer("ParcelProperties", "ParcelData", "ClaimDate")
|
||||
class DateSerializer(se.AdapterSubfieldSerializer):
|
||||
ADAPTER = DateAdapter(1)
|
||||
ORIG_INLINE = True
|
||||
|
||||
@@ -16,10 +16,14 @@ import weakref
|
||||
from defusedxml import minidom
|
||||
|
||||
from hippolyzer.lib.base import serialization as se, llsd
|
||||
from hippolyzer.lib.base.message.llsd_msg_serializer import LLSDMessageSerializer
|
||||
from hippolyzer.lib.base.message.message import Message
|
||||
from hippolyzer.lib.base.datatypes import TaggedUnion, UUID, TupleCoord
|
||||
from hippolyzer.lib.base.helpers import bytes_escape
|
||||
from hippolyzer.lib.base.message.message_formatting import HumanMessageSerializer
|
||||
from hippolyzer.lib.base.message.msgtypes import PacketFlags
|
||||
from hippolyzer.lib.base.message.template_dict import DEFAULT_TEMPLATE_DICT
|
||||
from hippolyzer.lib.base.network.transport import Direction
|
||||
from hippolyzer.lib.proxy.message_filter import MetaFieldSpecifier, compile_filter, BaseFilterNode, MessageFilterNode, \
|
||||
EnumFieldSpecifier, MatchResult
|
||||
from hippolyzer.lib.proxy.http_flow import HippoHTTPFlow
|
||||
@@ -614,6 +618,19 @@ class EQMessageLogEntry(AbstractMessageLogEntry):
|
||||
return "EQ"
|
||||
|
||||
def request(self, beautify=False, replacements=None):
|
||||
# TODO: This is a bit of a hack! Templated messages can be sent over the EQ, so let's
|
||||
# display them as template messages if that's what they are.
|
||||
if self.event['message'] in DEFAULT_TEMPLATE_DICT.message_templates:
|
||||
msg = LLSDMessageSerializer().deserialize(self.event)
|
||||
msg.synthetic = True
|
||||
msg.send_flags = PacketFlags.EQ
|
||||
msg.direction = Direction.IN
|
||||
# Annoyingly, templated messages sent over the EQ can have extra fields not specified
|
||||
# in the template, and this is often the case. ParcelProperties has fields that aren't
|
||||
# in the template. Luckily, we don't really care about extra fields, we just may not
|
||||
# be able to automatically decode U32 and friends without the hint from the template
|
||||
# that that is what they are.
|
||||
return HumanMessageSerializer.to_human_string(msg, replacements, beautify)
|
||||
return f'EQ {self.event["message"]}\n\n{self._format_llsd(self.event["body"])}'
|
||||
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user