diff --git a/.gitignore b/.gitignore index df345ea..98c4fbd 100644 --- a/.gitignore +++ b/.gitignore @@ -76,4 +76,6 @@ CMakeLists.txt.user* *.pyproject *.pyproject.user *.ui -settings.json \ No newline at end of file +settings.json + +*.kdev4 diff --git a/README.md b/README.md index 8e95a51..4c7f347 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ # Установка ```shell -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 +curl -fsSL lure.sh/install | bash && lure ar -n xpamych-repo -u https://gitflic.ru/project/xpamych/xpamych-lure-repo.git && lure in hlna-git ``` # Использование diff --git a/hlna.py b/hlna.py index 55eff5a..3f01062 100755 --- a/hlna.py +++ b/hlna.py @@ -1,23 +1,29 @@ #!/usr/bin/env python3 import os +import pprint import re import sys import zlib +import time import struct +import curses +import pprint +import tarfile import logging import datetime import requests +import threading import subprocess import yaml import click import colorama -import hlnaui - from pathlib import Path -from rcon.source import Client from PyQt6 import QtWidgets +from rcon.source import Client + +import hlnaui home_dir = Path.home() @@ -37,7 +43,7 @@ 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) - +list_allconfigs = list_config + delist_config def create_dir(directory): """Проверка и создание директории""" @@ -97,7 +103,7 @@ def unpack(src, dst): 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) + return print_line(msg, flag="RED") # Read the actual archive data data = b'' @@ -115,19 +121,19 @@ def unpack(src, dst): 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) + return print_line(msg, flag="RED") 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) + return print_line(msg, flag="RED") 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) + return print_line(msg, flag="RED") 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) + return print_line(msg, flag="RED") # Write the extracted data to disk with open(dst, 'wb') as f: @@ -135,12 +141,56 @@ def unpack(src, dst): logging.info("Archive has been extracted.") -def print_line(*text, flag=True): +def get_external_ip(): + response = requests.get('https://api.ipify.org') + return response.text + + +def print_line(*text, flag="", sep=" ", end="\n"): """Добавление обводки вокруг текста, покраска""" - 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) + if flag == "RED": + color = colorama.Fore.RED + elif flag == "YELLOW": + color = colorama.Fore.YELLOW + elif flag == "GREEN": + color = colorama.Fore.GREEN + elif flag == "CYAN": + color = colorama.Fore.CYAN + else: + color = colorama.Fore.WHITE + + len_text = str(*text) + len_text = len_text.split("\n") + max_length = max(len(str(string)) for string in len_text) + 2 + print(color + "." * max_length) + print(color, *text, sep=sep, end=end) + print(color + "." * max_length + colorama.Style.RESET_ALL) + + +def zmeyuka(stdscr, func): + print_line(func) + curses.curs_set(0) + stdscr.nodelay(1) + + def nachalo_stroki(): + while func.do_run: + zmejka = '⠇⠋⠙⠸⠴⠦' + for i in range(len(zmejka)): + print('\r' + zmejka[i], end="", flush=True) + time.sleep(0.25) + th = threading.Thread(target=func) + th.start() +# Запуск потока со змеюкой + zmeyuka_thread = threading.Thread(target=nachalo_stroki) + zmeyuka_thread.start() +# Ожидаем завершение функции + th.join() +# Убираем змеюку + zmeyuka_thread.do_run = False + zmeyuka_thread.join() + + curses.curs_set(1) + stdscr.nodelay(0) def check_int(number=""): @@ -153,21 +203,72 @@ def check_int(number=""): x = int(x) return x except ValueError: - print_line("Введите число") + print_line("Введите число", flag="CYAN") + + +@hlna.command(help='Восстановление бэкапов серверов в ') +@click.argument('g', nargs=1) +@click.option('-m', default='all', help="Название карты для запуска или all для запуска всех карт") +@click.option('-d', required=True, help="Путь до zip архива") +def restore(g, m, d): + """Получение пути к файлам внутри архива""" + with tarfile.open(d, 'r') as tar_file: + files = tar_file.getnames() + """Извлечение файлов""" + for i in files: + with tar_file.extract(d, 'r:gz') as tar_file: + path_extarct = "./" if g == 'test' else "/" + tar_file.extract(i, path_extarct) + print_line(f"i - перемещен", flag="GREEN") + print_line(f"Бэкап {d} восстановлен", flag="GREEN") + + +@hlna.command(help='Бэкап серверов выбранной игры > {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) + state_msg = "активных" if e else "не активных" + name_server = choose_map(g, m) + for i in name_server: + try: + data = read_yaml(g="ark", m=i, flag=not e) + if e: # добавить проверку занятости имени + if i in list_config: + data['SessionName'] = config_nameserver(i, flag=False)[-1] + 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['ServerPath'], data['Cluster'], data['map'], data['SessionName'], + data['Port'], + data['QueryPort'], data['RCONEnabled'], data['RCONPort'], data['ServerAdminPassword'], + data['ServerPassword'], data['MaxPlayers'], data['ModsId'], data['clusterid'], + data['clusterdir'], + data['Listen']) + 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: + # start = "start" if e else "stop" + enable = "enable" if e else "disable" + os.system(f"systemctl --user {enable} ark_{i.lower()}") + print_line(f"Выполнено для сервера- {i}", flag="GREEN") + else: + print_line(f"Ошибка перемещения {i}", flag="RED") + except: + print_line("ошибка операции", flag="RED") @hlna.command(help='Выводит статус настроеных серверов') -def status(list_config=list_config): - if list_config == [] and delist_config == []: - print_line("Сервера не сконфигурированы", flag=False) - else: - for i in list_config: - data = read_yaml(i, game="ARK") - x = os.system(f"lsof -w -i :{data['Port']}") - if x == 0: - print_line("Сервер запущен") - else: - print_line("Сервер не запущен", flag=False) - - # этот принт надо отдельной функцией сделать, чтобы убрать дублирование текста - - print(f""" - Имя сервера: {i} +@click.argument('g', nargs=1) +@click.option("-m", default='all', help="Название cервера") +def status(g, m="all", list_config=list_config): + """print_status делает вывод, flag - отвечает за показ активных/неактивных, под 7days надо будет дописать""" + def print_status(g, status_map, flag=True): + data = status_map + print_line(data["status"], flag=("YELLOW", "RED")[flag] if data['status'] == "Не запущен" else "GREEN") + print_line(f""" + Имя сервера: {data['SessionName']} Карта: {data['map']} Моды: {data['ModsId']} - Пароль: {data['ServerPassword']}S + Пароль: {data['ServerPassword']} Кластер: {data['Cluster']} Кластер id: {data['clusterid']} Query порт: {data['QueryPort']} Порт сервера: {data['Port']} Rcon включен: {data['RCONEnabled']} Rcon порт : {data['RCONPort']} - Максимальное кол-во игроков: {data['MaxPlayers']}""") - print("-" * 40) + Максимальное кол-во игроков: {data['MaxPlayers']} + steam://connect/{get_external_ip()}:{data['QueryPort']}""", flag=("YELLOW","CYAN")[flag]) - if delist_config != []: - x = input("Есть неактивные сервера, показать Y/n: ") - if x != "n": - for i in delist_config: - data = read_yaml(i, False) - print(f""" - Имя сервера: {i} - Карта: {data['map']} - Моды: {data['ModsId']} - Пароль: {data['ServerPassword']} - Кластер: {data['Cluster']} - Кластер id: {data['clusterid']} - Query порт: {data['QueryPort']} - Порт сервера: {data['Port']} - Rcon включен: {data['RCONEnabled']} - Rcon порт : {data['RCONPort']} - Максимальное кол-во игроков: {data['MaxPlayers']}""") - print("-" * 40) + def get_param(g, list_con, flag=True): + for i in list_con: + data = read_yaml(g=g, m=i, flag=flag) + status_map["ark"][flag][data["SessionName"]] = data + status_map["ark"][flag][data["SessionName"]]['status'] = "Не запущен" if os.system(f"lsof -w -i :{data['Port']}") else "Запущен" + if list_config == [] and delist_config == []: + print_line("Сервера не сконфигурированы", flag="RED") + exit() + "Это скорее всего можно как то покрасивее записать, но пока так)" + status_map = {} + status_map["ark"] = {} + status_map["ark"][True] = {} + status_map["ark"][False] = {} + get_param("ark", list_config) + get_param("ark", delist_config, False) + if g == "ark": + name_servers = choose_map(g, m) + for i in name_servers: + print_status(g, status_map[g][True][i]) + if delist_config != []: + x = input("Есть неактивные сервера, показать Y/n: ") + if x != "n": + for i in delist_config: + print_status(g, status_map[g][False][i], False) + return status_map -@hlna.command(help='Запуск, сконфигурированного сервера или кластера ') -@click.option('-g', required=True, help="Название игры для запуска. (ark, 7days") +@hlna.command(help='Запуск, сконфигурированного сервера или кластера ') +@click.argument('g', nargs=1) @click.option('-m', default='all', help="Название карты для запуска или all для запуска все карт") def start(g, m): """Запускает сервер выбранной игры""" - # добавить проверку на ввод аргумента ark/7day если else: давать подсказку + # добавить проверку на ввод аргумента ark/7days если else: давать подсказку # если нет конфигов, то выводим что серверов нет - modupdateall(g, m) + g = g.lower() + if g == "ark": + modupdateall(g, m) start_stop("start", g, m) -@hlna.command(help='Остановка, сконфигурированного сервера или кластера ') -@click.option('-g', required=True, help="Название игры для запуска. (ark, 7days") +@hlna.command(help='Остановка, сконфигурированного сервера или кластера ') +@click.argument('g', nargs=1) @click.option('-m', default='all', help="Название карты для запуска или all для запуска все карт") def stop(g, m): + g = g.lower() + if g == "ark": + modupdateall(g, m) start_stop("stop", g, m) -@hlna.command(help='Перезапуск, сконфигурированного сервера или кластера ') -@click.option('-g', required=True, help="Название игры для запуска. (ark, 7days") +@hlna.command(help='Перезапуск, сконфигурированного сервера или кластера ') +@click.argument('g', nargs=1) @click.option('-m', default='all', help="Название карты для запуска или all для запуска все карт") def restart(g, m): - modupdateall(g, m) + g = g.lower() + if g == "ark": + modupdateall(g, m) start_stop("restart", g, m) def check_exist_servers(g): - """Проверяет наличие конфигов для активных карт""" - if g == "ark": - if list_config == []: - print_line("Нет сконфигурированных серверов", flag=False) # добавить отсюда вилку на вопрос с конфигурацией - else: - return + g = g.lower() + if g == "ark" and not list_config: + print_line("Нет сконфигурированных серверов", flag="RED") # добавить отсюда вилку на вопрос с конфигурацией elif g == "7days": - print_line("7Days") + print_line("7Days", flag="CYAN") + else: + return 1 -def start_stop(action, g, m, list_config=list_config): +def start_stop(action, g, m): """Функция изменения статусов сервера""" + g = g.lower() 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}") - else: - names_serverstart = choose_map(names_serverstart) - for i in names_serverstart: - data = read_yaml(i, game="ARK") + x = check_exist_servers(g) + if x: + name_servers = choose_map(g, m) if m != 'all' else list_config + for i in name_servers: + data = read_yaml(g="ark", m=i, flag=True) if action == "stop" or action == "restart": rcon_local(i, "SaveWorld") x = os.system(f"systemctl --user {action} ark_{data['SessionName'].lower()}.service") - if x == 0: - print_line(f"Готово {action} для {g}") - + print_line(f"Готово {action} для {g} {i}", flag="GREEN") elif g == "7days": x = os.system(f"systemctl --user {action} 7days.service") if x == 0: - print_line("Готово") + print_line(f"Готово {action} для {m}", flag="GREEN") else: - return + print_line("доступные игры: ark и 7days") -def read_yaml(list_config=list_config, flag=True, game=""): - """Читает конфиги и отдаёт данные туда где их запросили""" - # Читаем конфиги активных или неактивных карт в зависимости от флага - if game == "ARK": - path_yaml = f"{dir_maps_ark}{list_config}" if flag else f"{dir_deactivated}{list_config}" - elif game == "path_server": +def read_yaml(g="", m="", flag=True): + """Читает конфиги активных или неактивных карт в зависимости от флага и отдаёт данные туда где их запросили""" + g = g.lower() + if g == "ark": + if m == "all": + print_line("Не правильный вызов yaml, должен вызываться из цикла") + path_yaml = f"{dir_maps_ark}{m}" if flag else f"{dir_deactivated}{m}" + elif g == "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): +def choose_map(g, m, list_config=list_config): """Функция выбора карт""" + g = g.lower() new_arr = [] - arr = sorted(arr) - print('Найдены сервера с этой картой') - for i, map in enumerate(arr): - print(f"{i + 1}) {map}") - while True: - try: - x = input("Выберите сервер из списка, либо несколько через запятую: ").split(',') - x = [int(i) for i in x] - break - except: - print_line("Неправильный ввод",flag=False) + if g == "ark": + dict_mapname = {} + dict_allmapname = [] + print_line(list_config) + for i in list_config: + data = read_yaml(g="ark", m=i) + dict_mapname[data['SessionName']] = data['map'] + dict_allmapname.append(data['SessionName']) + name_servers = [] + for ns, v in dict_mapname.items(): + if v in m: + name_servers.append(ns) + if list_config != []: # Перенести выше для проверки наличия конфигов + if m == "all": + new_arr = dict_allmapname + print_line(f"Выполняется для карт(-ы) {new_arr}", flag="CYAN") + else: + if name_servers: + name_servers = sorted(name_servers) + print_line('Найдены сервера с этой картой', flag="CYAN") + for i, map in enumerate(name_servers): + print_line(f"{i + 1}) {map}", flag="CYAN") + print_line(f"{i + 2} Все", flag="CYAN") + x = None + if i != 0: + while True: + try: + x = input("Выберите сервер из списка, либо несколько через запятую: ").split(',') + x = [int(j) for j in x] + if i + 2 in x: + return name_servers + break + except: + print_line("Неправильный ввод", flag="RED") + else: + x = [1] + for i in x: + new_arr.append(name_servers[i - 1]) + print_line(f"Выбранные сервера: {name_servers}", flag="CYAN") + else: + print_line("Не найдено серверов с картой") - for i in x: - new_arr.append(arr[i - 1]) - print('Выбранные сервера:', new_arr) - - return new_arr + return new_arr @hlna.command(help='Отправка команд на игровой сервер через rcon ') @@ -863,7 +1103,7 @@ def rcon_local(m, c): if list_config: rcon_ports = [] for i in list_config: - data = read_yaml(i, game="ARK") + data = read_yaml(g="ark", m=i) dict_mapname[data['RCONPort']] = data['map'] dict_adminpwd[data['RCONPort']] = data['ServerAdminPassword'] if m == "all": @@ -878,11 +1118,11 @@ def rcon_local(m, c): 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]}") + print_line(f"Rcon выполнен {response} {dict_mapname[port]}", flag="GREEN") else: pass except: - print_line(f"Ошибка отправки команды {c} в {m}", flag=False) + print_line(f"Ошибка отправки команды {c} в {m}", flag="RED") def zero(x=""): @@ -893,7 +1133,7 @@ def zero(x=""): if not os.path.exists(dir_config + "config"): dir_server = path_server() else: - data = read_yaml(game="path_server") + data = read_yaml(g="path_server") if data['path_server'] == "": path_server() else: @@ -903,7 +1143,7 @@ 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_ark_save = f"{dir_server_ark}ShooterGame/Saved/SavedArks/" dir_workshop_ark = f"{home_dir}/.local/share/Steam/steamapps/workshop" dir_shooter = "ShooterGame" dir_mods_ark = f"{dir_server_ark}ShooterGame/Content/Mods" @@ -922,12 +1162,6 @@ class HlnaApp(QtWidgets.QMainWindow, hlnaui.Ui_mainWindow): def __init__(self): super().__init__() self.setupUi(self) - self.lineEdit_namesession.returnPressed.connect(self.printable) - - - def printable(self): - namesession = self.lineEdit_namesession.text() - def hlnag(): diff --git a/hlnaui.py b/hlnaui.py index a16934f..c4aa63d 100644 --- a/hlnaui.py +++ b/hlnaui.py @@ -28,6 +28,7 @@ class Ui_mainWindow(object): self.gridLayout_2 = QtWidgets.QGridLayout(self.tabArk) self.gridLayout_2.setObjectName("gridLayout_2") self.tabServers = QtWidgets.QTabWidget(parent=self.tabArk) + self.tabServers.setEnabled(True) self.tabServers.setTabletTracking(False) self.tabServers.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus) self.tabServers.setTabPosition(QtWidgets.QTabWidget.TabPosition.North) @@ -40,8 +41,8 @@ class Ui_mainWindow(object): self.tabServers.setObjectName("tabServers") self.tab_5 = QtWidgets.QWidget() self.tab_5.setObjectName("tab_5") - self.horizontalLayout = QtWidgets.QHBoxLayout(self.tab_5) - self.horizontalLayout.setObjectName("horizontalLayout") + self.gridLayout = QtWidgets.QGridLayout(self.tab_5) + self.gridLayout.setObjectName("gridLayout") self.groupBox = QtWidgets.QGroupBox(parent=self.tab_5) self.groupBox.setMinimumSize(QtCore.QSize(500, 800)) self.groupBox.setMaximumSize(QtCore.QSize(500, 16777215)) @@ -169,7 +170,7 @@ class Ui_mainWindow(object): self.verticalLayout_3.addWidget(self.label_players) self.lineEdit_players = QtWidgets.QLineEdit(parent=self.groupBox_settings) self.lineEdit_players.setMinimumSize(QtCore.QSize(0, 35)) - self.lineEdit_players.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_players.setMaximumSize(QtCore.QSize(65, 35)) self.lineEdit_players.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) self.lineEdit_players.setClearButtonEnabled(True) self.lineEdit_players.setObjectName("lineEdit_players") @@ -230,12 +231,7 @@ class Ui_mainWindow(object): self.label_rconcmd.setScaledContents(True) self.label_rconcmd.setObjectName("label_rconcmd") self.verticalLayout_2.addWidget(self.groupBox_6) - self.horizontalLayout.addWidget(self.groupBox) - self.line_7 = QtWidgets.QFrame(parent=self.tab_5) - self.line_7.setFrameShape(QtWidgets.QFrame.Shape.VLine) - self.line_7.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - self.line_7.setObjectName("line_7") - self.horizontalLayout.addWidget(self.line_7) + self.gridLayout.addWidget(self.groupBox, 0, 0, 1, 1) self.scrollArea = QtWidgets.QScrollArea(parent=self.tab_5) self.scrollArea.setWidgetResizable(True) self.scrollArea.setObjectName("scrollArea") @@ -250,14 +246,14 @@ class Ui_mainWindow(object): self.tab_gameini.setObjectName("tab_gameini") self.gridLayout_4 = QtWidgets.QGridLayout(self.tab_gameini) self.gridLayout_4.setObjectName("gridLayout_4") - self.tableView = QtWidgets.QTableView(parent=self.tab_gameini) + self.kdialog = QtWidgets.QDialog(parent=self.tab_gameini) + self.kdialog.setObjectName("kdialog") + self.tableView = QtWidgets.QTableView(parent=self.kdialog) + self.tableView.setGeometry(QtCore.QRect(0, 0, 941, 862)) self.tableView.setGridStyle(QtCore.Qt.PenStyle.SolidLine) self.tableView.setObjectName("tableView") self.tableView.horizontalHeader().setVisible(True) self.tableView.horizontalHeader().setCascadingSectionResizes(False) - self.gridLayout_4.addWidget(self.tableView, 0, 1, 1, 1) - self.kdialog = QtWidgets.QDialog(parent=self.tab_gameini) - self.kdialog.setObjectName("kdialog") self.gridLayout_4.addWidget(self.kdialog, 0, 0, 1, 1) self.tabWidget_ini.addTab(self.tab_gameini, "") self.tab_gameusersettingsini = QtWidgets.QWidget() @@ -273,61 +269,270 @@ class Ui_mainWindow(object): self.tabWidget_ini.addTab(self.tab_gameusersettingsini, "") self.gridLayout_3.addWidget(self.tabWidget_ini, 0, 0, 1, 1) self.scrollArea.setWidget(self.scrollAreaWidgetContents) - self.horizontalLayout.addWidget(self.scrollArea) + self.gridLayout.addWidget(self.scrollArea, 0, 4, 1, 1) + self.line_7 = QtWidgets.QFrame(parent=self.tab_5) + self.line_7.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_7.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_7.setObjectName("line_7") + self.gridLayout.addWidget(self.line_7, 0, 1, 1, 1) self.tabServers.addTab(self.tab_5, "") - self.tab = QtWidgets.QWidget() - self.tab.setObjectName("tab") - self.tabServers.addTab(self.tab, "") self.gridLayout_2.addWidget(self.tabServers, 0, 0, 1, 1) icon1 = QtGui.QIcon() icon1.addPixmap(QtGui.QPixmap("./../../Yandex.Disk/Изображения/Оформление/ARK-2-Logo.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.tabGames.addTab(self.tabArk, icon1, "") self.tab_2 = QtWidgets.QWidget() self.tab_2.setObjectName("tab_2") - self.gridLayout = QtWidgets.QGridLayout(self.tab_2) - self.gridLayout.setObjectName("gridLayout") - 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.groupBox_3 = QtWidgets.QGroupBox(parent=self.tab_2) - self.groupBox_3.setTitle("") - self.groupBox_3.setObjectName("groupBox_3") - 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.lineEdit = QtWidgets.QLineEdit(parent=self.groupBox_3) - self.lineEdit.setGeometry(QtCore.QRect(10, 30, 441, 32)) - self.lineEdit.setObjectName("lineEdit") - self.gridLayout.addWidget(self.groupBox_3, 0, 0, 1, 1) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.tab_2) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.tabServers_2 = QtWidgets.QTabWidget(parent=self.tab_2) + self.tabServers_2.setTabletTracking(False) + self.tabServers_2.setFocusPolicy(QtCore.Qt.FocusPolicy.TabFocus) + self.tabServers_2.setTabPosition(QtWidgets.QTabWidget.TabPosition.North) + self.tabServers_2.setTabShape(QtWidgets.QTabWidget.TabShape.Rounded) + self.tabServers_2.setElideMode(QtCore.Qt.TextElideMode.ElideNone) + self.tabServers_2.setDocumentMode(False) + self.tabServers_2.setTabsClosable(True) + self.tabServers_2.setMovable(True) + self.tabServers_2.setTabBarAutoHide(False) + self.tabServers_2.setObjectName("tabServers_2") + self.tab_6 = QtWidgets.QWidget() + self.tab_6.setObjectName("tab_6") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.tab_6) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.groupBox_2 = QtWidgets.QGroupBox(parent=self.tab_6) + self.groupBox_2.setMinimumSize(QtCore.QSize(500, 800)) + self.groupBox_2.setMaximumSize(QtCore.QSize(500, 16777215)) + self.groupBox_2.setTitle("") + self.groupBox_2.setFlat(False) + self.groupBox_2.setObjectName("groupBox_2") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_2) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.groupBox_settings_2 = QtWidgets.QGroupBox(parent=self.groupBox_2) + self.groupBox_settings_2.setMinimumSize(QtCore.QSize(0, 600)) + self.groupBox_settings_2.setMaximumSize(QtCore.QSize(500, 16777215)) + self.groupBox_settings_2.setObjectName("groupBox_settings_2") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_settings_2) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.checkBox_cluster_2 = QtWidgets.QCheckBox(parent=self.groupBox_settings_2) + self.checkBox_cluster_2.setObjectName("checkBox_cluster_2") + self.verticalLayout_5.addWidget(self.checkBox_cluster_2) + self.lineEdit_cluster_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_cluster_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_cluster_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_cluster_2.setClearButtonEnabled(True) + self.lineEdit_cluster_2.setObjectName("lineEdit_cluster_2") + self.verticalLayout_5.addWidget(self.lineEdit_cluster_2) + self.checkBox_listen_2 = QtWidgets.QCheckBox(parent=self.groupBox_settings_2) + self.checkBox_listen_2.setObjectName("checkBox_listen_2") + self.verticalLayout_5.addWidget(self.checkBox_listen_2) + self.label_namesession_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_namesession_2.setMinimumSize(QtCore.QSize(0, 10)) + self.label_namesession_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_namesession_2.setScaledContents(True) + self.label_namesession_2.setObjectName("label_namesession_2") + self.verticalLayout_5.addWidget(self.label_namesession_2) + self.lineEdit_namesession_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_namesession_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_namesession_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_namesession_2.setClearButtonEnabled(True) + self.lineEdit_namesession_2.setObjectName("lineEdit_namesession_2") + self.verticalLayout_5.addWidget(self.lineEdit_namesession_2) + self.line_8 = QtWidgets.QFrame(parent=self.groupBox_settings_2) + self.line_8.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_8.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_8.setObjectName("line_8") + self.verticalLayout_5.addWidget(self.line_8) + self.label_port_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_port_2.setMinimumSize(QtCore.QSize(0, 10)) + self.label_port_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_port_2.setScaledContents(True) + self.label_port_2.setObjectName("label_port_2") + self.verticalLayout_5.addWidget(self.label_port_2) + self.lineEdit_port_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_port_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_port_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_port_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + self.lineEdit_port_2.setClearButtonEnabled(True) + self.lineEdit_port_2.setObjectName("lineEdit_port_2") + self.verticalLayout_5.addWidget(self.lineEdit_port_2) + self.label_querryport_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_querryport_2.setMinimumSize(QtCore.QSize(0, 15)) + self.label_querryport_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_querryport_2.setScaledContents(True) + self.label_querryport_2.setObjectName("label_querryport_2") + self.verticalLayout_5.addWidget(self.label_querryport_2) + self.lineEdit_querryport_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_querryport_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_querryport_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_querryport_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + self.lineEdit_querryport_2.setClearButtonEnabled(True) + self.lineEdit_querryport_2.setObjectName("lineEdit_querryport_2") + self.verticalLayout_5.addWidget(self.lineEdit_querryport_2) + self.label_rconport_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_rconport_2.setMinimumSize(QtCore.QSize(0, 15)) + self.label_rconport_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_rconport_2.setScaledContents(True) + self.label_rconport_2.setObjectName("label_rconport_2") + self.verticalLayout_5.addWidget(self.label_rconport_2) + self.lineEdit_rconport_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_rconport_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_rconport_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_rconport_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + self.lineEdit_rconport_2.setClearButtonEnabled(True) + self.lineEdit_rconport_2.setObjectName("lineEdit_rconport_2") + self.verticalLayout_5.addWidget(self.lineEdit_rconport_2) + self.line_2 = QtWidgets.QFrame(parent=self.groupBox_settings_2) + self.line_2.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_2.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_2.setObjectName("line_2") + self.verticalLayout_5.addWidget(self.line_2) + self.label_adminpassword_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_adminpassword_2.setMinimumSize(QtCore.QSize(0, 15)) + self.label_adminpassword_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_adminpassword_2.setScaledContents(True) + self.label_adminpassword_2.setObjectName("label_adminpassword_2") + self.verticalLayout_5.addWidget(self.label_adminpassword_2) + self.lineEdit_adminpassword_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_adminpassword_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_adminpassword_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_adminpassword_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.PasswordEchoOnEdit) + self.lineEdit_adminpassword_2.setPlaceholderText("") + self.lineEdit_adminpassword_2.setClearButtonEnabled(True) + self.lineEdit_adminpassword_2.setObjectName("lineEdit_adminpassword_2") + self.verticalLayout_5.addWidget(self.lineEdit_adminpassword_2) + self.label_password_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_password_2.setMinimumSize(QtCore.QSize(0, 15)) + self.label_password_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_password_2.setScaledContents(True) + self.label_password_2.setObjectName("label_password_2") + self.verticalLayout_5.addWidget(self.label_password_2) + self.lineEdit_password_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_password_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_password_2.setMaximumSize(QtCore.QSize(16777215, 35)) + self.lineEdit_password_2.setText("") + self.lineEdit_password_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.PasswordEchoOnEdit) + self.lineEdit_password_2.setObjectName("lineEdit_password_2") + self.verticalLayout_5.addWidget(self.lineEdit_password_2) + self.line_9 = QtWidgets.QFrame(parent=self.groupBox_settings_2) + self.line_9.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_9.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_9.setObjectName("line_9") + self.verticalLayout_5.addWidget(self.line_9) + self.label_players_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_players_2.setMinimumSize(QtCore.QSize(0, 15)) + self.label_players_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_players_2.setScaledContents(True) + self.label_players_2.setObjectName("label_players_2") + self.verticalLayout_5.addWidget(self.label_players_2) + self.lineEdit_players_2 = QtWidgets.QLineEdit(parent=self.groupBox_settings_2) + self.lineEdit_players_2.setMinimumSize(QtCore.QSize(0, 35)) + self.lineEdit_players_2.setMaximumSize(QtCore.QSize(65, 35)) + self.lineEdit_players_2.setEchoMode(QtWidgets.QLineEdit.EchoMode.Normal) + self.lineEdit_players_2.setClearButtonEnabled(True) + self.lineEdit_players_2.setObjectName("lineEdit_players_2") + self.verticalLayout_5.addWidget(self.lineEdit_players_2) + self.horizontalSlider_players_2 = QtWidgets.QSlider(parent=self.groupBox_settings_2) + self.horizontalSlider_players_2.setMaximum(70) + self.horizontalSlider_players_2.setPageStep(1) + self.horizontalSlider_players_2.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.horizontalSlider_players_2.setObjectName("horizontalSlider_players_2") + self.verticalLayout_5.addWidget(self.horizontalSlider_players_2) + self.line_10 = QtWidgets.QFrame(parent=self.groupBox_settings_2) + self.line_10.setFrameShape(QtWidgets.QFrame.Shape.HLine) + self.line_10.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_10.setObjectName("line_10") + self.verticalLayout_5.addWidget(self.line_10) + self.label_Maps_2 = QtWidgets.QLabel(parent=self.groupBox_settings_2) + self.label_Maps_2.setMinimumSize(QtCore.QSize(0, 15)) + self.label_Maps_2.setMaximumSize(QtCore.QSize(16777215, 15)) + self.label_Maps_2.setScaledContents(True) + self.label_Maps_2.setObjectName("label_Maps_2") + self.verticalLayout_5.addWidget(self.label_Maps_2) + self.comboBox_maps_2 = QtWidgets.QComboBox(parent=self.groupBox_settings_2) + self.comboBox_maps_2.setObjectName("comboBox_maps_2") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.comboBox_maps_2.addItem("") + self.verticalLayout_5.addWidget(self.comboBox_maps_2) + self.verticalLayout_4.addWidget(self.groupBox_settings_2) + self.groupBox_7 = QtWidgets.QGroupBox(parent=self.groupBox_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.groupBox_7.sizePolicy().hasHeightForWidth()) + self.groupBox_7.setSizePolicy(sizePolicy) + self.groupBox_7.setMinimumSize(QtCore.QSize(485, 80)) + self.groupBox_7.setMaximumSize(QtCore.QSize(0, 125)) + self.groupBox_7.setObjectName("groupBox_7") + self.lineEdit_rconcmd_2 = QtWidgets.QLineEdit(parent=self.groupBox_7) + self.lineEdit_rconcmd_2.setGeometry(QtCore.QRect(10, 40, 371, 32)) + self.lineEdit_rconcmd_2.setText("") + self.lineEdit_rconcmd_2.setObjectName("lineEdit_rconcmd_2") + self.pushButton_rconcmd_2 = QtWidgets.QPushButton(parent=self.groupBox_7) + self.pushButton_rconcmd_2.setGeometry(QtCore.QRect(390, 40, 88, 33)) + self.pushButton_rconcmd_2.setMinimumSize(QtCore.QSize(0, 33)) + self.pushButton_rconcmd_2.setMaximumSize(QtCore.QSize(16777215, 33)) + self.pushButton_rconcmd_2.setObjectName("pushButton_rconcmd_2") + self.label_rconcmd_2 = QtWidgets.QLabel(parent=self.groupBox_7) + self.label_rconcmd_2.setGeometry(QtCore.QRect(10, 20, 91, 18)) + self.label_rconcmd_2.setScaledContents(True) + self.label_rconcmd_2.setObjectName("label_rconcmd_2") + self.verticalLayout_4.addWidget(self.groupBox_7) + self.horizontalLayout_2.addWidget(self.groupBox_2) + self.line_11 = QtWidgets.QFrame(parent=self.tab_6) + self.line_11.setFrameShape(QtWidgets.QFrame.Shape.VLine) + self.line_11.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) + self.line_11.setObjectName("line_11") + self.horizontalLayout_2.addWidget(self.line_11) + self.scrollArea_2 = QtWidgets.QScrollArea(parent=self.tab_6) + self.scrollArea_2.setWidgetResizable(True) + self.scrollArea_2.setObjectName("scrollArea_2") + self.scrollAreaWidgetContents_2 = QtWidgets.QWidget() + self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 970, 923)) + self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2") + self.gridLayout_7 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_2) + self.gridLayout_7.setObjectName("gridLayout_7") + self.tabWidget_ini_2 = QtWidgets.QTabWidget(parent=self.scrollAreaWidgetContents_2) + self.tabWidget_ini_2.setObjectName("tabWidget_ini_2") + self.tab_gameini_2 = QtWidgets.QWidget() + self.tab_gameini_2.setObjectName("tab_gameini_2") + self.gridLayout_8 = QtWidgets.QGridLayout(self.tab_gameini_2) + self.gridLayout_8.setObjectName("gridLayout_8") + self.kdialog_2 = QtWidgets.QDialog(parent=self.tab_gameini_2) + self.kdialog_2.setObjectName("kdialog_2") + self.tableView_3 = QtWidgets.QTableView(parent=self.kdialog_2) + self.tableView_3.setGeometry(QtCore.QRect(0, 0, 941, 862)) + self.tableView_3.setGridStyle(QtCore.Qt.PenStyle.SolidLine) + self.tableView_3.setObjectName("tableView_3") + self.tableView_3.horizontalHeader().setVisible(True) + self.tableView_3.horizontalHeader().setCascadingSectionResizes(False) + self.gridLayout_8.addWidget(self.kdialog_2, 0, 0, 1, 1) + self.tabWidget_ini_2.addTab(self.tab_gameini_2, "") + self.tab_gameusersettingsini_2 = QtWidgets.QWidget() + self.tab_gameusersettingsini_2.setObjectName("tab_gameusersettingsini_2") + self.gridLayout_9 = QtWidgets.QGridLayout(self.tab_gameusersettingsini_2) + self.gridLayout_9.setObjectName("gridLayout_9") + self.tableView_4 = QtWidgets.QTableView(parent=self.tab_gameusersettingsini_2) + self.tableView_4.setGridStyle(QtCore.Qt.PenStyle.SolidLine) + self.tableView_4.setObjectName("tableView_4") + self.tableView_4.horizontalHeader().setVisible(True) + self.tableView_4.horizontalHeader().setCascadingSectionResizes(False) + self.gridLayout_9.addWidget(self.tableView_4, 0, 0, 1, 1) + self.tabWidget_ini_2.addTab(self.tab_gameusersettingsini_2, "") + self.gridLayout_7.addWidget(self.tabWidget_ini_2, 0, 0, 1, 1) + self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2) + self.horizontalLayout_2.addWidget(self.scrollArea_2) + self.tabServers_2.addTab(self.tab_6, "") + self.horizontalLayout_3.addWidget(self.tabServers_2) icon2 = QtGui.QIcon() icon2.addPixmap(QtGui.QPixmap("./../../Yandex.Disk/Изображения/Оформление/7dtd.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) self.tabGames.addTab(self.tab_2, icon2, "") @@ -340,6 +545,8 @@ class Ui_mainWindow(object): self.menu.setObjectName("menu") self.menu_2 = QtWidgets.QMenu(parent=self.menubar) self.menu_2.setObjectName("menu_2") + self.menu_3 = QtWidgets.QMenu(parent=self.menubar) + self.menu_3.setObjectName("menu_3") mainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(parent=mainWindow) self.statusbar.setObjectName("statusbar") @@ -352,6 +559,10 @@ class Ui_mainWindow(object): self.action_HLN_A.setObjectName("action_HLN_A") self.action_Discord = QtGui.QAction(parent=mainWindow) self.action_Discord.setObjectName("action_Discord") + self.action_3 = QtGui.QAction(parent=mainWindow) + self.action_3.setObjectName("action_3") + self.action_4 = QtGui.QAction(parent=mainWindow) + self.action_4.setObjectName("action_4") self.menu.addSeparator() self.menu.addAction(self.action_HLN_A) self.menu.addSeparator() @@ -361,6 +572,9 @@ class Ui_mainWindow(object): self.menu_2.addSeparator() self.menu_2.addAction(self.action_Discord) self.menu_2.addSeparator() + self.menu_3.addAction(self.action_3) + self.menu_3.addAction(self.action_4) + self.menubar.addAction(self.menu_3.menuAction()) self.menubar.addAction(self.menu.menuAction()) self.menubar.addAction(self.menu_2.menuAction()) @@ -368,6 +582,8 @@ class Ui_mainWindow(object): self.tabGames.setCurrentIndex(0) self.tabServers.setCurrentIndex(0) self.tabWidget_ini.setCurrentIndex(0) + self.tabServers_2.setCurrentIndex(0) + self.tabWidget_ini_2.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(mainWindow) def retranslateUi(self, mainWindow): @@ -389,7 +605,7 @@ class Ui_mainWindow(object): self.lineEdit_adminpassword.setText(_translate("mainWindow", "Marvin")) self.label_password.setText(_translate("mainWindow", "Пароль сессии")) self.label_players.setText(_translate("mainWindow", "Кол-во игроков")) - self.lineEdit_players.setText(_translate("mainWindow", "27042")) + self.lineEdit_players.setText(_translate("mainWindow", "70")) self.label_Maps.setText(_translate("mainWindow", "Карта")) self.comboBox_maps.setItemText(0, _translate("mainWindow", "Остров (The Island)")) self.comboBox_maps.setItemText(1, _translate("mainWindow", "Выжженые земли (Scorched Earth)")) @@ -409,23 +625,50 @@ class Ui_mainWindow(object): self.tabWidget_ini.setTabText(self.tabWidget_ini.indexOf(self.tab_gameini), _translate("mainWindow", "Game.ini")) self.tabWidget_ini.setTabText(self.tabWidget_ini.indexOf(self.tab_gameusersettingsini), _translate("mainWindow", "GameUserSettings.ini")) self.tabServers.setTabText(self.tabServers.indexOf(self.tab_5), _translate("mainWindow", "Tab 1")) - self.tabServers.setTabText(self.tabServers.indexOf(self.tab), _translate("mainWindow", "Страница")) self.tabGames.setTabText(self.tabGames.indexOf(self.tabArk), _translate("mainWindow", "ARK")) - self.groupBox_4.setTitle(_translate("mainWindow", "xml")) - 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_settings_2.setTitle(_translate("mainWindow", "Настройки")) + self.checkBox_cluster_2.setText(_translate("mainWindow", "Кластер")) + self.lineEdit_cluster_2.setText(_translate("mainWindow", "id-кластера")) + self.checkBox_listen_2.setText(_translate("mainWindow", "Передавать в глобальный список серверов")) + self.label_namesession_2.setText(_translate("mainWindow", "Имя сессии")) + self.lineEdit_namesession_2.setText(_translate("mainWindow", "Имя_сессии ")) + self.label_port_2.setText(_translate("mainWindow", "Порт")) + self.lineEdit_port_2.setText(_translate("mainWindow", "7777")) + self.label_querryport_2.setText(_translate("mainWindow", "Querry порт")) + self.lineEdit_querryport_2.setText(_translate("mainWindow", "27015")) + self.label_rconport_2.setText(_translate("mainWindow", "Rcon порт")) + self.lineEdit_rconport_2.setText(_translate("mainWindow", "27042")) + self.label_adminpassword_2.setText(_translate("mainWindow", "Пароль администратора (Marvin)")) + self.lineEdit_adminpassword_2.setText(_translate("mainWindow", "Marvin")) + self.label_password_2.setText(_translate("mainWindow", "Пароль сессии")) + self.label_players_2.setText(_translate("mainWindow", "Кол-во игроков")) + self.lineEdit_players_2.setText(_translate("mainWindow", "70")) + self.label_Maps_2.setText(_translate("mainWindow", "Карта")) + self.comboBox_maps_2.setItemText(0, _translate("mainWindow", "Остров (The Island)")) + self.comboBox_maps_2.setItemText(1, _translate("mainWindow", "Выжженые земли (Scorched Earth)")) + self.comboBox_maps_2.setItemText(2, _translate("mainWindow", "Аберация (Aberration)")) + self.comboBox_maps_2.setItemText(3, _translate("mainWindow", "Вымирание (Extinction)")) + self.comboBox_maps_2.setItemText(4, _translate("mainWindow", "Genesis: Part 1")) + self.comboBox_maps_2.setItemText(5, _translate("mainWindow", "Genesis: Part 2")) + self.comboBox_maps_2.setItemText(6, _translate("mainWindow", "Центр (The Center)")) + self.comboBox_maps_2.setItemText(7, _translate("mainWindow", "Рагнарёк (Ragnarok)")) + self.comboBox_maps_2.setItemText(8, _translate("mainWindow", "Valguero")) + self.comboBox_maps_2.setItemText(9, _translate("mainWindow", "Кристальные острова (Crystal Island)")) + self.comboBox_maps_2.setItemText(10, _translate("mainWindow", "Потерянные острова (Lost Island)")) + self.comboBox_maps_2.setItemText(11, _translate("mainWindow", "Фьйордур (Fjordur)")) + self.groupBox_7.setTitle(_translate("mainWindow", "RCON")) + self.pushButton_rconcmd_2.setText(_translate("mainWindow", "Отправить")) + self.label_rconcmd_2.setText(_translate("mainWindow", "Rcon команда")) + self.tabWidget_ini_2.setTabText(self.tabWidget_ini_2.indexOf(self.tab_gameini_2), _translate("mainWindow", "Game.ini")) + self.tabWidget_ini_2.setTabText(self.tabWidget_ini_2.indexOf(self.tab_gameusersettingsini_2), _translate("mainWindow", "GameUserSettings.ini")) + self.tabServers_2.setTabText(self.tabServers_2.indexOf(self.tab_6), _translate("mainWindow", "Tab 1")) self.tabGames.setTabText(self.tabGames.indexOf(self.tab_2), _translate("mainWindow", "7 Days to Die")) self.menu.setTitle(_translate("mainWindow", "Настройки")) self.menu_2.setTitle(_translate("mainWindow", "Справка")) + self.menu_3.setTitle(_translate("mainWindow", "Сервер")) self.action.setText(_translate("mainWindow", "Лицензия")) self.action_2.setText(_translate("mainWindow", "Об авторах")) self.action_HLN_A.setText(_translate("mainWindow", "Настроить HLN-A")) self.action_Discord.setText(_translate("mainWindow", "Сообщество Discord")) + self.action_3.setText(_translate("mainWindow", "Добавить")) + self.action_4.setText(_translate("mainWindow", "Удалить")) diff --git a/pytest.py b/pytest.py index 43d162e..296fe12 100644 --- a/pytest.py +++ b/pytest.py @@ -1,38 +1,141 @@ -#!/usr/bin/env python3 -from colorama import Fore, Style +#!/usr/bin/env python3 import os - -import time -from threading import Thread -from pathlib import Path - - - import hlna +import time +import unittest +import zipfile +from click.testing import CliRunner +from pathlib import Path home_dir = Path.home() config_hlna = f"{home_dir}/.config/hlna/" +from unittest.mock import patch, MagicMock -def config(): - hlna.config() -def servers(): - x = os.system("./hlna.py servers >> /dev/null") - if x == 0: - print("Servers - "+Fore.GREEN + "OK" + Style.RESET_ALL) - else: - print(Fore.RED + "Servers Fail" + Style.RESET_ALL) -def delete(): - print("Delete - " + Fore.RED + "False" + Style.RESET_ALL) +from hlna import config + + +class TestConfig(unittest.TestCase): + + @patch('builtins.input', side_effect=['1']) + @patch('hlna.config_ark') + def test_config_ark_valid(self, config_ark_mock, input_mock): + config() + config_ark_mock.assert_called_once() + + @patch('builtins.input', side_effect=['2']) + @patch('hlna.config_7daystodie') + def test_config_7daystodie_valid(self, config_7daystodie_mock, input_mock): + config() + config_7daystodie_mock.assert_called_once() + + @patch('builtins.input', side_effect=['3']) + @patch('sys.stdout', new_callable=MagicMock) + def test_config_invalid_game(self, mocked_stdout, input_mock): + config() + self.assertEqual(mocked_stdout.getvalue().strip(), "Пока есть только ARK и 7Days xD") + + @patch('builtins.input', side_effect=['a', '1']) + @patch('hlna.config_ark') + @patch('sys.stdout', new_callable=MagicMock) + def test_config_invalid_input(self, mocked_stdout, config_ark_mock, input_mock): + config() + config_ark_mock.assert_called_once() + self.assertEqual(mocked_stdout.getvalue().strip(), + "Выберите игру для конфигурирования\n1. ARK Survival Evolved\n2. 7 Days to Die\n: ") +class TestFindFile(unittest.TestCase): + def setUp(self): + self.test_dir = 'test_dir' + os.mkdir(self.test_dir) + self.test_files = ['foo.conf', 'bar.ini', 'qux.cfg', 'baz.txt', '.directory'] + for f in self.test_files: + open(os.path.join(self.test_dir, f), 'w').close() + + def tearDown(self): + for f in self.test_files: + os.remove(os.path.join(self.test_dir, f)) + os.rmdir(self.test_dir) + + def test_find_file(self): + res = hlna.find_file(self.test_dir) + self.assertEqual(sorted(res), sorted(['foo.conf', 'bar.ini', 'qux.cfg', 'baz.txt'])) + + def test_find_file_empty_dir(self): + res = hlna.find_file('empty_dir') + self.assertEqual(res, []) + +class TestCreateDir(unittest.TestCase): + def test_create_dir(self): + testdir = 'testdir' + self.assertFalse(os.path.exists(testdir)) + hlna.create_dir(testdir) + self.assertTrue(os.path.exists(testdir)) + os.rmdir(testdir) + +class TestRestore(unittest.TestCase): + + def setUp(self): + self.runner = CliRunner() + self.zip_file_name = "test_backup.zip" + os.system("touch test_file1.txt test_file2.txt") + self.file_names = ["test_file1.txt", "test_file2.txt"] + with zipfile.ZipFile(self.zip_file_name, 'w') as zip_file: + for file_name in self.file_names: + zip_file.write(file_name) + + def tearDown(self): + os.remove(self.zip_file_name) + for file_name in self.file_names: + os.remove(file_name) + + def test_restore(self): + result = self.runner.invoke(hlna.restore, ["test", "-d", self.zip_file_name]) + self.assertEqual(result.exit_code, 0) + for file_name in self.file_names: + self.assertTrue(os.path.exists("./"+file_name)) + +class TestBackup(unittest.TestCase): + + def setUp(self): + data = hlna.read_yaml(g="path_server") + if data['path_server'] == "": + data['path_server'] = f"{home_dir}Servers/ARK/Backups/test_backup/" + else: + data['path_server'] = f"{data['path_server']}ARK/Backups/test_backup/" + self.backup_path = data['path_server'] + self.runner = CliRunner() + with open(f"{config_hlna}/ARK/test_file.txt", "w") as file: + file.write("test content") + + def tearDown(self): + os.remove(f"{config_hlna}ARK/test_file.txt") + for root, dirs, files in os.walk(self.backup_path): + for file in files: + os.remove(os.path.join(root, file)) + os.rmdir(self.backup_path) + + def test_backup(self): + result = self.runner.invoke(hlna.backup, ["ark_test"]) + self.assertEqual(result.exit_code, 0) + self.assertTrue(os.path.exists(self.backup_path)) + + target = f"{self.backup_path}ark_test_backup-" + time.strftime('%Y_%m_%d') + '.zip' + self.assertTrue(os.path.exists(target)) + with zipfile.ZipFile(target, 'r') as zip_file: + self.assertEqual(zip_file.testzip(), None) + self.assertTrue(os.path.exists(os.path.join(target, f"{config_hlna}ARK/test_file.txt"))) + +class TestGetExternalIP(unittest.TestCase): + def test_backup(self): + self.assertTrue(bool(hlna.get_external_ip())) + +#class TestGetExternalIP(unittest.TestCase): +# def test_backup(self): +# self.assertTrue(bool(hlna.get_external_ip())) +if __name__ == '__main__': + unittest.main() -if __name__ == "__main__": - - servers() - delete() - config() -#print(f"{Fore.GREEN} + {text}") -#print(Fore.YELLOW + "-"*30 + Style.RESET_ALL)