Skip to content

Манипулятор для пИИ - #1179

Closed
kekoven1 wants to merge 10 commits into
WWhiteDreamProject:masterfrom
kekoven1:PAImanipulator
Closed

Манипулятор для пИИ#1179
kekoven1 wants to merge 10 commits into
WWhiteDreamProject:masterfrom
kekoven1:PAImanipulator

Conversation

@kekoven1

@kekoven1 kekoven1 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Описание PR

манипулятор для пИИ
манипулятор для персонального ИИ
манипулятор для персонального искусственного интеллекта
им можно водить туда сюда, хватать и перемещать, использовать и выкидывать
смысл слова МАНИПУЛЯТОР потерян


Медиа

https://youtu.be/A0O4awqkIKk <-- круто клавиши трогательность


Изменения

🆑

  • add: в пИИ вшили крутой манипулятор

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

RSI Diff Bot; head commit 2b27ddf merging into ff38ac4
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

Resources/Textures/_White/Interface/Actions/pai.rsi

State Old New Status
toggle Added

Resources/Textures/_White/Objects/Specific/pai_manipulator.rsi

State Old New Status
icon Added
rope Added

Edit: diff updated after 2b27ddf

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4137bd21-cc5e-4bb4-bed0-f43208091199

📥 Commits

Reviewing files that changed from the base of the PR and between 3e5f7c5 and 2b27ddf.

📒 Files selected for processing (2)
  • Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs
  • Resources/Prototypes/Entities/Objects/Fun/pai.yml

Walkthrough

Добавлены сетевое состояние и события манипулятора PAI, его прототип и ресурсы, клиентская обработка ввода, а также серверная логика создания, захвата, перемещения, взаимодействия и возврата.

Changes

Манипулятор PAI

Layer / File(s) Summary
Контракты и сетевое состояние
Content.Shared/PAI/PAIComponent.cs, Content.Shared/PAI/SharedPAISystem.cs, Content.Shared/_White/PAI/Components/*, Content.Shared/_White/PAI/Events/*
Оставлено действие toggle. Добавлены сетевые поля состояния, компонент владельца захваченной сущности и события перемещения, захвата и взаимодействия.
Прототип и ресурсы
Resources/Prototypes/Entities/Objects/Fun/pai.yml, Resources/Prototypes/_White/PAI/*, Resources/Prototypes/_White/Actions/pai.yml, Resources/Textures/_White/*/meta.json
PersonalAI подключает манипулятор. Добавлены прототип сущности, физика и визуальные метаданные. Удалены лишние action-прототипы и состояния.
Клиентский ввод
Content.Client/_White/PAI/ManipulatorSystem.cs
Клавиши мыши, E и Q преобразуются в сетевые события перемещения, взаимодействия и захвата после проверки подходящих сущностей.
Серверное управление манипулятором
Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs
Система обрабатывает сетевые события, создание и удаление манипулятора, захват, отсоединение, взаимодействие, изменение родителя, движение и возврат.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: remuchi, sleepyyapril

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Заголовок кратко и точно описывает основное изменение: добавление манипулятора для пИИ.
Description check ✅ Passed Описание связано с изменениями и перечисляет назначение манипулятора, управление, основные действия и запись в список изменений.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Content.Server/_White/PAI/Systems/ManipulatorSystem.cs`:
- Around line 21-30: Update ManipulatorSystem.Initialize to subscribe to the pAI
component’s ComponentShutdown event, then add a handler that calls Detach and
removes the active manipulator entity when the pAI is destroyed. Ensure any
currently grabbed item is detached before deleting the manipulator.
- Around line 122-130: Update OnInteract to set args.Handled after a valid
grabbed entity is successfully passed to _interact.UseInHandInteraction, while
preserving the early return when no entity is grabbed, so the configured
useDelay is applied.
- Line 48: Update the manipulator spawn in the relevant system method to pass
map coordinates from _transform.GetMapCoordinates(uid) instead of
Transform(uid).Coordinates, ensuring the spawned manipulator is created on the
map rather than inside the PAI’s current container.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1bd9659a-2303-4040-a304-4bc43ba8ccd9

📥 Commits

Reviewing files that changed from the base of the PR and between ff38ac4 and 6025d5b.

⛔ Files ignored due to path filters (6)
  • Resources/Textures/_White/Interface/Actions/pai.rsi/grab.png is excluded by !**/*.png
  • Resources/Textures/_White/Interface/Actions/pai.rsi/interact.png is excluded by !**/*.png
  • Resources/Textures/_White/Interface/Actions/pai.rsi/move.png is excluded by !**/*.png
  • Resources/Textures/_White/Interface/Actions/pai.rsi/toggle.png is excluded by !**/*.png
  • Resources/Textures/_White/Objects/Specific/pai_manipulator.rsi/icon.png is excluded by !**/*.png
  • Resources/Textures/_White/Objects/Specific/pai_manipulator.rsi/rope.png is excluded by !**/*.png
📒 Files selected for processing (11)
  • Content.Server/_White/PAI/Systems/ManipulatorSystem.cs
  • Content.Shared/PAI/PAIComponent.cs
  • Content.Shared/PAI/SharedPAISystem.cs
  • Content.Shared/_White/PAI/Components/ManipulatorComponent.cs
  • Content.Shared/_White/PAI/Components/UsedByManipulatorComponent.cs
  • Content.Shared/_White/PAI/Events/ManipulatorEvent.cs
  • Resources/Prototypes/Entities/Objects/Fun/pai.yml
  • Resources/Prototypes/_White/Actions/pai.yml
  • Resources/Prototypes/_White/PAI/manipulator.yml
  • Resources/Textures/_White/Interface/Actions/pai.rsi/meta.json
  • Resources/Textures/_White/Objects/Specific/pai_manipulator.rsi/meta.json

Comment thread Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs
if (comp.IsReturning)
return;

comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, Transform(uid).Coordinates);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'ManipulatorSystem.cs' . || true

echo "== file outline =="
if [ -f Content.Server/_White/PAI/Systems/ManipulatorSystem.cs ]; then
  ast-grep outline Content.Server/_White/PAI/Systems/ManipulatorSystem.cs || true
  echo "== relevant lines =="
  cat -n Content.Server/_White/PAI/Systems/ManipulatorSystem.cs | sed -n '1,140p'
fi

echo "== transform/mapping symbols usage =="
rg -n "GetMapCoordinates|Transform\\(uid\\)|SpawnAtPosition|ManipulatorProto|Manipulator = " Content.Server Content.Server/_White/PAI -g '*.cs' | head -200

Repository: WWhiteDreamProject/wwdpublic

Length of output: 32725


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== inspect other manipulator paths =="
cat -n Content.Server/_White/PAI/Systems/ManipulatorSystem.cs | sed -n '130,215p'

echo "== inspect pAI components and prototypes =="
rg -n "ManipulatorComponent|ManuallyAttachTo|IsReturning|TargetWorldPos|GetManuallyAttachedTo|AttachedEntity|ManipulatorProto|Manipulator" Content.Server/_White/PAI Content.Shared/_White/PAI -g '*.cs' | sed -n '1,240p'

echo "== inspect shared transform map-coordinate semantics in repository comments =="
rg -n "GetMapCoordinates|ToMapCoordinates|ManuallyAttachTo|AttachedEntity" Robust.Client/Robust.Server -g '*.cs' 2>/dev/null | head -120 || true

Repository: WWhiteDreamProject/wwdpublic

Length of output: 10471


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate entity coordinates and transform methods =="
rg -n "struct EntityCoordinates|class EntityCoordinates|partial class EntityCoordinates|GetMapCoordinates\\(|ToMapCoordinates\\(|GetParentCoordinates\\(" Robust.Server Robust.Shared -g '*.cs' 2>/dev/null | head -200 || true

echo "== read focused definitions if present =="
rg -l "struct EntityCoordinates|class EntityCoordinates|GetMapCoordinates\\(|ToMapCoordinates\\(|GetMoverCoordinates\\(|GetParentCoordinates\\(" Robust.Server Robust.Shared Robust.Client -g '*.cs' 2>/dev/null | head -50

Repository: WWhiteDreamProject/wwdpublic

Length of output: 262


Создавайте манипулятор на карте, а не в текущем контейнере pAI.

Transform(uid).Coordinates сохраняет родителя устройства, поэтому при pAI в руке или рюкзаке манипулятор спавнится внутри этого контейнера, а не на карте рядом с владельцем. Другие части логики уже предполагают map-координаты pAI — для spawn используйте _transform.GetMapCoordinates(uid).

Предлагаемое исправление
-            comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, Transform(uid).Coordinates);
+            comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, _transform.GetMapCoordinates(uid));
📝 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
comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, Transform(uid).Coordinates);
comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, _transform.GetMapCoordinates(uid));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Server/_White/PAI/Systems/ManipulatorSystem.cs` at line 48, Update
the manipulator spawn in the relevant system method to pass map coordinates from
_transform.GetMapCoordinates(uid) instead of Transform(uid).Coordinates,
ensuring the spawned manipulator is created on the map rather than inside the
PAI’s current container.

Comment thread Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs Outdated
@Lachklen

Copy link
Copy Markdown
Contributor

Мне кажется забракуют....

@RedFoxIV RedFoxIV left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

в текущем варианте это неликвид, к сожалению

идея интересная, но блять, движение манипулятора через экшены?
надо переделать это чудо природы на использование мышки и стандартных клавиш - авось, можно будет ещё пару интересных фич приделать, по типу ползанья по полу

Comment on lines +32 to +33
[DataField]
public MapCoordinates? TargetWorldPos;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

зачем DataField?

public bool IsActive = false;

[DataField, ViewVariables(VVAccess.ReadOnly)]
public bool IsGrabbin = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
public bool IsGrabbin = false;
public bool IsGrabbing = false;

Comment on lines +14 to +15
[DataField, ViewVariables(VVAccess.ReadOnly)]
public bool IsGrabbin = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Зачем DataField?

Comment on lines +34 to +59
if (comp.IsActive)
{
Detach(comp);

comp.IsActive = false;
comp.IsReturning = true;
comp.TargetWorldPos = _transform.GetMapCoordinates(uid);
args.Handled = true;
}
else
{
if (comp.IsReturning)
return;

comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, Transform(uid).Coordinates);
var man = comp.Manipulator;
var visuals = EnsureComp<JointVisualsComponent>(man.Value);

visuals.Sprite = comp.JointSpite;
visuals.OffsetA = new Vector2(0f, 0f);
visuals.Target = GetNetEntity(uid);
Dirty(man.Value, visuals);

comp.IsActive = true;
args.Handled = true;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (comp.IsActive)
{
Detach(comp);
comp.IsActive = false;
comp.IsReturning = true;
comp.TargetWorldPos = _transform.GetMapCoordinates(uid);
args.Handled = true;
}
else
{
if (comp.IsReturning)
return;
comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, Transform(uid).Coordinates);
var man = comp.Manipulator;
var visuals = EnsureComp<JointVisualsComponent>(man.Value);
visuals.Sprite = comp.JointSpite;
visuals.OffsetA = new Vector2(0f, 0f);
visuals.Target = GetNetEntity(uid);
Dirty(man.Value, visuals);
comp.IsActive = true;
args.Handled = true;
}
if (comp.IsActive)
{
Detach(comp);
comp.IsActive = false;
comp.IsReturning = true;
comp.TargetWorldPos = _transform.GetMapCoordinates(uid);
args.Handled = true;
return;
}
if (comp.IsReturning)
return;
comp.Manipulator = SpawnAtPosition(comp.ManipulatorProto, Transform(uid).Coordinates);
var man = comp.Manipulator;
var visuals = EnsureComp<JointVisualsComponent>(man.Value);
visuals.Sprite = comp.JointSpite;
visuals.OffsetA = new Vector2(0f, 0f);
visuals.Target = GetNetEntity(uid);
Dirty(man.Value, visuals);
comp.IsActive = true;
args.Handled = true;

