From 9755a2941b60c49aa73c03b037d4321a75782563 Mon Sep 17 00:00:00 2001 From: AleaJactaEst Date: Mon, 27 Jul 2020 22:17:48 +0200 Subject: [PATCH] decode visual properties --- spykhanat.py | 154 ++++++++++++++++++----- tools/BitStream.py | 30 +++++ tools/CAction.py | 47 ++++++- tools/CActionFactory.py | 18 ++- tools/CPropertyDecoder.py | 6 +- tools/Enum.py | 26 +++- tools/Impulse.py | 2 +- tools/TVPNodeBase.py | 252 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 486 insertions(+), 49 deletions(-) create mode 100644 tools/TVPNodeBase.py diff --git a/spykhanat.py b/spykhanat.py index 8a850cb..8118356 100755 --- a/spykhanat.py +++ b/spykhanat.py @@ -21,6 +21,11 @@ # Ex.: # sudo tcpdump -i docker0 -w capture2.pcap # ./spykhanat.py --pcap-file=../capture2.pcap --msg-xml=../khanat-opennel-code/code/ryzom/common/data_common/msg.xml +# ./spykhanat.py -m ../khanat-opennel-code/code/ryzom/common/data_common/msg.xml --yaml capture-2020-07-08-00-33-khanatclient.yml -w ../khanat-opennel-code/code/ryzom/common/data_common/database.xml -p capture-2020-07-08-00-33-khanatclient.pcap --filter-host-service='172.17.0.2:47851' +# +# FILEDATA="capture-2020-07-08-00-33-khanatclient.pcap" +# ./spykhanat.py -m ../khanat-opennel-code/code/ryzom/common/data_common/msg.xml --yaml ${FILEDATA::-5}.yml -w ../khanat-opennel-code/code/ryzom/common/data_common/database.xml -p $FILEDATA --filter-host-service='172.17.0.2:47851' +# # install pcapfile # pip install pypcapfile @@ -47,6 +52,7 @@ from tools import CImpulseDecoder from tools import CAction from tools import Impulse from tools import CPropertyDecoder +from tools import TVPNodeBase import xml.etree.ElementTree as ET from datetime import datetime @@ -195,62 +201,117 @@ class SpyPcap(): def add_registered_action(self, clientid, action): self.client_state[clientid]['RegisteredAction'].setdefault(action.Code, []) self.client_state[clientid]['RegisteredAction'][action.Code].append(action) - self.client_state[clientid]['PropertyDecoder'] = CPropertyDecoder() + self.client_state[clientid]['PropertyDecoder'] = CPropertyDecoder.CPropertyDecoder() + self.client_state[clientid]['VisualPropertyTreeRoot'] = TVPNodeBase.TVPNodeBase("_VisualPropertyTreeRoot") + self.client_state[clientid]['VisualPropertyTreeRoot'].build_tree() + +# def decodeDiscreetProperties(self, clientid, msgin): +# # khanat-opennel-code/code/ryzom/client/src/network_connection.h:148 void decodeDiscreetProperties( NLMISC::CBitMemStream& msgin ) +# BranchHasPayload = msgin.readBool("BranchHasPayload") +# if BranchHasPayload: +# if self.client_state[clientid]['VisualPropertyTreeRoot'].isLeaf(): +# PropIndex = self.client_state[clientid]['VisualPropertyTreeRoot'].PropIndex +# self.client_state[clientid]['VisualPropertyTreeRoot'].decodeDiscreetProperty(msgin, PropIndex) +#if ( BranchHasPayload ) +# { +# if ( isLeaf() ) +# { +# SlotContext.NetworkConnection->decodeDiscreetProperty( msgin, PropIndex ); +# } +# else +# { +# if ( a() ) a()->decodeDiscreetProperties( msgin ); +# if ( b() ) b()->decodeDiscreetProperties( msgin ); +# } +# } def decodeVisualProperties(self, clientid, msgin): """ - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) """ actions = [] + properties = [] try: while True: + property = {} # if ( msgin.getPosInBit() + (sizeof(TCLEntityId)*8) > msgin.length()*8 ) return - if msgin.sizeRead() + (8*8 ) > msgin.sizeData() * 8: - return + # if msgin.sizeRead() + (8*8 ) > msgin.sizeData() * 8: + #if msgin.needRead() < (8*8): + if msgin.needRead() < 8: + logging.getLogger(LOGGER).debug("too small no decodeVisualProperties [{0} > {1}]".format(msgin.sizeRead() + (8*8 ), msgin.sizeData() * 8 )) + print("-"*80) + print(properties) + return properties + logging.getLogger(LOGGER).debug("too small no decodeVisualProperties [{0} > {1}]".format(msgin.sizeRead() + (8*8 ), msgin.sizeData() * 8 )) slot = msgin.readUint8("Slot") + property['slot'] = slot associationBits = msgin.readSerial(2, "associationBits") + property['associationBits'] = associationBits + logging.getLogger(LOGGER).debug("slot:{0} (associationBits:{1})".format(slot, associationBits)) if self.client_state[clientid]['PropertyDecoder'] .associationBitsHaveChanged( slot, associationBits ) and (slot==0): if self.client_state[clientid]['PropertyDecoder'] .isUsed( slot ): sheet = self.client_state[clientid]['PropertyDecoder'] .getSheet(slot) + property['sheet'] = sheet + logging.getLogger(LOGGER).debug("sheet:{0}".format(sheet)) + # TODO - remove sheet found in the list timestampIsThere = msgin.readBool("timestampIsThere") if timestampIsThere: timestampDelta = msgin.readSerial(4, "timestampDelta") timestamp = self.client_state[clientid]['CurrentReceivedNumber'] - timestampDelta + logging.getLogger(LOGGER).debug("timestamp:{0} (timestampDelta:{1})".format(timestamp, timestampDelta)) else: timestamp = self.client_state[clientid]['CurrentReceivedNumber'] + logging.getLogger(LOGGER).debug("timestamp:{0}".format(timestamp)) + property['timestamp'] = timestamp + property.setdefault('Actions', []) # Tree # currentNode->a() - BranchHasPayload = msgin.readBool("BranchHasPayload") + BranchHasPayload = msgin.readBool("BranchHasPayload [_VisualPropertyTreeRoot.VPA]") + logging.getLogger(LOGGER).debug("_VisualPropertyTreeRoot.VPA->BranchHasPayload:{0}".format(BranchHasPayload)) if BranchHasPayload: # _PropertyDecoder.receive( _CurrentReceivedNumber, ap ); # Create a new action cActionPosition = CAction.CActionPosition(slot, Enum.TActionCode.ACTION_POSITION_CODE, self.client_state[clientid]['world']) + cActionPosition.set_name('POSITION_CODE') self.client_state[clientid]['PropertyDecoder'] .receive(cActionPosition) cActionPosition.unpack(msgin) actions.append(cActionPosition) + property['Actions'] .append(cActionPosition) # currentNode->b() - BranchHasPayload = msgin.readBool("BranchHasPayload") + BranchHasPayload = msgin.readBool("BranchHasPayload [_VisualPropertyTreeRoot.VPB]") + logging.getLogger(LOGGER).debug("_VisualPropertyTreeRoot.VPB->BranchHasPayload:{0}".format(BranchHasPayload)) if BranchHasPayload: # currentNode->b()->a() - BranchHasPayload = msgin.readBool("BranchHasPayload") - # Create a new action -> PROPERTY_ORIENTATION - cActionOrientation= CAction.CActionSint64(slot, self.client_state[clientid]['world']) - cActionOrientation.PropertyCode = Enum.TPropIndex.PROPERTY_ORIENTATION - cActionOrientation.unpack(msgin) - #self.client_state[clientid]['PropertyDecoder'] .receive(cActionPosition) - actions.append(cActionOrientation) + BranchHasPayload = msgin.readBool("BranchHasPayload [_VisualPropertyTreeRoot.VPB.VPA]") + logging.getLogger(LOGGER).debug("_VisualPropertyTreeRoot.VPB.VPA->BranchHasPayload:{0}".format(BranchHasPayload)) + if BranchHasPayload: + # Create a new action -> PROPERTY_ORIENTATION + cActionOrientation= CAction.CActionSint64(slot, Enum.TPropIndex.PROPERTY_ORIENTATION, self.client_state[clientid]['world']) + cActionOrientation.setNbBits(Enum.TPropIndex.PROPERTY_ORIENTATION, 'PROPERTY_ORIENTATION') + cActionOrientation.set_name('PROPERTY_ORIENTATION') + cActionOrientation.unpack(msgin) + #self.client_state[clientid]['PropertyDecoder'] .receive(cActionPosition) + actions.append(cActionOrientation) + property['Actions'] .append(cActionOrientation) # Discreet properties - - + # _VisualPropertyTreeRoot->b()->b() + actions = self.client_state[clientid]['VisualPropertyTreeRoot'].decodeDiscreetPropertiesVpbVpb(clientid, msgin, slot, self.client_state[clientid]['world']) + for action in actions: + property['Actions'] .append(action) + #self.decodeDiscreetProperties(clientid, msgin) + properties.append(property) + except Exception as e: + raise e except: # Detect end of stream (little hard to close) pass + return properties def decode_server(self, clientid, msgin, receivedPacket, receivedAck, nextSentPacket=0): """ - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1312 void CNetworkConnection::receiveNormalMessage(CBitMemStream &msgin) - khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp:38 void CImpulseDecoder::decode(CBitMemStream &inbox, TPacketNumber receivedPacket, TPacketNumber receivedAck, TPacketNumber nextSentPacket, vector &actions) - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1312 void CNetworkConnection::receiveNormalMessage(CBitMemStream &msgin) + khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp:38 void CImpulseDecoder::decode(CBitMemStream &inbox, TPacketNumber receivedPacket, TPacketNumber receivedAck, TPacketNumber nextSentPacket, vector &actions) + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) """ actions = [] for level in range(0, 3): @@ -282,7 +343,12 @@ class SpyPcap(): pass num += 1 #actionFactory = CAction.CActionFactory(None) - action = self.actionFactory.unpack(msgin) + logging.getLogger(LOGGER).debug("[decode_server] (%s)" % (msgin.showAllData())) + try: + action = self.actionFactory.unpack(msgin) + except Exception as e: + logging.getLogger(LOGGER).debug("[decode_server] (%s)" % (msgin.showAllData())) + raise e logging.getLogger(LOGGER).debug("action:%s" % action) #action = self._CActionFactory.unpack(msgin) if keep: @@ -291,9 +357,9 @@ class SpyPcap(): elif action: logging.getLogger(LOGGER).debug("append Code:%s" % str(action.Code)) self.add_registered_action(clientid, action) - # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) - self.decodeVisualProperties(clientid, msgin) - return actions + # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1512 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + properties = self.decodeVisualProperties(clientid, msgin) + return actions, properties def decode_client_send_normal_message(self, msgin, clientid, dst, sequenceid, name, Reference): ''' @@ -428,6 +494,7 @@ class SpyPcap(): def decode_khanat_message(self, msgin, src, dst, sequenceid, clientname, Parent, Source): target = "%s_%s" % (Source, Parent[7:]) actions = [] + properties = [] impulses = [] databases = [] CurrentSendNumber = msgin.readSint32('CurrentSendNumber') @@ -439,7 +506,7 @@ class SpyPcap(): if not SystemMode: _LastReceivedAck = msgin.readSint32('LastReceivedAck'); logging.getLogger(LOGGER).debug("[Server -> Client] Normal Mode {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d}" % (CurrentSendNumber, src, dst, _LastReceivedAck)) - actions = self.decode_server(dst, msgin, CurrentSendNumber, CurrentSendNumber-1) + actions, properties = self.decode_server(dst, msgin, CurrentSendNumber, CurrentSendNumber-1) if actions: logging.getLogger(LOGGER).debug('list actions: [' + str(len(actions)) + '] ' +','.join( [ str(x) for x in actions] ) ) else: @@ -624,7 +691,7 @@ class SpyPcap(): #cActionFactory.unpack(msgin) logging.getLogger(LOGGER).debug("[Server -> Client] msg:%s" % msgin.showAllData()) #logging.getLogger(LOGGER).info("impulses:%s" % str(impulses)) - return actions, impulses, databases + return actions, impulses, databases, properties def read(self): file = open( self.pcap_file , 'rb') @@ -659,6 +726,7 @@ class SpyPcap(): actions_servers = [] impulses_servers = [] impulses_clients = [] + properties_servers = [] if self.show_raw_packet: logging.getLogger(LOGGER).debug("[raw packet] timestamp:%s [%s] src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), @@ -695,7 +763,7 @@ class SpyPcap(): if (self.khanat_host_service and self.khanat_host_service.match(src)) or ( not self.khanat_host_service and khanat_host == src): _provenance = 'Server -> Client' logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData())) - actions_servers, impulses_servers, databases_servers = self.decode_khanat_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src]) + actions_servers, impulses_servers, databases_servers, properties_servers = self.decode_khanat_message(msgin, src, dst, sequenceid, list_host[dst], Reference, list_host[src]) else: _provenance = 'Client -> Server' logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData())) @@ -802,6 +870,29 @@ class SpyPcap(): id += 1 #print("-"*30) + if properties_servers: + self.outyaml.write("\nvisual_properties_%s_%d:\n" %(list_host[src], sequenceid)) + for property in properties_servers: + self.outyaml.write(" slot: %d\n" % (property['slot'])) + self.outyaml.write(" parents: %s\n" % Reference) + self.outyaml.write(" associationBits: %d\n" % (property['associationBits'])) + self.outyaml.write(" timestamp: %d\n" % (property['timestamp'])) + for action in property['Actions']: + try: + self.outyaml.write(" %s: %s\n" % (action.get_name(), str(action.value))) + except AttributeError: + # TODO - change print if CAtion is Position or other (and not CActionSint64) + params = action.get_parameter() + self.outyaml.write(" %s:\n" % (action.get_name())) + for key in params: + if key == "Reference": + pass + elif key == "GameCycle": + pass + elif key == "Slot": + pass + else: + self.outyaml.write(" %s: %s\n" % (key, params[key])) if actions_clients: self.outyaml.write("\nblock_%s_%d:\n" %(list_host[src], sequenceid)) @@ -834,6 +925,8 @@ class SpyPcap(): # params = Impulse.get_parameter() # self.outyaml.write(" %s:\n" % (Impulse.get_name())) # id += 1 + #if Reference == 'packet_409': + # raise "STOP" sequenceid += 1 sequencenum += 1 for client in self.client_state: @@ -850,12 +943,13 @@ def main(): # #logger.append(logging.getLogger(DecodeImpuls.LOGGER)) # #logger.append(logging.getLogger(BitStream.LOGGER)) # logger.append(logging.getLogger(CStringManager.LOGGER)) -# logger.append(logging.getLogger(CAction.LOGGER)) -# logger.append(logging.getLogger(CActionFactory.LOGGER)) + #logger.append(logging.getLogger(CAction.LOGGER)) + #logger.append(logging.getLogger(CActionFactory.LOGGER)) # logger.append(logging.getLogger(BitStream.LOGGER)) - logger.append(logging.getLogger(DecodeDatabase.LOGGER)) - logger.append(logging.getLogger(Impulse.LOGGER)) - CImpulseDecoder + #logger.append(logging.getLogger(DecodeDatabase.LOGGER)) + #logger.append(logging.getLogger(Impulse.LOGGER)) + #logger.append(logging.getLogger(TVPNodeBase.LOGGER)) + # CImpulseDecoder # logger.append(logging.getLogger('CGenericMultiPartTemp')) parser = argparse.ArgumentParser() diff --git a/tools/BitStream.py b/tools/BitStream.py index ec5c818..890b5db 100644 --- a/tools/BitStream.py +++ b/tools/BitStream.py @@ -125,6 +125,20 @@ class BitStream(): name = name.strip().split(' ')[0].strip() self._groupWrite.append((p1, p1+nbits, name, typeName, value)) + def pushSerial64(self, value, nbits, decode=True, typeName=None): + if nbits > 32: + p1 = self._pos + msd = value >> 32 + self.internalSerial(msd, nbits - 32, False, typeName) + self.internalSerial(value, 32, False, typeName) + value = msd << 32 | msd2 + if typeName is None: + typeName = 'Uint{0}'.format(nbits) + self._groupWrite.append((p1, p1+nbits, name, typeName, value)) + return value + else: + return self.internalSerial(value, nbits, decode, typeName) + def pushBool(self, value, decode=True): p1 = self._pos if value: @@ -553,6 +567,22 @@ class BitStream(): self._groupRead.append((v1, v1+nbits, name, typeName, value)) return value + def readSerial64(self, nbits, name="", decode=True, typeName=None): + if nbits > 32: + v1 = self._read + msd = self.readSerial(nbits - 32, name, False, typeName) + msd2 = self.readSerial(32, name, False, typeName) + value = msd << 32 | msd2 + if decode: + if typeName is None: + typeName = 'Uint{0}'.format(nbits) + self._groupRead.append((v1, v1+nbits, name, typeName, value)) + return value + else: + if typeName is None: + typeName = 'Uint{0}'.format(nbits) + return self.readSerial(nbits, name, decode, typeName) + def readBool(self, name): v1 = self._read v = self.readSerial(1, name=name, decode=False, typeName='Bool') diff --git a/tools/CAction.py b/tools/CAction.py index a2f8f78..9f2b802 100644 --- a/tools/CAction.py +++ b/tools/CAction.py @@ -27,6 +27,32 @@ from tools import Enum LOGGER='CActionFactory' INVALID_SLOT = 0xff +# khanat-opennel-code/code/ryzom/common/src/game_share/action_sint64.cpp:55 void CActionSint64::registerNumericPropertiesRyzom() +PROPERTY_TO_NB_BIT = [0 for _ in range(0, Enum.TPropIndex.NB_VISUAL_PROPERTIES)] +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_ORIENTATION] = 32 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_SHEET] = 52 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_BEHAVIOUR] = 48 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_NAME_STRING_ID] = 32 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_TARGET_ID] = 8 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_MODE] = 44 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_VPA] = 64 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_VPB] = 47 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_VPC] = 58 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_ENTITY_MOUNTED_ID] = 8 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_RIDER_ENTITY_ID] = 8 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_CONTEXTUAL] = 16 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_BARS] = 32 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_TARGET_LIST] = 32 # USER_DEFINED_PROPERTY_NB_BITS +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_VISUAL_FX] = 11 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_GUILD_SYMBOL] = 60 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_GUILD_NAME_ID] = 32 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_EVENT_FACTION_ID] = 32 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_PVP_MODE] = Enum.TPVPMode.NbBits +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_PVP_CLAN] = 32 +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_OWNER_PEOPLE] = 3 # 4 races + unknow +PROPERTY_TO_NB_BIT[Enum.TPropIndex.PROPERTY_OUTPOST_INFOS] = 16 # 15+1 + + class CAction: def __init__(self, slot, code, world): self.Code = code @@ -385,8 +411,10 @@ class CActionSint64(CAction): def __init__(self, slot, code, world): super().__init__(slot, code, world) self.value = 0 - self.NbBits = 0 - self.PropertyToNbBit = { TPropIndex.TPropIndex.PROPERTY_ORIENTATION: 32, + self.NbBits = 64 + self.NameProperty = 'None' + self.PropertyToNbBit = { + TPropIndex.TPropIndex.PROPERTY_ORIENTATION: 32, TPropIndex.TPropIndex.PROPERTY_SHEET: 52, TPropIndex.TPropIndex.PROPERTY_BEHAVIOUR: 48, TPropIndex.TPropIndex.PROPERTY_NAME_STRING_ID: 32, @@ -418,22 +446,31 @@ class CActionSint64(CAction): return "CActionSint64" + super().__str__() def unpack(self, msgin): + logging.getLogger(LOGGER).debug("nb bit:{0}".format(self.NbBits)) logging.getLogger(LOGGER).debug("msgin:%s" % msgin.showAllData()) - self.value = msgin.readUint64('value') + # self.value = msgin.readSerial( self.NbBits, 'value') + self.value = msgin.readSerial64( self.NbBits, self.NameProperty) logging.getLogger(LOGGER).debug("msgin:%s" % msgin.showAllData()) #self.NbBits = msgin.readUint32('NbBits') logging.getLogger(LOGGER).debug("value:%u" % self.value) logging.getLogger(LOGGER).debug("msgin:%s" % msgin.showAllData()) - def pack(self, msgout): super().pack(msgout) - msgout.pushUint64(self.value) + #msgout.pushUint64(self.value) + msgout.pushSerial64(self.value, self.NbBits) #msgout.pushUint32(self.NbBits) def reset(self): self.value = 0 self.NbBits = 0 + self.NameProperty = 'None' + + def setNbBits(self, propIndex, nameproperty): + self.NbBits = PROPERTY_TO_NB_BIT[propIndex] + self.NameProperty = nameproperty + self.set_name(nameproperty) + logging.getLogger(LOGGER).debug("NameProperty:{1} NbBits:{0}".format(self.NbBits, self.NameProperty )) class CActionBlock: diff --git a/tools/CActionFactory.py b/tools/CActionFactory.py index a36a0fb..0b16f73 100644 --- a/tools/CActionFactory.py +++ b/tools/CActionFactory.py @@ -91,6 +91,18 @@ class CActionFactory: action.Slot = slot return action + def createByPropIndex(self, slot, propIndex, nameproperty): + logging.getLogger(LOGGER).debug('createByPropIndex (slot:{0}, propIndex:{1}, nameproperty:{2})'.format(slot, propIndex, nameproperty)) + if propIndex == Enum.TPropIndex.PROPERTY_POSITION: + action = self.create(slot, Enum.TActionCode.ACTION_POSITION_CODE) + else: + action =self. create(slot, Enum.TActionCode.ACTION_SINT64) + action.setNbBits(propIndex, nameproperty) + action.PropertyCode = propIndex + return action + +# khanat-opennel-code/code/ryzom/common/src/game_share/action_factory.cpp:152 + def unpack(self, msgin, Reference = None, Name = None): ''' khanat-opennel-code/code/ryzom/common/src/game_share/action_factory.cpp : CAction *CActionFactory::unpack (NLMISC::CBitMemStream &message, NLMISC::TGameCycle /* currentCycle */ ) @@ -107,12 +119,10 @@ class CActionFactory: try: action.unpack (msgin); except RuntimeError: - log = logging.getLogger('myLogger') - log.warning('Missing code to unpack (code :%u)' % code) + logging.getLogger(LOGGER).warning('Missing code to unpack (code :%u)' % code) raise RuntimeError else: - log = logging.getLogger('myLogger') - log.warning('Unpacking an action with unknown code, skip it (%u)' % code) + logging.getLogger(LOGGER).warning('Unpacking an action with unknown code, skip it (%u)' % code) if Reference: action.add_reference(Reference) if Name: diff --git a/tools/CPropertyDecoder.py b/tools/CPropertyDecoder.py index 32a2caf..64d9554 100644 --- a/tools/CPropertyDecoder.py +++ b/tools/CPropertyDecoder.py @@ -38,9 +38,9 @@ class CEntityEntry(): class CPropertyDecoder(): def __init__(self): # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:2937 _PropertyDecoder.init (256); - - for i in range(0, 256): - self.Entities[i] = CEntityEntry() + self.Entities = [CEntityEntry() for _ in range(0, 256)] + #for i in range(0, 256): + # self.Entities[i] = CEntityEntry() self._RefPosX = 0 self._RefPosY = 0 self._RefBitsX = 0 diff --git a/tools/Enum.py b/tools/Enum.py index e52ccd1..cc0cf86 100644 --- a/tools/Enum.py +++ b/tools/Enum.py @@ -266,7 +266,6 @@ class TPeople(IntEnum): EndPeople = 142 - class ECharacterTitle(IntEnum): ''' khanat-opennel-code/code/ryzom/common/src/game_share/character_title.h # enum ECharacterTitle @@ -579,11 +578,11 @@ class NPC_ICON(IntEnum): # TNPCMissionGiverState NbMissionGiverStates = 7 class TInventoryId(IntEnum): - Bag = 0, - Packers = 1, - Room= 5, # Packers+MaxNbPackers, - InvalidInvId = 6, - NbInventoryIds = 6, # =InvalidInvId + Bag = 0, + Packers = 1, + Room= 5, # Packers+MaxNbPackers, + InvalidInvId = 6, + NbInventoryIds = 6, # =InvalidInvId class TItemPropId(IntEnum): Sheet = 0, @@ -646,3 +645,18 @@ class TPropIndex(IntEnum): PROPERTY_OUTPOST_INFOS = 27, NB_VISUAL_PROPERTIES = 28 +class TPVPMode(IntEnum): + NONE = 0, + PvpDuel = 1, + PvpChallenge = 2, + PvpZoneFree = 4, + PvpZoneFaction = 8, + PvpZoneGuild = 16, + PvpZoneOutpost = 32, + PvpFaction = 64, + PvpFactionFlagged = 128, + PvpZoneSafe = 256, + PvpSafe = 512, + #Unknown = 513, + #NbModes = 513, + NbBits = 10 #// number of bits needed to store all valid values diff --git a/tools/Impulse.py b/tools/Impulse.py index df8bca6..9c5ec5c 100644 --- a/tools/Impulse.py +++ b/tools/Impulse.py @@ -701,7 +701,7 @@ class impulseUserBars(ImpulseBase): super().__init__() def read(self, name, msgin, world): - # khanat-opennel-code/code/ryzom/server/src/entities_game_service/player_manager/character.cpp void CCharacter::barUpdate() + # khanat-opennel-code/code/ryzom/server/src/entities_game_service/player_manager/character.cpp void CCharacter::barUpdate() id = "UserBars" logging.getLogger(LOGGER).debug("read %s" % id) self.name = name.replace(':', '_') diff --git a/tools/TVPNodeBase.py b/tools/TVPNodeBase.py new file mode 100644 index 0000000..6b09d72 --- /dev/null +++ b/tools/TVPNodeBase.py @@ -0,0 +1,252 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module BitStream +# +# Copyright (C) 2019 AleaJactaEst +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import logging +#from ctypes import * +#import sys +#import inspect +#import copy +#import struct +#from tools import TPropIndex +from tools import Enum +#from tools import BitStream +from tools import CActionFactory + +LOGGER='TVPNodeBase' + +class TVPNodeBase(): + + def __init__(self, name=""): + # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:414 + self.VPParent = None # TVPNodeBase + self.VPA = None # TVPNodeBase + self.VPB = None # TVPNodeBase + self.PropIndex = Enum.TPropIndex.NB_VISUAL_PROPERTIES + self.BranchHasPayload = False + self.Name = name + self.NameProperty = "" + + def setNodePropIndex(self, property , name_property = ""): + self.PropIndex = property + self.NameProperty = name_property + + def makeChildren(self): + self.VPA = TVPNodeBase(self.Name + '.VPA') + self.VPA.VPParent = self + self.VPB = TVPNodeBase(self.Name + '.VPB') + self.VPB.VPParent = self + + def makeDescendants(self, nbLevels ): + self.makeChildren() + if nbLevels > 1: + self.VPA.makeDescendants( nbLevels-1 ); + self.VPB.makeDescendants( nbLevels-1 ); + + + def build_tree(self): + # khanat-opennel-code/code/ryzom/common/src/game_share/entity_types.h:458 uint buildTree() + self.makeChildren() + self.VPA.setNodePropIndex(Enum.TPropIndex.PROPERTY_POSITION, 'PROPERTY_POSITION') + self.VPB.makeChildren() + self.VPB.VPA.setNodePropIndex(Enum.TPropIndex.PROPERTY_ORIENTATION, 'PROPERTY_ORIENTATION') + self.VPB.VPB.makeDescendants( 3 ) # 8 leaves + those created by additional makeChildren() + self.VPB.VPB.VPA.VPA.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_SHEET, 'PROPERTY_SHEET'); + self.VPB.VPB.VPA.VPA.VPB.makeChildren(); + self.VPB.VPB.VPA.VPA.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_BEHAVIOUR, 'PROPERTY_BEHAVIOUR'); + self.VPB.VPB.VPA.VPA.VPB.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_OWNER_PEOPLE, 'PROPERTY_OWNER_PEOPLE'); + self.VPB.VPB.VPA.VPB.VPA.makeChildren(); + self.VPB.VPB.VPA.VPB.VPA.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_NAME_STRING_ID, 'PROPERTY_NAME_STRING_ID'); + self.VPB.VPB.VPA.VPB.VPA.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_CONTEXTUAL, 'PROPERTY_CONTEXTUAL'); + self.VPB.VPB.VPA.VPB.VPB.makeChildren(); + self.VPB.VPB.VPA.VPB.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_TARGET_LIST, 'PROPERTY_TARGET_LIST'); + self.VPB.VPB.VPA.VPB.VPB.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_TARGET_ID, 'PROPERTY_TARGET_ID'); + self.VPB.VPB.VPB.VPA.VPA.makeChildren(); + self.VPB.VPB.VPB.VPA.VPA.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_MODE, 'PROPERTY_MODE'); + self.VPB.VPB.VPB.VPA.VPA.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_VPA, 'PROPERTY_VPA'); + self.VPB.VPB.VPB.VPA.VPB.makeChildren(); + self.VPB.VPB.VPB.VPA.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_BARS, 'PROPERTY_BARS'); + self.VPB.VPB.VPB.VPA.VPB.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_VISUAL_FX, 'PROPERTY_VISUAL_FX'); + self.VPB.VPB.VPB.VPB.VPA.makeChildren(); + self.VPB.VPB.VPB.VPB.VPA.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_VPB, 'PROPERTY_VPB'); + self.VPB.VPB.VPB.VPB.VPA.VPB.makeChildren(); + self.VPB.VPB.VPB.VPB.VPA.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_VPC, 'PROPERTY_VPC'); + self.VPB.VPB.VPB.VPB.VPA.VPB.VPB.makeChildren(); + self.VPB.VPB.VPB.VPB.VPA.VPB.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_EVENT_FACTION_ID, 'PROPERTY_EVENT_FACTION_ID'); + self.VPB.VPB.VPB.VPB.VPA.VPB.VPB.VPB.makeChildren(); + self.VPB.VPB.VPB.VPB.VPA.VPB.VPB.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_PVP_MODE, 'PROPERTY_PVP_MODE'); + self.VPB.VPB.VPB.VPB.VPA.VPB.VPB.VPB.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_PVP_CLAN, 'PROPERTY_PVP_CLAN'); + self.VPB.VPB.VPB.VPB.VPB.makeChildren(); + self.VPB.VPB.VPB.VPB.VPB.VPA.makeChildren(); + self.VPB.VPB.VPB.VPB.VPB.VPA.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_ENTITY_MOUNTED_ID, 'PROPERTY_ENTITY_MOUNTED_ID'); + self.VPB.VPB.VPB.VPB.VPB.VPA.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_RIDER_ENTITY_ID, 'PROPERTY_RIDER_ENTITY_ID'); + self.VPB.VPB.VPB.VPB.VPB.VPB.makeChildren(); + self.VPB.VPB.VPB.VPB.VPB.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_OUTPOST_INFOS, 'PROPERTY_OUTPOST_INFOS'); + self.VPB.VPB.VPB.VPB.VPB.VPB.VPB.makeChildren(); + self.VPB.VPB.VPB.VPB.VPB.VPB.VPB.VPA.setNodePropIndex( Enum.TPropIndex.PROPERTY_GUILD_SYMBOL, 'PROPERTY_GUILD_SYMBOL'); + self.VPB.VPB.VPB.VPB.VPB.VPB.VPB.VPB.setNodePropIndex( Enum.TPropIndex.PROPERTY_GUILD_NAME_ID, 'PROPERTY_GUILD_NAME_ID'); + +# TVPNodeBase *getPOSITIONnode() { return VPA; } +# TVPNodeBase *getORIENTATIONnode() { return VPB->VPA; } +# +# // From the discrete root (mainroot->VPB->VPB) +# TVPNodeBase *getSHEETnode() { return VPA->VPA->VPA; } +# TVPNodeBase *getBEHAVIOURnode() { return VPA->VPA->VPB->VPA; } +# TVPNodeBase *getOWNER_PEOPLEnode() { return VPA->VPA->VPB->VPB; } +# TVPNodeBase *getNAME_STRING_IDnode() { return VPA->VPB->VPA->VPA; } +# TVPNodeBase *getCONTEXTUALnode() { return VPA->VPB->VPA->VPB; } +# TVPNodeBase *getTARGET_LISTnode() { return VPA->VPB->VPB->VPA; } +# TVPNodeBase *getTARGET_IDnode() { return VPA->VPB->VPB->VPB; } +# TVPNodeBase *getMODEnode() { return VPB->VPA->VPA->VPA; } +# TVPNodeBase *getVPAnode() { return VPB->VPA->VPA->VPB; } +# TVPNodeBase *getBARSnode() { return VPB->VPA->VPB->VPA; } +# TVPNodeBase *getVISUAL_FXnode() { return VPB->VPA->VPB->VPB; } +# TVPNodeBase *getVPBnode() { return VPB->VPB->VPA->VPA; } +# TVPNodeBase *getVPCnode() { return VPB->VPB->VPA->VPB->VPA; } +# TVPNodeBase *getEVENT_FACTION_IDnode() { return VPB->VPB->VPA->VPB->VPB->VPA; } +# TVPNodeBase *getPVP_MODEnode() { return VPB->VPB->VPA->VPB->VPB->VPB->VPA; } +# TVPNodeBase *getPVP_CLANnode() { return VPB->VPB->VPA->VPB->VPB->VPB->VPB; } +# TVPNodeBase *getENTITY_MOUNTED_IDnode() { return VPB->VPB->VPB->VPA->VPA; } +# TVPNodeBase *getRIDER_ENTITY_IDnode() { return VPB->VPB->VPB->VPA->VPB; } +# TVPNodeBase *getOUTPOST_INFOSnode() { return VPB->VPB->VPB->VPB->VPA; } +# TVPNodeBase *getGUILD_SYMBOLnode() { return VPB->VPB->VPB->VPB->VPB->VPA; } +# TVPNodeBase *getGUILD_NAME_IDnode() { return VPB->VPB->VPB->VPB->VPB->VPB; } + +#uint buildTree() +# { +# makeChildren(); +# setNodePropIndex( POSITION ); +# VPB->makeChildren(); +# setNodePropIndex( ORIENTATION ); +# +# TVPNodeBase *discreetRoot = VPB->VPB; +# discreetRoot->makeDescendants( 3 ); // 8 leaves + those created by additional makeChildren() +# discreetRoot->setNodePropIndex( SHEET ); +# discreetRoot->VPA->VPA->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( BEHAVIOUR ); +# discreetRoot->setNodePropIndex( OWNER_PEOPLE ); +# discreetRoot->VPA->VPB->VPA->makeChildren(); +# discreetRoot->setNodePropIndex( NAME_STRING_ID ); +# discreetRoot->setNodePropIndex( CONTEXTUAL ); +# discreetRoot->VPA->VPB->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( TARGET_LIST ); +# discreetRoot->setNodePropIndex( TARGET_ID ); +# discreetRoot->VPB->VPA->VPA->makeChildren(); +# discreetRoot->setNodePropIndex( MODE ); +# discreetRoot->setNodePropIndex( VPA ); +# discreetRoot->VPB->VPA->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( BARS ); +# discreetRoot->setNodePropIndex( VISUAL_FX ); +# discreetRoot->VPB->VPB->VPA->makeChildren(); +# discreetRoot->setNodePropIndex( VPB ); +# discreetRoot->VPB->VPB->VPA->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( VPC ); +# discreetRoot->VPB->VPB->VPA->VPB->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( EVENT_FACTION_ID ); +# discreetRoot->VPB->VPB->VPA->VPB->VPB->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( PVP_MODE ); +# discreetRoot->setNodePropIndex( PVP_CLAN ); +# discreetRoot->VPB->VPB->VPB->makeChildren(); +# discreetRoot->VPB->VPB->VPB->VPA->makeChildren(); +# discreetRoot->setNodePropIndex( ENTITY_MOUNTED_ID ); +# discreetRoot->setNodePropIndex( RIDER_ENTITY_ID ); +# discreetRoot->VPB->VPB->VPB->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( OUTPOST_INFOS ); +# discreetRoot->VPB->VPB->VPB->VPB->VPB->makeChildren(); +# discreetRoot->setNodePropIndex( GUILD_SYMBOL ); +# discreetRoot->setNodePropIndex( GUILD_NAME_ID ); +# +# return NB_VISUAL_PROPERTIES; + + def isRoot(self): + # khanat-opennel-code/code/ryzom/common/src/game_share/entity_types.h:407 bool isRoot() + return self.VPParent == None + + def isLeaf(self): + # khanat-opennel-code/code/ryzom/common/src/game_share/entity_types.h:410 bool isLeaf() + return self.PropIndex != Enum.TPropIndex.NB_VISUAL_PROPERTIES + + def getLevel(self): + level = 0 + node = self + while node != None: + level += 1 + node = node.VPParent + return level + + def decodeDiscreetProperty(self, msgin, propIndex, slot, world): + # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1746 void CNetworkConnection::decodeDiscreetProperty + logging.getLogger(LOGGER).debug("decodeDiscreetProperty") + action = None + if propIndex == Enum.TPropIndex.PROPERTY_TARGET_LIST: + logging.getLogger(LOGGER).debug("PROPERTY_TARGET_LIST") + listSize = msgin.readUint8("listSize") + logging.getLogger(LOGGER).debug("listSize:{0}".format(listSize)) + # TargetSlotsList.resize(listSize); + if listSize > 0: + for i in range(0, listSize): + d1 = msgin.readUint8( "TargetSlots_{0}".format(i)); + logging.getLogger(LOGGER).debug("data:{0}".format(d1)) + # Update property + return [] + + logging.getLogger(LOGGER).debug("CActionFactory : {0}".format(slot)) + action = CActionFactory.CActionFactory(world).createByPropIndex(slot, self.PropIndex, self.NameProperty) + logging.getLogger(LOGGER).debug("unpack: {0}".format(slot)) + action.unpack(msgin) + if self.PropIndex == Enum.TPropIndex.PROPERTY_SHEET: + aliasBit = msgin.readBool("aliasBit") + logging.getLogger(LOGGER).debug("aliasBit:{0}".format(aliasBit)) + if aliasBit: + alias = msgin.readUint32("alias"); + logging.getLogger(LOGGER).debug("alias:{0}".format(alias)) + elif self.PropIndex == Enum.TPropIndex.PROPERTY_MODE: + pass + else: + pass + logging.getLogger(LOGGER).debug("[decodeDiscreetProperty] (%s)" % (msgin.showAllData())) + return action + + + def decodeDiscreetProperties(self, clientid, msgin, slot, world): + # khanat-opennel-code/code/ryzom/client/src/network_connection.h:148 void decodeDiscreetProperties + logging.getLogger(LOGGER).debug("decodeDiscreetProperties " + self.Name) + self.BranchHasPayload = msgin.readBool("BranchHasPayload [{0}, NameProperty:{1}]".format(self.Name, self.NameProperty)) + actions = [] + if self.BranchHasPayload: + if self.isLeaf(): + # SlotContext.NetworkConnection->decodeDiscreetProperty( msgin, PropIndex ); + logging.getLogger(LOGGER).debug("isLeaf") + action = self.decodeDiscreetProperty(msgin, self.PropIndex, slot, world) + if action: + actions.append(action) + else: + if self.VPA: + tmp = self.VPA.decodeDiscreetProperties(clientid, msgin, slot, world) + for action in tmp: + actions.append(action) + if self.VPB: + tmp = self.VPB.decodeDiscreetProperties(clientid, msgin, slot, world) + for action in tmp: + actions.append(action) + return actions + + + def decodeDiscreetPropertiesVpbVpb(self, clientid, msgin, slot, world): + return self.VPB.VPB.decodeDiscreetProperties(clientid, msgin, slot, world)