diff --git a/api_sender.html b/api_sender.html
index a24a7bc..bc6255c 100644
--- a/api_sender.html
+++ b/api_sender.html
@@ -51,7 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
buttons.forEach(button => {
button.addEventListener('click', async event => {
const action = button.dataset.action;
- const token = "kPbH7QbrOzTrNcpqTnZGDJfSj3E3";
+ const token = "MpkbDMOO8PQddQgB5VgBQdTMWF53";
const framework = document.getElementById('serverFramework').value;
const subdomain = document.getElementById('subdomain').value;
const email = document.getElementById('accountEmail').value;
diff --git a/app.py b/app.py
index d801760..b0ce3e5 100644
--- a/app.py
+++ b/app.py
@@ -1,6 +1,7 @@
import http
import inspect
from typing import Union
+from apscheduler.schedulers.background import BackgroundScheduler
from flask import (Blueprint, Flask, Response, jsonify, request)
from flask_cors import CORS
@@ -125,4 +126,7 @@ app.register_blueprint(apiBP)
if __name__ == '__main__':
atexit.register(exit_safety)
+ scheduler = BackgroundScheduler()
+ scheduler.add_job(generic_executor.scheduled_actions, 'interval', minutes=1)
+ scheduler.start()
app.run(host='0.0.0.0', port=3000, debug=False)
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/requirements.txt b/requirements.txt
index e4ef2cc..6d13e35 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,4 +9,5 @@ Flask-Cors~=4.0.1
mcipc~=2.4.2
firebase-admin~=6.5.0
protobuf~=4.25.3
-typing_extensions~=4.9.0
\ No newline at end of file
+typing_extensions~=4.9.0
+APScheduler~=3.10.4
\ No newline at end of file
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
diff --git a/unit_test.py b/unit_test.py
index 11617b7..52a57d9 100644
--- a/unit_test.py
+++ b/unit_test.py
@@ -1,3 +1,6 @@
+import time
+from os import remove
+
import file_manager
import firebase_manager
@@ -14,4 +17,7 @@ if __name__ == '__main__':
#ban_user("MpkbDMOO8PQddQgB5VgBQdTMWF53")
#file_manager.log_action("gqZN3eCHF3V2er3Py3rlgk8u2t83", "test", "DeleteServer")
#firebase_manager.set_servers_not_running()
+ test: list[int] = [0, 987]
+ test.remove(85)
+ print(test)
pass