giantsd/dpnet/netserver.py

848 lines
40 KiB
Python
Raw Permalink Normal View History

2019-03-05 01:49:13 +01:00
import os
2020-04-22 02:22:34 +02:00
from geolite2 import geolite2
2019-01-22 01:30:42 +01:00
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
2019-01-23 10:54:51 +01:00
import struct
2019-02-05 01:35:22 +01:00
from _datetime import datetime
2019-01-22 01:30:42 +01:00
from utils.logger import setup_logger
2019-02-05 01:35:22 +01:00
from giants.player import Player, PlayerPhases
import time
from giants import APPLICATION_GUID
2019-02-28 04:01:03 +01:00
import random
2019-02-05 01:35:22 +01:00
from .DN_MSG_INTERNAL_SEND_CONNECT_INFO import DN_MSG_INTERNAl_SEND_CONNECT_INFO
import asyncio
2019-03-13 01:37:49 +01:00
from dpnet.gamepackets import *
2020-04-22 02:22:34 +02:00
import requests
import json
2019-01-22 01:30:42 +01:00
logger = setup_logger(__name__)
2020-04-22 02:22:34 +02:00
server_names = ["\x02Hello \x05from \x02A\x03m\x04a\x05z\x06e\x07d\x03",
"\x08Have fun playing Giants!\x01",
"\x02Oh hello \x03%IP%\x05",
"\x03!! DON'T JOIN THIS SERVER !!\x01",
"\x03COVID-19 total cases in \x02%COVID_COUNTRY%\03: %COVID%\x01",
"\x04Don't forget to join our Discord: \x06https://discord.gg/Avj4azU\x01",
"\x08vvv this server has the worst ping in the world vvv\x01"]
with open("countries.json") as fp:
countries = json.load(fp)
2019-01-22 01:30:42 +01:00
class Netserver(asyncio.DatagramProtocol):
2019-01-22 01:30:42 +01:00
def __init__(self, server):
self.remotesocket = None
2019-01-22 01:30:42 +01:00
self.statssocket = None
self.server = server
self.addrs = []
self.guid = uuid.uuid4().bytes
2019-02-05 01:35:22 +01:00
self.incoming_packets = []
super().__init__()
def connection_made(self, transport):
logger.debug("Connection made")
self.remotesocket = transport
2019-01-22 01:30:42 +01:00
def run(self):
return
2020-04-22 02:22:34 +02:00
logger.debug("Run")
2019-01-22 01:30:42 +01:00
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)
2019-02-05 01:35:22 +01:00
networkthread = threading.Thread(target=self.handle_packets)
networkthread.start()
2019-01-22 01:30:42 +01:00
if self.server.register_with_ms:
ms = MasterServer(self.server)
2020-04-22 02:22:34 +02:00
ms._register()
2019-02-05 01:35:22 +01:00
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)
2019-01-22 01:30:42 +01:00
2019-02-05 01:35:22 +01:00
networkthread.join()
def read_packets(self):
pass
def send_packets(self):
pass
2019-01-22 01:30:42 +01:00
def datagram_received(self, data, addr):
loop = asyncio.get_event_loop()
loop.create_task(self.handle_new_packet(addr, data))
2019-01-22 01:30:42 +01:00
async def handle_new_packet(self, addr, bData):
2019-01-22 01:30:42 +01:00
pPacket = Packet(bData)
first = pPacket.getByte()
if len(bData) >= 4 and first & DFrame.PACKET_COMMAND_DATA:
await self.handle_new_dframe(addr, pPacket)
2019-01-22 01:30:42 +01:00
elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME:
await self.handle_new_cframe(addr, pPacket)
2019-01-22 01:30:42 +01:00
elif first == EnumQuery.LEAD or first == EnumResponse.LEAD:
2019-02-15 01:49:34 +01:00
logger.debug("%s:%s > %s (ENUM)", addr[0], addr[1], bData.hex())
await self.handle_new_enum(addr, pPacket)
2019-01-22 01:30:42 +01:00
else:
logger.error("Unknown frame received")
return
async def handle_new_enum(self, addr, data):
2019-01-22 01:30:42 +01:00
# EnumQuery or EnumResponse
second = data.getByte()
if second == EnumQuery.COMMAND:
# EnumQuery
try:
2020-04-22 02:22:34 +02:00
random_players = random.randint(100, 1000)
2019-01-22 01:30:42 +01:00
eq = EnumQuery(data)
2019-02-15 01:49:34 +01:00
#logger.debug("Got EnumQuery, sending EnumResponse")
2019-01-22 01:30:42 +01:00
er = EnumResponse()
er.Payload = eq.Payload
er.ApplicationDescSize = 0x50
er.ApplicationDescFlags = 0x41
2020-04-22 02:22:34 +02:00
er.MaxPlayers = random_players
er.CurrentPlayers = 1337
# magic
random_name = random.choice(server_names)
if "%IP%" in random_name: random_name = random_name.replace("%IP%", addr[0])
if "%JOIN_COUNT%" in random_name: random_name = random_name.replace("%JOIN_COUNT%", str(Netserver.join_count))
if "%COVID%" in random_name:
reader = geolite2.reader()
player_country = reader.get(addr[0])
d = {country["ISO2"]: country for country in countries}
try:
d_covid = requests.get("https://api.covid19api.com/summary", timeout=1).json()
except requests.Timeout:
random_name = random_name.replace("%COVID%", "unknown :'(")
random_name = random_name.replace("%COVID_COUNTRY%", "Global")
else:
if player_country and player_country["country"]["iso_code"] in d:
random_name = random_name.replace("%COVID%", str(list(filter(lambda d: d["CountryCode"] == player_country["country"]["iso_code"], d_covid["Countries"]))[0]["TotalConfirmed"]))
random_name = random_name.replace("%COVID_COUNTRY%", player_country["country"]["names"]["en"])
else:
random_name = random_name.replace("%COVID%", str(d_covid["Global"]["TotalConfirmed"]))
random_name = random_name.replace("%COVID_COUNTRY%", "Global")
er.SessionName = random_name
2019-01-22 01:30:42 +01:00
er.ApplicationInstanceGUID = self.guid
er.ApplicationGUID = eq.ApplicationGUID
2019-02-05 22:33:25 +01:00
'''er.ApplicationReservedData = b'\xff' # Map ID
er.ApplicationReservedData += b'\x00\x04\x00' # game type and teams
2019-01-23 10:54:51 +01:00
er.ApplicationReservedData += struct.pack("<L", int(self.server.version*1000)) # game version
2019-01-22 01:30:42 +01:00
er.ApplicationReservedData += b'\x02\x92\x05\x00\x01\x00\x00\x00\x00\x00' # Unknown
2019-02-05 01:35:22 +01:00
er.ApplicationReservedData += b'\x9c\x53\xf4\xdd' # Seems to be a checksum of current map
2019-01-22 01:30:42 +01:00
# \x00\x00\x00\x00: [None]
# \x00\x00\x00\x01: <custom map>
# \x9c\x53\xf4\xdd: Three Way Island - Canyons
# \x1e\xe9\x39\xe1: Still Winter
2019-02-15 01:49:34 +01:00
# \x9f\xb2\x42\xec: test
2019-02-05 22:33:25 +01:00
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)
2019-02-19 01:01:31 +01:00
appdata.write(self.server.currentmap.checksum) # Seems to be a checksum of current map OR linked to the number of chars in the map name
2019-02-05 22:33:25 +01:00
appdata.write(self.server.currentmap.mapname.encode("ascii"))
appdata.write(b'\x00' * (32 - len(self.server.currentmap.mapname)))
er.ApplicationReservedData = appdata.getvalue()
2019-02-19 01:01:31 +01:00
logger.debug("Current map: %s, checksum: %s", self.server.currentmap.mapname, self.server.currentmap.checksum)
2019-01-22 01:30:42 +01:00
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):
2019-01-22 01:30:42 +01:00
try:
cframe = CFrame(data)
session = self.get_session(addr)
2019-02-05 01:35:22 +01:00
player = self.get_player(session) if session else None
2019-01-22 01:30:42 +01:00
if cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECT:
2019-02-15 01:49:34 +01:00
logger.debug("%s:%s > %s (CFRAME CONNECT)", addr[0], addr[1], data.getvalue().hex())
2019-01-22 01:30:42 +01:00
# CONNECT CFRAME
if session and session.Full:
2019-03-05 01:30:20 +01:00
if False:
logger.error("Session %s:%s was already fully connected. Ignoring.", session.ip, session.port)
return
else:
logger.error("Session %s:%s was already fully connected. Kicking the old one.", session.ip, session.port)
player = self.get_player(session)
if player:
self.server.remove_player(self.get_player(session))
self.addrs.remove(session)
2019-01-22 01:30:42 +01:00
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.server, self.remotesocket)
2019-01-22 01:30:42 +01:00
session.SessID = cframe.SessID
session.ip = addr[0]
session.port = addr[1]
await session.lock.acquire()
2019-01-22 01:30:42 +01:00
self.addrs.append(session)
session.send_cframe_connected(cframe)
2019-02-05 01:35:22 +01:00
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
2019-02-28 04:01:03 +01:00
#player.id = struct.unpack(">L", bxor(struct.pack("<L", len(self.server.players)), self.guid[0:4]))[0]
2019-02-05 01:35:22 +01:00
logger.debug("Value for connected player: %s", player.id)
player.phase = PlayerPhases.CFRAME_CONNECT
self.server.create_temp_player(player)
2019-01-22 01:30:42 +01:00
#session.setup_Connect_Retry_Timer()
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECTED:
2019-02-15 01:49:34 +01:00
logger.debug("%s:%s > %s (CFRAME CONNECTED)", addr[0], addr[1], data.getvalue().hex())
2019-01-22 01:30:42 +01:00
# CONNECTED CFRAME
# check if already sent a CONNECT packet
logger.debug("%s:%s sent back a CONNECTED CFrame.", session.ip, session.port)
if not session:
logger.error("%s sent a CONNECTED opcode without having sent a CONNECT before. GTFO.", addr)
return
if not cframe.SessID == session.SessID or cframe.Command & CFrame.PACKET_COMMAND_POLL:
logger.error("Sent a CONNECTED packet with incorrect SessID or had COMMAND_POLL")
return
session.Full = True # fully connected
session.cancel_Connect_Retry_Timer()
2019-02-05 01:35:22 +01:00
player.phase = PlayerPhases.CFRAME_CONNECTED
2019-02-05 22:33:25 +01:00
session.send_dframe_keepalive()
2019-01-22 01:30:42 +01:00
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_SACK:
2019-02-15 01:49:34 +01:00
logger.debug("%s:%s > %s (CFRAME SACK)", addr[0], addr[1], data.getvalue().hex())
2019-01-22 01:30:42 +01:00
if not session or not session.Full:
logger.error("Received a SACK packet for a non fully connected session")
return
sack_sent = False
if not cframe.NSeq == session.next_expected_seq:
logger.error("Received CFRAME (%s) does not have the same NSeq (%s). Did we miss a packet?", cframe.NSeq, session.next_expected_seq)
if not sack_sent:
session.send_cframe_sack()
sack_sent = True
2019-01-22 10:35:15 +01:00
if not session.next_send == cframe.NRecv:
logger.error("Received CFRAME (%s) does not have same NRcv (%s). One sent packet might have been lost.", cframe.NRecv, session.next_send)
2019-01-22 01:30:42 +01:00
if not sack_sent:
session.send_cframe_sack()
sack_sent = True
# release lock for new packet to be sent
2019-02-19 01:01:31 +01:00
if session.lock.locked():
session.lock.release()
2019-01-22 01:30:42 +01:00
# WIP: The bNSeq, bNRcv, optional selective acknowledgment (SACK), and optional send mask fields are then processed by using the standard rules in sections 3.1.5.2.1 through 3.1.5.2.4
# TODO: A successfully validated SACK packet SHOULD count as a valid receive and thus restart the KeepAlive timer
if cframe.Command & CFrame.PACKET_COMMAND_POLL:
if not session:
logger.error("Received a POLL packet for a non fully connected session")
return
2019-02-05 22:33:25 +01:00
#logger.debug("Got a CFrame POLL. Replying with a SACK.")
2019-01-22 01:30:42 +01:00
# must send back a ACK
2019-02-15 01:49:34 +01:00
session.send_cframe_sack()
2019-01-22 01:30:42 +01:00
except Exception:
logger.error("Should have been a CFRAME but could not parse it")
traceback.print_exc()
return
async def handle_new_dframe(self, addr, data):
2019-01-22 01:30:42 +01:00
try:
dframe = DFrame(data)
2019-02-15 01:49:34 +01:00
#logger.debug("Received DFRAME")
2019-01-22 01:30:42 +01:00
session = next((x for x in self.addrs if x.ip == addr[0] and x.port == addr[1]), None)
2019-02-28 04:01:03 +01:00
if not session and not dframe.Control & DFrame.PACKET_CONTROL_END_STREAM:
2019-01-22 01:30:42 +01:00
logger.debug("%s sent a DFRAME without having sent a CONNECT before. GTFO.", addr)
2019-02-28 04:01:03 +01:00
logger.debug("%s:%s > %s (DFRAME)", addr[0], addr[1], data.getvalue().hex())
2019-01-22 01:30:42 +01:00
return
if dframe.Control & DFrame.PACKET_CONTROL_END_STREAM:
2019-02-28 04:01:03 +01:00
logger.debug("%s:%s > %s (END_STREAM)", addr[0], addr[1], data.getvalue().hex())
2019-01-22 01:30:42 +01:00
if not session:
logger.error("Received a END STREAM packet for a non fully connected session")
session = Session(self.server, self.remotesocket)
2019-02-28 04:01:03 +01:00
session.ip = addr[0]
session.port = addr[1]
session.SessID = dframe.SessID
session.next_send = dframe.NRcv
session.next_expected_seq = dframe.Seq+1
session.send_cframe_sack()
2019-01-22 01:30:42 +01:00
resp = DFrame()
resp.Command = DFrame.PACKET_COMMAND_DATA | DFrame.PACKET_COMMAND_NEW_MSG | DFrame.PACKET_COMMAND_END_MSG | DFrame.PACKET_COMMAND_RELIABLE | DFrame.PACKET_COMMAND_SEQUENTIAL
resp.Control = DFrame.PACKET_CONTROL_END_STREAM
resp.Seq = dframe.NRcv
resp.NRcv = session.next_send
2019-02-28 04:01:03 +01:00
logger.debug(" %s:%s < %s (END_STREAM)", session.ip, session.port, resp.to_packet().getvalue().hex())
2019-01-22 01:30:42 +01:00
session.send(resp)
# TODO: broadcast session has disconnected
2019-02-28 04:01:03 +01:00
if self.get_session(addr):
self.addrs.remove(session)
2019-01-22 01:30:42 +01:00
return
2019-02-28 04:01:03 +01:00
if not dframe.Seq == session.next_expected_seq:
logger.error("%s unexpected SEQ. Got %s, expected %s", addr, dframe.Seq, session.next_expected_seq)
#return
2019-01-22 10:35:15 +01:00
if dframe.Control & DFrame.PACKET_CONTROL_KEEPALIVE_OR_CORRELATE:
2019-02-05 22:33:25 +01:00
pass
#session.send_dframe_keepalive()
2019-01-22 01:30:42 +01:00
2019-02-15 01:49:34 +01:00
if dframe.Command & DFrame.PACKET_COMMAND_POLL:
#logger.debug("Sending SACK")
2019-02-19 01:01:31 +01:00
session.send_cframe_sack()
2019-02-15 01:49:34 +01:00
pass
2019-02-19 01:01:31 +01:00
if session.next_expected_seq == 255:
session.next_expected_seq = 0
else:
session.next_expected_seq += 1
2019-01-22 01:30:42 +01:00
if dframe.Payload:
await self.handle_game_packet(session, dframe.Payload)
2019-01-22 01:30:42 +01:00
else:
pass
except Exception as e:
logger.error("Could not parse DFRAME: ")
traceback.print_exc()
return
async def handle_game_packet(self, session, payload):
2019-02-05 01:35:22 +01:00
player = self.get_temp_player(session)
2019-02-15 01:49:34 +01:00
logger.debug("%s:%s > %s (GAMEDATA)", session.ip, session.port, payload.hex())
#logger.debug("OPCODE: %s VALUES: %s", struct.pack("<B", payload[0]).hex(), payload[1:].hex())
#await self.server.broadcast_message("SENT OPCODE: %s VALUES: %s" % (struct.pack("<B", payload[0]).hex(), payload[1:].hex()))
2019-02-05 01:35:22 +01:00
2019-03-13 01:37:49 +01:00
event_player = self.get_player(session)
if event_player:
await self.server.broadcast_event("on_player_gamedata", player, payload)
2019-01-22 01:30:42 +01:00
if payload[0] == 0xc1:
2019-02-05 01:35:22 +01:00
if not player or not player.phase == PlayerPhases.CFRAME_CONNECTED:
logger.error("Fuck you. Playerphase was %s", player.phase)
return
# DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO_EX
ppayload = Packet(payload)
dwPacketType = ppayload.getULong()
dwFlags = ppayload.getULong()
dwDNETVersion = ppayload.getULong()
dwNameOffset = ppayload.getULong()
dwNameSize = ppayload.getULong()
dwDataOffset = ppayload.getULong()
dwDataSize = ppayload.getULong()
dwPasswordOffset = ppayload.getULong()
dwPasswordSize = ppayload.getULong()
dwConnectDataOffset = ppayload.getULong()
dwConnectDataSize = ppayload.getULong()
dwURLOffset = ppayload.getULong()
dwURLSize = ppayload.getULong()
guidInstance = payload[52:68]
guidApplication = payload[68:84]
2019-02-15 01:49:34 +01:00
name = payload[dwNameOffset+4:dwNameOffset+2+dwNameSize]
2019-02-05 01:35:22 +01:00
name = name.decode("utf-16-le")
2019-01-22 01:30:42 +01:00
logger.debug("%s connected!", name)
2019-02-05 01:35:22 +01:00
player.name = name
2019-02-05 22:33:25 +01:00
#session.send_dframe_keepalive()
2019-01-22 01:30:42 +01:00
2019-02-05 01:35:22 +01:00
# Response: DN_MSG_INTERNAL_SEND_CONNECT_INFO
d = DN_MSG_INTERNAl_SEND_CONNECT_INFO(self, player)
d.Seq = session.next_send
d.NRcv = session.next_expected_seq
appdata = Packet()
appdata.putByte(0xff) # map ID
appdata.putByte(self.server.game_type) # game type
appdata.putByte(self.server.teams) # teams
2019-02-05 22:33:25 +01:00
appdata.write(b'\x00') # original: 0x00 - does not seem to affect client
2019-02-05 01:35:22 +01:00
appdata.putShort(int(self.server.version * 1000))
appdata.write(b'\x03\x90') # original: 0292 - does not seem to affect client
appdata.putShort(self.server.points_per_capture)
appdata.putShort(self.server.points_per_kill)
2019-02-05 22:33:25 +01:00
appdata.write(b'\x00\x00') # original: 0000 - does not seem to affect client
2019-02-05 01:35:22 +01:00
appdata.putShort(self.server.detente_time)
2019-02-19 01:01:31 +01:00
appdata.write(self.server.currentmap.checksum) # Seems to be a checksum of current map OR linked to the number of chars in the map name
2019-02-05 01:35:22 +01:00
appdata.write(self.server.currentmap.mapname.encode("ascii"))
appdata.write(b'\x00' * (32 - len(self.server.currentmap.mapname)))
d.ApplicationReservedData = appdata.getvalue()
session.send(d)
2019-02-19 01:01:31 +01:00
logger.debug("%s:%s > %s (GAMEDATA)", session.ip, session.port, appdata.getvalue().hex())
2019-02-05 01:35:22 +01:00
player.phase = PlayerPhases.DN_SEND_CONNECT_INFO
2019-01-22 01:30:42 +01:00
2019-01-22 10:35:15 +01:00
elif payload[0] == 0xc3:
# CMESG_CONNECTED
2019-02-05 01:35:22 +01:00
player = self.get_temp_player(session)
if not player or not player.phase == PlayerPhases.DN_SEND_CONNECT_INFO:
2019-01-22 15:11:47 +01:00
return
2019-02-05 01:35:22 +01:00
player.phase = PlayerPhases.DN_ACK_CONNECT_INFO
await self.server.add_player(player)
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b'\x3c'+struct.pack("<L", player.id)+b"\x00", acknow=True)
2019-02-05 01:35:22 +01:00
await player.session.send_gamedata(self.build_players_dpid(player))
await self.server.broadcast_gamedata_except(player, self.build_players_dpid())
2019-02-05 01:35:22 +01:00
2019-02-28 04:01:03 +01:00
plid=0
for pplayer in self.server.players:
await self.server.broadcast_gamedata(b'\x3d'+struct.pack("<B", plid)+pplayer.name.encode("ascii")+b"\x00"*(33-len(pplayer.name.encode("ascii")))) # playername
2019-02-28 04:01:03 +01:00
plid+=1
2019-02-15 01:49:34 +01:00
elif payload[0] == 0x0f:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
inc = Packet(payload)
opcode = inc.getByte()
playerid = inc.getULong()
2019-02-28 04:01:03 +01:00
status = inc.getULong()
padding = inc.getShort()
2019-02-15 01:49:34 +01:00
out = Packet()
out.putByte(opcode)
out.putULong(playerid)
2019-02-28 04:01:03 +01:00
out.putULong(status)
out.putShort(padding)
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(out.getvalue(), acknow=False)
2019-02-19 01:01:31 +01:00
elif payload[0] == 0x2c: # CMSG_CHANGE_TEAM: team (byte), player id (int), unknown (byte)
2019-02-15 01:49:34 +01:00
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
2019-02-19 01:01:31 +01:00
p = Packet(payload)
opcode = p.getByte()
team = p.getByte()
playerid = p.getULong()
unknown = p.getByte()
if player.team > 3 >= team % 128:
# ignore
logger.info("%s tried to change team to %s but failed" % (player.name, team))
else:
await player.change_team(team % 128)
2019-02-19 01:01:31 +01:00
elif payload[0] == 0x35: # CMSG_SEND_CHAT_MESSAGE: messagetype? (byte), team? (byte), message (string)
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
p = Packet(payload)
opcode = p.getByte()
type = p.getByte()
team = p.getByte()
message = p.read().decode("utf8").replace("\x00", "")
await self.server.broadcast_event("on_player_chat", player, type, team, message)
2019-02-15 01:49:34 +01:00
elif payload[0] == 0x2a:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
# winning condition time limit
time_limit_minutes = 60 # 1 hour
await player.session.send_gamedata(b"\x29"+struct.pack("<H", time_limit_minutes)+b"\x00\x80"+(b"\x00"*17), acknow=True)
await player.session.send_gamedata(b"\x39"+(b"\x00"*21), acknow=False) # acknow=True
await player.session.send_gamedata(b"\x39\x01\x01\x01\x01"+(b"\x00"*17), acknow=True)
elif payload[0] == 0x0c:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
playerping = 1
2019-02-19 01:01:31 +01:00
await player.session.send_gamedata(b"\x2f\x02\x00\x00" + struct.pack("<H", playerping) + b"\x00", acknow=False) # acknow=True
2019-03-13 01:37:49 +01:00
# TODO: Understand this vv SPAWN TREES vv
#for i in range(0x06):
# ack = (i % 2 == 0)
# await player.session.send_gamedata(b"\x04" + struct.pack("<B", i) + b"\x00\x00\x00\x00" + struct.pack("<B", i+0x07) + b"\x00\x00\x00",acknow=ack)
await player.session.send_gamedata(b"\x04" + struct.pack("<B", 0) + b"\x00\x00\x00\x00" + struct.pack("<B", 0 + 0x07) + b"\x00\x00\x00")
2019-02-19 01:01:31 +01:00
2019-02-28 04:01:03 +01:00
# needed to avoid crash after Loading textures
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x1a\xee\x02\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x83\x5f\xa4\x44\x00\xf9\xa3\xc2\x00\x00\x00\x00\xff\xff\x33\x43\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x9e\xdc\x07\x00", acknow=True)
2019-02-28 04:01:03 +01:00
# has something to do with the base build
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x19\x96\x03\x00\x10\x00\x00\x04\x00\x0d\x00\x00\x83\x5f\xa4\x44\x00\xf9\xa3\xc2\x00\x00\x00\x00\xff\xff\x33\x43\x00\x00\x7a\x44\x00\x00\x00\x00\x00\x00\x00\x00\x00", acknow=False)
2019-02-28 04:01:03 +01:00
# useless?
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x39"+b"\x00"*21, acknow=True)
2019-02-28 04:01:03 +01:00
# useless? can be game status
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x39"+b"\x01"*4+b"\x00"*17, acknow=False)
2019-02-28 04:01:03 +01:00
# useless? can be game status
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x2e"+b"\x00"*9, acknow=True)
2019-02-28 04:01:03 +01:00
# useless? can be game status
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x3b\x00\x04\x00\x00\x00\x04\x00\x00\x00", acknow=False)
2019-02-28 04:01:03 +01:00
# useless?
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x3e\x00\x00\x00\x00\x00", acknow=True)
2019-02-28 04:01:03 +01:00
# useless?
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x3f\x00\x00", acknow=False)
2019-02-28 04:01:03 +01:00
# stuck in awaiting snapshot if not sent (could be the packet to go ingame?)
2019-02-15 01:49:34 +01:00
await player.session.send_gamedata(b"\x0e\x00", acknow=True)
2019-02-28 04:01:03 +01:00
# change team packet
2019-02-19 01:01:31 +01:00
await player.session.send_gamedata(b"\x10" + struct.pack("<B", player.team) + struct.pack("<L", player.id) + b"\x00\x00", acknow=False)
2019-02-28 04:01:03 +01:00
# spawn server as mecc
#await player.session.send_gamedata(bytes.fromhex("05007300000200000000"), acknow=False)
#await player.session.send_gamedata(bytes.fromhex("0a320000000073000001020000000000000000000000000000000000000000000000000000000000000000000000000000000a120000000073000003020000000000000000"), acknow=False)
2019-02-28 04:01:03 +01:00
# spawn Bot 1 as kab
#await player.session.send_gamedata(bytes.fromhex("05017400000100000000"), acknow=False)
#await player.session.send_gamedata(bytes.fromhex("0a320000000174000001010000000000000000000000000000000000000000000000000000000000000000000000000000000a120000000174000003020000000000000000"), acknow=False)
# spawn Bot 2 as reap
#await player.session.send_gamedata(bytes.fromhex("05027500000300000000"), acknow=False)
#await player.session.send_gamedata(bytes.fromhex("0a320000000275000001030000000000000000000000000000000000000000000000000000000000000000000000000000000a120000000275000003020000000000000000"), acknow=False)
# TODO: spawn all players except himself
await self.spawn_all_players_except(player)
2019-02-28 04:01:03 +01:00
# spawn Bot 3 as <id>
objid = "75"
plix = "01"
model = "4b"
2019-03-05 01:30:20 +01:00
#async def spawn(objid, plix, model):
# await player.session.send_gamedata(bytes.fromhex("05"+plix+objid+"0000"+model+"00000000"), acknow=False)
# await player.session.send_gamedata(bytes.fromhex("0a32000000"+plix+objid+"000001"+model+"0000000000000000000000000000000000000000000000000000000000000000000000000000000a12000000"+plix+objid+"0000"+model+"020000000000000000"), acknow=False)
#await spawn(objid, plix, model)
2019-02-28 04:01:03 +01:00
# makes one player change team
#await player.session.send_gamedata(bytes.fromhex("0f1c49e2450e0000000000"), acknow=False)
#await player.session.send_gamedata(bytes.fromhex("0f1e"+struct.pack("<L", player.id)+"0000000000"), acknow=False)
#await player.session.send_gamedata(bytes.fromhex("0f1e123456780000000000"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("090f000000015500000a02000000ff090f000000015500000a0200000001090f000000015500000a020000000300"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("2b0000000066fe954400"), acknow=False)
2019-03-13 01:37:49 +01:00
await self.server.broadcast_event("on_player_spawn", player)
2019-02-28 04:01:03 +01:00
player.phase = PlayerPhases.INGAME
2019-02-19 01:01:31 +01:00
elif payload[0] == 0x0a and payload[1] == 0x32:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
p = Packet(payload)
2019-03-05 03:58:01 +01:00
opcode = p.getByte()
length = p.getByte() # length?
unknown1 = p.getByte()
unknown2 = p.getByte()
unknown3 = p.getByte()
playerindex = p.getByte()
objid = p.getShort()
unknown5 = p.getByte()
unknown6 = p.getByte()
model = p.getULong()
2019-02-19 01:01:31 +01:00
playerx = p.getFloat()
playery = p.getFloat()
playerz = p.getFloat()
unknown4 = p.getULong()
unknown5 = p.getULong()
unknown6 = p.getULong()
unknown7 = p.getULong()
2019-03-05 03:58:01 +01:00
orientation = p.getULong() # orientation
2019-02-19 01:01:31 +01:00
unknown9 = p.getULong()
player.x = playerx
player.y = playery
player.z = playerz
2019-03-05 03:58:01 +01:00
player.oid = objid
player.o = orientation
await self.server.broadcast_gamedata_except(player, payload)
2019-03-05 03:58:01 +01:00
2019-02-28 04:01:03 +01:00
elif payload[0] == 0x3a:
# CLIENT EXITED
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
await self.server.remove_player(player)
await self.server.broadcast_gamedata(self.build_players_dpid())
plid = 0
for pplayer in self.server.players:
await self.server.broadcast_gamedata(b'\x3d' + struct.pack("<B", plid) + pplayer.name.encode("ascii") + b"\x00" * (33 - len(pplayer.name.encode("ascii"))), acknow=False) # playername
plid += 1
await self.server.broadcast_event("on_player_left", player)
2019-03-05 01:49:13 +01:00
elif payload[0] == 0x4d:
# CLIENT DOWNLOAD MAP
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
payload = Packet()
payload.putByte(0x4e) # packet opcode
#payload.putByte(0x9d) # packet opcode?
mapsize = os.stat("maps/"+self.server.currentmap.mappath).st_size
payload.putULong(mapsize) # map size
payload.putBytes((self.server.currentmap.mapname+"\x00"*(128-len(self.server.currentmap.mapname))).encode("ascii"))
await player.session.send_gamedata(payload.getvalue())
with open("maps/"+self.server.currentmap.mappath, "rb") as fh:
data = fh.read(512)
while data != b"":
if len(data) == 512:
# full packet
mapdl = Packet()
mapdl.putByte(0x4f) # opcode
mapdl.putByte(0x00) # unknown
mapdl.putByte(0x02) # unknown
mapdl.putByte(0x00) # unknown
mapdl.putByte(0x00) # unknown
mapdl.putBytes(data)
mapdl.putByte(0x00) # ending
await player.session.send_gamedata(mapdl.getvalue(), acknow=False) # map chunk
else:
# last packet
mapdl = Packet()
mapdl.putByte(0x4f) # opcode
mapdl.putULong(len(data)) # map end length
mapdl.putBytes(data)
# fill with crap at the end
mapdl.putBytes(b"\x00"*(512-len(data)))
mapdl.putByte(0x00) # ending
await player.session.send_gamedata(mapdl.getvalue(), acknow=False) # map chunk
data = fh.read(512)
elif payload[0] == 0x0a and payload[1] == 0x12:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
p = Packet(payload)
opcode = p.getByte()
length = p.getByte() # length?
unknown1 = p.getByte()
unknown2 = p.getByte()
unknown3 = p.getByte()
playerindex = p.getByte()
objid = p.getShort()
unknown5 = p.getByte()
unknown6 = p.getByte()
model = p.getULong()
direction = p.getShort()
orientation = p.getShort()
if not direction == 0:
if player.opts["debug"]:
await player.send_message("ObjID %s went: %s, o: %s" % (objid, direction, orientation))
2019-03-13 01:37:49 +01:00
# TODO: Replace with MSG_MOVE_PLAYER
await self.server.broadcast_gamedata_except(player, payload)
2019-03-13 01:37:49 +01:00
elif payload[0] == 0x05: # SPAWN OBJECT
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
p = Packet(payload)
opcode = p.getByte()
2019-03-13 01:37:49 +01:00
plix = p.getByte() # length?
oid = p.getShort()
unknown = p.getByte()
model = p.getULong()
2019-03-13 01:37:49 +01:00
player.oid = oid
2019-03-13 01:37:49 +01:00
await self.server.broadcast_gamedata_except(player, payload)
await player.send_message("SPAWN: %s" % payload.hex())
elif payload[0] == 0x09 and payload[1] == 0x23:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
await player.send_message("PROJECTILE SHOOT: %s" % payload.hex())
await self.server.broadcast_gamedata_except(player, payload)
2019-03-14 01:49:16 +01:00
await self.server.broadcast_event("on_player_shoot", player, payload)
2019-03-13 01:37:49 +01:00
elif payload[0] == 0x09 and payload[1] == 0x1a:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
await player.send_message("PROJECTILE EXPLOSION: %s" % payload.hex())
2019-03-14 01:49:16 +01:00
await self.server.broadcast_gamedata_except(player, payload)
oid = struct.unpack("<H", payload[6:8])[0]
2019-03-13 01:37:49 +01:00
x = struct.unpack("<f", payload[14:18])[0]
y = struct.unpack("<f", payload[18:22])[0]
z = struct.unpack("<f", payload[22:26])[0]
2019-03-14 01:49:16 +01:00
#await self.server.broadcast_gamedata(MSG_BULLET_EXPLOSION(player, oid, x, y, z).to_packet())
# POPUP
#popupoid = self.server.new_oid()
#type = 12 # maybe
#await self.server.broadcast_gamedata(MSG_NEW_PROJECTILE(player, popupoid, type, x, y, z, 0).to_packet())
#await self.server.broadcast_gamedata(bytes.fromhex("09160000000438000002250000000a00000004070000091300000004070000110200000004"+ struct.pack("<H", popupoid).hex() +"000a090e00000004380000032500000000")) # put popup on backpack
#await self.server.broadcast_gamedata(MSG_PLAYER_DROP_BACKPACK(player).to_packet()) # drop popup
#await self.server.broadcast_gamedata(MSG_BULLET_EXPLOSION(player, popupoid, x, y, z, True).to_packet()) # explode popup
#await self.server.broadcast_gamedata(MSG_PLAYER_USE_BACKPACK(player, 0x16).to_packet()) # use popup
2019-03-13 01:37:49 +01:00
elif payload[0] == 0x09 and payload[1] == 0x0f:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
p = Packet(payload)
opcode = p.getByte()
2019-03-13 01:37:49 +01:00
opcode2 = p.getByte()
unknown = p.getByte()
2019-03-13 01:37:49 +01:00
unknown = p.getShort()
player_index = p.getByte()
unknown = p.getShort()
unknown = p.getByte()
unknown = p.getByte()
unknown = p.getULong()
weapon = p.getByte()
await player.send_message("SWITCH WEAPON: %s" % payload.hex())
await self.server.broadcast_gamedata_except(player, MSG_SWITCH_WEAPON(player, weapon).to_packet())
elif payload[0] == 0x0b: # MESSAGE_HIT
# 0b380437000004070000d48325c32dea66c2c0a0cf429fea0442091a00000004370000034d000000d48325c32dea66c2c0a0cf4200
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
2019-03-14 01:49:16 +01:00
await self.server.broadcast_event("on_player_hit", player, None, None)
2019-03-13 01:37:49 +01:00
await player.send_message("HIT: %s" % payload.hex())
2019-03-14 01:49:16 +01:00
else:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
if player.opts["debug"]:
await player.send_message("Unknown: %s" % payload.hex())
await self.server.broadcast_gamedata_except(player, payload)
2019-01-22 01:30:42 +01:00
def build_players_dpid(self, exceptplayer=False):
p1 = b'\x01' + struct.pack("<B", len(self.server.players))
for pplayer in self.server.players:
p1 += struct.pack("<L", pplayer.id)
for pplayer in self.server.players:
if pplayer.name == "[Server]":
continue
p1 += b'\x0e'
p1 += b"\x01\x00"
for pplayer in self.server.players:
if pplayer == exceptplayer or pplayer.name == "[Server]":
continue
p1 += struct.pack("<B", pplayer.team)
p1 += b'\x00\x00'
return p1
async def spawn_player_except(self, player):
plix = self.server.get_player_index(player)
p = Packet()
p.putByte(0x05) # opcode
p.putByte(plix)
p.putShort(player.oid)
p.putByte(0x00)
p.putULong(2) # 2: mecc
p.putByte(0x00)
await self.server.broadcast_gamedata_except(player, p.getvalue())
async def spawn_all_players_except(self, player):
for pplayer in self.server.players:
if pplayer.name == "[Server]" or pplayer == player:
continue
plix = self.server.get_player_index(pplayer)
p = Packet()
p.putByte(0x05) # opcode
p.putByte(plix)
p.putShort(pplayer.oid)
p.putByte(0x00)
p.putULong(2) # 2: mecc
p.putByte(0x00)
await player.session.send_gamedata(p.getvalue())
2019-01-22 01:30:42 +01:00
def send_packet(self, addr, packet):
self.remotesocket.sendto(packet.getvalue(), addr)
2019-02-15 01:49:34 +01:00
#logger.debug("%s:%s < %s", addr[0], addr[1], packet.getvalue().hex())
2019-01-22 01:30:42 +01:00
def get_session(self, iporaddr, port=None):
if not port:
return next((x for x in self.addrs if x.ip == iporaddr[0] and x.port == iporaddr[1]), None)
else:
return next((x for x in self.addrs if x.ip == iporaddr and x.port == port), None)
2019-01-22 10:35:15 +01:00
def get_player(self, iporaddr, port=None):
if type(iporaddr) == Session:
2019-02-05 01:35:22 +01:00
a = next((x for x in self.server.players if x.session.ip == iporaddr.ip and x.session.port == iporaddr.port), None)
if a:
return a
else:
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr.ip and x.session.port == iporaddr.port),None)
if not port:
a = next((x for x in self.server.players if x.session.ip == iporaddr[0] and x.session.port == iporaddr[1]), None)
if a:
return a
else:
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr[0] and x.session.port == iporaddr[1]),None)
else:
a = next((x for x in self.server.players if x.session.ip == iporaddr and x.session.port == port), None)
if a:
return a
else:
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr and x.session.port == port), None)
def get_temp_player(self, iporaddr, port=None):
if type(iporaddr) == Session:
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr.ip and x.session.port == iporaddr.port), None)
2019-01-22 10:35:15 +01:00
if not port:
2019-02-05 01:35:22 +01:00
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr[0] and x.session.port == iporaddr[1]), None)
2019-01-22 10:35:15 +01:00
else:
2019-02-05 01:35:22 +01:00
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr and x.session.port == port), None)