diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..599e40e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +maps/*.gck \ No newline at end of file diff --git a/dpnet/netserver.py b/dpnet/netserver.py index 5e73ef1..da1f6f4 100644 --- a/dpnet/netserver.py +++ b/dpnet/netserver.py @@ -30,9 +30,10 @@ class Netserver: gameserverthread = threading.Thread(target=self.handle_packets) gameserverthread.start() - #ms = MasterServer(self.server) - #statsserverthread = threading.Thread(target=ms.register_and_run) - #statsserverthread.start() + if self.server.register_with_ms: + ms = MasterServer(self.server) + ms.register() + gameserverthread.join() #statsserverthread.join() @@ -74,7 +75,7 @@ class Netserver: er.ApplicationInstanceGUID = self.guid er.ApplicationGUID = eq.ApplicationGUID er.ApplicationReservedData = b'\xff' # Map ID - er.ApplicationReservedData += b'\x00\x04\x00' + er.ApplicationReservedData += b'\x00\x04\x00' # game type and teams er.ApplicationReservedData += b'\xd9\x05' # 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 diff --git a/giants/__init__.py b/giants/__init__.py index e69de29..06c7bef 100644 --- a/giants/__init__.py +++ b/giants/__init__.py @@ -0,0 +1,25 @@ +class Teams: + MvM = 0x00 + MvMvM = 0x01 + RvR = 0x02 + MvR = 0x03 + MvRvK = 0x04 + MvK = 0x05 + RvK = 0x06 + TeamB = 0x07 + Crash = 0x5a + + +class GameTypes: + TeamDeathmatch = 0x00 + TeamDeathmatchWithFullBase = 0x01 + CaptureSmartie = 0x02 + CaptureSmartieWithFullBase = 0x03 + BaseBuildDeathmatch = 0x04 + BaseBuildCaptureSmartie = 0x05 + DefendBase = 0x06 + DefendBaseCaptureSmartie = 0x07 + GTypeStone = 0x08 + GTypeWood = 0x09 + Crash = 0x0a + GTypeNull = 0x0c \ No newline at end of file diff --git a/giants/map.py b/giants/map.py index 0380516..34dc3af 100644 --- a/giants/map.py +++ b/giants/map.py @@ -1,11 +1,24 @@ -class Map: - def __init__(self, mapname="Unknown mapname"): - self.mapname = mapname - self.checksum = None +import os - def load_map(self, mapname): - self.checksum = Map.checksum(mapname) + +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(mapname): + # TODO return 1 diff --git a/giants/masterserver.py b/giants/masterserver.py index ac99863..8961de4 100644 --- a/giants/masterserver.py +++ b/giants/masterserver.py @@ -1,5 +1,6 @@ import socket from dpnet.packet import Packet +import threading class MasterServer: @@ -9,22 +10,27 @@ 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): self.socket.bind((self.server.listen_ip, 8911)) print("Listening to " + self.server.listen_ip + ":" + str(8911)) print("Registering to Master Server") - self.register() + self._register() self.keepalive() self.handle_packets() - def register(self): + def _register(self): packet = Packet() - packet.write("019711".encode("ascii")) + 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("119711".encode("ascii")) + packet.write(("1"+str(self.server.listen_port)).encode("ascii")) self.socket.sendto(packet.getvalue(), (self.masterserverip, self.masterserverport)) def handle_packets(self): diff --git a/plugins/__init__.py b/plugins/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/greetings.py b/plugins/greetings.py new file mode 100644 index 0000000..8c313d1 --- /dev/null +++ b/plugins/greetings.py @@ -0,0 +1,14 @@ +class Greetings: + def __init__(self, server): + self.server = server + + def on_player_join(self, player): + self.server.broadcast_message("Welcome "+player.name+"!") + + def on_map_change(self, newmap): + self.server.broadcast_message("You are now playing on "+newmap.mapname) + + +def setup(server): + plugin = Greetings(server) + server.add_plugin(plugin) \ No newline at end of file diff --git a/server.py b/server.py index ac13db3..ef78419 100644 --- a/server.py +++ b/server.py @@ -2,8 +2,14 @@ from giants.map import Map from giants.player import Player from dpnet.netserver import Netserver from dpnet.session import Session -from dpnet.DFrame import DFrame import socket +import importlib +import os +from giants import Teams, GameTypes +from utils.logger import setup_logger +import traceback + +logger = setup_logger(__name__) class Server: @@ -14,51 +20,69 @@ class Server: fake_session.ip = "127.0.0.1" fake_session.port = 3333 self.players = [Player("Amazed4", fake_session)] - self.currentmap = kwargs.get("map", Map("wow rly")) + 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 = False + self.register_with_ms = kwargs.get("register", False) + self.teams = kwargs.get("teams", Teams.MvM) + self.game_type = kwargs.get("gametype", GameTypes.TeamDeathmatchWithFullBase) + self._plugins = [] - # events - self._on_new_player = [] - self._on_new_map = [] - - def new_player(self, player): + def add_player(self, player): self.players.append(player) - for func in self._on_new_player: - func(player) + self._broadcast_event("on_player_join", player) - def change_map(self, mapname): - for func in self._on_new_map: - func(mapname) + def add_plugin(self, plugin): + self._plugins.append(plugin) - def on_new_player(self, func): - self._on_new_player.append(func) - return func + def change_map(self, mappath): + try: + self.currentmap = Map(mappath) + self._broadcast_event("on_map_change", self.currentmap) + except Exception: + logger.error("Could not change map") + + 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: + func(args) + except Exception: + logger.error("Could not call plugin function: "+plugin.__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.warn("Could not load plugin "+plugin) + traceback.print_exc() def broadcast_message(self, text): for player in self.players: player.session.send_gamedata(b'\x35\x80\x81'+text.encode("ascii")+b'\x00\x00') - def on_new_map(self, func): - self._on_new_map.append(func) - return func - def run(self): + self.load_plugins() return Netserver(self).run() if __name__ == '__main__': - server = Server(name="giantsd", map=Map("expect crashes"), maxplayers=10) - - @server.on_new_player - def new_player(player): - #print("A NEW PLAYER HAS JOINED: %s" % player) - pass - - @server.on_new_map - def new_map(mapname): - pass - + server = Server(name="giantsd", maxplayers=10, register=False) server.run() # blocking \ No newline at end of file