190 lines
6.1 KiB
Python
190 lines
6.1 KiB
Python
import socket
|
|
import struct
|
|
|
|
from time import time_ns
|
|
|
|
from .base import BaseClient
|
|
from ..raknet.bitstream import Bitstream
|
|
|
|
from ..helpers import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
STATE_CONNECTING = 0
|
|
STATE_CONNECTED = 1
|
|
|
|
|
|
class PlayerClient(BaseClient):
|
|
ranges = []
|
|
resend_list = dict()
|
|
|
|
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)
|
|
time = time_ns()
|
|
|
|
_, has_acks = bitstream.read_bit()
|
|
if has_acks:
|
|
#logger.debug("Unfinished code hit; has_acks: True")
|
|
await self.handle_acknowledgement(bitstream, time)
|
|
#return
|
|
|
|
handled = await self.handle_internal_packet(bitstream)
|
|
|
|
if not handled:
|
|
logger.debug("Internal packet were not handled")
|
|
|
|
async def handle_acknowledgement(self, bitstream: Bitstream, time: int) -> bool:
|
|
if not self.deserialize(bitstream):
|
|
return False
|
|
for i in range(len(self.ranges)):
|
|
min, max = self.ranges[i][0], self.ranges[i][1]
|
|
if min > max:
|
|
return False
|
|
for i in range(min, max + 1, 1):
|
|
acked_histogram_counter = self.remove_packet_from_resend_list_and_delete_older_reliable_sequence()
|
|
|
|
def deserialize(self, bitstream: Bitstream): # TODO: Something weird with range thing DS_RangeList.h@L92
|
|
self.ranges.clear()
|
|
|
|
success, count = bitstream.read_compressed(0x10, True)
|
|
if not success:
|
|
return False
|
|
|
|
count, = struct.unpack(b"<H", count)
|
|
|
|
min, max = 0, 0
|
|
for i in range(count):
|
|
_, max_equal_to_min = bitstream.read_bit()
|
|
success, min = bitstream.read_bits(0x10)
|
|
if not success:
|
|
return False
|
|
|
|
min, = struct.unpack(b"<H", min)
|
|
|
|
if not max_equal_to_min:
|
|
success, max = bitstream.read_bits(0x10)
|
|
if not success:
|
|
return False
|
|
|
|
max, = struct.unpack(b"<H", max)
|
|
|
|
if max < min:
|
|
return False
|
|
else:
|
|
max = min
|
|
self.ranges.append((min, max))
|
|
return True
|
|
|
|
def remove_packet_from_resend_list_and_delete_older_reliable_sequence(self, message_number: int, time: int):
|
|
return 0 # TODO
|
|
|
|
async def handle_internal_packet(self, bitstream: Bitstream) -> bool:
|
|
if bitstream.length - bitstream.offset < 0x10:
|
|
return False
|
|
|
|
_, message_number = bitstream.read_bits(0x10)
|
|
_, reliability = bitstream.read_bits(0x04)
|
|
|
|
if reliability in (7, 9, 10):
|
|
success, ordering_channel = bitstream.read_bits(0x05)
|
|
if not success:
|
|
return False
|
|
success, ordering_index = bitstream.read_bits(0x10)
|
|
if not success:
|
|
return False
|
|
|
|
_, is_split_packet = bitstream.read_bit()
|
|
|
|
if is_split_packet:
|
|
logger.warning("Skipping split packet")
|
|
return
|
|
|
|
# TODO: set global split_packet index and count back to 0
|
|
|
|
# Something I dont understand yet
|
|
# TODO: ReadCompressed
|
|
#bitstream.offset += 1
|
|
success, data_bit_length = bitstream.read_compressed(0x10, True)
|
|
if not success:
|
|
return False
|
|
|
|
#_, unknown = bitstream.read(0x01)
|
|
#_, bit_length = bitstream.read(0x08)
|
|
##
|
|
|
|
#logger.debug("bit_length: %d" % bit_length)
|
|
#logger.debug("bitstream: %s" % bitstream)
|
|
|
|
data_bit_length, = struct.unpack(b"<H", data_bit_length)
|
|
|
|
# TODO: ReadAlignedBytes
|
|
#_, data = bitstream.read(bit_length)
|
|
_, data = bitstream.read_aligned_bytes((data_bit_length + 7) >> 3)
|
|
|
|
logger.debug("data: %s" % data)
|
|
|
|
return await self.handle_packet(data)
|
|
|
|
async def handle_packet(self, packet: bytes):
|
|
if packet[0] == 0x0B: # ID_CONNECTION_REQUEST
|
|
pass # TODO: Send ack and data after
|
|
return False |