diff --git a/client.py b/client.py index abb2a91..50d06f4 100755 --- a/client.py +++ b/client.py @@ -52,6 +52,10 @@ class BitStream(): self._pos = 0 self._read = 0 self._tampon = [] + self._groupRead = [] + + def __len__(self): + return (self._pos + 7) // 8 def needRead(self): return self._pos - self._read @@ -62,6 +66,17 @@ class BitStream(): def sizeRead(self): return self._read + def getRead(self): + return self._read + + def getPos(self): + return self._pos + + def putRead(self, value): + if value > self._pos: + raise ValueError + self._read = value + # ------------------------------------ def internalSerial(self, value, nbits): if nbits == 0: @@ -153,14 +168,31 @@ class BitStream(): for i in valeur: self.pushUint8(i) + def pushBitStream(self, source): + srcRead = source.getRead() + source.putRead(0) + need = 8 - (self._pos % 8) + if need != 8: + self.internalSerial(source.readSerial(need), need) + while source.needRead() >= 8: + self.pushUint8(source.readUint8()) + + need = source.needRead() + if need > 0: + self.internalSerial(source.readSerial(need), need) + + source.putRead(srcRead) + # ------------------------------------ - def readSerial(self, nbits): + def readSerial(self, nbits, decode=True): if nbits == 0: return elif nbits > 32: raise "Out of range" if self._read + nbits > self._pos: raise "Stream Overflow" + if decode: + self._groupRead.append((self._read, self._read+nbits)) value = 0 pos = self._read // 8 _FreeBits = 8 - (self._read % 8) @@ -168,7 +200,7 @@ class BitStream(): if nbits > _FreeBits: value |= (v << (nbits-_FreeBits)) self._read += _FreeBits - value |= self.readSerial(nbits - _FreeBits) + value |= self.readSerial(nbits - _FreeBits, False) else: value |= (v >> (_FreeBits-nbits)) self._read += nbits @@ -262,8 +294,8 @@ class BitStream(): return ''.join([ chr(x) for x in self._tampon]) def message(self): - # return str(self._pos) + ':' + str(self._tampon) - return str(self._pos) + ':' + '.'.join([ format(x, "02x") for x in self._tampon]) + # return str(self._pos) + ':' + '.'.join([ format(x, "02x") for x in self._tampon]) + return str(self._pos) + ':' + '.'.join([ "{0:08b}".format(x) for x in self._tampon]) def toBytes(self): return bytes( self._tampon ) @@ -283,11 +315,33 @@ class BitStream(): data = self.readSerial(self._pos - self._read) if ret != "": ret += "." - ret += hex(data) + #ret += hex(data) + ret += "{0:08b}".format(data) 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.readSerial(8, False) + else: + data = self.readSerial(self._pos - self._read, False) + ret += "{0:08b}".format(data) + self._read = readBefore + ret2 = "" + + last = 0 + for x, y in self._groupRead: + ret2 += "[" + ret[x:y] + "]" + last = y + if last < self._pos: + ret2 += "{" + ret[last:] + "}" + + return ret2 + def showAllDataBis(self): ret = "" readBefore = self._read self._read = 0 @@ -298,8 +352,10 @@ class BitStream(): data = self.readSerial(self._pos - self._read) if ret != "": ret += "." - ret += hex(data) + #ret += hex(data) + ret += "{0:08b}".format(data) self._read = readBefore + ret += ' ' + '.'.join([ str(x) + ':' + str(y) for x, y in self._groupRead] ) return ret def TestBitStream(): @@ -359,6 +415,49 @@ def TestBitStream(): print(b.readChar()) print(b.readString()) print(b.toBytes()) + print("-" * 80) + c = BitStream() + c.pushBool(True) + c.pushBitStream(a) + c.pushBitStream(b) + print(c.readBool()) + print("-" * 80) + print(c.readBool()) + print(c.readBool()) + print(c.readBool()) + print(c.readBool()) + print(c.readUint32()) + print(c.readSint32()) + print(c.readUint16()) + print(c.readSint16()) + print(c.readUint8()) + print(c.readSint8()) + print(c.readFloat()) + print(c.readDouble()) + print(c.readUint64()) + print(c.readSint64()) + print(c.readChar()) + print(c.readString()) + print(c.toBytes()) + print("-" * 80) + print(c.readBool()) + print(c.readBool()) + print(c.readBool()) + print(c.readBool()) + print(c.readUint32()) + print(c.readSint32()) + print(c.readUint16()) + print(c.readSint16()) + print(c.readUint8()) + print(c.readSint8()) + print(c.readFloat()) + print(c.readDouble()) + print(c.readUint64()) + print(c.readSint64()) + print(c.readChar()) + print(c.readString()) + print(c.toBytes()) + NL_BITLEN = 32 class CBitSet: def __init__(self): @@ -935,6 +1034,61 @@ class CArg: # # ##################################################### +class CGenericMultiPartTemp(): + def __init__(self, log): + self.log = log + self.NbBlock = 0xFFFFFFFF + self.NbCurrentBlock = 0 + self.TempSize = 0 + self.Temp = [] + self.BlockReceived = [] + + def set(self, Number, Part, NbBlock, PartCont): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::CGenericMultiPartTemp::set (CActionGenericMultiPart *agmp, CNetworkConnection *parent) + ''' + if self.NbBlock == 0xFFFFFFFF: + # Initialize + self.NbBlock = NbBlock + self.NbCurrentBlock = 0 + self.TempSize = 0 + self.Temp = [] + while len(self.Temp) < NbBlock: + self.Temp.append(None) + while len(self.BlockReceived) < NbBlock: + self.BlockReceived.append(False) + if self.BlockReceived[Part]: + self.log.warning('This part is already received, discard it %d' % Part) + return + self.Temp[Part] = PartCont + self.BlockReceived[Part] = True + self.NbCurrentBlock += 1 + self.TempSize += len(PartCont) + + self.log.debug("NbCurrentBlock:%d / NbBlock:%d" % (self.NbCurrentBlock, self.NbBlock)) + if self.NbCurrentBlock == self.NbBlock: + # reform the total action + bms = BitStream() + + self.NbBlock == 0xFFFFFFFF + for data in self.Temp: + bms.pushBitStream(data) + self.log.debug("data : %s" % bms.showAllData()) + self.log.debug("*" * 80) + + +class World(): + def __init__(self, log): + self.log = log + self.GenericMultiPartTemp = {} + self.timestamp = 0 + + def addGenericMultiPartTemp(self, id): + self.GenericMultiPartTemp.setdefault(id, CGenericMultiPartTemp(self.log)) + + def setGenericMultiPartTemp(self, Number, Part, NbBlock, PartCont): + self.GenericMultiPartTemp[Number].set(Number, Part, NbBlock, PartCont) + class CPersistentDataRecord: def __init__(self, log): self.log = log @@ -1620,7 +1774,7 @@ def getPowerOf2(v): return ret class DecodeImpulse(): - def __init__(self, log): + def __init__(self, log, world): ''' khanat-opennel-code/code/ryzom/client/src/net_manager.cpp # void initializeNetwork() ''' @@ -1629,6 +1783,7 @@ class DecodeImpulse(): self.databaseXml = None self.GenericMsgHeaderMngr = {} self.initializeNetwork() + self.world = world def impulseDatabaseUpdatePlayer(self, msgin): self.log.debug("TODO:%s" % msgin) @@ -1679,7 +1834,7 @@ class DecodeImpulse(): self.log.debug("TODO") def impulseDynString(self, msgin): self.log.debug("TODO") - def inpulseDynStringInChatGroup(self, msgin): + def inpulseDynStringInChatGroup(self, msgin): self.log.debug("TODO") def impulseTell2(self, msgin): self.log.debug("TODO") @@ -1735,8 +1890,8 @@ class DecodeImpulse(): def impulseStringResp(self, msgin): self.log.debug("TODO") def impulseReloadCache(self, msgin): - timestamp = msgin.readUint32() - self.log.debug("Reload Cache timestamp:%d" % timestamp) + self.world.timestamp = msgin.readUint32() + self.log.debug("Reload Cache timestamp:%d" % self.world.timestamp) self.log.debug("Message not read (%d) %s" % (msgin.needRead(), msgin.showLastData() )) def impulseBotChatForceEnd(self, msgin): @@ -1841,11 +1996,11 @@ class DecodeImpulse(): self.log.debug("TODO") - def cbImpulsionGatewayOpen(self, msgin): + def cbImpulsionGatewayOpen(self, msgin): self.log.debug("TODO") def cbImpulsionGatewayMessage (self, msgin): self.log.debug("TODO") - def cbImpulsionGatewayClose (self, msgin): + def cbImpulsionGatewayClose (self, msgin): self.log.debug("TODO") def impulseOutpostChooseSide (self, msgin): @@ -1902,7 +2057,7 @@ class DecodeImpulse(): self.GenericMsgHeaderMngr.setdefault("STRING:FAR_TELL", self.impulseFarTell) self.GenericMsgHeaderMngr.setdefault("STRING:CHAT2", self.impulseChat2) self.GenericMsgHeaderMngr.setdefault("STRING:DYN_STRING", self.impulseDynString) - self.GenericMsgHeaderMngr.setdefault("STRING:DYN_STRING_GROUP", self.inpulseDynStringInChatGroup) + self.GenericMsgHeaderMngr.setdefault("STRING:DYN_STRING_GROUP", self.inpulseDynStringInChatGroup) self.GenericMsgHeaderMngr.setdefault("STRING:TELL2", self.impulseTell2) self.GenericMsgHeaderMngr.setdefault("TP:DEST", self.impulseTP) @@ -2001,7 +2156,7 @@ class DecodeImpulse(): self.GenericMsgHeaderMngr.setdefault( "MODULE_GATEWAY:FEOPEN", self.cbImpulsionGatewayOpen) self.GenericMsgHeaderMngr.setdefault( "MODULE_GATEWAY:GATEWAY_MSG", self.cbImpulsionGatewayMessage ) - self.GenericMsgHeaderMngr.setdefault( "MODULE_GATEWAY:FECLOSE", self.cbImpulsionGatewayClose ) + self.GenericMsgHeaderMngr.setdefault( "MODULE_GATEWAY:FECLOSE", self.cbImpulsionGatewayClose ) self.GenericMsgHeaderMngr.setdefault( "OUTPOST:CHOOSE_SIDE", self.impulseOutpostChooseSide ) self.GenericMsgHeaderMngr.setdefault( "OUTPOST:DECLARE_WAR_ACK", self.impulseOutpostDeclareWarAck ) @@ -2094,13 +2249,14 @@ class DecodeImpulse(): self.databaseXml = databaseXml class CAction: - def __init__(self, slot=INVALID_SLOT, code=0): + def __init__(self, slot, code, world): self.Code = code self.PropertyCode = code self.Slot = slot self._Priority = 1 self.Timeout = 0 self.GameCycle = 0 + self.world = world def unpack(self, message): raise RuntimeError @@ -2142,57 +2298,57 @@ class CAction: return "[%d,%d]" % (self.Code , self.Slot) class CActionPosition(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionPosition" + super().__str__() class CActionSync(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionSync" + super().__str__() class CActionDisconnection(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionDisconnection" + super().__str__() class CActionAssociation(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionAssociation" + super().__str__() class CActionDummy(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionDummy" + super().__str__() class CActionLogin(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionLogin" + super().__str__() class CActionTargetSlot(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionTargetSlot" + super().__str__() class CActionGeneric(CAction): - def __init__(self, slot, code): - super().__init__(slot, code) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) self._Message = None def unpack(self, message): @@ -2212,8 +2368,8 @@ 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) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) self.PartCont = [] self.Number = 0 self.Part = 0 @@ -2239,21 +2395,23 @@ class CActionGenericMultiPart(CAction): ''' self.log = logging.getLogger('myLogger') self.log.debug("Number:%d Part:%d NbBlock:%d" % (self.Number, self.Part, self.NbBlock)) - self.log.debug("TODO") + self.world.addGenericMultiPartTemp(self.Number) + self.world.setGenericMultiPartTemp(self.Number, self.Part, self.NbBlock, self.PartCont) 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) + def __init__(self, slot, code, world): + super().__init__(slot, code, world) def __str__(self): return "CActionSint64" + super().__str__() class CActionFactory: - def __init__(self, log): + def __init__(self, log, world): self.log = log + self.world = world self.RegisteredAction = {} self.RegisteredAction.setdefault(TActionCode.ACTION_POSITION_CODE, []) self.RegisteredAction.setdefault(TActionCode.ACTION_GENERIC_CODE, []) @@ -2269,34 +2427,34 @@ class CActionFactory: def createFactory(self, slot, code): if code == TActionCode.ACTION_POSITION_CODE: self.log.debug("Create CActionPosition") - return CActionPosition(slot, code) + return CActionPosition(slot, code, self.world) elif code == TActionCode.ACTION_GENERIC_CODE: self.log.debug("Create CActionGeneric") - return CActionGeneric(slot, code) + return CActionGeneric(slot, code, self.world) elif code == TActionCode.ACTION_GENERIC_MULTI_PART_CODE: self.log.debug("Create CActionGenericMultiPart") - return CActionGenericMultiPart(slot, code) + return CActionGenericMultiPart(slot, code, self.world) elif code == TActionCode.ACTION_SINT64: self.log.debug("Create CActionSint64") - return CActionSint64(slot, code) + return CActionSint64(slot, code, self.world) elif code == TActionCode.ACTION_SYNC_CODE: self.log.debug("Create CActionSync") - return CActionSync(slot, code) + return CActionSync(slot, code, self.world) elif code == TActionCode.ACTION_DISCONNECTION_CODE: self.log.debug("Create CActionDisconnection") - return CActionDisconnection(slot, code) + return CActionDisconnection(slot, code, self.world) elif code == TActionCode.ACTION_ASSOCIATION_CODE: self.log.debug("Create CActionAssociation") - return CActionAssociation(slot, code) + return CActionAssociation(slot, code, self.world) elif code == TActionCode.ACTION_LOGIN_CODE: self.log.debug("Create CActionLogin") - return CActionLogin(slot, code) + return CActionLogin(slot, code, self.world) elif code == TActionCode.ACTION_TARGET_SLOT_CODE: self.log.debug("Create CActionTargetSlot") - return CActionTargetSlot(slot, code) + return CActionTargetSlot(slot, code, self.world) elif code == TActionCode.ACTION_DUMMY_CODE: self.log.debug("Create CActionDummy") - return CActionDummy(slot, code) + return CActionDummy(slot, code, self.world) else: log = logging.getLogger('myLogger') log.warning('create() try to create an unknown action (%u)' % code) @@ -2347,15 +2505,17 @@ class CImpulseDecoder: ''' see : khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp ''' - def __init__(self, log): + def __init__(self, log, world): self.log = log + self.world = world self.reset() - self._CActionFactory = CActionFactory(log) + self._CActionFactory = CActionFactory(log, world) def removeCAction(self, action): self._CActionFactory.RegisteredAction[action.Code].append(action) def decode(self, msgin, receivedPacket, receivedAck, nextSentPacket): + self.log.debug("receivedPacket:%d receivedAck:%d nextSentPacket:%d" %(receivedPacket, receivedAck, nextSentPacket)) actions = [] for level in range(0, 3): if level == 0: @@ -2370,6 +2530,7 @@ class CImpulseDecoder: keep = True checkOnce = False num = 0 + self.log.debug("channel:%d lAck:%s" %(channel, ':'.join([str(x) for x in lAck]))) # lastAck = lAck[channel] while True: next = msgin.readBool() @@ -2378,8 +2539,10 @@ class CImpulseDecoder: if not checkOnce: checkOnce = True keep = receivedAck >= lAck[channel] + self.log.debug("keep:%s" % str(keep)) if keep: lAck[channel] = nextSentPacket + self.log.debug("lAck:%s" % ':'.join([str(x) for x in lAck])) num += 1 action = self._CActionFactory.unpack(msgin) if keep: @@ -2443,7 +2606,8 @@ class ClientNetworkConnection: self._AckBitMask = 0 self._LongAckBitField = CBitSet() self._LatestSyncTime = 0 - self._ImpulseDecoder = CImpulseDecoder(self.log) + self.world = World(self.log) + self._ImpulseDecoder = CImpulseDecoder(self.log, self.world) self._LongAckBitField.resize(1024) self._LatestProbeTime = 0 self._LatestProbe = 0 @@ -2452,7 +2616,7 @@ class ClientNetworkConnection: self._ReceivedAckQuit = False self._Actions = [] self._PacketStamps = [] - self.decodeImpulse = DecodeImpulse(self.log) + self.decodeImpulse = DecodeImpulse(self.log, self.world) def signal_exit(self, sig, frame): self.log.warning("Receive signal to quit program") @@ -2483,6 +2647,7 @@ class ClientNetworkConnection: def buildSystemHeader(self, msgout): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout) msgout.pushSint32(self._CurrentSendNumber) msgout.pushBool(True) # systemMode + self._CurrentSendNumber += 1 def sendSystemLogin(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemLogin() if self._sock is None: @@ -2564,12 +2729,12 @@ class ClientNetworkConnection: if self.checkMessageNumber and self._CurrentReceivedNumber > self._LastReceivedPacketInBothModes: self._TotalLostPackets += self._CurrentReceivedNumber - self._LastReceivedPacketInBothModes - 1 self._LastReceivedPacketInBothModes = self._CurrentReceivedNumber - else: - self._LastReceivedPacketInBothModes = self._CurrentReceivedNumber - 1 +# else: +# self._LastReceivedPacketInBothModes = self._CurrentReceivedNumber - 1 if not self._SystemMode: - self.log.debug("Normal Mode") self._LastReceivedAck = msg.readSint32(); + self.log.debug("Normal Mode _LastReceivedAck:%d" % self._LastReceivedAck) else: self.log.debug("System Mode") @@ -2891,9 +3056,9 @@ class ClientNetworkConnection: stateBroke = True while stateBroke: buffer, addr = self.buildStream() - self.log.debug("received message: %s" % buffer) msgin = BitStream() msgin.fromBytes(buffer) + self.log.debug("received message: %s" % msgin.showAllData()) if self._ConnectionState == TConnectionState.Login: self.log.debug("state:Login") stateBroke = self.stateLogin(msgin) @@ -2914,6 +3079,7 @@ class ClientNetworkConnection: stateBroke = self.stateQuit(msgin) else: stateBroke = False + self.log.debug("message decoded: %s" % msgin.showAllData()) counterLoop += 1 if counterLoop > 10: break