forked from hipstercat/giantsd
Compare commits
No commits in common. "master" and "master" have entirely different histories.
179
dpnet/DN_MSG_INTERNAL_SEND_CONNECT_INFO.py
Normal file
179
dpnet/DN_MSG_INTERNAL_SEND_CONNECT_INFO.py
Normal 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
258
dpnet/gamepackets.py
Normal 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
18
dpnet/netclient.py
Normal 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)
|
@ -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
96
dpnet/session.py
Normal 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
11
giants/entity.py
Normal 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
5
giants/gameobject.py
Normal file
@ -0,0 +1,5 @@
|
||||
from giants.entity import Entity
|
||||
|
||||
|
||||
class GameObject(Entity):
|
||||
pass
|
42
giants/map.py
Normal file
42
giants/map.py
Normal 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"
|
@ -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
58
giants/player.py
Normal 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
6
giants/projectile.py
Normal file
@ -0,0 +1,6 @@
|
||||
from giants.entity import Entity
|
||||
|
||||
|
||||
class Projectile(Entity):
|
||||
def __init__(self):
|
||||
super().__init__()
|
0
plugins/__init__.py
Normal file
0
plugins/__init__.py
Normal file
28
plugins/bullets.py
Normal file
28
plugins/bullets.py
Normal 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
185
plugins/commands.py
Normal 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
24
plugins/greetings.py
Normal 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
59
plugins/instagib.py
Normal 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
9
sendenum.py
Normal 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
292
server.py
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user