Первый commit
This commit is contained in:
		
							
								
								
									
										164
									
								
								Daemon/kf2-server.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										164
									
								
								Daemon/kf2-server.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| #!/usr/bin/python3 | ||||
|  | ||||
| import os | ||||
| import sys | ||||
| import signal | ||||
| import shutil | ||||
| import logging | ||||
| import tempfile | ||||
| import configparser | ||||
|  | ||||
| from subprocess import Popen, PIPE | ||||
|  | ||||
| ### функция перехвата сигнала от системы | ||||
| def receiveSignal(signalNumber, frame): | ||||
|     print(f"I'm killed - {os.getpid()}") | ||||
|     raise KeyboardInterrupt() | ||||
|  | ||||
| ### функция для преобразования элемента в список | ||||
| def ini_item(value: str=""): | ||||
|     tmp=value.split(',') | ||||
|     if len(tmp) > 1: | ||||
|         return tmp | ||||
|     return value | ||||
|  | ||||
| ### функция вычисления номера порта | ||||
| def set_port(value: list, c: int): | ||||
|     if len(value) > 1: | ||||
|         try: | ||||
|             factor=int(value[2]) | ||||
|         except Exception: | ||||
|             factor=1 | ||||
|         port=int(value[0])+1*factor*c | ||||
|         if int(value[1])>int(value[0]) and port<=int(value[1]): | ||||
|             return port | ||||
|         elif c<int(value[1]): | ||||
|             return port | ||||
|         else: | ||||
|             print(f"I'm killed - {os.getpid()}") | ||||
|             logging.error("You have exceeded the server port limit") | ||||
|             raise KeyboardInterrupt() | ||||
|     return value | ||||
|  | ||||
|  | ||||
| ### функция запуска сервера | ||||
| def daemon_server(count:int, game:str, dif:int, config:str): | ||||
|     try: | ||||
|         server_id=f"#{str(count+1)}" # id сервера (используем для именования папки и добавляем к имени сервера) | ||||
|         # конфигурационный файл серверов | ||||
|         c_cfg = configparser.ConfigParser() | ||||
|         c_cfg.optionxform = str # для правельного учета регистра | ||||
|         c_cfg.read(config) | ||||
|         cfg_settings={} | ||||
|         # пробегаем основные параметры | ||||
|         for key, value in c_cfg.items("SETTINGS"): | ||||
|             cfg_settings[key]=ini_item(value) | ||||
|  | ||||
|         # создаем объект класса 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',) | ||||
|  | ||||
|         # путь к папке с конфигурацией сервера | ||||
|         c_dir_ini=os.path.join(cfg_settings['dir_server_data'], f"KFGame/Config/{server_id}") | ||||
|         try: | ||||
|             # удаляем старый ln | ||||
|             shutil.rmtree(c_dir_ini, True) | ||||
|             os.remove(c_dir_ini) | ||||
|         except FileNotFoundError: | ||||
|             None | ||||
|         # создаём временную дерриктори под конфиг | ||||
|         tmp_dir=tempfile.TemporaryDirectory() | ||||
|         # изменяем владельца | ||||
|         shutil.chown(tmp_dir.name, cfg_settings['sys_user'], cfg_settings['sys_group']) | ||||
|         # создаем ln в папку с сервером | ||||
|         os.symlink(tmp_dir.name, c_dir_ini) | ||||
|  | ||||
|         # пробегаем дополнительные параметры конфигурации сервера | ||||
|         for key1 in c_cfg.sections(): | ||||
|             if key1 != "SETTINGS": | ||||
|                 # путь к файлу inic_dir_ini | ||||
|                 c_file_ini=f"{c_dir_ini}/{key1}" | ||||
|                 # если в теге есть параметры то анализируем | ||||
|                 c_key=c_cfg.items(key1) | ||||
|                 if len(c_key) > 0: | ||||
|                     c_ini = configparser.ConfigParser(strict=False) | ||||
|                     c_ini.optionxform = str # для правельного учета регистра | ||||
|                     # пробегаем парметры деррективы | ||||
|                     for key2, value in c_key: | ||||
|                         # разделяем ключ и значение | ||||
|                         c_param=ini_item(key2) | ||||
|                         # изменяем параметр | ||||
|                         if not c_param[0] in c_ini: | ||||
|                             c_ini[c_param[0]] = {} | ||||
|                         # дописываем индекс в название сервера | ||||
|                         if c_param[1] == 'ServerName' or c_param[1] == 'ClanMotto': | ||||
|                             value+=f" {server_id}" | ||||
|                         # обработка мараметра динамической конфигурации | ||||
|                         if c_param[1] == 'bUsedForTakeover': | ||||
|                             if int(value) > 0 and count < int(value): | ||||
|                                 value="FALSE" | ||||
|                             else: | ||||
|                                 value="TRUE" | ||||
|                         c_ini[c_param[0]][c_param[1]] = value | ||||
|                     # пишем изменения в файл | ||||
|                     with open(c_file_ini, "w") as f: | ||||
|                         c_ini.write(f) | ||||
|                     c_ini.clear() | ||||
|  | ||||
|         # запускаем бинарник сервера | ||||
|         command=f"{cfg_settings['bin_server']} kf-bioticslab?Game={game}?Difficulty={dif} -Port={set_port(cfg_settings['port_game'], count)} -QueryPort={set_port(cfg_settings['port_query'], count)} -WebAdminPort={set_port(cfg_settings['port_webadmin'], count)} -ConfigSubDir={server_id}" # команды запуска | ||||
|         while True: | ||||
|             with Popen(command, stdout=PIPE, stderr=PIPE, shell=True, user=cfg_settings['sys_user'], group=cfg_settings['sys_group'], ) as process: # запускаем процесс от пользователя steam | ||||
|                 logging.info(f"Start: {command}") | ||||
|                 process.communicate() # ждём, пока работает процеес и есть вывод в терминал | ||||
|                 logging.info(f"Stop: {command}") | ||||
|     except KeyboardInterrupt: | ||||
|         None | ||||
|     except Exception as err: | ||||
|         logging.info(f"Unexpected termination: {err}") | ||||
|     # при завершение подчищаем за собой | ||||
|     tmp_dir.cleanup() | ||||
|     try: | ||||
|         shutil.rmtree(c_dir_ini, True) | ||||
|         os.remove(c_dir_ini) | ||||
|     except FileNotFoundError: | ||||
|         None | ||||
|     # убиваемся | ||||
|     logging.info(f"Stop: {command}") | ||||
|     os.killpg(os.getpgid(process.pid), signal.SIGINT) | ||||
|  | ||||
| ### главная функция | ||||
| if __name__ == '__main__': | ||||
|     sys.stderr = open('/dev/null') | ||||
|     # ключи асоциативного массива | ||||
|     c_args_name=['script', 'count', 'game', 'dif', 'config'] | ||||
|     # если 2 аргумента, вызов из run скрипта | ||||
|     if len(sys.argv) == 2: | ||||
|         c_args_value=sys.argv[1].split() | ||||
|         c_args_value.insert(0, sys.argv[0]) | ||||
|     else: | ||||
|         # если 4 аргумента, вызов из shell | ||||
|         c_args_value=sys.argv | ||||
|     # проверяем кол-во передынных параветров вызова | ||||
|     if len(c_args_value) <= 4: | ||||
|         print("Incorrect number of arguments") | ||||
|         raise KeyError() | ||||
|     # получаем ассоциативный массив аргументов | ||||
|     c_args=dict(zip(c_args_name, c_args_value)) | ||||
|     # перехватываем сигналы от системы | ||||
|     signal.signal(signal.SIGHUP, receiveSignal) | ||||
|     #signal.signal(signal.SIGINT, receiveSignal) | ||||
|     signal.signal(signal.SIGQUIT, receiveSignal) | ||||
|     signal.signal(signal.SIGILL, receiveSignal) | ||||
|     signal.signal(signal.SIGTRAP, receiveSignal) | ||||
|     signal.signal(signal.SIGABRT, receiveSignal) | ||||
|     signal.signal(signal.SIGBUS, receiveSignal) | ||||
|     signal.signal(signal.SIGFPE, receiveSignal) | ||||
|     #signal.signal(signal.SIGKILL, receiveSignal) | ||||
|     signal.signal(signal.SIGUSR1, receiveSignal) | ||||
|     signal.signal(signal.SIGSEGV, receiveSignal) | ||||
|     signal.signal(signal.SIGUSR2, receiveSignal) | ||||
|     signal.signal(signal.SIGPIPE, receiveSignal) | ||||
|     signal.signal(signal.SIGALRM, receiveSignal) | ||||
|     signal.signal(signal.SIGTERM, receiveSignal) | ||||
|     # запускаем экземпляр сервера | ||||
|     daemon_server(int(c_args['count']), c_args['game'], int(c_args['dif']), c_args['config']) | ||||
							
								
								
									
										180
									
								
								Daemon/run.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										180
									
								
								Daemon/run.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| #!/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) | ||||
