Example #1
0
 def saveWorlds(self):
     Logger.debug("Starting Attempt to Save All Worlds",
                  module="world-save")
     if self.persistant and (self.server.config.worldSaveLocation
                             is not None):
         Logger.info("Saving All Worlds...", module="world-save")
         # Loop through all worlds and attempt to save!
         for worldName, world in self.worlds.items():
             Logger.debug(f"Trying To Save World {worldName}",
                          module="world-save")
             # Check persistance
             if world.persistant:
                 try:
                     # Saving World
                     Logger.info(f"Saving World {worldName}",
                                 module="world-save")
                     world.saveMap()
                 except Exception as e:
                     Logger.error(
                         f"Error While Saving World {worldName} - {type(e).__name__}: {e}",
                         module="world-save")
             else:
                 Logger.warn(
                     f"World {worldName} Is Non Persistant! Skipping World Save!",
                     module="world-save")
     else:
         Logger.warn(
             "World Manager Is Non Persistant! Skipping World Save!",
             module="world-save")
Example #2
0
    def closeWorlds(self):
        Logger.debug("Starting Attempt to Close All Worlds",
                     module="world-close")
        # Loop through all worlds and attempt to close
        for worldName in list(self.worlds.keys(
        )):  # Setting as list so dict size can change mid execution
            try:
                Logger.info(f"Closing World {worldName}", module="world-close")
                # Getting world obj
                world = self.worlds[worldName]

                # Removing world from dict
                Logger.debug("Removed world from dict", module="world-close")
                del self.worlds[worldName]

                # Checking if World and Server is Persistant
                if self.persistant and (self.server.config.worldSaveLocation
                                        is not None) and world.persistant:
                    # Closing worlds fileIO
                    Logger.debug("Closing World FileIO", module="world-close")
                    world.fileIO.close()
            except Exception as e:
                Logger.error(
                    f"Error While Closing World {worldName} - {type(e).__name__}: {e}",
                    module="world-close")
    def register(self, name: str, description: str, author: str, version: str,
                 dependencies: Optional[list], module: Type[AbstractModule]):
        Logger.info(f"Discovered Module {name}.", module="module-import")
        Logger.debug(f"Registering Module {name}", module="module-import")

        # Lowercase Name
        name = name.lower()
        # Checking If Module Is Already In Modules List
        if name in self._module_list.keys():
            raise InitRegisterError(
                f"Module {name} Has Already Been Registered!")
        # Check If Module Is Blacklisted
        if name in self._module_blacklist:
            return  # Skip
        # Format Empty Dependencies
        if dependencies is None:
            dependencies = []
        # Checking If Core Is Required
        if self._ensure_core:
            if "core" not in [m.NAME for m in dependencies] and name != "core":
                dependencies.append(Dependency("core"))

        # Attach Values As Attribute
        module.NAME = name
        module.DESCRIPTION = description
        module.AUTHOR = author
        module.VERSION = version
        module.DEPENDENCIES = dependencies
        self._module_list[name] = module
Example #4
0
    def __init__(self, server: Server, blacklist: List[str] = []):
        self.server = server
        self.worlds = dict()
        self.blacklist = blacklist
        self.persistant = True
        self.worldFormat = None

        # Get worldFormat Using Given World Format Key
        # Loop Through All World Formats
        for worldFormat in WorldFormats._format_list.values():
            # Check If key Matches With Config Key List
            if self.server.config.defaultSaveFormat.lower(
            ) in worldFormat.KEYS:
                # Set World Format
                self.worldFormat = worldFormat

        # Check If World Format Was Set
        if self.worldFormat is None:
            raise FatalError(
                f"Unknown World Format Key {self.server.config.defaultSaveFormat} Given In Server Config!"
            )
        Logger.info(f"Using World Format {self.worldFormat.NAME}",
                    module="init-world")

        # If World Location Was Not Given, Disable Persistance
        # (Don't Save / Load)
        if self.server.config.worldSaveLocation is None:
            Logger.warn(
                "World Save Location Was Not Defined. Creating Non-Persistant World!!!",
                module="init-world")
            self.persistant = False
    async def handlePlayerMessage(self, message: str):
        # Format, Process, and Handle incoming player message requests.
        Logger.debug(f"Handling Player Message From Player {self.name}", module="player")

        # Checking If Player Is Joined To A World
        if self.worldPlayerManager is None:
            Logger.debug(f"Player {self.name} Trying To handleBlockUpdate When No World Is Joined", module="player")
            return None  # Skip Rest

        # Checking If Message Is A Command
        if message[0] == "/":
            try:
                await self.handlePlayerCommand(message[1:])
            except CommandError as e:
                Logger.info(f"Command From Player {self.name} Failed With {str(e)}", module="command")
                await self.sendMessage(f"&c{str(e)}")
            return None  # Skip Rest

        # Check If Last Character Is '&' (Crashes All Clients)
        if message[-1:] == "&":
            message = message[:-1]  # Cut Last Character

        if len(message) > 32:  # Cut Message If Too Long
            # Cut Message In Half, then print each half
            await self.worldPlayerManager.sendWorldMessage(message[:32] + " - ", author=self)
            await self.worldPlayerManager.sendWorldMessage(" - " + message[32:], author=self)
            await self.sendMessage("&eWARN: Message Was Cut To Fit On Screen&f")
        else:
            await self.worldPlayerManager.sendWorldMessage(message, author=self)
