Custom Framework
Learn how to add custom frameworks.
Learn how to add custom frameworks.
If you use a custom framework or want to add a framework that's not supported. You can add it to your project by following these steps.
Create a new folder in the frameworks directory in the resource, named after your framework.
Create a client.lua and server.lua file in the folder. Should look like this:
├── frameworks
│ └── myframework
│ ├── client.lua
│ └── server.lua
Add the following code to the client.lua file:
AddFramework {
'myframework',
{
benchOptions = {
useCurrentWeapon = true, -- Use the current weapon the player is holding
showAllWeapons = false, -- Show all weapons in the bench
manualApplyMods = true -- Apply mods manually. If false, mods will be applied automatically
},
--[[
inventory = Get the inventory to use for the framework.
]]
-- inventory = useInventory('ox_inventory', { framework = 'esx' }),
--[[
init - Initialize the framework
@param callback
]]
init = function()
end,
--[[
getCurrentWeapon - Get the current weapon the player is holding
@param weaponHash
@return nil | table
]]
getCurrentWeapon = function(weaponHash)
if not framework.inventory?.getCurrentWeapon then
return {}
end
return framework.inventory.getCurrentWeapon()
end,
--[[
notification - Send a notification to the player
@param message
]]
notification = function(message)
end,
}
}Add the following code to the server.lua file:
AddFramework {
'myframework',
{
benchOptions = {
type = BenchTypes.ITEM, -- BenchTypes.ITEM | BenchTypes.PURCHASE
},
--[[
items - Get the items to use for the framework.
Use either
- the types specified in config.lua
- the nameGXT value which you can find for the components and tints in src/weapons.json
to set the items
These items will be overrided by some inventories, like ox_inventory
]]
items = {
components = {
default = 'weapon_component',
clip = 'clip_attachment',
suppressor = 'suppressor_attachment',
flashlight = 'flashlight_attachment',
compensator = 'compensator_attachment',
grip = 'grip_attachment',
small_scope = 'smallscope_attachment',
scope = 'medscope_attachment',
large_scope = 'largescope_attachment',
holo_scope = 'holoscope_attachment',
thermal_scope = 'thermalscope_attachment',
night_vision_scope = 'nvscope_attachment',
mounted_scope = 'medscope_attachment',
advanced_scope = 'advscope_attachment',
shell = 'shell_attachment',
['WCT_VAR_GOLD'] = 'luxuryfinish_attachment',
['WCT_CAMO_1'] = 'digicamo_attachment',
['WCT_CAMO_2'] = 'brushcamo_attachment',
['WCT_CAMO_3'] = 'woodcamo_attachment',
['WCT_CAMO_4'] = 'skullcamo_attachment',
['WCT_CAMO_5'] = 'sessantacamo_attachment',
['WCT_CAMO_6'] = 'perseuscamo_attachment',
['WCT_CAMO_7'] = 'leopardcamo_attachment',
['WCT_CAMO_8'] = 'zebracamo_attachment',
['WCT_CAMO_9'] = 'geocamo_attachment',
['WCT_CAMO_10'] = 'boomcamo_attachment',
['WCT_CAMO_IND'] = 'patriotcamo_attachment',
['WCT_MUZZ1'] = 'flat_muzzle_brake',
['WCT_MUZZ2'] = 'tactical_muzzle_brake',
['WCT_MUZZ3'] = 'fat_end_muzzle_brake',
['WCT_MUZZ4'] = 'precision_muzzle_brake',
['WCT_MUZZ5'] = 'heavy_duty_muzzle_brake',
['WCT_MUZZ6'] = 'slanted_muzzle_brake',
['WCT_MUZZ7'] = 'split_end_muzzle_brake',
['WCT_MUZZ8'] = 'squared_muzzle_brake',
['WCT_MUZZ9'] = 'bellend_muzzle_brake',
},
tints = {
default = 'tint_component',
['WM_TINT1'] = 'weapontint_1',
['WM_TINT2'] = 'weapontint_2',
['WM_TINT3'] = 'weapontint_3',
['WM_TINT4'] = 'weapontint_4',
['WM_TINT5'] = 'weapontint_5',
['WM_TINT6'] = 'weapontint_6',
['WM_TINT7'] = 'weapontint_7',
['WCT_TINT_0'] = 'weapontint_mk2_0',
['WCT_TINT_1'] = 'weapontint_mk2_1',
['WCT_TINT_2'] = 'weapontint_mk2_2',
['WCT_TINT_3'] = 'weapontint_mk2_3',
['WCT_TINT_4'] = 'weapontint_mk2_4',
['WCT_TINT_5'] = 'weapontint_mk2_5',
['WCT_TINT_6'] = 'weapontint_mk2_6',
['WCT_TINT_7'] = 'weapontint_mk2_7',
['WCT_TINT_8'] = 'weapontint_mk2_8',
['WCT_TINT_9'] = 'weapontint_mk2_9',
['WCT_TINT_10'] = 'weapontint_mk2_10',
['WCT_TINT_11'] = 'weapontint_mk2_11',
['WCT_TINT_12'] = 'weapontint_mk2_12',
['WCT_TINT_13'] = 'weapontint_mk2_13',
['WCT_TINT_14'] = 'weapontint_mk2_14',
['WCT_TINT_15'] = 'weapontint_mk2_15',
['WCT_TINT_16'] = 'weapontint_mk2_16',
['WCT_TINT_17'] = 'weapontint_mk2_17',
['WCT_TINT_18'] = 'weapontint_mk2_18',
['WCT_TINT_19'] = 'weapontint_mk2_19',
['WCT_TINT_20'] = 'weapontint_mk2_20',
['WCT_TINT_21'] = 'weapontint_mk2_21',
['WCT_TINT_22'] = 'weapontint_mk2_22',
['WCT_TINT_23'] = 'weapontint_mk2_23',
['WCT_TINT_24'] = 'weapontint_mk2_24',
['WCT_TINT_25'] = 'weapontint_mk2_25',
['WCT_TINT_26'] = 'weapontint_mk2_26',
['WCT_TINT_27'] = 'weapontint_mk2_27',
['WCT_TINT_28'] = 'weapontint_mk2_28',
['WCT_TINT_29'] = 'weapontint_mk2_29',
['WCT_TINT_30'] = 'weapontint_mk2_30',
['WCT_TINT_31'] = 'weapontint_mk2_31',
}
},
--[[
prices - Get the prices to use for the framework.
Use either
- the types specified in config.lua
- the nameGXT value which you can find for the components and tints in src/weapons.json
to set the prices
]]
prices = {
components = {
default = 100,
clip = 300,
suppressor = 400,
flashlight = 50,
compensator = 200,
grip = 300,
small_scope = 250,
scope = 300,
large_scope = 350,
holo_scope = 250,
thermal_scope = 250,
night_vision_scope = 400,
mounted_scope = 250,
advanced_scope = 400,
shell = 150,
['WCT_VAR_GOLD'] = 100,
['WCT_CAMO_1'] = 100,
['WCT_CAMO_2'] = 100,
['WCT_CAMO_3'] = 100,
['WCT_CAMO_4'] = 100,
['WCT_CAMO_5'] = 100,
['WCT_CAMO_6'] = 100,
['WCT_CAMO_7'] = 100,
['WCT_CAMO_8'] = 100,
['WCT_CAMO_9'] = 100,
['WCT_CAMO_10'] = 100,
['WCT_CAMO_IND'] = 100,
['WCT_MUZZ1'] = 100,
['WCT_MUZZ2'] = 100,
['WCT_MUZZ3'] = 100,
['WCT_MUZZ4'] = 100,
['WCT_MUZZ5'] = 100,
['WCT_MUZZ6'] = 100,
['WCT_MUZZ7'] = 100,
['WCT_MUZZ8'] = 100,
['WCT_MUZZ9'] = 100,
},
tints = {
default = 100,
['WM_TINT1'] = 100,
['WM_TINT2'] = 100,
['WM_TINT3'] = 100,
['WM_TINT4'] = 100,
['WM_TINT5'] = 100,
['WM_TINT6'] = 100,
['WM_TINT7'] = 100,
['WCT_TINT_0'] = 100,
['WCT_TINT_1'] = 100,
['WCT_TINT_2'] = 100,
['WCT_TINT_3'] = 100,
['WCT_TINT_4'] = 100,
['WCT_TINT_5'] = 100,
['WCT_TINT_6'] = 100,
['WCT_TINT_7'] = 100,
['WCT_TINT_8'] = 100,
['WCT_TINT_9'] = 100,
['WCT_TINT_10'] = 100,
['WCT_TINT_11'] = 100,
['WCT_TINT_12'] = 100,
['WCT_TINT_13'] = 100,
['WCT_TINT_14'] = 100,
['WCT_TINT_15'] = 100,
['WCT_TINT_16'] = 100,
['WCT_TINT_17'] = 100,
['WCT_TINT_18'] = 100,
['WCT_TINT_19'] = 100,
['WCT_TINT_20'] = 100,
['WCT_TINT_21'] = 100,
['WCT_TINT_22'] = 100,
['WCT_TINT_23'] = 100,
['WCT_TINT_24'] = 100,
['WCT_TINT_25'] = 100,
['WCT_TINT_26'] = 100,
['WCT_TINT_27'] = 100,
['WCT_TINT_28'] = 100,
['WCT_TINT_29'] = 100,
['WCT_TINT_30'] = 100,
['WCT_TINT_31'] = 100,
}
},
--[[
inventory - Get the inventory to use for the framework.
]]
inventory = useInventory('ox_inventory', { framework = 'esx' }),
--[[
init - Initialize the framework.
]]
init = function()
end,
--[[
getPlayerIdentifier - Get the player's unique identifier.
@param src number
@return string
]]
getPlayerIdentifier = function(src)
end,
--[[
getAccessIdentifiers - Get the access identifiers, used for checking if a player has access to a bench.
@return table { [string] = string }
]]
getAccessIdentifiers = function()
return {}
end,
--[[
getPlayerAccess - Get the player's access identifiers.
@param src number
@return table { [number] = string }
]]
getPlayerAccess = function(src)
return {}
end,
--[[
canUseEditor - Check if a player can use the editor.
@param src number
@param identifier string
@return boolean
]]
canUseEditor = function(src, identifier)
return IsPlayerAceAllowed(src, 'weaponmodbench.editor') == 1 or IsPlayerAceAllowed(src, 'command.editor') == 1
end,
--[[
onUseBench - Called when a player uses a weapon bench to determine if they have access.
@param src number
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onUseBench = function(src, bench)
return {
success = true
}
end,
--[[
onBuyComponent - Called when a player buys a component for a weapon.
@param src number
@param weapon table { hashKey = string, metadata = table }
@param component table { hashKey = string, price = number, item = string }
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onBuyComponent = function(src, weapon, component, bench)
if not framework.inventory?.buyComponent then
return {
success = true
}
else
return framework.inventory.buyComponent(src, weapon, component)
end
end,
--[[
onEquipComponent - Called when a player equips a component to a weapon.
@param src number
@param weapon table { hashKey = string, metadata = table }
@param component table { hashKey = string, price = number, item = string }
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onEquipComponent = function(src, weapon, component, bench)
if not framework.inventory?.equipComponent then
return {
success = true
}
else
return framework.inventory.equipComponent(src, weapon, component)
end
end,
--[[
onUnequipComponent - Called when a player equips a component to a weapon.
@param src number
@param weapon table { hashKey = string, metadata = table }
@param component table { hashKey = string, price = number, item = string }
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onUnequipComponent = function(src, weapon, component, bench)
if not framework.inventory?.unequipComponent then
return {
success = true
}
else
return framework.inventory.unequipComponent(src, weapon, component)
end
end,
--[[
onBuyTint - Called when a player buys a tint.
@param src number
@param weapon table { hashKey = string, metadata = table }
@param tint table { id = number, price = number, item = string }
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onBuyTint = function(src, weapon, tint, bench)
if not framework.inventory?.buyTint then
return {
success = true
}
else
return framework.inventory.buyTint(src, weapon, tint)
end
end,
--[[
onApplyTint - Called when a player applies a tint to a weapon.
@param src number
@param weapon table { hashKey = string, metadata = table }
@param tint table { id = number, price = number, item = string }
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onApplyTint = function(src, weapon, tint, bench)
if not framework.inventory?.applyTint then
return {
success = true
}
else
return framework.inventory.applyTint(src, weapon, tint)
end
end,
--[[
onRemoveTint - Called when a player removes a tint from a weapon.
@param src number
@param weapon table { hashKey = string, metadata = table }
@param tint table { id = number, price = number, item = string }
@param bench table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
@return table { success = boolean, message = string }
]]
onRemoveTint = function(src, weapon, tint, bench)
if not framework.inventory?.removeTint then
return {
success = true
}
else
return framework.inventory.removeTint(src, weapon, tint)
end
end,
--[[
getWeapons - Get the player's weapons. This is used when BenchOptions.useCurrentWeapon is set to false to show all the player's weapons.
@param src number
@return table { [number] = { data = { itemId = string, name = string }, label = string, modelHash = string } }
]]
getWeapons = function(src)
if not framework.inventory?.getWeapons then
return {}
else
return framework.inventory.getWeapons(src)
end
end,
--[[
getWeapon - Get a player's weapon.
@param src number
@param hash string
@param metadata table
@param weapon table { components = table, tints = table }
@return nil | table { components = table { [number] = { hash = string, isEquiped = boolean } }, tints = table { [number] = { id = number, isEquiped = boolean } } }
]]
getWeapon = function(src, hash, metadata, weapon)
if not framework.inventory?.getWeapon then
return {
components = {},
tints = {}
}
else
return framework.inventory?.getWeapon(src, hash, metadata, weapon)
end
end,
--[[
getWeaponMods - Get the weapon mods for a player. This is used if benchOptions.manualApplyMods is set to false in the client.lua.
@param src number
@param hash string
@param metadata table
]]
getWeaponMods = function(src, hash, metadata)
return {
components = {},
tint = nil
}
end,
--[[
doesItemExist - Check if an item exists.
@param name string
@return boolean
]]
doesItemExist = function(name)
if not framework.inventory?.doesItemExist then
return true
else
return framework.inventory.doesItemExist(name)
end
end,
database = {
init = function()
exports.oxmysql:transaction_async({
[[
CREATE TABLE IF NOT EXISTS `nxtgn_weap_benches` (
`uuid` uuid NOT NULL DEFAULT uuid(),
`title` varchar(50) NOT NULL,
`access` varchar(50) DEFAULT NULL,
`loc_x` float NOT NULL DEFAULT 0,
`loc_y` float NOT NULL DEFAULT 0,
`loc_z` float NOT NULL DEFAULT 0,
`loc_h` float NOT NULL DEFAULT 0,
`created_by` varchar(255) DEFAULT NULL,
`created_at` date NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`uuid`)
)
]]
})
end,
--[[
createBench - Create a new weapon bench.
@param data table { title = string, access = string, location = { x = number, y = number, z = number, h = number }, identifier = string }
@return string | boolean
]]
createBench = function(data)
local uuid = Math:generateUUID()
local res = exports.oxmysql:query_async([[
INSERT INTO `nxtgn_weap_benches` (`uuid`, `title`, `access`, `loc_x`, `loc_y`, `loc_z`, `loc_h`, `created_by`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
]], {
uuid,
data.title,
data.access,
data.location.x,
data.location.y,
data.location.z,
data.location.h,
data.identifier
})
if res.affectedRows == 0 then
return nil
end
return uuid
end,
--[[
updateBench - Update an existing weapon bench.
@param data table { title = string, access = string, location = { x = number, y = number, z = number, h = number }, uuid = string }
@return boolean
]]
updateBench = function(data)
local res = exports.oxmysql:query_async([[
UPDATE `nxtgn_weap_benches` SET `title` = ?, `access` = ?, `loc_x` = ?, `loc_y` = ?, `loc_z` = ?, `loc_h` = ? WHERE `uuid` = ?
]], {
data.title,
data.access,
data.location.x,
data.location.y,
data.location.z,
data.location.h,
data.uuid
})
if res.changedRows == 0 then
return false
end
return true
end,
--[[
removeBench - Remove a weapon bench.
@param data table { uuid = string }
@return boolean
]]
removeBench = function(data)
local res = exports.oxmysql:query_async([[
DELETE FROM `nxtgn_weap_benches` WHERE `uuid` = ?
]], { data.uuid })
if res.affectedRows == 0 then
return false
end
return true
end,
--[[
fetchBenches - Fetch benches.
@param data table { columnFilters = table { [string] = table { string }, orderColumn = string, orderDirection = string, count = number, offset = number }
@return nil | table { rows = table { [number] = table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }, pageCount = number }
]]
fetchBenches = function(data)
local columnFiltersTable = {}
local i = 0
for k,v in pairs(data.columnFilters) do
if i == 0 then
table.insert(columnFiltersTable, "WHERE")
elseif i > 0 then
table.insert(columnFiltersTable, "AND")
end
if type(v) == 'string' then
table.insert(columnFiltersTable, "`" .. k .. "` LIKE '" .. v .. "%'")
else
table.insert(columnFiltersTable, "`" .. k .. "` IN ('" .. table.concat(v, "','") .. "')")
end
i = i + 1
end
local columnFilters = table.concat(columnFiltersTable, ' ')
local countRow = exports.oxmysql:single_async(([[
SELECT COUNT(*) AS `count`
FROM `nxtgn_weap_benches`
%s
]]):format(columnFilters))
if not countRow or countRow.count == nil then
return nil
end
local res = exports.oxmysql:query_async(([[
SELECT
`uuid`,
`title`,
`access`,
`loc_x` AS `locationX`,
`loc_y` AS `locationY`,
`loc_z` AS `locationZ`,
`loc_h` AS `locationH`,
`created_by` AS `createdBy`,
`created_at` AS `createdAt`
FROM `nxtgn_weap_benches`
%s
ORDER BY `%s` %s
LIMIT %s OFFSET %s
]]):format(
columnFilters,
data.orderColumn,
data.orderDirection,
data.count,
data.offset
))
if not res then
return nil
end
local benches = {}
local accessIdentifiers = framework.getAccessIdentifiers()
for _, bench in ipairs(res) do
table.insert(benches, {
uuid = bench.uuid,
title = bench.title,
access = {
name = bench.access,
label = accessIdentifiers[bench.access] or bench.access,
},
location = {
x = bench.locationX,
y = bench.locationY,
z = bench.locationZ,
h = bench.locationH,
},
createdBy = bench.createdBy,
createdAt = bench.createdAt,
})
end
return {
rows = benches,
pageCount = math.ceil(countRow.count / data.count),
}
end,
--[[
fetchBench - Fetch a bench.
@param data table { uuid = string }
@return table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
]]
fetchAllBenches = function()
local benches = {}
local res = exports.oxmysql:query_async([[
SELECT
`uuid`,
`title`,
`access`,
`loc_x` AS `locationX`,
`loc_y` AS `locationY`,
`loc_z` AS `locationZ`,
`loc_h` AS `locationH`,
`created_by` AS `createdBy`,
`created_at` AS `createdAt`
FROM `nxtgn_weap_benches`
]])
if not res then
return nil
end
local accessIdentifiers = framework.getAccessIdentifiers()
for _, bench in ipairs(res) do
table.insert(benches, {
uuid = bench.uuid,
title = bench.title,
access = {
name = bench.access,
label = accessIdentifiers[bench.access] or bench.access,
},
location = {
x = bench.locationX,
y = bench.locationY,
z = bench.locationZ,
h = bench.locationH,
},
createdBy = bench.createdBy,
createdAt = bench.createdAt,
})
end
return benches
end,
--[[
fetchBench - Fetch a bench.
@param data table { uuid = string }
@return nil | table { uuid = string, title = string, access = table { name = string, label = string }, location = table { x = number, y = number, z = number, h = number }, createdBy = string, createdAt = string }
]]
fetchBench = function(data)
local res = exports.oxmysql:single_async([[
SELECT
`uuid`,
`title`,
`access`,
`loc_x` AS `locationX`,
`loc_y` AS `locationY`,
`loc_z` AS `locationZ`,
`loc_h` AS `locationH`,
`created_by` AS `createdBy`,
`created_at` AS `createdAt`
FROM `nxtgn_weap_benches` WHERE `uuid` = ?
]], {
data.uuid
})
if not res then
return nil
end
local accessIdentifiers = framework.getAccessIdentifiers()
return {
uuid = res.uuid,
title = res.title,
access = {
name = res.access,
label = accessIdentifiers[res.access] or res.access,
},
location = {
x = res.locationX,
y = res.locationY,
z = res.locationZ,
h = res.locationH,
},
createdBy = res.createdBy,
createdAt = res.createdAt,
}
end
}
}
}Replace myframework with the name of your framework in the code.
Write the code for your framework in the client.lua and server.lua files.
useInventory function.oxmysql exports.Change the Framework in the config.lua file to the name of your framework.
Restart the resource.