キャラクター枠を先に決める理由
FiveM の Vanilla 環境で自作基盤を作る場合、プレイヤーが接続したあとに、どのキャラクターを操作するのかを自分で決める必要があります。
HaruuuCore では、接続中の player を表す source、アカウント側の識別子である identifier、DB 上のゲーム内キャラクターを表す character_id を分けて扱う方針にしています。
その次に必要になったのが、1つの identifier に対して複数のキャラクター枠を持たせるための基準です。
どの slot にキャラクターを作るのか、どの character を選択中として扱うのか、削除した character をどう残すのかを決めないと、money、rank、job などの後続機能を安全に積み上げにくくなります。
この作業では、hc_character に character slot と論理削除の土台を追加し、hc_core や hc_money と接続できる状態まで確認しました。
identifierとcharacter slotの関係
identifier は、プレイヤーのアカウント側を識別する値として扱います。
HaruuuCore では、現時点では license identifier を優先して取得します。
ただし、identifier は character そのものではありません。
character slot は、1つの identifier が持てるキャラクター枠です。
現在は slot 1〜3 を扱い、rank や XP による slot unlock は入れていません。
すべての slot を最初から利用可能な枠として扱います。
identifier
├── slot 1
├── slot 2
└── slot 3この時点で重要なのは、slot を永続データの主キーにしないことです。
slot は UI 上の枠や選択位置として使い、money、rank、job などの gameplay data は character_id を基準にします。
slotはUI上の枠として扱う
HaruuuCore では、slot を「キャラクター選択画面に表示する枠」として扱います。 slot 1 に作成した character は slot 1 のカードに表示され、slot 2 が空なら新規作成できる枠として返します。
ただし、slot 自体を character の永続 ID にはしません。
character の実体は hc_characters.id であり、slot はその character をどの枠に表示するかを表す値です。
slot:
UI上の枠
character_id:
DB上のcharacter実体
identifier:
どのaccountが所有するcharacterかを判定する値この分け方にしておくと、削除後に同じ slot を再利用しても、過去の character と新しい character を character_id で区別できます。
active characterとdeleted characterの分離
character 一覧や slot payload では、通常利用できる character だけを返します。
HaruuuCore では、deleted_at が入っていない character を active character として扱います。
active character:
deleted_at IS NULL
slot IS NOT NULL
deleted character:
deleted_at IS NOT NULL
slot IS NULLdeleted character は通常の character 一覧や character selection payload には出しません。 これにより、削除済み character を誤って選択したり、job や money の対象にしたりする状態を避けます。
削除済み character の復元や管理者向け一覧は、現時点では扱いません。 まずは通常のプレイ導線から削除済み character を外すことを優先しています。
deleted_atで論理削除する
character を削除するとき、DB row は物理削除しません。
HaruuuCore では deleted_at を入れ、slot を NULL にする論理削除にしています。
delete character:
original_slot = 削除前のslot
slot = NULL
deleted_at = CURRENT_TIMESTAMP物理削除にしない理由は、削除という操作を後から追跡しやすくするためです。 現時点では削除済み character の復元機能は作っていませんが、DB 上に削除状態を残しておくことで、後から復元や監査の設計を検討できます。
一方で、slot は空けます。 これにより、削除後に同じ slot へ新しい character を作成できます。
original_slotを残す理由
削除時には、削除前の slot を original_slot に保存します。
slot は NULL にするため、削除後の row だけを見ると、元々どの枠にいた character なのか分からなくなります。
hc_characters
├── id
├── identifier
├── slot
├── original_slot
├── deleted_at
└── ...original_slot は、削除済み character の履歴を見たい場合の補助情報です。
通常の character selection では使いません。
active character の表示や選択は、あくまで slot が入っている character だけを対象にします。
この扱いにより、slot の再利用と削除履歴の保持を分けられます。
現在選択中のcharacterは削除しない
現在の実装では、選択中の character は削除できないようにしています。
削除しようとすると character_currently_selected として拒否します。
/deletechar 1
character_currently_selected選択中 character を削除するには、単に DB 上で deleted にするだけでは足りません。
hc_character の cache、hc_core の Players[source].characterId、hc_money の同期対象、進行中 job、spawn や待機状態の扱いまで整理する必要があります。
そのため、現時点では別 character に切り替えてから削除する方針にしています。 選択中 character の削除は、別の設計課題として残します。
slot payloadをUI向けに組み立てる
character selection UI では、単に active character の配列を返すだけでは不十分です。 slot 1〜3 のそれぞれが occupied なのか empty なのか、作成可能なのか、削除可能なのか、現在選択中なのかを UI が判断できる形にする必要があります。
slot payload:
slot
state
character
isSelected
canCreate
canDeleteHaruuuCore では、slot state は現時点で occupied と empty のみです。
locked slot や progression unlock は扱いません。
occupied slot:
canCreate = false
canDelete = true
empty slot:
canCreate = true
canDelete = false
selected character:
isSelected = true
canDelete = false選択中 character は削除できないため、occupied であっても canDelete=false にします。
UI 側はこの payload を見て、作成ボタンや削除ボタンを出し分けます。
自動作成しない選択用payload
HaruuuCore には、既存互換用の getCharacterSlots と、character selection 用の getCharacterSlotsForSelection を分けています。
getCharacterSlots:
既存互換
ensureCharacter(source) を呼ぶ
未ロード時に slot=1 を自動ロードまたは作成する可能性がある
getCharacterSlotsForSelection:
character selection 用
ensureCharacter(source) を呼ばない
slot=1 を自動作成しない
slot=1 を自動選択しないcharacter selection UI の初期表示では、まだ character を選んでいない状態を正常に扱う必要があります。
そのため、UI 用の payload では ensureCharacter(source) を呼ばず、selectedCharacterId=nil や selectedSlot=nil を正常な状態として返します。
これにより、接続直後に自動で slot 1 が作成されたり、勝手に selected character が決まったりする状態を避けられます。
createとselectを分ける
character の作成と選択は別の操作として扱います。
createCharacter は指定 slot に character を作成するだけで、自動選択しません。
createCharacter:
characterを作成する
selected characterは変更しない
hc_core stateは変更しない
money targetは変更しない
selectCharacter:
active characterを選択する
selected characterを変更する
hc_coreへcharacterSelectedを通知する
hc_moneyの同期対象が変わる作成した瞬間に自動選択すると、UI 上で「作成」と「その character で開始する」が同じ操作になってしまいます。 HaruuuCore では、作成後に明示的に select したときだけ selected character が変わるようにしています。
この線引きにより、slot browsing 中に money target が変わったり、core state が ready へ進んだりする状態を避けられます。
hc_characterとhc_coreの境界
hc_character は character DB、character API、slot payload の source of truth として扱います。
ただし、接続中 player の state は hc_core が持ちます。
hc_character:
character DB
active / deleted 判定
slot payload
create / select / delete
hc_core:
Players[source]
identifier
characterId
state
readyselectCharacter が成功したとき、hc_character は hc_core:server:characterSelected を発火します。
hc_core はこの event を受けて、Players[source].characterId を更新し、ready state へ進めます。
selectCharacter(source, slot)
↓
hc_core:server:characterSelected
↓
Players[source].characterId = character.id
Players[source].state = ready
Players[source].ready = trueDB 操作を hc_core に寄せず、player state を hc_character に寄せないことで、責務の境界を追いやすくしています。
moneyとの接続
hc_money は character_id ベースで cash / bank を扱います。
slot 一覧を見たり、新しい character を作成したりするだけでは、money の対象は変わりません。
money の同期対象が変わるのは、character を明示的に select したときです。
hc_money は character selection の結果を受けて、選択中 character の money を同期します。
slot browsing:
money targetは変わらない
create character:
money targetは変わらない
select character:
money targetがselected character_idへ変わるcharacter 未選択時の money sync skip は異常ではありません。 まだ money の対象になる character が決まっていない状態として扱います。
確認した操作
character slot と論理削除の挙動は、server command で確認しました。
ここでは、UI ではなく hc_character の API と state の確認を目的にしています。
/charslots_select/charslots_select では、slot 1〜3 の状態を確認します。
character 未選択状態では、selectedCharacterId=nil と selectedSlot=nil を正常に返します。
/createchar 2 Jiro Sato
/charslots_selectcharacter 作成後、slot 2 は occupied になります。 ただし、作成した character は自動選択されません。
/selectchar 2
/hcstate
/money
/rankcharacter を選択すると、hc_core の characterId が selected character に変わり、state は ready になります。
money と rank も selected character_id を基準に確認します。
/selectchar 1
/deletechar 2
/charslots_select選択中ではない slot 2 を削除すると、slot 2 は empty になります。 削除済み character は通常 payload から消え、同じ slot を再利用できる状態になります。
キャラクター枠の実装で到達した状態
この作業で、HaruuuCore の hc_character は、単一 character の自動作成だけではなく、slot 1〜3 を前提にした character 管理へ進みました。
確認済み:
slot 1〜3 の payload を返せる
active character と deleted character を分けられる
character を作成できる
character を明示的に選択できる
character を論理削除できる
削除後に同じ slot を再利用できる
現在選択中 character の削除を拒否できる
select 後に hc_core と hc_money へ接続できるまだ扱っていないものもあります。 選択中 character の削除、削除済み character の復元、last selected restore、appearance 保存、DB 座標 spawn、locked slot、progression unlock などは未実装です。
ただし、character slot、active / deleted の区別、create / select / delete の分離が入ったことで、character selection UI や money、rank、job へ接続するための土台は作れました。