clientbot/tools/ClientNetworkConnection.py

853 lines
41 KiB
Python

#!/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 <http://www.gnu.org/licenses/>.
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()