diff --git a/spykhanat.py b/spykhanat.py index b7560f0..b42a4b8 100755 --- a/spykhanat.py +++ b/spykhanat.py @@ -157,10 +157,12 @@ class SpyPcap(): 'world': World.World(), 'GenericMultiPartTemp': CGenericMultiPartTemp.GenericMultiPartTemp(), 'CImpulseDecoder': None, + 'CActionFactory': None, 'LastAck0': [-1], 'LastAck1': [-1, -1], 'LastAck2': [-1, -1, -1, -1]}) self.client_state[clientid]['CImpulseDecoder'] = CImpulseDecoder.CImpulseDecoder(self.client_state[clientid]['world']) + self.client_state[clientid]['CActionFactory'] = CActionFactory.CActionFactory(self.client_state[clientid]['world']) def add_registered_action(self, clientid, action): self.client_state[clientid]['RegisteredAction'].setdefault(action.Code, []) @@ -208,11 +210,37 @@ class SpyPcap(): self.add_registered_action(clientid, action) return actions - def decode_client_receive_normal_message(self, msgin, clientid, dst): - actions = self.client_state[clientid]['CImpulseDecoder'].decode(msgin, self.client_state[clientid]['CurrentReceivedNumber'], self.client_state[clientid]['LastReceivedAck'], self.client_state[clientid]['CurrentSendNumber'] ) - logging.getLogger(LOGGER).info("[Client -> Khanat] actions:%s" % str(actions)) - # decodeVisualProperties( msgin ); +# def decode_client_receive_normal_message(self, msgin, clientid, dst): +# actions = self.client_state[clientid]['CImpulseDecoder'].decode(msgin, self.client_state[clientid]['CurrentReceivedNumber'], self.client_state[clientid]['LastReceivedAck'], self.client_state[clientid]['CurrentSendNumber'] ) +# logging.getLogger(LOGGER).info("[Client -> Khanat] actions:%s" % str(actions)) +# # decodeVisualProperties( msgin ); + def decode_client_send_normal_message(self, msgin, clientid, dst): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:2029 void CNetworkConnection::sendNormalMessage() + khanat-opennel-code/code/ryzom/common/src/game_share/action_block.cpp:36 void CActionBlock::serial(CBitMemStream &msg) + ''' + #actions = self.client_state[clientid]['CImpulseDecoder'].decode(msgin, self.client_state[clientid]['CurrentReceivedNumber'], self.client_state[clientid]['LastReceivedAck'], self.client_state[clientid]['CurrentSendNumber'] ) + actions = [] + noerror = True + msgin.disable_LogErrorOnStreamOverflow() + while noerror: + try: + cycle = msgin.readUint32("Cycle") + num = msgin.readUint8("num") + logging.getLogger(LOGGER).debug("[Client -> Khanat] Cycle:%u num:%u" % (cycle, num)) + for i in range(0, num): + actions.append(self.client_state[clientid]['CActionFactory'].unpack(msgin)) + pass + except BitStream.OverflowError: + noerror = False + except Exception as e: + logging.getLogger(LOGGER).debug("[Client -> Khanat] end %s (not read:%u)" % (e.__class__, msgin.needRead())) + raise e + msgin.enable_LogErrorOnStreamOverflow() + + logging.getLogger(LOGGER).debug("[Client -> Khanat] detail actions [%d] => %s" % ( len(actions), ', '.join([str(x) for x in actions]))) + # decodeVisualProperties( msgin ); def decode_client_message(self, msgin, clientid, dst): CurrentReceivedNumber = msgin.readSint32('CurrentReceivedNumber') @@ -230,7 +258,7 @@ class SpyPcap(): self.client_state[clientid]['AckBitMask'] = AckBitMask logging.getLogger(LOGGER).info("[Client -> Khanat] Normal Mode {CurrentReceivedNumber:%d, src:%s, dst:%s, LastReceivedAck:%d}" % (CurrentReceivedNumber, clientid, dst, LastReceivedAck)) #self.decode_server(msgin, _CurrentReceivedNumber, _CurrentReceivedNumber-1) - self.decode_client_receive_normal_message(msgin, clientid, dst) + self.decode_client_send_normal_message(msgin, clientid, dst) else: message = msgin.readUint8('message') try: @@ -376,7 +404,10 @@ class SpyPcap(): logging.getLogger(LOGGER).debug("[%s] (message received) [%s] %s" % (_provenance, datetime.fromtimestamp(pkt.timestamp).strftime("%Y/%m/%d %H:%M:%S"), msgin.showAllData())) self.decode_client_message(msgin, src, dst) if self.show_message_decoded: - logging.getLogger(LOGGER).debug("[%s] %s" % (_provenance, msgin.showAllData())) + moredata='(message decoded)' + if not msgin.checkOnlyZeroAtEnd(): # msgin.needRead() > 7: + moredata = "(message decoded: not full)" + logging.getLogger(LOGGER).debug("[%s] %s %s" % (_provenance, moredata, msgin.showAllData())) for client in self.client_state: logging.getLogger(LOGGER).debug("%s [server tick:%d, client tick:%d]" %(client, self.client_state[client]['CurrentSendNumber'], self.client_state[client]['CurrentReceivedNumber'])) @@ -393,6 +424,7 @@ def main(): logger.append(logging.getLogger(CStringManager.LOGGER)) logger.append(logging.getLogger(CAction.LOGGER)) logger.append(logging.getLogger(CActionFactory.LOGGER)) + logger.append(logging.getLogger(BitStream.LOGGER)) CImpulseDecoder # logger.append(logging.getLogger('CGenericMultiPartTemp')) diff --git a/tools/BitStream.py b/tools/BitStream.py index 26523cd..d0690c1 100644 --- a/tools/BitStream.py +++ b/tools/BitStream.py @@ -18,11 +18,13 @@ # 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 +LOGGER='BitStream' class OverflowError(Exception): pass @@ -34,6 +36,7 @@ class BitStream(): self._tampon = [] self._groupRead = [] self._groupWrite = [] + self._CheckStreamOverflow = True def __len__(self): return (self._pos + 7) // 8 @@ -47,6 +50,12 @@ class BitStream(): ret._groupWrite = copy.deepcopy(self._groupWrite, memo) return ret + def enable_LogErrorOnStreamOverflow(self): + self._CheckStreamOverflow = True + + def disable_LogErrorOnStreamOverflow(self): + self._CheckStreamOverflow = False + def needRead(self): return self._pos - self._read @@ -515,7 +524,8 @@ class BitStream(): elif nbits > 32: raise "Out of range" if self._read + nbits > self._pos: - print("Error: Stream Overflow - nbits:%d/%d name:'%s' decode:'%s' typeName:'%s' emulate:%s msg:%s" %(nbits, self._pos-self._read, name, str(decode), typeName, str(emulate), self.showAllData()), file=sys.stderr) + if self._CheckStreamOverflow: + logging.getLogger(LOGGER).error("Error: Stream Overflow - nbits:%d/%d name:'%s' decode:'%s' typeName:'%s' emulate:%s msg:%s" % (nbits, self._pos-self._read, name, str(decode), typeName, str(emulate), self.showAllData())) raise OverflowError if emulate: oldRead = self._read @@ -855,6 +865,19 @@ class BitStream(): self._read = readBefore return ret + def checkOnlyZeroAtEnd(self): + readBefore = self._read + while self._read < self._pos: + if self._pos - self._read >= 8: + nsize = 8 + else: + nsize = self._pos - self._read + data = self.readSerial(nsize, decode=False) + if data > 0: + return False + self._read = readBefore + return True + def getPosInBit(self): return self._pos diff --git a/tools/CAction.py b/tools/CAction.py index dfbe01e..b98557c 100644 --- a/tools/CAction.py +++ b/tools/CAction.py @@ -359,6 +359,15 @@ class CActionBlock: for action in self.Actions: action.pack(msgout) + def readSerial(self, msgin): + actions = [] + _cycle = msgin.readUint32("Cycle") + _numberActions = msgin.readUint8("numberActions") + logging.getLogger(LOGGER).debug("_cycle:%d _numberActions:%d" % (_cycle, _numberActions)) + for i in range(0,_numberActions): + actions.append( msgin.unpack(msgin) ) + return actions + def insert(self, actions, begin, end): for i in range(0, end): if i>= begin: @@ -370,6 +379,7 @@ class CActionBlock: def push_back(self, action): self.Actions.append(action) + def __str__(self): return "CActionBlock [Cycle:" + str(self.Cycle) + ', FirstPacket:' + str(self.FirstPacket) + ', Data:' + ', '.join([ str(x) for x in self.Actions]) + "]"