Commit Graph

8064 Commits

Author SHA1 Message Date
_Not_ 46b9ecfb29 Merge pull request #1836 from seltonmt012/fix/inventory-quantity-dialog
fix(esx_inventory): let the quantity dialog accept a corrected amount
2026-08-14 19:42:47 -05:00
_Not_ 569ca0111e Merge pull request #1837 from seltonmt012/fix/esx-lib-helpers
fix(esx_lib): correct the shared table and string helpers
2026-08-14 19:42:19 -05:00
_Not_ 0a73be82c4 Merge pull request #1838 from seltonmt012/fix/translatecap-return
fix(es_extended/locale): return a single value from TranslateCap
2026-08-14 19:41:52 -05:00
_Not_ 4dd4c9c8ca Merge pull request #1839 from seltonmt012/fix/cron-callback-guard
fix(cron): keep the scheduler alive when a job errors
2026-08-14 19:41:00 -05:00
_Not_ ce044440fd Merge pull request #1840 from seltonmt012/fix/menu-focus-timer
fix(esx_menu_dialog, esx_menu_list): do not grab NUI focus after the menu closed
2026-08-14 19:40:12 -05:00
_Not_ 8f930535fa Merge pull request #1842 from seltonmt012/fix/progressbar-timing
fix(esx_progressbar): honour the requested duration and ignore cancelled runs
2026-08-14 19:39:45 -05:00
_Not_ cad375c63d Merge pull request #1844 from seltonmt012/fix/adjustments-relog-leak
fix(es_extended/client): stop stacking adjustment threads on every relog
2026-08-14 19:39:13 -05:00
_Not_ 41639c5323 Merge pull request #1845 from seltonmt012/fix/points-loop-once
fix(esx_lib/points): start the points loop only once
2026-08-14 19:37:02 -05:00
_Not_ 76b10dd5f6 Merge pull request #1847 from seltonmt012/fix/setplate-stale-handle
fix(es_extended/server/classes/vehicle): keep xVehicle usable after setPlate
2026-08-14 19:36:17 -05:00
_Not_ b9c317c0e5 Merge pull request #1846 from seltonmt012/fix/vehicle-secondary-colour
fix(esx_lib/game): keep the secondary colour when the primary is custom
2026-08-14 19:36:09 -05:00
_Not_ 5a662ffe4c Merge pull request #1850 from seltonmt012/fix/cron-midnight-catchup
fix(cron): catch up jobs that were missed across midnight
2026-08-14 19:34:37 -05:00
_Not_ e9b80d42df Merge pull request #1854 from seltonmt012/fix/createjob-existing-job
fix(es_extended/server/modules/createJob): let CreateJob add grades to an existing job
2026-08-14 19:33:45 -05:00
Selt 06ac429353 fix(es_extended/server/modules/createJob): store the encoded skin in ESX.Jobs
CreateJob wrote the caller's skin table straight into ESX.Jobs while the row
got the encoded string. setJob then ran json.decode over a table and died with
"bad argument #1 to 'json.decode' (string expected, got table)". A restart
appeared to repair the job, because the grade came back from the database as a
string. Cache the same value that goes into the row.
2026-08-08 20:25:15 +02:00
Selt a5623a2039 fix(es_extended/server/modules/createJob): cache only the grades that were inserted
A grade that already exists in the database is skipped for the insert, but it
was still written to ESX.Jobs. A second CreateJob call therefore replaced the
stored name, label and salary in memory while the row itself kept the old
values, and the two only agreed again after a restart. CreateJob now caches
just the grades it inserted.

