188 lines
8.3 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python3
import os
import sys
import time
import json
import psutil
import signal
import logging
import requests
import argparse
import configparser
from subprocess import Popen, PIPE
from multiprocessing.pool import ThreadPool
### функция для преобразования элемента в список
def ini_item(value: str=""):
tmp=value.split(',')
if len(tmp) > 1:
return tmp
return value
### функция запуска сервера
def server_start(param):
return Popen(["/usr/bin/python3", param[0], f"{param[1]} {str(param[2])} {param[3]} {str(param[4])} >/dev/null 2>&1"], stdout=PIPE, stderr=PIPE, shell=False).pid
### функция проверки версии игры
def server_version(ver):
try:
if 'steamcmd_update_check' in cfg_settings and cfg_settings['steamcmd_update_check'] != "":
# делаем запрос к api steam
result = requests.get(cfg_settings['steamcmd_update_check'] + str(ver))
if result.status_code == 200:
result=result.json()
print(result)
# если версия устарела, возвращаем текущую
if not result['response']['up_to_date']:
return str(result['response']['required_version'])
elif 'steamcmd_news_update_check' in cfg_settings and cfg_settings['steamcmd_news_update_check'] != "":
# делаем запрос к api steam
result = requests.get(cfg_settings['steamcmd_news_update_check'])
if result.status_code == 200:
result=result.json()['appnews']['newsitems'][0]
# проверяем связана ли новость с обновлением
title=str(result['title']).lower()
contents=str(result['contents']).lower()
if title and contents and \
('fix' in title or 'fix' in contents or \
'version' in title or 'version' in contents or \
'changelog' in title or 'changelog' in contents or \
'update' in title or 'update' in contents):
# если дата новости не равна текущей
if str(ver) != str(result['date']):
return str(result['date'])
except:
None
# если версия актуальна, или код ответа с ошибкой
return True
### главная функция
if __name__ == "__main__":
sys.stderr = open('/dev/null')
# парсим аргументы
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("action", choices=["start", "stop", "restart", "status"])
parser.add_argument("config")
args = parser.parse_args()
# конфигурационный файл серверов
c_cfg = configparser.ConfigParser()
c_cfg.optionxform = str # для правельного учета регистра
c_cfg.read(args.config)
cfg_settings={}
# пробегаем основные параметры
for key, value in c_cfg.items("SETTINGS"):
cfg_settings[key]=ini_item(value)
# проверяем существование папки для логов
if not os.path.exists(log_folder_path:=os.path.dirname(cfg_settings['log'])):
try:
# создание папки
os.makedirs(log_folder_path)
except:
None
# создаем объект класса logging
logging.basicConfig(filename=cfg_settings['log'], encoding='utf-8', level=logging.INFO,format='%(asctime)s.%(msecs)03d %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S',)
# пытаемся прочитать текущую версию из файла
try:
with open(cfg_settings['steamcmd_game_version'], "r") as f:
c_version = f.read()
except FileNotFoundError:
c_version=""
## функция запуска сервиса
def s_start(c_version, cfg_settings, args_config):
print("Start ...")
count=0
c_pid=str(os.getppid())
pool = ThreadPool()
for game in cfg_settings['modes_game']:
for dif in cfg_settings['difficulty_game']:
time.sleep(0.5)
c=pool.map(server_start, ([os.path.join(cfg_settings['dir_work'],"Daemon") + "/kf2-server.py", count, game, dif, args_config] ,))
c_pid+=f" {c[0]}"
count+=1
if game in ['KFGameContent.KFGameInfo_WeeklySurvival', 'KFGameContent.KFGameInfo_VersusSurvival']:
break
pool.close()
pool.join()
# пишем в файл pid процессов
with open(cfg_settings['pid_kf2'], "w") as f:
f.write(str(c_pid))
while True:
# проверяем наличие обновлений в цикле
version=server_version(c_version)
if version != True:
# если прилетели обновления, обновляем
command=f"echo {cfg_settings['sys_user']} | su --pty - {cfg_settings['sys_user']} -c \"steamcmd +force_install_dir {cfg_settings['dir_server_data']} {cfg_settings['steamcmd_update_key']}\""
with Popen(command, stdout=PIPE, stderr=PIPE, shell=True, user=cfg_settings['sys_user'], group=cfg_settings['sys_group'], ) as process: # запускаем процесс от пользователя steam
print("Update ...")
logging.info(f"Update: New version - {version}, old - {c_version}")
c_version=str(version)
process.communicate()
# после обновления пишим новую версию в файл
with open(cfg_settings['steamcmd_game_version'], "w") as f:
f.write(str(version))
# после обновления перезапускаемся
s_stop(1)
return s_start(c_version, cfg_settings, args_config)
time.sleep(3600) # проверяем обновление каждый час
## функция остановки сервиса
def s_stop(split: int=0):
global c_version,cfg_settings
print("Stop ...")
with open(cfg_settings['pid_kf2'], "r") as f:
c_pid = f.read()
c_pid=c_pid.split()
for i in range(len(c_pid)-split):
try:
os.killpg(os.getpgid(int(c_pid[(i+split)])), signal.SIGINT)
time.sleep(0.5)
except:
None
# если старт
if args.action == "start":
try:
# проверка на запущенность сервиса, проверяем первый pid
with open(cfg_settings['pid_kf2'], "r") as f:
c_pid = f.read()
c_pid=c_pid.split()
if str(os.getppid()) != c_pid[0]:
try:
# если запущен, выходим
os.getpgid(int(c_pid[0]))
raise KeyboardInterrupt()
except OSError:
# если не запущен, запускаем
s_start(c_version, cfg_settings, args.config)
# если запускаем из терминала
s_start(c_version, cfg_settings, args.config)
except Exception:
# если файл pid не существует, запускаем
s_start(c_version, cfg_settings, args.config)
# если стоп
elif args.action == "stop":
s_stop()
# если перезапуск
elif args.action == "restart":
s_stop()
time.sleep(1)
s_start()
# если информация о работе
elif args.action == "status":
bin_name=os.path.basename(cfg_settings['bin_server'])
# проверяем все системные процессы и показываем наши сервера
for pid in psutil.pids():
p = psutil.Process(pid)
if p.name() == bin_name:
print(f"[{pid}] {' '.join(p.cmdline())}")
# значение по умолчанию
else:
parser.print_help()