Skip to content

Commit 64e0567

Browse files
HandyS11claude
andauthored
fix: underwater-lab monument icons never rendered (path-qualified tokens) (#61)
* fix: map path-qualified underwater-lab/swamp prefab tokens to monument icons Rust+ sends full prefab paths (assets/.../underwater_lab_d.prefab) for the swamp and underwater-lab families, but MonumentTokenMap matched by bare-name prefix, so lab icons never rendered and every render logged an unknown token. TypeFor now reduces a token to its bare prefab name before lookup, and the underwater-lab-base interior-module family (moonpools, corridors) is deliberately skipped without the unknown-token log via IsIgnored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: state PrefabName's actual reduction behavior in its returns doc Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 2f3c4d4 commit 64e0567

4 files changed

Lines changed: 65 additions & 5 deletions

File tree

src/RustPlusBot.Features.Map/Assets/MonumentIconSource.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ public sealed partial class MonumentIconSource(IMonumentAssetSource assets, ILog
3333
var type = MonumentTokenMap.TypeFor(token);
3434
if (type is null)
3535
{
36-
ReportOnce(token, type: null);
36+
if (!MonumentTokenMap.IsIgnored(token))
37+
{
38+
ReportOnce(token, type: null);
39+
}
40+
3741
return null;
3842
}
3943

src/RustPlusBot.Features.Map/Assets/MonumentTokenMap.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,27 +59,53 @@ public static class MonumentTokenMap
5959
/// <summary>All exactly-mapped tokens (excludes the prefix families); exposed for drift-guard tests.</summary>
6060
internal static IReadOnlyCollection<string> KnownTokens => Map.Keys;
6161

62+
/// <summary>Indicates whether a token is a known non-monument that should be skipped silently.</summary>
63+
/// <param name="token">The Rust+ monument protobuf token (or prefab name).</param>
64+
/// <returns>True when the token is deliberately iconless; false otherwise.</returns>
65+
/// <remarks>
66+
/// Underwater labs are assembled from interior module prefabs (moonpools, corridors, …) that
67+
/// Rust+ lists alongside the lab itself; only the lab warrants an icon, so the module family is
68+
/// skipped without the unknown-token log.
69+
/// </remarks>
70+
public static bool IsIgnored(string? token) =>
71+
token?.Contains("underwater-lab-base/", StringComparison.Ordinal) == true;
72+
6273
/// <summary>Gets the RustMaps monument type for a Rust+ token, or null when unmapped.</summary>
6374
/// <param name="token">The Rust+ monument protobuf token (or prefab name).</param>
6475
/// <returns>The monument type whose asset represents the token, or null.</returns>
6576
public static MonumentType? TypeFor(string? token)
6677
{
67-
if (string.IsNullOrEmpty(token))
78+
var name = PrefabName(token);
79+
if (string.IsNullOrEmpty(name))
6880
{
6981
return null;
7082
}
7183

72-
if (Map.TryGetValue(token, out var type))
84+
if (Map.TryGetValue(name, out var type))
7385
{
7486
return type;
7587
}
7688

7789
// Rust+ sends prefab names (not fixed tokens) for these families.
78-
if (token.StartsWith("swamp", StringComparison.Ordinal))
90+
if (name.StartsWith("swamp", StringComparison.Ordinal))
7991
{
8092
return MonumentType.SwampC;
8193
}
8294

83-
return token.StartsWith("underwater_lab", StringComparison.Ordinal) ? MonumentType.UnderwaterA : null;
95+
return name.StartsWith("underwater_lab", StringComparison.Ordinal) ? MonumentType.UnderwaterA : null;
96+
}
97+
98+
/// <summary>Reduces a path-qualified prefab token ("assets/.../swamp_a.prefab") to its bare name.</summary>
99+
/// <param name="token">The raw Rust+ token: a bare display-name token or a full prefab path.</param>
100+
/// <returns>The last path segment with any ".prefab" suffix removed; bare tokens come back unchanged.</returns>
101+
private static string? PrefabName(string? token)
102+
{
103+
if (string.IsNullOrEmpty(token))
104+
{
105+
return token;
106+
}
107+
108+
var name = token[(token.LastIndexOf('/') + 1)..];
109+
return name.EndsWith(".prefab", StringComparison.Ordinal) ? name[..^".prefab".Length] : name;
84110
}
85111
}

tests/RustPlusBot.Features.Map.Tests/MonumentIconSourceTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ public void Monument_returns_null_and_logs_once_for_unknown_token()
4646
Assert.Equal(1, logger.Count(LogLevel.Information));
4747
}
4848

49+
[Fact]
50+
public void Monument_returns_null_silently_for_ignored_lab_module_tokens()
51+
{
52+
var logger = new RecordingLogger<MonumentIconSource>();
53+
var source = new MonumentIconSource(new MonumentAssetSource(), logger);
54+
55+
Assert.Null(source.Monument(
56+
"assets/bundled/prefabs/autospawn/underwater-lab-base/module_900x900_2way_moonpool.prefab", 30));
57+
58+
Assert.Equal(0, logger.Count(LogLevel.Information));
59+
}
60+
4961
[Theory]
5062
[InlineData(RigKind.Small)]
5163
[InlineData(RigKind.Large)]

tests/RustPlusBot.Features.Map.Tests/MonumentTokenMapTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,31 @@ public void TypeFor_matches_swamps_by_prefix(string token) =>
4040
public void TypeFor_matches_underwater_labs_by_prefix(string token) =>
4141
Assert.Equal(MonumentType.UnderwaterA, MonumentTokenMap.TypeFor(token));
4242

43+
[Theory]
44+
[InlineData("assets/bundled/prefabs/autospawn/monument/underwater_lab/underwater_lab_d.prefab",
45+
MonumentType.UnderwaterA)]
46+
[InlineData("assets/bundled/prefabs/autospawn/monument/medium/swamp_a.prefab", MonumentType.SwampC)]
47+
public void TypeFor_maps_path_qualified_prefab_tokens(string token, MonumentType expected) =>
48+
Assert.Equal(expected, MonumentTokenMap.TypeFor(token));
49+
4350
[Theory]
4451
[InlineData("definitely_not_a_monument")]
52+
[InlineData("assets/bundled/prefabs/autospawn/underwater-lab-base/module_900x900_2way_moonpool.prefab")]
4553
[InlineData("")]
4654
[InlineData(null)]
4755
public void TypeFor_returns_null_for_unknown_or_empty(string? token) =>
4856
Assert.Null(MonumentTokenMap.TypeFor(token));
4957

58+
[Theory]
59+
[InlineData("assets/bundled/prefabs/autospawn/underwater-lab-base/module_900x900_2way_moonpool.prefab", true)]
60+
[InlineData("assets/bundled/prefabs/autospawn/underwater-lab-base/module_1200x1200_a.prefab", true)]
61+
[InlineData("assets/bundled/prefabs/autospawn/monument/underwater_lab/underwater_lab_d.prefab", false)]
62+
[InlineData("definitely_not_a_monument", false)]
63+
[InlineData("", false)]
64+
[InlineData(null, false)]
65+
public void IsIgnored_flags_only_underwater_lab_interior_modules(string? token, bool expected) =>
66+
Assert.Equal(expected, MonumentTokenMap.IsIgnored(token));
67+
5068
[Fact]
5169
public void Every_mapped_type_has_a_package_asset()
5270
{

0 commit comments

Comments
 (0)