From f66e4219e6332b196d324027f4190e42308b73b6 Mon Sep 17 00:00:00 2001 From: AleaJactaEst Date: Sun, 2 Jun 2019 15:45:55 +0200 Subject: [PATCH] decode message STRING_MANAGER:RELOAD_CACHE --- client.py | 702 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 670 insertions(+), 32 deletions(-) diff --git a/client.py b/client.py index 8e9b6e2..abb2a91 100755 --- a/client.py +++ b/client.py @@ -244,12 +244,19 @@ class BitStream(): tmp += x _size -= 1 return tmp + def readArrayUint8(self, size): ret = [] for i in range(0, size): ret.append(self.readUint8()) return ret + def readBitStreamUint8(self, size): + ret = BitStream() + for i in range(0, size): + ret.pushUint8(self.readUint8()) + return ret + # ------------------------------------ def __str__(self): return ''.join([ chr(x) for x in self._tampon]) @@ -280,6 +287,21 @@ class BitStream(): self._read = readBefore return ret + def showAllData(self): + ret = "" + readBefore = self._read + self._read = 0 + while self._read < self._pos: + if self._pos - self._read >= 8: + data = self.readUint8() + else: + data = self.readSerial(self._pos - self._read) + if ret != "": + ret += "." + ret += hex(data) + self._read = readBefore + return ret + def TestBitStream(): a = BitStream() a.pushBool(True) @@ -1581,6 +1603,496 @@ class CPersistentDataRecord: +class CActionBlock: + def __init__(self): + self.Cycle = 0 + self.FirstPacket = 0 + self.Actions = None + self.Sucess = True + + +def getPowerOf2(v): + res=1; + ret=0; + while res %d' % (key, id)) + ret.append(id) + head = ele + break + id += 1 + return ret + + def execute(self, msgin): + head = self.msgXml + listpath = [] + while True: + nbBit = getPowerOf2(len(head)) + #self.log.debug('nbBit:%d' % nbBit) + id = msgin.readSerial(nbBit) + #self.log.debug('id:%s' % str(id)) + ele = head[id] + name = ele.attrib['name'] + #self.log.debug(name) + listpath.append(name) + fullname = ':'.join(listpath) + if fullname in self.GenericMsgHeaderMngr: + self.log.debug("Found : %s" % fullname) + self.GenericMsgHeaderMngr[fullname](msgin) + return True + else: + #self.log.debug("Non trouve") + for ele in head: + if ele.attrib['name'] == name: + head = ele + break + if head != ele: + self.log.error("Impossible to found %s" % fullname ) + return False + return False + + + def loadMsg(self, msgXml): + self.msgXml = msgXml +# print('-'*80) +# print(self.msgXml) +# print(dir(self.msgXml)) +# print('-'*80) +# print(self.sizeElement()) +# print(self.sizeElement('STRING')) +# print('-'*20) +# print(self.searchElement("DB_UPD_PLR")) +# print(self.searchElement("STRING:TELL")) +# print('-'*80) +# self.GenericMsgHeaderMngr['DB_UPD_PLR']('test') +# print('-'*80) + + def loadDatabase(self, databaseXml): + self.databaseXml = databaseXml + class CAction: def __init__(self, slot=INVALID_SLOT, code=0): self.Code = code @@ -1633,30 +2145,51 @@ class CActionPosition(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionPosition" + super().__str__() + class CActionSync(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionSync" + super().__str__() + class CActionDisconnection(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionDisconnection" + super().__str__() + class CActionAssociation(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionAssociation" + super().__str__() + class CActionDummy(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionDummy" + super().__str__() + class CActionLogin(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionLogin" + super().__str__() + class CActionTargetSlot(CAction): def __init__(self, slot, code): super().__init__(slot, code) + def __str__(self): + return "CActionTargetSlot" + super().__str__() + class CActionGeneric(CAction): def __init__(self, slot, code): super().__init__(slot, code) @@ -1664,9 +2197,21 @@ class CActionGeneric(CAction): def unpack(self, message): size = message.readUint32() - self._Message = message.readArrayUint8(size) + self._Message = message.readBitStreamUint8(size) + + def reset(self): + self._Message = None + + def genericAction(self, decodeImpulse): + decodeImpulse.execute(self._Message) + + def __str__(self): + return "CActionGeneric" + super().__str__() + "[" + self._Message.showAllData() + ']' class CActionGenericMultiPart(CAction): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/action_generic_multi_part.h # class CActionGenericMultiPart + ''' def __init__(self, slot, code): super().__init__(slot, code) self.PartCont = [] @@ -1680,47 +2225,105 @@ class CActionGenericMultiPart(CAction): self.NbBlock = message.readUint16() size = message.readUint32() - self.PartCont = message.readArrayUint8(size) + self.PartCont = message.readBitStreamUint8(size) + + def reset(self): + self.PartCont = [] + self.Number = 0 + self.Part = 0 + self.NbBlock = 0 + + def genericAction(self, decodeImpulse): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::genericAction (CActionGenericMultiPart *agmp) + ''' + self.log = logging.getLogger('myLogger') + self.log.debug("Number:%d Part:%d NbBlock:%d" % (self.Number, self.Part, self.NbBlock)) + self.log.debug("TODO") + + def __str__(self): + return "CActionGenericMultiPart" + super().__str__() + "[" + str(self.Number) + ',' + str(self.Part) + ',' + str(self.NbBlock) + ',' + self.PartCont.showAllData() + ']' class CActionSint64(CAction): def __init__(self, slot, code): super().__init__(slot, code) -class CActionFactory: - def __init__(self): - self.RegisteredAction = [] + def __str__(self): + return "CActionSint64" + super().__str__() - def create(self, slot, code): +class CActionFactory: + def __init__(self, log): + self.log = log + self.RegisteredAction = {} + self.RegisteredAction.setdefault(TActionCode.ACTION_POSITION_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_GENERIC_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_GENERIC_MULTI_PART_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_SINT64, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_SYNC_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_DISCONNECTION_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_ASSOCIATION_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_LOGIN_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_TARGET_SLOT_CODE, []) + self.RegisteredAction.setdefault(TActionCode.ACTION_DUMMY_CODE, []) + + def createFactory(self, slot, code): if code == TActionCode.ACTION_POSITION_CODE: + self.log.debug("Create CActionPosition") return CActionPosition(slot, code) elif code == TActionCode.ACTION_GENERIC_CODE: + self.log.debug("Create CActionGeneric") return CActionGeneric(slot, code) elif code == TActionCode.ACTION_GENERIC_MULTI_PART_CODE: + self.log.debug("Create CActionGenericMultiPart") return CActionGenericMultiPart(slot, code) elif code == TActionCode.ACTION_SINT64: + self.log.debug("Create CActionSint64") return CActionSint64(slot, code) elif code == TActionCode.ACTION_SYNC_CODE: + self.log.debug("Create CActionSync") return CActionSync(slot, code) elif code == TActionCode.ACTION_DISCONNECTION_CODE: + self.log.debug("Create CActionDisconnection") return CActionDisconnection(slot, code) elif code == TActionCode.ACTION_ASSOCIATION_CODE: + self.log.debug("Create CActionAssociation") return CActionAssociation(slot, code) elif code == TActionCode.ACTION_LOGIN_CODE: + self.log.debug("Create CActionLogin") return CActionLogin(slot, code) elif code == TActionCode.ACTION_TARGET_SLOT_CODE: + self.log.debug("Create CActionTargetSlot") return CActionTargetSlot(slot, code) elif code == TActionCode.ACTION_DUMMY_CODE: + self.log.debug("Create CActionDummy") return CActionDummy(slot, code) else: log = logging.getLogger('myLogger') log.warning('create() try to create an unknown action (%u)' % code) raise RuntimeError + def create(self, slot, code): + if code not in self.RegisteredAction: + log = logging.getLogger('myLogger') + log.warning('try to create an unknown action (%u)' % code) + raise None + elif not self.RegisteredAction[code]: + self.log.debug("new CAction") + action = self.createFactory(slot, code) + action.reset() + return action + else: + self.log.debug("update CAction") + action = self.RegisteredAction[code][-1] + action.reset() + action.PropertyCode = code + action.Slot = slot + return action + def unpack(self, msgin): ''' khanat-opennel-code/code/ryzom/common/src/game_share/action_factory.cpp : CAction *CActionFactory::unpack (NLMISC::CBitMemStream &message, NLMISC::TGameCycle /* currentCycle */ ) ''' - actions = [] if msgin.needRead() >= 8: shortcode = msgin.readBool() if shortcode: @@ -1735,19 +2338,22 @@ class CActionFactory: log = logging.getLogger('myLogger') log.warning('Missing code to unpack (code :%u)' % code) raise RuntimeError - actions.append(action) else: log = logging.getLogger('myLogger') log.warning('Unpacking an action with unknown code, skip it (%u)' % code) - return actions + return action class CImpulseDecoder: ''' see : khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp ''' - def __init__(self): + def __init__(self, log): + self.log = log self.reset() - self._CActionFactory = CActionFactory() + self._CActionFactory = CActionFactory(log) + + def removeCAction(self, action): + self._CActionFactory.RegisteredAction[action.Code].append(action) def decode(self, msgin, receivedPacket, receivedAck, nextSentPacket): actions = [] @@ -1777,10 +2383,11 @@ class CImpulseDecoder: num += 1 action = self._CActionFactory.unpack(msgin) if keep: + self.log.debug("keep") actions.append(action) - else: - pass - + elif action: + self.log.debug("append") + self.removeCAction(action) return actions def reset(self): @@ -1836,13 +2443,16 @@ class ClientNetworkConnection: self._AckBitMask = 0 self._LongAckBitField = CBitSet() self._LatestSyncTime = 0 - self._ImpulseDecoder = CImpulseDecoder() + self._ImpulseDecoder = CImpulseDecoder(self.log) self._LongAckBitField.resize(1024) self._LatestProbeTime = 0 self._LatestProbe = 0 self._LatestProbes = [] self._LatestQuitTime = 0 self._ReceivedAckQuit = False + self._Actions = [] + self._PacketStamps = [] + self.decodeImpulse = DecodeImpulse(self.log) def signal_exit(self, sig, frame): self.log.warning("Receive signal to quit program") @@ -1940,18 +2550,6 @@ class ClientNetworkConnection: for _ in range(0, propertyCount): pass - def impulseCallBack(self, data): - # code/ryzom/common/src/game_share/generic_xml_msg_mngr.h : CNode *select(NLMISC::CBitMemStream &strm) - msg = BitStream() - msg.fromBytes(data) - serverTick = msg.readUint32() - self.log.debug("serverTick:%d" % serverTick) - #self.readDelta(msg) - - def impulseDecode(self, msgin): - actions = self._ImpulseDecoder.decode(msgin, self._CurrentReceivedNumber, self._LastReceivedAck, self._CurrentSendNumber ) - self.log.debug(str(actions)) - def buildStream(self, buffersize=65536): # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::buildStream( CBitMemStream &msgin ) data, addr = self._sock.recvfrom(buffersize) @@ -1996,11 +2594,11 @@ class ClientNetworkConnection: self._AckBitMask = 0x00000000 self._LastAckBit = ackBit; for i in range(self._LastReceivedNumber+1, self._CurrentReceivedNumber+1): - self._LongAckBitField.clearBit(i & 511) - self._LongAckBitField.set(self._CurrentReceivedNumber & 511, ackBool) + self._LongAckBitField.clearBit(i & 511) # (512 - 1) mask 9bit + self._LongAckBitField.set(self._CurrentReceivedNumber & 511, ackBool) # (512 - 1) mask 9bit if self._LastAckInLongAck <= (self._CurrentReceivedNumber-512): - self._LastAckInLongAck = self._CurrentReceivedNumber-511; + self._LastAckInLongAck = self._CurrentReceivedNumber-511; # (512 - 1) mask 9bit self._LastReceivedNumber = self._CurrentReceivedNumber self.log.debug("_CurrentReceivedNumber:%d, _LastReceivedNumber:%d, ackBit:%d, _AckBitMask:%d _LongAckBitField:%s" % (self._CurrentReceivedNumber, self._LastReceivedNumber, ackBit, self._AckBitMask, self._LongAckBitField)) @@ -2042,9 +2640,47 @@ class ClientNetworkConnection: self._CurrentClientTime = self._UpdateTime - (self._LCT + self._MsPerTick) self.sendSystemAckSync() + def decodeVisualProperties(self, msgin): + self.log.debug("TODO") + def receiveNormalMessage(self, msgin): - self.log.debug("received normal message Packet (%d)" % (msgin.needRead())) - self.impulseDecode(msgin) + self.log.debug("received normal message Packet (%d) %s" % (msgin.needRead(), msgin.showLastData() )) + actions = self._ImpulseDecoder.decode(msgin, self._CurrentReceivedNumber, self._LastReceivedAck, self._CurrentSendNumber ) + if actions: + self.log.debug('actions: ' +','.join( [ str(x) for x in actions] ) ) + else: + self.log.debug('actions: None') + self.log.debug("Message not read (%d) %s" % (msgin.needRead(), msgin.showLastData() )) + + # remove all old action that are acked + while self._Actions and self._Actions[0].FirstPacket != 0 and self._Actions[0].FirstPacket : + self._Actions.pop(0) + + self._CurrentServerTick = self._CurrentReceivedNumber * 2 + self._Synchronize + + ## remove useless stamps in queue + #while self._PacketStamps and self._LastReceivedAck > self._PacketStamps[0].first: + # self._PacketStamps.pop(0) + + # Decode the actions received in the impulsions + for action in actions: + if action.Code == TActionCode.ACTION_DISCONNECTION_CODE: + self.log.debug("Action : ACTION_DISCONNECTION_CODE") + self.disconnect() + elif action.Code == TActionCode.ACTION_GENERIC_CODE: + self.log.debug("Action : ACTION_GENERIC_CODE") + action.genericAction(self.decodeImpulse) + elif action.Code == TActionCode.ACTION_GENERIC_MULTI_PART_CODE: + self.log.debug("Action : ACTION_GENERIC_MULTI_PART_CODE") + action.genericAction(self.decodeImpulse) + elif action.Code == TActionCode.ACTION_DUMMY_CODE: + self.log.debug("Action : ACTION_DUMMY_CODE") + self._ImpulseDecoder.removeCAction(action) + + # Decode the visual properties + self.decodeVisualProperties( msgin ); + + self._LastReceivedNormalTime = self._UpdateTime; def receiveSystemAckQuit(self, msgin): self.log.debug("received ACK_QUIT") @@ -2291,6 +2927,8 @@ class ClientNetworkConnection: self._MsgXmlMD5 = getTextMD5(msgRawXml) self._DatabaseXmlMD5 = getTextMD5(databaseRawXml) + self.decodeImpulse.loadMsg(self.msgXml) + self.decodeImpulse.loadDatabase(self.databaseXml) self.connect() self.log.info("Client Login") self.sendSystemLogin()