Example #6
0
 def saveMap(self):
     if self.persistant:
         Logger.info(f"Attempting To Save World {self.name}",
                     module="world-save")
         self.worldManager.worldFormat.saveWorld(self, self.fileIO,
                                                 self.worldManager)
     else:
         Logger.warn(f"World {self.name} Is Not Persistant! Not Saving.",
                     module="world-save")
Example #7
0
 async def run(self):
     try:
         if self.initialized:
             # Start Server
             Logger.info(f"Starting Server {self.name} On {self.address} Port {self.port}", module="obsidian")
             await self.server.serve_forever()
         else:
             raise ServerError("Server Did Not Initialize. This May Be Because server.init() Was Never Called Or An Error Occurred While Initializing")
     except ServerError as e:
         Logger.fatal(f"Error While Starting Server - {type(e).__name__}: {e}", module="server", printTb=False)
     except Exception as e:
         Logger.fatal(f"Error While Starting Server - {type(e).__name__}: {e}", module="server")
    async def _initConnection(self):
        # Log Connection
        Logger.info(f"New Connection From {self.ip}", module="network")

        # Check if user is IP banned
        if self.ip[0] in self.server.config.ipBlacklist:
            Logger.info(f"IP {self.ip} Is Blacklisted. Kicking!",
                        module="network")
            raise ClientError("Your IP Has Been Blacklisted From The Server!")

        # Start the server <-> client login protocol
        Logger.debug(f"{self.ip} | Starting Client <-> Server Handshake",
                     module="network")
        await self._handleInitialHandshake()
    async def handlePlayerCommand(self, cmdMessage: str):
        # Format, Process, and Handle incoming player commands.
        Logger.debug(f"Handling Command From Player {self.name}", module="command")

        # Splitting Command Data
        cmdName, *cmdArgs = cmdMessage.split(" ")
        Logger.info(f"Command {cmdName} Received From Player {self.name}", module="command")
        Logger.debug(f"Handling Command {cmdName} With Arguments {cmdArgs}", module="command")

        # Get Command Object
        command = Commands.getCommandFromName(cmdName)

        # Parse Command Arguments
        parsedArguments, parsedKwArgs = _parseArgs(command, cmdArgs)

        # Run Command
        try:
            await command.execute(self, *parsedArguments, **parsedKwArgs)
        except Exception as e:
            Logger.error(f"Command {command.NAME} Raised Error {str(e)}", module="command")
            await self.sendMessage("&cAn Unknown Internal Server Error Has Occurred!")
