from .packet import Packet from .EnumQuery import EnumQuery from .EnumResponse import EnumResponse from .CFrame import CFrame from .DFrame import DFrame from giants.masterserver import MasterServer from .session import Session import socket import threading import traceback import uuid import struct from _datetime import datetime from utils.logger import setup_logger from giants.player import Player, PlayerPhases import time from giants import APPLICATION_GUID from .DN_MSG_INTERNAL_SEND_CONNECT_INFO import DN_MSG_INTERNAl_SEND_CONNECT_INFO import asyncio logger = setup_logger(__name__) class Netserver(asyncio.DatagramProtocol): def __init__(self, server): self.remotesocket = None self.statssocket = None self.server = server self.addrs = [] self.guid = uuid.uuid4().bytes self.incoming_packets = [] super().__init__() def connection_made(self, transport): logger.debug("Connection made") self.remotesocket = transport def run(self): logger.debug("Run") return self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.bind((self.server.listen_ip, self.server.listen_port)) logger.debug("Listening to %s:%s", self.server.listen_ip, self.server.listen_port) networkthread = threading.Thread(target=self.handle_packets) networkthread.start() if self.server.register_with_ms: ms = MasterServer(self.server) ms.register() while self.server.running: start = datetime.now() self.read_packets() self.server.update() self.send_packets() sleep_ms = (start-datetime.now()).total_seconds()+1/self.server.ticks if sleep_ms > 0: time.sleep(sleep_ms) networkthread.join() def read_packets(self): pass def send_packets(self): pass def datagram_received(self, data, addr): logger.debug("%s:%s > %s", addr[0], addr[1], data.hex()) loop = asyncio.get_event_loop() loop.create_task(self.handle_new_packet(addr, data)) async def handle_new_packet(self, addr, bData): pPacket = Packet(bData) first = pPacket.getByte() if len(bData) >= 4 and first & DFrame.PACKET_COMMAND_DATA: await self.handle_new_dframe(addr, pPacket) elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME: await self.handle_new_cframe(addr, pPacket) elif first == EnumQuery.LEAD or first == EnumResponse.LEAD: await self.handle_new_enum(addr, pPacket) else: logger.error("Unknown frame received") return async def handle_new_enum(self, addr, data): # EnumQuery or EnumResponse second = data.getByte() if second == EnumQuery.COMMAND: # EnumQuery try: eq = EnumQuery(data) logger.debug("Got EnumQuery, sending EnumResponse") er = EnumResponse() er.Payload = eq.Payload er.ApplicationDescSize = 0x50 er.ApplicationDescFlags = 0x41 er.MaxPlayers = self.server.maxplayers er.CurrentPlayers = len(self.server.players) er.SessionName = self.server.name er.ApplicationInstanceGUID = self.guid er.ApplicationGUID = eq.ApplicationGUID '''er.ApplicationReservedData = b'\xff' # Map ID er.ApplicationReservedData += b'\x00\x04\x00' # game type and teams er.ApplicationReservedData += struct.pack(" # \x9c\x53\xf4\xdd: Three Way Island - Canyons # \x1e\xe9\x39\xe1: Still Winter er.ApplicationReservedData += self.server.currentmap.mapname.encode("ascii")''' appdata = Packet() appdata.putByte(0xff) # map ID appdata.putByte(self.server.game_type) # game type appdata.putByte(self.server.teams) # teams appdata.write(b'\x00') # original: 0x00 - does not seem to affect client appdata.putShort(int(self.server.version * 1000)) appdata.write(b'\x02\x92') # original: 0292 - does not seem to affect client appdata.putShort(self.server.points_per_capture) appdata.putShort(self.server.points_per_kill) appdata.write(b'\x00\x00') # original: 0000 - does not seem to affect client appdata.putShort(self.server.detente_time) appdata.write( b'\x9c\x53\xf4\xdd') # Seems to be a checksum of current map OR linked to the number of chars in the map name appdata.write(self.server.currentmap.mapname.encode("ascii")) appdata.write(b'\x00' * (32 - len(self.server.currentmap.mapname))) er.ApplicationReservedData = appdata.getvalue() logger.debug("Current map: %s", self.server.currentmap.mapname) er.ApplicationReservedData += b'\x00' * (32 - len(self.server.currentmap.mapname)) self.send_packet(addr, er.to_packet()) except Exception: logger.error("Could not parse EnumQuery or forge EnumResponse: ") traceback.print_exc() return elif second == EnumResponse.COMMAND: # wait what? ignore that shit return else: logger.error("Unknown DPLHP command: %s", second) async def handle_new_cframe(self, addr, data): try: cframe = CFrame(data) session = self.get_session(addr) player = self.get_player(session) if session else None if cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECT: # CONNECT CFRAME if session and session.Full: logger.error("Session %s:%s was already fully connected. Ignoring.", session.ip, session.port) return elif session and not session.Full and session.SessID == cframe.SessID: # send CONNECTED logger.debug("Already partially established connection. Sending a CFRAME CONNECTED again.") session.send_cframe_connected(cframe) #session.setup_Connect_Retry_Timer() return if not self.server.accept_new_players: # ignore new players return else: logger.debug("New connection. Sending a CFRAME CONNECTED.") session = Session(self.remotesocket) session.SessID = cframe.SessID session.ip = addr[0] session.port = addr[1] await session.lock.acquire() self.addrs.append(session) session.send_cframe_connected(cframe) player = Player("Unknown", session) def bxor(b1, b2): # use xor for bytes result = bytearray() for b1, b2 in zip(b1, b2): result.append(b1 ^ b2) return result player.id = struct.unpack(">L", bxor(struct.pack("