Label and type of an existing job come from the database for the same reason.
The grade lookup compares strings, so passing grade "1" for an existing grade 1
no longer inserts a second row.
2026-08-08 20:25:15 +02:00
Selt 4b07899061 fix(es_extended/server/modules/createJob): only insert what is missing
The jobs row was inserted unconditionally, so adding a grade to an existing
job hit a duplicate primary key and rolled the whole transaction back. The
guard above it only caught the case where there was nothing to add, and it
read ESX.Jobs, which does not list jobs that have no grades yet.
2026-08-08 20:25:14 +02:00
_Not_ 527a99c851 Merge pull request #1851 from seltonmt012/fix/esx-lib-uint-and-interactions
fix(esx_lib): make the uint check work and remove an unread table
2026-08-07 22:15:05 -05:00
_Not_ 9983c7c2a0 Merge pull request #1852 from seltonmt012/fix/console-commands-nil-xplayer
fix(es_extended/server/modules/commands): stop console commands from throwing on a missing xPlayer
2026-08-07 22:12:09 -05:00
_Not_ c4df5faa46 Merge pull request #1853 from seltonmt012/fix/discord-presence-placeholders
fix(es_extended/client/modules/adjustments): escape placeholder values before substitution
2026-08-07 22:07:09 -05:00
_Not_ 110b30a2ca Merge pull request #1855 from seltonmt012/fix/setinventoryitem-noop
fix(es_extended/server/classes/player): stop setInventoryItem throwing when nothing changes
2026-08-07 21:54:30 -05:00
_Not_ b5cbfb9841 Merge pull request #1856 from seltonmt012/fix/billing-target-length
fix(es_extended): widen the identifier columns that are too short
2026-08-07 21:52:38 -05:00
_Not_ fd2a1a037d Merge pull request #1857 from seltonmt012/fix/multichar-default-skin
fix(esx_multicharacter): correct the chin_3 typo in the default skins
2026-08-07 21:39:21 -05:00
_Not_ 08d83d238d Merge pull request #1859 from rwixy/centralize-admin
feat(commands): centralize admin permissions via Config.AdminGroups
2026-08-07 15:29:24 -05:00
Ihsan 4a22b68511 refactor(commands): simplify group filtering and update deprecation notice for AdminGroups 2026-08-08 00:16:03 +05:30
_Not_ 519d9fe6b2 Merge pull request #1862 from rwixy/ox-inv
refactor(oxinventory): improve inventory module initialization and cleanup
2026-08-06 13:27:35 -05:00
Ihsan 1a430fe604 refactor(ox_inventory): validate module methods and fix hasItem return contract 2026-08-06 23:43:48 +05:30
Ihsan 87003d41aa refactor(oxinventory): improve error handling and streamline inventory functions 2026-08-06 21:17:52 +05:30
Ihsan e85b05558c feat: per-command group permissions for admin commands 2026-08-05 13:54:44 +05:30
_Not_ cedac84504 Merge pull request #1811 from ASTROWwwW/feat/edition-detection
feat(esx_lib): add isEnhanced game edition detection
2026-08-04 18:26:44 -05:00
_Not_ e4887027dd Merge pull request #1861 from rwixy/sql-update
refactor(legacy.sql): consolidate addon_account entries and remove legacy Banker Job SQL
2026-08-04 18:26:15 -05:00
_Not_ 85936f0c4d Merge pull request #1803 from rwixy/medal-lib
feat: integrate Medal.tv globally
2026-08-04 18:21:33 -05:00
Ihsan 3aa3bf27d6 fix(legacy.sql): add missing bank_savings entry to addon_account and remove redundant banker job inserts 2026-08-01 20:52:52 +05:30
Ihsan 9128ad5857 feat(commands): centralize admin permissions via Config.AdminGroups 2026-08-01 20:18:23 +05:30
Selt f5ed52d03b fix(esx_menu_dialog): drop the timeout id bookkeeping
With the callback checking whether a menu is still open, cancelling the
previous timers by id is no longer needed.

The table it kept them in was never emptied, so every open iterated over
every id ever created and called ClearTimeout on all of them. That is
quadratic, and clearTimeout only marks an id in xLib's CancelledTimeouts,
which is cleared when the timer fires. Ids that had already fired stayed
in there for good, in a table shared by every resource.
2026-07-30 02:38:25 +02:00
Selt 760f31eb6c fix(esx_multicharacter): correct the chin_3 typo in the default skins
Both default skins spell the chin width key chin_13. skinchanger only
knows chin_1 through chin_4, so the value never reaches the ped and the
key is stored as junk in users.skin.

It also breaks the ped. SetFace reads every feature through
Normalise(weight, 10), which divides, so a missing chin_3 raises
"attempt to perform arithmetic on a nil value" at feature index 17.
Everything after that in ApplySkin is skipped: the remaining features,
the eye colour, the head overlays, the components and the props.
2026-07-30 01:50:20 +02:00
Selt a890abd110 fix(esx_lib): make table.dump work and drop the shadowed contains
table.dump called its own table argument as a function, so it threw
"attempt to call a table value" for every table that had at least one
entry. An empty table returned "{ } " and a non-table returned tostring,
which is why it looks fine until you actually dump something.

table.contains was defined twice in the same file. Lua keeps the second
one, so the annotated version further up was unreachable. Removed the
unreachable one; behaviour is unchanged.
2026-07-30 01:45:05 +02:00
Selt ad195a6516 fix(es_extended): widen rented_vehicles.owner to hold an identifier
The column is varchar(22), which is a leftover from Steam identifiers.
esx_vehicleshop writes a player identifier into it, and on a
multicharacter server those are 46 characters long, so renting a vehicle
out fails.

