Moar things

This commit is contained in:
Amazed 2019-02-05 01:35:22 +01:00
parent 831a4dbf9d
commit 024712cc8d
6 changed files with 360 additions and 112 deletions

View 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()

View File

@ -10,8 +10,12 @@ import threading
import traceback
import uuid
import struct
from _datetime import datetime
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__)
@ -23,21 +27,35 @@ class Netserver:
self.server = server
self.addrs = []
self.guid = uuid.uuid4().bytes
self.incoming_packets = []
def run(self):
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)
gameserverthread = threading.Thread(target=self.handle_packets)
gameserverthread.start()
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)
gameserverthread.join()
#statsserverthread.join()
networkthread.join()
def read_packets(self):
pass
def send_packets(self):
pass
def handle_packets(self):
while True:
@ -79,7 +97,7 @@ class Netserver:
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\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\x01: <custom map>
# \x9c\x53\xf4\xdd: Three Way Island - Canyons
@ -101,6 +119,7 @@ class Netserver:
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:
# CONNECT CFRAME
if session and session.Full:
@ -123,6 +142,17 @@ class Netserver:
session.port = addr[1]
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:
# CONNECTED CFRAME
@ -137,6 +167,7 @@ class Netserver:
return
session.Full = True # fully connected
session.cancel_Connect_Retry_Timer()
player.phase = PlayerPhases.CFRAME_CONNECTED
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_SACK:
if not session or not session.Full:
@ -215,102 +246,87 @@ class Netserver:
return
def handle_game_packet(self, session, payload):
#logger.debug("Payload: %s", payload)
player = self.get_temp_player(session)
if payload[0] == 0xc1:
# connect frame?
_ = payload[0:4]
_ = payload[4:8]
_ = payload[8:12]
_ = payload[12:16]
_ = payload[16:20]
_ = payload[20:52]
instanceguid = payload[52:68]
gameguid = payload[68:84]
_ = payload[84:88]
_ = payload[88:92]
name = payload[92:]
name = name.decode("utf-16-le")[0:-1]
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+4+dwNameSize]
name = name.decode("utf-16-le")
logger.debug("%s connected!", name)
player = Player(name, session)
self.server.new_player(player)
player.name = name
session.send_dframe_keepalive()
r = DFrame()
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
r.Control = 0x00
r.Seq = session.next_send
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"))
# 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
r.Payload = payload.getvalue()
session.send(r)
appdata = Packet()
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:
player = self.get_player(session)
if not player:
player = self.get_temp_player(session)
if not player or not player.phase == PlayerPhases.DN_SEND_CONNECT_INFO:
return
player.session.send_cframe_sack()
self.server.broadcast_message("%s: sorry bro, you're fucked." % player.name)
player.session.send_gamedata(b'\x3c\x56\xab\x31\x96\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
player.session.send_gamedata(b'\x01\x02\x57\xab\xa1\x96\x56\xab\x31\x96\x0e\x01\x00\x00\x00')
"""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.phase = PlayerPhases.DN_ACK_CONNECT_INFO
self.server.add_player(player)
#player.session.send_cframe_sack()
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'\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
@ -345,8 +361,28 @@ class Netserver:
def get_player(self, iporaddr, port=None):
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 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:
return next((x for x in self.server.players if x.session.ip == iporaddr[0] and x.session.port == iporaddr[1]), None)
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:
return next((x for x in self.server.players if x.session.ip == iporaddr and x.session.port == port), None)
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)

View File

@ -50,7 +50,7 @@ class Session:
self.connect_retry_timer = self.setup_Connect_Retry_Timer()
def send_gamedata(self, bPayload, **kwargs):
acknow = kwargs.get("acknow", False)
acknow = kwargs.get("acknow", True)
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:
@ -60,6 +60,9 @@ class Session:
dframe.NRcv = self.next_expected_seq
dframe.Payload = bPayload
self.send(dframe)
# lock acquire
# lock release
return dframe
def send_cframe_connected(self, connect):
response = CFrame()

View File

@ -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:
# 00: MvM
# 01: MvMvM

View File

@ -1,4 +1,14 @@
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):
@ -6,5 +16,7 @@ class Player(Entity):
super().__init__()
self.name = name
self.session = session
self.team = 0
self.team = 1
self.score = 0
self.id = random.getrandbits(32)
self.phase = PlayerPhases.NONE

View File

@ -16,27 +16,45 @@ class Server:
def __init__(self, **kwargs):
self.listen_ip = kwargs.get("ip", "0.0.0.0")
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.teams = kwargs.get("teams", Teams.MvM)
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.points_per_kill = 1
self.points_per_kill = 3
self.points_per_capture = 5
self.detente_time = 0 # minutes
self._plugins = []
self.tempplayers = []
self.running = True
self.ticks = 60
def update(self):
#logger.debug("Calling update")
pass
def add_player(self, player):
self.tempplayers.remove(player)
self.players.append(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):
self._plugins.append(plugin)
@ -54,12 +72,12 @@ class Server:
func = getattr(plugin, event)
if callable(func):
try:
func(args)
func(*args)
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()
def load_plugins(self):
def _load_plugins(self):
plugins = os.listdir("plugins")
for plugin in plugins:
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')
def run(self):
self.load_plugins()
self._load_plugins()
return Netserver(self).run()
if __name__ == '__main__':
server = Server(name="giantsd", maxplayers=10, register=False)
server = Server(name="giantsd", maxplayers=14, register=False)
server.run() # blocking