Merge pull request #4 from gmod-integration/api-v3

Api v3
This commit is contained in:
Linventif 2024-03-13 14:39:47 -07:00 committed by GitHub
commit 2b09ea91c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 1882 additions and 1321 deletions

View File

@ -1,66 +1,67 @@
if game.SinglePlayer() then return end
if (game.SinglePlayer()) then return print("Gmod Integration is not supported in Singleplayer!") end
//
// Variables
//
gmInte = gmInte or {}
gmInte.version = "0.2.3"
gmInte.config = {
["redownloadMaterials"] = false,
}
gmInte = gmInte || {}
gmInte.version = "0.3.0"
gmInte.config = {}
gmInte.materials = {}
//
// Functions
//
local function loadConfig()
if (SERVER) then
print(" | Loading File | gmod_integration/sv_config.lua")
RunConsoleCommand("sv_hibernate_think", "1")
if (!file.Exists("gm_integration", "DATA") || !file.Exists("gm_integration/config.json", "DATA")) then
file.CreateDir("gm_integration")
local function loadServerConfig()
RunConsoleCommand("sv_hibernate_think", "1")
if (!file.Exists("gm_integration", "DATA") || !file.Exists("gm_integration/config.json", "DATA")) then
file.CreateDir("gm_integration")
file.Write("gm_integration/config.json", util.TableToJSON(gmInte.config, true))
else
if (gmInte.config.id && gmInte.config.id != "") then return end
local oldConfig = util.JSONToTable(file.Read("gm_integration/config.json", "DATA"))
if (!oldConfig.version || (oldConfig.version < gmInte.version)) then
print(" | Merging Config | gmod_integration/sv_config.lua")
table.Merge(gmInte.config, oldConfig)
gmInte.config.version = gmInte.version
file.Write("gm_integration/config.json", util.TableToJSON(gmInte.config, true))
else
if (gmInte.config.id && gmInte.config.id != "") then return end
local oldConfig = util.JSONToTable(file.Read("gm_integration/config.json", "DATA"))
if (!oldConfig.version || (oldConfig.version < gmInte.version)) then
print(" | Merging Config | gmod_integration/sv_config.lua")
table.Merge(gmInte.config, oldConfig)
gmInte.config.version = gmInte.version
file.Write("gm_integration/config.json", util.TableToJSON(gmInte.config, true))
else
gmInte.config = oldConfig
end
gmInte.config = oldConfig
end
end
end
local function loadAllFiles(folder)
local files, folders = file.Find(folder .. "/*", "LUA")
for k, v in SortedPairs(files) do
local path = folder .. "/" .. v
for k, fileName in SortedPairs(files) do
local path = folder .. "/" .. fileName
print(" | Loading File | " .. path)
if string.StartWith(v, "cl_") then
if (string.StartWith(fileName, "cl_")) then
if SERVER then
AddCSLuaFile(path)
else
include(path)
end
elseif string.StartWith(v, "sv_") then
elseif (string.StartWith(fileName, "sv_")) then
if SERVER then
include(path)
end
elseif string.StartWith(v, "sh_") then
elseif (string.StartWith(fileName, "sh_")) then
if SERVER then
AddCSLuaFile(path)
end
include(path)
end
if (path == "gmod_integration/sv_config.lua") then loadConfig() continue end
if (fileName == "sv_config.lua") then loadServerConfig() continue end
end
for k, v in SortedPairs(folders, true) do
loadAllFiles(folder .. "/" .. v, name)
end

View File

@ -0,0 +1,23 @@
local colorTbl = {
["background"] = Color(41, 44, 54),
["primary"] = Color(58, 62, 73),
["primary-active"] = Color(58, 62, 73, 163),
["secondary"] = Color(44, 47, 59),
["secondary-active"] = Color(31, 33, 40),
["green"] = Color(78, 151, 53),
["green-active"] = Color(58, 122, 38),
["orange"] = Color(204, 145, 62),
["orange-active"] = Color(168, 122, 43),
["red"] = Color(201, 59, 59),
["red-active"] = Color(168, 43, 43),
["blue"] = Color(67, 197, 214),
["blue-active"] = Color(41, 152, 167),
["purple"] = Color(73, 90, 252),
["purple-active"] = Color(47, 63, 159),
["font"] = Color(255, 255, 255),
["font-secondary"] = Color(179, 179, 179)
}
function gmInte.getColor(name)
return colorTbl[name]
end

View File

@ -0,0 +1,12 @@
list.Set("DesktopWindows", "GmodIntegration:DesktopWindows", {
icon = "gmod_integration/logo_context.png",
title = "GM Integration",
width = 960,
height = 700,
onewindow = true,
init = function(icon, window)
window:Close()
gmInte.openAdminConfig()
end
}
)

View File

@ -1,7 +1,7 @@
surface.CreateFont("GmodIntegration_Roboto_16", {
font = "Roboto",
size = 16,
weight = 500,
weight = 100,
antialias = true,
shadow = false
})

View File

