113 lines
3.5 KiB
Python
113 lines
3.5 KiB
Python
import socket
|
|
import struct
|
|
|
|
from .base import BaseClient
|
|
from ..raknet.bitstream import Bitstream
|
|
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
STATE_CONNECTING = 0
|
|
STATE_CONNECTED = 1
|
|
|
|
|
|
class PlayerClient(BaseClient):
|
|
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
|
super().__init__(server, ip, port)
|
|
logger.debug("Client resolved to PlayerClient")
|
|
|
|
# TODO: check if banned and handle here
|
|
|
|
self.state = STATE_CONNECTING
|
|
|
|
# TODO: Make this static
|
|
self.handlers = {
|
|
"default": {
|
|
"default": self.on_unimplemented_state
|
|
},
|
|
STATE_CONNECTING: {
|
|
"default": self.on_unexpected,
|
|
0x18: self.on_connection_request
|
|
},
|
|
STATE_CONNECTED: {
|
|
"default": self.on_player_packet
|
|
}
|
|
}
|
|
|
|
async def on_packet(self, packet: bytes):
|
|
logger.debug("on_packet(%s)" % packet)
|
|
|
|
#try:
|
|
packet = self.server.compressor.decompress(packet)
|
|
|
|
current_handlers = self.handlers.get(self.state, self.handlers["default"])
|
|
await current_handlers.get(packet[0], current_handlers["default"])(packet)
|
|
#except Exception as err:
|
|
#logger.error(err)
|
|
|
|
async def on_unexpected(self, packet: bytes):
|
|
logger.warning("Received unexpected packet: %s" % packet)
|
|
|
|
async def on_unimplemented_state(self, packet: bytes):
|
|
logger.warning("Unimplemented state: %d" % self.state)
|
|
|
|
async def on_unimplemented(self, packet: bytes):
|
|
logger.warning("Unimplemented handler for packet: %s" % packet)
|
|
|
|
async def on_connection_request(self, packet: bytes):
|
|
logger.debug("on_connection_request(%s)" % packet)
|
|
|
|
challenge = (self.ip_uint ^ self.server.config.challenge_short) & 0xFFFF
|
|
challenge_solution = challenge ^ 0x6969 # 0x6969 being client_version?
|
|
|
|
challenge_answer, = struct.unpack_from(b"<H", packet, 1)
|
|
|
|
if challenge_answer != challenge_solution: # Did not pass challenge
|
|
logger.debug("challenge: failed")
|
|
await self.send(struct.pack(b"<BH", 0x1A, challenge))
|
|
return
|
|
|
|
logger.debug("challenge: passed")
|
|
|
|
self.state = STATE_CONNECTED
|
|
await self.send(b"\x19\x00") # Challenge passed
|
|
|
|
async def on_player_packet(self, packet: bytes):
|
|
bitstream = Bitstream(packet)
|
|
|
|
has_acks = bitstream.read(0x01)
|
|
if has_acks:
|
|
logger.debug("Unfinished code hit; has_acks: True")
|
|
return
|
|
|
|
handled = await self.handle_internal_packet(bitstream)
|
|
|
|
if not handled:
|
|
logger.debug("Internal packet were not handled")
|
|
|
|
async def handle_internal_packet(self, bitstream: Bitstream) -> bool:
|
|
if (len(bitstream.buffer) * 8) - bitstream.offset < 0x10:
|
|
return False
|
|
|
|
message_number = bitstream.read(0x10)
|
|
reliability = bitstream.read(0x04)
|
|
isSplitPacket = bitstream.read(0x01)
|
|
|
|
if isSplitPacket:
|
|
logger.warning("Skipping split packet")
|
|
return
|
|
|
|
# Something I dont understand yet
|
|
# TODO: ReadCompressed
|
|
bitstream.offset += 1
|
|
unknown = bitstream.read(0x01)
|
|
bit_length = bitstream.read(0x08)
|
|
##
|
|
|
|
logger.debug("bit_length: %d" % bit_length)
|
|
logger.debug("bitstream: %s" % bitstream)
|
|
|
|
# TODO: ReadAlignedBytes
|
|
data = bitstream.read(bit_length)
|
|
|
|
logger.debug("data: %s" % data) |