Skip to content

Fall back to a starting loadout when the client omits start_class_id - #38

Open
michaelinfaith wants to merge 1 commit into
themeldingwars:mainfrom
michaelinfaith:upstream/start-class-fallback
Open

michaelinfaith wants to merge 1 commit into
themeldingwars:mainfrom
michaelinfaith:upstream/start-class-fallback

Conversation

@michaelinfaith

Copy link
Copy Markdown

The client only sends start_class_id on 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 with battleframe_sdb_id 0. The zone selection paperdoll then throws "Invalid item ID" and "Enter World" can't be clicked.

  • Add SDB.GetStartingBattleframeId: returns the requested frame if it is a starting loadout in dbcharacter::CharCreateLoadout (is_starting_loadout = 1, is_dev = 0), otherwise the first starting loadout (Accord Dreadnaught), and logs a warning
  • CreateCharacter uses it instead of start_class_id directly

Tested: 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

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 eXpl0it3r 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.

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. 😉

Comment thread RIN.Core/DB/SDB/SDB.cs
Comment on lines +42 to +45
// 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.

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.

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. 😅

Comment thread RIN.Core/DB/SDB/SDB.cs
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();

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.

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.

Comment thread RIN.Core/DB/SDB/SDB.cs
Comment on lines +57 to +58
if (frameId != requestedFrameId)
Logger.LogWarning("Requested start class {Requested} is not a starting loadout, using {Fallback}", requestedFrameId, frameId);

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.

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants