GUI = {} local screenWidth, screenHeight = guiGetScreenSize() local comboCategories = { server = {"Server info", "Lua timing", "Lua time recordings", "Lua memory", "Packet usage", "Sqlite timing", "Bandwidth reduction", "Bandwidth usage", "Server timing", "Function stats", "Debug info", "Debug table", "Lib memory", "Help"}, client = {"Lua timing", "Lua time recordings", "Lua memory", "Lib memory", "Packet usage", "Help"}, } function GUI:exists() return self.window and true end function GUI:create() if self.window then return end local width = math.min((screenWidth - 40), 1280) local height = math.min((screenHeight - 40), 720) local panelWidth = 170 local contentWidth = width - panelWidth self.window = GuiWindow((screenWidth - width) / 2, (screenHeight - height) / 2, width, height, "Ingame Performance Browser", false) self.window:setAlpha(1.0) self.window:setSizable(false) self.window:setVisible(false) local targetLabel = GuiLabel(10, 30, panelWidth - 20, 15, "Target:", false, self.window) targetLabel:setFont("default-bold-small") targetLabel:setHorizontalAlign("center") self.target = GuiComboBox(10, 50, panelWidth - 20, 200, "", false, self.window) self.target:addItem("Client") self.target:addItem("Server") self.target:setSelected(0) --> Client self.target:autoHeight() self.targetItem = self.target:getItemText(self.target:getSelected()) addEventHandler("onClientGUIComboBoxAccepted", self.target, function (...) self:onTargetUpdate(...) end, false) local categoryLabel = GuiLabel(10, 80, panelWidth - 20, 15, "Category:", false, self.window) categoryLabel:setFont("default-bold-small") categoryLabel:setHorizontalAlign("center") self.category = GuiComboBox(10, 100, panelWidth - 20, 200, "", false, self.window) for index, categoryName in ipairs(comboCategories.client) do self.category:addItem(categoryName) end -- We assume we will always have a single item in the combobox self.category:setSelected(0) self.category:autoHeight() self.categoryItem = self.category:getItemText(self.category:getSelected()) addEventHandler("onClientGUIComboBoxAccepted", self.category, function (...) self:onCategoryUpdate(...) end, false) local optionsLabel = GuiLabel(10, 130, panelWidth - 20, 15, "Options:", false, self.window) optionsLabel:setFont("default-bold-small") optionsLabel:setHorizontalAlign("center") self.options = GuiEdit(10, 150, panelWidth - 20, 22, "", false, self.window) self.updateOptions = function () self:applyOptionsUpdate() end addEventHandler("onClientGUIChanged", self.options, function (...) self:onOptionsUpdate(...) end, false) local filterLabel = GuiLabel(10, 180, panelWidth - 20, 15, "Filter:", false, self.window) filterLabel:setFont("default-bold-small") filterLabel:setHorizontalAlign("center") self.filter = GuiEdit(10, 200, panelWidth - 20, 22, "", false, self.window) self.updateFilter = function () self:applyFilterUpdate() end addEventHandler("onClientGUIChanged", self.filter, function (...) self:onFilterUpdate(...) end, false) local closeButton = GuiButton(10, height - 30, panelWidth - 20, 20, "Close", false, self.window) addEventHandler("onClientGUIClick", closeButton, function (...) self:onCloseClick(...) end, false) self.statistics = GuiGridList(panelWidth, 30, contentWidth, height - 40, false, self.window) self.statistics:setSelectionMode(0) self.statistics:setSortingEnabled(false) self:updateStatistics(STATS_MODE_NEW_LISTENER) end function GUI:destroy() if not self.window then return end self.window:destroy() for key, value in pairs(self) do if type(value) ~= "function" then if isElement(value) or isTimer(value) then value:destroy() end self[key] = nil end end end function GUI:setVisible(visible) if not self.window then return end visible = visible and true self.window:setVisible(visible) showCursor(visible) if self.targetItem == "Server" then if self.optionsTimer and isTimer(self.optionsTimer) then self.optionsTimer:destroy() end if self.filterTimer and isTimer(self.filterTimer) then self.filterTimer:destroy() end self.optionsTimer = nil self.filterTimer = nil triggerServerEvent("ipb.toggle", localPlayer, visible, self.categoryItem) else if visible then if not self.updateTimer then self.updateTimer = Timer(function () self:updateStatistics(STATS_MODE_REFRESH) end, UPDATE_FREQUENCY, 0) end else if self.updateTimer and isTimer(self.updateTimer) then self.updateTimer:destroy() end self.updateTimer = nil end end if visible then if self.autoDestroyTimer and isTimer(self.autoDestroyTimer) then self.autoDestroyTimer:destroy() end self.autoDestroyTimer = nil else if not self.autoDestroyTimer then self.autoDestroyTimer = Timer(function () self:destroy() end, 60000, 1) end end end function GUI:onTargetUpdate() local itemID = self.target:getSelected() if not itemID or itemID == -1 then self.target:setSelected(0) self.targetItem = self.target:getItemText(self.target:getSelected()) return end local text = self.target:getItemText(itemID) local supported = comboCategories[text:lower()] if self.targetItem == text or not supported then return end self.options:setText("") self.filter:setText("") if self.optionsTimer and isTimer(self.optionsTimer) then self.optionsTimer:destroy() end if self.filterTimer and isTimer(self.filterTimer) then self.filterTimer:destroy() end self.targetItem = text self.category:clear() for index, categoryName in ipairs(supported) do self.category:addItem(categoryName) end self.category:setSelected(0) self.category:autoHeight() self.categoryItem = self.category:getItemText(self.category:getSelected()) self.statistics:wipe() triggerServerEvent("ipb.toggle", localPlayer, text == "Server", false) if text == "Client" then if not self.updateTimer then self.updateTimer = Timer(function () self:updateStatistics(STATS_MODE_REFRESH) end, UPDATE_FREQUENCY, 0) end self:updateStatistics(STATS_MODE_NEW_LISTENER) else if self.updateTimer and isTimer(self.updateTimer) then self.updateTimer:destroy() end self.updateTimer = nil end end function GUI:onCategoryUpdate() local itemID = self.category:getSelected() if not itemID or itemID == -1 then self.category:setSelected(0) self.categoryItem = self.category:getItemText(self.category:getSelected()) return end local text = self.category:getItemText(itemID) if self.categoryItem == text then return end self.categoryItem = text self.options:setText("") self.filter:setText("") if self.targetItem == "Server" then triggerServerEvent("ipb.updateCategory", localPlayer, text) else return self:updateStatistics(STATS_MODE_CATEGORY_CHANGE) end end function GUI:onOptionsUpdate() if self.targetItem ~= "Server" then return self:updateStatistics(STATS_MODE_OPTIONS_CHANGE) end if self.optionsTimer and isTimer(self.optionsTimer) then self.optionsTimer:destroy() end self.optionsTimer = Timer(self.updateOptions, 1000, 1) end function GUI:applyOptionsUpdate() self.optionsTimer = nil triggerServerEvent("ipb.updateOptions", localPlayer, self.options:getText()) end function GUI:onFilterUpdate() if self.targetItem ~= "Server" then return self:updateStatistics(STATS_MODE_FILTER_CHANGE) end if self.filterTimer and isTimer(self.filterTimer) then self.filterTimer:destroy() end self.filterTimer = Timer(self.updateFilter, 1000, 1) end function GUI:applyFilterUpdate() self.filterTimer = nil triggerServerEvent("ipb.updateFilter", localPlayer, self.filter:getText()) end function GUI:onCloseClick() GUI:setVisible(false) end function GUI:updateStatistics(mode) if self.targetItem ~= "Client" then return end self:fill(mode, getPerformanceStats(self.categoryItem, self.options:getText(), self.filter:getText())) end function GUI:fill(mode, columns, rows) if not self.window then return end if mode == STATS_MODE_NEW_LISTENER or mode == STATS_MODE_CATEGORY_CHANGE or mode == STATS_MODE_OPTIONS_CHANGE or mode == STATS_MODE_FILTER_CHANGE then self.statistics:wipe() for index, columnName in pairs(columns) do self.statistics:addColumn(columnName, 0.2) end end if mode == STATS_MODE_REFRESH or mode == STATS_MODE_OPTIONS_CHANGE or mode == STATS_MODE_FILTER_CHANGE then local availableRows = self.statistics:getRowCount() local requiredRows = #rows if availableRows > requiredRows then for index = availableRows, requiredRows, -1 do self.statistics:removeRow(index) end elseif availableRows < requiredRows then for index = availableRows, requiredRows - 1, 1 do self.statistics:addRow() end end end for i, row in pairs(rows) do for j, value in pairs(row) do self.statistics:setItemText(i - 1, j, tostring(value), false, false) end end self.statistics:resizeColumns() end function GuiComboBox:autoHeight() -- Add a pseudo item to figure out the amount of items local itemID = self:addItem("") self:removeItem(itemID) local width = self:getSize(false) return self:setSize(width, itemID * 14 + 40, false) end function GuiGridList:wipe() self:clear() self:clearColumns() end function GuiGridList:clearColumns() for column = self:getColumnCount(), 0, -1 do self:removeColumn(column) end end function GuiGridList:resizeColumns() -- Create a pseudo label to figure out the width of each item local label = GuiLabel(0, 0, 0, 0, "", false, nil) local rowCount = self:getRowCount() local columCount = self:getColumnCount() for column = 1, columCount do label:setText(self:getColumnTitle(column) or "") label:setFont("default-small") local width = label:getTextExtent() + 20 label:setFont("default-normal") for row = 0, rowCount - 1 do label:setText(self:getItemText(row, column) or "") local itemWidth = label:getTextExtent() + 20 width = math.max(width, itemWidth) end self:setColumnWidth(column, width, false) end label:destroy() end