Skip to content

Save shuttle#17

Draft
Schrodinger71 wants to merge 4 commits intomasterfrom
save-shuttle
Draft

Save shuttle#17
Schrodinger71 wants to merge 4 commits intomasterfrom
save-shuttle

Conversation

@Schrodinger71
Copy link
Collaborator

@Schrodinger71 Schrodinger71 commented Jan 23, 2026

О пулл-реквесте

Перенос отсюда https://github.com/Apxumekm0p/Eclipse-New-Frontier/pull/18/changes#diff-13e289574cbdac6cc3ef402f1de552c66b36d65474a4323f4a3f60ac5d0fdb0e

Технические детали

Нет

Изменения кода официальных разработчиков

Нет

Журнал изменений

🆑

  • add: Добавлено веселье!
  • remove: Удалено веселье!
  • tweak: Изменено веселье!
  • fix: Исправлено веселье!

@Schrodinger71 Schrodinger71 linked an issue Jan 23, 2026 that may be closed by this pull request
@Schrodinger71
Copy link
Collaborator Author

Schrodinger71 commented Jan 23, 2026

Content.Server/ParticleAccelerator/Components/ParticleAcceleratorControlBoxComponent.cs

Content.Shared/_Eclipse/SelfShipyard/BUI/SelfShipyardConsoleInterfaceState.cs

@coderabbitai
Copy link

coderabbitai bot commented Jan 23, 2026

Walkthrough

Этот pull request добавляет полную систему самостоятельного верфения (Self Shipyard) для игроков. На клиентской стороне реализованы UI-компоненты для консоли верфения, включая меню с отображением баланса, списком доступных кораблей и функциями покупки или продажи. На серверной стороне добавлена модель данных для хранения кораблей игроков, компонент SaveableShuttleComponent для маркировки сохраняемых кораблей, и система SelfShipyardSystem с логикой обработки покупок, продажи кораблей, управления дедом и взаимодействиями с банком. Также обновлена существующая система ShipyardSystem для интеграции с новыми компонентами, и удалены DataField атрибуты из двух старых компонентов.

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Title check ⚠️ Warning Название слишком общее и не отражает основные изменения в PR. 'Save shuttle' не раскрывает суть огромного набора изменений, включая новую UI, БД, системы и компоненты. Расширьте название, чтобы лучше отразить ключевые изменения: например, 'Add self-shipyard console UI and purchase/sell system' или 'Implement shipyard vessel purchase and persistence system'.
Docstring Coverage ⚠️ Warning Docstring coverage is 24.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive PR description содержит шаблон проекта с заполненными плейсхолдерами, но без конкретных технических деталей о реальных изменениях в коде. Заполните секцию 'Технические детали' конкретным описанием реализованной функциональности: что именно добавлено (механика сохранения шаттлов, UI, система покупки/продажи) и как это работает.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@Schrodinger71 Schrodinger71 marked this pull request as draft January 23, 2026 14:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In
`@Content.Client/_DH/SelfShipyard/BUI/SelfShipyardConsoleBoundUserInterface.cs`:
- Around line 1-9: The file imports and namespace use _Eclipse while the UI
types (SelfShipyardConsoleMenu, OwnedVesselRow) live under
Content.Client._DH.SelfShipyard.UI, causing missing-type build errors; update
the using directive to Content.Client._DH.SelfShipyard.UI and change the
namespace declaration for SelfShipyardConsoleBoundUserInterface to
Content.Client._DH.SelfShipyard.BUI (or switch the other files to _Eclipse if
that is the intended branch) so the types SelfShipyardConsoleMenu and
OwnedVesselRow resolve correctly.

