v1.14.4

Summary

This release introduces playtime-based jail sentences that only count down while a player is online and actively jailed, alongside the existing date-based expiry mode. Noclip and clothing reload behavior are routed through the action system for more reliable handling, NUI callbacks are fixed, and the admin UI receives several quality-of-life updates.

What's New

Features

  • Jail playtime mode — new time_mode option in config.json (date or playtime). Playtime sentences track duration_ms and remaining_ms in the database and only decrement while the player is online, loaded, and jailed.
  • Sentence command improvements — /sentence supports both time modes, auto-close timing (auto_close_seconds), and updated SQL active-jail detection.
  • Clothing reload — reloadskin is only run for supported appearance resources; fallback else branch ensures other setups still reload.

Bug Fixes

  • NUI callbacks — response handling and callback registration fixes.
  • UI fixes — assorted admin panel UI corrections (jail modal, moderation, monitoring, players, statistics, and related components).

Other

  • fxmanifest — production ui_page restored (web/index.html); local dev URL commented out.
  • Player jail management — optimizations to jail state handling on connect and during sessions.

File Changes

⚙️ Configuration & Bridge Files

These files are not encrypted. Review the changes below — you may need to manually apply updates to your server's config files.

fxmanifest.lua

 author 'luxu-gg'
 description 'Admin menu for FiveM'
-version '1.14.3'
+version '1.14.4'

 dependencies {
     'oxmysql',

 files { 'config/config.json', 'config/themes.css', 'config/permissions.json', 'config/sounds/*', 'config/help.lua', 'config/constants/*.json', 'config/logging/cl_logs.lua', 'config/teleports.lua', 'config/commands/cl_commands.lua', 'config/custom_functions/client.lua', 'locales/*.json', "web/index.html", 'web/**/*', "bridge/framework.lua", "bridge/shared/**/*.lua", "bridge/client/**/*.lua" }

---ui_page 'web/index.html'
-ui_page 'http://localhost:7017/'
+ui_page 'web/index.html'
+--ui_page 'http://localhost:7017/'

 escrow_ignore { 'config/**/*', 'bridge/**/*' }

bridge/client/clothing.lua

 local clothing = {}

 function clothing.loadPlayerSkin()
-    ExecuteCommand("reloadskin")
     if GetResourceState("qb-clothing") == "started" then
         TriggerServerEvent("qb-clothes:loadPlayerSkin")
     elseif GetResourceState("illenium-appearance") == "started" then
         local result = lib.callback.await("illenium-appearance:server:getAppearance")
         exports["illenium-appearance"]:setPlayerAppearance(result)
     elseif GetResourceState("rcore_clothing") == "started" then
         ExecuteCommand("reloadskin")
     elseif GetResourceState("skinchanger") == "started" and GetResourceState("esx_skin") == "started" then
         Framework.object.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin)
             TriggerEvent('skinchanger:loadSkin', skin)
         end)
     elseif GetResourceState("qs-appearance") == "started" or GetResourceState("ak47_clothing") == "started" or GetResourceState("ak47_qb_clothing") == "started" then
         ExecuteCommand("reloadskin")
+    else
+        ExecuteCommand("reloadskin")
     end
 end

config/config.json

   },
   "jail": {
     "enabled": true,
+    "time_mode": "playtime",
     "jail_cells": [
       { "name": "Jail Cell 1", "coords": { "x": 459.57, "y": -1001.68, "z": 24.9, "w": 266.29 }, "safeRadius": 5.0 },
       { "name": "Jail Cell 2", "coords": { "x": 458.89, "y": -998.03, "z": 24.9, "w": 269.29 }, "safeRadius": 5.0 },
       { "name": "Jail Cell 3", "coords": { "x": 460.5, "y": -994.34, "z": 24.9, "w": 269.29 }, "safeRadius": 5.0 }
     ],
-    "_comment_": "Unique Names | The safe radius is the radius around the jail cell where the player walk freely. If the player is not within the safe radius, they will be teleported to the cell."
+    "_comment_": "time_mode can be 'date' or 'playtime'. Date sentences expire at the exact timestamp. Playtime sentences only count down while the player is online, loaded, and actively jailed. Jail cell names must be unique. The safe radius is the radius around the jail cell where the player walk freely. If the player is not within the safe radius, they will be teleported to the cell."
   },
