forked from hipstercat/giantsd
91 lines
3.7 KiB
Python
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) |