mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-08-29 02:01:40 +00:00
ENH - make phone identity and SIM cards configurable
This commit is contained in:
+160
-29
@@ -2,6 +2,8 @@ Bridge.Database.AfterMigration("sky_phone", function()
|
||||
|
||||
SkyPhoneSim = {}
|
||||
|
||||
local unique_phones = Config.Phone.Unique ~= false
|
||||
local sim_cards_enabled = Config.Sim.Enabled ~= false
|
||||
local pending_insertions = {}
|
||||
local operation_locks = {}
|
||||
local sim_types = {
|
||||
@@ -24,14 +26,14 @@ local function uuid()
|
||||
return rows[1].id
|
||||
end
|
||||
|
||||
local function reserve_sim(sim_type)
|
||||
local function reserve_sim(sim_type, is_virtual)
|
||||
local sim_id
|
||||
local number = SkyPhoneSimNumber.Reserve(uuid, function(candidate)
|
||||
sim_id = uuid()
|
||||
local result = Bridge.Database.Query([[
|
||||
INSERT IGNORE INTO `sky_phone_sims` (`id`, `phone_number`, `sim_type`)
|
||||
VALUES (?, ?, ?)
|
||||
]], { sim_id, candidate, sim_type })
|
||||
INSERT IGNORE INTO `sky_phone_sims` (`id`, `phone_number`, `sim_type`, `is_virtual`)
|
||||
VALUES (?, ?, ?, ?)
|
||||
]], { sim_id, candidate, sim_type, is_virtual and 1 or 0 })
|
||||
return affected_rows(result) == 1
|
||||
end, Config.Sim.NumberLength, Config.Sim.NumberPrefix)
|
||||
if not number then
|
||||
@@ -62,6 +64,91 @@ local function sim_metadata(sim)
|
||||
return metadata
|
||||
end
|
||||
|
||||
local function set_phone_sim_metadata(source, phone_slot, sim)
|
||||
if not unique_phones then
|
||||
return true
|
||||
end
|
||||
if not phone_slot then
|
||||
return false
|
||||
end
|
||||
|
||||
local metadata = phone_slot.metadata or {}
|
||||
metadata.sim_id = sim and sim.id or nil
|
||||
metadata.phone_number = sim and sim.phone_number or nil
|
||||
metadata.formatted_number = sim and SkyPhoneSimNumber.Format(
|
||||
sim.phone_number,
|
||||
Config.Sim.NumberGroups,
|
||||
Config.Sim.NumberLength,
|
||||
Config.Sim.NumberPrefix
|
||||
) or nil
|
||||
return Bridge.Inventory.SetSlotMetadata(source, phone_slot.slot, metadata)
|
||||
end
|
||||
|
||||
local function prepare_device(source, phone_slot, imei)
|
||||
local device = SkyPhone.LoadDevice(imei)
|
||||
if not device then
|
||||
return false, "request_failed"
|
||||
end
|
||||
|
||||
local sim = device.sim_id and load_sim(device.sim_id) or nil
|
||||
if sim_cards_enabled then
|
||||
if sim and tonumber(sim.is_virtual) == 1 then
|
||||
local result = Bridge.Database.Query([[
|
||||
UPDATE `sky_phone_devices`
|
||||
SET `sim_id` = NULL
|
||||
WHERE `imei` = ? AND `sim_id` = ?
|
||||
]], { imei, sim.id })
|
||||
if affected_rows(result) ~= 1 then
|
||||
return false, "request_failed"
|
||||
end
|
||||
sim = nil
|
||||
end
|
||||
|
||||
if not set_phone_sim_metadata(source, phone_slot, sim) then
|
||||
return false, "metadata_unsupported"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if sim then
|
||||
if not set_phone_sim_metadata(source, phone_slot, sim) then
|
||||
return false, "metadata_unsupported"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local automatic_sim = reserve_sim("anonymous", true)
|
||||
local result = Bridge.Database.Query([[
|
||||
UPDATE `sky_phone_devices`
|
||||
SET `sim_id` = ?
|
||||
WHERE `imei` = ? AND `sim_id` IS NULL
|
||||
]], { automatic_sim.id, imei })
|
||||
if affected_rows(result) ~= 1 then
|
||||
Bridge.Database.Query("DELETE FROM `sky_phone_sims` WHERE `id` = ?", { automatic_sim.id })
|
||||
device = SkyPhone.LoadDevice(imei)
|
||||
sim = device and device.sim_id and load_sim(device.sim_id) or nil
|
||||
if not sim then
|
||||
return false, "request_failed"
|
||||
end
|
||||
if not set_phone_sim_metadata(source, phone_slot, sim) then
|
||||
return false, "metadata_unsupported"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if not set_phone_sim_metadata(source, phone_slot, automatic_sim) then
|
||||
Bridge.Database.Query(
|
||||
"UPDATE `sky_phone_devices` SET `sim_id` = NULL WHERE `imei` = ? AND `sim_id` = ?",
|
||||
{ imei, automatic_sim.id }
|
||||
)
|
||||
Bridge.Database.Query("DELETE FROM `sky_phone_sims` WHERE `id` = ?", { automatic_sim.id })
|
||||
return false, "metadata_unsupported"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
SkyPhoneSim.PrepareDevice = prepare_device
|
||||
|
||||
local function resolve_used_sim(source, used_item, item_name)
|
||||
local slot_id = used_item and (used_item.slot or used_item.id)
|
||||
local slot = slot_id and Bridge.Inventory.GetSlot(source, slot_id) or nil
|
||||
@@ -82,7 +169,12 @@ local function ensure_sim(source, slot, sim_type)
|
||||
end
|
||||
local metadata = slot.metadata or {}
|
||||
local sim = metadata.sim_id and load_sim(metadata.sim_id) or nil
|
||||
if metadata.sim_id and (not sim or sim.sim_type ~= sim_type or sim.phone_number ~= metadata.phone_number) then
|
||||
if metadata.sim_id and (
|
||||
not sim
|
||||
or tonumber(sim.is_virtual) == 1
|
||||
or sim.sim_type ~= sim_type
|
||||
or sim.phone_number ~= metadata.phone_number
|
||||
) then
|
||||
return nil, "invalid_sim"
|
||||
end
|
||||
if not sim then
|
||||
@@ -97,30 +189,46 @@ end
|
||||
|
||||
local function list_phone_choices(source)
|
||||
local choices = {}
|
||||
local seen = {}
|
||||
local choice_error
|
||||
for _, slot in ipairs(Bridge.Inventory.GetSlotsWithItem(source, Config.Phone.Item)) do
|
||||
local imei = SkyPhone.EnsureDevice(source, slot)
|
||||
if imei then
|
||||
local device = SkyPhone.LoadDevice(imei)
|
||||
choices[#choices + 1] = {
|
||||
imei = imei,
|
||||
name = device.device_name,
|
||||
occupied = device.sim_id ~= nil,
|
||||
number = device.phone_number,
|
||||
}
|
||||
local imei, error_code = SkyPhone.EnsureDevice(source, slot)
|
||||
if imei and not seen[imei] then
|
||||
local prepared, prepare_error = prepare_device(source, slot, imei)
|
||||
if prepared then
|
||||
local device = SkyPhone.LoadDevice(imei)
|
||||
choices[#choices + 1] = {
|
||||
imei = imei,
|
||||
name = device.device_name,
|
||||
occupied = device.sim_id ~= nil,
|
||||
number = device.phone_number,
|
||||
}
|
||||
seen[imei] = true
|
||||
else
|
||||
Bridge.Debug(
|
||||
"error",
|
||||
"[sky_phone] Could not prepare SIM target %s for source %s: %s.",
|
||||
imei,
|
||||
tostring(source),
|
||||
tostring(prepare_error)
|
||||
)
|
||||
choice_error = choice_error or prepare_error
|
||||
end
|
||||
elseif not imei then
|
||||
choice_error = choice_error or error_code
|
||||
end
|
||||
end
|
||||
return choices
|
||||
return choices, choice_error
|
||||
end
|
||||
|
||||
local function rollback_phone_metadata(source, phone_slot, old_sim)
|
||||
local metadata = phone_slot.metadata or {}
|
||||
metadata.sim_id = old_sim and old_sim.id or nil
|
||||
metadata.phone_number = old_sim and old_sim.phone_number or nil
|
||||
metadata.formatted_number = old_sim and SkyPhoneSimNumber.Format(old_sim.phone_number, Config.Sim.NumberGroups, Config.Sim.NumberLength, Config.Sim.NumberPrefix) or nil
|
||||
Bridge.Inventory.SetSlotMetadata(source, phone_slot.slot, metadata)
|
||||
set_phone_sim_metadata(source, phone_slot, old_sim)
|
||||
end
|
||||
|
||||
local function insert_sim(source, phone_imei, confirmed)
|
||||
if not sim_cards_enabled then
|
||||
return { success = false, error = "disabled" }
|
||||
end
|
||||
if operation_locks[source] then
|
||||
return { success = false, error = "operation_in_progress" }
|
||||
end
|
||||
@@ -139,6 +247,10 @@ local function insert_sim(source, phone_imei, confirmed)
|
||||
return { success = false, error = "phone_not_owned" }
|
||||
end
|
||||
local phone_slot = phone_matches[1]
|
||||
local prepared, prepare_error = prepare_device(source, phone_slot, phone_imei)
|
||||
if not prepared then
|
||||
return { success = false, error = prepare_error }
|
||||
end
|
||||
local device = SkyPhone.LoadDevice(phone_imei)
|
||||
local old_sim = device.sim_id and load_sim(device.sim_id) or nil
|
||||
if old_sim and not confirmed then
|
||||
@@ -146,11 +258,7 @@ local function insert_sim(source, phone_imei, confirmed)
|
||||
end
|
||||
|
||||
operation_locks[source] = true
|
||||
local phone_metadata = phone_slot.metadata or {}
|
||||
phone_metadata.sim_id = pending.sim.id
|
||||
phone_metadata.phone_number = pending.sim.phone_number
|
||||
phone_metadata.formatted_number = SkyPhoneSimNumber.Format(pending.sim.phone_number, Config.Sim.NumberGroups, Config.Sim.NumberLength, Config.Sim.NumberPrefix)
|
||||
if not Bridge.Inventory.SetSlotMetadata(source, phone_slot.slot, phone_metadata) then
|
||||
if not set_phone_sim_metadata(source, phone_slot, pending.sim) then
|
||||
operation_locks[source] = nil
|
||||
return { success = false, error = "metadata_unsupported" }
|
||||
end
|
||||
@@ -211,6 +319,9 @@ local function insert_sim(source, phone_imei, confirmed)
|
||||
end
|
||||
|
||||
local function use_sim(source, used_item)
|
||||
if not sim_cards_enabled then
|
||||
return false
|
||||
end
|
||||
local item_name = used_item and used_item.name
|
||||
local sim_type = item_name and sim_types[item_name]
|
||||
if not sim_type then
|
||||
@@ -234,10 +345,10 @@ local function use_sim(source, used_item)
|
||||
TriggerClientEvent("sky_phone:device:error", source, error_code)
|
||||
return false
|
||||
end
|
||||
local choices = list_phone_choices(source)
|
||||
local choices, choice_error = list_phone_choices(source)
|
||||
if #choices == 0 then
|
||||
operation_locks[source] = nil
|
||||
TriggerClientEvent("sky_phone:device:error", source, "phone_required")
|
||||
TriggerClientEvent("sky_phone:device:error", source, choice_error or "phone_required")
|
||||
return false
|
||||
end
|
||||
pending_insertions[source] = {
|
||||
@@ -257,10 +368,15 @@ local function use_sim(source, used_item)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Register the guarded callbacks in both modes so a resource-only restart can
|
||||
-- replace registrations left behind in framework-owned usable-item tables.
|
||||
Bridge.Inventory.RegisterUsableItem(Config.Sim.RegisteredItem, use_sim)
|
||||
Bridge.Inventory.RegisterUsableItem(Config.Sim.AnonymousItem, use_sim)
|
||||
|
||||
Bridge.Callbacks.Register("sky_phone:sim:insert", function(source, data)
|
||||
if not sim_cards_enabled then
|
||||
return { success = false, error = "disabled" }
|
||||
end
|
||||
if type(data) ~= "table" or not SkyPhoneImei.IsValid(data.imei) then
|
||||
return { success = false, error = "invalid_request" }
|
||||
end
|
||||
@@ -268,6 +384,9 @@ Bridge.Callbacks.Register("sky_phone:sim:insert", function(source, data)
|
||||
end)
|
||||
|
||||
Bridge.Callbacks.Register("sky_phone:sim:eject", function(source)
|
||||
if not sim_cards_enabled then
|
||||
return { success = false, error = "disabled" }
|
||||
end
|
||||
if operation_locks[source] then
|
||||
return { success = false, error = "operation_in_progress" }
|
||||
end
|
||||
@@ -277,7 +396,7 @@ Bridge.Callbacks.Register("sky_phone:sim:eject", function(source)
|
||||
end
|
||||
local device = SkyPhone.LoadDevice(session.imei)
|
||||
local sim = device and device.sim_id and load_sim(device.sim_id) or nil
|
||||
if not sim then
|
||||
if not sim or tonumber(sim.is_virtual) == 1 then
|
||||
return { success = false, error = "no_sim" }
|
||||
end
|
||||
local phone_slot = Bridge.Inventory.GetSlot(source, session.slot)
|
||||
@@ -288,7 +407,10 @@ Bridge.Callbacks.Register("sky_phone:sim:eject", function(source)
|
||||
end
|
||||
|
||||
operation_locks[source] = true
|
||||
rollback_phone_metadata(source, phone_slot, nil)
|
||||
if not set_phone_sim_metadata(source, phone_slot, nil) then
|
||||
operation_locks[source] = nil
|
||||
return { success = false, error = "metadata_unsupported" }
|
||||
end
|
||||
if not Bridge.Inventory.AddItem(source, item_name, 1, nil, metadata) then
|
||||
rollback_phone_metadata(source, phone_slot, sim)
|
||||
operation_locks[source] = nil
|
||||
@@ -313,4 +435,13 @@ AddEventHandler("playerDropped", function()
|
||||
pending_insertions[source] = nil
|
||||
operation_locks[source] = nil
|
||||
end)
|
||||
|
||||
if sim_cards_enabled then
|
||||
Bridge.Database.Query([[
|
||||
UPDATE `sky_phone_devices` d
|
||||
INNER JOIN `sky_phone_sims` s ON s.`id` = d.`sim_id`
|
||||
SET d.`sim_id` = NULL
|
||||
WHERE s.`is_virtual` = 1
|
||||
]], {})
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user