Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

18 changed files with 2032 additions and 40 deletions

View File

@ -0,0 +1,179 @@
from .packet import Packet
from .DFrame import DFrame
from giants import APPLICATION_GUID
class DN_NAMETABLE_ENTRY_INFO:
def __init__(self):
self.dpnid = 0
self.dpnidOwner = 0
self.dwFlags = 0
self.dwVersion = 2
self.dwVersionNotUsed = 0
self.dwDNETVersion = 7
self.dwNameOffset = 0
self.dwNameSize = 0
self.dwDataOffset = 0
self.dwDataSize = 0
self.dwURLOffset = 0
self.dwURLSize = 0
self.URL = b''
self.Data = b''
self.Name = b''
def to_packet(self):
p = Packet()
p.putULong(self.dpnid)
p.putULong(self.dpnidOwner)
p.putULong(self.dwFlags)
p.putULong(self.dwVersion)
p.putULong(self.dwVersionNotUsed)
p.putULong(self.dwDNETVersion)
p.putULong(self.dwNameOffset)
p.putULong(self.dwNameSize)
p.putULong(self.dwDataOffset)
p.putULong(self.dwDataSize)
p.putULong(self.dwURLOffset)
p.putULong(self.dwURLSize)
return p
class DN_MSG_INTERNAl_SEND_CONNECT_INFO(DFrame):
dwPacketType = 0xc2
def __init__(self, netserver, newplayer, packet=None):
super().__init__(packet)
self.Command = DFrame.PACKET_COMMAND_DATA | DFrame.PACKET_COMMAND_RELIABLE | DFrame.PACKET_COMMAND_SEQUENTIAL | DFrame.PACKET_COMMAND_POLL | DFrame.PACKET_COMMAND_NEW_MSG | DFrame.PACKET_COMMAND_END_MSG | DFrame.PACKET_COMMAND_USER_1
self.Control = 0x00
self.netserver = netserver
self.newplayer = newplayer
self.dwPacketType = DN_MSG_INTERNAl_SEND_CONNECT_INFO.dwPacketType
self.dwReplyOffset = 0
self.dwReplySize = 0
self.dwSize = 0x50
self.dwFlags = 0x41
self.dwMaxPlayers = netserver.server.maxplayers
self.dwCurrentPlayers = len(netserver.server.players) + len(netserver.server.tempplayers)
self.dwSessionNameOffset = 0
self.dwSessionNameSize = len((netserver.server.name+"\x00").encode("utf-16-le"))
self.dwPasswordOffset = 0
self.dwPasswordSize = 0
self.dwReservedDataOffset = 0
self.dwReservedDataSize = 0
self.dwApplicationReservedDataOffset = 0
self.dwApplicationReservedDataSize = 0
self.guidInstance = netserver.guid
self.guidApplication = APPLICATION_GUID
self.dpnid = newplayer.id
self.dwVersion = 3
self.dwVersionNotUsed = 0
self.dwEntryCount = 0
self.dwMembershipCount = 0
self.DN_NameTable_Entry_Info = []
self.DN_NameTable_Membership_Info = []
self.ApplicationReservedData = b''
self.ReservedData = b''
self.Password = b''
self.SessionName = (netserver.server.name+"\x00").encode("utf-16-le")
self.Reply = b''
if packet:
self.parse(packet)
def parse(self, packet):
pass
def to_packet(self):
var = 108
s = self.netserver.server.players[0]
entry_server = DN_NAMETABLE_ENTRY_INFO()
self.DN_NameTable_Entry_Info.append(entry_server)
p = self.newplayer
entry_player = DN_NAMETABLE_ENTRY_INFO()
self.DN_NameTable_Entry_Info.append(entry_player)
self.dwEntryCount = len(self.DN_NameTable_Entry_Info)
self.dwMembershipCount = len(self.DN_NameTable_Membership_Info)
entry_server.dpnid = s.id
entry_server.dwFlags = 0x0402
entry_server.dwVersion = 2
entry_player.dpnid = p.id
entry_player.dwFlags = 0x0200
entry_player.Name = p.name.encode("utf-16-le")
entry_player.dwVersion = 3
var = var + self.dwEntryCount * 48 # 48=size of DN_NAMETABLE_ENTRY_INFO
entry_player.dwNameOffset = var
entry_player.dwNameSize = len(entry_player.Name)
var += entry_player.dwNameSize
if self.ApplicationReservedData:
self.dwApplicationReservedDataSize = len(self.ApplicationReservedData)
self.dwApplicationReservedDataOffset = var
var += self.dwApplicationReservedDataSize
if self.ReservedData:
self.dwReservedDataSize = len(self.ReservedData)
self.dwReservedDataOffset = var
var += self.dwReservedDataSize
if self.Password:
self.dwPasswordSize = len(self.Password)
self.dwPasswordOffset = var
var += self.dwPasswordSize
if self.SessionName:
self.dwSessionNameSize = len(self.SessionName)
self.dwSessionNameOffset = var
var += self.dwSessionNameSize
if self.Reply:
self.dwReplySize = len(self.Reply)
self.dwReplyOffset = var
var += self.dwReplySize
packet = Packet()
packet.putULong(self.dwPacketType)
packet.putULong(self.dwReplyOffset)
packet.putULong(self.dwReplySize)
packet.putULong(self.dwSize)
packet.putULong(self.dwFlags)
packet.putLong(self.dwMaxPlayers)
packet.putULong(self.dwCurrentPlayers)
packet.putULong(self.dwSessionNameOffset)
packet.putULong(self.dwSessionNameSize)
packet.putULong(self.dwPasswordOffset)
packet.putULong(self.dwPasswordSize)
packet.putULong(self.dwReservedDataOffset)
packet.putULong(self.dwReservedDataSize)
packet.putULong(self.dwApplicationReservedDataOffset)
packet.putULong(self.dwApplicationReservedDataSize)
packet.putBytes(self.guidInstance)
packet.putBytes(self.guidApplication)
packet.putULong(self.dpnid)
packet.putULong(self.dwVersion)
packet.putULong(self.dwVersionNotUsed)
packet.putULong(self.dwEntryCount)
packet.putULong(self.dwMembershipCount)
for entry in self.DN_NameTable_Entry_Info:
packet.putBytes(entry.to_packet().getvalue())
for entry in self.DN_NameTable_Membership_Info:
packet.putBytes(entry.to_packet().getvalue())
if entry_player.URL:
packet.putBytes(entry_player.URL)
if entry_player.Data:
packet.putBytes(entry_player.Data)
if entry_player.Name:
packet.putBytes(entry_player.Name)
packet.putBytes(self.ApplicationReservedData)
packet.putBytes(self.ReservedData)
packet.putBytes(self.Password)
packet.putBytes(self.SessionName)
packet.putBytes(self.Reply)
self.Payload = packet.getvalue()
return super().to_packet()

258
dpnet/gamepackets.py Normal file
View File

