diff --git a/.gitignore b/.gitignore index 2c7e2d8..3063fa0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,78 @@ -__pycache__ -# Ignore dynaconf secret files -.secrets.* -.idea +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +#pyproject +*.pyproject +*.pyproject.user +*.ui \ No newline at end of file diff --git a/README.md b/README.md index 4f0931b..f307b69 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ -![](https://gitflic.ru/project/xpamych/hln-a/blob/raw?file=logo.png&commit=cd31f5b44ee0cafa0ee7c0e683d48f2c70ad4b90) +![](https://gitflic.ru/project/plemyakh/hln-a/blob/raw?file=logo.png&commit=cd31f5b44ee0cafa0ee7c0e683d48f2c70ad4b90) + +![](https://img.shields.io/badge/Python-3.11-blue?style=flat&logo=python)![](https://img.shields.io/badge/Qt-6.0-green?style=flat&logo=qt)[![](https://img.shields.io/discord/817759634105827358?label=%D0%9F%D0%BB%D0%B5%D0%BC%D1%8F-%D0%A5&logo=discord)](https://discord.gg/3cFebGvPQt)[![](https://img.shields.io/badge/Telegram-%D0%9F%D0%BB%D0%B5%D0%BC%D1%8F-%D0%A5?style=flat&logo=telegram)](https://t.me/plemyakh)[![](https://img.shields.io/badge/Boosty-Boosty-blue?style=flat&color=orange&logo=boosty)](https://boosty.to/xpamych/donate) + # Описание -Этот инструмент позволяет управлять выделенным сервером (dedicated server) ARK Survival Evolved на Linux. +Этот инструмент позволяет управлять выделенным сервером (dedicated server) ARK Survival Evolved и\или 7Days to Die на Linux. Он предоставляет множество функций, чтобы получить полный список, ознакомьтесь с разделом использования. (находится в разработке и может содержать ошибки) # Установка ```shell -curl https://www.arsenm.dev/lure.sh | bash & lure ar -n xpamych-repo -u https://gitflic.ru/project/xpamych/xpamych-lure-repo.git & lure in hlna-git +curl https://www.elara.ws/lure.sh | bash & lure ar -n xpamych-repo -u https://gitflic.ru/project/xpamych/xpamych-lure-repo.git & lure in hlna-git ``` # Использование @@ -16,9 +19,9 @@ hlna config Для запуска, сконфигурированного сервера или кластера ```bash -hlna start +hlna start -g ark ``` Подробнее в [WIKI](https://gitflic.ru/project/xpamych/hln-a/wiki) # Спасибы -Сей софт написан при активном участии Sitisll, Xpamych, Vano, а так же при моральной поддержке Nezrimka и Vajka)) \ No newline at end of file +Сей софт написан при активном участии [Sitisll](https://gitflic.ru/user/sitisll), [Xpamych](https://gitflic.ru/user/xpamych), Vano, ChatGPT, а так же при моральной поддержке Nezrimka и [Vajka](https://gitflic.ru/user/vajka)) diff --git a/hlna.py b/hlna.py index 69942d9..ec84cb2 100755 --- a/hlna.py +++ b/hlna.py @@ -1,23 +1,61 @@ #!/usr/bin/env python3 import os -import time -import threading - -from pathlib import Path -from pprint import pprint +import re +import sys +import zlib +import struct +import logging +import datetime +import requests +import subprocess import yaml import click import colorama -import click_completion +import hlnaui + +from pathlib import Path from rcon.source import Client +from PyQt6 import QtWidgets home_dir = Path.home() -dir_server = f"{home_dir}/ARK_Servers/" -dir_config = f"{home_dir}/.config/hlna/maps/" -mods_id = "" -listen_server = True + +logging.basicConfig(stream=sys.stderr, level=logging.CRITICAL) + + +def find_file(path): + """Находим все конфиги в зависимости от пути""" + arr = next(os.walk(path), (None, None, []))[2] # [] if no file + if '.directory' in arr: + arr.remove('.directory') + return arr + + +dir_config = f"{home_dir}/.config/hlna/" +dir_maps_ark = f"{dir_config}ARK/" +dir_deactivated = f"{dir_maps_ark}deactivated/" +list_config = find_file(dir_maps_ark) +delist_config = find_file(dir_deactivated) + + +def create_dir(directory): + """Проверка и создание директории""" + if not os.path.exists(directory): + os.makedirs(directory) + + +create_dir(dir_config) + + +def path_server(): + """Получение пути для хранения файлов серверов, записываем путь в yaml файл""" + dir_server = input(f"""Укажите путь до каталога, где будут храниться файлы сервера. По-умолчанию {home_dir}/Servers/ + :""") + if not dir_server: + dir_server = f"{home_dir}/Servers/" + yaml_create("path_server", dir_server) + return dir_server @click.group() @@ -25,40 +63,86 @@ def hlna(): pass -def find_file(path): - """Находим все конфиги в зависимости от пути""" - arr = next(os.walk(path), (None, None, []))[2] # [] if no file - x = arr.count('.directory') - y = arr.count('logs') - if x > 0: - arr.remove('.directory') - if y > 0: - arr.remove('logs') - return arr +def unpack(src, dst): + """Добавить документацию""" + with open(src, 'rb') as f: + sigver = struct.unpack('q', f.read(8))[0] + unpacked_chunk = f.read(8) + packed = f.read(8) + unpacked = f.read(8) + size_unpacked_chunk = struct.unpack('q', unpacked_chunk)[0] + size_packed = struct.unpack('q', packed)[0] + size_unpacked = struct.unpack('q', unpacked)[0] + + # Verify the integrity of the Archive Header + if sigver == 2653586369: + if isinstance(size_unpacked_chunk, int) and isinstance(size_packed, int) and isinstance(size_unpacked, int): + logging.info("Archive is valid.") + logging.debug( + f"Archive header size information. Unpacked Chunk: {size_unpacked_chunk}({unpacked_chunk}) Full Packed: {size_packed}({packed}) Full Unpacked: {size_unpacked}({unpacked})") + + # Obtain the Archive Compression Index + compression_index = [] + size_indexed = 0 + while size_indexed < size_unpacked: + raw_compressed = f.read(8) + raw_uncompressed = f.read(8) + compressed = struct.unpack('q', raw_compressed)[0] + uncompressed = struct.unpack('q', raw_uncompressed)[0] + compression_index.append((compressed, uncompressed)) + size_indexed += uncompressed + logging.debug( + f"{len(compression_index)}: {size_indexed}/{size_unpacked} ({compressed}/{uncompressed}) - {raw_compressed} - {raw_uncompressed}") + + if size_unpacked != size_indexed: + msg = f"Header-Index mismatch. Header indicates it should only have {size_unpacked} bytes when uncompressed but the index indicates {size_indexed} bytes." + logging.critical(msg) + return print_line(msg, flag=False) + + # Read the actual archive data + data = b'' + read_data = 0 + for compressed, uncompressed in compression_index: + compressed_data = f.read(compressed) + uncompressed_data = zlib.decompress(compressed_data) + + # Verify the size of the data is consistent with the archives index + if len(uncompressed_data) == uncompressed: + data += uncompressed_data + read_data += 1 + + # Verify there is only one partial chunk + if len(uncompressed_data) != size_unpacked_chunk and read_data != len(compression_index): + msg = f"Index contains more than one partial chunk: was {len(uncompressed_data)} when the full chunk size is {size_unpacked_chunk}, chunk {read_data}/{len(compression_index)}" + logging.critical(msg) + return print_line(msg, flag=False) + else: + msg = f"Uncompressed chunk size is not the same as in the index: was {len(uncompressed_data)} but should be {uncompressed}." + logging.critical(msg) + return print_line(msg, flag=False) + else: + msg = f"Data types in the headers should be int's. Size Types: unpacked_chunk({type(size_unpacked_chunk)}), packed({type(size_packed)}), unpacked({type(size_unpacked)})" + logging.critical(msg) + return print_line(msg, flag=False) + else: + msg = "The signature and format version is incorrect. Signature was {} should be 2653586369.".format(sigver) + logging.critical(msg) + return print_line(msg,flag=False) + + # Write the extracted data to disk + with open(dst, 'wb') as f: + f.write(data) + logging.info("Archive has been extracted.") -list_config = find_file(dir_config) -delist_config = find_file(dir_config+"deactivated") +def print_line(*text, flag=True): + """Добавление обводки вокруг текста, покраска""" + color = colorama.Fore.GREEN if flag else colorama.Fore.RED + print(colorama.Fore.YELLOW + "." * 30) + print(color, *text) + print(colorama.Fore.YELLOW + "." * 30 + colorama.Style.RESET_ALL) -def print_line(text): - """Добавление тире вокруг текста, покраска""" - print(colorama.Fore.YELLOW + "-"*30) - print(f"{colorama.Fore.GREEN} + {text}") - print(colorama.Fore.YELLOW + "-"*30 + colorama.Style.RESET_ALL) - - -def create_dir(directory): - """Проверка и создание директории""" - if not os.path.exists(directory): - os.mkdir(directory) - if not os.path.exists(f"{home_dir}/.config/hlna/"): - os.chdir(f"{home_dir}/.config/") - os.mkdir("hlna") - if not os.path.exists(f"{home_dir}/.config/hlna/maps"): - os.chdir(f"{home_dir}/.config/hlna/") - os.mkdir("maps") - def check_int(number=""): """Проверка на ввод числа""" while True: @@ -69,278 +153,551 @@ def check_int(number=""): x = int(x) return x except ValueError: - print("Введите число") + print_line("Введите число") -create_dir(dir_server) -create_dir(dir_config) +@hlna.command(help='Выбор игры и сбор настроек для сервера(-ов)') +def config(): + count_game = check_int("""Выберите игру для конфигурирования + 1. ARK Survival Evolved + 2. 7 Days to Die + : """) + if count_game == 1: + config_ark() + elif count_game == 2: + config_7daystodie() + else: + print_line("Пока есть только ARK и 7Days xD") -@hlna.command(help='Для конфигурирования параметров запускаемого сервера или кластера серверов') -def config(list_config=list_config): - data = {} +def ports_array(): port_s = [] - rcon_p = [] query_p = [] - + rcon_p = [] + for k in list_config: + data_port = read_yaml(k, game="ARK") + port_s.append(data_port['Port']) + query_p.append(data_port['QueryPort']) + rcon_p.append(data_port['RCONPort']) + return port_s, query_p, rcon_p + + +def ports(port, ports_arr, flag): + while True: + if not port: + if not ports_arr: + if flag == 0: + port = 7777 + elif flag == 1: + port = 27015 + elif flag == 2: + port = 27044 + print("Port=", port) + return port + else: + port = max(ports_arr) + 2 + if port in ports_arr: + print("Порт уже занят") + else: + return port + + +def config_cluster(): cluster_id = "" cluster_dir_override = "" - - count_cluster = check_int("""Укажите требуется ли кластер? + count_cluster = check_int("""Укажите требуется ли кластер? default: Нет 1. Да 2. Нет : """) - if count_cluster == 1: cluster_server = True - cluster_id = input("Укажите id для кластера, любое сочетание символов: \n") - create_dir(dir_server + cluster_id) - cluster_dir_override = (dir_server + cluster_id) + while True: + cluster_id = input("Укажите id для кластера, любое сочетание символов: \n") + if cluster_id == '': + print("Введите символы: ") + else: + create_dir(dir_server_ark + cluster_id) + cluster_dir_override = (dir_server_ark + cluster_id) + break else: cluster_server = False + return cluster_server, cluster_id, cluster_dir_override + + +def config_nummap(): + count_maps = check_int("Укажите количество карт: \n") + if count_maps == 0: # 0 возвращает check_int когда, ничего не было введено + count_maps = 1 + return count_maps + + +def config_maps(i): + while True: + """Проверка на выбор карты из списка""" + amount_map = check_int("""Выберите карту из списка указав номер + 1. The Island + 2. The Center + 3. Scorched Earth + 4. Ragnarok + 5. Aberration + 6. Extinction + 7. Valguero + 8. Genesis: Part 1 + 9. Crystal Isles + 10. Genesis: Part 2 + 11. Lost Island + 12. Fjordur + : """) + if amount_map == 0: # 0 возвращает check_int когда, ничего не было введено + amount_map = i + 1 + if 0 < amount_map <= 12: + break + + if list_config: + port_s, query_p, rcon_p = ports_array() + else: + port_s = query_p = rcon_p = [] + + if amount_map == 1: + map_s = "TheIsland" + elif amount_map == 2: + map_s = "TheCenter" + elif amount_map == 3: + map_s = "ScorchedEarth_P" + elif amount_map == 4: + map_s = "Ragnarok" + elif amount_map == 5: + map_s = "Aberration_P" + elif amount_map == 6: + map_s = "Extinction" + elif amount_map == 7: + map_s = "Valguero_P" + elif amount_map == 8: + map_s = "Genesis" + elif amount_map == 9: + map_s = "CrystalIsles" + elif amount_map == 10: + map_s = "Gen2" + elif amount_map == 11: + map_s = "LostIsland" + elif amount_map == 12: + map_s = "Fjordur" + else: + # Если вдруг каким-то боком проверка не отработает и не будет нужной цифры + map_s = 'TheIsland' + return map_s, port_s, query_p, rcon_p + + +def config_nameserver(map_s): + while True: + name_server = input("Укажите название Сервера: \n") + if name_server == "": + if map_s in list_config: + count = 1 + new_name = map_s + while new_name in list_config: + new_name = f"{map_s}{str(count)}" + count += 1 + list_config.append(new_name) + print_line(list_config) + break + else: + print_line(list_config) + list_config.append(map_s) + break + else: + if name_server in list_config: + print_line("Имя занято", flag=False) + else: + list_config.append(name_server) # если enter, то ставим последним элементом карту + break + return list_config + + +def config_ports(port_s): + port = check_int("Укажите порт сервера: ") + port_server = ports(port, port_s, True) + return port_server + + +def config_query(query_p): + port = check_int("Укажите query порт сервера: ") + query_port = ports(port, query_p, False) + return query_port + + +def config_rcon(rcon_p): + port = check_int("Укажите порт сервера: ") + rcon_port = ports(port, rcon_p, True) + return rcon_port + + +def config_password(): + password_server = input("Укажите пароль Сервера: \n") + return password_server + + +def config_adminpassword(): + adminpassword_server = input("Укажите пароль Сервера: \n") + return adminpassword_server + + +def config_players(): + max_players = check_int("Укажите максимальное количество игроков: \n") + if max_players == 0: + max_players = 70 + return max_players + + +def config_listen(): + print("Передавать сервер в глобальный список серверов steam?") + listen_server_amount = check_int("""\n +1. Да +2. Нет +:""") + if listen_server_amount == 1: + listen_server = True + elif listen_server_amount == 2: + listen_server = False + else: + listen_server = True + return listen_server + + +def config_ark(list_config=list_config): + """конфигурирование сервера арк""" + create_dir(dir_server_ark) + create_dir(dir_maps_ark) + + cluster_server, cluster_id, cluster_dir_override = config_cluster() if list_config: print("Уже установленные карты: ") for i in list_config: - data = read_yaml(i) + data = read_yaml(i, game="ARK") print(f"{i} : {data['map']}") - # id_srv[data['SessionName']] = data['id_server'] - - count_maps = check_int("Укажите количество карт: \n") - if count_maps == 0: # 0 возвращает check_int когда, ничего не было введено - count_maps = 1 - + count_maps = config_nummap() + print_line(count_maps) for i in range(count_maps): - while True: - "Проверка на выбор карты из списка" - - # os.system("clear") - amount_map = check_int("""Выберите карту из списка указав номер - 1. The Island - 2. The Center - 3. Scorched Earth - 4. Ragnarok - 5. Aberration - 6. Extinction - 7. Valguero - 8. Genesis: Part 1 - 9. Crystal Isles - 10. Genesis: Part 2 - 11. Lost Island - 12. Fjordur - : """) - if amount_map == 0: # 0 возвращает check_int когда, ничего не было введено - amount_map = i + 1 - if 0 < amount_map <= 12: + map_s, port_s, query_p, rcon_p = config_maps(i) + print_line(map_s, port_s, query_p, rcon_p) + list_config = config_nameserver(map_s) + port_server = config_ports(port_s) + query_port = config_query(query_p) + rcon_port = config_rcon(rcon_p) + rcon_enabled = True + password_server = config_password() + adminpassword_server = config_adminpassword() + max_players = config_players() + listen_server = config_listen() + + print_line(cluster_server, map_s, list_config[-1], port_server, query_port, + rcon_enabled, rcon_port, adminpassword_server, password_server, max_players, + cluster_id, cluster_dir_override, listen_server) + yaml_create("ARK", "", cluster_server, map_s, list_config[-1], port_server, query_port, + rcon_enabled, rcon_port, adminpassword_server, password_server, max_players, + cluster_id, cluster_dir_override, listen_server) + + +def config_7daystodie(): + """конфигурирование сервера 7 days to die""" + list_simvols = ("$", "@", "-", ".", "%", "{", "}", "+", "/") + create_dir(dir_server_7days) + config_7days = input("Введите имя конфига (serverconfig):\n") + if config_7days == "": + config_7days = "serverconfig" + elif config_7days == "serverconfig": + config_7days = "serverconfig" + elif config_7days in list_simvols: + print_line("Запрещённые символы", flag=False) + else: + xml_parser() + systemd_unit_create("7Days", config_7days) + + +def xml_parser(): + """добавить документацию""" + print("Я пока не умею парсить xml))", flag=False) + + +def yaml_create(game, dir_server="", cluster_server="", map_s="", name_server="", port_server="", query_port="", + rcon_enabled="", rcon_port="", adminpassword_server="", password_server="", max_players="", + id_mods_ark="", cluster_id="", cluster_dir_override="", listen_server=""): + """Создаёт на основании собранных данных yaml конфиг""" + if game == "ARK": + path_yaml = dir_maps_ark + name_server + settings = [ + { + 'map': map_s, + 'Cluster': cluster_server, + 'SessionName': name_server, + 'Port': port_server, + 'QueryPort': query_port, + 'RCONEnabled': rcon_enabled, + 'RCONPort': rcon_port, + 'ServerAdminPassword': adminpassword_server, + 'ServerPassword': password_server, + 'MaxPlayers': max_players, + 'ModsId': id_mods_ark, + 'Listen': listen_server, + 'ServerPath': dir_server_ark, + 'clusterid': cluster_id, + 'clusterdir': cluster_dir_override + } + ] + elif game == "path_server": + path_yaml = dir_config + "config" + settings = [ + { + 'path_server': dir_server + } + ] + + with open(path_yaml, 'w') as yamlfile: + yaml.dump(settings, yamlfile) + print(colorama.Fore.GREEN + f"Конфиг {path_yaml} создан" + colorama.Style.RESET_ALL) + if game != "path_server": + systemd_unit_create(game) + + +def systemd_unit_create(game, config_7days="", name_server=list_config): + """Создаёт на основании yaml конфига systemd юнит""" + if game == "ARK": + id_game = "376030" + for i in name_server: + data = read_yaml(i, game="ARK") + ntff = "" if not data['Cluster'] else "-NoTransferFromFiltering" + unit_dir_server = dir_server_ark + systemd_unit_exec = f"{dir_server_exec}ShooterGameServer {data['map']}?listen={data['Listen']}?GameModIds=2112724006?SessionName={data['SessionName']}?Port={data['Port']}?QueryPort={data['QueryPort']}?RCONEnabled={data['RCONEnabled']}?RCONPort={data['RCONPort']}?ServerAdminPassword={data['ServerAdminPassword']}??MaxPlayers={data['MaxPlayers']} -clusterid={data['clusterid']} -ClusterDirOverride={data['clusterdir']} {ntff}" + unit_file = f"{dir_unit}ark_{data['SessionName']}.service".lower() + elif game == "7Days": + id_game = "294420" + # сюда дописать обращение к xml_parser для получения уникального имени сервера + unit_dir_server = dir_server_7days + systemd_unit_exec = f"{dir_server_7days}startserver.sh -configfile={config_7days}.xml" + unit_file = f"{dir_unit}7days.service".lower() + + unit_text = f'''[Unit] +Description={game}: Server +Wants=network-online.target +After=syslog.target network.target nss-lookup.target network-online.target + +[Service] +ExecStartPre=/usr/bin/steamcmd +force_install_dir {unit_dir_server} +login anonymous +app_update {id_game} +quit +TimeoutStartSec=99999 +ExecStart={systemd_unit_exec} +LimitNOFILE=100000 +ExecReload=/bin/kill -s HUP $MAINPID +ExecStop=/bin/kill -s INT $MAINPID + +[Install] +WantedBy=default.target +''' + with open(unit_file, 'w') as systemd_unit: + systemd_unit.write(unit_text) + unit_name = unit_file.split("/")[-1] + os.system(f"systemctl --user unmask {unit_name}") + os.system('systemctl --user daemon-reload') + os.system(f"systemctl --user enable {unit_name}") + + +@hlna.command(help='Скачивание и установка модов ') +@click.argument('g', nargs=1) +@click.option('-m', default='all', help="Название карты для запуска или all для запуска всех карт") +@click.option("-i/-u", default=True, help="-i установить/обновить моды, -u удалить моды") +@click.argument('id_mods_ark', nargs=-1) +def mod(g, m, i, id_mods_ark): + if g == "ark": + id_game_workshop = "346110" + if not os.path.isdir(dir_mods_ark): + create_dir(dir_mods_ark) + id_mods_ark = id_mods_ark[0].split(',') + for id_mod in id_mods_ark: + dir_ark_mods = f"{dir_mods_ark}/{id_mod}" + if not os.path.isfile(f"{dir_ark_mods}.mod"): + if i: + moddownload(id_mod, id_game_workshop, dir_ark_mods) + else: + os.system(f"rm -rf {dir_ark_mods}") + print_line(f"{dir_ark_mods} удалён") + os.system(f"rm {dir_mods_ark}/{id_mod}.mod") + print_line(f"{dir_mods_ark}/{id_mod}.mod удалён") + os.system(f"mv {dir_workshop_ark}/appworkshop_{id_game_workshop}.acf {dir_ark_mods}/appworkshop_{id_game_workshop}.acf") + else: + modupdate(g, m, id_mod, dir_ark_mods) + + +def modupdate(g, m, id_mod, dir_ark_mods): + if g == "ark": + if not os.path.isfile(os.path.join(dir_ark_mods, f"appworkshop_{id_mod}.acf")): + return + with open(os.path.join(dir_ark_mods, f"appworkshop_{id_mod}.acf"), "r") as f: + content = f.readlines() + content = "".join(content) + locale_date = "" + + for line in content.split("\n"): + if '\t"WorkshopItemsInstalled"' in line: + for line in content.split("\n"): + if f'\t\t"{id_mod}"' in line: + locale_date = line.split('"')[5] + if '}' in line: + break break - if list_config: - for i in list_config: - data = read_yaml(i) - port_s.append(data['Port']) - rcon_p.append(data['RCONPort']) - query_p.append(data['QueryPort']) - - if amount_map == 1: - map_s = "TheIsland" - elif amount_map == 2: - map_s = "TheCenter" - elif amount_map == 3: - map_s = "ScorchedEarth_P" - elif amount_map == 4: - map_s = "Ragnarok" - elif amount_map == 5: - map_s = "Aberration_P" - elif amount_map == 6: - map_s = "Extinction" - elif amount_map == 7: - map_s = "Valguero_P" - elif amount_map == 8: - map_s = "Genesis" - elif amount_map == 9: - map_s = "CrystalIsles" - elif amount_map == 10: - map_s = "Gen2" - elif amount_map == 11: - map_s = "LostIsland" - elif amount_map == 12: - map_s = "Fjordur" - else: - # Если вдруг каким-то боком проверка не отработает и не будет нужной цифры - map_s = 'TheIsland' - - def ports(ports_arr): - while True: - port = check_int("") - if port == 0: - if not ports_arr: - print("Значение по умолчаню") - else: - port = max(ports_arr) + 2 - if port in ports_arr: - print("Порт уже занят") - else: - return port - - if list_config: - data = read_yaml(list_config[-1]) - - while True: - name_server = input("Укажите название Сервера: \n") - if name_server == "": - if map_s in list_config: - count = 1 - new_name = map_s - while new_name in list_config: - new_name = f"{map_s}{str(count)}" - count += 1 - - list_config.append(new_name) - print(list_config) - break - else: - list_config.append(map_s) - break - else: - if name_server in list_config: - print("Имя занято") - else: - list_config.append(name_server) # если enter, то ставим последним элементом карту - break - print("Укажите порт сервера:\n") - port_server = ports(port_s) - print("Укажите query порт сервера:\n") - query_port = ports(query_p) - - if port_server == 0: - port_server = 7777 - if query_port == 0: - query_port = 27015 - - print("Порт Сервера=", port_server) - print("Query Port=", query_port) - - rcon_enabled = True - if rcon_p == []: - rcon_port = 27020 - else: - rcon_port = max(rcon_p) + 1 - - password_server = input("Укажите пароль Сервера: \n") - adminpassword_server = 123 - max_players = check_int("Укажите максимальное количество игроков: \n") - if max_players == 0: - max_players = 70 - - yaml_create(cluster_server, map_s, list_config[-1], port_server, query_port, rcon_enabled, rcon_port, adminpassword_server, password_server, max_players, cluster_id, cluster_dir_override) - - -def yaml_create(cluster_server, map_s, name_server, port_server, query_port, rcon_enabled, rcon_port, adminpassword_server, password_server, max_players, cluster_id, cluster_dir_override): - settings_hlna = [ - { - 'map' : map_s, - 'Cluster' : cluster_server, - 'SessionName' : name_server, - 'Port' : port_server, - 'QueryPort' : query_port, - 'RCONEnabled' : rcon_enabled, - 'RCONPort' : rcon_port, - 'ServerAdminPassword': adminpassword_server, - 'ServerPassword' : password_server, - 'MaxPlayers' : max_players, - 'ModsId' : mods_id, - 'Listen' : listen_server, - 'ServerPath' : dir_server, - 'clusterid' : cluster_id, - 'clusterdir' : cluster_dir_override + data = { + 'itemcount': 1, + 'publishedfileids[0]': id_mod } - ] - with open(dir_config + f"{name_server}", 'w') as yamlfile: - yaml.dump(settings_hlna, yamlfile) - print(colorama.Fore.GREEN + "Конфиг создан" + colorama.Style.RESET_ALL) + zapros = requests.post('http://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1', data=data) + json_zapros = zapros.json() + steam_date = json_zapros['response']['publishedfiledetails'][0]['time_updated'] + + if steam_date and locale_date == steam_date: + return + + if steam_date and locale_date != steam_date: + print_line("хз") + +def modupdateall(): + for file in os.listdir(dir_mods_ark): + if os.path.isfile(os.path.join(dir_mods_ark, file)): + if file.endswith('.mod'): + print(file) -def test_mod_install(): - pathTest = f"{dir_server}ShooterGame/Saved/Config/LinuxServer/" - if os.path.exists(pathTest): - os.chdir(pathTest) -# "Добавить файл в Game.ini, если модов несколько добавляем еще строку ModIDS= [ModInstaller] ModIDS=" -# c этим не разобрался - os.system("echo ActiveMods=2943454417 >> GameUserSettings.ini") +def moddownload(id_mod, id_game_workshop, dir_ark_mods): + """Распаковывает файлы мода и создаёт .mod файл для него""" + dir_steam_workshop = f"{dir_workshop_ark}/content/{id_game_workshop}/{id_mod}/WindowsNoEditor" + dir_extract = dir_ark_mods + if id_mod == "111111111": + return + os.system(f"steamcmd +login anonymous +workshop_download_item {id_game_workshop} {id_mod} +quit") + try: + for curdir, subdirs, files in os.walk(os.path.join(dir_steam_workshop)): + for file in files: + name, ext = os.path.splitext(file) + if ext == ".z": + src = os.path.join(curdir, file) + dst = os.path.join(curdir, name) + uncompressed = os.path.join(curdir, file + ".uncompressed_size") + unpack(src, dst) + print("[+] Extracted " + file) + os.remove(src) + if os.path.isfile(uncompressed): + os.remove(uncompressed) + except Exception as e: + print(e) + print("[x] Unpacking .z files failed, aborting mod install") + return False + + os.system(f"rm -rf {dir_ark_mods}") + os.system(f"mv -f {dir_steam_workshop} {dir_ark_mods}") + + modname = subprocess.check_output( + ['curl', '-s', f'https://steamcommunity.com/sharedfiles/filedetails/?id={id_mod}']).decode('utf-8') + modname = re.search(r'
(.+)
', modname) + modname = modname and modname.group(1) + + if os.path.isfile(f"{dir_ark_mods}.mod"): + os.remove(f"{dir_ark_mods}.mod") + + with open(f"{dir_extract}/mod.info", "rb") as modinfo: + data = modinfo.read() + mapnamelen = struct.unpack_from("> {dir_config}logs") #Добавить текущее время - if x == 0: - print(f"Карта активирована - {i}") - else: - print(f"{i} либо уже активирована, либо такой карты нет") - except: - print("ошибка") - pass - else: +def switch(g, m, e): #добавить all + if g == "ark": + m = m.split(",") + if not os.path.isdir(dir_deactivated): + create_dir(dir_deactivated) + if e: + port_s, query_p, rcon_p = ports_array() - for i in m: - try: - if i in delist_config: - print(f"Карта {i} уже есть в деактивированных") - continue - x = os.system(f"mv {dir_config + i} {dir_config}deactivated/{i} 2>> {dir_config}logs") #Добавить текущее время - if x == 0: - print(f"Карта деактивирована - {i}") - else: - print(f"{i} либо деактивирована, либо такой карты нет") - except: - pass - + state_config = list_config if e else delist_config + state_msg = "активных" if e else "не активных" + for i in m: + try: + if i in state_config: + print(f"Карта {i} уже есть в {state_msg}") + continue + data = read_yaml(i, not e) + if e: + data['Port'] = ports(data['Port'], port_s, e) + data['QueryPort'] = ports(data['QueryPort'], port_s, e) + data['RCONPort'] = ports(data['RCONPort'], port_s, e) + yaml_create("ARK", data['map'], data['Cluster'], data['SessionName'], data['Port'], data['QueryPort'], + data['RCONEnabled'], data['RCONPort'], data['ServerAdminPassword'], data['ServerPassword'], + data['MaxPlayers'], data['ModsId'], data['Listen'], data['ServerPath'], data['clusterid'], + data['clusterdir']) + x = os.system( + f"rm {dir_deactivated}{i} >> {dir_logs}{date} 2>&1") + with open(f"{dir_logs}{date}.log", "a") as f: + f.write(f"[{t}] Сервер {i} перемещён из {state_msg}\n")# переписать эту залупу + else: + x = os.system(f"mv {dir_maps_ark}{i} {dir_deactivated} >> {dir_logs}{date} 2>&1") + with open(f"{dir_logs}{date}.log", "a") as f: + f.write(f"[{t}] Сервер {i} перемещён из {state_msg}\n") + if x == 0: + print(f"Готов - {i}") + #start = "start" if e else "stop" + enable = "enable" if e else "disable" + os.system(f"systemctl --user {enable} ark_{i}") + else: + print_line(f"Ошибка перемещения {i}", flag=False) + except: + print_line("ошибка операции", flag=False) -@hlna.command() -def servers(map_server=list_config): - # Добавить сортивку по кластерам и вывод несколько столбиков - if map_server == [] and delist_config == []: - print("Сервера не установлены") + +@hlna.command(help='Выводит статус настроеных серверов') +def status(list_config=list_config): + if list_config == [] and delist_config == []: + print_line("Сервера не сконфигурированы", flag=False) else: - for i in map_server: - data = read_yaml(i) + for i in list_config: + data = read_yaml(i, game="ARK") x = os.system(f"lsof -w -i :{data['Port']}") if x == 0: - print(colorama.Fore.GREEN + "Сервер запущен" + colorama.Style.RESET_ALL) + print_line("Сервер запущен") else: - print(colorama.Fore.RED + "Сервер не запущен" + colorama.Style.RESET_ALL) + print_line("Сервер не запущен", flag=False) + + # этот принт надо отдельной функцией сделать, чтобы убрать дублирование текста + print(f""" Имя сервера: {i} Карта: {data['map']} Моды: {data['ModsId']} - Пароль: {data['ServerPassword']} + Пароль: {data['ServerPassword']}S Кластер: {data['Cluster']} Кластер id: {data['clusterid']} Query порт: {data['QueryPort']} @@ -349,10 +706,10 @@ def servers(map_server=list_config): Rcon порт : {data['RCONPort']} Максимальное кол-во игроков: {data['MaxPlayers']}""") print("-" * 40) - - if delist_config!=[]: + + if delist_config != []: x = input("Есть неактивные сервера, показать Y/n: ") - if x!="n": + if x != "n": for i in delist_config: data = read_yaml(i, False) print(f""" @@ -368,127 +725,195 @@ def servers(map_server=list_config): Rcon порт : {data['RCONPort']} Максимальное кол-во игроков: {data['MaxPlayers']}""") print("-" * 40) - - -@hlna.command(help='Для запуска, сконфигурированного сервера или кластера') + + +@hlna.command(help='Запуск, сконфигурированного сервера или кластера ') +@click.option('-g', required=True, help="Название игры для запуска. (ark, 7days") @click.option('-m', default='all', help="Название карты для запуска или all для запуска все карт") -@click.option('-b', default='', help="") -def start(m, b, name_server=list_config): - dict_mapname = {} - dict_allmapname = [] - for i in name_server: - data = read_yaml(i) - print_line(f"Название сервера: {i} | Карта: {data['map']} | Кластер: {data['clusterid']}") - dict_mapname[data['SessionName']] = data['map'] - dict_allmapname.append(data['SessionName']) - print_line(f"Словарь названия сервера и карты {dict_mapname}") - names_serverstart = [] - for ns, v in dict_mapname.items(): - print_line(f"переменные v и m {v} & {m}") - if v in m: - names_serverstart.append(ns) - print_line(f"Карта которая запускается {ns}") - if name_server != []: - if b == '': +def start(g, m): + """Запускает сервер выбранной игры""" + # добавить проверку на ввод аргумента ark/7day если else: давать подсказку + # если нет конфигов, то выводим что серверов нет + start_stop("start", g, m) + + +@hlna.command(help='Остановка, сконфигурированного сервера или кластера ') +@click.option('-g', required=True, help="Название игры для запуска. (ark, 7days") +@click.option('-m', default='all', help="Название карты для запуска или all для запуска все карт") +def stop(g, m): + start_stop("stop", g, m) + + +@hlna.command(help='Перезапуск, сконфигурированного сервера или кластера ') +@click.option('-g', required=True, help="Название игры для запуска. (ark, 7days") +@click.option('-m', default='all', help="Название карты для запуска или all для запуска все карт") +def restart(g, m): + start_stop("restart", g, m) + + +def check_exist_servers(g): + + """Проверяет наличие конфигов для активных карт""" + if g == "ark": + if list_config == []: + print_line("Нет сконфигурированных серверов", flag=False) # добавить отсюда вилку на вопрос с конфигурацией + else: + return + elif g == "7days": + print_line("7Days") + + +def start_stop(action, g, m, list_config=list_config): + """Функция изменения статусов сервера""" + if g == "ark": + check_exist_servers(g) + dict_mapname = {} + dict_allmapname = [] + for i in list_config: + data = read_yaml(i, game="ARK") + dict_mapname[data['SessionName']] = data['map'] + dict_allmapname.append(data['SessionName']) + names_serverstart = [] + for ns, v in dict_mapname.items(): + if v in m: + names_serverstart.append(ns) + if list_config != []: # Перенести выше для проверки наличия конфигов if m == "all": names_serverstart = dict_allmapname - print(f"Запускаем все активные карты {names_serverstart}") + print(f"Выполняется для карт(-ы) {names_serverstart}") else: names_serverstart = choose_map(names_serverstart) + for i in names_serverstart: + data = read_yaml(i, game="ARK") + if stop or restart: + y = os.system( + f"~/git/hln-a/hlna.py rcon SaveWorld -m {i}") if action == "restart" or action == "stop" else "" + x = os.system(f"systemctl --user {action} ark_{data['SessionName'].lower()}.service") - server_dir = dir_server + "ShooterGame/Binaries/Linux/" - print_line("Валидация файлов сервера") - x = os.system(f"steamcmd +force_install_dir {dir_server} +login anonymous +app_update 376030 +quit") - os.chdir(server_dir) - for i in names_serverstart: - data = read_yaml(i) - starting_map = dict_mapname[i] - print_line(data['Cluster']) - if not data['Cluster']: - ntff = "" - else: - ntff = "-NoTransferFromFiltering" - - def starting(i): - os.system(f"{server_dir}ShooterGameServer {i}?SessionName={data['SessionName']}?Port={data['Port']}\ - ?QueryPort={data['QueryPort']}?RCONEnabled={data['RCONEnabled']}?RCONPort={data['RCONPort']}\ - ?ServerAdminPassword={data['ServerAdminPassword']}?MaxPlayers={data['MaxPlayers']}\ - ?GameModIds={data['ModsId']}?listen={data['Listen']} -clusterid={data['clusterid']}\ - -ClusterDirOverride={data['clusterdir']} {ntff}") - if x == 0: - print_line("Запускаем карту" + i) - threads = threading.Thread(target=starting, args=(starting_map,)) - threads.start() - else: - print(f"Карта не запущена, сервер не установлен") + if x == 0: + print_line(f"Готово {action} для {g}") + elif g == "7days": + x = os.system(f"systemctl --user {action} 7days.service") + if x == 0: + print_line("Готово") else: - print("Ни одной карты не установлено") - + return -def read_yaml(name_server, flag=True): + +def read_yaml(list_config=list_config, flag=True, game=""): + """Читает конфиги и отдаёт данные туда где их запросили""" # Читаем конфиги активных или неактивных карт в зависимости от флага - if flag: - dirs = f"{home_dir}/.config/hlna/maps/{name_server}" - else: - dirs = f"{home_dir}/.config/hlna/deactivated/{name_server}" - with open(dirs, "r") as yamlfile: + if game == "ARK": + path_yaml = f"{dir_maps_ark}{list_config}" if flag else f"{dir_deactivated}{list_config}" + elif game == "path_server": + path_yaml = dir_config + "config" + with open(path_yaml, "r") as yamlfile: data = yaml.load(yamlfile, Loader=yaml.FullLoader) return data[0] # возвращаем словарь со всеми значениями def choose_map(arr): + """Функция выбора карт""" new_arr = [] arr = sorted(arr) print('Найдены сервера с этой картой') - for i,map in enumerate(arr): - print(f"{i+1}) {map}") + for i, map in enumerate(arr): + print(f"{i + 1}) {map}") while True: try: x = input("Выберите сервер из списка, либо несколько через запятую: ").split(',') - x = [int (i) for i in x] + x = [int(i) for i in x] break except: - print("Неправильный ввод") - + print_line("Неправильный ввод",flag=False) + for i in x: - new_arr.append(arr[i-1]) + new_arr.append(arr[i - 1]) print('Выбранные сервера:', new_arr) - + return new_arr -@hlna.command() -@click.argument('c', nargs=1) +@hlna.command(help='Отправка команд на игровой сервер через rcon ') +@click.argument('c', nargs=1) @click.option('-m', required=True, help="Название карты для применения rcon команды") -def rcon(m,c): - print_line("Команда: ", c) - print_line("Карты: ", m) - dict_mapname = {} - dict_adminpwd = {} - for i in list_config: - data = read_yaml(i) - dict_mapname[data['RCONPort']] = data['map'] - dict_adminpwd[data['RCONPort']] = data['ServerAdminPassword'] - rcon_ports = [] - for ns, v in dict_mapname.items(): - print_line(f"переменные v и m {v} & {m}") # обьединить с таким же блоком в start() - if v in m: - rcon_ports.append(ns) - print_line(f"Карта которая запускается {ns}") - for port in rcon_ports: - print(port) - passwd = dict_adminpwd[port] - with Client('127.0.0.1', port, passwd=str(passwd)) as client: - response = client.run(c) - print(response) +def rcon(m, c): + try: + dict_mapname = {} + dict_adminpwd = {} + if list_config: + rcon_ports = [] + for i in list_config: + data = read_yaml(i, game="ARK") + dict_mapname[data['RCONPort']] = data['map'] + dict_adminpwd[data['RCONPort']] = data['ServerAdminPassword'] + if m == "all": + for rcon_p in dict_mapname: + rcon_ports.append(rcon_p) + else: + for rcon_p, name_map in dict_mapname.items(): + if name_map in m: + rcon_ports.append(rcon_p) + + for port in rcon_ports: + passwd = dict_adminpwd[port] + with Client('127.0.0.1', port, passwd=str(passwd)) as client: + response = client.run(c) + print_line(f"Rcon выполнен {response} {dict_mapname[port]}") + else: + pass + except: + print(f"Ошибка отправки команды в {m}", flag=False) def zero(x=""): + """Потом пригодится (нет)""" return "" -# if __name__ == 'hlna': -# input = zero +if not os.path.exists(dir_config + "config"): + dir_server = path_server() +else: + data = read_yaml(game="path_server") + if data['path_server'] == "": + path_server() + else: + dir_server = data['path_server'] + +dir_unit = f"{home_dir}/.config/systemd/user/" +dir_logs = f"{dir_config}logs/" + +dir_server_ark = f"{dir_server}ARK/" +dir_server_exec = f"{dir_server_ark}ShooterGame/Binaries/Linux/" +dir_workshop_ark = f"{home_dir}/.local/share/Steam/steamapps/workshop" +dir_shooter = "ShooterGame" +dir_mods_ark = f"{dir_server_ark}ShooterGame/Content/Mods" + +dir_server_7days = f"{dir_server}/7Days/" + +now = datetime.datetime.now() +date = now.strftime("%Y-%m-%d") +t = now.strftime("%H:%M:%S") +create_dir(dir_server) +create_dir(dir_unit) +create_dir(dir_logs) + + +class HlnaApp(QtWidgets.QMainWindow, hlnaui.Ui_MainWindow): + def __init__(self): + super().__init__() + self.setupUi(self) + +def hlnag(): + if len(sys.argv) > 1: + hlna() + else: + app = QtWidgets.QApplication(sys.argv) + window = HlnaApp() + window.show() + sys.exit(app.exec()) + + if __name__ == '__main__': - hlna() + hlnag() diff --git a/hlna_bot.py b/hlna_bot.py deleted file mode 100755 index 19c2a99..0000000 --- a/hlna_bot.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python3 -import errno -import time -import sys -import os -import telebot -from telebot import types -import subprocess -import yaml -import colorama - -import hlna - -list_config = hlna.list_config - - -def start(): - if not os.path.exists(f"{hlna.home_dir}/.config/hlna/bot/hlna_bot"): - key_api = input("Укажите ключ бота\n") - os.chdir(f"{hlna.home_dir}/.config/hlna/") - try: - os.mkdir("bot") - except OSError as e: - if e.errno == errno.EEXIST: - print('Каталог создан') - else: - raise - create_config(key_api) - else: - x = input("Уже есть запомненный ключа api. Запустить его? Y/n \n") - if x == "" or x.lower == 'y': - read_config() - - elif x == "n": - os.remove(f"{hlna.home_dir}/.config/hlna/bot/hlna_bot") - start() - - -def check_status(m): - for i in list_config: - data = hlna.read_yaml(i) - hlna.print_line(data) - x = os.system(f"lsof -w -i :{data['Port']}") - n_srv = data['SessionName'] - if x != 0: - hlna_bot.send_message(m.chat.id, text=f"Сервер {n_srv} не работает 🛑") - elif x == 0: - hlna_bot.send_message(m.chat.id, text=f"Сервер {n_srv} запустился 🟢") - - -def read_config(): - with open(f"{hlna.home_dir}/.config/hlna/bot/hlna_bot", "r") as yamlfile: - data = yaml.load(yamlfile, Loader=yaml.FullLoader) - return data[0] - - -def create_config(key_api): - config_hlna_bot = [ - { - 'key_api': key_api, - } - ] - with open(f"{hlna.home_dir}/.config/hlna/bot/hlna_bot", 'w') as yamlfile: - yaml.dump(config_hlna_bot, yamlfile) - print(colorama.Fore.GREEN + "Конфиг бота создан" + colorama.Style.RESET_ALL) - - -start() - - -data = read_config() -k_api = data['key_api'] -hlna_bot = telebot.TeleBot(f"{k_api}", parse_mode=None) - - -@hlna_bot.message_handler(commands=["start"]) -def m_mainmenu(m): - menu = types.ReplyKeyboardMarkup(resize_keyboard=True) - k_start = types.KeyboardButton("🦖 Старт") - k_stop = types.KeyboardButton("🦇 Выключить") - menu.add(k_start, k_stop) - - k_restart = types.KeyboardButton("🦕 Перезагрузить") - k_update = types.KeyboardButton("🔸 Обновить") - menu.add(k_restart, k_update) - - k_status = types.KeyboardButton("🧾 Статус") - menu.add(k_status) - - k_mods = types.KeyboardButton("⏬ Установка модов") - menu.add(k_mods) - - hlna_bot.send_message(m.chat.id, text="Привет, {0.first_name}! Это панель управления сервером".format(m.from_user), - reply_markup=menu) - - -@hlna_bot.message_handler(content_types=["text"]) -def m_chose(m): - if m.text.strip() == '🦖 Старт': - if list_config == []: - menu_install = types.ReplyKeyboardMarkup(resize_keyboard=True) - k_install = types.KeyboardButton("Установить") - k_back = types.KeyboardButton("Назад") - menu_install.add(k_back, k_install) - hlna_bot.send_message(m.chat.id, text="{0.first_name}, ниодна карта не установлена".format(m.from_user), - reply_markup=menu_install) - hlna_bot.register_next_step_handler(m, m_install) - else: - hlna.read_yaml(list_config[-1], flag=True) - menu_start = types.ReplyKeyboardMarkup(resize_keyboard=True) - for i in list_config: - k_mapname = types.KeyboardButton(i) - menu_start.add(k_mapname) - k_back = types.KeyboardButton("Назад") - menu_start.add(k_back) - hlna_bot.send_message(m.chat.id, text="{0.first_name}, выбери карту".format(m.from_user), reply_markup=menu_start) - elif m.text.strip() == '🧾 Статус': - check_status(m) - elif m.text.strip() == 'Назад': - m_mainmenu(m) - for i in list_config: - if m.text.strip() == f'{i}': - hlna.print_line(f"Старт карты {i}") - hlna.start() - -# @hlna_bot.message_handler(content_types=["text"]) -# def m_mapstart(m): -# hlna.print_line("Старт карты") -# for i in list_config: -# if m.text.strip() == f'{i}': -# hlna.print_line(f"Старт карты {i}") -# hlna.start() - - -def m_install(m): - if m.text.strip() == 'Установить': - hlna_bot.send_message(m.chat.id, subprocess.check_output(hlna.config(), shell=True)) - - -print("Бот запущен") -hlna_bot.polling(none_stop=True, interval=0, timeout=999) - -# subprocess.run(["arkmanager", "start", "--alwaysrestart", "@all"]) -# elif m.text.strip() == '🦇 Выключить': -# subprocess.run(["arkmanager", "stop", "@all"]) -# elif m.text.strip() == '🦕 Перезагрузить': -# subprocess.run(["arkmanager","restart", "--alwaysrestart", "--warn", "@all"]) -# elif (m.text.strip() == '🔸 Обновить'): -# subprocess.run(["arkmanager", "update", "--update-mods", "@all"]) -# -# -# elif (m.text.strip() == '⏬ Установка модов'): -# bot.send_message(m.from_user.id, "Введите id модификации") -# bot.register_next_step_handler(m, mods) -# else: -# pass -# else: -# if m.text.strip() == 'Zxx843Aj': -# th = Thread(target=check, args=(m,)) -# th.start() -# id.append(m.chat.id) -# bot.send_message(m.chat.id, text="{0.first_name} вы авторизованы, нажмите /start для работы с ботом".format(m.from_user)) -# return id -# def mods(m): -# id = m.text.strip() -# subprocess.run(["arkmanager", "installmods", id, "@all"]) -# subprocess.run(["arkmanager", "enablemod", id, "@all"]) diff --git a/hlnaui.py b/hlnaui.py new file mode 100644 index 0000000..7abe76d --- /dev/null +++ b/hlnaui.py @@ -0,0 +1,203 @@ +# Form implementation generated from reading ui file './hlna.ui' +# +# Created by: PyQt6 UI code generator 6.5.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(1552, 1000) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout.setObjectName("horizontalLayout") + self.tabWidget = QtWidgets.QTabWidget(parent=self.centralwidget) + self.tabWidget.setMaximumSize(QtCore.QSize(1551, 941)) + self.tabWidget.setObjectName("tabWidget") + self.tab = QtWidgets.QWidget() + self.tab.setObjectName("tab") + self.gridLayout_2 = QtWidgets.QGridLayout(self.tab) + self.gridLayout_2.setObjectName("gridLayout_2") + self.tabWidget_2 = QtWidgets.QTabWidget(parent=self.tab) + self.tabWidget_2.setTabsClosable(True) + self.tabWidget_2.setTabBarAutoHide(True) + self.tabWidget_2.setObjectName("tabWidget_2") + self.tab_5 = QtWidgets.QWidget() + self.tab_5.setObjectName("tab_5") + self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_5) + self.gridLayout_3.setObjectName("gridLayout_3") + self.groupBox = QtWidgets.QGroupBox(parent=self.tab_5) + self.groupBox.setTitle("") + self.groupBox.setObjectName("groupBox") + self.textEdit = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit.setGeometry(QtCore.QRect(10, 30, 441, 31)) + self.textEdit.setObjectName("textEdit") + self.label = QtWidgets.QLabel(parent=self.groupBox) + self.label.setGeometry(QtCore.QRect(10, 10, 81, 18)) + self.label.setScaledContents(True) + self.label.setObjectName("label") + self.textEdit_2 = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit_2.setGeometry(QtCore.QRect(10, 80, 131, 31)) + self.textEdit_2.setObjectName("textEdit_2") + self.label_2 = QtWidgets.QLabel(parent=self.groupBox) + self.label_2.setGeometry(QtCore.QRect(10, 60, 81, 18)) + self.label_2.setScaledContents(True) + self.label_2.setObjectName("label_2") + self.label_3 = QtWidgets.QLabel(parent=self.groupBox) + self.label_3.setGeometry(QtCore.QRect(170, 60, 81, 18)) + self.label_3.setScaledContents(True) + self.label_3.setObjectName("label_3") + self.textEdit_3 = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit_3.setGeometry(QtCore.QRect(170, 80, 131, 31)) + self.textEdit_3.setObjectName("textEdit_3") + self.textEdit_4 = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit_4.setGeometry(QtCore.QRect(330, 80, 121, 31)) + self.textEdit_4.setObjectName("textEdit_4") + self.label_4 = QtWidgets.QLabel(parent=self.groupBox) + self.label_4.setGeometry(QtCore.QRect(330, 60, 81, 18)) + self.label_4.setScaledContents(True) + self.label_4.setObjectName("label_4") + self.textEdit_5 = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit_5.setGeometry(QtCore.QRect(10, 140, 221, 31)) + self.textEdit_5.setObjectName("textEdit_5") + self.label_5 = QtWidgets.QLabel(parent=self.groupBox) + self.label_5.setGeometry(QtCore.QRect(10, 120, 101, 18)) + self.label_5.setScaledContents(True) + self.label_5.setObjectName("label_5") + self.label_6 = QtWidgets.QLabel(parent=self.groupBox) + self.label_6.setGeometry(QtCore.QRect(240, 120, 161, 18)) + self.label_6.setScaledContents(True) + self.label_6.setObjectName("label_6") + self.textEdit_6 = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit_6.setGeometry(QtCore.QRect(230, 140, 221, 31)) + self.textEdit_6.setObjectName("textEdit_6") + self.textEdit_7 = QtWidgets.QTextEdit(parent=self.groupBox) + self.textEdit_7.setGeometry(QtCore.QRect(10, 200, 71, 31)) + self.textEdit_7.setObjectName("textEdit_7") + self.label_7 = QtWidgets.QLabel(parent=self.groupBox) + self.label_7.setGeometry(QtCore.QRect(10, 180, 131, 18)) + self.label_7.setScaledContents(True) + self.label_7.setObjectName("label_7") + self.checkBox = QtWidgets.QCheckBox(parent=self.groupBox) + self.checkBox.setGeometry(QtCore.QRect(140, 200, 301, 31)) + self.checkBox.setObjectName("checkBox") + self.gridLayout_3.addWidget(self.groupBox, 0, 0, 1, 1) + self.groupBox_2 = QtWidgets.QGroupBox(parent=self.tab_5) + self.groupBox_2.setObjectName("groupBox_2") + self.gridLayout_3.addWidget(self.groupBox_2, 0, 1, 1, 1) + self.tabWidget_2.addTab(self.tab_5, "") + self.tab_6 = QtWidgets.QWidget() + self.tab_6.setObjectName("tab_6") + self.tabWidget_2.addTab(self.tab_6, "") + self.gridLayout_2.addWidget(self.tabWidget_2, 0, 0, 1, 1) + self.tabWidget.addTab(self.tab, "") + self.tab_2 = QtWidgets.QWidget() + self.tab_2.setObjectName("tab_2") + self.gridLayout = QtWidgets.QGridLayout(self.tab_2) + self.gridLayout.setObjectName("gridLayout") + self.groupBox_3 = QtWidgets.QGroupBox(parent=self.tab_2) + self.groupBox_3.setTitle("") + self.groupBox_3.setObjectName("groupBox_3") + self.textEdit_8 = QtWidgets.QTextEdit(parent=self.groupBox_3) + self.textEdit_8.setGeometry(QtCore.QRect(10, 30, 441, 31)) + self.textEdit_8.setObjectName("textEdit_8") + self.label_8 = QtWidgets.QLabel(parent=self.groupBox_3) + self.label_8.setGeometry(QtCore.QRect(10, 10, 81, 18)) + self.label_8.setScaledContents(True) + self.label_8.setObjectName("label_8") + self.textEdit_9 = QtWidgets.QTextEdit(parent=self.groupBox_3) + self.textEdit_9.setGeometry(QtCore.QRect(10, 80, 131, 31)) + self.textEdit_9.setObjectName("textEdit_9") + self.label_9 = QtWidgets.QLabel(parent=self.groupBox_3) + self.label_9.setGeometry(QtCore.QRect(10, 60, 81, 18)) + self.label_9.setScaledContents(True) + self.label_9.setObjectName("label_9") + self.textEdit_12 = QtWidgets.QTextEdit(parent=self.groupBox_3) + self.textEdit_12.setGeometry(QtCore.QRect(10, 140, 221, 31)) + self.textEdit_12.setObjectName("textEdit_12") + self.label_12 = QtWidgets.QLabel(parent=self.groupBox_3) + self.label_12.setGeometry(QtCore.QRect(10, 120, 101, 18)) + self.label_12.setScaledContents(True) + self.label_12.setObjectName("label_12") + self.label_13 = QtWidgets.QLabel(parent=self.groupBox_3) + self.label_13.setGeometry(QtCore.QRect(240, 120, 161, 18)) + self.label_13.setScaledContents(True) + self.label_13.setObjectName("label_13") + self.textEdit_13 = QtWidgets.QTextEdit(parent=self.groupBox_3) + self.textEdit_13.setGeometry(QtCore.QRect(230, 140, 221, 31)) + self.textEdit_13.setObjectName("textEdit_13") + self.textEdit_14 = QtWidgets.QTextEdit(parent=self.groupBox_3) + self.textEdit_14.setGeometry(QtCore.QRect(10, 200, 71, 31)) + self.textEdit_14.setObjectName("textEdit_14") + self.label_14 = QtWidgets.QLabel(parent=self.groupBox_3) + self.label_14.setGeometry(QtCore.QRect(10, 180, 131, 18)) + self.label_14.setScaledContents(True) + self.label_14.setObjectName("label_14") + self.gridLayout.addWidget(self.groupBox_3, 0, 0, 1, 1) + self.groupBox_4 = QtWidgets.QGroupBox(parent=self.tab_2) + self.groupBox_4.setObjectName("groupBox_4") + self.gridLayout.addWidget(self.groupBox_4, 0, 1, 1, 1) + self.tabWidget.addTab(self.tab_2, "") + self.horizontalLayout.addWidget(self.tabWidget) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1552, 30)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + self.tabWidget.setCurrentIndex(1) + self.tabWidget_2.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + self.label.setText(_translate("MainWindow", "Имя сессии")) + self.textEdit_2.setHtml(_translate("MainWindow", "\n" +"\n" +"

