Conversation
В AdventureTimeSS14/space_station_ADT#1416 было реализовано тоже самое, ID карта определяет название должности. Если этот код уже существует, то что ты сделал мне страшно даже думать. |
|
Вроде круто выглядит, прям сейчас не смогу протестить лично, но предварительно одобряю. |
Отображение в манифесте с них и взята. |
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
WalkthroughДобавлена поддержка альтернативных титулов должностей: новый прототип JobAlternateTitlePrototype и поле AlternateTitles в JobPrototype; HumanoidCharacterProfile получает словарь JobAlternateTitles, сериализацию/валидацию и метод WithJobAltTitle. Серверная модель сохраняет данные в новую сущность DBJobAlternateTitle с миграциями (Postgres/SQLite); данные загружаются при логине и используются при спавне, заполнении ID/PDA, объявлениях и записях станции. Клиентский UI (RequirementsSelector, HumanoidProfileEditor) расширен для выбора альтернативных титулов и передачи выбора в профиль; поведение включается через CCVar ic.alternate_job_titles_enable. Добавлены локализация и пример прототипа для Cargo Technician. Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (1)
1042-1081: Исправить семантику отображения альтернативных титулов в RequirementsSelector
Сейчас в Setup пункты добавляются в порядке: сохранённый альтернативный титул → основной → остальные, и при выборе первого пункта (Id=0) вызываетсяOnSelectedTitle(null), что сбрасывает титул на базовый, вместо сохранённого.
Предложение: в методе Setup файла Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs
- собрать
List<ProtoId<JobAlternateTitlePrototype>> mappedAltTitlesтак, чтобы в нём первым элементом шёлdefaultAltTitle(если не null), а затем остальные изaltTitles;- добавлять в OptionButton сначала
title(Id=0), потом все изmappedAltTitles(Id=1…);- в обработчике OnItemSelected делать
var selected = args.Id == 0 ? null : mappedAltTitles[args.Id - 1]; OnSelectedTitle?.Invoke(selected);Это гарантирует, что выбор сохранённого альтернативного титула не будет интерпретироваться как сброс.
Content.Server/Database/ServerDbBase.cs (2)
105-121: При сохранении слота стоит подгружать AltTitles у старого профиля.Чтобы EF корректно отследил замену/очистку коллекции альтернативных титулов, добавьте Include аналогично Jobs/Traits.
var oldProfile = db.DbContext.Profile .Include(p => p.Preference) .Where(p => p.Preference.UserId == userId.UserId) .Include(p => p.Jobs) .Include(p => p.Antags) .Include(p => p.Traits) + .Include(p => p.AltTitles) // Ganimed-JobAlt // ADT Start .Include(p => p.Languages)
1-134: Реализовать миграции AltJobTitle или убрать работу с AltTitles
Up/Down в файлах миграций пустые – таблицаDBJobAlternateTitleне создаётся. Либо добавить вUpсоздание таблицы с FK наProfileи уникальным индексом(ProfileId, RoleName)и включитьIncludeдля загрузкиAltTitles, либо удалить чтение/записьAltTitlesвServerDbBase.
♻️ Duplicate comments (1)
Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml (1)
21-23: Нейминг и место хранения примера alt‑титула
- ID
CargoTechnitianTitleWtf: опечатка в “Technician” и сомнительный суффиксWtf. Лучше привести к нейтральному ID и ключу локали.- Размещение примера в файле профессии перегружает YAML. Имеет смысл вынести
jobAlternateTitleв отдельный файл (как уже предлагали).Предлагаемый правящий дифф:
- - CargoTechnitianTitleWtf + - CargoTechnicianAltJunior - type: jobAlternateTitle - id: CargoTechnitianTitleWtf - name: job-name-alt-cargo-wtf +type: jobAlternateTitle +id: CargoTechnicianAltJunior +name: job-name-alt-cargo-juniorИ (по желанию) перенос блока
jobAlternateTitleв, например,Resources/Prototypes/Roles/Jobs/Cargo/alt_titles.yml.Also applies to: 35-37
🧹 Nitpick comments (13)
Content.Server.Database/Model.cs (1)
507-518: Именование полей — неточно; лучше хранить ProtoId в явных*IdЧтобы было ясно, что сохраняются идентификаторы прототипов, а не локализованные строки, переименуйте:
- public string RoleName { get; set; } = string.Empty; - public string AlternateTitle { get; set; } = string.Empty; + public string RoleId { get; set; } = string.Empty; + public string AlternateTitleId { get; set; } = string.Empty;Не забудьте синхронизировать соответствующие места в
ServerDbBase(конвертация профилей) и миграциях/индексах (RoleIdвместоRoleName).Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml (1)
4-4: Контейнер вместо Label: проверьте совместимость с код-бихайндом и событиями мышиЗаменили Label на голый Control. Убедитесь, что в RequirementsSelector.xaml.cs вы действительно добавляете внутрь TitleContent либо Label, либо OptionButton, и что отсутствие
MouseFilter="Stop"не вызывает «прокликивания» по контейнеру.Рекомендация (по желанию): задать минимальный размер/выравнивание контейнера, чтобы OptionButton не прыгал при разных локалях:
- <Control Name="TitleContent" Margin="5 0" /> + <Control Name="TitleContent" Margin="5 0" MinSize="120 0" VerticalAlignment="Center" />Content.Server/GameTicking/GameTicker.Spawning.cs (1)
234-245: Гард на CVAR (когда фича выключена)
По описанию PR, приalternate_job_titles_enable = falseальтернативы не должны применяться. В этом месте CVAR явно не проверяется; надеюсь, профиль при загрузке не содержит alt‑титулы. Лучше добавить явный гард.Вариант (если в классе уже есть IConfigurationManager):
+ if (_configurationManager.GetCVar(CCVars.ICAlternateJobTitlesEnable)) + { + // блок применения alt-title (с валидацией как выше) + }Если зависимости нет — ок, но убедитесь тестами, что при выключенной фиче
jobNameвсегда берётся изjobPrototype.LocalizedName.Content.Server/Access/Systems/IdCardSystem.cs (1)
97-116: API для смены должности на ID‑карте — ок; мелкие нитыЛогика корректна: обновляете ключ и локализованную строку, помечаете компонент грязным, возвращаете статус.
- Приведите XML‑комментарии к единому языку проекта (в основном англ.), чтобы не смешивать RU/EN.
- При необходимости аудит‑лога (если этот метод будет вызываться админками/консолями) — добавьте необязательный лог, но это уже за рамками PR.
- /// Ganimed-JobAlt - /// Изменяет должность на карте, обновляя локализованный ключ и строку отображения. + /// Ganimed-JobAlt + /// Changes the job title on an ID card, updating both the loc key and the localized display string.Content.Server/Access/Components/PresetIdCardComponent.cs (1)
15-17: Поле alternateTitle добавлено корректно; стоит упростить отладку через VVРекомендую пометить поле для просмотра в админ-панели/VV, чтобы быстро диагностировать несоответствия прототипов на сервере.
- [DataField("alternateTitle")] - public ProtoId<JobAlternateTitlePrototype>? AlternateTitleId; + [ViewVariables(VVAccess.ReadOnly)] + [DataField("alternateTitle")] + public ProtoId<JobAlternateTitlePrototype>? AlternateTitleId;Content.Server/StationRecords/Systems/StationRecordsSystem.cs (1)
161-179: Избежать дублирования логики с внешним репозиториемС учётом замечания ревьюера о наличии аналогичной реализации в ADT, имеет смысл вынести согласованный порядок разрешения титула (ID‑карта → профиль.alt для job → дефолт из JobPrototype) в общий helper/систему, чтобы не расходились поведения и локализации в разных репозиториях.
Content.Server/Station/Systems/StationSpawningSystem.cs (1)
259-260: Исправьте форматирование региона.Комментарии региона должны быть выровнены правильно для лучшей читаемости.
- - #endregion Player spawning helpers + #endregion Player spawning helpersContent.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs (1)
107-111: Избыточное копирование HashSet в List.Создание нового
ListизHashSetкажется избыточным, так как можно работать напрямую сHashSetили преобразовать его в список при необходимости.- _altTitles = new List<ProtoId<JobAlternateTitlePrototype>>(); - foreach (var entry in altTitles) - { - _altTitles.Add(entry); - } + _altTitles = altTitles.ToList();Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (1)
1091-1098: Подписываться на OnSelectedTitle только при включённой фиче.Сейчас обработчик вешается всегда, но ранний
if (!altJobTitlesEnable) return;в лямбде спасает. Чуть чище — подписывать внутри веткиif (altJobTitlesEnable).- selector.OnSelectedTitle += selectedTitle => - { - if (!altJobTitlesEnable) - return; - Profile = Profile?.WithJobAltTitle(job.ID, selectedTitle); - SetDirty(); - }; + if (altJobTitlesEnable) + { + selector.OnSelectedTitle += selectedTitle => + { + Profile = Profile?.WithJobAltTitle(job.ID, selectedTitle); + SetDirty(); + }; + }Content.Server/Database/ServerDbBase.cs (1)
239-249: Чтение AltTitles: устойчивость к дубликатам и мусорным данным.Текущее
altTitles.Add(...)кинет исключение при повторяющемсяRoleName. Рекомендую безопасно перетирать либо предварительно фильтровать пустые значения.- foreach (var role in profile.AltTitles) - { - altTitles.Add( - new ProtoId<JobPrototype>(role.RoleName), - new ProtoId<JobAlternateTitlePrototype>(role.AlternateTitle) - ); - } + foreach (var role in profile.AltTitles) + { + if (string.IsNullOrWhiteSpace(role.RoleName) || string.IsNullOrWhiteSpace(role.AlternateTitle)) + continue; + // перезаписываем последние значения без исключений + altTitles[new ProtoId<JobPrototype>(role.RoleName)] = + new ProtoId<JobAlternateTitlePrototype>(role.AlternateTitle); + }По желанию: дополнительно валидировать через
TryIndexи отбрасывать несуществующие прототипы.Content.Shared/Preferences/HumanoidCharacterProfile.cs (3)
49-55: Публичный изменяемый словарь нарушает «псевдо‑иммутабельность» профиля.Сейчас
JobAlternateTitles—publicи изменяемый. Рекомендую инкапсулировать поле и экспонироватьIReadOnlyDictionary, изменения — только черезWithJobAltTitle.- [DataField] - public Dictionary<ProtoId<JobPrototype>, ProtoId<JobAlternateTitlePrototype>> JobAlternateTitles = new(); + [DataField] + private Dictionary<ProtoId<JobPrototype>, ProtoId<JobAlternateTitlePrototype>> _jobAlternateTitles = new(); + public IReadOnlyDictionary<ProtoId<JobPrototype>, ProtoId<JobAlternateTitlePrototype>> JobAlternateTitles => _jobAlternateTitles;И далее скорректировать конструктор/копирование/EnsureValid/WithJobAltTitle на работу с
_jobAlternateTitles.
56-58: Поле AlternateJobTitle не используется. Удалить либо пояснить назначение.Есть риск путаницы из‑за параллельного механизма. Если цель — только пер‑job‑карта, строковое поле лучше убрать.
- [DataField("alternateJobTitle")] - public string? AlternateJobTitle { get; set; } + // Удалено: пер‑job карта JobAlternateTitles заменяет это поле.
585-587: SequenceEqual на словаре может флапать.
Dictionaryне гарантирует порядок перечисления, так чтоSequenceEqualпотенциально даст ложное неравенство. Для устойчивости сравнивайте по ключам/значениям независимо от порядка.if (!JobAlternateTitles.Count.Equals(other.JobAlternateTitles.Count) || JobAlternateTitles.Any(kv => !other.JobAlternateTitles.TryGetValue(kv.Key, out var v) || v != kv.Value)) return false;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (23)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs(3 hunks)Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml(1 hunks)Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs(4 hunks)Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.Designer.cs(1 hunks)Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.cs(1 hunks)Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.Designer.cs(1 hunks)Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.cs(1 hunks)Content.Server.Database/Model.cs(2 hunks)Content.Server/Access/Components/PresetIdCardComponent.cs(1 hunks)Content.Server/Access/Systems/IdCardSystem.cs(1 hunks)Content.Server/Access/Systems/PresetIdCardSystem.cs(1 hunks)Content.Server/Database/ServerDbBase.cs(3 hunks)Content.Server/GameTicking/GameTicker.Spawning.cs(2 hunks)Content.Server/Mind/MindSystem.cs(1 hunks)Content.Server/Station/Systems/StationSpawningSystem.cs(4 hunks)Content.Server/StationRecords/Systems/StationRecordsSystem.cs(2 hunks)Content.Shared/CCVar/CCVars.cs(1 hunks)Content.Shared/Preferences/HumanoidCharacterProfile.cs(9 hunks)Content.Shared/Roles/JobAlternateTitlePrototype.cs(1 hunks)Content.Shared/Roles/JobPrototype.cs(1 hunks)Content.Shared/Roles/Jobs/JobRoleComponent.cs(0 hunks)Resources/Locale/ru-RU/_Ganimed/job/job-alt-names.ftl(1 hunks)Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml(2 hunks)
💤 Files with no reviewable changes (1)
- Content.Shared/Roles/Jobs/JobRoleComponent.cs
🧰 Additional context used
🧬 Code graph analysis (12)
Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.cs (1)
Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.cs (3)
AltJobTitle(8-21)Up(11-14)Down(17-20)
Content.Shared/Roles/JobAlternateTitlePrototype.cs (1)
Content.Shared/Roles/JobPrototype.cs (1)
Prototype(13-170)
Content.Server.Database/Model.cs (1)
Content.Server/Database/ServerDbBase.cs (1)
Profile(313-425)
Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.cs (1)
Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.cs (3)
AltJobTitle(8-21)Up(11-14)Down(17-20)
Content.Server/Access/Systems/IdCardSystem.cs (1)
Content.Server/Station/Systems/StationSpawningSystem.cs (2)
EntityUid(70-81)EntityUid(96-202)
Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.Designer.cs (2)
Content.Server.Database/Model.cs (1)
Server(783-795)Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.cs (1)
AltJobTitle(8-21)
Content.Server/Station/Systems/StationSpawningSystem.cs (1)
Content.Server/Access/Systems/IdCardSystem.cs (1)
TryChangeJobTitle(105-116)
Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.Designer.cs (2)
Content.Server.Database/Model.cs (1)
Server(783-795)Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.cs (1)
AltJobTitle(8-21)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (2)
Content.Server/Database/ServerDbBase.cs (1)
Profile(313-425)Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs (1)
Setup(81-164)
Content.Shared/Preferences/HumanoidCharacterProfile.cs (1)
Content.Server/Database/ServerDbBase.cs (1)
HumanoidCharacterProfile(200-311)
Content.Server/Access/Systems/PresetIdCardSystem.cs (2)
Content.Server/Station/Systems/StationSpawningSystem.cs (2)
EntityUid(70-81)EntityUid(96-202)Content.Server/Access/Systems/IdCardSystem.cs (1)
TryChangeJobTitle(105-116)
Content.Server/Database/ServerDbBase.cs (1)
Content.Server.Database/Model.cs (1)
DBJobAlternateTitle(508-517)
🔇 Additional comments (19)
Content.Shared/CCVar/CCVars.cs (1)
48-54: CVar для фичефлага — ок; семантика и флаги корректные
SERVER | REPLICATEDподходит: сервер — источник истины, клиенту нужен визуальный флаг. Название и описание понятны.Content.Server/GameTicking/GameTicker.Spawning.cs (1)
254-254: Добавление роли майнду — ок
Вызов с передачейjobPrototype: jobIdоставлен без изменений по смыслу. Совместимо с логикой выше (имя для анонсов/логов берётся изjobName).Content.Server/StationRecords/Systems/StationRecordsSystem.cs (1)
149-153: Проверка существующей записи — окЛаконичная проверка через pattern matching; поведение без изменений.
Content.Server/Access/Systems/PresetIdCardSystem.cs (2)
65-66: Имя на карте задаётся условно — окБезопасно избегает лишних вызовов при отсутствии предустановленного имени.
74-78: Валидация прототипа работы — окРанняя проверка и логирование ошибки оставляют карту в согласованном состоянии.
Content.Server/Station/Systems/StationSpawningSystem.cs (3)
180-184: Корректная реализация загрузки альтернативных должностей.Логика получения альтернативной должности из профиля персонажа реализована правильно. Код проверяет наличие альтернативной должности в словаре
JobAlternateTitlesи корректно индексирует прототип.
189-189: Убедитесь в корректной передаче параметраaltTitle.Изменение вызова
SetPdaAndIdCardDataс добавлением параметраaltTitleвыглядит корректно и соответствует обновленной сигнатуре метода.
222-241: Корректная обработка альтернативных должностей в ID-карте.Логика установки названия должности в ID-карте правильно учитывает приоритет: если задана альтернативная должность, используется её локализованное название, иначе - стандартное из прототипа работы.
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs (3)
24-25: Добавлены необходимые поля для поддержки альтернативных должностей.Добавление поля
_altTitlesдля хранения списка альтернативных должностей корректно.
27-27: Корректное добавление события для выбора альтернативной должности.Событие
OnSelectedTitleправильно определено для передачи выбранной альтернативной должности.
137-144: Проверить корректность вычисления индекса для_altTitles[args.Id - 1]. При отсутствии defaultAltTitle смещение позиций может измениться и привести к выходу за пределы списка или неверному элементу.Content.Server.Database/Migrations/Sqlite/20241212015023_AltJobTitle.Designer.cs (2)
1-1903: Designer-файл миграции выглядит корректно.Это автоматически сгенерированный файл Entity Framework для SQLite миграции. Он содержит полное определение модели базы данных после применения миграции AltJobTitle. Структура соответствует аналогичному файлу для PostgreSQL.
12-14: Миграции AltJobTitle для SQLite и PostgreSQL идентичны. Up/Down методы в обеих миграциях пустые и совпадают.Content.Server/Database/ServerDbBase.cs (2)
361-373: Сериализация AltTitles в БД — ок.Очистка и пересборка коллекции понятна и консистентна с остальными частями профиля.
48-66: Добавить Include для AltTitles в GetPlayerPreferencesAsyncNavigation-свойство Profile.AltTitles не загружается, из-за чего ConvertProfiles всегда получает пустой список. Вставьте сразу после Include(h => h.Traits):
.Include(p => p.Profiles).ThenInclude(h => h.Traits) +.Include(p => p.Profiles).ThenInclude(h => h.AltTitles)Уточнить продуктовое ожидание и учесть возможные изменения миграций.
Content.Shared/Preferences/HumanoidCharacterProfile.cs (2)
720-734: Валидация alt‑титулов — хорошо.Фильтрация по существующим прототипам и принадлежности к
job.AlternateTitlesустраняет мусор из БД/клиента.
885-904: GetHashCode: ок, но учитывайте инкапсуляцию.Добавление словаря в хэш корректно; если перейдёте на приватное поле, обновите ссылку на
_jobAlternateTitles.Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.Designer.cs (2)
125-129: Индекс GIN по tsvector на admin_log.message — всё корректно.Настройка
Npgsql:TsVectorConfig = english+ методGINвыглядит верной и даёт быстрый полнотекстовый поиск по сообщениям.Also applies to: 133-134
662-665: Фильтрованный уникальный индексIX_job_one_high_priority— принято.Ограничение уникальности только для
priority = 3соответствует ожидаемой инварианте «ровно один high-priority на профиль».
| .HasAnnotation("ProductVersion", "8.0.0") | ||
| .HasAnnotation("Relational:MaxIdentifierLength", 63); |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Проверьте согласованность версий EF Core/Npgsql (ProductVersion = 8.0.0).
Снимок зафиксирован под EF Core 8.0.0. Убедитесь, что пакеты Microsoft.EntityFrameworkCore и Npgsql.EntityFrameworkCore.PostgreSQL в решении закреплены на совместимых 8.x и одинаковы для всех проектов, иначе возможны расхождения снимков/миграций.
Запустить для проверки:
🏁 Script executed:
#!/bin/bash
fd -a -g '**/*.csproj' -x rg -nP 'PackageReference Include="(Microsoft\.EntityFrameworkCore[^"]*|Npgsql\.EntityFrameworkCore\.PostgreSQL)"\s+Version="([^"]+)"' {}Length of output: 166
🏁 Script executed:
#!/bin/bash
# Поиск версий EF Core и Npgsql в файлах .csproj
rg -nP '<PackageReference Include="(Microsoft\.EntityFrameworkCore[^"]*|Npgsql\.EntityFrameworkCore\.PostgreSQL)"\s*Version="([^"]+)"' -g '*.csproj'Length of output: 150
🏁 Script executed:
#!/bin/bash
# Ищем упоминания EF Core и Npgsql в .csproj без указания версии
rg -n '<PackageReference Include="Microsoft.EntityFrameworkCore' -g '*.csproj' || true
rg -n '<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL' -g '*.csproj' || true
# Ищем центральный файл управления версиями
files=$(fd -g 'Directory.Packages.props')
for file in $files; do
echo "=== $file ==="
rg -n '<PackageVersion Include="Microsoft.EntityFrameworkCore' "$file" || true
rg -n '<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL' "$file" || true
doneLength of output: 1185
Пересгенерируйте миграцию под EF Core 9.x и синхронизируйте версии пакетов
В Directory.Packages.props указаны Microsoft.EntityFrameworkCore.Design/Sqlite.Core @ 9.0.1 и Npgsql.EntityFrameworkCore.PostgreSQL @ 9.0.3, тогда как в файле миграции стоит ProductVersion = 8.0.0. Обновите аннотации или пересоздайте миграцию (Content.Server.Database/Migrations/Postgres/20241212015038_AltJobTitle.Designer.cs:25-26) под актуальные версии.
…ьтернативных должностей в профль пользователя
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
Content.Server.Database/Model.cs (1)
133-143: Ужесточить уникальный индекс на (ProfileId, RoleName)В ConvertProfiles (ServerDbBase.cs строки 247–250) бизнес-логика группирует записи по RoleName и выбирает единственный AlternateTitle, значит на уровне БД должно быть не более одного титула на роль для одного профиля. В Model.cs (строка 141) замените индекс:
- modelBuilder.Entity<DBJobAlternateTitle>() - .HasIndex(p => new { p.ProfileId, p.RoleName, p.AlternateTitle }) - .IsUnique(); + modelBuilder.Entity<DBJobAlternateTitle>() + .HasIndex(p => new { p.ProfileId, p.RoleName }) + .IsUnique();Перегенерируйте или вручную обновите миграции для PostgreSQL и SQLite.
🧹 Nitpick comments (5)
Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (1)
17-22: Небольшая придирка: явная семантика полей-идентификаторовПоля
role_name/alternate_titleфактически хранят ProtoId. Чтобы снизить риск путаницы и регистрозависимости, можно в будущем переименовать колонки вjob_id/alt_title_idи договориться о нормализации регистра на уровне приложения. Не blocker.Content.Server/Database/ServerDbBase.cs (1)
363-375: Сохранение через Clear()+Add — корректно, но шумно по БДТекущий подход приведёт к DELETE/INSERT даже при отсутствии изменений. Можно микрооптимизировать: вычислять дельту (удалять/добавлять только изменившиеся пары). Это снизит churn и потенциальные блокировки на горячих профилях. Не blocker.
Пример дельты:
- profile.AltTitles.Clear(); - foreach (var (role, title) in humanoid.JobAlternateTitles) - { - var newTitle = new DBJobAlternateTitle { RoleName = role.Id, AlternateTitle = title.Id }; - profile.AltTitles.Add(newTitle); - } + var desired = humanoid.JobAlternateTitles.ToDictionary(kv => kv.Key.Id, kv => kv.Value.Id); + var existing = profile.AltTitles.ToDictionary(a => a.RoleName, a => a); + + // удаляем лишние + foreach (var a in profile.AltTitles.Where(a => !desired.ContainsKey(a.RoleName)).ToArray()) + profile.AltTitles.Remove(a); + + // добавляем/обновляем + foreach (var (roleId, altId) in desired) + { + if (existing.TryGetValue(roleId, out var e)) + e.AlternateTitle = altId; + else + profile.AltTitles.Add(new DBJobAlternateTitle { RoleName = roleId, AlternateTitle = altId }); + }Content.Server.Database/Model.cs (1)
519-530: Нейминг и ограничения длин — необязательные, но улучшающие читаемость/надёжность
- Рассмотрите переименование AlternateTitle → AlternateTitleId (мы храним именно ID прототипа, а не локализованное имя).
- Необязательно, но полезно задать MaxLength для RoleName/AlternateTitle, чтобы защититься от «случайно длинных» значений.
public class DBJobAlternateTitle { public int Id { get; set; } public Profile Profile { get; set; } = null!; public int ProfileId { get; set; } - public string RoleName { get; set; } = string.Empty; + [MaxLength(64)] + public string RoleName { get; set; } = string.Empty; - public string AlternateTitle { get; set; } = string.Empty; + [MaxLength(64)] + public string AlternateTitle { get; set; } = string.Empty; // или AlternateTitleId }Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs (1)
636-665: Новая таблица dbjob_alternate_title — уточнить семантику и ввести валидацию.
- Подтвердите, что AlternateTitle хранит ProtoId JobAlternateTitlePrototype, а не локализованный текст.
- Опционально: переименовать колонку в alt_title_proto_id для ясности; добавить CHECK на непустое значение.
Пример CHECK (SQLite):
migrationBuilder.Sql(""" CREATE TEMP TRIGGER IF NOT EXISTS validate_dbjob_alt_title_ins BEFORE INSERT ON dbjob_alternate_title FOR EACH ROW BEGIN SELECT CASE WHEN length(trim(NEW.alternate_title))=0 THEN RAISE(ABORT,'alternate_title empty') END; END; """);Уникальный индекс (ProfileId, RoleName, AlternateTitle) выглядит корректно и покрывает выборки по ProfileId.
Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs (1)
674-705: DBJobAlternateTitle (Postgres) — уточнения по данным и ограничениям.
- Подтвердите хранение ProtoId в AlternateTitle; если так — можно переименовать в alt_title_proto_id.
- Рассмотрите CHECK на непустые значения; при необходимости — citext/коллации, если ожидается регистронезависимость.
Пример CHECK (Postgres):
migrationBuilder.Sql(""" ALTER TABLE dbjob_alternate_title ADD CONSTRAINT ck_dbjob_alt_title_nonempty CHECK (length(btrim(alternate_title)) > 0 AND length(btrim(role_name)) > 0); """);Индекс (ProfileId, RoleName, AlternateTitle) — ОК.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.Designer.cs(1 hunks)Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs(1 hunks)Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs(6 hunks)Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.Designer.cs(1 hunks)Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs(1 hunks)Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs(6 hunks)Content.Server.Database/Model.cs(4 hunks)Content.Server/Database/ServerDbBase.cs(5 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (2)
Content.Server.Database/Model.cs (3)
Server(795-807)DBJobAlternateTitle(520-529)PrimaryKey(1345-1354)Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (3)
DBJobAlternateTitle(9-48)Up(12-40)Down(43-47)
Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (2)
Content.Server.Database/Model.cs (3)
Server(795-807)DBJobAlternateTitle(520-529)PrimaryKey(1345-1354)Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (3)
DBJobAlternateTitle(8-47)Up(11-39)Down(42-46)
Content.Server.Database/Model.cs (2)
Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(9-48)Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(8-47)
Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.Designer.cs (3)
Content.Server.Database/Model.cs (2)
Server(795-807)DBJobAlternateTitle(520-529)Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(9-48)Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(8-47)
Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.Designer.cs (2)
Content.Server.Database/Model.cs (2)
Server(795-807)DBJobAlternateTitle(520-529)Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(9-48)
Content.Server/Database/ServerDbBase.cs (3)
Content.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(9-48)Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (1)
DBJobAlternateTitle(8-47)Content.Server.Database/Model.cs (1)
DBJobAlternateTitle(520-529)
🔇 Additional comments (17)
Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.cs (2)
35-38: Уточнить уникальность: одна запись на роль для профиляСейчас уникальный индекс включает
alternate_title, тем самым позволяя хранить несколько альтернативных титулов на одну и ту же роль в одном профиле. В рантайме вы затем вынуждены выбирать «последнюю по Id», что делает поведение зависящим от порядка вставок и может флапать при гонках/миграциях.Рекомендую зафиксировать инвариант «ровно одно выбранное альтернативное название на роль и профиль» индексом
(profile_id, role_name)и упростить загрузку (безOrderByDescending(Id)).[ suggest_essential_refactor ]
Пример правки миграции:
- migrationBuilder.CreateIndex( - name: "IX_dbjob_alternate_title_profile_id_role_name_alternate_title", - table: "dbjob_alternate_title", - columns: new[] { "profile_id", "role_name", "alternate_title" }, - unique: true); + migrationBuilder.CreateIndex( + name: "IX_dbjob_alternate_title_profile_id_role_name", + table: "dbjob_alternate_title", + columns: new[] { "profile_id", "role_name" }, + unique: true);
25-32: FK с каскадным удалением — окСвязь с
profile(profile_id)и каскадное удаление корректны для «дочерних» записей альтернативных титулов. Замечаний нет.Content.Server/Database/ServerDbBase.cs (4)
53-54: Загрузка AltTitles при логине — окПодключение
AltTitlesв Include гарантирует наличие данных в профиле. Так и нужно.
112-113: Трекинг AltTitles при сохранении — ок
Include(p => p.AltTitles)обязателен, чтобыClear()действительно привёл к DELETE существующих строк. Исполнено корректно.
283-312: Актуализировать описание PR: сейчас альтернативные титулы сохраняются в БДВ описании PR сказано, что выбор не сохраняется между сессиями. По коду — сохраняется (миграции + сериализация/десериализация). Чтобы не вводить тестирующих в заблуждение, обновите комментарий в PR.
303-305: Все вызовы конструктора HumanoidCharacterProfile синхронизированы с новой сигнатуройContent.Server.Database/Migrations/Postgres/20250904114605_DBJobAlternateTitle.cs (2)
35-39: Схема уникальности аналогично SQLite: закрепить одну запись на рольТа же рекомендация: сделать уникальный индекс по
(profile_id, role_name)вместо тройного, чтобы не допускать дублирования и убрать потребность выбирать «последнюю по Id».[ suggest_essential_refactor ]
Правка миграции:
- migrationBuilder.CreateIndex( - name: "IX_dbjob_alternate_title_profile_id_role_name_alternate_title", - table: "dbjob_alternate_title", - columns: new[] { "profile_id", "role_name", "alternate_title" }, - unique: true); + migrationBuilder.CreateIndex( + name: "IX_dbjob_alternate_title_profile_id_role_name", + table: "dbjob_alternate_title", + columns: new[] { "profile_id", "role_name" }, + unique: true);
26-33: FK и стратегия идентичности — окПервичный ключ и
IdentityByDefaultдля PostgreSQL выставлены правильно, FK наprofileс каскадом — ожидаемо. Замечаний нет.Content.Server.Database/Model.cs (2)
51-51: DbSet зарегистрирован корректноDbSet для DBJobAlternateTitle добавлен в контекст; соответствующие миграции присутствуют. Замечаний нет.
471-471: Навигация ок, согласована со стилем файлаКоллекция AltTitles оформлена как навигация с get-only и инициализацией по умолчанию — это соответствует остальным навигациям (Jobs, Traits и т.д.) и корректно трекается EF Core.
Content.Server.Database/Migrations/Sqlite/20250904114556_DBJobAlternateTitle.Designer.cs (1)
639-667: The requested verification is in progress.Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs (3)
1864-1874: Каскадное удаление AltTitles при удалении Profile — ОК.
2283-2284: Навигация Profile.AltTitles — ОК, соответствует новой связи.
18-18: Проверьте таргет-фреймворки
EF Core-пакеты централизованно заданы в Directory.Packages.props (Design 9.0.1, Sqlite.Core 9.0.1, Npgsql 9.0.3) — все 9.x; убедитесь, чтоTargetFramework(используемый через$(TargetFramework)) при сборке выставлен в net8.0 или net9.0.Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs (3)
23-23: EF Core 9.0.1 (Postgres) — проверьте выравнивание версий.Убедитесь, что Npgsql.EntityFrameworkCore.PostgreSQL = 9.x и остальные EF-пакеты/TFM согласованы во всех проектах.
Используйте тот же скрипт из комментария к SQLite-снапшоту.
1952-1962: Связь DBJobAlternateTitle → Profile с каскадом — корректно.
2371-2372: Навигация Profile.AltTitles — соответствует модели, без замечаний.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs (2)
122-133: Сообщайте исходный выбор через событие.Сейчас
OnSelectedTitleвызывается только при клике. Стоит один раз уведомить подписчиков после установки дефолтного выбора.- if (defaultAltTitle != null) - { - var index = _altTitles.FindIndex(x => x == defaultAltTitle); - if (index >= 0) - titleOptions.SelectId(index + 1); - else - titleOptions.SelectId(0); - } - else - { - titleOptions.SelectId(0); - } + var selectedId = 0; + if (defaultAltTitle != null) + { + var index = _altTitles.FindIndex(x => x.Equals(defaultAltTitle)); + if (index >= 0) + selectedId = index + 1; // +1, т.к. 0 — основной титул + } + titleOptions.SelectId(selectedId); + OnSelectedTitle?.Invoke(selectedId == 0 ? null : _altTitles[selectedId - 1]);
144-152: Фолбэк без прототипов: тоже генерируйте событие по умолчанию.Когда нет
protoMan/altTitles, имеет смысл явно сообщитьnull, чтобы подписчики не зависели от факта клика.var titleLabel = new Label { ToolTip = description, Text = title }; TitleContent.AddChild(titleLabel); + OnSelectedTitle?.Invoke(null);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs(5 hunks)
🔇 Additional comments (2)
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs (2)
135-142: Маппинг индексов OptionButton к _altTitles корректен.Смещение на 1 относительно базового титула верное, обработка
Id == 0какnull— ожидаемое поведение.
113-114: Проверьте локализацию аргументаtitleпри вызове Setup в Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs:113–114. Убедитесь, что вызывающая сторона передаёт уже локализованную строку или оберните её вLoc.GetString(...).
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
Content.Server/Access/Systems/PresetIdCardSystem.cs (1)
82-118: Не перезаписываем тайтл, уважаем CVAR и передаём LocId, а не локализованную строкуСейчас:
- титул на ID-карте перезаписывается даже если уже выставлен раньше (например, StationSpawningSystem), из‑за чего теряется выбранный пользователем альт‑титул;
- в TryChangeJobTitle передаётся LocalizedName (строка), тогда как API ожидает LocId (ключ локализации) — это приведёт к некорректной локализации/поиску ключа;
- нет учёта флага CCVar, отключающего фичу.
Предлагаю:
- не трогать титул, если card.LocalizedJobTitle уже непустой;
- оборачивать выбор альтернативных тайтлов в проверку CCVars.ICAlternateJobTitlesEnable;
- передавать в TryChangeJobTitle altTitle.Name / job.Name (LocId), а не LocalizedName.
Применить внутри данного диапазона:
- // Ganimed-JobAlt-start - if (!TryComp<IdCardComponent>(uid, out var card)) - { - Log.Warning($"Entity {uid} does not have IdCardComponent, skipping title setup."); - return; - } - - string? titleToSet = null; - - if (id.AlternateTitleId != null && - _prototypeManager.TryIndex(id.AlternateTitleId.Value, out JobAlternateTitlePrototype? altTitle)) - { - titleToSet = altTitle.LocalizedName; - } - else if (job.AlternateTitles != null && job.AlternateTitles.Count > 0) - { - JobAlternateTitlePrototype? altFromJob = null; - foreach (var altId in job.AlternateTitles) - { - if (_prototypeManager.TryIndex(altId, out var proto)) - { - altFromJob = proto; - break; - } - } - - titleToSet = altFromJob?.LocalizedName ?? job.LocalizedName; - } - else - { - titleToSet = job.LocalizedName; - } - - if (!string.IsNullOrEmpty(titleToSet)) - _cardSystem.TryChangeJobTitle(uid, titleToSet); - // Ganimed-JobAlt-end + // Ganimed-JobAlt-start + if (!TryComp<IdCardComponent>(uid, out var card)) + { + Log.Warning($"Entity {uid} does not have IdCardComponent, skipping title setup."); + return; + } + + // Не перезаписываем уже заданный (например, персонализацией/спавном) титул + if (string.IsNullOrEmpty(card.LocalizedJobTitle)) + { + var useAltTitles = _cfg.GetCVar(CCVars.ICAlternateJobTitlesEnable); + var titleSet = false; + + if (useAltTitles + && id.AlternateTitleId != null + && _prototypeManager.TryIndex(id.AlternateTitleId.Value, out JobAlternateTitlePrototype? altTitle)) + { + _cardSystem.TryChangeJobTitle(uid, altTitle.Name); + titleSet = true; + } + else if (useAltTitles && job.AlternateTitles != null && job.AlternateTitles.Count > 0) + { + foreach (var altId in job.AlternateTitles) + { + if (_prototypeManager.TryIndex(altId, out JobAlternateTitlePrototype? proto)) + { + _cardSystem.TryChangeJobTitle(uid, proto.Name); + titleSet = true; + break; + } + } + } + + if (!titleSet) + _cardSystem.TryChangeJobTitle(uid, job.Name); + } + // Ganimed-JobAlt-endДополнительно (вне диапазона) добавьте зависимости и using:
// using-и вверху файла using Content.Shared.CCVar; using Robust.Shared.Configuration; // поле в классе PresetIdCardSystem [Dependency] private readonly IConfigurationManager _cfg = default!;Проверьте также YAML прототипы ID-карт: если вы рассчитываете на preset-альт‑титулы, у PresetIdCardComponent должны быть alternateTitle (AlternateTitleId).
Для самопроверки, найдите все неправильные вызовы TryChangeJobTitle с LocalizedName:
#!/bin/bash rg -n -C2 -P 'TryChangeJobTitle\([^,]+,\s*[A-Za-z_][A-Za-z0-9_]*\.LocalizedName' --type cs
🧹 Nitpick comments (2)
Content.Server/Access/Systems/PresetIdCardSystem.cs (2)
65-67: Не перезаписывать пустое имя и избегать лишних вызововЛучше проверять на пустые/пробельные строки, чтобы не дёргать систему карт без необходимости.
- if (id.IdName != null) // Ganimed-JobAlt + if (!string.IsNullOrWhiteSpace(id.IdName)) // Ganimed-JobAlt _cardSystem.TryChangeFullName(uid, id.IdName);
121-122: Проверка и индексация иконки должностиЕсли job.Icon — ProtoId?, проверка через string.IsNullOrEmpty некорректна. Лучше явная проверка на null.
- if (!string.IsNullOrEmpty(job.Icon) && _prototypeManager.TryIndex(job.Icon, out var jobIcon)) // Ganimed-JobAlt + if (job.Icon != null && _prototypeManager.TryIndex(job.Icon, out var jobIcon)) // Ganimed-JobAlt _cardSystem.TryChangeJobIcon(uid, jobIcon);Если job.Icon — всё-таки string, оставьте текущую проверку. Уточните тип и поправьте соответствующим образом.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
Content.Server/Access/Systems/PresetIdCardSystem.cs(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CrimeMoot
PR: CrimeMoot/Ganimed14#0
File: :0-0
Timestamp: 2025-09-05T09:59:35.975Z
Learning: В системе альтернативных названий должностей Ganimed, если альтернативные титулы работают в манифестах и объявлениях при спавне, но не отображаются на ID картах, проблема в том, что PresetIdCardSystem перезаписывает титулы после StationSpawningSystem. Нужно добавлять поле alternateTitle в PresetIdCard компоненты прототипов ID карт.
Learnt from: CrimeMoot
PR: CrimeMoot/Ganimed14#0
File: :0-0
Timestamp: 2025-09-05T10:11:18.316Z
Learning: В системе альтернативных названий должностей Ganimed, проблема с отображением альтернативных титулов на ID картах решается добавлением поля alternateTitle в PresetIdCard компонент прототипа ID карты. Например, для CargoIDCard нужно добавить alternateTitle: CargoTechnitianTitleWtf в PresetIdCard компонент в файле identification_cards.yml.
📚 Learning: 2025-09-05T09:59:35.975Z
Learnt from: CrimeMoot
PR: CrimeMoot/Ganimed14#0
File: :0-0
Timestamp: 2025-09-05T09:59:35.975Z
Learning: В системе альтернативных названий должностей Ganimed, если альтернативные титулы работают в манифестах и объявлениях при спавне, но не отображаются на ID картах, проблема в том, что PresetIdCardSystem перезаписывает титулы после StationSpawningSystem. Нужно добавлять поле alternateTitle в PresetIdCard компоненты прототипов ID карт.
Applied to files:
Content.Server/Access/Systems/PresetIdCardSystem.cs
📚 Learning: 2025-09-05T10:11:18.316Z
Learnt from: CrimeMoot
PR: CrimeMoot/Ganimed14#0
File: :0-0
Timestamp: 2025-09-05T10:11:18.316Z
Learning: В системе альтернативных названий должностей Ganimed, проблема с отображением альтернативных титулов на ID картах решается добавлением поля alternateTitle в PresetIdCard компонент прототипа ID карты. Например, для CargoIDCard нужно добавить alternateTitle: CargoTechnitianTitleWtf в PresetIdCard компонент в файле identification_cards.yml.
Applied to files:
Content.Server/Access/Systems/PresetIdCardSystem.cs
🧬 Code graph analysis (1)
Content.Server/Access/Systems/PresetIdCardSystem.cs (2)
Content.Server/Station/Systems/StationSpawningSystem.cs (2)
EntityUid(70-81)EntityUid(96-202)Content.Server/Access/Systems/IdCardSystem.cs (1)
TryChangeJobTitle(105-116)
🔇 Additional comments (1)
Content.Server/Access/Systems/PresetIdCardSystem.cs (1)
74-79: Индексация JobPrototype — окПолучение прототипа должности через TryIndex и обработка ошибки выглядят корректно.
Signed-off-by: CrimeMoot <wakeafa@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (1)
1045-1101: Учесть alternateTitle в PresetIdCardSystem и добавить его в прототипы ID-карт
- В методе SetupIdName (Content.Server/Access/Systems/PresetIdCardSystem.cs) добавить проверку
id.AlternateTitleIdи при наличии применять альтернативный титул вместо (или до) перезаписи черезid.IdName.- Во всех YAML-прототипах ID-карт (Resources/Prototypes/**/identification_cards.yml) в блоках
- type: PresetIdCardдля нужных ролей добавить полечтобы при спавне карты система подтягивала выбранный alt-титул.alternateTitle: <defaultAltTitle>
🧹 Nitpick comments (2)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (2)
1045-1045: Фича-флаг читается в нужном месте; можно чуть подсократить накладные расходы.Опционально: вынести
altJobTitlesEnableиз внутреннего цикла по вакансиям (вычислять один раз на категорию или до обоих циклов), т.к. значение CVar редко меняется на лету.
1094-1101: Лишний guard в обработчике выбора титула.Событие
OnSelectedTitleвозникает только когда alt‑титулы реально отрисованы (см.RequirementsSelector.Setup), поэтому проверкаif (!altJobTitlesEnable) return;избыточна.- selector.OnSelectedTitle += selectedTitle => - { - if (!altJobTitlesEnable) - return; - Profile = Profile?.WithJobAltTitle(job.ID, selectedTitle); - SetDirty(); - }; + selector.OnSelectedTitle += selectedTitle => + { + Profile = Profile?.WithJobAltTitle(job.ID, selectedTitle); + SetDirty(); + };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CrimeMoot
PR: CrimeMoot/Ganimed14#0
File: :0-0
Timestamp: 2025-09-05T09:59:35.975Z
Learning: В системе альтернативных названий должностей Ganimed, если альтернативные титулы работают в манифестах и объявлениях при спавне, но не отображаются на ID картах, проблема в том, что PresetIdCardSystem перезаписывает титулы после StationSpawningSystem. Нужно добавлять поле alternateTitle в PresetIdCard компоненты прототипов ID карт.
Learnt from: CrimeMoot
PR: CrimeMoot/Ganimed14#0
File: :0-0
Timestamp: 2025-09-05T10:11:18.316Z
Learning: В системе альтернативных названий должностей Ganimed, проблема с отображением альтернативных титулов на ID картах решается добавлением поля alternateTitle в PresetIdCard компонент прототипа ID карты. Например, для CargoIDCard нужно добавить alternateTitle: CargoTechnitianTitleWtf в PresetIdCard компонент в файле identification_cards.yml.
🧬 Code graph analysis (1)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (3)
Content.Server.Database/Model.cs (1)
Profile(444-480)Content.Server/Database/ServerDbBase.cs (1)
Profile(315-427)Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs (1)
Setup(81-163)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build (ubuntu-latest)
- GitHub Check: YAML map schema validator
- GitHub Check: YAML Linter
- GitHub Check: Test Packaging
🔇 Additional comments (1)
Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs (1)
1067-1067: Отрисовка иконки через Frame0() — ок.Замена на
jobIcon.Icon.Frame0()выглядит корректно и упрощает код.
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Signed-off-by: CrimeMoot <wakeafa@gmail.com>
|
Ожидаю возвращения к работе над PRом и реализации запрошенных мною изменений. |
|
@HyperB1 каких |
ПРИМЕЧАНИЕ: не обязательно делать именно так же отдельными эффектами как у лодаутов и ролей, но всё-же этот способ реализации не просто так используется в других местах. |
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Описание PR
Добавлена поддержка альтернативных должностей для профессий в системе спавна персонажей и в системе работы с ролями (JobSystem). Теперь у персонажей на ID-картах и PDA может отображаться альтернативное название должности, заданное в профиле персонажа через
JobAlternateTitlePrototype. Это позволяет гибко менять отображаемое название профессии, не меняя основного прототипа работы. Изначальный ресурс был взят сGaby-station, но переделан, для нормальной работы и реализации. Часть кода использует систему кода с PDA должностей от Времени Приключений.Также улучшено приветствие игрока при появлении роли (роль теперь отображается с альтернативным названием, если оно задано в ID-карте).
Технические детали
StationSpawningSystemпри спавне игрока теперь берётся альтернативное название должности из профиля персонажа (JobAlternateTitlePrototype), если оно есть, и устанавливается на ID-карту.JobSystemпри отправке приветствия игроку вместо стандартного имени работы используется название должности с ID-карты, если оно там задано.jobAlternateTitleдля альтернативных должностей.StartingGearEquippedEventсделана корректно (через ref).Медиа
Список изменений
🆑 CrimeMoot,
JobAlternateTitlePrototype. Их можно выбрать в процессе персонализации персонажа. Важно отметить, что выбранная должность не сохраняется после выхода из игры — при следующем входе её нужно будет выбрать заново. (Пока что синхронизация с базой данных не реализована, поэтому выбранный вариант сохраняется только на текущую сессию.)