Repository navigation
Fall back to a starting loadout when the client omits start_class_id - #38
michaelinfaith wants to merge 1 commit into
Conversation
The client only sends start_class_id on the first pass through the character creator. Re-entering it from the customize screen omits the field, and RIN stored battleframe_sdb_id 0, which makes the zone selection paperdoll throw "Invalid item ID" and leaves Enter World unclickable. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eXpl0it3r
left a comment
There was a problem hiding this comment.
Thanks for tracking this down!
I checked it against the 1962 client and the only character creation capture we have (BurstBiscuit's Fiddler session from 2017). The client sends "start_class_id": 75775 there and the character list returns it as frame_sdb_id: 75775, so matching against frame_id is correct. The five is_starting_loadout = 1, is_dev = 0 rows are also exactly the five frames the client hardcodes in c_BattleFrameIDs, as such the filter fits what the creator offers.
A few smaller things inline, mainly about when the client actually drops the field.
Also, please remove the Co-Author attribution, we're not being paid to advertise for Anthropic. 😉
| // The client omits start_class_id when the player re-enters the creator from the customize screen. | ||
| // Returns the requested frame when it is a starting loadout, otherwise the first starting loadout | ||
| // (Accord Dreadnaught), so a character never ends up with battleframe_sdb_id 0, which breaks the | ||
| // zone selection paperdoll ("Invalid item ID") and leaves "Enter World" dead. |
There was a problem hiding this comment.
The explanation doesn't quite match what the 1962 CharacterCreation.lua does. Going back from Customize to Select keeps g_currentFrame, OnCancel never touches it.
The field goes missing when g_currentFrame is nil, because BuildCreateHTTPRequest then just drops the key. Only OnCloseCC() resets it, which runs after a successful create, on Back from the Select screen and when the random name request fails. OnCloseCC() however doesn't call ClearCharacterSelection(), so the Next button stays visible. When you open the creator a second time in the same session, you can click Next without picking a frame and end up in Customize with no frame at all.
Can you adjust the comment, commit message and PR description to that? I'd also only keep the comment to the why, the paperdoll and "Enter World" symptoms are better placed in the commit message. Claude loves to write books in code comments. 😅
| var result = await DBCall(conn => conn.QueryAsync<int>(SELECT_SQL, new { requestedFrameId }), | ||
| exception => Logger.LogError($"Error looking up starting battleframe for requested frame {requestedFrameId} due to: {exception}")); | ||
|
|
||
| var frameId = result.FirstOrDefault(); |
There was a problem hiding this comment.
If the lookup comes back empty (e.g. SDB not imported), this returns 0, logs "using 0" and we store battleframe_sdb_id 0 again, so the exact bug this PR fixes, just quieter. And if the query throws, DBCall returns default, result is null and FirstOrDefault() throws an ArgumentNullException.
I'd rather fail loudly here and throw when no starting loadout is found, than create a broken character.
| if (frameId != requestedFrameId) | ||
| Logger.LogWarning("Requested start class {Requested} is not a starting loadout, using {Fallback}", requestedFrameId, frameId); |
There was a problem hiding this comment.
For the case this PR is about, this logs "Requested start class 0 is not a starting loadout", which is a bit misleading. Maybe log requestedFrameId == 0 separately as "client didn't send start_class_id" and keep this message for actual invalid ids?
The client only sends
start_class_idon the first pass through the character creator. When a player goes back into the creator from the customize screen, the field is missing, so RIN stores a battleframe loadout withbattleframe_sdb_id0. The zone selection paperdoll then throws "Invalid item ID" and "Enter World" can't be clicked.SDB.GetStartingBattleframeId: returns the requested frame if it is a starting loadout indbcharacter::CharCreateLoadout(is_starting_loadout = 1,is_dev = 0), otherwise the first starting loadout (Accord Dreadnaught), and logs a warningCreateCharacteruses it instead ofstart_class_iddirectlyTested: builds with no new warnings. This commit has been running on my local single-player stack (Calldown with PIN) since 2026-10-13.
Written with AI assistance (Claude Code); I reviewed it.
🤖 Generated with Claude Code