Both default skins spell the chin width key chin_13. skinchanger only
knows chin_1 through chin_4, so the value never reaches the ped and the
key is stored as junk in users.skin.
It also breaks the ped. SetFace reads every feature through
Normalise(weight, 10), which divides, so a missing chin_3 raises
"attempt to perform arithmetic on a nil value" at feature index 17.
Everything after that in ApplySkin is skipped: the remaining features,
the eye colour, the head overlays, the components and the props.
The character selection query used `WHERE identifier LIKE 'char%:<license>'`. Every
multicharacter identifier starts with `char`, so the primary key range covers the whole
table and it degrades to a full scan on every join.
Build the exact identifiers (`char1:<license>` .. `charN:<license>` for the player's
slots) and query with `IN`, which hits the primary key directly.
Measured on a 50k row users table (MariaDB 12.3.2): rows examined 45648 -> 3, query
time 52.7 ms -> 3.2 ms. In-game the selection screen shows the same characters in the
same slots.
ESX.PlayerId does not exist - the shared object only defines ESX.playerId,
and these were the only two places in the repo using the capitalised name.
The nil is passed to the natives as player index 0, so neither call touched
the local player. SetPlayerControl at line 266 already used the correct
spelling, which is why controls were re-enabled after spawning but never
disabled during character selection.
Measured on artifact 25770, local player index 128:
SetPlayerControl(ESX.PlayerId, false, 0) -> IsPlayerControlOn stays 1
SetPlayerControl(ESX.playerId, false, 0) -> IsPlayerControlOn becomes false
HideHud also ignored its argument: hidePlayers was hardcoded to true and the
volume override was set to 0.0 for both hiding and unhiding. Correcting only
the name would have made the mute apply for the first time and never lift it,
so the reset value and the argument have to be handled together.
HideComponents stores the value GetHudComponentSize returns and reads back
size.x and size.z when restoring, but the meaningful fields are x and y - the
hide branch a few lines above tests exactly those. size.z is 0.0, so the
components are restored at height zero and stay invisible for the rest of the
session.
Measured on artifact 25770 after character selection, components 11 and 12:
before: x=0.218 y=0.000 (width restored, height lost)
after: x=0.218 y=0.389