The stock vehicle is deleted from cardealer_vehicles before the insert
runs, so the dealer loses the car and the customer never gets a rental
contract. Neither side is told anything, because the handler dies on the
insert.

esx_vehicleshop's own esx_vehicleshop.sql already declares varchar(60).
2026-07-30 01:22:37 +02:00
Selt 6c50d22615 fix(es_extended): widen billing.target to hold an identifier
The column is varchar(40) but esx_billing writes an identifier into it, and
those are 46 characters once multicharacter is on, so the insert fails with
1406 and the handler dies before notifying either player. The other two
identifier columns in the same table are already varchar(60).
2026-07-28 03:06:20 +02:00
Selt e6b6d1f40a fix(es_extended/server/classes/player): make setInventoryItem a no-op on no change
Setting an item to the count it already has sent a zero difference to
removeInventoryItem, which treats zero as an error and calls error(). That
unwinds out of the caller, so anything after the call was skipped.
2026-07-28 01:54:04 +02:00
Selt e60674cb56 fix(es_extended/client/modules/adjustments): escape placeholder values
Placeholder results go straight into gsub as the replacement string, so a %
in a player name either throws and kills the presence thread or silently
mangles the output. The "Unknown" fallback above it was unreachable because
the error() on the previous line always propagates.
2026-07-28 01:46:58 +02:00
Selt 65d6d32c6e fix(es_extended/server/modules/commands): survive console use and fix notifications
RegisterCommand hands the callback xPlayer or false, so group, refreshitems
and fix threw from the server console instead of doing their work. The same
three lines still used the old showNotification argument list, which put a
number into title and made esx_notify throw, so players got no confirmation
either.
2026-07-28 01:16:36 +02:00
Selt bac379118d fix(esx_lib): match uint against integer and drop the unread interaction list
math.type only returns 'integer', 'float' or nil, so the 'uint' branch
comparing against 'int' never matched and threw whenever throw_error was set.
pressedInteractions was written on every interaction and read nowhere, so it
grew for the whole session and kept removed interactions reachable.
2026-07-28 00:53:00 +02:00
Selt 1b5e29c62b fix(cron): build the scheduled time from the day the job belongs to
OnTime always built scheduledTimestamp from the date of the current tick, so
a tick that skipped the 23:59 minute compared it against 23:59 of the next
day. The run was dropped without a trace. Fall back to the previous day when
the scheduled time lands in the future.
2026-07-28 00:44:30 +02:00
Selt 0bf541905c fix(es_extended/server/classes/vehicle): update self.plate in setPlate
setPlate re-keys Core.vehicles but never updates self.plate, so the object
it was called on keeps pointing at the removed key. isValid() fails from
then on and every later call on that handle is a no-op, including delete(),
which leaves the vehicle spawned and owned_vehicles.stored at false.
2026-07-28 00:13:58 +02:00
Selt eb58d4674b fix(esx_lib/game): keep the secondary colour when the primary is custom
setVehicleProperties passes props.color1 straight into SetVehicleColours when the
secondary colour comes from the palette. If the primary is a custom RGB colour,
the getter stored it as a table, so a table reaches a native that wants a paint
index and the call does nothing. The secondary colour is silently lost.

Any car sprayed with a custom primary and a palette secondary comes back from
storage with the wrong secondary colour.

Fall back to the vehicle's current primary when props.color1 is not a paint index.
The custom primary was already applied by the block above, so nothing is lost.

Measured in game on artifact 25770, reading properties off a car with a custom red
primary and palette secondary 12, then applying them to a second car:
  before: color2 came back 0
  after:  color2 came back 12
The custom primary survives either way, mods and wheels are untouched.
2026-07-27 23:04:19 +02:00
Selt 443953b24b fix(esx_lib/points): start the points loop only once
startLoop is called from the esx:playerLoaded handler in es_extended, which fires
again on every character switch, and the loop it creates runs `while true` with no
exit. Each relog therefore leaves another points thread running: they all scan the
same table, fire the same enter and leave callbacks, and none of them ever stop.

A guard makes a second call a no-op. The loop is meant to live for the resource
lifetime, so starting it twice is never right.

Measured in game on artifact 25770, counting scans over four seconds:
  before: 1.0 -> 2.0 -> 3.0 loops across two relogs
  after:  1.0 -> 1.0 -> 1.0
Points keep working, the surviving loop is unaffected.

