How Integrations Work#
Nothing inside Books Creator knows which framework or inventory you run. Each one sits behind a small, open file — not escrowed — and the resource picks the right one at start by looking at what is already running.
Layer | Supported | Where it lives |
|---|---|---|
Framework | ESX · QBCore · Qbox · l2s-core |
|
Inventory | Eleven, listed below |
|
HUD | The minimap · qs-interface |
|
Menus | ox_lib context menu — the Open / Edit choice | Built in |
Database | oxmysql | Required. The only one. |
Frameworks#
Framework | Detected from | Writer saved as |
|---|---|---|
ESX |
| The player's |
QBCore |
| The character's |
Qbox |
| The character's |
l2s-core |
| The character's |
A framework is not optional: the bridge is what records who wrote a book and makes the item usable. With none of the four running, Books Creator stops at start with an error.
Inventories#
A book lives in its item's metadata, so the inventory has to do two things: tell Books Creator when the item is used, and let it write the book id back into the item.
Inventory | Adapter | Book kept in the item |
|---|---|---|
|
| Yes |
|
| Yes — through the item's |
|
| Yes |
|
| Needs |
|
| Needs |
|
| Needs |
|
| Needs |
|
| Needs |
| None yet | Copy |
None, or the classic ESX inventory |
| No — books cannot be stored |
ox_inventory#
ox_inventory sends item uses through the server.export in the item's definition rather than a usable-item registry, so the export is what connects the two. A side effect worth using: any item with that export behaves as a book — a diary, a tome, a case file — each with its own label, weight and picture:
-- ox_inventory/data/items.lua
['diary'] = {
label = 'Diary',
weight = 150,
stack = false,
close = true,
consume = 0,
server = {
export = 'qs-book-creator.useBook',
},
},Adding an Inventory#
Supporting an inventory that is not on the list is one file and one line.
Copy the closest adapter
Duplicate a file in
server/custom/inventory/adapters/—qb.luafor a qb-inventory fork,qs.luaotherwise — and change the guard at the top to your inventory's name. Every file in that folder is loaded on its own.Fill in the four functions
Function
Purpose
supportsMetadata()Returns
truewhen items can carry metadata.registerUsable(item, cb)Calls
cb(source, { slot, name, metadata })when the item is used.setMetadata(source, slot, data)Replaces the metadata of the item in that slot.
getItems(source)Returns the player's items, each with
slot,nameandmetadata(orinfo).lua -- server/custom/inventory/adapters/my_inventory.lua if Config.Inventory ~= 'my-inventory' then return end function inventory.supportsMetadata() return true end function inventory.registerUsable(itemName, callback) sfr:registerUsableItem(itemName, function(source, item) callback(source, { slot = item.slot, name = item.name, metadata = item.info or item.metadata, }) end) end function inventory.setMetadata(source, slot, data) -- use your inventory's own export here exports['my-inventory']:SetItemMetadata(source, slot, data) end function inventory.getItems(source) return exports['my-inventory']:GetInventory(source) or {} end return inventoryRegister it for detection
Add one entry to the
inventorieslist inconfig/main.lua— the key is the resource name, the value is what your guard compares against — and restart.lua local inventories = { -- ... ['my-inventory'] = 'my-inventory', }
ps-inventory#
ps-inventory is already in the detection list but has no adapter of its own, and the book item never becomes usable. It is a qb-inventory fork, so copy qb.lua to ps.lua and change only its guard:
if Config.Inventory ~= 'ps-inventory' then
return
endHiding the HUD#
While the writer is open the minimap is hidden, and so is the qs-interface HUD when it is running. The reader leaves both alone. To hide another HUD as well, add its export to ToggleHud in custom/client.lua:
function ToggleHud(bool)
DisplayRadar(bool)
if GetResourceState('qs-interface') == 'started' then
exports['qs-interface']:ToggleHud(bool)
end
-- your HUD here, for example:
-- exports['my-hud']:SetVisible(bool)
endNotifications#
Messages such as Book saved successfully are drawn by the book interface itself, so they look the same on every server. To route them to your own notification resource instead, change the Notification function in custom/client.lua:
function Notification(msg, type)
-- type is 'info', 'success' or 'error'
lib.notify({ description = msg, type = type })
end


