forked from hipstercat/giantsd
Moar things
This commit is contained in:
parent
831a4dbf9d
commit
024712cc8d
177
dpnet/DN_MSG_INTERNAL_SEND_CONNECT_INFO.py
Normal file
177
dpnet/DN_MSG_INTERNAL_SEND_CONNECT_INFO.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
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 = 0
|
||||||
|
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 = 0x21
|
||||||
|
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_player.dpnid = p.id
|
||||||
|
entry_player.dwFlags = 0x0200
|
||||||
|
entry_player.Name = (p.name+"\x00").encode("utf-16-le")
|
||||||
|
|
||||||
|
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.putULong(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()
|
@ -10,8 +10,12 @@ import threading
|
|||||||
import traceback
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
import struct
|
import struct
|
||||||
|
from _datetime import datetime
|
||||||
from utils.logger import setup_logger
|
from utils.logger import setup_logger
|
||||||
from giants.player import Player
|
from giants.player import Player, PlayerPhases
|
||||||
|
import time
|
||||||
|
from giants import APPLICATION_GUID
|
||||||
|
from .DN_MSG_INTERNAL_SEND_CONNECT_INFO import DN_MSG_INTERNAl_SEND_CONNECT_INFO
|
||||||
|
|
||||||
logger = setup_logger(__name__)
|
logger = setup_logger(__name__)
|
||||||
|
|
||||||
@ -23,21 +27,35 @@ class Netserver:
|
|||||||
self.server = server
|
self.server = server
|
||||||
self.addrs = []
|
self.addrs = []
|
||||||
self.guid = uuid.uuid4().bytes
|
self.guid = uuid.uuid4().bytes
|
||||||
|
self.incoming_packets = []
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
self.socket.bind((self.server.listen_ip, self.server.listen_port))
|
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)
|
logger.debug("Listening to %s:%s", self.server.listen_ip, self.server.listen_port)
|
||||||
gameserverthread = threading.Thread(target=self.handle_packets)
|
networkthread = threading.Thread(target=self.handle_packets)
|
||||||
gameserverthread.start()
|
networkthread.start()
|
||||||
|
|
||||||
if self.server.register_with_ms:
|
if self.server.register_with_ms:
|
||||||
ms = MasterServer(self.server)
|
ms = MasterServer(self.server)
|
||||||
ms.register()
|
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)
|
||||||
|
|
||||||
gameserverthread.join()
|
networkthread.join()
|
||||||
#statsserverthread.join()
|
|
||||||
|
def read_packets(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send_packets(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def handle_packets(self):
|
def handle_packets(self):
|
||||||
while True:
|
while True:
|
||||||
@ -79,7 +97,7 @@ class Netserver:
|
|||||||
er.ApplicationReservedData += b'\x00\x04\x00' # game type and teams
|
er.ApplicationReservedData += b'\x00\x04\x00' # game type and teams
|
||||||
er.ApplicationReservedData += struct.pack("<L", int(self.server.version*1000)) # game version
|
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'\x02\x92\x05\x00\x01\x00\x00\x00\x00\x00' # Unknown
|
||||||
er.ApplicationReservedData += b'\x9c\x53\xf4\xde' # Seems to be a checksum of current map
|
er.ApplicationReservedData += b'\x9c\x53\xf4\xdd' # Seems to be a checksum of current map
|
||||||
# \x00\x00\x00\x00: [None]
|
# \x00\x00\x00\x00: [None]
|
||||||
# \x00\x00\x00\x01: <custom map>
|
# \x00\x00\x00\x01: <custom map>
|
||||||
# \x9c\x53\xf4\xdd: Three Way Island - Canyons
|
# \x9c\x53\xf4\xdd: Three Way Island - Canyons
|
||||||
@ -101,6 +119,7 @@ class Netserver:
|
|||||||
try:
|
try:
|
||||||
cframe = CFrame(data)
|
cframe = CFrame(data)
|
||||||
session = self.get_session(addr)
|
session = self.get_session(addr)
|
||||||
|
player = self.get_player(session) if session else None
|
||||||
if cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECT:
|
if cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECT:
|
||||||
# CONNECT CFRAME
|
# CONNECT CFRAME
|
||||||
if session and session.Full:
|
if session and session.Full:
|
||||||
@ -123,6 +142,17 @@ class Netserver:
|
|||||||
session.port = addr[1]
|
session.port = addr[1]
|
||||||
self.addrs.append(session)
|
self.addrs.append(session)
|
||||||
session.send_cframe_connected(cframe)
|
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()
|
#session.setup_Connect_Retry_Timer()
|
||||||
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECTED:
|
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECTED:
|
||||||
# CONNECTED CFRAME
|
# CONNECTED CFRAME
|
||||||
@ -137,6 +167,7 @@ class Netserver:
|
|||||||
return
|
return
|
||||||
session.Full = True # fully connected
|
session.Full = True # fully connected
|
||||||
session.cancel_Connect_Retry_Timer()
|
session.cancel_Connect_Retry_Timer()
|
||||||
|
player.phase = PlayerPhases.CFRAME_CONNECTED
|
||||||
|
|
||||||
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_SACK:
|
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_SACK:
|
||||||
if not session or not session.Full:
|
if not session or not session.Full:
|
||||||
@ -215,102 +246,87 @@ class Netserver:
|
|||||||
return
|
return
|
||||||
|
|
||||||
def handle_game_packet(self, session, payload):
|
def handle_game_packet(self, session, payload):
|
||||||
#logger.debug("Payload: %s", payload)
|
player = self.get_temp_player(session)
|
||||||
|
|
||||||
if payload[0] == 0xc1:
|
if payload[0] == 0xc1:
|
||||||
# connect frame?
|
if not player or not player.phase == PlayerPhases.CFRAME_CONNECTED:
|
||||||
_ = payload[0:4]
|
logger.error("Fuck you. Playerphase was %s", player.phase)
|
||||||
_ = payload[4:8]
|
return
|
||||||
_ = payload[8:12]
|
# DN_INTERNAL_MESSAGE_PLAYER_CONNECT_INFO_EX
|
||||||
_ = payload[12:16]
|
ppayload = Packet(payload)
|
||||||
_ = payload[16:20]
|
dwPacketType = ppayload.getULong()
|
||||||
_ = payload[20:52]
|
dwFlags = ppayload.getULong()
|
||||||
instanceguid = payload[52:68]
|
dwDNETVersion = ppayload.getULong()
|
||||||
gameguid = payload[68:84]
|
dwNameOffset = ppayload.getULong()
|
||||||
_ = payload[84:88]
|
dwNameSize = ppayload.getULong()
|
||||||
_ = payload[88:92]
|
dwDataOffset = ppayload.getULong()
|
||||||
name = payload[92:]
|
dwDataSize = ppayload.getULong()
|
||||||
name = name.decode("utf-16-le")[0:-1]
|
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+4+dwNameSize]
|
||||||
|
name = name.decode("utf-16-le")
|
||||||
logger.debug("%s connected!", name)
|
logger.debug("%s connected!", name)
|
||||||
player = Player(name, session)
|
player.name = name
|
||||||
self.server.new_player(player)
|
|
||||||
session.send_dframe_keepalive()
|
session.send_dframe_keepalive()
|
||||||
|
|
||||||
r = DFrame()
|
# Response: DN_MSG_INTERNAL_SEND_CONNECT_INFO
|
||||||
r.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
|
d = DN_MSG_INTERNAl_SEND_CONNECT_INFO(self, player)
|
||||||
r.Control = 0x00
|
d.Seq = session.next_send
|
||||||
r.Seq = session.next_send
|
d.NRcv = session.next_expected_seq
|
||||||
r.NRcv = session.next_expected_seq
|
|
||||||
payload = Packet()
|
|
||||||
payload.putLong(0xc2) # C2 = response to a C1 connection?
|
|
||||||
payload.putLong(0x00) # unknown
|
|
||||||
payload.putLong(0x00) # unknown
|
|
||||||
payload.putLong(0x50) # unknown - 80
|
|
||||||
payload.putLong(0x41) # unknown - 65
|
|
||||||
payload.putLong(self.server.maxplayers) # max players
|
|
||||||
payload.putLong(len(self.server.players)) # current players
|
|
||||||
payload.putByte(len((name + "\x00").encode("utf-16-le"))) # player name length
|
|
||||||
payload.write(b'\x01\x00\x00') # original: 010000 - client does not connect if wrong
|
|
||||||
payload.putLong(len((self.server.name + "\x00").encode("utf-16-le"))) # SERVERNAME LENGTH
|
|
||||||
for _ in range(4):
|
|
||||||
payload.putLong(0) # unknown
|
|
||||||
payload.putLong(204+len((name+"\x00").encode("utf-16-le"))) # 204+playerlen that's an offset FUN FACT: setting it to a bad value makes the client crash on connect
|
|
||||||
payload.putLong(0x12) # original: 0x34 - does not seem to affect the client
|
|
||||||
payload.write(self.guid) # instance guid
|
|
||||||
payload.write(
|
|
||||||
b"\x10\x5e\x62\xa7\x96\x1a\xd2\x11\x9a\xfc\x00\x60\x08\x45\xe5\x71") # app guid
|
|
||||||
payload.write(b'\x40\xf5\x62\xe1') # original: 40f562e1 - client does not connect if wrong
|
|
||||||
payload.putLong(0x10) # original: 0x21 - does not seem to affect the client
|
|
||||||
payload.putLong(0x00) # unknown
|
|
||||||
payload.putLong(0x02) # unknown - 2
|
|
||||||
payload.putLong(0x00) # original: 0x00
|
|
||||||
payload.write(b'\x01\x01\x01\x01') # original: 45f552e3 - does not seem to affect the client
|
|
||||||
payload.putLong(0x00)
|
|
||||||
payload.write(b'\x02\x04\x00\x00') # original: 02040000 - client does not connect if wrong
|
|
||||||
payload.putLong(0x02) # original: 0x02 - client does not connect if wrong
|
|
||||||
payload.putLong(0x00) # unknown
|
|
||||||
payload.putLong(0x07) # original: 0x07 unknown - 7
|
|
||||||
for _ in range(6):
|
|
||||||
payload.putLong(0) # unknown
|
|
||||||
payload.write(b'\x40\xf5\x62\xe1') # original: 40f562e1 - client does not connect if wrong
|
|
||||||
payload.putLong(0x00) # unknown
|
|
||||||
payload.write(b'\x00\x02\x00\x00') # unknown - ?
|
|
||||||
payload.putLong(0x21) # original: 0x21 unknown - 33
|
|
||||||
payload.putLong(0x00) # unknown - 0
|
|
||||||
payload.putLong(0x07) # original: 0x07 unknown - 7
|
|
||||||
payload.putLong(0xcc) # original: 0xcc - does not seem to affect client
|
|
||||||
payload.putLong(len((name + "\x00").encode("utf-16-le"))) # player name length
|
|
||||||
for _ in range(4):
|
|
||||||
payload.putLong(0) # unknown
|
|
||||||
payload.write((name + "\x00").encode("utf-16-le"))
|
|
||||||
payload.write(b'\xff') # map ID
|
|
||||||
payload.putByte(self.server.game_type) # game type
|
|
||||||
payload.putByte(self.server.teams) # teams
|
|
||||||
payload.write(b'\xcc') # original: 0x00 - does not seem to affect client
|
|
||||||
payload.putShort(int(self.server.version*1000))
|
|
||||||
payload.write(b'\x03\x90') # original: 0292 - does not seem to affect client
|
|
||||||
payload.putShort(self.server.points_per_capture)
|
|
||||||
payload.putShort(self.server.points_per_kill)
|
|
||||||
payload.write(b'\xff\xff') # original: 0000 - does not seem to affect client
|
|
||||||
payload.putShort(self.server.detente_time)
|
|
||||||
payload.write(b'\x9c\x53\xf4\xdf') # Seems to be a checksum of current map OR linked to the number of chars in the map name
|
|
||||||
payload.write(self.server.currentmap.mapname.encode("ascii"))
|
|
||||||
payload.write(b'\x00' * (32 - len(self.server.currentmap.mapname)))
|
|
||||||
payload.write((self.server.name + "\x00").encode("utf-16-le"))
|
|
||||||
|
|
||||||
r.Payload = payload.getvalue()
|
appdata = Packet()
|
||||||
session.send(r)
|
appdata.putByte(0xff) # map ID
|
||||||
|
appdata.putByte(self.server.game_type) # game type
|
||||||
|
appdata.putByte(self.server.teams) # teams
|
||||||
|
appdata.write(b'\xcc') # 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'\xff\xff') # original: 0000 - does not seem to affect client
|
||||||
|
appdata.putShort(self.server.detente_time)
|
||||||
|
appdata.write(b'\x9c\x53\xf4\xdd') # 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)
|
||||||
|
player.phase = PlayerPhases.DN_SEND_CONNECT_INFO
|
||||||
|
|
||||||
elif payload[0] == 0xc3:
|
elif payload[0] == 0xc3:
|
||||||
player = self.get_player(session)
|
player = self.get_temp_player(session)
|
||||||
if not player:
|
if not player or not player.phase == PlayerPhases.DN_SEND_CONNECT_INFO:
|
||||||
return
|
return
|
||||||
player.session.send_cframe_sack()
|
|
||||||
|
|
||||||
self.server.broadcast_message("%s: sorry bro, you're fucked." % player.name)
|
player.phase = PlayerPhases.DN_ACK_CONNECT_INFO
|
||||||
player.session.send_gamedata(b'\x3c\x56\xab\x31\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
|
self.server.add_player(player)
|
||||||
player.session.send_gamedata(b'\x01\x02\x57\xab\xa1\x96\x56\xab\x31\x96\x0e\x01\x00\x00\x00')
|
#player.session.send_cframe_sack()
|
||||||
"""player.session.send_gamedata(b'\x3d\x00\x5b\x53\x65\x72\x76\x65\x72\x5d') # [SERVER]
|
|
||||||
player.session.send_gamedata(b'\x3d\x01'+player.name.encode("ascii")+b"\x00") # playername
|
player.session.send_gamedata(b'\x3c'+struct.pack("<L", player.id)+b"\x00")
|
||||||
|
|
||||||
|
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 == player or pplayer.name == "[Server]":
|
||||||
|
continue
|
||||||
|
p1 += struct.pack("<B", pplayer.team)
|
||||||
|
p1 += b'\x00\x00'
|
||||||
|
|
||||||
|
player.session.send_gamedata(p1, acknow=False)
|
||||||
|
player.session.send_gamedata(b'\x3d\x00\x5b\x53\x65\x72\x76\x65\x72\x5d'+b"\x00"*25) # [SERVER]
|
||||||
|
#player.session.send_gamedata(b'\x3d\x01'+player.name.encode("ascii")+b"\x00") # playername
|
||||||
|
"""
|
||||||
player.session.send_gamedata(b'\x0f\x56\xab\x31\x96\x06\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
|
player.session.send_gamedata(b'\x0f\x56\xab\x31\x96\x06\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
|
||||||
player.session.send_gamedata(b'\x10\x02\x56\xab\x31\x96\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
|
player.session.send_gamedata(b'\x10\x02\x56\xab\x31\x96\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
|
||||||
player.session.send_gamedata(b'\x29\x28\x00\x00\x80\x32\x00\x00\x80\x05\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
|
player.session.send_gamedata(b'\x29\x28\x00\x00\x80\x32\x00\x00\x80\x05\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
|
||||||
@ -345,8 +361,28 @@ class Netserver:
|
|||||||
|
|
||||||
def get_player(self, iporaddr, port=None):
|
def get_player(self, iporaddr, port=None):
|
||||||
if type(iporaddr) == Session:
|
if type(iporaddr) == Session:
|
||||||
return next((x for x in self.server.players if x.session.ip == iporaddr.ip and x.session.port == iporaddr.port), None)
|
a = next((x for x in self.server.players if x.session.ip == iporaddr.ip and x.session.port == iporaddr.port), None)
|
||||||
if not port:
|
if a:
|
||||||
return next((x for x in self.server.players if x.session.ip == iporaddr[0] and x.session.port == iporaddr[1]), None)
|
return a
|
||||||
else:
|
else:
|
||||||
return next((x for x in self.server.players if x.session.ip == iporaddr and x.session.port == port), None)
|
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)
|
@ -50,7 +50,7 @@ class Session:
|
|||||||
self.connect_retry_timer = self.setup_Connect_Retry_Timer()
|
self.connect_retry_timer = self.setup_Connect_Retry_Timer()
|
||||||
|
|
||||||
def send_gamedata(self, bPayload, **kwargs):
|
def send_gamedata(self, bPayload, **kwargs):
|
||||||
acknow = kwargs.get("acknow", False)
|
acknow = kwargs.get("acknow", True)
|
||||||
dframe = DFrame()
|
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
|
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:
|
if acknow:
|
||||||
@ -60,6 +60,9 @@ class Session:
|
|||||||
dframe.NRcv = self.next_expected_seq
|
dframe.NRcv = self.next_expected_seq
|
||||||
dframe.Payload = bPayload
|
dframe.Payload = bPayload
|
||||||
self.send(dframe)
|
self.send(dframe)
|
||||||
|
# lock acquire
|
||||||
|
# lock release
|
||||||
|
return dframe
|
||||||
|
|
||||||
def send_cframe_connected(self, connect):
|
def send_cframe_connected(self, connect):
|
||||||
response = CFrame()
|
response = CFrame()
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
APPLICATION_GUID = b"\x10\x5e\x62\xa7\x96\x1a\xd2\x11\x9a\xfc\x00\x60\x08\x45\xe5\x71"
|
||||||
|
|
||||||
class Teams:
|
class Teams:
|
||||||
# 00: MvM
|
# 00: MvM
|
||||||
# 01: MvMvM
|
# 01: MvMvM
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
from .entity import Entity
|
from .entity import Entity
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class Player(Entity):
|
class Player(Entity):
|
||||||
@ -6,5 +16,7 @@ class Player(Entity):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.session = session
|
self.session = session
|
||||||
self.team = 0
|
self.team = 1
|
||||||
self.score = 0
|
self.score = 0
|
||||||
|
self.id = random.getrandbits(32)
|
||||||
|
self.phase = PlayerPhases.NONE
|
||||||
|
46
server.py
46
server.py
@ -16,27 +16,45 @@ class Server:
|
|||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.listen_ip = kwargs.get("ip", "0.0.0.0")
|
self.listen_ip = kwargs.get("ip", "0.0.0.0")
|
||||||
self.listen_port = kwargs.get("port", 19711)
|
self.listen_port = kwargs.get("port", 19711)
|
||||||
fake_session = Session(socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
|
|
||||||
fake_session.ip = "127.0.0.1"
|
|
||||||
fake_session.port = 3333
|
|
||||||
self.players = [Player("Amazed4", fake_session)]
|
|
||||||
self.currentmap = kwargs.get("map", Map("Three Way Island - Canyons.gck"))
|
|
||||||
self.maxplayers = kwargs.get("maxplayers", 20)
|
|
||||||
self.name = kwargs.get("name", "Default Server Name")
|
|
||||||
self.accept_new_players = True
|
|
||||||
self.register_with_ms = kwargs.get("register", False)
|
self.register_with_ms = kwargs.get("register", False)
|
||||||
self.teams = kwargs.get("teams", Teams.MvM)
|
self.teams = kwargs.get("teams", Teams.MvM)
|
||||||
self.game_type = kwargs.get("gametype", GameTypes.TeamDeathmatchWithFullBase)
|
self.game_type = kwargs.get("gametype", GameTypes.TeamDeathmatchWithFullBase)
|
||||||
|
self.currentmap = kwargs.get("map", Map("Three Way Island - Canyons.gck"))
|
||||||
|
self.maxplayers = kwargs.get("maxplayers", 20)
|
||||||
|
self.name = kwargs.get("name", "Default Server Name")
|
||||||
|
|
||||||
|
fake_session = Session(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)]
|
||||||
|
|
||||||
|
self.accept_new_players = True
|
||||||
self.version = 1.497
|
self.version = 1.497
|
||||||
self.points_per_kill = 1
|
self.points_per_kill = 3
|
||||||
self.points_per_capture = 5
|
self.points_per_capture = 5
|
||||||
self.detente_time = 0 # minutes
|
self.detente_time = 0 # minutes
|
||||||
self._plugins = []
|
self._plugins = []
|
||||||
|
self.tempplayers = []
|
||||||
|
self.running = True
|
||||||
|
self.ticks = 60
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
#logger.debug("Calling update")
|
||||||
|
pass
|
||||||
|
|
||||||
def add_player(self, player):
|
def add_player(self, player):
|
||||||
|
self.tempplayers.remove(player)
|
||||||
self.players.append(player)
|
self.players.append(player)
|
||||||
self._broadcast_event("on_player_join", player)
|
self._broadcast_event("on_player_join", player)
|
||||||
|
|
||||||
|
# todo: remove
|
||||||
|
def create_temp_player(self, player):
|
||||||
|
self.tempplayers.append(player)
|
||||||
|
|
||||||
|
def remove_player(self, player):
|
||||||
|
self.players.remove(player)
|
||||||
|
self._broadcast_event("on_player_left", player)
|
||||||
|
|
||||||
def add_plugin(self, plugin):
|
def add_plugin(self, plugin):
|
||||||
self._plugins.append(plugin)
|
self._plugins.append(plugin)
|
||||||
|
|
||||||
@ -54,12 +72,12 @@ class Server:
|
|||||||
func = getattr(plugin, event)
|
func = getattr(plugin, event)
|
||||||
if callable(func):
|
if callable(func):
|
||||||
try:
|
try:
|
||||||
func(args)
|
func(*args)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error("Could not call plugin function: "+plugin.__name__+"."+event)
|
logger.error("Could not call plugin function: "+plugin.__class__.__name__+"."+event)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
def load_plugins(self):
|
def _load_plugins(self):
|
||||||
plugins = os.listdir("plugins")
|
plugins = os.listdir("plugins")
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
if os.path.isdir("plugins/"+plugin) or plugin == "__init__.py":
|
if os.path.isdir("plugins/"+plugin) or plugin == "__init__.py":
|
||||||
@ -83,10 +101,10 @@ class Server:
|
|||||||
player.session.send_gamedata(b'\x35\x80\x81'+text.encode("ascii")+b'\x00\x00')
|
player.session.send_gamedata(b'\x35\x80\x81'+text.encode("ascii")+b'\x00\x00')
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.load_plugins()
|
self._load_plugins()
|
||||||
return Netserver(self).run()
|
return Netserver(self).run()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
server = Server(name="giantsd", maxplayers=10, register=False)
|
server = Server(name="giantsd", maxplayers=14, register=False)
|
||||||
server.run() # blocking
|
server.run() # blocking
|
Loading…
Reference in New Issue
Block a user