Custom bridges β
RZ scripts include readable integration files so a developer can adapt the core, inventory and notifications without changing protected gameplay or menu code. The files ship with working VORP calls. Standard VORP: leave them as supplied. Another framework requires a developer to replace these calls with its actual API.
Which file do I edit? β
| File | Edit this for |
|---|---|
public/bridge_server.lua | Character readiness, character names, inventory items and character-loaded events |
public/bridge_client.lua | On-screen notifications only |
public/eateffects.lua (Herbs) | Effects applied when a plant is eaten |
These files remain readable under Cfx escrow. Gameplay, synchronization, permissions, validation, menu and native notification implementation stay in .rz. Do not edit .rz: builds regenerate it.
How to change an integration β
- Find the relevant function in the public file. Its short comment explains its inputs and required result.
- Read the active VORP implementation as a reference.
- Replace its body, keeping the function name and arguments. Do not append a second call after the VORP one.
- Return the required result. A callback-based external API must complete before returning the result; returning inside a later callback alone will not work.
- Restart the product and test that operation.
There is no hidden VORP fallback. Missing functions, errors and invalid results do not trigger a second implementation. An empty inventory function does nothing and does not report a successful item addition.
For a custom inventory, adapt Catalog, CanCarry and AddItem together. For a custom core, adapt IsReady, Profile and the character-loaded event. You can keep the VORP core with a custom inventory, or the reverse.
Each resource owns its public files. Keep the core/inventory integration consistent in every installed RZ product: the shared owner menu can be hosted by any eligible RZ resource, and the host's Catalog supplies the common item selector. Notifications and gameplay rewards run through the product that sends them.
Back up public files before updates. SDK builds preserve existing files, including obsolete adapters: when moving from an older empty-override template, compare the supplied public files and retain your custom calls. Do not restore empty old functions over the new active VORP implementations.
Server operations β
source is a connected player's server ID, not a character ID, license or client PlayerId(). The override has the same arguments as its matching product API.
| Product API | Override | Default |
|---|---|---|
RZ.Player.IsReady(source) | RZ.Bridge.Player.IsReady(source) | VORP Core.getUser(source).getUsedCharacter exists |
RZ.Player.Profile(source) | RZ.Bridge.Player.Profile(source) | VORP character firstname / lastname |
RZ.Inventory.Catalog() | RZ.Bridge.Inventory.Catalog() | Started vorp_inventory; oxmysql query on items, columns item, label |
RZ.Inventory.CanCarry(source,item,count) | RZ.Bridge.Inventory.CanCarry(source,item,count) | exports.vorp_inventory:canCarryItem(source,item,count) |
RZ.Inventory.AddItem(source,item,count) | RZ.Bridge.Inventory.AddItem(source,item,count) | exports.vorp_inventory:addItem(source,item,count) |
RZ.Notify.Left(source,payload) | Client only: RZ.Bridge.Notify.Left(payload) | Resource-scoped RZ event to the target client, then SDK-native rendering |
Character readiness β
Override: return true when loaded, false when not loaded, or nil, "CUSTOM_ERROR" on failure.
Public call: local ok, ready, code = RZ.Player.IsReady(source). ok indicates whether the check succeeded; ready indicates whether the character is loaded.
Character profile β
Override: return { first_name = "Jane", last_name = "Smith" }. Both names must be non-empty strings of at most 96 bytes. On failure return nil, "CUSTOM_ERROR".
Public call: local profile, code = RZ.Player.Profile(source).
Character identifiers, jobs, grades, balances and a full player-data API are not yet implemented in this bridge.
Item catalog β
Override: return a contiguous array:
{
{ item = "water", label = "Water" },
{ item = "bread", label = "Bread" },
}Maximum 4096 entries. Item names are unique strings of 1β96 bytes; labels are 1β256 bytes. Extra fields are discarded. {} means empty; nil, "CUSTOM_ERROR" means failure.
Public call: local items, code = RZ.Inventory.Catalog(). The SDK menu uses this for item/reward selectors. A custom inventory must normalize its own data source into this array.
Capacity and adding items β
item: name of 1β96 bytes. count: integer 1β100000.
CanCarryreturnstruewhen the amount fits,falseotherwise. It must not add items.AddItemreturnstrueonly after successful addition,falseif refused.- Return
false, "CUSTOM_ERROR"for integration failures.
Public calls return boolean, code. An ordinary negative result may be false, "OK": always inspect the boolean.
AddItem does not automatically call CanCarry. Item removal, item counts, metadata and custom containers are not currently exposed by this bridge.
Notification data β
| Payload field | Contract |
|---|---|
title | Non-empty string, maximum 128 bytes |
message | Non-empty string, maximum 512 bytes |
dictionary, icon | Non-empty strings, maximum 96 bytes each |
duration_ms | Integer 1β60000, milliseconds |
color | Optional, passed through |
Notification customization is client-only. The public client function normally calls RZ.NativeNotify.Left(payload) to render the SDK-native Left notification without exposing its implementation. Replace that call with your system. No return value is required for a custom notification; failures do not trigger a second notification.
Public server call: local sent, code = RZ.Notify.Left(source, payload).
Herbs: customize the notification β
To change only collection and eating wording, edit public/notifications.lua: notification text guide. The bridge below replaces the system that displays the notification.
Edit only public/bridge_client.lua, in RZ.Bridge.Notify.Left. Herbs uses this same hook for collection and eating. There is no server notification bridge to configure.
The supplied body is:
RZ.Bridge.Notify.Left = function(payload)
return RZ.NativeNotify.Left(payload)
endRZ.NativeNotify.Left renders the original Left notification directly; it does not call the bridge again. To customize it, replace that return line with your notification call. Do not leave both calls active.
payload is a Lua table containing the notification Herbs has already prepared. You do not need to calculate rewards or compose the message.
| Value | Meaning |
|---|---|
payload.title | Translated heading, such as Herbs |
payload.message | Complete translated message, including quantities and awarded item names |
payload.duration_ms | Duration in milliseconds: 8000 for awarded items, 5000 for eating or no reward |
payload.dictionary | RedM texture dictionary for the plant image |
payload.icon | Plant texture name within that dictionary |
payload.color | Herbs sends COLOR_WHITE |
Examples of prepared messages β
- One item:
Collected: x1 Yarrow. - Several items:
Collected: x1 Yarrow, x2 Waterβ a single notification with all successfully added rewards. - Nothing added:
You did not receive any items (no reward or inventory full).. - Eating:
You ate Yarrow.β sent after accepted consumption even if additional effects are OFF.
For collection, Herbs uses the reward's saved label when present; otherwise it displays the internal item name. It does not query the inventory label again for each notification. Only successfully added rewards appear. Separate reward entries remain separate even when they award the same item.
For eating, Herbs uses the configured plant name. Text follows the script language, not the owner's panel language. The image is the plant image, including when several different items are awarded. These examples do not configure rewards; quantities and items depend on your menu settings.
Connect another notification system β
Map the prepared values to your system's documented arguments:
-- Example only: replace my_notify and Show with your resource's real API.
RZ.Bridge.Notify.Left = function(payload)
exports.my_notify:Show(payload.title, payload.message, payload.duration_ms)
endOnly your call runs. No extra RZ notification appears and no return value is required. A hook containing only print(payload.message) prints the message and displays nothing on screen.
To restore native notifications, restore the supplied body:
RZ.Bridge.Notify.Left = function(payload)
return RZ.NativeNotify.Left(payload)
endAn empty function intentionally displays nothing. A missing hook returns RZF-E-BRIDGE-FUNCTION; it does not silently display a native notification. Keep this file and restore the supplied call if you want native rendering. Do not call RZ.Notify.Left inside its own hook; use RZ.NativeNotify.Left to call the original implementation safely. Effects belong in public/eateffects.lua; the script already sends the eating notification, so do not duplicate it in the effect function.
Character-loaded event β
Product code subscribes using RZ.Player.OnReady(function(source) ... end). The public server file contains the active VORP registration:
AddEventHandler("vorp:SelectedCharacter", function(playerSource)
RZ.Bridge.PlayerReady(playerSource)
end)Replace this registration with your framework's trusted server character-loaded event and pass the real player server ID to RZ.Bridge.PlayerReady. Do not add a client-triggerable event that accepts an arbitrary player ID. The SDK no longer registers VORP's event internally, so there is no duplicate hidden event to disable.
Player.IsReady checks players who are already online when the resource restarts. The event handles later character selection or relog. They serve different moments of the same lifecycle.
Inventory restart and menu catalog β
The public server file includes this local function, so the cache signal is visible:
local function RefreshItemCatalog()
TriggerEvent("rz:sdk:v1:inventory_changed")
endThis server-only SDK event discards cached item names and labels in the menu. It does not modify a player's inventory or query the database immediately. The next catalog request runs Inventory.Catalog again. Keep the SDK event name unchanged.
The public start/stop handlers call this function when vorp_inventory restarts. For another inventory, change that resource name in both handlers. If your inventory supports reloading its item definitions while running, call RefreshItemCatalog() from that inventory's documented server reload event too.
Client notifications β
The call chain is script β RZ.Notify.Left β public RZ.Bridge.Notify.Left β RZ.NativeNotify.Left. The last call renders the native notification directly, without re-entering the bridge. Replace only the public function body to use another notification system. These names belong to the current resource, not a global shared across all resources.
For a product using three types, keep three functions in public/bridge_client.lua:
RZ.Bridge.Notify.Left = function(payload)
return RZ.NativeNotify.Left(payload)
end
RZ.Bridge.Notify.Tip = function(payload)
return RZ.NativeNotify.Tip(payload)
end
RZ.Bridge.Notify.Center = function(payload)
return RZ.NativeNotify.Center(payload)
endChanging one body does not change the others. Never call RZ.Notify.Left inside its own Left bridge: that re-enters the same function. Use RZ.NativeNotify.Left there.
Keep the public integration files. A build preserves existing public files, including your custom code; it cannot repair a function you emptied. An empty notification intentionally does nothing. A missing notification function returns an error. Empty required player/inventory functions return validation errors and cannot replace working framework calls. Restore the product's supplied files if you removed them accidentally.
All calls use RZ.Notify.Method(payload) with no source argument and return sent, code. Override one method through RZ.Bridge.Notify.Method = function(payload) ... end in public/bridge_client.lua. No return value is required after dispatch. Return false, "CUSTOM_ERROR" to report failure. A defined function never falls back, even when empty. Never call the same RZ method inside its override.
The template includes all 17 public functions. Each explicitly calls RZ.NativeNotify.Method(payload), which builds native UI_FEED buffers using DataView and invokes RedM natives directly. Products may ship only the functions they actually use; Herbs ships Left. To use another type in a product, copy its public function from the template. No VORP export or event is involved. A custom failure never falls back to the native renderer. Success means dispatched, not confirmed visible on screen.
| RZ method | Payload fields |
|---|---|
Left | title, message, dictionary, icon, duration_ms, color |
Tip | message, duration_ms |
RightTip | message, duration_ms |
Objective | message, duration_ms |
Top | message, location, duration_ms |
SimpleTop | title, message, duration_ms |
Advanced | message, dictionary, icon, color, duration_ms, quality, show_quality |
Center | message, duration_ms, color |
BottomRight | message, duration_ms |
Fail | title, message, duration_ms |
Dead | title, audio_ref, audio_name, duration_ms |
Update | title, message, duration_ms |
Warning | title, message, audio_ref, audio_name, duration_ms |
LeftRank | title, message, dictionary, icon, duration_ms, color |
ThreeSimpleTop | title, message, secondary_message, duration_ms |
OneSimpleTop | title, duration_ms |
LeftInteractive | title, message, secondary_message, dictionary, icon, audio_ref, audio_name, duration_ms, color |
All listed fields are required except color, quality, show_quality, which default to "COLOR_WHITE", 1, false. duration_ms: integer 1β60000. quality: signed 32-bit integer; meaningful visual values depend on the RedM preset. show_quality: boolean. Strings must be non-empty: title/location up to 128 bytes, message/secondary_message up to 512, other strings up to 96. Unknown extra fields are not forwarded to the native defaults.
RZ.Notify.Tip({ message = "Saved", duration_ms = 4000 })
-- In public/bridge_client.lua, when using your custom notification system:
-- RZ.Bridge.Notify.Tip = function(payload)
-- -- Call that system here with payload.message and payload.duration_ms.
-- -- Return true after dispatch, or false, "CUSTOM_ERROR".
-- endThe server currently exposes RZ.Notify.Left(source, payload). It sends a resource-scoped RZ event; the target client calls the public RZ.Bridge.Notify.Left function. The shipped function explicitly calls RZ.NativeNotify.Left. Neither notification path requires VORP. Client inventory and profile bridges are not implemented.
Texture loading times out after 2 seconds with RZSDK-E-NOTIFICATION-TEXTURE. Fail, Dead, Update and Warning schedule handle removal instead of waiting for their display duration; outstanding persistent handles are also removed when the resource stops. Native failures return RZSDK-E-NOTIFICATION-NATIVE; an invalid persistent handle returns RZSDK-E-NOTIFICATION-HANDLE.
Local tests exercise native buffers and lifecycle without a framework; visual behavior still needs in-game verification. Existing owner bridge files are preserved by builds. An automatically discovered external notification resource is future work, not a current dependency.
Example: keep VORP, customize one call β
In public/bridge_server.lua, define only:
RZ.Bridge.Inventory.AddItem = function(source, item, count)
if not exports.vorp_inventory:canCarryItem(source, item, count) then
return false, "INVENTORY_FULL"
end
return exports.vorp_inventory:addItem(source, item, count)
endAll other calls keep their defaults. This does not make the capacity check and addition atomic: gameplay must still check the addition result.
Do not call RZ.Inventory.AddItem inside its own override: it would call itself recursively. For a different inventory, translate identifiers, argument order, field names and result values according to its documentation. A return inside a later callback does not return from the original call; asynchronous APIs need an appropriate adapter.
Troubleshooting β
| Error | Meaning |
|---|---|
RZF-E-PLAYER | Invalid/disconnected server player ID |
RZF-E-INVENTORY-INPUT | Invalid item or quantity |
RZF-E-NOTIFICATION-INPUT | Invalid payload |
RZF-E-ADAPTER-RESULT | Invalid profile/readiness result or failed client notification result |
RZF-E-INVENTORY-CATALOG | Invalid catalog shape or limits |
RZF-E-ADAPTER-FAILURE | Integration threw an error; inspect the SDK assistance report and console |
RZF-E-NOT-READY | Server bridge has not initialized |
RZF-E-BRIDGE-FUNCTION | No callable handler available |
RZR-E-UPDATE-REQUIRED | Known mandatory update rejects server API calls |
VORP dependencies are looked up at call time. Missing integrations fail that call rather than disabling the entire configuration menu. A bridge in ready state does not prove every external function works.
Restart the product resource after adapting it and test the affected operation against your installed systems. This page describes the current SDK contract; it does not claim live compatibility with every framework.
