From 10e2278dcb8ccbd0714181f232d4227c61a24068 Mon Sep 17 00:00:00 2001
From: DateFTP <301770276+DateFTP@users.noreply.github.com>
Date: Tue, 4 Aug 2026 13:33:57 +0300
Subject: [PATCH] Add licenseplate resource
New resource that allows players to set a custom license plate
on the vehicle they are driving via an in-game GUI.
Commands:
- /setplate - open the license plate editor window
- /license - alias for /setplate
The client validates the plate length (max 8 characters) and sends
the new value to the server, which applies it with
setVehiclePlateText.
---
[gameplay]/licenseplate/client.lua | 89 ++++++++++++++++++++++++++++++
[gameplay]/licenseplate/meta.xml | 7 +++
[gameplay]/licenseplate/server.lua | 26 +++++++++
3 files changed, 122 insertions(+)
create mode 100644 [gameplay]/licenseplate/client.lua
create mode 100644 [gameplay]/licenseplate/meta.xml
create mode 100644 [gameplay]/licenseplate/server.lua
diff --git a/[gameplay]/licenseplate/client.lua b/[gameplay]/licenseplate/client.lua
new file mode 100644
index 000000000..758a95871
--- /dev/null
+++ b/[gameplay]/licenseplate/client.lua
@@ -0,0 +1,89 @@
+local GUI = {
+ button = {},
+ window = {},
+ label = {},
+ edit = {}
+}
+
+local isWindowOpen = false
+
+function createLicenseWindow()
+ if GUI.window[1] then return end
+
+ GUI.window[1] = guiCreateWindow(671, 319, 307, 152, "License plate", false)
+ guiWindowSetSizable(GUI.window[1], false)
+
+ GUI.edit[1] = guiCreateEdit(13, 46, 284, 44, "", false, GUI.window[1])
+ GUI.label[1] = guiCreateLabel(16, 23, 281, 19, "Specify the number you want to install.", false, GUI.window[1])
+ guiSetFont(GUI.label[1], "default-bold-small")
+
+ GUI.button[1] = guiCreateButton(9, 104, 129, 38, "Set the number", false, GUI.window[1])
+ GUI.button[2] = guiCreateButton(168, 104, 129, 38, "Cancel", false, GUI.window[1])
+
+ guiSetVisible(GUI.window[1], false)
+end
+
+function showLicenseWindow()
+ if isWindowOpen then
+ hideLicenseWindow()
+ return
+ end
+
+ if not GUI.window[1] then
+ createLicenseWindow()
+ end
+ guiSetVisible(GUI.window[1], true)
+ guiSetInputEnabled(true)
+ guiBringToFront(GUI.window[1])
+ guiSetText(GUI.edit[1], "")
+ isWindowOpen = true
+end
+
+function hideLicenseWindow()
+ if GUI.window[1] then
+ guiSetVisible(GUI.window[1], false)
+ guiSetInputEnabled(false)
+ isWindowOpen = false
+ end
+end
+
+addCommandHandler("setplate", showLicenseWindow)
+addCommandHandler("license", showLicenseWindow)
+
+function onClientGUIClick(button, state)
+ if button ~= "left" or state ~= "up" then return end
+ local clicked = source
+ if clicked == GUI.button[1] then
+ local plate = guiGetText(GUI.edit[1])
+ plate = plate:gsub("^%s*(.-)%s*$", "%1")
+
+ if plate == "" then
+ outputChatBox("Please enter a license plate number.", 255, 0, 0)
+ return
+ end
+
+ if #plate > 8 then
+ outputChatBox("License plate cannot exceed 8 characters.", 255, 0, 0)
+ return
+ end
+
+ -- (Optionally – uncomment if necessary)
+ -- if not string.match(plate, "^[A-Za-z0-9 ]+$") then
+ -- outputChatBox("License plate contains invalid characters. Use only letters, numbers and spaces.", 255, 0, 0)
+ -- return
+ -- end
+
+ local vehicle = getPedOccupiedVehicle(localPlayer)
+ if not vehicle then
+ outputChatBox("You are not in a vehicle.", 255, 0, 0)
+ return
+ end
+
+ triggerServerEvent("LicensePlate:set", localPlayer, vehicle, plate)
+ hideLicenseWindow()
+
+ elseif clicked == GUI.button[2] then
+ hideLicenseWindow()
+ end
+end
+addEventHandler("onClientGUIClick", root, onClientGUIClick)
\ No newline at end of file
diff --git a/[gameplay]/licenseplate/meta.xml b/[gameplay]/licenseplate/meta.xml
new file mode 100644
index 000000000..6da65ced2
--- /dev/null
+++ b/[gameplay]/licenseplate/meta.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/[gameplay]/licenseplate/server.lua b/[gameplay]/licenseplate/server.lua
new file mode 100644
index 000000000..87aa7ca72
--- /dev/null
+++ b/[gameplay]/licenseplate/server.lua
@@ -0,0 +1,26 @@
+addEvent("LicensePlate:set", true)
+addEventHandler("LicensePlate:set", root,
+ function(vehicle, plate)
+ local player = source
+
+ if not player or not isElement(player) then return end
+ if not vehicle or not isElement(vehicle) then return end
+
+ if getPedOccupiedVehicle(player) ~= vehicle then
+ outputChatBox("You are not in that vehicle.", player, 255, 0, 0)
+ return
+ end
+
+ if #plate > 8 then
+ outputChatBox("License plate too long.", player, 255, 0, 0)
+ return
+ end
+ --[[if not string.match(plate, "^[A-Za-z0-9 ]+$") then
+ outputChatBox("Invalid characters in license plate.", player, 255, 0, 0)
+ return
+ end]]
+
+ setVehiclePlateText(vehicle, plate)
+ outputChatBox("License plate set to: " .. plate, player, 0, 255, 0)
+ end
+)
\ No newline at end of file