@ -0,0 +1,258 @@
from dpnet.packet import Packet
class MSG_CHANGE_TEAM():
def __init__(self, player, teamid: int, respawn: bool = True):
self.player = player
self.teamid = teamid
self.respawn = respawn
if self.teamid > 255:
raise ValueError("Team ID must be < 256")
def to_packet(self):
p = Packet()
p.putByte(0x10) # opcode
p.putByte(self.teamid)
p.putULong(self.player.id)
p.putByte(self.respawn)
p.putByte(0x00) # end
return p.getvalue()
class MSG_MOVE_PLAYER():
def __init__(self, player, direction, orientation):
self.player = player
self.direction = direction
self.orientation = orientation
def to_packet(self):
p = Packet()
p.putByte(0x0f) # opcode
p.putByte(0x12) # opcode2?
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(self.player) # player index
p.putShort(self.player.oid)
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putULong(self.player.model())
p.putShort(self.direction)
p.putShort(self.orientation)
p.putByte(0x00) # end
return p.getvalue()
class MSG_PLAYER_PING():
def __init__(self, player, ping):
self.player = player
self.ping = ping
def to_packet(self):
p = Packet()
p.putByte(0x2f) # opcode
p.putByte(self.player.get_index())
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putShort(self.ping) # ping
p.putByte(0x00) # end
return p.getvalue()
class MSG_SWITCH_WEAPON():
def __init__(self, player, newweapon):
self.player = player
self.newweapon = newweapon
def to_packet(self):
p = Packet()
p.putByte(0x09) # opcode
p.putByte(0x0f) # opcode2?
p.putByte(0x00)
p.putShort(0x0000)
p.putByte(self.player.get_index())
p.putShort(self.player.oid)
p.putByte(0x00)
p.putByte(0x0a)
p.putULong(0x02)
p.putByte(self.newweapon)
p.putByte(0x00) # end
return p.getvalue()
class MSG_DROP_WEAPON():
def __init__(self, player, oid, weapon, ammo):
self.player = player
self.weapon = weapon
self.ammo = ammo
self.oid = oid
def to_packet(self):
p = Packet()
p.putByte(0x20) # opcode
# TODO: fixit
p.putShort(0x012e) # can also be 1303 ... oid? my oid was 7 at the time
#p.putShort(self.oid) # can also be 1303 ... oid? my oid was 7 at the time
p.putShort(0x0000)
p.putByte(self.player.get_index())
p.putByte(0x38)
p.putShort(0x0000)
p.putFloat(self.player.x)
p.putFloat(self.player.y)
p.putFloat(self.player.z)
p.putFloat(self.player.o)
p.putFloat(0xc79e28c1) # ??
p.putShort(0x0000)
p.putULong(0x70410000)
p.putByte(self.weapon)
p.putByte(0x00)
p.putULong(self.ammo)
p.putShort(0x0000)
p.putShort(0x0000)
p.putByte(0x00)
return p.getvalue()
class MSG_NEW_PROJECTILE():
def __init__(self, player, oid, projectiletype, x, y, z, o):
self.player = player
self.x = x
self.y = y
self.z = z
self.o = o
self.projectiletype = projectiletype
self.oid = oid
def to_packet(self):
p = Packet()
p.putByte(0x09) # opcode
p.putULong(0x23)
p.putByte(self.player.get_index())
p.putShort(self.player.oid)
p.putByte(0x00)
p.putByte(0x10)
p.putULong(0x02)
p.putFloat(self.x)
p.putFloat(self.y)
p.putFloat(self.z)
p.putULong(self.o)
p.putShort(self.oid)
p.putShort(0x00)
p.putByte(self.projectiletype)
p.putByte(0x00)
return p.getvalue()
class MSG_BULLET_EXPLOSION():
def __init__(self, player, oid, x, y, z, isbackpackitem):
self.player = player
self.oid = oid
self.x = x
self.y = y
self.z = z
self.isbackpackitem = isbackpackitem
def to_packet(self):
p = Packet()
p.putByte(0x09) # opcode
p.putULong(0x1a)
p.putByte(self.player.get_index())
p.putShort(self.oid)
p.putByte(0x00)
p.putByte(0x03) if not self.isbackpackitem else p.putByte(0x01) # 0x01 for backpackitem, 0x03 for anything else?
p.putULong(0x4d)
p.putFloat(self.x)
p.putFloat(self.y)
p.putFloat(self.z)
p.putByte(0x00)
return p.getvalue()
class MSG_PLAYER_DIE():
def __init__(self, player, deathtype):
self.player = player
self.deathtype = deathtype
def to_packet(self):
p = Packet()
p.putByte(0x0b) # opcode
p.putULong(0x1a)
p.putByte(0x00)
p.putByte(self.player.get_index())
p.putByte(0x07)
p.putShort(0x0000)
p.putFloat(self.player.x)
p.putFloat(self.player.y)
p.putFloat(self.player.z)
p.putFloat(self.player.o)
p.putShort(0x1409) # oid?
p.putByte(0x00)
p.putShort(0x0000)
p.putByte(self.player.get_index())
p.putByte(0x07)
p.putShort(0x0000)
p.putByte(0x04)
p.putByte(0x02)
p.putShort(0x0000)
p.putByte(0x00)
p.putULong(self.deathtype)
p.putByte(0x00)
p.putByte(0x00) # end
return p.getvalue()
class MSG_PLAYER_TELEPORT():
def __init__(self, player, newx, newy, newz, newo):
self.player = player
self.newx = newx
self.newy = newy
self.newz = newz
self.newo = newo
def to_packet(self):
p = Packet()
p.putByte(0x13) # opcode
p.putByte(self.player.get_index())
p.putShort(self.player.oid)
p.putByte(0x00)
p.putFloat(0x00000000)
p.putFloat(self.newx)
p.putFloat(self.newy)
p.putFloat(self.newz)
p.putByte(0x00)
return p.getvalue()
class MSG_PLAYER_USE_BACKPACK():
def __init__(self, player, backpackitem):
self.player = player
self.backpackitem = backpackitem
def to_packet(self):
p = Packet()
p.putByte(0x09) # opcode
p.putULong(0x12)
p.putByte(self.player.get_index())
p.putShort(self.player.oid)
p.putByte(0x00)
p.putByte(self.backpackitem) # popup?
p.putULong(0x02)
p.putULong(0x46aae90a) # ?
p.putByte(0x00)
return p.getvalue()
class MSG_PLAYER_DROP_BACKPACK():
def __init__(self, player):
self.player = player
def to_packet(self):
p = Packet()
p.putByte(0x09) # opcode
p.putULong(0x13)
p.putByte(self.player.get_index())
p.putShort(self.player.oid)
p.putByte(0x11)
p.putULong(0x02)
p.putULong(0x3a04)
p.putByte(0x0a)
p.putByte(0x00)
return p.getvalue()

18
dpnet/netclient.py Normal file
View File

@ -0,0 +1,18 @@
from .packet import Packet
from .EnumQuery import EnumQuery
import socket
import threading
class Netclient:
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
def send(self, packet):
self.socket.sendto(packet.getvalue(), (self.ip, self.port))
print("R>", packet.getvalue().hex())
def receive(self):
return self.socket.recv(4096)

View File

