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 logger = setup_logger(__name__) class Netserver: def __init__(self, server): self.socket = None self.statssocket = None self.server = server self.addrs = [] self.guid = uuid.uuid4().bytes self.incoming_packets = [] def run(self): 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 handle_packets(self): while True: data, addr = self.socket.recvfrom(1024) logger.debug("%s:%s > %s", addr[0], addr[1], data.hex()) self.handle_new_packet(addr, data) def handle_new_packet(self, addr, bData): pPacket = Packet(bData) first = pPacket.getByte() if len(bData) >= 4 and first & DFrame.PACKET_COMMAND_DATA: self.handle_new_dframe(addr, pPacket) elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME: self.handle_new_cframe(addr, pPacket) elif first == EnumQuery.LEAD or first == EnumResponse.LEAD: self.handle_new_enum(addr, pPacket) else: logger.error("Unknown frame received") return 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) 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.socket) session.SessID = cframe.SessID session.ip = addr[0] session.port = addr[1] 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("