@ -1,436 +0,0 @@
local function saveConfig(setting, value)
gmInte.SendNet(3, {
[setting] = value
})
end
local configCat = {
"Authentication",
"Main",
"Trust & Safety",
"Punishment",
"Other"
}
local possibleConfig = {
["id"] = {
["label"] = "ID",
["description"] = "Server ID found on the webpanel.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Authentication"
},
["token"] = {
["label"] = "Token",
["description"] = "Server Token found on the webpanel.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Authentication"
},
["sendLog"] = {
["label"] = "Logs",
["description"] = "Activate or deactivate logs.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Main"
},
["logBotActions"] = {
["label"] = "Log Bot Actions",
["description"] = "Activate or deactivate logs for bot actions.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Main"
},
["filterOnBan"] = {
["label"] = "Block Discord Ban Player",
["description"] = "Block players banned on the discord server.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Trust & Safety"
},
["filterOnTrust"] = {
["label"] = "Block UnTrust Player",
["description"] = "Block players with a trust level lower than the minimal trust level set in the config.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Trust & Safety"
},
["minimalTrust"] = {
["label"] = "Minimal Trust Level",
["description"] = "The minimal trust level to be able to join the server.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Trust & Safety"
},
["syncChat"] = {
["label"] = "Sync Chat",
["description"] = "Sync chat between the server and the discord server.",
["websocket"] = true,
["restart"] = true,
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Main"
},
["syncBan"] = {
["label"] = "Sync Ban",
["description"] = "Sync chat between the server and the discord server.",
["type"] = "checkbox",
["disable"] = true,
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Punishment"
},
["syncTimeout"] = {
["label"] = "Sync Timeout",
["description"] = "Sync chat between the server and the discord server.",
["type"] = "checkbox",
["disable"] = true,
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Punishment"
},
["syncKick"] = {
["label"] = "Sync Kick",
["description"] = "Sync chat between the server and the discord server.",
["type"] = "checkbox",
["disable"] = true,
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Punishment"
},
["forcePlayerLink"] = {
["label"] = "Force Player Verif",
["description"] = "Sync chat between the server and the discord server.",
["type"] = "checkbox",
["disable"] = true,
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Main"
},
["supportLink"] = {
["label"] = "Support Link",
["description"] = "Server ID found on the webpanel.",
["type"] = "textEntry",
["disable"] = true,
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Trust & Safety"
},
["debug"] = {
["label"] = "Debug",
["description"] = "Activate or deactivate debug mode.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Other"
},
['devInstance'] = {
["label"] = "Dev Instance",
["description"] = "Activate or deactivate the dev instance of the API and Websocket.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Other"
}
}
local buttonsInfo = {
{
["label"] = "Open Webpanel",
["func"] = function()
gui.OpenURL("https://gmod-integration.com/config/server")
end,
},
{
["label"] = "Test Connection",
["func"] = function()
gmInte.SendNet(1)
end,
},
{
["label"] = "Buy Premium",
["func"] = function()
gui.OpenURL("https://gmod-integration.com/premium")
end,
},
{
["label"] = "Install Websocket",
["condition"] = function(data)
return !data.websocket
end,
["func"] = function()
gui.OpenURL("https://github.com/FredyH/GWSockets/releases")
end,
},
{
["label"] = "Load Server Config",
["condition"] = function(data)
return data.debug
end,
["func"] = function(data)
gmInte.config = data
end,
}
}
function gmInte.needRestart()
local frame = vgui.Create("DFrame")
frame:SetSize(400, 120)
frame:Center()
frame:SetTitle("Gmod Integration - Restart Required")
frame:SetDraggable(true)
frame:ShowCloseButton(true)
frame:MakePopup()
local messagePanel = vgui.Create("DPanel", frame)
messagePanel:Dock(TOP)
messagePanel:SetSize(300, 40)
messagePanel:DockMargin(10, 0, 10, 10)
messagePanel:SetBackgroundColor(Color(0, 0, 0, 0))
local messageLabel = vgui.Create("DLabel", messagePanel)
messageLabel:Dock(FILL)
messageLabel:SetText("Some changes require a restart to be applied.\nRestart now ?")
messageLabel:SetContentAlignment(5)
messageLabel:SetWrap(true)
local buttonGrid = vgui.Create("DGrid", frame)
buttonGrid:Dock(BOTTOM)
buttonGrid:DockMargin(5, 10, 5, 5)
buttonGrid:SetCols(2)
buttonGrid:SetColWide(frame:GetWide() / 2 - 10)
buttonGrid:SetRowHeight(35)
local button = vgui.Create("DButton")
button:SetText("Restart")
button.DoClick = function()
frame:Close()
gmInte.SendNet(5)
end
button:SetSize(buttonGrid:GetColWide(), buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
local button = vgui.Create("DButton")
button:SetText("Maybe Later")
button.DoClick = function()
frame:Close()
end
button:SetSize(buttonGrid:GetColWide(), buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
end
function gmInte.openConfigMenu(data)
local needRestart = false
local frame = vgui.Create("DFrame")
frame:SetSize(400, (600 / 1080) * ScrH())
frame:Center()
frame:SetTitle("Gmod Integration - Server Config")
frame:SetDraggable(true)
frame:ShowCloseButton(true)
frame:MakePopup()
local scrollPanel = vgui.Create("DScrollPanel", frame)
scrollPanel:Dock(FILL)
local messagePanel = vgui.Create("DPanel", scrollPanel)
messagePanel:Dock(TOP)
messagePanel:SetSize(300, 60)
messagePanel:DockMargin(10, 0, 10, 10)
messagePanel:SetBackgroundColor(Color(0, 0, 0, 0))
local messageLabel = vgui.Create("DLabel", messagePanel)
messageLabel:Dock(FILL)
messageLabel:SetText("This config is superior to the webpanel config.\nIf you change something here you can override the webpanel config.\nSome features require a websocket connection to work properly.")
messageLabel:SetWrap(true)
for k, catName in pairs(configCat) do
local collapsibleCategory = vgui.Create("DCollapsibleCategory", scrollPanel)
collapsibleCategory:Dock(TOP)
collapsibleCategory:DockMargin(10, 0, 10, 10)
collapsibleCategory:SetLabel(catName)
collapsibleCategory:SetExpanded(true)
local configList = vgui.Create("DPanelList", collapsibleCategory)
configList:Dock(FILL)
configList:SetSpacing(5)
configList:EnableHorizontal(false)
configList:EnableVerticalScrollbar(false)
collapsibleCategory:SetContents(configList)
for k, v in pairs(possibleConfig) do
if v.category == catName then
local panel = vgui.Create("DPanel", configList)
panel:Dock(TOP)
panel:SetSize(300, 25)
panel:SetBackgroundColor(Color(0, 0, 0, 0))
local label = vgui.Create("DLabel", panel)
label:Dock(LEFT)
label:SetSize(140, 25)
label:SetText(v.label)
label:SetContentAlignment(4)
local input
if v.type == "textEntry" then
input = vgui.Create("DTextEntry", panel)
input:SetText(v.value(k, data[k]))
local isLastID = 0
input.OnChange = function(self)
isLastID = isLastID + 1
local isLocalLastID = isLastID
timer.Simple(v.onEditDelay || 0.5, function()
if isLocalLastID == isLastID then
v.onEdit(k, self:GetValue())
end
end)
end
elseif (v.type == "checkbox") then
input = vgui.Create("DComboBox", panel)
if (v.disable) then
input:SetEnabled(false)
end
input:AddChoice("Enabled")
input:AddChoice("Disabled")
input:SetText(v.value(k, data[k]) && "Enabled" || "Disabled")
input.OnSelect = function(self, index, value)
if (v.restart) then
needRestart = true
end
v.onEdit(k, value)
end
end
input:Dock(FILL)
input:SetSize(150, 25)
if (v.description) then
if (v.websocket && !data.websocket) then
v.description = v.description .. "\n\nThis feature require a websocket connection to work properly."
end
if (v.disable) then
v.description = v.description .. "\n\nThis feature will be available soon."
end
input:SetTooltip(v.description)
end
configList:AddItem(panel)
end
end
end
local buttonGrid = vgui.Create("DGrid", frame)
buttonGrid:Dock(BOTTOM)
buttonGrid:DockMargin(5, 10, 5, 5)
buttonGrid:SetCols(2)
buttonGrid:SetColWide(frame:GetWide() / 2 - 10)
buttonGrid:SetRowHeight(35)
local buttonsCount = 0
for k, v in pairs(buttonsInfo) do
if (v.condition && !v.condition(data)) then continue end
local button = vgui.Create("DButton")
button:SetText(v.label)
button.DoClick = function()
v.func(data)
end
button:SetSize(buttonGrid:GetColWide(), buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
buttonsCount = buttonsCount + 1
end
if (buttonsCount % 2 == 1) then
local lastButton = buttonGrid:GetItems()[buttonsCount]
lastButton:SetWide(frame:GetWide() - 20)
end
frame.OnClose = function()
if (needRestart) then gmInte.needRestart() end
end
end
list.Set("DesktopWindows", "GmodIntegration:DesktopWindows", {
icon = "gmod_integration/logo_context.png",
title = "GM Integration",
width = 960,
height = 700,
onewindow = true,
init = function(icon, window)
window:Close()
gmInte.openAdminConfig()
end
}
)

View File

@ -0,0 +1,490 @@
local function saveConfig(setting, value)
gmInte.SendNet("saveConfig", {
[setting] = value
})
end
local configCat = {
"Authentication",
"Main",
"Trust & Safety",
-- "Punishment",
"Advanced",
}
local possibleConfig = {
{
["id"] = "id",
["label"] = "Server ID",
["description"] = "Server ID found on the webpanel.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Authentication"
},
{
["id"]= "token",
["label"] = "Server Token",
["description"] = "Server Token found on the webpanel.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Authentication"
},
-- {
-- ["id"]= "sendLog",
-- ["label"] = "Logs",
-- ["description"] = "Activate or deactivate logs.",
-- ["type"] = "checkbox",
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Main"
-- },
-- {
-- ["id"]= "logBotActions",
-- ["label"] = "Log Bot Actions",
-- ["description"] = "Activate or deactivate logs for bot actions.",
-- ["type"] = "checkbox",
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Main"
-- },
{
["id"]= "maintenance",
["label"] = "Maintenance",
["description"] = "Activate or deactivate maintenance mode.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Main"
},
{
["id"]= "filterOnBan",
["label"] = "Block Discord Ban Player",
["description"] = "Block players banned on the discord server.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Trust & Safety"
},
-- {
-- ["id"]= "filterOnTrust",
-- ["label"] = "Block UnTrust Player",
-- ["description"] = "Block players with a trust level lower than the minimal trust level set in the config.",
-- ["type"] = "checkbox",
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Trust & Safety"
-- },
-- {
-- ["id"]= "minimalTrust",
-- ["label"] = "Minimal Trust Level",
-- ["description"] = "The minimal trust level to be able to join the server.",
-- ["type"] = "textEntry",
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value)
-- end,
-- ["onEditDelay"] = 0.5,
-- ["category"] = "Trust & Safety"
-- },
-- {
-- ["id"]= "syncChat",
-- ["label"] = "Sync Chat",
-- ["description"] = "Sync chat between the server and the discord server.",
-- ["websocket"] = true,
-- ["restart"] = true,
-- ["type"] = "checkbox",
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Main"
-- },
-- {
-- ["id"]= "syncBan",
-- ["label"] = "Sync Ban",
-- ["description"] = "Sync chat between the server and the discord server.",
-- ["type"] = "checkbox",
-- ["condition"] = function(data)
-- return false // Disabled for now
-- end,
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Punishment"
-- },
-- {
-- ["id"]= "syncTimeout",
-- ["label"] = "Sync Timeout",
-- ["description"] = "Sync chat between the server and the discord server.",
-- ["type"] = "checkbox",
-- ["condition"] = function(data)
-- return false // Disabled for now
-- end,
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Punishment"
-- },
-- {
-- ["id"]= "syncKick",
-- ["label"] = "Sync Kick",
-- ["description"] = "Sync chat between the server and the discord server.",
-- ["type"] = "checkbox",
-- ["condition"] = function(data)
-- return false // Disabled for now
-- end,
-- ["value"] = function(setting, value)
-- return value
-- end,
-- ["onEdit"] = function(setting, value)
-- saveConfig(setting, value == "Enabled" && true || false)
-- end,
-- ["category"] = "Punishment"
-- },
{
["id"]= "forcePlayerLink",
["label"] = "Force Player Verif",
["description"] = "Sync chat between the server and the discord server.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Main"
},
{
["id"]= "supportLink",
["label"] = "Support Link",
["description"] = "Server ID found on the webpanel.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Trust & Safety"
},
{
["id"]= "debug",
["label"] = "Debug",
["description"] = "Activate or deactivate debug mode.",
["type"] = "checkbox",
["value"] = function(setting, value)
return value
end,
["position"] = 1,
["onEdit"] = function(setting, value)
saveConfig(setting, value == "Enabled" && true || false)
end,
["category"] = "Advanced"
},
{
["id"]= "websocketFQDN",
["label"] = "Websocket FQDN",
["description"] = "Websocket FQDN that will be used for the Websocket connection.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Advanced"
},
{
["id"]= "apiFQDN",
["label"] = "API FQDN",
["description"] = "API FQDN that will be used for the API connection.",
["type"] = "textEntry",
["value"] = function(setting, value)
return value
end,
["onEdit"] = function(setting, value)
saveConfig(setting, value)
end,
["onEditDelay"] = 0.5,
["category"] = "Advanced"
},
}
local buttonsInfo = {
{
["label"] = "Open Webpanel",
["func"] = function()
gui.OpenURL("https://gmod-integration.com/config/server")
end,
},
{
["label"] = "Test Connection",
["func"] = function()
gmInte.SendNet("testConnection")
end,
},
{
["label"] = "Buy Premium",
["func"] = function()
gui.OpenURL("https://gmod-integration.com/premium")
end,
},
{
["label"] = "Install Websocket",
["condition"] = function(data)
return !data.websocket
end,
["func"] = function()
gui.OpenURL("https://github.com/FredyH/GWSockets/releases")
end,
},
{
["label"] = "Load Server Config",
["condition"] = function(data)
return data.debug
end,
["func"] = function(data)
gmInte.config = data
end,
}
}
local colorTable = {
["text"] = Color(255, 255, 255, 255),
["background"] = Color(0, 0, 0, 200),
["button"] = Color(0, 0, 0, 200),
["buttonHover"] = Color(0, 0, 0, 255),
["buttonText"] = Color(255, 255, 255, 255),
["buttonTextHover"] = Color(255, 255, 255, 255),
}
function gmInte.needRestart()
local frame = vgui.Create("DFrame")
frame:SetSize(400, 120)
frame:Center()
frame:SetTitle("Gmod Integration - Restart Required")
frame:SetDraggable(true)
frame:ShowCloseButton(true)
frame:MakePopup()
local messagePanel = vgui.Create("DPanel", frame)
messagePanel:Dock(TOP)
messagePanel:SetSize(300, 40)
messagePanel:DockMargin(10, 0, 10, 10)
messagePanel:SetBackgroundColor(Color(0, 0, 0, 0))
local messageLabel = vgui.Create("DLabel", messagePanel)
messageLabel:Dock(FILL)
messageLabel:SetText("Some changes require a restart to be applied.\nRestart now ?")
messageLabel:SetContentAlignment(5)
messageLabel:SetWrap(true)
local buttonGrid = vgui.Create("DGrid", frame)
buttonGrid:Dock(BOTTOM)
buttonGrid:DockMargin(5, 10, 5, 5)
buttonGrid:SetCols(2)
buttonGrid:SetColWide(frame:GetWide() / 2 - 10)
buttonGrid:SetRowHeight(35)
local button = vgui.Create("DButton")
button:SetText("Restart")
button.DoClick = function()
frame:Close()
gmInte.SendNet("restartMap")
end
button:SetSize(buttonGrid:GetColWide(), buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
local button = vgui.Create("DButton")
button:SetText("Maybe Later")
button.DoClick = function()
frame:Close()
end
button:SetSize(buttonGrid:GetColWide(), buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
end
function gmInte.openConfigMenu(data)
local needRestart = false
local frame = vgui.Create("DFrame")
frame:SetSize(400, (600 / 1080) * ScrH())
frame:Center()
frame:SetTitle("Gmod Integration - Server Config")
frame:SetDraggable(true)
frame:ShowCloseButton(true)
frame:MakePopup()
local scrollPanel = vgui.Create("DScrollPanel", frame)
scrollPanel:Dock(FILL)
local messagePanel = vgui.Create("DPanel", scrollPanel)
messagePanel:Dock(TOP)
messagePanel:SetSize(300, 60)
messagePanel:DockMargin(10, 0, 10, 10)
messagePanel:SetBackgroundColor(Color(0, 0, 0, 0))
local messageLabel = vgui.Create("DLabel", messagePanel)
messageLabel:Dock(FILL)
messageLabel:SetText("This config is superior to the webpanel config.\nIf you change something here you can override the webpanel config.\nSome features require a websocket connection to work properly.")
messageLabel:SetWrap(true)
for k, catName in pairs(configCat) do
local collapsibleCategory = vgui.Create("DCollapsibleCategory", scrollPanel)
collapsibleCategory:Dock(TOP)
collapsibleCategory:DockMargin(10, 0, 10, 10)
collapsibleCategory:SetLabel(catName)
collapsibleCategory:SetExpanded(true)
local configList = vgui.Create("DPanelList", collapsibleCategory)
configList:Dock(FILL)
configList:SetSpacing(5)
configList:EnableHorizontal(false)
configList:EnableVerticalScrollbar(false)
collapsibleCategory:SetContents(configList)
local categoryConfig = {}
for k, v in pairs(possibleConfig) do
if v.category == catName then
table.insert(categoryConfig, v)
end
end
// Sort by position
table.sort(categoryConfig, function(a, b)
return (a.position || 0) < (b.position || 0)
end)
for k, actualConfig in pairs(categoryConfig) do
local panel = vgui.Create("DPanel", configList)
panel:Dock(TOP)
panel:SetSize(300, 25)
panel:SetBackgroundColor(Color(0, 0, 0, 0))
local label = vgui.Create("DLabel", panel)
label:Dock(LEFT)
label:SetSize(140, 25)
label:SetText(actualConfig.label)
label:SetContentAlignment(4)
local input
if actualConfig.type == "textEntry" then
input = vgui.Create("DTextEntry", panel)
input:SetText(actualConfig.value(actualConfig.id, data[actualConfig.id] || ""))
local isLastID = 0
input.OnChange = function(self)
isLastID = isLastID + 1
local isLocalLastID = isLastID
timer.Simple(actualConfig.onEditDelay || 0.5, function()
if isLocalLastID == isLastID then
actualConfig.onEdit(actualConfig.id, self:GetValue())
end
end)
end
elseif (actualConfig.type == "checkbox") then
input = vgui.Create("DComboBox", panel)
if (actualConfig.condition && !actualConfig.condition(data)) then
input:SetEnabled(false)
end
input:AddChoice("Enabled")
input:AddChoice("Disabled")
input:SetText(actualConfig.value(actualConfig.id, data[actualConfig.id]) && "Enabled" || "Disabled")
input.OnSelect = function(self, index, value)
if (actualConfig.restart) then
needRestart = true
end
actualConfig.onEdit(actualConfig.id, value)
end
end
input:Dock(FILL)
input:SetSize(150, 25)
if (actualConfig.description) then
if (actualConfig.websocket && !data.websocket) then
actualConfig.description = actualConfig.description .. "\n\nThis feature require a websocket connection to work properly."
end
if (actualConfig.disable) then
actualConfig.description = actualConfig.description .. "\n\nThis feature will be available soon."
end
input:SetTooltip(actualConfig.description)
end
configList:AddItem(panel)
end
end
local buttonGrid = vgui.Create("DGrid", frame)
buttonGrid:Dock(BOTTOM)
buttonGrid:DockMargin(5, 10, 5, 5)
buttonGrid:SetCols(2)
buttonGrid:SetColWide(frame:GetWide() / 2 - 10)
buttonGrid:SetRowHeight(35)
local buttonsCount = 0
for k, v in pairs(buttonsInfo) do
if (v.condition && !v.condition(data)) then continue end
local button = vgui.Create("DButton")
button:SetText(v.label)
button.DoClick = function()
v.func(data)
end
button:SetSize(buttonGrid:GetColWide(), buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
buttonsCount = buttonsCount + 1
end
if (buttonsCount % 2 == 1) then
local lastButton = buttonGrid:GetItems()[buttonsCount]
lastButton:SetWide(frame:GetWide() - 20)
end
frame.OnClose = function()
if (needRestart) then gmInte.needRestart() end
end
end

View File

@ -0,0 +1,65 @@
function gmInte.openVerifPopup()
local frame = vgui.Create("DFrame")
frame:SetSize(400, 200)
frame:Center()
frame:SetTitle("Gmod Integration - Verification Required")
frame:SetDraggable(false)
frame:ShowCloseButton(false)
frame:MakePopup()
frame.Paint = function(self, w, h)
draw.RoundedBox(8, 0, 0, w, h, gmInte.getColor("background"))
end
local messageLabel = vgui.Create("DLabel", frame)
messageLabel:Dock(FILL)
messageLabel:DockMargin(10, 0, 10, 0)
messageLabel:SetText("Hey,\nIt looks like you haven't linked your Steam account to Discord yet. This is required to play on this server. Please click the button below to link your account.\n\nAfter you've done that, click the refresh button.")
messageLabel:SetContentAlignment(5)
messageLabel:SetFont("GmodIntegration_Roboto_16")
messageLabel:SetWrap(true)
local buttonGrid = vgui.Create("DGrid", frame)
buttonGrid:Dock(BOTTOM)
buttonGrid:DockMargin(10, 0, 10, 10)
buttonGrid:SetCols(2)
buttonGrid:SetColWide(frame:GetWide() / 2 - 10)
buttonGrid:SetRowHeight(35)
local button = vgui.Create("DButton")
button:SetText("Open Verification Page")
button.DoClick = function()
gui.OpenURL("https://verif.gmod-integration.com")
end
button:SetSize(buttonGrid:GetColWide() - 10, buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
button:SetTextColor(Color(255, 255, 255))
button.Paint = function(self, w, h)
local color = gmInte.getColor("primary")
if self:IsHovered() then
color = gmInte.getColor("primary-active")
end
draw.RoundedBox(8, 0, 0, w, h, color)
end
local button = vgui.Create("DButton")
button:SetText("Refresh Verification")
button.DoClick = function()
gmInte.http.get("/users/" .. LocalPlayer():SteamID64(), function(code, body)
gmInte.SendNet("verifyMe")
frame:Close()
end,
function(err)
LocalPlayer():ChatPrint("Failed to refresh verification: " .. err)
end)
end
button:SetSize(buttonGrid:GetColWide() - 10, buttonGrid:GetRowHeight())
buttonGrid:AddItem(button)
button:SetTextColor(Color(255, 255, 255))
button.Paint = function(self, w, h)
local color = gmInte.getColor("primary")
if self:IsHovered() then
color = gmInte.getColor("primary-active")
end
draw.RoundedBox(8, 0, 0, w, h, color)
end
end

View File

@ -4,7 +4,7 @@
// Player Finish Init
hook.Add("InitPostEntity", "gmInte:Ply:Ready", function()
gmInte.SendNet(0)
gmInte.SendNet("ready")
end)
hook.Add("OnPlayerChat", "gmInte:OnPlayerChat:AdminCmd", function(ply, strText, bTeamOnly, bPlayerIsDead)
@ -12,7 +12,7 @@ hook.Add("OnPlayerChat", "gmInte:OnPlayerChat:AdminCmd", function(ply, strText,
strText = string.lower(strText)
if (strText == "/gminte") then
if (strText == "/gmi") then
gmInte.openAdminConfig()
return true
end

View File

@ -2,18 +2,13 @@
// Main
//
local function formatName(name)
// all un down case
name = string.lower(name)
// first leter in upper case
name = string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2)
// every letter after a space in upper case
name = string.gsub(name, "(%a)([%w_']*)", function(a,b) return string.upper(a) .. string.lower(b) end)
return name
end
function gmInte.discordSyncChatPly(data)
chat.AddText(Color(92, 105, 255), "(DISCORD) ", Color(12, 151, 12), formatName(data.name) .. ": ", Color(255, 255, 255), data.content)
function gmInte.chatAddText(data)
local args = {}
for _, v in ipairs(data) do
table.insert(args, v.color)
table.insert(args, v.text)
end
chat.AddText(unpack(args))
end
function gmInte.showTestConnection(data)
@ -30,70 +25,11 @@ function gmInte.openAdminConfig()
return
end
gmInte.SendNet(2)
end
local ScreenshotRequested = false
hook.Add("PostRender", "gmInteScreenshot", function()
if (!ScreenshotRequested) then return end
ScreenshotRequested = false
local captureData = {
format = "png",
x = 0,
y = 0,
w = ScrW(),
h = ScrH()
}
local screenCapture = render.Capture(captureData)
screenCapture = util.Base64Encode(screenCapture)
gmInte.log("Screenshot Taken - " .. string.len(#screenCapture / 1024) .. "KB", true)
gmInte.post("/player/screenshots",
{
["steamID64"] = LocalPlayer():SteamID64(),
["screenshot"] = screenCapture,
["options"] = captureData,
["name"] = LocalPlayer():Nick()
},
function(code, body)
gmInte.log("Screenshot sent to Discord", true)
end,
function(code, body)
gmInte.log("Screenshot failed to send to Discord, error code: " .. code, true)
end
)
end)
function gmInte.takeScreenShot(serverID, authToken)
gmInte.config.id = serverID
gmInte.config.token = authToken
timer.Simple(0.2, function()
ScreenshotRequested = true
end)
gmInte.SendNet("getConfig")
end
//
// Concommands
//
concommand.Add("gmod_integration_admin", gmInte.openAdminConfig)
concommand.Add("gmod_integration_screenshot", function()
gmInte.SendNet(4)
end)
//
// Chat Commands
//
hook.Add("OnPlayerChat", "gmInteChatCommands", function(ply, text, teamChat, isDead)
if (ply != LocalPlayer()) then return end
text = string.lower(text)
text = string.sub(text, 2)
if (text == "screen") then
gmInte.SendNet(4)
end
end)
concommand.Add("gmod_integration_admin", gmInte.openAdminConfig)

View File

@ -1,33 +1,30 @@
//
// Network
// Send Net
//
/*
Upload
0 - Say I'm ready
1 - Test Connection
2 - Get Config
3 - Save Config
4 - Take ScreenShot
5 - Restart Map
Receive
1 - Sync Chat
2 - Get Config
3 - Test Connection
4 - Take ScreenShot
*/
local netSend = {
["ready"] = 0,
["testConnection"] = 1,
["getConfig"] = 2,
["saveConfig"] = 3,
["takeScreenShot"] = 4,
["restartMap"] = 5,
["verifyMe"] = 6,
}
// Send
function gmInte.SendNet(id, args, func)
net.Start("gmIntegration")
net.WriteUInt(id, 8)
net.WriteUInt(netSend[id], 8)
net.WriteString(util.TableToJSON(args || {}))
if (func) then func() end
net.SendToServer()
end
// Receive
local netFunc = {
//
// Receive Net
//
local netReceive = {
[1] = function(data)
gmInte.discordSyncChatPly(data)
end,
@ -37,13 +34,22 @@ local netFunc = {
[3] = function(data)
gmInte.showTestConnection(data)
end,
[4] = function(data)
gmInte.takeScreenShot(data.serverID, data.authToken)
[5] = function(data)
gmInte.config = table.Merge(gmInte.config, data)
end,
[6] = function(data)
gmInte.chatAddText(data)
end,
[7] = function()
gmInte.openVerifPopup()
end,
[8] = function(data)
gmInte.config.token = data.token
end
}
net.Receive("gmIntegration", function()
local id = net.ReadUInt(8)
local args = util.JSONToTable(net.ReadString())
if (netFunc[id]) then netFunc[id](args) end
if (netReceive[id]) then netReceive[id](args) end
end)

View File

@ -0,0 +1,89 @@
//
// Hooks
//
local ScreenshotRequested = false
local FailAttempts = 0
hook.Add("PostRender", "gmInteScreenshot", function()
if (!ScreenshotRequested) then return end
ScreenshotRequested = false
local captureData = {
format = "jpeg",
x = 0,
y = 0,
w = ScrW(),
h = ScrH(),
quality = 95,
}
local screenCapture = render.Capture(captureData)
if (!screenCapture) then
if (FailAttempts < 3) then
timer.Simple(0.5, function()
ScreenshotRequested = true
FailAttempts = FailAttempts + 1
gmInte.log("Failed to take screenshot, retrying... (" .. FailAttempts .. "/3)", true)
end)
return
else
FailAttempts = 0
chat.AddText(Color(255, 130, 92), "[Gmod Integration] ", Color(102, 63, 63), "Failed to take screenshot, your system may not support this feature.")
return
end
end
local base64Capture = util.Base64Encode(screenCapture)
local size = math.Round(string.len(base64Capture) / 1024)
gmInte.log("Screenshot Taken - " .. size .. "KB", true)
gmInte.http.post("/screenshots",
{
["player"] = gmInte.getPlayerFormat(LocalPlayer()),
["screenshot"] = base64Capture,
["captureData"] = captureData,
["size"] = size .. "KB"
},
function(code, body)
gmInte.log("Screenshot sent to Discord", true)
chat.AddText(Color(255, 130, 92), "[Gmod Integration] ", Color(255, 255, 255), "Screenshot sent to Discord.")
end,
function(code, body)
gmInte.log("Screenshot failed to send to Discord, error code: " .. code, true)
end
)
end)
//
// Methods
//
function gmInte.takeScreenShot()
timer.Simple(0.5, function()
ScreenshotRequested = true
end)
end
//
// Console Commands
//
concommand.Add("gmod_integration_screenshot", function()
gmInte.takeScreenShot()
end)
//
// Chat Commands
//
hook.Add("OnPlayerChat", "gmInteChatCommands", function(ply, text, teamChat, isDead)
if (ply != LocalPlayer()) then return end
text = string.lower(text)
text = string.sub(text, 2)
if (text == "screen") then
gmInte.takeScreenShot()
return true
end
end)

View File

@ -0,0 +1,57 @@
//
// Hooks
//
local StreamsRequeted = false
hook.Add("PostRender", "gmInte:PostRender:Stream:Frame", function()
if (!StreamsRequeted) then return end
StreamsRequeted = false
// Capture frame
local captureConfig = {
format = "jpeg",
x = 0,
y = 0,
w = ScrW(),
h = ScrH(),
quality = 30,
}
local screenCapture = render.Capture(captureConfig)
if (!screenCapture) then return end
screenCapture = util.Base64Encode(screenCapture)
local size = math.Round(string.len(screenCapture) / 1024)
gmInte.log("Frame captured, size: " .. size .. "KB", true)
gmInte.http.post("/streams/frames",
{
["player"] = gmInte.getPlayerFormat(LocalPlayer()),
["base64Capture"] = screenCapture,
["captureConfig"] = captureConfig,
["size"] = size .. "KB"
},
function(code, body)
gmInte.log("Frame sent to WebPanel, size: " .. size .. "KB", true)
end,
function(code, body)
gmInte.log("Failed to send frame to WebPanel", true)
end
)
end)
local Steam = false
timer.Create("gmInte:Stream:Frame", 0.5, 0, function()
if (Steam) then
StreamsRequeted = true
end
end)
//
// Console Commands
//
concommand.Add("gmod_integration_stream", function()
Steam = !Steam
gmInte.log("Streaming frames to WebPanel: " .. tostring(Steam))
end)

View File

@ -0,0 +1,13 @@
local function formatName(name)
// all un down case
name = string.lower(name)
// first leter in upper case
name = string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2)
// every letter after a space in upper case
name = string.gsub(name, "(%a)([%w_']*)", function(a,b) return string.upper(a) .. string.lower(b) end)
return name
end
function gmInte.discordSyncChatPly(data)
chat.AddText(Color(92, 105, 255), "(DISCORD) ", Color(12, 151, 12), formatName(data.name) .. ": ", Color(255, 255, 255), data.content)
end

View File

@ -30,16 +30,7 @@ if (!GWSockets) then
end
local function getWebSocketURL()
local url = "wss://ws.gmod-integration.com"
local devURL = "wss://dev-ws.gmod-integration.com"
if (!gmInte.config.debug) then return url end
if (gmInte.config.devInstance) then
gmInte.log("Using dev Instance", true)
return devURL
end
return url
return "wss://" .. gmInte.config.websocketFQDN
end
local socket = GWSockets.createWebSocket(getWebSocketURL())
@ -55,14 +46,12 @@ end
// log on message
function socket:onMessage(txt)
gmInte.log("WebSocket Message: " .. txt, true)
local data = util.JSONToTable(txt)
if (gmInte.config.debug) then
gmInte.log("WebSocket Message: " .. txt, true)
end
if (gmInte[data.method]) then
gmInte[data.method](data)
else
gmInte.logError("WebSocket Message: " .. txt .. " is not a valid method !")
gmInte.logError("WebSocket Message: " .. txt .. " is not a valid method !", true)
end
end

View File

@ -1,7 +1,3 @@
//
// Console Commands
//
local conFuncs = {
["version"] = function()
gmInte.log("Version: " .. gmInte.version)

View File

@ -0,0 +1,86 @@
//
// Methods
//
local function filterMessage(reason)
local Message = {
"\n----------------------------------------\n",
"You cannot join this server",
"",
"Reason: " .. (reason && reason || "none"),
"Help URL: " .. (gmInte.config.supportLink && gmInte.config.supportLink || "none"),
"",
"Have a nice day",
"\n----------------------------------------\n",
"Service provided by Gmod Integration",
}
for k, v in pairs(Message) do
Message[k] = "\n" .. v
end
return table.concat(Message)
end
local function checkTrustFactor(trustLevel)
if (gmInte.config.filterOnTrust && (trustLevel < gmInte.config.minimalTrust)) then
return false
end
return true
end
local function checkBanStatus(banStatus)
if (gmInte.config.filterOnBan && banStatus) then
return false
end
return true
end
local function checkDiscordBanStatus(banStatus)
if (gmInte.config.syncBan && banStatus) then
return false
end
return true
end
local function playerFilter(data)
if (data.bot == 1) then return end
data.steamID64 = util.SteamIDTo64(data.networkid)
gmInte.http.get("/players/" .. data.steamID64,
function(code, body)
if (gmInte.config.maintenance && !body.bypassMaintenance) then
game.KickID(data.networkid, filterMessage("The server is currently under maintenance and you are not whitelisted."))
end
if (!checkBanStatus(body.ban)) then
game.KickID(data.networkid, filterMessage("You are banned from this server."))
end
if (!checkDiscordBanStatus(body.discord_ban)) then
game.KickID(data.networkid, filterMessage("You are banned from our discord server."))
end
-- if (!checkTrustFactor(body.trust)) then
-- game.KickID(data.networkid, filterMessage("Your trust factor is too low."))
-- end
end,
function (code, body)
if (gmInte.config.maintenance) then
game.KickID(data.networkid, filterMessage("The server is currently under maintenance and we cannot verify your account.\nVerification URL: https://verif.gmod-integration.com"))
end
end
)
end
//
// Hooks
//
gameevent.Listen("player_connect")
hook.Add("player_connect", "gmInte:Player:Connect:Filter", function(data)
playerFilter(data)
end)

View File

@ -1,40 +0,0 @@
//
// Server Hooks
//
hook.Add("ShutDown", "gmInte:Server:ShutDown", function()
gmInte.serverShutDown()
end)
hook.Add("Initialize", "gmInte.sendStatus", function()
timer.Simple(1, function()
gmInte.serverStart()
end)
end)
//
// Player Hooks
//
gameevent.Listen("player_connect")
hook.Add("player_connect", "gmInte:Player:Connect", function(data)
gmInte.playerConnect(data)
gmInte.playerFilter(data)
end)
gameevent.Listen("server_addban")
hook.Add("server_addban", "gmInte:Player:Ban", function(data)
gmInte.playerBan(data)
end)
hook.Add("PlayerDisconnected", "gmInte:Player:Disconnect", function(ply)
gmInte.playerDisconnected(ply)
end)
hook.Add("onPlayerChangedName", "gmInte:PlayerChangeName", function(ply, old, new)
gmInte.playerChangeName(ply, old, new)
end)
hook.Add("PlayerSay", "gmInte:PlayerSay", function(ply, text, team)
gmInte.playerSay(ply, text, team)
end)

View File

@ -1,248 +0,0 @@
//
// Functions
//
local function logFormatWeapon(weapon, data)
data = data or {}
data.class = weapon:GetClass()
data.printName = weapon:GetPrintName()
return data
end
local function logFormatEntity(ent, data)
data = data or {}
data.class = ent:GetClass()
data.model = ent:GetModel()
data.pos = ent:GetPos()
data.ang = ent:GetAngles()
return data
end
local function logFormatVector(vec, data)
data = data or {}
data.x = vec.x
data.y = vec.y
data.z = vec.z
return data
end
local function logFormatAngle(ang, data)
data = data or {}
data.p = ang.p
data.y = ang.y
data.r = ang.r
return data
end
local function logFormatTeam(teamID, data)
data = data or {}
data.id = teamID
data.name = team.GetName(teamID)
return data
end
local function logFormatPlayer(ply, data)
data = data or {}
data.steamID64 = ply:SteamID64()
data.steamID = ply:SteamID()
data.nick = ply:Nick()
data.userGroup = ply:GetUserGroup()
data.team = logFormatTeam(ply:Team())
return data
end
local function logDisable()
return !gmInte.config.sendLog
end
local function validLogAndPlayers(players)
if (logDisable()) then return false end
for _, ply in pairs(players) do
// return if not valid, player or bot and bots logs are disabled
if (!IsValid(ply)) then return false end
if (!ply:IsPlayer()) then return false end
if (!ply:IsBot() && !gmInte.config.logBotActions) then return false end
end
return true
end
//
// Posts
//
function gmInte.postLogPlayerSay(ply, text, teamChat)
if (!validLogAndPlayers({ply})) then return end
gmInte.post("/server/log/playerSay",
{
["ply"] = logFormatPlayer(ply),
["text"] = text,
["teamChat"] = teamChat
}
)
end
function gmInte.postLogPlayerDeath(ply, inflictor, attacker)
if (!validLogAndPlayers({ply, attacker})) then return end
gmInte.post("/server/log/playerDeath",
{
["ply"] = logFormatPlayer(ply),
["inflictor"] = logFormatEntity(inflictor),
["attacker"] = logFormatPlayer(attacker)
}
)
end
function gmInte.postLogPlayerInitialSpawn(ply)
if (!validLogAndPlayers({ply})) then return end
gmInte.post("/server/log/playerInitialSpawn",
{
["ply"] = logFormatPlayer(ply)
}
)
end
function gmInte.postLogPlayerHurt(ply, attacker, healthRemaining, damageTaken)
if (!validLogAndPlayers({ply, attacker})) then return end
// Wait a second to see if the player is going to be hurt again
ply.gmodInteLastHurt = ply.gmodInteLastHurt || {}
local locCurTime = CurTime()
ply.gmodInteLastHurt[attacker:SteamID64()] = locCurTime
timer.Simple(1, function()
if (ply.gmodInteLastHurt[attacker:SteamID64()] != locCurTime) then
ply.gmodInteTotalDamage = ply.gmodInteTotalDamage || 0
ply.gmodInteTotalDamage = ply.gmodInteTotalDamage + damageTaken
return
end
gmInte.post("/server/log/playerHurt",
{
["ply"] = logFormatPlayer(ply),
["attacker"] = logFormatPlayer(attacker),
["healthRemaining"] = healthRemaining,
["damageTaken"] = ply.gmodInteTotalDamage
}
)
end)
end
function gmInte.postLogPlayerSpawnedSomething(object, ply, ent, model)
if (!validLogAndPlayers({ply})) then return end
gmInte.post("/server/log/playerSpawnedSomething",
{
["object"] = object,
["ply"] = logFormatPlayer(ply),
["ent"] = logFormatEntity(ent),
["model"] = model || ""
}
)
end
function gmInte.postLogPlayerSpawn(ply)
if (!validLogAndPlayers({ply})) then return end
gmInte.post("/server/log/playerSpawn",
{
["ply"] = logFormatPlayer(ply)
}
)
end
function gmInte.postLogPlayerDisconnect(ply)
if (!validLogAndPlayers({ply})) then return end
gmInte.post("/server/log/playerDisconnect",
{
["ply"] = logFormatPlayer(ply)
}
)
end
function gmInte.postLogPlayerConnect(data)
if (logDisable() || data.bot) then return end
gmInte.post("/server/log/playerConnect",
{
["steamID64"] = util.SteamIDTo64(data.networkid),
["steamID"] = data.networkid,
["name"] = data.name,
["ip"] = data.address
}
)
end
function gmInte.postLogPlayerGivet(ply, class, swep)
if (!validLogAndPlayers({ply})) then return end
gmInte.post("/server/log/playerGive",
{
["ply"] = logFormatPlayer(ply),
["class"] = class,
["swep"] = swep
}
)
end
//
// Hooks
//
gameevent.Listen("player_connect")
// Sandbox - Player
hook.Add("PlayerSay", "gmInte:Log:PlayerSay", function(ply, text, teamChat)
gmInte.postLogPlayerSay(ply, text, teamChat)
end)
hook.Add("PlayerSpawn", "gmInte:Log:PlayerSpawn", function(ply)
gmInte.postLogPlayerSpawn(ply)
end)
hook.Add("PlayerInitialSpawn", "gmInte:Log:PlayerInitialSpawn", function(ply)
gmInte.postLogPlayerInitialSpawn(ply)
end)
hook.Add("PlayerDisconnected", "gmInte:Log:PlayerDisconnected", function(ply)
gmInte.postLogPlayerDisconnect(ply)
end)
hook.Add("PlayerGiveSWEP", "gmInte:Log:PlayerSWEPs", function( ply, class, swep )
gmInte.postLogPlayerGivet(ply, class, swep)
end)
// Sandbox - Server Events
hook.Add("player_connect", "gmInte:Log:PlayerConnect", function(data)
gmInte.postLogPlayerConnect(data)
end)
// Sandbox - Player Combat
hook.Add("PlayerDeath", "gmInte:Log:PlayerDeath", function(ply, inflictor, attacker)
gmInte.postLogPlayerDeath(ply, inflictor, attacker)
end)
hook.Add("PlayerHurt", "gmInte:Log:PlayerHurt", function(ply, attacker, healthRemaining, damageTaken)
gmInte.postLogPlayerHurt(ply, attacker, healthRemaining, damageTaken)
end)
// Sandbox - Spawnables
hook.Add("PlayerSpawnedProp", "gmInte:Log:PlayerSpawnedProp", function(ply, model, ent)
gmInte.postLogPlayerSpawnedSomething("SENT", ply, ent, model)
end)
hook.Add("PlayerSpawnedSENT", "gmInte:Log:PlayerSpawnedSENT", function(ply, ent)
gmInte.postLogPlayerSpawnedSomething("SENT", ply, ent)
end)
hook.Add("PlayerSpawnedNPC", "gmInte:Log:PlayerSpawnedNPC", function(ply, ent)
gmInte.postLogPlayerSpawnedSomething("NPC", ply, ent)
end)
hook.Add("PlayerSpawnedVehicle", "gmInte:Log:PlayerSpawnedVehicle", function(ply, ent)
gmInte.postLogPlayerSpawnedSomething("Vehicle", ply, ent)
end)
hook.Add("PlayerSpawnedEffect", "gmInte:Log:PlayerSpawnedEffect", function(ply, model, ent)
gmInte.postLogPlayerSpawnedSomething("Effect", ply, ent, model)
end)
hook.Add("PlayerSpawnedRagdoll", "gmInte:Log:PlayerSpawnedRagdoll", function(ply, model, ent)
gmInte.postLogPlayerSpawnedSomething("Ragdoll", ply, ent, model)
end)
hook.Add("PlayerSpawnedSWEP", "gmInte:Log:PlayerSpawnedSWEP", function(ply, ent)
gmInte.postLogPlayerSpawnedSomething("SWEP", ply, ent)
end)

View File

@ -1,300 +0,0 @@
//
// Functions
//
function gmInte.removePort(ip)
return string.Explode(":", ip)[1]
end
function gmInte.plyValid(ply)
return ply:IsValid() && ply:IsPlayer() && !ply:IsBot()
end
function gmInte.saveSetting(setting, value)
// save this in data/gmod_integration/setting.json but first check if variable is valid
if gmInte.config[setting] == nil then
gmInte.log("Unknown Setting")
return
end
// Boolean
if (value == "true") then value = true end
if (value == "false") then value = false end
// Number
if (tonumber(value) != nil) then value = tonumber(value) end
gmInte.config[setting] = value
file.Write("gm_integration/config.json", util.TableToJSON(gmInte.config, true))
gmInte.log("Setting Saved")
end
function gmInte.playerConnect(data)
if (data.bot == 1) then return end
data.steam = util.SteamIDTo64(data.networkid)
gmInte.post("/server/user/connect", data)
end
local function getCustomValues(ply)
local customValues = {}
customValues.money = ply:gmInteGetTotalMoney() || 0
return customValues
end
local function getTriggerInfo(text)
for k, v in pairs(gmInte.config.chatTrigger) do
if (string.StartWith(text, k)) then
local defaultConfig = {
["trigger"] = k,
["prefix"] = "",
["show_rank"] = false,
["anonymous"] = false,
["channel"] = "admin_sync_chat"
}
for k2, v2 in pairs(v) do
defaultConfig[k2] = v2
end
return defaultConfig
end
end
return false
end
function gmInte.playerSay(ply, text, team)
if (!gmInte.config.syncChat) then return end
gmInte.post("/server/user/say",
{
["steamID64"] = ply:SteamID64(),
["message"] = text,
["name"] = ply:Nick(),
["usergroup"] = ply:GetUserGroup(),
["message_info"] = triggerInfo
}
)
end
function gmInte.wsPlayerSay(data)
gmInte.SendNet(1, data, nil)
end
function gmInte.generatePlayerToken(steamID64)
return util.SHA256(steamID64 .. '-' .. gmInte.config.token .. '-' .. gmInte.publicTempToken)
end
function gmInte.takeScreenshot(ply)
gmInte.SendNet(4, {
["serverID"] = gmInte.config.id,
["authToken"] = gmInte.generatePlayerToken(ply:SteamID64())
}, ply)
end
function gmInte.wsPlayerScreen(data)
for _, ply in pairs(player.GetAll()) do
if (ply:SteamID64() == data.steamID64) then
gmInte.takeScreenshot(ply)
end
end
end
function gmInte.wsRcon(data)
gmInte.log("Rcon Command from Discord '" .. data.command .. "' by " .. data.steamID)
game.ConsoleCommand(data.command .. "\n")
end
function gmInte.playerBan(data)
data.steam = util.SteamIDTo64(data.networkid)
gmInte.post("/server/user/ban", data)
end
function gmInte.userFinishConnect(ply)
if (!gmInte.plyValid(ply)) then return end
gmInte.post("/server/user/finishConnect",
{
["steam"] = ply:SteamID64(),
["name"] = ply:Nick(),
}
)
end
function gmInte.serverShutDown()
gmInte.post("/server/shutdown")
end
function gmInte.sendStatus(start)
if (gmInte.config.id == "" || gmInte.config.token == "") then
gmInte.logError("ID or Token is empty: (id: " .. (gmInte.config.id == "" && "empty" || gmInte.config.id) .. ", token: " .. (gmInte.config.token == "" && "empty" || "not empty but hide") .. ")")
gmInte.logHint("Use 'gmod-integration setting id YOUR_SERVER_ID' and 'gmod-integration setting token YOUR_SERVER_TOKEN' to set your credentials, you can find them on https://gmod-integration.com/config/servers")
return
end
gmInte.post("/server/status",
{
["start"] = start || false,
["hostname"] = GetHostName(),
["ip"] = game.GetIPAddress(),
["port"] = GetConVar("hostport"):GetInt(),
["map"] = game.GetMap(),
["players"] = #player.GetAll(),
["maxplayers"] = game.MaxPlayers(),
["gamemode"] = engine.ActiveGamemode()
},
function(code, body)
if (body.publicTempToken) then
gmInte.publicTempToken = body.publicTempToken
end
end,
function(code, body, headers)
gmInte.logError("Your Credentials are Invalid: (id: " .. (gmInte.config.id == "" && "empty" || gmInte.config.id) .. ", token: " .. (gmInte.config.token == "" && "empty" || "not empty but hide") .. ")")
gmInte.logHint("Use 'gmod-integration setting id YOUR_SERVER_ID' and 'gmod-integration setting token YOUR_SERVER_TOKEN' to set your credentials, you can find them on https://gmod-integration.com/config/servers")
end
)
end
function gmInte.serverStart()
gmInte.sendStatus(true)
end
// every 5 minutes
timer.Create("gmInte.sendStatus", 300, 0, function()
gmInte.sendStatus()
end)
function gmInte.playerChangeName(ply, old, new)
if (!gmInte.plyValid(ply)) then return end
gmInte.post("/server/user/changeName",
{
["steamID64"] = ply:SteamID64(),
["oldName"] = old,
["newName"] = new,
}
)
end
function gmInte.playerDisconnected(ply)
if (!gmInte.plyValid(ply)) then return end
gmInte.post("/server/user/disconnect",
{
["steam"] = ply:SteamID64(),
["kills"] = ply:Frags() || 0,
["deaths"] = ply:Deaths() || 0,
["customValues"] = getCustomValues(ply),
["rank"] = ply:GetUserGroup() || "user",
["time"] = math.Round(RealTime() - ply.gmIntTimeConnect) || 0,
}
)
end
function gmInte.tryConfig()
gmInte.get("/server",
function(code, body)
print(" ")
gmInte.log("Congratulations your server is now connected to Gmod Integration")
gmInte.log("Server Name: " .. body.name)
gmInte.log("Server ID: " .. body.id)
print(" ")
end)
end
function gmInte.testConnection(ply)
gmInte.get("/server",
function(code, body)
gmInte.SendNet(3, body, ply)
end,
function(code, body, headers)
gmInte.SendNet(3, body, ply)
end)
end
function gmInte.serverShutDown()
for ply, ply in pairs(player.GetAll()) do
gmInte.playerDisconnected(ply)
end
end
function gmInte.refreshSettings()
gmInte.config = util.JSONToTable(file.Read("gm_integration/config.json", "DATA"))
gmInte.log("Settings Refreshed")
gmInte.tryConfig()
end
local function filterMessage(reason)
local Message = {
[1] = "\n",
[2] = "This server has player filtering enabled",
[3] = "You are not allowed to join this server",
[4] = "",
[5] = "Reason: " .. reason,
[6] = "",
[7] = "For more information, please contact the server owner",
[8] = "Help URL: " .. (gmInte.config.supportLink && gmInte.config.supportLink || "No Support Link"),
[9] = "",
[10] = "You can also contact us on our discord server",
[11] = "https://gmod-integration.com/discord",
[12] = "",
[13] = "Have a nice day",
[14] = "",
[15] = "Service provided by Gmod Integration",
}
for k, v in pairs(Message) do
Message[k] = v .. "\n"
end
return table.concat(Message)
end
function gmInte.playerFilter(data)
if (data.bot == 1) then return end
data.steamID64 = util.SteamIDTo64(data.networkid)
// get data
gmInte.get("/server/user" .. "?steamID64=" .. data.steamID64,
function(code, body)
if (!body || !body.trust) then return end
// Gmod Integration Trust
if (gmInte.config.filterOnTrust && (body.trust < gmInte.config.minimalTrust)) then
// kick player
game.KickID(data.networkid, filterMessage("Insufficient Trust Level\nYour Trust Level: " .. body.trust .. "\nMinimal Trust Level: " .. gmInte.config.minimalTrust))
end
// Gmod Integration Ban
if (gmInte.config.filterOnBan && body.ban) then
// kick player
game.KickID(data.networkid, filterMessage("You are banned from Gmod Integration"))
end
// Server Discord Ban
if (gmInte.config.syncBan && body.discord_ban) then
// kick player
game.KickID(data.networkid, filterMessage("You are banned from the discord server\nReason: " .. (body.discord_ban_reason && body.discord_ban_reason || "No Reason")))
end
end
)
end
function gmInte.superadminGetConfig(ply)
if (!gmInte.plyValid(ply) || !ply:IsSuperAdmin()) then return end
gmInte.config.websocket = GWSockets && true || false
gmInte.SendNet(2, gmInte.config, ply)
end
function gmInte.superadminSetConfig(ply, data)
if (!gmInte.plyValid(ply) || !ply:IsSuperAdmin()) then return end
for k, v in pairs(data) do
gmInte.saveSetting(k, v)
end
if data.token || data.id then
gmInte.testConnection(ply)
end
end

View File

@ -1,12 +0,0 @@
// Meta
local ply = FindMetaTable("Player")
function ply:gmInteGetTotalMoney()
// if darkrp
if DarkRP then
return self:getDarkRPVar("money")
end
// else
return 0
end

View File

@ -1,29 +1,30 @@
//
// Network
// Networking
//
/*
Upload
1 - Add Chat Message
2 - Get Config
3 - Test Connection
4 - Take Screenshot
Receive
0 - Player is Ready
1 - Test Connection
2 - Get Config
3 - Set Config
4 - Take Screenshot
5 - Restart Map
*/
util.AddNetworkString("gmIntegration")
//
// Send Net
//
local netSend = {
["wsRelayDiscordChat"] = 1,
["adminConfig"] = 2,
["testApiConnection"] = 3,
["publicConfig"] = 5,
["chatColorMessage"] = 6,
["openVerifPopup"] = 7,
["savePlayerToken"] = 8
}
// Send
function gmInte.SendNet(id, data, ply, func)
if (!netSend[id]) then return end
net.Start("gmIntegration")
net.WriteUInt(id, 8)
net.WriteString(util.TableToJSON(data))
net.WriteUInt(netSend[id], 8)
net.WriteString(util.TableToJSON(data || {}))
if (func) then func() end
if (ply == nil) then
net.Broadcast()
@ -32,11 +33,13 @@ function gmInte.SendNet(id, data, ply, func)
end
end
// Receive
local netFuncs = {
//
// Receive net
//
local netReceive = {
[0] = function(ply)
gmInte.userFinishConnect(ply)
ply.gmIntTimeConnect = math.Round(RealTime())
hook.Run("gmInte:PlayerReady", ply)
end,
[1] = function(ply, data)
gmInte.testConnection(ply, data)
@ -54,11 +57,21 @@ local netFuncs = {
if (!ply:IsSuperAdmin()) then return end
RunConsoleCommand("changelevel", game.GetMap())
end,
[6] = function(ply)
gmInte.verifyPlayer(ply)
end,
[7] = function(ply, data)
sendPlayerToken(ply)
end
}
net.Receive("gmIntegration", function(len, ply)
if !ply:IsPlayer() then return end
if (!ply || ply && !ply:IsValid()) then return end
local id = net.ReadUInt(8)
local data = util.JSONToTable(net.ReadString() || "{}")
if (netFuncs[id]) then netFuncs[id](ply, data) end
if (!netReceive[id]) then return end
netReceive[id](ply, data)
end)

View File

@ -0,0 +1,191 @@
//
// Methods
//
function gmInte.playerReady(ply)
if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
// Initialize Time
ply.gmIntTimeConnect = math.Round(RealTime())
// Send Public Config
gmInte.publicGetConfig(ply)
gmInte.http.post("/players/" .. ply:SteamID64() .. "/ready", {
["player"] = gmInte.getPlayerFormat(ply)
})
end
function gmInte.playerConnect(data)
data.steamID64 = util.SteamIDTo64(data.networkid)
gmInte.http.post("/players/" .. util.SteamIDTo64(data.networkid) .. "/connect", data)
end
function gmInte.playerDisconnected(ply)
if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
gmInte.http.post("/players/" .. ply:SteamID64() .. "/disconnect",
{
["player"] = gmInte.getPlayerFormat(ply),
}
)
end
-- function gmInte.playerSpawn(ply)
-- if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
-- gmInte.http.post("/players/" .. ply:SteamID64() .. "/spawn",
-- {
-- ["player"] = gmInte.getPlayerFormat(ply)
-- }
-- )
-- end
-- function gmInte.postLogPlayerDeath(ply, inflictor, attacker)
-- if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
-- if (!attacker:IsValid() || !attacker:IsPlayer(attacker)) then return end
-- if (!inflictor:IsValid()) then return end
-- gmInte.http.post("/logs/playerDeath",
-- {
-- ["player"] = gmInte.getPlayerFormat(ply),
-- ["inflictor"] = gmInte.getEntityFormat(inflictor),
-- ["attacker"] = gmInte.getPlayerFormat(attacker)
-- }
-- )
-- end
-- function gmInte.postLogPlayerInitialSpawn(ply)
-- if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
-- gmInte.http.post("/logs/playerInitialSpawn",
-- {
-- ["ply"] = gmInte.getPlayerFormat(ply)
-- }
-- )
-- end
-- function gmInte.postLogPlayerHurt(ply, attacker, healthRemaining, damageTaken)
-- if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
-- if (!attacker:IsValid() || !attacker:IsPlayer(attacker)) then return end
-- // Wait a second to see if the player is going to be hurt again
-- ply.gmodInteLastHurt = ply.gmodInteLastHurt || {}
-- local locCurTime = CurTime()
-- ply.gmodInteLastHurt[attacker:SteamID64()] = locCurTime
-- timer.Simple(1, function()
-- if (ply.gmodInteLastHurt[attacker:SteamID64()] != locCurTime) then
-- ply.gmodInteTotalDamage = ply.gmodInteTotalDamage || 0
-- ply.gmodInteTotalDamage = ply.gmodInteTotalDamage + damageTaken
-- return
-- end
-- gmInte.http.post("/logs/playerHurt",
-- {
-- ["victim"] = gmInte.getPlayerFormat(ply),
-- ["attacker"] = gmInte.getPlayerFormat(attacker),
-- ["healthRemaining"] = healthRemaining,
-- ["damageTaken"] = ply.gmodInteTotalDamage
-- }
-- )
-- end)
-- end
-- function gmInte.postLogPlayerSpawnedSomething(object, ply, ent, model)
-- if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
-- if (!ent:IsValid()) then return end
-- gmInte.http.post("/logs/playerSpawnedSomething",
-- {
-- ["object"] = object,
-- ["player"] = gmInte.getPlayerFormat(ply),
-- ["entity"] = gmInte.getEntityFormat(ent),
-- ["model"] = model || ""
-- }
-- )
-- end
-- function gmInte.postLogPlayerGivet(ply, class, swep)
-- if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
-- gmInte.http.post("/logs/playerGive",
-- {
-- ["player"] = gmInte.getPlayerFormat(ply),
-- ["class"] = class,
-- ["swep"] = swep
-- }
-- )
-- end
//
// Hooks
//
hook.Add("gmInte:PlayerReady", "gmInte:Player:Ready", function(ply)
gmInte.playerReady(ply)
end)
hook.Add("ShutDown", "gmInte:Server:Shutdown:SavePlayers", function()
for ply, ply in pairs(player.GetAll()) do
gmInte.playerDisconnected(ply)
end
end)
gameevent.Listen("player_connect")
hook.Add("player_connect", "gmInte:Player:Connect", function(data)
gmInte.playerConnect(data)
end)
hook.Add("PlayerDisconnected", "gmInte:Player:Disconnect", function(ply)
gmInte.playerDisconnected(ply)
end)
-- hook.Add("PlayerSpawn", "gmInte:Player:Spawn", function(ply)
-- gmInte.playerSpawn(ply)
-- end)
-- hook.Add("PlayerInitialSpawn", "gmInte:Player:InitialSpawn", function(ply)
-- gmInte.postLogPlayerInitialSpawn(ply)
-- end)
-- hook.Add("PlayerGiveSWEP", "gmInte:Player:SWEPs", function( ply, class, swep )
-- gmInte.postLogPlayerGivet(ply, class, swep)
-- end)
-- hook.Add("PlayerDeath", "gmInte:Player:Death", function(ply, inflictor, attacker)
-- gmInte.postLogPlayerDeath(ply, inflictor, attacker)
-- end)
-- hook.Add("PlayerHurt", "gmInte:Player:Hurt", function(ply, attacker, healthRemaining, damageTaken)
-- gmInte.postLogPlayerHurt(ply, attacker, healthRemaining, damageTaken)
-- end)
-- hook.Add("PlayerSpawnedProp", "gmInte:Player:SpawnedProp", function(ply, model, ent)
-- gmInte.postLogPlayerSpawnedSomething("SENT", ply, ent, model)
-- end)
-- hook.Add("PlayerSpawnedSENT", "gmInte:Player:SpawnedSENT", function(ply, ent)
-- gmInte.postLogPlayerSpawnedSomething("SENT", ply, ent)
-- end)
-- hook.Add("PlayerSpawnedNPC", "gmInte:Player:SpawnedNPC", function(ply, ent)
-- gmInte.postLogPlayerSpawnedSomething("NPC", ply, ent)
-- end)
-- hook.Add("PlayerSpawnedVehicle", "gmInte:Player:SpawnedVehicle", function(ply, ent)
-- gmInte.postLogPlayerSpawnedSomething("Vehicle", ply, ent)
-- end)
-- hook.Add("PlayerSpawnedEffect", "gmInte:Player:SpawnedEffect", function(ply, model, ent)
-- gmInte.postLogPlayerSpawnedSomething("Effect", ply, ent, model)
-- end)
-- hook.Add("PlayerSpawnedRagdoll", "gmInte:Player:SpawnedRagdoll", function(ply, model, ent)
-- gmInte.postLogPlayerSpawnedSomething("Ragdoll", ply, ent, model)
-- end)
-- hook.Add("PlayerSpawnedSWEP", "gmInte:Player:SpawnedSWEP", function(ply, ent)
-- gmInte.postLogPlayerSpawnedSomething("SWEP", ply, ent)
-- end)

View File

@ -0,0 +1,42 @@
//
// Methods
//
function gmInte.verifyPlayer(ply)
if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
gmInte.http.get("/players/" .. ply:SteamID64(), function(code, data)
if (!gmInte.config.forcePlayerLink) then return end
if (data && data.steamID64) then
if (ply.gmIntVerified) then return end
gmInte.SendNet("chatColorMessage", {
[1] = {
["text"] = "You have been verified",
["color"] = Color(255, 255, 255)
}
}, ply)
ply:Freeze(false)
ply.gmIntVerified = true
else
gmInte.SendNet("chatColorMessage", {
[1] = {
["text"] = "You are not verified",
["color"] = Color(255, 0, 0)
}
}, ply)
ply:Freeze(true)
gmInte.SendNet("openVerifPopup", nil, ply)
end
end)
end
//
// Hooks
//
hook.Add("gmInte:PlayerReady", "gmInte:Verif:PlayerReady", function(ply)
if (!gmInte.config.forcePlayerLink) then return end
gmInte.verifyPlayer(ply)
end)

View File

@ -0,0 +1,8 @@
//
// Websocket
//
function gmInte.wsRcon(data)
gmInte.log("Rcon Command from Discord '" .. data.command .. "' by " .. data.steamID)
game.ConsoleCommand(data.command .. "\n")
end

View File

@ -0,0 +1,84 @@
function gmInte.saveSetting(setting, value)
if gmInte.config[setting] == nil then
gmInte.log("Unknown Setting")
return
end
// Boolean
if (value == "true") then value = true end
if (value == "false") then value = false end
// Number
if (tonumber(value) != nil) then value = tonumber(value) end
gmInte.config[setting] = value
file.Write("gm_integration/config.json", util.TableToJSON(gmInte.config, true))
gmInte.log("Setting Saved")
// send to all players if it's a public setting
for _, ply in pairs(player.GetAll()) do
if (ply:IsValid() && ply:IsPlayer(ply)) then
gmInte.log("Sending new Public Config to " .. ply:Nick())
gmInte.publicGetConfig(ply)
end
end
end
function gmInte.tryConfig()
gmInte.http.get("",
function(code, body)
print(" ")
gmInte.log("Congratulations your server is now connected to Gmod Integration")
gmInte.log("Server Name: " .. body.name)
gmInte.log("Server ID: " .. body.id)
print(" ")
end
)
end
function gmInte.testConnection(ply)
gmInte.http.get("",
function(code, body)
if (ply) then gmInte.SendNet("testApiConnection", body, ply) end
end,
function(code, body)
if (ply) then gmInte.SendNet("testApiConnection", body, ply) end
end
)
end
function gmInte.refreshSettings()
gmInte.config = util.JSONToTable(file.Read("gm_integration/config.json", "DATA"))
gmInte.log("Settings Refreshed")
gmInte.tryConfig()
end
function gmInte.superadminGetConfig(ply)
if (!ply:IsValid() || !ply:IsPlayer(ply) || !ply:IsSuperAdmin()) then return end
gmInte.config.websocket = GWSockets && true || false
gmInte.SendNet("adminConfig", gmInte.config, ply)
end
function gmInte.publicGetConfig(ply)
if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
gmInte.SendNet("publicConfig", {
["id"] = gmInte.config.id,
["debug"] = gmInte.config.debug,
["apiFQDN"] = gmInte.config.apiFQDN,
["websocketFQDN"] = gmInte.config.websocketFQDN,
}, ply)
end
function gmInte.superadminSetConfig(ply, data)
if (!ply:IsValid() || !ply:IsPlayer(ply) || !ply:IsSuperAdmin()) then return end
for k, v in pairs(data) do
gmInte.saveSetting(k, v)
end
if data.token || data.id then
gmInte.testConnection(ply)
end
end

View File

@ -0,0 +1,38 @@
//
// Methods
//
function gmInte.sendStatus()
gmInte.http.post("/status", gmInte.getServerFormat())
end
-- function gmInte.serverStart()
-- gmInte.http.post("/start", gmInte.getServerFormat())
-- end
function gmInte.serverShutDown()
gmInte.http.post("/shutdown")
end
//
// Timers
//
timer.Create("gmInte.sendStatus", 300, 0, function()
gmInte.sendStatus()
end)
//
// Hooks
//
hook.Add("Initialize", "gmInte:Server:Initialize:SendStatus", function()
timer.Simple(1, function()
-- gmInte.serverStart()
gmInte.sendStatus()
end)
end)
hook.Add("ShutDown", "gmInte:Server:ShutDown:SendStatus", function()
gmInte.serverShutDown()
end)

View File

@ -0,0 +1,29 @@
//
// Websocket
//
function gmInte.wsSyncBan(data)
for _, ply in ipairs(player.GetAll()) do
if (ply:SteamID64() == data.steam) then
ply:Kick(data.reason || "You have been banned from the server.")
end
end
end
//
// Methods
//
function gmInte.playerBan(data)
data.steamID64 = util.SteamIDTo64(data.networkid)
gmInte.http.post("/players/" .. util.SteamIDTo64(data.networkid) .. "/ban", data)
end
//
// Hooks
//
gameevent.Listen("server_addban")
hook.Add("server_addban", "gmInte:Player:Ban", function(data)
gmInte.playerBan(data)
end)

View File

@ -0,0 +1,31 @@
//
// Websocket
//
function gmInte.wsPlayerSay(data)
gmInte.SendNet("wsRelayDiscordChat", data, nil)
end
//
// Methods
//
function gmInte.playerSay(ply, text, teamOnly)
if (!gmInte.config.syncChat) then return end
gmInte.http.post("/players/" .. ply:SteamID64() .. "/say",
{
["player"] = gmInte.getPlayerFormat(ply),
["text"] = text,
["teamOnly"] = teamOnly,
}
)
end
//
// Hooks
//
hook.Add("PlayerSay", "gmInte:SyncChat:PlayerSay", function(ply, text, team)
gmInte.playerSay(ply, text, team)
end)

View File

@ -0,0 +1,30 @@
//
// Websocket
//
function gmInte.wsSyncKick(data)
for _, ply in ipairs(player.GetAll()) do
if (ply:SteamID64() == data.steam) then
ply:Kick(data.reason || "You have been banned from the server.")
end
end
end
//
// Methods
//
function gmInte.playerKick(data)
gmInte.http.post("/players/" .. util.SteamIDTo64(data.networkid) .. "/kick", data)
end
//
// Hooks
//
gameevent.Listen("player_disconnect")
hook.Add("player_disconnect", "gmInte:SyncKick:Disconnect", function(data)
if (string.StartWith(data.reason, "Kicked by ") || data.reason == "No reason provided.") then
gmInte.playerKick(data)
end
end)

View File

@ -0,0 +1,33 @@
//
// Websocket
//
function gmInte.wsSyncName(data)
local ply = player.GetBySteamID(data.player.steamID64)
if not IsValid(ply) then return end
ply:SetName(data.newName)
end
//
// Methods
//
function gmInte.playerChangeName(ply, oldName, newName)
if (!ply:IsValid() || !ply:IsPlayer(ply)) then return end
gmInte.http.post("/players/" .. ply:SteamID64() .. "/name",
{
["player"] = gmInte.getPlayerFormat(ply),
["oldName"] = oldName,
["newName"] = newName,
}
)
end
//
// Hooks
//
hook.Add("onPlayerChangedName", "gmInte:PlayerChangeName", function(ply, old, new)
gmInte.playerChangeName(ply, old, new)
end)

View File

@ -0,0 +1,101 @@
//
// Websocket
//
local cachedPlayers = {}
function gmInte.wsPlayerUpdateGroup(data)
if (cachedPlayers[steamID64] == data.group) then return end
data.steamID = util.SteamIDFrom64(data.steamID64)
data.group = data.add && data.group || "user"
cachedPlayers[data.steamID64] = data.group
local ply = player.GetBySteamID(data.steamID)
if (ply && ply:IsValid()) then
ply:SetUserGroup(data.group)
end
// ULX
if (ULib) then
ULib.ucl.addUser(data.steamID, nil, nil, data.group)
end
// ServerGuard
if (serverguard) then
local ply = player.GetBySteamID(data.steamID)
if (ply) then
local rankData = serverguard.ranks:GetRank(data.group)
serverguard.player:SetRank(ply, data.group)
serverguard.player:SetImmunity(ply, rankData.immunity)
serverguard.player:SetTargetableRank(ply, rankData.targetable)
serverguard.player:SetBanLimit(ply, rankData.banlimit)
else
serverguard.player:SetRank(data.steamID, data.group)
end
end
// Evolve
if (evolve) then
evolve:RankPlayer(data.steamID64, data.group)
end
// SAM
if (SAM) then
SAM:PlayerSetRank(data.steamID64, data.group)
end
// sam (wtf another one?)
if (sam) then
sam.player.setRank(data.steamID64, data.group)
end
// xAdmin
if (xAdmin) then
xAdmin.SetRank(data.steamID64, data.group)
end
// maestro
if (maestro) then
maestro.userrank(data.steamID64, data.group)
end
// D3A
if (D3A) then
D3A.Ranks.SetSteamIDRank(data.steamID, data.group)
end
// Mercury
if (Mercury) then
RunConsoleCommand("hg", "setrank", data.steamID, data.group)
end
// FAdmin
if (FAdmin) then
RunConsoleCommand("fadmin", "setaccess", data.steamID, data.group)
end
gmInte.log("[Sync Role] Player " .. data.steamID .. " has been updated to group " .. data.group)
end
function gmInte.playerChangeGroup(steamID64, oldGroup, newGroup)
if (cachedPlayers[steamID64] == newGroup) then return end
cachedPlayers[steamID64] = newGroup
gmInte.http.post("/players/" .. steamID64 .. "/group", {
["player"] = gmInte.getPlayerFormat(ply),
["oldGroup"] = oldGroup || "user",
["newGroup"] = newGroup
})
end
hook.Add('CAMI.PlayerUsergroupChanged', 'gmInte:SyncChat:CAMI:PlayerUsergroupChanged', function(ply, old, new)
if (ply:IsBot() || !ply:IsValid()) then return end
gmInte.playerChangeGroup(ply:SteamID64(), old, new)
end)
hook.Add('CAMI.SteamIDUsergroupChanged', 'gmInte:SyncChat:CAMI:SteamIDUsergroupChanged', function(SteamID64, old, new)
if (string.StartWith(SteamID64, "STEAM_")) then SteamID64 = util.SteamIDTo64(SteamID64) end
gmInte.playerChangeGroup(SteamID64, old, new)
end)

View File

@ -0,0 +1,50 @@
//
// Methods
//
gmInte.serverPublicToken = gmInte.serverPublicToken || nil
function gmInte.getPublicServerToken(callback)
if (gmInte.serverPublicToken) then
if (callback) then callback(gmInte.serverPublicToken) end
return
end
gmInte.http.get("/public-token", function(code, data)
gmInte.serverPublicToken = data.publicTempToken
callback(data.publicTempToken)
end)
end
hook.Add("Initialize", "gmInte:Server:Initialize:GetPublicToken", function()
timer.Simple(1, function()
gmInte.getPublicServerToken(function(publicToken)
gmInte.log("Server Public Token Received: " .. publicToken)
end)
end)
end)
gmInte.serverPlayerTempTokens = gmInte.serverPlayerTempTokens || {}
function gmInte.getPlayerTempToken(ply, callback)
if (gmInte.serverPlayerTempTokens[ply:SteamID64()] && gmInte.serverPlayerTempTokens[ply:SteamID64()].userID == ply:UserID()) then
if (callback) then callback(gmInte.serverPlayerTempTokens[ply:SteamID64()].token) end
return
end
gmInte.getPublicServerToken(function(publicToken)
local token = util.SHA256(ply:SteamID64() .. "-" .. publicToken .. "-" .. gmInte.config.token .. "-" .. ply:UserID()) .. " " .. ply:UserID()
gmInte.serverPlayerTempTokens[ply:SteamID64()] = { token = token, userID = ply:UserID() }
callback(token)
end)
end
function sendPlayerToken(ply)
gmInte.getPlayerTempToken(ply, function(token)
gmInte.SendNet("savePlayerToken", {
token = token,
}, ply)
end)
end
hook.Add("gmInte:PlayerReady", "gmInte:Verif:PlayerReady", function(ply)
sendPlayerToken(ply)
end)

View File

@ -0,0 +1,74 @@
function gmInte.getPlayerFormat(ply)
if (!IsValid(ply) || !ply:IsPlayer()) then return end
return {
["steamID"] = ply:SteamID(),
["steamID64"] = ply:SteamID64(),
["userGroup"] = ply:GetUserGroup(),
["team"] = gmInte.getTeamFormat(ply:Team()),
["name"] = ply:Nick(),
["kills"] = ply:Frags(),
["deaths"] = ply:Deaths(),
["customValues"] = ply:gmIntGetCustomValues(),
["connectTime"] = math.Round(RealTime() - ply:gmIntGetConnectTime()),
["ping"] = ply:Ping(),
["position"] = gmInte.getVectorFormat(ply:GetPos()),
["angle"] = gmInte.getAngleFormat(ply:EyeAngles())
}
end
function gmInte.getServerFormat()
return {
["hostname"] = GetHostName(),
["ip"] = game.GetIPAddress(),
["port"] = GetConVar("hostport"):GetInt(),
["map"] = game.GetMap(),
["players"] = #player.GetAll(),
["maxPlayers"] = game.MaxPlayers(),
["gameMode"] = engine.ActiveGamemode(),
["uptime"] = math.Round(RealTime())
}
end
function gmInte.getWeaponFormat(weapon)
if (!IsValid(weapon) || !weapon:IsWeapon()) then return end
return {
["class"] = weapon:GetClass(),
["printName"] = weapon:GetPrintName()
}
end
function gmInte.getEntityFormat(ent)
if (!IsValid(ent)) then return end
return {
["class"] = ent:GetClass(),
["model"] = ent:GetModel(),
["position"] = gmInte.getVectorFormat(ent:GetPos()),
["angle"] = gmInte.getAngleFormat(ent:GetAngles())
}
end
function gmInte.getVectorFormat(vec)
if (!isvector(vec)) then return end
return {
["x"] = vec.x,
["y"] = vec.y,
["z"] = vec.z
}
end
function gmInte.getAngleFormat(ang)
if (!isangle(ang)) then return end
return {
["p"] = ang.p,
["y"] = ang.y,
["r"] = ang.r
}
end
function gmInte.getTeamFormat(teamID)
if (!isnumber(teamID)) then return end
return {
["id"] = teamID,
["name"] = team.GetName(teamID)
}
end

View File

@ -0,0 +1,39 @@
//
// Methods
//
function gmInte.sendLuaErrorReport(err, realm, stack, name, id, uptime)
if (name != "gmod_integration") then return end
if (SERVER && math.Round(RealTime()) == 0) then
return timer.Simple(1, function()
gmInte.sendLuaErrorReport(err, realm, stack, name, id, math.Round(RealTime()))
end)
end
if (CLIENT && (!IsValid(LocalPlayer()) || !gmInte.config.token)) then
return timer.Simple(1, function()
gmInte.sendLuaErrorReport(err, realm, stack, name, id, math.Round(RealTime()))
end)
end
gmInte.http.post("/errors",
{
["error"] = err,
["realm"] = realm,
["stack"] = stack,
["name"] = name,
["id"] = id,
["uptime"] = uptime || math.Round(RealTime()),
["identifier"] = SERVER && gmInte.config.id || LocalPlayer():SteamID64()
}
)
end
//
// Hooks
//
hook.Add("OnLuaError", "gmInte:OnLuaError:SendReport", function(err, realm, stack, name, id)
gmInte.sendLuaErrorReport(err, realm, stack, name, id)
end)

View File

@ -1,126 +1,144 @@
local apiVersion = "v3"
gmInte.http = gmInte.http || {}
//
// HTTP
//
local failMessage = {
["401"] = "Bad Credentials",
["403"] = "Forbidden",
["404"] = "Not Found",
["500"] = "Internal Server Error",
["503"] = "Service Unavailable",
["504"] = "Gateway Timeout",
}
local function getAPIURL(endpoint)
local url = "https://" .. gmInte.config.apiFQDN .. "/" .. apiVersion
local function errorMessage(body, code)
if (body && body.error) then
if (failMessage[code]) then
return failMessage[code]
else
return body.error
end
elseif (failMessage[code]) then
return failMessage[code]
if (SERVER) then
url = url .. "/servers/" .. gmInte.config.id
else
return code
end
end
if (string.sub(endpoint, 1, 8) == "/users") then
return url .. endpoint
end
local function getAPIURL()
local url = "https://api.gmod-integration.com"
local devURL = "https://dev-api.gmod-integration.com"
if (!gmInte.config.debug) then return url end
if (gmInte.config.devInstance) then
gmInte.log("Using dev Instance", true)
return devURL
url = url .. "/clients/" .. LocalPlayer():SteamID64() .. "/servers/" .. gmInte.config.id
end
return url
return url .. endpoint
end
local function sendHTTP(params)
// Log the HTTP request
gmInte.log("HTTP Request: " .. params.method .. " " .. params.endpoint, true)
gmInte.log("HTTP Body: " .. (params.body || "No body"), true)
local function showableBody(endpoint)
// if start with /streams or /screenshots return false
if (string.sub(endpoint, 1, 8) == "/streams" || string.sub(endpoint, 1, 12) == "/screenshots") then
return false
end
// Send the HTTP request
return true
end
local function genRequestID()
return "gmInte-" .. util.CRC(tostring(SysTime()))
end
function gmInte.http.requestAPI(params)
local body = params.body && util.TableToJSON(params.body || {}) || ""
local bodyLength = string.len(body)
local token = params.token || gmInte.config.token || ""
local url = getAPIURL(params.endpoint)
local method = params.method
local success = params.success || function() end
local failed = params.failed || function() if (!gmInte.config.debug) then gmInte.log("HTTP Failed, if this error persists please contact support") end end
local version = gmInte.config.version
local showableBody = showableBody(params.endpoint)
local requestID = genRequestID()
local headers = {
["Content-Type"] = "application/json",
["Content-Length"] = bodyLength,
["Authorization"] = "Bearer " .. token,
["Version"] = version
}
local type = "application/json"
// Log
gmInte.log("HTTP FQDN: " .. gmInte.config.apiFQDN, true)
gmInte.log("HTTP Request ID: " .. requestID, true)
gmInte.log("HTTP Request: " .. method .. " " .. url, true)
gmInte.log("HTTP Body: " .. (showableBody && body || "HIDDEN"), true)
// Send
HTTP({
url = getAPIURL() .. params.endpoint,
method = params.method,
headers = {
["Content-Type"] = "application/json",
["Content-Length"] = params.body && string.len(params.body) || 0,
["id"] = gmInte.config.id,
["token"] = gmInte.config.token,
["version"] = gmInte.version
},
body = params.body && params.body || "",
type = "application/json",
success = function(code, body, headers)
// Log the HTTP response
["url"] = url,
["method"] = method,
["headers"] = headers,
["body"] = body,
["type"] = type,
["success"] = function(code, body, headers)
// Log
gmInte.log("HTTP Request ID: " .. requestID, true)
gmInte.log("HTTP Response: " .. code, true)
if (gmInte.config.debug) then gmInte.log("HTTP Body: " .. body, true) end
// if body and is json extract it
if (body && string.sub(headers["Content-Type"], 1, 16) == "application/json") then
body = util.JSONToTable(body)
// if not application/json return failed
if (string.sub(headers["Content-Type"], 1, 16) != "application/json") then
gmInte.log("HTTP Failed: Invalid Content-Type", true)
return failed({ ["error"] = "Invalid Content-Type" }, code, headers)
end
// Check if the request was successful
if (string.sub(code, 1, 1) == "2") then
if (params.success) then
params.success(code, body, headers)
else
gmInte.log("HTTP Request Successful", true)
end
else
if (params.failed) then
params.failed(code, body, headers)
else
gmInte.logError(errorMessage(body, code))
end
// Parse body
body = util.JSONToTable(body || "{}")
// if not 2xx return failed
if (code < 200 || code >= 300) then
return failed(code, body, headers)
end
// Return success
return success(code, body)
end,
failed = function(error)
gmInte.logError(error)
["failed"] = function(error)
// Log
gmInte.log("HTTP Request ID: " .. requestID, true)
gmInte.log("HTTP Failed: " .. error, true)
// Return failed
return failed({ ["error"] = error })
end
})
end
function gmInte.get(endpoint, onSuccess, onFailed)
sendHTTP({
endpoint = endpoint,
method = "GET",
success = onSuccess,
failed = onFailed
//
// HTTP Methods
//
function gmInte.http.get(endpoint, onSuccess, onFailed)
gmInte.http.requestAPI({
["endpoint"] = endpoint,
["method"] = "GET",
["success"] = onSuccess,
["failed"] = onFailed
})
end
function gmInte.post(endpoint, data, onSuccess, onFailed)
sendHTTP({
endpoint = endpoint,
method = "POST",
body = util.TableToJSON(data),
success = onSuccess,
failed = onFailed
function gmInte.http.post(endpoint, data, onSuccess, onFailed)
gmInte.http.requestAPI({
["endpoint"] = endpoint,
["method"] = "POST",
["body"] = data,
["success"] = onSuccess,
["failed"] = onFailed
})
end
function gmInte.put(endpoint, data, onSuccess, onFailed)
sendHTTP({
endpoint = endpoint,
method = "PUT",
body = util.TableToJSON(data),
success = onSuccess,
failed = onFailed
function gmInte.http.put(endpoint, data, onSuccess, onFailed)
gmInte.http.requestAPI({
["endpoint"] = endpoint,
["method"] = "PUT",
["body"] = data,
["success"] = onSuccess,
["failed"] = onFailed
})
end
function gmInte.delete(endpoint, onSuccess, onFailed)
sendHTTP({
endpoint = endpoint,
method = "DELETE",
success = onSuccess,
failed = onFailed
function gmInte.http.delete(endpoint, onSuccess, onFailed)
gmInte.http.requestAPI({
["endpoint"] = endpoint,
["method"] = "DELETE",
["success"] = onSuccess,
["failed"] = onFailed
})
end

View File

@ -0,0 +1,72 @@
//
// Meta
//
local ply = FindMetaTable("Player")
function ply:gmIntGetConnectTime()
return self.gmIntTimeConnect || 0
end
function ply:gmIntSetCustomValue(key, value)
self.gmIntCustomValues = self.gmIntCustomValues || {}
self.gmIntCustomValues[key] = value
end
function ply:gmIntGetCustomValue(key)
return self.gmIntCustomValues && self.gmIntCustomValues[key]
end
function ply:gmIntRemoveCustomValue(key)
if (self.gmIntCustomValues) then
self.gmIntCustomValues[key] = nil
end
end
//
// Compatibility
//
local function getCustomCompatability(ply)
local values = {}
// DarkRP
if (DarkRP) then
values.money = ply:getDarkRPVar("money")
values.job = ply:getDarkRPVar("job")
end
// GUI Level System
if (GUILevelSystem) then
values.level = ply:GetLevel()
values.xp = ply:GetXP()
end
return values
end
//
// Methods
//
local function getCustomValues(ply)
local values = {}
// Get compatability values
for key, value in pairs(getCustomCompatability(ply)) do
values[key] = value
end
// Get custom values or overwrite compatability values
if (ply.gmIntCustomValues) then
for key, value in pairs(ply.gmIntCustomValues) do
values[key] = value
end
end
return values
end
function ply:gmIntGetCustomValues()
return getCustomValues(self)
end

View File

@ -1,6 +1,6 @@
/*
Informations:
This file is prioritized over the configuration file in data/gmod-integration/config.json until the id are set.
This file is prioritized over the configuration file in data/gmod-integration/config.json if id and token are set in this file.
We don't recommend to use a static version of our addon, you should use the workshop version instead.
Add Server:
@ -25,50 +25,31 @@
// API Connection
gmInte.config.id = "" // Server ID
gmInte.config.token = "" // Server Token
// Websocket
/*
This is a premium feature, you can buy premium on our website: https://gmod-integration.com/premium
Websocket allow you to made a real-time connection between your server and our servers.
And so use the real-time features of our addon (like the chat syncronization, role syncronization, ...)
*/
gmInte.config.websocket = false // If true, the addon will use the websocket instead of the http requests
gmInte.config.websocketFQDN = "ws.gmod-integration.com" // The FQDN of the websocket server
gmInte.config.apiFQDN = "api.gmod-integration.com" // The FQDN of the API server
// Other
gmInte.config.forcePlayerLink = false // If true, the addon will force the players to link their discord account to their steam account before playing
gmInte.config.supportLink = "" // The link of your support (shown when a player do not have the requiments to join the server)
gmInte.config.logBotActions = false // If true, the addon will log the messages of the bot in the console
gmInte.config.maintenance = false // If true, the addon will only allow the players with the "gmod-integration.maintenance" permission to join the server
//
// Syncronization
//
// General
gmInte.config.syncChat = false // If true, the addon will sync the chat gmod with a selected channel on discord (need to be enabled on the dashboard)
gmInte.config.syncPlayerStat = true // If true, the addon will sync the player stats (kills, deaths, playtime, ...)
// Punishment
gmInte.config.syncBan = true // If true, the addon will sync gmod bans with discord bans (and vice versa)
gmInte.config.syncTimeout = false // If true, the addon will sync gmod timeouts with discord timeouts (and vice versa)
gmInte.config.syncKick = false // If true, the addon will sync gmod kicks with discord kicks (and vice versa)
//
// Player Filter
//
// Trust Factor
gmInte.config.minimalTrust = 30 // The minimal trust factor of an user to be able to join the server (0 to 100)
gmInte.config.filterOnTrust = true // If true, the addon will filter the players according to their trust factor
// Ban
gmInte.config.filterOnBan = true // If true, the addon will filter the players according to their ban status
//
// Features Kill Switch
// Materials
//
// Will disable the features of the addon
gmInte.config.sendLog = false // Disable the logs
gmInte.config.redownloadMaterials = false // If true, the addon will redownload the materials of the addon (useful if you have a problem with the materials)
//
// Debug & Development