Commit 4b37c73a authored by aleajactaest's avatar aleajactaest

decode some message server, and reoganize code

parent bc4a1cbd
......@@ -45,6 +45,8 @@ import hashlib
import time
INVALID_SLOT = 0xff
class BitStream():
def __init__(self):
self._pos = 0
......@@ -278,21 +280,7 @@ class BitStream():
self._read = readBefore
return ret
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()
def Test():
def TestBitStream():
a = BitStream()
a.pushBool(True)
a.pushBool(False)
......@@ -349,6 +337,94 @@ def Test():
print(b.readChar())
print(b.readString())
print(b.toBytes())
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):
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<<mask;
return self.data[bitNumber >> 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)
msgout.pushUint8(0) # currentVersion =0
msgout.pushUint32(self.NumBits)
msgout.pushUint32(len(self.data)) # il est lié à 'self.NumBits' dommage que l'on envoie celui-la
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):
......@@ -440,6 +516,30 @@ class TConnectionState(IntEnum):
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
......@@ -546,37 +646,37 @@ class CArgV5(Union):
("f64", c_double),
("ex", CArgV4)]
# 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;
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
......@@ -1478,7 +1578,223 @@ class CPersistentDataRecord:
break
class CAction:
def __init__(self, slot=INVALID_SLOT, code=0):
self.Code = code
self.PropertyCode = code
self.Slot = slot
self._Priority = 1
self.Timeout = 0
self.GameCycle = 0
def unpack(self, message):
raise RuntimeError
def pack(self, message):
raise RuntimeError
def serialIn(self, msgin):
raise RuntimeError
def serialOut(self, msgout):
raise RuntimeError
def size(self):
raise RuntimeError
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):
super().__init__(slot, code)
class CActionSync(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionDisconnection(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionAssociation(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionDummy(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionLogin(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionTargetSlot(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionGeneric(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
self._Message = None
def unpack(self, message):
size = message.readUint32()
self._Message = message.readArrayUint8(size)
class CActionGenericMultiPart(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
self.PartCont = []
self.Number = 0
self.Part = 0
self.NbBlock = 0
def unpack(self, message):
self.Number = message.readUint8()
self.Part = message.readUint16()
self.NbBlock = message.readUint16()
size = message.readUint32()
self.PartCont = message.readArrayUint8(size)
class CActionSint64(CAction):
def __init__(self, slot, code):
super().__init__(slot, code)
class CActionFactory:
def __init__(self):
self.RegisteredAction = []
def create(self, slot, code):
if code == TActionCode.ACTION_POSITION_CODE:
return CActionPosition(slot, code)
elif code == TActionCode.ACTION_GENERIC_CODE:
return CActionGeneric(slot, code)
elif code == TActionCode.ACTION_GENERIC_MULTI_PART_CODE:
return CActionGenericMultiPart(slot, code)
elif code == TActionCode.ACTION_SINT64:
return CActionSint64(slot, code)
elif code == TActionCode.ACTION_SYNC_CODE:
return CActionSync(slot, code)
elif code == TActionCode.ACTION_DISCONNECTION_CODE:
return CActionDisconnection(slot, code)
elif code == TActionCode.ACTION_ASSOCIATION_CODE:
return CActionAssociation(slot, code)
elif code == TActionCode.ACTION_LOGIN_CODE:
return CActionLogin(slot, code)
elif code == TActionCode.ACTION_TARGET_SLOT_CODE:
return CActionTargetSlot(slot, code)
elif code == TActionCode.ACTION_DUMMY_CODE:
return CActionDummy(slot, code)
else:
log = logging.getLogger('myLogger')
log.warning('create() try to create an unknown action (%u)' % code)
raise RuntimeError
def unpack(self, msgin):
'''
khanat-opennel-code/code/ryzom/common/src/game_share/action_factory.cpp : CAction *CActionFactory::unpack (NLMISC::CBitMemStream &message, NLMISC::TGameCycle /* currentCycle */ )
'''
actions = []
if msgin.needRead() >= 8:
shortcode = msgin.readBool()
if shortcode:
code = msgin.readSerial(2)
else:
code = msgin.readUint8()
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
actions.append(action)
else:
log = logging.getLogger('myLogger')
log.warning('Unpacking an action with unknown code, skip it (%u)' % code)
return actions
class CImpulseDecoder:
'''
see : khanat-opennel-code/code/ryzom/client/src/impulse_decoder.cpp
'''
def __init__(self):
self.reset()
self._CActionFactory = CActionFactory()
def decode(self, msgin, 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
# lastAck = lAck[channel]
while True:
next = msgin.readBool()
if not next:
break
if not checkOnce:
checkOnce = True
keep = receivedAck >= lAck[channel]
if keep:
lAck[channel] = nextSentPacket
num += 1
action = self._CActionFactory.unpack(msgin)
if keep:
actions.append(action)
else:
pass
return actions
def reset(self):
self._LastAck0 = [-1]
self._LastAck1 = [-1, -1]
self._LastAck2 = [-1, -1, -1, -1]
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,
......@@ -1491,8 +1807,7 @@ class ClientNetworkConnection:
self._ConnectionState = TConnectionState.NotInitialised
self.UserAddr, self.UserKey, self.UserId = None, None, None
self.frontend = (khanat_host, khanat_port_frontend)
self._sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
self._sock = None
self._CurrentReceivedNumber = 0
self._SystemMode = 0
self._LastReceivedAck = 0
......@@ -1518,7 +1833,21 @@ class ClientNetworkConnection:
self.checkMessageNumber = checkMessageNumber
self._LastAckBit = 0
self._AckBitMask = 0
self._LongAckBitField = 0
self._LongAckBitField = CBitSet()
self._LatestSyncTime = 0
self._ImpulseDecoder = CImpulseDecoder()
self._LongAckBitField.resize(1024)
def connect(self):
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
......@@ -1528,22 +1857,22 @@ class ClientNetworkConnection:
def reset(self):
self._CurrentSendNumber += 0
def buildSystemHeader(self, msg): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout)
msg.pushSint32(self._CurrentSendNumber)
msg.pushBool(True) # systemMode
def buildSystemHeader(self, msgout): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout)
msgout.pushSint32(self._CurrentSendNumber)
msgout.pushBool(True) # systemMode
def sendSystemLogin(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemLogin()
if self._sock is None:
raise ValueError
msg = BitStream()
self.buildSystemHeader(msg)
msg.pushUint8(0) # SYSTEM_LOGIN_CODE
msg.pushUint32(self.UserAddr)
msg.pushUint32(self.UserKey)
msg.pushUint32(self.UserId)
msg.pushString(self.LanguageCode)
self._sock.sendto(msg.toBytes(), self.frontend)
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._sock.sendto(msgout.toBytes(), self.frontend)
self._CurrentSendNumber += 1
self._ConnectionState = TConnectionState.Login
......@@ -1553,25 +1882,37 @@ class ClientNetworkConnection:
if self._sock is None:
raise ValueError
self._QuitId += 1
msg = BitStream()
self.buildSystemHeader(msg)
msg.pushUint8(8) # SYSTEM_LOGIN_CODE
msg.pushSint32(self._QuitId) # _QuitId
self._sock.sendto(msg.toBytes(), self.frontend)
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_QUIT_CODE)
msgout.pushSint32(self._QuitId) # _QuitId
self._sock.sendto(msgout.toBytes(), self.frontend)
self._ConnectionState = TConnectionState.Quit
def sendSystemAckSync(self): # code/ryzom/client/src/network_connection.cpp # void CNetworkConnection::sendSystemAckSync()
if self._sock is None:
raise ValueError
msg = BitStream()
self.buildSystemHeader(msg)
msg.pushUint8(2) # SYSTEM_ACK_SYNC_CODE
msg.pushSint32(self._LastReceivedNumber)
msg.pushSint32(self._LastAckInLongAck)
msg.pushSint32(self._LongAckBitField) # Signale le nombre de packet perdu
msg.pushSint32(self._LatestSync)
self.log.error("TODO")
# self._sock.sendto(msg.toBytes(), self.frontend)
self.log.debug("sendSystemAckSync")
msgout = BitStream()
self.buildSystemHeader(msgout)
msgout.pushUint8(CLFECOMMON.SYSTEM_ACK_SYNC_CODE)
msgout.pushSint32(self._LastReceivedNumber)
msgout.pushSint32(self._LastAckInLongAck)
self._LongAckBitField.writeSerial(msgout) # Signale le nombre de packet perdu
msgout.pushSint32(self._LatestSync)
self.log.debug("%s" % msgout.message())
self.log.debug("sendSystemAckSync -> send")
self._sock.sendto(msgout.toBytes(), self.frontend)
self._LatestSyncTime = self._UpdateTime
def sendSystemDisconnection(self):
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 readDelta(self, msg):
propertyCount = msg.readUint16()
......@@ -1587,8 +1928,12 @@ class ClientNetworkConnection:
self.log.debug("serverTick:%d" % serverTick)
#self.readDelta(msg)
# khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::buildStream( CBitMemStream &msgin )
def impulseDecode(self, msgin):
actions = self._ImpulseDecoder.decode(msgin, self._CurrentReceivedNumber, self._LastReceivedAck, self._CurrentSendNumber )
self.log.debug(str(actions))
def buildStream(self, buffersize=65536):
# khanat-opennel-code/code/ryzom/client/src/network_connection.cpp # bool CNetworkConnection::buildStream( CBitMemStream &msgin )
data, addr = self._sock.recvfrom(buffersize)
return data, addr
......@@ -1605,7 +1950,10 @@ class ClientNetworkConnection:
self._LastReceivedPacketInBothModes = self._CurrentReceivedNumber - 1
if not self._SystemMode:
self.log.debug("Normal Mode")
self._LastReceivedAck = msg.readSint32();
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))
......@@ -1628,13 +1976,14 @@ class ClientNetworkConnection:
self._AckBitMask = 0x00000000
self._LastAckBit = ackBit;
for i in range(self._LastReceivedNumber+1, self._CurrentReceivedNumber+1):
if self._LongAckBitField & (i & (512-1)) > 1:
self._LongAckBitField = self._LongAckBitField ^ (i & (512-1))
if ackBool:
self._LongAckBitField = self._LongAckBitField | (self._CurrentReceivedNumber & (512 - 1))
self._LongAckBitField %= 512
self._LongAckBitField.clearBit(i & 511)
self._LongAckBitField.set(self._CurrentReceivedNumber & 511, ackBool)
if self._LastAckInLongAck <= (self._CurrentReceivedNumber-512):
self._LastAckInLongAck = self._CurrentReceivedNumber-511;
self._LastReceivedNumber = self._CurrentReceivedNumber
self.log.debug("_CurrentReceivedNumber:%d, _LastReceivedNumber:%d, ackBit:%d, _AckBitMask:%d _LongAckBitField:%d" % (self._CurrentReceivedNumber, self._LastReceivedNumber, ackBit, self._AckBitMask, self._LongAckBitField))
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):
......@@ -1645,6 +1994,7 @@ class ClientNetworkConnection:
self.log.debug("received STALLED")
def receiveSystemSync(self, msg):
self._LatestSyncTime = self._UpdateTime
self._Synchronize = msg.readUint32()
stime = msg.readSint64()
self._LatestSync = msg.readUint32()
......@@ -1670,8 +2020,113 @@ class ClientNetworkConnection:
self._CurrentClientTime = self._UpdateTime - (self._LCT + self._MsPerTick)
self.sendSystemAckSync()
def receiveNormalMessage(self, msgin):
self.log.debug("received normal message Packet (%d)" % (msgin.needRead()))
self.impulseDecode(msgin)
def disconnect(self):
pass
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()
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)