Custom Framework
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 frameworksdirectory in the resource, named after your framework.
- 
Create a client.luaandserver.luafile in the folder. Should look like this:├── frameworks │ └── myframework │ ├── client.lua │ └── server.lua
- 
Add the following code to the client.luafile:/frameworks/myframework/client.lua 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.luafile:/frameworks/myframework/server.lua 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 myframeworkwith the name of your framework in the code.
- 
Write the code for your framework in the client.luaandserver.luafiles.- Make sure that the functions returns the correct values.
- If you want to use an inventory, you can use the useInventoryfunction.
- If you want to use a database, you can use the oxmysqlexports.
- If your framework are similar to an existing framework, you can use the existing framework as a base.
 
- 
Change the Frameworkin theconfig.luafile to the name of your framework.
- 
Restart the resource.