-  "weather":{
+  "weather": {
     "enabled": false,
     "__comment__": "Use luxu's built-in weather system"
   },
     "sentence": {
       "enabled": true,
       "name": "sentence",
-      "description": "Check your remaining jail time"
+      "description": "Check your remaining jail time",
+      "auto_close_seconds": 5,
+      "__comment__": "Set auto_close_seconds to 0 to make it persistent"
     },
     "jail": {
       "enabled": true,

config/commands/cl_commands.lua

         if not HasPermission("self.noclip", false, true) then return end
         local freeFall = IsDisabledControlPressed(0, 36)

-        ToggleNoclip(nil, freeFall)
+        Luxu.callbackAsync("actions:noclip", false, function() end, {
+            target = Luxu.cache.serverId,
+            freeFall = freeFall
+        })
     end or function() end
 })

config/commands/sv_commands.lua

         restricted = false,
     }, function(src, args)
         if not HasPermission(src, "self.noclip", true, true) then return end
-        Luxu.triggerClientEvent('actions:noclip', src);
+        exports[GetCurrentResourceName()]:action("noclip", src, {
+            target = src
+        })
     end)
 end

     end)
 end

+local JAIL_ACTIVE_CONDITION =
+    "((COALESCE(time_mode, 'date') = 'date' AND expires_at > UTC_TIMESTAMP()) OR (time_mode = 'playtime' AND remaining_ms > 0))"

 if commands.sentence.enabled then
     Luxu.addCommand(commands.sentence.name, {
         help = commands.sentence.description,

         -- Get jail information from database
         local jailInfo = MySQL.prepare.await(
-            "SELECT *, TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', expires_at) as expires_at, TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', created_at) as created_at FROM luxu_admin_jail WHERE char_id = ? AND expires_at > UTC_TIMESTAMP()",
+            "SELECT *, TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', expires_at) as expires_at, TIMESTAMPDIFF(SECOND, '1970-01-01 00:00:00', created_at) as created_at FROM luxu_admin_jail WHERE char_id = ? AND " ..
+            JAIL_ACTIVE_CONDITION .. " ORDER BY id DESC LIMIT 1",
             { charId })


             return
         end

-        jailInfo.expires_at = jailInfo.expires_at * 1000
-        jailInfo.created_at = jailInfo.created_at * 1000
+        jailInfo.time_mode = jailInfo.time_mode == "playtime" and "playtime" or "date"
+        jailInfo.expires_at = (tonumber(jailInfo.expires_at) or 0) * 1000
+        jailInfo.created_at = (tonumber(jailInfo.created_at) or 0) * 1000
+        jailInfo.duration_ms = tonumber(jailInfo.duration_ms) or math.max(0, jailInfo.expires_at - jailInfo.created_at)
+        jailInfo.remaining_ms = math.max(0, tonumber(jailInfo.remaining_ms) or 0)

         -- Calculate remaining time
         local currentTime = os.time() * 1000 -- Convert to milliseconds
-        local remainingMs = jailInfo.expires_at - currentTime
+        local remainingMs = jailInfo.time_mode == "playtime" and jailInfo.remaining_ms or jailInfo.expires_at - currentTime
         local isExpired = remainingMs <= 0

         -- Send sentence information to client for display

sql/install.sql

   `cell_name` varchar(255) DEFAULT NULL,
   `last_coords` longtext NOT NULL,
   `bucket` int(10) unsigned NOT NULL DEFAULT 0,
+  `time_mode` enum('date','playtime') NOT NULL DEFAULT 'date',
+  `duration_ms` bigint(20) unsigned NOT NULL DEFAULT 0,
+  `remaining_ms` bigint(20) unsigned NOT NULL DEFAULT 0,
   `expires_at` datetime NOT NULL,
   `created_at` datetime NOT NULL DEFAULT current_timestamp(),
   PRIMARY KEY (`id`),

Full list of files changed

Change File
Modified bridge/client/clothing.lua
Modified client/modules/actions.lua
Modified client/modules/nui.lua
Modified client/modules/zones.lua
Modified client/utils/cl_functions.lua
Modified client/utils/noclip.lua
Modified config/commands/cl_commands.lua
Modified config/commands/sv_commands.lua
Modified config/config.json
Modified fxmanifest.lua
Modified server/modules/jail.lua
Modified sql/install.sql
Modified server/index.js
Modified web/