Note this touches the same file as #1831, which guards the callbacks inside the
loop. The two changes are in different places and independent of each other.
2026-07-27 22:50:24 +02:00
Selt d41b0f1feb fix(es_extended/client): stop stacking adjustment threads on every relog
Adjustments:Load() runs from the esx:playerLoaded handler, so it runs again on
every character switch. AmmoAndVehicleRewards, Multipliers and DiscordPresence
each start a `while true` thread that never exits, and SeatShuffle and
DisableRadio register another esx:enteredVehicle handler. Nothing tears any of it
down, so a player who switches characters a few times ends up with several
per-frame threads all writing the same values, and client performance degrades
until they reconnect.

The loops now run `while ESX.PlayerLoaded`, the same way StartServerSyncLoops and
Actions:SlowLoop already do, so they end on logout and a fresh one starts on the
next load. The two event handlers are registered once. Actions:Init already
documents this exact concern in a comment; Adjustments never got the same
treatment.

Measured in game on artifact 25770 by counting thread ticks per frame:
  before: 2.01 -> 3.01 -> 4.02 across two relogs
  after:  1.00 -> 1.00 -> 1.00
2026-07-27 22:36:56 +02:00
Selt 07b9253401 fix(esx_progressbar): honour the requested duration and ignore cancelled runs
The processing thread counted the remaining time down in 1000 ms steps and read
the global CurrentProgress on every wake, which caused two problems.

Any duration that is not a whole number of seconds finished late, since the
thread only checked after another full second. The NUI bar runs on wall clock
time, so the bar completed and the player then stood frozen waiting for onFinish.

Cancelling did not stop the thread either. CancelProgressbar clears
CurrentProgress, but the sleeping thread only notices up to a second later, and if
a new bar was started in that window it saw a non-nil CurrentProgress and kept
decrementing the new one alongside its own thread. The new bar then finished at
roughly half its length, firing onFinish early.

The run now carries an id, so a thread stops as soon as its run is no longer the
current one, and the end is an absolute timestamp rather than a countdown.

Measured in game on artifact 25770, requested versus actual onFinish:
  before: 500 -> 1011, 4200 -> 5042, cancel then 3000 -> 1427
  after:  500 ->  519, 4200 -> 4222, cancel then 3000 -> 3058
2026-07-27 21:51:15 +02:00
Selt e801856d1a fix(esx_menu_dialog, esx_menu_list): do not grab NUI focus after the menu closed
Opening a menu schedules SetNuiFocus(true, true) 200 ms later so the NUI has time
to render. Closing sets the focus off but does not cancel that timer, so a menu
closed within those 200 ms leaves the timer to fire afterwards and turn the focus
back on with nothing open: the player gets a cursor and loses movement and input
until another menu is opened and closed. esx_menu_list never stored the timer id
at all, so it could not be cancelled in any case.

Checking inside the callback whether a menu is still open fixes both, and also
covers a quick open-close-open where cancelling by id would drop a focus that is
still wanted. esx_menu_default is unaffected, it has no timer.

Tested on artifact 25770 with a dialog closed 50 ms after opening: before,
IsNuiFocused() was still true 600 ms later; after, it stays false.
2026-07-27 21:18:26 +02:00
Selt f98a6e2245 fix(cron): keep the scheduler alive when a job errors
Registered jobs are third party callbacks and were invoked bare. An error inside
one propagates out of OnTime into Tick, so Tick never reaches its
SetTimeout(60000, Tick) at the end and nothing ever reschedules it. From that
moment cron is dead for every resource on the server - paychecks, cleanups,
anything registered through cron:runAt - until a restart. The operator sees one
stack trace and then silence.

Wrapping the callback keeps the loop and the reschedule intact, and names the job
that failed.

Simulated the scheduler chain in standalone Lua with three jobs where the first
raises: before, the tick died before SetTimeout and the two later jobs never ran
again; after, the failure is reported and both later jobs fire.
2026-07-27 21:17:12 +02:00
Selt 19272bc3e8 fix(es_extended/locale): return a single value from TranslateCap
TranslateCap returned the result of gsub directly, and gsub returns the string
plus the number of substitutions. Whenever TranslateCap is the last argument of
a call, that count is passed along as an extra argument.

The common case is showNotification(msg, notifyType, ...), which is called with
just the message in about 49 places in the core. Those all pass notifyType = 1
instead of nil, so the notification is rendered with a numeric type rather than
the intended default. 71 call sites in total have TranslateCap in trailing
position.

Wrapping the call in parentheses truncates it to one value. The string itself is
unchanged, and nothing reads the second value.
2026-07-27 21:14:26 +02:00