diff --git a/firebase_manager.py b/firebase_manager.py index 2c076ad..463e72b 100644 --- a/firebase_manager.py +++ b/firebase_manager.py @@ -1,3 +1,4 @@ +import time from datetime import datetime from typing import Union @@ -7,6 +8,7 @@ from firebase_admin import auth, credentials, firestore from google.api_core.exceptions import Aborted, DataLoss, NotFound, OutOfRange, PermissionDenied, ResourceExhausted import file_manager +from generic_executor import mc_manager cred = credentials.Certificate('secrets/servii.json') app = firebase_admin.initialize_app(cred) @@ -185,5 +187,22 @@ def log_exception_to_firestore(exception: Exception = None, user_id: str = None, print(f"Failed to add log entry: {e}") +def close_idle_server(user_id: Union[str, None], name: Union[str, None], port: int, + server_stamp: Union[float, None]) -> None: + if any(var is None for var in (user_id, name, server_stamp)): + return + try: + mc_manager.stop_server(port) + update_server_running_state(user_id, name, False) + now: float = time.time() + elapsed_seconds = now - server_stamp + hours = int(elapsed_seconds // 3600) + minutes = int((elapsed_seconds % 3600) // 60) + seconds = round(elapsed_seconds % 60, 2) + file_manager.log_action(user_id, name, "ServerStop (idle)", + f"Suspended inactive server activities. Uptime : {hours}h {minutes}m {seconds}s") + except Exception as e: + print(e, user_id, name, server_stamp) + if __name__ == "__main__": pass diff --git a/generic_executor.py b/generic_executor.py index fd69c1f..1e309d2 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -1,7 +1,6 @@ -import json import os from http import HTTPStatus -from typing import Union, Tuple, Dict +from typing import Union from cloudflare.types.dns import SRVRecord from firebase_admin.auth import UserRecord @@ -218,7 +217,7 @@ def server_run(user: UserRecord, name: str) -> tuple[HTTPStatus, Union[str, None mc_manager.set_cooldown(user_id=user_id) try: port: int = firebase_manager.get_server_port(user_id) - server_id = mc_manager.start_server(f"users/{user_id}/{name}", port) + server_id = mc_manager.start_server(f"users/{user_id}/{name}", port, user_id, name) if server_id is None: return HTTPStatus.OK, f"You cannot run multiples instances at this time." mc_manager.servers[server_id]['port'] = int(port) @@ -302,5 +301,8 @@ def run_command(user: UserRecord, command: str, name: str) -> tuple[HTTPStatus, return HTTPStatus.INTERNAL_SERVER_ERROR, f"Error executing command: {command} || {str(e)}" +def scheduled_actions() -> None: + mc_manager.check_servers_idle() + if __name__ == "__main__": pass diff --git a/server_mc_manager.py b/server_mc_manager.py index df94b76..7e7421c 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -5,6 +5,9 @@ import mcipc.query import mcipc.query.client from typing import Union +import firebase_manager +import generic_executor + class MinecraftServerManager: allowed_properties: list[str] = ["difficulty", "gamemode", "force-gamemode", "hardcore", "generate-structures", @@ -14,8 +17,9 @@ class MinecraftServerManager: self.servers: dict = {} self.servers_count: int = 0 self.cooldowns = {} + self.offline_ports: list[int] = [] - def start_server(self, server_directory: str, port: int, + def start_server(self, server_directory: str, port: int, user_id: str, server_name: str, java_executable='java', jar_file='server.jar', memory_size='2048M') -> Union[int, None]: if port in self.servers: @@ -38,7 +42,10 @@ class MinecraftServerManager: self.servers[port] = { 'process': process, 'directory': server_directory, - 'port': port + 'port': port, + 'user_id': user_id, + 'name': server_name, + 'time': time.time(), } return port @@ -56,6 +63,8 @@ class MinecraftServerManager: process = self.servers[port]['process'] process.communicate(input=b"stop\n") del self.servers[port] + if port in self.offline_ports: + self.offline_ports.remove(port) return True def stop_server_forcefully(self, port) -> bool: @@ -98,6 +107,34 @@ class MinecraftServerManager: del self.cooldowns[user_id] return False + def check_servers_idle(self) -> None: + servers: dict = self.servers.copy() + + for port, server_info in servers.items(): + online_players = self.get_online_players(port) + + if online_players == 0: + if port not in self.offline_ports: + self.offline_ports.append(port) + else: + print(f"Closing {port} server.") + user_id = server_info.get("user_id", None) + name = server_info.get("name", None) + server_stamp = server_info.get("time", None) + print(f"{user_id}, {name}, {server_stamp}") + firebase_manager.close_idle_server( + user_id=user_id, + name=name, + server_stamp=server_stamp, + port=port, + ) + else: + if port in self.offline_ports: + self.offline_ports.remove(port) + + print(f"Offline_servers : {self.offline_ports}") + return + if __name__ == "__main__": pass