前回の記事では、FiveM の Vanilla 環境を起点として、自作フレームワークである HaruuuCore をどのように作り始めるかを整理しました。 標準 resource をすべて外すのではなく、spawnmanager や sessionmanager などの基本部分は残しつつ、character、core、money などを段階的に自作する方針です。
本記事では、その続きとして、HaruuuCore の中核になる source、identifier、character_id の扱いを整理します。
FiveM で自作基盤を作る場合、接続中の player、アカウント、ゲーム内キャラクターを同じものとして扱うと、後から money、job、rank、ownership などを拡張しにくくなります。
この記事でいう「自作基盤」は、Docker や Ansible などのサーバー構築部分ではなく、FiveM の resources 配下に作っている HaruuuCore の中核 resource 群を指します。
1. 今回整理する範囲
HaruuuCore では、現在 resources/[hc] 配下に複数の resource を分けて配置しています。
1つの巨大な resource にすべてを詰め込むのではなく、character、core state、money、progression、job、UI などを役割ごとに分ける方針です。
resources/[hc]/
├── hc_minimal
├── hc_character
├── hc_core
├── hc_money
├── hc_progression
├── hc_job_delivery
├── hc_character_ui
└── hc_test今回の記事では、この中でも hc_character、hc_core、hc_money に関係する ID 設計を扱います。
具体的には、FiveM の source、player の identifier、DB 上の character_id を分けて扱う理由です。
ここを先に整理しておくと、cash / bank、rank / XP、job progress、owned vehicle、property、inventory などを、どの単位に紐づけるべきか判断しやすくなります。
2. FiveMのsourceは接続中だけの一時的なhandle
FiveM の server event では、現在イベントを発火した player を source で参照できます。
たとえば、server 側の event handler の中では、次のように source を取得できます。
RegisterNetEvent("example:server:test", function()
local src = source
print(("source=%s"):format(src))
end)source は、今接続している player を扱うためには便利です。
command、event、client への通知、現在接続中の player state など、接続中の処理では基本的に source を起点にできます。
ただし、source は永続的な player ID ではありません。
再接続すれば変わる可能性があり、DB に保存する主キーとして使うものではありません。
HaruuuCore では、source を「現在接続している player handle」として扱い、永続データの基準にはしない方針にしています。
source:
- 現在接続中の player を指す
- server event や command の処理で使う
- 再接続後も同じとは限らない
- DB の永続キーにはしない3. identifierはアカウント側の識別子
source が接続中だけの handle である一方、identifier は player のアカウント側を識別するための値として扱います。
HaruuuCore では、現時点では license identifier を優先して取得する方針にしています。
ただし、ここで重要なのは、identifier をそのまま character と同一視しないことです。
identifier は「誰のアカウントか」を表す値であり、「どのキャラクターを操作しているか」までは表しません。
identifier:
player account を識別する値
character_id:
その account が持つ game character を識別する値1つの identifier に対して、複数の character slot を持たせる場合、identifier と character は 1:1 ではありません。 そのため、money や rank を identifier に直接紐づけてしまうと、複数キャラクターを扱う設計にしたときに分離しにくくなります。
HaruuuCore では、identifier は「どの account の character か」を判定するために使い、実際の gameplay data は character_id 側へ寄せる方針にしています。
4. character_idはゲーム内キャラクターの永続ID
HaruuuCore では、character 情報を hc_characters table に保存しています。
この table の id が、HaruuuCore における character_id です。
hc_characters
├── id
├── identifier
├── slot
├── first_name
├── last_name
├── gender
├── model
├── position_x
├── position_y
├── position_z
├── heading
├── cash
├── bank
├── deleted_at
└── original_slotcharacter_id は、DB 上に永続する game character の ID です。
cash / bank、rank / XP、job progress、owned vehicle、property、inventory などは、基本的にこの character_id を基準に積み上げる想定です。
たとえば、同じ account が slot 1 と slot 3 に別々の character を持っている場合、それぞれ別の character_id を持ちます。
このとき、slot は UI 上の枠として扱い、永続的な gameplay data の主キーは character_id 側に寄せます。
id | identifier | slot | first_name | last_name | cash | bank
1 | [hidden] | 1 | Test | Character | 1700 | 5100
7 | [hidden] | 3 | HaRuuu | Test | 1000 | 5000記事やログに出す場合、実際の identifier は伏せます。
identifier は account に紐づく情報なので、公開記事では [hidden] のように置き換えて扱うのが安全です。
5. 3つのIDを混ぜると何が困るか
source、identifier、character_id は、それぞれ役割が違います。
ここを曖昧にしたまま実装すると、最初は動いていても、character slot、money、rank、job などを増やしたときに破綻しやすくなります。
source:
今接続している player handle
identifier:
player account を識別する値
character_id:
DB 上の game character を識別する値source を DB の主キーとして使うと、再接続や server session をまたいだときに永続データとして扱えません。
接続中の処理には使えますが、保存先のキーとしては不向きです。
identifier に money や rank を直接紐づけると、account 単位のデータになります。
1 account 1 character の設計なら成立しますが、複数 character slot を持たせる場合、character ごとの所持金や進行度を分けにくくなります。
character_id を使わないと、「現在選択中の character に対して money を増やす」「この character の rank を上げる」「この character が job を完了した」といった処理を安全に表現しにくくなります。
値 役割 永続データの基準
source 接続中の player handle しない
identifier account の識別 account 所有判定に使う
character_id game character の識別 money / rank / job の基準にする6. hc_characterはcharacter DB/APIを担当する
HaruuuCore では、character に関する DB 操作と API を hc_character に集約しています。
ここでは、identifier に紐づく character の作成、取得、選択、削除、slot payload の生成などを扱います。
hc_character
├── identifier の取得
├── hc_characters table の管理
├── character 作成
├── character 選択
├── character 論理削除
├── slot 1〜3 の payload 生成
└── character API / exportshc_character は、character data の source of truth として扱います。
active な character、deleted な character、empty slot など、character DB に関する判定はこの resource の責務です。
一方で、hc_character は「今この player が gameplay ready かどうか」までは持ちません。
それは接続中の player state の話なので、hc_core 側の責務として分けます。
hc_character:
character DB / API の責務
hc_core:
接続中 player の state 管理の責務7. hc_coreはPlayers[source]の状態を持つ
hc_core は、接続中の player state を管理する resource として設計しています。
DB を直接操作する resource ではなく、Players[source] を持ち、現在の接続状態、identifier、選択中 characterId、ready 状態などを扱います。
Players[source]
├── source
├── identifier
├── characterId
├── state
├── ready
└── joinedAtHaruuuCore では、接続直後に character が未選択である状態を異常扱いしません。
character selection 前であれば、character_id がないのは正常な状態です。
joining
↓
character_selecting
↓ select character
readyこの設計により、playerReady の時点で selected character がなくても、すぐに error として扱わず、character_selecting として管理できます。
UI や command から character を選択したタイミングで、hc_core の characterId が更新され、ready な状態へ進みます。
逆に、slot 一覧取得、character 作成、character 削除は、参照または mutation であって、selected character を変更する操作ではありません。
HaruuuCore では、Select したときだけ hc_core の selected characterId を変える方針にしています。
8. hc_moneyはcharacter_id基準でcash/bankを扱う
money system も、ID 設計の影響を強く受ける部分です。
HaruuuCore では、cash / bank を source や identifier に直接紐づけず、選択中の character_id に紐づけます。
source
↓
hc_core:getCharacterId(source)
↓
character_id
↓
hc_characters.cash / hc_characters.bank現時点では、cash / bank は hc_characters table に保存しています。
将来的に money table を分ける可能性はありますが、基準になる ID は character_id です。
character が未選択の状態では、money sync は行いません。 これは異常ではなく、まだ money の対象になる character が決まっていない状態として扱います。
selected character_id がある:
cash / bank を取得して表示へ同期する
selected character_id がない:
character_not_selected として sync を skip する表示面では、GTA の ESC menu 側に cash / bank を同期しつつ、通常 gameplay 中の常時表示 money HUD は抑制しています。 このあたりの詳細は、別の記事として整理する予定です。
9. playerConnectingでcharacterを読み込まない理由
FiveM では、接続時に playerConnecting を使って player の接続を検知できます。
ただし、HaruuuCore では playerConnecting の時点で character を読み込む方針にはしていません。
理由は、接続初期の event と、実際に gameplay 側の player state を確定させる flow を分けたいためです。 接続ログとして identifier を確認することと、character を選択して money や job の対象を決めることは、別の処理として扱います。
playerConnecting:
接続ログ、identifier 確認の入口
playerReady:
標準 spawn 後、HaruuuCore 側の初期 state 整理
character selection:
selected character_id を確定する操作接続初期の source を過信して character load や money sync を始めると、初期化順序の切り分けが難しくなります。 そのため、HaruuuCore では playerConnecting では character を確定させず、接続後の flow で character selection を行う設計にしています。
10. 現時点の責務分離
ここまでの整理を踏まえると、現在の HaruuuCore では、character、core、money の責務を次のように分けています。
hc_character:
- character DB
- identifier に紐づく character 一覧
- slot payload
- create / select / delete
- logical delete
hc_core:
- Players[source]
- source / identifier / characterId
- state
- ready
- character selection 後の selected characterId
hc_money:
- character_id based cash / bank
- DB上の money 更新
- GTA側表示への同期
- character未選択時の sync skipこの分け方にしておくと、今後の機能追加でも判断基準が明確になります。
たとえば、rank / XP は account ではなく character に紐づけたいので character_id 基準にできます。
job reward も、現在選択中の character に対して支払う処理として表現できます。
また、UI から slot 一覧を見たり、新しい character を作成したりしても、それだけでは selected character は変わりません。 selected character が変わるのは、明示的に Select したときだけです。 この線引きにより、UI 操作、core state、money sync の関係を追いやすくしています。
11. 今後の拡張につなげる
今回の整理で、HaruuuCore の中で何を source に紐づけ、何を identifier に紐づけ、何を character_id に紐づけるかが見えやすくなりました。
source:
接続中の処理、client event、temporary state
identifier:
account 所有判定、character 一覧の取得
character_id:
money、rank、job、ownership、inventory、statsこの分離は、今後の character slot、論理削除、money sync、rank / XP、delivery job、property、inventory などの土台になります。
特に、複数 character を前提にする場合、早い段階で identifier = character としない設計にしておくことが重要です。
次回以降は、この ID 設計の上に、character slot と logical delete、GTA ESC menu を使った money 表示、NUI character UI の cleanup、rank / XP と delivery job などを順に整理していきます。