FiveM Docs
Addons

esx_accessories

Set up esx_accessories for ESX Legacy. Covers mask, helmet, glasses and ear shops, the datastore SQL, every config key, server events, callbacks and the J keybind.

esx_accessories adds shop markers where players buy masks, helmets, glasses and ear accessories, and a personal menu (default key J) to put them on and take them off again. Purchases are stored per character in esx_datastore, so the accessory survives relogs.

Overview

Resourceesx_accessories
SideClient + server
Dependenciesesx_lib, es_extended, esx_skin, esx_datastore, oxmysql (loaded via @oxmysql/lib/MySQL.lua)
DatabaseFour rows in the datastore table
UIESX.OpenContext menus, esx_skin restricted skin menu

In-game flow:

  1. The player walks into a shop marker (Config.Zones) and presses E.
  2. esx_skin opens a restricted menu that only shows the <type>_1 and <type>_2 sliders of that accessory.
  3. On confirmation the server charges Config.Price from cash and stores the chosen drawable and texture.
  4. Later the player presses J, picks Helmet, Ears, Mask or Glasses and toggles the saved item on or off.

Installation

Import the SQL

Run esx_accessories.sql against your ESX database. It inserts the four datastores the script reads and writes:

esx_accessories.sql
INSERT INTO `datastore` (name, label, shared) VALUES
	('user_ears', 'Ears', 0),
	('user_glasses', 'Glasses', 0),
	('user_helmet', 'Helmet', 0),
	('user_mask', 'Mask', 0)
;

Translated labels are in the localization/ folder (de, fr, it, nl, sl, sr, sv, tr). Import only one of them.

Ensure the resource

Start it after its dependencies:

server.cfg
ensure oxmysql
ensure es_extended
ensure esx_lib
ensure skinchanger
ensure esx_skin
ensure esx_datastore
ensure esx_accessories

Configuration

All keys live in config.lua.

KeyDefaultDescription
Config.LocaleGetConvar('esx:locale', 'en')Locale used for menus and notifications.
Config.Price100Cash price of one accessory. Charged with xPlayer.removeMoney.
Config.ShopDistance3.0Server-side maximum distance (meters) to a shop position for buying and saving.
Config.PurchaseSessionDuration60000Milliseconds after payment during which the server accepts the save event.
Config.EnableControlstrueRegisters the accessory key mapping (default J) that opens the accessory menu.
Config.DrawDistance10Distance at which shop markers are drawn.
Config.Size{x = 1.5, y = 1.5, z = 1.0}Marker size. Size.x is also the interaction radius.
Config.Color{r = 50, g = 200, b = 50}Marker color (alpha is fixed to 255).
Config.Type1Marker type. Set to -1 to create no markers at all.
Config.ShopsBlipsOnly Mask at vector3(-1338.1, -1278.2, 3.8)Map blips per accessory type: Pos (list of vector3) and Blip = {sprite, color}.
Config.ZonesEars, Mask, Helmet, GlassesShop positions per accessory type, each { Pos = { vector3, ... } }.

Zone keys are fixed

The keys of Config.Zones must be Ears, Mask, Helmet or Glasses. The server normalizes the accessory name against that list and rejects anything else, so a custom key like Hats will never be purchasable.

Setting Config.Type = -1 skips the whole marker thread. Because the markers also handle enter and exit detection, the shops then stop working. Use a very small Config.Size or a transparent color if you only want to hide the visuals.

The default Config.ShopsBlips only adds a blip for the mask shop because the other zones sit inside clothing stores that esx_clotheshop already marks on the map.

Server events

esx_accessories:pay

TriggerServerEvent('esx_accessories:pay', accessory)
NameTypeDescription
accessorystring'Ears', 'Mask', 'Helmet' or 'Glasses' (case-insensitive).

Charges Config.Price and opens a purchase session for that accessory. The server checks that the player exists, stands near a matching zone and has enough cash. The built-in client uses the esx_accessories:buy callback instead, which runs the same logic and returns the result.

Client-triggerable

Any client can fire this event. It is safe because the server validates position and balance, but it only removes money; nothing is granted until esx_accessories:save follows.

esx_accessories:save

TriggerServerEvent('esx_accessories:save', skin, accessory)
NameTypeDescription
skintableSkin table from skinchanger:getSkin. Only <type>_1 and <type>_2 are stored.
accessorystringAccessory type, same values as above.

Writes has<Type> = true and skin = { <type>_1, <type>_2 } into the user_<type> datastore. The server rejects the call unless there is an unexpired paid session for that accessory and the player is still near the shop. The session is consumed on success.

Callbacks

Callbacks are registered with xLib.callback.registerCompat, so both xLib.callback and ESX.TriggerServerCallback can call them.

NameArgumentsReturns
esx_accessories:getaccessoryhasAccessory (boolean), skin (table with <type>_1 and <type>_2)
esx_accessories:buyaccessorytrue if payment succeeded and a session was opened
esx_accessories:checkMoneyaccessorytrue if the player is near the shop and has at least Config.Price cash

Example: check whether the local player owns a helmet before offering it in your own menu.

client/main.lua
xLib.callback('esx_accessories:get', false, function(hasHelmet, helmetSkin)
    if not hasHelmet then
        return ESX.ShowNotification('You do not own a helmet yet.')
    end

    TriggerEvent('skinchanger:getSkin', function(skin)
        TriggerEvent('skinchanger:loadClothes', skin, helmetSkin)
    end)
end, 'Helmet')

Client events

These are local events (not network events) fired by the marker zones. You can listen to them, for example to show your own help text.

EventArgumentFired when
esx_accessories:hasEnteredMarkerzone (string, e.g. 'Mask')The player enters a shop marker.
esx_accessories:hasExitedMarkerzoneThe player leaves a shop marker. The open context menu is closed.

Reading the stored data from another resource

The data is a normal private datastore, so server scripts can read it through esx_datastore:

server/main.lua
local xPlayer = ESX.GetPlayerFromId(source)

TriggerEvent('esx_datastore:getDataStore', 'user_mask', xPlayer.getIdentifier(), function(store)
    local owned = store.get('hasMask') or false
    local skin = store.get('skin') or {}
    print(('Owns mask: %s, drawable %s'):format(owned, skin.mask_1))
end)

Common issues

  • "No mask/helmet" notification although the item was bought. The datastore rows from the SQL file are missing, or esx_datastore started after the purchase. Check that user_mask and the others exist in the datastore table.
  • Purchase charged but nothing saved. The player walked out of Config.ShopDistance or took longer than Config.PurchaseSessionDuration in the confirmation menu. The money is not refunded in that case.
  • Shop does nothing when pressing E. You changed Config.Type to -1 or renamed a zone key. See the configuration notes above.
  • Masks taken off show a blank head. Mask "off" sets drawable 0; props (helmet, glasses, ears) use -1. Custom clothing packs may need other values.

Frequently asked questions

Independent documentation

esx-fivem.com is a community project and is not affiliated with the ESX Framework team. Facts on this page were checked against the upstream source. Official resources: docs.esx-framework.org and github.com/esx-framework.
Edit on GitHub

Last updated on

On this page