Summary

This release introduces a phone break-and-repair system — phones can now be damaged (e.g. while swimming) and repaired at NPCs around the map. It also brings better payphone defaults, several bridge improvements (target retry mechanism, new notify event), Cupid UI polish, and assorted bug fixes across music, V app login, and item checks for broken phones.

What's New

Features

Bug Fixes


File Changes

⚙️ Configuration & Bridge Files

These files are not encrypted. Review the changes below — you may need to manually apply updates to your server's config files.

bridge/client/notify.lua

+RegisterNetEvent("okokPhone:Client:notify", function(data)
+      Notify(data)
+end)

bridge/client/target.lua

-local target = {}
+local target = {}
+
+local registrations = {}
+local pendingRegistrations = {}
+local retryingRegistrations = false

----@param models string[]
----@param label string
----@param cb fun(entity: number)
----@param distance number
-function target.addModels(models, label, cb, distance)
+local function getRegistrationKey(models, label)
+    local keys = {}
+    for i, model in ipairs(models) do
+        keys[i] = tostring(model)
+    end
+    return ("%s:%s"):format(table.concat(keys, "|"), label)
+end
+
+local function addModelsNow(models, label, cb, distance)
     if GetResourceState("ox_target") == "started" then
         exports.ox_target:addModel(models, {
             distance = distance,
@@ -14,6 +22,7 @@ function target.addModels(models, label, cb, distance)
                 cb(data.entity)
             end
         })
+        return true
     elseif GetResourceState("qb-target") == "started" then
         exports["qb-target"]:AddTargetModel(models, {
             options = {
@@ -28,16 +37,90 @@ function target.addModels(models, label, cb, distance)
             },
             distance = distance
         })
+        return true
+    end
+
+    return false
+end
+
+local function retryPendingRegistrations()
+    if retryingRegistrations then return end
+    retryingRegistrations = true
+
+    CreateThread(function()
+        while next(pendingRegistrations) do
+            for key, data in pairs(pendingRegistrations) do
+                if addModelsNow(data.models, data.label, data.cb, data.distance) then
+                    pendingRegistrations[key] = nil
+                end
+            end
+
+            if next(pendingRegistrations) then
+                Wait(1000)
+            end
+        end
+
+        retryingRegistrations = false
+    end)
+end
+
+AddEventHandler("onClientResourceStart", function(resource)
+    if resource ~= "ox_target" and resource ~= "qb-target" then return end
+
+    for key, data in pairs(registrations) do
+        if addModelsNow(data.models, data.label, data.cb, data.distance) then
+            pendingRegistrations[key] = nil
+        end
     end
+end)
+
+---@param models string[]
+---@param label string
+---@param cb fun(entity: number)
+---@param distance number
+function target.addModels(models, label, cb, distance)
+    local key = getRegistrationKey(models, label)
+    registrations[key] = {
+        models = models,
+        label = label,
+        cb = cb,
+        distance = distance
+    }
+
+    if addModelsNow(models, label, cb, distance) then return true end
+
+    pendingRegistrations[key] = registrations[key]
+    retryPendingRegistrations()
+    return false
 end

 ---@param models string[]
 function target.cleanupModels(models)
+    for key, data in pairs(registrations) do
+        if data.models == models then
+            registrations[key] = nil
+        end
+    end
+
+    for key, data in pairs(pendingRegistrations) do
+        if data.models == models then
+            pendingRegistrations[key] = nil
+        end
+    end
+
     if GetResourceState("ox_target") == "started" then
         exports.ox_target:removeModel(models)
-    elseif GetResourceState("qb-target") == "started" then
+    end
+
+    if GetResourceState("qb-target") == "started" then
         exports["qb-target"]:RemoveTargetModel(models)
     end
 end

+function target.addLocalEntity(entity, data)
+    if GetResourceState("ox_target") == "started" then
+        exports.ox_target:addLocalEntity(entity, data)
+    end
+end
+
 return target

client/open/phone_repairs.lua