Example #10
0
    def createWorld(
            self,
            worldName,
            sizeX,
            sizeY,
            sizeZ,
            generator: AbstractMapGenerator,
            *args,  # Arguments To Be Passed To World Generator
            persistant=True,
            spawnX: Optional[int] = None,
            spawnY: Optional[int] = None,
            spawnZ: Optional[int] = None,
            spawnPitch: Optional[int] = None,
            spawnYaw: Optional[int] = None,
            **kwargs  # Keyword Arguments To Be Passed To World Generator
    ):
        Logger.info(f"Creating New World {worldName}...",
                    module="world-create")
        # Check If World Already Exists
        if worldName in self.worlds.keys():
            raise WorldError(
                f"Trying To Generate World With Already Existing Name {worldName}!"
            )

        # Creating Save File If World Is Persistant
        Logger.debug("Creating Save File If World Is Persistant",
                     module="world-create")
        if self.persistant:
            fileIO = self.createWorldFile(self.server.config.worldSaveLocation,
                                          worldName)
            Logger.debug(f"World Is Persistant! Created New FileIO {fileIO}",
                         module="world-create")
            pass
        else:
            Logger.debug("World Is Not Persistant! Creating FileIO",
                         module="world-create")
            fileIO = None

        # Create World
        self.worlds[worldName] = World(
            self,  # Pass In World Manager
            worldName,  # Pass In World Name
            sizeX,
            sizeY,
            sizeZ,  # Passing World X, Y, Z
            self.generateMap(sizeX, sizeY, sizeZ, generator, *args,
                             **kwargs),  # Generating Map Data
            # Spawn Information
            spawnX=spawnX,
            spawnY=spawnY,
            spawnZ=spawnZ,
            spawnPitch=spawnPitch,
            spawnYaw=spawnYaw,
            # World Config Info
            generator=generator,  # Pass In World Generator
            persistant=persistant,  # Pass In Persistant Flag
            fileIO=fileIO  # Pass In FileIO Object (if persistant set)
        )

        # Saving World
        Logger.info(f"Saving World {worldName}", module="world-create")
        self.worlds[worldName].saveMap()
