scheduler, version change, database work, controller init servers, etc etc

This commit is contained in:
Phillip Tarrant 2020-08-16 22:47:53 -04:00
parent 25affba2e6
commit ad541347af
15 changed files with 714 additions and 62 deletions

View File

@ -0,0 +1,138 @@
import os
import time
import logging
import sys
import yaml
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.shared.models import db_helper
from app.classes.minecraft.server import Server
from app.classes.minecraft.server_props import ServerProps
logger = logging.getLogger(__name__)
class Controller:
def __init__(self):
self.servers_list = []
def init_all_servers(self):
# if we have servers defined, let's destroy it and start over
if len(self.servers_list) > 0:
self.servers_list = []
servers = db_helper.get_all_defined_servers()
for s in servers:
settings_file = os.path.join(s['path'], 'server.properties')
settings = ServerProps(settings_file)
temp_server_dict = {
'server_id': s.get('server_id'),
'server_data_obj': s,
'server_obj': Server(),
'server_settings': settings.props
}
# setup the server, do the auto start and all that jazz
temp_server_dict['server_obj'].do_server_setup(s)
# add this temp object to the list of init servers
self.servers_list.append(temp_server_dict)
console.info("Loaded Server: ID {} | Name: {} | Autostart: {} | Delay: {} ".format(
s['server_id'],
s['server_name'],
s['auto_start'],
s['auto_start_delay']
))
def get_server_obj(self, server_id):
for s in self.servers_list:
if int(s['server_id']) == int(server_id):
return s['server_obj']
logger.warning("Unable to find server object for server id {}".format(server_id))
return False
@staticmethod
def list_defined_servers():
servers = db_helper.get_all_defined_servers()
def list_running_servers(self):
running_servers = []
if len(self.servers_list) > 0:
# for each server
for s in self.servers_list:
# is the server running?
srv_obj = s['server_obj']
running = srv_obj.check_running()
# if so, let's add a dictionary to the list of running servers
if running:
running_servers.append({
'id': srv_obj.server_id,
'name': srv_obj.name
})
return running_servers
def stop_all_servers(self):
servers = self.list_running_servers()
logger.info("Found {} running server(s)".format(len(servers)))
console.info("Found {} running server(s)".format(len(servers)))
logger.info("Stopping All Servers")
console.info("Stopping All Servers")
for s in servers:
logger.info("Stopping Server ID {} - {}".format(s['id'], s['name']))
console.info("Stopping Server ID {} - {}".format(s['id'], s['name']))
# get object
svr_obj = self.get_server_obj(s['id'])
running = svr_obj.check_running(True)
# issue the stop command
svr_obj.stop_threaded_server()
# while it's running, we wait
x = 0
while running:
logger.info("Server {} is still running - waiting 2s to see if it stops".format(s['name']))
console.info("Server {} is still running - waiting 2s to see if it stops".format(s['name']))
running = svr_obj.check_running()
# let's keep track of how long this is going on...
x = x + 1
# if we have been waiting more than 120 seconds. let's just kill the pid
if x >= 60:
logger.error("Server {} is taking way too long to stop. Killing this process".format(s['name']))
console.error("Server {} is taking way too long to stop. Killing this process".format(s['name']))
svr_obj.killpid(svr_obj.PID)
running = False
# if we killed the server, let's clean up the object
if not running:
svr_obj.cleanup_server_object()
time.sleep(2)
# let's wait 2 seconds to let everything flush out
time.sleep(2)
logger.info("All Servers Stopped")
console.info("All Servers Stopped")
controller = Controller()

View File

