Skip to content
14 changes: 12 additions & 2 deletions Content.Client/Humanoid/MarkingPicker.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BoxContainer;
// Arcane - Start
using Content.Shared._Arcane.SpecialWhitelist;

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.

Usings НИКОГДА не помечают пометками об изменениях в виде // Arcane...

using Robust.Client.Player;
// Arcane - End

namespace Content.Client.Humanoid;

Expand All @@ -31,6 +35,7 @@ public sealed partial class MarkingPicker : Control
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; // Arcane

private readonly SpriteSystem _sprite;

Expand Down Expand Up @@ -239,8 +244,13 @@ public void Populate(string filter)
continue;
}

// Arcane - Start

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.

Пробелы между '-' и Arcane со стартом бы убрать.
Должно быть Arcane-Start
Сделай так со всеми остальными.

if (!MarkingWhitelistManager.IsMarkingAllowed(marking, _playerManager.LocalSession))
continue;
// Arcane - End

var item = CMarkingsUnused.AddItem($"{GetMarkingName(marking)}", _sprite.Frame0(marking.Sprites[0]));
item.Metadata = marking;
item.Metadata = marking;
}

CMarkingPoints.Visible = _currentMarkings.PointsLeft(_selectedMarkingCategory) != -1;
Expand Down Expand Up @@ -561,4 +571,4 @@ private void MarkingRemove()
CMarkingColors.Visible = false;
OnMarkingRemoved?.Invoke(_currentMarkings);
}
}
}
11 changes: 10 additions & 1 deletion Content.Client/Humanoid/SingleMarkingPicker.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Content.Shared._Arcane.SpecialWhitelist;
using Robust.Client.Player;

namespace Content.Client.Humanoid;

Expand All @@ -21,6 +23,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer
{
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; // Arcane

private readonly SpriteSystem _sprite;

Expand Down Expand Up @@ -201,6 +204,12 @@ public void PopulateList(string filter)

foreach (var (id, marking) in sortedMarkings)
{
// Arcane - Start
if (!MarkingWhitelistManager.IsMarkingAllowed(marking, _playerManager.LocalSession))
{
continue;
}
// Arcane - End
var item = MarkingList.AddItem(Loc.GetString($"marking-{id}"), _sprite.Frame0(marking.Sprites[0]));
item.Metadata = marking.ID;

Expand Down Expand Up @@ -310,4 +319,4 @@ private string GetMarkingName(MarkingPrototype marking)
{
return Loc.GetString($"marking-{marking.ID}");
}
}
}
5 changes: 5 additions & 0 deletions Content.Shared/Humanoid/Markings/MarkingPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Content.Shared._Arcane.SpecialWhitelist;