Example #11
0
    def __init__(self,
                 worldManager: WorldManager,
                 name: str,
                 sizeX: int,
                 sizeY: int,
                 sizeZ: int,
                 mapArray: bytearray,
                 spawnX: Optional[int] = None,
                 spawnY: Optional[int] = None,
                 spawnZ: Optional[int] = None,
                 spawnYaw: Optional[int] = None,
                 spawnPitch: Optional[int] = None,
                 generator: AbstractMapGenerator = None,
                 persistant: bool = False,
                 fileIO: Optional[io.BufferedRandom] = None,
                 canEdit: bool = True,
                 maxPlayers: int = 250,
                 displayName: str = None,
                 uuid: str = None):
        # Y is the height
        self.worldManager = worldManager
        self.name = name
        self.generator = generator
        self.sizeX = sizeX
        self.sizeY = sizeY
        self.sizeZ = sizeZ
        self.spawnX = spawnX
        self.spawnZ = spawnZ
        self.spawnY = spawnY
        self.spawnYaw = spawnYaw
        self.spawnPitch = spawnPitch
        self.mapArray = mapArray
        self.persistant = persistant
        self.fileIO = fileIO
        self.canEdit = canEdit
        self.maxPlayers = maxPlayers
        self.displayName = displayName  # Displayname for CW Capability
        self.uuid = uuid  # UUID for CW Capability

        # Check if file IO was given if persistant
        if self.persistant:
            if self.fileIO is None:
                # Setting persistance to false because fileIO was not given
                self.persistant = False
                Logger.error(
                    f"World Format {self.worldManager.worldFormat.NAME} Created Persistant World Without Providing FileIO! Please Report To Author! Setting World As Non-Persistant.",
                    "world-load")
                Logger.askConfirmation()
            else:
                Logger.debug(f"Persistant World Has FileIO {self.fileIO}",
                             "world-load")

        # Generate/Set Spawn Coords
        # Set spawnX
        if self.spawnX is None:
            # Generate SpawnX (Set to middle of map)
            self.spawnX = (self.sizeX // 2) * 32 + 51
            Logger.verbose(
                f"spawnX was not provided. Generated to {self.spawnX}",
                "world-load")

        # Set spawnZ
        if self.spawnZ is None:
            # Generate SpawnZ (Set to middle of map)
            self.spawnZ = (self.sizeZ // 2) * 32 + 51
            Logger.verbose(
                f"spawnZ was not provided. Generated to {self.spawnZ}",
                "world-load")

        # Set spawnY
        if self.spawnY is None:
            # Kinda hacky to get the block coords form the in-game coords
            self.spawnY = (self.getHighestBlock(round(
                (self.spawnX - 51) / 32), round(
                    (self.spawnZ - 51) / 32)) + 1) * 32 + 51
            Logger.verbose(
                f"spawnY was not provided. Generated to {self.spawnY}",
                "world-load")

        # Set spawnYaw
        if spawnYaw is None:
            # Generate SpawnYaw (0)
            self.spawnYaw = 0
            Logger.verbose(
                f"spawnYaw was not provided. Generated to {self.spawnYaw}",
                "world-load")

        # Set spawnPitch
        if spawnPitch is None:
            # Generate SpawnPitch (0)
            self.spawnPitch = 0
            Logger.verbose(
                f"spawnYaw was not provided. Generated to {self.spawnYaw}",
                "world-load")

        # Initialize WorldPlayerManager
        Logger.info("Initializing World Player Manager", module="init-world")
        self.playerManager = WorldPlayerManager(self)
Example #12
0
 def loadWorlds(self):
     Logger.debug("Starting Attempt to Load All Worlds",
                  module="world-load")
     if self.persistant and (self.server.config.worldSaveLocation
                             is not None):
         Logger.debug(
             f"Beginning To Scan Through {self.server.config.worldSaveLocation} Dir",
             module="world-load")
         # Loop Through All Files Given In World Folder
         for filename in os.listdir(
                 os.path.join(SERVERPATH,
                              self.server.config.worldSaveLocation)):
             # Get Pure File Name (No Extentions)
             saveName = os.path.splitext(os.path.basename(filename))[0]
             Logger.verbose(
                 f"Checking Extention and Status of World File {filename}",
                 module="world-load")
             # Check If File Type Matches With The Extentions Provided By worldFormat
             if not any([
                     filename.endswith(ext)
                     for ext in self.worldFormat.EXTENTIONS
             ]):
                 Logger.debug(
                     f"Ignoring World File {filename}. File Extention Not Known!",
                     module="world-load")
             # Also Check If World Is Blacklisted
             elif saveName in self.server.config.worldBlacklist:
                 Logger.info(
                     f"Ignoring World File {filename}. World Name Is Blacklisted!",
                     module="world-load")
             # Also Check If World Name Is Already Loaded (Same File Names with Different Extentions)
             elif saveName in self.worlds.keys():
                 Logger.warn(
                     f"Ignoring World File {filename}. World With Similar Name Has Already Been Registered!",
                     module="world-load")
                 Logger.warn(
                     f"World File {os.path.basename(self.worlds[saveName].fileIO.name)} Conflicts With World File {filename}!",
                     module="world-load")
                 Logger.askConfirmation()
             else:
                 Logger.debug(
                     f"Detected World File {filename}. Attempting To Load World",
                     module="world-load")
                 # (Attempt) To Load Up World
                 try:
                     Logger.info(f"Loading World {saveName}",
                                 module="world-load")
                     fileIO = open(
                         os.path.join(SERVERPATH,
                                      self.server.config.worldSaveLocation,
                                      filename), "rb+")
                     self.worlds[saveName] = self.worldFormat.loadWorld(
                         fileIO, self, persistant=self.persistant)
                 except Exception as e:
                     Logger.error(
                         f"Error While Loading World {filename} - {type(e).__name__}: {e}",
                         module="world-load")
                     Logger.askConfirmation()
         # Check If Default World Is Loaded
         if self.server.config.defaultWorld not in self.worlds.keys():
             # Check if other worlds were loaded as well
             if len(self.worlds.keys()) > 0:
                 Logger.warn(
                     f"Default World {self.server.config.defaultWorld} Not Loaded.",
                     module="world-load")
                 Logger.warn(
                     "Consider Checking If World Exists. Consider Changing The Default World and/or File Format In Config.",
                     module="world-load")
                 # Ask User If They Want To Continue With World Generation
                 Logger.warn(
                     f"Other Worlds Were Detected. Generate New World With Name {self.server.config.defaultWorld}?",
                     module="world-load")
                 Logger.askConfirmation(message="Generate New World?")
             else:
                 Logger.warn(
                     "No Existing Worlds Were Detected. Generating New World!",
                     module="world-load")
             # Generate New World
             defaultWorldName = self.server.config.defaultWorld
             defaultGenerator = MapGenerators[
                 self.server.config.defaultGenerator]
             Logger.debug(f"Creating World {defaultWorldName}",
                          module="world-load")
             self.createWorld(
                 defaultWorldName,
                 self.server.config.defaultWorldSizeX,
                 self.server.config.defaultWorldSizeY,
                 self.server.config.defaultWorldSizeZ,
                 defaultGenerator,
                 persistant=self.persistant,
                 grassHeight=self.server.config.defaultWorldSizeY // 2)
     else:
         Logger.debug("World Manager Is Non Persistant!",
                      module="world-load")
         # Create Non-Persistant Temporary World
         defaultWorldName = self.server.config.defaultWorld
         defaultGenerator = MapGenerators[
             self.server.config.defaultGenerator]
         Logger.debug(f"Creating Temporary World {defaultWorldName}",
                      module="world-load")
         self.createWorld(
             defaultWorldName,
             self.server.config.defaultWorldSizeX,
             self.server.config.defaultWorldSizeY,
             self.server.config.defaultWorldSizeZ,
             defaultGenerator,
             persistant=False,
             maxPlayers=self.server.config.worldMaxPlayers,
             grassHeight=self.server.config.defaultWorldSizeY // 2)
    def initModules(self, blacklist: List[str] = [], ensureCore: bool = True):
        # Setting Vars
        self._ensure_core = ensureCore
        self._module_blacklist = blacklist

        # --- PreInitialization ---
        Logger.info("=== (1/6) PreInitializing Modules ===",
                    module="init-modules")

        # Initialization Step One => Scanning and Loading Modules using PkgUtils
        Logger.info(f"Scanning modules in {MODULESFOLDER}",
                    module="module-import")
        self._importModules()

        # --- Dependency Resolving ---
        Logger.info("=== (2/6) Resolving Dependencies ===",
                    module="init-modules")

        # Initialization Part Two => Checking and Initializing Dependencies
        Logger.info("Checking and Initializing Dependencies...",
                    module="module-resolve")
        self._initDependencies()
        Logger.info("Dependencies Initialized!", module="module-resolve")

        # Initialization Part Two and a Half => Resolving Dependency Cycles
        Logger.info("Resolving Dependency Cycles...", module="module-verify")
        self._resolveDependencyCycles()
        Logger.info("Cycles Resolved!", module="module-verify")

        # --- Initialization Preparation ---
        Logger.info("=== (3/6) Preparing Initialization ===",
                    module="init-modules")

        # Initialization Part Three => Building Dependency Graph
        Logger.info("Building Dependency Graph...", module="module-prep")
        self._buildDependencyGraph()
        Logger.info("Dependency Graph Generated!", module="module-prep")

        Logger.info("Dependencies Resolved!", module="module-resolve")
        Logger.info("PreInitializing Done!", module="module-preinit")

        # --- Initialization (Submodules) ---
        Logger.info("=== (4/6) Initializing Submodules ===",
                    module="init-modules")

        # Initialization Part Four => Initialize Submodules
        Logger.info("Initializing Submodules...", module="submodule-init")
        self._initSubmodules()
        Logger.info("Submodules Initialized!", module="submodule-init")

        # --- Initialization (Modules) ---
        Logger.info("=== (5/6) Initializing Submodules ===",
                    module="init-modules")

        # Initialization Part Five => Initialize Modules
        Logger.info("Initializing Modules...", module="module-init")
        self._initModules()
        Logger.info("Modules Initialized!", module="module-init")

        Logger.info("Initializing Done!", module="module-init")

        # --- Finalizing Initialization ---
        Logger.info("=== (6/6) Finalizing Initialization ===",
                    module="init-modules")

        # Initialization Part Six => Running Post-Initialization
        Logger.info("Running Post-Initializing...", module="post-init")
        self._postInit()
        Logger.info("Post-Initializing Done!...", module="post-init")

        Logger.info("Module Done Finalizing!", module="module-init")
Example #14
0
    async def _init(self):
        Logger.info(f"=== Initializing Server '{self.name}' ===", module="init")

        # Testing If Debug Is Enabled
        Logger.debug("Debug Is Enabled", module="init")
        Logger.verbose("Verbose Is Enabled", module="init")
        Logger.info("Use '-d' and/or '-v' To Enable Debug Mode Or Verbose Mode", module="init")

        # Initializing Config
        Logger.info("Initializing Server Config", module="init")
        # Ensuring Config Path
        self._ensureFileStructure(os.path.dirname(self.config.configPath))
        # Initing Config
        self.config.init()

        # Setting Up File Structure
        Logger.info("Setting Up File Structure", module="init")
        if self.config.worldSaveLocation is not None:
            self.ensureFiles.append(MODULESFOLDER)
            self.ensureFiles.append(self.config.worldSaveLocation)
        self._ensureFileStructure(self.ensureFiles)

        # Load and Log Modules
        Logger.info("Starting Module Initialization", module="init")
        ModuleManager.initModules(blacklist=self.config.moduleBlacklist, ensureCore=True)
        Logger.info("All Modules Initialized!!!", module="init")

        Logger.info(f"{ModuleManager.numModules} Modules Initialized", module="init")
        Logger.info(f"{PacketManager.numPackets} Packets Initialized", module="init")
        Logger.info(f"{BlockManager.numBlocks} Blocks Initialized", module="init")
        Logger.info(f"{CommandManager.numCommands} Commands Initialized", module="init")
        Logger.info(f"{MapGeneratorManager.numMapGenerators} Map Generators Initialized", module="init")

        # Print Pretty List of All Modules
        Logger.info(f"Module List:\n{ModuleManager.generateTable()}", module="init")
        # Only Print Packet And World Generators List If Debug Enabled
        if Logger.DEBUG:
            Logger.debug(f"Packets List:\n{PacketManager.generateTable()}", module="init")
            Logger.debug(f"World Formats List:\n{WorldFormatManager.generateTable()}", module="init")
            Logger.debug(f"Map Generators List:\n{MapGeneratorManager.generateTable()}", module="init")
            Logger.debug(f"Commands List:\n{CommandManager.generateTable()}", module="init")

        # Only Print Block List If Verbose Enabled (Very Big)
        if Logger.VERBOSE:
            Logger.verbose(f"Blocks List:\n{BlockManager.generateTable()}", module="init")

        # Printing Error If Error Occurs During Init
        if len(ModuleManager._error_list) != 0:
            Logger.warn("Some Modules Files Failed To Load!\n", module="init")
            Logger.warn("!!! Failed Modules May Cause Compatibility Issues And/Or Data Corruption !!!\n", module="init-module")
            Logger.warn(f"Failed: {ModuleManager._error_list}\n", module="init")
            Logger.askConfirmation()

        # Initialize WorldManager
        Logger.info("Initializing World Manager", module="init")
        self.worldManager = WorldManager(self, blacklist=self.config.worldBlacklist)
        Logger.info("Loading Worlds", module="init")
        self.worldManager.loadWorlds()

        # Initialize PlayerManager
        Logger.info("Initializing Player Manager", module="init")
        self.playerManager = PlayerManager(self, maxSize=self.config.serverMaxPlayers)

        # Create Asyncio Socket Server
        # When new connection occurs, run callback _getConnHandler
        Logger.info(f"Setting Up Server {self.name}", module="init")
        self.server = await asyncio.start_server(self._getConnHandler(), self.address, self.port)

        self.initialized = True
Example #15
0
    async def stop(self):
        try:
            # Setting initialized to false to prevent multiple ctl-c
            if self.stopping:
                # Ignoring Repetitive Ctl-Cs
                return None

            if not self.initialized:
                Logger.info("Trying to shut down server that is not initialized!", module="server-stop")
                Logger.info("Skipping Shutdown and Cleanup Procedure", module="server-stop")
                sys.exit(0)
                return None

            # Preparing to stop server!
            Logger.info("Stopping Server...", module="server-stop")
            self.initialized = False
            self.stopping = True

            # Sending Disconnect Packet To All Server Members
            Logger.info("Sending Disconnect Packet To All Members", module="server-stop")
            await self.playerManager.sendGlobalPacket(
                Packets.Response.DisconnectPlayer,
                "Disconnected: Server Shutting Down"
            )

            # Stopping Connection Handler
            Logger.info("Stopping Connection Handler Loop", module="server-stop")
            if self.server is not None:
                self.server.close()

            # Saving Worlds
            Logger.info("Saving All Worlds", module="server-stop")
            self.worldManager.saveWorlds()

            # Closing Worlds
            Logger.info("Closing All Worlds", module="server-stop")
            self.worldManager.closeWorlds()

            # Closing Server
            Logger.info("Terminating Process", module="server-stop")
            Logger.log("Goodbye!")
            sys.exit(0)
        except Exception as e:
            # Server Stop Failed! Last Ditch Attempt To Clean Up
            # Not Using Logger Incase Thats What Breaks It
            print(f"Error occurred while stopping server - {type(e).__name__}: {e}")
            print("Force Terminating Server")
            sys.exit(0)