это руководство по модуляризации являтеся не обязательным к прочтению и необязательным по исполнению. Некоторые моменты противоречат тому что вы делаете. За дополнительной информацией обращаться к кодерам проекта.
МодуляризацияЭто руководство несёт рекомендательный характер.
Содержание документа
Введение
Разработка и поддержка отдельной кодовой базы - это громадная, тяжёлая задача, пытаясь справиться с которой, многие потерпели неудачу и пострадали от последствий, таких как устаревший и беспорядочный код. Это не обязательно вина нехватки навыков у людей, поддерживающих кодбазу - просто нехватка ресурсов и то, сколько непрерывных усилий требует такая попытка.
Git, как система контроля версий, очень удобен, однако это просто очень методичная вещь, которая следует своим многочисленным алгоритмам, и они, к сожалению, не всегда могут разумно разрешать определенные изменения в коде однозначным образом, вызывая конфликты, которые приходится решать вручную.
Так как возможность долгосрочной поддержки является одной из основных причин использования как основы другой кодбазы, это руководство по модуляризации являтеся обязательным к прочтению и использованию. Хорошо организованный, задокументированный и атомизированный код избавляет мейнтейнеров от тонны головной боли. Не перекладывайте на них работу, которую можете сделать сами.
Этот документ может обновляться время от времени, если вдруг появятся новые исключения из правил. Иногда его стоило бы перепроверять.
Тестите PRы!
Ты ответственнен за тестирование твоих PR. PR нельзя отмечать готовым к ревью, пока он тобой не протестирован. Если для тестов требуется второй клиент - ты можешь зайти с использованием гостевого аккаунта. Для этого нужно лишь выйти из аккаунта BYOND и зайти на сервер. Тест мёржи - не для поиска багов, а для стресс-тестов, которые локально повторить не получится.
Природа конфликтов
Например, у нас есть переменная
var/something = 1
В кор коде мы решили заменить 1 на 2
- var/something = 1
+ var/something = 2 // JUICY
А на апстриме решили сменить на 1 вообще на 4
- var/something = 1
+ var/something = 4
Таким образом получается достаточно простой конфликт
- var/something = 1
<<<<<< ours
+ var/something = 2 // JUICY
======
+ var/something = 4
>>>>>>> theirs
Где мы выбираем предпочитаемый вариант вручную.
Решение
Конфликты - это то, что не может и не должно решаться автоматически, потому что это может привести к ошибкам и багам, которые будет очень сложно отследить, не говоря уже и о более сложных примерах, например таких, что добавляют, изменяют и перемещают строки кода повсюду.
Вкратце, программа изо всех сил старается, но она слишком глупая. Поэтому мы сами должны что-то делать, чтобы она могла без усилий выполнять большую часть работы, сводя к минимуму участие мейнтейнеров, вмешательство которых будет требоваться лишь когда конфликты будут неизбежны.
Решение - модуляризация кода.
Модуляризация означает, что максимально возможное количество изменений будут производиться в отдельной папке modular_juicy/ , независимой от кор кода насколько это возможно, а те изменения, которые ну никак не поддаются модуляризации будут отмечены специальными комментариями с указанием где они начинаются, заканчиваются и частью какого мода они являются.
Руководство по модуляризации
Начни с определения темы/цели того что ты хочешь сделать. Возможно ты можешь не создавать новый мод, а добавить функционал к уже существующему.
В ином случае, выбери ID для своего мода, например DNA_FEATURE_WINGS , XENOARCHEOLOGY или SHUTTLE_TOGGLE - он будет использоваться для документирования и идентифицирования мода. Именно этот ID должен использоваться ВЕЗДЕ, чтобы можно было легко найти всё что относится к твоему моду.
Далее тебе нужно создать папку, название которой - ID твоего мода строчными буквами. Например, для HELLO_WORLD это будет путь modular_juicy/hello_world/
Модпаки
Модпаки загружаются компилятором сразу после всего остального кода и после карты, что позволяет делать оверрайды, безболезненно добавлять предметы, пользуясь дефайнами из кор кода и много много всего.
Инициализируются модпаки на этапе SS_INIT_EARLY с вызовом соответствующей процедуры у синглтона. Подробнее можно почитать в modular_juicy/_modpack.dm
Создание модпака
Любой модпак состоит из:
README.md файла с информацией о моде
.dme файла с подключением всех остальных
.dm файла с синглтоном, содержащем информацию о паке
- Возможных в нём подпапок:
code - содержит только .dm файлы
maps - содержит только .dmm файлы
Структура модпака
Если мы условимся, что наш пак будет называется hello_world , то это будет выглядеть так:
modular_juicy/hello_world
├─ code
│ ├─ any_file.dm
│ ╰─ some_file.dm
├─ maps
│ ├─ any_map.dmm
│ ╰─ some_map.dmm
├─ _hello_world.dm
├─ _hello_world.dme
╰─ README.md
А вот иконки и звуки стоит хранить в двух общих паках
icons - содержит только файлы изображений
sounds - содержит только файлы звуков
Это _storge_icons и _storge_sounds и выглядеть их структура будет так:
modular_juicy/_storge_icons
├─ icons
│ ├─ any_icon.dmi
│ ╰─ some_icon.png
├─ _storge_icons.dm
├─ _storge_icons.dme
╰─ README.md
modular_juicy/_storge_sounds
├─ sound
│ ├─ any_sound.ogg
│ ╰─ some_sound.mp3
├─ _storge_sounds.dm
├─ _storge_sounds.dme
╰─ README.md
Быстрое создание основы модаАвтоматически
Для любой системы с установленным Python 3 - запустить файл modular_juicy/CREATE_MOD.py .
В Windows можно это сделать ещё двумя способами:
- Открыть конмандную строку в папке вашего репозитория и прописать:
powershell modular_juicy\CREATE_MOD.ps1
- Открыть PowerShell в папке вашего репозитория и прописать:
.\modular_juicy\CREATE_MOD.ps1
Те, кто пользуется Linux или WSL могут использовать bash файл:
./modular_juicy/CREATE_MOD.sh
Вручную
Чтобы реализовать основу мода достаточно выполнить три действия:
- Создать папку
modular_juicy/hello_world/ .
- Скопировать в папку
modular_juicy/hello_world/ все файлы из /modular_juicy/_example/ .
- Переименовать
example.dme и example.dm в _hello_world.dme и _hello_world.dm .
Назначение каждого из файловФайл README.md
В нём содержатся:
- ID мода
- Ссылка на PR, в котором был создан мод.
- Ссылки на PR с крупными изменениями мода.
- Название мода и его подробное описание.
- Список файлов, изменённых в кор коде с коротким описанием изменений.
- Список изменений, внесённых в другие моды, необходимых для функционирования этого.
- Автор кода.
Все подробности по заполнению файла находятся в нём самом.
Файл _hello_world.dm
В синглтоне описывается базовая информация о моде, отображаемая игрокам.
-
name - Имя мода из readme.md , либо оно же, но на русском.
-
desc - Описание мода. Не из readme.md .
Хотелось бы, чтобы описание было достаточно подробным, чтобы можно было понять что содержит мод, но не сильно замудрённым, чтобы любой игрок осилил читать буквы.
-
author - Автор (или авторы) кода из readme.md .
Здесь не стоит размещать имплементацию процедур этого синглтона.
Файл _hello_world.dme
Здесь подключаются все необходимые файлы, включая файл с синглтоном. Ничего особенного.
Пути здесь указываются локальные, а не глобальные. То есть писать нужно не так:
#include "modular_juicy/hello_world/_hello_world.dm"
А вот так:
#include "_hello_world.dm"
Папки code/ , icons/ , sounds/ , maps/
Не повторяй файловую структуру кор кода в своём модуле!
Примеры:
code/ :
- ✅ Правильно:
/modular_juicy/hello_world/code/disease_mob.dm
- ❌ Неправильно:
/modular_juicy/hello_world/code/modules/antagonists/disease/disease_mob.dm
icons/ :
- ✅ Правильно:
/modular_juicy/hello_world/icons/mining_righthand.dmi
- ❌ Неправильно:
/modular_juicy/hello_world/icons/mob/inhands/equipment/mining_righthand.dmi
С sounds/ и maps/ абсолютно то же самое.
Комментирование кода мода - Не делай этого!
Если это не кор код, то старый код комментировать не нужно - просто удали его.
Даже если ты думаешь, что кто-то возможно захочет отменить изменения - не комментируй, удаляй. Для этого гит и существует как система контроля версий.
Не относится к кор коду.
Папка _master_files
Ты всегда должен помещать любые модульные переопределения иконок, звуков, кода и всего другого в эту папку. Она должна соответствовать структуре папки основного кода.
Например: code/modules/mob/living/living.dm → modular_juicy/_master_files/code/modules/mob/living/living.dm
Это сделано для того, чтобы было проще выяснить, что изменилось в основном файле, без необходимости поиска в определениях процедур.
Это также помогает предотвратить многократное ненужное переопределение модулями одной и той же процедуры.
ОверрайдыАссеты: Изображения, иконки, звуки и другие бинарные файлы
Гит не умеет решать конфликты в бинарных файлах и именно поэтому изменение бинарных файлов в кор коде запрещено. Исключением может быть лишь очень очень очень хорошая причина.
Все ассеты должны быть размещены в той же папке мода, в который находится и твой код. То есть всё хранится именно в папке мода: код, звуки, изображения...
Код: Модульные изменения
Код можно легко дописывать к выполнению процедуры из кор кода как до её выполнения, так и после, используя вызов родителя - ..() . И переменные тоже можно добавлять и переписывать без изменения кор кода.
Эти модульные изменения должны быть размещены в папке _master_files и следует избегать размещения их где-либо ещё кроме как здесь.
Предположим, что ты хочешь, чтобы оружие искрило при выстреле, для имитации дульного выстрела или для чего-либо ещё, и ты хочешь сделать это потенциально для всех видов оружия.
В модульном файле мы добавляем переменную
/obj/item/gun
var/muzzle_flash = TRUE
И это будет прекрасно работать.
После этого, предположим, ты хочешь проверить эту переменную и создать искры после выстрела. Зная, что исходная процедура вызываемая стрельбой - это
/obj/item/gun/proc/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1)
Ты можешь создать дочернюю процедуру, которая будет вставлена в цепочку наследования связанных процедур (умные слова, но в этом простом примере можно особо над ними не задумываться)
/obj/item/gun/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1)
// . - это стандартное возвращаемое значение.
// Ему мы присваиваем то, что возвращает родительская процедура
// Заметь, здесь вызываем родителя перед нашей вставкой
. = ..()
if(muzzle_flash)
// Для упрощения, предположим, что ты уже написал свой proc для этого
spawn_sparks(src)
Вот и основа модуьрных изменений.
Код: Немодульные изменения
Время от времени происходит случай, когда редактирование файлов кор кода становится неизбежным.
Обязательно укажите все изменённые файлы в README.md мода.
В этом случае применяются следующие обозначения:
- Добавление
// [JUICY-ADD] - SHUTTLE_TOGGLE - (Необязательно - причина или комментарий)
var/adminEmergencyNoRecall = FALSE
var/lastMode = SHUTTLE_IDLE
var/lastCallTime = 6000
// [/JUICY-ADD]
- Удаление
// [JUICY-REMOVE] - SHUTTLE_TOGGLE - (Необязательно - причина или комментарий)
/*
for(var/obj/docking_port/stationary/S in stationary)
if(S.id = id)
return S
*/
// [/JUICY-REMOVE]
Для удаления с перемещением в другой файл:
// [JUICY-REMOVE] - SHUTTLE_TOGGLE - (Перемещено в /modular_juicy/shuttle_toggle/randomverbs.dm)
/*
/client/proc/admin_call_shuttle()
set category = "Admin - Events"
set name = "Call Shuttle"
if(EMERGENCY_AT_LEAST_DOCKED)
return
...
message_admins(span_adminnotice("[key_name_admin(usr)] admin-called the emergency shuttle."))
return
*/
// [/JUICY-REMOVE]
- Изменение
// [JUICY-EDIT] - SHUTTLE_TOGGLE - (Optional Reason/comment)
// if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE) // JUICY-EDIT - ORIGINAL
if(SHUTTLE_STRANDED, SHUTTLE_ESCAPE, SHUTTLE_DISABLED)
// [/JUICY-EDIT]
return 1
NanoUI
Новый интерфейс необходимо создавать в папке nano/templates/modular_juicy . Для редактирования существующего - создать там его копию и включить в игру с помощью мода/модулярного изменения.
Для подключения интерфейса из этой папки, нужно к названию файла добавить префикс modular_juicy- .
То есть для файла nano/templates/modular_juicy/jukebox.tmpl при открытии интерфейса нужно использовать название "modular_juicy-jukebox.tmpl" вместо обычного "jukebox.tmpl" .
Исключительные случаи
Из каждого правила есть исключения, обусловленные многими обстоятельствами. Не задумывайся об этом сильно.
Дефайны (#define )
Из-за того, как BYOND загружает файлы, есть необходимость существования отдельной папки для хранения дефайнов модов. Эта папка - code/__defines/~modular_juicy/ , в которой ты можешь создать новый файл, либо дописать код в существующий.
Если у тебя есть #define , который используется более чем в одном файле, он должен быть объявлен именно здесь.
Если #define используется только в одном файле и больше нигде не будет использоваться, объяви его вверху, а в самом низу файла допиши #undef MY_DEFINE . Это делается для того, чтобы контекстные меню при разработке оставались чистыми, а также для предотвращения путаницы у тех, кто использует IDE с автозаполнением. |