fix(client): fail fast on boot/frontend fallbacks (P4)

- MapService load failure is no longer swallowed.
- Character creation fails on unknown selected class instead of ?? 1.
- No prefilled 'Wanderer'/'password' credentials; empty account name is an
  error; unknown MCP class id throws instead of ?? 'so'.
- CommandMapper no longer sends BeltUse with fabricated itemId slot+1.
This commit is contained in:
troytt 2026-10-01 01:41:14 +00:00
parent 7db8c3c9b8
commit 3d20dc9c77
3 changed files with 17 additions and 20 deletions

View File

@ -253,8 +253,9 @@ export function createInitialFlowState(initialRoster: RosterCharacter[] = []): B
stageTicks: 0,
globalTick: 0,
accountName: 'Wanderer',
accountPassword: 'password',
// No prefilled credentials: the user (or a configured bot) must type them.
accountName: '',
accountPassword: '',
loginError: null,
campfireClasses,
@ -338,7 +339,8 @@ export function cancelConnecting(state: BnetFlowState): void {
export function submitBattleNetLogin(state: BnetFlowState): boolean {
if (state.stage !== 'bnet_login') return false
if (!state.accountName.trim()) {
state.accountName = 'Wanderer'
state.loginError = 'Account name required'
return false
}
state.loginError = null
state.stageTicks = 0
@ -589,7 +591,8 @@ export function charSummaryToRosterCharacter(summary: {
readonly hardcore: boolean
readonly ladder: boolean
}): RosterCharacter {
const classId = CHAR_CLASS_ID_TO_CAMPFIRE[summary.charClass] ?? 'so'
const classId = CHAR_CLASS_ID_TO_CAMPFIRE[summary.charClass]
if (!classId) throw new Error(`MCP character "${summary.name}" has unknown class id ${summary.charClass}`)
const spec = CAMPFIRE_CLASS_SPECS[classId]
return {
id: `${spec.heroToken}-${summary.name}`,

View File

@ -634,18 +634,9 @@ export class CommandMapper {
if (event.key === '1' || event.key === '2' || event.key === '3' || event.key === '4') {
const slotIndex = Number(event.key) - 1
event.preventDefault?.()
if (this.hudModel) {
// BeltUse needs the real server item id of the potion, which only HudModel (ClientWorld) knows.
if (!this.hudModel) return false
this.hudModel.useBeltSlot(slotIndex, Boolean(event.shiftKey))
} else {
this.dispatchRateLimited(
{
type: 'BeltUse',
itemId: slotIndex + 1,
shift: Boolean(event.shiftKey),
},
nowMs,
)
}
return true
}

View File

@ -193,11 +193,12 @@ async function bootPlayApp(): Promise<PlayAppController> {
})
await hudManager.load('/ui')
let mapService: MapService | null = null
// Fail fast: without the packed act tiles there is no 1.13c map (automap, collision, picking).
let mapService: MapService
try {
mapService = await MapService.fromPackedUrl('/act-packs/tiles')
} catch {
mapService = null
} catch (err) {
throw new Error(`MapService: failed to load /act-packs/tiles: ${err instanceof Error ? err.message : String(err)}`)
}
// Kick off asynchronous GPU atlas preloading for WebGL2 world rendering
@ -468,9 +469,11 @@ async function bootPlayApp(): Promise<PlayAppController> {
dz: 5,
as: 6,
}
const classId = st.selectedClass === null ? undefined : classMap[st.selectedClass]
if (classId === undefined) throw new Error(`Unknown selected class ${String(st.selectedClass)}`)
const chars = await session.createCharacter({
name,
classId: classMap[st.selectedClass] ?? 1,
classId,
hardcore: st.isHardcore,
expansion: st.isExpansion,
ladder: st.isLadder,