namespace Content.Shared.Humanoid.Markings
{
Expand Down Expand Up @@ -69,5 +70,9 @@ public Marking AsMarking()
{
return new Marking(ID, Sprites.Count);
}
// Arcane - Start
[DataField("whitelist")]
public MarkingWhitelistData? Whitelist { get; private set; }
// Arcane - End
Comment on lines +73 to +76

@coderabbitai coderabbitai Bot Jul 3, 2026

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.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Явное имя [DataField("whitelist")] тоже избыточно.

Совпадает с дефолтным именем от Whitelist — то же замечание, что и для MarkingWhitelistData.

♻️ Предложение
-        // Arcane - Start
-        [DataField("whitelist")]
+        // Arcane - Start
+        [DataField]
         public MarkingWhitelistData? Whitelist { get; private set; }
         // Arcane - End

Маркировка // Arcane - Start/End для многострочной вставки в файле вне _Arcane-пути соответствует уже принятому в файле паттерну (см. /// Impstation start/end рядом) — нареканий нет.

As per coding guidelines, "Prefer [DataField] without string field names on new code unless serializer compatibility or a non-default data name is required."

📝 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
// Arcane - Start
[DataField("whitelist")]
public MarkingWhitelistData? Whitelist { get; private set; }
// Arcane - End
// Arcane - Start
[DataField]
public MarkingWhitelistData? Whitelist { get; private set; }
// Arcane - End
🤖 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/Humanoid/Markings/MarkingPrototype.cs` around lines 73 - 76,
Remove the explicit DataField name from MarkingPrototype. The Whitelist property
already matches the default serialized name, so update the
MarkingPrototype.Whitelist declaration to use [DataField] without the
"whitelist" string, keeping the existing Arcane comment markers unchanged.

Source: Coding guidelines

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.

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.

@UmbiMax, принято.

}
}
14 changes: 14 additions & 0 deletions Content.Shared/Preferences/HumanoidCharacterProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
// Arcane - Start
using Content.Shared.Humanoid.Markings;
using Content.Shared._Arcane.SpecialWhitelist;
// Arcane - End
Comment on lines +72 to +75

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.

Сноси пометки.


namespace Content.Shared.Preferences
{
Expand Down Expand Up @@ -1106,6 +1110,16 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection
{
_loadouts.Remove(value);
}
// Arcane - Start
foreach (var markingId in Appearance.Markings.ToList())
{
if (!prototypeManager.TryIndex<MarkingPrototype>(markingId.MarkingId, out var proto))
continue;

if (!MarkingWhitelistManager.IsMarkingAllowed(proto, session))
Appearance.Markings.Remove(markingId);
}
// Arcane - End
}

// Art-TTS Start
Expand Down
10 changes: 10 additions & 0 deletions Content.Shared/_Arcane/MarkingWhitelistData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Robust.Shared.Serialization;

namespace Content.Shared._Arcane.SpecialWhitelist;

[DataDefinition]
public sealed partial class MarkingWhitelistData
{
[DataField("allowed")]
public List<string> Allowed { get; private set; } = new();
Comment on lines +8 to +9

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.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Явное имя [DataField("allowed")] избыточно.

Строковое имя совпадает с дефолтным (camelCase от Allowed), так что атрибут можно упростить.

♻️ Предложение
-    [DataField("allowed")]
+    [DataField]
     public List<string> Allowed { get; private set; } = new();

As per coding guidelines, "Prefer [DataField] without string field names on new code unless serializer compatibility or a non-default data name is required."

📝 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
[DataField("allowed")]
public List<string> Allowed { get; private set; } = new();
[DataField]
public List<string> Allowed { get; private set; } = new();
🤖 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/_Arcane/MarkingWhitelistData.cs` around lines 8 - 9, The
[DataField("allowed")] annotation on MarkingWhitelistData.Allowed is redundant
because it matches the default camelCase name; simplify it to a bare [DataField]
unless a non-default serialized name is required. Update the Allowed property in
MarkingWhitelistData to use the convention preferred by the serializer
guidelines and keep the property name as the unique reference point.

Source: Coding guidelines

}
29 changes: 29 additions & 0 deletions Content.Shared/_Arcane/MarkingWhitelistManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Shared.Humanoid.Markings;
using Robust.Shared.Player;

namespace Content.Shared._Arcane.SpecialWhitelist;