@ -1,13 +1,26 @@
import os
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 import GameTypes, Teams
from giants.player import Player, PlayerPhases
import time
from giants import APPLICATION_GUID
import random
from .DN_MSG_INTERNAL_SEND_CONNECT_INFO import DN_MSG_INTERNAl_SEND_CONNECT_INFO
import asyncio
from dpnet.gamepackets import *
logger = setup_logger(__name__)
@ -26,6 +39,36 @@ class Netserver(asyncio.DatagramProtocol):
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):
loop = asyncio.get_event_loop()
loop.create_task(self.handle_new_packet(addr, data))
@ -34,9 +77,9 @@ class Netserver(asyncio.DatagramProtocol):
pPacket = Packet(bData)
first = pPacket.getByte()
if len(bData) >= 4 and first & DFrame.PACKET_COMMAND_DATA:
return
await self.handle_new_dframe(addr, pPacket)
elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME:
return
await self.handle_new_cframe(addr, pPacket)
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)
@ -51,41 +94,719 @@ class Netserver(asyncio.DatagramProtocol):
# 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])
#logger.debug("Got EnumQuery, sending EnumResponse")
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.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("<L", int(self.server.version*1000)) # game version
er.ApplicationReservedData += b'\x02\x92\x05\x00\x01\x00\x00\x00\x00\x00' # Unknown
er.ApplicationReservedData += b'\x9c\x53\xf4\xdd' # Seems to be a checksum of current map
# \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
# \x9f\xb2\x42\xec: test
er.ApplicationReservedData += self.server.currentmap.mapname.encode("ascii")'''
appdata = Packet()
appdata.putByte(0xff) # map ID
appdata.putByte(GameTypes.GTypeNull) # game type
appdata.putByte(Teams.MvRvK) # teams
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(version * 1000))
appdata.putShort(int(self.server.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.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(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)))
appdata.putShort(self.server.detente_time)
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
appdata.write(self.server.currentmap.mapname.encode("ascii"))
appdata.write(b'\x00' * (32 - len(self.server.currentmap.mapname)))
er.ApplicationReservedData = appdata.getvalue()
er.ApplicationReservedData += b'\x00' * (32 - len(map_name))
logger.debug("Current map: %s, checksum: %s", self.server.currentmap.mapname, self.server.currentmap.checksum)
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:
logger.debug("%s:%s > %s (CFRAME CONNECT)", addr[0], addr[1], data.getvalue().hex())
# CONNECT CFRAME
if session and session.Full:
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)
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)
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("<L", len(self.server.players)), self.guid[0:4]))[0]
logger.debug("Value for connected player: %s", player.id)
player.phase = PlayerPhases.CFRAME_CONNECT
self.server.create_temp_player(player)
#session.setup_Connect_Retry_Timer()
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECTED:
logger.debug("%s:%s > %s (CFRAME CONNECTED)", addr[0], addr[1], data.getvalue().hex())
# 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()
player.phase = PlayerPhases.CFRAME_CONNECTED
session.send_dframe_keepalive()
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_SACK:
logger.debug("%s:%s > %s (CFRAME SACK)", addr[0], addr[1], data.getvalue().hex())
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
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)
if not sack_sent:
session.send_cframe_sack()
sack_sent = True
# release lock for new packet to be sent
if session.lock.locked():
session.lock.release()
# 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
#logger.debug("Got a CFrame POLL. Replying with a SACK.")
# must send back a ACK
session.send_cframe_sack()
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):
try:
dframe = DFrame(data)
#logger.debug("Received DFRAME")
session = next((x for x in self.addrs if x.ip == addr[0] and x.port == addr[1]), None)
if not session and not dframe.Control & DFrame.PACKET_CONTROL_END_STREAM:
logger.debug("%s sent a DFRAME without having sent a CONNECT before. GTFO.", addr)
logger.debug("%s:%s > %s (DFRAME)", addr[0], addr[1], data.getvalue().hex())
return
if dframe.Control & DFrame.PACKET_CONTROL_END_STREAM:
logger.debug("%s:%s > %s (END_STREAM)", addr[0], addr[1], data.getvalue().hex())
if not session:
logger.error("Received a END STREAM packet for a non fully connected session")
session = Session(self.server, self.remotesocket)
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()
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
logger.debug(" %s:%s < %s (END_STREAM)", session.ip, session.port, resp.to_packet().getvalue().hex())
session.send(resp)
# TODO: broadcast session has disconnected
if self.get_session(addr):
self.addrs.remove(session)
return
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
if dframe.Control & DFrame.PACKET_CONTROL_KEEPALIVE_OR_CORRELATE:
pass
#session.send_dframe_keepalive()
if dframe.Command & DFrame.PACKET_COMMAND_POLL:
#logger.debug("Sending SACK")
session.send_cframe_sack()
pass
if session.next_expected_seq == 255:
session.next_expected_seq = 0
else:
session.next_expected_seq += 1
if dframe.Payload:
await self.handle_game_packet(session, dframe.Payload)
else:
pass
except Exception as e:
logger.error("Could not parse DFRAME: ")
traceback.print_exc()
return
async def handle_game_packet(self, session, payload):
player = self.get_temp_player(session)
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()))
event_player = self.get_player(session)
if event_player:
await self.server.broadcast_event("on_player_gamedata", player, payload)
if payload[0] == 0xc1:
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]
name = payload[dwNameOffset+4:dwNameOffset+2+dwNameSize]
name = name.decode("utf-16-le")
logger.debug("%s connected!", name)
player.name = name
#session.send_dframe_keepalive()
# 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
appdata.write(b'\x00') # original: 0x00 - does not seem to affect client
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)
appdata.write(b'\x00\x00') # original: 0000 - does not seem to affect client
appdata.putShort(self.server.detente_time)
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
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)
logger.debug("%s:%s > %s (GAMEDATA)", session.ip, session.port, appdata.getvalue().hex())
player.phase = PlayerPhases.DN_SEND_CONNECT_INFO
elif payload[0] == 0xc3:
# CMESG_CONNECTED
player = self.get_temp_player(session)
if not player or not player.phase == PlayerPhases.DN_SEND_CONNECT_INFO:
return
player.phase = PlayerPhases.DN_ACK_CONNECT_INFO
await self.server.add_player(player)
await player.session.send_gamedata(b'\x3c'+struct.pack("<L", player.id)+b"\x00", acknow=True)
await player.session.send_gamedata(self.build_players_dpid(player))
await self.server.broadcast_gamedata_except(player, 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")))) # playername
plid+=1
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()
status = inc.getULong()
padding = inc.getShort()
out = Packet()
out.putByte(opcode)
out.putULong(playerid)
out.putULong(status)
out.putShort(padding)
await player.session.send_gamedata(out.getvalue(), acknow=False)
elif payload[0] == 0x2c: # CMSG_CHANGE_TEAM: team (byte), player id (int), unknown (byte)
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
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)
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)
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
await player.session.send_gamedata(b"\x2f\x02\x00\x00" + struct.pack("<H", playerping) + b"\x00", acknow=False) # acknow=True
# 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")
# needed to avoid crash after Loading textures
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)
# has something to do with the base build
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)
# useless?
await player.session.send_gamedata(b"\x39"+b"\x00"*21, acknow=True)
# useless? can be game status
await player.session.send_gamedata(b"\x39"+b"\x01"*4+b"\x00"*17, acknow=False)
# useless? can be game status
await player.session.send_gamedata(b"\x2e"+b"\x00"*9, acknow=True)
# useless? can be game status
await player.session.send_gamedata(b"\x3b\x00\x04\x00\x00\x00\x04\x00\x00\x00", acknow=False)
# useless?
await player.session.send_gamedata(b"\x3e\x00\x00\x00\x00\x00", acknow=True)
# useless?
await player.session.send_gamedata(b"\x3f\x00\x00", acknow=False)
# stuck in awaiting snapshot if not sent (could be the packet to go ingame?)
await player.session.send_gamedata(b"\x0e\x00", acknow=True)
# change team packet
await player.session.send_gamedata(b"\x10" + struct.pack("<B", player.team) + struct.pack("<L", player.id) + b"\x00\x00", acknow=False)
# spawn server as mecc
#await player.session.send_gamedata(bytes.fromhex("05007300000200000000"), acknow=False)
#await player.session.send_gamedata(bytes.fromhex("0a320000000073000001020000000000000000000000000000000000000000000000000000000000000000000000000000000a120000000073000003020000000000000000"), acknow=False)
# 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)
# spawn Bot 3 as <id>
objid = "75"
plix = "01"
model = "4b"
#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)
# 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)
await self.server.broadcast_event("on_player_spawn", player)
player.phase = PlayerPhases.INGAME
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)
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()
playerx = p.getFloat()
playery = p.getFloat()
playerz = p.getFloat()
unknown4 = p.getULong()
unknown5 = p.getULong()
unknown6 = p.getULong()
unknown7 = p.getULong()
orientation = p.getULong() # orientation
unknown9 = p.getULong()
player.x = playerx
player.y = playery
player.z = playerz
player.oid = objid
player.o = orientation
await self.server.broadcast_gamedata_except(player, payload)
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)
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))
# TODO: Replace with MSG_MOVE_PLAYER
await self.server.broadcast_gamedata_except(player, payload)
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()
plix = p.getByte() # length?
oid = p.getShort()
unknown = p.getByte()
model = p.getULong()
player.oid = oid
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)
await self.server.broadcast_event("on_player_shoot", player, payload)
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())
await self.server.broadcast_gamedata_except(player, payload)
oid = struct.unpack("<H", payload[6:8])[0]
x = struct.unpack("<f", payload[14:18])[0]
y = struct.unpack("<f", payload[18:22])[0]
z = struct.unpack("<f", payload[22:26])[0]
#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
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()
opcode2 = p.getByte()
unknown = p.getByte()
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
await self.server.broadcast_event("on_player_hit", player, None, None)
await player.send_message("HIT: %s" % payload.hex())
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)
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())
def send_packet(self, addr, packet):
self.remotesocket.sendto(packet.getvalue(), addr)
self.remotesocket.sendto(packet.getvalue(), addr)
#logger.debug("%s:%s < %s", addr[0], addr[1], packet.getvalue().hex())
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)
def get_player(self, iporaddr, port=None):
if type(iporaddr) == Session:
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)
if not port:
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr[0] and x.session.port == iporaddr[1]), None)
else:
return next((x for x in self.server.tempplayers if x.session.ip == iporaddr and x.session.port == port), None)

96
dpnet/session.py Normal file
View File

@ -0,0 +1,96 @@
from threading import Timer
from utils.logger import setup_logger
from dpnet.CFrame import CFrame
from dpnet.DFrame import DFrame
import asyncio
logger = setup_logger(__name__)
class Session:
def __init__(self, server, serversock):
self.serversock = serversock
self.server = server
self.ip = ""
self.port = 0
self.SessID = 0
self.next_expected_seq = 0
self.next_send = 0
self.Full = False
self.LastMsgID = 0
self.connect_retry_timer_num = 0
self.connect_retry_timer = False
self.lock = asyncio.Lock()
def send(self, packet):
rawpacket = packet.to_packet()
rawbytes = rawpacket.getvalue()
self.serversock.sendto(rawbytes, (self.ip, self.port))
if len(rawbytes) >= 4 and rawbytes[0] & DFrame.PACKET_COMMAND_DATA:
self.next_send += 1
def setup_Connect_Retry_Timer(self):
time = 0.2 * pow(2, self.connect_retry_timer_num)
if time > 5:
return False
t = Timer(time, self._send_connected)
t.start()
return t
def cancel_Connect_Retry_Timer(self):
if self.connect_retry_timer:
self.connect_retry_timer.cancel()
self.connect_retry_timer_num = 0
def _send_connected(self):
response = CFrame()
response.ExtOpCode = CFrame.FRAME_EXOPCODE_CONNECTED
response.RspId = self.LastMsgID
response.SessID = self.SessID
logger.debug("Timer sending CONNECTED")
self.send(response)
self.connect_retry_timer = self.setup_Connect_Retry_Timer()
async def send_gamedata(self, bPayload, **kwargs):
acknow = kwargs.get("acknow", False)
dframe = DFrame()
dframe.Command = DFrame.PACKET_COMMAND_DATA | DFrame.PACKET_COMMAND_RELIABLE | DFrame.PACKET_COMMAND_SEQUENTIAL | DFrame.PACKET_COMMAND_NEW_MSG | DFrame.PACKET_COMMAND_END_MSG
if acknow:
dframe.Command = dframe.Command | DFrame.PACKET_COMMAND_POLL
dframe.Control = 0x00
dframe.Seq = self.next_send
dframe.NRcv = self.next_expected_seq
dframe.Payload = bPayload
logger.debug(" %s:%s < %s (GAMEDATA)", self.ip, self.port, bPayload.hex())
self.send(dframe)
if acknow:
await self.lock.acquire()
return dframe
def send_cframe_connected(self, connect):
response = CFrame()
response.ExtOpCode = CFrame.FRAME_EXOPCODE_CONNECTED
response.RspId = connect.MsgID
response.SessID = connect.SessID
logger.debug(" %s:%s < %s (CFRAME CONNECTED)", self.ip, self.port, response.to_packet().getvalue().hex())
self.send(response)
def send_cframe_sack(self):
r = CFrame()
r.Command = CFrame.PACKET_COMMAND_FRAME
r.ExtOpCode = CFrame.FRAME_EXOPCODE_SACK
r.Flags = CFrame.SACK_FLAGS_RESPONSE
r.NRecv = self.next_expected_seq
r.NSeq = self.next_send
logger.debug(" %s:%s < %s (CFRAME SACK)", self.ip, self.port, r.to_packet().getvalue().hex())
self.send(r)
def send_dframe_keepalive(self):
dframe = DFrame()
dframe.Command = DFrame.PACKET_COMMAND_DATA | DFrame.PACKET_COMMAND_RELIABLE | DFrame.PACKET_COMMAND_SEQUENTIAL | DFrame.PACKET_COMMAND_POLL | DFrame.PACKET_COMMAND_NEW_MSG | DFrame.PACKET_COMMAND_END_MSG
dframe.Control = DFrame.PACKET_CONTROL_KEEPALIVE_OR_CORRELATE
dframe.Seq = self.next_send
dframe.NRcv = self.next_expected_seq
dframe.SessID = self.SessID
self.send(dframe)

11
giants/entity.py Normal file
View File

@ -0,0 +1,11 @@
class Entity:
def __init__(self):
self.x = 0
self.y = 0
self.z = 0
self.o = 0
self.oid = 0
self._model = 0
def model(self):
return self._model

5
giants/gameobject.py Normal file
View File

@ -0,0 +1,5 @@
from giants.entity import Entity
class GameObject(Entity):
pass

42
giants/map.py Normal file
View File

@ -0,0 +1,42 @@
import os
class Map:
def __init__(self, mappath):
self.mappath = mappath
self.checksum = None
self.mapname = "Unknown map"
self.load_map(mappath)
def load_map(self, mappath):
if not os.path.exists("maps/"+mappath):
raise Exception("Map not found: "+mappath)
if not mappath.endswith(".gck"):
raise Exception("Server only supports GCK maps")
self.mapname = mappath.split(".gck")[0]
self.checksum = Map.checksum(mappath)
@staticmethod
def checksum(mappath):
# TODO
if mappath == "Three Way Island - Canyons.gck":
return b"\x9c\x53\xf4\xdd"
if mappath == "test1.gck":
return b"\x9f\xb2\x42\xec"
if mappath == "test.gck":
return b"\x48\x52\x33\x23"
if mappath == "test2.gck":
return b"\xc7\x5f\x61\x1f"
if mappath == "test3.gck":
return b"\x59\xbb\xab\x52"
if mappath == "Testmap.gck":
return b"\x74\x07\x98\xaf"
# \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
# \x9f\xb2\x42\xec: testv1
# \x48\x52\x33\x23: testv2
return b"\x00\x00\x00\x00"

View File

@ -1,6 +1,9 @@
import socket
from dpnet.packet import Packet
import threading
import time
from utils.logger import setup_logger
logger = setup_logger(__name__)
class MasterServer:
@ -10,12 +13,42 @@ class MasterServer:
self.masterserverip = "gckms.no-ip.org"
self.masterserverport = 27900
def register(self):
statsserverthread = threading.Thread(target=self.register_and_run)
statsserverthread.start()
statsserverthread.join()
def register_and_run(self):
register_thread = threading.Thread(target=self._register)
register_thread.start()
return register_thread
self.socket.bind((self.server.listen_ip, 8911))
logger.info("Listening to " + self.server.listen_ip + ":" + str(8911))
logger.info("Registering to Master Server")
self._register()
self.keepalive()
self.handle_packets()
def _register(self):
packet = Packet()
packet.write(("0"+str(self.server.listen_port)).encode("ascii"))
self.socket.sendto(packet.getvalue(), (self.masterserverip, self.masterserverport))
def keepalive(self):
packet = Packet()
packet.write(("1"+str(self.server.listen_port)).encode("ascii"))
self.socket.sendto(packet.getvalue(), (self.masterserverip, self.masterserverport))
def handle_packets(self):
while True:
self.socket.sendto(("0"+str(self.server.listen_port)).encode("ascii"), (self.masterserverip, self.masterserverport))
time.sleep(120)
data, addr = self.socket.recvfrom(1024)
print(addr[0], ":", addr[1], ">", data.hex())
data = Packet(data)
command = data.read().decode("ascii")
print("Received command:", command)
if command == "\\status\\":
resp = Packet()
respstr = "\\gamename\\giants\\gamever\\"+str(self.server.version)+"\\hostname\\" + self.server.name + "\\hostport\\" + str(
self.server.listen_port) + "\\mapname\\" + self.server.currentmap.mapname + "\\gametype\\Capture Smartie with full base\\numplayers\\" + str(
len(self.server.players)) + "\\maxplayers\\" + str(
self.server.maxplayers) + "\\gamemode\\openplaying\\timelimit\\60\\fraglimit\\0\\teamfraglimit\\0\\firstbasecomplete\\0\\player_0\\Amazed\\frags_0\\0\\deaths_0\\0\\ping_0\\0\\team_0\\Green\\final\\\\queryid\\2.1"
resp.putBytes(respstr.encode("ascii"))
self.socket.sendto(resp.getvalue(), addr)
print(addr[0], ":", addr[1], "<", resp.getvalue().hex())

58
giants/player.py Normal file
View File

@ -0,0 +1,58 @@
from .entity import Entity
import random
import struct
from . import ChatColor, ChatType, Models
from dpnet.DFrame import DFrame
from dpnet.gamepackets import MSG_CHANGE_TEAM, MSG_PLAYER_PING
class PlayerPhases:
NONE = 0
CFRAME_CONNECT = 1
CFRAME_CONNECTED = 2
DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO_EX = 3
DN_SEND_CONNECT_INFO = 4
DN_ACK_CONNECT_INFO = 5
INGAME = 6
class Player(Entity):
def __init__(self, name, session):
super().__init__()
self.name = name
self.session = session
self.team = 2
self.score = 0
self.id = random.getrandbits(16)
self.phase = PlayerPhases.NONE
self.ping = 1
self.opts = {"debug": True}
def get_index(self):
return self.session.server.get_player_index(self)
async def change_team(self, newteam, respawn=True):
self.team = newteam
await self.session.server.broadcast_event("on_player_change_team", self, self.team)
await self.session.server.broadcast_gamedata(MSG_CHANGE_TEAM(self, newteam, respawn).to_packet())
async def send_ping(self, ping):
await self.session.server.broadcast_gamedata(MSG_PLAYER_PING(self, ping).to_packet())
async def send_message(self, message, type=ChatType.All, playerindex=0, color=ChatColor.Orange):
options = type | color
playeri = 0x80 + playerindex
await self.session.send_gamedata(b"\x35" + struct.pack("<B", playeri)+ struct.pack("<B", options) + message.encode("utf8") + b"\x00\x00", acknow=False)
async def kick(self):
dframe = DFrame()
dframe.Command = DFrame.PACKET_COMMAND_DATA | DFrame.PACKET_COMMAND_RELIABLE | DFrame.PACKET_COMMAND_SEQUENTIAL | DFrame.PACKET_COMMAND_POLL | DFrame.PACKET_COMMAND_NEW_MSG | DFrame.PACKET_COMMAND_END_MSG
dframe.Control = DFrame.PACKET_CONTROL_END_STREAM
dframe.Seq = self.session.next_send
dframe.NRcv = self.session.next_expected_seq
dframe.SessID = self.session.SessID
self.session.send(dframe)
def model(self):
# TODO: Fix
return Models.Characters_Mecc

6
giants/projectile.py Normal file
View File

@ -0,0 +1,6 @@
from giants.entity import Entity
class Projectile(Entity):
def __init__(self):
super().__init__()

0
plugins/__init__.py Normal file
View File

28
plugins/bullets.py Normal file
View File

@ -0,0 +1,28 @@
from dpnet.gamepackets import *
import struct
class Bullets:
def __init__(self, server):
self.server = server
async def on_player_chat(self, player, type, team, message):
command = message.split(" ")
if len(command) > 1 and command[0] == "bullet":
t = int(command[1])
player.opts["bullet"] = t
async def on_player_shoot(self, player, payload):
if "bullet" in player.opts and player.opts["bullet"] != 0:
oid = struct.unpack("<H", payload[31:33])[0]
print(oid)
x = struct.unpack("<f", payload[14:18])[0]
y = struct.unpack("<f", payload[18:22])[0]
z = struct.unpack("<f", payload[22:26])[0]
o = struct.unpack("<L", payload[26:30])[0]
await self.server.broadcast_gamedata(MSG_BULLET_EXPLOSION(player, oid, x, y, z, False).to_packet())
await self.server.broadcast_gamedata(MSG_NEW_PROJECTILE(player, self.server.new_oid(), player.opts["bullet"], x, y, z, o).to_packet())
def setup(server):
plugin = Bullets(server)
server.add_plugin(plugin)

185
plugins/commands.py Normal file
View File

@ -0,0 +1,185 @@
from giants import ChatColor
import asyncio
import struct
from dpnet.packet import Packet
from dpnet.gamepackets import *
from random import randint
class Commands:
def __init__(self, server):
self.server = server
self.recorded = b''
self.recording = False
self.recordingplayer = None
self.nextweapon = 1
async def on_player_gamedata(self, player, gamedatabytes):
if self.recording:
self.recorded = gamedatabytes
self.recording = False
await self.recordingplayer.send_message("Packet recorded: %s" % (self.recorded.hex(),), color=ChatColor.Cyan)
async def on_player_chat(self, player, type, team, message):
await self.server.broadcast_message_except(player, "%s: %s" % (player.name, message), color=ChatColor.Yellow)
print(message)
command = message.split(" ")
if len(command) > 1 and command[0] == "team":
newteam = command[1]
respawn = True
if len(command) > 2:
respawn = False if command[2] == "off" or command[2] == "no" or command[2] == "false" else True
print("Changing team of %s to %s" % (player.name, newteam))
await self.server.broadcast_message("%s switched to team %s using command" % (player.name, newteam))
await player.change_team(int(newteam), respawn=respawn)
if len(command) > 1 and command[0] == "ping":
newping = command[1]
print("Changing ping of %s to %s" % (player.name, newping))
await self.server.broadcast_message("%s set his ping to %s" % (player.name, newping))
await player.send_ping(int(newping))
if command[0] == "ping":
print("ping function")
await self.server.broadcast_message("pong")
if command[0] == "colors":
# color codes seem to go from 01 to 0F, loop and show each value:
for i in range(1, 0xf + 1):
print("Trying value %s" % i)
await player.send_message("Color %s" % i, color=i)
await asyncio.sleep(2) # sleep between each value to see what value makes the client crash
if command[0] == "chattypes":
# type codes seem to go from 00 to 0F, loop and show each value: CRASH
for i in range(0, 0xf+1):
print("Trying value %s" % i)
await self.server.broadcast_message("Type %s" % i, type=i)
await asyncio.sleep(2) # sleep between each value to see what value makes the client crash
if command[0] == "doomchat":
# tries every value of first byte and second byte
for i in range(3,0xff+1):
for j in range(0, 0x81):
if i == 0 and j == 2:
break # ;)
message = "(%s, %s)" % (i, j)
print(message)
await player.session.send_gamedata(b"\x35" + struct.pack("<B", i)+ struct.pack("<B", j) + message.encode("utf8") + b"\x00\x00", acknow=False)
await asyncio.sleep(0.1) # sleep between each value to see what value makes the client crash
if command[0] == "die" and len(command) > 1:
diemethod = int(command[1])
await player.send_message("Death by id: %s" % diemethod)
await self.server.broadcast_gamedata(MSG_PLAYER_DIE(player, diemethod).to_packet())
#await player.session.send_gamedata(bytes.fromhex("0b1a0000000002070000a5b0d4c35d9714c450abef354a68fb410914000000020700000402000000")+struct.pack("<L",diemethod)+bytes.fromhex("0000"))
if command[0] == "spawn" and len(command) > 1:
await self.server.broadcast_message("Spawning " + command[1] + "")
await self.server.spawn(player, int(command[1]))
if command[0] == "changeme" and len(command) > 1:
await self.server.broadcast_message("Changing your model to " + command[1] + "")
await self.server.change_model(player, int(command[1]))
if command[0] == "change" and len(command) > 2:
await self.server.broadcast_message("Changing model of "+command[1]+" to "+command[2]+"")
await self.server.change_model(self.server.get_player_by_index(int(command[1])), int(command[2]))
if command[0] == "debug":
if player.opts["debug"]:
player.opts["debug"] = False
await player.send_message("Debug disabled")
else:
player.opts["debug"] = True
await player.send_message("Debug enabled")
if command[0] == "record":
self.recording = True
self.recordingplayer = player
await player.send_message("Recording next packet...", color=ChatColor.Cyan)
if command[0] == "replayshow":
await player.send_message("Packet: %s" % (self.recorded.hex(),), color=ChatColor.Cyan)
if command[0] == "replay" or command[0] == "r" and len(command) == 1:
await player.session.send_gamedata(self.recorded)
await player.send_message("Sent packet %s to you" % (self.recorded.hex(),), color=ChatColor.Cyan)
if command[0] == "switch" and len(command) > 1:
newweap = int(command[1])
await player.send_message("Got you weapon %s" % newweap, color=ChatColor.Cyan)
await self.server.broadcast_gamedata(MSG_SWITCH_WEAPON(player, newweap).to_packet())
if command[0] == "s":
await player.send_message("Got you weapon %s" % (self.nextweapon,), color=ChatColor.Cyan)
await self.server.broadcast_gamedata(MSG_SWITCH_WEAPON(player, self.nextweapon).to_packet())
self.nextweapon += 1
if command[0] == "emptyweapons":
for _ in range(4):
await self.server.broadcast_gamedata(MSG_SWITCH_WEAPON(player, 0xff).to_packet())
await player.send_message("Emptied your weapons", color=ChatColor.Cyan)
if command[0] == "sniper":
p = Packet()
p.putByte(0x20)
p.putShort(self.server._nextid) # oid?
self.server._nextid += 1
p.putShort(0x0000)
p.putShort(0x0440)
p.putShort(0x0100)
p.putFloat(0) # x
p.putFloat(0) # y
p.putFloat(0) # z
p.putFloat(0) # o
p.putFloat(0x621c68c1) # wtf?
p.putShort(0x0000)
p.putShort(0x7041) # oid?
p.putShort(0x0000)
p.putShort(0x0300)
p.putLong(0x1e0000)
p.putLong(0x0)
p.putByte(0x00) # end
await self.server.broadcast_gamedata(p.getvalue())
if len(command) > 1 and command[0] == "drop":
weap = int(command[1])
await player.send_message("Dropped weapon %s" % weap, color=ChatColor.Cyan)
await self.server.broadcast_gamedata(MSG_DROP_WEAPON(player, self.server.new_oid(), weap, 255).to_packet())
if command[0] == "coords":
await player.send_message("You are at (%s,%s,%s)" % (player.x, player.y, player.z), color=ChatColor.Cyan)
if command[0] == "tp":
x = randint(-200,200)
y = randint(-200,200)
z = randint(-200,200)
o = 0
await player.send_message("Teleporting you to (%s,%s,%s)" % (x, y, z), color=ChatColor.Cyan)
await self.server.broadcast_gamedata(MSG_PLAYER_TELEPORT(player, x, y, z, o).to_packet())
if len(command) > 1 and command[0] == "fire":
weap = int(command[1])
if len(command) > 2:
i = int(command[2])
else:
i = 1
for _ in range(i):
x = randint(-200, 200)
y = randint(-200, 200)
z = randint(-200, 200)
o = 0
await player.send_message("Firing to (%s,%s,%s)" % (x, y, z), color=ChatColor.Cyan)
await self.server.broadcast_gamedata(MSG_NEW_PROJECTILE(player, self.server.new_oid(), weap, x, y, z, o).to_packet())
if command[0] == "popup":
await player.send_message("Spawned popup", color=ChatColor.Cyan)
await self.server.broadcast_gamedata(bytes.fromhex("09160000000438000002250000000a000000040700000913000000040700001102000000041d00000a090e00000004380000032500000000"))
def setup(server):
plugin = Commands(server)
server.add_plugin(plugin)

24
plugins/greetings.py Normal file
View File

@ -0,0 +1,24 @@
import random
class Greetings:
def __init__(self, server):
self.server = server
self.join_messages = ["Welcome %s!", "Beware! %s has arrived!", "A wild %s appears.", "%s has come and is ready to pwn."]
self.left_messages = ["%s has left.", "%s has stopped Giants and started playing Apex. Shame!", "A wild %s disappeared.", "%s was happy with the score and left the game."]
async def on_player_join(self, player):
await self.server.broadcast_message(random.choice(self.join_messages) % player.name)
async def on_map_change(self, newmap):
await self.server.broadcast_message("You are now playing on "+newmap.mapname)
async def on_player_change_team(self, player, newteam):
await self.server.broadcast_message("%s switched to team %s" % (player.name, newteam))
async def on_player_left(self, player):
await self.server.broadcast_message(random.choice(self.left_messages) % player.name)
def setup(server):
plugin = Greetings(server)
server.add_plugin(plugin)

59
plugins/instagib.py Normal file
View File

@ -0,0 +1,59 @@
from dpnet.packet import Packet
from dpnet.gamepackets import *
from random import randint
class Instagib:
def __init__(self, server):
self.server = server
async def on_player_spawn(self, player):
await self.server.broadcast_message("%s has spawned" % player.name)
# random teleport
await self.random_teleport(player)
# give stuff
await self.give_stuff(player)
async def on_player_respawn(self, player):
await self.server.broadcast_message("%s has respawned" % player.name)
# random teleport
await self.random_teleport(player)
# give stuff
await self.give_stuff(player)
async def on_player_shoot(self, player, payload):
await self.spawn_sniper(player)
async def on_player_hit(self, player, shooter, damage, weapon):
# make player die
#await self.server.broadcast_gamedata(MSG_PLAYER_DIE(player, 1))
pass
async def give_stuff(self, player):
# remove current weapon
#await self.remove_current_weapon(player)
# add sniper
await self.spawn_sniper(player)
async def spawn_sniper(self, player):
await self.server.broadcast_gamedata(MSG_DROP_WEAPON(player, self.server.new_oid(), 6, 255).to_packet())
async def remove_current_weapon(self, player):
await self.server.broadcast_gamedata(MSG_SWITCH_WEAPON(player, 0xff).to_packet())
async def random_teleport(self, player):
# random teleport
maxdist = 2000
x = randint(-(maxdist/2), maxdist/2)
y = randint(-(maxdist/2), maxdist/2)
z = randint(-(maxdist/2), maxdist/2)
await self.server.broadcast_gamedata(MSG_PLAYER_TELEPORT(player, x, y, z, 0).to_packet())
def setup(server):
plugin = Instagib(server)
server.add_plugin(plugin)

9
sendenum.py Normal file
View File

@ -0,0 +1,9 @@
from dpnet.EnumQuery import EnumQuery
from dpnet.netclient import Netclient
client = Netclient("163.158.182.243", 19711)
eq = EnumQuery()
eq.ApplicationGUID = b"\x10\x5e\x62\xa7\x96\x1a\xd2\x11\x9a\xfc\x00\x60\x08\x45\xe5\x71"
client.send(eq.to_packet())
data = client.receive()
print("<R", data.hex())

292
server.py
View File

@ -1,35 +1,295 @@
from dpnet.packet import Packet
from giants.map import Map
from giants.player import Player
from dpnet.netserver import Netserver
from giants.masterserver import MasterServer
from dpnet.session import Session
import socket
import importlib
import os
from giants import Teams, GameTypes, ChatColor, ChatType
from utils.logger import setup_logger
import traceback
import asyncio
from aioconsole import ainput
import struct
from curses import wrapper
logger = setup_logger(__name__)
num_players_alltime = 0
class Server:
def __init__(self, **kwargs):
self.listen_ip = kwargs.get("ip", "0.0.0.0")
self.listen_port = kwargs.get("port", 19711)
self.register_with_ms = kwargs.get("register", True)
self.register_with_ms = kwargs.get("register", False)
self.teams = kwargs.get("teams", Teams.MvM)
self.game_type = kwargs.get("gametype", GameTypes.TeamDeathmatchWithFullBase)
self.currentmap = kwargs.get("map", Map("Testmap.gck"))
self.maxplayers = kwargs.get("maxplayers", 20)
self.name = kwargs.get("name", "Default Server Name")
async def on_connection_received(self, ip, port):
version = 1.497
server_name = "Hello %s" % ip
global num_players_alltime
num_players_alltime = num_players_alltime + 1
player_count = num_players_alltime
max_player_count = 20
map_name = "best map ever"
return version, server_name, player_count, max_player_count, map_name
fake_session = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_session.ip = "127.0.0.1"
fake_session.port = 3333
self.players = [Player("[Server]", fake_session)]
fake_player = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_player.ip = "127.0.0.1"
fake_player.port = 3334
self.players.append(Player("Bot 1", fake_player))
fake_player = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_player.ip = "127.0.0.1"
fake_player.port = 3334
self.players.append(Player("Bot 2", fake_player))
fake_player = Session(self, socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
fake_player.ip = "127.0.0.1"
fake_player.port = 3334
self.players.append(Player("Bot 3", fake_player))
self.accept_new_players = True
self.version = 1.497
self.points_per_kill = 1
self.points_per_capture = 5
self.detente_time = 0 # minutes
self._plugins = []
self.tempplayers = []
self.running = True
self.ticks = 60
self._nextid = 118
def update(self):
#logger.debug("Calling update")
pass
async def add_player(self, player):
self.tempplayers.remove(player)
self.players.append(player)
await self.broadcast_event("on_player_join", player)
# todo: remove
def create_temp_player(self, player):
self.tempplayers.append(player)
async def remove_player(self, player):
self.players.remove(player)
def add_plugin(self, plugin):
self._plugins.append(plugin)
async def change_map(self, mappath):
try:
self.currentmap = Map(mappath)
await self.broadcast_event("on_map_change", self.currentmap)
except Exception:
logger.error("Could not change map")
async def broadcast_event(self, event, *args):
logger.debug("Broadcasting event "+event)
for plugin in self._plugins:
if hasattr(plugin, event):
func = getattr(plugin, event)
if callable(func):
try:
await func(*args)
except Exception:
logger.error("Could not call plugin function: "+plugin.__class__.__name__+"."+event)
traceback.print_exc()
def load_plugins(self):
plugins = os.listdir("plugins")
for plugin in plugins:
if os.path.isdir("plugins/"+plugin) or plugin == "__init__.py":
continue
if not plugin.endswith(".py"):
continue
pluginname = plugin.split(".py")[0]
try:
module = importlib.import_module("plugins."+pluginname)
if hasattr(module, "setup"):
setup = getattr(module, "setup")
if callable(setup):
logger.info("Loading plugin "+module.__name__)
module.setup(self)
except Exception:
logger.warning("Could not load plugin "+plugin)
traceback.print_exc()
async def broadcast_message(self, text, color=ChatColor.Orange, type=ChatType.All):
for player in self.players:
if player.name == "[Server]":
continue
await player.send_message(text, color=color, type=type)
async def ask_command(self):
while True:
try:
cmd = await ainput(">")
logger.debug("Sending game payload %s", cmd)
for player in self.players:
if player.name == "[Server]":
continue
await player.session.send_gamedata(bytes.fromhex(cmd), acknow=False)
except:
traceback.print_exc()
async def broadcast_gamedata(self, payload, **kwargs):
for player in self.players:
if player.name == "[Server]":
continue
await player.session.send_gamedata(payload, **kwargs)
async def broadcast_gamedata_except(self, player, payload, **kwargs):
for pplayer in self.players:
if pplayer.name == "[Server]" or pplayer == player:
continue
await pplayer.session.send_gamedata(payload, **kwargs)
async def broadcast_message_except(self, player, text, color=ChatColor.Yellow, type=ChatType.All):
for pplayer in self.players:
if pplayer.name == "[Server]" or pplayer == player:
continue
await pplayer.send_message(text, color=color, type=type)
async def new_object(self, model):
oid = self._nextid
p = Packet()
p.putByte(0x05) # opcode
p.putByte(0)
p.putShort(self._nextid)
p.putByte(0x00)
p.putULong(model)
p.putByte(0x00)
self._nextid += 1
await self.broadcast_gamedata(p.getvalue())
return oid
async def spawn(self, player, model):
plix = self.get_player_index(player)
p = Packet()
p.putByte(0x05) # opcode
p.putByte(plix)
p.putShort(self._nextid)
p.putByte(0x00)
p.putULong(model)
p.putByte(0x00)
await self.broadcast_gamedata(p.getvalue(), acknow=False)
#await self.broadcast_gamedata(bytes.fromhex("05" + plix + struct.pack("<H", self._nextid).hex() + "00" + model + "00000000"), acknow=False)
p = Packet()
p.putByte(0x0a) # opcode
p.putByte(0x12) # length
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(plix) # player index
p.putShort(self._nextid) # object id
p.putByte(0x00) # unknown
p.putByte(0x01) # unknown
p.putULong(model) # model
p.putFloat(player.x) # player x
p.putFloat(player.y) # player y
p.putFloat(player.z) # player z
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # orientation
p.putULong(0x00) # unknown
await self.broadcast_gamedata(p.getvalue(), acknow=False)
self._nextid+=1
#await self.broadcast_gamedata(bytes.fromhex("0a12000000" + plix + struct.pack("<H", self._nextid).hex() + "0003" + model + "00000000"), acknow=False)
#await self.broadcast_gamedata(bytes.fromhex("0a32000000" + plix + struct.pack("<B", self._nextid).hex() + "000001" + model + "000000ff00000000000000000000000000000000000000000000000000000000000000000000000a12000000" + plix + struct.pack("<B", self._nextid).hex() + "0000" + model + "020000000000000000"), acknow=False)
async def change_model(self, player, model):
plid = self.get_player_index(player)
# p = Packet()
# p.putByte(0x0a) # opcode
# p.putByte(0x12) # length
# p.putByte(0x00) # unknown
# p.putByte(0x00) # unknown
# p.putByte(0x00) # unknown
# p.putByte(plid) # player index
# p.putShort(player.oid) # object id
# p.putByte(0x00) # unknown
# p.putByte(0x02) # 4 -> death, 3 -> little move
# p.putULong(model) # model
# p.putFloat(player.x) # x
# p.putFloat(player.y) # y
# p.putFloat(player.z) # z
# p.putULong(0x00) # unknown
# p.putULong(0x00) # unknown
# p.putULong(0x00) # unknown
# p.putULong(0x00) # unknown
# p.putULong(player.o + 30) # orientation
# p.putULong(0x00) # unknown
p = Packet()
p.putByte(0x0a) # opcode
p.putByte(0x32) # length
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(plid) # player index
p.putShort(player.oid) # object id
p.putByte(0x00) # unknown
p.putByte(0x01) # 1 -> stand still, 4 -> death, 3 -> little move
p.putULong(model) # model
p.putFloat(0x00) # x
p.putFloat(player.y) # y
p.putFloat(player.z) # z
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong(0x00) # unknown
p.putULong((player.o + 30) % 360) # orientation
p.putULong(0x00) # unknown
p.putByte(0x0a) # opcode
p.putByte(0x12) # length
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(0x00) # unknown
p.putByte(plid) # player index
p.putShort(player.oid) # object id
p.putByte(0x00) # unknown
p.putByte(0x00) # 1 -> stand still, 4 -> death, 3 -> little move
p.putULong(model) # model
p.putULong(0x00)
p.putByte(0x00)
p.putByte(0x00)
await self.broadcast_gamedata(p.getvalue(), acknow=False)
#await self.broadcast_gamedata(bytes.fromhex("0a32000000" + plix + struct.pack("<B", objid).hex() + "000001" + model + "000000ff00000000000000000000000000000000000000000000000000000000000000000000000a12000000" + plix + struct.pack("<B", objid).hex() + "0000" + model + "020000000000000000"), acknow=False)
def new_oid(self):
a = self._nextid
self._nextid += 1
return a
def get_player_index(self, player):
for iplayer in range(len(self.players)):
if self.players[iplayer] == player:
return iplayer
return False
def get_player_by_index(self, playerindex):
return self.players[playerindex]
if __name__ == '__main__':
server = Server()
if server.register_with_ms:
ms = MasterServer(server)
register_thread = ms.register_and_run()
server = Server(name="giantsd", maxplayers=20, register=False, teams=Teams.MvM, map=Map("Three Way Island - Canyons.gck")) #, map=Map("Three Way Island - Canyons.gck"))
server.load_plugins()
loop = asyncio.get_event_loop()
listen = loop.create_datagram_endpoint(lambda: Netserver(server), local_addr=(server.listen_ip, server.listen_port))
transport, protocol = loop.run_until_complete(listen)
loop.create_task(server.ask_command())
try:
loop.run_forever()
except KeyboardInterrupt: