monitor/resource/cl_playerlist.lua
2025-04-16 22:30:27 +07:00

203 lines
6.9 KiB
Lua

-- Prevent running if menu is disabled
if not TX_MENU_ENABLED then return end
-- =============================================
-- Client PlayerList handler
-- =============================================
-- Optimizations
local tonumber = tonumber
local tostring = tostring
local floor = math.floor
-- Variables & Consts
LOCAL_PLAYERLIST = {} -- available globally in tx
local requirePlayerNames = false
local vTypeMap = {
["0"] = "walking",
["1"] = "driving", --automobile
["2"] = "biking",
["3"] = "boating",
["4"] = "flying", --heli
["5"] = "flying", --plane
["6"] = "boating", --submarine
["7"] = "driving", --trailer
["8"] = "driving", --train
}
-- Transforms the playerlist and sends to react
-- The playerlist is converted to an object array to save refactor time
function sendReactPlayerlist()
local upload = {}
for pids, playerData in pairs(LOCAL_PLAYERLIST) do
upload[#upload + 1] = {
id = tonumber(pids),
name = playerData.name or "unknown",
health = playerData.health,
dist = playerData.dist,
vType = playerData.vType,
admin = playerData.admin
}
end
-- print("========== function sendReactPlayerlist()")
-- print(json.encode(upload))
-- print("------------------------------------")
sendMenuMessage('setPlayerList', upload)
end
-- Triggered when the admin authenticates
-- Replaces current playerlist
RegisterNetEvent('txcl:plist:setInitial', function(payload)
-- print("========== EVENT setInitialPlayerlist")
-- print(json.encode(payload)) -- [[id, name]]
-- print("------------------------------------")
LOCAL_PLAYERLIST = {}
for _, playerData in pairs(payload) do
local pids = tostring(playerData[1])
LOCAL_PLAYERLIST[pids] = {
name = playerData[2],
health = 0,
dist = -1,
vType = "unknown",
admin = false
}
end
-- print("------------------------------------")
-- print(json.encode(LOCAL_PLAYERLIST, {indent = true}))
-- print("------------------------------------")
sendReactPlayerlist()
end)
-- Triggered on the return of "getDetailedPlayerlist"
-- > run through inbound playerlist updating existing data
-- > try to get the dist from all players (onesync only)
RegisterNetEvent('txcl:plist:setDetailed', function(players, admins)
-- print("========== EVENT setDetailedPlayerlist")
-- print(json.encode(players)) -- [[id, health, vType, xCoord, yCoord, name]]
-- print("------------------------------------")
local myID = GetPlayerServerId(PlayerId())
local myPed = PlayerPedId()
local myCoords = GetEntityCoords(myPed)
for _, playerData in pairs(players) do
local pid = playerData[1]
local pidStr = tostring(playerData[1])
local localPlayer = LOCAL_PLAYERLIST[pidStr]
-- Set inbound data
if localPlayer == nil then
debugPrint("Playerlist: received detailed info for player "..pidStr.." not present in local playerlist")
requirePlayerNames = true
LOCAL_PLAYERLIST[pidStr] = {
name = "unknown",
health = playerData[2],
vType = vTypeMap[tostring(playerData[3])] or "unknown",
admin = false
}
else
LOCAL_PLAYERLIST[pidStr].health = playerData[2]
LOCAL_PLAYERLIST[pidStr].vType = vTypeMap[tostring(playerData[3])] or "unknown"
-- Set the player name if available - used when the playerJoining (txcl:plist:updatePlayer) was missed
if playerData[6] then
LOCAL_PLAYERLIST[pidStr].name = playerData[6]
end
end
--Mark as updated
LOCAL_PLAYERLIST[pidStr].inLastUpdate = true
-- Getting distance + health for RedM
-- NOTE: RedM doesn't save ped health data on server, so need to get locally
if pid == myID then
LOCAL_PLAYERLIST[pidStr].dist = 0
if IS_REDM then
LOCAL_PLAYERLIST[pidStr].health = GetPedHealthPercent(myPed)
end
else
--calc distance (onesync only, 2d only)
if playerData[4] == nil or playerData[5] == nil then
LOCAL_PLAYERLIST[pidStr].dist = -1
else
local remoteCoords = vector3(playerData[4], playerData[5], myCoords.z)
LOCAL_PLAYERLIST[pidStr].dist = floor(#(myCoords - remoteCoords))
end
--get health locally
if IS_REDM then
local remotePlayer = GetPlayerFromServerId(pid)
if remotePlayer == -1 then
LOCAL_PLAYERLIST[pidStr].health = -1
else
local remotePed = GetPlayerPed(remotePlayer)
LOCAL_PLAYERLIST[pidStr].health = GetPedHealthPercent(remotePed)
end
end
end
end
--Check if player disconnected and the playerDropped (txcl:plist:updatePlayer) was missed
for playerID, playerData in pairs(LOCAL_PLAYERLIST) do
if playerData.inLastUpdate == true then
playerData.inLastUpdate = false
else
debugPrint("Playerlist: did not receive detailed info for player "..playerID.." present in local playerlist")
LOCAL_PLAYERLIST[playerID] = nil
end
end
-- Mark admins
for _, adminID in pairs(admins) do
local id = tostring(adminID)
if LOCAL_PLAYERLIST[id] ~= nil then
LOCAL_PLAYERLIST[id].admin = true
end
end
-- print("------------------------------------")
-- print(json.encode(LOCAL_PLAYERLIST, {indent = true}))
-- print("------------------------------------")
sendReactPlayerlist()
end)
-- Triggered on player join/leave
-- add/remove specific id to playerlist
RegisterNetEvent('txcl:plist:updatePlayer', function(id, data)
local pids = tostring(id)
if data == false then
debugPrint("^2txcl:plist:updatePlayer: ^3"..id.."^2 disconnected")
LOCAL_PLAYERLIST[pids] = nil
else
debugPrint("^2txcl:plist:updatePlayer: ^3"..id.."^2 connected")
LOCAL_PLAYERLIST[pids] = {
name = data,
health = 0,
dist = -1,
vType = "unknown",
admin = false
}
end
sendReactPlayerlist()
end)
-- Triggered when the "player" tab opens in the menu, and every 5s after that
RegisterSecureNuiCallback('signalPlayersPageOpen', function(_, cb)
TriggerServerEvent('txsv:req:plist:getDetailed', requirePlayerNames)
requirePlayerNames = false
cb({})
end)
-- DEBUG only
-- RegisterCommand('tnew', function()
-- TriggerServerEvent('txsv:req:plist:getDetailed')
-- end)
-- RegisterCommand('tprint', function()
-- print("------------------------------------")
-- print(json.encode(LOCAL_PLAYERLIST, {indent = true}))
-- print("------------------------------------")
-- end)