Files
2026-08-13 18:59:54 -04:00

123 lines
4.0 KiB
Lua

if not TowBarMod then TowBarMod = {} end
TowBarMod.Persistence = TowBarMod.Persistence or {}
local Persistence = TowBarMod.Persistence
local RegistryName = "TowBar.PersistentPairsV2"
local function getRegistry()
if not ModData or not ModData.getOrCreate then return nil end
local registry = ModData.getOrCreate(RegistryName)
registry.pairs = registry.pairs or {}
return registry
end
local function getSqlId(vehicle)
if not vehicle or not vehicle.getSqlId then return nil end
local ok, sqlId = pcall(function() return vehicle:getSqlId() end)
if ok and type(sqlId) == "number" and sqlId >= 0 then return sqlId end
return nil
end
local function makeKey(kind, towingSqlId, towedSqlId)
if type(kind) ~= "string" or type(towingSqlId) ~= "number" or type(towedSqlId) ~= "number" then
return nil
end
return kind .. ":" .. tostring(towingSqlId) .. ":" .. tostring(towedSqlId)
end
local function transmit()
if isServer and isServer() and ModData.transmit then
ModData.transmit(RegistryName)
end
end
function Persistence.savePair(kind, towingVehicle, towedVehicle, attachmentA, attachmentB, heightLevel)
local towingSqlId = getSqlId(towingVehicle)
local towedSqlId = getSqlId(towedVehicle)
local key = makeKey(kind, towingSqlId, towedSqlId)
local registry = getRegistry()
if not key or not registry then return false end
local previous = registry.pairs[key]
if previous and previous.attachmentA == attachmentA and previous.attachmentB == attachmentB
and previous.heightLevel == heightLevel then
return true
end
registry.pairs[key] = {
kind = kind,
towingSqlId = towingSqlId,
towedSqlId = towedSqlId,
attachmentA = attachmentA,
attachmentB = attachmentB,
heightLevel = heightLevel
}
transmit()
return true
end
function Persistence.getPair(kind, towingVehicle, towedVehicle)
local key = makeKey(kind, getSqlId(towingVehicle), getSqlId(towedVehicle))
local registry = getRegistry()
return key and registry and registry.pairs[key] or nil
end
function Persistence.removePair(kind, towingVehicle, towedVehicle)
local key = makeKey(kind, getSqlId(towingVehicle), getSqlId(towedVehicle))
local registry = getRegistry()
if not key or not registry or registry.pairs[key] == nil then return false end
registry.pairs[key] = nil
transmit()
return true
end
function Persistence.forEachPair(kind, callback)
local registry = getRegistry()
if not registry or not callback then return end
for _, record in pairs(registry.pairs) do
if type(record) == "table" and record.kind == kind then callback(record) end
end
end
function Persistence.notePeerAvailability(state, available)
state = state or {}
if not available then
state.peerMissing = true
return state
end
if state.peerMissing then
-- A missing vehicle means its cell was not loaded. When both members
-- return, treat the absent native constraint as load recovery rather
-- than a runtime break of a previously confirmed pair.
state.peerMissing = nil
state.confirmed = false
state.pendingUntil = nil
state.unlinkedSince = nil
end
return state
end
function Persistence.advanceRecoveryState(state, now, linked, occupied, retryMs, breakMs)
state = state or {}
if linked then
state.confirmed = true
state.pendingUntil = nil
state.unlinkedSince = nil
return state, "adopt"
end
if occupied then
state.unlinkedSince = nil
return state, "wait"
end
if state.confirmed then
state.unlinkedSince = state.unlinkedSince or now
if now - state.unlinkedSince >= breakMs then return state, "break" end
return state, "wait"
end
if not state.pendingUntil or now >= state.pendingUntil then
state.pendingUntil = now + retryMs
return state, "restore"
end
return state, "wait"
end
Persistence.getSqlId = getSqlId