Comment on lines +113 to +120
private void OnMove(EntityUid uid, ManipulatorComponent comp, ManipulatorMoveActionEvent args)
{
if (!comp.IsActive || comp.Manipulator == null || comp.IsReturning)
return;

comp.TargetWorldPos = _transform.ToMapCoordinates(args.Target);
args.Handled = true;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Рак.

continue;

if (!EntityManager.EntityExists(entity))
return;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
return;
continue;

Comment on lines +20 to +21
[DataField, ViewVariables]
public EntityUid? Manipulator;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Зачем DataField?

Comment on lines +23 to +24
[DataField, ViewVariables]
public EntityUid? GrabbedEntity;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Зачем DataField?

Comment on lines +29 to +30
[DataField]
public bool IsReturning = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ну ты понял

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs`:
- Around line 132-149: Обновите Detach и ветку проверки недействительного
манипулятора в Update, вынеся единый cleanup для потерянных Manipulator или
GrabbedEntity. Cleanup должен снимать UsedByManipulatorComponent, если сущность
существует, и безусловно сбрасывать IsGrabbing, GrabbedEntity, Manipulator,
IsActive, IsReturning и TargetWorldPos; не допускайте пропуска очистки при
null-ссылках или отсутствии PhysicsComponent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 703f4c27-5a96-49d1-be5c-9575a5d157f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6025d5b and 2bd1293.

📒 Files selected for processing (2)
  • Content.Shared/_White/PAI/Components/ManipulatorComponent.cs
  • Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Content.Shared/_White/PAI/Components/ManipulatorComponent.cs

Comment on lines +132 to +149
private void Detach(ManipulatorComponent comp)
{
if (comp.Manipulator == null)
return;

if (comp.GrabbedEntity != null && EntityManager.EntityExists(comp.GrabbedEntity))
{
var ent = comp.GrabbedEntity.Value;
var manipulatorCoords = _transform.GetMoverCoordinates(comp.Manipulator.Value);

_transform.AttachToGridOrMap(ent, Transform(ent));
_transform.SetCoordinates(ent, manipulatorCoords);

RemComp<UsedByManipulatorComponent>(ent);
}

comp.GrabbedEntity = null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Сбрасывайте состояние при потере манипулятора или предмета.

Detach не сбрасывает IsGrabbing во всех ветках, а Update просто пропускает манипулятор без PhysicsComponent, сохраняя ненулевой Manipulator. После этого следующий toggle переводит pAI в IsReturning, но возврат никогда не завершается; grab-toggle также остаётся в ветке отпускания. Вынесите единый cleanup для недействительных ссылок и безусловно сбрасывайте IsGrabbing, GrabbedEntity, Manipulator, IsActive, IsReturning и TargetWorldPos, снимая маркер захвата, если сущность ещё существует.

Also applies to: 178-184

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs` around lines 132 -
149, Обновите Detach и ветку проверки недействительного манипулятора в Update,
вынеся единый cleanup для потерянных Manipulator или GrabbedEntity. Cleanup
должен снимать UsedByManipulatorComponent, если сущность существует, и
безусловно сбрасывать IsGrabbing, GrabbedEntity, Manipulator, IsActive,
IsReturning и TargetWorldPos; не допускайте пропуска очистки при null-ссылках
или отсутствии PhysicsComponent.

@kekoven1

Copy link
Copy Markdown
Contributor Author

ну вот, ЛКМ это двигать, Q это подобрать, E это активация

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs (2)

111-129: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Detach не сбрасывает IsGrabbing — манипулятор навсегда теряет возможность что-либо захватить после первого отпускания.

Detach сбрасывает только comp.GrabbedEntity, но не comp.IsGrabbing. Поскольку ManipulatorComponent не пересоздаётся при выключении/включении манипулятора, после первого цикла "схватить → Q для отпускания" IsGrabbing навсегда остаётся true: OnGrab при каждом следующем нажатии Q будет уходить в ветку Detach (уже no-op, так как GrabbedEntity == null) и никогда не даст захватить новый предмет. Это тот же незакрытый (без ✅) комментарий из прошлого ревью про сброс состояния; также применимо к ветке в Update (151-199), где отсутствие PhysicsComponent у манипулятора пропускает итерацию без сброса Manipulator/IsReturning.

🐛 Предлагаемый фикс
         comp.GrabbedEntity = null;
+        comp.IsGrabbing = false;
         Dirty(uid, comp);

Also applies to: 151-199

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs` around lines 111 -
129, Обновите состояние в Detach: после отпускания сущности сбрасывайте
comp.IsGrabbing вместе с comp.GrabbedEntity, чтобы следующий вызов OnGrab мог
захватить новый объект. Также в ветке Update, где отсутствует PhysicsComponent у
манипулятора, сбрасывайте связанные состояния Manipulator и IsReturning перед
пропуском итерации.

131-143: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Не хватает Dirty после изменения сетевых полей в OnParentChanged.

man.GrabbedEntity и man.IsGrabbing помечены AutoNetworkedField, но после сброса здесь не вызывается Dirty(comp.ManipulatorOwner, man), в отличие от Detach и OnToggle. Клиенты не получат актуальное состояние захвата после того, как предмет был отсоединён внешним изменением родителя.

🔧 Предлагаемый фикс
             man.GrabbedEntity = null;
             man.IsGrabbing = false;
+            Dirty(comp.ManipulatorOwner, man);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs` around lines 131 -
143, В методе OnParentChanged после сброса man.GrabbedEntity и man.IsGrabbing
вызови Dirty(comp.ManipulatorOwner, man), чтобы изменения сетевых полей были
отправлены клиентам; сохрани существующие проверки и удаление
UsedByManipulatorComponent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Content.Client/_White/PAI/ManipulatorSystem.cs`:
- Around line 21-26: Добавьте переопределение Shutdown для отписки обработчика
OnKey от _input.FirstChanceOnKeyEvent перед завершением системы; сохраните
корректный вызов базового Shutdown, чтобы повторная инициализация не накапливала
подписчиков.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs`:
- Around line 87-97: В методе OnMove проверяйте, что msg.Coords находится на той
же карте, что и ent, и ограничивайте целевую позицию максимальной допустимой
дистанцией от владельца перед присваиванием man.TargetWorldPos. Отклоняйте
координаты с другим MapId и нормализуйте слишком удалённые координаты до границы
допустимого радиуса.
- Around line 99-109: Добавьте защиту от частых вызовов в OnInteract: ограничьте
обработку ManipulatorInteractEvent задержкой, эквивалентной прежнему useDelay,
прежде чем вызывать _interact.UseInHandInteraction. Отклоняйте события до
истечения задержки и обновляйте таймер только при принятом взаимодействии,
сохранив существующие проверки сущности и GrabbedEntity.
- Around line 61-85: Добавьте в OnGrab серверную валидацию target до вызова
_transform.SetParent: убедитесь, что NetEntity существует и разрешается в
сущность, находится в допустимом радиусе манипулятора, имеет ItemComponent и
BodyStatus.OnGround, не заанкорена и не находится в контейнере, а также не
является владельцем ПАИ или самой сущностью манипулятора. При любой непрошедшей
проверке немедленно отклоняйте захват, сохранив текущую логику только для
валидной цели.

---

Outside diff comments:
In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs`:
- Around line 111-129: Обновите состояние в Detach: после отпускания сущности
сбрасывайте comp.IsGrabbing вместе с comp.GrabbedEntity, чтобы следующий вызов
OnGrab мог захватить новый объект. Также в ветке Update, где отсутствует
PhysicsComponent у манипулятора, сбрасывайте связанные состояния Manipulator и
IsReturning перед пропуском итерации.
- Around line 131-143: В методе OnParentChanged после сброса man.GrabbedEntity и
man.IsGrabbing вызови Dirty(comp.ManipulatorOwner, man), чтобы изменения сетевых
полей были отправлены клиентам; сохрани существующие проверки и удаление
UsedByManipulatorComponent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e98af917-9875-4697-a0fc-7009e202d03f

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd1293 and 3e5f7c5.

📒 Files selected for processing (8)
  • Content.Client/_White/PAI/ManipulatorSystem.cs
  • Content.Shared/PAI/PAIComponent.cs
  • Content.Shared/PAI/SharedPAISystem.cs
  • Content.Shared/_White/PAI/Components/ManipulatorComponent.cs
  • Content.Shared/_White/PAI/Events/ManipulatorEvent.cs
  • Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs
  • Resources/Prototypes/_White/Actions/pai.yml
  • Resources/Textures/_White/Interface/Actions/pai.rsi/meta.json
💤 Files with no reviewable changes (3)
  • Resources/Textures/_White/Interface/Actions/pai.rsi/meta.json
  • Resources/Prototypes/_White/Actions/pai.yml
  • Content.Shared/PAI/PAIComponent.cs

Comment on lines +21 to +26
public override void Initialize()
{
base.Initialize();

_input.FirstChanceOnKeyEvent += OnKey;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Отписывайтесь от FirstChanceOnKeyEvent в Shutdown.

Подписка _input.FirstChanceOnKeyEvent += OnKey; не снимается — нет переопределения Shutdown(). При повторной инициализации системы (реконнект и т.п.) подписчики будут накапливаться, что приведёт к дублированию сетевых событий на одно нажатие клавиши.

🔧 Предлагаемый фикс
     public override void Initialize()
     {
         base.Initialize();

         _input.FirstChanceOnKeyEvent += OnKey;
     }
+
+    public override void Shutdown()
+    {
+        _input.FirstChanceOnKeyEvent -= OnKey;
+
+        base.Shutdown();
+    }
📝 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
public override void Initialize()
{
base.Initialize();
_input.FirstChanceOnKeyEvent += OnKey;
}
public override void Initialize()
{
base.Initialize();
_input.FirstChanceOnKeyEvent += OnKey;
}
public override void Shutdown()
{
_input.FirstChanceOnKeyEvent -= OnKey;
base.Shutdown();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Client/_White/PAI/ManipulatorSystem.cs` around lines 21 - 26,
Добавьте переопределение Shutdown для отписки обработчика OnKey от
_input.FirstChanceOnKeyEvent перед завершением системы; сохраните корректный
вызов базового Shutdown, чтобы повторная инициализация не накапливала
подписчиков.

Comment on lines +61 to +85
private void OnGrab(ManipulatorGrabEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { } ent
|| !TryComp<ManipulatorComponent>(ent, out var man))
return;

if (!man.IsActive || man.Manipulator == null || man.IsReturning)
return;

if (man.IsGrabbing)
{
Detach(man, ent);
return;
}

var target = GetEntity(msg.Ent);

_transform.SetParent(target, man.Manipulator.Value);

man.GrabbedEntity = target;
var marker = EnsureComp<UsedByManipulatorComponent>(target);
marker.ManipulatorOwner = ent;

man.IsGrabbing = true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

Валидируйте цель захвата на сервере — сейчас клиент полностью контролирует, что реперентится.

OnGrab берёт msg.Ent от клиента и сразу вызывает _transform.SetParent(target, man.Manipulator.Value) без каких-либо серверных проверок: не проверяется существование сущности, дистанция до манипулятора, наличие ItemComponent, что цель не заанкорена, что это не сам манипулятор/владелец-пАИ и что цель не в контейнере. Клиентские фильтры в Content.Client/_White/PAI/ManipulatorSystem.cs (радиус 0.1, ItemComponent, BodyStatus.OnGround, не в контейнере) — это только UX-подсказка, модифицированный клиент может отправить ManipulatorGrabEvent напрямую с любым NetEntity, включая закреплённые механизмы, других игроков или сам пАИ, что приведёт к репарентингу произвольных сущностей в мире.

Продублируйте на сервере как минимум те же проверки (наличие сущности, дистанция до манипулятора, ItemComponent, BodyStatus.OnGround, отсутствие анкера, отсутствие в контейнере, цель ≠ манипулятор/владелец) перед SetParent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs` around lines 61 - 85,
Добавьте в OnGrab серверную валидацию target до вызова _transform.SetParent:
убедитесь, что NetEntity существует и разрешается в сущность, находится в
допустимом радиусе манипулятора, имеет ItemComponent и BodyStatus.OnGround, не
заанкорена и не находится в контейнере, а также не является владельцем ПАИ или
самой сущностью манипулятора. При любой непрошедшей проверке немедленно
отклоняйте захват, сохранив текущую логику только для валидной цели.

Comment on lines +87 to +97
private void OnMove(ManipulatorMoveEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { } ent
|| !TryComp<ManipulatorComponent>(ent, out var man))
return;

if (!man.IsActive || man.Manipulator == null || man.IsReturning)
return;

man.TargetWorldPos = msg.Coords;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Ограничьте TargetWorldPos дистанцией/картой владельца.

OnMove присваивает man.TargetWorldPos = msg.Coords без проверки, что координаты находятся на той же карте, что и владелец, и без ограничения максимальной дистанции. Модифицированный клиент может отправить произвольные координаты (в том числе с другим MapId), заставляя манипулятор двигаться в непредсказуемом направлении.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs` around lines 87 - 97,
В методе OnMove проверяйте, что msg.Coords находится на той же карте, что и ent,
и ограничивайте целевую позицию максимальной допустимой дистанцией от владельца
перед присваиванием man.TargetWorldPos. Отклоняйте координаты с другим MapId и
нормализуйте слишком удалённые координаты до границы допустимого радиуса.

Comment on lines +99 to +109
private void OnInteract(ManipulatorInteractEvent msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { } ent
|| !TryComp<ManipulatorComponent>(ent, out var man))
return;

if (man.GrabbedEntity == null)
return;

_interact.UseInHandInteraction(ent, man.GrabbedEntity.Value, false, false, true);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Нет анти-спам защиты в OnInteract.

Раньше задержка обеспечивалась через useDelay на action-обработчике; теперь это сырое сетевое событие без какого-либо ограничения частоты — модифицированный клиент может спамить ManipulatorInteractEvent каждый тик, вызывая UseInHandInteraction многократно быстрее, чем позволял старый механизм.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Content.Shared/_White/PAI/Systems/ManipulatorSystem.cs` around lines 99 -
109, Добавьте защиту от частых вызовов в OnInteract: ограничьте обработку
ManipulatorInteractEvent задержкой, эквивалентной прежнему useDelay, прежде чем
вызывать _interact.UseInHandInteraction. Отклоняйте события до истечения
задержки и обновляйте таймер только при принятом взаимодействии, сохранив
существующие проверки сущности и GrabbedEntity.

@HSKveez

HSKveez commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Почему... Мне придется оплатить годовой сеанс психотерапии и прорабатывать увиденное

@RedFoxIV

RedFoxIV commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

тяжело

@RedFoxIV RedFoxIV closed this Aug 5, 2026
@RedFoxIV

RedFoxIV commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

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

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.

4 participants