Jump to content
Korean Random
PVirex

[2.1.0.0] Python mods installer

Recommended Posts

Всем привет. 

Начало этой эпопеи можно прочитать тут, там же есть демо версия exe. Если кратко, это довольно простой инсталятор написанный на Python 3, но мне кажется будет работать и на 2 без особых переделок. Сам проект возник как шуточный, демонстративный, но со временем меня затянул и я решил доделать до конца, т.е до рабочего прототипа. Я в курсе, что есть более профильные решения, возможно даже проще, но у меня была цель сделать это на Python)

 

Ссылка на репозиторий.

 

Текущая версия: 2.1.0.0

 

Окружение:

  • Python 3.8.1
  • wxPython
  • pyinstaller
pip install wxpython
pip install pyinstaller

Сложно понять, с чего начать) Пожалуй начну с самого начала, а именно mods_install.py т.к именно с него осуществляется запуск.

В модуле содержится информация о панелях и инициализация самого приложения. Для добавления новой необходимо сделать импорт и внести в PANEL_INFO по примеру.

 

В common расположены основные константы и пути, а также общие функции.

constants.py

# для отладки и запуска из IDE необходимо в root создать пустой файл start_python без расширения, это необходимо для поиска абстрактного пути ресурсов
g_PYTHON_START = True if os.path.isfile(os.path.join(os.getcwd(), 'start_python')) else False

# константа используется для включения записи логов в текстовый файл, см LOG_FOLDER  в path.py
g_DEBUG = True if os.path.isfile(os.path.join(os.getcwd(), 'debug')) else False

# версия инсталятора
VERSION = '2.0'

# Имя рамки
TITLE = 'My personal installer {}'.format(VERSION)

# Очень важная константа!!! берется из version.xml игрового клиента, если версии не совпадут с клиентской, поставить моды не получится.
VERSION_CLIENT = 'v.1.7.1.2'

# размер фрейма и панели, панель чуть меньше из-за особенностей отрисовки wxWidget
SIZE_FRAME = (600, 660)
SIZE_PANEL = (585, 625)

# стиль фрейма
FRAME_STYLE = wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL | wx.BORDER

# список папок которые будут удалены при выборе опции "Удалить кеш игры" пользователем
DROP_GAME_FOLDER = ['battle_results', 'clan_cache', 'custom_data', 'veh_cmp_cache', 'dossier_cache', 'web_cache']

# список папок которые будут удалены при выборе опции "Удалить кеш модификаций" пользователем
DROP_XVM_FOLDER = ['xvm\\Hitlog', 'xvm\\cache', 'xvm\\custom_data', 'xvm\\statistics']

DEFAULT_LANG = 'RU'
# важная настройка, распаковывает моды не в конкретную папку, а как есть в корень игры. Это тнужно если в модпаке содержатся моды как mods так # и в res_mods
SET_IN_GAME_FOLDER = True

path.py

ICON_PATH = resource_path('res_image\\main.ico')
MAIN_LOGO_600x100_PATH = resource_path('res_image\\logo_600_100.png')
MAIN_LOGO_600x500_PATH = resource_path('res_image\\logo_600x500.png')

WGC_DEFAULT_PATH = os.path.join(os.environ['PROGRAMDATA'], 'Wargaming.net', 'GameCenter', 'preferences.xml')

NOT_PATH_DEFAULT = resource_path('res_image\\not_found.jpg')
# путь до кеша игры
PATH_TO_CACHE_WOT = os.path.join(os.environ['appdata'], 'Wargaming.net', 'WorldOfTanks')

LOG_FOLDER = os.path.join(os.environ['appdata'], 'TEMP_INSTALLER_LOGS', 'INSTALLER_LOGS')

g_MODS_CONFIG = json.load(open(resource_path('mods_config.json'), encoding='utf-8'))

g_PRESET_SETTINGS = json.load(open(resource_path('stream_settings.json'), encoding='utf-8'))

PRESET_NAMES = list(g_PRESET_SETTINGS.keys())

В директории core расположены базовые класса и механики.

 

Важным нюансом является то, что все ресурсы обязаны находиться в директории res, это больше обусловлено механизмом работы exe собранным с помощью pyinstaller.

Для доступа к любому не py файлу должен осуществляться через функцию common_utils.resource_path

def resource_path(relative_path):
    """
    Функция для получения пути к ресурсам, все не .py файлы обязаны храниться в директории res.
    Такая зависимость обусловлена особенностями упаковки с помощью pyinstaller, т.к root меняется при запуске из .ехе
    """
    if not g_PYTHON_START:
        base_path = sys._MEIPASS
    else:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, 'res', relative_path)

Конфиги.

Самым важным безусловно является mods_config.json, такой формат выбран для простоты использования, описать можно вот так:

{
  "P0LIROID Mods": {
    "checkBox": true,  // флаг если для группы нужен именно чекбокс а не радиобуттон
    "Просмотр попадний в ангаре": [  // имя модификации в селекторе
      "res_image/battle_hits_poliroid.jpg",  // относительный путь в ресурсах, обратить внимание что директорию res не учитываем
      "mods/battlehints_poliroid.zip",  // относительный путь в ресурсах, до самого мода, обязательно архив zip
      "Текстовое описание, опционально, если не нужно то поставить null или пустую строку"
    ],
    "Менеджер реплеев": [
      "res_image/replays_manager_poliroid.jpg",
      "mods/replays_manager.zip",
      "Текстовое описание, опционально, если не нужно то поставить null или пустую строку"
    ],
    "Кастомизация танков": [
      "res_image/cust_veh.jpg",
      "mods/branding_vehicle.zip",
      null
    ]
  },
   "Мои моды": [
	  "res_image/мой мод.jpg",
      "mods/мой мод.zip",
      null]
  
	],
  "Radio button": {
  	"Mods 1": [
      	  "res_image/мой мод 1.jpg",
      "mods/мой мод1.zip",
      null],
     "Mods 2": [
      	  "res_image/мой мод 2.jpg",
      "mods/мой мод2.zip",
      null]
  }
}

preset.json - устанавливает флажки для указанных групп, можно сделать сколько угодно пресетов.

{
  "P0LIROID": [
    "Просмотр попадний в ангаре", // просто добавляем имена модов как есть
    "Менеджер реплеев"
  ]
}

Модуль логирования logger.py имеет несколько уровней логирования: общий лог и только gui. Такой подход будет полезен в будущем если захочется использовать дополнительные библиотеки, которые тоже используют logger.

Доступны следующие уровни:

from core.logger import logger
logger.info(msg)
logger.debug(msg)
logger.warning(msg)
logger.error(msg)
logger.exception(msg)
try:
	pass
exception Exception as e:
	logger.exception(e.msg)

Основной UI который виден пользователю находится в step_panel. В текущий момент там 6 панелей: приветствие, поиск игры, выбор модификаций, подтверждение выбора, дополнительные настройки, установка. Возможно в будущем уделю больше внимания верстке и основной логике, в принципе там ничего сложного нет, если вы посвящены в начальные тайны ООП на Python.

 

Сборка в ехе осуществляется либой pyinstaller. 

# -*- mode: python ; coding: utf-8 -*-
import os

block_cipher = None

path_cwd = os.getcwd()


a = Analysis(['mods_install.py'],
             pathex=['E:\\my_project\\mod_installer_stage_2'], # путь до проекта
             binaries=[],
             datas=[('res/locales/RU/LC_MESSAGES/ru.mo', 'res/locales/RU/LC_MESSAGES')], # локализации помещены отдельно специально
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
# указываем относительный путь в проекте, полный путь до файла
a.datas += [('res/res_image/battle_hits_poliroid.jpg', os.path.join(path_cwd, 'res', 'res_image', 'battle_hits_poliroid.jpg'), 'DATA')]
a.datas += [('res/res_image/cust_veh.jpg', os.path.join(path_cwd, 'res', 'res_image', 'cust_veh.jpg'), 'DATA')]
a.datas += [('res/res_image/logo_600_100.png', os.path.join(path_cwd, 'res', 'res_image', 'logo_600_100.png'), 'DATA')]
a.datas += [('res/res_image/logo_600x500.png', os.path.join(path_cwd, 'res', 'res_image', 'logo_600x500.png'), 'DATA')]
a.datas += [('res/res_image/not_found.jpg', os.path.join(path_cwd, 'res', 'res_image', 'not_found.jpg'), 'DATA')]
a.datas += [('res/res_image/replays_manager_poliroid.jpg', os.path.join(path_cwd, 'res', 'res_image', 'replays_manager_poliroid.jpg'), 'DATA')]
a.datas += [('res/res_image/main.ico', os.path.join(path_cwd, 'res', 'res_image', 'main.ico'), 'DATA')]
a.datas += [('res/mods_config.json', os.path.join(path_cwd, 'res', 'mods_config.json'), 'DATA')]
a.datas += [('res/stream_settings.json', os.path.join(path_cwd, 'res', 'stream_settings.json'), 'DATA')]
a.datas += [('res/mods/battlehints_poliroid.zip', os.path.join(path_cwd, 'res', 'mods', 'battlehints_poliroid.zip'), 'DATA')]
a.datas += [('res/mods/branding_vehicle.zip', os.path.join(path_cwd, 'res', 'mods', 'branding_vehicle.zip'), 'DATA')]
a.datas += [('res/mods/replays_manager.zip', os.path.join(path_cwd, 'res', 'mods', 'replays_manager.zip'), 'DATA')]

pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='Name EXE',
          debug=True,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False,  # если поставить True, то приложение будет запускаться с консолью, иногда удобно для отладки
          icon=os.path.join(path_cwd, 'res\\res_image\\main.ico')) 

Для сборки запустить build.bat

 

Edited by PVirex
  • Upvote 3

Share this post


Link to post

Short link
Share on other sites

Сборка исполняемого файла

Коммитом от on Mar 5, 2020 добавил парсинг mods_config.json в mods_install.spec Таким образом, конечному пользователю нет необходимости вносить правки в mods_install.spec, что может плохо закончиться. Теперь все ресурсы берутся из конфига автоматически и запаковываются.

 

Для сборки необходимо:

Шаг 1: установить python и либы, можно использовать setup.bat

Шаг 2: занести нужные моды в конфиг  mods_config.json

Шаг 3: запустить сборщик

 

9 апреля, добавил упаковку модов в zip из директории mods_unpack. Это повышает удобство при пересборке модпака, достаточно хранить все необходимые моды в таком виде, как они должны находиться в клиенте игры. Например:

replays/mods/1.8.0.2/replays.wotmod
battlehints/res_mods/1.8.0.2/scripts/client/gui/mods/mod_battlehints.pyc

После запуска батника в директории: res/mods появятся архивы с названием корневой папки и полной вложенностью как в оригинале replays.zip и battlehints.zip. Останется только внести zip архивы в mods_config.json

Edited by PVirex

Share this post


Link to post

Short link
Share on other sites

2.0.1

* убрана необходимость вносить ресурсы в main.spec

2.0.2.0

* добавлено сохранение в кеш последнего указанного пути и последних установленных модификаций

2.0.3.0

добавил упаковку модов в zip из директории mods_unpack. Это повышает удобство при пересборке модпака, достаточно хранить все необходимые моды в таком виде, как они должны находиться в клиенте игры. При этом моды у которых не совпадает версия папки для модов с той, что в клиенте добавляться не будут

2.1.0.0

* добавлена поддержка локализаций с сохранением выбора в кеш

Edited by PVirex

Share this post


Link to post

Short link
Share on other sites

Это типа установщик модов, написанный на пайтоне, с компиляцией exe через wxPython?

Share this post


Link to post

Short link
Share on other sites
5 минут назад, StranikS_Scan сказал:

Это типа установщик модов, написанный на пайтоне, с компиляцией exe через wxPython?

Наоборот немного, инсталятор модификаций написанный на Python с помощью wxPython и запакованный в ехе с помощью pyinstaller

Edited by PVirex
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites

а оно работает ?:\ все скачал, установил, скомпилировал без ошибок - а кнопка Далее (после выбора папки) не активна

spacer.png

Edited by tunut
  • Upvote 1

Share this post


Link to post

Short link
Share on other sites
23.03.2020 в 11:54, tunut сказал:

а оно работает ?:\ все скачал, установил, скомпилировал без ошибок - а кнопка Далее (после выбора папки) не активна

 

  Скрыть содержимое

spacer.png

 

Должно работать. Обрати внимание на версию клиентав constants.py, текущая версия на проде вот такая

VERSION_CLIENT = 'v.1.8.0.1'

Извини за долгий ответ, мне казалось никому не интересно и я не стал дописывать мануал

Обнови бранч, я внес правки и поправил версию.

Edited by PVirex

Share this post


Link to post

Short link
Share on other sites
49 минут назад, PVirex сказал:

Обрати внимание на версию клиентав constants.py,

конечно поменял,

если версия клиента не правильная - выводится соответствующее сообщение 

а тут ничего, просто кнопка не активна

Share this post


Link to post

Short link
Share on other sites

@tunut а WGC стоит? или ты выбираешь путь до игры руками? мб и баг у меня где-то)))

 

я воспроизвел, это баг. ты не используешь WGC. Поправлю минут через 10-20. Спаисбо за инфу

Edited by PVirex

Share this post


Link to post

Short link
Share on other sites
5 минут назад, PVirex сказал:

WGC стоит?

стоит, но запускаю его только при обновлении клиента, а так запускаю через ехе-шник игру

Share this post


Link to post

Short link
Share on other sites

@tunut значит стоит не по стандартному пути, не в programdata. Я поправил, должна кнопка теперь разблокироваться при ручном выборе пути.

 

Fixed at head revision

Edited by PVirex

Share this post


Link to post

Short link
Share on other sites
21 минуту назад, PVirex сказал:

Я поправил, должна кнопка теперь разблокироваться при ручном выборе пути.

так  и есть, спс

 

а зафиксировать тултип к левому или правому краю установщика можно? что-б не гулял за мышкой... а то бесит.

Share this post


Link to post

Short link
Share on other sites

@tunut оххх, это тултип отдельная боль и страдания. Я не нашел нормального решения как сделать тултип для three и начал колхозить сам) вышло то, что есть. По идее, мне кажестся можно зафиксировать его справа или слева, но тут есть трудности, я подумаю что можно сделать.

 

Не забудьте поделиться ссылкой на Ваш модпак, очень интересно, что вышло в оконечном результате. У меня есть планы на доработку)

  • Upvote 2

Share this post


Link to post

Short link
Share on other sites

Наверняка потребуется возможность выбора языка установки.

Share this post


Link to post

Short link
Share on other sites
1 минуту назад, ktulho сказал:

Наверняка потребуется возможность выбора языка установки.

было в планах на будущее как и кеш, есть даже отработанная технология

Edited by PVirex

Share this post


Link to post

Short link
Share on other sites

Добавил кеш, теперь сохраняется последний указанный путь и выбранные модификации

  • Upvote 2

Share this post


Link to post

Short link
Share on other sites

Добавил упаковку модов в zip из директории mods_unpack. Это повышает удобство при пересборке модпака, достаточно хранить все необходимые моды в таком виде, как они должны находиться в клиенте игры. При этом моды у которых не совпадает версия папки для модов с той, что в клиенте добавляться не будут

  • Upvote 3

Share this post


Link to post

Short link
Share on other sites
25.03.2020 в 16:35, ktulho сказал:

Наверняка потребуется возможность выбора языка установки.

Добавлена поддержка локализаций: RU, EN

Выбирается 1 раз при первом запуске и сохраняется в кеш. Для переопределения снести кеш или поменять/удалить ключ 'language'

  • Upvote 3

Share this post


Link to post

Short link
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...