0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FiveMのStatebagを使う

Posted at

Statebagとは

Statebagは、効率的に状態を管理・同期するための仕組みです。
各エンティティ(プレイヤー、車両、オブジェクト)に紐づけられたデータをサーバー・クライアント間でやり取りできます。

従来のように

サーバークライアント
サーバークライアント

で都度イベントを発行していた方法に比べて、簡潔に書けます。

グローバルStatebag

他のクライアント、サーバー、リソースでも参照できるグローバルなStatebagです。

サーバー側

server.lua
local globalState = GlobalState.testState = 'Test'

クライアント側

client.lua
print(GlobalState.testState) --Test と出力される
GlobalState.testState = 'Sample' --書き換え不可

クライアント側で書き換えることは出来ません。

機密性の高いもの、共有したくないデータを持たせるのはやめましょう。

書き込み(state:set)

local playerId = source
local playerEntity = GetPlayerPed(playerId)
Entity(playerEntity).state:set("isSpeaking", true, true)-- 第3引数true: グローバル同期

読み取り

local playerPed = GetPlayerPed(-1)
local isSpeaking = Entity(playerPed).state.isSpeaking

stateの変更を監視

AddStateBagChangeHandlerを使えば変更があったときだけ反応できます。

AddStateBagChangeHandler("blockTasks", nil, function(bagName, key, value) 
    local entity = GetEntityFromStateBagName(bagName)
    if entity == 0 then return end
    while not HasCollisionLoadedAroundEntity(entity) do
        if not DoesEntityExist(entity) then return end
        Wait(250)
    end
    SetEntityInvincible(entity, value)
    FreezeEntityPosition(entity, value)
    TaskSetBlockingOfNonTemporaryEvents(entity, value)
end)

Statebagを使わない書き方

クライアント側

sample_client.lua
local flashlightStates = {}

RegisterNetEvent("updateFlashlightState", function(playerId, state)
    flashlightStates[playerId] = state
end)

CreateThread(function()
    while true do
        Wait(100)
        local ped = PlayerPedId()
        local flashlightOn = IsFlashLightOn(ped)

        if flashlightState ~= flashlightOn then
            flashlightState = flashlightOn
            TriggerServerEvent("syncFlashlightState", flashlightOn)
        end
    end
end)

サーバー側

sample_server.lua
RegisterNetEvent("syncFlashlightState", function(state)
    local src = source
    TriggerClientEvent("updateFlashlightState", -1, src, state)
end)

Statebagを使った書き方

クライアント側

sample_client.lua
AddStateBagChangeHandler('flashlightOn', nil, function(bagName, key, value)
    local netId = tonumber(bagName:gsub("entity:", ""), 10)
    local entity = NetworkGetEntityFromNetworkId(netId)

    if DoesEntityExist(entity) then
        print(("Entity %s flashlight: %s"):format(netId, tostring(value)))
        --描画処理を加えたり
    end
end)

CreateThread(function()
    while true do
        Wait(100)
        local ped = PlayerPedId()
        local flashlightOn = IsFlashLightOn(ped)

        if flashlightState ~= flashlightOn then
            flashlightState = flashlightOn
            Entity(ped).state:set("flashlightOn", flashlightOn, false)
        end
    end
end)

サーバー側

不要です。

非推奨なこと

ネストされたプロパティの直接変更

-- これは動作しない(変更が同期されない)
Entity(x).state.x.y = 'b'

Statebagは内部的に浅い構造で管理しているので同期されません。

非効率なデータアクセス

-- 非効率な例(同じデータを2回デシリアライズする)
local y = Entity(x).state.x.y
local z = Entity(x).state.x.z

解決方法

ネスト構造を避けてフラットなキー名を使用。

Entity(x).state['x:y'] = 'b'
Entity(x).state['x:z'] = 'c'

一度だけデシリアライズしましょう。

local xState = Entity(x).state.x
local y = xState.y
local z = xState.z

他のプラグインと競合しそうな単純な名前も避けたほうが良いです。

statebagのレート制限

server.cfgにこれを追加します

set rateLimiter_stateBag_rate 2000
set rateLimiter_stateBag_burst 3000

これにより不具合を避けたり、プレイヤーがキックされるのを減らせます。

まとめ

便利な半面、構造の複雑化が増すこともあるのでうまいこと使い分けるのが良いと思いました。
イベントとの使い分けを考える必要がありそうです。

引用

docs.fivem.net
How to use State Bags - forum.cfx.re
wiki.bigdaddyscripts.com

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?