Commit before potential bitstream rewrite
This commit is contained in:
parent
0cd474601a
commit
1b78fb98bc
|
@ -13,7 +13,7 @@
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 7777,
|
"port": 7777,
|
||||||
"hostname": "Python > C",
|
"hostname": "Python > C",
|
||||||
"password": "",
|
"password": "test",
|
||||||
"rcon_password": "changeme",
|
"rcon_password": "changeme",
|
||||||
"max_players": 50,
|
"max_players": 50,
|
||||||
"mode": "debug",
|
"mode": "debug",
|
||||||
|
|
|
@ -9,5 +9,4 @@
|
||||||
0x25 ID_INVALID_PASSWORD
|
0x25 ID_INVALID_PASSWORD
|
||||||
0x27 ID_PONG
|
0x27 ID_PONG
|
||||||
0x37 ID_ADVERTISE_SYSTEM
|
0x37 ID_ADVERTISE_SYSTEM
|
||||||
|
0x26 ID_MODIFIED_PACKET
|
||||||
0x26 ID_MODIFIED_PACKET # default behavior (did not find the packet||could not handle because not found)
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ import socket
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from .base import BaseClient
|
from .base import BaseClient
|
||||||
|
from ..raknet.bitstream import Bitstream
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
STATE_CONNECTING = 0
|
STATE_CONNECTING = 0
|
||||||
STATE_AUTH = 1
|
STATE_CONNECTED = 1
|
||||||
STATE_CONNECTED = 2
|
|
||||||
|
|
||||||
class PlayerClient(BaseClient):
|
class PlayerClient(BaseClient):
|
||||||
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
|
||||||
|
@ -19,29 +19,43 @@ class PlayerClient(BaseClient):
|
||||||
|
|
||||||
self.state = STATE_CONNECTING
|
self.state = STATE_CONNECTING
|
||||||
|
|
||||||
|
# TODO: Make this static
|
||||||
self.handlers = {
|
self.handlers = {
|
||||||
STATE_CONNECTING: self.on_connection_request,
|
"default": {
|
||||||
#0x00: self.on_state_dependent_handle
|
"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):
|
async def on_packet(self, packet: bytes):
|
||||||
logger.debug("on_packet(%s)" % packet)
|
logger.debug("on_packet(%s)" % packet)
|
||||||
|
|
||||||
try:
|
#try:
|
||||||
packet = self.server.compressor.decompress(packet)
|
packet = self.server.compressor.decompress(packet)
|
||||||
await self.handlers.get(self.state, self.on_unimplemented)(packet)
|
|
||||||
except Exception as err:
|
current_handlers = self.handlers.get(self.state, self.handlers["default"])
|
||||||
logger.error(err)
|
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):
|
async def on_unimplemented(self, packet: bytes):
|
||||||
pass
|
logger.warning("Unimplemented handler for packet: %s" % packet)
|
||||||
|
|
||||||
async def on_connection_request(self, packet: bytes):
|
async def on_connection_request(self, packet: bytes):
|
||||||
logger.debug("on_connection_request(%s)" % packet)
|
logger.debug("on_connection_request(%s)" % packet)
|
||||||
|
|
||||||
if self.state != STATE_CONNECTING: # Client should not send this after it has passed
|
|
||||||
return
|
|
||||||
|
|
||||||
challenge = (self.ip_uint ^ self.server.config.challenge_short) & 0xFFFF
|
challenge = (self.ip_uint ^ self.server.config.challenge_short) & 0xFFFF
|
||||||
challenge_solution = challenge ^ 0x6969 # 0x6969 being client_version?
|
challenge_solution = challenge ^ 0x6969 # 0x6969 being client_version?
|
||||||
|
|
||||||
|
@ -57,16 +71,42 @@ class PlayerClient(BaseClient):
|
||||||
self.state = STATE_CONNECTED
|
self.state = STATE_CONNECTED
|
||||||
await self.send(b"\x19\x00") # Challenge passed
|
await self.send(b"\x19\x00") # Challenge passed
|
||||||
|
|
||||||
async def parse_internal_packet(self, packet: bytes):
|
async def on_player_packet(self, packet: bytes):
|
||||||
pass
|
bitstream = Bitstream(packet)
|
||||||
|
|
||||||
async def on_state_dependent_handle(self, packet: bytes):
|
has_acks = bitstream.read(0x01)
|
||||||
switch = {
|
if has_acks:
|
||||||
STATE_AUTH: self.on_connection_graph_request
|
logger.debug("Unfinished code hit; has_acks: True")
|
||||||
}
|
return
|
||||||
await switch.get(self.state, self.on_unimplemented)(packet)
|
|
||||||
|
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
|
||||||
|
|
||||||
async def on_connection_graph_request(self, packet: bytes):
|
message_number = bitstream.read(0x10)
|
||||||
packet = packet[1:] # Skip first byte (0x00)
|
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)
|
|
@ -1,9 +1,14 @@
|
||||||
|
import ctypes as c
|
||||||
|
|
||||||
from array import array
|
from array import array
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Bitstream:
|
class Bitstream:
|
||||||
def __init__(self):
|
def __init__(self, data: bytes = b""):
|
||||||
self.offset = 0
|
self.offset = 0
|
||||||
self.buffer = array("B")
|
self.buffer = array("B", data)
|
||||||
|
|
||||||
# This is not finished.. would like to implement bit_length support
|
# This is not finished.. would like to implement bit_length support
|
||||||
def write(self, value: bool):
|
def write(self, value: bool):
|
||||||
|
@ -43,6 +48,8 @@ class Bitstream:
|
||||||
bytes_to_read = byte_read_to - byte_read_from + 1
|
bytes_to_read = byte_read_to - byte_read_from + 1
|
||||||
|
|
||||||
if byte_read_to >= len(self.buffer):
|
if byte_read_to >= len(self.buffer):
|
||||||
|
logger.debug("byte_read_to: %d" % byte_read_to)
|
||||||
|
logger.debug("len(self.buffer): %d" % len(self.buffer))
|
||||||
raise Exception("Reading beyond the buffer")
|
raise Exception("Reading beyond the buffer")
|
||||||
|
|
||||||
value = 0
|
value = 0
|
||||||
|
@ -57,6 +64,29 @@ class Bitstream:
|
||||||
|
|
||||||
self.offset += bit_length
|
self.offset += bit_length
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def read_compressed(self, bit_length: int, unsigned: bool):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def read_aligned_bytes(self, byte_length: int) -> bytes:
|
||||||
|
if byte_length <= 0:
|
||||||
|
return b""
|
||||||
|
|
||||||
|
self.align_to_byte()
|
||||||
|
|
||||||
|
if self.offset + (byte_length * 8) > len(self.buffer):
|
||||||
|
logger.debug("Reading beyond the buffer")
|
||||||
|
return b""
|
||||||
|
|
||||||
|
byte_read_from = self.offset // 8
|
||||||
|
byte_read_to = byte_read_from + byte_length
|
||||||
|
self.offset += byte_length
|
||||||
|
|
||||||
|
return self.buffer[byte_read_from:byte_read_to + 1]
|
||||||
|
|
||||||
|
def align_to_byte(self):
|
||||||
|
if self.offset % 8 != 0:
|
||||||
|
self.offset += 8 - (self.offset % 8)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
b = bytearray(" " + " ".join(" ".join(format(c, "08b")) for c in self.buffer) + " ", "ascii")
|
b = bytearray(" " + " ".join(" ".join(format(c, "08b")) for c in self.buffer) + " ", "ascii")
|
||||||
|
|
|
@ -52,7 +52,7 @@ class Server:
|
||||||
if addr not in self.clients:
|
if addr not in self.clients:
|
||||||
ip, port = addr
|
ip, port = addr
|
||||||
self.clients[addr] = Client(self, ip, port)
|
self.clients[addr] = Client(self, ip, port)
|
||||||
await (self.clients[addr].onpacket(data)) #await self.clients[addr].on_packet(data)
|
await self.clients[addr].on_packet(data)
|
||||||
|
|
||||||
disconnected = [c for c in self.clients.values() if c.connected == False]
|
disconnected = [c for c in self.clients.values() if c.connected == False]
|
||||||
for c in disconnected: # Remove dead connections
|
for c in disconnected: # Remove dead connections
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
from . import server
|
from . import server
|
||||||
|
from . import packet
|
Loading…
Reference in New Issue
Block a user