diff --git a/file_manager.py b/file_manager.py index 9b0f400..fff5cef 100644 --- a/file_manager.py +++ b/file_manager.py @@ -64,7 +64,7 @@ def update_server_property(file_path, property_name, new_value): file.writelines(content) -async def log_error(error_type: str, error_message: str): +def log_error(error_type: str, error_message: str): logging.basicConfig(filename='logs.txt', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') diff --git a/firebase_manager.py b/firebase_manager.py index 6db0a6d..cb73a4f 100644 --- a/firebase_manager.py +++ b/firebase_manager.py @@ -6,7 +6,7 @@ import firebase_admin import jwt from firebase_admin import auth, credentials, firestore from google.api_core.exceptions import Aborted, DataLoss, NotFound, OutOfRange, PermissionDenied, ResourceExhausted -from google.cloud.firestore_v1 import FieldFilter +from google.cloud.firestore_v1 import FieldFilter, DocumentReference import file_manager from generic_executor import mc_manager @@ -133,6 +133,21 @@ def create_server(user_id: str, server_name: str, version: str, port: int, frame "enableCommandBlock": "false"}) +def get_server_field(user_id: str, name: str, field_name: str) -> Union[str, bool, None]: + try: + server_doc: DocumentReference = firestore_database.document(f'users/{user_id}/servers/{name}') + doc = server_doc.get() + if doc.exists: + field = doc.to_dict().get(field_name) + return field + else: + return None + except (NotFound, PermissionDenied, Aborted, ResourceExhausted, + OutOfRange, DataLoss, TypeError, Exception, ValueError) as e: + log_exception_to_firestore(e, user_id, {"function": "get_server_field", "name": name, "field": field_name}) + return None + + def delete_server(user_id: str, server_name: str): user_ref = firestore_database.collection('users').document(user_id) servers_ref = user_ref.collection('servers') diff --git a/generic_executor.py b/generic_executor.py index 80003c3..c22905f 100644 --- a/generic_executor.py +++ b/generic_executor.py @@ -236,10 +236,12 @@ 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, user_id, name) + version: str = firebase_manager.get_server_field(user_id, name, "version") + if version is None: + return HTTPStatus.NOT_FOUND, f"Server {name} not found." + server_id = mc_manager.start_server(f"users/{user_id}/{name}", port, user_id, name, version) 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) try: firebase_manager.update_server_running_state(user_id, name, True) except Exception as e: @@ -324,5 +326,6 @@ def run_command(user: UserRecord, command: str, name: str) -> tuple[HTTPStatus, def scheduled_actions() -> None: mc_manager.check_servers_idle() + if __name__ == "__main__": pass diff --git a/readme.md b/readme.md index 648e9c0..568b15a 100644 --- a/readme.md +++ b/readme.md @@ -45,6 +45,11 @@ pip install -r requirements.txt gunicorn -w 4 -b 0.0.0.0:3000 app:app ``` +*you also may be tempted to use parameters* +```bash +gunicorn -w 4 -b 0.0.0.0:3000 app:app --interval 20 +``` + ## Documentation The API's {[Documentation]} is now complete !! This project is self documented and ready to understand. diff --git a/server_mc_manager.py b/server_mc_manager.py index c6d5cd9..810433f 100644 --- a/server_mc_manager.py +++ b/server_mc_manager.py @@ -1,7 +1,7 @@ import shlex import subprocess import time -from typing import Union +from typing import Union, Callable import mcipc.query import mcipc.query.client @@ -21,7 +21,7 @@ class MinecraftServerManager: self.offline_ports: list[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]: + version: str, jar_file='server.jar', memory_size='4G') -> Union[int, None]: if port in self.servers: return None @@ -35,7 +35,9 @@ class MinecraftServerManager: " -XX:MaxTenuringThreshold=1 -Daikars.new.flags=true" " -Dusing.aikars.flags=https://mcutils.com") - command = f"{java_executable} -Xmx{memory_size} {reg_flags} -jar {jar_file} --nogui" + java: str = f"/usr/lib/jvm/java-{get_sdk_version(version)}-openjdk/bin/java" + + command = f"{java} -Xmx{memory_size} {reg_flags} -jar {jar_file} --nogui" process = subprocess.Popen(shlex.split(command), cwd=server_directory, stdin=subprocess.PIPE) #TODO: Track process behavior and stderr, while excepting Advanced Terminal features not to be avail. @@ -46,6 +48,7 @@ class MinecraftServerManager: 'port': port, 'user_id': user_id, 'name': server_name, + 'version': version, 'time': time.time(), } return port @@ -136,5 +139,38 @@ class MinecraftServerManager: return +def version_range_checker(*ranges: tuple[str, str, str], default_sdk: str = "java") -> Callable[[str], str]: + def compare_mc_versions(version1: str): + def inner(version2: str) -> int: + v1_parts: list[int] = list(map(int, version1.split('.'))) + v2_parts: list[int] = list(map(int, version2.split('.'))) + for v1, v2 in zip(v1_parts, v2_parts): + if v1 > v2: + return 1 + elif v1 < v2: + return -1 + if len(v1_parts) > len(v2_parts): + return 1 + elif len(v1_parts) < len(v2_parts): + return -1 + return 0 + return inner + + def check_range(lower_bound: str, upper_bound: str, version: str) -> bool: + return compare_mc_versions(lower_bound)(version) <= 0 <= compare_mc_versions(upper_bound)(version) + + def check_version(version: str) -> str: + for lower_bound, upper_bound, sdk in ranges: + if check_range(lower_bound, upper_bound, version): + return sdk + return default_sdk + + return check_version + + +get_sdk_version: Callable[[str], str] = version_range_checker(("1.0", "1.15.2", "11"), + ("1.16.1", "1.18.1", "17"), + default_sdk="21") + if __name__ == "__main__": pass diff --git a/unit_test.py b/unit_test.py index 7c25236..88cf05a 100644 --- a/unit_test.py +++ b/unit_test.py @@ -1,8 +1,10 @@ -import asyncio import os +import shutil +from typing import Callable, Union -import file_manager import firebase_manager +import server_mc_manager +from generic_executor import mc_manager def ban_user(user_id: str): @@ -13,10 +15,30 @@ def ban_user(user_id: str): print("Error banning user " + user_id, "|", str(e), type(e).__name__) +def organize_minecraft_files(file_list: list[str], target_directory: str): + os.makedirs(target_directory, exist_ok=True) + + for file_name in file_list: + if file_name.endswith('.jar'): + version = file_name.split('-')[1] + version_folder = os.path.join(target_directory, version) + + os.makedirs(version_folder, exist_ok=True) + + source_path = os.path.join(target_directory, file_name) + destination_path = os.path.join(version_folder, 'server.jar') + + shutil.move(source_path, destination_path) + + if __name__ == '__main__': #ban_user("MpkbDMOO8PQddQgB5VgBQdTMWF53") #file_manager.log_action("gqZN3eCHF3V2er3Py3rlgk8u2t83", "test", "DeleteServer") #firebase_manager.set_servers_not_running() - current_dir_content: list[str] = os.listdir(".") - parsed_extension: str = '.py' - print(file_manager.filter_directory_contents(parsed_extension, current_dir_content)) + #print(firebase_manager.get_server_field("secret :)", "AMITIE" ,"version")) + ''' + organize_minecraft_files( + listdir("/home/hapso/Desktop/Personal/servii-backend/servers/paper"), + "/home/hapso/Desktop/Personal/servii-backend/servers/paper") + ''' + pass