public static class MarkingWhitelistManager
{
public static bool IsMarkingAllowed(MarkingPrototype marking, ICommonSession? session)

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Наименование check-метода не соответствует конвенции.

IsMarkingAllowed — это проверочный метод, по конвенции такие должны называться через Can... (например, CanApplyMarking).

As per coding guidelines, "Check methods should prefer Can...."

🤖 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/_Arcane/MarkingWhitelistManager.cs` at line 8, Rename the
check method in MarkingWhitelistManager from IsMarkingAllowed to a Can...-style
name, such as CanApplyMarking, to match the project’s convention for
predicate/check methods. Update the method declaration and every call site or
reference that uses IsMarkingAllowed so the API name is consistent and clearly
communicates that it returns whether marking can be applied.

Source: Coding guidelines

{
// Если вайтлиста нет — разметка доступна всем
if (marking.Whitelist == null || marking.Whitelist.Allowed.Count == 0)
return true;

// Если сессии нет (например, локальный просмотр без игрока), скрываем на всякий случай
if (session == null)
return false;

// Для привязки к аккаунту используем login username, а не отображаемое имя персонажа.
Comment on lines +10 to +18

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.

Почему пометки на русском?

var ckey = session.Data.UserName;

foreach (var allowedCkey in marking.Whitelist.Allowed)
{
if (string.Equals(allowedCkey, ckey, StringComparison.OrdinalIgnoreCase))
return true;
}

return false;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
3 changes: 3 additions & 0 deletions Resources/Locale/ru-RU/_Arcane/markings/humanoid_xeno.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ marking-HumanoidXenoTailRunner = Хвост бегуна
marking-HumanoidXenoTailBoiler = Хвост бойлера
marking-HumanoidXenoTailCrusher = Хвост крушителя
marking-HumanoidXenoTailNeomprph = Хвост неоморфа
marking-HumanoidXenoTailRen = Хвост Рэна
marking-HumanoidXenoTailQueenLong = Длинный хвост королевы
marking-HumanoidXenoHeadDrone = Голова дрона
marking-HumanoidXenoHeadSpitter = Голова плевателя
Expand All @@ -21,6 +22,7 @@ marking-HumanoidXenoHeadCrusher = Голова крушителя
marking-HumanoidXenoHeadCarrier = Голова носителя
marking-HumanoidXenoHeadNeomorph = Голова неоморфа
marking-HumanoidXenoHeadWorker = Голова рабочего
marking-HumanoidXenoHeadRen = Голова Рэна
marking-HumanoidXenoChestPredalien = Грудь предалиена
marking-HumanoidXenoChestSpitter = Грудь плевателя
marking-HumanoidXenoChestMuscles = "Мышцы"
Expand All @@ -35,6 +37,7 @@ marking-HumanoidXenoTracheasBoiler = Трахеи бойлера
marking-HumanoidXenoTracheasCrusher = Трахеи крушителя
marking-HumanoidXenoTracheasNeomorph = Трахеи неоморфа
marking-HumanoidXenoTracheasCarrier = Трахеи носителя
marking-HumanoidXenoTracheasRen = Трахеи Рэна
marking-HumanoidXenoLegsPredalien = Ноги предалиена
marking-HumanoidXenoLegsMuscles = "Мышцы"
marking-HumanoidXenoArmsPredalien = Руки предалиена
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/tail.rsi
state: neomorph_tail

- type: marking
id: HumanoidXenoTailRen
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [HumanoidXeno]
sprites:
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/tail.rsi
state: ren_tail
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/tail.rsi
state: ren_tail_grad
whitelist:
allowed:
- Bulbo44key

Comment on lines +120 to +133

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 | 🏗️ Heavy lift

Whitelist только на клиенте — фильтр обходится напрямую.

Три новых маркинга (HumanoidXenoTailRen, HumanoidXenoHeadRen, HumanoidXenoTracheasRen) ограничены через whitelist.allowed: Bulbo44key. Судя по описанию PR, MarkingWhitelistManager — чисто клиентская/shared проверка без серверной валидации при сохранении/применении профиля. Игрок может обойти клиентский пикер и выставить этот маркинг напрямую (например, через редактирование профиля/сети), и сервер это не проверит.

Нужна серверная валидация профиля персонажа (при применении/сохранении), а не только фильтрация в UI-пикере.

Also applies to: 298-309, 440-451

🤖 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
`@Resources/Prototypes/_Arcane/Entities/Mobs/Customization/Markings/humanoid_xeno.yml`
around lines 120 - 133, The three Ren humanoid xeno markings are only gated by
the client-side whitelist, so add server-side validation in the character
profile apply/save path to reject unauthorized markings. Update the logic around
HumanoidXenoTailRen, HumanoidXenoHeadRen, and HumanoidXenoTracheasRen so the
server checks the allowed user before persisting or applying the profile, rather
than relying on MarkingWhitelistManager/UI filtering alone.

# Head

- type: marking
Expand Down Expand Up @@ -281,6 +295,18 @@
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/head.rsi
state: worker_shine

- type: marking
id: HumanoidXenoHeadRen
bodyPart: Head
markingCategory: Head
speciesRestriction: [HumanoidXeno]
sprites:
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/head.rsi
state: ren_head
whitelist:
allowed:
- Bulbo44key

# Chest

- type: marking
Expand Down Expand Up @@ -411,6 +437,18 @@
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/long_tracheas.rsi
state: carrier_tubes

- type: marking
id: HumanoidXenoTracheasRen
bodyPart: Chest
markingCategory: Tracheas
speciesRestriction: [HumanoidXeno]
sprites:
- sprite: _Arcane/Mobs/Customization/HumanoidXeno/tracheas.rsi
state: ren_tubes
whitelist:
allowed:
- Bulbo44key

# Legs

- type: marking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@
{
"name": "worker_teeth",
"directions": 4
},
{
"name": "ren_head",
"directions": 4
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@
{
"name": "neomorph_tail",
"directions": 4
},
{
"name": "ren_tail",
"directions": 4
},
{
"name": "ren_tail_grad",
"directions": 4
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
{
"name": "neomorph_tubes",
"directions": 4
},
{
"name": "ren_tubes",
"directions": 4
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading