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 // Variables
// //
gmInte = gmInte or {} gmInte = gmInte || {}
gmInte.version = "0.2.3"
gmInte.config = { gmInte.version = "0.3.0"
["redownloadMaterials"] = false, gmInte.config = {}
}
gmInte.materials = {} gmInte.materials = {}
// //
// Functions // Functions
// //
local function loadConfig() local function loadServerConfig()
if (SERVER) then RunConsoleCommand("sv_hibernate_think", "1")
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
if (!file.Exists("gm_integration", "DATA") || !file.Exists("gm_integration/config.json", "DATA")) then file.CreateDir("gm_integration")
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)) file.Write("gm_integration/config.json", util.TableToJSON(gmInte.config, true))
else else
if (gmInte.config.id && gmInte.config.id != "") then return end gmInte.config = oldConfig
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
end end
end end
end end
local function loadAllFiles(folder) local function loadAllFiles(folder)
local files, folders = file.Find(folder .. "/*", "LUA") 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) print(" | Loading File | " .. path)
if string.StartWith(v, "cl_") then
if (string.StartWith(fileName, "cl_")) then
if SERVER then if SERVER then
AddCSLuaFile(path) AddCSLuaFile(path)
else else
include(path) include(path)
end end
elseif string.StartWith(v, "sv_") then elseif (string.StartWith(fileName, "sv_")) then
if SERVER then if SERVER then
include(path) include(path)
end end
elseif string.StartWith(v, "sh_") then elseif (string.StartWith(fileName, "sh_")) then
if SERVER then if SERVER then
AddCSLuaFile(path) AddCSLuaFile(path)
end end
include(path) include(path)
end end
if (path == "gmod_integration/sv_config.lua") then loadConfig() continue end
if (fileName == "sv_config.lua") then loadServerConfig() continue end
end end
for k, v in SortedPairs(folders, true) do for k, v in SortedPairs(folders, true) do
loadAllFiles(folder .. "/" .. v, name) loadAllFiles(folder .. "/" .. v, name)
end 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", { surface.CreateFont("GmodIntegration_Roboto_16", {
font = "Roboto", font = "Roboto",
size = 16, size = 16,
weight = 500, weight = 100,
antialias = true, antialias = true,
shadow = false 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 // Player Finish Init
hook.Add("InitPostEntity", "gmInte:Ply:Ready", function() hook.Add("InitPostEntity", "gmInte:Ply:Ready", function()
gmInte.SendNet(0) gmInte.SendNet("ready")
end) end)
hook.Add("OnPlayerChat", "gmInte:OnPlayerChat:AdminCmd", function(ply, strText, bTeamOnly, bPlayerIsDead) 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) strText = string.lower(strText)
if (strText == "/gminte") then if (strText == "/gmi") then
gmInte.openAdminConfig() gmInte.openAdminConfig()
return true return true
end end

View File

@ -2,18 +2,13 @@
// Main // Main
// //
local function formatName(name) function gmInte.chatAddText(data)
// all un down case local args = {}
name = string.lower(name) for _, v in ipairs(data) do
// first leter in upper case table.insert(args, v.color)
name = string.upper(string.sub(name, 1, 1)) .. string.sub(name, 2) table.insert(args, v.text)
// every letter after a space in upper case end
name = string.gsub(name, "(%a)([%w_']*)", function(a,b) return string.upper(a) .. string.lower(b) end) chat.AddText(unpack(args))
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 end
function gmInte.showTestConnection(data) function gmInte.showTestConnection(data)
@ -30,70 +25,11 @@ function gmInte.openAdminConfig()
return return
end end
gmInte.SendNet(2) gmInte.SendNet("getConfig")
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)
end end
// //
// Concommands // Concommands
// //
concommand.Add("gmod_integration_admin", gmInte.openAdminConfig) 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)

View File

@ -1,33 +1,30 @@
// //
// Network // Send Net
// //
/* local netSend = {
Upload ["ready"] = 0,
0 - Say I'm ready ["testConnection"] = 1,
1 - Test Connection ["getConfig"] = 2,
2 - Get Config ["saveConfig"] = 3,
3 - Save Config ["takeScreenShot"] = 4,
4 - Take ScreenShot ["restartMap"] = 5,
5 - Restart Map ["verifyMe"] = 6,
Receive }
1 - Sync Chat
2 - Get Config
3 - Test Connection
4 - Take ScreenShot
*/
// Send
function gmInte.SendNet(id, args, func) function gmInte.SendNet(id, args, func)
net.Start("gmIntegration") net.Start("gmIntegration")
net.WriteUInt(id, 8) net.WriteUInt(netSend[id], 8)
net.WriteString(util.TableToJSON(args || {})) net.WriteString(util.TableToJSON(args || {}))
if (func) then func() end if (func) then func() end
net.SendToServer() net.SendToServer()
end end
// Receive //
local netFunc = { // Receive Net
//
local netReceive = {
[1] = function(data) [1] = function(data)
gmInte.discordSyncChatPly(data) gmInte.discordSyncChatPly(data)
end, end,
@ -37,13 +34,22 @@ local netFunc = {
[3] = function(data) [3] = function(data)
gmInte.showTestConnection(data) gmInte.showTestConnection(data)
end, end,
[4] = function(data) [5] = function(data)
gmInte.takeScreenShot(data.serverID, data.authToken) 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 end
} }
net.Receive("gmIntegration", function() net.Receive("gmIntegration", function()
local id = net.ReadUInt(8) local id = net.ReadUInt(8)
local args = util.JSONToTable(net.ReadString()) local args = util.JSONToTable(net.ReadString())
if (netFunc[id]) then netFunc[id](args) end if (netReceive[id]) then netReceive[id](args) end
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 end
local function getWebSocketURL() local function getWebSocketURL()
local url = "wss://ws.gmod-integration.com" return "wss://" .. gmInte.config.websocketFQDN
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
end end
local socket = GWSockets.createWebSocket(getWebSocketURL()) local socket = GWSockets.createWebSocket(getWebSocketURL())
@ -55,14 +46,12 @@ end
// log on message // log on message
function socket:onMessage(txt) function socket:onMessage(txt)
gmInte.log("WebSocket Message: " .. txt, true) gmInte.log("WebSocket Message: " .. txt, true)
local data = util.JSONToTable(txt) local data = util.JSONToTable(txt)
if (gmInte.config.debug) then
gmInte.log("WebSocket Message: " .. txt, true)
end
if (gmInte[data.method]) then if (gmInte[data.method]) then
gmInte[data.method](data) gmInte[data.method](data)
else else
gmInte.logError("WebSocket Message: " .. txt .. " is not a valid method !") gmInte.logError("WebSocket Message: " .. txt .. " is not a valid method !", true)
end end
end end

View File

@ -1,7 +1,3 @@
//
// Console Commands
//
local conFuncs = { local conFuncs = {
["version"] = function() ["version"] = function()
gmInte.log("Version: " .. gmInte.version) 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") util.AddNetworkString("gmIntegration")
//
// Send Net
//
local netSend = {
["wsRelayDiscordChat"] = 1,
["adminConfig"] = 2,
["testApiConnection"] = 3,
["publicConfig"] = 5,
["chatColorMessage"] = 6,
["openVerifPopup"] = 7,
["savePlayerToken"] = 8
}
// Send // Send
function gmInte.SendNet(id, data, ply, func) function gmInte.SendNet(id, data, ply, func)
if (!netSend[id]) then return end
net.Start("gmIntegration") net.Start("gmIntegration")
net.WriteUInt(id, 8) net.WriteUInt(netSend[id], 8)
net.WriteString(util.TableToJSON(data)) net.WriteString(util.TableToJSON(data || {}))
if (func) then func() end if (func) then func() end
if (ply == nil) then if (ply == nil) then
net.Broadcast() net.Broadcast()
@ -32,11 +33,13 @@ function gmInte.SendNet(id, data, ply, func)
end end
end end
// Receive //
local netFuncs = { // Receive net
//
local netReceive = {
[0] = function(ply) [0] = function(ply)
gmInte.userFinishConnect(ply) hook.Run("gmInte:PlayerReady", ply)
ply.gmIntTimeConnect = math.Round(RealTime())
end, end,
[1] = function(ply, data) [1] = function(ply, data)
gmInte.testConnection(ply, data) gmInte.testConnection(ply, data)
@ -54,11 +57,21 @@ local netFuncs = {
if (!ply:IsSuperAdmin()) then return end if (!ply:IsSuperAdmin()) then return end
RunConsoleCommand("changelevel", game.GetMap()) RunConsoleCommand("changelevel", game.GetMap())
end, end,
[6] = function(ply)
gmInte.verifyPlayer(ply)
end,
[7] = function(ply, data)
sendPlayerToken(ply)
end
} }
net.Receive("gmIntegration", function(len, ply) 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 id = net.ReadUInt(8)
local data = util.JSONToTable(net.ReadString() || "{}") 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) 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 // HTTP
// //
local failMessage = { local function getAPIURL(endpoint)
["401"] = "Bad Credentials", local url = "https://" .. gmInte.config.apiFQDN .. "/" .. apiVersion
["403"] = "Forbidden",
["404"] = "Not Found",
["500"] = "Internal Server Error",
["503"] = "Service Unavailable",
["504"] = "Gateway Timeout",
}
local function errorMessage(body, code) if (SERVER) then
if (body && body.error) then url = url .. "/servers/" .. gmInte.config.id
if (failMessage[code]) then
return failMessage[code]
else
return body.error
end
elseif (failMessage[code]) then
return failMessage[code]
else else
return code if (string.sub(endpoint, 1, 8) == "/users") then
end return url .. endpoint
end end
local function getAPIURL() url = url .. "/clients/" .. LocalPlayer():SteamID64() .. "/servers/" .. gmInte.config.id
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
end end
return url return url .. endpoint
end end
local function sendHTTP(params) local function showableBody(endpoint)
// Log the HTTP request // if start with /streams or /screenshots return false
gmInte.log("HTTP Request: " .. params.method .. " " .. params.endpoint, true) if (string.sub(endpoint, 1, 8) == "/streams" || string.sub(endpoint, 1, 12) == "/screenshots") then
gmInte.log("HTTP Body: " .. (params.body || "No body"), true) 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({ HTTP({
url = getAPIURL() .. params.endpoint, ["url"] = url,
method = params.method, ["method"] = method,
headers = { ["headers"] = headers,
["Content-Type"] = "application/json", ["body"] = body,
["Content-Length"] = params.body && string.len(params.body) || 0, ["type"] = type,
["id"] = gmInte.config.id, ["success"] = function(code, body, headers)
["token"] = gmInte.config.token, // Log
["version"] = gmInte.version gmInte.log("HTTP Request ID: " .. requestID, true)
},
body = params.body && params.body || "",
type = "application/json",
success = function(code, body, headers)
// Log the HTTP response
gmInte.log("HTTP Response: " .. code, true) gmInte.log("HTTP Response: " .. code, true)
if (gmInte.config.debug) then gmInte.log("HTTP Body: " .. body, true) end if (gmInte.config.debug) then gmInte.log("HTTP Body: " .. body, true) end
// if body and is json extract it // if not application/json return failed
if (body && string.sub(headers["Content-Type"], 1, 16) == "application/json") then if (string.sub(headers["Content-Type"], 1, 16) != "application/json") then
body = util.JSONToTable(body) gmInte.log("HTTP Failed: Invalid Content-Type", true)
return failed({ ["error"] = "Invalid Content-Type" }, code, headers)
end end
// Check if the request was successful // Parse body
if (string.sub(code, 1, 1) == "2") then body = util.JSONToTable(body || "{}")
if (params.success) then
params.success(code, body, headers) // if not 2xx return failed
else if (code < 200 || code >= 300) then
gmInte.log("HTTP Request Successful", true) return failed(code, body, headers)
end
else
if (params.failed) then
params.failed(code, body, headers)
else
gmInte.logError(errorMessage(body, code))
end
end end
// Return success
return success(code, body)
end, end,
failed = function(error) ["failed"] = function(error)
gmInte.logError(error) // Log
gmInte.log("HTTP Request ID: " .. requestID, true)
gmInte.log("HTTP Failed: " .. error, true)
// Return failed
return failed({ ["error"] = error })
end end
}) })
end end
function gmInte.get(endpoint, onSuccess, onFailed) //
sendHTTP({ // HTTP Methods
endpoint = endpoint, //
method = "GET",
success = onSuccess, function gmInte.http.get(endpoint, onSuccess, onFailed)
failed = onFailed gmInte.http.requestAPI({
["endpoint"] = endpoint,
["method"] = "GET",
["success"] = onSuccess,
["failed"] = onFailed
}) })
end end
function gmInte.post(endpoint, data, onSuccess, onFailed) function gmInte.http.post(endpoint, data, onSuccess, onFailed)
sendHTTP({ gmInte.http.requestAPI({
endpoint = endpoint, ["endpoint"] = endpoint,
method = "POST", ["method"] = "POST",
body = util.TableToJSON(data), ["body"] = data,
success = onSuccess, ["success"] = onSuccess,
failed = onFailed ["failed"] = onFailed
}) })
end end
function gmInte.put(endpoint, data, onSuccess, onFailed) function gmInte.http.put(endpoint, data, onSuccess, onFailed)
sendHTTP({ gmInte.http.requestAPI({
endpoint = endpoint, ["endpoint"] = endpoint,
method = "PUT", ["method"] = "PUT",
body = util.TableToJSON(data), ["body"] = data,
success = onSuccess, ["success"] = onSuccess,
failed = onFailed ["failed"] = onFailed
}) })
end end
function gmInte.delete(endpoint, onSuccess, onFailed) function gmInte.http.delete(endpoint, onSuccess, onFailed)
sendHTTP({ gmInte.http.requestAPI({
endpoint = endpoint, ["endpoint"] = endpoint,
method = "DELETE", ["method"] = "DELETE",
success = onSuccess, ["success"] = onSuccess,
failed = onFailed ["failed"] = onFailed
}) })
end 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: 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. We don't recommend to use a static version of our addon, you should use the workshop version instead.
Add Server: Add Server:
@ -25,50 +25,31 @@
// API Connection // API Connection
gmInte.config.id = "" // Server ID gmInte.config.id = "" // Server ID
gmInte.config.token = "" // Server Token gmInte.config.token = "" // Server Token
gmInte.config.websocketFQDN = "ws.gmod-integration.com" // The FQDN of the websocket server
// Websocket gmInte.config.apiFQDN = "api.gmod-integration.com" // The FQDN of the API server
/*
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
// Other // 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.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.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 // 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 // Punishment
gmInte.config.syncBan = true // If true, the addon will sync gmod bans with discord bans (and vice versa) 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.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) 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 // Ban
gmInte.config.filterOnBan = true // If true, the addon will filter the players according to their ban status 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.redownloadMaterials = false // If true, the addon will redownload the materials of the addon (useful if you have a problem with the materials)
gmInte.config.sendLog = false // Disable the logs
// //
// Debug & Development // Debug & Development