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")
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
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)
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")
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!")
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()
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)
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")
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
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)