fix(bugs): some gacha and visual issues

This commit is contained in:
capyb2222 committed 2026-09-08 17:35:42 -07:00
1 parent 116ea108f9
commit 138b5a9484
5 files changed
+57 -2

No files matched your search

+7
View File
@@ -175,6 +175,13 @@ public sealed class TutorialGuideGroupExcel
public uint GroupID { get; set; }
}
public sealed class GachaCeilingExcel
{
public string GachaType { get; set; } = string.Empty;
public List<uint> CeilingItemList { get; set; } = [];
}
// maps an item id to its main/sub type. the client NPEs on an item it cannot type.
public sealed class ItemConfigExcel
{
+5
View File
@@ -114,4 +114,9 @@ public sealed partial class GameData
row.TryGetPropertyValue(name, out var node) && node is not null && node.GetValueKind() is JsonValueKind.Number
? node.GetValue<uint>()
: null;
private static string? Str(JsonObject row, string name) =>
row.TryGetPropertyValue(name, out var node) && node is not null && node.GetValueKind() is JsonValueKind.String
? node.GetValue<string>()
: null;
}
+6
View File
@@ -120,6 +120,9 @@ public sealed partial class GameData
public IReadOnlyList<uint> TutorialGuideIds { get; private set; } = [];
// the standard banner's guaranteed 5* pool
public IReadOnlyList<uint> GachaCeilingAvatars { get; private set; } = [];
// every relic / lightcone id the client can resolve a type for
public IReadOnlySet<uint> KnownRelicIds { get; private set; } = new HashSet<uint>();
@@ -207,6 +210,9 @@ public sealed partial class GameData
data.MainMissions = data.Merge<MainMissionExcel>("MainMission.json", r => UInt(r, "MainMissionID"), required: false);
data.TutorialIds = [.. data.Merge<TutorialExcel>("TutorialData.json", r => UInt(r, "TutorialID"), required: false).Keys];
data.TutorialGuideIds = [.. data.Merge<TutorialGuideGroupExcel>("TutorialGuideGroup.json", r => UInt(r, "GroupID"), required: false).Keys];
data.GachaCeilingAvatars = [.. data
.Merge<GachaCeilingExcel>("GachaCeiling.json", r => Str(r, "GachaType") == "Normal" ? 0u : null, required: false)
.Values.SelectMany(r => r.CeilingItemList)];
data.KnownRelicIds = data.Merge<ItemConfigExcel>("ItemConfigRelic.json", r => UInt(r, "ID"), required: false).Keys.ToHashSet();
data.KnownLightconeIds = data.Merge<ItemConfigExcel>("ItemConfigEquipment.json", r => UInt(r, "ID"), required: false).Keys.ToHashSet();
+25 -2
View File
@@ -8,6 +8,8 @@ namespace CapySR.GameServer.Handlers;
[Handlers]
public static class MiscHandlers
{
private const uint StandardBannerId = 1001;
// client settings the game stores server side (auto battle, speed, ...)
public static Task OnGetAllServerPrefsData(PlayerSession session, GetAllServerPrefsDataCsReq request)
{
@@ -63,8 +65,29 @@ public static class MiscHandlers
public static Task OnGetVideoVersionKey(PlayerSession session, GetVideoVersionKeyCsReq request) =>
session.SendAsync(new GetVideoVersionKeyScRsp { Retcode = 0 });
public static Task OnGetGachaInfo(PlayerSession session, GetGachaInfoCsReq request) =>
session.SendAsync(new GetGachaInfoScRsp { Retcode = 0 });
// an empty list leaves the gacha red-dot filter indexing nil every UI tick, which throws
// out of UIManager.Tick every frame. hand it the standard banner and nothing else.
public static Task OnGetGachaInfo(PlayerSession session, GetGachaInfoCsReq request)
{
var ceiling = new GachaCeiling { CeilingNum = 300, IsClaimed = false };
foreach (var avatarId in session.World.Data.GachaCeilingAvatars)
{
ceiling.AvatarList.Add(new GachaCeilingAvatar { AvatarId = avatarId, RepeatedCnt = 300 });
}
var response = new GetGachaInfoScRsp { Retcode = 0, GachaRandom = 0 };
response.GachaInfoList.Add(new GachaInfo
{
GachaId = StandardBannerId,
GachaCeiling = ceiling,
BeginTime = 0,
EndTime = 2524608000,
});
return session.SendAsync(response);
}
public static Task OnGetDailyActiveInfo(PlayerSession session, GetDailyActiveInfoCsReq request) =>
session.SendAsync(new GetDailyActiveInfoScRsp { Retcode = 0 });
+14
View File
@@ -36,6 +36,20 @@ public class GameDataTests(ITestOutputHelper output)
Assert.NotEmpty(data.Equipment);
}
// an empty banner leaves the client's gacha red dot throwing once a frame
[Fact]
public void ReadsStandardBannerCeiling()
{
var data = TryLoad(output);
if (data is null)
{
return;
}
Assert.NotEmpty(data.GachaCeilingAvatars);
Assert.Contains(1003u, data.GachaCeilingAvatars);
}
[Fact]
public void ParsesMarch7th()
{