giantsd/dpnet/netserver.py

91 lines
3.7 KiB
Python

from .packet import Packet
from .EnumQuery import EnumQuery
from .EnumResponse import EnumResponse
from .CFrame import CFrame
from .DFrame import DFrame
import traceback
import uuid
from utils.logger import setup_logger
from giants import GameTypes, Teams
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 datagram_received(self, data, addr):
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:
return
elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME:
return
elif first == EnumQuery.LEAD or first == EnumResponse.LEAD:
logger.debug("%s:%s > %s (ENUM)", addr[0], addr[1], bData.hex())
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)
version, server_name, player_count, max_player_count, map_name = await self.server.on_connection_received(addr[0], addr[1])
er = EnumResponse()
er.Payload = eq.Payload
er.ApplicationDescSize = 0x50
er.ApplicationDescFlags = 0x41
er.MaxPlayers = max_player_count
er.CurrentPlayers = player_count
er.SessionName = server_name
er.ApplicationInstanceGUID = self.guid
er.ApplicationGUID = eq.ApplicationGUID
appdata = Packet()
appdata.putByte(0xff) # map ID
appdata.putByte(GameTypes.GTypeNull) # game type
appdata.putByte(Teams.MvRvK) # teams
appdata.write(b'\x00') # original: 0x00 - does not seem to affect client
appdata.putShort(int(version * 1000))
appdata.write(b'\x02\x92') # original: 0292 - does not seem to affect client
appdata.putShort(0) # points per capture
appdata.putShort(0) # points per kill
appdata.write(b'\x00\x00') # original: 0000 - does not seem to affect client
appdata.putShort(0) # detente time
appdata.write(b"\xff\xff\xff\xff") # Seems to be a checksum of current map OR linked to the number of chars in the map name
appdata.write(map_name.encode("ascii"))
appdata.write(b'\x00' * (32 - len(map_name)))
er.ApplicationReservedData = appdata.getvalue()
er.ApplicationReservedData += b'\x00' * (32 - len(map_name))
self.send_packet(addr, er.to_packet())
except Exception:
logger.error("Could not parse EnumQuery or forge EnumResponse: ")
traceback.print_exc()
return
else:
logger.error("Unknown DPLHP command: %s", second)
def send_packet(self, addr, packet):
self.remotesocket.sendto(packet.getvalue(), addr)