|  | ||||
|     # создаем объект класса 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() | ||||
							
								
								
									
										86
									
								
								config.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								config.ini
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| ; глобальные параметры | ||||
| [SETTINGS] | ||||
|     ; имя пользователя от которого запущен сервер | ||||
|     sys_user=steam | ||||
|     ; группа пользователя от которой запущен сервер | ||||
|     sys_group=steam | ||||
|     ; pid файл сервиса | ||||
|     pid_kf2=/var/run/kf2.pid | ||||
|     ; рабочая папка со всеми файлами сервера | ||||
|     dir_work=/opt/KillingFloor2 | ||||
|     ; путь установки сервера игры | ||||
|     dir_server_data=/opt/KillingFloor2/Data | ||||
|     ; bin файл сервера | ||||
|     bin_server=/opt/KillingFloor2/Data/Binaries/Win64/KFGameSteamServer.bin.x86_64 | ||||
|     ; файл версии игры, нужно для проверки обновлений по API | ||||
|     steamcmd_game_version=/tmp/kf2_version.txt | ||||
|     ; дополнительные параметры для выгрузки обновлений, путь берем из предыдущего параметра | ||||
|     steamcmd_update_key=+login anonymous +app_update 232130 validate +exit | ||||
|     ; ссылка Api Steam, для проверки обновлений. Json формат ответа, приоритет steamcmd_update_check | ||||
|     ;steamcmd_update_check=http://api.steampowered.com/ISteamApps/UpToDateCheck/v1?appid=232130&version= | ||||
|     steamcmd_news_update_check=http://api.steampowered.com/ISteamNews/GetNewsForApp/v0002/?appid=232090&count=1&maxlength=0&format=json | ||||
|     ; порты сервера, вторым параметром можно указать кол-во итераций | ||||
|     port_game=7780,30,2 | ||||
|     ; порты для Query, вторым параметром можно указать кол-во итераций | ||||
|     port_query=27100,30 | ||||
|     ; порты для Web Admin(ки), вторым параметром можно указать кол-во итераций | ||||
|     port_webadmin=9100,30 | ||||
|     ; сложность игры: 0 = Нормально, 1 = Тяжело, 2 = Суицидально, 3 = Ад на Земле | ||||
|     difficulty_game=1,2,3 | ||||
|     ; режимы игры: | ||||
|     ; KFGameContent.KFGameInfo_Endless - Бесконечный | ||||
|     ; KFGameContent.KFGameInfo_Objective - Цель | ||||
|     ; KFGameContent.KFGameInfo_Survival - Выживание | ||||
|     ; KFGameContent.KFGameInfo_VersusSurvival - Выживания на 2 команды | ||||
|     ; KFGameContent.KFGameInfo_WeeklySurvival - Еженедельные задания | ||||
|     modes_game=KFGameContent.KFGameInfo_Endless,KFGameContent.KFGameInfo_Objective,KFGameContent.KFGameInfo_Survival,KFGameContent.KFGameInfo_WeeklySurvival,KFGameContent.KFGameInfo_VersusSurvival,KFGameContent.KFGameInfo_Endless,KFGameContent.KFGameInfo_Objective,KFGameContent.KFGameInfo_Survival,KFGameContent.KFGameInfo_WeeklySurvival,KFGameContent.KFGameInfo_VersusSurvival | ||||
|     ; файл лога | ||||
|     log=/opt/KillingFloor2/Log/killingfloor2.log | ||||
|     ; временная tpmfs деррриктория | ||||
|     tmp=/opt/KillingFloor2/Tmp | ||||
|  | ||||
| ; custom параметры в файле KFAI.ini | ||||
| [KFAI.ini] | ||||
|  | ||||
| ; custom параметры в файле KFWeb.ini | ||||
| [KFWeb.ini] | ||||
|     ; включить веб админку | ||||
|     IpDrv.WebServer,bEnabled=true | ||||
|  | ||||
| ; custom параметры в файле KFWebAdmin.ini | ||||
| [KFWebAdmin.ini] | ||||
|  | ||||
| ; custom параметры в файле LinuxServer-KFEngine.ini | ||||
| [LinuxServer-KFEngine.ini] | ||||
|  | ||||
| ; custom параметры в файле LinuxServer-KFGame.ini | ||||
| [LinuxServer-KFGame.ini] | ||||
|     ; имя сервера | ||||
|     Engine.GameReplicationInfo,ServerName=KillingFloor2 | ||||
|     ; пароль администратора | ||||
|     Engine.AccessControl,AdminPassword=1q2w3e4r5t | ||||
|     ; время голосования за карту | ||||
|     Engine.GameInfo,VoteTime=5.0 | ||||
|     ; кол-во волн | ||||
|         ; 0 - Short (4 волны) | ||||
|         ; 1 - Normal (7 волн) | ||||
|         ; 2 - Long (10 волн) | ||||
|         ; 3 - Custom | ||||
|     KFGame.KFGameInfo,GameLength=2 | ||||
|     ; банер | ||||
|     KFGame.KFGameInfo,BannerLink=http://art.tripwirecdn.com/TestItemIcons/MOTDServer.png | ||||
|     ; преведствие | ||||
|     KFGame.KFGameInfo,ServerMOTD=\n            Welcome to our server:\n                KillingFloor2\n | ||||
|     KFGame.KFGameInfo,ClanMotto=KillingFloor2 | ||||
|     ; сайт | ||||
|     KFGame.KFGameInfo,WebsiteLink=https://killingfloor2.com/ | ||||
|     ; указываем кол-во серверов с фиксированной конфигурацией, | ||||
|     ; остальные с открытой конфигурацией | ||||
|     ; если не хотим фиксировать, убираем параметр или ставим в 0 | ||||
|     Engine.GameEngine,bUsedForTakeover=11 | ||||
|  | ||||
| ; custom параметры в файле LinuxServer-KFInput.ini | ||||
| [LinuxServer-KFInput.ini] | ||||
|  | ||||
| ; custom параметры в файле LinuxServer-KFSystemSettings.ini | ||||
| [LinuxServer-KFSystemSettings.ini] | ||||
							
								
								
									
										20
									
								
								kf2.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								kf2.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| [Unit] | ||||