@ -0,0 +1,270 @@
import os
import re
import json
import time
import psutil
import pexpect
import datetime
import threading
import schedule
import logging.config
from pexpect.popen_spawn import PopenSpawn
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
logger = logging.getLogger(__name__)
class Server:
def __init__(self):
# holders for our process
self.process = None
self.line = False
self.PID = None
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
self.name = None
self.is_crashed = False
self.restart_count = 0
def do_server_setup(self, server_data_obj):
logger.info('Creating Server object: {} | Server Name: {} | Auto Start: {}'.format(
server_data_obj['server_id'],
server_data_obj['server_name'],
server_data_obj['auto_start']
))
self.server_id = server_data_obj['server_id']
self.name = server_data_obj['server_name']
self.settings = server_data_obj
# build our server run command
self.setup_server_run_command()
if server_data_obj['auto_start']:
delay = int(self.settings['auto_start_delay'])
logger.info("Scheduling server {} to start in {} seconds".format(self.name, delay))
console.info("Scheduling server {} to start in {} seconds".format(self.name, delay))
schedule.every(delay).seconds.do(self.run_scheduled_server)
def run_scheduled_server(self):
console.info("Starting Minecraft server ID: {} - {}".format(self.server_id, self.name))
logger.info("Starting Minecraft server {}".format(self.server_id, self.name))
self.run_threaded_server()
# remove the scheduled job since it's ran
return schedule.CancelJob
def run_threaded_server(self):
# start the server
self.server_thread = threading.Thread(target=self.start_server, daemon=True)
self.server_thread.start()
def setup_server_run_command(self):
# configure the server
server_exec_path = self.settings['executable']
self.server_command = self.settings['execution_command']
self.server_path = self.settings['path']
# 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):
logger.critical("Server executable path: {} does not seem to exist".format(full_path))
console.critical("Server executable path: {} does not seem to exist".format(full_path))
helper.do_exit()
if not helper.check_path_exits(self.server_path):
logger.critical("Server path: {} does not seem to exits".format(self.server_path))
console.critical("Server path: {} does not seem to exits".format(self.server_path))
helper.do_exit()
if not helper.check_writeable(self.server_path):
logger.critical("Unable to write/access {}".format(self.server_path))
console.warning("Unable to write/access {}".format(self.server_path))
helper.do_exit()
def start_server(self):
# 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
logger.info("Launching Server {} with command {}".format(self.name, self.server_command))
console.info("Launching Server {} with command {}".format(self.name, self.server_command))
if os.name == "nt":
logger.info("Windows Detected - launching cmd")
self.server_command = self.server_command.replace('\\', '/')
logging.info("Opening CMD prompt")
self.process = pexpect.popen_spawn.PopenSpawn('cmd \r\n', timeout=None, encoding=None)
drive_letter = self.server_path[:1]
if drive_letter.lower() != "c":
logger.info("Server is not on the C drive, changing drive letter to {}:".format(drive_letter))
self.process.send("{}:\r\n".format(drive_letter))
logging.info("changing directories to {}".format(self.server_path.replace('\\', '/')))
self.process.send('cd {} \r\n'.format(self.server_path.replace('\\', '/')))
logging.info("Sending command {} to CMD".format(self.server_command))
self.process.send(self.server_command + "\r\n")
self.is_crashed = False
else:
logger.info("Linux Detected - launching Bash")
self.process = pexpect.popen_spawn.PopenSpawn('/bin/bash \n', timeout=None, encoding=None)
logger.info("Changing directory to {}".format(self.server_path))
self.process.send('cd {} \n'.format(self.server_path))
logger.info("Sending server start command: {} to shell".format(self.server_command))
self.process.send(self.server_command + '\n')
self.is_crashed = False
ts = time.time()
self.start_time = str(datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S'))
if psutil.pid_exists(self.process.pid):
parent = psutil.Process(self.process.pid)
time.sleep(.5)
children = parent.children(recursive=True)
for c in children:
self.PID = c.pid
logger.info("Server {} running with PID {}".format(self.name, self.PID))
console.info("Server {} running with PID {}".format(self.name, self.PID))
self.is_crashed = False
else:
logger.warning("Server PID {} died right after starting - is this a server config issue?".format(self.PID))
console.warning("Server PID {} died right after starting - is this a server config issue?".format(self.PID))
if self.settings['crash_detection']:
logger.info("Server {} has crash detection enabled - starting watcher task".format(self.name))
console.info("Server {} has crash detection enabled - starting watcher task".format(self.name))
# TODO: create crash detection watcher and such
# schedule.every(30).seconds.do(self.check_running).tag(self.name)
def stop_threaded_server(self):
self.stop_server()
if self.server_thread:
self.server_thread.join()
def stop_server(self):
if self.settings['stop_command_needed']:
self.send_command(self.settings['stop_command'])
else:
self.killpid(self.PID)
def cleanup_server_object(self):
self.process = None
self.PID = None
self.start_time = None
self.name = None
def check_running(self, shutting_down=False):
# if process is None, we never tried to start
if self.PID is None:
return False
try:
running = psutil.pid_exists(self.PID)
except Exception as e:
logger.error("Unable to find if server PID exists: {}".format(self.PID))
running = False
pass
if not running:
# did the server crash?
if not shutting_down:
# do we have crash detection turned on?
if self.settings['crash_detection']:
# 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
return False
# we have tried to restart 4 times...
elif self.restart_count == 4:
logger.warning("Server {} has been restarted {} times. It has crashed, not restarting.".format(
self.name, self.restart_count))
# set to 99 restart attempts so this elif is skipped next time. (no double logging)
self.restart_count = 99
self.is_crashed = True
return False
else:
self.is_crashed = True
return False
self.process = None
self.PID = None
self.name = None
return False
return True
def send_command(self, command):
if not self.check_running() and command.lower() != 'start':
logger.warning("Server not running, unable to send command \"{}\"".format(command))
return False
logger.debug("Sending command {} to server via pexpect".format(command))
# send it
self.process.send(command + '\n')
def crash_detected(self, name):
# the server crashed, or isn't found - so let's reset things.
logger.warning("The server {} seems to have vanished unexpectedly, did it crash?".format(name))
if self.settings['crash_detection']:
logger.info("The server {} has crashed and will be restarted. Restarting server".format(name))
self.run_threaded_server()
return True
else:
logger.info("The server {} has crashed, crash detection is disabled and it will not be restarted".format(name))
return False
def killpid(self, pid):
logger.info("Terminating PID {} and all child processes".format(pid))
process = psutil.Process(pid)
# 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!)
logger.info("Sending SIGKILL to PID {}".format(proc.name))
proc.kill()
# kill the main process we are after
logger.info('Sending SIGKILL to parent')
process.kill()
def get_start_time(self):
if self.check_running():
return self.start_time
else:
return False

View File

@ -0,0 +1,66 @@
import pprint
import os
class ServerProps:
def __init__(self, filepath):
self.filepath = filepath
self.props = self._parse()
def _parse(self):
"""Loads and parses the file speified in self.filepath"""
with open(self.filepath) as fp:
line = fp.readline()
d = {}
if os.path.exists(".header"):
os.remove(".header")
while line:
if '#' != line[0]:
s = line
s1 = s[:s.find('=')]
if '\n' in s:
s2 = s[s.find('=')+1:s.find('\\')]
else:
s2 = s[s.find('=')+1:]
d[s1] = s2
else:
with open(".header", "a+") as h:
h.write(line)
line = fp.readline()
return d
def print(self):
"""Prints the properties dictionary (using pprint)"""
pprint.pprint(self.props)
def get(self):
"""Returns the properties dictionary"""
return self.props
def update(self, key, val):
"""Updates property in the properties dictionary [ update("pvp", "true") ] and returns boolean condition"""
if key in self.props.keys():
self.props[key] = val
return True
else:
return False
def save(self):
"""Writes to the new file"""
with open(self.filepath, "a+") as f:
f.truncate(0)
with open(".header") as header:
line = header.readline()
while line:
f.write(line)
line = header.readline()
header.close()
for key, value in self.props.items():
f.write(key + "=" + value + "\n")
if os.path.exists(".header"):
os.remove(".header")
@staticmethod
def cleanup():
if os.path.exists(".header"):
os.remove(".header")

View File

@ -32,7 +32,7 @@ class Helpers:
self.session_file = os.path.join(self.root_dir, 'session.lock')
self.settings_file = os.path.join(self.root_dir, 'config.ini')
self.webroot = os.path.join(self.root_dir, 'app', 'frontend')
self.db_path = os.path.join(self.root_dir, 'commander.sqlite')
self.db_path = os.path.join(self.root_dir, 'crafty.sqlite')
self.passhasher = PasswordHasher()
self.exiting = False
@ -74,6 +74,15 @@ class Helpers:
return version_data
def get_version_string(self):
version_data = self.get_version()
# set some defaults if we don't get version_data from our helper
version = "{}.{}.{}".format(version_data.get('major', '?'),
version_data.get('minor', '?'),
version_data.get('sub', '?'))
return str(version)
def do_exit(self):
exit_file = os.path.join(self.root_dir, 'exit.txt')
try:
@ -111,6 +120,7 @@ class Helpers:
def ensure_logging_setup(self):
log_file = os.path.join(os.path.curdir, 'logs', 'commander.log')
session_log_file = os.path.join(os.path.curdir, 'logs', 'session.log')
logger.info("Checking app directory writable")
@ -136,7 +146,7 @@ class Helpers:
# del any old session.lock file as this is a new session
try:
os.remove(self.session_file)
os.remove(session_log_file)
except:
pass
@ -326,5 +336,11 @@ class Helpers:
"""
return ''.join(random.choice(chars) for x in range(size))
@staticmethod
def is_os_windows():
if os.name == 'nt':
return True
else:
return False
helper = Helpers()

View File

@ -1,15 +1,18 @@
import os
import sys
import logging
import datetime
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.minecraft.server_props import ServerProps
logger = logging.getLogger(__name__)
try:
from peewee import *
from playhouse.shortcuts import model_to_dict
import yaml
except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
@ -59,6 +62,23 @@ class Host_Stats(BaseModel):
table_name = "host_stats"
class Servers(BaseModel):
server_id = AutoField()
created = DateTimeField(default=datetime.datetime.now)
server_name = CharField(default="Server")
path = CharField(default="")
executable = CharField(default="")
log_path = CharField(default="")
execution_command = CharField(default="")
auto_start = BooleanField(default=0)
auto_start_delay = IntegerField(default=10)
crash_detection = BooleanField(default=0)
stop_command = CharField(default="stop")
class Meta:
table_name = "servers"
class Webhooks(BaseModel):
id = AutoField()
name = CharField(max_length=64, unique=True)
@ -80,6 +100,7 @@ class Backups(BaseModel):
class Meta:
table_name = 'backups'
class db_builder:
@staticmethod
@ -89,7 +110,8 @@ class db_builder:
Backups,
Users,
Host_Stats,
Webhooks
Webhooks,
Servers
])
@staticmethod
@ -105,7 +127,25 @@ class db_builder:
def is_fresh_install():
if helper.check_file_exists(helper.db_path):
return False
return True
class db_shortcuts:
def return_rows(self, query):
rows = []
if query:
for s in query:
rows.append(model_to_dict(s))
else:
rows.append({})
return rows
def get_all_defined_servers(self):
query = Servers.select()
return self.return_rows(query)
installer = db_builder()
db_helper = db_shortcuts()

View File

@ -5,13 +5,20 @@ import time
import logging
import threading
from app.classes.shared.helpers import helper
from app.classes.shared.console import console
from app.classes.web.tornado import webserver
from app.classes.minecraft import server_props
logger = logging.getLogger(__name__)
try:
import schedule
except ModuleNotFoundError as e:
logger.critical("Import Error: Unable to load {} module".format(e, e.name))
console.critical("Import Error: Unable to load {} module".format(e, e.name))
sys.exit(1)
class TasksManager:
@ -22,6 +29,8 @@ class TasksManager:
self.main_kill_switch_thread = threading.Thread(target=self.main_kill_switch, daemon=True, name="main_loop")
self.main_thread_exiting = False
self.schedule_thread = threading.Thread(target=self.scheduler_thread, daemon=True, name="scheduler")
def get_main_thread_run_status(self):
return self.main_thread_exiting
@ -39,9 +48,12 @@ class TasksManager:
# commander.stop_all_servers()
logger.info("***** Crafty Shutting Down *****\n\n")
console.info("***** Crafty Shutting Down *****\n\n")
os.remove(helper.session_file)
os.remove(os.path.join(helper.root_dir, 'exit.txt'))
# ServerProps.cleanup()
try:
os.remove(helper.session_file)
os.remove(os.path.join(helper.root_dir, 'exit.txt'))
os.remove(os.path.join(helper.root_dir, '.header'))
except:
pass
self.main_thread_exiting = True
def start_webserver(self):
@ -57,6 +69,16 @@ class TasksManager:
def stop_webserver(self):
self.tornado.stop_web_server()
def start_scheduler(self):
logger.info("Launching Scheduler Thread...")
console.info("Launching Scheduler Thread...")
self.schedule_thread.start()
@staticmethod
def scheduler_thread():
while True:
schedule.run_pending()
time.sleep(1)

View File

@ -5,12 +5,13 @@ import tornado.escape
import bleach
from app.classes.shared.console import console
from app.classes.shared.models import Users
from app.classes.shared.models import Users, installer
from app.classes.web.base_handler import BaseHandler
from app.classes.minecraft.controller import controller
logger = logging.getLogger(__name__)
class PanelHandler(BaseHandler):
@tornado.web.authenticated
@ -25,6 +26,8 @@ class PanelHandler(BaseHandler):
'user_data': user_data
}
servers = controller.list_defined_servers()
if page == 'unauthorized':
template = "panel/denied.html"

View File

@ -40,7 +40,9 @@ class PublicHandler(BaseHandler):
self.clear_cookie("user")
self.clear_cookie("user_data")
# print(page)
page_data = {
'version': helper.get_version_string()
}
error = bleach.clean(self.get_argument('error', ""))
@ -51,19 +53,14 @@ class PublicHandler(BaseHandler):
# sensible defaults
template = "public/404.html"
page_data = "{}"
# if we have no page, let's go to login
if page is None:
self.redirect("public/login")
if page == "login":
template = "public/login.html"
page_data = {'error': error_msg}
page_data['error'] = error_msg
# our default 404 template
# if we have no page, let's go to login
else:
page_data = {'error': error_msg}
self.redirect("public/login")
self.render(template, data=page_data)

View File

@ -1,5 +1,5 @@
{
"major": 0.1,
"minor": 1,
"major": 4,
"minor": 0,
"sub": "Alpha"
}

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{% block title %}{{ _('Default') }}{% end %}</title>
<!-- plugins:css -->
<link rel="stylesheet" href="/static/assets/vendors/mdi/css/materialdesignicons.min.css">
<link rel="stylesheet" href="/static/assets/vendors/flag-icon-css/css/flag-icon.min.css">
<link rel="stylesheet" href="/static/assets/vendors/ti-icons/css/themify-icons.css">
<link rel="stylesheet" href="/static/assets/vendors/typicons/typicons.css">
<link rel="stylesheet" href="/static/assets/vendors/css/vendor.bundle.base.css">
<!-- endinject -->
<!-- Plugin css for this page -->
<!-- End Plugin css for this page -->
<!-- Layout styles -->
<link rel="stylesheet" href="/static/assets/css/dark/style.css">
<!-- End Layout styles -->
<link rel="shortcut icon" href="/static/assets/images/favicon.png" />
</head>
<body class="dark-theme">
<div class="container-scroller">
<div class="container-fluid page-body-wrapper full-page-wrapper">
<div class="content-wrapper d-flex align-items-center auth auth-bg-1 theme-one">
<div class="row w-100">
<div class="col-lg-4 mx-auto">
{% block content %}
{% end %}
</div>
</div>
</div>
<!-- content-wrapper ends -->
</div>
<!-- page-body-wrapper ends -->
</div>
<!-- container-scroller -->
<!-- plugins:js -->
<script src="/static/assets/vendors/js/vendor.bundle.base.js"></script>
<!-- endinject -->
<!-- inject:js -->
<script src="/static/assets/js/shared/off-canvas.js"></script>
<script src="/static/assets/js/shared/hoverable-collapse.js"></script>
<script src="/static/assets/js/shared/misc.js"></script>
<script src="/static/assets/js/shared/settings.js"></script>
<script src="/static/assets/js/shared/todolist.js"></script>
<!-- endinject -->
</body>
</html>

View File

@ -6,28 +6,13 @@
<li class="nav-item nav-category" style="margin-top:10px;">Main Menu</li>
<li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#dashboard-dropdown" aria-expanded="false" aria-controls="dashboard-dropdown">
<i class="menu-icon fas fa-chart-network"></i>
<span class="menu-title">Nodes</span>
<i class="menu-arrow"></i>
<a class="nav-link" href="/panel/dashboard">
<i class="fas fa-chart-network"></i>&nbsp;
<span class="menu-title">Dashboard</span>
</a>
<div class="collapse" id="dashboard-dropdown">
<ul class="nav flex-column sub-menu">
<li class="nav-item">
<a class="nav-link" href="/pro/node_names">Nodes Claims</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.html">Buntu-Server</a>
</li>
<li class="nav-item">
<a class="nav-link" href="pages/dashboards/dashboard-2.html">RMDC-Stone</a>
</li>
</ul>
</div>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="collapse" href="#page-layouts" aria-expanded="false" aria-controls="page-layouts">
<i class="menu-icon typcn typcn-archive"></i>

View File

@ -25,14 +25,14 @@
<div class="col-lg-3 col-md-6">
<div class="d-flex">
<div class="wrapper">
<h5 class="mb-1 font-weight-medium text-primary"> Nodes</h5>
<h3 class="mb-0 font-weight-semibold">2 </h3>
<h5 class="mb-1 font-weight-medium text-primary"> Host</h5>
<h3 class="mb-0 font-weight-semibold"> <i class="fas fa-chart-line"></i></h3>
</div>
<div class="wrapper my-auto ml-auto ml-lg-4">
<p class="mb-0 text-success">1 Online</p>
<p class="mb-0 text-danger">1 Offline</p>
<p class="mb-0 text-success">3.5% CPU</p>
<p class="mb-0 text-danger">80% Memory</p>
</div>
</div>
</div>

View File

@ -55,15 +55,7 @@
</div>
<div class="text-block text-center my-3">
<span class="text-small font-weight-semibold">Not a member ?</span>
<a href="/public/register" class="text-small">Closed During Alpha Testing</a>
</div>
<div class="text-block text-center my-3">
<span class="text-small font-weight-semibold">Want to know more?</span>
<a href="/public/timeline" class="text-small">See the release timeline</a>
</div>
<div class="text-block text-center my-3">
<span class="text-small font-weight-semibold">Copyright © 2020 Rent My Data Center. All rights reserved.</span>
<span class="text-small font-weight-semibold"><a href="https://craftycontrol.com/">Crafty Control {{data['version'] }}</a> </span>
</div>
</form>

View File

@ -0,0 +1,66 @@
{% extends ../blank_base.html %}
{% block title %}Crafty Controller - Setup 1{% end %}
{% block content %}
<div class="auto-form-wrapper">
<div class="text-center">
<!-- <img src="/static/assets/images/logo_long.jpg">-->
{{ _('Configure Your Existing Server') }}<br /><br />
</div>
<form action="/public/login" method="post">
{% raw xsrf_form_html() %}
<div class="form-group">
<label class="label">
{{ _('Server Name') }} - <small>{{ _('Example Survival Server') }}</small>
</label>
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ _('Server Name') }}" name="server_name" value="{{_('MyFirstServer') }}" maxlength="55">
</div>
</div>
<div class="form-group">
{% if data['is_windows'] %}
<label class="label">
{{ _('Server Path') }} - <small>{{ _('Example c:\minecraft\server') }}</small>
</label>
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
value="c:\windows\minecraft" maxlength="255">
</div>
{% else %}
<label class="label">
{{ _('Server Path') }} - <small>{{ _("Example: /var/opt/minecraft/server") }}</small>
</label>
<div class="input-group">
<input type="text" class="form-control" placeholder="{{ _('Server Path') }}" name="server_path"
value="c:\windows\minecraft" maxlength="255">
</div>
{% end %}
</div>
<div class="form-group">
<label class="label">
{{ _('Server Jar') }} - <small>{{ _('Example paper.jar') }}</small>
</label>
<input type="text" class="form-control" placeholder="{{ _('Server Jar') }}" name="server_jar" value="paper.jar" maxlength="255">
</div>
<div class="form-group">
<button class="btn btn-primary submit-btn btn-block">Save</button>
</div>
</form>
</div>
{% end %}

24
main.py
View File

@ -10,21 +10,16 @@ from app.classes.shared.console import console
from app.classes.shared.helpers import helper
from app.classes.shared.models import installer
from app.classes.shared.tasks import tasks_manager
from app.classes.minecraft.controller import controller
def do_intro():
logger.info("***** Commander Agent Launched *****")
logger.info("***** Crafty Controller Started *****")
version_data = helper.get_version()
# set some defaults if we don't get version_data from our helper
version = "{}.{}.{}".format(version_data.get('major', '?'),
version_data.get('minor', '?'),
version_data.get('sub', '?'))
version = helper.get_version_string()
intro = """
{lines}
#\t\tWelcome to Crafty Controller - v.{version}\t\t #
#\t\t\t Codename: Commander \t\t\t\t #
{lines}
# \tServer Manager / Web Portal for your Minecraft server \t #
# \t\tHomepage: www.craftycontrol.com\t\t\t #
@ -85,6 +80,10 @@ if __name__ == '__main__':
helper.create_session_file(ignore=args.ignore)
tasks_manager.start_webserver()
tasks_manager.start_scheduler()
# slowing down reporting just for a 1/2 second so messages look cleaner
time.sleep(.5)
# this should always be last
tasks_manager.start_main_kill_switch_watcher()
@ -94,7 +93,14 @@ if __name__ == '__main__':
installer.create_tables()
installer.default_settings()
# our main loop
# init servers
logger.info("Initializing all servers defined")
console.info("Initializing all servers defined")
controller.init_all_servers()
servers = controller.list_defined_servers()
# our main loop - eventually a shell
while True:
if tasks_manager.get_main_thread_run_status():
sys.exit(0)