In `@Content.Client/_DH/SelfShipyard/UI/SelfShipyardConsoleMenu.xaml.cs`:
- Around line 90-97: Заменить хардкод `"None"` на локализуемую строку: добавить
ключ в .ftl (например "selfshipyard-deed-none") и в
SelfShipyardConsoleMenu.xaml.cs использовать
Loc.GetString("selfshipyard-deed-none") при установке DeedTitle.Text (в блоке,
где проверяется state.ShipDeedTitle). Обновить ресурсный файл .ftl с переводимой
фразой и убедиться, что ключ уникален и соответствует неймингу проекта.

In `@Content.Server.Database/Model.cs`:
- Around line 1403-1418: Add the missing EF table attribute and fix the DH
marker: annotate the OwnedShuttles class with [Table("owned_shuttles")] (apply
System.ComponentModel.DataAnnotations.Schema) so EF uses snake_case, rename the
comment markers from "// DH-Start: Owned Shuttles" and any matching end marker
to "// DH-Tweak-Start" and "// DH-Tweak-End" (or use "// DH-Tweak: ..." for
single-line notes), then generate and verify a migration in
Content.Server.Database/Migrations/Postgres that creates the owned_shuttles
table (ensure the new migration and the snapshot include OwnedShuttles); if
missing, run the EF migration add command and commit the migration files.

In `@Content.Server/_DH/SelfShipyard/Systems/SelfShipyardSystem.Consoles.cs`:
- Around line 244-246: shuttleStation can be null which causes a NullReference
when calling _records.Synchronize(shuttleStation!.Value); update the logic in
SelfShipyardSystem.Consoles (the block that calls _records.Synchronize) to check
shuttleStation for null/HasValue before calling
_records.Synchronize(shuttleStation.Value) — either skip that call or
early-return if shuttleStation is missing; keep the existing
_records.Synchronize(station) behavior unchanged and reference the
shuttleStation variable and _records.Synchronize method when making the guard.
- Around line 238-242: The code writes a hard-coded "Captain" string into
records via _records.CreateGeneralRecord; replace that literal with a localized
string using Loc.GetString(...) and add the corresponding key to your .ftl file
(e.g. selfshipyard-record-position-captain) so the role shown to players is
localizable; update the CreateGeneralRecord call to pass
Loc.GetString("selfshipyard-record-position-captain") instead of "Captain" and
ensure any tests or documentation referencing the literal are updated
accordingly.

In `@Content.Server/_DH/SelfShipyard/Systems/SelfShipyardSystem.cs`:
- Around line 281-319: Сейчас DeleteStation(shuttleStationUid),
CleanGrid(shuttleUid, consoleUid) и _docking.UndockDocks(shuttleUid) выполняются
до проверки средств и успешного сохранения, что приводит к необратимым
изменениям при ошибках; измените поток так, чтобы эти мутации выполнялись только
после успешного TryBankWithdraw(player, bill) и успешного
_mapLoader.TrySaveGrid(shuttleUid, resPath) либо реализуйте явный откат: если вы
решаете сохранять раньше, захватите состояние до изменений и при ошибке
вызывайте _db.RemoveOwnedShuttle(id, ...), _bank.TryBankDeposit(player, bill) и
восстановите станцию/докинг/гриду (или повторно создавайте станцию через
_station.GetOwningStation/DeleteStation обратным действием), иначе просто
переместите вызовы DeleteStation, CleanGrid и _docking.UndockDocks в блок после
успешного AddOwnedShuttle и успешного _mapLoader.TrySaveGrid; используйте
идентификаторы функций DeleteStation, CleanGrid, _docking.UndockDocks,
TryBankWithdraw, AddOwnedShuttle, TrySaveGrid/_mapLoader.TrySaveGrid,
RemoveOwnedShuttle и TryBankDeposit для навигации по коду.
- Around line 167-182: The code treats a failed dock attempt as a successful
purchase: if _shuttle.TryFTLDock(shuttleGrid.Value, shuttleComponent,
targetGrid.Value, out var config) returns false you must undo the purchase by
deleting the loaded shuttle grid (shuttleGrid) and return false instead of
setting shuttleEntityUid and returning true; update the flow in the method
containing TryFTLDock so that on a false result you call the appropriate grid
cleanup/removal routine for shuttleGrid (or unload it via the same subsystem
used to spawn it), log the failure, ensure no dockName/shuttleEntityUid are set,
and then return false to indicate the purchase failed.
🧹 Nitpick comments (4)
Content.Server/_DH/SelfShipyard/Components/SaveableShuttleComponent.cs (1)

