2020-08-17 02:47:53 +00:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import time
|
|
|
|
import datetime
|
2022-03-03 21:01:12 +00:00
|
|
|
import base64
|
2020-08-17 02:47:53 +00:00
|
|
|
import threading
|
|
|
|
import logging.config
|
2021-09-25 19:29:28 +00:00
|
|
|
import subprocess
|
2021-08-10 20:17:56 +00:00
|
|
|
import html
|
2022-03-01 01:02:30 +00:00
|
|
|
import tempfile
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-03-08 04:40:44 +00:00
|
|
|
from app.classes.minecraft.stats import Stats
|
|
|
|
from app.classes.minecraft.mc_ping import ping, ping_bedrock
|
2022-03-03 21:01:12 +00:00
|
|
|
from app.classes.models.servers import Server_Stats, servers_helper
|
2022-01-26 01:45:30 +00:00
|
|
|
from app.classes.models.management import management_helper
|
|
|
|
from app.classes.models.users import users_helper
|
|
|
|
from app.classes.models.server_permissions import server_permissions
|
2020-08-17 02:47:53 +00:00
|
|
|
from app.classes.shared.helpers import helper
|
|
|
|
from app.classes.shared.console import console
|
2021-09-13 19:37:59 +00:00
|
|
|
from app.classes.shared.translation import translation
|
2022-03-01 01:02:30 +00:00
|
|
|
from app.classes.shared.file_helpers import file_helper
|
2022-01-26 01:45:30 +00:00
|
|
|
from app.classes.web.websocket_helper import websocket_helper
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2020-08-24 23:11:17 +00:00
|
|
|
try:
|
2021-09-25 19:29:28 +00:00
|
|
|
import psutil
|
2022-03-08 04:40:44 +00:00
|
|
|
#TZLocal is set as a hidden import on win pipeline
|
|
|
|
from tzlocal import get_localzone
|
|
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
2020-08-24 23:11:17 +00:00
|
|
|
|
|
|
|
except ModuleNotFoundError as e:
|
2022-03-08 04:40:44 +00:00
|
|
|
helper.auto_installer_fix(e)
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-03-08 04:40:44 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2021-08-10 20:17:56 +00:00
|
|
|
class ServerOutBuf:
|
|
|
|
lines = {}
|
2021-08-18 16:50:13 +00:00
|
|
|
|
2021-09-25 19:29:28 +00:00
|
|
|
def __init__(self, proc, server_id):
|
|
|
|
self.proc = proc
|
2021-08-10 20:17:56 +00:00
|
|
|
self.server_id = str(server_id)
|
|
|
|
# Buffers text for virtual_terminal_lines config number of lines
|
|
|
|
self.max_lines = helper.get_setting('virtual_terminal_lines')
|
|
|
|
self.line_buffer = ''
|
|
|
|
ServerOutBuf.lines[self.server_id] = []
|
2021-11-15 00:17:23 +00:00
|
|
|
self.lsi = 0
|
2021-08-10 20:17:56 +00:00
|
|
|
|
2021-11-06 17:06:26 +00:00
|
|
|
def process_byte(self, char):
|
2021-11-15 00:17:23 +00:00
|
|
|
if char == os.linesep[self.lsi]:
|
|
|
|
self.lsi += 1
|
|
|
|
else:
|
|
|
|
self.lsi = 0
|
|
|
|
self.line_buffer += char
|
|
|
|
|
|
|
|
if self.lsi >= len(os.linesep):
|
|
|
|
self.lsi = 0
|
2021-11-06 17:06:26 +00:00
|
|
|
ServerOutBuf.lines[self.server_id].append(self.line_buffer)
|
|
|
|
|
|
|
|
self.new_line_handler(self.line_buffer)
|
|
|
|
self.line_buffer = ''
|
|
|
|
# Limit list length to self.max_lines:
|
|
|
|
if len(ServerOutBuf.lines[self.server_id]) > self.max_lines:
|
|
|
|
ServerOutBuf.lines[self.server_id].pop(0)
|
|
|
|
|
2021-08-10 20:17:56 +00:00
|
|
|
def check(self):
|
2021-11-06 17:06:26 +00:00
|
|
|
while True:
|
|
|
|
if self.proc.poll() is None:
|
2021-12-14 02:44:22 +00:00
|
|
|
char = self.proc.stdout.read(1).decode('utf-8', 'ignore')
|
2021-11-06 17:06:26 +00:00
|
|
|
# TODO: we may want to benchmark reading in blocks and userspace processing it later, reads are kind of expensive as a syscall
|
|
|
|
self.process_byte(char)
|
2021-08-10 20:17:56 +00:00
|
|
|
else:
|
2021-12-14 02:44:22 +00:00
|
|
|
flush = self.proc.stdout.read().decode('utf-8', 'ignore')
|
2021-11-06 17:06:26 +00:00
|
|
|
for char in flush:
|
|
|
|
self.process_byte(char)
|
2021-11-20 00:14:32 +00:00
|
|
|
break
|
2021-08-10 20:17:56 +00:00
|
|
|
|
|
|
|
def new_line_handler(self, new_line):
|
2022-01-29 00:00:14 +00:00
|
|
|
new_line = re.sub('(\033\\[(0;)?[0-9]*[A-z]?(;[0-9])?m?)', ' ', new_line)
|
2021-08-11 20:29:31 +00:00
|
|
|
new_line = re.sub('[A-z]{2}\b\b', '', new_line)
|
2021-08-10 20:17:56 +00:00
|
|
|
highlighted = helper.log_colors(html.escape(new_line))
|
|
|
|
|
2021-08-11 20:29:31 +00:00
|
|
|
logger.debug('Broadcasting new virtual terminal line')
|
2021-08-10 20:17:56 +00:00
|
|
|
|
|
|
|
# TODO: Do not send data to clients who do not have permission to view this server's console
|
|
|
|
websocket_helper.broadcast_page_params(
|
|
|
|
'/panel/server_detail',
|
|
|
|
{
|
|
|
|
'id': self.server_id
|
|
|
|
},
|
|
|
|
'vterm_new_line',
|
|
|
|
{
|
2021-08-11 20:29:31 +00:00
|
|
|
'line': highlighted + '<br />'
|
2021-08-10 20:17:56 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-03-03 21:01:12 +00:00
|
|
|
#************************************************************************************************
|
|
|
|
# Minecraft Server Class
|
|
|
|
#************************************************************************************************
|
2020-08-17 02:47:53 +00:00
|
|
|
class Server:
|
|
|
|
|
2021-03-22 04:02:18 +00:00
|
|
|
def __init__(self, stats):
|
2020-08-17 02:47:53 +00:00
|
|
|
# holders for our process
|
|
|
|
self.process = None
|
|
|
|
self.line = False
|
|
|
|
self.start_time = None
|
|
|
|
self.server_command = None
|
|
|
|
self.server_path = None
|
|
|
|
self.server_thread = None
|
|
|
|
self.settings = None
|
|
|
|
self.updating = False
|
|
|
|
self.server_id = None
|
2021-07-25 01:09:39 +00:00
|
|
|
self.jar_update_url = None
|
2020-08-17 02:47:53 +00:00
|
|
|
self.name = None
|
|
|
|
self.is_crashed = False
|
|
|
|
self.restart_count = 0
|
2021-03-22 04:02:18 +00:00
|
|
|
self.stats = stats
|
2022-01-15 00:52:51 +00:00
|
|
|
tz = get_localzone()
|
|
|
|
self.server_scheduler = BackgroundScheduler(timezone=str(tz))
|
2022-01-28 01:43:23 +00:00
|
|
|
self.server_scheduler.start()
|
2021-11-20 00:14:32 +00:00
|
|
|
self.backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name=f"backup_{self.name}")
|
2022-01-12 06:24:03 +00:00
|
|
|
self.is_backingup = False
|
2022-01-28 01:43:23 +00:00
|
|
|
#Reset crash and update at initialization
|
|
|
|
servers_helper.server_crash_reset(self.server_id)
|
|
|
|
servers_helper.set_update(self.server_id, False)
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-03-03 21:01:12 +00:00
|
|
|
|
|
|
|
#************************************************************************************************
|
|
|
|
# Minecraft Server Management
|
|
|
|
#************************************************************************************************
|
2020-10-07 13:19:47 +00:00
|
|
|
def reload_server_settings(self):
|
2021-09-08 22:01:10 +00:00
|
|
|
server_data = servers_helper.get_server_data_by_id(self.server_id)
|
2020-10-07 13:19:47 +00:00
|
|
|
self.settings = server_data
|
|
|
|
|
2020-08-17 02:47:53 +00:00
|
|
|
def do_server_setup(self, server_data_obj):
|
2022-01-26 01:45:30 +00:00
|
|
|
serverId = server_data_obj['server_id']
|
|
|
|
serverName = server_data_obj['server_name']
|
|
|
|
autoStart = server_data_obj['auto_start']
|
|
|
|
|
|
|
|
logger.info(f'Creating Server object: {serverId} | Server Name: {serverName} | Auto Start: {autoStart}')
|
|
|
|
self.server_id = serverId
|
|
|
|
self.name = serverName
|
2020-08-17 02:47:53 +00:00
|
|
|
self.settings = server_data_obj
|
|
|
|
|
2022-03-03 22:13:40 +00:00
|
|
|
self.record_server_stats()
|
|
|
|
|
2020-08-17 02:47:53 +00:00
|
|
|
# build our server run command
|
|
|
|
|
|
|
|
if server_data_obj['auto_start']:
|
|
|
|
delay = int(self.settings['auto_start_delay'])
|
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Scheduling server {self.name} to start in {delay} seconds")
|
|
|
|
console.info(f"Scheduling server {self.name} to start in {delay} seconds")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-01-11 23:30:06 +00:00
|
|
|
self.server_scheduler.add_job(self.run_scheduled_server, 'interval', seconds=delay, id=str(self.server_id))
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
def run_scheduled_server(self):
|
2022-01-26 01:45:30 +00:00
|
|
|
console.info(f"Starting server ID: {self.server_id} - {self.name}")
|
|
|
|
logger.info(f"Starting server ID: {self.server_id} - {self.name}")
|
2022-01-14 07:35:26 +00:00
|
|
|
#Sets waiting start to false since we're attempting to start the server.
|
|
|
|
servers_helper.set_waiting_start(self.server_id, False)
|
2021-11-27 22:10:43 +00:00
|
|
|
self.run_threaded_server(None)
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
# remove the scheduled job since it's ran
|
2022-01-11 23:30:06 +00:00
|
|
|
return self.server_scheduler.remove_job(str(self.server_id))
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2021-11-27 22:10:43 +00:00
|
|
|
def run_threaded_server(self, user_id):
|
2020-08-17 02:47:53 +00:00
|
|
|
# start the server
|
2022-01-26 01:45:30 +00:00
|
|
|
self.server_thread = threading.Thread(target=self.start_server, daemon=True, args=(user_id,), name=f'{self.server_id}_server_thread')
|
2020-08-17 02:47:53 +00:00
|
|
|
self.server_thread.start()
|
|
|
|
|
2022-03-03 21:01:12 +00:00
|
|
|
#Register an shedule for polling server stats when running
|
|
|
|
logger.info(f"Polling server statistics {self.name} every {5} seconds")
|
|
|
|
console.info(f"Polling server statistics {self.name} every {5} seconds")
|
2022-03-08 02:48:06 +00:00
|
|
|
try:
|
|
|
|
self.server_scheduler.add_job(self.realtime_stats, 'interval', seconds=5, id="stats_"+str(self.server_id))
|
|
|
|
except:
|
|
|
|
self.server_scheduler.remove_job('stats_'+str(self.server_id))
|
|
|
|
self.server_scheduler.add_job(self.realtime_stats, 'interval', seconds=5, id="stats_"+str(self.server_id))
|
2022-03-03 21:01:12 +00:00
|
|
|
|
|
|
|
|
2020-08-17 02:47:53 +00:00
|
|
|
def setup_server_run_command(self):
|
|
|
|
# configure the server
|
2021-11-21 10:52:29 +00:00
|
|
|
server_exec_path = helper.get_os_understandable_path(self.settings['executable'])
|
2021-09-25 19:29:28 +00:00
|
|
|
self.server_command = helper.cmdparse(self.settings['execution_command'])
|
2021-11-21 10:52:29 +00:00
|
|
|
self.server_path = helper.get_os_understandable_path(self.settings['path'])
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
# let's do some quick checking to make sure things actually exists
|
|
|
|
full_path = os.path.join(self.server_path, server_exec_path)
|
|
|
|
if not helper.check_file_exists(full_path):
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.critical(f"Server executable path: {full_path} does not seem to exist")
|
|
|
|
console.critical(f"Server executable path: {full_path} does not seem to exist")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2021-01-17 17:20:28 +00:00
|
|
|
if not helper.check_path_exists(self.server_path):
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.critical(f"Server path: {self.server_path} does not seem to exits")
|
|
|
|
console.critical(f"Server path: {self.server_path} does not seem to exits")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
if not helper.check_writeable(self.server_path):
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.critical(f"Unable to write/access {self.server_path}")
|
|
|
|
console.warning(f"Unable to write/access {self.server_path}")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2021-11-27 22:10:43 +00:00
|
|
|
def start_server(self, user_id):
|
|
|
|
if not user_id:
|
|
|
|
user_lang = helper.get_setting('language')
|
|
|
|
else:
|
|
|
|
user_lang = users_helper.get_user_lang_by_id(user_id)
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-03-04 00:36:36 +00:00
|
|
|
if servers_helper.get_download_status(self.server_id):
|
|
|
|
if user_id:
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
|
|
|
'error': translation.translate('error', 'not-downloaded', user_lang)
|
|
|
|
})
|
|
|
|
return False
|
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Start command detected. Reloading settings from DB for server {self.name}")
|
2021-07-25 15:28:09 +00:00
|
|
|
self.setup_server_run_command()
|
2020-08-17 02:47:53 +00:00
|
|
|
# fail safe in case we try to start something already running
|
|
|
|
if self.check_running():
|
|
|
|
logger.error("Server is already running - Cancelling Startup")
|
|
|
|
console.error("Server is already running - Cancelling Startup")
|
|
|
|
return False
|
2021-07-26 16:11:46 +00:00
|
|
|
if self.check_update():
|
|
|
|
logger.error("Server is updating. Terminating startup.")
|
2021-07-25 21:04:10 +00:00
|
|
|
return False
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Launching Server {self.name} with command {self.server_command}")
|
|
|
|
console.info(f"Launching Server {self.name} with command {self.server_command}")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-02-26 16:59:19 +00:00
|
|
|
#Checks for eula. Creates one if none detected.
|
|
|
|
#If EULA is detected and not set to one of these true vaiants we offer to set it true.
|
2021-11-23 21:11:23 +00:00
|
|
|
if helper.check_file_exists(os.path.join(self.settings['path'], 'eula.txt')):
|
2022-01-26 01:45:30 +00:00
|
|
|
f = open(os.path.join(self.settings['path'], 'eula.txt'), 'r', encoding='utf-8')
|
2021-11-23 21:11:23 +00:00
|
|
|
line = f.readline().lower()
|
|
|
|
if line == 'eula=true':
|
|
|
|
e_flag = True
|
|
|
|
|
|
|
|
elif line == 'eula = true':
|
|
|
|
e_flag = True
|
|
|
|
|
|
|
|
elif line == 'eula= true':
|
|
|
|
e_flag = True
|
|
|
|
|
|
|
|
elif line == 'eula =true':
|
|
|
|
e_flag = True
|
|
|
|
|
|
|
|
else:
|
|
|
|
e_flag = False
|
|
|
|
else:
|
|
|
|
e_flag = False
|
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
if not e_flag:
|
2021-11-27 23:43:14 +00:00
|
|
|
if user_id:
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_eula_bootbox', {
|
|
|
|
'id': self.server_id
|
|
|
|
})
|
|
|
|
else:
|
|
|
|
logger.error("Autostart failed due to EULA being false. Agree not sent due to auto start.")
|
|
|
|
return False
|
2021-11-23 21:11:23 +00:00
|
|
|
return False
|
|
|
|
f.close()
|
2021-11-21 10:52:29 +00:00
|
|
|
if helper.is_os_windows():
|
2021-02-13 05:47:30 +00:00
|
|
|
logger.info("Windows Detected")
|
2020-08-17 02:47:53 +00:00
|
|
|
else:
|
2021-09-25 19:29:28 +00:00
|
|
|
logger.info("Unix Detected")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Starting server in {self.server_path} with command: {self.server_command}")
|
|
|
|
|
2022-02-27 17:22:52 +00:00
|
|
|
#checks to make sure file is openable (downloaded) and exists.
|
|
|
|
try:
|
|
|
|
f = open(os.path.join(self.server_path, servers_helper.get_server_data_by_id(self.server_id)['executable']), "r", encoding="utf-8")
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
except:
|
|
|
|
if user_id:
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
|
|
|
'error': translation.translate('error', 'not-downloaded', user_lang)
|
|
|
|
})
|
|
|
|
return
|
|
|
|
|
2022-02-26 16:59:19 +00:00
|
|
|
if not helper.is_os_windows() and servers_helper.get_server_type_by_id(self.server_id) == "minecraft-bedrock":
|
2022-02-26 17:31:55 +00:00
|
|
|
logger.info(f"Bedrock and Unix detected for server {self.name}. Switching to appropriate execution string")
|
2022-02-26 16:59:19 +00:00
|
|
|
my_env = os.environ
|
|
|
|
my_env["LD_LIBRARY_PATH"] = self.server_path
|
|
|
|
try:
|
|
|
|
self.process = subprocess.Popen(
|
|
|
|
self.server_command, cwd=self.server_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=my_env)
|
|
|
|
except Exception as ex:
|
|
|
|
logger.error(f"Server {self.name} failed to start with error code: {ex}")
|
2022-02-26 16:25:08 +00:00
|
|
|
if user_id:
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
2022-02-26 16:59:19 +00:00
|
|
|
'error': translation.translate('error', 'start-error', user_lang).format(self.name, ex)
|
|
|
|
})
|
|
|
|
return False
|
|
|
|
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
self.process = subprocess.Popen(
|
|
|
|
self.server_command, cwd=self.server_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
|
|
except Exception as ex:
|
|
|
|
#Checks for java on initial fail
|
|
|
|
if os.system("java -version") == 32512:
|
|
|
|
if user_id:
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
|
|
|
'error': translation.translate('error', 'noJava', user_lang).format(self.name)
|
|
|
|
})
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
logger.error(f"Server {self.name} failed to start with error code: {ex}")
|
|
|
|
if user_id:
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error',{
|
|
|
|
'error': translation.translate('error', 'start-error', user_lang).format(self.name, ex)
|
|
|
|
})
|
2021-11-29 21:22:46 +00:00
|
|
|
return False
|
2022-01-09 18:14:59 +00:00
|
|
|
|
2021-08-10 20:17:56 +00:00
|
|
|
out_buf = ServerOutBuf(self.process, self.server_id)
|
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.debug(f'Starting virtual terminal listener for server {self.name}')
|
|
|
|
threading.Thread(target=out_buf.check, daemon=True, name=f'{self.server_id}_virtual_terminal').start()
|
2021-08-18 16:50:13 +00:00
|
|
|
|
2021-02-13 05:47:30 +00:00
|
|
|
self.is_crashed = False
|
2022-01-28 01:43:23 +00:00
|
|
|
servers_helper.server_crash_reset(self.server_id)
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2021-04-17 21:20:23 +00:00
|
|
|
self.start_time = str(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2021-09-25 19:29:28 +00:00
|
|
|
if self.process.poll() is None:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Server {self.name} running with PID {self.process.pid}")
|
|
|
|
console.info(f"Server {self.name} running with PID {self.process.pid}")
|
2021-02-13 05:47:30 +00:00
|
|
|
self.is_crashed = False
|
2022-01-28 01:43:23 +00:00
|
|
|
servers_helper.server_crash_reset(self.server_id)
|
2022-03-03 21:01:12 +00:00
|
|
|
self.record_server_stats()
|
2022-01-26 01:45:30 +00:00
|
|
|
check_internet_thread = threading.Thread(
|
|
|
|
target=self.check_internet_thread, daemon=True, args=(user_id, user_lang, ), name=f"{self.name}_Internet")
|
2022-01-09 18:14:59 +00:00
|
|
|
check_internet_thread.start()
|
|
|
|
#Checks if this is the servers first run.
|
2022-01-10 19:58:38 +00:00
|
|
|
if servers_helper.get_first_run(self.server_id):
|
|
|
|
servers_helper.set_first_run(self.server_id)
|
2022-01-09 18:14:59 +00:00
|
|
|
loc_server_port = servers_helper.get_server_stats_by_id(self.server_id)['server_port']
|
|
|
|
#Sends port reminder message.
|
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error', {
|
|
|
|
'error': translation.translate('error', 'portReminder', user_lang).format(self.name, loc_server_port)
|
|
|
|
})
|
2022-01-22 01:43:44 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
|
|
|
if user != user_id:
|
|
|
|
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
|
|
|
})
|
2022-01-09 18:14:59 +00:00
|
|
|
else:
|
2022-01-22 01:43:44 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
|
|
|
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
2022-01-09 18:14:59 +00:00
|
|
|
})
|
2020-08-17 02:47:53 +00:00
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.warning(f"Server PID {self.process.pid} died right after starting - is this a server config issue?")
|
|
|
|
console.warning(f"Server PID {self.process.pid} died right after starting - is this a server config issue?")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
if self.settings['crash_detection']:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Server {self.name} has crash detection enabled - starting watcher task")
|
|
|
|
console.info(f"Server {self.name} has crash detection enabled - starting watcher task")
|
|
|
|
|
2022-01-28 01:43:23 +00:00
|
|
|
self.server_scheduler.add_job(self.detect_crash, 'interval', seconds=30, id=f"c_{self.server_id}")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2021-12-10 04:52:37 +00:00
|
|
|
def check_internet_thread(self, user_id, user_lang):
|
2021-12-09 22:58:36 +00:00
|
|
|
if user_id:
|
2022-01-09 18:14:59 +00:00
|
|
|
if not helper.check_internet():
|
2021-12-09 22:58:36 +00:00
|
|
|
websocket_helper.broadcast_user(user_id, 'send_start_error', {
|
|
|
|
'error': translation.translate('error', 'internet', user_lang)
|
|
|
|
})
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2022-01-31 02:04:08 +00:00
|
|
|
def stop_crash_detection(self):
|
|
|
|
#This is only used if the crash detection settings change while the server is running.
|
|
|
|
if self.check_running():
|
|
|
|
logger.info(f"Detected crash detection shut off for server {self.name}")
|
|
|
|
try:
|
|
|
|
self.server_scheduler.remove_job('c_' + str(self.server_id))
|
|
|
|
except:
|
|
|
|
logger.error(f"Removing crash watcher for server {self.name} failed. Assuming it was never started.")
|
|
|
|
|
|
|
|
def start_crash_detection(self):
|
|
|
|
#This is only used if the crash detection settings change while the server is running.
|
|
|
|
if self.check_running():
|
|
|
|
logger.info(f"Server {self.name} has crash detection enabled - starting watcher task")
|
|
|
|
console.info(f"Server {self.name} has crash detection enabled - starting watcher task")
|
|
|
|
self.server_scheduler.add_job(self.detect_crash, 'interval', seconds=30, id=f"c_{self.server_id}")
|
|
|
|
|
2020-08-17 02:47:53 +00:00
|
|
|
def stop_threaded_server(self):
|
|
|
|
self.stop_server()
|
|
|
|
|
|
|
|
if self.server_thread:
|
|
|
|
self.server_thread.join()
|
|
|
|
|
|
|
|
def stop_server(self):
|
2020-08-24 23:11:17 +00:00
|
|
|
if self.settings['stop_command']:
|
2020-08-17 02:47:53 +00:00
|
|
|
self.send_command(self.settings['stop_command'])
|
2022-01-30 20:28:48 +00:00
|
|
|
if self.settings['crash_detection']:
|
|
|
|
#remove crash detection watcher
|
|
|
|
logger.info(f"Removing crash watcher for server {self.name}")
|
2022-01-31 02:04:08 +00:00
|
|
|
try:
|
|
|
|
self.server_scheduler.remove_job('c_' + str(self.server_id))
|
|
|
|
except:
|
|
|
|
logger.error(f"Removing crash watcher for server {self.name} failed. Assuming it was never started.")
|
2021-09-25 19:29:28 +00:00
|
|
|
else:
|
|
|
|
#windows will need to be handled separately for Ctrl+C
|
2021-11-20 00:14:32 +00:00
|
|
|
self.process.terminate()
|
2021-09-25 19:29:28 +00:00
|
|
|
running = self.check_running()
|
|
|
|
if not running:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Can't stop server {self.name} if it's not running")
|
|
|
|
console.info(f"Can't stop server {self.name} if it's not running")
|
2021-09-25 19:29:28 +00:00
|
|
|
return
|
|
|
|
x = 0
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2021-09-25 19:29:28 +00:00
|
|
|
# caching the name and pid number
|
|
|
|
server_name = self.name
|
|
|
|
server_pid = self.process.pid
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-02-11 06:16:45 +00:00
|
|
|
|
2021-09-25 19:29:28 +00:00
|
|
|
while running:
|
|
|
|
x = x+1
|
2022-01-26 01:45:30 +00:00
|
|
|
logstr = f"Server {server_name} is still running - waiting 2s to see if it stops ({int(60-(x*2))} seconds until force close)"
|
2021-09-25 19:29:28 +00:00
|
|
|
logger.info(logstr)
|
|
|
|
console.info(logstr)
|
|
|
|
running = self.check_running()
|
|
|
|
time.sleep(2)
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2021-09-25 19:29:28 +00:00
|
|
|
# if we haven't closed in 60 seconds, let's just slam down on the PID
|
|
|
|
if x >= 30:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Server {server_name} is still running - Forcing the process down")
|
|
|
|
console.info(f"Server {server_name} is still running - Forcing the process down")
|
2021-09-25 19:29:28 +00:00
|
|
|
self.kill()
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Stopped Server {server_name} with PID {server_pid}")
|
|
|
|
console.info(f"Stopped Server {server_name} with PID {server_pid}")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2020-08-24 23:11:17 +00:00
|
|
|
# massive resetting of variables
|
|
|
|
self.cleanup_server_object()
|
2022-01-22 01:43:44 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-03-03 21:01:12 +00:00
|
|
|
# remove the stats polling job since server is stopped
|
|
|
|
self.server_scheduler.remove_job("stats_"+str(self.server_id))
|
|
|
|
|
|
|
|
self.record_server_stats()
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2022-01-22 01:43:44 +00:00
|
|
|
for user in server_users:
|
|
|
|
websocket_helper.broadcast_user(user, 'send_start_reload', {
|
|
|
|
})
|
|
|
|
|
2021-11-27 22:10:43 +00:00
|
|
|
def restart_threaded_server(self, user_id):
|
2020-08-24 23:11:17 +00:00
|
|
|
# if not already running, let's just start
|
|
|
|
if not self.check_running():
|
2021-11-27 22:10:43 +00:00
|
|
|
self.run_threaded_server(user_id)
|
2020-08-24 23:11:17 +00:00
|
|
|
else:
|
|
|
|
self.stop_threaded_server()
|
|
|
|
time.sleep(2)
|
2021-11-27 22:10:43 +00:00
|
|
|
self.run_threaded_server(user_id)
|
2020-08-24 23:11:17 +00:00
|
|
|
|
2020-08-17 02:47:53 +00:00
|
|
|
def cleanup_server_object(self):
|
|
|
|
self.start_time = None
|
2020-08-24 23:11:17 +00:00
|
|
|
self.restart_count = 0
|
|
|
|
self.is_crashed = False
|
|
|
|
self.updating = False
|
|
|
|
self.process = None
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2020-12-11 14:52:36 +00:00
|
|
|
def check_running(self):
|
2020-08-17 02:47:53 +00:00
|
|
|
# if process is None, we never tried to start
|
2021-09-25 19:29:28 +00:00
|
|
|
if self.process is None:
|
|
|
|
return False
|
|
|
|
poll = self.process.poll()
|
|
|
|
if poll is None:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
self.last_rc = poll
|
|
|
|
return False
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
def send_command(self, command):
|
|
|
|
if not self.check_running() and command.lower() != 'start':
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.warning(f"Server not running, unable to send command \"{command}\"")
|
2020-08-17 02:47:53 +00:00
|
|
|
return False
|
2022-01-26 01:45:30 +00:00
|
|
|
console.info(f"COMMAND TIME: {command}")
|
|
|
|
logger.debug(f"Sending command {command} to server")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
# send it
|
2022-01-26 01:45:30 +00:00
|
|
|
self.process.stdin.write(f"{command}\n".encode('utf-8'))
|
2021-09-25 19:29:28 +00:00
|
|
|
self.process.stdin.flush()
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
def crash_detected(self, name):
|
|
|
|
|
2020-12-11 14:52:36 +00:00
|
|
|
# clear the old scheduled watcher task
|
2022-01-30 20:28:48 +00:00
|
|
|
self.server_scheduler.remove_job(f"c_{self.server_id}")
|
2022-03-04 20:09:13 +00:00
|
|
|
# remove the stats polling job since server is stopped
|
|
|
|
self.server_scheduler.remove_job("stats_"+str(self.server_id))
|
2020-12-11 14:52:36 +00:00
|
|
|
|
2020-08-17 02:47:53 +00:00
|
|
|
# the server crashed, or isn't found - so let's reset things.
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.warning(f"The server {name} seems to have vanished unexpectedly, did it crash?")
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
if self.settings['crash_detection']:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.warning(f"The server {name} has crashed and will be restarted. Restarting server")
|
|
|
|
console.warning(f"The server {name} has crashed and will be restarted. Restarting server")
|
2021-11-27 22:10:43 +00:00
|
|
|
self.run_threaded_server(None)
|
2020-08-17 02:47:53 +00:00
|
|
|
return True
|
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.critical(f"The server {name} has crashed, crash detection is disabled and it will not be restarted")
|
|
|
|
console.critical(f"The server {name} has crashed, crash detection is disabled and it will not be restarted")
|
2020-08-17 02:47:53 +00:00
|
|
|
return False
|
|
|
|
|
2021-09-25 19:29:28 +00:00
|
|
|
def kill(self):
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Terminating server {self.server_id} and all child processes")
|
2021-09-25 19:29:28 +00:00
|
|
|
process = psutil.Process(self.process.pid)
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
# for every sub process...
|
|
|
|
for proc in process.children(recursive=True):
|
|
|
|
# kill all the child processes - it sounds too wrong saying kill all the children (kevdagoat: lol!)
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Sending SIGKILL to server {proc.name}")
|
2020-08-17 02:47:53 +00:00
|
|
|
proc.kill()
|
|
|
|
# kill the main process we are after
|
|
|
|
logger.info('Sending SIGKILL to parent')
|
2022-03-08 02:48:06 +00:00
|
|
|
self.server_scheduler.remove_job("stats_"+str(self.server_id))
|
2021-09-25 19:29:28 +00:00
|
|
|
self.process.kill()
|
2020-08-17 02:47:53 +00:00
|
|
|
|
|
|
|
def get_start_time(self):
|
|
|
|
if self.check_running():
|
|
|
|
return self.start_time
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2021-09-13 19:03:47 +00:00
|
|
|
def get_pid(self):
|
2021-09-25 19:29:28 +00:00
|
|
|
if self.process is not None:
|
|
|
|
return self.process.pid
|
|
|
|
else:
|
|
|
|
return None
|
2021-11-21 10:52:29 +00:00
|
|
|
|
2020-12-11 14:52:36 +00:00
|
|
|
def detect_crash(self):
|
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Detecting possible crash for server: {self.name} ")
|
2020-12-11 14:52:36 +00:00
|
|
|
|
|
|
|
running = self.check_running()
|
|
|
|
|
|
|
|
# if all is okay, we just exit out
|
|
|
|
if running:
|
|
|
|
return
|
2022-02-11 06:16:45 +00:00
|
|
|
#check the exit code -- This could be a fix for /stop
|
|
|
|
if self.process.returncode == 0:
|
|
|
|
logger.warning(f'Process {self.process.pid} exited with code {self.process.returncode}. This is considered a clean exit'+
|
|
|
|
' supressing crash handling.')
|
|
|
|
# cancel the watcher task
|
|
|
|
self.server_scheduler.remove_job("c_"+str(self.server_id))
|
|
|
|
return
|
2020-12-11 14:52:36 +00:00
|
|
|
|
2022-01-28 01:43:23 +00:00
|
|
|
servers_helper.sever_crashed(self.server_id)
|
2020-12-11 14:52:36 +00:00
|
|
|
# if we haven't tried to restart more 3 or more times
|
|
|
|
if self.restart_count <= 3:
|
|
|
|
|
|
|
|
# start the server if needed
|
|
|
|
server_restarted = self.crash_detected(self.name)
|
|
|
|
|
|
|
|
if server_restarted:
|
|
|
|
# add to the restart count
|
|
|
|
self.restart_count = self.restart_count + 1
|
|
|
|
|
|
|
|
# we have tried to restart 4 times...
|
|
|
|
elif self.restart_count == 4:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.critical(f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting.")
|
|
|
|
console.critical(f"Server {self.name} has been restarted {self.restart_count} times. It has crashed, not restarting.")
|
2020-12-11 14:52:36 +00:00
|
|
|
|
2022-01-28 01:43:23 +00:00
|
|
|
self.restart_count = 0
|
2020-12-11 14:52:36 +00:00
|
|
|
self.is_crashed = True
|
2022-01-28 01:43:23 +00:00
|
|
|
servers_helper.sever_crashed(self.server_id)
|
2020-12-11 14:52:36 +00:00
|
|
|
|
|
|
|
# cancel the watcher task
|
2022-01-28 01:43:23 +00:00
|
|
|
self.server_scheduler.remove_job("c_"+str(self.server_id))
|
2020-08-17 02:47:53 +00:00
|
|
|
|
2020-12-11 14:52:36 +00:00
|
|
|
def remove_watcher_thread(self):
|
|
|
|
logger.info("Removing old crash detection watcher thread")
|
|
|
|
console.info("Removing old crash detection watcher thread")
|
2022-01-26 01:45:30 +00:00
|
|
|
self.server_scheduler.remove_job('c_'+str(self.server_id))
|
2021-03-22 04:02:18 +00:00
|
|
|
|
2021-11-27 22:10:43 +00:00
|
|
|
def agree_eula(self, user_id):
|
2021-11-23 21:11:23 +00:00
|
|
|
file = os.path.join(self.server_path, 'eula.txt')
|
2022-01-26 01:45:30 +00:00
|
|
|
f = open(file, 'w', encoding='utf-8')
|
2021-11-23 21:11:23 +00:00
|
|
|
f.write('eula=true')
|
2022-01-26 01:45:30 +00:00
|
|
|
f.close()
|
2021-11-27 22:10:43 +00:00
|
|
|
self.run_threaded_server(user_id)
|
2021-11-23 21:11:23 +00:00
|
|
|
|
2021-07-25 15:05:16 +00:00
|
|
|
def is_backup_running(self):
|
|
|
|
if self.is_backingup:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2021-03-22 04:02:18 +00:00
|
|
|
def backup_server(self):
|
2021-11-20 00:14:32 +00:00
|
|
|
backup_thread = threading.Thread(target=self.a_backup_server, daemon=True, name=f"backup_{self.name}")
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Starting Backup Thread for server {self.settings['server_name']}.")
|
|
|
|
if self.server_path is None:
|
2021-11-21 10:52:29 +00:00
|
|
|
self.server_path = helper.get_os_understandable_path(self.settings['path'])
|
2021-11-19 06:07:53 +00:00
|
|
|
logger.info("Backup Thread - Local server path not defined. Setting local server path variable.")
|
2021-07-25 15:05:16 +00:00
|
|
|
#checks if the backup thread is currently alive for this server
|
|
|
|
if not self.is_backingup:
|
|
|
|
try:
|
|
|
|
backup_thread.start()
|
|
|
|
except Exception as ex:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.error(f"Failed to start backup: {ex}")
|
2021-07-25 15:05:16 +00:00
|
|
|
return False
|
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.error(f"Backup is already being processed for server {self.settings['server_name']}. Canceling backup request")
|
2021-07-25 15:05:16 +00:00
|
|
|
return False
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Backup Thread started for server {self.settings['server_name']}.")
|
2021-07-25 15:05:16 +00:00
|
|
|
|
|
|
|
def a_backup_server(self):
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Starting server {self.name} (ID {self.server_id}) backup")
|
2022-03-01 03:40:11 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
2022-03-01 06:14:26 +00:00
|
|
|
websocket_helper.broadcast_user(user, 'notification', translation.translate('notify',
|
2022-03-08 01:22:30 +00:00
|
|
|
'backupStarted', users_helper.get_user_lang_by_id(user)).format(self.name))
|
2022-03-01 03:40:11 +00:00
|
|
|
time.sleep(3)
|
2021-08-12 04:33:18 +00:00
|
|
|
self.is_backingup = True
|
2021-09-08 22:01:10 +00:00
|
|
|
conf = management_helper.get_backup_config(self.server_id)
|
2021-08-12 04:33:18 +00:00
|
|
|
try:
|
2022-01-26 01:45:30 +00:00
|
|
|
backup_filename = f"{self.settings['backup_path']}/{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}"
|
|
|
|
logger.info(f"Creating backup of server '{self.settings['server_name']}'" +
|
|
|
|
f" (ID#{self.server_id}, path={self.server_path}) at '{backup_filename}'")
|
2022-03-01 01:02:30 +00:00
|
|
|
|
|
|
|
tempDir = tempfile.mkdtemp()
|
|
|
|
# pylint: disable=unexpected-keyword-arg
|
|
|
|
file_helper.copy_dir(self.server_path, tempDir, dirs_exist_ok=True)
|
|
|
|
excluded_dirs = management_helper.get_excluded_backup_dirs(self.server_id)
|
|
|
|
server_dir = helper.get_os_understandable_path(self.settings['path'])
|
|
|
|
|
|
|
|
for my_dir in excluded_dirs:
|
|
|
|
# Take the full path of the excluded dir and replace the server path with the temp path
|
|
|
|
# This is so that we're only deleting excluded dirs from the temp path and not the server path
|
|
|
|
excluded_dir = helper.get_os_understandable_path(my_dir).replace(server_dir, helper.get_os_understandable_path(tempDir))
|
|
|
|
# Next, check to see if it is a directory
|
|
|
|
if os.path.isdir(excluded_dir):
|
|
|
|
# If it is a directory, recursively delete the entire directory from the backup
|
|
|
|
file_helper.del_dirs(excluded_dir)
|
|
|
|
else:
|
|
|
|
# If not, just remove the file
|
|
|
|
os.remove(excluded_dir)
|
2022-03-02 16:34:26 +00:00
|
|
|
if conf['compress']:
|
|
|
|
logger.debug("Found compress backup to be true. Calling compressed archive")
|
|
|
|
file_helper.make_compressed_archive(helper.get_os_understandable_path(backup_filename), tempDir)
|
|
|
|
else:
|
|
|
|
logger.debug("Found compress backup to be false. Calling NON-compressed archive")
|
|
|
|
file_helper.make_archive(helper.get_os_understandable_path(backup_filename), tempDir)
|
2022-03-01 01:02:30 +00:00
|
|
|
|
2021-08-14 05:07:24 +00:00
|
|
|
while len(self.list_backups()) > conf["max_backups"] and conf["max_backups"] > 0:
|
2021-08-12 04:33:18 +00:00
|
|
|
backup_list = self.list_backups()
|
|
|
|
oldfile = backup_list[0]
|
2022-01-26 01:45:30 +00:00
|
|
|
oldfile_path = f"{conf['backup_path']}/{oldfile['path']}"
|
|
|
|
logger.info(f"Removing old backup '{oldfile['path']}'")
|
2021-11-21 10:52:29 +00:00
|
|
|
os.remove(helper.get_os_understandable_path(oldfile_path))
|
2022-03-01 01:02:30 +00:00
|
|
|
|
2021-08-12 04:33:18 +00:00
|
|
|
self.is_backingup = False
|
2022-03-01 01:02:30 +00:00
|
|
|
file_helper.del_dirs(tempDir)
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Backup of server: {self.name} completed")
|
2022-03-01 03:40:11 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
2022-03-01 06:14:26 +00:00
|
|
|
websocket_helper.broadcast_user(user, 'notification', translation.translate('notify', 'backupComplete',
|
2022-03-08 01:22:30 +00:00
|
|
|
users_helper.get_user_lang_by_id(user)).format(self.name))
|
2022-03-01 03:40:11 +00:00
|
|
|
time.sleep(3)
|
2021-08-12 04:33:18 +00:00
|
|
|
return
|
|
|
|
except:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.exception(f"Failed to create backup of server {self.name} (ID {self.server_id})")
|
2021-08-12 04:33:18 +00:00
|
|
|
self.is_backingup = False
|
|
|
|
return
|
2021-03-22 04:02:18 +00:00
|
|
|
|
|
|
|
def list_backups(self):
|
2021-11-28 00:28:53 +00:00
|
|
|
if self.settings['backup_path']:
|
|
|
|
if helper.check_path_exists(helper.get_os_understandable_path(self.settings['backup_path'])):
|
2022-01-26 01:45:30 +00:00
|
|
|
files = (
|
|
|
|
helper.get_human_readable_files_sizes(helper.list_dir_by_date(helper.get_os_understandable_path(self.settings['backup_path']))))
|
|
|
|
return [{
|
|
|
|
"path": os.path.relpath(f['path'],
|
|
|
|
start=helper.get_os_understandable_path(self.settings['backup_path'])),
|
|
|
|
"size": f["size"]
|
|
|
|
} for f in files]
|
2021-11-28 00:28:53 +00:00
|
|
|
else:
|
|
|
|
return []
|
2021-03-22 04:02:18 +00:00
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Error putting backup file list for server with ID: {self.server_id}")
|
2021-11-28 00:28:53 +00:00
|
|
|
return[]
|
2021-07-25 01:09:39 +00:00
|
|
|
|
|
|
|
def jar_update(self):
|
2021-09-08 22:01:10 +00:00
|
|
|
servers_helper.set_update(self.server_id, True)
|
2021-11-20 00:14:32 +00:00
|
|
|
update_thread = threading.Thread(target=self.a_jar_update, daemon=True, name=f"exe_update_{self.name}")
|
2021-07-25 21:04:10 +00:00
|
|
|
update_thread.start()
|
|
|
|
|
2021-07-26 16:11:46 +00:00
|
|
|
def check_update(self):
|
2022-01-28 03:29:18 +00:00
|
|
|
|
2022-01-28 03:27:24 +00:00
|
|
|
if servers_helper.get_server_stats_by_id(self.server_id)['updating']:
|
2021-07-26 16:11:46 +00:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2021-07-25 21:04:10 +00:00
|
|
|
def a_jar_update(self):
|
2021-07-25 15:05:16 +00:00
|
|
|
wasStarted = "-1"
|
2021-07-27 01:14:36 +00:00
|
|
|
self.backup_server()
|
2021-07-25 01:09:39 +00:00
|
|
|
#checks if server is running. Calls shutdown if it is running.
|
|
|
|
if self.check_running():
|
2021-07-25 15:05:16 +00:00
|
|
|
wasStarted = True
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Server with PID {self.process.pid} is running. Sending shutdown command")
|
2021-07-25 01:09:39 +00:00
|
|
|
self.stop_threaded_server()
|
2021-07-25 15:05:16 +00:00
|
|
|
else:
|
|
|
|
wasStarted = False
|
2021-07-27 01:45:33 +00:00
|
|
|
if len(websocket_helper.clients) > 0:
|
|
|
|
# There are clients
|
|
|
|
self.check_update()
|
2021-07-27 01:49:47 +00:00
|
|
|
message = '<a data-id="'+str(self.server_id)+'" class=""> UPDATING...</i></a>'
|
2021-11-27 23:32:53 +00:00
|
|
|
websocket_helper.broadcast_page('/panel/server_detail', 'update_button_status', {
|
2021-07-27 01:45:33 +00:00
|
|
|
'isUpdating': self.check_update(),
|
|
|
|
'server_id': self.server_id,
|
|
|
|
'wasRunning': wasStarted,
|
|
|
|
'string': message
|
|
|
|
})
|
2021-11-27 23:32:53 +00:00
|
|
|
websocket_helper.broadcast_page('/panel/dashboard', 'send_start_reload', {
|
|
|
|
})
|
2021-11-21 10:52:29 +00:00
|
|
|
backup_dir = os.path.join(helper.get_os_understandable_path(self.settings['path']), 'crafty_executable_backups')
|
2021-07-25 01:32:16 +00:00
|
|
|
#checks if backup directory already exists
|
|
|
|
if os.path.isdir(backup_dir):
|
|
|
|
backup_executable = os.path.join(backup_dir, 'old_server.jar')
|
2021-07-25 01:09:39 +00:00
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Executable backup directory not found for Server: {self.name}. Creating one.")
|
2021-07-25 01:32:16 +00:00
|
|
|
os.mkdir(backup_dir)
|
|
|
|
backup_executable = os.path.join(backup_dir, 'old_server.jar')
|
|
|
|
|
|
|
|
if os.path.isfile(backup_executable):
|
|
|
|
#removes old backup
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Old backup found for server: {self.name}. Removing...")
|
2021-07-25 01:32:16 +00:00
|
|
|
os.remove(backup_executable)
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"Old backup removed for server: {self.name}.")
|
2021-07-25 01:09:39 +00:00
|
|
|
else:
|
2022-01-26 01:45:30 +00:00
|
|
|
logger.info(f"No old backups found for server: {self.name}")
|
2021-07-25 01:09:39 +00:00
|
|
|
|
2021-11-21 10:52:29 +00:00
|
|
|
current_executable = os.path.join(helper.get_os_understandable_path(self.settings['path']), self.settings['executable'])
|
2021-07-25 01:09:39 +00:00
|
|
|
|
2021-07-25 01:32:16 +00:00
|
|
|
#copies to backup dir
|
|
|
|
helper.copy_files(current_executable, backup_executable)
|
2021-07-25 01:09:39 +00:00
|
|
|
|
2021-07-25 01:32:16 +00:00
|
|
|
#boolean returns true for false for success
|
|
|
|
downloaded = helper.download_file(self.settings['executable_update_url'], current_executable)
|
2021-07-25 01:09:39 +00:00
|
|
|
|
2021-09-08 22:01:10 +00:00
|
|
|
while servers_helper.get_server_stats_by_id(self.server_id)['updating']:
|
2021-08-12 03:33:18 +00:00
|
|
|
if downloaded and not self.is_backingup:
|
|
|
|
logger.info("Executable updated successfully. Starting Server")
|
|
|
|
|
2021-09-08 22:01:10 +00:00
|
|
|
servers_helper.set_update(self.server_id, False)
|
2021-08-12 03:33:18 +00:00
|
|
|
if len(websocket_helper.clients) > 0:
|
|
|
|
# There are clients
|
|
|
|
self.check_update()
|
2022-01-20 18:37:45 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
|
|
|
websocket_helper.broadcast_user(user, 'notification', "Executable update finished for " + self.name)
|
2021-08-12 03:33:18 +00:00
|
|
|
time.sleep(3)
|
2021-11-27 23:32:53 +00:00
|
|
|
websocket_helper.broadcast_page('/panel/server_detail', 'update_button_status', {
|
2021-08-12 03:33:18 +00:00
|
|
|
'isUpdating': self.check_update(),
|
|
|
|
'server_id': self.server_id,
|
|
|
|
'wasRunning': wasStarted
|
|
|
|
})
|
2021-11-27 23:32:53 +00:00
|
|
|
websocket_helper.broadcast_page('/panel/dashboard', 'send_start_reload', {
|
|
|
|
})
|
2022-01-20 18:37:45 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
|
|
|
websocket_helper.broadcast_user(user, 'notification', "Executable update finished for "+self.name)
|
2021-08-12 03:33:18 +00:00
|
|
|
|
2022-01-26 01:45:30 +00:00
|
|
|
management_helper.add_to_audit_log_raw(
|
|
|
|
'Alert', '-1', self.server_id, "Executable update finished for "+self.name, self.settings['server_ip'])
|
2021-08-12 03:33:18 +00:00
|
|
|
if wasStarted:
|
|
|
|
self.start_server()
|
2021-08-12 03:49:06 +00:00
|
|
|
elif not downloaded and not self.is_backingup:
|
|
|
|
time.sleep(5)
|
2022-01-20 18:37:45 +00:00
|
|
|
server_users = server_permissions.get_server_user_list(self.server_id)
|
|
|
|
for user in server_users:
|
|
|
|
websocket_helper.broadcast_user(user,'notification',
|
2021-08-12 03:49:06 +00:00
|
|
|
"Executable update failed for " + self.name + ". Check log file for details.")
|
|
|
|
logger.error("Executable download failed.")
|
2022-03-03 21:01:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#************************************************************************************************
|
|
|
|
# Minecraft Servers Statistics
|
|
|
|
#************************************************************************************************
|
|
|
|
|
|
|
|
def realtime_stats(self):
|
|
|
|
total_players = 0
|
|
|
|
max_players = 0
|
|
|
|
servers_ping = []
|
|
|
|
raw_ping_result = []
|
|
|
|
raw_ping_result = self.get_raw_server_stats(self.server_id)
|
|
|
|
|
2022-03-04 00:47:49 +00:00
|
|
|
if f"{raw_ping_result.get('icon')}" == "b''":
|
2022-03-03 21:01:12 +00:00
|
|
|
raw_ping_result['icon'] = False
|
|
|
|
|
|
|
|
servers_ping.append({
|
|
|
|
'id': raw_ping_result.get('id'),
|
|
|
|
'started': raw_ping_result.get('started'),
|
|
|
|
'running': raw_ping_result.get('running'),
|
|
|
|
'cpu': raw_ping_result.get('cpu'),
|
|
|
|
'mem': raw_ping_result.get('mem'),
|
|
|
|
'mem_percent': raw_ping_result.get('mem_percent'),
|
|
|
|
'world_name': raw_ping_result.get('world_name'),
|
|
|
|
'world_size': raw_ping_result.get('world_size'),
|
|
|
|
'server_port': raw_ping_result.get('server_port'),
|
|
|
|
'int_ping_results': raw_ping_result.get('int_ping_results'),
|
|
|
|
'online': raw_ping_result.get('online'),
|
|
|
|
'max': raw_ping_result.get('max'),
|
|
|
|
'players': raw_ping_result.get('players'),
|
|
|
|
'desc': raw_ping_result.get('desc'),
|
|
|
|
'version': raw_ping_result.get('version'),
|
|
|
|
'icon': raw_ping_result.get('icon')
|
|
|
|
})
|
2022-03-04 00:41:30 +00:00
|
|
|
if len(websocket_helper.clients) > 0:
|
2022-03-03 21:01:12 +00:00
|
|
|
websocket_helper.broadcast_page_params(
|
|
|
|
'/panel/server_detail',
|
|
|
|
{
|
|
|
|
'id': str(self.server_id)
|
|
|
|
},
|
|
|
|
'update_server_details',
|
|
|
|
{
|
|
|
|
'id': raw_ping_result.get('id'),
|
|
|
|
'started': raw_ping_result.get('started'),
|
|
|
|
'running': raw_ping_result.get('running'),
|
|
|
|
'cpu': raw_ping_result.get('cpu'),
|
|
|
|
'mem': raw_ping_result.get('mem'),
|
|
|
|
'mem_percent': raw_ping_result.get('mem_percent'),
|
|
|
|
'world_name': raw_ping_result.get('world_name'),
|
|
|
|
'world_size': raw_ping_result.get('world_size'),
|
|
|
|
'server_port': raw_ping_result.get('server_port'),
|
|
|
|
'int_ping_results': raw_ping_result.get('int_ping_results'),
|
|
|
|
'online': raw_ping_result.get('online'),
|
|
|
|
'max': raw_ping_result.get('max'),
|
|
|
|
'players': raw_ping_result.get('players'),
|
|
|
|
'desc': raw_ping_result.get('desc'),
|
|
|
|
'version': raw_ping_result.get('version'),
|
|
|
|
'icon': raw_ping_result.get('icon')
|
|
|
|
}
|
|
|
|
)
|
|
|
|
total_players += int(raw_ping_result.get('online'))
|
|
|
|
max_players += int(raw_ping_result.get('max'))
|
|
|
|
|
|
|
|
self.record_server_stats()
|
|
|
|
|
|
|
|
if (len(servers_ping) > 0) & (len(websocket_helper.clients) > 0):
|
|
|
|
try:
|
|
|
|
websocket_helper.broadcast_page('/panel/dashboard', 'update_server_status', servers_ping)
|
|
|
|
websocket_helper.broadcast_page('/status', 'update_server_status', servers_ping)
|
|
|
|
except:
|
|
|
|
console.warning("Can't broadcast server status to websocket")
|
|
|
|
|
|
|
|
def get_servers_stats(self):
|
|
|
|
|
|
|
|
server_stats = {}
|
|
|
|
|
|
|
|
logger.info("Getting Stats for Server " + self.name + " ...")
|
|
|
|
|
|
|
|
server_id = self.server_id
|
|
|
|
server = servers_helper.get_server_data_by_id(server_id)
|
|
|
|
|
|
|
|
logger.debug(f'Getting stats for server: {server_id}')
|
|
|
|
|
|
|
|
# get our server object, settings and data dictionaries
|
|
|
|
self.reload_server_settings()
|
|
|
|
|
|
|
|
# world data
|
|
|
|
server_path = server['path']
|
|
|
|
|
|
|
|
# process stats
|
|
|
|
p_stats = Stats._get_process_stats(self.process)
|
|
|
|
|
|
|
|
# TODO: search server properties file for possible override of 127.0.0.1
|
|
|
|
internal_ip = server['server_ip']
|
|
|
|
server_port = server['server_port']
|
2022-03-03 22:13:40 +00:00
|
|
|
server_name = server.get('server_name', f"ID#{server_id}")
|
2022-03-03 21:01:12 +00:00
|
|
|
|
|
|
|
logger.debug("Pinging server '{server}' on {internal_ip}:{server_port}")
|
|
|
|
if servers_helper.get_server_type_by_id(server_id) == 'minecraft-bedrock':
|
|
|
|
int_mc_ping = ping_bedrock(internal_ip, int(server_port))
|
|
|
|
else:
|
|
|
|
int_mc_ping = ping(internal_ip, int(server_port))
|
|
|
|
|
|
|
|
int_data = False
|
|
|
|
ping_data = {}
|
|
|
|
|
|
|
|
# if we got a good ping return, let's parse it
|
|
|
|
if int_mc_ping:
|
|
|
|
int_data = True
|
|
|
|
if servers_helper.get_server_type_by_id(server['server_id']) == 'minecraft-bedrock':
|
|
|
|
ping_data = Stats.parse_server_RakNet_ping(int_mc_ping)
|
|
|
|
else:
|
|
|
|
ping_data = Stats.parse_server_ping(int_mc_ping)
|
|
|
|
#Makes sure we only show stats when a server is online otherwise people have gotten confused.
|
|
|
|
if self.check_running():
|
|
|
|
server_stats = {
|
|
|
|
'id': server_id,
|
|
|
|
'started': self.get_start_time(),
|
|
|
|
'running': self.check_running(),
|
|
|
|
'cpu': p_stats.get('cpu_usage', 0),
|
|
|
|
'mem': p_stats.get('memory_usage', 0),
|
|
|
|
"mem_percent": p_stats.get('mem_percentage', 0),
|
|
|
|
'world_name': server_name,
|
|
|
|
'world_size': Stats.get_world_size(server_path),
|
|
|
|
'server_port': server_port,
|
|
|
|
'int_ping_results': int_data,
|
|
|
|
'online': ping_data.get("online", False),
|
|
|
|
"max": ping_data.get("max", False),
|
|
|
|
'players': ping_data.get("players", False),
|
|
|
|
'desc': ping_data.get("server_description", False),
|
|
|
|
'version': ping_data.get("server_version", False)
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
server_stats = {
|
|
|
|
'id': server_id,
|
|
|
|
'started': self.get_start_time(),
|
|
|
|
'running': self.check_running(),
|
|
|
|
'cpu': p_stats.get('cpu_usage', 0),
|
|
|
|
'mem': p_stats.get('memory_usage', 0),
|
|
|
|
"mem_percent": p_stats.get('mem_percentage', 0),
|
|
|
|
'world_name': server_name,
|
|
|
|
'world_size': Stats.get_world_size(server_path),
|
|
|
|
'server_port': server_port,
|
|
|
|
'int_ping_results': int_data,
|
|
|
|
'online': False,
|
|
|
|
"max": False,
|
|
|
|
'players': False,
|
|
|
|
'desc': False,
|
|
|
|
'version': False
|
|
|
|
}
|
|
|
|
|
|
|
|
return server_stats
|
|
|
|
|
|
|
|
def get_server_players(self):
|
|
|
|
|
|
|
|
server = servers_helper.get_server_data_by_id(self.server_id)
|
|
|
|
|
|
|
|
logger.info(f"Getting players for server {server}")
|
|
|
|
|
|
|
|
# get our settings and data dictionaries
|
|
|
|
# server_settings = server.get('server_settings', {})
|
|
|
|
# server_data = server.get('server_data_obj', {})
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: search server properties file for possible override of 127.0.0.1
|
|
|
|
internal_ip = server['server_ip']
|
|
|
|
server_port = server['server_port']
|
|
|
|
|
|
|
|
logger.debug("Pinging {internal_ip} on port {server_port}")
|
|
|
|
if servers_helper.get_server_type_by_id(self.server_id) != 'minecraft-bedrock':
|
|
|
|
int_mc_ping = ping(internal_ip, int(server_port))
|
|
|
|
|
|
|
|
|
|
|
|
ping_data = {}
|
|
|
|
|
|
|
|
# if we got a good ping return, let's parse it
|
|
|
|
if int_mc_ping:
|
|
|
|
ping_data = Stats.parse_server_ping(int_mc_ping)
|
|
|
|
return ping_data['players']
|
|
|
|
return []
|
|
|
|
|
|
|
|
def get_raw_server_stats(self, server_id):
|
|
|
|
|
|
|
|
try:
|
|
|
|
server = servers_helper.get_server_obj(server_id)
|
|
|
|
except:
|
|
|
|
return {'id': server_id,
|
|
|
|
'started': False,
|
|
|
|
'running': False,
|
|
|
|
'cpu': 0,
|
|
|
|
'mem': 0,
|
|
|
|
"mem_percent": 0,
|
|
|
|
'world_name': None,
|
|
|
|
'world_size': None,
|
|
|
|
'server_port': None,
|
|
|
|
'int_ping_results': False,
|
|
|
|
'online': False,
|
|
|
|
'max': False,
|
|
|
|
'players': False,
|
|
|
|
'desc': False,
|
|
|
|
'version': False,
|
|
|
|
'icon': False}
|
|
|
|
|
|
|
|
server_stats = {}
|
|
|
|
server = servers_helper.get_server_obj(server_id)
|
|
|
|
if not server:
|
|
|
|
return {}
|
|
|
|
server_dt = servers_helper.get_server_data_by_id(server_id)
|
|
|
|
|
|
|
|
|
|
|
|
logger.debug(f'Getting stats for server: {server_id}')
|
|
|
|
|
|
|
|
# get our server object, settings and data dictionaries
|
|
|
|
self.reload_server_settings()
|
|
|
|
|
|
|
|
# world data
|
|
|
|
server_name = server_dt['server_name']
|
|
|
|
server_path = server_dt['path']
|
|
|
|
|
|
|
|
# process stats
|
|
|
|
p_stats = Stats._get_process_stats(self.process)
|
|
|
|
|
|
|
|
# TODO: search server properties file for possible override of 127.0.0.1
|
|
|
|
#internal_ip = server['server_ip']
|
|
|
|
#server_port = server['server_port']
|
|
|
|
internal_ip = server_dt['server_ip']
|
|
|
|
server_port = server_dt['server_port']
|
|
|
|
|
|
|
|
|
2022-03-03 22:13:40 +00:00
|
|
|
logger.debug(f"Pinging server '{self.name}' on {internal_ip}:{server_port}")
|
2022-03-03 21:01:12 +00:00
|
|
|
if servers_helper.get_server_type_by_id(server_id) == 'minecraft-bedrock':
|
|
|
|
int_mc_ping = ping_bedrock(internal_ip, int(server_port))
|
|
|
|
else:
|
|
|
|
int_mc_ping = ping(internal_ip, int(server_port))
|
|
|
|
|
|
|
|
int_data = False
|
|
|
|
ping_data = {}
|
|
|
|
#Makes sure we only show stats when a server is online otherwise people have gotten confused.
|
|
|
|
if self.check_running():
|
|
|
|
# if we got a good ping return, let's parse it
|
|
|
|
if servers_helper.get_server_type_by_id(server_id) != 'minecraft-bedrock':
|
|
|
|
if int_mc_ping:
|
|
|
|
int_data = True
|
|
|
|
ping_data = Stats.parse_server_ping(int_mc_ping)
|
|
|
|
|
|
|
|
server_stats = {
|
|
|
|
'id': server_id,
|
|
|
|
'started': self.get_start_time(),
|
|
|
|
'running': self.check_running(),
|
|
|
|
'cpu': p_stats.get('cpu_usage', 0),
|
|
|
|
'mem': p_stats.get('memory_usage', 0),
|
|
|
|
"mem_percent": p_stats.get('mem_percentage', 0),
|
|
|
|
'world_name': server_name,
|
|
|
|
'world_size': Stats.get_world_size(server_path),
|
|
|
|
'server_port': server_port,
|
|
|
|
'int_ping_results': int_data,
|
|
|
|
'online': ping_data.get("online", False),
|
|
|
|
"max": ping_data.get("max", False),
|
|
|
|
'players': ping_data.get("players", False),
|
|
|
|
'desc': ping_data.get("server_description", False),
|
|
|
|
'version': ping_data.get("server_version", False),
|
|
|
|
'icon': ping_data.get("server_icon", False)
|
|
|
|
}
|
|
|
|
|
|
|
|
else:
|
|
|
|
if int_mc_ping:
|
|
|
|
int_data = True
|
|
|
|
ping_data = Stats.parse_server_RakNet_ping(int_mc_ping)
|
|
|
|
try:
|
|
|
|
server_icon = base64.encodebytes(ping_data['icon'])
|
2022-03-04 00:41:30 +00:00
|
|
|
except Exception as ex:
|
2022-03-03 21:01:12 +00:00
|
|
|
server_icon = False
|
2022-03-04 00:41:30 +00:00
|
|
|
logger.info(f"Unable to read the server icon : {ex}")
|
2022-03-03 21:01:12 +00:00
|
|
|
|
|
|
|
server_stats = {
|
|
|
|
'id': server_id,
|
|
|
|
'started': self.get_start_time(),
|
|
|
|
'running': self.check_running(),
|
|
|
|
'cpu': p_stats.get('cpu_usage', 0),
|
|
|
|
'mem': p_stats.get('memory_usage', 0),
|
|
|
|
"mem_percent": p_stats.get('mem_percentage', 0),
|
|
|
|
'world_name': server_name,
|
|
|
|
'world_size': Stats.get_world_size(server_path),
|
|
|
|
'server_port': server_port,
|
|
|
|
'int_ping_results': int_data,
|
|
|
|
'online': ping_data['online'],
|
|
|
|
'max': ping_data['max'],
|
|
|
|
'players': [],
|
|
|
|
'desc': ping_data['server_description'],
|
|
|
|
'version': ping_data['server_version'],
|
|
|
|
'icon': server_icon
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
server_stats = {
|
|
|
|
'id': server_id,
|
|
|
|
'started': self.get_start_time(),
|
|
|
|
'running': self.check_running(),
|
|
|
|
'cpu': p_stats.get('cpu_usage', 0),
|
|
|
|
'mem': p_stats.get('memory_usage', 0),
|
|
|
|
"mem_percent": p_stats.get('mem_percentage', 0),
|
|
|
|
'world_name': server_name,
|
|
|
|
'world_size': Stats.get_world_size(server_path),
|
|
|
|
'server_port': server_port,
|
|
|
|
'int_ping_results': int_data,
|
|
|
|
'online': False,
|
|
|
|
'max': False,
|
|
|
|
'players': False,
|
|
|
|
'desc': False,
|
|
|
|
'version': False,
|
|
|
|
'icon': False
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
server_stats = {
|
|
|
|
'id': server_id,
|
|
|
|
'started': self.get_start_time(),
|
|
|
|
'running': self.check_running(),
|
|
|
|
'cpu': p_stats.get('cpu_usage', 0),
|
|
|
|
'mem': p_stats.get('memory_usage', 0),
|
|
|
|
"mem_percent": p_stats.get('mem_percentage', 0),
|
|
|
|
'world_name': server_name,
|
|
|
|
'world_size': Stats.get_world_size(server_path),
|
|
|
|
'server_port': server_port,
|
|
|
|
'int_ping_results': int_data,
|
|
|
|
'online': False,
|
|
|
|
"max": False,
|
|
|
|
'players': False,
|
|
|
|
'desc': False,
|
|
|
|
'version': False
|
|
|
|
}
|
|
|
|
|
|
|
|
return server_stats
|
|
|
|
|
|
|
|
def record_server_stats(self):
|
|
|
|
|
|
|
|
server = self.get_servers_stats()
|
|
|
|
Server_Stats.insert({
|
|
|
|
Server_Stats.server_id: server.get('id', 0),
|
|
|
|
Server_Stats.started: server.get('started', ""),
|
|
|
|
Server_Stats.running: server.get('running', False),
|
|
|
|
Server_Stats.cpu: server.get('cpu', 0),
|
|
|
|
Server_Stats.mem: server.get('mem', 0),
|
|
|
|
Server_Stats.mem_percent: server.get('mem_percent', 0),
|
|
|
|
Server_Stats.world_name: server.get('world_name', ""),
|
|
|
|
Server_Stats.world_size: server.get('world_size', ""),
|
|
|
|
Server_Stats.server_port: server.get('server_port', ""),
|
|
|
|
Server_Stats.int_ping_results: server.get('int_ping_results', False),
|
|
|
|
Server_Stats.online: server.get("online", False),
|
|
|
|
Server_Stats.max: server.get("max", False),
|
|
|
|
Server_Stats.players: server.get("players", False),
|
|
|
|
Server_Stats.desc: server.get("desc", False),
|
|
|
|
Server_Stats.version: server.get("version", False)
|
|
|
|
}).execute()
|
|
|
|
|
|
|
|
# delete old data
|
|
|
|
max_age = helper.get_setting("history_max_age")
|
|
|
|
now = datetime.datetime.now()
|
|
|
|
last_week = now.day - max_age
|
|
|
|
|
|
|
|
Server_Stats.delete().where(Server_Stats.created < last_week).execute()
|