From 3121a918bbc6d752d6e8347ee946375e1578ed4e Mon Sep 17 00:00:00 2001 From: AleaJactaEst Date: Thu, 24 Oct 2019 19:45:24 +0200 Subject: [PATCH] adding spy tools & update clientbot --- client.py | 4903 +----------------------------- spykhanat.py | 416 +++ tools/BitStream.py | 980 ++++++ tools/CAction.py | 375 +++ tools/CActionFactory.py | 117 + tools/CArg.py | 319 ++ tools/CBNPCategorySet.py | 33 + tools/CBNPFile.py | 32 + tools/CBNPFileVersion.py | 32 + tools/CBitSet.py | 110 + tools/CCharacterSummary.py | 146 + tools/CFileChild.py | 28 + tools/CFileContainer.py | 79 + tools/CFileList.py | 34 + tools/CGenericMultiPartTemp.py | 91 + tools/CI18N.py | 31 + tools/CImpulseDecoder.py | 89 + tools/CMainlandSummary.py | 39 + tools/CPersistentDataRecord.py | 695 +++++ tools/CSessionId.py | 28 + tools/CShardName.py | 25 + tools/CStringManager.py | 153 + tools/ClientNetworkConnection.py | 853 ++++++ tools/CodeMsgXml.py | 44 + tools/DecodeImpulse.py | 573 ++++ tools/Enum.py | 565 ++++ tools/TExtendType.py | 24 + tools/TMessageType.py | 27 + tools/TPVPMode.py | 39 + tools/TPropIndex.py | 91 + tools/TStampQueue.py | 26 + tools/TStreamFormat.py | 26 + tools/World.py | 171 ++ tools/__init__.py | 0 tools/convertUStringToString.py | 27 + tools/getPowerOf2.py | 28 + 36 files changed, 6431 insertions(+), 4818 deletions(-) create mode 100755 spykhanat.py create mode 100644 tools/BitStream.py create mode 100644 tools/CAction.py create mode 100644 tools/CActionFactory.py create mode 100644 tools/CArg.py create mode 100644 tools/CBNPCategorySet.py create mode 100644 tools/CBNPFile.py create mode 100644 tools/CBNPFileVersion.py create mode 100644 tools/CBitSet.py create mode 100644 tools/CCharacterSummary.py create mode 100644 tools/CFileChild.py create mode 100644 tools/CFileContainer.py create mode 100644 tools/CFileList.py create mode 100644 tools/CGenericMultiPartTemp.py create mode 100644 tools/CI18N.py create mode 100644 tools/CImpulseDecoder.py create mode 100644 tools/CMainlandSummary.py create mode 100644 tools/CPersistentDataRecord.py create mode 100644 tools/CSessionId.py create mode 100644 tools/CShardName.py create mode 100644 tools/CStringManager.py create mode 100644 tools/ClientNetworkConnection.py create mode 100644 tools/CodeMsgXml.py create mode 100644 tools/DecodeImpulse.py create mode 100644 tools/Enum.py create mode 100644 tools/TExtendType.py create mode 100644 tools/TMessageType.py create mode 100644 tools/TPVPMode.py create mode 100644 tools/TPropIndex.py create mode 100644 tools/TStampQueue.py create mode 100644 tools/TStreamFormat.py create mode 100644 tools/World.py create mode 100644 tools/__init__.py create mode 100644 tools/convertUStringToString.py create mode 100644 tools/getPowerOf2.py diff --git a/client.py b/client.py index 3ee1674..caa90ed 100755 --- a/client.py +++ b/client.py @@ -33,4791 +33,39 @@ import sys import urllib.request import urllib.parse import tempfile -from enum import IntEnum -from ctypes import * +#from enum import IntEnum +#from ctypes import * import re import random import lzma -import socket -import struct -import xml.etree.ElementTree as ET -import hashlib -import time -import signal -import inspect - - -INVALID_SLOT = 0xff - -def convertUStringToString(name): - ret = '' - for car in name: - if ord(car) >= 32 and ord(car)<=127: - ret += car - return ret - -class BitStream(): - def __init__(self): - self._pos = 0 - self._read = 0 - self._tampon = [] - self._groupRead = [] - self._groupWrite = [] - - def __len__(self): - return (self._pos + 7) // 8 - - def needRead(self): - return self._pos - self._read - - def sizeData(self): - return self._pos - - def sizeRead(self): - return self._read - - def getRead(self): - return self._read - - def getPos(self): - return self._pos - - def buffer(self): - return self._tampon - - def putRead(self, value): - if value > self._pos: - raise ValueError - self._read = value - - # ------------------------------------ - def internalSerial(self, value, nbits, decode=True, typeName=''): - p1 = self._pos - if nbits == 0: - return - elif nbits > 32: - raise "Out of range" - pos = self._pos % 8 - if pos == 0: - self._tampon.append(0) - value = c_uint32(value).value - if nbits != 32: - mask = (1 << nbits) - 1; - v = value & mask; - else: - v = value; - _FreeBits = 8 - (self._pos % 8) - if nbits > _FreeBits: - self._tampon[-1] |= (v >> ( nbits - _FreeBits)) - self._pos += _FreeBits - self.internalSerial( v , nbits - _FreeBits, decode=False) - else: - self._tampon[-1] |= (v << ( _FreeBits - nbits)) - self._pos += nbits - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+nbits, name, typeName, value)) - - def pushBool(self, value, decode=True): - p1 = self._pos - if value: - v = 1 - else: - v = 0 - self.internalSerial(v, 1, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+1, name, 'Bool', value)) - - def pushUint32(self, value, decode=True, typeName='Uint32'): - p1 = self._pos - self.internalSerial(value, 32, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+32, name, typeName, value)) - - def pushSint32(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 32, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+32, name, 'Sint32', value)) - - def pushUint16(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 16, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+16, name, 'Uint16', value)) - - def pushSint16(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 16, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+16, name, 'Sint16', value)) - - def pushUint8(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 8, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+8, name, 'Uint8', value)) - - def pushSint8(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 8, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+8, name, 'Sint8', value)) - - def pushUint64(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 32, decode=False) - self.internalSerial(value >> 32, 32, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+64, name, 'Uint64', value)) - - def pushSint64(self, value, decode=True): - p1 = self._pos - self.internalSerial(value, 32, decode=False) - self.internalSerial(value >> 32, 32, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+64, name, 'Sint64', value)) - - def pushFloat(self, value, decode=True): - p1 = self._pos - v = c_float(value).value - v1 = struct.pack('f', v) - v2 = struct.unpack('> 32, 32, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+64, name, 'Float', value)) - - def pushChar(self, value, decode=True): - p1 = self._pos - v = ord(value) - self.internalSerial(v, 8, decode=False) - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p1+8, name, 'Char', value)) - - def pushString(self, value, decode=True): - lenValue = len(value) - self.pushUint32(lenValue, decode=True, typeName='String:len') - p1 = self._pos - for _char in value: - self.pushChar(_char, decode=False) - p2 = self._pos - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p2, name, 'String', value)) - - def pushUString(self, value, decode=True): - lenValue = len(value) - self.pushUint32(lenValue, decode=True, typeName='UString:len') - p1 = self._pos - for x in value: - _char16bit = ord(x) - self.internalSerial(_char16bit, 16, decode=False) - p2 = self._pos - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p2, name, 'UString', value)) - - def pushArrayUint8(self, value, decode=True): - ' ex.: pushArrayChar([0,1,3,4]) ' - p1 = self._pos - for i in value: - self.pushUint8(i, decode=False) - p2 = self._pos - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p2, name, 'ArrayUint8', value)) - - def pushBuffer(self, source, decode=True): - ''' - Push BitStream with all byte (extend to byte if necessary) - ''' - p1 = self._pos - srcRead = source.getRead() - source.putRead(0) - for ele in source._tampon: - self.pushUint8(ele, decode=False) - source.putRead(srcRead) - p2 = self._pos - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p2, name, 'Buffer', '')) - - def pushBitStream(self, source, decode=True): - p1 = self._pos - srcRead = source.getRead() - source.putRead(0) - need = 8 - (self._pos % 8) - if need != 8: - self.internalSerial(source.readSerial(need, decode=False), need, decode=False) - while source.needRead() >= 8: - self.pushUint8(source.readSerial(8, decode=False), decode=False) - - need = source.needRead() - if need > 0: - self.internalSerial(source.readSerial(need, decode=False), need, decode=False) - - source.putRead(srcRead) - p2 = self._pos - if decode: - frame = inspect.currentframe() - frame = inspect.getouterframes(frame)[1] - string = inspect.getframeinfo(frame[0]).code_context[0].strip() - args = string[string.find('(') + 1:-1].split(',') - name = '?' - for i in args: - if i.find('=') != -1: - if i.split('=')[0].strip() == "value": - name = i.split('=')[1].strip() - break - else: - name = i - break - name = name.strip().split(' ')[0].strip() - self._groupWrite.append((p1, p2, name, 'BitStream', '')) - - # ------------------------------------ - def readSerial(self, nbits, name="", decode=True, typeName='', emulate=False): - v1 = self._read - if nbits == 0: - return - elif nbits > 32: - raise "Out of range" - if self._read + nbits > self._pos: - logging.getLogger('myLogger').debug(self.showAllData()) - logging.getLogger('myLogger').error("Stream Overflow - nbits:%d name:%s decode:%s typeName:%s emulate:%s" %(nbits, name, str(decode), typeName, str(emulate))) - raise "Stream Overflow" - if emulate: - oldRead = self._read - value = 0 - pos = self._read // 8 - _FreeBits = 8 - (self._read % 8) - v = self._tampon[pos] & ((1 << _FreeBits) - 1) - if nbits > _FreeBits: - value |= (v << (nbits-_FreeBits)) - self._read += _FreeBits - value |= self.readSerial(nbits - _FreeBits, decode=False) - else: - value |= (v >> (_FreeBits-nbits)) - self._read += nbits - if emulate: - self._read = oldRead - if decode and not emulate: - self._groupRead.append((v1, v1+nbits, name, typeName, value)) - return value - - def readBool(self, name): - v1 = self._read - v = self.readSerial(1, name=name, decode=False, typeName='Bool') - self._groupRead.append((v1, v1+1, name, 'Bool', 'True' if v != 0 else 'False')) - if v != 0: - return True - else: - return False - - def readUint32(self, name, decode=True, typeName='Uint32'): - v = self.readSerial(32, name=name, decode=decode, typeName=typeName) - return v - - def readSint32(self, name, decode=True): - v = self.readSerial(32, name=name, decode=decode, typeName='Sint32') - return c_int32(v).value - - def readUint16(self, name, decode=True): - v = self.readSerial(16, name=name, decode=decode, typeName='Uint16') - return v - - def readSint16(self, name): - v = self.readSerial(16, name=name, typeName='Sint16') - return c_int16(v).value - - def readUint8(self, name, decode=True, typeName='Uint8'): - v = self.readSerial(8, decode=decode, name=name, typeName=typeName) - return v - - def readSint8(self, name): - v = self.readSerial(8, name=name, typeName='Sint8') - return c_int8(v).value - - def readUint64(self, name): - v1 = self._read - v = self.readSerial(32, decode=False) - v1 = self.readSerial(32, decode=False) - v2 = v | (v1 << 32) - self._groupRead.append((v1, v1+64, name, 'Uint64', v2)) - return v2 - - def readSint64(self, name): - v1 = self._read - v = self.readSerial(32, decode=False) - v1 = self.readSerial(32, decode=False) - v2 = v | (v1 << 32) - self._groupRead.append((v1, v1+64, name, 'Sint64', c_int64(v2).value)) - return c_int64(v2).value - - def readFloat(self, name): - p1 = self._read - v = self.readSerial(32, name=name, decode=False, typeName='Float') - v1 = struct.pack('I', v) - v2 = struct.unpack(' 0: - x = self.readChar('', decode=False) - tmp += x - _size -= 1 - v2 = self._read - if v2 > self._pos: - raise ValueError - if v1 < v2: - self._groupRead.append((v1, v2, name + ':string', 'String', tmp)) - return tmp - - def readUString(self, name): - tmp = '' - _size = self.readUint32(name + ':size', decode=True) - v1 = self._read - while _size > 0: - x = self.readUint16(name='', decode=False) - tmp += chr(x) - _size -= 1 - v2 = self._read - if v2 > self._pos: - raise ValueError - if v1 < v2: - self._groupRead.append((v1, v2, name + ':ustring', 'UString', tmp)) - return tmp - - def readArrayUint8(self, size, name): - ret = [] - v1 = self._read - for i in range(0, size): - ret.append(self.readUint8('', decode=False)) - v2 = self._read - self._groupRead.append((v1, v2, name, 'ArrayUint8', '')) - return ret - - def readBitStreamUint8(self, size, name): - ret = BitStream() - v1 = self._read - for i in range(0, size): - ret.pushUint8(self.readUint8('', decode=False), decode=False) - v2 = self._read - self._groupRead.append((v1, v2, name, 'StreamUint8', '')) - return ret - - def readCont(self, name): - ret = BitStream() - size = self.readSint32(name = name + ':len', decode=True) - v1 = self._read - for i in range(0, size): - ret.pushBool(self.readSerial(1,name = '', decode=False)) - v2 = self._read - self._groupRead.append((v1, v2, name + ':data', 'readCont', '')) - return size, ret - - # ------------------------------------ - def __str__(self): - return ''.join([ chr(x) for x in self._tampon]) - - def message(self): - # 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 ) - - def fromBytes(self, data): - self._read = 0 - self._tampon = [int(x) for x in data] - self._pos = len(self._tampon) * 8 - - def showLastData(self): - ret = "" - 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 nsize == 1: - ret += "{0:01b}".format(data) - elif nsize == 2: - ret += "{0:02b}".format(data) - elif nsize == 3: - ret += "{0:03b}".format(data) - elif nsize == 4: - ret += "{0:04b}".format(data) - elif nsize == 5: - ret += "{0:05b}".format(data) - elif nsize == 6: - ret += "{0:06b}".format(data) - elif nsize == 7: - ret += "{0:07b}".format(data) - else: - ret += "{0:08b}".format(data) - if ret != "": - ret += "." - 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: - nsize = 8 - else: - nsize = self._pos - self._read - data = self.readSerial(nsize, decode=False) - if nsize == 1: - ret += "{0:01b}".format(data) - elif nsize == 2: - ret += "{0:02b}".format(data) - elif nsize == 3: - ret += "{0:03b}".format(data) - elif nsize == 4: - ret += "{0:04b}".format(data) - elif nsize == 5: - ret += "{0:05b}".format(data) - elif nsize == 6: - ret += "{0:06b}".format(data) - elif nsize == 7: - ret += "{0:07b}".format(data) - else: - ret += "{0:08b}".format(data) - self._read = readBefore - ret2 = "" - - last = 0 - for x, y, name, typeName, value in self._groupRead: - ret2 += "[<" + str(x) + ':' + str(y-1) + "> " + str(name) + ' (' + typeName + ') : ' + ret[x:y] + ' => ' + str(value) + "]" - last = y - if last < self._pos: - ret2 += "{" + ret[last:] + "}" - - return ret2 - - def showAllDataWrite(self): - ret = "" - readBefore = self._read - self._read = 0 - 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 nsize == 1: - ret += "{0:01b}".format(data) - elif nsize == 2: - ret += "{0:02b}".format(data) - elif nsize == 3: - ret += "{0:03b}".format(data) - elif nsize == 4: - ret += "{0:04b}".format(data) - elif nsize == 5: - ret += "{0:05b}".format(data) - elif nsize == 6: - ret += "{0:06b}".format(data) - elif nsize == 7: - ret += "{0:07b}".format(data) - else: - ret += "{0:08b}".format(data) - self._read = readBefore - ret2 = "" - - last = 0 - for x, y, name, typeName, value in self._groupWrite: - ret2 += "[<" + str(x) + ':' + str(y-1) + "> " + str(name) + ' (' + typeName + ') : ' + ret[x:y] + ' => ' + str(value) + "]" - last = y - if last < self._pos: - ret2 += "{" + ret[last:] + "}" - - return ret2 - - def showAllDataRaw(self): - ret = "" - readBefore = self._read - self._read = 0 - 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 nsize == 1: - ret += "{0:01b}".format(data) - elif nsize == 2: - ret += "{0:02b}".format(data) - elif nsize == 3: - ret += "{0:03b}".format(data) - elif nsize == 4: - ret += "{0:04b}".format(data) - elif nsize == 5: - ret += "{0:05b}".format(data) - elif nsize == 6: - ret += "{0:06b}".format(data) - elif nsize == 7: - ret += "{0:07b}".format(data) - else: - ret += "{0:08b}".format(data) - if ret != "": - ret += "." - self._read = readBefore - return ret - - def getPosInBit(self): - return self._pos - -def TestBitStream(): - a = BitStream() - vrai = True - a.pushBool(decode=True, value=vrai) - a.pushBool(False) - a.pushBool(True) - a.pushBool(True) - a.pushUint32(1234567890) - a.pushSint32(-1234567890) - a.pushUint16(12345) - a.pushSint16(-12345) - a.pushUint8(123) - a.pushSint8(-123) - #-3.4E+38) # 1.2339999675750732) - a.pushFloat(-3.3999999521443642e+38) - a.pushDouble(-1.7E+308) - a.pushUint64(16045690709418696365) - a.pushSint64(-1) - a.pushChar('a') - a.pushString("Test A Faire") - print("-" * 40) - print(a.showAllData()) - print('raw:') - print(a.showAllDataRaw()) - print("-" * 20) - print("-" * 80) - print(a.readBool('a1')) - print(a.readBool('a2')) - print(a.readBool('a3')) - print(a.readBool('a4')) - print(a.readUint32('a5')) - print(a.readSint32('a6')) - print(a.readUint16('a7')) - print(a.readSint16('a8')) - print(a.readUint8('a9')) - print(a.readSint8('a10')) - print(a.readFloat('a11')) - print(a.readDouble('a12')) - print(a.readUint64('a13')) - print(a.readSint64('a14')) - print(a.readChar('a15')) - print(a.readString('a16')) - print(a.toBytes()) - print("-" * 40) - print(a.showAllData()) - print("-" * 80) - b = BitStream() - b.fromBytes(a.toBytes()) - print(b.readBool('b1')) - print(b.readBool('b2')) - print(b.readBool('b3')) - print(b.readBool('b4')) - print(b.readUint32('b5')) - print(b.readSint32('b6')) - print(b.readUint16('b7')) - print(b.readSint16('b8')) - print(b.readUint8('b9')) - print(b.readSint8('b10')) - print(b.readFloat('b11')) - print(b.readDouble('b12')) - print(b.readUint64('b13')) - print(b.readSint64('b14')) - print(b.readChar('b15')) - print(b.readString('b16')) - print(b.toBytes()) - print("-" * 40) - print(b.showAllData()) - print("-" * 80) - c = BitStream() - c.pushBool(True) - c.pushBitStream(a) - c.pushBitStream(b) - print(c.readBool('c1')) - print("-" * 80) - print(c.readBool('c2')) - print(c.readBool('c3')) - print(c.readBool('c4')) - print(c.readBool('c5')) - print(c.readUint32('c6')) - print(c.readSint32('c7')) - print(c.readUint16('c8')) - print(c.readSint16('c9')) - print(c.readUint8('c10')) - print(c.readSint8('c11')) - print(c.readFloat('c12')) - print(c.readDouble('c13')) - print(c.readUint64('c14')) - print(c.readSint64('c15')) - print(c.readChar('c16')) - print(c.readString('c17')) - print(c.toBytes()) - print("-" * 50) - print(c.showAllData()) - print("-" * 50) - print(c.readBool('c18')) - print(c.readBool('c19')) - print(c.readBool('c20')) - print(c.readBool('c21')) - print(c.readUint32('c22')) - print(c.readSint32('c23')) - print(c.readUint16('c24')) - print(c.readSint16('c25')) - print(c.readUint8('c26')) - print(c.readSint8('c27')) - print(c.readFloat('c28')) - print(c.readDouble('c29')) - print(c.readUint64('c30')) - print(c.readSint64('c31')) - print(c.readChar('c32')) - print("-" * 40) - print(c.showAllData()) - print(c.readString('c33')) - print("-" * 40) - print(c.showAllData()) - print(c.toBytes()) - print("-" * 40) - print(c.showAllDataRaw()) - print("-" * 20) - print(c.showAllData()) - print("-" * 80) - -NL_BITLEN = 32 -class CBitSet: - def __init__(self): - self.data = self.resize(1024) - self.NumBits = 0 - self.MaskLast = 0 - - def resize(self, numBits): - self.data = [ 0 for _ in range(0, (numBits +NL_BITLEN - 1) // NL_BITLEN) ] - self.NumBits = numBits - nLastBits = self.NumBits & (NL_BITLEN-1) - if nLastBits == 0: - self.MaskLast = ~0 - else: - self.MaskLast = (1 << nLastBits)-1 - self.clearAll() - - def clearData(self): - self.data = [] - self.NumBits = 0 - self.MaskLast = 0 - - def clearAll(self): - for i in range(0, len(self.data)): - self.data[i] = 0 - - def set(self, bitNumber, value): - logging.getLogger('myLogger').debug("CBitSet::set %d %s" % (bitNumber, str(value))) - mask = bitNumber & (NL_BITLEN-1) - mask = 1 << mask - if value: - self.data[bitNumber >> 5] |= mask - else: - self.data[bitNumber >> 5] &= ~mask - - def get(self, bitNumber): - mask= bitNumber&(NL_BITLEN-1); - mask= 1<> 5] & mask != 0 - - def setBit(self, bitNumber): - self.set(bitNumber, True) - - def clearBit(self, bitNumber): - self.set(bitNumber, False) - - def __str__(self): - return '.'.join([hex(x) for x in self.data]) - def writeSerial(self, msgout): -# v = 0 # currentVersion -# if v >= 0xff: -# msgout.pushUint8(0xff) -# msgout.pushUint8(v) -# else: -# msgout.pushUint8(v) - - log = logging.getLogger('myLogger') - log.debug("CBitSet::writeSerial NumBits:%d len:%d" % (self.NumBits, len(self.data))) - - currentVersion = 0 - msgout.pushUint8(currentVersion) - msgout.pushUint32(self.NumBits) - # il est lié à 'self.NumBits' dommage que l'on envoie celui-la - msgout.pushUint32(len(self.data)) - for x in self.data: - msgout.pushUint32(x) - -def TestCBitSet(): - cBitSet = CBitSet() - cBitSet.resize(1024) - cBitSet.set(1, True) - cBitSet.set(3, True) - cBitSet.set(2, False) - cBitSet.set(13, True) - cBitSet.set(128, True) - cBitSet.set(1023, True) - print(cBitSet) - print(cBitSet.get(3)) - cBitSet.set(3, False) - print(cBitSet) - print(cBitSet.get(3)) - -def getTextMD5(dataRawXml): - log = logging.getLogger('myLogger') - dataNew = '' - for data in dataRawXml: - if data != '\r': # '\015' ignore caractère \r\n => - dataNew += data - else: - log.debug("***** data:%d" % (ord(data))) - m = hashlib.md5() - m.update(dataNew.encode('utf-8')) - #print(m.hexdigest()) - #print(m.digest()) - return m.digest() - -class CFileChild(): - def __init__(self, name, pos, size): - self.name = name - self.pos = pos - self.size = size - - def __str__(self): - return self.name + '(pos:' + str(self.pos) + ', size:' + str(self.size) + ')' - -class CFileList(): - def __init__(self, name, fullpath): - self.name = name - self.fullpath = fullpath - self.child = [] - - def addchild(self, name, pos, size): - child = CFileChild(name, pos, size) - self.child.append(child) - - def __str__(self): - return self.name + '[' + ', '.join([str(x) for x in self.child]) + ']' - -class CFileContainer(): - def __init__(self): - self.log = logging.getLogger('myLogger') - self.list = [] - - def addSearchPath(self, path): - if not path: - return - self.log.debug("read path:" + str(path)) - onlyfiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] - self.log.debug("read files:" + ','.join(onlyfiles)) - for filename in onlyfiles: - extension = os.path.splitext(filename)[1] - if extension == '.bnp': - # Container for multi file - fullpath = os.path.join(path, filename) - size = os.path.getsize(fullpath) - data = CFileList(filename, fullpath) - with open(fullpath, 'rb') as fp: - fp.seek(size-4) - nOffsetFromBeginning = int.from_bytes(fp.read(4), byteorder='little', signed=False) - self.log.debug("[%s] nOffsetFromBeginning:%u" % (filename, nOffsetFromBeginning)) - fp.seek(nOffsetFromBeginning) - nNbFile = int.from_bytes(fp.read(4), byteorder='little', signed=False) - self.log.debug("[%s] nNbFile:%u" % (filename, nNbFile)) - for i in range(0, nNbFile): - nStringSize = int.from_bytes(fp.read(1), byteorder='little', signed=False) - FileName = fp.read(nStringSize).decode() - nFileSize2 = int.from_bytes(fp.read(4), byteorder='little', signed=False) - - nFilePos = int.from_bytes(fp.read(4), byteorder='little', signed=False) - self.log.debug("[%s] (%d) sizestring:%d file:%s size2:%d pos:%d" % (filename, i, nStringSize, FileName, nFileSize2, nFilePos)) - data.addchild(FileName, nFilePos, nFileSize2) - fp.close() - self.list.append(data) - - def search(self, name): - for x in self.list: - for y in x.child: - if y.name == name: - self.log.debug("file:%s child:%s pos:%d size:%d", x.name, y.name, y.pos, y.size) - return x.fullpath, y.pos, y.size - self.log.debug('-'*80) - return None, None, None - - def getdata(self, name): - fullpath, pos, size = self.search(name) - self.log.debug("file:%s pos:%d size:%d", fullpath, pos, size) - data = None - with open(fullpath, 'rb') as fp: - fp.seek(pos) - data = fp.read(size) - fp.close() - return data - - -class TConnectionState(IntEnum): - NotInitialised = 0 # nothing happened yet - NotConnected = 1 # init() called - Authenticate = 2 # connect() called, identified by the login server - Login = 3 # connecting to the frontend, sending identification - Synchronize = 4 # connection accepted by the frontend, synchronizing - Connected = 5 # synchronized, connected, ready to work - Probe = 6 # connection lost by frontend, probing for response - Stalled = 7 # server is stalled - Disconnect = 8 # disconnect() called, or timeout, or connection closed by frontend - Quit = 9 # quit() called - - -class TActionCode(IntEnum): - ACTION_POSITION_CODE = 0 - ACTION_GENERIC_CODE = 1 - ACTION_GENERIC_MULTI_PART_CODE = 2 - ACTION_SINT64 = 3 - ACTION_SYNC_CODE = 10 - ACTION_DISCONNECTION_CODE = 11 - ACTION_ASSOCIATION_CODE = 12 - ACTION_LOGIN_CODE = 13 - ACTION_TARGET_SLOT_CODE = 40 - ACTION_DUMMY_CODE = 99 - - -class CLFECOMMON(IntEnum): - SYSTEM_LOGIN_CODE = 0 - SYSTEM_SYNC_CODE = 1 - SYSTEM_ACK_SYNC_CODE = 2 - SYSTEM_PROBE_CODE = 3 - SYSTEM_ACK_PROBE_CODE = 4 - SYSTEM_DISCONNECTION_CODE = 5 - SYSTEM_STALLED_CODE = 6 - SYSTEM_SERVER_DOWN_CODE = 7 - SYSTEM_QUIT_CODE = 8 - SYSTEM_ACK_QUIT_CODE = 9 - NumBitsInLongAck = 512 - - -class Card(IntEnum): - BEGIN_TOKEN = 0 - END_TOKEN = 1 - SINT_TOKEN = 2 - UINT_TOKEN = 3 - FLOAT_TOKEN = 4 - STRING_TOKEN = 5 - FLAG_TOKEN = 6 - EXTEND_TOKEN = 7 - - -class TType(IntEnum): - STRUCT_BEGIN = 0 - STRUCT_END = 1 - FLAG = 2 - SINT32 = 3 - UINT32 = 4 - FLOAT32 = 5 - STRING = 6 - SINT64 = 7 - UINT64 = 8 - FLOAT64 = 9 - EXTEND_TYPE = 10 - NB_TYPE = 11 - - -class TState(IntEnum): - # initial state - st_start = 0 - # display login screen and options - st_login = 1 - # auto login using cmd lien parameters (used with patch reboot) - st_auto_login = 2 - # display the shard list - st_shard_list = 3 - # lauch the configurator and close ryzom - st_start_config = 4 - # run the scan data thread - st_scan_data = 5 - # display the eula and wait for validation - st_display_eula = 6 - # check the data to determine need for patch - st_check_patch = 7 - # display the list of optional patch category for patching - st_display_cat = 8 - # run the patch process and display progress - st_patch = 9 - # terminate the client and quit - st_close_client = 10 - # display the reboot screen and wait validation - st_reboot_screen = 11 - # restart the client with login bypass params - st_restart_client = 12 - # connect to the FS (start the 'in game' mode) - st_connect = 13 - # show the outgame browser - st_browser_screen = 14 - # ingame state - st_ingame = 15 - # leave the current shard (the exit action progress, Far TP part 1.1; Server Hop part 1-2) - st_leave_shard = 16 - # let the main loop finish the current frame and leave it (Far TP part 1.2) - st_enter_far_tp_main_loop = 17 - # disconnect from the FS (Far TP part 2) - st_disconnect = 18 - # connect to a new FS (Far TP & Server Hop part 3.1) - st_reconnect_fs = 19 - # after reconnecting, bypass character selection ui & select the same character (Far TP & Server Hop part 3.2) - st_reconnect_select_char = 20 - # after reconnecting and receiving ready, send ready (Far TP part 3.3) - st_reconnect_ready = 21 - # between global menu exit and sending ready (Server Hop part 3.3) - st_exit_global_menu = 22 - # error while reconnecting - st_reconnect_error = 23 - # Rate a ring session. should pop a web windows pointing the rate session page - st_rate_session = 24 - # create account - st_create_account = 25 - # try to login with alternate login system - st_alt_login = 26 - # pseudo state to leave the state machine - st_end = 27 - st_unknown = 28 - - -class EGender(IntEnum): - male = 0 - female = 1 - neutral = 2 - unknown = 4 - - -class TPeople(IntEnum): - Undefined = -1 - Humanoid = 0 - Playable = 0 - Fyros = 0 - Matis = 1 - Tryker = 2 - Zorai = 3 - EndPlayable = 4 - Karavan = 4 - Tribe = 5 - Common = 6 - EndHumanoid = 7 - Creature = 7 - Fauna = 7 - Arma = 7 - Balduse = 8 - Bul = 9 - Capryni = 10 - Chonari = 11 - Clapclap = 12 - Cococlaw = 13 - Cute = 14 - Dag = 15 - Diranak = 16 - Estrasson = 17 - Filin = 18 - Frahar = 19 - Gibbai = 20 - Hachtaha = 21 - Jungler = 22 - Kakty = 23 - Kalab = 24 - Kami = 25 - Kazoar = 26 - Kitin = 27 - Kitins = 28 - Kitifly = 28 - Kitihank = 29 - Kitiharak = 30 - Kitikil = 31 - Kitimandib = 32 - Kitinagan = 33 - Kitinega = 34 - Kitinokto = 35 - EndKitins = 36 - Lightbird = 36 - Mektoub = 37 - MektoubPacker = 38 - MektoubMount = 39 - Pucetron = 40 - Regus = 41 - Ryzerb = 42 - Ryzoholo = 43 - Ryzoholok = 44 - Vampignon = 45 - Varinx = 46 - Yber = 47 - Zerx = 48 - race_c1 = 49 - race_c2 = 50 - race_c3 = 51 - race_c4 = 52 - race_c5 = 53 - race_c6 = 54 - race_c7 = 55 - race_h1 = 56 - race_h2 = 57 - race_h3 = 58 - race_h4 = 59 - race_h5 = 60 - race_h6 = 61 - race_h7 = 62 - race_h8 = 63 - race_h9 = 64 - race_h10 = 65 - race_h11 = 66 - race_h12 = 67 - EndFauna = 68 - Flora = 68 - Cephaloplant = 68 - Electroalgs = 69 - Phytopsy = 70 - SapEnslaver = 71 - SpittingWeeds = 72 - Swarmplants = 73 - EndFlora = 74 - Goo = 74 - GooFauna = 74 - GooArma = 74 - GooBalduse = 75 - GooBul = 76 - GooCapryni = 77 - GooChonari = 78 - GooClapclap = 79 - GooCococlaw = 80 - GooCute = 81 - GooDag = 82 - GooDiranak = 83 - GooEstrasson = 84 - GooFilin = 85 - GooFrahar = 86 - GooGibbai = 87 - GooHachtaha = 88 - GooJungler = 89 - GooKakty = 90 - GooKalab = 91 - GooKami = 92 - GooKazoar = 93 - GooKitifly = 94 - GooKitihank = 95 - GooKitiharak = 96 - GooKitikil = 97 - GooKitimandib = 98 - GooKitin = 99 - GooKitinagan = 100 - GooKitinega = 101 - GooKitinokto = 102 - GooLightbird = 103 - GooMektoub = 104 - GooMektoubPacker = 105 - GooMektoubMount = 106 - GooPucetron = 107 - GooRegus = 108 - GooRyzerb = 109 - GooRyzoholo = 110 - GooRyzoholok = 111 - GooVampignon = 112 - GooVarinx = 113 - GooYber = 114 - GooZerx = 115 - Goorace_c1 = 116 - Goorace_c2 = 117 - Goorace_c3 = 118 - Goorace_c4 = 119 - Goorace_c5 = 120 - Goorace_c6 = 121 - Goorace_c7 = 122 - Goorace_h1 = 123 - Goorace_h2 = 124 - Goorace_h3 = 125 - Goorace_h4 = 126 - Goorace_h5 = 127 - Goorace_h6 = 128 - Goorace_h7 = 129 - Goorace_h8 = 130 - Goorace_h9 = 131 - Goorace_h10 = 132 - Goorace_h11 = 133 - Goorace_h12 = 134 - EndGooFauna = 135 - GooPlant = 135 - GooCephaloplant = 135 - GooElectroalgs = 136 - GooPhytopsy = 137 - GooSapEnslaver = 138 - GooSpittingWeeds = 139 - GooSwarmplants = 140 - EndGooPlant = 141 - EndGoo = 141 - EndCreature = 141 - ___TPeople_useSize = 142 - Unknown = 142 - EndPeople = 142 - -class TExtendType: - ET_SHEET_ID = 0 - ET_64_BIT_EXTENDED_TYPES = 0x80000000 - ET_ENTITY_ID = 0x80000000 # ET_ENTITY_ID = ET_64_BIT_EXTENDED_TYPES - - -class CBNPFileVersion: - def __init__(self): - self.VersionNumber = None - self.FileTime = None - self.FileSize = None - self.v7ZFileSize = None - self.PatchSize = None - self.HashKey = [] - - def __str__(self): - return "VersionNumber:" + str(self.VersionNumber) + ", FileTime:" + str(self.FileTime) + ", FileSize:" + str(self.FileSize) + ", 7ZFileSize:" + str(self.v7ZFileSize) + ", PatchSize:" + str(self.PatchSize) + ", HashKey:" + str(self.HashKey) - - -class CBNPFile: - def __init__(self): - self.FileName = None - self.Versions = [] - self.IsIncremental = False - - def __str__(self): - return str(self.FileName) +' (' + ', '.join( [str(x) for x in self.Versions]) + ')' - - def update(self, FileName): - self.FileName = FileName - -class CBNPCategorySet: - def __init__(self): - self._Name = "" - self._IsOptional = False - self._UnpackTo = "" - self._IsIncremental = False - self._CatRequired = "" - self._Hidden = False - self._Files = [] - - def __str__(self): - return self._Name + ' (IsOptional:' + str(self._IsOptional) + ', UnpackTo:' + self._UnpackTo + ', IsIncremental:' + str(self._IsIncremental) + ', CatRequired:' + self._CatRequired + ', Hidden:' + str(self._Hidden) + ', Files:' + str(self._Files) + ')' - -# ##################################################### -# persistent_data.h:140 # struct CArg -# ##################################################### -class CArgV1(Structure): - _fields_ = [("i32_1", c_uint), - ("i32_2", c_uint)] - - -class CArgV2(Structure): - _fields_ = [("ex32_1", c_uint), - ("ex32_2", c_uint)] - - -class CArgV3(Union): - _fields_ = [("ex32", CArgV2), - ("ExData32", c_uint), - ("ExData64", c_ulong)] - - -class CArgV4(Structure): - _fields_ = [("ExType", c_uint), - ("ex", CArgV3)] - - -class CArgV5(Union): - _fields_ = [("i", CArgV1), - ("ii32", c_int), - ("ii64", c_long), - ("i32", c_uint), - ("i64", c_ulong), - ("f32", c_float), - ("f64", c_double), - ("ex", CArgV4)] - -class CArg: - # union - # { - # struct - # { - # uint32 i32_1; - # uint32 i32_2; - # } i; - # - # sint32 i32; - # sint64 i64; - # float f32; - # double f64; - # - # struct - # { - # uint32 ExType; - # union - # { - # struct - # { - # uint32 ex32_1; - # uint32 ex32_2; - # }; - # - # uint32 ExData32; - # uint64 ExData64; - # } ex; - # } ex; - # } _Value; - - def __init__(self): - self._value = CArgV5() - self._value.ii64 = 0 - self._value.i64 = 0 - self._type = 0 - self._string = 0 - self._type64 = False - - def read_Type(self): - return self._type - - def write_Type(self, value): - self._type = value - - def write_Type64(self, value): - self._type64 = value - - def read_String(self): - return self._string - - def write_String(self, value): - self._string = value - - def read_i32_1(self): - return self._value.i.i32_1 - - def write_i32_1(self, value): - self._value.i.i32_1 = value - - def read_i32_2(self): - return self._value.i.i32_2 - - def write_i32_2(self, value): - self._value.i.i32_2 = value - - def read_i32(self): - return self._value.i32 - - def write_i32(self, value): - self._value.i32 = value - - def read_i64(self): - return self._value.i64 - - def write_i64(self, value): - self._value.i64 = value - - def read_f32(self): - return self._value.f32 - - def write_f32(self, value): - self._value.f32 = value - - def read_f64(self): - return self._value.f64 - - def write_f64(self, value): - self._value.f64 = value - - def read_ExType(self): - return self._value.ex.ExType - - def write_ExType(self, value): - self._value.ex.ExType = value - - def read_ex32_1(self): - return self._value.ex.ex.ex32.ex32_1 - - def write_ex32_1(self, value): - self._value.ex.ex.ex32.ex32_1 = value - - def read_ex32_2(self): - return self._value.ex.ex.ex32.ex32_2 - - def write_ex32_2(self, value): - self._value.ex.ex.ex32.ex32_2 = value - - def read_ExData32(self): - return self._value.ex.ex.ExData32 - - def write_ExData32(self, value): - self._value.ex.ex.ExData32 = value - - def read_ExData64(self): - return self._value.ex.ex.ExData64 - - def write_ExData64(self, value): - self._value.ex.ex.ExData64 = value - - def isExtended(self): - if self._type == TType.EXTEND_TYPE: - return True - elif self._type == TType.STRUCT_BEGIN: - self.log.error("Can't extract a value from a structure delimiter") - sys.exit(2) - elif self._type == TType.STRUCT_END: - self.log.error("Can't extract a value from a structure delimiter") - sys.exit(2) - return False - - def isFlag(self): - if self._type == TType.FLAG: - return True - else: - return False - - def asUint(self): - if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END: - self.log.error("Can't extract a value from a structure delimiter") - sys.exit(2) - elif self._type == TType.SINT32: - return self.read_i32() - elif self._type == TType.UINT32: - return self.read_i32() - elif self._type == TType.SINT64: - return self.read_i64() - elif self._type == TType.UINT64: - return self.read_i64() - elif self._type == TType.FLOAT32: - return self.read_i32() - elif self._type == TType.FLOAT64: - return self.read_i64() - elif self._type == TType.STRING: - return int(self._string) - elif self._type == TType.FLAG: - return "1" - elif self._type == TType.EXTEND_TYPE: - if self.read_ExType() == TExtendType.ET_SHEET_ID: - return self.read_ExData32() - elif self.read_ExType() == TExtendType.ET_ENTITY_ID: - return self.read_ExData64() - log = logging.getLogger('myLogger') - log.error("This should never happen!") - sys.exit(2) - - def __str__(self): - log = logging.getLogger('myLogger') - log.debug(self._type) - if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END: - return '' - elif self._type64: - # To be confirm for extend - return str(self.read_ExData64()) - elif self._type == TType.SINT32: - return str(self.read_i32()) - elif self._type == TType.UINT32: - return str(self.read_i32()) - elif self._type == TType.SINT64: - return str(self.read_i64()) - elif self._type == TType.UINT64: - return str(self.read_i64()) - elif self._type == TType.FLOAT32: - return str(self.read_i32()) - elif self._type == TType.FLOAT64: - return str(self.read_i64()) - elif self._type == TType.STRING: - return self._string - elif self._type == TType.FLAG: - return "1" - return '?' - - def asSint(self): - self.log.error("TODO") - sys.exit(2) - - def asFloat(self): - self.log.error("TODO") - sys.exit(2) - - def asDouble(self): - self.log.error("TODO") - sys.exit(2) - - def asString(self): - if self._type == TType.STRUCT_BEGIN or self._type == TType.STRUCT_END: - self.log.error("Can't extract a value from a structure delimiter") - sys.exit(2) - elif self._type == TType.SINT32: - return str(self.read_ii32()) - elif self._type == TType.UINT32: - return str(self.read_i32()) - elif self._type == TType.SINT64: - return str(self.read_ii64()) - elif self._type == TType.UINT64: - return str(self.read_i64()) - elif self._type == TType.FLOAT32: - return str(self.read_f32()) - elif self._type == TType.FLOAT64: - return str(self.read_f64()) - elif self._type == TType.STRING: - return self._string - elif self._type == TType.FLAG: - return "1" - elif self._type == TType.EXTEND_TYPE: - self.log.error("TODO") - sys.exit(2) - # switch(_Value.ExType) - # { - # case ET_SHEET_ID: - # { - # NLMISC::CSheetId sheetId(_Value.ExData32); - # return sheetId.toString(true); - # } - # case ET_ENTITY_ID: - # { - # NLMISC::CEntityId entityId(_Value.ExData64); - # return entityId.toString(); - # } - # default: - # break; - # } - self.log.error("This should never happen!") - sys.exit(2) - - def asUCString(self): - self.log.error("TODO") - sys.exit(2) - - def asEntityId(self): - self.log.error("TODO") - sys.exit(2) - - def asSheetId(self): - self.log.error("TODO") - sys.exit(2) - - def typeName(self): - self.log.error("TODO") - sys.exit(2) - -# ##################################################### -# -# ##################################################### - -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, decodeImpulse): - ''' - 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("CGenericMultiPartTemp : data : %s" % bms.showAllData()) - self.log.debug("TODO") - decodeImpulse.execute(bms) - else: - self.log.debug("CGenericMultiPartTemp : Wait other block") - - - -class ECharacterTitle(IntEnum): - ''' - khanat-opennel-code/code/ryzom/common/src/game_share/character_title.h # enum ECharacterTitle - ''' - Refugee = 0 - Homin = 1 # - Novice_Artisan = 2 # - Novice_Warrior = 3 # - Novice_Harvester = 4 # - Novice_Magician = 5 # - Artisan_Apprentice = 6 # - Magician_Apprentice = 7 # - Defensive_Magician = 8 # - Offensive_Magician = 9 # - Mentalist = 10 # - Summoner = 11 # - Master_Of_Illusion = 12 # - Mind_Lord = 13 # - Healer = 14 # - Priest = 15 # - Master_Of_Life = 16 # - Celestial_Guardian = 17 # - Disturber = 18 # - Affliction_Bringer = 19 # - Master_Of_Torment = 20 # - Avatar_Of_Sorrow = 21 # - Destroyer = 22 # - Archmage = 23 # - Master_Of_Pain = 24 # - Avatar_Of_Destruction = 25 # - Elementalist = 26 # - Alchemist = 27 # - Biomancer = 28 # - Master_Of_Energies = 29 # - Chosen_Of_Atys = 30 # - Warrior_Apprentice = 31 # - Melee_Warrior = 32 # - Range_Fighter = 33 # - Light_Armsman = 34 # - Heavy_Armsman = 35 # - Close_Fighter = 36 # - Gunman = 37 # - Heavy_Gunman = 38 # - Advanced_Gunman = 39 # - Advanced_Heavy_Gunman = 40 # - Bludgeoner = 41 # - Skewerer = 42 # - Slasher = 43 # - Smasher = 44 # - Impaler = 45 # - Cleaver = 46 # - Advanced_Close_Fighter = 47 # - Maceman = 48 # - Brute = 49 # - Spearman = 50 # - Axeman = 51 # - Swordsman = 52 # - Heavy_Maceman = 53 # - Pikeman = 54 # - Heavy_Axeman = 55 # - Heavy_Swordsman = 56 # - Knifeman = 57 # - Hand_To_Hand_Fighter = 58 # - Bowman = 59 # - Pistoleer = 60 # - Heavy_Bowman = 61 # - Artilleryman = 62 # - Rifleman = 63 # - Master_Maceman = 64 # - Master_Brute = 65 # - Master_Spearman = 66 # - Master_Axeman = 67 # - Master_Swordsman = 68 # - Master_Heavy_Maceman = 69 # - Master_Pikeman = 70 # - Master_Heavy_Axeman = 71 # - Master_Heavy_Swordsman = 72 # - Master_Knifeman = 73 # - Master_Hand_To_Hand_Fighter = 74 # - Master_Bowman = 75 # - Master_Pistoleer = 76 # - Master_Heavy_Bowman = 77 # - Master_Artilleryman = 78 # - Master_Rifleman = 79 # - Armorer_Apprentice = 80 # - Jeweler_Apprentice = 81 # - Melee_Weapon_Smith_Apprentice = 82 # - Range_Weapon_Smith_Apprentice = 83 # - Heavy_Armorer = 84 # - Light_Armorer = 85 # - Medium_Armorer = 86 # - Shield_Smith = 87 # - Jeweler = 88 # - Melee_Weapon_Smith = 89 # - Melee_Heavy_Weapon_Smith = 90 # - Melee_Light_Weapon_Smith = 91 # - Range_Weapon_Smith = 92 # - Range_Heavy_Weapon_Smith = 93 # - Advanced_Heavy_Armorer = 94 # - Advanced_Light_Armorer = 95 # - Advanced_Medium_Armorer = 96 # - Advanced_Shield_Smith = 97 # - Advanced_Jeweler = 98 # - Advanced_Melee_Weapon_Smith = 99 # - Advanced_Melee_Heavy_Weapon_Smith = 100 # - Advanced_Melee_Light_Weapon_Smith = 101 # - Advanced_Range_Weapon_Smith = 102 # - Advanced_Range_Heavy_Weapon_Smith = 103 # - Expert_Heavy_Armorer = 104 # - Expert_Light_Armorer = 105 # - Expert_Medium_Armorer = 106 # - Expert_Shield_Smith = 107 # - Expert_Jeweler = 108 # - Expert_Melee_Weapon_Smith = 109 # - Expert_Melee_Heavy_Weapon_Smith = 110 # - Expert_Melee_Light_Weapon_Smith = 111 # - Expert_Range_Weapon_Smith = 112 # - Expert_Range_Heavy_Weapon_Smith = 113 # - Heavy_Armorer_Master = 114 # - Light_Armorer_Master = 115 # - Medium_Armorer_Master = 116 # - Shield_Smith_Master = 117 # - Jeweler_Master = 118 # - Melee_Weapon_Smith_Master = 119 # - Melee_Heavy_Weapon_Smith_Master = 120 # - Melee_Light_Weapon_Smith_Master = 121 # - Range_Weapon_Smith_Master = 122 # - Range_Heavy_Weapon_Smith_Master = 123 # - Forager_Apprentice = 124 # - Forager = 125 # - Desert_Forager = 126 # - Forest_Forager = 127 # - Jungle_Forager = 128 # - Lacustre_Forager = 129 # - Prime_Roots_Forager = 130 # - Advanced_Desert_Forager = 131 # - Advanced_Forest_Forager = 132 # - Advanced_Jungle_Forager = 133 # - Advanced_Lacustre_Forager = 134 # - Advanced_Prime_Roots_Forager = 135 # - Expert_Desert_Forager = 136 # - Expert_Forest_Forager = 137 # - Expert_Jungle_Forager = 138 # - Expert_Lacustre_Forager = 139 # - Expert_Prime_Roots_Forager = 140 # - Master_Desert_Forager = 141 # - Master_Forest_Forager = 142 # - Master_Jungle_Forager = 143 # - Master_Lacustre_Forager = 144 # - Master_Prime_Roots_Forager = 145 # - Kami_Ally = 146 # - Karavan_Ally = 147 # - Title00000 = 148 # - Title00001 = 149 # // Journeyer - Title00002 = 150 # // Novice Kitin Hunter - Title00003 = 151 # // Kitin Hunter - Title00004 = 152 # // Master Kitin Hunter - Title00005 = 153 # // Kitin Eradicator - Title00006 = 154 # // Kitin Mass Murderer - Title00007 = 155 # // Matis Guardian - Title00008 = 156 # // Fyros Guardian - Title00009 = 157 # // Tryker Guardian - Title00010 = 158 # // Zorai Guardian - Title00011 = 159 # // Atys Guardian - Title00012 = 160 # - Title00013 = 161 # - Title00014 = 162 # // The fortunate - Title00015 = 163 # // Jinxed - Title00016 = 164 # - Title00017 = 165 # - Title00018 = 166 # - Title00019 = 167 # - Title00020 = 168 # // Fyros Patriot - Title00021 = 169 # // Matis Vassal - Title00022 = 170 # // Tryker Citizen - Title00023 = 171 # // Zorai Initiate - Title00024 = 172 # // Kami Disciple - Title00025 = 173 # // Karavan Follower - Title00026 = 174 # // Fyros Akenak - Title00027 = 175 # // Matis Noble - Title00028 = 176 # // Tryker Taliar - Title00029 = 177 # // Zorai Awakened - Title00030 = 178 # // Marauder - Title00031 = 179 # // Fyros Ambassador - Title00032 = 180 # // Matis Ambassador - Title00033 = 181 # // Tryker Ambassador - Title00034 = 182 # // Zorai Ambassador - Title00035 = 183 # - Title00036 = 184 # - Title00037 = 185 # - Title00038 = 186 # - Title00039 = 187 # - Title00040 = 188 # - Title00041 = 189 # - Title00042 = 190 # - Title00043 = 191 # - Title00044 = 192 # - Title00045 = 193 # - Title00046 = 194 # - Title00047 = 195 # // Machinegunner - Title00048 = 196 # // Assault Machinegunner - Title00049 = 197 # - Title00050 = 198 # // Apprentice Butcher - Title00051 = 199 # // Butcher - Title00052 = 200 # // Apprentice Florist - Title00053 = 201 # // Florist - Title00054 = 202 # // Apprentice Water-Carrier - Title00055 = 203 # // Water-Carrier - Title00056 = 204 # // Apprentice Magnetic - Title00057 = 205 # // Magnetic Cartographe - Title00058 = 206 # // Apprentice Toolmaker - Title00059 = 207 # // Toolmaker - Title00060 = 208 # // Apprentice Rescuer - Title00061 = 209 # // Rescuer - Title00062 = 210 # // Apprentice Larvester - Title00063 = 211 # // Larvester - Title00064 = 212 # // Apprentice Scrollmaker - Title00065 = 213 # // Scrollmaker - Title00066 = 214 # - Title00067 = 215 # - Title00068 = 216 # - Title00069 = 217 # - Title00070 = 218 # - Title00071 = 219 # - Title00072 = 220 # - Title00073 = 221 # - Title00074 = 222 # - Title00075 = 223 # - Title00076 = 224 # - Title00077 = 225 # - Title00078 = 226 # - Title00079 = 227 # // Wayfarer - WIND = Title00079 # // Title for player come from old Windermmer community - FBT = 228 # - BeginGmTitle = 229 # - # SGM = BeginGmTitle # - GM = 230 # - VG = 231 # - SG = 232 # - G = 233 # - CM = 234 # - EM = 235 # - EG = 236 # - OBSERVER = 237 # - # EndGmTitle = OBSERVER, - NB_CHARACTER_TITLE = 238 - - -class SPropVisualA(): - def __init__(self): - ''' - khanat-opennel-code/code/ryzom/common/src/game_share/player_visual_properties.h # struct SPropVisualA - ''' - self.Sex = False #: 1; // max: 2 current: 2 - self.JacketModel = 0 #: 8; // max: 256 current: 93 - self.JacketColor = 0 #: 3; // max: 8 current: 8 - self.TrouserModel = 0 #: 8; // max: 256 current: 104 - self.TrouserColor = 0 #: 3; // max: 8 current: 8 - self.WeaponRightHand = 0 #: 10; // max: 1024 current: 457 - self.WeaponLeftHand = 0 #: 8; // max: 256 current: 63 - self.ArmModel = 0 #: 8; // max: 256 current: 94 - self.ArmColor = 0 #: 3; // max: 8 current: 8 - self.HatModel = 0 #: 9; // max: 512 current: 192 - self.HatColor = 0 #: 3; // max: 8 current: 8 - - def read(self, msgin): - self.Sex = msgin.readBool('Sex') - self.JacketModel = msgin.readUint8('JacketModel') - self.JacketColor = msgin.readSerial(3, 'JacketModel') - self.TrouserModel = msgin.readSerial(8, 'TrouserModel') - self.TrouserColor = msgin.readSerial(3, 'TrouserColor') - self.WeaponRightHand = msgin.readSerial(10, 'WeaponRightHand') - self.WeaponLeftHand = msgin.readSerial(8, 'WeaponLeftHand') - self.ArmModel = msgin.readSerial(8, 'ArmModel') - self.ArmColor = msgin.readSerial(3, 'ArmColor') - self.HatModel = msgin.readSerial(9, 'HatModel') - self.HatColor = msgin.readSerial(3, 'HatColor') - - -class SPropVisualB(): - def __init__(self): - self.Name = "" #: 16; - self.HandsModel = 0 #: 9; // max: 512 current: 90 - self.HandsColor = 0 #: 3; // max: 8 current: 8 - self.FeetModel = 0 #: 9; // max: 512 current: 94 - self.FeetColor = 0 #: 3; // max: 8 current: 8 - self.RTrail = 0 #: 4; - self.LTrail = 0 #: 3; - self.NotUsed = 0 # 17 : # not used -> just to complete 64 bit - def read(self, msgin): - self.Name = msgin.readSerial(16, 'Name') - self.HandsModel = msgin.readSerial(9, 'HandsModel') - self.HandsColor = msgin.readSerial(3, 'HandsColor') - self.FeetModel = msgin.readSerial(9, 'FeetModel') - self.FeetColor = msgin.readSerial(3, 'FeetColor') - self.RTrail = msgin.readSerial(4, 'RTrail') - self.LTrail = msgin.readSerial(3, 'LTrail') - self.NotUsed = msgin.readSerial(17, 'NotUsed') - - -class SPropVisualC(): - def __init__(self): - self.MorphTarget1 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget2 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget3 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget4 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget5 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget6 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget7 = 0 # : 3; // max: 8 current: 8 - self.MorphTarget8 = 0 # : 3; // max: 8 current: 8 - self.EyesColor = 0 # : 3; // max: 8 current: 8 - self.Tattoo = 0 # : 7; // max: 128 current: 64 - self.CharacterHeight = 0 # : 4; // max: 16 current: 16 - self.TorsoWidth = 0 # : 4; // max: 16 current: 16 - self.ArmsWidth = 0 # : 4; // max: 16 current: 16 - self.LegsWidth = 0 # : 4; // max: 16 current: 16 - self.BreastSize = 0 # : 4; // max: 16 current: 16 - self.NotUsed = 0 # 10 : # not used -> just to complete 64 bit - def read(self, msgin): - self.MorphTarget1 = msgin.readSerial(3, 'MorphTarget1') - self.MorphTarget2 = msgin.readSerial(3, 'MorphTarget2') - self.MorphTarget3 = msgin.readSerial(3, 'MorphTarget3') - self.MorphTarget4 = msgin.readSerial(3, 'MorphTarget4') - self.MorphTarget5 = msgin.readSerial(3, 'MorphTarget5') - self.MorphTarget6 = msgin.readSerial(3, 'MorphTarget6') - self.MorphTarget7 = msgin.readSerial(3, 'MorphTarget7') - self.MorphTarget8 = msgin.readSerial(3, 'MorphTarget8') - self.EyesColor = msgin.readSerial(3, 'EyesColor') - self.Tattoo = msgin.readSerial(7, 'Tattoo') - self.CharacterHeight = msgin.readSerial(4, 'CharacterHeight') - self.TorsoWidth = msgin.readSerial(4, 'TorsoWidth') - self.ArmsWidth = msgin.readSerial(4, 'ArmsWidth') - self.LegsWidth = msgin.readSerial(4, 'LegsWidth') - self.BreastSize = msgin.readSerial(4, 'BreastSize') - self.NotUsed = msgin.readSerial(10, 'NotUsed') - - -class CCharacterSummary(): - def __init__(self): - self.version = -1 - self.Mainland = 0 # CSessionId - self.Name = "" - self.People = TPeople.Unknown - self.Location = 0 - self.sPropVisualA = SPropVisualA() - self.sPropVisualB = SPropVisualB() - self.sPropVisualC = SPropVisualC() - self.sheetId = 0 - self.Title = ECharacterTitle.NB_CHARACTER_TITLE - self.CharacterSlot = 255 - self.InRingSession = False - self.HasEditSession = False - self.InNewbieland = False - - def read(self, msgin): - ''' - khanat-opennel-code/code/ryzom/common/src/game_share/character_summary.cpp # void CCharacterSummary::serial(NLMISC::IStream &f) - ''' - self.version = msgin.readUint8('version') - self.Mainland = msgin.readUint32('Mainland') - self.Name = convertUStringToString(msgin.readUString('Name')) - self.People = msgin.readSint32('People') - self.Location = msgin.readUint32('Location') - self.sPropVisualA.read(msgin) - self.sPropVisualB.read(msgin) - self.sPropVisualC.read(msgin) - self.sheetId = msgin.readUint32('SheetId') - self.Title = msgin.readSint32('Title') # see ECharacterTitle - self.CharacterSlot = msgin.readUint8('CharacterSlot') - self.InRingSession = msgin.readBool('InRingSession') - self.HasEditSession = msgin.readBool('HasEditSession') - self.InNewbieland = msgin.readBool('InNewbieland') - - -class CSessionId(): - def __init__(self, id = 0): - self.id = id - def read(self, msgin): - self.id = msgin.readUint32('id') - def push(self, msgout): - msgout.pushUint32(self.id) - - -class CMainlandSummary(): - def __init__(self): - self.Id = CSessionId() - self.Name = "" - self.Description = "" - self.LanguageCode = "" - self.Online = False - def read(self, msgin): - ''' - khanat-opennel-code/code/ryzom/common/src/game_share/mainland_summary.h # void serial(NLMISC::IStream &f) - ''' - self.Id.read(msgin) - self.Name = msgin.readUString('Name') - self.Description = msgin.readUString('Description') - self.LanguageCode = msgin.readString('LanguageCode') - self.Online = msgin.readBool('Online') - - -class CShardName(): - def __init__(self, SessionId, DisplayName, ShortName): - self.SessionId = SessionId - self.DisplayName = DisplayName - self.ShortName = ShortName - - -def CodeMsgXml(msgXml, key): - head = msgXml - listpath = key.split(':') - ret = [] - for id in listpath: - nbBit = getPowerOf2(len(head)) - found = False - i = 0 - for ele in head: - if ele.attrib['name'] == id: - found = True - ret.append([nbBit, i, id]) - break - i +=1 - if not found: - log = logging.getLogger('myLogger') - log.error("Impossible to found value (all key:%s, value:%s)" % (key, id)) - raise ValueError - head = ele - return ret - - - -class GenericMultiPartTemp(): - def __init__(self, log): - self.log = log - self.data = {} - - def addGenericMultiPartTemp(self, id): - self.data.setdefault(id, CGenericMultiPartTemp(self.log)) - - def setGenericMultiPartTemp(self, Number, Part, NbBlock, PartCont, decodeImpulse): - self.data[Number].set(Number, Part, NbBlock, PartCont, decodeImpulse) - - -class TStreamFormat(IntEnum): - UseDefault = 0 - Binary = 1 - String = 2 - -class TMessageType(IntEnum): - OneWay = 0 - Request = 1 - Response = 2 - Except = 3 - -#class CMessage(): -# def __init__(self, name, inputStream=False, streamformat=TStreamFormat.UseDefault, defaultCapacity=1000 ): -# self._Name = name -# self._Type = TMessageType.OneWay -# self._SubMessagePosR = 0 -# self._LengthR = 0 -# self._HeaderSize = 0xFFFFFFFF -# self._TypeSet = False -# if streamformat == TStreamFormat.UseDefault: -# self._DefaultStringMode = False -# else: -# self._DefaultStringMode = (streamformat == TStreamFormat.String) -# self.msgout = BitStream() -# -# def serial(self, uid, bms): -# self.msgout.pushUint64(uid) - - -class World(): - def __init__(self, log, HeadName): - self.log = log - #self.GenericMultiPartTemp = {} - self.timestamp = 0 - self.ServerPeopleActive = 255 - self.ServerCareerActive = 255 - self.CharacterSummaries = [] - self.Mainlands = [] - self.CShardNames = [] - self.UserPrivileges = '' - self.FreeTrial = False - self.HeadName = HeadName - self.CurrentState = TState.st_unknown - self.UseFemaleTitles = False - - def CreaterCharacter(self, msgXml): - ''' - khanat-opennel-code/code/ryzom/client/src/connection.cpp # class CAHAskCreateChar : public IActionHandler - khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h # void setupFromCharacterSummary (const CCharacterSummary &cs) - khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h # void serialBitMemStream(NLMISC::CBitMemStream &f) - ''' - Slot = 0 - SheetId = 0 - #Mainland= 302 - cSessionId = CSessionId(self.CShardNames[0].SessionId) - - # [A-Za-z]{3,15} # quand je pense qu'ils ont code pour avoir du UTF !!!!! - name = '' - for car in self.HeadName: - if car >= 'a' and car <= 'z': - pass - elif car >= 'A' and car <= 'Z': - pass - elif car >= '0' and car <= '9': - car = chr(ord(car)+ord('A')-ord('0')) - else: - car ='Z' - name += car - #name = 'Tester' - People = 1 - Sex = 0 - StartPoint = 1 - NbPointFighter = 2 - NbPointCaster = 1 - NbPointCrafter = 1 - NbPointHarvester = 1 - GabaritHeight = 9 - GabaritTorsoWidth = 10 - GabaritArmsWidth = 7 - GabaritLegsWidth = 4 - GabaritBreastSize = 7 - MorphTarget1 = 3 - MorphTarget2 = 3 - MorphTarget3 = 3 - MorphTarget4 = 5 - MorphTarget5 = 5 - MorphTarget6 = 5 - MorphTarget7 = 4 - MorphTarget8 = 4 - EyesColor = 0 - Tattoo = 0 - HairType = 5 - HairColor = 1 - JacketColor = 0 - TrousersColor = 0 - HatColor = 1 - ArmsColor = 0 - HandsColor = 0 - FeetColor = 0 - msgout = BitStream() - # GenericMsgHeaderMngr.pushNameToStream("CONNECTION:CREATE_CHAR", out)) - ref = CodeMsgXml(msgXml, 'CONNECTION:CREATE_CHAR') - for size, value, id in ref: - msgout.internalSerial(value, size, typeName=id) - # khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h # void serialBitMemStream(NLMISC::CBitMemStream &f) - msgout.pushUint8(Slot) - msgout.pushUint32(SheetId) - cSessionId.push(msgout) - msgout.pushUString(name) - msgout.pushUint8(People) - msgout.pushUint8(Sex) - - msgout.pushUint8(NbPointFighter) - msgout.pushUint8(NbPointCaster) - msgout.pushUint8(NbPointCrafter) - msgout.pushUint8(NbPointHarvester) - - msgout.pushSint32(StartPoint) - - msgout.pushSint8(HairType) - msgout.pushSint8(HairColor) - - # GabaritHeight => 0 - 15 - msgout.pushSint8(GabaritHeight) - msgout.pushSint8(GabaritTorsoWidth) - msgout.pushSint8(GabaritArmsWidth) - msgout.pushSint8(GabaritLegsWidth) - msgout.pushSint8(GabaritBreastSize) - - # MorphTarget1 => 0 - 7 - msgout.pushSint8(MorphTarget1) - msgout.pushSint8(MorphTarget2) - msgout.pushSint8(MorphTarget3) - msgout.pushSint8(MorphTarget4) - msgout.pushSint8(MorphTarget5) - msgout.pushSint8(MorphTarget6) - msgout.pushSint8(MorphTarget7) - msgout.pushSint8(MorphTarget8) - # EyesColor => 0 - 7 - msgout.pushSint8(EyesColor) - # Tattoo) => 0 = neutral, 1 - 64 Tattoo - msgout.pushSint8(Tattoo) - - #// color for equipement slots (Only for pre-equipped perso created with sheet) - msgout.pushSint8(JacketColor) - msgout.pushSint8(TrousersColor) - msgout.pushSint8(HatColor) - msgout.pushSint8(ArmsColor) - msgout.pushSint8(HandsColor) - msgout.pushSint8(FeetColor) - - #self.Commands.append(msgout) - return msgout - - - def impulsionCreateChar(self, msgXml, uid): - ''' - khanat-opennel-code/code/ryzom/server/src/frontend_service/uid_impulsions.cpp # static void impulsionCreateChar(uint32 uid, NLMISC::CBitMemStream &bms, NLMISC::TGameCycle gameCycle) - ''' - bms = self.CreaterCharacter(msgXml) - #msgout = CMessage("CREATE_CHAR") - #msgout.serial(uid, bms) - - def SelectChar(self, msgXml, PlayerSelectedSlot): - ''' - khanat-opennel-code/code/ryzom/client/src/far_tp.cpp # void CFarTP::selectCharAndEnter() - ''' - msgout = BitStream() - ref = CodeMsgXml(msgXml, 'CONNECTION:SELECT_CHAR') - for size, value, id in ref: - msgout.internalSerial(value, size, typeName=id) - msgout.pushUint8(PlayerSelectedSlot) - return msgout - - -class CPersistentDataRecord: - def __init__(self, log): - self.log = log - self.TokenTable = [] - self.ArgTable = [] - self.StringTable = [ ] - self.ReadingStructStack = [] - self.offsetToken = 0 - self.ArgOffset = 0 - self.version = 0 - self.totalSize = 0 - self.tokenCount = 0 - self.argCount = 0 - self.stringCount = 0 - self.stringsSize = 0 - self.CBNPFile = [] - self.Categories = [] - - def show(self): - for x in self.CBNPFile: - self.log.debug("File:%s" % str(x)) - for x in self.Categories: - self.log.debug("Categorie:%s" % str(x)) - - # ---------------- Manipulate Token ---------------- - - # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ - # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ - # | Token ID | Token Type | - # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ - - def token2Type(self, token, extend): # persistent_data_inline.h:1102 CPersistentDataRecord::CArg::TType CPersistentDataRecord::CArg::token2Type(uint32 token,bool extend) - self.log.debug("token:%d, extend:%d" % (token, extend)) - if token == Card.BEGIN_TOKEN: - return TType.STRUCT_BEGIN - elif token == Card.END_TOKEN: - return TType.STRUCT_END - elif token == Card.FLAG_TOKEN: - return TType.FLAG - elif token == Card.SINT_TOKEN: - if extend: - return TType.SINT64 - else: - return TType.SINT32 - elif token == Card.UINT_TOKEN: - if extend: - return TType.UINT64 - else: - return TType.UINT32 - elif token == Card.FLOAT_TOKEN: - if extend: - return TType.FLOAT64 - else: - return TType.FLOAT32 - elif token == Card.STRING_TOKEN: - if extend: - return TType.EXTEND_TYPE - else: - return TType.STRING - self.log.error('This should never happen!') - sys.exit(2) - - def type2Token(self, type): # persistent_data_inline.h:1118 CPersistentDataRecord::TToken CPersistentDataRecord::CArg::type2Token(uint32 type) - self.log.debug("type: %d" %(type)) - if type == TType.STRUCT_BEGIN: - return Card.BEGIN_TOKEN - elif type == TType.STRUCT_END: - return Card.END_TOKEN - elif type == TType.FLAG: - return Card.FLAG_TOKEN - elif type == TType.SINT32: - return Card.SINT_TOKEN - elif type == TType.UINT32: - return Card.UINT_TOKEN - elif type == TType.FLOAT32: - return Card.FLOAT_TOKEN - elif type == TType.STRING: - return Card.STRING_TOKEN - elif type == TType.SINT64: - return Card.SINT_TOKEN - elif type == TType.UINT64: - return Card.UINT_TOKEN - elif type == TType.FLOAT64: - return Card.FLOAT_TOKEN - elif type == TType.EXTEND_TYPE: - return Card.STRING_TOKEN - self.log.error('This should never happen!') - sys.exit(2) - - def peekNextToken(self): - token = self.TokenTable[self.offsetToken] - self.log.debug("[%d] token:%d" %(self.offsetToken, token)) - return token // 8 # persistent_data_inline.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() # _TokenTable[_TokenOffset]>>3; - - def peekNextTokenType(self): # persistent_data_limit.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() const - self.log.debug("peekNextTokenType - old offset token:%d" % self.offsetToken) - if self.isEndOfData(): - self.log.error('Attempt to read past end of input data') - sys.exit(2) - token = self.TokenTable[self.offsetToken] - tokenType = token & 7 - if tokenType == Card.EXTEND_TOKEN: - if self.offsetToken + 1 > self.tokenCount: - self.log.error('Attempt to read past end of input data') - sys.exit(2) - tokenType = self.TokenTable[self.offsetToken+1] - self.log.debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType)) - return self.token2Type(tokenType, True) - self.log.debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType)) - return self.token2Type(tokenType, False) - - def isEndOfData(self): - if self.offsetToken == self.tokenCount: - return True - return False - - def isEndOfStruct(self): - if self.isEndOfData(): - self.log.debug("isEndOfData") - return True - elif len(self.ReadingStructStack) == 0: - self.log.debug("ReadingStructStack") - return False - elif self.peekNextTokenType() != TType.STRUCT_END: - self.log.debug("peekNextTokenType != TType.STRUCT_END") - return False - elif self.ReadingStructStack[-1] != self.peekNextToken(): - self.log.error("Opening and closing structure tokens don't match") - sys.exit(2) - self.log.debug("isEndOfStruct") - return True - - def isStartOfStruct(self): - if self.peekNextTokenType() == TType.STRUCT_BEGIN: - return True - return False - - def popStructBegin(self, token): - if self.peekNextToken() != token: - self.log.error('Attempting to enter a structure with the wrong delimiting token') - sys.exit(2) - if self.peekNextTokenType() != TType.STRUCT_BEGIN: - self.log.error('Attempting to leave a structure with the wrong delimiting token type') - sys.exit(2) - self.ReadingStructStack.append(token) - self.offsetToken += 1 - - def popStructEnd(self, token): - if len(self.ReadingStructStack) == 0: - self.log.error('Attempting to pop end of a structure with nothing left in the open structure stack') - sys.exit(2) - nextToken = self.peekNextToken() - topToken = self.ReadingStructStack[-1] - if topToken != token: - self.log.error('Attempting to pop end of a structure with the wrong delimiting token') - sys.exit(2) - if nextToken != token: - self.log.error('Attempting to pop end of a structure with the wrong delimiting token') - sys.exit(2) - if self.peekNextTokenType() != TType.STRUCT_END: - self.log.error('Attempting to leave a structure with the wrong delimiting token type') - sys.exit(2) - del self.ReadingStructStack[-1] - self.offsetToken += 1 - - # ---------------- Manipulate StringTable ---------------- - def lookupString(self, idx): - if idx >= self.stringCount: - self.log.error("Attempting to access past end of string table") - sys.exit(2) - return self.StringTable[idx] - - # ---------------- Manipulate Arg ---------------- - def peekNextArg(self): # persistent_data_limit.h:339 CPersistentDataRecord::peekNextArg(CPersistentDataRecord::CArg& result) const - _type = self.peekNextTokenType() - result = CArg() - result.write_Type(_type) - result.write_Type64(False) - self.log.debug("peekNextArg - Type:%d ArgOffset:%d" % (_type, self.ArgOffset)) - if result.isExtended(): - self.log.debug("Extended") - result.write_i32_1(self.ArgTable[self.ArgOffset]) - result.write_i32_2(self.ArgTable[self.ArgOffset+1]) - if result.read_Type() == TType.EXTEND_TYPE and result.read_ExType() == TExtendType.ET_64_BIT_EXTENDED_TYPES: - result.write_ex32_2(self.ArgTable[self.ArgOffset+2]); - result.write_Type64(True) - elif not result.isFlag(): - # result._Value.i32_1 = _ArgTable[_ArgOffset]; - result.write_i32_1(self.ArgTable[self.ArgOffset]) - self.log.debug("peekNextArg - id :%d" % result.read_i32_1()) - if result.read_Type() == TType.STRING: - result.write_String(self.lookupString(result.read_i32_1())) - self.log.debug("peekNextArg - String:%s" % result.read_String()) - return result - - def popNextArg(self, token): # persistent_data_limit.h:414 CPersistentDataRecord::popNextArg(TToken token,CPersistentDataRecord::CArg& result) - result = self.peekNextArg() - if result.isFlag(): - self.offsetToken += 1 - elif result.isExtended(): - self.ArgOffset += 2 - self.offsetToken += 2 - if result.read_Type() == TType.EXTEND_TYPE and result.read_ExType() == TExtendType.ET_64_BIT_EXTENDED_TYPES: - self.ArgOffset += 1 - self.offsetToken += 1 - else: - self.ArgOffset += 1 - self.offsetToken += 1 - self.log.debug("popNextArg - Arg:%d", result.read_i32_1()) - return result - - def popString(self, token): - TempArg = self.popNextArg(token) - return TempArg.asString() - - def popUint32(self, token): - TempArg = self.popNextArg(token) - return TempArg.asUint() - - def popBool(self, token): - TempArg = self.popNextArg(token) - return TempArg.asUint() != 0 - - # ---------------- Read Data ---------------- - def readFromBinFile(self, filename): # persistent_data.cpp:835 # bool CPersistentDataRecord::fromBuffer(const char *src, uint32 bufferSize) - self.log.debug('Read Bin File %s' % filename) - with open(filename, "rb") as fp: - buffer = fp.read() - fp.close() - - self.version = int.from_bytes(buffer[0:4], byteorder='little', signed=False) - self.totalSize = int.from_bytes(buffer[4:8], byteorder='little', signed=False) - self.tokenCount = int.from_bytes(buffer[8:12], byteorder='little', signed=False) - self.argCount = int.from_bytes(buffer[12:16], byteorder='little', signed=False) - self.stringCount = int.from_bytes(buffer[16:20], byteorder='little', signed=False) - self.stringsSize = int.from_bytes(buffer[20:24], byteorder='little', signed=False) - offset = 24 - self.log.debug("version:%d, totalSize:%d, tokenCount:%d, argCount:%d, stringCount:%d, stringsSize:%d" % (self.version, self.totalSize, self.tokenCount, self.argCount, self.stringCount, self.stringsSize)) - if len(buffer) != self.totalSize: - self.log.error("Failed to parse buffer due to invalid header (file:%s, size:%d, size define:%d)" % (filename, len(buffer), self.totalSize )) - sys.exit(2) - if self.version > 0: - self.log.error("PDR ERROR: Wrong file format version! (file:%s, version:%d)" % (filename, self.version)) - sys.exit(2) - if (self.stringCount != 0 and self.stringsSize == 0) or (self.stringCount == 0 and self.stringsSize != 0): - self.log.error("PDR ERROR: Invalid string table parameters! (file:%s, stringCount:%d, stringsSize:%d)" % (filename, self.stringCount, self.stringsSize)) - sys.exit(2) - # i = offset+tokenCount*sizeof(TToken)+argCount*sizeof(uint32)+stringsSize - i = offset + self.tokenCount * 2 + self.argCount * 4 + self.stringsSize; - if self.totalSize != i: - self.log.error("PDR ERROR: Invalid source data (file:%s, totalSize:%d != datasize:%s)" % (filename, self.totalSize, i)) - sys.exit(2) - - # READ the tokens - self.TokenTable = [] - for i in range(0, self.tokenCount): - tmp = int.from_bytes(buffer[offset:offset+2], byteorder='little', signed=False) - self.log.debug("token %5d => %3d id:%3d type:%d" %(i, tmp, tmp // 8, tmp & 7)) - self.TokenTable.append(tmp) - offset += 2 - - # READ the arguments - self.ArgTable = [] - for i in range(0, self.argCount): - tmp = int.from_bytes(buffer[offset:offset+4], byteorder='little', signed=False) - self.ArgTable.append(tmp) - offset += 4 - - # READ the string table data - if self.stringsSize != 0: - chaine = '' - self.StringTable = [ ] - while offset < self.totalSize: - car = buffer[offset:offset+1].decode() - if car != '\0': - chaine += car - else: - self.StringTable.append(chaine) - chaine = '' - offset += 1 - self.log.debug(self.StringTable) - if chaine != '': - self.log.error("PDR ERROR: Too few strings found in string table (file:%s)" % (filename)) - sys.exit(2) - self.log.debug("Red %s" % filename) - - def decrypt_token(self): - i = 0 - lvl = 0 - posArg = 0 - extend = False - extend64 = False - result = CArg() - - print("^ Position ^ Token ^") - for value in self.TokenTable: - print("| %5d | %3d |" %(i, value)) - i += 1 - - i = 0 - print("^ Position ^ Argument ^") - for value in self.ArgTable: - print("| %5d | %3d |" %(i, value)) - i += 1 - - i = 0 - print("^ Position ^ String ^") - for value in self.StringTable: - print("| %5d | %s |" %(i, value)) - i += 1 - - i = 0 - print("^ Position ^ Niveau ^ Token ^ Token ID ^^ Token Type (Card) ^^^ Result ^") - print("^ ^^ (entrée) ^ Valeur ^ Quoi ^ Valeur ^ Card ^ Type ^ ^") - for token in self.TokenTable: - tokenId = token // 8 - tokenTypeValue = token & 7 - result.write_String("-") - if tokenTypeValue == 0: - tokenCard = 'BEGIN_TOKEN' - tokenType = 'STRUCT_BEGIN' - result.write_Type(TType.STRUCT_BEGIN) - if lvl <= 1: - print("| |||||||") - lvl += 1 - elif tokenTypeValue == 1: - tokenCard = 'END_TOKEN' - tokenType = 'STRUCT_END' - result.write_Type(TType.STRUCT_END) - extend = False - extend64 = False - elif tokenTypeValue == 2: - tokenCard = 'SINT_TOKEN' - if extend: - tokenType = 'SINT64' - result.write_Type(TType.SINT64) - result.write_i32_1(self.ArgTable[posArg]) - result.write_i32_2(self.ArgTable[posArg+1]) - if extend64: - result.write_ex32_2(self.ArgTable[posArg+2]); - posArg += 3 - else: - posArg += 2 - else: - tokenType = 'SINT32' - result.write_Type(TType.SINT32) - result.write_i32_1(self.ArgTable[posArg]) - posArg += 1 - extend = False - extend64 = False - elif tokenTypeValue == 3: - tokenCard = 'UINT_TOKEN' - if extend: - tokenType = 'UINT64' - result.write_Type(TType.UINT64) - result.write_i32_1(self.ArgTable[posArg]) - result.write_i32_2(self.ArgTable[posArg+1]) - if extend64: - result.write_ex32_2(self.ArgTable[posArg+2]); - posArg += 3 - else: - posArg += 2 - else: - tokenType = 'UINT32' - result.write_Type(TType.UINT32) - result.write_i32_1(self.ArgTable[posArg]) - posArg += 1 - extend = False - extend64 = False - elif tokenTypeValue == 4: - tokenCard = 'FLOAT_TOKEN' - if extend: - tokenType = 'FLOAT64' - result.write_Type(TType.FLOAT64) - result.write_i32_1(self.ArgTable[posArg]) - result.write_i32_2(self.ArgTable[posArg+1]) - if extend64: - result.write_ex32_2(self.ArgTable[posArg+2]); - posArg += 3 - else: - posArg += 2 - else: - tokenType = 'FLOAT32' - result.write_Type(TType.FLOAT32) - result.write_i32_1(self.ArgTable[posArg]) - posArg += 1 - extend = False - extend64 = False - elif tokenTypeValue == 5: - tokenCard = 'STRING_TOKEN' - if extend: - tokenType = 'EXTEND_TYPE' - result.write_Type(TType.EXTEND_TYPE) - result.write_i32_1(self.ArgTable[posArg]) - result.write_i32_2(self.ArgTable[posArg+1]) - if extend64: - result.write_ex32_2(self.ArgTable[posArg+2]); - posArg += 3 - else: - posArg += 2 - else: - tokenType = 'STRING' - result.write_Type(TType.STRING) - result.write_i32_1(self.ArgTable[posArg]) - tmp = result.read_i32_1() - result.write_String(self.StringTable[tmp]) - posArg += 1 - extend = False - extend64 = False - elif tokenType == 6: - tokenCard = 'FLAG_TOKEN' - tokenType = 'FLAG' - result.write_Type(TType.FLAG) - extend = False - extend64 = False - elif tokenTypeValue == 7: - if extend: - extend64 = True - tokenCard = 'EXTEND_TOKEN' - result.write_Type(TType.EXTEND_TYPE) - tokenType = '' - extend = True - # print("token %5d => %3d id:%3d [%s] type:%d [%s]" %(i, token, tokenId, self.StringTable[tokenId], tokenType, tokenCard)) - print("| %5d | %3d | %3d | %3d | %s | %d | %s | %s | %s |" %(i, lvl, token, tokenId, self.StringTable[tokenId], tokenTypeValue, tokenCard , tokenType, result)) - if tokenTypeValue == 1: - lvl -= 1 - i += 1 - - def addString(self, name): # persistent_data.cpp:100 uint16 CPersistentDataRecord::addString(const string& name) - for i in range(0, len(self.StringTable)): - if self.StringTable[i] == name: - return i - self.StringTable.append(name) - return len(self.StringTable) - 1 - - def CProductDescriptionForClient_apply(self): # persistent_data_template.h:459 # void PERSISTENT_CLASS::apply(CPersistentDataRecord &pdr _PERSISTENT_APPLY_ARGS) - __Tok__MapKey = self.addString("__Key__") - __Tok__MapVal = self.addString("__Val__") - __Tok_Files = self.addString("_Files") - __Tok_Categories = self.addString("_Categories") - self.log.debug("MapKey:%d, MapVal:%d, Files:%d, Categories:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files, __Tok_Categories)) - while not self.isEndOfStruct(): - nextToken = self.peekNextToken() - self.log.debug("nextToken:%d" % (nextToken)) - if nextToken == __Tok_Files: - self.popStructBegin(__Tok_Files) - self.CBNPFileSet_apply() - self.popStructEnd(__Tok_Files) - continue - elif nextToken == __Tok_Categories: - self.popStructBegin(__Tok_Categories) - # (_Categories).apply(pdr); - self.CBNPCategorySet_apply() - self.popStructEnd(__Tok_Categories) - continue - self.log.error("TODO") - sys.exit(2) - - def CBNPFileSet_apply(self): - __Tok__MapKey = self.addString("__Key__") - __Tok__MapVal = self.addString("__Val__") - __Tok_Files = self.addString("_Files") - self.log.debug("MapKey:%d, MapVal:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files)) - while not self.isEndOfStruct(): - nextToken = self.peekNextToken() - self.log.debug("nextToken:%d" % (nextToken)) - if nextToken == __Tok_Files: - self.popStructBegin(__Tok_Files) - self.CBNPFile.append(CBNPFile()) - self.CBNPFile_apply(self.CBNPFile[-1]) - self.popStructEnd(__Tok_Files) - continue - self.log.error("TODO") - sys.exit(2) - - def CBNPFile_apply(self, _CBNPFile): - __Tok__MapKey = self.addString("__Key__") - __Tok__MapVal = self.addString("__Val__") - __Tok_FileName = self.addString("_FileName") - __Tok_Versions = self.addString("_Versions") - _FileName = None - self.log.debug("MapKey:%d, MapVal:%d, Filename:%d, Versions:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_FileName, __Tok_Versions)) - while not self.isEndOfStruct(): - nextToken = self.peekNextToken() - self.log.debug("nextToken:%d" % (nextToken)) - if nextToken == __Tok_FileName: - _FileName = self.popString(nextToken) - _CBNPFile.FileName = _FileName - self.log.debug("filename: %s" % _FileName) - continue - if nextToken == __Tok_Versions: - self.popStructBegin(__Tok_Versions) - # vectAppend(_Versions).apply(pdr); - _CBNPFile.Versions.append(CBNPFileVersion()) - self.CBNPFileVersion_apply(_CBNPFile.Versions[-1]) - self.popStructEnd(__Tok_Versions) - continue - stack = [] - while True: - if self.isStartOfStruct(): - stack.append(self.peekNextToken()) - self.popStructBegin(stack) - elif self.isEndOfStruct(): - self.popStructEnd(stack[-1]) - if len(stack) > 0: - del stack[-1] - else: - self.popNextArg(self.peekNextToken()) - if self.isEndOfData() and len(stack) == 0: - break - self.log.debug("CBNPFile: %s" % _CBNPFile) - - def CBNPFileVersion_apply(self, _CBNPFileVersion): # persistent_data_template.h:459 # void CBNPFileVersion::apply(CPersistentDataRecord &pdr ) - __Tok__MapKey = self.addString("__Key__") - __Tok__MapVal = self.addString("__Val__") - __Tok_VersionNumber = self.addString("_VersionNumber") - __Tok_FileSize = self.addString("_FileSize") - __Tok_7ZFileSize = self.addString("_7ZFileSize") - __Tok_FileTime = self.addString("_FileTime") - __Tok_PatchSize = self.addString("_PatchSize") - __Tok_HashKey = self.addString("_HashKey") - self.log.debug("MapKey:%d, MapVal:%d, VersionNumber:%d, FileSize:%d, 7ZFileSize:%d, FileTime:%d, PatchSize:%d, HashKey:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_VersionNumber, __Tok_FileSize, __Tok_7ZFileSize, __Tok_FileTime, __Tok_PatchSize, __Tok_HashKey)) - - while not self.isEndOfStruct(): - nextToken = self.peekNextToken() - self.log.debug("nextToken:%d" % (nextToken)) - if nextToken == __Tok_VersionNumber: - self.log.debug("__Tok_VersionNumber") - _CBNPFileVersion.VersionNumber = self.popUint32(__Tok_VersionNumber) - self.log.debug("VersionNumber: %s" % _CBNPFileVersion.VersionNumber) - continue - elif nextToken == __Tok_FileSize: - self.log.debug("__Tok_FileSize") - _CBNPFileVersion.FileSize = self.popUint32(__Tok_FileSize) - self.log.debug("FileSize: %s" % _CBNPFileVersion.FileSize) - continue - elif nextToken == __Tok_7ZFileSize: - self.log.debug("__Tok_7ZFileSize") - _CBNPFileVersion.v7ZFileSize = self.popUint32(__Tok_7ZFileSize) - self.log.debug("7ZFileSize: %s" % _CBNPFileVersion.v7ZFileSize) - continue - elif nextToken == __Tok_FileTime: - self.log.debug("__Tok_FileTime") - _CBNPFileVersion.FileTime = self.popUint32(__Tok_FileTime) - self.log.debug("FileTime: %s" % _CBNPFileVersion.FileTime) - continue - elif nextToken == __Tok_PatchSize: - self.log.debug("__Tok_PatchSize") - _CBNPFileVersion.PatchSize = self.popUint32(__Tok_PatchSize) - self.log.debug("PatchSize: %s" % _CBNPFileVersion.PatchSize) - continue - elif nextToken == __Tok_HashKey: - self.log.debug("__Tok_HashKey") - _CBNPFileVersion.HashKey.append(self.popUint32(__Tok_HashKey)) - self.log.debug("HashKey: %s" % _CBNPFileVersion.HashKey[-1]) - continue - # Vidage des autres clefs (inconnues) - stack = [] - while True: - if self.isStartOfStruct(): - stack.append(self.peekNextToken()) - self.popStructBegin(stack) - elif self.isEndOfStruct(): - self.popStructEnd(stack[-1]) - if len(stack) > 0: - del stack[-1] - else: - self.popNextArg(self.peekNextToken()) - if self.isEndOfData() and len(stack) == 0: - break - - def CBNPCategorySet_apply(self): # persistent_data_template.h:459 # void CBNPCategorySet::apply(CPersistentDataRecord &pdr ) - #__Tok__MapKey = self.addString("__Key__") - #__Tok__MapVal = self.addString("__Val__") - __Tok_Category = self.addString("_Category") - while not self.isEndOfStruct(): - nextToken = self.peekNextToken() - self.log.debug("nextToken:%d" % (nextToken)) - if nextToken == __Tok_Category: - self.log.debug("__Tok_Category") - self.popStructBegin(__Tok_Category) - self.Categories.append(CBNPCategorySet()) - self.CBNPCategory_apply(self.Categories[-1]) - self.popStructEnd(__Tok_Category) - continue - # Vidage des autres clefs (inconnues) - stack = [] - while True: - if self.isStartOfStruct(): - stack.append(self.peekNextToken()) - self.popStructBegin(stack) - elif self.isEndOfStruct(): - self.popStructEnd(stack[-1]) - if len(stack) > 0: - del stack[-1] - else: - self.popNextArg(self.peekNextToken()) - if self.isEndOfData() and len(stack) == 0: - break - - - def CBNPCategory_apply(self, _CBNPCategory): # persistent_data_template.h:459 # void CBNPCategory::apply(CPersistentDataRecord &pdr ) - __Tok__MapKey = self.addString("__Key__") - __Tok__MapVal = self.addString("__Val__") - __Tok_Name = self.addString("_Name") - __Tok_IsOptional = self.addString("_IsOptional") - __Tok_UnpackTo = self.addString("_UnpackTo") - __Tok_IsIncremental = self.addString("_IsIncremental") - __Tok_CatRequired = self.addString("_CatRequired") - __Tok_Hidden = self.addString("_Hidden") - __Tok_Files = self.addString("_Files") - self.log.debug("MapKey:%d, MapVal:%d, Name:%d, IsOptional:%d, UnpackTo:%d, IsIncremental:%d, CatRequired:%d, Hidden:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Name, __Tok_IsOptional, __Tok_UnpackTo, __Tok_IsIncremental, __Tok_CatRequired, __Tok_Hidden, __Tok_Files)) - while not self.isEndOfStruct(): - nextToken = self.peekNextToken() - self.log.debug("nextToken:%d" % (nextToken)) - if nextToken == __Tok_Name: - self.log.debug("__Tok_Name") - _CBNPCategory._Name = self.popString(nextToken) - self.log.debug("_Name: %s" % _CBNPCategory._Name) - continue - elif nextToken == __Tok_IsOptional: - self.log.debug("__Tok_IsOptional") - _CBNPCategory._IsOptional = self.popBool(nextToken) - self.log.debug("_IsOptional: %s" % str(_CBNPCategory._IsOptional)) - continue - elif nextToken == __Tok_UnpackTo: - self.log.debug("__Tok_UnpackTo") - _CBNPCategory._UnpackTo = self.popString(nextToken) - self.log.debug("_UnpackTo: %s" % str(_CBNPCategory._UnpackTo)) - continue - elif nextToken == __Tok_IsIncremental: - self.log.debug("__Tok_IsIncremental") - _CBNPCategory._IsIncremental = self.popBool(nextToken) - self.log.debug("_IsIncremental: %s" % str(_CBNPCategory._IsIncremental)) - continue - elif nextToken == __Tok_CatRequired: - self.log.debug("__Tok_CatRequired") - _CBNPCategory._CatRequired = self.popString(nextToken) - self.log.debug("_CatRequired: %s" % str(_CBNPCategory._CatRequired)) - continue - elif nextToken == __Tok_Hidden: - self.log.debug("__Tok_Hidden") - _CBNPCategory._Hidden = self.popBool(nextToken) - self.log.debug("_Hidden: %s" % str(_CBNPCategory._Hidden)) - continue - elif nextToken == __Tok_Files: - self.log.debug("__Tok_Files") - _CBNPCategory._Files.append(self.popString(nextToken)) - self.log.debug("_Files: %s" % str(_CBNPCategory._Files)) - continue - # Vidage des autres clefs (inconnues) - stack = [] - while True: - if self.isStartOfStruct(): - stack.append(self.peekNextToken()) - self.popStructBegin(stack) - elif self.isEndOfStruct(): - self.popStructEnd(stack[-1]) - if len(stack) > 0: - del stack[-1] - else: - self.popNextArg(self.peekNextToken()) - if self.isEndOfData() and len(stack) == 0: - break - - -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): - self.log.debug("execute") - head = self.msgXml - listpath = [] - while True: - nbBit = getPowerOf2(len(head)) - id = msgin.readSerial(nbBit, name='MsgXML', typeName='Number', emulate=True) - - ele = head[id] - name = ele.attrib['name'] - listpath.append(name) - fullname = ':'.join(listpath) - - id = msgin.readSerial(nbBit, name='MsgXML', typeName='XML <' + name + '>') - self.log.debug(fullname) - if fullname in self.GenericMsgHeaderMngr: - self.log.debug("Found : %s" % fullname) - self.GenericMsgHeaderMngr[fullname](msgin) - self.log.debug("MessageXML decoded: %s" % msgin.showAllData() ) - 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 ) - self.log.debug("MessageXML decoded: %s" % msgin.showAllData() ) - return False - self.log.debug("MessageXML decoded: %s" % msgin.showAllData() ) - return False - - def loadMsg(self, msgXml): - self.msgXml = msgXml - - def loadDatabase(self, databaseXml): - self.databaseXml = databaseXml - -class CAction: - 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 - - def pack(self, msgout): - if self.Code < 4: - modeShort = True - msgout.pushBool(modeShort) - code = self.Code - msgout.internalSerial(self.Code, 2) - else: - modeShort = False - msgout.pushBool(modeShort) - code = self.Code - msgout.pushUint8(code) - - def serialIn(self, msgin): - raise RuntimeError - - def serialOut(self, msgout): - raise RuntimeError - - def size(self): - headerBitSize = 0 - if self.Code < 4: - headerBitSize = 1 + 2 - else: - headerBitSize = 1 + (1 * 8) - return headerBitSize - - def getMaxSizeInBit(self): - raise RuntimeError - - def setPriority(self, prio): - raise RuntimeError - - def priority(self): - raise RuntimeError - - def getValue(self): - raise RuntimeError - - def setValue(self, value): - raise RuntimeError - - def isContinuous(self): - raise RuntimeError - - def reset(self): - raise RuntimeError - - def __str__(self): - return "[%d,%d]" % (self.Code , self.Slot) - -class CActionPosition(CAction): - 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, world): - super().__init__(slot, code, world) - - def __str__(self): - return "CActionSync" + super().__str__() - -class CActionDisconnection(CAction): - 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, world): - super().__init__(slot, code, world) - - def __str__(self): - return "CActionAssociation" + super().__str__() - -class CActionDummy(CAction): - 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, world): - super().__init__(slot, code, world) - - def __str__(self): - return "CActionLogin" + super().__str__() - -class CActionTargetSlot(CAction): - 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, world): - super().__init__(slot, code, world) - self._Message = None - - def set(self, message): - self._Message = message - - def unpack(self, message): - size = message.readUint32('size') - self._Message = message.readBitStreamUint8(size, 'message') - - def pack(self, msgout): - super().pack(msgout) - sizeMessage = len(self._Message) - msgout.pushUint32(sizeMessage) - msgout.pushBuffer(self._Message) - - def reset(self): - self._Message = None - - def genericAction(self, decodeImpulse): - decodeImpulse.execute(self._Message) - - def __str__(self): - return "CActionGeneric" + super().__str__() + "[read:" + self._Message.showAllData() + '/write:' + self._Message.showAllDataWrite() + ']' - - def size(self): - size = super().size() - if self._Message: - return 4 + len(self._Message) * 8 + size - else: - return 4 + size - -class CActionGenericMultiPart(CAction): - ''' - khanat-opennel-code/code/ryzom/common/src/game_share/action_generic_multi_part.h # class CActionGenericMultiPart - ''' - def __init__(self, slot, code, world): - super().__init__(slot, code, world) - self.PartCont = [] - self.Number = 0 - self.Part = 0 - self.NbBlock = 0 - - def set(self, number, part, buffer, bytelen, size, nbBlock): - ''' - khanat-opennel-code/code/ryzom/common/src/game_share/action_generic_multi_part.h # void set (uint8 number, uint16 part, const uint8 *buffer, uint32 bytelen, uint32 size, uint16 nbBlock) - ''' - logging.getLogger('myLogger').debug("number:%d part:%d bytelen:%d size:%d nbBlock:%d" %(number, part, bytelen, size, nbBlock)) - start = part*size - end = start + size - logging.getLogger('myLogger').debug("start:%d end:%d bytelen:%d" % (start, end, bytelen)) - if end > bytelen: - end = bytelen - - logging.getLogger('myLogger').debug("start:%d end:%d" % (start, end)) - self.PartCont = [] - # memcpy( &PartCont[0], buffer + start, end - start ); - for i in range(start, end): - logging.getLogger('myLogger').debug("Append : %d/%d" % (i, len(buffer))) - self.PartCont.append(buffer[i]) - self.Number = number - self.Part = part - self.NbBlock = nbBlock - - def unpack(self, message): - self.Number = message.readUint8('Number') - self.Part = message.readUint16('Part') - self.NbBlock = message.readUint16('NbBlock') - - size = message.readUint32('size') - self.PartCont = message.readBitStreamUint8(size, 'PartCont') - self.log = logging.getLogger('myLogger') - self.log.debug("unpack - Number:%d Part:%d NbBlock:%d" % (self.Number, self.Part, self.NbBlock)) - - def pack(self, msgout): - super().pack(msgout) - msgout.pushUint8(self.Number) - msgout.pushUint16(self.Part) - msgout.pushUint16(self.NbBlock) - msgout.pushArrayUint8(self.PartCont) - - 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)) - decodeImpulse.GenericMultiPartTemp.addGenericMultiPartTemp(self.Number) - decodeImpulse.GenericMultiPartTemp.setGenericMultiPartTemp(self.Number, self.Part, self.NbBlock, self.PartCont, decodeImpulse) - - def __str__(self): - return "CActionGenericMultiPart" + super().__str__() + "[" + str(self.Number) + ',' + str(self.Part) + ',' + str(self.NbBlock) + ',read:' + self.PartCont.showAllData() + ',write:' + self.PartCont.showAllDataWrite() + ']' - - def size(self): - size = super().size() - bytesize = 1 + 2 + 2 + 4 # header - # self.PartCont - for ele in self.PartCont: - bytesize += len(ele) - return bytesize * 8 + size - - -class CActionSint64(CAction): - def __init__(self, slot, code, world): - super().__init__(slot, code, world) - - def __str__(self): - return "CActionSint64" + super().__str__() - -class CActionFactory: - 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, []) - 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, self.world) - elif code == TActionCode.ACTION_GENERIC_CODE: - self.log.debug("Create CActionGeneric") - return CActionGeneric(slot, code, self.world) - elif code == TActionCode.ACTION_GENERIC_MULTI_PART_CODE: - self.log.debug("Create CActionGenericMultiPart") - return CActionGenericMultiPart(slot, code, self.world) - elif code == TActionCode.ACTION_SINT64: - self.log.debug("Create CActionSint64") - return CActionSint64(slot, code, self.world) - elif code == TActionCode.ACTION_SYNC_CODE: - self.log.debug("Create CActionSync") - return CActionSync(slot, code, self.world) - elif code == TActionCode.ACTION_DISCONNECTION_CODE: - self.log.debug("Create CActionDisconnection") - return CActionDisconnection(slot, code, self.world) - elif code == TActionCode.ACTION_ASSOCIATION_CODE: - self.log.debug("Create CActionAssociation") - return CActionAssociation(slot, code, self.world) - elif code == TActionCode.ACTION_LOGIN_CODE: - self.log.debug("Create CActionLogin") - return CActionLogin(slot, code, self.world) - elif code == TActionCode.ACTION_TARGET_SLOT_CODE: - self.log.debug("Create CActionTargetSlot") - return CActionTargetSlot(slot, code, self.world) - elif code == TActionCode.ACTION_DUMMY_CODE: - self.log.debug("Create CActionDummy") - return CActionDummy(slot, code, self.world) - 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 */ ) - ''' - if msgin.needRead() >= 8: - shortcode = msgin.readBool('shortcode') - if shortcode: - code = msgin.readSerial(2, 'code') - else: - code = msgin.readUint8('code') - action = self.create(INVALID_SLOT, code) - if action: - try: - action.unpack (msgin); - except RuntimeError: - log = logging.getLogger('myLogger') - log.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) - return action - - -class CActionBlock: - def __init__(self): - self.Cycle = 0 - self.FirstPacket = 0 - self.Actions = [] - self.Success = True - - def serial(self, msgout, actionFactory): - msgout.pushUint32(self.Cycle) - msgout.pushUint8(len(self.Actions)) - for action in self.Actions: - # msgPosBefore = msgout.getPosInBit() - actionFactory.pack(action, msgout) - # msgPosAfter = msgout.getPosInBit() - # actionSize = actionFactory.size(action) - - def writeSerial(self, msgout): - msgout.pushUint32(self.Cycle) - numberActions = len(self.Actions) - msgout.pushUint8(numberActions) - for action in self.Actions: - action.pack(msgout) - - def insert(self, actions, begin, end): - for i in range(0, end): - if i>= begin: - self.Actions.append(actions[i]) - - def eraseToEnd(self, begin): - while len(self.Actions) >= begin: - self.Actions.pop() - - 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]) + "]" - - -class CImpulseDecoder: - ''' - see : khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp - ''' - def __init__(self, log, world): - self.log = log - self.world = world - self.reset() - 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: - lAck = self._LastAck0 - channel = 0 - elif level == 1: - lAck = self._LastAck1 - channel = receivedPacket & 1 - elif level == 2: - lAck = self._LastAck2 - channel = receivedPacket & 3 - keep = True - checkOnce = False - num = 0 - self.log.debug("level:%d channel:%d lAck:%s" %(level, channel, ':'.join([str(x) for x in lAck]))) - # lastAck = lAck[channel] - while True: - next = msgin.readBool('next:' + str(level) + ':' + str(channel)) - if not next: - break - if not checkOnce: - checkOnce = True - keep = receivedAck >= lAck[channel] - self.log.debug("keep:%s [%d => %d]" % (str(keep), receivedAck, lAck[channel])) - 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: - self.log.debug("keep") - actions.append(action) - elif action: - self.log.debug("append") - self.removeCAction(action) - return actions - - def reset(self): - self._LastAck0 = [-1] - self._LastAck1 = [-1, -1] - self._LastAck2 = [-1, -1, -1, -1] - -class TStampQueue: - def __init__(self, first=None, second=None): - self.first = first - self.second = second - -class ClientNetworkConnection: - ''' - Partie client de la gestion de la communication reseau avec le serveur: - - client : - code/ryzom/client/src/network_connection.cpp - server : - khanat-opennel-code/code/ryzom/server/src/frontend_service/fe_receive_sub.cpp # void CFeReceiveSub::handleReceivedMsg( CClientHost *clienthost ) - ''' - def __init__(self, - khanat_host, - khanat_port_frontend, - headAccount, - LanguageCode="fr", - checkMessageNumber = True): - self.log = logging.getLogger('myLogger') - self._CurrentSendNumber = 0 - self.GenericMsgHeaderMngr = {} - self.GenericMultiPartTemp = GenericMultiPartTemp(self.log) - self.LanguageCode = LanguageCode - self._QuitId = 0 - self._ConnectionState = TConnectionState.NotInitialised - self.UserAddr, self.UserKey, self.UserId = None, None, None - self.frontend = (khanat_host, khanat_port_frontend) - self._sock = None - self._CurrentReceivedNumber = 0 - self._SystemMode = 0 - self._LastReceivedAck = 0 - self._LastReceivedNumber = 0 - self._LastAckInLongAck = 0 - self._MsgXmlMD5 = None - self._DatabaseXmlMD5 = None - self.msgXml = None - self.databaseXml = None - self._Synchronize = 0 - self._LatestSync = 0 - self._CurrentServerTick = 0 - self._MsPerTick = 0 - self._LCT = 100 - self._UpdateTime = 0 - #self._UpdateTicks = 0 - self._ReceivedSync = False - self._LastReceivedTime = 0 - self._LastReceivedPacketInBothModes = 0 - self._TotalMessages = 0 - self._TotalLostPackets = 0 - self.checkMessageNumber = checkMessageNumber - self._LastAckBit = 0 - self._AckBitMask = 0 - self._LongAckBitField = CBitSet() - self._LatestSyncTime = 0 - self.world = World(self.log, headAccount) - self._ImpulseDecoder = CImpulseDecoder(self.log, self.world) - 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, self.world, self.GenericMsgHeaderMngr, self.GenericMultiPartTemp) - self._InstantPing = 0 - self._BestPing = 10000 - self._MsPerTick = 100 - self._LastSendTime = 0 - self._ImpulseMultiPartNumber = 0 - self.clientTick = 0 - self.stepAction = 0 - - def signal_exit(self, sig, frame): - self.log.warning("Receive signal to quit program") - self.sendSystemQuit() - sys.exit(0) - - def connect(self): - signal.signal(signal.SIGINT, self.signal_exit) - signal.signal(signal.SIGTERM, self.signal_exit) - try: - self._sock = socket.socket(socket.AF_INET, # Internet - socket.SOCK_DGRAM) # UDP - except: - self.log.error("Impossible to connect on khanat") - return False - self._ConnectionState = TConnectionState.Login - self._LatestSyncTime = int(time.clock_gettime(1)*1000) - return True - - def cookiesInit(self, UserAddr, UserKey, UserId): - self.UserAddr = UserAddr - self.UserKey = UserKey - self.UserId = UserId - - def reset(self): - self._CurrentSendNumber += 0 - - def buildSystemHeader(self, msgout): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout) - msgout.pushSint32(self._CurrentSendNumber) - systemMode = True - msgout.pushBool(systemMode) - self._PacketStamps.append( TStampQueue(self._CurrentSendNumber, self._UpdateTime) ) - self._CurrentSendNumber += 1 - - def sendSystemLogin(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemLogin() - self.log.debug("sendSystemLogin") - if self._sock is None: - raise ValueError - msgout = BitStream() - self.buildSystemHeader(msgout) - msgout.pushUint8(CLFECOMMON.SYSTEM_LOGIN_CODE) - msgout.pushUint32(self.UserAddr) - msgout.pushUint32(self.UserKey) - msgout.pushUint32(self.UserId) - msgout.pushString(self.LanguageCode) - - self.log.debug("sendSystemLogin:%s" % msgout.showAllDataWrite()) - self._sock.sendto(msgout.toBytes(), self.frontend) - self._CurrentSendNumber += 1 - - self._ConnectionState = TConnectionState.Login - - def sendSystemQuit(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemQuit() - self.log.debug("sendSystemQuit") - # Disconnect - if self._sock is None: - raise ValueError - self._QuitId += 1 - msgout = BitStream() - self.buildSystemHeader(msgout) - msgout.pushUint8(CLFECOMMON.SYSTEM_QUIT_CODE) - msgout.pushSint32(self._QuitId) - self.log.debug("sendSystemQuit:%s" % msgout.showAllDataWrite()) - self._sock.sendto(msgout.toBytes(), self.frontend) - self._ConnectionState = TConnectionState.Quit - - def sendSystemAckSync(self): - ''' - code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemAckSync() - ''' - self.log.debug("sendSystemAckSync _LastReceivedNumber:%d _LastAckInLongAck:%d _LatestSync:%d" % (self._LastReceivedNumber, self._LastAckInLongAck, self._LatestSync)) - msgout = BitStream() - self.buildSystemHeader(msgout) - msgout.pushUint8(CLFECOMMON.SYSTEM_ACK_SYNC_CODE) - msgout.pushSint32(self._LastReceivedNumber) - msgout.pushSint32(self._LastAckInLongAck) - self._LongAckBitField.writeSerial(msgout) - msgout.pushSint32(self._LatestSync) - - self.log.debug("sendSystemAckSync:%s" % msgout.showAllDataWrite()) - self._sock.sendto(msgout.toBytes(), self.frontend) - - self._LatestSyncTime = self._UpdateTime - - def sendSystemAckProbe(self): - self.log.debug("sendSystemAckProbe") - msgout = BitStream() - self.buildSystemHeader(msgout) - msgout.pushUint8(CLFECOMMON.SYSTEM_ACK_PROBE_CODE) - msgout.pushSint32(len(self._LatestProbes)) - for data in self._LatestProbes: - msgout.pushSint32(data) - self._LatestProbes = [] - self.log.debug("sendSystemAckProbe:%s" % msgout.showAllDataWrite()) - self._sock.sendto(msgout.toBytes(), self.frontend) - - - def sendSystemDisconnection(self): - self.log.debug("sendSystemDisconnection") - if self._sock is None: - raise ValueError - msgout = BitStream() - self.buildSystemHeader(msgout) - msgout.pushUint8(CLFECOMMON.SYSTEM_DISCONNECTION_CODE) - self._sock.sendto(msgout.toBytes(), self.frontend) - - def sendNormalMessage(self): - ''' - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendNormalMessage() - ''' - self.log.debug("sendNormalMessage") - if self._sock is None: - raise ValueError - msgout = BitStream() - msgout.pushSint32(self._CurrentSendNumber) - systemMode = False # Normal - msgout.pushBool(systemMode) - msgout.pushSint32(self._LastReceivedNumber) - msgout.pushSint32(self._AckBitMask) - - numPacked = 0 - - for block in self._Actions: - if block.Cycle == 0: - break - - if block.FirstPacket == 0: - block.FirstPacket = self._CurrentSendNumber; - - block.writeSerial(msgout) - numPacked += 1 - if msgout.getPosInBit() > 480*8: # easy version - break - - self.log.debug("Send:%s" % msgout.showAllDataWrite()) - self._sock.sendto(msgout.toBytes(), self.frontend) - - self._LastSendTime = int(time.clock_gettime(1)*1000) - self._PacketStamps.append( TStampQueue(self._CurrentSendNumber, self._UpdateTime) ) - self._CurrentSendNumber += 1 - - def readDelta(self, msg): - propertyCount = msg.readUint16('propertyCount') - self.log.debug("propertyCount:%d" % propertyCount) - self.log.debug("TODO") - for _ in range(0, propertyCount): - pass - - 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) - return data, addr - - def decodeHeader(self, msg): - ''' - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::decodeHeader(CBitMemStream &msgin, bool checkMessageNumber) - ''' - self._TotalMessages += 1 - self._LastReceivedTime = self._UpdateTime - self._CurrentReceivedNumber = msg.readSint32('CurrentReceivedNumber') - self._SystemMode = msg.readBool('SystemMode') - - 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 - - if not self._SystemMode: - self._LastReceivedAck = msg.readSint32('LastReceivedAck'); - self.log.debug("Normal Mode _LastReceivedAck:%d" % self._LastReceivedAck) - else: - self.log.debug("System Mode") - - if self._CurrentReceivedNumber > self._LastReceivedNumber+1: - self.log.debug("lost messages server->client [%d; %d]" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber - 1)) - elif self._CurrentReceivedNumber == self._LastReceivedNumber: - self.log.debug("awaiting packet %d, received packet %d" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber)) - return False - elif self._CurrentReceivedNumber < self._LastReceivedNumber: - self.log.debug("received an old message, awaiting packet %d, received packet %d" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber)) - return false - ackBool = ( not self._SystemMode ) and (self._ConnectionState == TConnectionState.Connected or self._ConnectionState == TConnectionState.Synchronize) - if ackBool: - ackBit = 1 - else: - ackBit = 0 - if self._CurrentReceivedNumber - self._LastReceivedNumber < 32: - self._AckBitMask <<= self._CurrentReceivedNumber - self._LastReceivedNumber - self._AckBitMask |= self._LastAckBit << (self._CurrentReceivedNumber - self._LastReceivedNumber - 1) - elif (self_CurrentReceivedNumber - self_LastReceivedNumber) == 32 and self._LastAckBit != 0: - self._AckBitMask = 0x80000000 - else: - self._AckBitMask = 0x00000000 - self._LastAckBit = ackBit; - for i in range(self._LastReceivedNumber+1, self._CurrentReceivedNumber): - self._LongAckBitField.clearBit(i & 511) # (512 - 1) mask 9bit - self._LongAckBitField.set(self._CurrentReceivedNumber & 511, ackBool) # (512 - 1) mask 9bit - - self.log.debug("_LastAckInLongAck:%d _CurrentReceivedNumber:%d" % (self._LastAckInLongAck, self._CurrentReceivedNumber)) - if self._LastAckInLongAck <= (self._CurrentReceivedNumber-512): - 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)) - return True - - def receiveSystemProbe(self, msg): - self.log.debug("receiveSystemProbe") - self._LatestProbeTime = self._UpdateTime - self._LatestProbe = msg.readSint32('LatestProbe') - self.log.debug("LatestProbe: %d" % self._LatestProbe) - self._LatestProbes.append(self._LatestProbe) - - def receiveSystemStalled(self, msg): - self.log.debug("received STALLED") - - def receiveSystemSync(self, msg): - self.log.debug("receiveSystemSync") - self._LatestSyncTime = self._UpdateTime - self._Synchronize = msg.readUint32('Synchronize') - stime = msg.readSint64('stime') - self._LatestSync = msg.readUint32('LatestSync') - self.log.debug("%d %d %d" %(self._Synchronize, stime, self._LatestSync)) - # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp : void CNetworkConnection::receiveSystemSync(CBitMemStream &msgin) - MsgData = msg.readArrayUint8(16, 'MsgData') - DatabaseData = msg.readArrayUint8(16, 'DatabaseData') - self.log.debug("MsgData:" + str(MsgData)) - self.log.debug("DatabaseData:" + str(DatabaseData)) - md5Msg = bytes(MsgData) - md5Database = bytes(DatabaseData) - if md5Msg == self._MsgXmlMD5: - self.log.info("Check MD5 msg.xml : OK") - else: - self.log.error("Check MD5 msg.xml : KO") - if md5Database == self._DatabaseXmlMD5: - self.log.info("Check MD5 database.xml : OK") - else: - self.log.error("Check MD5 database.xml : KO") - self._MsPerTick = 100 - self._CurrentServerTick = self._Synchronize + self._CurrentReceivedNumber + 2 - self._CurrentClientTick = self._CurrentServerTick - ( self._LCT + self._MsPerTick ) / self._MsPerTick - self._CurrentClientTime = self._UpdateTime - (self._LCT + self._MsPerTick) - self.sendSystemAckSync() - - def decodeVisualProperties(self, msgin): - ''' - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) - ''' - while True: - if msgin.getPosInBit() + 8*8 > len(msgin)*8: - return - slot = msgin.readUint8('slot') - associationBits = msgin.readUint32('associationBits') - self.log.debug("TODO") - return - - def receiveNormalMessage(self, msgin): - self.log.debug("receiveNormalMessage : 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 actions that are acked - while self._Actions and self._Actions[0].FirstPacket != 0 and self._Actions[0].FirstPacket < self._LastReceivedAck: - self.log.debug("remove old action [%d/%d] : %s" % (self._Actions[0].FirstPacket, self._LastReceivedAck, self._Actions[0])) - self._Actions.pop(0) - - self._CurrentServerTick = self._CurrentReceivedNumber * 2 + self._Synchronize - - # remove useless stamps in queue - while len(self._PacketStamps) != 0 and self._LastReceivedAck > self._PacketStamps[0].first: - self._PacketStamps.pop(0) - - # Statistique ! - if len(self._PacketStamps) == 0 or self._PacketStamps[0].first > self._LastReceivedAck: - pass - else: - ackedPacketTime = self._PacketStamps[0].second - ping = self._UpdateTime - ackedPacketTime - self._InstantPing = ping - if ping < self._BestPing: - self._BestPing = ping - earliest = ackedPacketTime + self._BestPing//2 - latest = self._UpdateTime - self._BestPing//2 - - numStepTick = self._CurrentServerTick - self._CurrentClientTick - if numStepTick > 0 and earliest > self._CurrentClientTime and latest > self._CurrentClientTime: - if self._CurrentClientTime + self._MsPerTick * numStepTick < earliest: - self._MsPerTick = (earliest - self._CurrentClientTime)//numStepTick - if (self._CurrentClientTime + self._MsPerTick * numStepTick) > latest: - self._MsPerTick = (latest - self._CurrentClientTime)//numStepTick - if self._MsPerTick == 0: - self.log.warning("_MsPerTick is 0 because server tick is too big %d compare to the client tick is %d" %(self._CurrentServerTick, self._CurrentClientTick)) - self._MsPerTick = 1 - elif numStepTick <= 0: - self._MsPerTick = self._LCT - - - ## 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 = int(time.clock_gettime(1)*1000) - - def receiveSystemAckQuit(self, msgin): - self.log.debug("received ACK_QUIT") - self._ReceivedAckQuit = True - - - def disconnect(self): - self.log.info("Disconnect") - self.sendSystemDisconnection() - self._sock.close() - selc._sock = None - self._ConnectionState = TConnectionState.Disconnect - - def stateLogin(self, msgin): - self.decodeHeader(msgin) - if self._SystemMode: - message = msgin.readUint8('message') - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) - if message == CLFECOMMON.SYSTEM_SYNC_CODE: - self._ConnectionState = TConnectionState.Synchronize - self.log.debug("Login->synchronize") - self.receiveSystemSync(msgin) - return True - elif message == CLFECOMMON.SYSTEM_STALLED_CODE: - self.log.debug("received STALLED") - self._ConnectionState = TConnectionState.Stalled - self.receiveSystemStalled(msgin) - return True - elif message == CLFECOMMON.SYSTEM_PROBE_CODE: - self.log.debug("Login->probe") - self._ConnectionState = TConnectionState.Probe - self.receiveSystemProbe(msgin) - return True - elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: - self.disconnect() - self.log.warning("BACK-END DOWN") - return False - else: - self.log.warning("CNET: received system %d in state Login" % message) - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) - else: - self.log.warning("CNET: received normal in state Login") - return False - - def stateSynchronize(self, msgin): - self.log.debug("stateSynchronize") - self.decodeHeader(msgin) - if self._SystemMode: - message = msgin.readUint8('message') - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) - if message == CLFECOMMON.SYSTEM_PROBE_CODE: - self.log.debug("synchronize->probe") - self._ConnectionState = TConnectionState.Probe - self.receiveSystemProbe(msgin) - return True - elif message == CLFECOMMON.SYSTEM_STALLED_CODE: - self.log.debug("received STALLED") - self._ConnectionState = TConnectionState.Stalled - self.receiveSystemStalled(msgin) - return True - elif message == CLFECOMMON.SYSTEM_SYNC_CODE: - self.log.debug("synchronize->synchronize") - self.receiveSystemSync(msgin) - elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: - self.disconnect() - self.log.warning("BACK-END DOWN") - return False - else: - self.log.warning("CNET: received system %d in state Synchronize" % message) - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) - else: - self._ConnectionState = TConnectionState.Connected - self.log.warning("CNET: synchronize->connected") - # _Changes.push_back(CChange(0, ConnectionReady)); - self._ImpulseDecoder.reset(); - self.receiveNormalMessage(msgin); - return True - self.log.debug("sendSystemAckSync ? (%d , %d , %d)" %(self._UpdateTime, self._LatestSyncTime, self._UpdateTime - self._LatestSyncTime)) - if self._UpdateTime - self._LatestSyncTime > 300: - self.sendSystemAckSync(); - return False - - def stateConnected(self, msgin): - self.decodeHeader(msgin) - if self._SystemMode: - message = msgin.readUint8('message') - self.log.debug("SystemMode _CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) - if message == CLFECOMMON.SYSTEM_PROBE_CODE: - self.log.debug("Connected->probe") - self._ConnectionState = TConnectionState.Probe - self.receiveSystemProbe(msgin) - return True - elif message == CLFECOMMON.SYSTEM_SYNC_CODE: - self.log.debug("Connected->synchronize") - self.receiveSystemSync(msgin) - return True - elif message == CLFECOMMON.SYSTEM_STALLED_CODE: - self.log.debug("received STALLED") - self._ConnectionState = TConnectionState.Stalled - self.receiveSystemStalled(msgin) - return True - elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: - self.disconnect() - self.log.warning("BACK-END DOWN") - return False - else: - self.log.warning("CNET: received system %d in state Connected" % message) - self.log.debug("NormalMode _CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) - else: - self.receiveNormalMessage(msgin); - return True - return False - - def stateProbe(self, msgin): - self.decodeHeader(msgin) - if self._SystemMode: - message = msgin.readUint8('message') - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) - if message == CLFECOMMON.SYSTEM_SYNC_CODE: - self.log.debug("probe->synchronize") - self._ConnectionState = TConnectionState.Synchronize - self.receiveSystemSync(msgin) - return True - elif message == CLFECOMMON.SYSTEM_STALLED_CODE: - self.log.debug("probe->stalled") - self._ConnectionState = TConnectionState.Stalled - self.receiveSystemStalled(msgin) - return True - elif message == CLFECOMMON.SYSTEM_PROBE_CODE: - self.receiveSystemProbe(msgin) - elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: - self.disconnect() - self.log.warning("BACK-END DOWN") - return False - else: - self.log.warning("CNET: received system %d in state Probe" % message) - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) - else: - self.log.warning("received normal in state Probe") - - if (len(self._LatestProbes) > 0) or (self._UpdateTime - self._LatestProbeTime > 300): - self.sendSystemAckProbe() - self._LatestProbeTime = self._UpdateTime - - return False - - def stateStalled(self, msgin): - self.decodeHeader(msgin) - if self._SystemMode: - message = msgin.readUint8('message') - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) - if message == CLFECOMMON.SYSTEM_SYNC_CODE: - self.log.debug("stalled->synchronize") - self._ConnectionState = TConnectionState.Synchronize - self.receiveSystemSync(msgin) - return True - elif message == CLFECOMMON.SYSTEM_PROBE_CODE: - self.log.debug("stalled->probe") - self._ConnectionState = TConnectionState.Probe - self.receiveSystemProbe(msgin) - elif message == CLFECOMMON.SYSTEM_STALLED_CODE: - self.receiveSystemStalled(msgin) - elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: - self.disconnect() - self.log.warning("BACK-END DOWN") - return False - else: - self.log.warning("CNET: received system %d in state Stalled" % message) - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) - else: - self.log.warning("received normal in state Stalled") - return False - - def stateQuit(self, msgin): - self.decodeHeader(msgin) - if self._SystemMode: - message = msgin.readUint8('message') - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) - if message == CLFECOMMON.SYSTEM_SYNC_CODE: - self.log.debug("quit->synchronize") - self._ConnectionState = TConnectionState.Synchronize - self.receiveSystemSync(msgin) - return True - elif message == CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: - self.disconnect() - self.log.warning("BACK-END DOWN") - return False - elif message == CLFECOMMON.SYSTEM_ACK_QUIT_CODE: - self.receiveSystemAckQuit(msgin) - else: - self.log.warning("CNET: received system %d in state Quit" % message) - self.log.debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) - else: - self.log.warning("received normal in state Quit") - if not self._ReceivedAckQuit and (self._UpdateTime - self._LatestQuitTime > 100): - self.sendSystemQuit() - self._LatestQuitTime = self._UpdateTime - return False - - def update(self): - # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::update() - self._UpdateTime = int(time.clock_gettime(1)*1000) - self._ReceivedSync = False - - if not self._sock: - return False - - # TODO - REMOVE this counter (just to stop loop) - counterLoop = 0 - - stateBroke = True - while stateBroke: - buffer, addr = self.buildStream() - 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) - elif self._ConnectionState == TConnectionState.Synchronize: - self.log.debug("state:Synchronize") - stateBroke = self.stateSynchronize(msgin) - elif self._ConnectionState == TConnectionState.Connected: - self.log.debug("state:Connected") - stateBroke = self.stateConnected(msgin) - elif self._ConnectionState == TConnectionState.Probe: - self.log.debug("state:Probe") - stateBroke = self.stateProbe(msgin) - elif self._ConnectionState == TConnectionState.Stalled: - self.log.debug("state:Stalled") - stateBroke = self.stateStalled(msgin) - elif self._ConnectionState == TConnectionState.Quit: - self.log.debug("state:Quit") - stateBroke = self.stateQuit(msgin) - else: - stateBroke = False - self.log.debug("message decoded: %s" % msgin.showAllData()) - counterLoop += 1 - if counterLoop > 10: - break - - def push_back(self, action): - ''' - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::push(CAction *action) - ''' - # search to aggregate Action (send by block) - if len(self._Actions) == 0 or self._Actions[-1].Cycle != 0: - self._Actions.append(CActionBlock()) - self._Actions[-1].push_back(action) - - def push(self, msgout): - ''' - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::push(CBitMemStream &msg) - ''' - self.log.debug("Push :%s" % msgout.showAllDataWrite()) - maxImpulseBitSize = 1840 # = 230*8 - cp = CActionFactory(self.log, self.world) - ag = cp.createFactory(INVALID_SLOT, TActionCode.ACTION_GENERIC_CODE) - bytelen = (msgout.sizeData() + 7) // 8 - impulseMinBitSize = ag.size() - impulseBitSize = impulseMinBitSize + (4 + bytelen)*8 - self.log.debug("maxImpulseBitSize:%d bytelen:%d impulseMinBitSize:%d impulseBitSize:%d" %(maxImpulseBitSize, bytelen, impulseMinBitSize, impulseBitSize)) - if impulseBitSize < maxImpulseBitSize: - ag.set(msgout); - self.push_back(ag) - else: - agmp = cp.createFactory(INVALID_SLOT, TActionCode.ACTION_GENERIC_MULTI_PART_CODE) - minimumBitSizeForMP = agmp.size() - availableSize = (maxImpulseBitSize - minimumBitSizeForMP) // 8 - nbBlock = (bytelen + availableSize - 1) // availableSize - num = self._ImpulseMultiPartNumber - self._ImpulseMultiPartNumber += 1 - self.log.debug("minimumBitSizeForMP:%d availableSize:%d nbBlock:%d num:%d _ImpulseMultiPartNumber:%d" % (minimumBitSizeForMP, availableSize, nbBlock, num, self._ImpulseMultiPartNumber)) - for i in range(0, nbBlock): - self.log.debug("i:%d nbBlock:%d" % (i, nbBlock)) - if i != 0: - # Create a new CActionFactory - agmp = cp.createFactory(INVALID_SLOT, TActionCode.ACTION_GENERIC_MULTI_PART_CODE) - agmp.set(num, i, msgout.buffer(), bytelen, availableSize, nbBlock) - self.push_back(agmp) - - def sendFastMode(self): - if self._ConnectionState == TConnectionState.Connected and self._LastSendTime > 100: - self.sendNormalMessage() - - def send(self): - ''' - khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::send(TGameCycle cycle) - ''' - cycle = self._CurrentServerTick - bitSize = 32*8 # block size is 32 (cycle) + 8 (number of actions - if len(self._Actions) == 0 or self._Actions[-1].Cycle != 0: - self.log.debug("No Action") - pass - else: - block = self._Actions[-1] - block.Cycle = cycle - # check last block isn't bigger than maximum allowed - i = 0 - for action in block.Actions: - bitSize += action.size() - if bitSize >= 480*8: - break - i += 1 - if i < len(self._Actions): - # Too big block -> split block - newBlock = CActionBlock() - newBlock.Cylce = 0 - newBlock.insert(self._Actions, i, len(block.Actions)) - block.eraseToEnd(i) - - if self._ConnectionState == TConnectionState.Connected: - self.sendNormalMessage() - - #if len(self.world.Commands) > 0: - # cmd = self.world.Commands.pop(0) - - def analyze(self): - if self.world.CurrentState == TState.st_connect: - if self.world.CShardNames != []: - if self.stepAction == 0 and self.world.CharacterSummaries != [] and self.clientTick > 0: - if self.world.CharacterSummaries[0].People == TPeople.Unknown: - bms = self.world.CreaterCharacter(self.msgXml) - self.push(bms) - self.stepAction = 1 - else: - self.stepAction = 1 - elif self.stepAction == 1 and self.world.CharacterSummaries != []: - if self.world.CharacterSummaries[0].People != TPeople.Unknown: - self.log.info("Account defined %s" % self.world.CharacterSummaries[0].Name) - bms = self.world.SelectChar(self.msgXml, 0) - self.push(bms) - self.stepAction = 2 - - def EmulateFirst(self, msgRawXml, databaseRawXml): - self.world.CurrentState = TState.st_start - self.msgXml = ET.fromstring(msgRawXml) - #ET.dump(msgXml) - self.databaseXml = ET.fromstring(databaseRawXml) - #ET.dump(databaseXml) - - 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() - self.world.CurrentState = TState.st_connect - - self.log.info("Receive Message") - self.clientTick = 0 - for _ in range(0, 50): - #while True: - self.log.debug("%s [%s: %d / %d] %s" % ("*" * 40, "Loop", self.clientTick, self.stepAction, "*" * 40)) - self.update() - self.analyze() - self.send() - self.clientTick += 1 - - self.log.info("Client Quit") - self.sendSystemQuit() - +#import socket +#import xml.etree.ElementTree as ET +#import hashlib +#import time +#import signal +#from tools import BitStream +#from tools import CCharacterSummary +#from tools import CBitSet +#from tools import getPowerOf2 +#from tools import CFileChild +#from tools import CFileList +from tools import CFileContainer +#from tools import Enum +#from tools import World +#from tools import CActionFactory +#from tools import CSessionId +#from tools import CGenericMultiPartTemp +#from tools import CMainlandSummary +#from tools import CAction +#from tools import DecodeImpulse +#from tools import CImpulseDecoder +#from tools import CodeMsgXml +from tools import CPersistentDataRecord +from tools import ClientNetworkConnection +from tools import CStringManager + +#INVALID_SLOT = 0xff +LOGGER = 'Client' class ClientKhanat: def __init__(self, @@ -4833,11 +81,10 @@ class ClientKhanat: download_patch = False, show_patch_detail=False, size_buffer_file=1024): - self.log = logging.getLogger('myLogger') if suffix is None: suffix = str(random.randrange(1, 9999)) - self.log.debug("suffix : %s" % suffix) + logging.getLogger(LOGGER).debug("suffix : %s" % suffix) self.download_patch = download_patch self.show_patch_detail = show_patch_detail @@ -4851,12 +98,12 @@ class ClientKhanat: self.url = url self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl = None, None, None, None, None, None, None, None self.tempdir = tempfile.TemporaryDirectory(".khanat") - self.log.debug("Temporary directory:%s" % self.tempdir) - self.khanat_idx = CPersistentDataRecord(self.log) + logging.getLogger(LOGGER).debug("Temporary directory:%s" % self.tempdir) + self.khanat_idx = CPersistentDataRecord.CPersistentDataRecord() self.UserAddr, self.UserKey, self.UserId = None, None, None - self.clientNetworkConnection = ClientNetworkConnection(self.khanat_host, self.khanat_port_frontend, self.login) + self.clientNetworkConnection = ClientNetworkConnection.ClientNetworkConnection(self.khanat_host, self.khanat_port_frontend, self.login) self.size_buffer_file = size_buffer_file - self.cFileContainer = CFileContainer() + self.cFileContainer = CFileContainer.CFileContainer() def createAccount(self): conn = http.client.HTTPConnection(host=self.khanat_host, port=self.khanat_port_login) @@ -4874,16 +121,22 @@ class ClientKhanat: headers = {'Content-Type': 'application/x-www-form-urlencoded'} params = urllib.parse.urlencode({'Username': self.login, 'Password': self.password, 'ConfirmPass': self.password, 'Email': self.login+'@khaganat.net', 'TaC': 'on', 'function': 'add_user'}) + logging.getLogger(LOGGER).debug("POST %s" % (cmd)) + print( "host%s, port:%s" % (self.khanat_host, str(self.khanat_port_login))) + print("cmd:%s" % cmd) + print("params:%s" % params) + print("headers:%s" % headers) conn.request("POST", cmd, params, headers) response = conn.getresponse() if ( int(response.status) == 302 ): conn.close() - self.log.info("Account : %s" % self.login) + logging.getLogger(LOGGER).info("Account created : %s" % self.login) return elif ( int(response.status) != 200 ): - self.log.error("Impossible to create account (return code:" + str(response.status) + ")") + logging.getLogger(LOGGER).error("Impossible to create account (return code:" + str(response.status) + ")") sys.exit(2) ret = response.read() + print("ret:%s" % ret) conn.close() ret2 = ret.decode() @@ -4893,30 +146,32 @@ class ClientKhanat: state = 1 comment = "" if int(state) != 1: - self.log.error("Impossible to create account (state:" + state + ", comment:" + comment.strip() + ")") + logging.getLogger(LOGGER).error("Impossible to create account (state:" + state + ", comment:" + comment.strip() + ")") sys.exit(2) errordetected = False for line in ret2.split('\n'): - m = re.search("((?P.*) Error )(?P[^.]+)", line) + m = re.search("((?P.*) Error )(?P[^.]+)", line) if m: if m.group('comment') == 'Username ' + self.login + ' is in use': continue if m.group('comment') == 'Email is in use': continue - self.log.error('Impossible to create account: field:%s (%s)' % (m.group('type'), m.group('comment'))) + logging.getLogger(LOGGER).error('Impossible to create account: field:%s (%s)' % (m.group('type'), m.group('comment'))) errordetected = True if errordetected: sys.exit(2) - self.log.info("Reuse account : %s" % self.login) + logging.getLogger(LOGGER).info("Reuse account : %s" % self.login) + sys.exit(0) def connectR2(self): conn = http.client.HTTPConnection(host=self.khanat_host, port=self.khanat_port_login) cmd = self.url + "?cmd=ask&cp=2&login=" + self.login + "&lg=" + self.LanguageCode + logging.getLogger(LOGGER).debug("GET %s" % (cmd)) conn.request("GET", cmd) response = conn.getresponse() if ( int(response.status) != 200 ): - self.log.error("Impossible to get salt (return code:" + str(response.status) + ")") + logging.getLogger(LOGGER).error("Impossible to get salt (return code:" + str(response.status) + ")") sys.exit(2) ret = response.read() conn.close() @@ -4927,32 +182,33 @@ class ClientKhanat: try: state, salt = ret.decode(encoding='cp1252').split(":", 1) except UnicodeDecodeError: - self.log.error("Impossible to read output login") + logging.getLogger(LOGGER).error("Impossible to read output login") sys.exit(2) if int(state) != 1: - self.log.error("Impossible to get salt (state:" + state + ")") + logging.getLogger(LOGGER).error("Impossible to get salt (state:" + state + ")") cryptedPassword = crypt.crypt(self.password, salt) conn = http.client.HTTPConnection(host=self.khanat_host, port=self.khanat_port_login) cmd = self.url + "?cmd=login&login=" + self.login + "&password=" + cryptedPassword + "&clientApplication=" + self.clientApp + "&cp=2" + "&lg=" + self.LanguageCode + logging.getLogger(LOGGER).debug("GET %s" % (cmd)) conn.request("GET", cmd) response = conn.getresponse() - self.log.debug("%s %s" %(response.status, response.reason)) + logging.getLogger(LOGGER).debug("%s %s" %(response.status, response.reason)) ret = response.read() - self.log.debug(ret) + logging.getLogger(LOGGER).debug(ret) try: line = ret.decode().split('\n') except UnicodeDecodeError: try: line = ret.decode(encoding='cp1252').split('\n') except UnicodeDecodeError: - self.log.error("Impossible to read output login") + logging.getLogger(LOGGER).error("Impossible to read output login") sys.exit(2) - self.log.debug(line[0]) - self.log.debug("line 0 '%s'" % line[0]) - self.log.debug("line 1 '%s'" % line[1]) + logging.getLogger(LOGGER).debug(line[0]) + logging.getLogger(LOGGER).debug("line 0 '%s'" % line[0]) + logging.getLogger(LOGGER).debug("line 1 '%s'" % line[1]) try: state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat = line[0].split("#", 6) except: @@ -4963,19 +219,19 @@ class ClientKhanat: state, error = line[0].split(":", 1) if int(state) != 1: - self.log.error(error) + logging.getLogger(LOGGER).error(error) sys.exit(2) self.r2serverversion, self.r2backuppatchurl, self.r2patchurl = line[1].split("#") - self.log.debug("%s %s %s %s %s %s %s %s %s" % (state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl)) + logging.getLogger(LOGGER).debug("%s %s %s %s %s %s %s %s %s" % (state, self.cookie, self.fsaddr, self.ringmainurl, self.fartp, self.stat, self.r2serverversion, self.r2backuppatchurl, self.r2patchurl)) self.UserAddr, self.UserKey, self.UserId = [ int(x, 16) for x in self.cookie.split('|') ] conn.close() - self.log.info("Login Ok") + logging.getLogger(LOGGER).info("Login Ok") self.clientNetworkConnection.cookiesInit(self.UserAddr, self.UserKey, self.UserId) def downloadFileUrl(self, source, dest): - self.log.info("Download %s (destination:%s)" % (source, dest)) + logging.getLogger(LOGGER).info("Download %s (destination:%s)" % (source, dest)) with urllib.request.urlopen(source) as conn : header = conn.getheaders() file_size = 0 @@ -4983,7 +239,7 @@ class ClientKhanat: if key == 'Content-Length': file_size = int(value) break - self.log.debug("size:%d", file_size) + logging.getLogger(LOGGER).debug("size:%d", file_size) file_size_dl = 0 block_size = self.size_buffer_file # 1024 @@ -4994,9 +250,9 @@ class ClientKhanat: break file_size_dl += len(buffer) fp.write(buffer) - self.log.debug("Download %s %10d [%6.2f%%]" % (source, file_size_dl, file_size_dl * 100. / file_size)) + logging.getLogger(LOGGER).debug("Download %s %10d [%6.2f%%]" % (source, file_size_dl, file_size_dl * 100. / file_size)) fp.close() - self.log.debug("Downloaded %s (%d)" % (source, file_size)) + logging.getLogger(LOGGER).debug("Downloaded %s (%d)" % (source, file_size)) def getServerFile(self, name, bZipped = False, specifyDestName = None): srcName = name @@ -5007,7 +263,7 @@ class ClientKhanat: if bZipped: srcName += ".ngz" dstName += ".ngz" - self.log.info("Download %s (destination:%s, zip:%d)" % (srcName, dstName, bZipped)) + logging.getLogger(LOGGER).info("Download %s (destination:%s, zip:%d)" % (srcName, dstName, bZipped)) dstName = os.path.join(self.tempdir.name, dstName) self.downloadFileUrl( 'http://' + self.r2patchurl + '/' + srcName, dstName) return dstName @@ -5021,7 +277,7 @@ class ClientKhanat: with open(dstName, "wb") as fout: data = fin.read() fout.write(data) - self.log.info("%s" % dstName) + logging.getLogger(LOGGER).info("%s" % dstName) os.remove(tmp) # khanat-opennel-code/code/ryzom/client/src/login_patch.cpp # void CCheckThread::run () FilesToPatch = [] @@ -5069,7 +325,7 @@ class ClientKhanat: pass def DownloadMinimum(self): - self.log.debug("-" * 80) + logging.getLogger(LOGGER).debug("-" * 80) for file in self.khanat_idx.CBNPFile: if file.FileName != "kh_server.bnp": continue @@ -5079,7 +335,7 @@ class ClientKhanat: with open(dstName, "wb") as fout: data = fin.read() fout.write(data) - self.log.info("%s" % dstName) + logging.getLogger(LOGGER).info("%s" % dstName) os.remove(tmp) def Emulate(self): @@ -5099,7 +355,7 @@ class ClientKhanat: self.downloadAllPatch() else: self.DownloadMinimum() - self.cFileContainer = CFileContainer() + self.cFileContainer = CFileContainer.CFileContainer() self.cFileContainer.addSearchPath(self.tempdir.name) msgRawXml = self.cFileContainer.getdata("msg.xml").decode() databaseRawXml = self.cFileContainer.getdata("database.xml").decode() @@ -5109,7 +365,16 @@ class ClientKhanat: def main(): FORMAT = '%(asctime)-15s %(levelname)s %(filename)s:%(lineno)d %(message)s' logging.basicConfig(format=FORMAT) - log = logging.getLogger('myLogger') + + logger = [] + logger.append(logging.getLogger(LOGGER)) + #logger.append(logging.getLogger(CImpulseDecoder.LOGGER)) + #logger.append(logging.getLogger(DecodeImpuls.LOGGER)) + #logger.append(logging.getLogger(BitStream.LOGGER)) + logger.append(logging.getLogger(CStringManager.LOGGER)) + logger.append(logging.getLogger(CPersistentDataRecord.LOGGER)) + logger.append(logging.getLogger(ClientNetworkConnection.LOGGER)) + logger.append(logging.getLogger(LOGGER)) parser = argparse.ArgumentParser() parser.add_argument("--khanat-host", help="khanat host to auhtenticate", default='localhost') @@ -5127,11 +392,13 @@ def main(): level = logging.getLevelName('DEBUG') else: level = logging.getLevelName('INFO') - log.setLevel(level) + + for logid in logger: + logid.setLevel(level) client = ClientKhanat(args.khanat_host, khanat_port_login=args.khanat_port_login, khanat_port_frontend=args.khanat_port_frontend, suffix=args.suffix, download_patch=args.download_patch, show_patch_detail=args.show_patch_detail, size_buffer_file=args.size_buffer_file) client.Emulate() - log.info("End") + logging.getLogger(LOGGER).info("End") if __name__ == "__main__": #TestBitStream() diff --git a/spykhanat.py b/spykhanat.py new file mode 100755 index 0000000..b1b1b50 --- /dev/null +++ b/spykhanat.py @@ -0,0 +1,416 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# script to read pcap file (communcation with khanat) +# +# 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 . + +# 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 + +import argparse +import logging +from pcapfile import savefile +from pcapfile.protocols.linklayer import ethernet +from pcapfile.protocols.network import ip +from pcapfile.protocols.transport import udp +from pcapfile.protocols.transport import tcp +import binascii +import re +from tools import BitStream +from tools import Enum +from tools import CActionFactory +from tools import CBitSet +from tools import DecodeImpulse +from tools import World +from tools import CGenericMultiPartTemp +from tools import CImpulseDecoder +from tools import CStringManager +from tools import CAction +import xml.etree.ElementTree as ET + +LOGGER = 'SpyKhanat' + +#file = open('capture2.pcap' , 'rb') +#pcapfile = savefile.load_savefile(file,verbose=True) +#pkt = pcapfile.packets[0] +#print(pkt.raw()) +#print(pkt.timestamp) +#eth_frame = ethernet.Ethernet(pkt.raw()) +#print(eth_frame) +#ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload)) +#print(ip_packet) + +class SpyPcap(): + def __init__(self, khanat_host_service, pcap_file, msg_xml, filter_host_service, show_raw_packet, show_message_decoded): + if khanat_host_service: + self.khanat_host_service = re.compile(khanat_host_service) + else: + self.khanat_host_service = None + self.pcap_file = pcap_file + self.msg_xml = msg_xml + if filter_host_service: + self.filter_host_service = re.compile(filter_host_service) + else: + self.filter_host_service = None + self.show_raw_packet = show_raw_packet + self.show_message_decoded = show_message_decoded + self.actionFactory = CActionFactory.CActionFactory(None) + self.client_state = {} + self.decodeImpulse = DecodeImpulse.DecodeImpulse() + + fp = open(msg_xml , 'rt') + msgRawXml = fp.read() + self.msgXml = ET.fromstring(msgRawXml) + self.decodeImpulse.loadMsg(self.msgXml) + + def readRaw(self): + file = open( self.pcap_file , 'rb') + pcapfile = savefile.load_savefile(file,verbose=False) + for pkt in pcapfile.packets: + print("pkt:", dir(pkt)) + print("pkt.header:", dir(pkt.header)) + print("pkt.packet:", dir(pkt.packet)) + logging.getLogger(LOGGER).debug("raw: %s" % pkt.raw()) + logging.getLogger(LOGGER).debug("timestamp: %s" % pkt.timestamp) + eth_frame = ethernet.Ethernet(pkt.raw()) + logging.getLogger(LOGGER).debug("eth_frame: %s" % eth_frame) + if eth_frame.type == 2048: + ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload)) + logging.getLogger(LOGGER).debug("ip packet: %s" % ip_packet) + print("ip_packet:", dir(ip_packet)) + logging.getLogger(LOGGER).debug("ip packet: %s ->%s" % (ip_packet.src.decode(), ip_packet.dst.decode()) ) + if ip_packet.p == 17: + # UDP + logging.getLogger(LOGGER).debug("ip packet: protocol UDP (%s)" % ip_packet.p) + udp_packet = udp.UDP(binascii.unhexlify(ip_packet.payload)) + print("udp_packet:", dir(udp_packet)) + logging.getLogger(LOGGER).debug("UDP packet: %s" % udp_packet) + data = udp_packet.payload + print("data:", dir(data)) + logging.getLogger(LOGGER).debug("data packet: %s" % data) + data = udp_packet.payload + logging.getLogger(LOGGER).info("data packet: timestamp:%s src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp, + ip_packet.src.decode(), udp_packet.src_port, + ip_packet.dst.decode(), udp_packet.dst_port, + data.decode())) + + elif ip_packet.p == 6: + # TCP + logging.getLogger(LOGGER).debug("ip packet: protocol TCP (%s)" % ip_packet.p) + tcp_packet = tcp.TCP(binascii.unhexlify(ip_packet.payload)) + data = tcp_packet.payload + logging.getLogger(LOGGER).info("data packet: timestamp:%s src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp, + ip_packet.src.decode(), tcp_packet.src_port, + ip_packet.dst.decode(), tcp_packet.dst_port, + data.decode())) + else: + logging.getLogger(LOGGER).debug("ip packet: protocol (%s)" % ip_packet.p) + + def detect_khanat_server(self, packets): + hostdetected = {} + for pkt in packets: + eth_frame = ethernet.Ethernet(pkt.raw()) + if eth_frame.type == 2048: + ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload)) + if ip_packet.p == 17: # UDP + udp_packet = udp.UDP(binascii.unhexlify(ip_packet.payload)) + data = udp_packet.payload + host = "%s:%d" % (ip_packet.src.decode(), udp_packet.src_port) + hostdetected.setdefault(host, 0) + if len(data) == 20: + hostdetected[host] += 1 + id = None + max = 0 + for host in hostdetected: + if id: + if max < hostdetected[host]: + max = hostdetected[host] + id = host + else: + max = hostdetected[host] + id = host + logging.getLogger(LOGGER).info("khanat host :%s" % id) + return id + + def initialize_client(self, clientid): + self.client_state.setdefault(clientid, {'CurrentReceivedNumber': 0, + 'CurrentSendNumber': 0, + 'LastReceivedAck': 0, + 'RegisteredAction': {}, + 'world': World.World(), + 'GenericMultiPartTemp': CGenericMultiPartTemp.GenericMultiPartTemp(), + 'CImpulseDecoder': None, + 'LastAck0': [-1], + 'LastAck1': [-1, -1], + 'LastAck2': [-1, -1, -1, -1]}) + self.client_state[clientid]['CImpulseDecoder'] = CImpulseDecoder.CImpulseDecoder(self.client_state[clientid]['world']) + + def add_registered_action(self, clientid, action): + self.client_state[clientid]['RegisteredAction'].setdefault(action.Code, []) + self.client_state[clientid]['RegisteredAction'][action.Code].append(action) + + def decode_server(self, clientid, msgin, receivedPacket, receivedAck, nextSentPacket=0): + actions = [] + for level in range(0, 3): + if level == 0: + lAck = self.client_state[clientid]['LastAck0'] + channel = 0 + elif level == 1: + lAck = self.client_state[clientid]['LastAck1'] + channel = receivedPacket & 1 + elif level == 2: + lAck = self.client_state[clientid]['LastAck2'] + channel = receivedPacket & 3 + keep = True + checkOnce = False + num = 0 + #logging.getLogger(LOGGER).debug("level:%d channel:%d lAck:%s" %(level, channel, ':'.join([str(x) for x in lAck]))) + # lastAck = lAck[channel] + while True: + next = msgin.readBool('next:' + str(level) + ':' + str(channel)) + if not next: + break + if not checkOnce: + checkOnce = True + keep = receivedAck >= lAck[channel] + logging.getLogger(LOGGER).debug("keep:%s [%d => %d]" % (str(keep), receivedAck, lAck[channel])) + if keep: + lAck[channel] = nextSentPacket + logging.getLogger(LOGGER).debug("lAck:%s" % ':'.join([str(x) for x in lAck])) + pass + num += 1 + #actionFactory = CAction.CActionFactory(None) + action = self.actionFactory.unpack(msgin) + logging.getLogger(LOGGER).debug("action:%s" % action) + #action = self._CActionFactory.unpack(msgin) + if keep: + logging.getLogger(LOGGER).debug("keep Code:%s" % str(action.Code)) + actions.append(action) + elif action: + logging.getLogger(LOGGER).debug("append Code:%s" % str(action.Code)) + 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_message(self, msgin, clientid, dst): + CurrentReceivedNumber = msgin.readSint32('CurrentReceivedNumber') + SystemMode = msgin.readBool('SystemMode') + logging.getLogger(LOGGER).debug("[Client -> Khanat] {CurrentReceivedNumber:%d, SystemMode:%d, src:%s, dst:%s}" % (CurrentReceivedNumber, SystemMode, clientid, dst)) + self.initialize_client(clientid) + self.client_state[clientid]['CurrentReceivedNumber'] = CurrentReceivedNumber + if not SystemMode: + LastReceivedAck = msgin.readSint32('LastReceivedAck') + self.client_state[clientid]['LastReceivedAck'] = LastReceivedAck + 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) + else: + message = msgin.readUint8('message') + try: + typeMessage = Enum.CLFECOMMON(message).name + except ValueError: + typeMessage = "Unknown" + if message == Enum.CLFECOMMON.SYSTEM_LOGIN_CODE: + UserAddr = msgin.readUint32('UserAddr') + UserKey = msgin.readUint32('UserKey') + UserId = msgin.readUint32('UserId') + LanguageCode = msgin.readString('LanguageCode') + logging.getLogger(LOGGER).info("[Client -> Khanat] System Mode:%s {CurrentReceivedNumber:%d, src:%s, dst:%s, UserAddr:%d, UserId:%d, UserAddr:%d LanguageCode:%s}" % + (typeMessage, CurrentReceivedNumber, clientid, dst, UserAddr, UserKey, UserId, LanguageCode)) + elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + LatestProbe = msgin.readSint32('LatestProbe') + logging.getLogger(LOGGER).info("[Client -> Khanat] System Mode:%s probe:%d {CurrentReceivedNumber:%d, src:%s, dst:%s}" % (typeMessage, + LatestProbe, CurrentReceivedNumber, clientid, dst)) + elif message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + Synchronize = msgin.readUint32('Synchronize') + stime = msgin.readSint64('stime') + LatestSync = msgin.readUint32('LatestSync') + MsgData = msgin.readArrayUint8(16, 'MsgData') + md5Msg = bytes(MsgData) + DatabaseData = msgin.readArrayUint8(16, 'DatabaseData') + md5Database = bytes(DatabaseData) + logging.getLogger(LOGGER).info("[Client -> Khanat] System Mode:%s {CurrentReceivedNumber:%d, src:%s, dst:%s, Synchronize:%d, stime:%d, LatestSync:%d, MsgData:%s, DatabaseData:%s}" % ( + typeMessage, CurrentReceivedNumber, clientid, dst, Synchronize, stime, LatestSync, binascii.hexlify(md5Msg).decode(), binascii.hexlify(md5Database).decode())) + elif message == Enum.CLFECOMMON.SYSTEM_ACK_SYNC_CODE: + LastReceivedNumber = msgin.readSint32('LastReceivedNumber') + LastAckInLongAck = msgin.readSint32('LastAckInLongAck') + LongAckBitField = CBitSet.CBitSet() + LongAckBitField.readSerial(msgin, 'LongAckBitField') + LatestSync = msgin.readSint32('LatestSync') + logging.getLogger(LOGGER).info("[Client -> Khanat] System Mode:%s {CurrentReceivedNumber:%d, src:%s, dst:%s, LastReceivedNumber:%d, LastAckInLongAck:%d, LatestSync:%d, LongAckBitField:%s}" % ( + typeMessage, CurrentReceivedNumber, clientid, dst, LastReceivedNumber, LastAckInLongAck, LatestSync, LongAckBitField)) + elif message == Enum.CLFECOMMON.SYSTEM_ACK_PROBE_CODE: + SizeLatestProbes = msgin.readSint32('SizeLatestProbes') + LatestProbes = [] + for data in range(0, SizeLatestProbes): + LatestProbes.append(msgin.readSint32('LatestProbes')) + logging.getLogger(LOGGER).info("[Client -> Khanat] System Mode:%s (%d) {CurrentReceivedNumber:%d, src:%s, dst:%s, SizeLatestProbes:%d, LatestProbes:%s}" % (typeMessage, message, CurrentReceivedNumber, clientid, dst, SizeLatestProbes, str(LatestProbes))) + else: + logging.getLogger(LOGGER).info("[Client -> Khanat] System Mode:%s (%d) {CurrentReceivedNumber:%d, src:%s, dst:%s}" % (typeMessage, message, CurrentReceivedNumber, clientid, dst)) + logging.getLogger(LOGGER).debug("[Client -> Khanat] msg:%s" % msgin.showAllData()) + + def decode_khanat_message(self, msgin, src, dst): + CurrentSendNumber = msgin.readSint32('CurrentSendNumber') + logging.getLogger(LOGGER).debug("[Khanat -> Client] {CurrentSendNumber:%d, src:%s, dst:%s}" % (CurrentSendNumber, src, dst)) + SystemMode = msgin.readBool('SystemMode') + self.initialize_client(dst) + self.client_state[dst]['CurrentSendNumber'] = CurrentSendNumber + if not SystemMode: + _LastReceivedAck = msgin.readSint32('LastReceivedAck'); + logging.getLogger(LOGGER).debug("[Khanat -> Client] Normal Mode {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d}" % (CurrentSendNumber, src, dst, _LastReceivedAck)) + actions = 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: + logging.getLogger(LOGGER).debug('list actions: None') + + # Decode the actions received in the impulsions + logging.getLogger(LOGGER).debug('=' * 80) + for action in actions: + logging.getLogger(LOGGER).debug('-' * 80) + logging.getLogger(LOGGER).debug('Analyse actions:%s', action) + if action.Code == Enum.TActionCode.ACTION_DISCONNECTION_CODE: + logging.getLogger(LOGGER).info("Action : ACTION_DISCONNECTION_CODE") + elif action.Code == Enum.TActionCode.ACTION_GENERIC_CODE: + action.genericAction(self.decodeImpulse, self.client_state[dst]['world'], self.client_state[dst]['GenericMultiPartTemp']) + logging.getLogger(LOGGER).info("[Khanat -> Client] ACTION_GENERIC_CODE : {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, action:%s}" % (CurrentSendNumber, src, dst, _LastReceivedAck, action)) + elif action.Code == Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE: + action.genericAction(self.decodeImpulse, self.client_state[dst]['world'], self.client_state[dst]['GenericMultiPartTemp']) + logging.getLogger(LOGGER).debug("[Khanat -> Client] ACTION_GENERIC_MULTI_PART_CODE : %s" % action) + for id in self.client_state[dst]['GenericMultiPartTemp'].data: + if self.client_state[dst]['GenericMultiPartTemp'].data[id].isAvailable(): + logging.getLogger(LOGGER).info("[Khanat -> Client] ACTION_GENERIC_MULTI_PART_CODE {CurrentSendNumber:%d, src:%s, dst:%s, _LastReceivedAck:%d, id:%d, msg:%s}" % (CurrentSendNumber, src, dst, _LastReceivedAck, id, self.client_state[dst]['GenericMultiPartTemp'].data[id].read().showAllData())) + elif action.Code == Enum.TActionCode.ACTION_DUMMY_CODE: + logging.getLogger(LOGGER).info("Action : ACTION_DUMMY_CODE") + self.add_registered_action(dst, action) +# # remove all old actions that are acked +# while self._Actions and self._Actions[0].FirstPacket != 0 and self._Actions[0].FirstPacket < self._LastReceivedAck: +# logging.getLogger(LOGGER).debug("remove old action [%d/%d] : %s" % (self._Actions[0].FirstPacket, self._LastReceivedAck, self._Actions[0])) +# self._Actions.pop(0) + else: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("[Khanat -> Client] System Mode {CurrentSendNumber:%d, src:%s, dst:%s, message:%d" % (CurrentSendNumber, src, dst, message)) + if message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + logging.getLogger(LOGGER).debug("[Khanat -> Client] Synchronize") + Synchronize = msgin.readUint32('Synchronize') + stime = msgin.readSint64('stime') + LatestSync = msgin.readUint32('LatestSync') + MsgData = msgin.readArrayUint8(16, 'MsgData') + DatabaseData = msgin.readArrayUint8(16, 'DatabaseData') + logging.getLogger(LOGGER).info("[Khanat -> Client] System Mode / Synchronize {CurrentSendNumber:%d, src:%s, dst:%s, message:%d, Synchronize:%d, stime:%d, LatestSync:%d, MsgData:%s, DatabaseData:%s}" % (CurrentSendNumber, src, dst, message, Synchronize, stime, LatestSync, str(MsgData), str(DatabaseData))) + elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE: + logging.getLogger(LOGGER).debug("[Khanat -> Client] Stalled") + logging.getLogger(LOGGER).info("[Khanat -> Client] System Mode / Stalled {CurrentSendNumber:%d, src:%s, dst:%s, message:%d}" % (CurrentSendNumber, src, dst, message)) + elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + logging.getLogger(LOGGER).debug("[Khanat -> Client] Probe") + LatestProbe = msgin.readSint32('LatestProbe') + logging.getLogger(LOGGER).info("[Khanat -> Client] System Mode / Probe {CurrentSendNumber:%d, src:%s, dst:%s, message:%d, LatestProbe:%d}" % (CurrentSendNumber, src, dst, message, LatestProbe)) + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + logging.getLogger(LOGGER).info("[Khanat -> Client] System Mode / BACK-END DOWN {CurrentSendNumber:%d, src:%s, dst:%s, message:%d}" % (CurrentSendNumber, src, dst, message)) + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Login" % message) + #cActionFactory = CAction.CActionFactory(None) + #cActionFactory.unpack(msgin) + logging.getLogger(LOGGER).debug("[Khanat -> Client] msg:%s" % msgin.showAllData()) + + def read(self): + file = open( self.pcap_file , 'rb') + pcapfile = savefile.load_savefile(file,verbose=False) + khanat_host = self.detect_khanat_server(pcapfile.packets) + for pkt in pcapfile.packets: + eth_frame = ethernet.Ethernet(pkt.raw()) + if eth_frame.type == 2048: + ip_packet = ip.IP(binascii.unhexlify(eth_frame.payload)) + if ip_packet.p == 17: + # UDP + udp_packet = udp.UDP(binascii.unhexlify(ip_packet.payload)) + data = udp_packet.payload + if udp_packet.src_port == 53 or udp_packet.dst_port == 53: + continue + if not self.filter_host_service or self.filter_host_service.match("%s:%d" % (ip_packet.src.decode(), udp_packet.src_port)) or self.filter_host_service.match("%s:%d" % (ip_packet.dst.decode(), udp_packet.dst_port)): + logging.getLogger(LOGGER).debug("-" * 80) + if self.show_raw_packet: + logging.getLogger(LOGGER).debug("[raw packet] timestamp:%s src:%s:%d dst:%s:%d data:%s" % (pkt.timestamp, + ip_packet.src.decode(), udp_packet.src_port, + ip_packet.dst.decode(), udp_packet.dst_port, + data.decode())) + msgin = BitStream.BitStream() + msgin.fromBytes(binascii.unhexlify(data)) + src = "%s:%d" % (ip_packet.src.decode(), udp_packet.src_port) + dst = "%s:%d" % (ip_packet.dst.decode(), udp_packet.dst_port) + if (self.khanat_host_service and self.khanat_host_service.match(src)) or ( not self.khanat_host_service and khanat_host == src): + self.decode_khanat_message(msgin, src, dst) + else: + self.decode_client_message(msgin, src, dst) + if self.show_message_decoded: + logging.getLogger(LOGGER).debug("[message decoded] %s" % 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'])) + + +def main(): + FORMAT = '%(asctime)-15s %(levelname)s %(filename)s:%(lineno)d %(message)s' + logging.basicConfig(format=FORMAT) + + logger = [] + logger.append(logging.getLogger(LOGGER)) + logger.append(logging.getLogger(CImpulseDecoder.LOGGER)) + #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)) + CImpulseDecoder +# logger.append(logging.getLogger('CGenericMultiPartTemp')) + + parser = argparse.ArgumentParser() + parser.add_argument("--khanat-host-service", help="filter to detect khanat host:service (FES)") + parser.add_argument("--filter-host-service", help="filter host:service") + parser.add_argument("-d", "--debug", help="show debug message", action='store_true') + parser.add_argument("-p", "--pcap-file", help="file pcap to read", required=True) + parser.add_argument("-m", "--msg-xml", help="file msg.xml (from server khanat)", required=True) + parser.add_argument("-r", "--raw", help="show message raw", action='store_true') + parser.add_argument("--show-raw-packet", help="show packet (raw data)", action='store_true') + parser.add_argument("--show-message-decoded", help="show packet (raw data)", action='store_true') + + args = parser.parse_args() + + if args.debug: + level = logging.getLevelName('DEBUG') + else: + level = logging.getLevelName('INFO') + + for logid in logger: + logid.setLevel(level) + logging.getLogger(LOGGER).info("Begin") + + spy = SpyPcap(khanat_host_service=args.khanat_host_service, pcap_file=args.pcap_file, msg_xml=args.msg_xml, filter_host_service=args.filter_host_service, + show_raw_packet=args.show_raw_packet, show_message_decoded=args.show_message_decoded) + if args.raw: + spy.readRaw() + else: + spy.read() + logging.getLogger(LOGGER).info("End") + +if __name__ == "__main__": + main() diff --git a/tools/BitStream.py b/tools/BitStream.py new file mode 100644 index 0000000..26523cd --- /dev/null +++ b/tools/BitStream.py @@ -0,0 +1,980 @@ +#!/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 . + +from ctypes import * +import sys +import inspect +import copy + + +class OverflowError(Exception): + pass + +class BitStream(): + def __init__(self): + self._pos = 0 + self._read = 0 + self._tampon = [] + self._groupRead = [] + self._groupWrite = [] + + def __len__(self): + return (self._pos + 7) // 8 + + def __deepcopy__(self, memo): + ret = BitStream() + ret._pos = self._pos + ret._read = self._read + ret._tampon = copy.deepcopy(self._tampon, memo) + ret._groupRead = copy.deepcopy(self._groupRead, memo) + ret._groupWrite = copy.deepcopy(self._groupWrite, memo) + return ret + + def needRead(self): + return self._pos - self._read + + def sizeData(self): + return self._pos + + def sizeRead(self): + return self._read + + def getRead(self): + return self._read + + def getPos(self): + return self._pos + + def buffer(self): + return self._tampon + + def putRead(self, value): + if value > self._pos: + raise ValueError + self._read = value + + # ------------------------------------ + def internalSerial(self, value, nbits, decode=True, typeName=''): + p1 = self._pos + if nbits == 0: + return + elif nbits > 32: + raise "Out of range" + pos = self._pos % 8 + if pos == 0: + self._tampon.append(0) + value = c_uint32(value).value + if nbits != 32: + mask = (1 << nbits) - 1; + v = value & mask; + else: + v = value; + _FreeBits = 8 - (self._pos % 8) + if nbits > _FreeBits: + self._tampon[-1] |= (v >> ( nbits - _FreeBits)) + self._pos += _FreeBits + self.internalSerial( v , nbits - _FreeBits, decode=False) + else: + self._tampon[-1] |= (v << ( _FreeBits - nbits)) + self._pos += nbits + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+nbits, name, typeName, value)) + + def pushBool(self, value, decode=True): + p1 = self._pos + if value: + v = 1 + else: + v = 0 + self.internalSerial(v, 1, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+1, name, 'Bool', value)) + + def pushUint32(self, value, decode=True, typeName='Uint32'): + p1 = self._pos + self.internalSerial(value, 32, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+32, name, typeName, value)) + + def pushSint32(self, value, decode=True): + p1 = self._pos + self.internalSerial(value, 32, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+32, name, 'Sint32', value)) + + def pushUint16(self, value, decode=True): + p1 = self._pos + self.internalSerial(value, 16, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+16, name, 'Uint16', value)) + + def pushSint16(self, value, decode=True): + p1 = self._pos + self.internalSerial(value, 16, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+16, name, 'Sint16', value)) + + def pushUint8(self, value, decode=True): + p1 = self._pos + self.internalSerial(value, 8, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+8, name, 'Uint8', value)) + + def pushSint8(self, value, decode=True): + p1 = self._pos + self.internalSerial(value, 8, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+8, name, 'Sint8', value)) + + def pushUint64(self, value, decode=True): + p1 = self._pos + if sys.byteorder == "little": + self.internalSerial(value>>32, 32, decode=False) + self.internalSerial(value, 32, decode=False) + else: + self.internalSerial(value, 32, decode=False) + self.internalSerial(value>>32, 32, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+64, name, 'Uint64', value)) + + def pushSint64(self, value, decode=True): + p1 = self._pos + if sys.byteorder == "little": + self.internalSerial(value>>32, 32, decode=False) + self.internalSerial(value, 32, decode=False) + else: + self.internalSerial(value, 32, decode=False) + self.internalSerial(value>>32, 32, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+64, name, 'Sint64', value)) + + def pushFloat(self, value, decode=True): + p1 = self._pos + v = c_float(value).value + v1 = struct.pack('f', v) + v2 = struct.unpack('> 32, 32, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+64, name, 'Float', value)) + + def pushChar(self, value, decode=True): + p1 = self._pos + v = ord(value) + self.internalSerial(v, 8, decode=False) + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p1+8, name, 'Char', value)) + + def pushString(self, value, decode=True): + lenValue = len(value) + self.pushUint32(lenValue, decode=True, typeName='String:len') + p1 = self._pos + for _char in value: + self.pushChar(_char, decode=False) + p2 = self._pos + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p2, name, 'String', value)) + + def pushUString(self, value, decode=True): + lenValue = len(value) + self.pushUint32(lenValue, decode=True, typeName='UString:len') + p1 = self._pos + for x in value: + _char16bit = ord(x) + self.internalSerial(_char16bit, 16, decode=False) + p2 = self._pos + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p2, name, 'UString', value)) + + def pushArrayUint8(self, value, decode=True): + ' ex.: pushArrayChar([0,1,3,4]) ' + p1 = self._pos + for i in value: + self.pushUint8(i, decode=False) + p2 = self._pos + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p2, name, 'ArrayUint8', value)) + + def pushBuffer(self, source, decode=True): + ''' + Push BitStream with all byte (extend to byte if necessary) + ''' + p1 = self._pos + srcRead = source.getRead() + source.putRead(0) + for ele in source._tampon: + self.pushUint8(ele, decode=False) + source.putRead(srcRead) + p2 = self._pos + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p2, name, 'Buffer', '')) + + def pushBitStream(self, source, decode=True): + p1 = self._pos + srcRead = source.getRead() + source.putRead(0) + need = 8 - (self._pos % 8) + if need != 8: + self.internalSerial(source.readSerial(need, decode=False), need, decode=False) + while source.needRead() >= 8: + self.pushUint8(source.readSerial(8, decode=False), decode=False) + + need = source.needRead() + if need > 0: + self.internalSerial(source.readSerial(need, decode=False), need, decode=False) + + source.putRead(srcRead) + p2 = self._pos + if decode: + frame = inspect.currentframe() + frame = inspect.getouterframes(frame)[1] + string = inspect.getframeinfo(frame[0]).code_context[0].strip() + args = string[string.find('(') + 1:-1].split(',') + name = '?' + for i in args: + if i.find('=') != -1: + if i.split('=')[0].strip() == "value": + name = i.split('=')[1].strip() + break + else: + name = i + break + name = name.strip().split(' ')[0].strip() + self._groupWrite.append((p1, p2, name, 'BitStream', '')) + + # ------------------------------------ + def readSerial(self, nbits, name="", decode=True, typeName='', emulate=False): + v1 = self._read + if nbits == 0: + return + 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) + raise OverflowError + if emulate: + oldRead = self._read + value = 0 + pos = self._read // 8 + _FreeBits = 8 - (self._read % 8) + v = self._tampon[pos] & ((1 << _FreeBits) - 1) + if nbits > _FreeBits: + value |= (v << (nbits-_FreeBits)) + self._read += _FreeBits + value |= self.readSerial(nbits - _FreeBits, decode=False) + else: + value |= (v >> (_FreeBits-nbits)) + self._read += nbits + if emulate: + self._read = oldRead + if decode and not emulate: + self._groupRead.append((v1, v1+nbits, name, typeName, value)) + return value + + def readBool(self, name): + v1 = self._read + v = self.readSerial(1, name=name, decode=False, typeName='Bool') + self._groupRead.append((v1, v1+1, name, 'Bool', 'True' if v != 0 else 'False')) + if v != 0: + return True + else: + return False + + def readUint32(self, name, decode=True, typeName='Uint32'): + v = self.readSerial(32, name=name, decode=decode, typeName=typeName) + return v + + def readSint32(self, name, decode=True): + v = self.readSerial(32, name=name, decode=decode, typeName='Sint32') + return c_int32(v).value + + def readUint16(self, name, decode=True): + v = self.readSerial(16, name=name, decode=decode, typeName='Uint16') + return v + + def readSint16(self, name): + v = self.readSerial(16, name=name, typeName='Sint16') + return c_int16(v).value + + def readUint8(self, name, decode=True, typeName='Uint8'): + v = self.readSerial(8, decode=decode, name=name, typeName=typeName) + return v + + def readSint8(self, name): + v = self.readSerial(8, name=name, typeName='Sint8') + return c_int8(v).value + + def readUint64(self, name): + p1 = self._read + if sys.byteorder == "little": + v1 = self.readSerial(32, decode=False) + v2 = self.readSerial(32, decode=False) + else: + v2 = self.readSerial(32, decode=False) + v1 = self.readSerial(32, decode=False) + v3 = (v1 << 32) | v2 + self._groupRead.append((p1, p1+64, name, 'Uint64', v3)) + return v3 + + def readSint64(self, name): + p1 = self._read + if sys.byteorder == "little": + v1 = self.readSerial(32, decode=False) + v2 = self.readSerial(32, decode=False) + else: + v2 = self.readSerial(32, decode=False) + v1 = self.readSerial(32, decode=False) + v3 = (v1 << 32) | v2 + self._groupRead.append((p1, p1+64, name, 'Sint64', c_int64(v3).value)) + return c_int64(v3).value + + def readFloat(self, name): + p1 = self._read + v = self.readSerial(32, name=name, decode=False, typeName='Float') + v1 = struct.pack('I', v) + v2 = struct.unpack(' 0: + x = self.readChar('', decode=False) + tmp += x + _size -= 1 + v2 = self._read + if v2 > self._pos: + raise ValueError + if v1 < v2: + self._groupRead.append((v1, v2, name + ':string', 'String', tmp)) + return tmp + + def readUtf8String(self, name): + tmp = b'' + _size = self.readUint32(name + ':size', decode=True) + v1 = self._read + while _size > 0: + x = self.readUint8(name='', decode=False) + tmp += bytes(chr(x), encoding='latin1') + _size -= 1 + v2 = self._read + if v2 > self._pos: + raise ValueError + if v1 < v2: + self._groupRead.append((v1, v2, name + ':ustring', 'UString', tmp)) + return tmp.decode(encoding='utf-8') + + def readUString(self, name): + tmp = '' + _size = self.readUint32(name + ':size', decode=True) + v1 = self._read + while _size > 0: + x = self.readUint16(name='', decode=False) + tmp += chr(x) + _size -= 1 + v2 = self._read + if v2 > self._pos: + raise ValueError + if v1 < v2: + self._groupRead.append((v1, v2, name + ':ustring', 'UString', tmp)) + return tmp + + def readArrayUint8(self, size, name): + ret = [] + v1 = self._read + for i in range(0, size): + ret.append(self.readUint8('', decode=False)) + v2 = self._read + self._groupRead.append((v1, v2, name, 'ArrayUint8', '')) + return ret + + def readBitStreamUint8(self, size, name): + ret = BitStream() + v1 = self._read + for i in range(0, size): + ret.pushUint8(self.readUint8('', decode=False), decode=False) + v2 = self._read + self._groupRead.append((v1, v2, name, 'StreamUint8', '')) + return ret + + def readCont(self, name): + ret = BitStream() + size = self.readSint32(name = name + ':len', decode=True) + v1 = self._read + for i in range(0, size): + ret.pushBool(self.readSerial(1,name = '', decode=False)) + v2 = self._read + self._groupRead.append((v1, v2, name + ':data', 'readCont', '')) + return size, ret + + def getNotRead(self): + ret = BitStream() + 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) + ret.internalSerial(data, nsize, decode=False) + self._read = readBefore + return ret + # ------------------------------------ + def __str__(self): + return ''.join([ chr(x) for x in self._tampon]) + + def message(self): + # 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 ) + + def fromBytes(self, data): + self._read = 0 + self._tampon = [int(x) for x in data] + self._pos = len(self._tampon) * 8 + + def showLastData(self): + ret = "" + 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 nsize == 1: + ret += "{0:01b}".format(data) + elif nsize == 2: + ret += "{0:02b}".format(data) + elif nsize == 3: + ret += "{0:03b}".format(data) + elif nsize == 4: + ret += "{0:04b}".format(data) + elif nsize == 5: + ret += "{0:05b}".format(data) + elif nsize == 6: + ret += "{0:06b}".format(data) + elif nsize == 7: + ret += "{0:07b}".format(data) + else: + ret += "{0:08b}".format(data) + if ret != "": + ret += "." + 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: + nsize = 8 + else: + nsize = self._pos - self._read + data = self.readSerial(nsize, decode=False) + if nsize == 1: + ret += "{0:01b}".format(data) + elif nsize == 2: + ret += "{0:02b}".format(data) + elif nsize == 3: + ret += "{0:03b}".format(data) + elif nsize == 4: + ret += "{0:04b}".format(data) + elif nsize == 5: + ret += "{0:05b}".format(data) + elif nsize == 6: + ret += "{0:06b}".format(data) + elif nsize == 7: + ret += "{0:07b}".format(data) + else: + ret += "{0:08b}".format(data) + self._read = readBefore + ret2 = "" + + last = 0 + for x, y, name, typeName, value in self._groupRead: + ret2 += "[<" + str(x) + ':' + str(y-1) + "> " + str(name) + ' (' + typeName + ') : ' + ret[x:y] + ' => ' + str(value) + "]" + last = y + if last < self._pos: + ret2 += "{" + ret[last:] + "}" + + return ret2 + + def showAllDataWrite(self): + ret = "" + readBefore = self._read + self._read = 0 + 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 nsize == 1: + ret += "{0:01b}".format(data) + elif nsize == 2: + ret += "{0:02b}".format(data) + elif nsize == 3: + ret += "{0:03b}".format(data) + elif nsize == 4: + ret += "{0:04b}".format(data) + elif nsize == 5: + ret += "{0:05b}".format(data) + elif nsize == 6: + ret += "{0:06b}".format(data) + elif nsize == 7: + ret += "{0:07b}".format(data) + else: + ret += "{0:08b}".format(data) + self._read = readBefore + ret2 = "" + + last = 0 + for x, y, name, typeName, value in self._groupWrite: + ret2 += "[<" + str(x) + ':' + str(y-1) + "> " + str(name) + ' (' + typeName + ') : ' + ret[x:y] + ' => ' + str(value) + "]" + last = y + if last < self._pos: + ret2 += "{" + ret[last:] + "}" + + return ret2 + + def showAllDataRaw(self): + ret = "" + readBefore = self._read + self._read = 0 + 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 nsize == 1: + ret += "{0:01b}".format(data) + elif nsize == 2: + ret += "{0:02b}".format(data) + elif nsize == 3: + ret += "{0:03b}".format(data) + elif nsize == 4: + ret += "{0:04b}".format(data) + elif nsize == 5: + ret += "{0:05b}".format(data) + elif nsize == 6: + ret += "{0:06b}".format(data) + elif nsize == 7: + ret += "{0:07b}".format(data) + else: + ret += "{0:08b}".format(data) + if ret != "": + ret += "." + self._read = readBefore + return ret + + def getPosInBit(self): + return self._pos + +def TestBitStream(): + a = BitStream() + vrai = True + a.pushBool(decode=True, value=vrai) + a.pushBool(False) + a.pushBool(True) + a.pushBool(True) + a.pushUint32(1234567890) + a.pushSint32(-1234567890) + a.pushUint16(12345) + a.pushSint16(-12345) + a.pushUint8(123) + a.pushSint8(-123) + #-3.4E+38) # 1.2339999675750732) + a.pushFloat(-3.3999999521443642e+38) + a.pushDouble(-1.7E+308) + a.pushUint64(16045690709418696365) + a.pushSint64(-1) + a.pushChar('a') + a.pushString("Test A Faire") + print("-" * 40) + print(a.showAllData()) + print('raw:') + print(a.showAllDataRaw()) + print("-" * 20) + print("-" * 80) + print(a.readBool('a1')) + print(a.readBool('a2')) + print(a.readBool('a3')) + print(a.readBool('a4')) + print(a.readUint32('a5')) + print(a.readSint32('a6')) + print(a.readUint16('a7')) + print(a.readSint16('a8')) + print(a.readUint8('a9')) + print(a.readSint8('a10')) + print(a.readFloat('a11')) + print(a.readDouble('a12')) + print(a.readUint64('a13')) + print(a.readSint64('a14')) + print(a.readChar('a15')) + print(a.readString('a16')) + print(a.toBytes()) + print("-" * 40) + print(a.showAllData()) + print("-" * 80) + b = BitStream() + b.fromBytes(a.toBytes()) + print(b.readBool('b1')) + print(b.readBool('b2')) + print(b.readBool('b3')) + print(b.readBool('b4')) + print(b.readUint32('b5')) + print(b.readSint32('b6')) + print(b.readUint16('b7')) + print(b.readSint16('b8')) + print(b.readUint8('b9')) + print(b.readSint8('b10')) + print(b.readFloat('b11')) + print(b.readDouble('b12')) + print(b.readUint64('b13')) + print(b.readSint64('b14')) + print(b.readChar('b15')) + print(b.readString('b16')) + print(b.toBytes()) + print("-" * 40) + print(b.showAllData()) + print("-" * 80) + c = BitStream() + c.pushBool(True) + c.pushBitStream(a) + c.pushBitStream(b) + print(c.readBool('c1')) + print("-" * 80) + print(c.readBool('c2')) + print(c.readBool('c3')) + print(c.readBool('c4')) + print(c.readBool('c5')) + print(c.readUint32('c6')) + print(c.readSint32('c7')) + print(c.readUint16('c8')) + print(c.readSint16('c9')) + print(c.readUint8('c10')) + print(c.readSint8('c11')) + print(c.readFloat('c12')) + print(c.readDouble('c13')) + print(c.readUint64('c14')) + print(c.readSint64('c15')) + print(c.readChar('c16')) + print(c.readString('c17')) + print(c.toBytes()) + print("-" * 50) + print(c.showAllData()) + print("-" * 50) + print(c.readBool('c18')) + print(c.readBool('c19')) + print(c.readBool('c20')) + print(c.readBool('c21')) + print(c.readUint32('c22')) + print(c.readSint32('c23')) + print(c.readUint16('c24')) + print(c.readSint16('c25')) + print(c.readUint8('c26')) + print(c.readSint8('c27')) + print(c.readFloat('c28')) + print(c.readDouble('c29')) + print(c.readUint64('c30')) + print(c.readSint64('c31')) + print(c.readChar('c32')) + print("-" * 40) + print(c.showAllData()) + print(c.readString('c33')) + print("-" * 40) + print(c.showAllData()) + print(c.toBytes()) + print("-" * 40) + print(c.showAllDataRaw()) + print("-" * 20) + print(c.showAllData()) + print("-" * 80) diff --git a/tools/CAction.py b/tools/CAction.py new file mode 100644 index 0000000..dfbe01e --- /dev/null +++ b/tools/CAction.py @@ -0,0 +1,375 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CActionFactory +# +# 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 . + +#from tools import Enum +import logging +from tools import TPropIndex +from tools import TPVPMode + +LOGGER='CActionFactory' +INVALID_SLOT = 0xff + +class CAction: + 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 + + def pack(self, msgout): + if self.Code < 4: + modeShort = True + msgout.pushBool(modeShort) + code = self.Code + msgout.internalSerial(self.Code, 2) + else: + modeShort = False + msgout.pushBool(modeShort) + code = self.Code + msgout.pushUint8(code) + + def serialIn(self, msgin): + raise RuntimeError + + def serialOut(self, msgout): + raise RuntimeError + + def size(self): + headerBitSize = 0 + if self.Code < 4: + headerBitSize = 1 + 2 + else: + headerBitSize = 1 + (1 * 8) + return headerBitSize + + def getMaxSizeInBit(self): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def setPriority(self, prio): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def priority(self): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def getValue(self): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def setValue(self, value): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def isContinuous(self): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def reset(self): + logging.getLogger(LOGGER).debug("New Object but missing method reset") + raise RuntimeError + + def __str__(self): + return "[%d,%d]" % (self.Code , self.Slot) + +class CActionPosition(CAction): + def __init__(self, slot, code, world): + super().__init__(slot, code, world) + self.Position = [0, 0, 0] + self.Position16 = [0, 0, 0] + self.IsRelative = False + self.Interior = False + + def __str__(self): + return "CActionPosition" + super().__str__() + " x:" + str(self.Position16[0]) + " y:" + str(self.Position16[1]) + " z:" + str(self.Position16[2]) + " IsRelative:" + str(self.IsRelative) + " Interior:" + str(self.Interior) + + def unpack(self, message): + self.Position16[0] = message.readUint16('px') + self.Position16[1] = message.readUint16('py') + self.Position16[2] = message.readUint16('pz') + self.IsRelative = (self.Position16[2] & 0x1) != 0 + self.Interior = (self.Position16[2] & 0x2) != 0 + +# message.serialAndLog1( Position16[0] ); +# message.serialAndLog1( Position16[1] ); +# message.serialAndLog1( Position16[2] ); +# IsRelative = (Position16[2] & (uint16)0x1)!=0; +# Interior = (Position16[2] & (uint16)0x2)!=0; + + def reset(self): + pass + +class CActionSync(CAction): + 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, world): + super().__init__(slot, code, world) + + def __str__(self): + return "CActionDisconnection" + super().__str__() + +class CActionAssociation(CAction): + 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, world): + super().__init__(slot, code, world) + + def __str__(self): + return "CActionDummy" + super().__str__() + +class CActionLogin(CAction): + 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, world): + super().__init__(slot, code, world) + + def __str__(self): + return "CActionTargetSlot" + super().__str__() + +class CActionGeneric(CAction): + def __init__(self, slot, code, world): + super().__init__(slot, code, world) + self._Message = None + self.decoded = False + + def set(self, message): + self._Message = message + + def unpack(self, message): + size = message.readUint32('size') + self._Message = message.readBitStreamUint8(size, 'message') + + def pack(self, msgout): + super().pack(msgout) + sizeMessage = len(self._Message) + msgout.pushUint32(sizeMessage) + msgout.pushBuffer(self._Message) + + def reset(self): + self._Message = None + + def genericAction(self, decodeImpulse, world, cGenericMultiPartTemp): + decodeImpulse.execute(self._Message, world) + self.decoded = True + + def __str__(self): + if self.decoded: + return "CActionGeneric" + super().__str__() + ' => ' + self._Message.showAllData() + else: + return "CActionGeneric" + super().__str__() + "[read:" + self._Message.showAllData() + '/write:' + self._Message.showAllDataWrite() + ']' + + def size(self): + size = super().size() + if self._Message: + return 4 + len(self._Message) * 8 + size + else: + return 4 + size + +class CActionGenericMultiPart(CAction): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/action_generic_multi_part.h # class CActionGenericMultiPart + ''' + def __init__(self, slot, code, world): + super().__init__(slot, code, world) + self.PartCont = [] + self.Number = 0 + self.Part = 0 + self.NbBlock = 0 + + def set(self, number, part, buffer, bytelen, size, nbBlock): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/action_generic_multi_part.h # void set (uint8 number, uint16 part, const uint8 *buffer, uint32 bytelen, uint32 size, uint16 nbBlock) + ''' + logging.getLogger(LOGGER).debug("number:%d part:%d bytelen:%d size:%d nbBlock:%d" %(number, part, bytelen, size, nbBlock)) + start = part*size + end = start + size + logging.getLogger(LOGGER).debug("start:%d end:%d bytelen:%d" % (start, end, bytelen)) + if end > bytelen: + end = bytelen + + logging.getLogger(LOGGER).debug("start:%d end:%d" % (start, end)) + self.PartCont = [] + # memcpy( &PartCont[0], buffer + start, end - start ); + for i in range(start, end): + logging.getLogger(LOGGER).debug("Append : %d/%d" % (i, len(buffer))) + self.PartCont.append(buffer[i]) + self.Number = number + self.Part = part + self.NbBlock = nbBlock + + def unpack(self, message): + self.Number = message.readUint8('Number') + self.Part = message.readUint16('Part') + self.NbBlock = message.readUint16('NbBlock') + + size = message.readUint32('size') + self.PartCont = message.readBitStreamUint8(size, 'PartCont') + logging.getLogger(LOGGER).debug("unpack - Number:%d Part:%d NbBlock:%d" % (self.Number, self.Part, self.NbBlock)) + + def pack(self, msgout): + super().pack(msgout) + msgout.pushUint8(self.Number) + msgout.pushUint16(self.Part) + msgout.pushUint16(self.NbBlock) + msgout.pushArrayUint8(self.PartCont) + + def reset(self): + self.PartCont = [] + self.Number = 0 + self.Part = 0 + self.NbBlock = 0 + + def genericAction(self, decodeImpulse, world, cGenericMultiPartTemp): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::genericAction (CActionGenericMultiPart *agmp) + ''' + logging.getLogger(LOGGER).debug("Number:%d Part:%d NbBlock:%d" % (self.Number, self.Part, self.NbBlock)) + cGenericMultiPartTemp.addGenericMultiPartTemp(self.Number) + cGenericMultiPartTemp.setGenericMultiPartTemp(self.Number, self.Part, self.NbBlock, self.PartCont, decodeImpulse, world) + + def __str__(self): + return "CActionGenericMultiPart" + super().__str__() + "[" + str(self.Number) + ',' + str(self.Part) + ',' + str(self.NbBlock) + ',read:' + self.PartCont.showAllData() + ',write:' + self.PartCont.showAllDataWrite() + ']' + + def size(self): + size = super().size() + bytesize = 1 + 2 + 2 + 4 # header + # self.PartCont + for ele in self.PartCont: + bytesize += len(ele) + return bytesize * 8 + size + + +class CActionSint64(CAction): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/action_sint64.cpp + + + A lire : + /home/jerome/Projets/khanat/khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1311 receiveNormalMessage(CBitMemStream &msgin) + /home/jerome/Projets/khanat/khanat-opennel-code/code/ryzom/client/src/network_connection.cpp:1472 receiveNormalMessage(CBitMemStream &msgin) + ''' + def __init__(self, slot, code, world): + super().__init__(slot, code, world) + self.value = 0 + self.NbBits = 0 + self.PropertyToNbBit = { TPropIndex.TPropIndex.PROPERTY_ORIENTATION: 32, + TPropIndex.TPropIndex.PROPERTY_SHEET: 52, + TPropIndex.TPropIndex.PROPERTY_BEHAVIOUR: 48, + TPropIndex.TPropIndex.PROPERTY_NAME_STRING_ID: 32, + TPropIndex.TPropIndex.PROPERTY_TARGET_ID: 8, + TPropIndex.TPropIndex.PROPERTY_MODE: 44, + TPropIndex.TPropIndex.PROPERTY_VPA: 64, + TPropIndex.TPropIndex.PROPERTY_VPB: 47, + TPropIndex.TPropIndex.PROPERTY_VPC: 58, + TPropIndex.TPropIndex.PROPERTY_ENTITY_MOUNTED_ID: 8 , # slot + TPropIndex.TPropIndex.PROPERTY_RIDER_ENTITY_ID: 8 , # slot + TPropIndex.TPropIndex.PROPERTY_CONTEXTUAL: 16 , + TPropIndex.TPropIndex.PROPERTY_BARS: 32 , # please do not lower it (or tell Olivier: used for forage sources) + TPropIndex.TPropIndex.PROPERTY_TARGET_LIST: TPropIndex.USER_DEFINED_PROPERTY_NB_BITS , + TPropIndex.TPropIndex.PROPERTY_VISUAL_FX: 11 , # please do not lower it (or tell Olivier: used for forage sources) + TPropIndex.TPropIndex.PROPERTY_GUILD_SYMBOL: 60 , + TPropIndex.TPropIndex.PROPERTY_GUILD_NAME_ID: 32 , + TPropIndex.TPropIndex.PROPERTY_EVENT_FACTION_ID: 32 , + TPropIndex.TPropIndex.PROPERTY_PVP_MODE: TPVPMode.NbBits , + TPropIndex.TPropIndex.PROPERTY_PVP_CLAN: 32 , + TPropIndex.TPropIndex.PROPERTY_OWNER_PEOPLE: 3 , # 4 races and unknow + TPropIndex.TPropIndex.PROPERTY_OUTPOST_INFOS: 16 } # 15+1 + + def __str__(self): + return "CActionSint64" + super().__str__() + + def unpack(self, msgin): + logging.getLogger(LOGGER).debug("msgin:%s" % msgin.showAllData()) + self.value = msgin.readUint64('value') + 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.pushUint32(self.NbBits) + + def reset(self): + self.value = 0 + self.NbBits = 0 + + +class CActionBlock: + def __init__(self): + self.Cycle = 0 + self.FirstPacket = 0 + self.Actions = [] + self.Success = True + + def serial(self, msgout, actionFactory): + msgout.pushUint32(self.Cycle) + msgout.pushUint8(len(self.Actions)) + for action in self.Actions: + # msgPosBefore = msgout.getPosInBit() + actionFactory.pack(action, msgout) + # msgPosAfter = msgout.getPosInBit() + # actionSize = actionFactory.size(action) + + def writeSerial(self, msgout): + msgout.pushUint32(self.Cycle) + numberActions = len(self.Actions) + msgout.pushUint8(numberActions) + for action in self.Actions: + action.pack(msgout) + + def insert(self, actions, begin, end): + for i in range(0, end): + if i>= begin: + self.Actions.append(actions[i]) + + def eraseToEnd(self, begin): + while len(self.Actions) >= begin: + self.Actions.pop() + + 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]) + "]" + diff --git a/tools/CActionFactory.py b/tools/CActionFactory.py new file mode 100644 index 0000000..a904cf9 --- /dev/null +++ b/tools/CActionFactory.py @@ -0,0 +1,117 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CActionFactory +# +# 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 tools import Enum +from tools import CAction + +LOGGER='CActionFactory' + +class CActionFactory: + def __init__(self, world): + self.world = world + self.RegisteredAction = {} + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_POSITION_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_GENERIC_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_SINT64, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_SYNC_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_DISCONNECTION_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_ASSOCIATION_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_LOGIN_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_TARGET_SLOT_CODE, []) + self.RegisteredAction.setdefault(Enum.TActionCode.ACTION_DUMMY_CODE, []) + + def createFactory(self, slot, code): + if code == Enum.TActionCode.ACTION_POSITION_CODE: + logging.getLogger(LOGGER).debug("Create CActionPosition") + return CAction.CActionPosition(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_GENERIC_CODE: + logging.getLogger(LOGGER).debug("Create CActionGeneric") + return CAction.CActionGeneric(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE: + logging.getLogger(LOGGER).debug("Create CActionGenericMultiPart") + return CAction.CActionGenericMultiPart(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_SINT64: + logging.getLogger(LOGGER).debug("Create CActionSint64") + return CAction.CActionSint64(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_SYNC_CODE: + logging.getLogger(LOGGER).debug("Create CActionSync") + return CAction.CActionSync(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_DISCONNECTION_CODE: + logging.getLogger(LOGGER).debug("Create CActionDisconnection") + return CAction.CActionDisconnection(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_ASSOCIATION_CODE: + logging.getLogger(LOGGER).debug("Create CActionAssociation") + return CAction.CActionAssociation(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_LOGIN_CODE: + logging.getLogger(LOGGER).debug("Create CActionLogin") + return CAction.CActionLogin(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_TARGET_SLOT_CODE: + logging.getLogger(LOGGER).debug("Create CActionTargetSlot") + return CAction.CActionTargetSlot(slot, code, self.world) + elif code == Enum.TActionCode.ACTION_DUMMY_CODE: + logging.getLogger(LOGGER).debug("Create CActionDummy") + return CAction.CActionDummy(slot, code, self.world) + else: + logging.getLogger(LOGGER).warning('create() try to create an unknown action (%u)' % code) + raise RuntimeError + + def create(self, slot, code): + if code not in self.RegisteredAction: + logging.getLogger(LOGGER).warning('try to create an unknown action (code:%u)' % code) + raise None + elif not self.RegisteredAction[code]: + logging.getLogger(LOGGER).debug('new CAction (code:%u)' % code) + action = self.createFactory(slot, code) + action.reset() + return action + else: + logging.getLogger(LOGGER).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 */ ) + ''' + if msgin.needRead() >= 8: + shortcode = msgin.readBool('shortcode') + if shortcode: + code = msgin.readSerial(2, 'code') + else: + code = msgin.readUint8('code') + action = self.create(CAction.INVALID_SLOT, code) + if action: + try: + action.unpack (msgin); + except RuntimeError: + log = logging.getLogger('myLogger') + log.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) + return action + + diff --git a/tools/CArg.py b/tools/CArg.py new file mode 100644 index 0000000..1960baa --- /dev/null +++ b/tools/CArg.py @@ -0,0 +1,319 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CArg +# +# 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 . + +from ctypes import * +from tools import Enum + +# ##################################################### +# persistent_data.h:140 # struct CArg +# ##################################################### +class CArgV1(Structure): + _fields_ = [("i32_1", c_uint), + ("i32_2", c_uint)] + + +class CArgV2(Structure): + _fields_ = [("ex32_1", c_uint), + ("ex32_2", c_uint)] + + +class CArgV3(Union): + _fields_ = [("ex32", CArgV2), + ("ExData32", c_uint), + ("ExData64", c_ulong)] + + +class CArgV4(Structure): + _fields_ = [("ExType", c_uint), + ("ex", CArgV3)] + + +class CArgV5(Union): + _fields_ = [("i", CArgV1), + ("ii32", c_int), + ("ii64", c_long), + ("i32", c_uint), + ("i64", c_ulong), + ("f32", c_float), + ("f64", c_double), + ("ex", CArgV4)] + +class CArg: + # union + # { + # struct + # { + # uint32 i32_1; + # uint32 i32_2; + # } i; + # + # sint32 i32; + # sint64 i64; + # float f32; + # double f64; + # + # struct + # { + # uint32 ExType; + # union + # { + # struct + # { + # uint32 ex32_1; + # uint32 ex32_2; + # }; + # + # uint32 ExData32; + # uint64 ExData64; + # } ex; + # } ex; + # } _Value; + + def __init__(self): + self._value = CArgV5() + self._value.ii64 = 0 + self._value.i64 = 0 + self._type = 0 + self._string = 0 + self._type64 = False + + def read_Type(self): + return self._type + + def write_Type(self, value): + self._type = value + + def write_Type64(self, value): + self._type64 = value + + def read_String(self): + return self._string + + def write_String(self, value): + self._string = value + + def read_i32_1(self): + return self._value.i.i32_1 + + def write_i32_1(self, value): + self._value.i.i32_1 = value + + def read_i32_2(self): + return self._value.i.i32_2 + + def write_i32_2(self, value): + self._value.i.i32_2 = value + + def read_i32(self): + return self._value.i32 + + def write_i32(self, value): + self._value.i32 = value + + def read_i64(self): + return self._value.i64 + + def write_i64(self, value): + self._value.i64 = value + + def read_f32(self): + return self._value.f32 + + def write_f32(self, value): + self._value.f32 = value + + def read_f64(self): + return self._value.f64 + + def write_f64(self, value): + self._value.f64 = value + + def read_ExType(self): + return self._value.ex.ExType + + def write_ExType(self, value): + self._value.ex.ExType = value + + def read_ex32_1(self): + return self._value.ex.ex.ex32.ex32_1 + + def write_ex32_1(self, value): + self._value.ex.ex.ex32.ex32_1 = value + + def read_ex32_2(self): + return self._value.ex.ex.ex32.ex32_2 + + def write_ex32_2(self, value): + self._value.ex.ex.ex32.ex32_2 = value + + def read_ExData32(self): + return self._value.ex.ex.ExData32 + + def write_ExData32(self, value): + self._value.ex.ex.ExData32 = value + + def read_ExData64(self): + return self._value.ex.ex.ExData64 + + def write_ExData64(self, value): + self._value.ex.ex.ExData64 = value + + def isExtended(self): + if self._type == Enum.TType.EXTEND_TYPE: + return True + elif self._type == Enum.TType.STRUCT_BEGIN: + self.log.error("Can't extract a value from a structure delimiter") + sys.exit(2) + elif self._type == Enum.TType.STRUCT_END: + self.log.error("Can't extract a value from a structure delimiter") + sys.exit(2) + return False + + def isFlag(self): + if self._type == Enum.TType.FLAG: + return True + else: + return False + + def asUint(self): + if self._type == Enum.TType.STRUCT_BEGIN or self._type == Enum.TType.STRUCT_END: + self.log.error("Can't extract a value from a structure delimiter") + sys.exit(2) + elif self._type == Enum.TType.SINT32: + return self.read_i32() + elif self._type == Enum.TType.UINT32: + return self.read_i32() + elif self._type == Enum.TType.SINT64: + return self.read_i64() + elif self._type == Enum.TType.UINT64: + return self.read_i64() + elif self._type == Enum.TType.FLOAT32: + return self.read_i32() + elif self._type == Enum.TType.FLOAT64: + return self.read_i64() + elif self._type == Enum.TType.STRING: + return int(self._string) + elif self._type == Enum.TType.FLAG: + return "1" + elif self._type == Enum.TType.EXTEND_TYPE: + if self.read_ExType() == TExtendType.ET_SHEET_ID: + return self.read_ExData32() + elif self.read_ExType() == TExtendType.ET_ENTITY_ID: + return self.read_ExData64() + log = logging.getLogger('myLogger') + log.error("This should never happen!") + sys.exit(2) + + def __str__(self): + log = logging.getLogger('myLogger') + log.debug(self._type) + if self._type == Enum.TType.STRUCT_BEGIN or self._type == Enum.TType.STRUCT_END: + return '' + elif self._type64: + # To be confirm for extend + return str(self.read_ExData64()) + elif self._type == Enum.TType.SINT32: + return str(self.read_i32()) + elif self._type == Enum.TType.UINT32: + return str(self.read_i32()) + elif self._type == Enum.TType.SINT64: + return str(self.read_i64()) + elif self._type == Enum.TType.UINT64: + return str(self.read_i64()) + elif self._type == Enum.TType.FLOAT32: + return str(self.read_i32()) + elif self._type == Enum.TType.FLOAT64: + return str(self.read_i64()) + elif self._type == Enum.TType.STRING: + return self._string + elif self._type == Enum.TType.FLAG: + return "1" + return '?' + + def asSint(self): + self.log.error("TODO") + sys.exit(2) + + def asFloat(self): + self.log.error("TODO") + sys.exit(2) + + def asDouble(self): + self.log.error("TODO") + sys.exit(2) + + def asString(self): + if self._type == Enum.TType.STRUCT_BEGIN or self._type == Enum.TType.STRUCT_END: + self.log.error("Can't extract a value from a structure delimiter") + sys.exit(2) + elif self._type == Enum.TType.SINT32: + return str(self.read_ii32()) + elif self._type == Enum.TType.UINT32: + return str(self.read_i32()) + elif self._type == Enum.TType.SINT64: + return str(self.read_ii64()) + elif self._type == Enum.TType.UINT64: + return str(self.read_i64()) + elif self._type == Enum.TType.FLOAT32: + return str(self.read_f32()) + elif self._type == Enum.TType.FLOAT64: + return str(self.read_f64()) + elif self._type == Enum.TType.STRING: + return self._string + elif self._type == Enum.TType.FLAG: + return "1" + elif self._type == Enum.TType.EXTEND_TYPE: + self.log.error("TODO") + sys.exit(2) + # switch(_Value.ExType) + # { + # case ET_SHEET_ID: + # { + # NLMISC::CSheetId sheetId(_Value.ExData32); + # return sheetId.toString(true); + # } + # case ET_ENTITY_ID: + # { + # NLMISC::CEntityId entityId(_Value.ExData64); + # return entityId.toString(); + # } + # default: + # break; + # } + self.log.error("This should never happen!") + sys.exit(2) + + def asUCString(self): + self.log.error("TODO") + sys.exit(2) + + def asEntityId(self): + self.log.error("TODO") + sys.exit(2) + + def asSheetId(self): + self.log.error("TODO") + sys.exit(2) + + def typeName(self): + self.log.error("TODO") + sys.exit(2) + +# ##################################################### diff --git a/tools/CBNPCategorySet.py b/tools/CBNPCategorySet.py new file mode 100644 index 0000000..8622a2f --- /dev/null +++ b/tools/CBNPCategorySet.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CBNPCategorySet +# +# 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 . + +class CBNPCategorySet: + def __init__(self): + self._Name = "" + self._IsOptional = False + self._UnpackTo = "" + self._IsIncremental = False + self._CatRequired = "" + self._Hidden = False + self._Files = [] + + def __str__(self): + return self._Name + ' (IsOptional:' + str(self._IsOptional) + ', UnpackTo:' + self._UnpackTo + ', IsIncremental:' + str(self._IsIncremental) + ', CatRequired:' + self._CatRequired + ', Hidden:' + str(self._Hidden) + ', Files:' + str(self._Files) + ')' + diff --git a/tools/CBNPFile.py b/tools/CBNPFile.py new file mode 100644 index 0000000..8d4e886 --- /dev/null +++ b/tools/CBNPFile.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CBNPFile +# +# 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 . + +class CBNPFile: + def __init__(self): + self.FileName = None + self.Versions = [] + self.IsIncremental = False + + def __str__(self): + return str(self.FileName) +' (' + ', '.join( [str(x) for x in self.Versions]) + ')' + + def update(self, FileName): + self.FileName = FileName + diff --git a/tools/CBNPFileVersion.py b/tools/CBNPFileVersion.py new file mode 100644 index 0000000..88cc042 --- /dev/null +++ b/tools/CBNPFileVersion.py @@ -0,0 +1,32 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CBNPFileVersion +# +# 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 . + +class CBNPFileVersion: + def __init__(self): + self.VersionNumber = None + self.FileTime = None + self.FileSize = None + self.v7ZFileSize = None + self.PatchSize = None + self.HashKey = [] + + def __str__(self): + return "VersionNumber:" + str(self.VersionNumber) + ", FileTime:" + str(self.FileTime) + ", FileSize:" + str(self.FileSize) + ", 7ZFileSize:" + str(self.v7ZFileSize) + ", PatchSize:" + str(self.PatchSize) + ", HashKey:" + str(self.HashKey) + diff --git a/tools/CBitSet.py b/tools/CBitSet.py new file mode 100644 index 0000000..a05f3a0 --- /dev/null +++ b/tools/CBitSet.py @@ -0,0 +1,110 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CBitSet +# +# 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 . + +class CBitSet: + def __init__(self, NL_BITLEN=32): + self.NumBits = 0 + self.MaskLast = 0 + self.NL_BITLEN = NL_BITLEN + self.data = self.resize(1024) + + def resize(self, numBits): + self.data = [ 0 for _ in range(0, (numBits + self.NL_BITLEN - 1) // self.NL_BITLEN) ] + self.NumBits = numBits + nLastBits = self.NumBits & (self.NL_BITLEN-1) + if nLastBits == 0: + self.MaskLast = ~0 + else: + self.MaskLast = (1 << nLastBits)-1 + self.clearAll() + + def clearData(self): + self.data = [] + self.NumBits = 0 + self.MaskLast = 0 + + def clearAll(self): + for i in range(0, len(self.data)): + self.data[i] = 0 + + def set(self, bitNumber, value): + #logging.getLogger('myLogger').debug("CBitSet::set %d %s" % (bitNumber, str(value))) + mask = bitNumber & (self.NL_BITLEN-1) + mask = 1 << mask + if value: + self.data[bitNumber >> 5] |= mask + else: + self.data[bitNumber >> 5] &= ~mask + + def get(self, bitNumber): + mask= bitNumber&(self.NL_BITLEN-1); + mask= 1<> 5] & mask != 0 + + def setBit(self, bitNumber): + self.set(bitNumber, True) + + def clearBit(self, bitNumber): + self.set(bitNumber, False) + + def __str__(self): + return '.'.join([hex(x) for x in self.data]) + def writeSerial(self, msgout): +# v = 0 # currentVersion +# if v >= 0xff: +# msgout.pushUint8(0xff) +# msgout.pushUint8(v) +# else: +# msgout.pushUint8(v) + + #log = logging.getLogger('myLogger') + #log.debug("CBitSet::writeSerial NumBits:%d len:%d" % (self.NumBits, len(self.data))) + + currentVersion = 0 + msgout.pushUint8(currentVersion) + msgout.pushUint32(self.NumBits) + # il est lié à 'self.NumBits' dommage que l'on envoie celui-la + msgout.pushUint32(len(self.data)) + for x in self.data: + msgout.pushUint32(x) + + def readSerial(self, msgin, name): + _ = msgin.readSint8(name+':currentVersion') + NumBits = msgin.readUint32(name+':NumBits') + Size = msgin.readUint32(name+':Size') + self.resize(NumBits) + for i in range(0, Size): + y = msgin.readUint32( name+':Data' ) + self.data[i] = y + +def TestCBitSet(): + cBitSet = CBitSet() + cBitSet.resize(1024) + cBitSet.set(1, True) + cBitSet.set(3, True) + cBitSet.set(2, False) + cBitSet.set(13, True) + cBitSet.set(128, True) + cBitSet.set(1023, True) + print(cBitSet) + print(cBitSet.get(3)) + cBitSet.set(3, False) + print(cBitSet) + print(cBitSet.get(3)) diff --git a/tools/CCharacterSummary.py b/tools/CCharacterSummary.py new file mode 100644 index 0000000..acb0960 --- /dev/null +++ b/tools/CCharacterSummary.py @@ -0,0 +1,146 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CCharacterSummary +# +# 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 . + +from tools import Enum +from tools import convertUStringToString + +class SPropVisualA(): + def __init__(self): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/player_visual_properties.h # struct SPropVisualA + ''' + self.Sex = False #: 1; // max: 2 current: 2 + self.JacketModel = 0 #: 8; // max: 256 current: 93 + self.JacketColor = 0 #: 3; // max: 8 current: 8 + self.TrouserModel = 0 #: 8; // max: 256 current: 104 + self.TrouserColor = 0 #: 3; // max: 8 current: 8 + self.WeaponRightHand = 0 #: 10; // max: 1024 current: 457 + self.WeaponLeftHand = 0 #: 8; // max: 256 current: 63 + self.ArmModel = 0 #: 8; // max: 256 current: 94 + self.ArmColor = 0 #: 3; // max: 8 current: 8 + self.HatModel = 0 #: 9; // max: 512 current: 192 + self.HatColor = 0 #: 3; // max: 8 current: 8 + + def read(self, msgin): + self.Sex = msgin.readBool('Sex') + self.JacketModel = msgin.readUint8('JacketModel') + self.JacketColor = msgin.readSerial(3, 'JacketModel') + self.TrouserModel = msgin.readSerial(8, 'TrouserModel') + self.TrouserColor = msgin.readSerial(3, 'TrouserColor') + self.WeaponRightHand = msgin.readSerial(10, 'WeaponRightHand') + self.WeaponLeftHand = msgin.readSerial(8, 'WeaponLeftHand') + self.ArmModel = msgin.readSerial(8, 'ArmModel') + self.ArmColor = msgin.readSerial(3, 'ArmColor') + self.HatModel = msgin.readSerial(9, 'HatModel') + self.HatColor = msgin.readSerial(3, 'HatColor') + + +class SPropVisualB(): + def __init__(self): + self.Name = "" #: 16; + self.HandsModel = 0 #: 9; // max: 512 current: 90 + self.HandsColor = 0 #: 3; // max: 8 current: 8 + self.FeetModel = 0 #: 9; // max: 512 current: 94 + self.FeetColor = 0 #: 3; // max: 8 current: 8 + self.RTrail = 0 #: 4; + self.LTrail = 0 #: 3; + self.NotUsed = 0 # 17 : # not used -> just to complete 64 bit + def read(self, msgin): + self.Name = msgin.readSerial(16, 'Name') + self.HandsModel = msgin.readSerial(9, 'HandsModel') + self.HandsColor = msgin.readSerial(3, 'HandsColor') + self.FeetModel = msgin.readSerial(9, 'FeetModel') + self.FeetColor = msgin.readSerial(3, 'FeetColor') + self.RTrail = msgin.readSerial(4, 'RTrail') + self.LTrail = msgin.readSerial(3, 'LTrail') + self.NotUsed = msgin.readSerial(17, 'NotUsed') + + +class SPropVisualC(): + def __init__(self): + self.MorphTarget1 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget2 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget3 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget4 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget5 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget6 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget7 = 0 # : 3; // max: 8 current: 8 + self.MorphTarget8 = 0 # : 3; // max: 8 current: 8 + self.EyesColor = 0 # : 3; // max: 8 current: 8 + self.Tattoo = 0 # : 7; // max: 128 current: 64 + self.CharacterHeight = 0 # : 4; // max: 16 current: 16 + self.TorsoWidth = 0 # : 4; // max: 16 current: 16 + self.ArmsWidth = 0 # : 4; // max: 16 current: 16 + self.LegsWidth = 0 # : 4; // max: 16 current: 16 + self.BreastSize = 0 # : 4; // max: 16 current: 16 + self.NotUsed = 0 # 10 : # not used -> just to complete 64 bit + def read(self, msgin): + self.MorphTarget1 = msgin.readSerial(3, 'MorphTarget1') + self.MorphTarget2 = msgin.readSerial(3, 'MorphTarget2') + self.MorphTarget3 = msgin.readSerial(3, 'MorphTarget3') + self.MorphTarget4 = msgin.readSerial(3, 'MorphTarget4') + self.MorphTarget5 = msgin.readSerial(3, 'MorphTarget5') + self.MorphTarget6 = msgin.readSerial(3, 'MorphTarget6') + self.MorphTarget7 = msgin.readSerial(3, 'MorphTarget7') + self.MorphTarget8 = msgin.readSerial(3, 'MorphTarget8') + self.EyesColor = msgin.readSerial(3, 'EyesColor') + self.Tattoo = msgin.readSerial(7, 'Tattoo') + self.CharacterHeight = msgin.readSerial(4, 'CharacterHeight') + self.TorsoWidth = msgin.readSerial(4, 'TorsoWidth') + self.ArmsWidth = msgin.readSerial(4, 'ArmsWidth') + self.LegsWidth = msgin.readSerial(4, 'LegsWidth') + self.BreastSize = msgin.readSerial(4, 'BreastSize') + self.NotUsed = msgin.readSerial(10, 'NotUsed') + +class CCharacterSummary(): + def __init__(self): + self.version = -1 + self.Mainland = 0 # CSessionId + self.Name = "" + self.People = Enum.TPeople.Unknown + self.Location = 0 + self.sPropVisualA = SPropVisualA() + self.sPropVisualB = SPropVisualB() + self.sPropVisualC = SPropVisualC() + self.sheetId = 0 + self.Title = Enum.ECharacterTitle.NB_CHARACTER_TITLE + self.CharacterSlot = 255 + self.InRingSession = False + self.HasEditSession = False + self.InNewbieland = False + + def read(self, msgin): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/character_summary.cpp # void CCharacterSummary::serial(NLMISC::IStream &f) + ''' + self.version = msgin.readUint8('version') + self.Mainland = msgin.readUint32('Mainland') + self.Name = convertUStringToString.convertUStringToString(msgin.readUString('Name')) + self.People = msgin.readSint32('People') + self.Location = msgin.readUint32('Location') + self.sPropVisualA.read(msgin) + self.sPropVisualB.read(msgin) + self.sPropVisualC.read(msgin) + self.sheetId = msgin.readUint32('SheetId') + self.Title = msgin.readSint32('Title') # see ECharacterTitle + self.CharacterSlot = msgin.readUint8('CharacterSlot') + self.InRingSession = msgin.readBool('InRingSession') + self.HasEditSession = msgin.readBool('HasEditSession') + self.InNewbieland = msgin.readBool('InNewbieland') diff --git a/tools/CFileChild.py b/tools/CFileChild.py new file mode 100644 index 0000000..74e09de --- /dev/null +++ b/tools/CFileChild.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CFileChild +# +# 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 . + +class CFileChild(): + def __init__(self, name, pos, size): + self.name = name + self.pos = pos + self.size = size + + def __str__(self): + return self.name + '(pos:' + str(self.pos) + ', size:' + str(self.size) + ')' diff --git a/tools/CFileContainer.py b/tools/CFileContainer.py new file mode 100644 index 0000000..fdf66d4 --- /dev/null +++ b/tools/CFileContainer.py @@ -0,0 +1,79 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CFileContainer +# +# 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 +import os +from tools import CFileList + + +class CFileContainer(): + def __init__(self): + self.log = logging.getLogger('myLogger') + self.list = [] + + def addSearchPath(self, path): + if not path: + return + self.log.debug("read path:" + str(path)) + onlyfiles = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] + self.log.debug("read files:" + ','.join(onlyfiles)) + for filename in onlyfiles: + extension = os.path.splitext(filename)[1] + if extension == '.bnp': + # Container for multi file + fullpath = os.path.join(path, filename) + size = os.path.getsize(fullpath) + data = CFileList.CFileList(filename, fullpath) + with open(fullpath, 'rb') as fp: + fp.seek(size-4) + nOffsetFromBeginning = int.from_bytes(fp.read(4), byteorder='little', signed=False) + self.log.debug("[%s] nOffsetFromBeginning:%u" % (filename, nOffsetFromBeginning)) + fp.seek(nOffsetFromBeginning) + nNbFile = int.from_bytes(fp.read(4), byteorder='little', signed=False) + self.log.debug("[%s] nNbFile:%u" % (filename, nNbFile)) + for i in range(0, nNbFile): + nStringSize = int.from_bytes(fp.read(1), byteorder='little', signed=False) + FileName = fp.read(nStringSize).decode() + nFileSize2 = int.from_bytes(fp.read(4), byteorder='little', signed=False) + + nFilePos = int.from_bytes(fp.read(4), byteorder='little', signed=False) + self.log.debug("[%s] (%d) sizestring:%d file:%s size2:%d pos:%d" % (filename, i, nStringSize, FileName, nFileSize2, nFilePos)) + data.addchild(FileName, nFilePos, nFileSize2) + fp.close() + self.list.append(data) + + def search(self, name): + for x in self.list: + for y in x.child: + if y.name == name: + self.log.debug("file:%s child:%s pos:%d size:%d", x.name, y.name, y.pos, y.size) + return x.fullpath, y.pos, y.size + self.log.debug('-'*80) + return None, None, None + + def getdata(self, name): + fullpath, pos, size = self.search(name) + self.log.debug("file:%s pos:%d size:%d", fullpath, pos, size) + data = None + with open(fullpath, 'rb') as fp: + fp.seek(pos) + data = fp.read(size) + fp.close() + return data diff --git a/tools/CFileList.py b/tools/CFileList.py new file mode 100644 index 0000000..4885eaa --- /dev/null +++ b/tools/CFileList.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CFileList +# +# 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 . + +from tools import CFileChild + +class CFileList(): + def __init__(self, name, fullpath): + self.name = name + self.fullpath = fullpath + self.child = [] + + def addchild(self, name, pos, size): + child = CFileChild.CFileChild(name, pos, size) + self.child.append(child) + + def __str__(self): + return self.name + '[' + ', '.join([str(x) for x in self.child]) + ']' diff --git a/tools/CGenericMultiPartTemp.py b/tools/CGenericMultiPartTemp.py new file mode 100644 index 0000000..0c647cf --- /dev/null +++ b/tools/CGenericMultiPartTemp.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CGenericMultiPartTemp +# +# 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 tools import BitStream + +LOGGER='CGenericMultiPartTemp' + +class CGenericMultiPartTemp(): + def __init__(self): + self.NbBlock = 0xFFFFFFFF + self.NbCurrentBlock = 0 + self.TempSize = 0 + self.Temp = [] + self.BlockReceived = [] + self.MsgDecoded = None + self.FirstRead = False + + def set(self, Number, Part, NbBlock, PartCont, decodeImpulse, world): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::CGenericMultiPartTemp::set (CActionGenericMultiPart *agmp, CNetworkConnection *parent) + ''' + logging.getLogger(LOGGER).debug("set Number:%d Part:%d NbBlock:%d" % (Number, Part, NbBlock)) + 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]: + logging.getLogger(LOGGER).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) + + logging.getLogger(LOGGER).debug("NbCurrentBlock:%d / NbBlock:%d" % (self.NbCurrentBlock, self.NbBlock)) + if self.NbCurrentBlock == self.NbBlock: + # reform the total action + bms = BitStream.BitStream() + + self.NbBlock == 0xFFFFFFFF + for data in self.Temp: + bms.pushBitStream(data) + decodeImpulse.execute(bms, world) + logging.getLogger(LOGGER).debug("CGenericMultiPartTemp : data : %s" % bms.showAllData()) + self.MsgDecoded = bms + else: + logging.getLogger(LOGGER).debug("CGenericMultiPartTemp : Wait other block") + + def isAvailable(self): + if self.MsgDecoded and not self.FirstRead: + return True + return False + + def read(self): + self.FirstRead = True + return self.MsgDecoded + + +class GenericMultiPartTemp(): + def __init__(self): + self.data = {} + + def addGenericMultiPartTemp(self, Number): + self.data.setdefault(Number, CGenericMultiPartTemp()) + + def setGenericMultiPartTemp(self, Number, Part, NbBlock, PartCont, decodeImpulse, world): + self.data[Number].set(Number, Part, NbBlock, PartCont, decodeImpulse, world) diff --git a/tools/CI18N.py b/tools/CI18N.py new file mode 100644 index 0000000..7d9ecd3 --- /dev/null +++ b/tools/CI18N.py @@ -0,0 +1,31 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CI18N +# +# 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 . + +# Data from ryzom/tools/translation/translated/[en|fr|...].uxt + +class CI18N: + def __init__(self): + self.message = {} + self.message.setdefault("uiMissionTimerHour", "[h]") + self.message.setdefault("uiMissionTimerMinute", "[m]") + self.message.setdefault("uiMissionTimerSecond", "[s]") + + def get(self, key): + return self.message[key] diff --git a/tools/CImpulseDecoder.py b/tools/CImpulseDecoder.py new file mode 100644 index 0000000..af4b894 --- /dev/null +++ b/tools/CImpulseDecoder.py @@ -0,0 +1,89 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CImpulseDecoder +# +# 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 +import copy +from tools import CActionFactory + +LOGGER='CImpulseDecoder' + +class CImpulseDecoder: + ''' + see : khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp + ''' + def __init__(self, world): + self.world = world + self.reset() + self._CActionFactory = CActionFactory.CActionFactory(world) + + def removeCAction(self, action): + self._CActionFactory.RegisteredAction[action.Code].append(action) + + def decode(self, msgin, receivedPacket, receivedAck, nextSentPacket): + ''' + khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp:38 oid CImpulseDecoder::decode(CBitMemStream &inbox, TPacketNumber receivedPacket, TPacketNumber receivedAck, TPacketNumber nextSentPacket, vector &actions) + ''' + logging.getLogger(LOGGER).debug("*" * 80) + logging.getLogger(LOGGER).debug("receivedPacket:%d receivedAck:%d nextSentPacket:%d" %(receivedPacket, receivedAck, nextSentPacket)) + actions = [] + for level in range(0, 3): + logging.getLogger(LOGGER).debug("*" * 60) + if level == 0: + lAck = self._LastAck0 + channel = 0 + elif level == 1: + lAck = self._LastAck1 + channel = receivedPacket & 1 + elif level == 2: + lAck = self._LastAck2 + channel = receivedPacket & 3 + keep = True + checkOnce = False + num = 0 + logging.getLogger(LOGGER).debug("level:%d channel:%d lAck:%s" %(level, channel, ':'.join([str(x) for x in lAck]))) + # lastAck = lAck[channel] + while True: + logging.getLogger(LOGGER).debug("*" * 40) + next = msgin.readBool('next:' + str(level) + ':' + str(channel)) + if not next: + break + if not checkOnce: + checkOnce = True + keep = receivedAck >= lAck[channel] + logging.getLogger(LOGGER).debug("keep:%s [%d => %d]" % (str(keep), receivedAck, lAck[channel])) + if keep: + lAck[channel] = nextSentPacket + logging.getLogger(LOGGER).debug("lAck:%s" % ':'.join([str(x) for x in lAck])) + num += 1 + action = self._CActionFactory.unpack(msgin) + if keep: + logging.getLogger(LOGGER).debug("keep : %s" % str(action)) + actions.append(copy.copy(action)) + elif action: + logging.getLogger(LOGGER).debug("append : %s" % str(action)) + self.removeCAction(copy.copy(action)) + logging.getLogger(LOGGER).debug('actions: ' +','.join( [ str(x) for x in actions] ) ) + logging.getLogger(LOGGER).debug("*" * 80) + return actions + + def reset(self): + self._LastAck0 = [-1] + self._LastAck1 = [-1, -1] + self._LastAck2 = [-1, -1, -1, -1] diff --git a/tools/CMainlandSummary.py b/tools/CMainlandSummary.py new file mode 100644 index 0000000..a00d077 --- /dev/null +++ b/tools/CMainlandSummary.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CMainlandSummary +# +# 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 . + +from tools import CSessionId + + +class CMainlandSummary(): + def __init__(self): + self.Id = CSessionId.CSessionId() + self.Name = "" + self.Description = "" + self.LanguageCode = "" + self.Online = False + def read(self, msgin): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/mainland_summary.h # void serial(NLMISC::IStream &f) + ''' + self.Id.read(msgin) + self.Name = msgin.readUString('Name') + self.Description = msgin.readUString('Description') + self.LanguageCode = msgin.readString('LanguageCode') + self.Online = msgin.readBool('Online') diff --git a/tools/CPersistentDataRecord.py b/tools/CPersistentDataRecord.py new file mode 100644 index 0000000..c861b7b --- /dev/null +++ b/tools/CPersistentDataRecord.py @@ -0,0 +1,695 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CPersistentDataRecord +# +# 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 +import sys +from tools import Enum +from tools import CArg +from tools import TExtendType +from tools import CBNPCategorySet +from tools import CBNPFile +from tools import CBNPFileVersion + +LOGGER='CPersistentDataRecord' + +class CPersistentDataRecord: + def __init__(self): + self.TokenTable = [] + self.ArgTable = [] + self.StringTable = [ ] + self.ReadingStructStack = [] + self.offsetToken = 0 + self.ArgOffset = 0 + self.version = 0 + self.totalSize = 0 + self.tokenCount = 0 + self.argCount = 0 + self.stringCount = 0 + self.stringsSize = 0 + self.CBNPFile = [] + self.Categories = [] + + def show(self): + for x in self.CBNPFile: + logging.getLogger(LOGGER).debug("File:%s" % str(x)) + for x in self.Categories: + logging.getLogger(LOGGER).debug("Categorie:%s" % str(x)) + + # ---------------- Manipulate Token ---------------- + + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + # | Token ID | Token Type | + # +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + def token2Type(self, token, extend): # persistent_data_inline.h:1102 CPersistentDataRecord::CArg::TType CPersistentDataRecord::CArg::token2Type(uint32 token,bool extend) + logging.getLogger(LOGGER).debug("token:%d, extend:%d" % (token, extend)) + if token == Enum.Card.BEGIN_TOKEN: + return Enum.TType.STRUCT_BEGIN + elif token == Enum.Card.END_TOKEN: + return Enum.TType.STRUCT_END + elif token == Enum.Card.FLAG_TOKEN: + return Enum.TType.FLAG + elif token == Enum.Card.SINT_TOKEN: + if extend: + return Enum.TType.SINT64 + else: + return Enum.TType.SINT32 + elif token == Enum.Card.UINT_TOKEN: + if extend: + return Enum.TType.UINT64 + else: + return Enum.TType.UINT32 + elif token == Enum.Card.FLOAT_TOKEN: + if extend: + return Enum.TType.FLOAT64 + else: + return Enum.TType.FLOAT32 + elif token == Enum.Card.STRING_TOKEN: + if extend: + return Enum.TType.EXTEND_TYPE + else: + return Enum.TType.STRING + logging.getLogger(LOGGER).error('This should never happen!') + sys.exit(2) + + def type2Token(self, type): # persistent_data_inline.h:1118 CPersistentDataRecord::TToken CPersistentDataRecord::CArg::type2Token(uint32 type) + logging.getLogger(LOGGER).debug("type: %d" %(type)) + if type == Enum.TType.STRUCT_BEGIN: + return Enum.Card.BEGIN_TOKEN + elif type == Enum.TType.STRUCT_END: + return Enum.Card.END_TOKEN + elif type == Enum.TType.FLAG: + return Enum.Card.FLAG_TOKEN + elif type == Enum.TType.SINT32: + return Enum.Card.SINT_TOKEN + elif type == Enum.TType.UINT32: + return Enum.Card.UINT_TOKEN + elif type == Enum.TType.FLOAT32: + return Enum.Card.FLOAT_TOKEN + elif type == Enum.TType.STRING: + return Enum.Card.STRING_TOKEN + elif type == Enum.TType.SINT64: + return Enum.Card.SINT_TOKEN + elif type == Enum.TType.UINT64: + return Enum.Card.UINT_TOKEN + elif type == Enum.TType.FLOAT64: + return Enum.Card.FLOAT_TOKEN + elif type == Enum.TType.EXTEND_TYPE: + return Enum.Card.STRING_TOKEN + logging.getLogger(LOGGER).error('This should never happen!') + sys.exit(2) + + def peekNextToken(self): + token = self.TokenTable[self.offsetToken] + logging.getLogger(LOGGER).debug("[%d] token:%d" %(self.offsetToken, token)) + return token // 8 # persistent_data_inline.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() # _TokenTable[_TokenOffset]>>3; + + def peekNextTokenType(self): # persistent_data_limit.h:308 CPersistentDataRecord::TToken CPersistentDataRecord::peekNextToken() const + logging.getLogger(LOGGER).debug("peekNextTokenType - old offset token:%d" % self.offsetToken) + if self.isEndOfData(): + logging.getLogger(LOGGER).error('Attempt to read past end of input data') + sys.exit(2) + token = self.TokenTable[self.offsetToken] + tokenType = token & 7 + if tokenType == Enum.Card.EXTEND_TOKEN: + if self.offsetToken + 1 > self.tokenCount: + logging.getLogger(LOGGER).error('Attempt to read past end of input data') + sys.exit(2) + tokenType = self.TokenTable[self.offsetToken+1] + logging.getLogger(LOGGER).debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType)) + return self.token2Type(tokenType, True) + logging.getLogger(LOGGER).debug("peekNextTokenType [%d] token:%d type:%d" %(self.offsetToken, token, tokenType)) + return self.token2Type(tokenType, False) + + def isEndOfData(self): + if self.offsetToken == self.tokenCount: + return True + return False + + def isEndOfStruct(self): + if self.isEndOfData(): + logging.getLogger(LOGGER).debug("isEndOfData") + return True + elif len(self.ReadingStructStack) == 0: + logging.getLogger(LOGGER).debug("ReadingStructStack") + return False + elif self.peekNextTokenType() != Enum.TType.STRUCT_END: + logging.getLogger(LOGGER).debug("peekNextTokenType != Enum.TType.STRUCT_END") + return False + elif self.ReadingStructStack[-1] != self.peekNextToken(): + logging.getLogger(LOGGER).error("Opening and closing structure tokens don't match") + sys.exit(2) + logging.getLogger(LOGGER).debug("isEndOfStruct") + return True + + def isStartOfStruct(self): + if self.peekNextTokenType() == Enum.TType.STRUCT_BEGIN: + return True + return False + + def popStructBegin(self, token): + if self.peekNextToken() != token: + logging.getLogger(LOGGER).error('Attempting to enter a structure with the wrong delimiting token') + sys.exit(2) + if self.peekNextTokenType() != Enum.TType.STRUCT_BEGIN: + logging.getLogger(LOGGER).error('Attempting to leave a structure with the wrong delimiting token type') + sys.exit(2) + self.ReadingStructStack.append(token) + self.offsetToken += 1 + + def popStructEnd(self, token): + if len(self.ReadingStructStack) == 0: + logging.getLogger(LOGGER).error('Attempting to pop end of a structure with nothing left in the open structure stack') + sys.exit(2) + nextToken = self.peekNextToken() + topToken = self.ReadingStructStack[-1] + if topToken != token: + logging.getLogger(LOGGER).error('Attempting to pop end of a structure with the wrong delimiting token') + sys.exit(2) + if nextToken != token: + logging.getLogger(LOGGER).error('Attempting to pop end of a structure with the wrong delimiting token') + sys.exit(2) + if self.peekNextTokenType() != Enum.TType.STRUCT_END: + logging.getLogger(LOGGER).error('Attempting to leave a structure with the wrong delimiting token type') + sys.exit(2) + del self.ReadingStructStack[-1] + self.offsetToken += 1 + + # ---------------- Manipulate StringTable ---------------- + def lookupString(self, idx): + if idx >= self.stringCount: + logging.getLogger(LOGGER).error("Attempting to access past end of string table") + sys.exit(2) + return self.StringTable[idx] + + # ---------------- Manipulate Arg ---------------- + def peekNextArg(self): # persistent_data_limit.h:339 CPersistentDataRecord::peekNextArg(CPersistentDataRecord::CArg& result) const + _type = self.peekNextTokenType() + result = CArg.CArg() + result.write_Type(_type) + result.write_Type64(False) + logging.getLogger(LOGGER).debug("peekNextArg - Type:%d ArgOffset:%d" % (_type, self.ArgOffset)) + if result.isExtended(): + logging.getLogger(LOGGER).debug("Extended") + result.write_i32_1(self.ArgTable[self.ArgOffset]) + result.write_i32_2(self.ArgTable[self.ArgOffset+1]) + if result.read_Type() == Enum.TType.EXTEND_TYPE and result.read_ExType() == TExtendType.TExtendType.ET_64_BIT_EXTENDED_TYPES: + result.write_ex32_2(self.ArgTable[self.ArgOffset+2]); + result.write_Type64(True) + elif not result.isFlag(): + # result._Value.i32_1 = _ArgTable[_ArgOffset]; + result.write_i32_1(self.ArgTable[self.ArgOffset]) + logging.getLogger(LOGGER).debug("peekNextArg - id :%d" % result.read_i32_1()) + if result.read_Type() == Enum.TType.STRING: + result.write_String(self.lookupString(result.read_i32_1())) + logging.getLogger(LOGGER).debug("peekNextArg - String:%s" % result.read_String()) + return result + + def popNextArg(self, token): # persistent_data_limit.h:414 CPersistentDataRecord::popNextArg(TToken token,CPersistentDataRecord::CArg& result) + result = self.peekNextArg() + if result.isFlag(): + self.offsetToken += 1 + elif result.isExtended(): + self.ArgOffset += 2 + self.offsetToken += 2 + if result.read_Type() == Enum.TType.EXTEND_TYPE and result.read_ExType() == TExtendType.TExtendType.ET_64_BIT_EXTENDED_TYPES: + self.ArgOffset += 1 + self.offsetToken += 1 + else: + self.ArgOffset += 1 + self.offsetToken += 1 + logging.getLogger(LOGGER).debug("popNextArg - Arg:%d", result.read_i32_1()) + return result + + def popString(self, token): + TempArg = self.popNextArg(token) + return TempArg.asString() + + def popUint32(self, token): + TempArg = self.popNextArg(token) + return TempArg.asUint() + + def popBool(self, token): + TempArg = self.popNextArg(token) + return TempArg.asUint() != 0 + + # ---------------- Read Data ---------------- + def readFromBinFile(self, filename): # persistent_data.cpp:835 # bool CPersistentDataRecord::fromBuffer(const char *src, uint32 bufferSize) + logging.getLogger(LOGGER).debug('Read Bin File %s' % filename) + with open(filename, "rb") as fp: + buffer = fp.read() + fp.close() + + self.version = int.from_bytes(buffer[0:4], byteorder='little', signed=False) + self.totalSize = int.from_bytes(buffer[4:8], byteorder='little', signed=False) + self.tokenCount = int.from_bytes(buffer[8:12], byteorder='little', signed=False) + self.argCount = int.from_bytes(buffer[12:16], byteorder='little', signed=False) + self.stringCount = int.from_bytes(buffer[16:20], byteorder='little', signed=False) + self.stringsSize = int.from_bytes(buffer[20:24], byteorder='little', signed=False) + offset = 24 + logging.getLogger(LOGGER).debug("version:%d, totalSize:%d, tokenCount:%d, argCount:%d, stringCount:%d, stringsSize:%d" % (self.version, self.totalSize, self.tokenCount, self.argCount, self.stringCount, self.stringsSize)) + if len(buffer) != self.totalSize: + logging.getLogger(LOGGER).error("Failed to parse buffer due to invalid header (file:%s, size:%d, size define:%d)" % (filename, len(buffer), self.totalSize )) + sys.exit(2) + if self.version > 0: + logging.getLogger(LOGGER).error("PDR ERROR: Wrong file format version! (file:%s, version:%d)" % (filename, self.version)) + sys.exit(2) + if (self.stringCount != 0 and self.stringsSize == 0) or (self.stringCount == 0 and self.stringsSize != 0): + logging.getLogger(LOGGER).error("PDR ERROR: Invalid string table parameters! (file:%s, stringCount:%d, stringsSize:%d)" % (filename, self.stringCount, self.stringsSize)) + sys.exit(2) + # i = offset+tokenCount*sizeof(TToken)+argCount*sizeof(uint32)+stringsSize + i = offset + self.tokenCount * 2 + self.argCount * 4 + self.stringsSize; + if self.totalSize != i: + logging.getLogger(LOGGER).error("PDR ERROR: Invalid source data (file:%s, totalSize:%d != datasize:%s)" % (filename, self.totalSize, i)) + sys.exit(2) + + # READ the tokens + self.TokenTable = [] + for i in range(0, self.tokenCount): + tmp = int.from_bytes(buffer[offset:offset+2], byteorder='little', signed=False) + logging.getLogger(LOGGER).debug("token %5d => %3d id:%3d type:%d" %(i, tmp, tmp // 8, tmp & 7)) + self.TokenTable.append(tmp) + offset += 2 + + # READ the arguments + self.ArgTable = [] + for i in range(0, self.argCount): + tmp = int.from_bytes(buffer[offset:offset+4], byteorder='little', signed=False) + self.ArgTable.append(tmp) + offset += 4 + + # READ the string table data + if self.stringsSize != 0: + chaine = '' + self.StringTable = [ ] + while offset < self.totalSize: + car = buffer[offset:offset+1].decode() + if car != '\0': + chaine += car + else: + self.StringTable.append(chaine) + chaine = '' + offset += 1 + logging.getLogger(LOGGER).debug(self.StringTable) + if chaine != '': + logging.getLogger(LOGGER).error("PDR ERROR: Too few strings found in string table (file:%s)" % (filename)) + sys.exit(2) + logging.getLogger(LOGGER).debug("Red %s" % filename) + + def decrypt_token(self): + i = 0 + lvl = 0 + posArg = 0 + extend = False + extend64 = False + result = CArg.CArg() + + print("^ Position ^ Token ^") + for value in self.TokenTable: + print("| %5d | %3d |" %(i, value)) + i += 1 + + i = 0 + print("^ Position ^ Argument ^") + for value in self.ArgTable: + print("| %5d | %3d |" %(i, value)) + i += 1 + + i = 0 + print("^ Position ^ String ^") + for value in self.StringTable: + print("| %5d | %s |" %(i, value)) + i += 1 + + i = 0 + print("^ Position ^ Niveau ^ Token ^ Token ID ^^ Token Type (Card) ^^^ Result ^") + print("^ ^^ (entrée) ^ Valeur ^ Quoi ^ Valeur ^ Card ^ Type ^ ^") + for token in self.TokenTable: + tokenId = token // 8 + tokenTypeValue = token & 7 + result.write_String("-") + if tokenTypeValue == 0: + tokenCard = 'BEGIN_TOKEN' + tokenType = 'STRUCT_BEGIN' + result.write_Type(Enum.TType.STRUCT_BEGIN) + if lvl <= 1: + print("| |||||||") + lvl += 1 + elif tokenTypeValue == 1: + tokenCard = 'END_TOKEN' + tokenType = 'STRUCT_END' + result.write_Type(Enum.TType.STRUCT_END) + extend = False + extend64 = False + elif tokenTypeValue == 2: + tokenCard = 'SINT_TOKEN' + if extend: + tokenType = 'SINT64' + result.write_Type(Enum.TType.SINT64) + result.write_i32_1(self.ArgTable[posArg]) + result.write_i32_2(self.ArgTable[posArg+1]) + if extend64: + result.write_ex32_2(self.ArgTable[posArg+2]); + posArg += 3 + else: + posArg += 2 + else: + tokenType = 'SINT32' + result.write_Type(Enum.TType.SINT32) + result.write_i32_1(self.ArgTable[posArg]) + posArg += 1 + extend = False + extend64 = False + elif tokenTypeValue == 3: + tokenCard = 'UINT_TOKEN' + if extend: + tokenType = 'UINT64' + result.write_Type(Enum.TType.UINT64) + result.write_i32_1(self.ArgTable[posArg]) + result.write_i32_2(self.ArgTable[posArg+1]) + if extend64: + result.write_ex32_2(self.ArgTable[posArg+2]); + posArg += 3 + else: + posArg += 2 + else: + tokenType = 'UINT32' + result.write_Type(Enum.TType.UINT32) + result.write_i32_1(self.ArgTable[posArg]) + posArg += 1 + extend = False + extend64 = False + elif tokenTypeValue == 4: + tokenCard = 'FLOAT_TOKEN' + if extend: + tokenType = 'FLOAT64' + result.write_Type(Enum.TType.FLOAT64) + result.write_i32_1(self.ArgTable[posArg]) + result.write_i32_2(self.ArgTable[posArg+1]) + if extend64: + result.write_ex32_2(self.ArgTable[posArg+2]); + posArg += 3 + else: + posArg += 2 + else: + tokenType = 'FLOAT32' + result.write_Type(Enum.TType.FLOAT32) + result.write_i32_1(self.ArgTable[posArg]) + posArg += 1 + extend = False + extend64 = False + elif tokenTypeValue == 5: + tokenCard = 'STRING_TOKEN' + if extend: + tokenType = 'EXTEND_TYPE' + result.write_Type(Enum.TType.EXTEND_TYPE) + result.write_i32_1(self.ArgTable[posArg]) + result.write_i32_2(self.ArgTable[posArg+1]) + if extend64: + result.write_ex32_2(self.ArgTable[posArg+2]); + posArg += 3 + else: + posArg += 2 + else: + tokenType = 'STRING' + result.write_Type(Enum.TType.STRING) + result.write_i32_1(self.ArgTable[posArg]) + tmp = result.read_i32_1() + result.write_String(self.StringTable[tmp]) + posArg += 1 + extend = False + extend64 = False + elif tokenType == 6: + tokenCard = 'FLAG_TOKEN' + tokenType = 'FLAG' + result.write_Type(Enum.TType.FLAG) + extend = False + extend64 = False + elif tokenTypeValue == 7: + if extend: + extend64 = True + tokenCard = 'EXTEND_TOKEN' + result.write_Type(Enum.TType.EXTEND_TYPE) + tokenType = '' + extend = True + # print("token %5d => %3d id:%3d [%s] type:%d [%s]" %(i, token, tokenId, self.StringTable[tokenId], tokenType, tokenCard)) + print("| %5d | %3d | %3d | %3d | %s | %d | %s | %s | %s |" %(i, lvl, token, tokenId, self.StringTable[tokenId], tokenTypeValue, tokenCard , tokenType, result)) + if tokenTypeValue == 1: + lvl -= 1 + i += 1 + + def addString(self, name): # persistent_data.cpp:100 uint16 CPersistentDataRecord::addString(const string& name) + for i in range(0, len(self.StringTable)): + if self.StringTable[i] == name: + return i + self.StringTable.append(name) + return len(self.StringTable) - 1 + + def CProductDescriptionForClient_apply(self): # persistent_data_template.h:459 # void PERSISTENT_CLASS::apply(CPersistentDataRecord &pdr _PERSISTENT_APPLY_ARGS) + __Tok__MapKey = self.addString("__Key__") + __Tok__MapVal = self.addString("__Val__") + __Tok_Files = self.addString("_Files") + __Tok_Categories = self.addString("_Categories") + logging.getLogger(LOGGER).debug("MapKey:%d, MapVal:%d, Files:%d, Categories:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files, __Tok_Categories)) + while not self.isEndOfStruct(): + nextToken = self.peekNextToken() + logging.getLogger(LOGGER).debug("nextToken:%d" % (nextToken)) + if nextToken == __Tok_Files: + self.popStructBegin(__Tok_Files) + self.CBNPFileSet_apply() + self.popStructEnd(__Tok_Files) + continue + elif nextToken == __Tok_Categories: + self.popStructBegin(__Tok_Categories) + # (_Categories).apply(pdr); + self.CBNPCategorySet_apply() + self.popStructEnd(__Tok_Categories) + continue + logging.getLogger(LOGGER).error("TODO") + sys.exit(2) + + def CBNPFileSet_apply(self): + __Tok__MapKey = self.addString("__Key__") + __Tok__MapVal = self.addString("__Val__") + __Tok_Files = self.addString("_Files") + logging.getLogger(LOGGER).debug("MapKey:%d, MapVal:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Files)) + while not self.isEndOfStruct(): + nextToken = self.peekNextToken() + logging.getLogger(LOGGER).debug("nextToken:%d" % (nextToken)) + if nextToken == __Tok_Files: + self.popStructBegin(__Tok_Files) + self.CBNPFile.append(CBNPFile.CBNPFile()) + self.CBNPFile_apply(self.CBNPFile[-1]) + self.popStructEnd(__Tok_Files) + continue + logging.getLogger(LOGGER).error("TODO") + sys.exit(2) + + def CBNPFile_apply(self, _CBNPFile): + __Tok__MapKey = self.addString("__Key__") + __Tok__MapVal = self.addString("__Val__") + __Tok_FileName = self.addString("_FileName") + __Tok_Versions = self.addString("_Versions") + _FileName = None + logging.getLogger(LOGGER).debug("MapKey:%d, MapVal:%d, Filename:%d, Versions:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_FileName, __Tok_Versions)) + while not self.isEndOfStruct(): + nextToken = self.peekNextToken() + logging.getLogger(LOGGER).debug("nextToken:%d" % (nextToken)) + if nextToken == __Tok_FileName: + _FileName = self.popString(nextToken) + _CBNPFile.FileName = _FileName + logging.getLogger(LOGGER).debug("filename: %s" % _FileName) + continue + if nextToken == __Tok_Versions: + self.popStructBegin(__Tok_Versions) + # vectAppend(_Versions).apply(pdr); + _CBNPFile.Versions.append(CBNPFileVersion.CBNPFileVersion()) + self.CBNPFileVersion_apply(_CBNPFile.Versions[-1]) + self.popStructEnd(__Tok_Versions) + continue + stack = [] + while True: + if self.isStartOfStruct(): + stack.append(self.peekNextToken()) + self.popStructBegin(stack) + elif self.isEndOfStruct(): + self.popStructEnd(stack[-1]) + if len(stack) > 0: + del stack[-1] + else: + self.popNextArg(self.peekNextToken()) + if self.isEndOfData() and len(stack) == 0: + break + logging.getLogger(LOGGER).debug("CBNPFile: %s" % _CBNPFile) + + def CBNPFileVersion_apply(self, _CBNPFileVersion): # persistent_data_template.h:459 # void CBNPFileVersion::apply(CPersistentDataRecord &pdr ) + __Tok__MapKey = self.addString("__Key__") + __Tok__MapVal = self.addString("__Val__") + __Tok_VersionNumber = self.addString("_VersionNumber") + __Tok_FileSize = self.addString("_FileSize") + __Tok_7ZFileSize = self.addString("_7ZFileSize") + __Tok_FileTime = self.addString("_FileTime") + __Tok_PatchSize = self.addString("_PatchSize") + __Tok_HashKey = self.addString("_HashKey") + logging.getLogger(LOGGER).debug("MapKey:%d, MapVal:%d, VersionNumber:%d, FileSize:%d, 7ZFileSize:%d, FileTime:%d, PatchSize:%d, HashKey:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_VersionNumber, __Tok_FileSize, __Tok_7ZFileSize, __Tok_FileTime, __Tok_PatchSize, __Tok_HashKey)) + + while not self.isEndOfStruct(): + nextToken = self.peekNextToken() + logging.getLogger(LOGGER).debug("nextToken:%d" % (nextToken)) + if nextToken == __Tok_VersionNumber: + logging.getLogger(LOGGER).debug("__Tok_VersionNumber") + _CBNPFileVersion.VersionNumber = self.popUint32(__Tok_VersionNumber) + logging.getLogger(LOGGER).debug("VersionNumber: %s" % _CBNPFileVersion.VersionNumber) + continue + elif nextToken == __Tok_FileSize: + logging.getLogger(LOGGER).debug("__Tok_FileSize") + _CBNPFileVersion.FileSize = self.popUint32(__Tok_FileSize) + logging.getLogger(LOGGER).debug("FileSize: %s" % _CBNPFileVersion.FileSize) + continue + elif nextToken == __Tok_7ZFileSize: + logging.getLogger(LOGGER).debug("__Tok_7ZFileSize") + _CBNPFileVersion.v7ZFileSize = self.popUint32(__Tok_7ZFileSize) + logging.getLogger(LOGGER).debug("7ZFileSize: %s" % _CBNPFileVersion.v7ZFileSize) + continue + elif nextToken == __Tok_FileTime: + logging.getLogger(LOGGER).debug("__Tok_FileTime") + _CBNPFileVersion.FileTime = self.popUint32(__Tok_FileTime) + logging.getLogger(LOGGER).debug("FileTime: %s" % _CBNPFileVersion.FileTime) + continue + elif nextToken == __Tok_PatchSize: + logging.getLogger(LOGGER).debug("__Tok_PatchSize") + _CBNPFileVersion.PatchSize = self.popUint32(__Tok_PatchSize) + logging.getLogger(LOGGER).debug("PatchSize: %s" % _CBNPFileVersion.PatchSize) + continue + elif nextToken == __Tok_HashKey: + logging.getLogger(LOGGER).debug("__Tok_HashKey") + _CBNPFileVersion.HashKey.append(self.popUint32(__Tok_HashKey)) + logging.getLogger(LOGGER).debug("HashKey: %s" % _CBNPFileVersion.HashKey[-1]) + continue + # Vidage des autres clefs (inconnues) + stack = [] + while True: + if self.isStartOfStruct(): + stack.append(self.peekNextToken()) + self.popStructBegin(stack) + elif self.isEndOfStruct(): + self.popStructEnd(stack[-1]) + if len(stack) > 0: + del stack[-1] + else: + self.popNextArg(self.peekNextToken()) + if self.isEndOfData() and len(stack) == 0: + break + + def CBNPCategorySet_apply(self): # persistent_data_template.h:459 # void CBNPCategorySet::apply(CPersistentDataRecord &pdr ) + #__Tok__MapKey = self.addString("__Key__") + #__Tok__MapVal = self.addString("__Val__") + __Tok_Category = self.addString("_Category") + while not self.isEndOfStruct(): + nextToken = self.peekNextToken() + logging.getLogger(LOGGER).debug("nextToken:%d" % (nextToken)) + if nextToken == __Tok_Category: + logging.getLogger(LOGGER).debug("__Tok_Category") + self.popStructBegin(__Tok_Category) + self.Categories.append(CBNPCategorySet.CBNPCategorySet()) + self.CBNPCategory_apply(self.Categories[-1]) + self.popStructEnd(__Tok_Category) + continue + # Vidage des autres clefs (inconnues) + stack = [] + while True: + if self.isStartOfStruct(): + stack.append(self.peekNextToken()) + self.popStructBegin(stack) + elif self.isEndOfStruct(): + self.popStructEnd(stack[-1]) + if len(stack) > 0: + del stack[-1] + else: + self.popNextArg(self.peekNextToken()) + if self.isEndOfData() and len(stack) == 0: + break + + + def CBNPCategory_apply(self, _CBNPCategory): # persistent_data_template.h:459 # void CBNPCategory::apply(CPersistentDataRecord &pdr ) + __Tok__MapKey = self.addString("__Key__") + __Tok__MapVal = self.addString("__Val__") + __Tok_Name = self.addString("_Name") + __Tok_IsOptional = self.addString("_IsOptional") + __Tok_UnpackTo = self.addString("_UnpackTo") + __Tok_IsIncremental = self.addString("_IsIncremental") + __Tok_CatRequired = self.addString("_CatRequired") + __Tok_Hidden = self.addString("_Hidden") + __Tok_Files = self.addString("_Files") + logging.getLogger(LOGGER).debug("MapKey:%d, MapVal:%d, Name:%d, IsOptional:%d, UnpackTo:%d, IsIncremental:%d, CatRequired:%d, Hidden:%d, Files:%d" %(__Tok__MapKey, __Tok__MapVal, __Tok_Name, __Tok_IsOptional, __Tok_UnpackTo, __Tok_IsIncremental, __Tok_CatRequired, __Tok_Hidden, __Tok_Files)) + while not self.isEndOfStruct(): + nextToken = self.peekNextToken() + logging.getLogger(LOGGER).debug("nextToken:%d" % (nextToken)) + if nextToken == __Tok_Name: + logging.getLogger(LOGGER).debug("__Tok_Name") + _CBNPCategory._Name = self.popString(nextToken) + logging.getLogger(LOGGER).debug("_Name: %s" % _CBNPCategory._Name) + continue + elif nextToken == __Tok_IsOptional: + logging.getLogger(LOGGER).debug("__Tok_IsOptional") + _CBNPCategory._IsOptional = self.popBool(nextToken) + logging.getLogger(LOGGER).debug("_IsOptional: %s" % str(_CBNPCategory._IsOptional)) + continue + elif nextToken == __Tok_UnpackTo: + logging.getLogger(LOGGER).debug("__Tok_UnpackTo") + _CBNPCategory._UnpackTo = self.popString(nextToken) + logging.getLogger(LOGGER).debug("_UnpackTo: %s" % str(_CBNPCategory._UnpackTo)) + continue + elif nextToken == __Tok_IsIncremental: + logging.getLogger(LOGGER).debug("__Tok_IsIncremental") + _CBNPCategory._IsIncremental = self.popBool(nextToken) + logging.getLogger(LOGGER).debug("_IsIncremental: %s" % str(_CBNPCategory._IsIncremental)) + continue + elif nextToken == __Tok_CatRequired: + logging.getLogger(LOGGER).debug("__Tok_CatRequired") + _CBNPCategory._CatRequired = self.popString(nextToken) + logging.getLogger(LOGGER).debug("_CatRequired: %s" % str(_CBNPCategory._CatRequired)) + continue + elif nextToken == __Tok_Hidden: + logging.getLogger(LOGGER).debug("__Tok_Hidden") + _CBNPCategory._Hidden = self.popBool(nextToken) + logging.getLogger(LOGGER).debug("_Hidden: %s" % str(_CBNPCategory._Hidden)) + continue + elif nextToken == __Tok_Files: + logging.getLogger(LOGGER).debug("__Tok_Files") + _CBNPCategory._Files.append(self.popString(nextToken)) + logging.getLogger(LOGGER).debug("_Files: %s" % str(_CBNPCategory._Files)) + continue + # Vidage des autres clefs (inconnues) + stack = [] + while True: + if self.isStartOfStruct(): + stack.append(self.peekNextToken()) + self.popStructBegin(stack) + elif self.isEndOfStruct(): + self.popStructEnd(stack[-1]) + if len(stack) > 0: + del stack[-1] + else: + self.popNextArg(self.peekNextToken()) + if self.isEndOfData() and len(stack) == 0: + break diff --git a/tools/CSessionId.py b/tools/CSessionId.py new file mode 100644 index 0000000..af6331d --- /dev/null +++ b/tools/CSessionId.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CSessionId +# +# 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 . + + +class CSessionId(): + def __init__(self, id = 0): + self.id = id + def read(self, msgin): + self.id = msgin.readUint32('id') + def push(self, msgout): + msgout.pushUint32(self.id) diff --git a/tools/CShardName.py b/tools/CShardName.py new file mode 100644 index 0000000..c778f0d --- /dev/null +++ b/tools/CShardName.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CShardName +# +# 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 . + +class CShardName(): + def __init__(self, SessionId, DisplayName, ShortName): + self.SessionId = SessionId + self.DisplayName = DisplayName + self.ShortName = ShortName diff --git a/tools/CStringManager.py b/tools/CStringManager.py new file mode 100644 index 0000000..1ec8209 --- /dev/null +++ b/tools/CStringManager.py @@ -0,0 +1,153 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CStringManager +# +# 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 tools import BitStream +import copy +from tools import CI18N + +LOGGER='CStringManager' + +class CStringManager: + def __init__(self): + # Message not decoded + self.unknown = [] + # Parameter + self.unknownParameterString = [] + # Message decoded + self.decoded = [] + # Template (id, string) + self.templates = {} + self.ci18n = CI18N.CI18N() + + def receiveMessage(self, StringId, msgin): + try: + template = self.templates[StringId] + logging.getLogger(LOGGER).debug("template:%s" % template) + except KeyError: + logging.getLogger(LOGGER).debug("Impossible to decode :%d" % StringId) + self.unknown.append((StringId, msgin)) + + def receiveTemplate(self, StringId, strUtf8): + self.templates.setdefault(StringId, "") + self.templates[StringId] = strUtf8 + self.decodeMessage() + + def cleanDecodedMessage(self): + self.decoded = [] + + def decodeMessage(self): + ''' + khanat-opennel-code/code/ryzom/client/src/string_manager_client.cpp:608 bool CStringManagerClient::buildDynString(TDynStringInfo &dynInfo) + ''' + newUnknown = [] + for key, dataRead in self.unknown: + if key in self.templates: + logging.getLogger(LOGGER).debug('key : %d, data:%s, template:%s' %(key, dataRead.showAllData(), self.templates[key] )) + """ + Method to decode message + khanat-opennel-code/code/ryzom/client/src/string_manager_client.cpp:608 bool CStringManagerClient::buildDynString(TDynStringInfo &dynInfo) + """ + errorDetected = False + i = 0 + template = self.templates[key] + data = copy.deepcopy(dataRead) + ret = "" + while i < len(template): + if template[i] == '%' and i < len(template) -1: + i += 1 + if template[i] == 's': + # string + StringId = data.readUint32('StringId') + logging.getLogger(LOGGER).debug("replacement tag string[pos:%d, template:%s, char:%%%s, stringID:%d]" % (i, template, template[i], StringId)) + if StringId in self.templates: + ParameterString = self.templates[StringId] + logging.getLogger(LOGGER).debug("StringId:%d, value:%s" % (StringId, ParameterString)) + ret += ParameterString + else: + logging.getLogger(LOGGER).debug("Impossible to decode :%d" % StringId) + if StringId not in self.unknown: + if StringId not in self.unknownParameterString: + logging.getLogger(LOGGER).debug("Impossible to decode :%d (send query)" % StringId) + self.unknownParameterString.append(StringId) + errorDetected = True + elif template[i] == 'i': + # integer + Integer = data.readSint32('Integer') + logging.getLogger(LOGGER).debug("replacement tag integer [pos:%d, template:%s, char:%%%s, integer:%d]" % (i, template, template[i], Integer)) + ret += str(Integer) + elif template[i] == 't': + # time + Time = data.readUint32('Time') + valTime = "" + if Time > (10*60*60): + nbHours = Time / (10*60*60) + Time -= nbHours * 10 * 60 * 60 + valTime += str(nbHours) + ' ' + self.ci18n.get('uiMissionTimerHour') + ' ' + nbMinutes = Time / (10*60) + Time -= nbMinutes * 10 * 60 + valTime += str(nbMinutes) + ' ' + self.ci18n.get('uiMissionTimerMinute') + ' ' + elif Time >= (10*60): + nbMinutes = Time / (10*60) + Time -= nbMinutes * 10 * 60 + valTime += str(nbMinutes) + ' ' + self.ci18n.get('uiMissionTimerMinute') + ' ' + nbSeconds = Time / 10 + valTime += str(nbSeconds) + ' ' + self.ci18n.get('uiMissionTimerSecond') + ' ' + + logging.getLogger(LOGGER).debug("replacement tag time [pos:%d, template:%s, char:%%%s, Time:%d, valTime:%s]" % (i, template, template[i], Time, valTime)) + ret += valTime + elif template[i] == '$': + # money + Money = data.readUint64('Money') + logging.getLogger(LOGGER).debug("replacement tag money [pos:%d, template:%s, char:%%%s, Money:%u]" % (i, template, template[i], Money)) + ret += str(Integer) + elif template[i] == 'm': + # dyn_string_id + DynStringId = data.readUint32('StringId') + logging.getLogger(LOGGER).debug("replacement tag dyn_string_id [pos:%d, template:%s, char:%%%s, DynStringId:%d]" % (i, template, template[i], DynStringId)) + if DynStringId in self.templates: + ParameterString = self.templates[DynStringId] + logging.getLogger(LOGGER).debug("DynStringId:%d, value:%s" % (StringId, ParameterString)) + ret += ParameterString + else: + logging.getLogger(LOGGER).debug("Impossible to decode :%d - TODO [check if same method to get string value]" % DynStringId) + self.unknownParameterString.append((DynStringId)) + errorDetected = True + elif template[i] == '%': + # %% => % + ret += template[i] + logging.getLogger(LOGGER).debug("replacement tag %% [pos:%d, template:%s, char:%%%s]" % (i, template, template[i])) + pass + else: + logging.getLogger(LOGGER).warning("Error: unknown replacement tag [pos:%d, template:%s, char:%%%s]" % (i, template, template[i])) + ret += template[i] + else: + ret += template[i] + i += 1 + if errorDetected: + logging.getLogger(LOGGER).debug('Impossible to decode id:%d' % key) + newUnknown.append((key, dataRead)) + else: + logging.getLogger(LOGGER).debug('Message:%s' % ret) + self.decoded.append(ret) + else: + logging.getLogger(LOGGER).debug('Impossible to decode id:%d' % key) + newUnknown.append((key, data)) + self.unknown = newUnknown diff --git a/tools/ClientNetworkConnection.py b/tools/ClientNetworkConnection.py new file mode 100644 index 0000000..ba93288 --- /dev/null +++ b/tools/ClientNetworkConnection.py @@ -0,0 +1,853 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module ClientNetworkConnection +# +# 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 +import sys +import signal +import socket +import time +import xml.etree.ElementTree as ET +import hashlib +from tools import CGenericMultiPartTemp +from tools import Enum +from tools import CBitSet +from tools import World +from tools import CImpulseDecoder +from tools import DecodeImpulse +from tools import TStampQueue +from tools import BitStream +from tools import CodeMsgXml +from tools import CAction +from tools import CActionFactory + +LOGGER='ClientNetworkConnection' + + +def getTextMD5(dataRawXml): + dataNew = '' + for data in dataRawXml: + if data != '\r': # '\015' ignore caractère \r\n => + dataNew += data + else: + logging.getLogger(LOGGER).debug("***** data:%d" % (ord(data))) + m = hashlib.md5() + m.update(dataNew.encode('utf-8')) + #print(m.hexdigest()) + #print(m.digest()) + return m.digest() + + +class ClientNetworkConnection: + ''' + Partie client de la gestion de la communication reseau avec le serveur: + + client : + code/ryzom/client/src/network_connection.cpp + server : + khanat-opennel-code/code/ryzom/server/src/frontend_service/fe_receive_sub.cpp # void CFeReceiveSub::handleReceivedMsg( CClientHost *clienthost ) + ''' + def __init__(self, + khanat_host, + khanat_port_frontend, + headAccount, + LanguageCode="fr", + checkMessageNumber = True): + self._CurrentSendNumber = 0 + self.GenericMsgHeaderMngr = {} + self.GenericMultiPartTemp = CGenericMultiPartTemp.GenericMultiPartTemp() + self.LanguageCode = LanguageCode + self._QuitId = 0 + self._ConnectionState = Enum.TConnectionState.NotInitialised + self.UserAddr, self.UserKey, self.UserId = None, None, None + self.frontend = (khanat_host, khanat_port_frontend) + self._sock = None + self._CurrentReceivedNumber = 0 + self._SystemMode = 0 + self._LastReceivedAck = 0 + self._LastReceivedNumber = 0 + self._LastAckInLongAck = 0 + self._MsgXmlMD5 = None + self._DatabaseXmlMD5 = None + self.msgXml = None + self.databaseXml = None + self._Synchronize = 0 + self._LatestSync = 0 + self._CurrentServerTick = 0 + self._MsPerTick = 0 + self._LCT = 100 + self._UpdateTime = 0 + #self._UpdateTicks = 0 + self._ReceivedSync = False + self._LastReceivedTime = 0 + self._LastReceivedPacketInBothModes = 0 + self._TotalMessages = 0 + self._TotalLostPackets = 0 + self.checkMessageNumber = checkMessageNumber + self._LastAckBit = 0 + self._AckBitMask = 0 + self._LongAckBitField = CBitSet.CBitSet() + self._LatestSyncTime = 0 + self.headAccount = headAccount + self.world = World.World() + self._ImpulseDecoder = CImpulseDecoder.CImpulseDecoder(self.world) + 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.DecodeImpulse() + self._InstantPing = 0 + self._BestPing = 10000 + self._MsPerTick = 100 + self._LastSendTime = 0 + self._ImpulseMultiPartNumber = 0 + self.clientTick = 0 + self.stepAction = 0 + + def signal_exit(self, sig, frame): + logging.getLogger(LOGGER).warning("Receive signal to quit program") + self.sendSystemQuit() + sys.exit(0) + + def connect(self): + signal.signal(signal.SIGINT, self.signal_exit) + signal.signal(signal.SIGTERM, self.signal_exit) + try: + self._sock = socket.socket(socket.AF_INET, # Internet + socket.SOCK_DGRAM) # UDP + except: + logging.getLogger(LOGGER).error("Impossible to connect on khanat") + return False + self._ConnectionState = Enum.TConnectionState.Login + self._LatestSyncTime = int(time.clock_gettime(1)*1000) + return True + + def cookiesInit(self, UserAddr, UserKey, UserId): + self.UserAddr = UserAddr + self.UserKey = UserKey + self.UserId = UserId + + def reset(self): + self._CurrentSendNumber += 0 + + def buildSystemHeader(self, msgout): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout) + msgout.pushSint32(self._CurrentSendNumber) + systemMode = True + msgout.pushBool(systemMode) + self._PacketStamps.append( TStampQueue.TStampQueue(self._CurrentSendNumber, self._UpdateTime) ) + self._CurrentSendNumber += 1 + + def sendSystemLogin(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemLogin() + logging.getLogger(LOGGER).debug("sendSystemLogin") + if self._sock is None: + raise ValueError + msgout = BitStream.BitStream() + self.buildSystemHeader(msgout) + msgout.pushUint8(Enum.CLFECOMMON.SYSTEM_LOGIN_CODE) + msgout.pushUint32(self.UserAddr) + msgout.pushUint32(self.UserKey) + msgout.pushUint32(self.UserId) + msgout.pushString(self.LanguageCode) + + logging.getLogger(LOGGER).debug("sendSystemLogin:%s" % msgout.showAllDataWrite()) + self._sock.sendto(msgout.toBytes(), self.frontend) + self._CurrentSendNumber += 1 + + self._ConnectionState = Enum.TConnectionState.Login + + def sendSystemQuit(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemQuit() + logging.getLogger(LOGGER).debug("sendSystemQuit") + # Disconnect + if self._sock is None: + raise ValueError + self._QuitId += 1 + msgout = BitStream.BitStream() + self.buildSystemHeader(msgout) + msgout.pushUint8(Enum.CLFECOMMON.SYSTEM_QUIT_CODE) + msgout.pushSint32(self._QuitId) + logging.getLogger(LOGGER).debug("sendSystemQuit:%s" % msgout.showAllDataWrite()) + self._sock.sendto(msgout.toBytes(), self.frontend) + self._ConnectionState = Enum.TConnectionState.Quit + + def sendSystemAckSync(self): + ''' + code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemAckSync() + ''' + logging.getLogger(LOGGER).debug("sendSystemAckSync _LastReceivedNumber:%d _LastAckInLongAck:%d _LatestSync:%d" % (self._LastReceivedNumber, self._LastAckInLongAck, self._LatestSync)) + msgout = BitStream.BitStream() + self.buildSystemHeader(msgout) + msgout.pushUint8(Enum.CLFECOMMON.SYSTEM_ACK_SYNC_CODE) + msgout.pushSint32(self._LastReceivedNumber) + msgout.pushSint32(self._LastAckInLongAck) + self._LongAckBitField.writeSerial(msgout) + msgout.pushSint32(self._LatestSync) + + logging.getLogger(LOGGER).debug("sendSystemAckSync:%s" % msgout.showAllDataWrite()) + self._sock.sendto(msgout.toBytes(), self.frontend) + + self._LatestSyncTime = self._UpdateTime + + def sendSystemAckProbe(self): + logging.getLogger(LOGGER).debug("sendSystemAckProbe") + msgout = BitStream.BitStream() + self.buildSystemHeader(msgout) + msgout.pushUint8(Enum.CLFECOMMON.SYSTEM_ACK_PROBE_CODE) + msgout.pushSint32(len(self._LatestProbes)) + for data in self._LatestProbes: + msgout.pushSint32(data) + self._LatestProbes = [] + logging.getLogger(LOGGER).debug("sendSystemAckProbe:%s" % msgout.showAllDataWrite()) + self._sock.sendto(msgout.toBytes(), self.frontend) + + + def sendSystemDisconnection(self): + logging.getLogger(LOGGER).debug("sendSystemDisconnection") + if self._sock is None: + raise ValueError + msgout = BitStream.BitStream() + self.buildSystemHeader(msgout) + msgout.pushUint8(Enum.CLFECOMMON.SYSTEM_DISCONNECTION_CODE) + self._sock.sendto(msgout.toBytes(), self.frontend) + + def sendStringRequest(self, stringId): + # Launch query for StringID (we will rfeceived the full text + # khanat-opennel-code/code/ryzom/client/src/string_manager_client.cpp:333 bool CStringManagerClient::getString(uint32 stringId, ucstring &result) + # Send STRING_MANAGER:STRING_RQ + logging.getLogger(LOGGER).debug("sendStringRequest") + if self._sock is None: + raise ValueError + msgout = BitStream.BitStream() + ref = CodeMsgXml.CodeMsgXml(self.msgXml, 'STRING_MANAGER:STRING_RQ') + for size, value, id in ref: + msgout.internalSerial(value, size, typeName=id) + msgout.pushUint32(stringId) + self.push(msgout) + + def sendNormalMessage(self): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendNormalMessage() + ''' + logging.getLogger(LOGGER).debug("sendNormalMessage") + if self._sock is None: + raise ValueError + msgout = BitStream.BitStream() + msgout.pushSint32(self._CurrentSendNumber) + systemMode = False # Normal + msgout.pushBool(systemMode) + msgout.pushSint32(self._LastReceivedNumber) + msgout.pushSint32(self._AckBitMask) + + numPacked = 0 + + for block in self._Actions: + if block.Cycle == 0: + break + + if block.FirstPacket == 0: + block.FirstPacket = self._CurrentSendNumber; + + block.writeSerial(msgout) + numPacked += 1 + if msgout.getPosInBit() > 480*8: # easy version + break + + logging.getLogger(LOGGER).debug("Send:%s" % msgout.showAllDataWrite()) + self._sock.sendto(msgout.toBytes(), self.frontend) + + self._LastSendTime = int(time.clock_gettime(1)*1000) + self._PacketStamps.append( TStampQueue.TStampQueue(self._CurrentSendNumber, self._UpdateTime) ) + self._CurrentSendNumber += 1 + + def readDelta(self, msg): + propertyCount = msg.readUint16('propertyCount') + logging.getLogger(LOGGER).debug("propertyCount:%d" % propertyCount) + logging.getLogger(LOGGER).debug("TODO") + for _ in range(0, propertyCount): + pass + + 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) + return data, addr + + def decodeHeader(self, msg): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::decodeHeader(CBitMemStream &msgin, bool checkMessageNumber) + ''' + self._TotalMessages += 1 + self._LastReceivedTime = self._UpdateTime + self._CurrentReceivedNumber = msg.readSint32('CurrentReceivedNumber') + self._SystemMode = msg.readBool('SystemMode') + + 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 + + if not self._SystemMode: + self._LastReceivedAck = msg.readSint32('LastReceivedAck'); + logging.getLogger(LOGGER).debug("Normal Mode _LastReceivedAck:%d" % self._LastReceivedAck) + else: + logging.getLogger(LOGGER).debug("System Mode") + + if self._CurrentReceivedNumber > self._LastReceivedNumber+1: + logging.getLogger(LOGGER).debug("lost messages server->client [%d; %d]" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber - 1)) + elif self._CurrentReceivedNumber == self._LastReceivedNumber: + logging.getLogger(LOGGER).debug("awaiting packet %d, received packet %d" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber)) + return False + elif self._CurrentReceivedNumber < self._LastReceivedNumber: + logging.getLogger(LOGGER).debug("received an old message, awaiting packet %d, received packet %d" %(self._LastReceivedPacketInBothModes + 1, self._CurrentReceivedNumber)) + return False + ackBool = ( not self._SystemMode ) and (self._ConnectionState == Enum.TConnectionState.Connected or self._ConnectionState == Enum.TConnectionState.Synchronize) + if ackBool: + ackBit = 1 + else: + ackBit = 0 + if self._CurrentReceivedNumber - self._LastReceivedNumber < 32: + self._AckBitMask <<= self._CurrentReceivedNumber - self._LastReceivedNumber + self._AckBitMask |= self._LastAckBit << (self._CurrentReceivedNumber - self._LastReceivedNumber - 1) + elif (self._CurrentReceivedNumber - self._LastReceivedNumber) == 32 and self._LastAckBit != 0: + self._AckBitMask = 0x80000000 + else: + self._AckBitMask = 0x00000000 + self._LastAckBit = ackBit; + for i in range(self._LastReceivedNumber+1, self._CurrentReceivedNumber): + self._LongAckBitField.clearBit(i & 511) # (512 - 1) mask 9bit + self._LongAckBitField.set(self._CurrentReceivedNumber & 511, ackBool) # (512 - 1) mask 9bit + + logging.getLogger(LOGGER).debug("_LastAckInLongAck:%d _CurrentReceivedNumber:%d" % (self._LastAckInLongAck, self._CurrentReceivedNumber)) + if self._LastAckInLongAck <= (self._CurrentReceivedNumber-512): + self._LastAckInLongAck = self._CurrentReceivedNumber-511; # (512 - 1) mask 9bit + + self._LastReceivedNumber = self._CurrentReceivedNumber + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d, _LastReceivedNumber:%d, ackBit:%d, _AckBitMask:%d _LongAckBitField:%s" % (self._CurrentReceivedNumber, self._LastReceivedNumber, ackBit, self._AckBitMask, self._LongAckBitField)) + return True + + def receiveSystemProbe(self, msg): + logging.getLogger(LOGGER).debug("receiveSystemProbe") + self._LatestProbeTime = self._UpdateTime + self._LatestProbe = msg.readSint32('LatestProbe') + logging.getLogger(LOGGER).debug("LatestProbe: %d" % self._LatestProbe) + self._LatestProbes.append(self._LatestProbe) + logging.getLogger(LOGGER).debug("Msg Received:" + msg.showAllData()) + + def receiveSystemStalled(self, msg): + logging.getLogger(LOGGER).debug("received STALLED") + logging.getLogger(LOGGER).debug("Msg Received:" + msg.showAllData()) + + def receiveSystemSync(self, msg): + logging.getLogger(LOGGER).debug("receiveSystemSync") + self._LatestSyncTime = self._UpdateTime + self._Synchronize = msg.readUint32('Synchronize') + stime = msg.readSint64('stime') + self._LatestSync = msg.readUint32('LatestSync') + logging.getLogger(LOGGER).debug("%d %d %d" %(self._Synchronize, stime, self._LatestSync)) + # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp : void CNetworkConnection::receiveSystemSync(CBitMemStream &msgin) + MsgData = msg.readArrayUint8(16, 'MsgData') + DatabaseData = msg.readArrayUint8(16, 'DatabaseData') + logging.getLogger(LOGGER).debug("MsgData:" + str(MsgData)) + logging.getLogger(LOGGER).debug("DatabaseData:" + str(DatabaseData)) + md5Msg = bytes(MsgData) + md5Database = bytes(DatabaseData) + if md5Msg == self._MsgXmlMD5: + logging.getLogger(LOGGER).info("Check MD5 msg.xml : OK") + else: + logging.getLogger(LOGGER).error("Check MD5 msg.xml : KO") + if md5Database == self._DatabaseXmlMD5: + logging.getLogger(LOGGER).info("Check MD5 database.xml : OK") + else: + logging.getLogger(LOGGER).error("Check MD5 database.xml : KO") + logging.getLogger(LOGGER).debug("Msg Received:" + msg.showAllData()) + self._MsPerTick = 100 + self._CurrentServerTick = self._Synchronize + self._CurrentReceivedNumber + 2 + self._CurrentClientTick = self._CurrentServerTick - ( self._LCT + self._MsPerTick ) / self._MsPerTick + self._CurrentClientTime = self._UpdateTime - (self._LCT + self._MsPerTick) + self.sendSystemAckSync() + + def decodeVisualProperties(self, msgin): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin ) + ''' + while True: + if msgin.getPosInBit() + 8*8 > len(msgin)*8: + return + slot = msgin.readUint8('slot') + associationBits = msgin.readUint32('associationBits') + logging.getLogger(LOGGER).debug("slot:%d associationBits:%d" %(slot, associationBits)) + logging.getLogger(LOGGER).debug("TODO") + return + + def receiveNormalMessage(self, msgin): + logging.getLogger(LOGGER).debug("receiveNormalMessage : received normal message Packet (%d) %s" % (msgin.needRead(), msgin.showLastData() )) + actions = self._ImpulseDecoder.decode(msgin, self._CurrentReceivedNumber, self._LastReceivedAck, self._CurrentSendNumber ) + if actions: + logging.getLogger(LOGGER).debug('actions: ' +','.join( [ str(x) for x in actions] ) ) + else: + logging.getLogger(LOGGER).debug('actions: None') + logging.getLogger(LOGGER).debug("Message not read (%d) %s" % (msgin.needRead(), msgin.showLastData() )) + + # remove all old actions that are acked + while self._Actions and self._Actions[0].FirstPacket != 0 and self._Actions[0].FirstPacket < self._LastReceivedAck: + logging.getLogger(LOGGER).debug("remove old action [%d/%d] : %s" % (self._Actions[0].FirstPacket, self._LastReceivedAck, self._Actions[0])) + self._Actions.pop(0) + + self._CurrentServerTick = self._CurrentReceivedNumber * 2 + self._Synchronize + + # remove useless stamps in queue + while len(self._PacketStamps) != 0 and self._LastReceivedAck > self._PacketStamps[0].first: + self._PacketStamps.pop(0) + + # Statistique ! + if len(self._PacketStamps) == 0 or self._PacketStamps[0].first > self._LastReceivedAck: + pass + else: + ackedPacketTime = self._PacketStamps[0].second + ping = self._UpdateTime - ackedPacketTime + self._InstantPing = ping + if ping < self._BestPing: + self._BestPing = ping + earliest = ackedPacketTime + self._BestPing//2 + latest = self._UpdateTime - self._BestPing//2 + + numStepTick = self._CurrentServerTick - self._CurrentClientTick + if numStepTick > 0 and earliest > self._CurrentClientTime and latest > self._CurrentClientTime: + if self._CurrentClientTime + self._MsPerTick * numStepTick < earliest: + self._MsPerTick = (earliest - self._CurrentClientTime)//numStepTick + if (self._CurrentClientTime + self._MsPerTick * numStepTick) > latest: + self._MsPerTick = (latest - self._CurrentClientTime)//numStepTick + if self._MsPerTick == 0: + logging.getLogger(LOGGER).warning("_MsPerTick is 0 because server tick is too big %d compare to the client tick is %d" %(self._CurrentServerTick, self._CurrentClientTick)) + self._MsPerTick = 1 + elif numStepTick <= 0: + self._MsPerTick = self._LCT + + + ## remove useless stamps in queue + #while self._PacketStamps and self._LastReceivedAck > self._PacketStamps[0].first: + # self._PacketStamps.pop(0) + if actions: + logging.getLogger(LOGGER).debug('list actions: [' + str(len(actions)) + '] ' +','.join( [ str(x) for x in actions] ) ) + else: + logging.getLogger(LOGGER).debug('list actions: None') + + # Decode the actions received in the impulsions + logging.getLogger(LOGGER).debug('=' * 80) + for action in actions: + logging.getLogger(LOGGER).debug('-' * 80) + if action.Code == Enum.TActionCode.ACTION_DISCONNECTION_CODE: + logging.getLogger(LOGGER).debug("Action : ACTION_DISCONNECTION_CODE") + self.disconnect() + elif action.Code == Enum.TActionCode.ACTION_GENERIC_CODE: + logging.getLogger(LOGGER).debug("Action : ACTION_GENERIC_CODE") + action.genericAction(self.decodeImpulse, self.world, self.GenericMultiPartTemp) + logging.getLogger(LOGGER).info("Action: ACTION_GENERIC_CODE : %s" % action) + elif action.Code == Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE: + logging.getLogger(LOGGER).debug("Action : ACTION_GENERIC_MULTI_PART_CODE") + action.genericAction(self.decodeImpulse, self.world, self.GenericMultiPartTemp) + elif action.Code == Enum.TActionCode.ACTION_DUMMY_CODE: + logging.getLogger(LOGGER).debug("Action : ACTION_DUMMY_CODE") + self._ImpulseDecoder.removeCAction(action) + + # Decode the visual properties + self.decodeVisualProperties( msgin ); + + self._LastReceivedNormalTime = int(time.clock_gettime(1)*1000) + logging.getLogger(LOGGER).debug("Msg Received:" + msgin.showAllData()) + + def receiveSystemAckQuit(self, msgin): + logging.getLogger(LOGGER).debug("received ACK_QUIT") + self._ReceivedAckQuit = True + logging.getLogger(LOGGER).debug("Msg Received:" + msgin.showAllData()) + + + def disconnect(self): + logging.getLogger(LOGGER).info("Disconnect") + self.sendSystemDisconnection() + self._sock.close() + self._sock = None + self._ConnectionState = Enum.TConnectionState.Disconnect + + def stateLogin(self, msgin): + self.decodeHeader(msgin) + if self._SystemMode: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) + if message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + self._ConnectionState = Enum.TConnectionState.Synchronize + logging.getLogger(LOGGER).debug("Login->synchronize") + self.receiveSystemSync(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE: + logging.getLogger(LOGGER).debug("received STALLED") + self._ConnectionState = Enum.TConnectionState.Stalled + self.receiveSystemStalled(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + logging.getLogger(LOGGER).debug("Login->probe") + self._ConnectionState = Enum.TConnectionState.Probe + self.receiveSystemProbe(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + self.disconnect() + logging.getLogger(LOGGER).warning("BACK-END DOWN") + return False + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Login" % message) + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) + else: + logging.getLogger(LOGGER).warning("CNET: received normal in state Login") + return False + + def stateSynchronize(self, msgin): + logging.getLogger(LOGGER).debug("stateSynchronize") + self.decodeHeader(msgin) + if self._SystemMode: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) + if message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + logging.getLogger(LOGGER).debug("synchronize->probe") + self._ConnectionState = Enum.TConnectionState.Probe + self.receiveSystemProbe(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE: + logging.getLogger(LOGGER).debug("received STALLED") + self._ConnectionState = Enum.TConnectionState.Stalled + self.receiveSystemStalled(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + logging.getLogger(LOGGER).debug("synchronize->synchronize") + self.receiveSystemSync(msgin) + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + self.disconnect() + logging.getLogger(LOGGER).warning("BACK-END DOWN") + return False + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Synchronize" % message) + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) + else: + self._ConnectionState = Enum.TConnectionState.Connected + logging.getLogger(LOGGER).warning("CNET: synchronize->connected") + # _Changes.push_back(CChange(0, ConnectionReady)); + self._ImpulseDecoder.reset(); + self.receiveNormalMessage(msgin); + return True + logging.getLogger(LOGGER).debug("sendSystemAckSync ? (%d , %d , %d)" %(self._UpdateTime, self._LatestSyncTime, self._UpdateTime - self._LatestSyncTime)) + if self._UpdateTime - self._LatestSyncTime > 300: + self.sendSystemAckSync(); + return False + + def stateConnected(self, msgin): + self.decodeHeader(msgin) + if self._SystemMode: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("SystemMode _CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) + if message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + logging.getLogger(LOGGER).debug("Connected->probe") + self._ConnectionState = Enum.TConnectionState.Probe + self.receiveSystemProbe(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + logging.getLogger(LOGGER).debug("Connected->synchronize") + self.receiveSystemSync(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE: + logging.getLogger(LOGGER).debug("received STALLED") + self._ConnectionState = Enum.TConnectionState.Stalled + self.receiveSystemStalled(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + self.disconnect() + logging.getLogger(LOGGER).warning("BACK-END DOWN") + return False + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Connected" % message) + logging.getLogger(LOGGER).debug("NormalMode _CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) + else: + self.receiveNormalMessage(msgin); + return True + return False + + def stateProbe(self, msgin): + self.decodeHeader(msgin) + if self._SystemMode: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) + if message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + logging.getLogger(LOGGER).debug("probe->synchronize") + self._ConnectionState = Enum.TConnectionState.Synchronize + self.receiveSystemSync(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE: + logging.getLogger(LOGGER).debug("probe->stalled") + self._ConnectionState = Enum.TConnectionState.Stalled + self.receiveSystemStalled(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + self.receiveSystemProbe(msgin) + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + self.disconnect() + logging.getLogger(LOGGER).warning("BACK-END DOWN") + return False + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Probe" % message) + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) + else: + logging.getLogger(LOGGER).warning("received normal in state Probe") + + if (len(self._LatestProbes) > 0) or (self._UpdateTime - self._LatestProbeTime > 300): + self.sendSystemAckProbe() + self._LatestProbeTime = self._UpdateTime + + return False + + def stateStalled(self, msgin): + self.decodeHeader(msgin) + if self._SystemMode: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) + if message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + logging.getLogger(LOGGER).debug("stalled->synchronize") + self._ConnectionState = Enum.TConnectionState.Synchronize + self.receiveSystemSync(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_PROBE_CODE: + logging.getLogger(LOGGER).debug("stalled->probe") + self._ConnectionState = Enum.TConnectionState.Probe + self.receiveSystemProbe(msgin) + elif message == Enum.CLFECOMMON.SYSTEM_STALLED_CODE: + self.receiveSystemStalled(msgin) + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + self.disconnect() + logging.getLogger(LOGGER).warning("BACK-END DOWN") + return False + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Stalled" % message) + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) + else: + logging.getLogger(LOGGER).warning("received normal in state Stalled") + return False + + def stateQuit(self, msgin): + self.decodeHeader(msgin) + if self._SystemMode: + message = msgin.readUint8('message') + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d]" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead())) + if message == Enum.CLFECOMMON.SYSTEM_SYNC_CODE: + logging.getLogger(LOGGER).debug("quit->synchronize") + self._ConnectionState = Enum.TConnectionState.Synchronize + self.receiveSystemSync(msgin) + return True + elif message == Enum.CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: + self.disconnect() + logging.getLogger(LOGGER).warning("BACK-END DOWN") + return False + elif message == Enum.CLFECOMMON.SYSTEM_ACK_QUIT_CODE: + self.receiveSystemAckQuit(msgin) + else: + logging.getLogger(LOGGER).warning("CNET: received system %d in state Quit" % message) + logging.getLogger(LOGGER).debug("_CurrentReceivedNumber:%d (mode:%s) %d [%d/%d/%d] '%s'" % (self._CurrentReceivedNumber, str(self._SystemMode), message, msgin.sizeData(), msgin.sizeRead(), msgin.needRead(), msgin.showLastData())) + else: + logging.getLogger(LOGGER).warning("received normal in state Quit") + if not self._ReceivedAckQuit and (self._UpdateTime - self._LatestQuitTime > 100): + self.sendSystemQuit() + self._LatestQuitTime = self._UpdateTime + return False + + def update(self): + # khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::update() + self._UpdateTime = int(time.clock_gettime(1)*1000) + self._ReceivedSync = False + + if not self._sock: + return False + + # TODO - REMOVE this counter (just to stop loop) + counterLoop = 0 + + stateBroke = True + while stateBroke: + buffer, addr = self.buildStream() + msgin = BitStream.BitStream() + msgin.fromBytes(buffer) + logging.getLogger(LOGGER).debug("received message: %s" % msgin.showAllData()) + if self._ConnectionState == Enum.TConnectionState.Login: + logging.getLogger(LOGGER).debug("state:Login") + stateBroke = self.stateLogin(msgin) + elif self._ConnectionState == Enum.TConnectionState.Synchronize: + logging.getLogger(LOGGER).debug("state:Synchronize") + stateBroke = self.stateSynchronize(msgin) + elif self._ConnectionState == Enum.TConnectionState.Connected: + logging.getLogger(LOGGER).debug("state:Connected") + stateBroke = self.stateConnected(msgin) + elif self._ConnectionState == Enum.TConnectionState.Probe: + logging.getLogger(LOGGER).debug("state:Probe") + stateBroke = self.stateProbe(msgin) + elif self._ConnectionState == Enum.TConnectionState.Stalled: + logging.getLogger(LOGGER).debug("state:Stalled") + stateBroke = self.stateStalled(msgin) + elif self._ConnectionState == Enum.TConnectionState.Quit: + logging.getLogger(LOGGER).debug("state:Quit") + stateBroke = self.stateQuit(msgin) + else: + stateBroke = False + logging.getLogger(LOGGER).debug("message decoded: %s" % msgin.showAllData()) + counterLoop += 1 + if counterLoop > 10: + break + + def push_back(self, action): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::push(CAction *action) + ''' + # search to aggregate Action (send by block) + if len(self._Actions) == 0 or self._Actions[-1].Cycle != 0: + self._Actions.append(CAction.CActionBlock()) + self._Actions[-1].push_back(action) + + def push(self, msgout): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::push(CBitMemStream &msg) + ''' + logging.getLogger(LOGGER).debug("Push :%s" % msgout.showAllDataWrite()) + maxImpulseBitSize = 1840 # = 230*8 + cp = CActionFactory.CActionFactory(self.world) + ag = cp.createFactory(CAction.INVALID_SLOT, Enum.TActionCode.ACTION_GENERIC_CODE) + bytelen = (msgout.sizeData() + 7) // 8 + impulseMinBitSize = ag.size() + impulseBitSize = impulseMinBitSize + (4 + bytelen)*8 + logging.getLogger(LOGGER).debug("maxImpulseBitSize:%d bytelen:%d impulseMinBitSize:%d impulseBitSize:%d" %(maxImpulseBitSize, bytelen, impulseMinBitSize, impulseBitSize)) + if impulseBitSize < maxImpulseBitSize: + ag.set(msgout); + self.push_back(ag) + else: + agmp = cp.createFactory(CAction.INVALID_SLOT, Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE) + minimumBitSizeForMP = agmp.size() + availableSize = (maxImpulseBitSize - minimumBitSizeForMP) // 8 + nbBlock = (bytelen + availableSize - 1) // availableSize + num = self._ImpulseMultiPartNumber + self._ImpulseMultiPartNumber += 1 + logging.getLogger(LOGGER).debug("minimumBitSizeForMP:%d availableSize:%d nbBlock:%d num:%d _ImpulseMultiPartNumber:%d" % (minimumBitSizeForMP, availableSize, nbBlock, num, self._ImpulseMultiPartNumber)) + for i in range(0, nbBlock): + logging.getLogger(LOGGER).debug("i:%d nbBlock:%d" % (i, nbBlock)) + if i != 0: + # Create a new CActionFactory + agmp = cp.createFactory(CAction.INVALID_SLOT, Enum.TActionCode.ACTION_GENERIC_MULTI_PART_CODE) + agmp.set(num, i, msgout.buffer(), bytelen, availableSize, nbBlock) + self.push_back(agmp) + + def sendFastMode(self): + if self._ConnectionState == Enum.TConnectionState.Connected and self._LastSendTime > 100: + self.sendNormalMessage() + + def send(self): + ''' + khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::send(TGameCycle cycle) + ''' + cycle = self._CurrentServerTick + bitSize = 32*8 # block size is 32 (cycle) + 8 (number of actions + if len(self._Actions) == 0 or self._Actions[-1].Cycle != 0: + logging.getLogger(LOGGER).debug("No Action") + pass + else: + logging.getLogger(LOGGER).debug("Prepare Action") + block = self._Actions[-1] + block.Cycle = cycle + # check last block isn't bigger than maximum allowed + i = 0 + for action in block.Actions: + bitSize += action.size() + if bitSize >= 480*8: + break + i += 1 + if i < len(self._Actions): + # Too big block -> split block + newBlock = CAction.CActionBlock() + newBlock.Cylce = 0 + newBlock.insert(self._Actions, i, len(block.Actions)) + block.eraseToEnd(i) + + if self._ConnectionState == Enum.TConnectionState.Connected: + self.sendNormalMessage() + + #if len(self.world.Commands) > 0: + # cmd = self.world.Commands.pop(0) + + def analyze(self): + for id, data in self.world.StringManager.unknown: + logging.getLogger(LOGGER).debug("id:%d, data:%s" % (id, data.showAllData())) + self.sendStringRequest(id) + for id in self.world.StringManager.unknownParameterString: + logging.getLogger(LOGGER).debug("id:%d" % (id)) + self.sendStringRequest(id) + for msg in self.world.StringManager.decoded: + logging.getLogger(LOGGER).info("message:%s" % (msg)) + self.world.StringManager.cleanDecodedMessage() + if self.world.CurrentState == Enum.TState.st_connect: + if self.world.CShardNames != []: + if self.stepAction == 0 and self.world.CharacterSummaries != [] and self.clientTick > 0: + if self.world.CharacterSummaries[0].People == Enum.TPeople.Unknown: + bms = self.world.CreaterCharacter(self.msgXml, self.headAccount) + self.push(bms) + self.stepAction = 1 + else: + self.stepAction = 1 + elif self.stepAction == 1 and self.world.CharacterSummaries != []: + if self.world.CharacterSummaries[0].People != Enum.TPeople.Unknown: + logging.getLogger(LOGGER).info("Account defined %s" % self.world.CharacterSummaries[0].Name) + bms = self.world.SelectChar(self.msgXml, 0) + self.push(bms) + self.stepAction = 2 + + def EmulateFirst(self, msgRawXml, databaseRawXml): + self.world.CurrentState = Enum.TState.st_start + self.msgXml = ET.fromstring(msgRawXml) + #ET.dump(msgXml) + self.databaseXml = ET.fromstring(databaseRawXml) + #ET.dump(databaseXml) + + self._MsgXmlMD5 = getTextMD5(msgRawXml) + self._DatabaseXmlMD5 = getTextMD5(databaseRawXml) + + self.decodeImpulse.loadMsg(self.msgXml) + self.decodeImpulse.loadDatabase(self.databaseXml) + + self.connect() + logging.getLogger(LOGGER).info("Client Login") + self.sendSystemLogin() + self.world.CurrentState = Enum.TState.st_connect + + logging.getLogger(LOGGER).info("Receive Message") + self.clientTick = 0 + for _ in range(0, 50): + #while True: + logging.getLogger(LOGGER).debug("%s [%s: %d / %d] %s" % ("*" * 40, "Loop", self.clientTick, self.stepAction, "*" * 40)) + self.update() + self.analyze() + self.send() + self.clientTick += 1 + + logging.getLogger(LOGGER).info("Client Quit") + self.sendSystemQuit() + + diff --git a/tools/CodeMsgXml.py b/tools/CodeMsgXml.py new file mode 100644 index 0000000..9048404 --- /dev/null +++ b/tools/CodeMsgXml.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CodeMsgXml +# +# 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 tools import getPowerOf2 + +LOGGER='CodeMsgXml' + +def CodeMsgXml(msgXml, key): + head = msgXml + listpath = key.split(':') + ret = [] + for id in listpath: + nbBit = getPowerOf2.getPowerOf2(len(head)) + found = False + i = 0 + for ele in head: + if ele.attrib['name'] == id: + found = True + ret.append([nbBit, i, id]) + break + i +=1 + if not found: + logging.getLogger(LOGGER).error("Impossible to found value (all key:%s, value:%s)" % (key, id)) + raise ValueError + head = ele + return ret diff --git a/tools/DecodeImpulse.py b/tools/DecodeImpulse.py new file mode 100644 index 0000000..d06c390 --- /dev/null +++ b/tools/DecodeImpulse.py @@ -0,0 +1,573 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module DecodeImpulse +# +# 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 tools import BitStream +from tools import getPowerOf2 +from tools import CShardName +from tools import CCharacterSummary +from tools import CMainlandSummary + + +LOGGER='DecodeImpulse' + +class DecodeImpulse(): + def __init__(self): + ''' + khanat-opennel-code/code/ryzom/client/src/net_manager.cpp # void initializeNetwork() + ''' + self.msgXml = None + self.databaseXml = None + self.GenericMsgHeaderMngr = {} + self.initializeNetwork() + + def updatePatcherPriorityBasedOnCharacters(self, world): + logging.getLogger(LOGGER).debug('Load character') + hasMainlandChar = False + for ele in world.CharacterSummaries: + if ele.Name == "": + continue + if not ele.InNewbieland: + hasMainlandChar = True + break + if hasMainlandChar: + logging.getLogger(LOGGER).debug('hasMainlandChar : True') + else: + logging.getLogger(LOGGER).debug('hasMainlandChar : False') + # requestDownloadThreadPriority(hasMainlandChar ? BGDownloader::ThreadPriority_Normal : BGDownloader::ThreadPriority_Low, false); + # khanat-opennel-code/code/ryzom/client/src/bg_downloader_access.cpp # void CBGDownloaderAccess::requestDownloadThreadPriority(BGDownloader::TThreadPriority newPriority, bool freezeUI) + #msgout = BitStream.BitStream() + logging.getLogger(LOGGER).debug("TODO") + + def impulseDatabaseUpdatePlayer(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO:%s" % msgin) + def impulseUpdateInventory(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseInitInventory(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDatabaseUpdateBank(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDatabaseInitBank(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDatabaseResetBank(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseNoUserChar(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseUserChars(self, msgin, world): + ''' + khanat-opennel-code/code/ryzom/client/src/net_manager.cpp # void impulseUserChars(NLMISC::CBitMemStream &impulse) + ''' + world.ServerPeopleActive = msgin.readUint8('ServerPeopleActive') + world.ServerCareerActive = msgin.readUint8('ServerCareerActive') + # impulse.serialCont (CharacterSummaries); + world.CharacterSummaries = [] + size = msgin.readUint32('CharacterSummaries:len') + logging.getLogger(LOGGER).debug(msgin.showAllData()) + logging.getLogger(LOGGER).debug("size:%d" % size) + for _ in range(0, size): + tmp = CCharacterSummary.CCharacterSummary() + tmp.read(msgin) + world.CharacterSummaries.append(tmp) + # impulse.serialCont (shardNames); + size = msgin.readUint32('shardNames:len') + shardNames = [] + for i in range(0, size): + shardNames.append(msgin.readString('shardNames_' + str(i))) + # CShardNames::getInstance().loadShardNames(shardNames); + # int(shardNames[i*3+0]) = SessionId / shardNames[i*3+1] = DisplayName / shardNames[i*3+2] = ShortName + for i in range(0, size // 3): + tmp = CShardName.CShardName(int(shardNames[i*3+0]), shardNames[i*3+1], shardNames[i*3+2]) + world.CShardNames.append(tmp) + + #_, shardNames = msgin.readCont('shardNames') + # readPrivileges(impulse); + world.UserPrivileges = msgin.readString('UserPrivileges') + world.FreeTrial = msgin.readBool('FreeTrial') + world.FreeTrial = False # We read and ignore this param :) + + # impulse.serialCont(Mainlands); + self.Mainlands = [] + size = msgin.readUint32('Mainlands:len') + for _ in range(0, size): + tmp = CMainlandSummary.CMainlandSummary() + tmp.read(msgin) + world.Mainlands.append(tmp) + self.updatePatcherPriorityBasedOnCharacters(world); # Load player config from server to client + + def impulseUserChar(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseFarTP(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseServerReady(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCharNameValid(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseShardId(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseServerQuitOk(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseServerQuitAbort(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseMailNotification(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseForumNotification(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePermanentBan(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePermanentUnban(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseChat(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTell(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseFarTell(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseChat2(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDynString(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def inpulseDynStringInChatGroup(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTell2(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseTP(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTPWithSeason(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCorrectPos(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCombatEngageFailed(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDynChatOpen(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDynChatClose(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseBeginCast(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamShareOpen(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamShareInvalid(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamShareClose(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamContactInit(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamContactCreate(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamContactStatus(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseTeamContactRemove(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseExchangeInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseExchangeCloseInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseMountAbort(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseWhere(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCounter(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + + def impulsePhraseSend(self, msgin, world): + # khanat-opennel-code/code/ryzom/client/src/net_manager.cpp:2283 void impulsePhraseSend(NLMISC::CBitMemStream &impulse) + dynId = msgin.readUint32('dynId') + logging.getLogger(LOGGER).debug("dynId:%d" % dynId) + StringId = msgin.readUint32('StringId') + logging.getLogger(LOGGER).debug("dynId:%d StringId:%s" % (dynId, StringId)) + world.StringManager.receiveMessage(StringId, msgin.getNotRead()) + + def impulseStringResp(self, msgin, world): + # khanat-opennel-code/code/ryzom/client/src/net_manager.cpp:2295 void impulseStringResp(NLMISC::CBitMemStream &impulse) + StringId = msgin.readUint32('StringId') + strUtf8 = msgin.toBytes() + strUtf8 = msgin.readUtf8String('strUtf8') + logging.getLogger(LOGGER).debug("StringId:%d strUtf8:%s" % (StringId, strUtf8)) + world.StringManager.receiveTemplate(StringId, strUtf8) + + def impulseReloadCache(self, msgin, world): + world.timestamp = msgin.readUint32('timestamp') + logging.getLogger(LOGGER).debug("Reload Cache timestamp:%d" % world.timestamp) + logging.getLogger(LOGGER).debug("Message not read (%d) %s" % (msgin.needRead(), msgin.showLastData() )) + + def impulseBotChatForceEnd(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseJournalInitCompletedMissions(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseJournalUpdateCompletedMissions(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + + def impulseJournalAddCompass(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseJournalRemoveCompass(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseGuildJoinProposal(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseGuildAscensor(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseGuildLeaveAscensor(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseGuildAbortCreation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseGuildOpenGuildWindow(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseGuildOpenInventory(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseGuildCloseInventory(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseGuildUpdatePlayerTitle(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseGuildUseFemaleTitles(self, msgin, world): + world.UseFemaleTitles = msgin.readBool('UseFemaleTitles') + logging.getLogger(LOGGER).debug("UseFemaleTitles") + + def impulseCloseTempInv(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseRemoteAdmin(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulsePhraseDownLoad(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePhraseConfirmBuy(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePhraseAckExecuteCyclic(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePhraseAckExecuteNext(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseItemInfoSet(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseItemInfoRefreshVersion(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePrereqInfoSet(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseItemOpenRoomInventory(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseItemCloseRoomInventory(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseDeathRespawnPoint(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDeathRespawn(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseDuelInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDuelCancelInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulsePVPChallengeInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePVPChallengeCancelInvitation(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulsePVPFactionPushFactionWar(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePVPFactionPopFactionWar(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulsePVPFactionFactionWars(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseEncyclopediaUpdate(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseEncyclopediaInit(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseUserBars(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseUserPopup(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + + def impulseEnterCrZoneProposal(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCloseEnterCrZoneProposal(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + + def cbImpulsionGatewayOpen(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def cbImpulsionGatewayMessage (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def cbImpulsionGatewayClose (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseOutpostChooseSide (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseOutpostDeclareWarAck (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseCombatFlyingChaScore1Delta (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCombatFlyingTextItemSpecialEffectProc (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseCombatFlyingText (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseSetSeason (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseDssDown (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseSetNpcIconDesc (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseServerEventForMissionAvailability (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + def impulseSetNpcIconTimer (self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def impulseDatabaseInitPlayer(self, msgin, world): + logging.getLogger(LOGGER).debug("TODO") + + def initializeNetwork(self): + self.GenericMsgHeaderMngr.setdefault('DB_UPD_PLR', self.impulseDatabaseUpdatePlayer) + self.GenericMsgHeaderMngr.setdefault('DB_INIT:PLR', self.impulseDatabaseInitPlayer) + self.GenericMsgHeaderMngr.setdefault("DB_UPD_INV", self.impulseUpdateInventory) + self.GenericMsgHeaderMngr.setdefault("DB_INIT:INV", self.impulseInitInventory) + self.GenericMsgHeaderMngr.setdefault("DB_GROUP:UPDATE_BANK", self.impulseDatabaseUpdateBank) + self.GenericMsgHeaderMngr.setdefault("DB_GROUP:INIT_BANK", self.impulseDatabaseInitBank) + self.GenericMsgHeaderMngr.setdefault("DB_GROUP:RESET_BANK", self.impulseDatabaseResetBank) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:NO_USER_CHAR", self.impulseNoUserChar) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:USER_CHARS", self.impulseUserChars) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:USER_CHAR", self.impulseUserChar) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:FAR_TP", self.impulseFarTP) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:READY", self.impulseServerReady) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:VALID_NAME", self.impulseCharNameValid) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:SHARD_ID", self.impulseShardId) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:SERVER_QUIT_OK", self.impulseServerQuitOk) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:SERVER_QUIT_ABORT", self.impulseServerQuitAbort) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:MAIL_AVAILABLE", self.impulseMailNotification) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:GUILD_MESSAGE_AVAILABLE", self.impulseForumNotification) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:PERMANENT_BAN", self.impulsePermanentBan) + self.GenericMsgHeaderMngr.setdefault("CONNECTION:UNBAN", self.impulsePermanentUnban) + + self.GenericMsgHeaderMngr.setdefault("STRING:CHAT", self.impulseChat) + self.GenericMsgHeaderMngr.setdefault("STRING:TELL", self.impulseTell) + 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:TELL2", self.impulseTell2) + + self.GenericMsgHeaderMngr.setdefault("TP:DEST", self.impulseTP) + self.GenericMsgHeaderMngr.setdefault("TP:DEST_WITH_SEASON", self.impulseTPWithSeason) + self.GenericMsgHeaderMngr.setdefault("TP:CORRECT", self.impulseCorrectPos) + self.GenericMsgHeaderMngr.setdefault("COMBAT:ENGAGE_FAILED", self.impulseCombatEngageFailed) + self.GenericMsgHeaderMngr.setdefault("BOTCHAT:DYNCHAT_OPEN", self.impulseDynChatOpen) + self.GenericMsgHeaderMngr.setdefault("BOTCHAT:DYNCHAT_CLOSE", self.impulseDynChatClose) + + self.GenericMsgHeaderMngr.setdefault("CASTING:BEGIN", self.impulseBeginCast) + self.GenericMsgHeaderMngr.setdefault("TEAM:INVITATION", self.impulseTeamInvitation) + self.GenericMsgHeaderMngr.setdefault("TEAM:SHARE_OPEN", self.impulseTeamShareOpen) + self.GenericMsgHeaderMngr.setdefault("TEAM:SHARE_INVALID", self.impulseTeamShareInvalid) + self.GenericMsgHeaderMngr.setdefault("TEAM:SHARE_CLOSE", self.impulseTeamShareClose) + self.GenericMsgHeaderMngr.setdefault("TEAM:CONTACT_INIT", self.impulseTeamContactInit) + self.GenericMsgHeaderMngr.setdefault("TEAM:CONTACT_CREATE", self.impulseTeamContactCreate) + self.GenericMsgHeaderMngr.setdefault("TEAM:CONTACT_STATUS", self.impulseTeamContactStatus) + self.GenericMsgHeaderMngr.setdefault("TEAM:CONTACT_REMOVE", self.impulseTeamContactRemove) + + self.GenericMsgHeaderMngr.setdefault("EXCHANGE:INVITATION", self.impulseExchangeInvitation) + self.GenericMsgHeaderMngr.setdefault("EXCHANGE:CLOSE_INVITATION", self.impulseExchangeCloseInvitation) + self.GenericMsgHeaderMngr.setdefault("ANIMALS:MOUNT_ABORT", self.impulseMountAbort) + + self.GenericMsgHeaderMngr.setdefault("DEBUG:REPLY_WHERE", self.impulseWhere) + self.GenericMsgHeaderMngr.setdefault("DEBUG:COUNTER", self.impulseCounter) + + + self.GenericMsgHeaderMngr.setdefault("STRING_MANAGER:PHRASE_SEND", self.impulsePhraseSend) + self.GenericMsgHeaderMngr.setdefault("STRING_MANAGER:STRING_RESP", self.impulseStringResp) + self.GenericMsgHeaderMngr.setdefault("STRING_MANAGER:RELOAD_CACHE", self.impulseReloadCache) + + self.GenericMsgHeaderMngr.setdefault("BOTCHAT:FORCE_END", self.impulseBotChatForceEnd) + + self.GenericMsgHeaderMngr.setdefault("JOURNAL:INIT_COMPLETED_MISSIONS", self.impulseJournalInitCompletedMissions) + self.GenericMsgHeaderMngr.setdefault("JOURNAL:UPDATE_COMPLETED_MISSIONS", self.impulseJournalUpdateCompletedMissions) + + + self.GenericMsgHeaderMngr.setdefault("JOURNAL:ADD_COMPASS", self.impulseJournalAddCompass) + self.GenericMsgHeaderMngr.setdefault("JOURNAL:REMOVE_COMPASS", self.impulseJournalRemoveCompass) + + self.GenericMsgHeaderMngr.setdefault("GUILD:JOIN_PROPOSAL", self.impulseGuildJoinProposal) + + self.GenericMsgHeaderMngr.setdefault("GUILD:ASCENSOR", self.impulseGuildAscensor) + self.GenericMsgHeaderMngr.setdefault("GUILD:LEAVE_ASCENSOR", self.impulseGuildLeaveAscensor) + self.GenericMsgHeaderMngr.setdefault("GUILD:ABORT_CREATION", self.impulseGuildAbortCreation) + self.GenericMsgHeaderMngr.setdefault("GUILD:OPEN_GUILD_WINDOW", self.impulseGuildOpenGuildWindow) + + self.GenericMsgHeaderMngr.setdefault("GUILD:OPEN_INVENTORY", self.impulseGuildOpenInventory) + self.GenericMsgHeaderMngr.setdefault("GUILD:CLOSE_INVENTORY", self.impulseGuildCloseInventory) + + self.GenericMsgHeaderMngr.setdefault("GUILD:UPDATE_PLAYER_TITLE", self.impulseGuildUpdatePlayerTitle) + self.GenericMsgHeaderMngr.setdefault("GUILD:USE_FEMALE_TITLES", self.impulseGuildUseFemaleTitles) + + + self.GenericMsgHeaderMngr.setdefault("HARVEST:CLOSE_TEMP_INVENTORY", self.impulseCloseTempInv) + + self.GenericMsgHeaderMngr.setdefault("COMMAND:REMOTE_ADMIN", self.impulseRemoteAdmin) + + self.GenericMsgHeaderMngr.setdefault("PHRASE:DOWNLOAD", self.impulsePhraseDownLoad) + self.GenericMsgHeaderMngr.setdefault("PHRASE:CONFIRM_BUY", self.impulsePhraseConfirmBuy) + self.GenericMsgHeaderMngr.setdefault("PHRASE:EXEC_CYCLIC_ACK", self.impulsePhraseAckExecuteCyclic) + self.GenericMsgHeaderMngr.setdefault("PHRASE:EXEC_NEXT_ACK", self.impulsePhraseAckExecuteNext) + + self.GenericMsgHeaderMngr.setdefault("ITEM_INFO:SET", self.impulseItemInfoSet) + self.GenericMsgHeaderMngr.setdefault("ITEM_INFO:REFRESH_VERSION", self.impulseItemInfoRefreshVersion) + self.GenericMsgHeaderMngr.setdefault("MISSION_PREREQ:SET", self.impulsePrereqInfoSet) + self.GenericMsgHeaderMngr.setdefault("ITEM:OPEN_ROOM_INVENTORY", self.impulseItemOpenRoomInventory) + self.GenericMsgHeaderMngr.setdefault("ITEM:CLOSE_ROOM_INVENTORY", self.impulseItemCloseRoomInventory) + + self.GenericMsgHeaderMngr.setdefault("DEATH:RESPAWN_POINT", self.impulseDeathRespawnPoint) + self.GenericMsgHeaderMngr.setdefault("DEATH:RESPAWN", self.impulseDeathRespawn) + + self.GenericMsgHeaderMngr.setdefault("DUEL:INVITATION", self.impulseDuelInvitation) + self.GenericMsgHeaderMngr.setdefault("DUEL:CANCEL_INVITATION", self.impulseDuelCancelInvitation) + + self.GenericMsgHeaderMngr.setdefault("PVP_CHALLENGE:INVITATION", self.impulsePVPChallengeInvitation) + self.GenericMsgHeaderMngr.setdefault("PVP_CHALLENGE:CANCEL_INVITATION", self.impulsePVPChallengeCancelInvitation) + + self.GenericMsgHeaderMngr.setdefault("PVP_FACTION:PUSH_FACTION_WAR", self.impulsePVPFactionPushFactionWar) + self.GenericMsgHeaderMngr.setdefault("PVP_FACTION:POP_FACTION_WAR", self.impulsePVPFactionPopFactionWar) + self.GenericMsgHeaderMngr.setdefault("PVP_FACTION:FACTION_WARS", self.impulsePVPFactionFactionWars) + + + + + self.GenericMsgHeaderMngr.setdefault("ENCYCLOPEDIA:UPDATE", self.impulseEncyclopediaUpdate) + self.GenericMsgHeaderMngr.setdefault("ENCYCLOPEDIA:INIT", self.impulseEncyclopediaInit) + + self.GenericMsgHeaderMngr.setdefault("USER:BARS", self.impulseUserBars) + self.GenericMsgHeaderMngr.setdefault("USER:POPUP", self.impulseUserPopup) + + + self.GenericMsgHeaderMngr.setdefault("MISSION:ASK_ENTER_CRITICAL", self.impulseEnterCrZoneProposal) + self.GenericMsgHeaderMngr.setdefault("MISSION:CLOSE_ENTER_CRITICAL", self.impulseCloseEnterCrZoneProposal) + + + 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( "OUTPOST:CHOOSE_SIDE", self.impulseOutpostChooseSide ) + self.GenericMsgHeaderMngr.setdefault( "OUTPOST:DECLARE_WAR_ACK", self.impulseOutpostDeclareWarAck ) + + self.GenericMsgHeaderMngr.setdefault( "COMBAT:FLYING_ChaScore1_DELTA", self.impulseCombatFlyingChaScore1Delta ) + self.GenericMsgHeaderMngr.setdefault( "COMBAT:FLYING_TEXT_ISE", self.impulseCombatFlyingTextItemSpecialEffectProc ) + self.GenericMsgHeaderMngr.setdefault( "COMBAT:FLYING_TEXT", self.impulseCombatFlyingText ) + + self.GenericMsgHeaderMngr.setdefault( "SEASON:SET", self.impulseSetSeason ) + self.GenericMsgHeaderMngr.setdefault( "RING_MISSION:DSS_DOWN", self.impulseDssDown ) + + self.GenericMsgHeaderMngr.setdefault( "NPC_ICON:SET_DESC", self.impulseSetNpcIconDesc ) + self.GenericMsgHeaderMngr.setdefault( "NPC_ICON:SVR_EVENT_MIS_AVL", self.impulseServerEventForMissionAvailability ) + self.GenericMsgHeaderMngr.setdefault( "NPC_ICON:SET_TIMER", self.impulseSetNpcIconTimer ) + + def sizeElement(self, keys = None): + head = self.msgXml + if not keys: + return len(head) + ret = len(head) + for key in keys.split(':'): + for ele in head: + if ele.attrib['name'] == key: + head = ele + ret = len(head) + break + return ret + + def searchElement(self, keys): + ret = [] + head = self.msgXml + logging.getLogger(LOGGER).debug(len(head)) + for key in keys.split(':'): + #print(key) + id = 0 + for ele in head: + if ele.attrib['name'] == key: + logging.getLogger(LOGGER).debug('%s => %d' % (key, id)) + ret.append(id) + head = ele + break + id += 1 + return ret + + def execute(self, msgin, world): + ''' + khanat-opennel-code/code/ryzom/client/src/net_manager.cpp:3746 void impulseCallBack(NLMISC::CBitMemStream &impulse, sint32 packet, void *arg) + khanat-opennel-code/code/ryzom/common/src/game_share/generic_xml_msg_mngr.cpp:121 void CGenericXmlMsgHeaderManager::execute(CBitMemStream &strm) + khanat-opennel-code/code/ryzom/common/src/game_share/generic_xml_msg_mngr.h:431 CNode *select(NLMISC::CBitMemStream &strm) + + uint32 index = 0; + uint NbBits; + + strm.serialAndLog2(index, node->NbBits); + ''' + logging.getLogger(LOGGER).debug("execute") + head = self.msgXml + listpath = [] + while True: + nbBit = getPowerOf2.getPowerOf2(len(head)) + id = msgin.readSerial(nbBit, name='MsgXML', typeName='Number', emulate=True) + + ele = head[id] + name = ele.attrib['name'] + listpath.append(name) + fullname = ':'.join(listpath) + + id = msgin.readSerial(nbBit, name='MsgXML', typeName='XML <' + name + '>') + logging.getLogger(LOGGER).debug(fullname) + if fullname in self.GenericMsgHeaderMngr: + logging.getLogger(LOGGER).debug("Found : %s" % fullname) + self.GenericMsgHeaderMngr[fullname](msgin, world) + logging.getLogger(LOGGER).debug("MessageXML decoded: %s" % msgin.showAllData() ) + return True + else: + #logging.getLogger(LOGGER).debug("Non trouve") + for ele in head: + if ele.attrib['name'] == name: + head = ele + break + if head != ele: + logging.getLogger(LOGGER).error("Impossible to found %s" % fullname ) + logging.getLogger(LOGGER).debug("MessageXML decoded: %s" % msgin.showAllData() ) + return False + # End While + logging.getLogger(LOGGER).debug("MessageXML decoded: %s" % msgin.showAllData() ) + return False + + def loadMsg(self, msgXml): + self.msgXml = msgXml + + def loadDatabase(self, databaseXml): + self.databaseXml = databaseXml diff --git a/tools/Enum.py b/tools/Enum.py new file mode 100644 index 0000000..d9fdbae --- /dev/null +++ b/tools/Enum.py @@ -0,0 +1,565 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module CLFECOMMON +# +# 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 . + + +from enum import IntEnum + +class TConnectionState(IntEnum): + NotInitialised = 0 # nothing happened yet + NotConnected = 1 # init() called + Authenticate = 2 # connect() called, identified by the login server + Login = 3 # connecting to the frontend, sending identification + Synchronize = 4 # connection accepted by the frontend, synchronizing + Connected = 5 # synchronized, connected, ready to work + Probe = 6 # connection lost by frontend, probing for response + Stalled = 7 # server is stalled + Disconnect = 8 # disconnect() called, or timeout, or connection closed by frontend + Quit = 9 # quit() called + +class TState(IntEnum): + # initial state + st_start = 0 + # display login screen and options + st_login = 1 + # auto login using cmd lien parameters (used with patch reboot) + st_auto_login = 2 + # display the shard list + st_shard_list = 3 + # lauch the configurator and close ryzom + st_start_config = 4 + # run the scan data thread + st_scan_data = 5 + # display the eula and wait for validation + st_display_eula = 6 + # check the data to determine need for patch + st_check_patch = 7 + # display the list of optional patch category for patching + st_display_cat = 8 + # run the patch process and display progress + st_patch = 9 + # terminate the client and quit + st_close_client = 10 + # display the reboot screen and wait validation + st_reboot_screen = 11 + # restart the client with login bypass params + st_restart_client = 12 + # connect to the FS (start the 'in game' mode) + st_connect = 13 + # show the outgame browser + st_browser_screen = 14 + # ingame state + st_ingame = 15 + # leave the current shard (the exit action progress, Far TP part 1.1; Server Hop part 1-2) + st_leave_shard = 16 + # let the main loop finish the current frame and leave it (Far TP part 1.2) + st_enter_far_tp_main_loop = 17 + # disconnect from the FS (Far TP part 2) + st_disconnect = 18 + # connect to a new FS (Far TP & Server Hop part 3.1) + st_reconnect_fs = 19 + # after reconnecting, bypass character selection ui & select the same character (Far TP & Server Hop part 3.2) + st_reconnect_select_char = 20 + # after reconnecting and receiving ready, send ready (Far TP part 3.3) + st_reconnect_ready = 21 + # between global menu exit and sending ready (Server Hop part 3.3) + st_exit_global_menu = 22 + # error while reconnecting + st_reconnect_error = 23 + # Rate a ring session. should pop a web windows pointing the rate session page + st_rate_session = 24 + # create account + st_create_account = 25 + # try to login with alternate login system + st_alt_login = 26 + # pseudo state to leave the state machine + st_end = 27 + st_unknown = 28 + + +class EGender(IntEnum): + male = 0 + female = 1 + neutral = 2 + unknown = 4 + + +class TPeople(IntEnum): + Undefined = -1 + Humanoid = 0 + Playable = 0 + Fyros = 0 + Matis = 1 + Tryker = 2 + Zorai = 3 + EndPlayable = 4 + Karavan = 4 + Tribe = 5 + Common = 6 + EndHumanoid = 7 + Creature = 7 + Fauna = 7 + Arma = 7 + Balduse = 8 + Bul = 9 + Capryni = 10 + Chonari = 11 + Clapclap = 12 + Cococlaw = 13 + Cute = 14 + Dag = 15 + Diranak = 16 + Estrasson = 17 + Filin = 18 + Frahar = 19 + Gibbai = 20 + Hachtaha = 21 + Jungler = 22 + Kakty = 23 + Kalab = 24 + Kami = 25 + Kazoar = 26 + Kitin = 27 + Kitins = 28 + Kitifly = 28 + Kitihank = 29 + Kitiharak = 30 + Kitikil = 31 + Kitimandib = 32 + Kitinagan = 33 + Kitinega = 34 + Kitinokto = 35 + EndKitins = 36 + Lightbird = 36 + Mektoub = 37 + MektoubPacker = 38 + MektoubMount = 39 + Pucetron = 40 + Regus = 41 + Ryzerb = 42 + Ryzoholo = 43 + Ryzoholok = 44 + Vampignon = 45 + Varinx = 46 + Yber = 47 + Zerx = 48 + race_c1 = 49 + race_c2 = 50 + race_c3 = 51 + race_c4 = 52 + race_c5 = 53 + race_c6 = 54 + race_c7 = 55 + race_h1 = 56 + race_h2 = 57 + race_h3 = 58 + race_h4 = 59 + race_h5 = 60 + race_h6 = 61 + race_h7 = 62 + race_h8 = 63 + race_h9 = 64 + race_h10 = 65 + race_h11 = 66 + race_h12 = 67 + EndFauna = 68 + Flora = 68 + Cephaloplant = 68 + Electroalgs = 69 + Phytopsy = 70 + SapEnslaver = 71 + SpittingWeeds = 72 + Swarmplants = 73 + EndFlora = 74 + Goo = 74 + GooFauna = 74 + GooArma = 74 + GooBalduse = 75 + GooBul = 76 + GooCapryni = 77 + GooChonari = 78 + GooClapclap = 79 + GooCococlaw = 80 + GooCute = 81 + GooDag = 82 + GooDiranak = 83 + GooEstrasson = 84 + GooFilin = 85 + GooFrahar = 86 + GooGibbai = 87 + GooHachtaha = 88 + GooJungler = 89 + GooKakty = 90 + GooKalab = 91 + GooKami = 92 + GooKazoar = 93 + GooKitifly = 94 + GooKitihank = 95 + GooKitiharak = 96 + GooKitikil = 97 + GooKitimandib = 98 + GooKitin = 99 + GooKitinagan = 100 + GooKitinega = 101 + GooKitinokto = 102 + GooLightbird = 103 + GooMektoub = 104 + GooMektoubPacker = 105 + GooMektoubMount = 106 + GooPucetron = 107 + GooRegus = 108 + GooRyzerb = 109 + GooRyzoholo = 110 + GooRyzoholok = 111 + GooVampignon = 112 + GooVarinx = 113 + GooYber = 114 + GooZerx = 115 + Goorace_c1 = 116 + Goorace_c2 = 117 + Goorace_c3 = 118 + Goorace_c4 = 119 + Goorace_c5 = 120 + Goorace_c6 = 121 + Goorace_c7 = 122 + Goorace_h1 = 123 + Goorace_h2 = 124 + Goorace_h3 = 125 + Goorace_h4 = 126 + Goorace_h5 = 127 + Goorace_h6 = 128 + Goorace_h7 = 129 + Goorace_h8 = 130 + Goorace_h9 = 131 + Goorace_h10 = 132 + Goorace_h11 = 133 + Goorace_h12 = 134 + EndGooFauna = 135 + GooPlant = 135 + GooCephaloplant = 135 + GooElectroalgs = 136 + GooPhytopsy = 137 + GooSapEnslaver = 138 + GooSpittingWeeds = 139 + GooSwarmplants = 140 + EndGooPlant = 141 + EndGoo = 141 + EndCreature = 141 + ___TPeople_useSize = 142 + Unknown = 142 + EndPeople = 142 + + + +class ECharacterTitle(IntEnum): + ''' + khanat-opennel-code/code/ryzom/common/src/game_share/character_title.h # enum ECharacterTitle + ''' + Refugee = 0 + Homin = 1 # + Novice_Artisan = 2 # + Novice_Warrior = 3 # + Novice_Harvester = 4 # + Novice_Magician = 5 # + Artisan_Apprentice = 6 # + Magician_Apprentice = 7 # + Defensive_Magician = 8 # + Offensive_Magician = 9 # + Mentalist = 10 # + Summoner = 11 # + Master_Of_Illusion = 12 # + Mind_Lord = 13 # + Healer = 14 # + Priest = 15 # + Master_Of_Life = 16 # + Celestial_Guardian = 17 # + Disturber = 18 # + Affliction_Bringer = 19 # + Master_Of_Torment = 20 # + Avatar_Of_Sorrow = 21 # + Destroyer = 22 # + Archmage = 23 # + Master_Of_Pain = 24 # + Avatar_Of_Destruction = 25 # + Elementalist = 26 # + Alchemist = 27 # + Biomancer = 28 # + Master_Of_Energies = 29 # + Chosen_Of_Atys = 30 # + Warrior_Apprentice = 31 # + Melee_Warrior = 32 # + Range_Fighter = 33 # + Light_Armsman = 34 # + Heavy_Armsman = 35 # + Close_Fighter = 36 # + Gunman = 37 # + Heavy_Gunman = 38 # + Advanced_Gunman = 39 # + Advanced_Heavy_Gunman = 40 # + Bludgeoner = 41 # + Skewerer = 42 # + Slasher = 43 # + Smasher = 44 # + Impaler = 45 # + Cleaver = 46 # + Advanced_Close_Fighter = 47 # + Maceman = 48 # + Brute = 49 # + Spearman = 50 # + Axeman = 51 # + Swordsman = 52 # + Heavy_Maceman = 53 # + Pikeman = 54 # + Heavy_Axeman = 55 # + Heavy_Swordsman = 56 # + Knifeman = 57 # + Hand_To_Hand_Fighter = 58 # + Bowman = 59 # + Pistoleer = 60 # + Heavy_Bowman = 61 # + Artilleryman = 62 # + Rifleman = 63 # + Master_Maceman = 64 # + Master_Brute = 65 # + Master_Spearman = 66 # + Master_Axeman = 67 # + Master_Swordsman = 68 # + Master_Heavy_Maceman = 69 # + Master_Pikeman = 70 # + Master_Heavy_Axeman = 71 # + Master_Heavy_Swordsman = 72 # + Master_Knifeman = 73 # + Master_Hand_To_Hand_Fighter = 74 # + Master_Bowman = 75 # + Master_Pistoleer = 76 # + Master_Heavy_Bowman = 77 # + Master_Artilleryman = 78 # + Master_Rifleman = 79 # + Armorer_Apprentice = 80 # + Jeweler_Apprentice = 81 # + Melee_Weapon_Smith_Apprentice = 82 # + Range_Weapon_Smith_Apprentice = 83 # + Heavy_Armorer = 84 # + Light_Armorer = 85 # + Medium_Armorer = 86 # + Shield_Smith = 87 # + Jeweler = 88 # + Melee_Weapon_Smith = 89 # + Melee_Heavy_Weapon_Smith = 90 # + Melee_Light_Weapon_Smith = 91 # + Range_Weapon_Smith = 92 # + Range_Heavy_Weapon_Smith = 93 # + Advanced_Heavy_Armorer = 94 # + Advanced_Light_Armorer = 95 # + Advanced_Medium_Armorer = 96 # + Advanced_Shield_Smith = 97 # + Advanced_Jeweler = 98 # + Advanced_Melee_Weapon_Smith = 99 # + Advanced_Melee_Heavy_Weapon_Smith = 100 # + Advanced_Melee_Light_Weapon_Smith = 101 # + Advanced_Range_Weapon_Smith = 102 # + Advanced_Range_Heavy_Weapon_Smith = 103 # + Expert_Heavy_Armorer = 104 # + Expert_Light_Armorer = 105 # + Expert_Medium_Armorer = 106 # + Expert_Shield_Smith = 107 # + Expert_Jeweler = 108 # + Expert_Melee_Weapon_Smith = 109 # + Expert_Melee_Heavy_Weapon_Smith = 110 # + Expert_Melee_Light_Weapon_Smith = 111 # + Expert_Range_Weapon_Smith = 112 # + Expert_Range_Heavy_Weapon_Smith = 113 # + Heavy_Armorer_Master = 114 # + Light_Armorer_Master = 115 # + Medium_Armorer_Master = 116 # + Shield_Smith_Master = 117 # + Jeweler_Master = 118 # + Melee_Weapon_Smith_Master = 119 # + Melee_Heavy_Weapon_Smith_Master = 120 # + Melee_Light_Weapon_Smith_Master = 121 # + Range_Weapon_Smith_Master = 122 # + Range_Heavy_Weapon_Smith_Master = 123 # + Forager_Apprentice = 124 # + Forager = 125 # + Desert_Forager = 126 # + Forest_Forager = 127 # + Jungle_Forager = 128 # + Lacustre_Forager = 129 # + Prime_Roots_Forager = 130 # + Advanced_Desert_Forager = 131 # + Advanced_Forest_Forager = 132 # + Advanced_Jungle_Forager = 133 # + Advanced_Lacustre_Forager = 134 # + Advanced_Prime_Roots_Forager = 135 # + Expert_Desert_Forager = 136 # + Expert_Forest_Forager = 137 # + Expert_Jungle_Forager = 138 # + Expert_Lacustre_Forager = 139 # + Expert_Prime_Roots_Forager = 140 # + Master_Desert_Forager = 141 # + Master_Forest_Forager = 142 # + Master_Jungle_Forager = 143 # + Master_Lacustre_Forager = 144 # + Master_Prime_Roots_Forager = 145 # + Kami_Ally = 146 # + Karavan_Ally = 147 # + Title00000 = 148 # + Title00001 = 149 # // Journeyer + Title00002 = 150 # // Novice Kitin Hunter + Title00003 = 151 # // Kitin Hunter + Title00004 = 152 # // Master Kitin Hunter + Title00005 = 153 # // Kitin Eradicator + Title00006 = 154 # // Kitin Mass Murderer + Title00007 = 155 # // Matis Guardian + Title00008 = 156 # // Fyros Guardian + Title00009 = 157 # // Tryker Guardian + Title00010 = 158 # // Zorai Guardian + Title00011 = 159 # // Atys Guardian + Title00012 = 160 # + Title00013 = 161 # + Title00014 = 162 # // The fortunate + Title00015 = 163 # // Jinxed + Title00016 = 164 # + Title00017 = 165 # + Title00018 = 166 # + Title00019 = 167 # + Title00020 = 168 # // Fyros Patriot + Title00021 = 169 # // Matis Vassal + Title00022 = 170 # // Tryker Citizen + Title00023 = 171 # // Zorai Initiate + Title00024 = 172 # // Kami Disciple + Title00025 = 173 # // Karavan Follower + Title00026 = 174 # // Fyros Akenak + Title00027 = 175 # // Matis Noble + Title00028 = 176 # // Tryker Taliar + Title00029 = 177 # // Zorai Awakened + Title00030 = 178 # // Marauder + Title00031 = 179 # // Fyros Ambassador + Title00032 = 180 # // Matis Ambassador + Title00033 = 181 # // Tryker Ambassador + Title00034 = 182 # // Zorai Ambassador + Title00035 = 183 # + Title00036 = 184 # + Title00037 = 185 # + Title00038 = 186 # + Title00039 = 187 # + Title00040 = 188 # + Title00041 = 189 # + Title00042 = 190 # + Title00043 = 191 # + Title00044 = 192 # + Title00045 = 193 # + Title00046 = 194 # + Title00047 = 195 # // Machinegunner + Title00048 = 196 # // Assault Machinegunner + Title00049 = 197 # + Title00050 = 198 # // Apprentice Butcher + Title00051 = 199 # // Butcher + Title00052 = 200 # // Apprentice Florist + Title00053 = 201 # // Florist + Title00054 = 202 # // Apprentice Water-Carrier + Title00055 = 203 # // Water-Carrier + Title00056 = 204 # // Apprentice Magnetic + Title00057 = 205 # // Magnetic Cartographe + Title00058 = 206 # // Apprentice Toolmaker + Title00059 = 207 # // Toolmaker + Title00060 = 208 # // Apprentice Rescuer + Title00061 = 209 # // Rescuer + Title00062 = 210 # // Apprentice Larvester + Title00063 = 211 # // Larvester + Title00064 = 212 # // Apprentice Scrollmaker + Title00065 = 213 # // Scrollmaker + Title00066 = 214 # + Title00067 = 215 # + Title00068 = 216 # + Title00069 = 217 # + Title00070 = 218 # + Title00071 = 219 # + Title00072 = 220 # + Title00073 = 221 # + Title00074 = 222 # + Title00075 = 223 # + Title00076 = 224 # + Title00077 = 225 # + Title00078 = 226 # + Title00079 = 227 # // Wayfarer + WIND = Title00079 # // Title for player come from old Windermmer community + FBT = 228 # + BeginGmTitle = 229 # + # SGM = BeginGmTitle # + GM = 230 # + VG = 231 # + SG = 232 # + G = 233 # + CM = 234 # + EM = 235 # + EG = 236 # + OBSERVER = 237 # + # EndGmTitle = OBSERVER, + NB_CHARACTER_TITLE = 238 + + +class CLFECOMMON(IntEnum): + SYSTEM_LOGIN_CODE = 0 + SYSTEM_SYNC_CODE = 1 + SYSTEM_ACK_SYNC_CODE = 2 + SYSTEM_PROBE_CODE = 3 + SYSTEM_ACK_PROBE_CODE = 4 + SYSTEM_DISCONNECTION_CODE = 5 + SYSTEM_STALLED_CODE = 6 + SYSTEM_SERVER_DOWN_CODE = 7 + SYSTEM_QUIT_CODE = 8 + SYSTEM_ACK_QUIT_CODE = 9 + NumBitsInLongAck = 512 + +class TActionCode(IntEnum): + ACTION_POSITION_CODE = 0 + ACTION_GENERIC_CODE = 1 + ACTION_GENERIC_MULTI_PART_CODE = 2 + ACTION_SINT64 = 3 + ACTION_SYNC_CODE = 10 + ACTION_DISCONNECTION_CODE = 11 + ACTION_ASSOCIATION_CODE = 12 + ACTION_LOGIN_CODE = 13 + ACTION_TARGET_SLOT_CODE = 40 + ACTION_DUMMY_CODE = 99 + +class Card(IntEnum): + BEGIN_TOKEN = 0 + END_TOKEN = 1 + SINT_TOKEN = 2 + UINT_TOKEN = 3 + FLOAT_TOKEN = 4 + STRING_TOKEN = 5 + FLAG_TOKEN = 6 + EXTEND_TOKEN = 7 + +class TType(IntEnum): + STRUCT_BEGIN = 0 + STRUCT_END = 1 + FLAG = 2 + SINT32 = 3 + UINT32 = 4 + FLOAT32 = 5 + STRING = 6 + SINT64 = 7 + UINT64 = 8 + FLOAT64 = 9 + EXTEND_TYPE = 10 + NB_TYPE = 11 diff --git a/tools/TExtendType.py b/tools/TExtendType.py new file mode 100644 index 0000000..5e3ac1d --- /dev/null +++ b/tools/TExtendType.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module TExtendType +# +# 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 . + +class TExtendType: + ET_SHEET_ID = 0 + ET_64_BIT_EXTENDED_TYPES = 0x80000000 + ET_ENTITY_ID = 0x80000000 # ET_ENTITY_ID = ET_64_BIT_EXTENDED_TYPES diff --git a/tools/TMessageType.py b/tools/TMessageType.py new file mode 100644 index 0000000..453bfdb --- /dev/null +++ b/tools/TMessageType.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module TMessageType +# +# 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 . + +from enum import IntEnum + +class TMessageType(IntEnum): + OneWay = 0 + Request = 1 + Response = 2 + Except = 3 diff --git a/tools/TPVPMode.py b/tools/TPVPMode.py new file mode 100644 index 0000000..32f6ff6 --- /dev/null +++ b/tools/TPVPMode.py @@ -0,0 +1,39 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module TPropIndex +# +# 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 . + + +from enum import IntEnum + +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/TPropIndex.py b/tools/TPropIndex.py new file mode 100644 index 0000000..b3845a9 --- /dev/null +++ b/tools/TPropIndex.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module TPropIndex +# +# 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 . + + +from enum import IntEnum +import math + +# Ref : khanat-opennel-code/code/ryzom/common/src/game_share/entity_types.h + +MAX_THRESHOLD = (1024*1024) + +class TPropIndex(IntEnum): + PROPERTY_POSITION = 0 + PROPERTY_POSX = 0 + PROPERTY_POSY = 1 + PROPERTY_POSZ = 2 + PROPERTY_ORIENTATION = 3 + PROPERTY_SHEET = 4 + PROPERTY_BEHAVIOUR = 5 + PROPERTY_NAME_STRING_ID = 6 + PROPERTY_TARGET_ID = 7 + PROPERTY_MODE = 8 + PROPERTY_VPA = 9 + PROPERTY_VPB = 10 + PROPERTY_VPC = 11 + PROPERTY_ENTITY_MOUNTED_ID = 12 + PROPERTY_RIDER_ENTITY_ID = 13 + PROPERTY_CONTEXTUAL = 14 + PROPERTY_BARS = 15 + PROPERTY_TARGET_LIST = 16 + PROPERTY_TARGET_LIST_0 = 16 + PROPERTY_TARGET_LIST_1 = 17 + PROPERTY_TARGET_LIST_2 = 18 + PROPERTY_TARGET_LIST_3 = 19 + PROPERTY_GUILD_SYMBOL = 20 + PROPERTY_GUILD_NAME_ID = 21 + PROPERTY_VISUAL_FX = 22 + PROPERTY_EVENT_FACTION_ID = 23 + PROPERTY_PVP_MODE = 24 + PROPERTY_PVP_CLAN = 25 + PROPERTY_OWNER_PEOPLE = 26 + PROPERTY_OUTPOST_INFOS = 27 + INVALID_PROP_INDEX = 0xff + +THRESHOLD_SHEET = MAX_THRESHOLD +THRESHOLD_BEHAVIOUR = 60000 +THRESHOLD_NAME_STRING_ID = 100000 +THRESHOLD_TARGET_ID = 60000 +THRESHOLD_TARGET_ID_CLIENT_M = 55.0 +THRESHOLD_MODE = MAX_THRESHOLD +THRESHOLD_VPA = MAX_THRESHOLD +THRESHOLD_VPB = MAX_THRESHOLD +THRESHOLD_VPC = 10000 +THRESHOLD_ENTITY_MOUNTED_ID = MAX_THRESHOLD +THRESHOLD_CONTEXTUAL = 100000 +THRESHOLD_CONTEXTUAL_NPC = MAX_THRESHOLD +THRESHOLD_BARS = 30000 +THRESHOLD_BARS_CLIENT_M = 28.0 +THRESHOLD_TARGET_LIST = (100000 * math.sqrt(2.0)) +THRESHOLD_GUILD_SYMBOL = MAX_THRESHOLD +THRESHOLD_GUILD_NAME_ID = MAX_THRESHOLD +THRESHOLD_VISUAL_FX = 30000 +THRESHOLD_EVENT_FACTION_ID = 60000 +THRESHOLD_PVP_MODE = 60000 +THRESHOLD_PVP_CLAN = 60000 +THRESHOLD_OWNER_PEOPLE = 60000 +THRESHOLD_OUTPOST_INFOS = 60000 + +USER_DEFINED_PROPERTY_NB_BITS = 32 +INVALID_PROPERTY = 0xFFFF +NB_VISUAL_PROPERTIES = 28 +MAX_PROPERTIES_PER_ENTITY = NB_VISUAL_PROPERTIES +THRESHOLD_RIDER_ENTITY_ID = MAX_THRESHOLD + diff --git a/tools/TStampQueue.py b/tools/TStampQueue.py new file mode 100644 index 0000000..e51a1e6 --- /dev/null +++ b/tools/TStampQueue.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module TStampQueue +# +# 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 . + +class TStampQueue: + def __init__(self, first=None, second=None): + self.first = first + self.second = second + + diff --git a/tools/TStreamFormat.py b/tools/TStreamFormat.py new file mode 100644 index 0000000..2331a87 --- /dev/null +++ b/tools/TStreamFormat.py @@ -0,0 +1,26 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module TStreamFormat +# +# 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 . + +from enum import IntEnum + +class TStreamFormat(IntEnum): + UseDefault = 0 + Binary = 1 + String = 2 diff --git a/tools/World.py b/tools/World.py new file mode 100644 index 0000000..36a64be --- /dev/null +++ b/tools/World.py @@ -0,0 +1,171 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module World +# +# 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 . + +from tools import Enum +from tools import CSessionId +from tools import BitStream +from tools import CodeMsgXml +from tools import CStringManager + +class World(): + def __init__(self): + self.timestamp = 0 + self.ServerPeopleActive = 255 + self.ServerCareerActive = 255 + self.CharacterSummaries = [] + self.Mainlands = [] + self.CShardNames = [] + self.UserPrivileges = '' + self.FreeTrial = False + self.CurrentState = Enum.TState.st_unknown + self.UseFemaleTitles = False + self.MessageServerTemplate = {} + self.StringManager = CStringManager.CStringManager() + + def CreaterCharacter(self, msgXml, HeadName): + ''' + khanat-opennel-code/code/ryzom/client/src/connection.cpp # class CAHAskCreateChar : public IActionHandler + khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h # void setupFromCharacterSummary (const CCharacterSummary &cs) + khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h # void serialBitMemStream(NLMISC::CBitMemStream &f) + ''' + Slot = 0 + SheetId = 0 + #Mainland= 302 + cSessionId = CSessionId.CSessionId(self.CShardNames[0].SessionId) + + # [A-Za-z]{3,15} # quand je pense qu'ils ont code pour avoir du UTF !!!!! + name = '' + for car in HeadName: + if car >= 'a' and car <= 'z': + pass + elif car >= 'A' and car <= 'Z': + pass + elif car >= '0' and car <= '9': + car = chr(ord(car)+ord('A')-ord('0')) + else: + car ='Z' + name += car + #name = 'Tester' + People = 1 + Sex = 0 + StartPoint = 1 + NbPointFighter = 2 + NbPointCaster = 1 + NbPointCrafter = 1 + NbPointHarvester = 1 + GabaritHeight = 9 + GabaritTorsoWidth = 10 + GabaritArmsWidth = 7 + GabaritLegsWidth = 4 + GabaritBreastSize = 7 + MorphTarget1 = 3 + MorphTarget2 = 3 + MorphTarget3 = 3 + MorphTarget4 = 5 + MorphTarget5 = 5 + MorphTarget6 = 5 + MorphTarget7 = 4 + MorphTarget8 = 4 + EyesColor = 0 + Tattoo = 0 + HairType = 5 + HairColor = 1 + JacketColor = 0 + TrousersColor = 0 + HatColor = 1 + ArmsColor = 0 + HandsColor = 0 + FeetColor = 0 + msgout = BitStream.BitStream() + # GenericMsgHeaderMngr.pushNameToStream("CONNECTION:CREATE_CHAR", out)) + ref = CodeMsgXml.CodeMsgXml(msgXml, 'CONNECTION:CREATE_CHAR') + for size, value, id in ref: + msgout.internalSerial(value, size, typeName=id) + # khanat-opennel-code/code/ryzom/common/src/game_share/msg_client_server.h # void serialBitMemStream(NLMISC::CBitMemStream &f) + msgout.pushUint8(Slot) + msgout.pushUint32(SheetId) + cSessionId.push(msgout) + msgout.pushUString(name) + msgout.pushUint8(People) + msgout.pushUint8(Sex) + + msgout.pushUint8(NbPointFighter) + msgout.pushUint8(NbPointCaster) + msgout.pushUint8(NbPointCrafter) + msgout.pushUint8(NbPointHarvester) + + msgout.pushSint32(StartPoint) + + msgout.pushSint8(HairType) + msgout.pushSint8(HairColor) + + # GabaritHeight => 0 - 15 + msgout.pushSint8(GabaritHeight) + msgout.pushSint8(GabaritTorsoWidth) + msgout.pushSint8(GabaritArmsWidth) + msgout.pushSint8(GabaritLegsWidth) + msgout.pushSint8(GabaritBreastSize) + + # MorphTarget1 => 0 - 7 + msgout.pushSint8(MorphTarget1) + msgout.pushSint8(MorphTarget2) + msgout.pushSint8(MorphTarget3) + msgout.pushSint8(MorphTarget4) + msgout.pushSint8(MorphTarget5) + msgout.pushSint8(MorphTarget6) + msgout.pushSint8(MorphTarget7) + msgout.pushSint8(MorphTarget8) + # EyesColor => 0 - 7 + msgout.pushSint8(EyesColor) + # Tattoo) => 0 = neutral, 1 - 64 Tattoo + msgout.pushSint8(Tattoo) + + #// color for equipement slots (Only for pre-equipped perso created with sheet) + msgout.pushSint8(JacketColor) + msgout.pushSint8(TrousersColor) + msgout.pushSint8(HatColor) + msgout.pushSint8(ArmsColor) + msgout.pushSint8(HandsColor) + msgout.pushSint8(FeetColor) + + #self.Commands.append(msgout) + return msgout + + + def impulsionCreateChar(self, msgXml, uid): + ''' + khanat-opennel-code/code/ryzom/server/src/frontend_service/uid_impulsions.cpp # static void impulsionCreateChar(uint32 uid, NLMISC::CBitMemStream &bms, NLMISC::TGameCycle gameCycle) + ''' + _ = self.CreaterCharacter(msgXml) + # bms = self.CreaterCharacter(msgXml) + #msgout = CMessage("CREATE_CHAR") + #msgout.serial(uid, bms) + + def SelectChar(self, msgXml, PlayerSelectedSlot): + ''' + khanat-opennel-code/code/ryzom/client/src/far_tp.cpp # void CFarTP::selectCharAndEnter() + ''' + msgout = BitStream.BitStream() + ref = CodeMsgXml.CodeMsgXml(msgXml, 'CONNECTION:SELECT_CHAR') + for size, value, id in ref: + msgout.internalSerial(value, size, typeName=id) + msgout.pushUint8(PlayerSelectedSlot) + return msgout + diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/convertUStringToString.py b/tools/convertUStringToString.py new file mode 100644 index 0000000..617766f --- /dev/null +++ b/tools/convertUStringToString.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module convertUStringToString +# +# 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 . + + +def convertUStringToString(name): + ret = '' + for car in name: + if ord(car) >= 32 and ord(car)<=127: + ret += car + return ret diff --git a/tools/getPowerOf2.py b/tools/getPowerOf2.py new file mode 100644 index 0000000..2361d80 --- /dev/null +++ b/tools/getPowerOf2.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +# +# module getPowerOf2 +# +# 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 . + + +def getPowerOf2(v): + res=1; + ret=0; + while res