mirror of
https://port.numenaute.org/aleajactaest/clientbot.git
synced 2024-11-24 16:16:17 +00:00
decode visual properties
This commit is contained in:
parent
317fd2cf71
commit
9755a2941b
8 changed files with 486 additions and 49 deletions
130
spykhanat.py
130
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,56 +201,111 @@ 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 )
|
||||
"""
|
||||
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")
|
||||
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, self.client_state[clientid]['world'])
|
||||
cActionOrientation.PropertyCode = Enum.TPropIndex.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):
|
||||
"""
|
||||
|
@ -282,7 +343,12 @@ class SpyPcap():
|
|||
pass
|
||||
num += 1
|
||||
#actionFactory = CAction.CActionFactory(None)
|
||||
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:
|
||||
|
@ -292,8 +358,8 @@ class SpyPcap():
|
|||
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
|
||||
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:
|
||||
|
@ -853,9 +946,10 @@ def main():
|
|||
#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()
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
|
252
tools/TVPNodeBase.py
Normal file
252
tools/TVPNodeBase.py
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
Loading…
Reference in a new issue