I'm asking because can't realize why my client stucks on 'Connected' or 'Logging in to game server'...
World Server code:
Code:
class WorldServer(BaseServer):
def __init__(self, host, port):
super().__init__(host, port)
self.connections = []
self.writer = None
async def handle_connection(self, reader: StreamReader, writer: StreamWriter):
peername = writer.get_extra_info('peername')
Logger.debug('[World Server]: Accept connection from {}'.format(peername))
self.writer = writer
if not session.is_authenticated:
# in this loop we redirecting client requests to Login Server and sending response from Login Server back to client
while True:
r, w = await asyncio.open_connection(LoginConfig.HOST.value, LoginConfig.PORT.value)
data = await reader.read(1024)
if not data:
break
# sending client request to Login Server
w.write(data)
response = await r.read(1024)
# sending Login Server response to client
writer.write(response)
await self.accept_connection(peername)
auth = AuthSessionManager(reader, writer)
auth.prepare()
await auth.process()
while True:
# stucks here... getting no request
Logger.error('[World Server]: Trying to get data...')
data = await reader.read(4096)
if data:
Logger.error('data exists... {}'.format(data))
async def accept_connection(self, peername):
self.connections.append(peername)
await queue.put(QueueMessages.WORLD_SERVER_ACCEPT_CONNECTION.value)
session.is_connection_accepted = True
Auth Session Manager:
Code:
class AuthSessionManager(object):
state = AUTH_SESSION_STATES.INIT.value
def __init__(self, reader: StreamReader, writer: StreamWriter):
self.reader = reader
self.writer = writer
self.data = bytes()
self.build = 0
self.unk = 0
self.account_name = None
self.client_seed = 0
self.client_hash = bytes()
self.session_key = bytes()
self.server_hash = bytes()
def prepare(self):
# auth seed need to generate header_crypt
auth_seed = int.from_bytes(urandom(4), 'little')
session.auth_seed = auth_seed
auth_seed_bytes = pack('<I', auth_seed)
response = WorldPacket(WorldOpCode.SMSG_AUTH_CHALLENGE.value, auth_seed_bytes).generate_packet()
self.writer.write(response)
async def process(self):
has_timeout = False
response = None
try:
Logger.debug('Trying to read packet')
request = await wait_for(self.reader.read(1024), timeout=3.0)
except TimeoutError:
Logger.error('[Auth Session Manager]: Timeout error')
has_timeout = True
else:
self._parse_data(request)
self._load_session_key()
self._setup_encryption()
self._generate_server_hash()
if self.server_hash != self.client_hash:
Logger.error('[Auth Session Manager]: Server hash is differs from client hash')
else:
try:
response = self._get_addon_info_packet()
self.writer.write(response)
response = self._get_response()
Logger.debug('[Auth Session Manager]: responding...')
self.writer.write(response)
except Exception as e:
Logger.error('exeption = {}'.format(e))
traceback.print_exc(file=sys.stdout)
finally:
return has_timeout, response
def _parse_data(self, data: bytes):
# omit first 6 bytes, cause 01-02 = packet size, 03-04 = opcode (0x1ED), 05-06 - unknown null-bytes
tmp_buf = BytesIO(data[6:])
self.build = unpack('<H', tmp_buf.read(2))[0]
# remove next 6 unknown null-bytes (\x00)
tmp_buf.read(6)
self.account_name = self._parse_account_name(tmp_buf)
self.client_seed = tmp_buf.read(4)
self.client_hash = tmp_buf.read(20)
def _generate_server_hash(self):
auth_seed = session.auth_seed
del session.auth_seed
to_hash = (
self.account_name.encode('ascii') +
bytes(4) +
self.client_seed +
int.to_bytes(auth_seed, 4, 'little') +
self.session_key
)
self.server_hash = sha1(to_hash).digest()
def _load_session_key(self):
self.session_key = session.session_key_as_bytes
def _setup_encryption(self):
Logger.debug('Setup encryption')
try:
session.header_crypt = HeaderCrypt(self.session_key)
except Exception as e:
Logger.error('header c = {}'.format(e))
def _parse_account_name(self, buffer: BytesIO):
result = bytes()
while True:
char = buffer.read(1)
if char and char != b'\x00':
result += char
else:
break
return result.decode('ascii')
def _get_response(self):
# updating session request
response = pack('<BIBIB',
AUTH_SESSION_RESPONSE_CODES.AUTH_OK.value,
0x00, # BillingTimeRemaining
0x00, # BillingPlanFlags
0x00, # BillingTimeRested
0x01 # Expansion, 0 - normal, 1 - TBC, must be set manually for each account
)
packet = WorldPacket(
WorldOpCode.SMSG_AUTH_RESPONSE.value,
response
).generate_packet(session.header_crypt)
return packet
Login server code:
Code:
class LoginServer(BaseServer):
def __init__(self, host, port):
super().__init__(host, port)
async def handle_connection(self, reader: StreamReader, writer: StreamWriter):
peername = writer.get_extra_info('peername')
Logger.info('[Login Server]: Accepted connection from {}'.format(peername))
if not session.is_connection_accepted:
auth = AuthManager(reader, writer)
has_timeout = await auth.process()
writer.close()
Auth Manager:
Code:
class AuthManager(object):
AUTH_HANDLERS = {
0x00: LoginChallenge,
0x01: LoginProof,
0x02: ReconChallenge,
0x03: ReconProof,
0x10: Realmlist
}
state = AUTH_STATES.INIT.value
def __init__(self, reader: StreamReader, writer: StreamWriter):
self.reader = reader
self.writer = writer
async def process(self):
Logger.debug('[Auth Manager]: reading packet...')
has_timeout = False
try:
request = await asyncio.wait_for(self.reader.read(1024), timeout=1.0)
opcode = request[0]
handler = AuthManager.AUTH_HANDLERS[opcode]
# first byte of client request is opcode
packet = request[1:]
state, response = handler(packet).process()
if response:
Logger.info('sending response back to client')
self.writer.write(response)
except TimeoutError:
Logger.error('[Auth Manager]: Timeout')
has_timeout = True
finally:
return has_timeout
Note:
AuthSessionManager using on World Server (SMSG_AUTH_CHALLENGE, CMSG_AUTH_SESSION, SMSG_AUTH_RESPONSE)
AuthManager using on Login Server (CHALLENGE, PROOF, REALMLIST)
What is wrong with my code ? What I should add to fix the problem with stuck on connection ?