1-9: Рекомендую добавить XML-документацию.

Компонент важен для системы сохранения шаттлов. Согласно правилам проекта, для важных классов желательна XML-документация.

📝 Предлагаемые изменения
+using Robust.Shared.GameObjects;
+using Robust.Shared.Serialization.Manager.Attributes;
+
 namespace Content.Server._DH.SelfShipyard.Components
 {
+    /// <summary>
+    /// Компонент для маркировки шаттлов, которые могут быть сохранены в базу данных игрока.
+    /// </summary>
     [RegisterComponent]
     public sealed partial class SaveableShuttleComponent : Component
     {
+        /// <summary>
+        /// Идентификатор прототипа корабля для восстановления при загрузке.
+        /// </summary>
         [DataField]
         public string? PrototypeId;
     }
 }
Content.Server/_NF/Shipyard/Systems/ShipyardSystem.Consoles.cs (1)

60-60: Добавь маркер DH-Tweak для using.

Согласно правилам проекта, одиночные изменения вне папок /_DH/ должны быть помечены комментарием.

📝 Предлагаемые изменения
-using Content.Server._DH.SelfShipyard.Components;
+using Content.Server._DH.SelfShipyard.Components; // DH-Tweak
Content.Client/_DH/SelfShipyard/UI/OwnedVesselRow.xaml (1)

17-17: Пустой ToolTip.

Атрибут ToolTip="" пуст. Если подсказка планируется — лучше использовать локализованную строку. Если не нужна — можно убрать атрибут.

Content.Server.Database/Model.cs (1)

384-390: Отсутствует индекс на PlayerUserId и поведение OnDelete.

Для оптимизации запросов по игроку рекомендуется добавить индекс. Также стоит явно указать поведение при удалении игрока.

📝 Предлагаемые изменения
             // DH-Start: Owned Shuttles
             modelBuilder.Entity<OwnedShuttles>()
                 .HasOne(s => s.Player)
                 .WithMany(player => player.OwnedShuttles)
                 .HasForeignKey(s => s.PlayerUserId)
-                .HasPrincipalKey(player => player.UserId);
+                .HasPrincipalKey(player => player.UserId)
+                .OnDelete(DeleteBehavior.Cascade);
+
+            modelBuilder.Entity<OwnedShuttles>()
+                .HasIndex(s => s.PlayerUserId);
             // DH-End

Comment on lines +1 to +9
using Content.Client._Eclipse.SelfShipyard.UI;
using Content.Shared.Containers.ItemSlots;
using Content.Shared._Eclipse.SelfShipyard.BUI;
using Content.Shared._Eclipse.SelfShipyard.Events;
using static Robust.Client.UserInterface.Controls.BaseButton;

namespace Content.Client._Eclipse.SelfShipyard.BUI;

public sealed class SelfShipyardConsoleBoundUserInterface : BoundUserInterface
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Несовпадение неймспейсов _Eclipse/_DH ломает сборку UI.
SelfShipyardConsoleMenu и OwnedVesselRow объявлены в Content.Client._DH.SelfShipyard.UI, а здесь подключается _Eclipse, и сам класс находится в _Eclipse‑неймспейсе. В результате типы не найдутся. Нужна единая ветка неймспейсов и соответствующие using/namespace.