7777

")) + self.label_2.setText(_translate("MainWindow", "Порт")) + self.label_3.setText(_translate("MainWindow", "Querry порт")) + self.textEdit_3.setHtml(_translate("MainWindow", "\n" +"\n" +"

27015

")) + self.textEdit_4.setHtml(_translate("MainWindow", "\n" +"\n" +"

27042

")) + self.label_4.setText(_translate("MainWindow", "Rcon порт")) + self.label_5.setText(_translate("MainWindow", "Пароль сессии")) + self.label_6.setText(_translate("MainWindow", "Пароль администратора")) + self.label_7.setText(_translate("MainWindow", "Количество игроков")) + self.checkBox.setText(_translate("MainWindow", "Передавать в глобальный список серверов")) + self.groupBox_2.setTitle(_translate("MainWindow", "ini")) + self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_5), _translate("MainWindow", "Tab 1")) + self.tabWidget_2.setTabText(self.tabWidget_2.indexOf(self.tab_6), _translate("MainWindow", "Tab 2")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "ARK")) + self.label_8.setText(_translate("MainWindow", "Имя сессии")) + self.textEdit_9.setHtml(_translate("MainWindow", "\n" +"\n" +"


")) + self.label_9.setText(_translate("MainWindow", "Порт")) + self.label_12.setText(_translate("MainWindow", "Пароль сессии")) + self.label_13.setText(_translate("MainWindow", "Пароль администратора")) + self.label_14.setText(_translate("MainWindow", "Количество игроков")) + self.groupBox_4.setTitle(_translate("MainWindow", "xml")) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "7 Days to Die"))