| Description=KF2 game service | ||||
| After=network.target | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| WorkingDirectory=/opt/KillingFloor2 | ||||
| RestartSec=60 | ||||
| KillSignal=SIGINT | ||||
| ExecStart=/usr/bin/sh -c '/opt/KillingFloor2/Daemon/run.py start "/opt/KillingFloor2/config.ini"' | ||||
| ExecStop=/usr/bin/sh -c '/opt/KillingFloor2/Daemon/run.py stop "/opt/KillingFloor2/config.ini"' | ||||
| #ExecRestart=/opt/KillingFloor2/Daemon/run.py restart $CFG | ||||
| #ExecStatus=/opt/KillingFloor2/Daemon/run.py status $CFG | ||||
| Restart=always | ||||
| User=root | ||||
| Group=root | ||||
| TimeoutStartSec=15s | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										32
									
								
								steamcmd
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								steamcmd
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| #!/bin/sh | ||||
| # Copyright (C) 2015 Alexandre Detiste <alexandre@detiste.be> | ||||
| # License: MIT | ||||
|  | ||||
| # check for old install < 0~20180105-4 | ||||
| if [ -e ~/.steam/steamcmd ] | ||||
| then | ||||
| 	exec ~/.steam/steamcmd/steamcmd.sh $@ | ||||
| fi | ||||
|  | ||||
| # create a fake Steam installation to avoid | ||||
| # that steamcmd uses "/home/$user/Steam" instead | ||||
|  | ||||
| STEAMROOT="${XDG_DATA_HOME:-"$HOME/.local/share"}/Steam" | ||||
| if [ ! -e ~/.steam ] | ||||
| then | ||||
| 	mkdir -p "$STEAMROOT/.steam/appcache/" | ||||
| 	mkdir -p "$STEAMROOT/.steam/config/" | ||||
| 	mkdir -p "$STEAMROOT/.steam/logs/" | ||||
| 	mkdir -p "$STEAMROOT/.steam/SteamApps/common/" | ||||
| 	ln -s "$STEAMROOT" ~/.steam/root | ||||
| 	ln -s "$STEAMROOT" ~/.steam/steam | ||||
| fi | ||||
|  | ||||
| if [ ! -e "$STEAMROOT/steamcmd" ] | ||||
| then | ||||
| 	mkdir -p "$STEAMROOT/steamcmd/linux32" | ||||
| 	# steamcmd will replace these files with newer ones itself on first run | ||||
| 	cp /usr/lib/games/steam/steamcmd.sh "$STEAMROOT/steamcmd/" | ||||
| 	cp /usr/lib/games/steam/steamcmd    "$STEAMROOT/steamcmd/linux32/" | ||||
| fi | ||||
| exec "$STEAMROOT/steamcmd/steamcmd.sh" $@ | ||||
		Reference in New Issue
	
	Block a user