💡 Возможный фикс (если целевая ветка — _DH)
-using Content.Client._Eclipse.SelfShipyard.UI;
+using Content.Client._DH.SelfShipyard.UI;
...
-namespace Content.Client._Eclipse.SelfShipyard.BUI;
+namespace Content.Client._DH.SelfShipyard.BUI;
🤖 Prompt for AI Agents
In `@Content.Client/_DH/SelfShipyard/BUI/SelfShipyardConsoleBoundUserInterface.cs`
around lines 1 - 9, The file imports and namespace use _Eclipse while the UI
types (SelfShipyardConsoleMenu, OwnedVesselRow) live under
Content.Client._DH.SelfShipyard.UI, causing missing-type build errors; update
the using directive to Content.Client._DH.SelfShipyard.UI and change the
namespace declaration for SelfShipyardConsoleBoundUserInterface to
Content.Client._DH.SelfShipyard.BUI (or switch the other files to _Eclipse if
that is the intended branch) so the types SelfShipyardConsoleMenu and
OwnedVesselRow resolve correctly.

Comment on lines +90 to +97
if (state.ShipDeedTitle != null)
{
DeedTitle.Text = state.ShipDeedTitle;
}
else
{
DeedTitle.Text = $"None";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Нелокализованная строка в UI.
"None" показывается игрокам; вынесите в .ftl и используйте Loc.GetString(...). Согласно coding guidelines.

💡 Вариант исправления
-            DeedTitle.Text = $"None";
+            DeedTitle.Text = Loc.GetString("shipyard-console-no-deed");
🤖 Prompt for AI Agents
In `@Content.Client/_DH/SelfShipyard/UI/SelfShipyardConsoleMenu.xaml.cs` around
lines 90 - 97, Заменить хардкод `"None"` на локализуемую строку: добавить ключ в
.ftl (например "selfshipyard-deed-none") и в SelfShipyardConsoleMenu.xaml.cs
использовать Loc.GetString("selfshipyard-deed-none") при установке
DeedTitle.Text (в блоке, где проверяется state.ShipDeedTitle). Обновить
ресурсный файл .ftl с переводимой фразой и убедиться, что ключ уникален и
соответствует неймингу проекта.

Comment on lines +1403 to +1418
// DH-Start: Owned Shuttles
public class OwnedShuttles
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ShuttleId { get; set; }
[Required, ForeignKey("Player")] public Guid PlayerUserId { get; set; }
public Player Player { get; set; } = default!;

[Required] public string ShuttlePrototypeId { get; set; } = default!;

[Required] public string ShuttleName { get; set; } = default!;

public string? ShuttleDescription { get; set; }
[Required] public int ShuttleSavePrice { get; set; }

[Required] public string ShuttlePath { get; set; } = default!;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Поиск других определений таблиц с [Table] атрибутом в Model.cs
fd -e cs Content.Server.Database | head -20

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Ищем примеры использования [Table] в проекте
rg -B2 "\[Table\(" --type cs | head -50

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 2304


🏁 Script executed:

# Ищем миграции, связанные с OwnedShuttles
fd -e cs . | xargs rg "OwnedShuttles" | grep -i "migration\|Migrations"

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Проверим общую структуру определений сущностей в Model.cs
rg "public class.*\n\s*\{" Content.Server.Database/Model.cs -A 1 | head -30

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 268


🏁 Script executed:

# Прочитаем точно те строки из Model.cs
sed -n '1403,1418p' Content.Server.Database/Model.cs

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 729


🏁 Script executed:

# Ищем папку с миграциями
fd -type d -name "*migrations*" -o -name "*Migrations*"

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 247


🏁 Script executed:

# Ищем файлы миграций
fd -type f -path "*igrations*" -name "*.cs" | head -20

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 247


🏁 Script executed:

# Проверим другие DH-отмеченные блоки для правильного формата
rg "// DH-" Content.Server.Database/Model.cs | head -10

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 385


🏁 Script executed:

# Ищем миграции другим способом
find . -name "*.cs" -path "*Migrations*" 2>/dev/null | head -10

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 894


🏁 Script executed:

# Поищем папку Migrations в Content.Server.Database
find Content.Server.Database -type d -name "*igration*"

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 111


🏁 Script executed:

# Ищем файлы, которые содержат "CreateTable" или "OwnedShuttles"
rg "CreateTable.*OwnedShuttles|OwnedShuttles" --type cs --type txt

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 870


🏁 Script executed:

# Проверим структуру папок в Content.Server.Database
ls -la Content.Server.Database/

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 818


🏁 Script executed:

# Поищем в последних миграциях упоминание OwnedShuttles
ls Content.Server.Database/Migrations/Postgres/ | sort | tail -10

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 422


🏁 Script executed:

# Проверим последние миграции для OwnedShuttles
rg "OwnedShuttles" Content.Server.Database/Migrations/

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Посчитаем всего миграций
ls Content.Server.Database/Migrations/Postgres/ | wc -l

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 80


🏁 Script executed:

# Посмотрим на самую последнюю миграцию
ls Content.Server.Database/Migrations/Postgres/ | sort | tail -1 | xargs -I {} head -30 Content.Server.Database/Migrations/Postgres/{}

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 1085


🏁 Script executed:

# Ищем OwnedShuttles в всех файлах миграций
rg -l "OwnedShuttles" Content.Server.Database/Migrations/Postgres/

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Посмотрим последний snapshot для наличия OwnedShuttles
rg "OwnedShuttles" Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Проверим последние миграции на предмет создания таблицы owned_shuttles
for file in $(ls Content.Server.Database/Migrations/Postgres/*.cs | sort -r | head -5); do echo "=== $file ==="; grep -i "shuttles\|owned" "$file" || echo "not found"; done

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 560


🏁 Script executed:

# Посмотрим на ModelBuilder конфигурацию OwnedShuttles в Model.cs
rg -A 10 "modelBuilder.Entity<OwnedShuttles>" Content.Server.Database/Model.cs

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 573


🏁 Script executed:

# Проверим, есть ли миграция именно для добавления OwnedShuttles таблицы
grep -r "CreateTable.*shuttles\|scaffolded\|AddEntityType.*OwnedShuttles" Content.Server.Database/Migrations/

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Проверим полный список последних миграций с датами
ls -lh Content.Server.Database/Migrations/Postgres/*.cs | grep -v Designer | tail -5

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 705


🏁 Script executed:

# Проверим, есть ли какие-то файлы миграций содержащие "Shuttle"
find Content.Server.Database/Migrations -type f -name "*.cs" -exec grep -l -i "shuttle" {} \;

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Посмотрим на текущий DbContext, чтобы убедиться что OwnedShuttles там зарегистрирован
rg "public DbSet<OwnedShuttles>" Content.Server.Database/ -B 2 -A 2

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 557


🏁 Script executed:

# Проверим, есть ли какой-нибудь миграционный файл, который содержит определение OwnedShuttles
find Content.Server.Database/Migrations -name "*.cs" -type f -exec wc -l {} + | sort -n | tail -1 | awk '{print $NF}' | xargs head -100

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 143


🏁 Script executed:

# Проверим основной snapshot для наличия информации о OwnedShuttles таблице
grep -i "owned" Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 59


🏁 Script executed:

# Посмотрим на размер и содержание последней миграции
head -50 Content.Server.Database/Migrations/Postgres/20251226151249_DynamicMarket.cs

Repository: Dark-Haven-inc/dh-sector-frontier

Length of output: 1487


Добавь атрибут [Table("owned_shuttles")] и проверь наличие миграции.

Проект использует snake_case для имён таблиц (см. [Table("player")], [Table("server_ban")] и другие). Без явного указания EF создаст таблицу с именем OwnedShuttles, что нарушит соглашение о именовании.

Кроме того, в миграциях (Content.Server.Database/Migrations/Postgres/) OwnedShuttles не найден — ни в одном файле и ни в snapshot. Убедись, что миграция для этой таблицы была создана, иначе таблица не будет создана в базе данных.

Также исправь комментарий маркера: по гайдам должно быть // DH-Tweak-Start и // DH-Tweak-End для блоков, а // DH-Tweak: пояснение для отдельных строк.

📝 Необходимые изменения
-    // DH-Start: Owned Shuttles
+    [Table("owned_shuttles")]
+    // DH-Tweak-Start: Owned Shuttles
     public class OwnedShuttles
     {
         [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ShuttleId { get; set; }
🤖 Prompt for AI Agents
In `@Content.Server.Database/Model.cs` around lines 1403 - 1418, Add the missing
EF table attribute and fix the DH marker: annotate the OwnedShuttles class with
[Table("owned_shuttles")] (apply System.ComponentModel.DataAnnotations.Schema)
so EF uses snake_case, rename the comment markers from "// DH-Start: Owned
Shuttles" and any matching end marker to "// DH-Tweak-Start" and "//
DH-Tweak-End" (or use "// DH-Tweak: ..." for single-line notes), then generate
and verify a migration in Content.Server.Database/Migrations/Postgres that
creates the owned_shuttles table (ensure the new migration and the snapshot
include OwnedShuttles); if missing, run the EF migration add command and commit
the migration files.

Comment on lines +238 to +242
TryComp<FingerprintComponent>(player, out var fingerprintComponent);
TryComp<DnaComponent>(player, out var dnaComponent);
TryComp<StationRecordsComponent>(shuttleStation, out var stationRec);
_records.CreateGeneralRecord(shuttleStation.Value, targetId, profile.Name, profile.Age, profile.Species, profile.Gender, $"Captain", fingerprintComponent!.Fingerprint, dnaComponent!.DNA, profile, stationRec!);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Жёсткая строка должности без локализации.
"Captain" попадёт в записи и видна игрокам; вынесите строку в .ftl и используйте Loc.GetString(...). Согласно coding guidelines.

💡 Вариант исправления
-                _records.CreateGeneralRecord(shuttleStation.Value, targetId, profile.Name, profile.Age, profile.Species, profile.Gender, $"Captain", fingerprintComponent!.Fingerprint, dnaComponent!.DNA, profile, stationRec!);
+                _records.CreateGeneralRecord(shuttleStation.Value, targetId, profile.Name, profile.Age, profile.Species, profile.Gender, Loc.GetString("self-shipyard-captain-title"), fingerprintComponent!.Fingerprint, dnaComponent!.DNA, profile, stationRec!);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TryComp<FingerprintComponent>(player, out var fingerprintComponent);
TryComp<DnaComponent>(player, out var dnaComponent);
TryComp<StationRecordsComponent>(shuttleStation, out var stationRec);
_records.CreateGeneralRecord(shuttleStation.Value, targetId, profile.Name, profile.Age, profile.Species, profile.Gender, $"Captain", fingerprintComponent!.Fingerprint, dnaComponent!.DNA, profile, stationRec!);
}
TryComp<FingerprintComponent>(player, out var fingerprintComponent);
TryComp<DnaComponent>(player, out var dnaComponent);
TryComp<StationRecordsComponent>(shuttleStation, out var stationRec);
_records.CreateGeneralRecord(shuttleStation.Value, targetId, profile.Name, profile.Age, profile.Species, profile.Gender, Loc.GetString("self-shipyard-captain-title"), fingerprintComponent!.Fingerprint, dnaComponent!.DNA, profile, stationRec!);
}
🤖 Prompt for AI Agents
In `@Content.Server/_DH/SelfShipyard/Systems/SelfShipyardSystem.Consoles.cs`
around lines 238 - 242, The code writes a hard-coded "Captain" string into
records via _records.CreateGeneralRecord; replace that literal with a localized
string using Loc.GetString(...) and add the corresponding key to your .ftl file
(e.g. selfshipyard-record-position-captain) so the role shown to players is
localizable; update the CreateGeneralRecord call to pass
Loc.GetString("selfshipyard-record-position-captain") instead of "Captain" and
ensure any tests or documentation referencing the literal are updated
accordingly.

Comment on lines +244 to +246
_records.Synchronize(shuttleStation!.Value);
_records.Synchronize(station);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Возможен NullReference при отсутствии shuttleStation.
Если GameMapPrototype не найден, shuttleStation остаётся null, но далее вызывается _records.Synchronize(shuttleStation!.Value). Нужна проверка на null перед вызовом.

🐛 Возможный фикс
-        _records.Synchronize(shuttleStation!.Value);
+        if (shuttleStation != null)
+            _records.Synchronize(shuttleStation.Value);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_records.Synchronize(shuttleStation!.Value);
_records.Synchronize(station);
if (shuttleStation != null)
_records.Synchronize(shuttleStation.Value);
_records.Synchronize(station);
🤖 Prompt for AI Agents
In `@Content.Server/_DH/SelfShipyard/Systems/SelfShipyardSystem.Consoles.cs`
around lines 244 - 246, shuttleStation can be null which causes a NullReference
when calling _records.Synchronize(shuttleStation!.Value); update the logic in
SelfShipyardSystem.Consoles (the block that calls _records.Synchronize) to check
shuttleStation for null/HasValue before calling
_records.Synchronize(shuttleStation.Value) — either skip that call or
early-return if shuttleStation is missing; keep the existing
_records.Synchronize(station) behavior unchanged and reference the
shuttleStation variable and _records.Synchronize method when making the guard.

Comment on lines +167 to +182
_sawmill.Info($"Shuttle {shuttlePath} was purchased at {ToPrettyString(stationUid)} for {price:f2}");
//can do TryFTLDock later instead if we need to keep the shipyard map paused
if (_shuttle.TryFTLDock(shuttleGrid.Value, shuttleComponent, targetGrid.Value, out var config))
{
foreach (var (_, _, dockA, dockB) in config.Docks)
{
if (dockB.Name is not null)
{
dockName = Loc.GetString(dockB.Name);
break;
}
}
}

shuttleEntityUid = shuttleGrid;
return true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Провал стыковки сейчас считается успешной покупкой.
Если TryFTLDock возвращает false, метод всё равно возвращает true и отдаёт UID шаттла; в итоге покупка считается успешной, но корабль остаётся на верфи. Лучше трактовать это как ошибку, удалить загруженный грид и вернуть false.

🐛 Возможный фикс
-        if (_shuttle.TryFTLDock(shuttleGrid.Value, shuttleComponent, targetGrid.Value, out var config))
+        if (_shuttle.TryFTLDock(shuttleGrid.Value, shuttleComponent, targetGrid.Value, out var config))
         {
             foreach (var (_, _, dockA, dockB) in config.Docks)
             {
                 if (dockB.Name is not null)
                 {
                     dockName = Loc.GetString(dockB.Name);
                     break;
                 }
             }
         }
+        else
+        {
+            QueueDel(shuttleGrid);
+            shuttleEntityUid = null;
+            return false;
+        }
🤖 Prompt for AI Agents
In `@Content.Server/_DH/SelfShipyard/Systems/SelfShipyardSystem.cs` around lines
167 - 182, The code treats a failed dock attempt as a successful purchase: if
_shuttle.TryFTLDock(shuttleGrid.Value, shuttleComponent, targetGrid.Value, out
var config) returns false you must undo the purchase by deleting the loaded
shuttle grid (shuttleGrid) and return false instead of setting shuttleEntityUid
and returning true; update the flow in the method containing TryFTLDock so that
on a false result you call the appropriate grid cleanup/removal routine for
shuttleGrid (or unload it via the same subsystem used to spawn it), log the
failure, ensure no dockName/shuttleEntityUid are set, and then return false to
indicate the purchase failed.

Comment on lines +281 to +319
//just yeet and delete for now. Might want to split it into another function later to send back to the shipyard map first to pause for something
//also superman 3 moment
if (_station.GetOwningStation(shuttleUid) is { Valid: true } shuttleStationUid)
{
_station.DeleteStation(shuttleStationUid);
}

if (TryComp<SelfShipyardConsoleComponent>(consoleUid, out var comp))
{
CleanGrid(shuttleUid, consoleUid);
}

var shuttle_cost = (int)_pricing.AppraiseGrid(shuttleUid, LacksPreserveOnSaleComp);

var bill = (int)(shuttle_cost * _percentSaveRate) + _constantSaveRate;

if (!_bank.TryBankWithdraw(player, bill))
{
result.Error = ShipyardSaleError.InsufficientFunds;
return (result, bill);
}

_docking.UndockDocks(shuttleUid);

var id = await _db.AddOwnedShuttle(playerSession.UserId, saveableShuttle.PrototypeId, shuttleName, null, shuttle_cost, ResPath.Root);

string path = $"/OwnedShuttles/{playerSession.UserId}/{id}.yml";

var resPath = new ResPath(path);

_resMan.UserData.CreateDir(resPath.Directory);

if (!_mapLoader.TrySaveGrid(shuttleUid, resPath))
{
await _db.RemoveOwnedShuttle(id, playerSession.UserId);
_bank.TryBankDeposit(player, bill);
result.Error = ShipyardSaleError.InvalidShip;
return (result, bill);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Необратимые операции выполняются до подтверждения сделки.
DeleteStation, CleanGrid и UndockDocks идут до списания средств и до успешного сохранения. При недостатке средств или ошибке сохранения шаттл уже изменён/расстыкован и станция удалена. Перенесите эти действия на успешный путь или добавьте откат, чтобы при отказе состояние сохранялось.

🤖 Prompt for AI Agents
In `@Content.Server/_DH/SelfShipyard/Systems/SelfShipyardSystem.cs` around lines
281 - 319, Сейчас DeleteStation(shuttleStationUid), CleanGrid(shuttleUid,
consoleUid) и _docking.UndockDocks(shuttleUid) выполняются до проверки средств и
успешного сохранения, что приводит к необратимым изменениям при ошибках;
измените поток так, чтобы эти мутации выполнялись только после успешного
TryBankWithdraw(player, bill) и успешного _mapLoader.TrySaveGrid(shuttleUid,
resPath) либо реализуйте явный откат: если вы решаете сохранять раньше,
захватите состояние до изменений и при ошибке вызывайте
_db.RemoveOwnedShuttle(id, ...), _bank.TryBankDeposit(player, bill) и
восстановите станцию/докинг/гриду (или повторно создавайте станцию через
_station.GetOwningStation/DeleteStation обратным действием), иначе просто
переместите вызовы DeleteStation, CleanGrid и _docking.UndockDocks в блок после
успешного AddOwnedShuttle и успешного _mapLoader.TrySaveGrid; используйте
идентификаторы функций DeleteStation, CleanGrid, _docking.UndockDocks,
TryBankWithdraw, AddOwnedShuttle, TrySaveGrid/_mapLoader.TrySaveGrid,
RemoveOwnedShuttle и TryBankDeposit для навигации по коду.

@github-actions github-actions bot added size/M and removed size/L labels Jan 26, 2026
@HacksLua
Copy link
Contributor

HacksLua commented Feb 7, 2026

ЭЭээ бля ХРЕНЬ! лучше не делайте, лучше помогите нам сделать полный сейв! оригинальный пр: space-wizards/RobustToolbox#6166

Мои продолжения Lua-Frontier/RobustToolbox#1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Сохранение шаттлов

2 participants