Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96eaf09b4f | ||
|
|
dadbe83ec2 | ||
|
|
e282392d3d | ||
|
|
d48b018f4d | ||
|
|
7181fcb124 | ||
|
|
c21217a270 | ||
|
|
73479cff9e |
12
README.md
@@ -1,11 +1,11 @@
|
||||
# Willard Networks: Development Server
|
||||
This branch contains files related to the **Willard Networks: Development server**. They were retrieved on 2024/08/05 and are presented as is with minimal edits. This means that potential developers' bugs have been kept in their original form and it is up to you to resolve them. We do not and will not provide direct support (i.e. regular updates or patches). However, you can always create an issue, pointing out the problem.
|
||||
# Willard Networks: France (Echo One)
|
||||
This branch contains files related to the **Willard Networks: Echo One**. They were retrieved on 2024/08/04 and are presented as is with minimal edits. This means that potential developers' bugs have been kept in their original form and it is up to you to resolve them. We do not and will not provide direct support (i.e. regular updates or patches). However, you can always create an issue, pointing out the problem.
|
||||
|
||||
**The files provided contain:**
|
||||
* Half-Life 2 RPG framework by Willard Networks, used on development server as of 2024/08/05 in English.
|
||||
* Settings (data/\*, settings/\*) used on the development server as of 2024/08/05.
|
||||
* Credentials from the development server database (including the master and SAM database).
|
||||
* Helix logs (data/helix/logs) from the development server as of 2024/08/05.
|
||||
* Half-Life 2 RPG framework by Willard Networks, used on the French server as of 2024/08/04.
|
||||
* Settings (data/\*, settings/\*) used on the French server as of 2024/08/04.
|
||||
* Credentials from the French server database (including the master and SAM database).
|
||||
* Helix logs (data/helix/logs) from the French server as of 2024/08/04.
|
||||
|
||||
**The files provided do not contain:**
|
||||
* Exported dump of databases (including the master and SAM database), since these files are overweight and cannot be uploaded to GitHub. Despite this, it is included in builds distributed via [The Twilight Zone](https://wnsrc.plymouth.thetwilightzone.ru/).
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if slib and slib.loadFolder then slib.loadFolder("e_protect/", true) end
|
||||
hook.Add("slib:loadedUtils", "eP:Initialize", function() slib.loadFolder("e_protect/", true) end)
|
||||
@@ -1,940 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.data = eProtect.data or {}
|
||||
|
||||
local function networkData(data, ...)
|
||||
local args = {...}
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(1, 2)
|
||||
net.WriteUInt(#args, 3)
|
||||
|
||||
for k,v in pairs(args) do
|
||||
net.WriteString(v)
|
||||
end
|
||||
|
||||
local statement = slib.getStatement(data)
|
||||
|
||||
if statement == "bool" then
|
||||
net.WriteUInt(1, 2)
|
||||
net.WriteBool(data)
|
||||
elseif statement == "int" then
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteInt(data, 32)
|
||||
elseif statement == "table" or statement == "color" then
|
||||
net.WriteUInt(3, 2)
|
||||
|
||||
data = table.Copy(data)
|
||||
|
||||
local converted_tbl = {}
|
||||
|
||||
for k,v in pairs(data) do
|
||||
local isSID = util.SteamIDFrom64(k) != "STEAM_0:0:0"
|
||||
|
||||
if isSID then
|
||||
converted_tbl["sid64_"..k] = v
|
||||
else
|
||||
converted_tbl[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
data = converted_tbl
|
||||
|
||||
data = util.Compress(util.TableToJSON(data))
|
||||
net.WriteUInt(#data, 32)
|
||||
net.WriteData(data, #data)
|
||||
end
|
||||
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
local convertedTbl
|
||||
|
||||
convertedTbl = function(tbl)
|
||||
local converted_tbl = {}
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
if istable(v) then v = convertedTbl(v) end
|
||||
|
||||
if string.sub(k, 1, 6) == "sid64_" then
|
||||
local sid64 = string.sub(k, 7, #k)
|
||||
|
||||
if util.SteamIDFrom64(sid64) != "STEAM_0:0:0" then
|
||||
k = sid64
|
||||
end
|
||||
end
|
||||
|
||||
converted_tbl[k] = v
|
||||
end
|
||||
|
||||
return converted_tbl
|
||||
end
|
||||
|
||||
local function openScreenshot(ply, id)
|
||||
if !IsValid(ply) then return end
|
||||
http.Fetch("https://stromic.dev/eprotect/img.php?id="..id, function(result)
|
||||
local sc_frame = vgui.Create("SFrame")
|
||||
sc_frame:SetSize(slib.getScaledSize(960, "x"), slib.getScaledSize(540, "y") + slib.getScaledSize(25, "y"))
|
||||
:setTitle(slib.getLang("eprotect", eProtect.config["language"], "sc-preview")..ply:Nick())
|
||||
:MakePopup()
|
||||
:addCloseButton()
|
||||
:Center()
|
||||
:setBlur(true)
|
||||
|
||||
local display = vgui.Create("HTML", sc_frame.frame)
|
||||
display:Dock(FILL)
|
||||
display:SetHTML([[<img src="data:image/jpeg;base64,]] ..result.. [[" style="height:]]..(sc_frame.frame:GetTall())..[[px;width:]]..(sc_frame.frame:GetWide())..[[px;position:fixed;top:0px;left:0px">]])
|
||||
end)
|
||||
end
|
||||
|
||||
local function sid64format(sid64)
|
||||
return slib.findName(sid64).." ("..sid64..")"
|
||||
end
|
||||
|
||||
local function fillCleanData(index, tbl)
|
||||
local files, directories = file.Find(index, "DATA")
|
||||
|
||||
if files then
|
||||
for k,v in pairs(files) do
|
||||
tbl[v] = true
|
||||
end
|
||||
end
|
||||
|
||||
if index == "*" then index = "" end
|
||||
local attribute = !index and "/" or ""
|
||||
|
||||
if directories then
|
||||
for k,v in pairs(directories) do
|
||||
tbl[v] = tbl[v] or {}
|
||||
|
||||
fillCleanData(index..attribute..v.."/*", tbl[v])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local margin = slib.getTheme("margin")
|
||||
local maincolor_7, maincolor_10, hovercolor, linecol = slib.getTheme("maincolor", 7), slib.getTheme("maincolor", 10), slib.getTheme("hovercolor"), Color(0,0,0,160)
|
||||
local arrow_ico = Material("slib/down-arrow.png", "smooth noclamp")
|
||||
|
||||
local createPaginator = function(parent)
|
||||
local font = slib.createFont("Roboto", 16)
|
||||
local paginator_tall = slib.getScaledSize(25, "y")
|
||||
local paginator = vgui.Create("EditablePanel", parent)
|
||||
paginator:Dock(BOTTOM)
|
||||
paginator:DockPadding(margin,margin,margin,margin)
|
||||
paginator:SetTall(paginator_tall)
|
||||
paginator.page = 1
|
||||
paginator.maxpage = 5
|
||||
|
||||
paginator.Paint = function(s,w,h)
|
||||
surface.SetDrawColor(linecol)
|
||||
surface.DrawRect(0,0,w,1)
|
||||
|
||||
draw.SimpleText(slib.getLang("eprotect", eProtect.config["language"], "page_of_page", s.page, s.maxpage), font, w * .5, h * .5, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
surface.SetFont(font)
|
||||
local prev_w = select(1, surface.GetTextSize(slib.getLang("eprotect", eProtect.config["language"], "previous")))
|
||||
local next_w = select(1, surface.GetTextSize(slib.getLang("eprotect", eProtect.config["language"], "next")))
|
||||
|
||||
local left = vgui.Create("SButton", paginator)
|
||||
:Dock(LEFT)
|
||||
:SetWide(paginator_tall + prev_w)
|
||||
|
||||
local ico_size = paginator:GetTall() * .5
|
||||
|
||||
left.Paint = function(s,w,h)
|
||||
surface.SetDrawColor(maincolor_7)
|
||||
surface.DrawRect(0,0,w,h)
|
||||
|
||||
local hover = s:IsHovered()
|
||||
local curCol = slib.lerpColor(s, hover and hovercolor or color_white)
|
||||
|
||||
s.move = s.move or 1
|
||||
s.move = math.Clamp(hover and s.move + .05 or s.move - .05, 0, 2)
|
||||
|
||||
surface.SetDrawColor(curCol)
|
||||
surface.SetMaterial(arrow_ico)
|
||||
surface.DrawTexturedRectRotated(h * .5 - s.move, h * .5,ico_size ,ico_size, -90)
|
||||
|
||||
draw.SimpleText(slib.getLang("eprotect", eProtect.config["language"], "previous"), font, w - margin, h * .5, curCol, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
left.DoClick = function()
|
||||
if paginator.page <= 1 then return end
|
||||
local nextpage = paginator.page - 1
|
||||
|
||||
paginator.onPageChanged(nextpage)
|
||||
end
|
||||
|
||||
local right = vgui.Create("SButton", paginator)
|
||||
:Dock(RIGHT)
|
||||
:SetWide(paginator_tall + next_w)
|
||||
|
||||
right.Paint = function(s,w,h)
|
||||
surface.SetDrawColor(maincolor_7)
|
||||
surface.DrawRect(0,0,w,h)
|
||||
|
||||
local hover = s:IsHovered()
|
||||
local curCol = slib.lerpColor(s, hover and hovercolor or color_white)
|
||||
|
||||
s.move = s.move or 1
|
||||
s.move = math.Clamp(hover and s.move + .05 or s.move - .05, 0, 2)
|
||||
|
||||
surface.SetDrawColor(curCol)
|
||||
surface.SetMaterial(arrow_ico)
|
||||
surface.DrawTexturedRectRotated(w - (h * .5 - s.move), h * .5,ico_size ,ico_size, 90)
|
||||
|
||||
draw.SimpleText(slib.getLang("eprotect", eProtect.config["language"], "next"), font, margin, h * .5, curCol, TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
right.DoClick = function()
|
||||
if paginator.page >= paginator.maxpage then return end
|
||||
local nextpage = paginator.page + 1
|
||||
|
||||
paginator.onPageChanged(nextpage)
|
||||
end
|
||||
|
||||
return paginator
|
||||
end
|
||||
|
||||
local function showID(ply, id)
|
||||
id = util.JSONToTable(util.Base64Decode(id))
|
||||
if !id or !istable(id) then slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "ply-sent-invalid-data")) return end
|
||||
|
||||
local id_list = vgui.Create("SFrame")
|
||||
id_list:SetSize(slib.getScaledSize(500, "x"),slib.getScaledSize(330, "y"))
|
||||
:Center()
|
||||
:MakePopup()
|
||||
:addCloseButton()
|
||||
:setTitle(slib.getLang("eprotect", eProtect.config["language"], "id-info")..ply:Nick(), slib.createFont("Roboto", 17))
|
||||
:setBlur(true)
|
||||
|
||||
local id_details = vgui.Create("SListView", id_list.frame)
|
||||
id_details:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "player"), slib.getLang("eprotect", eProtect.config["language"], "date"))
|
||||
|
||||
for i, z in pairs(id) do
|
||||
local sid64 = util.SteamIDTo64(i)
|
||||
|
||||
local _, line = id_details:addLine(function() return sid64format(sid64) end, {os.date("%H:%M:%S - %d/%m/%Y", z), z})
|
||||
line.DoClick = function()
|
||||
gui.OpenURL("http://steamcommunity.com/profiles/"..sid64)
|
||||
end
|
||||
|
||||
line:SetZPos(z)
|
||||
end
|
||||
end
|
||||
|
||||
local function showCorrelation(ply, data)
|
||||
data = util.JSONToTable(util.Base64Decode(data))
|
||||
if !data or !istable(data) then return end
|
||||
|
||||
local correlation_list = vgui.Create("SFrame")
|
||||
correlation_list:SetSize(slib.getScaledSize(450, "x"),slib.getScaledSize(330, "y"))
|
||||
:Center()
|
||||
:MakePopup()
|
||||
:addCloseButton()
|
||||
:setTitle(slib.getLang("eprotect", eProtect.config["language"], "ip-correlation")..ply:Nick(), slib.createFont("Roboto", 17))
|
||||
:setBlur(true)
|
||||
|
||||
local correlation_details = vgui.Create("SListView", correlation_list.frame)
|
||||
correlation_details:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "player"), slib.getLang("eprotect", eProtect.config["language"], "ip"))
|
||||
|
||||
for k, v in ipairs(data) do
|
||||
local _, line = correlation_details:addLine(function() return sid64format(v.sid64) end, v.ip)
|
||||
line.DoClick = function()
|
||||
gui.OpenURL("http://steamcommunity.com/profiles/"..v.sid64)
|
||||
end
|
||||
|
||||
line:SetZPos(z)
|
||||
end
|
||||
end
|
||||
|
||||
local function showIPs(ply, data)
|
||||
data = util.JSONToTable(util.Base64Decode(data))
|
||||
if !data or !istable(data) then return end
|
||||
|
||||
local ip_list = vgui.Create("SFrame")
|
||||
ip_list:SetSize(slib.getScaledSize(400, "x"),slib.getScaledSize(280, "y"))
|
||||
:Center()
|
||||
:MakePopup()
|
||||
:addCloseButton()
|
||||
:setTitle(slib.getLang("eprotect", eProtect.config["language"], "ip-info")..ply:Nick(), slib.createFont("Roboto", 17))
|
||||
:setBlur(true)
|
||||
|
||||
local ip_details = vgui.Create("SListView", ip_list.frame)
|
||||
ip_details:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "country-code"), slib.getLang("eprotect", eProtect.config["language"], "ip"), slib.getLang("eprotect", eProtect.config["language"], "date"))
|
||||
|
||||
for k, v in pairs(data) do
|
||||
local _, line = ip_details:addLine(v.country, v.ip, {os.date("%H:%M:%S - %d/%m/%Y", v.logged_time), v.logged_time})
|
||||
line.DoClick = function()
|
||||
gui.OpenURL("https://whatismyipaddress.com/ip/"..v.ip)
|
||||
end
|
||||
|
||||
line:SetZPos(v.logged_time)
|
||||
end
|
||||
end
|
||||
|
||||
local requestLogData = function(id, page, search)
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(0, 2)
|
||||
net.WriteUInt(id, 1)
|
||||
net.WriteUInt(page, 15)
|
||||
net.WriteString(search)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
local eprotect_menu
|
||||
|
||||
local function openMenu()
|
||||
eprotect_menu = vgui.Create("SFrame")
|
||||
eprotect_menu:SetSize(slib.getScaledSize(720, "x"),slib.getScaledSize(530, "y"))
|
||||
:setTitle("eProtect")
|
||||
:Center()
|
||||
:addCloseButton()
|
||||
:MakePopup()
|
||||
:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-general"), "eprotect/tabs/general.png")
|
||||
|
||||
if !eProtect.config["disabledModules"]["identifier"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-identifier"), "eprotect/tabs/identifier.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disabledModules"]["detection_log"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-detectionlog"), "eprotect/tabs/detectionlog.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disabledModules"]["net_limiter"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-netlimiter"),"eprotect/tabs/netlimit.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disabledModules"]["net_logger"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-netlogger"), "eprotect/tabs/netlog.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disablehttplogging"] and ((!VC and !XEON and !mLib) or eProtect.config["ignoreDRM"]) then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-httplogger"), "eprotect/tabs/httplog.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disabledModules"]["exploit_patcher"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-exploitpatcher"), "eprotect/tabs/exploitpatcher.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disabledModules"]["exploit_finder"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-exploitfinder"), "eprotect/tabs/exploitfinder.png")
|
||||
end
|
||||
|
||||
if !eProtect.config["disabledModules"]["fake_exploits"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-fakeexploits"), "eprotect/tabs/fakeexploit.png")
|
||||
end
|
||||
|
||||
|
||||
if !eProtect.config["disabledModules"]["data_snooper"] then
|
||||
eprotect_menu:addTab(slib.getLang("eprotect", eProtect.config["language"], "tab-datasnooper"), "eprotect/tabs/datasnooper.png")
|
||||
end
|
||||
|
||||
eprotect_menu:setActiveTab(slib.getLang("eprotect", eProtect.config["language"], "tab-general"))
|
||||
|
||||
local generalscroller = vgui.Create("SScrollPanel", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-general")])
|
||||
generalscroller:Dock(FILL)
|
||||
generalscroller:GetCanvas():DockPadding(0,slib.getTheme("margin"),0,slib.getTheme("margin"))
|
||||
|
||||
local player_list = vgui.Create("SListPanel", generalscroller)
|
||||
player_list:setTitle(slib.getLang("eprotect", eProtect.config["language"], "player-list"))
|
||||
:addSearchbar()
|
||||
:SetZPos(-200)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "disable-networking"),
|
||||
function(s)
|
||||
if !s.selected or !IsValid(s.selected) then return end
|
||||
local sid = s.selected:SteamID()
|
||||
eProtect.data.disabled[sid] = !eProtect.data.disabled[sid]
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(1, 3)
|
||||
net.WriteUInt(s.selected:EntIndex(), 14)
|
||||
net.WriteBool(eProtect.data.disabled[sid])
|
||||
net.SendToServer()
|
||||
end,
|
||||
function(s, bttn)
|
||||
if !s.selected or !IsValid(s.selected) then
|
||||
bttn:setTitle(slib.getLang("eprotect", eProtect.config["language"], "disable-networking"))
|
||||
return end
|
||||
|
||||
if eProtect.data.disabled[s.selected:SteamID()] then
|
||||
bttn:setTitle(slib.getLang("eprotect", eProtect.config["language"], "enable-networking"))
|
||||
else
|
||||
bttn:setTitle(slib.getLang("eprotect", eProtect.config["language"], "disable-networking"))
|
||||
end
|
||||
end)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "capture"), function(s)
|
||||
if !s.selected or !IsValid(s.selected) then return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(2, 3)
|
||||
net.WriteUInt(s.selected:EntIndex(), 14)
|
||||
net.WriteUInt(1, 2)
|
||||
net.SendToServer()
|
||||
end)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "check-ips"), function(s)
|
||||
if !s.selected or !IsValid(s.selected) then return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(3, 3)
|
||||
net.WriteUInt(s.selected:EntIndex(), 14)
|
||||
net.WriteBit(0)
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
for k,v in pairs(player.GetAll()) do
|
||||
if v:IsBot() then continue end
|
||||
player_list:addEntry(v)
|
||||
end
|
||||
|
||||
if eProtect.data.general then
|
||||
for k,v in pairs(eProtect.data.general) do
|
||||
local type = slib.getStatement(eProtect.BaseConfig[k][1])
|
||||
local cur_type = slib.getStatement(v)
|
||||
if type ~= cur_type then v = eProtect.BaseConfig[k][1] end
|
||||
local option = vgui.Create("SStatement", generalscroller)
|
||||
local _, element = option:SetZPos(eProtect.BaseConfig[k][2])
|
||||
:addStatement(slib.getLang("eprotect", eProtect.config["language"], k), v)
|
||||
|
||||
if type == "int" then
|
||||
element:SetMin(eProtect.BaseConfig[k][3].min)
|
||||
element:SetMax(eProtect.BaseConfig[k][3].max)
|
||||
elseif type == "table" then
|
||||
element.onElementOpen = function(s)
|
||||
s.title = slib.getLang("eprotect", eProtect.config["language"], k)
|
||||
s:SetSize(slib.getScaledSize(850, "x"), slib.getScaledSize(350, "y"))
|
||||
s:Center()
|
||||
s:addEntry()
|
||||
s:addSuggestions(isfunction(eProtect.BaseConfig[k][3]) and eProtect.BaseConfig[k][3]() or {})
|
||||
s:addSearch(s.viewbox, s.viewer)
|
||||
s:addSearch(s.suggestionbox, s.suggestions)
|
||||
|
||||
s.OnRemove = function()
|
||||
if s.modified then
|
||||
element.onValueChange(s.viewer.tbl)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
element.onValueChange = function(value)
|
||||
networkData(value, "general", k)
|
||||
end
|
||||
|
||||
slib.createTooltip(slib.getLang("eprotect", eProtect.config["language"], k.."-tooltip"), option)
|
||||
end
|
||||
end
|
||||
|
||||
-- Identifier tab
|
||||
if !eProtect.config["disabledModules"]["identifier"] then
|
||||
local search_id = vgui.Create("SSearchBar", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-identifier")])
|
||||
search_id:DockMargin(0,0,0,0)
|
||||
:addIcon()
|
||||
|
||||
search_id.bg = maincolor_10
|
||||
|
||||
local identifier = vgui.Create("SScrollPanel", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-identifier")])
|
||||
identifier:Dock(FILL)
|
||||
identifier:GetCanvas():DockPadding(0,slib.getTheme("margin"),0,slib.getTheme("margin"))
|
||||
|
||||
search_id.entry.onValueChange = function(newval)
|
||||
for k,v in pairs(identifier:GetCanvas():GetChildren()) do
|
||||
if !string.find(string.lower(v.name), string.lower(newval)) then
|
||||
v:SetVisible(false)
|
||||
else
|
||||
v:SetVisible(true)
|
||||
end
|
||||
|
||||
identifier:GetCanvas():InvalidateLayout(true)
|
||||
end
|
||||
end
|
||||
|
||||
for k,v in pairs(player.GetAll()) do
|
||||
if v:IsBot() then continue end
|
||||
local ply = vgui.Create("SPlayerPanel", identifier)
|
||||
ply:setPlayer(v)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "check-ids"), function()
|
||||
if !v or !IsValid(v) then return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(2, 3)
|
||||
net.WriteUInt(v:EntIndex(), 14)
|
||||
net.WriteUInt(2, 2)
|
||||
net.SendToServer()
|
||||
end)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "correlate-ip"), function()
|
||||
if !v or !IsValid(v) then return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(3, 3)
|
||||
net.WriteUInt(v:EntIndex(), 14)
|
||||
net.WriteBit(1)
|
||||
net.SendToServer()
|
||||
end)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "family-share-check"), function()
|
||||
if !v or !IsValid(v) then return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(4, 3)
|
||||
net.WriteUInt(v:EntIndex(), 14)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Punishment log
|
||||
if !eProtect.config["disabledModules"]["detection_log"] then
|
||||
local search_punishments = vgui.Create("SSearchBar", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-detectionlog")])
|
||||
search_punishments:DockMargin(0,0,0,2)
|
||||
:addIcon()
|
||||
|
||||
search_punishments.bg = maincolor_10
|
||||
|
||||
search_punishments.entry.onValueChange = function(newval)
|
||||
requestLogData(1, 1, newval)
|
||||
end
|
||||
|
||||
local punishment_log = vgui.Create("SListView", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-detectionlog")])
|
||||
punishment_log:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "player"), slib.getLang("eprotect", eProtect.config["language"], "reason"), slib.getLang("eprotect", eProtect.config["language"], "info"), slib.getLang("eprotect", eProtect.config["language"], "type"))
|
||||
|
||||
local typeToLang = {
|
||||
[1] = "kicked",
|
||||
[2] = "banned",
|
||||
[3] = "notified"
|
||||
}
|
||||
|
||||
local detections_paginator = createPaginator(eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-detectionlog")])
|
||||
detections_paginator.onPageChanged = function(page)
|
||||
local val = search_punishments.entry:GetValue()
|
||||
|
||||
requestLogData(1, page, val == search_punishments.entry.placeholder and "" or val)
|
||||
end
|
||||
|
||||
detections_paginator.onPageChanged(1)
|
||||
|
||||
punishment_log.paginator = detections_paginator
|
||||
|
||||
punishment_log.rebuild = function()
|
||||
for k,v in ipairs(punishment_log:GetCanvas():GetChildren()) do
|
||||
if !v.isLine then continue end
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
local tbl_detections = eProtect.data["requestedDetections"] and eProtect.data["requestedDetections"].result or {}
|
||||
|
||||
detections_paginator.page, detections_paginator.maxpage = eProtect.data["requestedDetections"].page, eProtect.data["requestedDetections"].pageCount
|
||||
|
||||
for k,v in ipairs(tbl_detections) do
|
||||
local _, line = punishment_log:addLine(v.name, function() return slib.getLang("eprotect", eProtect.config["language"], v.reason) end, v.info, function() return slib.getLang("eprotect", eProtect.config["language"], typeToLang[tonumber(v.type)]) end)
|
||||
line.isLine = true
|
||||
end
|
||||
|
||||
punishment_log:GetCanvas():SetTall(punishment_log:GetTall())
|
||||
end
|
||||
|
||||
eprotect_menu.punishment_log = punishment_log
|
||||
end
|
||||
|
||||
-- Net limitation
|
||||
if eProtect.data.netLimitation and !eProtect.config["disabledModules"]["net_limiter"] then
|
||||
local search = vgui.Create("SSearchBar", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-netlimiter")])
|
||||
search:DockMargin(0,0,0,0)
|
||||
:addIcon()
|
||||
|
||||
search.bg = maincolor_10
|
||||
|
||||
|
||||
local scroller = vgui.Create("SScrollPanel", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-netlimiter")])
|
||||
scroller:Dock(FILL)
|
||||
scroller:GetCanvas():DockPadding(0,slib.getTheme("margin"),0,slib.getTheme("margin"))
|
||||
|
||||
|
||||
search.entry.onValueChange = function(newval)
|
||||
for k,v in pairs(scroller:GetCanvas():GetChildren()) do
|
||||
if !string.find(string.lower(v.name), string.lower(newval)) then
|
||||
v:SetVisible(false)
|
||||
else
|
||||
v:SetVisible(true)
|
||||
end
|
||||
|
||||
scroller:GetCanvas():InvalidateLayout(true)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for k,v in pairs(eProtect.data.netLimitation) do
|
||||
if eProtect.data.fakeNets and eProtect.data.fakeNets[k] or !util.NetworkStringToID(k) then continue end
|
||||
|
||||
local netstring = vgui.Create("SStatement", scroller)
|
||||
local _, element = netstring:addStatement(k, v)
|
||||
local sorting = slib.sortAlphabeticallyByKeyValues(eProtect.data.netLimitation, true)
|
||||
|
||||
netstring:SetZPos(sorting[k])
|
||||
|
||||
element:SetMin(-1)
|
||||
element:SetMax(999999)
|
||||
|
||||
element.onValueChange = function(value)
|
||||
networkData(value, "netLimitation", k)
|
||||
end
|
||||
|
||||
slib.createTooltip(slib.getLang("eprotect", eProtect.config["language"], "net-limit-desc"), netstring)
|
||||
end
|
||||
end
|
||||
|
||||
-- Net logger tab
|
||||
if !eProtect.config["disabledModules"]["net_logger"] then
|
||||
local net_log_search = vgui.Create("SSearchBar", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-netlogger")])
|
||||
:DockMargin(0,0,0,2)
|
||||
:addIcon()
|
||||
|
||||
net_log_search.bg = maincolor_10
|
||||
|
||||
local net_logging = vgui.Create("SListView", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-netlogger")])
|
||||
net_logging:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "net-string"), slib.getLang("eprotect", eProtect.config["language"], "called"), slib.getLang("eprotect", eProtect.config["language"], "len"))
|
||||
|
||||
net_logging.Columns[1].maxTxtLen = 56
|
||||
|
||||
net_log_search.entry.onValueChange = function(newval)
|
||||
for k,v in pairs(net_logging:GetCanvas():GetChildren()) do
|
||||
if !v.name or v:GetZPos() < 0 then continue end
|
||||
if !string.find(string.lower(v.name), string.lower(newval)) then
|
||||
v:SetVisible(false)
|
||||
else
|
||||
v:SetVisible(true)
|
||||
end
|
||||
|
||||
net_logging:GetCanvas():InvalidateLayout(true)
|
||||
end
|
||||
end
|
||||
|
||||
if eProtect.data.netLogging then
|
||||
for k,v in pairs(eProtect.data.netLogging) do
|
||||
if !v or !istable(v) then continue end
|
||||
local _, button = net_logging:addLine(k, v.called, v.len)
|
||||
button.DoClick = function()
|
||||
if IsValid(button.Menu) then button.Menu:Remove() end
|
||||
|
||||
button.Menu = vgui.Create("SFrame")
|
||||
button.Menu:SetSize(slib.getScaledSize(450, "x"),slib.getScaledSize(320, "y"))
|
||||
:Center()
|
||||
:MakePopup()
|
||||
:addCloseButton()
|
||||
:setTitle(slib.getLang("eprotect", eProtect.config["language"], "net-info")..k, slib.createFont("Roboto", 17))
|
||||
:setBlur(true)
|
||||
|
||||
local player_details = vgui.Create("SListView", button.Menu.frame)
|
||||
player_details:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "player"), slib.getLang("eprotect", eProtect.config["language"], "called"))
|
||||
|
||||
for i, z in pairs(v.playercalls) do
|
||||
local sid64 = util.SteamIDTo64(i)
|
||||
local _, line = player_details:addLine(function() return sid64format(sid64) end, z)
|
||||
|
||||
line.DoClick = function()
|
||||
gui.OpenURL("http://steamcommunity.com/profiles/"..sid64)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Http logger tab
|
||||
if !eProtect.config["disablehttplogging"] and ((!VC and !XEON and !mLib) or eProtect.config["ignoreDRM"]) then
|
||||
local search_http = vgui.Create("SSearchBar", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-httplogger")])
|
||||
search_http:DockMargin(0,0,0,2)
|
||||
:addIcon()
|
||||
|
||||
search_http.bg = maincolor_10
|
||||
|
||||
local http_logging = vgui.Create("SListView", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-httplogger")])
|
||||
http_logging:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "url"), slib.getLang("eprotect", eProtect.config["language"], "called"), slib.getLang("eprotect", eProtect.config["language"], "type"))
|
||||
|
||||
http_logging.Columns[1].maxTxtLen = 64
|
||||
|
||||
search_http.entry.onValueChange = function(newval)
|
||||
requestLogData(0, 1, newval)
|
||||
end
|
||||
|
||||
local http_paginator = createPaginator(eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-httplogger")])
|
||||
http_paginator.onPageChanged = function(page)
|
||||
local val = search_http.entry:GetValue()
|
||||
|
||||
requestLogData(0, page, val == search_http.entry.placeholder and "" or val)
|
||||
end
|
||||
|
||||
http_logging.paginator = http_paginator
|
||||
|
||||
http_logging.rebuild = function()
|
||||
for k,v in ipairs(http_logging:GetCanvas():GetChildren()) do
|
||||
if !v.isLine then continue end
|
||||
v:Remove()
|
||||
end
|
||||
|
||||
local tbl_http = eProtect.data["requestedHTTP"] and eProtect.data["requestedHTTP"].result or {}
|
||||
|
||||
http_paginator.page, http_paginator.maxpage = eProtect.data["requestedHTTP"].page, eProtect.data["requestedHTTP"].pageCount
|
||||
|
||||
for k,v in ipairs(tbl_http) do
|
||||
local _, line = http_logging:addLine(v.link, v.called, v.type)
|
||||
line.isLine = true
|
||||
|
||||
line.DoClick = function()
|
||||
SetClipboardText(v.link)
|
||||
slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "copied_clipboard"))
|
||||
end
|
||||
end
|
||||
|
||||
http_logging:GetCanvas():SetTall(http_logging:GetTall())
|
||||
end
|
||||
|
||||
eprotect_menu.http_logger = http_logging
|
||||
|
||||
requestLogData(0, 1, "")
|
||||
end
|
||||
|
||||
-- Exploit patcher tab
|
||||
if !eProtect.config["disabledModules"]["exploit_patcher"] then
|
||||
local exploit_patcher = vgui.Create("SListView", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-exploitpatcher")])
|
||||
exploit_patcher:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "net-string"), slib.getLang("eprotect", eProtect.config["language"], "secure"))
|
||||
|
||||
if eProtect.data.exploitPatcher then
|
||||
for k,v in pairs(eProtect.data.exploitPatcher) do
|
||||
exploit_patcher:addLine(k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Exploit finder tab
|
||||
if !eProtect.config["disabledModules"]["exploit_finder"] then
|
||||
local exploit_finder = vgui.Create("SListView", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-exploitfinder")])
|
||||
exploit_finder:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "net-string"), slib.getLang("eprotect", eProtect.config["language"], "type"), slib.getLang("eprotect", eProtect.config["language"], "status"))
|
||||
|
||||
if eProtect.data.badNets then
|
||||
for k,v in pairs(eProtect.data.badNets) do
|
||||
local validateNet = tobool(util.NetworkStringToID(k))
|
||||
|
||||
if !validateNet or (validateNet and eProtect.data and eProtect.data.fakeNets[k] and eProtect.data.fakeNets[k].enabled) then continue end
|
||||
|
||||
local fixed = slib.getLang("eprotect", eProtect.config["language"], "unknown")
|
||||
|
||||
if eProtect.data and eProtect.data.exploitPatcher and eProtect.data.exploitPatcher[k] then
|
||||
fixed = slib.getLang("eprotect", eProtect.config["language"], "secured")
|
||||
end
|
||||
|
||||
exploit_finder:addLine(k, v.type, fixed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fake exploits tab
|
||||
if !eProtect.config["disabledModules"]["fake_exploits"] then
|
||||
local fake_nets = vgui.Create("SListView", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-fakeexploits")])
|
||||
fake_nets:Dock(FILL)
|
||||
:addColumns(slib.getLang("eprotect", eProtect.config["language"], "net-string"), slib.getLang("eprotect", eProtect.config["language"], "type"), slib.getLang("eprotect", eProtect.config["language"], "activated"))
|
||||
|
||||
if eProtect.data.fakeNets then
|
||||
for k,v in pairs(eProtect.data.fakeNets) do
|
||||
fake_nets:addLine(k, v.type, v.enabled)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Data snooper tab
|
||||
if !eProtect.config["disabledModules"]["data_snooper"] then
|
||||
local search_ds = vgui.Create("SSearchBar", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-datasnooper")])
|
||||
search_ds:DockMargin(0,0,0,0)
|
||||
:addIcon()
|
||||
|
||||
search_ds.bg = maincolor_10
|
||||
|
||||
local data_snooper = vgui.Create("SScrollPanel", eprotect_menu.tab[slib.getLang("eprotect", eProtect.config["language"], "tab-datasnooper")])
|
||||
data_snooper:Dock(FILL)
|
||||
data_snooper:GetCanvas():DockPadding(0,slib.getTheme("margin"),0,slib.getTheme("margin"))
|
||||
|
||||
search_ds.entry.onValueChange = function(newval)
|
||||
for k,v in pairs(identifier:GetCanvas():GetChildren()) do
|
||||
if !string.find(string.lower(v.name), string.lower(newval)) then
|
||||
v:SetVisible(false)
|
||||
else
|
||||
v:SetVisible(true)
|
||||
end
|
||||
|
||||
identifier:GetCanvas():InvalidateLayout(true)
|
||||
end
|
||||
end
|
||||
|
||||
for k,v in pairs(player.GetAll()) do
|
||||
if v:IsBot() then continue end
|
||||
local ply = vgui.Create("SPlayerPanel", data_snooper)
|
||||
ply:setPlayer(v)
|
||||
:addButton(slib.getLang("eprotect", eProtect.config["language"], "fetch-data"), function()
|
||||
if !v or !IsValid(v) then return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(1)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteUInt(2, 3)
|
||||
net.WriteUInt(v:EntIndex(), 14)
|
||||
net.WriteUInt(3, 2)
|
||||
net.SendToServer()
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("eprotect_menu", function() RunConsoleCommand("say", "!eprotect") end)
|
||||
|
||||
net.Receive("eP:Handeler", function()
|
||||
local action = net.ReadUInt(3)
|
||||
if action == 1 then
|
||||
local chunk = net.ReadUInt(32)
|
||||
local json = util.Decompress(net.ReadData(chunk))
|
||||
if !json then return end
|
||||
local data = util.JSONToTable(json)
|
||||
local specific = net.ReadString()
|
||||
|
||||
if !specific then
|
||||
eProtect.data = convertedTbl(data)
|
||||
else
|
||||
eProtect.data[specific] = convertedTbl(data)
|
||||
end
|
||||
elseif action == 2 then
|
||||
openMenu()
|
||||
elseif action == 3 then
|
||||
local subaction = net.ReadUInt(2)
|
||||
local target = net.ReadUInt(14)
|
||||
target = Entity(target)
|
||||
local open = net.ReadBool()
|
||||
local data
|
||||
|
||||
if open then
|
||||
if subaction == 3 then
|
||||
local chunk = net.ReadUInt(32)
|
||||
data = net.ReadData(chunk)
|
||||
data = util.Decompress(data)
|
||||
else
|
||||
data = net.ReadString()
|
||||
end
|
||||
end
|
||||
|
||||
if data == "Failed" or data == "" then slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "ply-failed-retrieving-data", target:Nick())) return end
|
||||
|
||||
if subaction == 1 then
|
||||
if open then
|
||||
openScreenshot(target, data)
|
||||
else
|
||||
eProtect.performSC = true
|
||||
end
|
||||
elseif subaction == 2 then
|
||||
if open then
|
||||
showID(target, data)
|
||||
else
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(0)
|
||||
net.WriteUInt(1, 2)
|
||||
net.WriteUInt(2, 2)
|
||||
net.WriteString(file.Read("eid.txt", "DATA") or "")
|
||||
net.SendToServer()
|
||||
end
|
||||
elseif subaction == 3 then
|
||||
if open then
|
||||
data = util.JSONToTable(data)
|
||||
|
||||
local display_data = vgui.Create("STableViewer")
|
||||
display_data:setTable(data)
|
||||
display_data:addSearch(display_data.viewbox, display_data.viewer)
|
||||
display_data.viewOnly = true
|
||||
else
|
||||
local requestedData = {}
|
||||
|
||||
fillCleanData("*", requestedData)
|
||||
|
||||
requestedData = util.TableToJSON(requestedData)
|
||||
requestedData = util.Compress(requestedData)
|
||||
|
||||
if string.len(requestedData) >= 65533 then requestedData = util.Compress("Failed") end
|
||||
|
||||
local chunk = #requestedData
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteBit(0)
|
||||
net.WriteUInt(1, 2)
|
||||
net.WriteUInt(3, 2)
|
||||
net.WriteUInt(chunk, 32)
|
||||
net.WriteData(requestedData, chunk)
|
||||
net.SendToServer()
|
||||
end
|
||||
end
|
||||
elseif action == 4 then
|
||||
local target = net.ReadUInt(14)
|
||||
local ids = net.ReadString()
|
||||
local bit = net.ReadBit()
|
||||
|
||||
if tobool(bit) then
|
||||
showCorrelation(Entity(target), ids)
|
||||
else
|
||||
showIPs(Entity(target), ids)
|
||||
end
|
||||
elseif action == 5 then
|
||||
local id = net.ReadUInt(1)
|
||||
local chunk = net.ReadUInt(32)
|
||||
local data = net.ReadData(chunk)
|
||||
|
||||
data = util.Decompress(data)
|
||||
|
||||
if !data then return end
|
||||
|
||||
data = util.JSONToTable(data)
|
||||
|
||||
if id == 0 then
|
||||
eProtect.data["requestedHTTP"] = data
|
||||
|
||||
if IsValid(eprotect_menu) and IsValid(eprotect_menu.http_logger) then
|
||||
eprotect_menu.http_logger.rebuild()
|
||||
end
|
||||
elseif id == 1 then
|
||||
eProtect.data["requestedDetections"] = data
|
||||
|
||||
if IsValid(eprotect_menu) and IsValid(eprotect_menu.punishment_log) then
|
||||
eprotect_menu.punishment_log.rebuild()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -1,31 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local function doshit()
|
||||
local data = {}
|
||||
|
||||
if file.Exists("eid.txt", "DATA") then
|
||||
data = file.Read("eid.txt", "DATA")
|
||||
data = util.Base64Decode(data)
|
||||
data = util.JSONToTable(data)
|
||||
end
|
||||
|
||||
data = data or {}
|
||||
data[LocalPlayer():SteamID()] = os.time()
|
||||
|
||||
file.Write("eid.txt", util.Base64Encode(util.TableToJSON(data)))
|
||||
end
|
||||
|
||||
hook.Add("Think", "eP:doLogging", function()
|
||||
if !IsValid(LocalPlayer()) then return end
|
||||
hook.Remove("Think", "eP:doLogging")
|
||||
doshit()
|
||||
end)
|
||||
@@ -1,40 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.overrides = eProtect.overrides or {}
|
||||
|
||||
if vgui and !eProtect.overrides["vguiCreate"] then
|
||||
local oldFunc = vgui.Create
|
||||
|
||||
vgui.Create = function(...)
|
||||
local pnl = oldFunc(...)
|
||||
|
||||
hook.Run("eP:PostInitPanel", pnl)
|
||||
|
||||
return pnl
|
||||
end
|
||||
|
||||
eProtect.overrides["vguiCreate"] = true
|
||||
end
|
||||
|
||||
if MsgC and !eProtect.overrides["MsgC"] then
|
||||
local oldFunc = MsgC
|
||||
|
||||
MsgC = function(...)
|
||||
local pnl = oldFunc(...)
|
||||
|
||||
hook.Run("eP:MsgCExecuted", {...})
|
||||
|
||||
return pnl
|
||||
end
|
||||
|
||||
eProtect.overrides["MsgC"] = true
|
||||
end
|
||||
@@ -1,148 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- This is the default language! 76561198002319953
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "en", "sc-preview", "Screenshot Preview - ")
|
||||
slib.setLang("eprotect", "en", "net-info", "Net Info - ")
|
||||
slib.setLang("eprotect", "en", "ip-info", "IP Info - ")
|
||||
slib.setLang("eprotect", "en", "id-info", "ID Info - ")
|
||||
slib.setLang("eprotect", "en", "ip-correlation", "IP Correlation - ")
|
||||
slib.setLang("eprotect", "en", "table-viewer", "Table Viewer")
|
||||
|
||||
slib.setLang("eprotect", "en", "tab-general", "General")
|
||||
slib.setLang("eprotect", "en", "tab-identifier", "Identifier")
|
||||
slib.setLang("eprotect", "en", "tab-detectionlog", "Detection Log")
|
||||
slib.setLang("eprotect", "en", "tab-netlimiter", "Net Limiter")
|
||||
slib.setLang("eprotect", "en", "tab-netlogger", "Net Logger")
|
||||
slib.setLang("eprotect", "en", "tab-httplogger", "HTTP Logger")
|
||||
slib.setLang("eprotect", "en", "tab-exploitpatcher", "Exploit Patcher")
|
||||
slib.setLang("eprotect", "en", "tab-exploitfinder", "Exploit Finder")
|
||||
slib.setLang("eprotect", "en", "tab-fakeexploits", "Fake Exploits")
|
||||
slib.setLang("eprotect", "en", "tab-datasnooper", "Data Snooper")
|
||||
|
||||
slib.setLang("eprotect", "en", "player-list", "Player List")
|
||||
|
||||
slib.setLang("eprotect", "en", "ratelimit", "Ratelimit")
|
||||
slib.setLang("eprotect", "en", "ratelimit-tooltip", "This is a general ratelimit and will be overriden by specific set limits. (Xs/Y)")
|
||||
|
||||
slib.setLang("eprotect", "en", "timeout", "Timeout")
|
||||
slib.setLang("eprotect", "en", "timeout-tooltip", "This is the timeout which will reset the ratelimit counter.")
|
||||
|
||||
slib.setLang("eprotect", "en", "overflowpunishment", "Overflow Punishment")
|
||||
slib.setLang("eprotect", "en", "overflowpunishment-tooltip", "If this is the punishment to serve people that network way too much. (1 = kick, 2 = ban, 3 = block)")
|
||||
|
||||
slib.setLang("eprotect", "en", "whitelistergroup", "Whitelister Group")
|
||||
slib.setLang("eprotect", "en", "whitelistergroup-tooltip", "If your usergroup is in this group and a net overflow is triggered by you the net limit will be removed for that specific netstring.")
|
||||
|
||||
slib.setLang("eprotect", "en", "bypass-vpn", "Bypass VPN")
|
||||
slib.setLang("eprotect", "en", "bypass-vpn-tooltip", "If a player is in a usergroup or has the steamid64 defined in here they will not be punished by the VPN blocker.")
|
||||
|
||||
slib.setLang("eprotect", "en", "bypassgroup", "Bypass Group")
|
||||
slib.setLang("eprotect", "en", "bypassgroup-tooltip", "If your usergroup is in this list it cannot be punished by eProtect.")
|
||||
|
||||
slib.setLang("eprotect", "en", "bypass_sids", "Bypass SteamID")
|
||||
slib.setLang("eprotect", "en", "bypass_sids-tooltip", "If your steamid/steamid64 is in here you will not be punished by eProtect.")
|
||||
|
||||
slib.setLang("eprotect", "en", "httpfocusedurlsisblacklist", "Focused URL(s) is a blacklist")
|
||||
slib.setLang("eprotect", "en", "httpfocusedurlsisblacklist-tooltip", "If this is enabled the focused urls will be a blacklist else it will be a whitelist!")
|
||||
|
||||
slib.setLang("eprotect", "en", "httpfocusedurls", "HTTP Focused URL(s)")
|
||||
slib.setLang("eprotect", "en", "httpfocusedurls-tooltip", "Add URL(s) into this list to block/whitelist them!")
|
||||
|
||||
slib.setLang("eprotect", "en", "enable-networking", "Enable networking")
|
||||
slib.setLang("eprotect", "en", "disable-networking", "Disable networking")
|
||||
|
||||
slib.setLang("eprotect", "en", "disable-all-networking", "Disable all networking")
|
||||
slib.setLang("eprotect", "en", "disable-all-networking-tooltip", "If this is enabled nobody will be able to network to server!")
|
||||
|
||||
slib.setLang("eprotect", "en", "automatic-identifier", "Automatic identifier")
|
||||
slib.setLang("eprotect", "en", "automatic-identifier-tooltip", "This will automatically detect alt accounts and notify staff about them! (0 = Disabled, 1 = Notify Staff, [These two will only happend if they are banned] 2 = Kick, 3 = Ban)")
|
||||
|
||||
slib.setLang("eprotect", "en", "block-vpn", "Block VPN")
|
||||
slib.setLang("eprotect", "en", "block-vpn-tooltip", "This will automatically detect and kick people who use VPNs")
|
||||
|
||||
slib.setLang("eprotect", "en", "notification-groups", "Notification Groups")
|
||||
slib.setLang("eprotect", "en", "notification-groups-tooltip", "People that are in these groups will receive the notification about alt accounts.")
|
||||
|
||||
slib.setLang("eprotect", "en", "player", "Player")
|
||||
slib.setLang("eprotect", "en", "net-string", "Net String")
|
||||
slib.setLang("eprotect", "en", "url", "URL")
|
||||
slib.setLang("eprotect", "en", "called", "Called")
|
||||
slib.setLang("eprotect", "en", "len", "Len")
|
||||
slib.setLang("eprotect", "en", "type", "Type")
|
||||
slib.setLang("eprotect", "en", "punishment", "Punishment")
|
||||
slib.setLang("eprotect", "en", "reason", "Reason")
|
||||
slib.setLang("eprotect", "en", "info", "Info")
|
||||
slib.setLang("eprotect", "en", "activated", "Activated")
|
||||
slib.setLang("eprotect", "en", "secure", "Secured")
|
||||
slib.setLang("eprotect", "en", "ip", "IP Adress")
|
||||
slib.setLang("eprotect", "en", "date", "Date")
|
||||
slib.setLang("eprotect", "en", "country-code", "Country code")
|
||||
slib.setLang("eprotect", "en", "status", "Status")
|
||||
|
||||
slib.setLang("eprotect", "en", "unknown", "Unknown")
|
||||
slib.setLang("eprotect", "en", "secured", "Secured")
|
||||
|
||||
slib.setLang("eprotect", "en", "check-ids", "Check ID(s)")
|
||||
slib.setLang("eprotect", "en", "correlate-ip", "Correlate IP(s)")
|
||||
slib.setLang("eprotect", "en", "family-share-check", "Check Family Share")
|
||||
|
||||
slib.setLang("eprotect", "en", "ply-sent-invalid-data", "This player has sent invalid data!")
|
||||
slib.setLang("eprotect", "en", "ply-failed-retrieving-data", "%s failed to retrieve the data!")
|
||||
|
||||
slib.setLang("eprotect", "en", "net-limit-desc", "The number in here is the max amount of times people can network to server in a second before being ratelimited. (0 = Use general limit, -1 = No limit)")
|
||||
|
||||
slib.setLang("eprotect", "en", "capture", "Screenshot")
|
||||
slib.setLang("eprotect", "en", "check-ips", "Check IP(s)")
|
||||
slib.setLang("eprotect", "en", "fetch-data", "Fetch Data")
|
||||
|
||||
slib.setLang("eprotect", "en", "patched-exploit", "Patched Exploit")
|
||||
slib.setLang("eprotect", "en", "fake-exploit", "Fake Exploit")
|
||||
slib.setLang("eprotect", "en", "net-overflow", "Net Overflow")
|
||||
slib.setLang("eprotect", "en", "exploit-menu", "Exploit Menu")
|
||||
slib.setLang("eprotect", "en", "alt-detection", "Alt Detection")
|
||||
|
||||
slib.setLang("eprotect", "en", "banned", "Banned")
|
||||
slib.setLang("eprotect", "en", "kicked", "Kicked")
|
||||
slib.setLang("eprotect", "en", "notified", "Notified")
|
||||
|
||||
slib.setLang("eprotect", "en", "copied_clipboard", "Copied to clipboard")
|
||||
|
||||
slib.setLang("eprotect", "en", "page_of_page", "Page %s/%s")
|
||||
slib.setLang("eprotect", "en", "previous", "Previous")
|
||||
slib.setLang("eprotect", "en", "next", "Next")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "en", "correlated-ip", "Correlated IP")
|
||||
slib.setLang("eprotect", "en", "family-share", "Family Share")
|
||||
|
||||
slib.setLang("eprotect", "en", "invalid-player", "This player is invalid!")
|
||||
slib.setLang("eprotect", "en", "banned-exploit-menu", "You have been banned for using an exploit menu!")
|
||||
slib.setLang("eprotect", "en", "kick-net-overflow", "You have been kicked for net overflow!")
|
||||
slib.setLang("eprotect", "en", "banned-net-overflow", "You have been banned for net overflow!")
|
||||
slib.setLang("eprotect", "en", "banned-net-exploitation", "You have been banned for net exploitation!")
|
||||
slib.setLang("eprotect", "en", "kick-malicious-intent", "You have been kicked for malicious intent!")
|
||||
slib.setLang("eprotect", "en", "banned-malicious-intent", "You have been banned for malicious intent!")
|
||||
|
||||
slib.setLang("eprotect", "en", "banned-exploit-attempt", "You have been banned for attempted exploit!")
|
||||
|
||||
slib.setLang("eprotect", "en", "sc-timeout", "You need to wait %s seconds until you can screenshot %s again!")
|
||||
slib.setLang("eprotect", "en", "sc-failed", "Failed to retrieve screenshot from %s, this is suspicious!")
|
||||
|
||||
slib.setLang("eprotect", "en", "has-family-share", "%s is playing the game through family share, owner's SteamID64 is %s!")
|
||||
slib.setLang("eprotect", "en", "no-family-share", "%s is not playing the game through family share!")
|
||||
slib.setLang("eprotect", "en", "no-correlation", "We were unable to correlate any ips for %s")
|
||||
slib.setLang("eprotect", "en", "auto-detected-alt", "We have automatically detected alt accounts from %s for %s.")
|
||||
slib.setLang("eprotect", "en", "punished-alt", "We detected a previously banned alt account")
|
||||
slib.setLang("eprotect", "en", "vpn-blocked", "VPNs are blocked on this server")
|
||||
|
||||
slib.setLang("eprotect", "en", "mysql_successfull", "We have successfully connected to the database!")
|
||||
slib.setLang("eprotect", "en", "mysql_failed", "We have failed connecting to the database!")
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "fr", "sc-preview", "Pré-visualisation des Captures D'ecrans - ")
|
||||
slib.setLang("eprotect", "fr", "net-info", "Info des Nets - ")
|
||||
slib.setLang("eprotect", "fr", "ip-info", "Info de l'IP - ")
|
||||
slib.setLang("eprotect", "fr", "id-info", "Info de l'ID - ")
|
||||
slib.setLang("eprotect", "fr", "ip-correlation", "Corrélation de l'IP - ")
|
||||
slib.setLang("eprotect", "fr", "table-viewer", "Visionneur de Table")
|
||||
|
||||
slib.setLang("eprotect", "fr", "tab-general", "Général")
|
||||
slib.setLang("eprotect", "fr", "tab-identifier", "Identifiant")
|
||||
slib.setLang("eprotect", "fr", "tab-netlimiter", "Limiteur de Net")
|
||||
slib.setLang("eprotect", "fr", "tab-netlogger", "Sauvegarde de net (logs)")
|
||||
slib.setLang("eprotect", "fr", "tab-exploitpatcher", "Correcteur d'Exploit")
|
||||
slib.setLang("eprotect", "fr", "tab-exploitfinder", "Rechercheur d'Exploit")
|
||||
slib.setLang("eprotect", "fr", "tab-fakeexploits", "Faux Exploit")
|
||||
slib.setLang("eprotect", "fr", "tab-datasnooper", "Fouineur de Data")
|
||||
|
||||
slib.setLang("eprotect", "fr", "player-list", "Liste des Joueurs")
|
||||
|
||||
slib.setLang("eprotect", "fr", "ratelimit", "Limite de flux")
|
||||
slib.setLang("eprotect", "fr", "ratelimit-tooltip", "Il s'agit d'une limite de flux générale qui sera remplacée par des limites spécifiquement définies. (Xs/Y)")
|
||||
|
||||
slib.setLang("eprotect", "fr", "timeout", "Délai")
|
||||
slib.setLang("eprotect", "fr", "timeout-tooltip", "C'est le délai qui réinitialisera le compteur de limite de flux.")
|
||||
|
||||
slib.setLang("eprotect", "fr", "overflowpunishment", "Punition d'Overflow")
|
||||
slib.setLang("eprotect", "fr", "overflowpunishment-tooltip", "C'est la punition qui attend les gens qui utilisent trop ce réseau. (1 = kick, 2 = ban)")
|
||||
|
||||
slib.setLang("eprotect", "fr", "enable-networking", "Activer la mise en réseau")
|
||||
slib.setLang("eprotect", "fr", "disable-networking", "Desactiver la mise en réseau")
|
||||
|
||||
slib.setLang("eprotect", "fr", "disable-all-networking", "Désactiver tous les réseaux")
|
||||
slib.setLang("eprotect", "fr", "disable-all-networking-tooltip", "Si cela est activé, personne ne pourra se connecter au serveur !")
|
||||
|
||||
slib.setLang("eprotect", "fr", "player", "Joueur")
|
||||
slib.setLang("eprotect", "fr", "net-string", "Chaine de réseaux (string)")
|
||||
slib.setLang("eprotect", "fr", "called", "appelée")
|
||||
slib.setLang("eprotect", "fr", "len", "Len")
|
||||
slib.setLang("eprotect", "fr", "type", "Type")
|
||||
slib.setLang("eprotect", "fr", "activated", "Activé")
|
||||
slib.setLang("eprotect", "fr", "secure", "Securise")
|
||||
slib.setLang("eprotect", "fr", "ip", "Adresse IP")
|
||||
slib.setLang("eprotect", "fr", "date", "Date")
|
||||
slib.setLang("eprotect", "fr", "country-code", "Code Pays")
|
||||
slib.setLang("eprotect", "fr", "status", "Statut")
|
||||
|
||||
slib.setLang("eprotect", "fr", "unknown", "Inconnu")
|
||||
slib.setLang("eprotect", "fr", "secured", "Securisé")
|
||||
|
||||
slib.setLang("eprotect", "fr", "check-ids", "Verifier l'ID")
|
||||
slib.setLang("eprotect", "fr", "correlate-ip", "corréler l'IP")
|
||||
slib.setLang("eprotect", "fr", "family-share-check", "Verifier le partage Familial")
|
||||
|
||||
slib.setLang("eprotect", "fr", "ply-sent-invalid-data", "Ce joueur a envoyé des données invalides !")
|
||||
slib.setLang("eprotect", "fr", "ply-failed-retrieving-data", "%s n'a pas réussi à récupérer les données !")
|
||||
|
||||
slib.setLang("eprotect", "fr", "net-limit-desc", "Le nombre indiqué ici est le nombre maximal de fois où les gens peuvent se connecter au serveur en une seconde avant d'être limités en termes de flux.")
|
||||
|
||||
slib.setLang("eprotect", "fr", "capture", "Capture d'Ecran")
|
||||
slib.setLang("eprotect", "fr", "check-ips", "Verifier l'IP")
|
||||
slib.setLang("eprotect", "fr", "fetch-data", "Récupérer les données")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "fr", "invalid-player", "Ce joueur n'est pas valide !")
|
||||
slib.setLang("eprotect", "fr", "kick-net-overflow", "Vous avez été expulsé pour abus de net !")
|
||||
slib.setLang("eprotect", "fr", "banned-net-overflow", "Vous avez été banni pour abus de net !")
|
||||
slib.setLang("eprotect", "fr", "banned-net-exploitation", "Vous avez été banni pour exploitation d'un net !")
|
||||
slib.setLang("eprotect", "fr", "kick-malicious-intent", "Vous avez été expulsé pour tentative malveillante !")
|
||||
slib.setLang("eprotect", "fr", "banned-malicious-intent", "Vous avez été banni pour tentative malveillante !")
|
||||
|
||||
slib.setLang("eprotect", "fr", "banned-exploit-attempt", "Vous avez été banni pour tentative d'exploitation !")
|
||||
|
||||
slib.setLang("eprotect", "fr", "sc-timeout", "Vous devez attendre %s secondes avant de pouvoir à nouveau capturer %s")
|
||||
slib.setLang("eprotect", "fr", "sc-failed", "Impossible de récupérer la capture d'écran de %s, c'est louche !")
|
||||
|
||||
slib.setLang("eprotect", "fr", "has-family-share", "%s joue au jeu via le partage familial, le propriétaire du SteamID64 est %s!")
|
||||
slib.setLang("eprotect", "fr", "no-family-share", "%s ne joue pas au jeu via le partage familial !")
|
||||
slib.setLang("eprotect", "fr", "no-correlation", "Nous n'avons pas pu corréler les ips pour %s")
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "de", "sc-preview", "Screenshot Vorschau - ")
|
||||
slib.setLang("eprotect", "de", "net-info", "Net Info - ")
|
||||
slib.setLang("eprotect", "de", "ip-info", "IP Info - ")
|
||||
slib.setLang("eprotect", "de", "id-info", "ID Info - ")
|
||||
slib.setLang("eprotect", "de", "ip-correlation", "IP Korrelation - ")
|
||||
slib.setLang("eprotect", "de", "table-viewer", "Tabellenbetrachtung")
|
||||
|
||||
slib.setLang("eprotect", "de", "tab-general", "Generell")
|
||||
slib.setLang("eprotect", "de", "tab-identifier", "Kennung")
|
||||
slib.setLang("eprotect", "de", "tab-netlimiter", "Net-Begrenzer")
|
||||
slib.setLang("eprotect", "de", "tab-netlogger", "Net Logger")
|
||||
slib.setLang("eprotect", "de", "tab-exploitpatcher", "Exploit Patcher")
|
||||
slib.setLang("eprotect", "de", "tab-exploitfinder", "Exploit Finder")
|
||||
slib.setLang("eprotect", "de", "tab-fakeexploits", "Fake Exploits")
|
||||
slib.setLang("eprotect", "de", "tab-datasnooper", "Datenschnüffler")
|
||||
|
||||
slib.setLang("eprotect", "de", "player-list", "Spielerliste")
|
||||
|
||||
slib.setLang("eprotect", "de", "ratelimit", "Bewertungslimit")
|
||||
slib.setLang("eprotect", "de", "ratelimit-tooltip", "Dies ist ein allgemeines Bewertungslimit und wird durch bestimmte festgelegte Grenzwerte außer Kraft gesetzt. (Xs / Y)")
|
||||
|
||||
slib.setLang("eprotect", "de", "timeout", "Timeout")
|
||||
slib.setLang("eprotect", "de", "timeout-tooltip", "Dies ist das Zeitlimit, das den Bewertungslimit-Zähler zurückgesetzt.")
|
||||
|
||||
slib.setLang("eprotect", "de", "overflowpunishment", "Overflow Bestrafung")
|
||||
slib.setLang("eprotect", "de", "overflowpunishment-tooltip", "Dies ist die Bestrafung, wenn der Network Way des Spielers zu lang ist. (1 = Kick, 2 = Bann)")
|
||||
|
||||
slib.setLang("eprotect", "de", "enable-networking", "Aktiviere Networking")
|
||||
slib.setLang("eprotect", "de", "disable-networking", "Deaktiviere Networking")
|
||||
|
||||
slib.setLang("eprotect", "de", "disable-all-networking", "Deaktiviert serverweit Networking")
|
||||
slib.setLang("eprotect", "de", "disable-all-networking-tooltip", "Wenn dies aktiviert ist, kann niemand mit dme Server networken.")
|
||||
|
||||
slib.setLang("eprotect", "de", "player", "Spieler")
|
||||
slib.setLang("eprotect", "de", "net-string", "Net String")
|
||||
slib.setLang("eprotect", "de", "called", "Aufgerufen")
|
||||
slib.setLang("eprotect", "de", "len", "Len")
|
||||
slib.setLang("eprotect", "de", "type", "Typ")
|
||||
slib.setLang("eprotect", "de", "activated", "Aktiviert")
|
||||
slib.setLang("eprotect", "de", "secure", "Gesichert")
|
||||
slib.setLang("eprotect", "de", "ip", "IP Adresse")
|
||||
slib.setLang("eprotect", "de", "date", "Datum")
|
||||
slib.setLang("eprotect", "de", "country-code", "Landesvorwahl")
|
||||
slib.setLang("eprotect", "de", "status", "Status")
|
||||
|
||||
slib.setLang("eprotect", "de", "unknown", "Unbekannt")
|
||||
slib.setLang("eprotect", "de", "secured", "Gesichert")
|
||||
|
||||
slib.setLang("eprotect", "de", "check-ids", "Check ID(s)")
|
||||
slib.setLang("eprotect", "de", "correlate-ip", "Zusammenhängende IP(s)")
|
||||
slib.setLang("eprotect", "de", "family-share-check", "Prüfe Family Share")
|
||||
|
||||
slib.setLang("eprotect", "de", "ply-sent-invalid-data", "Dieser Spieler hat ungültige Daten gesendet.")
|
||||
slib.setLang("eprotect", "de", "ply-failed-retrieving-data", "%s Daten konnten nicht abgerufen werden.")
|
||||
|
||||
slib.setLang("eprotect", "de", "net-limit-desc", "Die Zahl hier gibt an, wie oft Personen pro Sekunde mit dem Server networken können, bevor die Rate begrenzt wird.")
|
||||
|
||||
slib.setLang("eprotect", "de", "capture", "Screenshot")
|
||||
slib.setLang("eprotect", "de", "check-ips", "Prüfe IP(s)")
|
||||
slib.setLang("eprotect", "de", "fetch-data", "Daten abrufen")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "de", "invalid-player", "Dieser Spieler ist ungültig!")
|
||||
slib.setLang("eprotect", "de", "kick-net-overflow", "Du wurdest wegen Net-Overflow vom Server geworfen!")
|
||||
slib.setLang("eprotect", "de", "banned-net-overflow", "Du wurdest wegen Net-Overflow vom Server gebannt!")
|
||||
slib.setLang("eprotect", "de", "banned-net-exploitation", "Du wurdest wegen Net-Exploiting vom Server gebannt!")
|
||||
slib.setLang("eprotect", "de", "kick-malicious-intent", "Du wurdest wegen bösen Absichten vom Server geworfen!")
|
||||
slib.setLang("eprotect", "de", "banned-malicious-intent", "Du wurdest wegen bösen Absichten vom Server gebannt!")
|
||||
|
||||
slib.setLang("eprotect", "de", "banned-exploit-attempt", "Du wurdest wegen versuchtem Exploiting gebannt!")
|
||||
|
||||
slib.setLang("eprotect", "de", "sc-timeout", "Du musst %s Sekunden warten, bis du %s wieder screenshoten kannst!")
|
||||
slib.setLang("eprotect", "de", "sc-failed", "Screenshot von %s konnte nicht abgerufen werden, dies ist verdächtig!")
|
||||
|
||||
slib.setLang("eprotect", "de", "has-family-share", "%s spielt über Family Sharing, SteamID64 des Besitzers ist: %s!")
|
||||
slib.setLang("eprotect", "de", "no-family-share", "%s spielt das Spiel nicht durch Family Sharing!")
|
||||
slib.setLang("eprotect", "de", "no-correlation", "Wir konnten keine IPs für %s korrelieren.")
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "pl", "sc-preview", "Podgląd zrzutu ekranu - ")
|
||||
slib.setLang("eprotect", "pl", "net-info", "Net Info - ")
|
||||
slib.setLang("eprotect", "pl", "ip-info", "IP Info - ")
|
||||
slib.setLang("eprotect", "pl", "id-info", "ID Info - ")
|
||||
slib.setLang("eprotect", "pl", "ip-correlation", "Lokalizacja IP - ")
|
||||
slib.setLang("eprotect", "pl", "table-viewer", "Table Viewer")
|
||||
|
||||
slib.setLang("eprotect", "pl", "tab-general", "Ogólne")
|
||||
slib.setLang("eprotect", "pl", "tab-identifier", "Identifier")
|
||||
slib.setLang("eprotect", "pl", "tab-netlimiter", "Ogranicznik Net")
|
||||
slib.setLang("eprotect", "pl", "tab-netlogger", "Rejestrator Net")
|
||||
slib.setLang("eprotect", "pl", "tab-exploitpatcher", "Łatka Exploitów")
|
||||
slib.setLang("eprotect", "pl", "tab-exploitfinder", "Exploit Finder")
|
||||
slib.setLang("eprotect", "pl", "tab-fakeexploits", "Fake Exploits")
|
||||
slib.setLang("eprotect", "pl", "tab-datasnooper", "Data Snooper")
|
||||
|
||||
slib.setLang("eprotect", "pl", "player-list", "Lista graczy")
|
||||
|
||||
slib.setLang("eprotect", "pl", "ratelimit", "Ratelimit")
|
||||
slib.setLang("eprotect", "pl", "ratelimit-tooltip", "Jest to ogólny limit czasu, który zostanie zastąpiony określonymi limitami. (Xs/Y)")
|
||||
|
||||
slib.setLang("eprotect", "pl", "timeout", "Timeout")
|
||||
slib.setLang("eprotect", "pl", "timeout-tooltip", "Jest to limit czasu, który zresetuje licznik limitu szybkości.")
|
||||
|
||||
slib.setLang("eprotect", "pl", "overflowpunishment", "Próg kary Net Exploit")
|
||||
slib.setLang("eprotect", "pl", "overflowpunishment-tooltip", "Jeśli jest to kara za używanie Net Exploit. (1 = kick, 2 = ban)")
|
||||
|
||||
slib.setLang("eprotect", "pl", "enable-networking", "Włącz sieć")
|
||||
slib.setLang("eprotect", "pl", "disable-networking", "Wyłącz sieć")
|
||||
|
||||
slib.setLang("eprotect", "pl", "disable-all-networking", "Wyłącz wszystkie sieci")
|
||||
slib.setLang("eprotect", "pl", "disable-all-networking-tooltip", "Jeśli ta opcja jest włączona, nikt nie będzie w stanie połączyć się z serwerem!")
|
||||
|
||||
slib.setLang("eprotect", "pl", "player", "Gracz")
|
||||
slib.setLang("eprotect", "pl", "net-string", "Zmienna Net")
|
||||
slib.setLang("eprotect", "pl", "called", "Zapytanie")
|
||||
slib.setLang("eprotect", "pl", "len", "Rozmiar")
|
||||
slib.setLang("eprotect", "pl", "type", "Typ")
|
||||
slib.setLang("eprotect", "pl", "activated", "Aktywowany")
|
||||
slib.setLang("eprotect", "pl", "secure", "Zabezpieczone")
|
||||
slib.setLang("eprotect", "pl", "ip", "IP Adress")
|
||||
slib.setLang("eprotect", "pl", "date", "Data")
|
||||
slib.setLang("eprotect", "pl", "country-code", "Kod kraju")
|
||||
slib.setLang("eprotect", "pl", "status", "Status")
|
||||
|
||||
slib.setLang("eprotect", "pl", "unknown", "Nieznany")
|
||||
slib.setLang("eprotect", "pl", "secured", "Zabezpieczone")
|
||||
|
||||
slib.setLang("eprotect", "pl", "check-ids", "Sprawdź ID")
|
||||
slib.setLang("eprotect", "pl", "correlate-ip", "Lokalizacja IP")
|
||||
slib.setLang("eprotect", "pl", "family-share-check", "Sprawdź Family Share")
|
||||
|
||||
slib.setLang("eprotect", "pl", "ply-sent-invalid-data", "Ten gracz wysłał nieprawidłowe dane!")
|
||||
slib.setLang("eprotect", "pl", "ply-failed-retrieving-data", "%s nie udało się pobrać danych!")
|
||||
|
||||
slib.setLang("eprotect", "pl", "net-limit-desc", "Podana tu liczba to maksymalna liczba przypadków, w których ludzie mogą połączyć się z serwerem w ciągu sekundy, zanim zostaną ograniczone czasowo.")
|
||||
|
||||
slib.setLang("eprotect", "pl", "capture", "Screenshot")
|
||||
slib.setLang("eprotect", "pl", "check-ips", "Sprawdź IP(s)")
|
||||
slib.setLang("eprotect", "pl", "fetch-data", "Sprawdź Dane")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "pl", "invalid-player", "Nie ma takiego Gracza!")
|
||||
slib.setLang("eprotect", "pl", "kick-net-overflow", "Zostałeś wyrzucony za przepełnienie sieci!")
|
||||
slib.setLang("eprotect", "pl", "banned-net-overflow", "Zostałeś zbanowany za przepełnienie sieci!")
|
||||
slib.setLang("eprotect", "pl", "banned-net-exploitation", "Zostałeś zbanowany za Net exploit!")
|
||||
slib.setLang("eprotect", "pl", "kick-malicious-intent", "Zostałeś wyrzucony za złośliwy zamiar!")
|
||||
slib.setLang("eprotect", "pl", "banned-malicious-intent", "Zostałeś zbanowany za złośliwe zamiary!")
|
||||
|
||||
slib.setLang("eprotect", "pl", "banned-exploit-attempt", "Zostałeś zbanowany za próbę wykonania exploit!")
|
||||
|
||||
slib.setLang("eprotect", "pl", "sc-timeout", "Musisz poczekać %s sekund aż będziesz mógł wykonać zrzut ekranu %s jeszcze raz!")
|
||||
slib.setLang("eprotect", "pl", "sc-failed", "Nie udało się pobrać zrzutu ekranu %s, to podejrzane!")
|
||||
|
||||
slib.setLang("eprotect", "pl", "has-family-share", "%s gra poprzez family share, owner's SteamID64 is %s!")
|
||||
slib.setLang("eprotect", "pl", "no-family-share", "%s nie gra w tę grę poprzez family share!")
|
||||
slib.setLang("eprotect", "pl", "no-correlation", "Nie mogliśmy skorelować żadnych adresów IP dla %s")
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "ru", "sc-preview", "Предпросмотр снимка экрана - ")
|
||||
slib.setLang("eprotect", "ru", "net-info", "Net Информация - ")
|
||||
slib.setLang("eprotect", "ru", "ip-info", "IP Информация - ")
|
||||
slib.setLang("eprotect", "ru", "id-info", "ID Информация - ")
|
||||
slib.setLang("eprotect", "ru", "ip-correlation", "IP Совпадение - ")
|
||||
slib.setLang("eprotect", "ru", "table-viewer", "Просмотр таблиц")
|
||||
|
||||
slib.setLang("eprotect", "ru", "tab-general", "Главная")
|
||||
slib.setLang("eprotect", "ru", "tab-identifier", "Идентификатор")
|
||||
slib.setLang("eprotect", "ru", "tab-netlimiter", "Net Лимит")
|
||||
slib.setLang("eprotect", "ru", "tab-netlogger", "Net Регистратор")
|
||||
slib.setLang("eprotect", "ru", "tab-exploitpatcher", "Патчер эксплойтов")
|
||||
slib.setLang("eprotect", "ru", "tab-exploitfinder", "Поиск эксплойтов")
|
||||
slib.setLang("eprotect", "ru", "tab-fakeexploits", "Поддельные эксплойты")
|
||||
slib.setLang("eprotect", "ru", "tab-datasnooper", "Просмотр Data'ы игрока")
|
||||
|
||||
slib.setLang("eprotect", "ru", "player-list", "Список игроков")
|
||||
|
||||
slib.setLang("eprotect", "ru", "ratelimit", "Ограничение скорости оборотов")
|
||||
slib.setLang("eprotect", "ru", "ratelimit-tooltip", "Это общий предел скорости оборотов, который будет отменен конкретными установленными пределами. (Xs / Y)")
|
||||
|
||||
slib.setLang("eprotect", "ru", "timeout", "Тайм-аут")
|
||||
slib.setLang("eprotect", "ru", "timeout-tooltip", "Это тайм-аут, который сбросит счетчик предельного числа оборотов..")
|
||||
|
||||
slib.setLang("eprotect", "ru", "overflowpunishment", "Наказание за переполнение")
|
||||
slib.setLang("eprotect", "ru", "overflowpunishment-tooltip", "Если включено, то люди получат наказание за слишком большое количество трафика сети. (1 = Кик, 2 = Бан)")
|
||||
|
||||
slib.setLang("eprotect", "ru", "enable-networking", "Включить сеть")
|
||||
slib.setLang("eprotect", "ru", "disable-networking", "Отключить сеть")
|
||||
|
||||
slib.setLang("eprotect", "ru", "disable-all-networking", "Отключить все сети")
|
||||
slib.setLang("eprotect", "ru", "disable-all-networking-tooltip", "Если этот параметр включен, никто не сможет подключиться к серверу по сети!")
|
||||
|
||||
slib.setLang("eprotect", "ru", "player", "Игрок")
|
||||
slib.setLang("eprotect", "ru", "net-string", "Net строка")
|
||||
slib.setLang("eprotect", "ru", "called", "Называется")
|
||||
slib.setLang("eprotect", "ru", "len", "Длина")
|
||||
slib.setLang("eprotect", "ru", "type", "Тип")
|
||||
slib.setLang("eprotect", "ru", "activated", "Активирована")
|
||||
slib.setLang("eprotect", "ru", "secure", "Защищена")
|
||||
slib.setLang("eprotect", "ru", "ip", "IP Адрес")
|
||||
slib.setLang("eprotect", "ru", "date", "Дата")
|
||||
slib.setLang("eprotect", "ru", "country-code", "Код страны")
|
||||
slib.setLang("eprotect", "ru", "status", "Статус")
|
||||
|
||||
slib.setLang("eprotect", "ru", "unknown", "Неизвестно")
|
||||
slib.setLang("eprotect", "ru", "secured", "Защищена")
|
||||
|
||||
slib.setLang("eprotect", "ru", "check-ids", "Проверить ID")
|
||||
slib.setLang("eprotect", "ru", "correlate-ip", "Соотнести IP")
|
||||
slib.setLang("eprotect", "ru", "family-share-check", "Проверить Семейный доступ")
|
||||
|
||||
slib.setLang("eprotect", "ru", "ply-sent-invalid-data", "Этот игрок отправил неверные данные!")
|
||||
slib.setLang("eprotect", "ru", "ply-failed-retrieving-data", "%s не удалось получить данные!")
|
||||
|
||||
slib.setLang("eprotect", "ru", "net-limit-desc", "Число здесь - это максимальное количество раз, которое люди могут отправить на сервер в секунду, прежде чем будут ограничены по частоте.")
|
||||
|
||||
slib.setLang("eprotect", "ru", "capture", "Скриншот")
|
||||
slib.setLang("eprotect", "ru", "check-ips", "Проверить IP")
|
||||
slib.setLang("eprotect", "ru", "fetch-data", "Получить Data'у ")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "ru", "invalid-player", "Этот игрок недействителен!")
|
||||
slib.setLang("eprotect", "ru", "kick-net-overflow", "Вас выгнали за переполнение сети!")
|
||||
slib.setLang("eprotect", "ru", "banned-net-overflow", "Вас забанили за переполнение сети!")
|
||||
slib.setLang("eprotect", "ru", "banned-net-exploitation", "Вас забанили за эксплуатацию в сети!")
|
||||
slib.setLang("eprotect", "ru", "kick-malicious-intent", "Вас выгнали за злой умысел!")
|
||||
slib.setLang("eprotect", "ru", "banned-malicious-intent", "Вас забанили за злой умысел!")
|
||||
|
||||
slib.setLang("eprotect", "ru", "banned-exploit-attempt", "Вас забанили за попытку использовать эксплоит!")
|
||||
|
||||
slib.setLang("eprotect", "ru", "sc-timeout", "Вам нужно подождать %s секунд, пока вы снова не сможете сделать снимок экрана %s!")
|
||||
slib.setLang("eprotect", "ru", "sc-failed", "Не удалось получить снимок экрана %s, это подозрительно!")
|
||||
|
||||
slib.setLang("eprotect", "ru", "has-family-share", "%s играет в игру через семейный ресурс, SteamID64 владельца %s!")
|
||||
slib.setLang("eprotect", "ru", "no-family-share", "%s не играет в игру через семейный просмотр.")
|
||||
slib.setLang("eprotect", "ru", "no-correlation", "Нам не удалось сопоставить IP-адреса для %s")
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "es", "sc-preview", " Preview de Screenshot - ")
|
||||
slib.setLang("eprotect", "es", "net-info", "Información Net - ")
|
||||
slib.setLang("eprotect", "es", "ip-info", "Información de IP - ")
|
||||
slib.setLang("eprotect", "es", "id-info", "Información de ID - ")
|
||||
slib.setLang("eprotect", "es", "ip-correlation", "Correlación de IP - ")
|
||||
slib.setLang("eprotect", "es", "table-viewer", "Visor de Mesas")
|
||||
|
||||
slib.setLang("eprotect", "es", "tab-general", "General")
|
||||
slib.setLang("eprotect", "es", "tab-identifier", "Identificador")
|
||||
slib.setLang("eprotect", "es", "tab-netlimiter", "Limitador Net")
|
||||
slib.setLang("eprotect", "es", "tab-netlogger", "Loggs Net")
|
||||
slib.setLang("eprotect", "es", "tab-exploitpatcher", "Parcheador de Exploits")
|
||||
slib.setLang("eprotect", "es", "tab-exploitfinder", "Buscador de Exploits")
|
||||
slib.setLang("eprotect", "es", "tab-fakeexploits", "Exploits Falsos")
|
||||
slib.setLang("eprotect", "es", "tab-datasnooper", "Fisgón de Datos")
|
||||
|
||||
slib.setLang("eprotect", "es", "player-list", "Lista de Jugadores")
|
||||
|
||||
slib.setLang("eprotect", "es", "ratelimit", "LimitadorRate")
|
||||
slib.setLang("eprotect", "es", "ratelimit-tooltip", "Este es un Limitador-Rate y funciona para poner ciertos limites. (Xs/Y)")
|
||||
|
||||
slib.setLang("eprotect", "es", "timeout", "Tiempo")
|
||||
slib.setLang("eprotect", "es", "timeout-tooltip", "El tiempo el el intervalo entre que se resetea el contador del LimitadorRate.")
|
||||
|
||||
slib.setLang("eprotect", "es", "overflowpunishment", "Castigo de Overflow")
|
||||
slib.setLang("eprotect", "es", "overflowpunishment-tooltip", "Este es el castigo que va a tener la gente que usa demasiado network. (1 = kick, 2 = ban)")
|
||||
|
||||
slib.setLang("eprotect", "es", "enable-networking", "Habilitar networking")
|
||||
slib.setLang("eprotect", "es", "disable-networking", "Deshabilitar networking")
|
||||
|
||||
slib.setLang("eprotect", "es", "disable-all-networking", "Deshabilitar todo el networking")
|
||||
slib.setLang("eprotect", "es", "disable-all-networking-tooltip", "Si esto esta habilitado, nadie va a poder usar el network en el server!")
|
||||
|
||||
slib.setLang("eprotect", "es", "player", "Jugador")
|
||||
slib.setLang("eprotect", "es", "net-string", "Net String")
|
||||
slib.setLang("eprotect", "es", "called", "LLamado")
|
||||
slib.setLang("eprotect", "es", "len", "Len")
|
||||
slib.setLang("eprotect", "es", "type", "Tipo")
|
||||
slib.setLang("eprotect", "es", "activated", "Activado")
|
||||
slib.setLang("eprotect", "es", "secure", "Seguro")
|
||||
slib.setLang("eprotect", "es", "ip", "Dirección IP")
|
||||
slib.setLang("eprotect", "es", "date", "Fecha")
|
||||
slib.setLang("eprotect", "es", "country-code", "Código de País")
|
||||
slib.setLang("eprotect", "es", "status", "Status")
|
||||
|
||||
slib.setLang("eprotect", "es", "unknown", "Desconocido")
|
||||
slib.setLang("eprotect", "es", "secured", "Seguro")
|
||||
|
||||
slib.setLang("eprotect", "es", "check-ids", "Checkear ID(s)")
|
||||
slib.setLang("eprotect", "es", "correlate-ip", "Correlacionar IP(s)")
|
||||
slib.setLang("eprotect", "es", "family-share-check", "Checkear Cuentas Familiares")
|
||||
|
||||
slib.setLang("eprotect", "es", "ply-sent-invalid-data", "Este Jugador envió datos inválidos!")
|
||||
slib.setLang("eprotect", "es", "ply-failed-retrieving-data", "%s Fallo al recibir datos!")
|
||||
|
||||
slib.setLang("eprotect", "es", "net-limit-desc", "Este nuemero es la cantidad de veces que la gente puede usar network en un segundo antes de usar el LimitadorRate.")
|
||||
|
||||
slib.setLang("eprotect", "es", "capture", "Screenshot")
|
||||
slib.setLang("eprotect", "es", "check-ips", "Checkear IP(s)")
|
||||
slib.setLang("eprotect", "es", "fetch-data", "Buscar Datos")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "es", "invalid-player", "Este Jugador en invalido!")
|
||||
slib.setLang("eprotect", "es", "kick-net-overflow", "Fuiste expulsado por net overflow!")
|
||||
slib.setLang("eprotect", "es", "banned-net-overflow", "Fuiste suspendido por net overflow!")
|
||||
slib.setLang("eprotect", "es", "banned-net-exploitation", "Fuiste expulsado por exploitiar mensajes net!")
|
||||
slib.setLang("eprotect", "es", "kick-malicious-intent", "Fuiste expulsado por intenciones maliciosas!")
|
||||
slib.setLang("eprotect", "es", "banned-malicious-intent", "Fuiste suspendido por intenciones maliciosas!")
|
||||
|
||||
slib.setLang("eprotect", "es", "banned-exploit-attempt", "Fuiste suspendido por intentar usar un maliciosas!")
|
||||
|
||||
slib.setLang("eprotect", "es", "sc-timeout", "Tenes que esperar %s segundos antes de poder screeshotear a %s otra vez!")
|
||||
slib.setLang("eprotect", "es", "sc-failed", "Fallo al cargar screenshot de %s, sospechoso...")
|
||||
|
||||
slib.setLang("eprotect", "es", "has-family-share", "%s esta jugando con una cuenta familiar, el SteamD64 del dueño es %s")
|
||||
slib.setLang("eprotect", "es", "no-family-share", "%s no esta jugando con una cuenta familiar!")
|
||||
slib.setLang("eprotect", "es", "no-correlation", "No se pudieron correlacionar IPs con %s")
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if CLIENT then
|
||||
slib.setLang("eprotect", "tr", "sc-preview", "Ekranı Görüntüle - ")
|
||||
slib.setLang("eprotect", "tr", "net-info", "Ağ bilgisi - ")
|
||||
slib.setLang("eprotect", "tr", "ip-info", "IP bilgisi - ")
|
||||
slib.setLang("eprotect", "tr", "id-info", "ID bilgisi - ")
|
||||
slib.setLang("eprotect", "tr", "ip-correlation", "IP ilişkisi - ")
|
||||
slib.setLang("eprotect", "tr", "table-viewer", "Masa Görüntülemesi")
|
||||
|
||||
slib.setLang("eprotect", "tr", "tab-general", "Genel")
|
||||
slib.setLang("eprotect", "tr", "tab-identifier", "Tanımlayıcı")
|
||||
slib.setLang("eprotect", "tr", "tab-netlimiter", "Bağ limitleyicisi")
|
||||
slib.setLang("eprotect", "tr", "tab-netlogger", "Ağ kayıtçısı")
|
||||
slib.setLang("eprotect", "tr", "tab-exploitpatcher", "Exploit Güncelleyici")
|
||||
slib.setLang("eprotect", "tr", "tab-exploitfinder", "Exploit Bulucusu")
|
||||
slib.setLang("eprotect", "tr", "tab-fakeexploits", "Sahte Exploitler")
|
||||
slib.setLang("eprotect", "tr", "tab-datasnooper", "Kayıt ağı")
|
||||
|
||||
slib.setLang("eprotect", "tr", "player-list", "Oyuncu Listesi")
|
||||
|
||||
slib.setLang("eprotect", "tr", "ratelimit", "Hız sınırlayıcısı")
|
||||
slib.setLang("eprotect", "tr", "ratelimit-tooltip", "Bu genel bir hız sınırlayıcısı. (Xs/Y)")
|
||||
|
||||
slib.setLang("eprotect", "tr", "timeout", "Zaman Aşımı")
|
||||
slib.setLang("eprotect", "tr", "timeout-tooltip", "Bu zaman aşımı genel hız sınırlayıcısına karşıdır.")
|
||||
|
||||
slib.setLang("eprotect", "tr", "overflowpunishment", "Taşma cezası")
|
||||
slib.setLang("eprotect", "tr", "overflowpunishment-tooltip", "Bu ceza ağı rahatlatmak için uygulanır. (1 = kick, 2 = ban)")
|
||||
|
||||
slib.setLang("eprotect", "tr", "enable-networking", "Ağ oluşturmayı aktifleştir")
|
||||
slib.setLang("eprotect", "tr", "disable-networking", "Ağ oluşturmayı engelle")
|
||||
|
||||
slib.setLang("eprotect", "tr", "disable-all-networking", "Tüm ağ oluşturmayı engelle")
|
||||
slib.setLang("eprotect", "tr", "disable-all-networking-tooltip", "Eğer bu aktif olursa kimse sunucuya giremez!")
|
||||
|
||||
slib.setLang("eprotect", "tr", "player", "Player")
|
||||
slib.setLang("eprotect", "tr", "net-string", "Net String")
|
||||
slib.setLang("eprotect", "tr", "called", "Called")
|
||||
slib.setLang("eprotect", "tr", "len", "Len")
|
||||
slib.setLang("eprotect", "tr", "type", "Type")
|
||||
slib.setLang("eprotect", "tr", "activated", "Activated")
|
||||
slib.setLang("eprotect", "tr", "secure", "Secured")
|
||||
slib.setLang("eprotect", "tr", "ip", "IP Adress")
|
||||
slib.setLang("eprotect", "tr", "date", "Date")
|
||||
slib.setLang("eprotect", "tr", "country-code", "Country code")
|
||||
slib.setLang("eprotect", "tr", "status", "Status")
|
||||
|
||||
slib.setLang("eprotect", "tr", "unknown", "Unknown")
|
||||
slib.setLang("eprotect", "tr", "secured", "Secured")
|
||||
|
||||
slib.setLang("eprotect", "tr", "check-ids", "ID'leri kontrol et")
|
||||
slib.setLang("eprotect", "tr", "correlate-ip", "IP adreslerini ilişkilendir")
|
||||
slib.setLang("eprotect", "tr", "family-share-check", "Aile paylaşımını kontrol et")
|
||||
|
||||
slib.setLang("eprotect", "tr", "ply-sent-invalid-data", "Bu oyuncu geçersiz data gönderdi!")
|
||||
slib.setLang("eprotect", "tr", "ply-failed-retrieving-data", "%s data alınamadı!")
|
||||
|
||||
slib.setLang("eprotect", "tr", "net-limit-desc", "Buradaki sayı, insanların hız sınırlandırılmadan önce bir saniye içinde sunucuya maksimum ağ kurma sayısıdır..")
|
||||
|
||||
slib.setLang("eprotect", "tr", "capture", "Screenshot")
|
||||
slib.setLang("eprotect", "tr", "check-ips", "Check IP(s)")
|
||||
slib.setLang("eprotect", "tr", "fetch-data", "Fetch Data")
|
||||
elseif SERVER then
|
||||
slib.setLang("eprotect", "tr", "invalid-player", "böyle bir oyuncu yok!")
|
||||
slib.setLang("eprotect", "tr", "kick-net-overflow", "Net ağına karşı kötü niyetli eylem gerçekleştirdiğinizden dolayı atıldınız!")
|
||||
slib.setLang("eprotect", "tr", "banned-net-overflow", "Net ağına karşı kötü niyetli eylem gerçekleştirdiğinizden dolayı yasaklandınız !")
|
||||
slib.setLang("eprotect", "tr", "banned-net-exploitation", "Net ağına karşı kötü niyetli eylem gerçekleştirdiğinizden dolayı yasaklandınız!")
|
||||
slib.setLang("eprotect", "tr", "kick-malicious-intent", "Kötü niyetli eylem gerçekleştirdiğinizden dolayı sunucudan atıldınız!")
|
||||
slib.setLang("eprotect", "tr", "banned-malicious-intent", "Kötü niyetli eylem gerçekleştirdiğinizden dolayı sunucudan yasaklandınız!")
|
||||
|
||||
slib.setLang("eprotect", "tr", "banned-exploit-attempt", "Exploit kullanmaya çalıştığın için sunucudan yasaklandın !")
|
||||
|
||||
slib.setLang("eprotect", "tr", "sc-timeout", "Şu kadar %s saniye beklemen gerek %s tekrar ekran görüntüsü almak için!")
|
||||
slib.setLang("eprotect", "tr", "sc-failed", "Şu kişiden ekran görüntüsü alınamadı %s, şüpheli!")
|
||||
|
||||
slib.setLang("eprotect", "tr", "has-family-share", "%s Aile paylaşımından oynuyor oyunu ödünç aldığı kişinin ID'si %s!")
|
||||
slib.setLang("eprotect", "tr", "no-family-share", "%s Aile paylaşımından oynamıyor!")
|
||||
slib.setLang("eprotect", "tr", "no-correlation", "Şu değer için hiç bir IP adresi ilişkilendirilemedi %s")
|
||||
end
|
||||
@@ -1,29 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local punished = {}
|
||||
|
||||
eProtect.logDetectionHandeler = function(ply, reason, info, type)
|
||||
if eProtect.data.general["bypassgroup"][ply:GetUserGroup()] or eProtect.config["disabledModules"]["detection_log"] then return end
|
||||
local sid, sid64 = ply:SteamID(), ply:SteamID64()
|
||||
|
||||
if eProtect.data.general["bypass_sids"][sid] or eProtect.data.general["bypass_sids"][sid64] then return end
|
||||
|
||||
if punished[sid] and CurTime() < punished[sid] then return end
|
||||
punished[sid] = CurTime() + eProtect.data.general.timeout + 1
|
||||
|
||||
local name, sid64 = ply:Nick(), ply:SteamID64()
|
||||
|
||||
eProtect.logDetection(name, sid64, reason, info, type)
|
||||
end
|
||||
|
||||
if eProtect.queueNetworking then
|
||||
eProtect.queueNetworking(nil, "punishmentLogging")
|
||||
end
|
||||
@@ -1,50 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.data = eProtect.data or {}
|
||||
eProtect.data.exploitPatcher = eProtect.data.exploitPatcher or {}
|
||||
|
||||
eProtect.patcher = eProtect.patcher or {}
|
||||
|
||||
local function addExploitPatch(str, func)
|
||||
eProtect.patcher[str] = func
|
||||
eProtect.data.exploitPatcher[str] = true
|
||||
end
|
||||
|
||||
addExploitPatch("start_wd_emp", function(ply)
|
||||
if IsValid(ply) and ply:IsPlayer() then
|
||||
local wep = ply:GetActiveWeapon()
|
||||
if !IsValid(wep) or wep:GetClass() ~= "weapon_hack_phone" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
addExploitPatch("gPrinters.removePrinter", function(ply)
|
||||
if IsValid(ply) and ply:IsPlayer() then
|
||||
return ply:IsSuperAdmin()
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("eP:PreNetworking", "eP:ExploitPatches", function(ply, netstring, len)
|
||||
if eProtect.patcher[netstring] and isfunction(eProtect.patcher[netstring]) and !eProtect.config["disabledModules"]["exploit_patcher"] then
|
||||
local result = eProtect.patcher[netstring](ply)
|
||||
|
||||
if result == false then
|
||||
eProtect.logDetectionHandeler(ply, "patched-exploit", netstring, 2)
|
||||
eProtect.punish(ply, 2, slib.getLang("eprotect", eProtect.config["language"], "banned-exploit-attempt"))
|
||||
return false end
|
||||
end
|
||||
end)
|
||||
|
||||
if eProtect.queueNetworking then
|
||||
eProtect.queueNetworking(nil, "exploitPatcher")
|
||||
end
|
||||
@@ -1,60 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.data = eProtect.data or {}
|
||||
eProtect.data.fakeNets = eProtect.data.fakeNets or {}
|
||||
|
||||
local generatedOnes = {}
|
||||
|
||||
eProtect.getRandUniqueNum = function()
|
||||
local rand = math.random(1, 999999)
|
||||
if generatedOnes[rand] then return eProtect.getRandUniqueNum() end
|
||||
generatedOnes[rand] = true
|
||||
|
||||
return rand
|
||||
end
|
||||
|
||||
eProtect.createFakeNets = function()
|
||||
if eProtect.config["disabledModules"]["fake_exploits"] then return end
|
||||
|
||||
local createdNets = 0
|
||||
local maxFakeNets = 3
|
||||
|
||||
local mixedTbl = {}
|
||||
|
||||
for k,v in pairs(eProtect.data.badNets) do
|
||||
mixedTbl[eProtect.getRandUniqueNum()] = k
|
||||
end
|
||||
|
||||
for k, netstring in pairs(mixedTbl) do
|
||||
local validateNet = tobool(util.NetworkStringToID(netstring))
|
||||
if validateNet then continue end
|
||||
createdNets = createdNets + 1
|
||||
|
||||
eProtect.data.fakeNets[netstring] = eProtect.data.fakeNets[netstring] or eProtect.data.badNets[netstring]
|
||||
eProtect.data.fakeNets[netstring].enabled = true
|
||||
util.AddNetworkString(netstring)
|
||||
|
||||
net.Receive(netstring, function(_, ply)
|
||||
eProtect.logDetectionHandeler(ply, "fake-exploit", netstring, 2)
|
||||
eProtect.punish(ply, 2, slib.getLang("eprotect", eProtect.config["language"], "banned-net-exploitation"))
|
||||
end)
|
||||
|
||||
if maxFakeNets > 0 and (createdNets >= maxFakeNets) then break end
|
||||
end
|
||||
|
||||
if eProtect.queueNetworking then
|
||||
eProtect.queueNetworking(nil, "fakeNets")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
eProtect.createFakeNets()
|
||||
@@ -1,13 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
hook.Add("eP:PostHTTP", "eP:HTTPLoggingHandeler", function(url, type)
|
||||
eProtect.logHTTP(url, type)
|
||||
end)
|
||||
@@ -1,36 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local function handleIPLoggin(ply, ip)
|
||||
local sid64 = ply:SteamID64()
|
||||
http.Fetch("http://ip-api.com/json/"..ip, function(json)
|
||||
json = util.JSONToTable(json)
|
||||
local result = json["countryCode"]
|
||||
|
||||
if !result then result = "N/A" end
|
||||
|
||||
eProtect.registerIP(sid64, ip, result)
|
||||
end, function()
|
||||
eProtect.registerIP(sid64, ip, "N/A")
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "eP:IPLogging", function(ply)
|
||||
if ply:IsBot() or eProtect.config["disabledModules"]["identifier"] then return end
|
||||
local ip = ""
|
||||
|
||||
for k,v in ipairs(string.ToTable(ply:IPAddress())) do
|
||||
if v == ":" then break end
|
||||
|
||||
ip = ip..v
|
||||
end
|
||||
|
||||
handleIPLoggin(ply, ip)
|
||||
end)
|
||||
@@ -1,78 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.data = eProtect.data or {}
|
||||
eProtect.data.netLimitation = eProtect.data.netLimitation or {}
|
||||
|
||||
for i = 1, 2048 do
|
||||
local netstring = util.NetworkIDToString(i)
|
||||
if !netstring then continue end
|
||||
if netstring and isstring(netstring) and eProtect.data.netLimitation[netstring] == nil and !eProtect.data.fakeNets[netstring] then
|
||||
local func = net.Receivers[string.lower(netstring)]
|
||||
if func then eProtect.data.netLimitation[netstring] = 0 end
|
||||
end
|
||||
end
|
||||
|
||||
if eProtect.queueNetworking then
|
||||
eProtect.queueNetworking(nil, "netLimitation")
|
||||
end
|
||||
|
||||
local generalCounter = {}
|
||||
local specificCounter = {}
|
||||
local timeout = {}
|
||||
|
||||
hook.Add("eP:PreNetworking", "eP:NetLimiter", function(ply, netstring, len)
|
||||
if !eProtect.data or !eProtect.data.general or eProtect.data.netLimitation[netstring] == -1 or eProtect.config["disabledModules"]["net_limiter"] then return end
|
||||
|
||||
if !eProtect.data.netLimitation[netstring] then
|
||||
local func = net.Receivers[string.lower(netstring)]
|
||||
if func then eProtect.data.netLimitation[netstring] = 0 end
|
||||
|
||||
eProtect.queueNetworking(nil, "netLimitation")
|
||||
end
|
||||
|
||||
local sid, sid64 = ply:SteamID(), ply:SteamID64()
|
||||
local specific = eProtect.data.netLimitation[netstring] ~= nil and eProtect.data.netLimitation[netstring] > 0 or false
|
||||
|
||||
specificCounter[sid] = specificCounter[sid] or {}
|
||||
|
||||
if !timeout[sid] then timeout[sid] = CurTime() end
|
||||
|
||||
if timeout[sid] and ((CurTime() - timeout[sid]) >= eProtect.data.general.timeout) then
|
||||
specificCounter[sid] = {}
|
||||
generalCounter[sid] = 0
|
||||
timeout[sid] = nil
|
||||
end
|
||||
|
||||
if specific then
|
||||
specificCounter[sid][netstring] = specificCounter[sid][netstring] or 0
|
||||
specificCounter[sid][netstring] = specificCounter[sid][netstring] + 1
|
||||
else
|
||||
generalCounter[sid] = generalCounter[sid] or 0
|
||||
generalCounter[sid] = generalCounter[sid] + 1
|
||||
end
|
||||
|
||||
local counter = specific and specificCounter[sid][netstring] or generalCounter[sid]
|
||||
local limit = specific and eProtect.data.netLimitation[netstring] or eProtect.data.general.ratelimit
|
||||
if limit > -1 and counter > limit and eProtect.data.general.overflowpunishment > 0 and !eProtect.data.general["bypassgroup"][ply:GetUserGroup()] and !eProtect.data.general["bypass_sids"][sid] and !eProtect.data.general["bypass_sids"][sid64] then
|
||||
if eProtect.data.general["whitelistergroup"][ply:GetUserGroup()] then
|
||||
eProtect.data.netLimitation[netstring] = -1
|
||||
eProtect.queueNetworking(nil, "netLimitation")
|
||||
return end
|
||||
|
||||
eProtect.logDetectionHandeler(ply, "net-overflow", netstring, eProtect.data.general.overflowpunishment)
|
||||
if eProtect.data.general.overflowpunishment <= 2 then
|
||||
eProtect.punish(ply, eProtect.data.general.overflowpunishment, slib.getLang("eprotect", eProtect.config["language"], eProtect.data.general.overflowpunishment == 1 and "kick-net-overflow" or "banned-net-overflow"))
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end)
|
||||
@@ -1,31 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.data = eProtect.data or {}
|
||||
eProtect.data.netLogging = eProtect.data.netLogging or {}
|
||||
|
||||
hook.Add("eP:PostNetworking", "eP:LogNetworking", function(ply, net, len)
|
||||
if !net or len == nil or eProtect.config["disabledModules"]["net_logger"] then return end
|
||||
eProtect.data.netLogging[net] = eProtect.data.netLogging[net] or {called = 0, len = 0, playercalls = {}}
|
||||
|
||||
eProtect.data.netLogging[net].called = eProtect.data.netLogging[net].called + 1
|
||||
eProtect.data.netLogging[net].len = eProtect.data.netLogging[net].len + len
|
||||
|
||||
if IsValid(ply) and ply:IsPlayer() then
|
||||
local sid = ply:SteamID()
|
||||
eProtect.data.netLogging[net].playercalls[sid] = eProtect.data.netLogging[net].playercalls[sid] or 0
|
||||
eProtect.data.netLogging[net].playercalls[sid] = eProtect.data.netLogging[net].playercalls[sid] + 1
|
||||
end
|
||||
|
||||
if eProtect.queueNetworking then
|
||||
eProtect.queueNetworking(nil, "netLogging")
|
||||
end
|
||||
end)
|
||||
@@ -1,575 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.queneData = eProtect.queneData or {}
|
||||
eProtect.saveQueue = eProtect.saveQueue or {}
|
||||
|
||||
eProtect.data = eProtect.data or {}
|
||||
eProtect.data.disabled = eProtect.data.disabled or {}
|
||||
|
||||
local ignoreSaving = {
|
||||
["fakeNets"] = true,
|
||||
["netLogging"] = true,
|
||||
["exploitPatcher"] = true
|
||||
}
|
||||
|
||||
util.AddNetworkString("eP:Handeler")
|
||||
|
||||
local function openMenu(ply)
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(2, 3)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
local convertedTbl
|
||||
|
||||
convertedTbl = function(tbl)
|
||||
local converted_tbl = {}
|
||||
|
||||
for k,v in pairs(tbl) do
|
||||
if istable(v) then v = convertedTbl(v) end
|
||||
|
||||
local isSID = util.SteamIDFrom64(k) != "STEAM_0:0:0"
|
||||
|
||||
if isSID then
|
||||
converted_tbl["sid64_"..k] = v
|
||||
else
|
||||
converted_tbl[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return converted_tbl
|
||||
end
|
||||
|
||||
local function networkData(ply, data, specific)
|
||||
if !data then return end
|
||||
local data = util.TableToJSON(convertedTbl(data))
|
||||
|
||||
data = util.Compress(data)
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(1, 3)
|
||||
net.WriteUInt(#data, 32)
|
||||
net.WriteData(data, #data)
|
||||
|
||||
if specific then
|
||||
net.WriteString(specific)
|
||||
end
|
||||
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
eProtect.hasPermission = function(ply, specific)
|
||||
return eProtect.config["permission"][ply:GetUserGroup()]
|
||||
end
|
||||
|
||||
local punished = {}
|
||||
|
||||
eProtect.getData = function(specific)
|
||||
local data = file.Read("eprotect/data.json", "DATA")
|
||||
|
||||
if !data then return end
|
||||
|
||||
data = util.JSONToTable(data)
|
||||
|
||||
if specific then
|
||||
data = data[specific]
|
||||
end
|
||||
|
||||
for k,v in pairs(data) do
|
||||
eProtect.data[k] = v
|
||||
end
|
||||
|
||||
return table.Copy(data)
|
||||
end
|
||||
|
||||
eProtect.dataVerification = function()
|
||||
local data = eProtect.getData()
|
||||
data = data or {}
|
||||
|
||||
data["general"] = data["general"] or {}
|
||||
|
||||
for k,v in pairs(eProtect.BaseConfig) do
|
||||
if data["general"][k] then continue end
|
||||
data["general"][k] = v[1]
|
||||
end
|
||||
|
||||
for k,v in pairs(eProtect.data) do
|
||||
if ignoreSaving[k] or k == "general" then continue end
|
||||
data[k] = v
|
||||
end
|
||||
|
||||
file.CreateDir("eprotect")
|
||||
file.Write("eprotect/data.json", util.TableToJSON(data))
|
||||
|
||||
eProtect.getData()
|
||||
eProtect.queueNetworking()
|
||||
end
|
||||
|
||||
eProtect.saveData = function()
|
||||
file.CreateDir("eprotect")
|
||||
|
||||
local data = table.Copy(eProtect.data)
|
||||
|
||||
for k, v in pairs(data) do
|
||||
if ignoreSaving[k] then data[k] = nil end
|
||||
end
|
||||
|
||||
file.Write("eprotect/data.json", util.TableToJSON(data))
|
||||
end
|
||||
|
||||
eProtect.canNetwork = function(ply, netstring)
|
||||
if !IsValid(ply) or !ply:IsPlayer() then return end
|
||||
if (punished[ply:SteamID()] or eProtect.data.disabled[ply:SteamID()] or eProtect.data.general["disable-all-networking"]) and (netstring ~= "eP:Handeler") then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
eProtect.punish = function(ply, type, msg, duration)
|
||||
if eProtect.data.general["bypassgroup"][ply:GetUserGroup()] or eProtect.data.general["bypass_sids"][ply:SteamID()] or eProtect.data.general["bypass_sids"][ply:SteamID64()] then return end
|
||||
msg = eProtect.config["prefix"]..msg
|
||||
|
||||
punished[ply:SteamID()] = true
|
||||
|
||||
slib.punish(ply, type, msg, duration)
|
||||
end
|
||||
|
||||
eProtect.networkData = function(ply)
|
||||
if eProtect.queneData[ply:SteamID()] then
|
||||
for k,v in pairs(eProtect.queneData[ply:SteamID()]) do
|
||||
networkData(ply, eProtect.data[k], k)
|
||||
eProtect.queneData[ply:SteamID()][k] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function registerQuene(ply, specific)
|
||||
if specific then
|
||||
eProtect.queneData[ply:SteamID()] = eProtect.queneData[ply:SteamID()] and eProtect.queneData[ply:SteamID()] or {}
|
||||
eProtect.queneData[ply:SteamID()][specific] = true
|
||||
else
|
||||
for k,v in pairs(eProtect.data) do
|
||||
registerQuene(ply, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
eProtect.queueNetworking = function(ply, specific)
|
||||
if ply then
|
||||
registerQuene(ply, specific)
|
||||
else
|
||||
for k,v in pairs(player.GetAll()) do
|
||||
if !IsValid(v) then continue end
|
||||
registerQuene(v, specific)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local screenshotRequested = {}
|
||||
local idRequested = {}
|
||||
local dataRequested = {}
|
||||
local limitSC = {}
|
||||
|
||||
local function requestData(ply, target, type)
|
||||
local data
|
||||
|
||||
if type == 1 then
|
||||
local sid = target:SteamID()
|
||||
if limitSC[sid] and CurTime() - limitSC[sid] < 10 then
|
||||
slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "sc-timeout", math.Round(10 - (CurTime() - limitSC[sid])), target:Nick()), ply)
|
||||
return end
|
||||
|
||||
limitSC[sid] = CurTime()
|
||||
|
||||
data = screenshotRequested
|
||||
elseif type == 2 then
|
||||
data = idRequested
|
||||
elseif type == 3 then
|
||||
data = dataRequested
|
||||
end
|
||||
|
||||
if data[target] then return end
|
||||
|
||||
data[target] = ply
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(3, 3)
|
||||
net.WriteUInt(type, 2)
|
||||
net.WriteBool(false)
|
||||
net.Send(target)
|
||||
|
||||
timer.Simple(10, function()
|
||||
if !target or !ply then return end
|
||||
if data[target] then
|
||||
data[target] = nil
|
||||
slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "sc-failed", target:Nick()), ply)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "eP:NetworkingQueuer", function(ply)
|
||||
eProtect.queueNetworking(ply)
|
||||
local sid = ply:SteamID()
|
||||
if punished[sid] then punished[sid] = nil end
|
||||
end)
|
||||
|
||||
local function verifyBannedAlt(ply, sid64, type)
|
||||
sid64 = sid64 or ply:SteamID64()
|
||||
local isBanned = slib.isBanned(sid64, function(banned, sid) if banned then if IsValid(ply) then slib.punish(ply, type, eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "punished-alt")) end end end)
|
||||
|
||||
if isBanned then
|
||||
slib.punish(ply, type, eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "punished-alt"))
|
||||
end
|
||||
|
||||
return isBanned
|
||||
end
|
||||
|
||||
local settingConverter = { // Its reverted on clientside
|
||||
[1] = 3,
|
||||
[2] = 1,
|
||||
[3] = 2
|
||||
}
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "eP:AutomaticChecks", function(ply)
|
||||
local automatic_identifier = tonumber(eProtect.data.general["automatic-identifier"]) or 1
|
||||
|
||||
timer.Simple(1.5, function() -- Giving time to set usergroup.
|
||||
if !IsValid(ply) or !ply:IsPlayer() or ply:IsBot() or eProtect.data.general["bypassgroup"][ply:GetUserGroup()] or eProtect.data.general["bypass_sids"][ply:SteamID64()] or eProtect.data.general["bypass_sids"][ply:SteamID()] then return end
|
||||
|
||||
if eProtect.data.general["block-vpn"] and !eProtect.data.general["bypass-vpn"][ply:GetUserGroup()] and !eProtect.data.general["bypass-vpn"][ply:SteamID64()] then
|
||||
local ip = ""
|
||||
|
||||
for k,v in ipairs(string.ToTable(ply:IPAddress())) do
|
||||
if v == ":" then break end
|
||||
|
||||
ip = ip..v
|
||||
end
|
||||
|
||||
http.Fetch("https://proxycheck.io/v2/"..ip.."?vpn=1", function(result)
|
||||
result = result and util.JSONToTable(result)
|
||||
|
||||
if result[ip] and result[ip].proxy == "yes" then
|
||||
ply:Kick(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "vpn-blocked"))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if automatic_identifier > 0 then
|
||||
eProtect.correlateIP(ply, function(result)
|
||||
local correlatedIPs = result
|
||||
local plysid64, ownerplysid64 = ply:SteamID64(), ply:OwnerSteamID64()
|
||||
local familyShare = ply:SteamID64() ~= ply:OwnerSteamID64()
|
||||
local detections = !familyShare and !table.IsEmpty(correlatedIPs)
|
||||
local altsDetected = {}
|
||||
|
||||
if detections then
|
||||
local detect_type
|
||||
detections = ""
|
||||
if correlatedIPs and istable(correlatedIPs) and !table.IsEmpty(correlatedIPs) then
|
||||
detect_type = "correlated-ip"
|
||||
|
||||
local foundAlts = {}
|
||||
for k,v in ipairs(correlatedIPs) do
|
||||
table.insert(altsDetected, v.sid64)
|
||||
end
|
||||
|
||||
detections = slib.getLang("eprotect", eProtect.config["language"], "correlated-ip")
|
||||
end
|
||||
|
||||
if familyShare then
|
||||
detect_type = "family-share"
|
||||
|
||||
detections = detections == "" and slib.getLang("eprotect", eProtect.config["language"], "family-share") or detections.." "..slib.getLang("eprotect", eProtect.config["language"], "and").." "..slib.getLang("eprotect", eProtect.config["language"], "family-share")
|
||||
table.insert(altsDetected, ownerplysid64)
|
||||
end
|
||||
|
||||
if detections ~= "" then
|
||||
local doneAction
|
||||
|
||||
if automatic_identifier == 1 then
|
||||
for k, v in ipairs(player.GetAll()) do
|
||||
if eProtect.data.general["notification-groups"][v:GetUserGroup()] then
|
||||
slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "auto-detected-alt", ply:Nick(), detections), ply)
|
||||
end
|
||||
end
|
||||
|
||||
doneAction = true
|
||||
elseif automatic_identifier == 2 then
|
||||
for k,v in ipairs(altsDetected) do
|
||||
doneAction = doneAction or verifyBannedAlt(ply, v, 1)
|
||||
end
|
||||
elseif automatic_identifier == 3 then
|
||||
for k,v in ipairs(altsDetected) do
|
||||
doneAction = doneAction or verifyBannedAlt(ply, v, 2)
|
||||
end
|
||||
end
|
||||
|
||||
if doneAction then
|
||||
eProtect.logDetectionHandeler(ply, "alt-detection", slib.getLang("eprotect", eProtect.config["language"], detect_type), settingConverter[automatic_identifier])
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSay", "eP:OpenMenu", function(ply, text, public)
|
||||
if eProtect.config["command"] == string.lower(text) then
|
||||
if !eProtect.hasPermission(ply) then
|
||||
return text
|
||||
end
|
||||
|
||||
eProtect.networkData(ply)
|
||||
|
||||
openMenu(ply)
|
||||
return ""
|
||||
end
|
||||
end )
|
||||
|
||||
hook.Add("eP:PreNetworking", "eP:Restrictions", function(ply, netstring, len)
|
||||
if !eProtect.canNetwork(ply, netstring) then return false end
|
||||
if len >= 512000 then eProtect.logDetectionHandeler(ply, "net-overflow", netstring, 1) eProtect.punish(ply, 1, slib.getLang("eprotect", eProtect.config["language"], "kick-net-overflow")) return false end
|
||||
end)
|
||||
|
||||
hook.Add("eP:PreHTTP", "eP:PreventBlockedHTTP", function(url)
|
||||
if eProtect.data.general["httpfocusedurls"] then
|
||||
return eProtect.data.general["httpfocusedurlsisblacklist"] == !tobool(eProtect.data.general["httpfocusedurls"][url])
|
||||
end
|
||||
end)
|
||||
|
||||
timer.Create("eP:SaveCache", eProtect.config["process-save-queue"], 0, function()
|
||||
if !eProtect.saveQueue then return end
|
||||
eProtect.saveData()
|
||||
|
||||
eProtect.saveQueue = nil
|
||||
end)
|
||||
|
||||
net.Receive("eP:Handeler", function(len, ply)
|
||||
local gateway = net.ReadBit()
|
||||
local action = net.ReadUInt(2)
|
||||
|
||||
if tobool(gateway) then
|
||||
if !eProtect.hasPermission(ply) then return end
|
||||
|
||||
if action == 0 then
|
||||
local id = net.ReadUInt(1)
|
||||
local page = net.ReadUInt(15)
|
||||
local search = net.ReadString()
|
||||
|
||||
if id == 0 then
|
||||
eProtect.requestHTTPLog(ply, page, search)
|
||||
elseif id == 1 then
|
||||
eProtect.requestDetectionLog(ply, page, search)
|
||||
end
|
||||
elseif action == 1 then
|
||||
local specific = net.ReadUInt(3)
|
||||
local strings = {}
|
||||
|
||||
for i=1,specific do
|
||||
strings[i] = net.ReadString()
|
||||
end
|
||||
|
||||
local statement = net.ReadUInt(2)
|
||||
local data
|
||||
|
||||
if statement == 1 then
|
||||
data = net.ReadBool()
|
||||
elseif statement == 2 then
|
||||
data = net.ReadInt(32)
|
||||
elseif statement == 3 then
|
||||
local chunk = net.ReadUInt(32)
|
||||
data = net.ReadData(chunk)
|
||||
|
||||
data = util.Decompress(data)
|
||||
data = util.JSONToTable(data)
|
||||
|
||||
local converted_tbl = {}
|
||||
|
||||
for k, v in pairs(data) do
|
||||
if string.sub(k, 1, 6) == "sid64_" then
|
||||
local sid64 = string.sub(k, 7, #k)
|
||||
|
||||
if util.SteamIDFrom64(sid64) != "STEAM_0:0:0" then
|
||||
k = sid64
|
||||
end
|
||||
end
|
||||
|
||||
converted_tbl[k] = v
|
||||
end
|
||||
|
||||
data = converted_tbl
|
||||
end
|
||||
|
||||
local finaldestination = eProtect.data
|
||||
for k,v in ipairs(strings) do
|
||||
finaldestination = finaldestination[v]
|
||||
if k >= (#strings - 1) then break end
|
||||
end
|
||||
|
||||
finaldestination[strings[#strings]] = data
|
||||
|
||||
eProtect.saveQueue = true
|
||||
eProtect.queueNetworking(nil, strings[1])
|
||||
elseif action == 2 then
|
||||
local subaction = net.ReadUInt(3)
|
||||
local target = net.ReadUInt(14)
|
||||
|
||||
target = Entity(target)
|
||||
|
||||
if !IsValid(target) or !target:IsPlayer() then slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "invalid-player"), ply) return end
|
||||
|
||||
local sid = target:SteamID()
|
||||
|
||||
if subaction == 1 then
|
||||
eProtect.data.disabled[sid] = net.ReadBool()
|
||||
eProtect.queueNetworking(nil, "disabled")
|
||||
elseif subaction == 2 then
|
||||
requestData(ply, target, net.ReadUInt(2))
|
||||
elseif subaction == 3 then
|
||||
local bit = net.ReadBit()
|
||||
if tobool(bit) then
|
||||
eProtect.correlateIP(target, function(result)
|
||||
if !IsValid(target) or !IsValid(ply) then return end
|
||||
if table.IsEmpty(result) then slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "no-correlation", target:Nick()), ply) return end
|
||||
|
||||
result = util.TableToJSON(result)
|
||||
result = util.Base64Encode(result)
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(4,3)
|
||||
net.WriteUInt(target:EntIndex(), 14)
|
||||
net.WriteString(result)
|
||||
net.WriteBit(1)
|
||||
net.Send(ply)
|
||||
end)
|
||||
else
|
||||
eProtect.showIPs(target, ply)
|
||||
end
|
||||
elseif subaction == 4 then
|
||||
local sid64 = target:SteamID64()
|
||||
local ownersid64 = target:OwnerSteamID64()
|
||||
|
||||
if sid64 == ownersid64 then
|
||||
slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "no-family-share", target:Nick()), ply)
|
||||
else
|
||||
slib.notify(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "has-family-share", target:Nick(), ownersid64), ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if action == 1 then
|
||||
local subaction = net.ReadUInt(2)
|
||||
|
||||
local data
|
||||
|
||||
if subaction == 1 then
|
||||
data = screenshotRequested
|
||||
elseif subaction == 2 then
|
||||
data = idRequested
|
||||
elseif subaction == 3 then
|
||||
data = dataRequested
|
||||
end
|
||||
|
||||
if !data[ply] then if eProtect.config["punishMaliciousIntent"] then eProtect.punish(ply, 1, slib.getLang("eprotect", eProtect.config["language"], "kick-malicious-intent")) end return end
|
||||
local target = data[ply]
|
||||
data[ply] = nil
|
||||
|
||||
local id
|
||||
|
||||
if subaction == 3 then
|
||||
local chunk = net.ReadUInt(32)
|
||||
id = net.ReadData(chunk)
|
||||
else
|
||||
id = net.ReadString()
|
||||
end
|
||||
|
||||
if !id or id == "" then
|
||||
if eProtect.config["punishMaliciousIntent"] then
|
||||
eProtect.punish(ply, 1, slib.getLang("eprotect", eProtect.config["language"], "kick-malicious-intent"))
|
||||
end
|
||||
return end
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(3, 3)
|
||||
net.WriteUInt(subaction, 2)
|
||||
net.WriteUInt(ply:EntIndex(), 14)
|
||||
net.WriteBool(true)
|
||||
|
||||
if subaction == 3 then
|
||||
local chunk = #id
|
||||
net.WriteUInt(chunk, 32)
|
||||
net.WriteData(id, chunk)
|
||||
else
|
||||
net.WriteString(id)
|
||||
end
|
||||
|
||||
net.Send(target)
|
||||
elseif action == 2 then
|
||||
local menu = net.ReadUInt(2)
|
||||
local menus = {
|
||||
[1] = "Loki",
|
||||
[2] = "Exploit City"
|
||||
}
|
||||
|
||||
eProtect.logDetectionHandeler(ply, "exploit-menu", menus[menu], 2)
|
||||
eProtect.punish(ply, 2, slib.getLang("eprotect", eProtect.config["language"], "banned-exploit-menu"))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("eP:SQLConnected", "eP:TransferOldIPs", function()
|
||||
local files = file.Find("eprotect/ips/*", "DATA")
|
||||
for k,v in pairs(files) do
|
||||
local sid64 = string.gsub(v, ".json", "")
|
||||
|
||||
local ips = file.Read("eprotect/ips/"..v, "DATA")
|
||||
ips = util.JSONToTable(ips)
|
||||
if !ips then continue end
|
||||
|
||||
for ip, data in pairs(ips) do
|
||||
eProtect.registerIP(sid64, ip, data[1], data[2])
|
||||
end
|
||||
|
||||
file.Delete("eprotect/ips/"..v)
|
||||
end
|
||||
file.Delete("eprotect/ips")
|
||||
|
||||
local save = false
|
||||
|
||||
if eProtect.data.httpLogging then
|
||||
for url, v in pairs(eProtect.data.httpLogging) do
|
||||
eProtect.logHTTP(url, v.type, v.called)
|
||||
end
|
||||
|
||||
eProtect.data.httpLogging = nil
|
||||
|
||||
save = true
|
||||
end
|
||||
|
||||
if eProtect.data.punishmentLogging then
|
||||
for i = #eProtect.data.punishmentLogging, 1, -1 do
|
||||
local data = eProtect.data.punishmentLogging[i]
|
||||
|
||||
eProtect.logDetection(data.ply, "", data.reason, data.info, data.type)
|
||||
end
|
||||
|
||||
eProtect.data.punishmentLogging = nil
|
||||
|
||||
save = true
|
||||
end
|
||||
|
||||
if save then
|
||||
eProtect.saveData()
|
||||
end
|
||||
end)
|
||||
|
||||
eProtect.dataVerification()
|
||||
@@ -1,67 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
eProtect.overrides = eProtect.overrides or {}
|
||||
|
||||
if !eProtect.overrides["net.Incoming"] then
|
||||
eProtect.overrides["net.Incoming"] = true
|
||||
function net.Incoming( len, client )
|
||||
local i = net.ReadHeader()
|
||||
local strName = util.NetworkIDToString( i )
|
||||
|
||||
if ( !strName ) then return end
|
||||
|
||||
local func = net.Receivers[ strName:lower() ]
|
||||
if ( !func ) then return end
|
||||
|
||||
len = len - 16
|
||||
|
||||
local pre = hook.Run("eP:PreNetworking", client, strName, len)
|
||||
|
||||
if pre == false then return end
|
||||
|
||||
func( len, client )
|
||||
|
||||
hook.Run("eP:PostNetworking", client, strName, len)
|
||||
end
|
||||
end
|
||||
|
||||
if !eProtect.config["disablehttplogging"] and ((!VC and !XEON and !mLib) or eProtect.config["ignoreDRM"]) then
|
||||
if !eProtect.overrides["http.Fetch"] then
|
||||
eProtect.overrides["http.Fetch"] = true
|
||||
local oldFetch = http.Fetch
|
||||
function http.Fetch(...)
|
||||
local args = {...}
|
||||
local result = hook.Run("eP:PreHTTP", args[1], "fetch")
|
||||
|
||||
if result == false then return end
|
||||
|
||||
oldFetch(...)
|
||||
|
||||
hook.Run("eP:PostHTTP", args[1], "fetch")
|
||||
end
|
||||
end
|
||||
|
||||
if !eProtect.overrides["http.Post"] then
|
||||
eProtect.overrides["http.Post"] = true
|
||||
local oldPost = http.Post
|
||||
function http.Post(...)
|
||||
local args = {...}
|
||||
local result = hook.Run("eP:PreHTTP", args[1], "post")
|
||||
|
||||
if result == false then return end
|
||||
|
||||
oldPost(...)
|
||||
|
||||
hook.Run("eP:PostHTTP", args[1], "post")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,245 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local query, db
|
||||
local escape_str = function(str) return SQLStr(str, true) end
|
||||
|
||||
local create_queries = {
|
||||
[1] = [[CREATE TABLE IF NOT EXISTS eprotect_ips(
|
||||
id INTEGER PRIMARY KEY %s,
|
||||
sid64 CHAR(17),
|
||||
ip CHAR(15),
|
||||
country CHAR(3),
|
||||
logged_time INTEGER DEFAULT 0
|
||||
)]],
|
||||
[2] = [[CREATE TABLE IF NOT EXISTS eprotect_detections(
|
||||
id INTEGER PRIMARY KEY %s,
|
||||
name CHAR(32),
|
||||
sid64 CHAR(17),
|
||||
reason CHAR(32),
|
||||
info CHAR(32),
|
||||
type INTEGER DEFAULT 0,
|
||||
logged_time INTEGER DEFAULT 0
|
||||
)]],
|
||||
[3] = [[CREATE TABLE IF NOT EXISTS eprotect_http(
|
||||
id INTEGER PRIMARY KEY %s,
|
||||
link CHAR(64),
|
||||
type CHAR(6),
|
||||
called INTEGER DEFAULT 0
|
||||
)]]
|
||||
}
|
||||
|
||||
local function makeTables()
|
||||
for i = 1, #create_queries do
|
||||
query(string.format(create_queries[i], eProtect.config["storage_type"] == "sql_local" and "AUTOINCREMENT" or "AUTO_INCREMENT"))
|
||||
end
|
||||
end
|
||||
|
||||
if eProtect.config["storage_type"] == "mysql" then
|
||||
require("mysqloo")
|
||||
|
||||
query = function() end
|
||||
|
||||
local dbinfo = eProtect.config["mysql_info"]
|
||||
|
||||
db = mysqloo.connect(dbinfo.host, dbinfo.username, dbinfo.password, dbinfo.database, dbinfo.port)
|
||||
|
||||
function db:onConnected()
|
||||
print(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "mysql_successfull"))
|
||||
|
||||
query = function(str, func)
|
||||
local q = db:query(str)
|
||||
q.onSuccess = function(_, data)
|
||||
if func then
|
||||
func(data)
|
||||
end
|
||||
end
|
||||
|
||||
q.onError = function(_, err) end
|
||||
|
||||
q:start()
|
||||
end
|
||||
|
||||
escape_str = function(str) return db:escape(tostring(str)) end
|
||||
|
||||
makeTables()
|
||||
|
||||
hook.Run("eP:SQLConnected")
|
||||
end
|
||||
|
||||
function db:onConnectionFailed(err)
|
||||
print(eProtect.config["prefix"]..slib.getLang("eprotect", eProtect.config["language"], "mysql_failed"))
|
||||
print( "Error:", err )
|
||||
end
|
||||
|
||||
db:connect()
|
||||
else
|
||||
local oldFunc = sql.Query
|
||||
query = function(str, func)
|
||||
local result = oldFunc(str)
|
||||
|
||||
if func then
|
||||
func(result)
|
||||
end
|
||||
end
|
||||
|
||||
makeTables()
|
||||
end
|
||||
|
||||
local function handleCallbacksCorrelation(parent_tbl, correlated, callback)
|
||||
for k,v in ipairs(parent_tbl) do
|
||||
if !v then return end
|
||||
end
|
||||
|
||||
if !callback then return end
|
||||
callback(correlated or {})
|
||||
end
|
||||
|
||||
eProtect.correlateIP = function(target, callback)
|
||||
if !IsValid(target) then return end
|
||||
|
||||
local sid64 = target:SteamID64()
|
||||
local tbl = {}
|
||||
query("SELECT * FROM eprotect_ips WHERE sid64 = '"..sid64.."'", function(result)
|
||||
if result and result[1] then
|
||||
local parent_tbl = {}
|
||||
for k, v in ipairs(result) do
|
||||
parent_tbl[k] = false
|
||||
end
|
||||
|
||||
for key, plydata in ipairs(result) do
|
||||
query("SELECT * FROM eprotect_ips WHERE ip = '"..(plydata.ip).."'", function(result)
|
||||
if result and result[1] then
|
||||
for k,v in ipairs(result) do
|
||||
if v.sid64 == sid64 then continue end
|
||||
table.insert(tbl, {sid64 = v.sid64, ip = v.ip})
|
||||
end
|
||||
end
|
||||
|
||||
parent_tbl[key] = true
|
||||
|
||||
handleCallbacksCorrelation(parent_tbl, tbl, callback)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
eProtect.showIPs = function(target, ply)
|
||||
local sid64 = target:SteamID64()
|
||||
|
||||
query("SELECT * FROM eprotect_ips WHERE sid64 = '"..sid64.."'", function(result)
|
||||
if !IsValid(target) or !IsValid(ply) or !result or !result[1] then return end
|
||||
|
||||
result = util.TableToJSON(result)
|
||||
result = util.Base64Encode(result)
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(4,3)
|
||||
net.WriteUInt(target:EntIndex(), 14)
|
||||
net.WriteString(result)
|
||||
net.WriteBit(0)
|
||||
net.Send(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
eProtect.registerIP = function(sid64, ip, country, time)
|
||||
query("SELECT * FROM eprotect_ips WHERE ip = '"..ip.."' AND sid64 = '"..sid64.."'", function(result)
|
||||
if result and result[1] then return end
|
||||
query(string.format("INSERT INTO eprotect_ips(ip, sid64, country, logged_time) VALUES('%s', %s, '%s', %s)", escape_str(ip), sid64, escape_str(country), time or os.time()))
|
||||
end)
|
||||
end
|
||||
|
||||
eProtect.logDetection = function(name, sid64, reason, info, type)
|
||||
query(string.format("INSERT INTO eprotect_detections(name, sid64, reason, info, type, logged_time) VALUES('%s', '%s', '%s', '%s', '%s', %s)", escape_str(name), escape_str(sid64), escape_str(reason), escape_str(info), escape_str(type), os.time()))
|
||||
end
|
||||
|
||||
eProtect.logHTTP = function(link, type, called)
|
||||
link = escape_str(link)
|
||||
|
||||
query("SELECT * FROM eprotect_http WHERE link = '"..link.."'", function(result)
|
||||
if result and result[1] then
|
||||
query("UPDATE eprotect_http SET called = "..(result[1].called + 1).." WHERE link = '"..link.."'")
|
||||
return end
|
||||
|
||||
query(string.format("INSERT INTO eprotect_http(link, type, called) VALUES('%s', '%s', "..(tonumber(called) or 1)..")", link, escape_str(type)))
|
||||
end)
|
||||
end
|
||||
|
||||
local function networkData(ply, data, id)
|
||||
local compressed = util.Compress(util.TableToJSON(data))
|
||||
|
||||
net.Start("eP:Handeler")
|
||||
net.WriteUInt(5, 3)
|
||||
net.WriteUInt(id, 1)
|
||||
net.WriteUInt(#compressed, 32)
|
||||
net.WriteData(compressed, #compressed)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
local http_cd, detection_cd = {}, {}
|
||||
|
||||
eProtect.requestHTTPLog = function(ply, page, search)
|
||||
if http_cd[ply] and http_cd[ply] > CurTime() then return end
|
||||
http_cd[ply] = CurTime() + .1
|
||||
|
||||
search = search ~= "" and escape_str(search) or nil
|
||||
|
||||
local perpage, pageCount = 20, 1
|
||||
local start = perpage * ((tonumber(page) or 1) - 1)
|
||||
local data = {}
|
||||
|
||||
local search_str = search and " WHERE (link LIKE '%"..search.."%')" or ""
|
||||
|
||||
query("SELECT COUNT(id) FROM eprotect_http"..search_str, function(pageresult)
|
||||
if pageresult and pageresult[1] and pageresult[1]["COUNT(id)"] then
|
||||
data.pageCount = math.max(math.ceil((pageresult[1]["COUNT(id)"] or 0) / perpage), 1)
|
||||
end
|
||||
|
||||
data.page = page
|
||||
|
||||
query("SELECT * FROM eprotect_http "..search_str.." LIMIT "..start..", "..perpage, function(result)
|
||||
data.result = result
|
||||
|
||||
networkData(ply, data, 0)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
eProtect.requestDetectionLog = function(ply, page, search)
|
||||
if detection_cd[ply] and detection_cd[ply] > CurTime() then return end
|
||||
detection_cd[ply] = CurTime() + .1
|
||||
|
||||
search = search ~= "" and escape_str(search) or nil
|
||||
|
||||
local perpage, pageCount = 20, 1
|
||||
local start = perpage * ((tonumber(page) or 1) - 1)
|
||||
local data = {}
|
||||
|
||||
local search_str = search and " WHERE (sid64 LIKE '%"..search.."%' OR name LIKE '%"..search.."%') " or ""
|
||||
|
||||
query("SELECT COUNT(id) FROM eprotect_detections"..search_str, function(pageresult)
|
||||
if pageresult and pageresult[1] and pageresult[1]["COUNT(id)"] then
|
||||
data.pageCount = math.max(math.ceil((pageresult[1]["COUNT(id)"] or 0) / perpage), 1)
|
||||
end
|
||||
|
||||
data.page = page
|
||||
|
||||
query("SELECT * FROM eprotect_detections"..search_str.." ORDER BY id DESC LIMIT "..start..", "..perpage, function(result)
|
||||
data.result = result
|
||||
|
||||
networkData(ply, data, 1)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
if eProtect.config["storage_type"] == "sql_local" then
|
||||
hook.Run("eP:SQLConnected")
|
||||
end
|
||||
@@ -1,109 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
------------------------------------------------------
|
||||
-- NO NOT TOUCH ANYTHING IN HERE!!!!!!!!!
|
||||
------------------------------------------------------
|
||||
|
||||
eProtect = eProtect or {}
|
||||
|
||||
eProtect.BaseConfig = eProtect.BaseConfig or {}
|
||||
|
||||
eProtect.BaseConfig["disable-all-networking"] = {false, 1}
|
||||
|
||||
eProtect.BaseConfig["automatic-identifier"] = {1, 2, {min = 0, max = 3}}
|
||||
|
||||
eProtect.BaseConfig["block-vpn"] = {false, 3}
|
||||
|
||||
eProtect.BaseConfig["bypass-vpn"] = {{["76561198002319944"] = true}, 4, function()
|
||||
local list = {}
|
||||
|
||||
for k,v in ipairs(player.GetAll()) do
|
||||
local sid64 = v:SteamID64()
|
||||
if !sid64 then continue end
|
||||
list[sid64] = true
|
||||
end
|
||||
|
||||
if CAMI and CAMI.GetUsergroups then
|
||||
for k,v in pairs(CAMI.GetUsergroups()) do
|
||||
list[k] = true
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end}
|
||||
|
||||
|
||||
eProtect.BaseConfig["notification-groups"] = {{["superadmin"] = true}, 5, CAMI and CAMI.GetUsergroups and function() local tbl = {} for k,v in pairs(CAMI.GetUsergroups()) do tbl[k] = true end return tbl end or {}}
|
||||
|
||||
eProtect.BaseConfig["ratelimit"] = {500, 6, {min = -1, max = 100000}}
|
||||
|
||||
eProtect.BaseConfig["timeout"] = {3, 7, {min = 0, max = 5000}}
|
||||
|
||||
eProtect.BaseConfig["overflowpunishment"] = {2, 8, {min = 0, max = 3}}
|
||||
|
||||
eProtect.BaseConfig["whitelistergroup"] = {{}, 9, function()
|
||||
local list = {}
|
||||
|
||||
if CAMI and CAMI.GetUsergroups then
|
||||
for k,v in pairs(CAMI.GetUsergroups()) do
|
||||
list[k] = true
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end}
|
||||
|
||||
eProtect.BaseConfig["bypassgroup"] = {{}, 10, function()
|
||||
local list = {
|
||||
["superadmin"] = true,
|
||||
["owner"] = true
|
||||
}
|
||||
|
||||
if CAMI and CAMI.GetUsergroups then
|
||||
for k,v in pairs(CAMI.GetUsergroups()) do
|
||||
list[k] = true
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end}
|
||||
|
||||
eProtect.BaseConfig["bypass_sids"] = {{["76561198002319944"] = true}, 11, function()
|
||||
local list = {}
|
||||
|
||||
for k,v in ipairs(player.GetAll()) do
|
||||
local sid64 = v:SteamID64()
|
||||
if !sid64 then continue end
|
||||
list[sid64] = true
|
||||
end
|
||||
|
||||
return list
|
||||
end}
|
||||
|
||||
eProtect.BaseConfig["httpfocusedurlsisblacklist"] = {true, 12}
|
||||
|
||||
eProtect.BaseConfig["httpfocusedurls"] = {{}, 13, function()
|
||||
local list = {}
|
||||
|
||||
local tbl_http = eProtect.data["requestedHTTP"] and eProtect.data["requestedHTTP"].result or {}
|
||||
|
||||
if tbl_http then
|
||||
for k,v in ipairs(tbl_http) do
|
||||
list[v.link] = true
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end}
|
||||
|
||||
------------------------------------------------------
|
||||
-- NO NOT TOUCH ANYTHING IN HERE!!!!!!!!!
|
||||
------------------------------------------------------76561198002319944
|
||||
@@ -1,43 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
|
||||
eProtect.config = eProtect.config or {}
|
||||
|
||||
eProtect.config["language"] = "en"
|
||||
|
||||
eProtect.config["prefix"] = "[eProtect] "
|
||||
|
||||
eProtect.config["storage_type"] = "mysql"-- (sql_local or mysql)
|
||||
|
||||
eProtect.config["disablehttplogging"] = false -- If a DRM is ran after eProtect it could break if they check for HTTP modifications! If so make this true.
|
||||
|
||||
eProtect.config["ignoreDRM"] = false
|
||||
|
||||
eProtect.config["punishMaliciousIntent"] = true
|
||||
|
||||
eProtect.config["disabledModules"] = {
|
||||
["identifier"] = false,
|
||||
["detection_log"] = false,
|
||||
["net_limiter"] = false,
|
||||
["net_logger"] = false,
|
||||
["exploit_patcher"] = false,
|
||||
["exploit_finder"] = false,
|
||||
["fake_exploits"] = false,
|
||||
["data_snooper"] = false
|
||||
}
|
||||
|
||||
eProtect.config["permission"] = {
|
||||
["owner"] = true,
|
||||
["superadmin"] = true,
|
||||
["community_manager"] = true,
|
||||
["headofstaff"] = true
|
||||
}
|
||||
@@ -1,550 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
eProtect = eProtect or {}
|
||||
|
||||
eProtect.config = eProtect.config or {}
|
||||
|
||||
eProtect.config["mysql_info"] = {
|
||||
host = "144.76.235.183",
|
||||
port = 3306,
|
||||
database = "s6_eprotect",
|
||||
username = "u6_PouapUJD6P",
|
||||
password = "V!3P!7^NQSXGvyBZPE76O^GY"
|
||||
}
|
||||
|
||||
eProtect.config["command"] = "!eprotect"
|
||||
|
||||
eProtect.config["process-save-queue"] = 10 --- This will check if we should save data to the file. 00
|
||||
|
||||
eProtect.data = eProtect.data or {}
|
||||
|
||||
eProtect.data.badNets = eProtect.data.badNets or {}
|
||||
|
||||
eProtect.registerBadNet = function(netstring, num)
|
||||
eProtect.data.badNets[netstring] = {type = num}
|
||||
end
|
||||
|
||||
|
||||
-- Credits to meepen for the table with known exploits and backdoors! <3
|
||||
|
||||
--- Known Exploits
|
||||
eProtect.registerBadNet("pplay_deleterow", 1)
|
||||
eProtect.registerBadNet("pplay_addrow", 1)
|
||||
eProtect.registerBadNet("pplay_sendtable", 1)
|
||||
eProtect.registerBadNet("WriteQuery", 1)
|
||||
eProtect.registerBadNet("SendMoney", 1)
|
||||
eProtect.registerBadNet("BailOut", 1)
|
||||
eProtect.registerBadNet("customprinter_get", 1)
|
||||
eProtect.registerBadNet("textstickers_entdata", 1)
|
||||
eProtect.registerBadNet("NC_GetNameChange", 1)
|
||||
eProtect.registerBadNet("ATS_WARP_REMOVE_CLIENT", 1)
|
||||
eProtect.registerBadNet("ATS_WARP_FROM_CLIENT", 1)
|
||||
eProtect.registerBadNet("ATS_WARP_VIEWOWNER", 1)
|
||||
eProtect.registerBadNet("CFRemoveGame", 1)
|
||||
eProtect.registerBadNet("CFJoinGame", 1)
|
||||
eProtect.registerBadNet("CFEndGame", 1)
|
||||
eProtect.registerBadNet("CreateCase", 1)
|
||||
eProtect.registerBadNet("rprotect_terminal_settings", 1)
|
||||
eProtect.registerBadNet("StackGhost", 1)
|
||||
eProtect.registerBadNet("RevivePlayer", 1)
|
||||
eProtect.registerBadNet("ARMORY_RetrieveWeapon", 1)
|
||||
eProtect.registerBadNet("TransferReport", 1)
|
||||
eProtect.registerBadNet("SimplicityAC_aysent", 1)
|
||||
eProtect.registerBadNet("pac_to_contraption", 1)
|
||||
eProtect.registerBadNet("SyncPrinterButtons76561198056171650", 1)
|
||||
eProtect.registerBadNet("sendtable", 1)
|
||||
eProtect.registerBadNet("steamid2", 1)
|
||||
eProtect.registerBadNet("Kun_SellDrug", 1)
|
||||
eProtect.registerBadNet("net_PSUnBoxServer", 1)
|
||||
eProtect.registerBadNet("pplay_deleterow", 1)
|
||||
eProtect.registerBadNet("pplay_addrow", 1)
|
||||
eProtect.registerBadNet("CraftSomething", 1)
|
||||
eProtect.registerBadNet("banleaver", 1)
|
||||
eProtect.registerBadNet("75_plus_win", 1)
|
||||
eProtect.registerBadNet("ATMDepositMoney", 1)
|
||||
eProtect.registerBadNet("Taxi_Add", 1)
|
||||
eProtect.registerBadNet("Kun_SellOil", 1)
|
||||
eProtect.registerBadNet("SellMinerals", 1)
|
||||
eProtect.registerBadNet("TakeBetMoney", 1)
|
||||
eProtect.registerBadNet("PoliceJoin", 1)
|
||||
eProtect.registerBadNet("CpForm_Answers", 1)
|
||||
eProtect.registerBadNet("DepositMoney", 1)
|
||||
eProtect.registerBadNet("MDE_RemoveStuff_C2S", 1)
|
||||
eProtect.registerBadNet("NET_SS_DoBuyTakeoff", 1)
|
||||
eProtect.registerBadNet("NET_EcSetTax", 1)
|
||||
eProtect.registerBadNet("RP_Accept_Fine", 1)
|
||||
eProtect.registerBadNet("RP_Fine_Player", 1)
|
||||
eProtect.registerBadNet("RXCAR_Shop_Store_C2S", 1)
|
||||
eProtect.registerBadNet("RXCAR_SellINVCar_C2S", 1)
|
||||
eProtect.registerBadNet("drugseffect_remove", 1)
|
||||
eProtect.registerBadNet("drugs_money", 1)
|
||||
eProtect.registerBadNet("CRAFTINGMOD_SHOP", 1)
|
||||
eProtect.registerBadNet("drugs_ignite", 1)
|
||||
eProtect.registerBadNet("drugseffect_hpremove", 1)
|
||||
eProtect.registerBadNet("DarkRP_Kun_ForceSpawn", 1)
|
||||
eProtect.registerBadNet("drugs_text", 1)
|
||||
eProtect.registerBadNet("NLRKick", 1)
|
||||
eProtect.registerBadNet("RecKickAFKer", 1)
|
||||
eProtect.registerBadNet("GMBG:PickupItem", 1)
|
||||
eProtect.registerBadNet("DL_Answering", 1)
|
||||
eProtect.registerBadNet("data_check", 1)
|
||||
eProtect.registerBadNet("plyWarning", 1)
|
||||
eProtect.registerBadNet("NLR.ActionPlayer", 1)
|
||||
eProtect.registerBadNet("timebombDefuse", 1)
|
||||
eProtect.registerBadNet("start_wd_emp", 1)
|
||||
eProtect.registerBadNet("kart_sell", 1)
|
||||
eProtect.registerBadNet("FarmingmodSellItems", 1)
|
||||
eProtect.registerBadNet("ClickerAddToPoints", 1)
|
||||
eProtect.registerBadNet("bodyman_model_change", 1)
|
||||
eProtect.registerBadNet("TOW_PayTheFine", 1)
|
||||
eProtect.registerBadNet("FIRE_CreateFireTruck", 1)
|
||||
eProtect.registerBadNet("hitcomplete", 1)
|
||||
eProtect.registerBadNet("hhh_request", 1)
|
||||
eProtect.registerBadNet("DaHit", 1)
|
||||
eProtect.registerBadNet("TCBBuyAmmo", 1)
|
||||
eProtect.registerBadNet("DataSend", 1)
|
||||
eProtect.registerBadNet("gBan.BanBuffer", 1)
|
||||
eProtect.registerBadNet("fp_as_doorHandler", 1)
|
||||
eProtect.registerBadNet("Upgrade", 1)
|
||||
eProtect.registerBadNet("TowTruck_CreateTowTruck", 1)
|
||||
eProtect.registerBadNet("TOW_SubmitWarning", 1)
|
||||
eProtect.registerBadNet("duelrequestguiYes", 1)
|
||||
eProtect.registerBadNet("JoinOrg", 1)
|
||||
eProtect.registerBadNet("pac_submit", 1)
|
||||
eProtect.registerBadNet("NDES_SelectedEmblem", 1)
|
||||
eProtect.registerBadNet("join_disconnect", 1)
|
||||
eProtect.registerBadNet("Morpheus.StaffTracker", 1)
|
||||
eProtect.registerBadNet("casinokit_chipexchange", 1)
|
||||
eProtect.registerBadNet("BuyKey", 1)
|
||||
eProtect.registerBadNet("BuyCrate", 1)
|
||||
eProtect.registerBadNet("FactionInviteConsole", 1)
|
||||
eProtect.registerBadNet("FacCreate", 1)
|
||||
eProtect.registerBadNet("1942_Fuhrer_SubmitCandidacy", 1)
|
||||
eProtect.registerBadNet("pogcp_report_submitReport", 1)
|
||||
eProtect.registerBadNet("hsend", 1)
|
||||
eProtect.registerBadNet("BuilderXToggleKill", 1)
|
||||
eProtect.registerBadNet("Chatbox_PlayerChat", 1)
|
||||
eProtect.registerBadNet("reports.submit", 1)
|
||||
eProtect.registerBadNet("services_accept", 1)
|
||||
eProtect.registerBadNet("Warn_CreateWarn", 1)
|
||||
eProtect.registerBadNet("NewReport", 1)
|
||||
eProtect.registerBadNet("soez", 1)
|
||||
eProtect.registerBadNet("GiveHealthNPC", 1)
|
||||
eProtect.registerBadNet("DarkRP_SS_Gamble", 1)
|
||||
eProtect.registerBadNet("buyinghealth", 1)
|
||||
eProtect.registerBadNet("DarkRP_preferredjobmodel", 1)
|
||||
eProtect.registerBadNet("whk_setart", 1)
|
||||
eProtect.registerBadNet("WithdrewBMoney", 1)
|
||||
eProtect.registerBadNet("DuelMessageReturn", 1)
|
||||
eProtect.registerBadNet("ban_rdm", 1)
|
||||
eProtect.registerBadNet("BuyCar", 1)
|
||||
eProtect.registerBadNet("ats_send_toServer", 1)
|
||||
eProtect.registerBadNet("dLogsGetCommand", 1)
|
||||
eProtect.registerBadNet("disguise", 1)
|
||||
eProtect.registerBadNet("gportal_rpname_change", 1)
|
||||
eProtect.registerBadNet("AbilityUse", 1)
|
||||
eProtect.registerBadNet("ClickerAddToPoints", 1)
|
||||
eProtect.registerBadNet("race_accept", 1)
|
||||
eProtect.registerBadNet("give_me_weapon", 1)
|
||||
eProtect.registerBadNet("FinishContract", 1)
|
||||
eProtect.registerBadNet("NLR_SPAWN", 1)
|
||||
eProtect.registerBadNet("Kun_ZiptieStruggle", 1)
|
||||
eProtect.registerBadNet("JB_Votekick", 1)
|
||||
eProtect.registerBadNet("Letthisdudeout", 1)
|
||||
eProtect.registerBadNet("ckit_roul_bet", 1)
|
||||
eProtect.registerBadNet("pac.net.TouchFlexes.ClientNotify", 1)
|
||||
eProtect.registerBadNet("ply_pick_shit", 1)
|
||||
eProtect.registerBadNet("TFA_Attachment_RequestAll", 1)
|
||||
eProtect.registerBadNet("BuyFirstTovar", 1)
|
||||
eProtect.registerBadNet("BuySecondTovar", 1)
|
||||
eProtect.registerBadNet("GiveHealthNPC", 1)
|
||||
eProtect.registerBadNet("MONEY_SYSTEM_GetWeapons", 1)
|
||||
eProtect.registerBadNet("MCon_Demote_ToServer", 1)
|
||||
eProtect.registerBadNet("withdrawp", 1)
|
||||
eProtect.registerBadNet("PCAdd", 1)
|
||||
eProtect.registerBadNet("ActivatePC", 1)
|
||||
eProtect.registerBadNet("PCDelAll", 1)
|
||||
eProtect.registerBadNet("viv_hl2rp_disp_message", 1)
|
||||
eProtect.registerBadNet("ATM_DepositMoney_C2S", 1)
|
||||
eProtect.registerBadNet("BM2.Command.SellBitcoins", 1)
|
||||
eProtect.registerBadNet("BM2.Command.Eject", 1)
|
||||
eProtect.registerBadNet("tickbooksendfine", 1)
|
||||
eProtect.registerBadNet("egg", 1)
|
||||
eProtect.registerBadNet("RHC_jail_player", 1)
|
||||
eProtect.registerBadNet("PlayerUseItem", 1)
|
||||
eProtect.registerBadNet("Chess Top10", 1)
|
||||
eProtect.registerBadNet("ItemStoreUse", 1)
|
||||
eProtect.registerBadNet("EZS_PlayerTag", 1)
|
||||
eProtect.registerBadNet("simfphys_gasspill", 1)
|
||||
eProtect.registerBadNet("sphys_dupe", 1)
|
||||
eProtect.registerBadNet("sw_gokart", 1)
|
||||
eProtect.registerBadNet("wordenns", 1)
|
||||
eProtect.registerBadNet("SyncPrinterButtons16690", 1)
|
||||
eProtect.registerBadNet("AttemptSellCar", 1)
|
||||
eProtect.registerBadNet("uPLYWarning", 1)
|
||||
eProtect.registerBadNet("atlaschat.rqclrcfg", 1)
|
||||
eProtect.registerBadNet("dlib.getinfo.replicate", 1)
|
||||
eProtect.registerBadNet("SetPermaKnife", 1)
|
||||
eProtect.registerBadNet("EnterpriseWithdraw", 1)
|
||||
eProtect.registerBadNet("SBP_addtime", 1)
|
||||
eProtect.registerBadNet("NetData", 1)
|
||||
eProtect.registerBadNet("CW20_PRESET_LOAD", 1)
|
||||
eProtect.registerBadNet("minigun_drones_switch", 1)
|
||||
eProtect.registerBadNet("NET_AM_MakePotion", 1)
|
||||
eProtect.registerBadNet("bitcoins_request_turn_off", 1)
|
||||
eProtect.registerBadNet("bitcoins_request_turn_on", 1)
|
||||
eProtect.registerBadNet("bitcoins_request_withdraw", 1)
|
||||
eProtect.registerBadNet("PermwepsNPCSellWeapon", 1)
|
||||
eProtect.registerBadNet("ncpstoredoact", 1)
|
||||
eProtect.registerBadNet("DuelRequestClient", 1)
|
||||
eProtect.registerBadNet("BeginSpin", 1)
|
||||
eProtect.registerBadNet("tickbookpayfine", 1)
|
||||
eProtect.registerBadNet("fg_printer_money", 1)
|
||||
eProtect.registerBadNet("IGS.GetPaymentURL", 1)
|
||||
eProtect.registerBadNet("AirDrops_StartPlacement", 1)
|
||||
eProtect.registerBadNet("SlotsRemoved", 1)
|
||||
eProtect.registerBadNet("FARMINGMOD_DROPITEM", 1)
|
||||
eProtect.registerBadNet("cab_sendmessage", 1)
|
||||
eProtect.registerBadNet("cab_cd_testdrive", 1)
|
||||
eProtect.registerBadNet("blueatm", 1)
|
||||
eProtect.registerBadNet("SCP-294Sv", 1)
|
||||
eProtect.registerBadNet("dronesrewrite_controldr", 1)
|
||||
eProtect.registerBadNet("desktopPrinter_Withdraw", 1)
|
||||
eProtect.registerBadNet("RemoveTag", 1)
|
||||
eProtect.registerBadNet("IDInv_RequestBank", 1)
|
||||
eProtect.registerBadNet("UseMedkit", 1)
|
||||
eProtect.registerBadNet("WipeMask", 1)
|
||||
eProtect.registerBadNet("SwapFilter", 1)
|
||||
eProtect.registerBadNet("RemoveMask", 1)
|
||||
eProtect.registerBadNet("DeployMask", 1)
|
||||
eProtect.registerBadNet("ZED_SpawnCar", 1)
|
||||
eProtect.registerBadNet("levelup_useperk", 1)
|
||||
eProtect.registerBadNet("passmayorexam", 1)
|
||||
eProtect.registerBadNet("Selldatride", 1)
|
||||
eProtect.registerBadNet("ORG_VaultDonate", 1)
|
||||
eProtect.registerBadNet("ORG_NewOrg", 1)
|
||||
eProtect.registerBadNet("ScannerMenu", 1)
|
||||
eProtect.registerBadNet("misswd_accept", 1)
|
||||
eProtect.registerBadNet("D3A_Message", 1)
|
||||
eProtect.registerBadNet("LawsToServer", 1)
|
||||
eProtect.registerBadNet("Shop_buy", 1)
|
||||
eProtect.registerBadNet("D3A_CreateOrg", 1)
|
||||
eProtect.registerBadNet("Gb_gasstation_BuyGas", 1)
|
||||
eProtect.registerBadNet("Gb_gasstation_BuyJerrycan", 1)
|
||||
eProtect.registerBadNet("MineServer", 1)
|
||||
eProtect.registerBadNet("AcceptBailOffer", 1)
|
||||
eProtect.registerBadNet("LawyerOfferBail", 1)
|
||||
eProtect.registerBadNet("buy_bundle", 1)
|
||||
eProtect.registerBadNet("AskPickupItemInv", 1)
|
||||
eProtect.registerBadNet("donatorshop_itemtobuy", 1)
|
||||
eProtect.registerBadNet("netOrgVoteInvite_Server", 1)
|
||||
eProtect.registerBadNet("Chess ClientWager", 1)
|
||||
eProtect.registerBadNet("AcceptRequest", 1)
|
||||
eProtect.registerBadNet("deposit", 1)
|
||||
eProtect.registerBadNet("CubeRiot CaptureZone Update", 1)
|
||||
eProtect.registerBadNet("NPCShop_BuyItem", 1)
|
||||
eProtect.registerBadNet("SpawnProtection", 1)
|
||||
eProtect.registerBadNet("hoverboardpurchase", 1)
|
||||
eProtect.registerBadNet("soundArrestCommit", 1)
|
||||
eProtect.registerBadNet("LotteryMenu", 1)
|
||||
eProtect.registerBadNet("updateLaws", 1)
|
||||
eProtect.registerBadNet("TMC_NET_FirePlayer", 1)
|
||||
eProtect.registerBadNet("thiefnpc", 1)
|
||||
eProtect.registerBadNet("TMC_NET_MakePlayerWanted", 1)
|
||||
eProtect.registerBadNet("SyncRemoveAction", 1)
|
||||
eProtect.registerBadNet("HV_AmmoBuy", 1)
|
||||
eProtect.registerBadNet("NET_CR_TakeStoredMoney", 1)
|
||||
eProtect.registerBadNet("nox_addpremadepunishment", 1)
|
||||
eProtect.registerBadNet("GrabMoney", 1)
|
||||
eProtect.registerBadNet("LAWYER.GetBailOut", 1)
|
||||
eProtect.registerBadNet("LAWYER.BailFelonOut", 1)
|
||||
eProtect.registerBadNet("br_send_pm", 1)
|
||||
eProtect.registerBadNet("GET_Admin_MSGS", 1)
|
||||
eProtect.registerBadNet("OPEN_ADMIN_CHAT", 1)
|
||||
eProtect.registerBadNet("LB_AddBan", 1)
|
||||
eProtect.registerBadNet("redirectMsg", 1)
|
||||
eProtect.registerBadNet("RDMReason_Explain", 1)
|
||||
eProtect.registerBadNet("JB_SelectWarden", 1)
|
||||
eProtect.registerBadNet("JB_GiveCubics", 1)
|
||||
eProtect.registerBadNet("SendSteamID", 1)
|
||||
eProtect.registerBadNet("wyozimc_playply", 1)
|
||||
eProtect.registerBadNet("SpecDM_SendLoadout", 1)
|
||||
eProtect.registerBadNet("sv_saveweapons", 1)
|
||||
eProtect.registerBadNet("DL_StartReport", 1)
|
||||
eProtect.registerBadNet("DL_ReportPlayer", 1)
|
||||
eProtect.registerBadNet("DL_AskLogsList", 1)
|
||||
eProtect.registerBadNet("DailyLoginClaim", 1)
|
||||
eProtect.registerBadNet("GiveWeapon", 1)
|
||||
eProtect.registerBadNet("GovStation_SpawnVehicle", 1)
|
||||
eProtect.registerBadNet("inviteToOrganization", 1)
|
||||
eProtect.registerBadNet("createFaction", 1)
|
||||
eProtect.registerBadNet("sellitem", 1)
|
||||
eProtect.registerBadNet("giveArrestReason", 1)
|
||||
eProtect.registerBadNet("unarrestPerson", 1)
|
||||
eProtect.registerBadNet("JoinFirstSS", 1)
|
||||
eProtect.registerBadNet("bringNfreeze", 1)
|
||||
eProtect.registerBadNet("start_wd_hack", 1)
|
||||
eProtect.registerBadNet("DestroyTable", 1)
|
||||
eProtect.registerBadNet("nCTieUpStart", 1)
|
||||
eProtect.registerBadNet("IveBeenRDMed", 1)
|
||||
eProtect.registerBadNet("FIGHTCLUB_StartFight", 1)
|
||||
eProtect.registerBadNet("FIGHTCLUB_KickPlayer", 1)
|
||||
eProtect.registerBadNet("ReSpawn", 1)
|
||||
eProtect.registerBadNet("CP_Test_Results", 1)
|
||||
eProtect.registerBadNet("AcceptBailOffer", 1)
|
||||
eProtect.registerBadNet("IS_SubmitSID_C2S", 1)
|
||||
eProtect.registerBadNet("IS_GetReward_C2S", 1)
|
||||
eProtect.registerBadNet("ChangeOrgName", 1)
|
||||
eProtect.registerBadNet("DisbandOrganization", 1)
|
||||
eProtect.registerBadNet("CreateOrganization", 1)
|
||||
eProtect.registerBadNet("newTerritory", 1)
|
||||
eProtect.registerBadNet("InviteMember", 1)
|
||||
eProtect.registerBadNet("sendDuelInfo", 1)
|
||||
eProtect.registerBadNet("DoDealerDeliver", 1)
|
||||
eProtect.registerBadNet("PurchaseWeed", 1)
|
||||
eProtect.registerBadNet("guncraft_removeWorkbench", 1)
|
||||
eProtect.registerBadNet("wordenns", 1)
|
||||
eProtect.registerBadNet("userAcceptPrestige", 1)
|
||||
eProtect.registerBadNet("vj_npcspawner_sv_create", 1)
|
||||
eProtect.registerBadNet("DuelMessageReturn", 1)
|
||||
eProtect.registerBadNet("Client_To_Server_OpenEditor", 1)
|
||||
eProtect.registerBadNet("GiveSCP294Cup", 1)
|
||||
eProtect.registerBadNet("GiveArmor100", 1)
|
||||
eProtect.registerBadNet("SprintSpeedset", 1)
|
||||
eProtect.registerBadNet("ArmorButton", 1)
|
||||
eProtect.registerBadNet("HealButton", 1)
|
||||
eProtect.registerBadNet("SRequest", 1)
|
||||
eProtect.registerBadNet("ClickerForceSave", 1)
|
||||
eProtect.registerBadNet("rpi_trade_end", 1)
|
||||
eProtect.registerBadNet("NET_BailPlayer", 1)
|
||||
eProtect.registerBadNet("vj_testentity_runtextsd", 1)
|
||||
eProtect.registerBadNet("vj_fireplace_turnon2", 1)
|
||||
eProtect.registerBadNet("requestmoneyforvk", 1)
|
||||
eProtect.registerBadNet("gPrinters.sendID", 1)
|
||||
eProtect.registerBadNet("FIRE_RemoveFireTruck", 1)
|
||||
eProtect.registerBadNet("drugs_effect", 1)
|
||||
eProtect.registerBadNet("drugs_give", 1)
|
||||
eProtect.registerBadNet("NET_DoPrinterAction", 1)
|
||||
eProtect.registerBadNet("opr_withdraw", 1)
|
||||
eProtect.registerBadNet("money_clicker_withdraw", 1)
|
||||
eProtect.registerBadNet("NGII_TakeMoney", 1)
|
||||
eProtect.registerBadNet("gPrinters.retrieveMoney", 1)
|
||||
eProtect.registerBadNet("revival_revive_accept", 1)
|
||||
eProtect.registerBadNet("chname", 1)
|
||||
eProtect.registerBadNet("NewRPNameSQL", 1)
|
||||
eProtect.registerBadNet("UpdateRPUModelSQL", 1)
|
||||
eProtect.registerBadNet("SetTableTarget", 1)
|
||||
eProtect.registerBadNet("SquadGiveWeapon", 1)
|
||||
eProtect.registerBadNet("BuyUpgradesStuff", 1)
|
||||
eProtect.registerBadNet("REPAdminChangeLVL", 1)
|
||||
eProtect.registerBadNet("SendMail", 1)
|
||||
eProtect.registerBadNet("DemotePlayer", 1)
|
||||
eProtect.registerBadNet("OpenGates", 1)
|
||||
eProtect.registerBadNet("VehicleUnderglow", 1)
|
||||
eProtect.registerBadNet("Hopping_Test", 1)
|
||||
eProtect.registerBadNet("CREATE_REPORT", 1)
|
||||
eProtect.registerBadNet("CreateEntity", 1)
|
||||
eProtect.registerBadNet("FiremanLeave", 1)
|
||||
eProtect.registerBadNet("DarkRP_Defib_ForceSpawn", 1)
|
||||
eProtect.registerBadNet("Resupply", 1)
|
||||
eProtect.registerBadNet("BTTTStartVotekick", 1)
|
||||
eProtect.registerBadNet("_nonDBVMVote", 1)
|
||||
eProtect.registerBadNet("REPPurchase", 1)
|
||||
eProtect.registerBadNet("deathrag_takeitem", 1)
|
||||
eProtect.registerBadNet("FacCreate", 1)
|
||||
eProtect.registerBadNet("InformPlayer", 1)
|
||||
eProtect.registerBadNet("lockpick_sound", 1)
|
||||
eProtect.registerBadNet("SetPlayerModel", 1)
|
||||
eProtect.registerBadNet("changeToPhysgun", 1)
|
||||
eProtect.registerBadNet("VoteBanNO", 1)
|
||||
eProtect.registerBadNet("VoteKickNO", 1)
|
||||
eProtect.registerBadNet("shopguild_buyitem", 1)
|
||||
eProtect.registerBadNet("MG2.Request.GangRankings", 1)
|
||||
eProtect.registerBadNet("RequestMAPSize", 1)
|
||||
eProtect.registerBadNet("gMining.sellMineral", 1)
|
||||
eProtect.registerBadNet("ItemStoreDrop", 1)
|
||||
eProtect.registerBadNet("optarrest", 1)
|
||||
eProtect.registerBadNet("TalkIconChat", 1)
|
||||
eProtect.registerBadNet("UpdateAdvBoneSettings", 1)
|
||||
eProtect.registerBadNet("ViralsScoreboardAdmin", 1)
|
||||
eProtect.registerBadNet("PowerRoundsForcePR", 1)
|
||||
eProtect.registerBadNet("showDisguiseHUD", 1)
|
||||
eProtect.registerBadNet("withdrawMoney", 1)
|
||||
eProtect.registerBadNet("SyncPrinterButtons76561198027292625", 1)
|
||||
eProtect.registerBadNet("phone", 1)
|
||||
eProtect.registerBadNet("STLoanToServer", 1)
|
||||
eProtect.registerBadNet("TCBDealerStore", 1)
|
||||
eProtect.registerBadNet("TCBDealerSpawn", 1)
|
||||
eProtect.registerBadNet("ts_buytitle", 1)
|
||||
eProtect.registerBadNet("gMining.registerAchievement", 1)
|
||||
eProtect.registerBadNet("gPrinters.openUpgrades", 1)
|
||||
|
||||
--- Known Backdoors
|
||||
eProtect.registerBadNet("Sbox_gm_attackofnullday_key", 2)
|
||||
eProtect.registerBadNet("c", 2)
|
||||
eProtect.registerBadNet("enablevac", 2)
|
||||
eProtect.registerBadNet("ULXQUERY2", 2)
|
||||
eProtect.registerBadNet("Im_SOCool", 2)
|
||||
eProtect.registerBadNet("MoonMan", 2)
|
||||
eProtect.registerBadNet("LickMeOut", 2)
|
||||
eProtect.registerBadNet("SessionBackdoor", 2)
|
||||
eProtect.registerBadNet("OdiumBackDoor", 2)
|
||||
eProtect.registerBadNet("ULX_QUERY2", 2)
|
||||
eProtect.registerBadNet("Sbox_itemstore", 2)
|
||||
eProtect.registerBadNet("Sbox_darkrp", 2)
|
||||
eProtect.registerBadNet("Sbox_Message", 2)
|
||||
eProtect.registerBadNet("_blacksmurf", 2)
|
||||
eProtect.registerBadNet("nostrip", 2)
|
||||
eProtect.registerBadNet("Remove_Exploiters", 2)
|
||||
eProtect.registerBadNet("Sandbox_ArmDupe", 2)
|
||||
eProtect.registerBadNet("rconadmin", 2)
|
||||
eProtect.registerBadNet("jesuslebg", 2)
|
||||
eProtect.registerBadNet("disablebackdoor", 2)
|
||||
eProtect.registerBadNet("blacksmurfBackdoor", 2)
|
||||
eProtect.registerBadNet("jeveuttonrconleul", 2)
|
||||
eProtect.registerBadNet("lag_ping", 2)
|
||||
eProtect.registerBadNet("memeDoor", 2)
|
||||
eProtect.registerBadNet("DarkRP_AdminWeapons", 2)
|
||||
eProtect.registerBadNet("Fix_Keypads", 2)
|
||||
eProtect.registerBadNet("noclipcloakaesp_chat_text", 2)
|
||||
eProtect.registerBadNet("_CAC_ReadMemory", 2)
|
||||
eProtect.registerBadNet("Ulib_Message", 2)
|
||||
eProtect.registerBadNet("Ulogs_Infos", 2)
|
||||
eProtect.registerBadNet("ITEM", 2)
|
||||
eProtect.registerBadNet("nocheat", 2)
|
||||
eProtect.registerBadNet("adsp_door_length", 2)
|
||||
eProtect.registerBadNet("ξpsilon", 2)
|
||||
eProtect.registerBadNet("JQerystrip.disable", 2)
|
||||
eProtect.registerBadNet("Sandbox_GayParty", 2)
|
||||
eProtect.registerBadNet("DarkRP_UTF8", 2)
|
||||
eProtect.registerBadNet("PlayerKilledLogged", 2)
|
||||
eProtect.registerBadNet("OldNetReadData", 2)
|
||||
eProtect.registerBadNet("Backdoor", 2)
|
||||
eProtect.registerBadNet("cucked", 2)
|
||||
eProtect.registerBadNet("NoNerks", 2)
|
||||
eProtect.registerBadNet("kek", 2)
|
||||
eProtect.registerBadNet("DarkRP_Money_System", 2)
|
||||
eProtect.registerBadNet("BetStrep", 2)
|
||||
eProtect.registerBadNet("ZimbaBackdoor", 2)
|
||||
eProtect.registerBadNet("something", 2)
|
||||
eProtect.registerBadNet("random", 2)
|
||||
eProtect.registerBadNet("strip0", 2)
|
||||
eProtect.registerBadNet("fellosnake", 2)
|
||||
eProtect.registerBadNet("idk", 2)
|
||||
eProtect.registerBadNet("||||", 2)
|
||||
eProtect.registerBadNet("EnigmaIsthere", 2)
|
||||
eProtect.registerBadNet("ALTERED_CARB0N", 2)
|
||||
eProtect.registerBadNet("killserver", 2)
|
||||
eProtect.registerBadNet("fuckserver", 2)
|
||||
eProtect.registerBadNet("cvaraccess", 2)
|
||||
eProtect.registerBadNet("_Defcon", 2)
|
||||
eProtect.registerBadNet("dontforget", 2)
|
||||
eProtect.registerBadNet("aze46aez67z67z64dcv4bt", 2)
|
||||
eProtect.registerBadNet("nolag", 2)
|
||||
eProtect.registerBadNet("changename", 2)
|
||||
eProtect.registerBadNet("music", 2)
|
||||
eProtect.registerBadNet("_Defqon", 2)
|
||||
eProtect.registerBadNet("xenoexistscl", 2)
|
||||
eProtect.registerBadNet("R8", 2)
|
||||
eProtect.registerBadNet("AnalCavity", 2)
|
||||
eProtect.registerBadNet("DefqonBackdoor", 2)
|
||||
eProtect.registerBadNet("fourhead", 2)
|
||||
eProtect.registerBadNet("echangeinfo", 2)
|
||||
eProtect.registerBadNet("PlayerItemPickUp", 2)
|
||||
eProtect.registerBadNet("thefrenchenculer", 2)
|
||||
eProtect.registerBadNet("elfamosabackdoormdr", 2)
|
||||
eProtect.registerBadNet("stoppk", 2)
|
||||
eProtect.registerBadNet("noprop", 2)
|
||||
eProtect.registerBadNet("reaper", 2)
|
||||
eProtect.registerBadNet("Abcdefgh", 2)
|
||||
eProtect.registerBadNet("JSQuery.Data(Post(false))", 2)
|
||||
eProtect.registerBadNet("pjHabrp9EY", 2)
|
||||
eProtect.registerBadNet("_Raze", 2)
|
||||
eProtect.registerBadNet("88", 2)
|
||||
eProtect.registerBadNet("Dominos", 2)
|
||||
eProtect.registerBadNet("NoOdium_ReadPing", 2)
|
||||
eProtect.registerBadNet("m9k_explosionradius", 2)
|
||||
eProtect.registerBadNet("gag", 2)
|
||||
eProtect.registerBadNet("_cac_", 2)
|
||||
eProtect.registerBadNet("_Battleye_Meme_", 2)
|
||||
eProtect.registerBadNet("legrandguzmanestla", 2)
|
||||
eProtect.registerBadNet("ULogs_B", 2)
|
||||
eProtect.registerBadNet("arivia", 2)
|
||||
eProtect.registerBadNet("_Warns", 2)
|
||||
eProtect.registerBadNet("xuy", 2)
|
||||
eProtect.registerBadNet("samosatracking57", 2)
|
||||
eProtect.registerBadNet("striphelper", 2)
|
||||
eProtect.registerBadNet("m9k_explosive", 2)
|
||||
eProtect.registerBadNet("GaySploitBackdoor", 2)
|
||||
eProtect.registerBadNet("_GaySploit", 2)
|
||||
eProtect.registerBadNet("slua", 2)
|
||||
eProtect.registerBadNet("Bilboard.adverts:Spawn(false)", 2)
|
||||
eProtect.registerBadNet("BOOST_FPS", 2)
|
||||
eProtect.registerBadNet("FPP_AntiStrip", 2)
|
||||
eProtect.registerBadNet("ULX_QUERY_TEST2", 2)
|
||||
eProtect.registerBadNet("FADMIN_ANTICRASH", 2)
|
||||
eProtect.registerBadNet("ULX_ANTI_BACKDOOR", 2)
|
||||
eProtect.registerBadNet("UKT_MOMOS", 2)
|
||||
eProtect.registerBadNet("rcivluz", 2)
|
||||
eProtect.registerBadNet("SENDTEST", 2)
|
||||
eProtect.registerBadNet("MJkQswHqfZ", 2)
|
||||
eProtect.registerBadNet("INJ3v4", 2)
|
||||
eProtect.registerBadNet("_clientcvars", 2)
|
||||
eProtect.registerBadNet("_main", 2)
|
||||
eProtect.registerBadNet("GMOD_NETDBG", 2)
|
||||
eProtect.registerBadNet("thereaper", 2)
|
||||
eProtect.registerBadNet("audisquad_lua", 2)
|
||||
eProtect.registerBadNet("anticrash", 2)
|
||||
eProtect.registerBadNet("ZernaxBackdoor", 2)
|
||||
eProtect.registerBadNet("bdsm", 2)
|
||||
eProtect.registerBadNet("waoz", 2)
|
||||
eProtect.registerBadNet("stream", 2)
|
||||
eProtect.registerBadNet("adm_network", 2)
|
||||
eProtect.registerBadNet("antiexploit", 2)
|
||||
eProtect.registerBadNet("ReadPing", 2)
|
||||
eProtect.registerBadNet("berettabest", 2)
|
||||
eProtect.registerBadNet("componenttolua", 2)
|
||||
eProtect.registerBadNet("theberettabcd", 2)
|
||||
eProtect.registerBadNet("negativedlebest", 2)
|
||||
eProtect.registerBadNet("mathislebg", 2)
|
||||
eProtect.registerBadNet("SparksLeBg", 2)
|
||||
eProtect.registerBadNet("DOGE", 2)
|
||||
eProtect.registerBadNet("FPSBOOST", 2)
|
||||
eProtect.registerBadNet("N::B::P", 2)
|
||||
eProtect.registerBadNet("PDA_DRM_REQUEST_CONTENT", 2)
|
||||
eProtect.registerBadNet("shix", 2)
|
||||
eProtect.registerBadNet("Inj3", 2)
|
||||
eProtect.registerBadNet("AidsTacos", 2)
|
||||
eProtect.registerBadNet("verifiopd", 2)
|
||||
eProtect.registerBadNet("pwn_wake", 2)
|
||||
eProtect.registerBadNet("pwn_http_answer", 2)
|
||||
eProtect.registerBadNet("pwn_http_send", 2)
|
||||
eProtect.registerBadNet("The_Dankwoo", 2)
|
||||
eProtect.registerBadNet("PRDW_GET", 2)
|
||||
eProtect.registerBadNet("fancyscoreboard_leave", 2)
|
||||
eProtect.registerBadNet("DarkRP_Gamemodes", 2)
|
||||
eProtect.registerBadNet("DarkRP_Armors", 2)
|
||||
eProtect.registerBadNet("yohsambresicianatik<3", 2)
|
||||
eProtect.registerBadNet("EnigmaProject", 2)
|
||||
eProtect.registerBadNet("PlayerCheck", 2)
|
||||
eProtect.registerBadNet("Ulx_Error_88", 2)
|
||||
eProtect.registerBadNet("FAdmin_Notification_Receiver", 2)
|
||||
eProtect.registerBadNet("DarkRP_ReceiveData", 2)
|
||||
eProtect.registerBadNet("Weapon_88", 2)
|
||||
eProtect.registerBadNet("__G____CAC", 2)
|
||||
eProtect.registerBadNet("AbSoluT", 2)
|
||||
eProtect.registerBadNet("mecthack", 2)
|
||||
eProtect.registerBadNet("SetPlayerDeathCount", 2)
|
||||
eProtect.registerBadNet("awarn_remove", 2)
|
||||
eProtect.registerBadNet("fijiconn", 2)
|
||||
eProtect.registerBadNet("nw.readstream", 2)
|
||||
eProtect.registerBadNet("LuaCmd", 2)
|
||||
eProtect.registerBadNet("The_DankWhy", 2)
|
||||
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>PNG
|
||||
|
||||
|
Before Width: | Height: | Size: 8 B |
@@ -1,502 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -1,61 +0,0 @@
|
||||
# Garry's mod profiler
|
||||
|
||||
A profiler based on `debug.sethook` and `SysTime`. Perfect for the following things:
|
||||
|
||||
- Finding performance bottlenecks (i.e. which code is spent the most time on)
|
||||
- Finding out which functions are called most
|
||||
- Identifying the source of lag spikes
|
||||
- Profiling specific functions
|
||||
|
||||
This profiler distinguishes itself from [DBugR](https://github.com/oubliette32/DBugR) in its approach.
|
||||
DBugR profiles at hooks, timers and net messages specifically. FProfiler looks purely at functions outside of their context.
|
||||
|
||||
I would recommend DBugR to measure networking and/or specific hooks and/or timers. I would recommend FProfiler for the things mentioned above.
|
||||
|
||||
## Using FProfiler
|
||||
The `FProfiler` console command opens the profiler. Everything can be done from there.
|
||||
|
||||
Here's an explanation of the FProfiler menu:
|
||||
|
||||
UI thing | Description
|
||||
------------ | -------------
|
||||
Realm | Whether you're profiling the client or the server. Note: You need to be a SuperAdmin (or have the `FProfiler` permission in your favourite admin mod) to be allowed to do any serverside profiling!
|
||||
(Re)start profiling | Starts a profiling session. If there is any previous profiling session, it starts anew, disregarding any old data.
|
||||
Stop profiling | Stop an ongoing profiling session.
|
||||
Continue profiling | Continue a profiling session that has previously been stopped. It will simply continue gathering data.
|
||||
Profiling Focus | Focus the profiling on a specific function. Note: you **cannot** put arbitrary Lua in there, just function names! E.g. `player.GetAll` will work, but `hook.GetTable().Think.Cavity` will _not_.
|
||||
Bottlenecks | Shows the functions that the game has spent its most time on. The top ones are the ones that hurt your FPS most.
|
||||
Top n most expensive | Perfect for finding the cause of lag spikes. Lists the functions that took a long time on specific times they were called. Differs from the Bottlenecks tab in that Bottlenecks is about *all* the times the functions were called, this tab is about the single times they ran at their slowest.
|
||||
Focus button | Sets the profiling focus to the selected function
|
||||
|
||||
## Using FProfiler in code
|
||||
|
||||
FProfiler has an API, a simple one too. All functions listed below are shared:
|
||||
```lua
|
||||
-- Starts profiling.
|
||||
-- When focus is given, the profiler will only profile the focussed upon function, and the functions it calls
|
||||
FProfiler.start([focus])
|
||||
|
||||
-- Stops profiling
|
||||
FProfiler.stop()
|
||||
|
||||
-- Continue profiling
|
||||
FProfiler.continueProfiling()
|
||||
```
|
||||
|
||||
All the data of the profiling sessions can be seen in the `FProfiler` menu. Because of that, there need to be **no** data retrieving functions in the API.
|
||||
If you don't want to use the UI, you *probably* want the profiling in a custom format. There are some internal functions available for that. Check out `lua/fprofiler/gather.lua` and `lua/fprofiler/report`.
|
||||
|
||||
|
||||
## About bottlenecks
|
||||
|
||||
When faced with performance problems, people tend to dive in the code to perform micro-optimisations. Think of localising libraries to the scope of a file, storing `LocalPlayer()` in a variable for re-use and that kind of stuff. This is a naive approach and is unlikely to get you very far.
|
||||
The reason for that is very simple: micro-optimisations have **very** little effect on the eventual performance of the game. They're called micro-optimisations for a reason.
|
||||
|
||||
What you *should* be after is macro-optimisations, i.e. the big guys. Attacking those will give you the biggest benefit. Doubling your FPS is not uncommon when you attack the big guys.
|
||||
|
||||
What do I mean by macro-optimisation/the big guys you ask? Think of reducing an O(n^2) algorithm to an O(n lg n) one. Think of things like using more efficient data structures, more efficient algorithms, caching the results of complicated calculations, alternative ways to calculate things that don't give the exact right result, but give a "good enough" result and are *way faster* than the original algorithm. **THAT** kind of shit.
|
||||
|
||||
That's where the profiler comes in. Always mistrust what you **think** is a big performance hog is, **measure** it. Even the assumptions of people who have optimising code as their profession are often proven wrong by the profiler. Don't be smug and think you can do any better.
|
||||
|
||||
When working on performance, the profiler is to be your guide. The profiler is to tell you what to optimise. Do not bother with anything other than the most expensive functions for you will be wasting your time.
|
||||
@@ -1,48 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
FProfiler = FProfiler or {}
|
||||
FProfiler.Internal = {}
|
||||
FProfiler.UI = {}
|
||||
|
||||
AddCSLuaFile()
|
||||
AddCSLuaFile("fprofiler/cami.lua")
|
||||
AddCSLuaFile("fprofiler/gather.lua")
|
||||
AddCSLuaFile("fprofiler/report.lua")
|
||||
AddCSLuaFile("fprofiler/util.lua")
|
||||
AddCSLuaFile("fprofiler/prettyprint.lua")
|
||||
|
||||
AddCSLuaFile("fprofiler/ui/model.lua")
|
||||
AddCSLuaFile("fprofiler/ui/frame.lua")
|
||||
AddCSLuaFile("fprofiler/ui/clientcontrol.lua")
|
||||
AddCSLuaFile("fprofiler/ui/servercontrol.lua")
|
||||
|
||||
include("fprofiler/cami.lua")
|
||||
|
||||
CAMI.RegisterPrivilege{
|
||||
Name = "FProfiler",
|
||||
MinAccess = "superadmin"
|
||||
}
|
||||
|
||||
|
||||
include("fprofiler/prettyprint.lua")
|
||||
include("fprofiler/util.lua")
|
||||
include("fprofiler/gather.lua")
|
||||
include("fprofiler/report.lua")
|
||||
|
||||
|
||||
if CLIENT then
|
||||
include("fprofiler/ui/model.lua")
|
||||
include("fprofiler/ui/frame.lua")
|
||||
include("fprofiler/ui/clientcontrol.lua")
|
||||
include("fprofiler/ui/servercontrol.lua")
|
||||
else
|
||||
include("fprofiler/ui/server.lua")
|
||||
end
|
||||
@@ -1,534 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
CAMI - Common Admin Mod Interface.
|
||||
Makes admin mods intercompatible and provides an abstract privilege interface
|
||||
for third party addons.
|
||||
|
||||
IMPORTANT: This is a draft script. It is very much WIP.
|
||||
|
||||
Follows the specification on this page:
|
||||
https://docs.google.com/document/d/1QIRVcAgZfAYf1aBl_dNV_ewR6P25wze2KmUVzlbFgMI
|
||||
|
||||
|
||||
Structures:
|
||||
CAMI_USERGROUP, defines the charactaristics of a usergroup:
|
||||
{
|
||||
Name
|
||||
string
|
||||
The name of the usergroup
|
||||
Inherits
|
||||
string
|
||||
The name of the usergroup this usergroup inherits from
|
||||
}
|
||||
|
||||
CAMI_PRIVILEGE, defines the charactaristics of a privilege:
|
||||
{
|
||||
Name
|
||||
string
|
||||
The name of the privilege
|
||||
MinAccess
|
||||
string
|
||||
One of the following three: user/admin/superadmin
|
||||
HasAccess
|
||||
function(
|
||||
privilege :: CAMI_PRIVILEGE,
|
||||
actor :: Player,
|
||||
target :: Player
|
||||
) :: bool
|
||||
optional
|
||||
Function that decides whether a player can execute this privilege,
|
||||
optionally on another player (target).
|
||||
}
|
||||
]]
|
||||
|
||||
-- Version number in YearMonthDay format.
|
||||
local version = 20150902.1
|
||||
|
||||
if CAMI and CAMI.Version >= version then return end
|
||||
|
||||
CAMI = CAMI or {}
|
||||
CAMI.Version = version
|
||||
|
||||
--[[
|
||||
usergroups
|
||||
Contains the registered CAMI_USERGROUP usergroup structures.
|
||||
Indexed by usergroup name.
|
||||
]]
|
||||
local usergroups = CAMI.GetUsergroups and CAMI.GetUsergroups() or {
|
||||
user = {
|
||||
Name = "user",
|
||||
Inherits = "user"
|
||||
},
|
||||
admin = {
|
||||
Name = "admin",
|
||||
Inherits = "user"
|
||||
},
|
||||
superadmin = {
|
||||
Name = "superadmin",
|
||||
Inherits = "admin"
|
||||
}
|
||||
}
|
||||
|
||||
--[[
|
||||
privileges
|
||||
Contains the registered CAMI_PRIVILEGE privilege structures.
|
||||
Indexed by privilege name.
|
||||
]]
|
||||
local privileges = CAMI.GetPrivileges and CAMI.GetPrivileges() or {}
|
||||
|
||||
--[[
|
||||
CAMI.RegisterUsergroup
|
||||
Registers a usergroup with CAMI.
|
||||
|
||||
Parameters:
|
||||
usergroup
|
||||
CAMI_USERGROUP
|
||||
(see CAMI_USERGROUP structure)
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
Use this to make sure CAMI.RegisterUsergroup function and the
|
||||
CAMI.OnUsergroupRegistered hook don't cause an infinite loop
|
||||
|
||||
|
||||
|
||||
Return value:
|
||||
CAMI_USERGROUP
|
||||
The usergroup given as argument.
|
||||
]]
|
||||
function CAMI.RegisterUsergroup(usergroup, source)
|
||||
usergroups[usergroup.Name] = usergroup
|
||||
|
||||
hook.Call("CAMI.OnUsergroupRegistered", nil, usergroup, source)
|
||||
return usergroup
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UnregisterUsergroup
|
||||
Unregisters a usergroup from CAMI. This will call a hook that will notify
|
||||
all other admin mods of the removal.
|
||||
|
||||
Call only when the usergroup is to be permanently removed.
|
||||
|
||||
Parameters:
|
||||
usergroupName
|
||||
string
|
||||
The name of the usergroup.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
Use this to make sure CAMI.UnregisterUsergroup function and the
|
||||
CAMI.OnUsergroupUnregistered hook don't cause an infinite loop
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether the unregistering succeeded.
|
||||
]]
|
||||
function CAMI.UnregisterUsergroup(usergroupName, source)
|
||||
if not usergroups[usergroupName] then return false end
|
||||
|
||||
local usergroup = usergroups[usergroupName]
|
||||
usergroups[usergroupName] = nil
|
||||
|
||||
hook.Call("CAMI.OnUsergroupUnregistered", nil, usergroup, source)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetUsergroups
|
||||
Retrieves all registered usergroups.
|
||||
|
||||
Return value:
|
||||
Table of CAMI_USERGROUP, indexed by their names.
|
||||
]]
|
||||
function CAMI.GetUsergroups()
|
||||
return usergroups
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetUsergroup
|
||||
Receives information about a usergroup.
|
||||
|
||||
Return value:
|
||||
CAMI_USERGROUP
|
||||
Returns nil when the usergroup does not exist.
|
||||
]]
|
||||
function CAMI.GetUsergroup(usergroupName)
|
||||
return usergroups[usergroupName]
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UsergroupInherits
|
||||
Returns true when usergroupName1 inherits usergroupName2.
|
||||
Note that usergroupName1 does not need to be a direct child.
|
||||
Every usergroup trivially inherits itself.
|
||||
|
||||
Parameters:
|
||||
usergroupName1
|
||||
string
|
||||
The name of the usergroup that is queried.
|
||||
usergroupName2
|
||||
string
|
||||
The name of the usergroup of which is queried whether usergroupName1
|
||||
inherits from.
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether usergroupName1 inherits usergroupName2.
|
||||
]]
|
||||
function CAMI.UsergroupInherits(usergroupName1, usergroupName2)
|
||||
repeat
|
||||
if usergroupName1 == usergroupName2 then return true end
|
||||
|
||||
usergroupName1 = usergroups[usergroupName1] and
|
||||
usergroups[usergroupName1].Inherits or
|
||||
usergroupName1
|
||||
until not usergroups[usergroupName1] or
|
||||
usergroups[usergroupName1].Inherits == usergroupName1
|
||||
|
||||
-- One can only be sure the usergroup inherits from user if the
|
||||
-- usergroup isn't registered.
|
||||
return usergroupName1 == usergroupName2 or usergroupName2 == "user"
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.InheritanceRoot
|
||||
All usergroups must eventually inherit either user, admin or superadmin.
|
||||
Regardless of what inheritance mechism an admin may or may not have, this
|
||||
always applies.
|
||||
|
||||
This method always returns either user, admin or superadmin, based on what
|
||||
usergroups eventually inherit.
|
||||
|
||||
Parameters:
|
||||
usergroupName
|
||||
string
|
||||
The name of the usergroup of which the root of inheritance is
|
||||
requested
|
||||
|
||||
Return value:
|
||||
string
|
||||
The name of the root usergroup (either user, admin or superadmin)
|
||||
]]
|
||||
function CAMI.InheritanceRoot(usergroupName)
|
||||
if not usergroups[usergroupName] then return end
|
||||
|
||||
local inherits = usergroups[usergroupName].Inherits
|
||||
while inherits ~= usergroups[usergroupName].Inherits do
|
||||
usergroupName = usergroups[usergroupName].Inherits
|
||||
end
|
||||
|
||||
return usergroupName
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.RegisterPrivilege
|
||||
Registers a privilege with CAMI.
|
||||
Note: do NOT register all your admin mod's privileges with this function!
|
||||
This function is for third party addons to register privileges
|
||||
with admin mods, not for admin mods sharing the privileges amongst one
|
||||
another.
|
||||
|
||||
Parameters:
|
||||
privilege
|
||||
CAMI_PRIVILEGE
|
||||
See CAMI_PRIVILEGE structure.
|
||||
|
||||
Return value:
|
||||
CAMI_PRIVILEGE
|
||||
The privilege given as argument.
|
||||
]]
|
||||
function CAMI.RegisterPrivilege(privilege)
|
||||
privileges[privilege.Name] = privilege
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeRegistered", nil, privilege)
|
||||
|
||||
return privilege
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.UnregisterPrivilege
|
||||
Unregisters a privilege from CAMI. This will call a hook that will notify
|
||||
all other admin mods of the removal.
|
||||
|
||||
Call only when the privilege is to be permanently removed.
|
||||
|
||||
Parameters:
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
|
||||
Return value:
|
||||
bool
|
||||
Whether the unregistering succeeded.
|
||||
]]
|
||||
function CAMI.UnregisterPrivilege(privilegeName)
|
||||
if not privileges[privilegeName] then return false end
|
||||
|
||||
local privilege = privileges[privilegeName]
|
||||
privileges[privilegeName] = nil
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeUnregistered", nil, privilege)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPrivileges
|
||||
Retrieves all registered privileges.
|
||||
|
||||
Return value:
|
||||
Table of CAMI_PRIVILEGE, indexed by their names.
|
||||
]]
|
||||
function CAMI.GetPrivileges()
|
||||
return privileges
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPrivilege
|
||||
Receives information about a privilege.
|
||||
|
||||
Return value:
|
||||
CAMI_PRIVILEGE when the privilege exists.
|
||||
nil when the privilege does not exist.
|
||||
]]
|
||||
function CAMI.GetPrivilege(privilegeName)
|
||||
return privileges[privilegeName]
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.PlayerHasAccess
|
||||
Queries whether a certain player has the right to perform a certain action.
|
||||
Note: this function does NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
actorPly
|
||||
Player
|
||||
The player of which is requested whether they have the privilege.
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(bool, string)
|
||||
This function will be called with the answer. The bool signifies the
|
||||
yes or no answer as to whether the player is allowed. The string
|
||||
will optionally give a reason.
|
||||
targetPly
|
||||
Optional.
|
||||
The player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
Fallback
|
||||
string
|
||||
Either of user/admin/superadmin. When no admin mod replies,
|
||||
the decision is based on the admin status of the user.
|
||||
Defaults to admin if not given.
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
|
||||
Return value:
|
||||
None, the answer is given in the callback function in order to allow
|
||||
for the admin mod to perform e.g. a database lookup.
|
||||
]]
|
||||
-- Default access handler
|
||||
local defaultAccessHandler = {["CAMI.PlayerHasAccess"] =
|
||||
function(_, actorPly, privilegeName, callback, _, extraInfoTbl)
|
||||
-- The server always has access in the fallback
|
||||
if not IsValid(actorPly) then return callback(true, "Fallback.") end
|
||||
|
||||
local priv = privileges[privilegeName]
|
||||
|
||||
local fallback = extraInfoTbl and (
|
||||
not extraInfoTbl.Fallback and actorPly:IsAdmin() or
|
||||
extraInfoTbl.Fallback == "user" and true or
|
||||
extraInfoTbl.Fallback == "admin" and actorPly:IsAdmin() or
|
||||
extraInfoTbl.Fallback == "superadmin" and actorPly:IsSuperAdmin())
|
||||
|
||||
|
||||
if not priv then return callback(fallback, "Fallback.") end
|
||||
|
||||
callback(
|
||||
priv.MinAccess == "user" or
|
||||
priv.MinAccess == "admin" and actorPly:IsAdmin() or
|
||||
priv.MinAccess == "superadmin" and actorPly:IsSuperAdmin()
|
||||
, "Fallback.")
|
||||
end,
|
||||
["CAMI.SteamIDHasAccess"] =
|
||||
function(_, _, _, callback)
|
||||
callback(false, "No information available.")
|
||||
end
|
||||
}
|
||||
function CAMI.PlayerHasAccess(actorPly, privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
hook.Call("CAMI.PlayerHasAccess", defaultAccessHandler, actorPly,
|
||||
privilegeName, callback, targetPly, extraInfoTbl)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.GetPlayersWithAccess
|
||||
Finds the list of currently joined players who have the right to perform a
|
||||
certain action.
|
||||
NOTE: this function will NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(players)
|
||||
This function will be called with the list of players with access.
|
||||
targetPly
|
||||
Optional.
|
||||
The player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
Fallback
|
||||
string
|
||||
Either of user/admin/superadmin. When no admin mod replies,
|
||||
the decision is based on the admin status of the user.
|
||||
Defaults to admin if not given.
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
]]
|
||||
function CAMI.GetPlayersWithAccess(privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
local allowedPlys = {}
|
||||
local allPlys = player.GetAll()
|
||||
local countdown = #allPlys
|
||||
|
||||
local function onResult(ply, hasAccess, _)
|
||||
countdown = countdown - 1
|
||||
|
||||
if hasAccess then table.insert(allowedPlys, ply) end
|
||||
if countdown == 0 then callback(allowedPlys) end
|
||||
end
|
||||
|
||||
for _, ply in pairs(allPlys) do
|
||||
CAMI.PlayerHasAccess(ply, privilegeName,
|
||||
function(...) onResult(ply, ...) end,
|
||||
targetPly, extraInfoTbl)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SteamIDHasAccess
|
||||
Queries whether a player with a steam ID has the right to perform a certain
|
||||
action.
|
||||
Note: the player does not need to be in the server for this to
|
||||
work.
|
||||
|
||||
Note: this function does NOT return an immediate result!
|
||||
The result is in the callback!
|
||||
|
||||
Parameters:
|
||||
actorSteam
|
||||
Player
|
||||
The SteamID of the player of which is requested whether they have
|
||||
the privilege.
|
||||
privilegeName
|
||||
string
|
||||
The name of the privilege.
|
||||
callback
|
||||
function(bool, string)
|
||||
This function will be called with the answer. The bool signifies the
|
||||
yes or no answer as to whether the player is allowed. The string
|
||||
will optionally give a reason.
|
||||
targetSteam
|
||||
Optional.
|
||||
The SteamID of the player on which the privilege is executed.
|
||||
extraInfoTbl
|
||||
Optional.
|
||||
Table containing extra information.
|
||||
Officially supported members:
|
||||
IgnoreImmunity
|
||||
bool
|
||||
Ignore any immunity mechanisms an admin mod might have.
|
||||
CommandArguments
|
||||
table
|
||||
Extra arguments that were given to the privilege command.
|
||||
|
||||
Return value:
|
||||
None, the answer is given in the callback function in order to allow
|
||||
for the admin mod to perform e.g. a database lookup.
|
||||
]]
|
||||
function CAMI.SteamIDHasAccess(actorSteam, privilegeName, callback,
|
||||
targetSteam, extraInfoTbl)
|
||||
hook.Call("CAMI.SteamIDHasAccess", defaultAccessHandler, actorSteam,
|
||||
privilegeName, callback, targetSteam, extraInfoTbl)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SignalUserGroupChanged
|
||||
Signify that your admin mod has changed the usergroup of a player. This
|
||||
function communicates to other admin mods what it thinks the usergroup
|
||||
of a player should be.
|
||||
|
||||
Listen to the hook to receive the usergroup changes of other admin mods.
|
||||
|
||||
Parameters:
|
||||
ply
|
||||
Player
|
||||
The player for which the usergroup is changed
|
||||
old
|
||||
string
|
||||
The previous usergroup of the player.
|
||||
new
|
||||
string
|
||||
The new usergroup of the player.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
]]
|
||||
function CAMI.SignalUserGroupChanged(ply, old, new, source)
|
||||
hook.Call("CAMI.PlayerUsergroupChanged", nil, ply, old, new, source)
|
||||
end
|
||||
|
||||
--[[
|
||||
CAMI.SignalSteamIDUserGroupChanged
|
||||
Signify that your admin mod has changed the usergroup of a disconnected
|
||||
player. This communicates to other admin mods what it thinks the usergroup
|
||||
of a player should be.
|
||||
|
||||
Listen to the hook to receive the usergroup changes of other admin mods.
|
||||
|
||||
Parameters:
|
||||
ply
|
||||
string
|
||||
The steam ID of the player for which the usergroup is changed
|
||||
old
|
||||
string
|
||||
The previous usergroup of the player.
|
||||
new
|
||||
string
|
||||
The new usergroup of the player.
|
||||
source
|
||||
any
|
||||
Identifier for your own admin mod. Can be anything.
|
||||
]]
|
||||
function CAMI.SignalSteamIDUserGroupChanged(steamId, old, new, source)
|
||||
hook.Call("CAMI.SteamIDUsergroupChanged", nil, steamId, old, new, source)
|
||||
end
|
||||
@@ -1,267 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local timeMeasurementFunc = SysTime
|
||||
|
||||
-- Helper function
|
||||
-- Get all local variables
|
||||
local NIL = {}
|
||||
setmetatable(NIL, {__tostring = function() return "nil" end})
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Call counts:
|
||||
|
||||
registers how often function have been called
|
||||
---------------------------------------------------------------------------]]
|
||||
local callcounts = {}
|
||||
|
||||
|
||||
-- Gets the call counts
|
||||
FProfiler.Internal.getCallCounts = function() return callcounts end
|
||||
|
||||
|
||||
-- Resets the call counts
|
||||
function FProfiler.Internal.resetCallCounts()
|
||||
callcounts = {}
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Inclusive function times
|
||||
|
||||
Keeps track of how long functions take in total
|
||||
i.e. average time between the start and return of a function * times called
|
||||
|
||||
This includes the time that any function called by this function takes
|
||||
(that's what the "inclusive" refers to).
|
||||
Note: recursive calls are not counted double
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
local inclusiveTimes = {}
|
||||
|
||||
-- Gets the inclusive times
|
||||
FProfiler.Internal.getInclusiveTimes = function() return inclusiveTimes end
|
||||
|
||||
-- Resets the inclusive times
|
||||
function FProfiler.Internal.resetInclusiveTimes()
|
||||
inclusiveTimes = {}
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Top n most expensive single function calls
|
||||
Keeps track of the functions that took the longest time to run
|
||||
Note: functions can appear in this list at most once
|
||||
---------------------------------------------------------------------------]]
|
||||
local mostExpensiveSingleCalls = {}
|
||||
|
||||
-- Gets most expensive single calls
|
||||
FProfiler.Internal.getMostExpensiveSingleCalls = function() return mostExpensiveSingleCalls end
|
||||
|
||||
-- Dictionary to make sure the same function doesn't appear multiple times
|
||||
-- in the top n
|
||||
local mostExpensiveSingleDict = {}
|
||||
|
||||
function FProfiler.Internal.resetMostExpensiveSingleCalls()
|
||||
for i = 1, 50 do mostExpensiveSingleCalls[i] = {runtime = 0} end
|
||||
mostExpensiveSingleDict = {}
|
||||
end
|
||||
|
||||
-- Initial empty list
|
||||
FProfiler.Internal.resetMostExpensiveSingleCalls()
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Function information
|
||||
Using debug.getinfo on a function object won't give you any function names
|
||||
that's because functions can have multiple names.
|
||||
Luckily, when the functions are called, debug.getinfo(level) gives the
|
||||
function name and scope
|
||||
---------------------------------------------------------------------------]]
|
||||
local functionNames = {}
|
||||
|
||||
FProfiler.Internal.getFunctionNames = function() return functionNames end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Recursion depth
|
||||
|
||||
Used internally to make sure recursive functions' times aren't counted
|
||||
multiple times
|
||||
---------------------------------------------------------------------------]]
|
||||
local recursiveCount = {}
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Function start times
|
||||
|
||||
Used internally to keep track of when functions were called
|
||||
---------------------------------------------------------------------------]]
|
||||
local startTimes = {}
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Lua code event handlers
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
-- The recursion depth of the function that is in focus.
|
||||
-- Only applies when profiling a specific function (i.e. laying focus upon)
|
||||
local focusDepth = 0
|
||||
|
||||
-- Called when a function in the code is called
|
||||
local function registerFunctionCall(funcInfo)
|
||||
local func = funcInfo.func
|
||||
|
||||
-- Update call counts
|
||||
callcounts[func] = (callcounts[func] or 0) + 1
|
||||
|
||||
-- Increase recursion depth for this function
|
||||
recursiveCount[func] = (recursiveCount[func] or 0) + 1
|
||||
|
||||
-- Store function info
|
||||
local funcname = funcInfo.name or ""
|
||||
functionNames[func] = functionNames[func] or {}
|
||||
functionNames[func][funcname] = functionNames[func][funcname] or
|
||||
{ namewhat = funcInfo.namewhat,
|
||||
nparams = funcInfo.nparams
|
||||
}
|
||||
|
||||
local time = timeMeasurementFunc()
|
||||
|
||||
-- Update inclusive function times,
|
||||
-- only when we're on the first recursive call
|
||||
if recursiveCount[func] == 1 then
|
||||
startTimes[func] = time
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Called when a function returns
|
||||
local function registerReturn(funcInfo)
|
||||
local time = timeMeasurementFunc()
|
||||
local func = funcInfo.func
|
||||
local runtime = time - startTimes[func]
|
||||
|
||||
-- Update inclusive function time
|
||||
-- Only update on the topmost call, to prevent recursive
|
||||
-- calls for being counted multiple times.
|
||||
if recursiveCount[func] == 1 then
|
||||
inclusiveTimes[func] = (inclusiveTimes[func] or 0) + runtime
|
||||
end
|
||||
|
||||
-- Maintain recursion depth
|
||||
recursiveCount[func] = recursiveCount[func] - 1
|
||||
|
||||
-- Update top n list
|
||||
-- This path will be taken most often: the function isn't special
|
||||
-- Also only counts the top recursion
|
||||
if runtime <= mostExpensiveSingleCalls[50].runtime or recursiveCount[func] > 1 then return end
|
||||
|
||||
-- If the function already appears in the top 10, replace it or discard the result
|
||||
if mostExpensiveSingleDict[func] then
|
||||
local i = mostExpensiveSingleDict[func]
|
||||
|
||||
-- Discard this info
|
||||
if runtime < mostExpensiveSingleCalls[i].runtime then return end
|
||||
|
||||
-- Update the entry
|
||||
mostExpensiveSingleCalls[i].runtime = runtime
|
||||
mostExpensiveSingleCalls[i].info = funcInfo
|
||||
mostExpensiveSingleCalls[i].func = func
|
||||
|
||||
-- Move the updated entry up the top 10 list if applicable
|
||||
while i > 1 and runtime > mostExpensiveSingleCalls[i - 1].runtime do
|
||||
mostExpensiveSingleDict[mostExpensiveSingleCalls[i - 1].func] = i
|
||||
mostExpensiveSingleCalls[i - 1], mostExpensiveSingleCalls[i] = mostExpensiveSingleCalls[i], mostExpensiveSingleCalls[i - 1]
|
||||
i = i - 1
|
||||
end
|
||||
|
||||
mostExpensiveSingleDict[func] = i
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Knowing that the function belongs in the top n, find its position
|
||||
local i = 50
|
||||
while i >= 1 and runtime > mostExpensiveSingleCalls[i].runtime do
|
||||
-- Update the dictionary
|
||||
-- All functions faster than the current one move down the list
|
||||
if not mostExpensiveSingleCalls[i].func then i = i - 1 continue end
|
||||
mostExpensiveSingleDict[mostExpensiveSingleCalls[i].func] = i + 1
|
||||
|
||||
i = i - 1
|
||||
end
|
||||
|
||||
-- Insert the expensive call in the top n
|
||||
mostExpensiveSingleDict[func] = i + 1
|
||||
table.insert(mostExpensiveSingleCalls, i + 1,
|
||||
{
|
||||
func = func,
|
||||
runtime = runtime,
|
||||
info = funcInfo,
|
||||
})
|
||||
|
||||
|
||||
-- What was previously the 50th most expensive function
|
||||
-- is now kicked out of the top 10
|
||||
if mostExpensiveSingleCalls[51].func then
|
||||
mostExpensiveSingleDict[mostExpensiveSingleCalls[51].func] = nil
|
||||
end
|
||||
mostExpensiveSingleCalls[51] = nil
|
||||
end
|
||||
|
||||
|
||||
-- Called on any Lua event
|
||||
local function onLuaEvent(event, focus)
|
||||
local info = debug.getinfo(3)
|
||||
local func = info.func
|
||||
|
||||
if event == "call" or event == "tail call" then
|
||||
-- Only track the focussed function and the functions
|
||||
-- called by the focussed function
|
||||
if focus == func then focusDepth = focusDepth + 1 end
|
||||
if focus and focusDepth == 0 then return end
|
||||
|
||||
registerFunctionCall(info)
|
||||
else
|
||||
-- Functions that return right after the call to FProfiler.Internal.start()
|
||||
-- are not to be counted
|
||||
if not recursiveCount[func] or recursiveCount[func] == 0 then return end
|
||||
|
||||
if focus == func then focusDepth = focusDepth - 1 end
|
||||
if focus and focusDepth == 0 then return end
|
||||
|
||||
registerReturn(info)
|
||||
end
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Profiling control
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
-- Start profiling
|
||||
-- focus: only measure data of everything that happens within a certain function
|
||||
function FProfiler.Internal.start(focus)
|
||||
-- Empty start times, so unfinished functions aren't
|
||||
-- registered as returns on a second profiling session
|
||||
-- local time = SysTime()
|
||||
-- for k,v in pairs(startTimes) do startTimes[k] = time end
|
||||
table.Empty(startTimes)
|
||||
table.Empty(recursiveCount)
|
||||
|
||||
debug.sethook(function(event) onLuaEvent(event, focus) end, "cr")
|
||||
end
|
||||
|
||||
|
||||
-- Stop profiling
|
||||
function FProfiler.Internal.stop()
|
||||
debug.sethook()
|
||||
end
|
||||
|
||||
-- Reset all profiling data
|
||||
function FProfiler.Internal.reset()
|
||||
FProfiler.Internal.resetCallCounts()
|
||||
FProfiler.Internal.resetInclusiveTimes()
|
||||
FProfiler.Internal.resetMostExpensiveSingleCalls()
|
||||
end
|
||||
@@ -1,594 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Based on MDave's thing
|
||||
-- https://gist.github.com/mentlerd/d56ad9e6361f4b86af84
|
||||
if SERVER then AddCSLuaFile() end
|
||||
|
||||
local type_weight = {
|
||||
[TYPE_FUNCTION] = 1,
|
||||
[TYPE_TABLE] = 2,
|
||||
}
|
||||
|
||||
local type_colors = {
|
||||
[TYPE_BOOL] = Color(175, 130, 255),
|
||||
[TYPE_NUMBER] = Color(175, 130, 255),
|
||||
[TYPE_STRING] = Color(230, 220, 115),
|
||||
[TYPE_FUNCTION] = Color(100, 220, 240)
|
||||
}
|
||||
|
||||
local color_neutral = Color(220, 220, 220)
|
||||
local color_name = Color(260, 150, 30)
|
||||
|
||||
local color_reference = Color(150, 230, 50)
|
||||
local color_comment = Color( 30, 210, 30)
|
||||
|
||||
-- 'nil' value
|
||||
local NIL = {}
|
||||
|
||||
-- Localise for faster access
|
||||
local pcall = pcall
|
||||
|
||||
local string_len = string.len
|
||||
local string_sub = string.sub
|
||||
local string_find = string.find
|
||||
|
||||
local table_concat = table.concat
|
||||
local table_insert = table.insert
|
||||
local table_sort = table.sort
|
||||
|
||||
|
||||
-- Stream interface
|
||||
local gMsgF -- Print fragment
|
||||
local gMsgN -- Print newline
|
||||
local gMsgC -- Set print color
|
||||
|
||||
local PrintLocals, gBegin, gFinish, PrintTableGrep
|
||||
|
||||
do
|
||||
local grep_color = Color(235, 70, 70)
|
||||
|
||||
-- Grep parameters (static between gBegin/gEnd)
|
||||
local grep
|
||||
local grep_raw
|
||||
|
||||
local grep_proximity
|
||||
|
||||
|
||||
-- Current line parameters
|
||||
local buffer
|
||||
local colors
|
||||
local markers
|
||||
|
||||
local baseColor
|
||||
local currColor
|
||||
|
||||
local length
|
||||
|
||||
-- History
|
||||
local history
|
||||
local remain
|
||||
|
||||
|
||||
-- Actual printing
|
||||
local function gCheckMatch( buffer )
|
||||
local raw = table_concat(buffer)
|
||||
|
||||
return raw, string_find(raw, grep, 0, grep_raw)
|
||||
end
|
||||
|
||||
local function gFlushEx( raw, markers, colors, baseColor )
|
||||
|
||||
-- Print entire buffer
|
||||
local len = string_len(raw)
|
||||
|
||||
-- Keep track of the current line properties
|
||||
local index = 1
|
||||
local marker = 1
|
||||
|
||||
local currColor = baseColor
|
||||
|
||||
-- Method to print to a preset area
|
||||
local function printToIndex( limit, color )
|
||||
local mark = markers and markers[marker]
|
||||
|
||||
-- Print all marker areas until we would overshoot
|
||||
while mark and mark < limit do
|
||||
|
||||
-- Catch up to the marker
|
||||
MsgC(color or currColor or color_neutral, string_sub(raw, index, mark))
|
||||
index = mark +1
|
||||
|
||||
-- Set new color
|
||||
currColor = colors[marker]
|
||||
|
||||
-- Select next marker
|
||||
marker = marker +1
|
||||
mark = markers[marker]
|
||||
|
||||
end
|
||||
|
||||
-- Print the remaining between the last marker and the limit
|
||||
MsgC(color or currColor or color_neutral, string_sub(raw, index, limit))
|
||||
index = limit +1
|
||||
end
|
||||
|
||||
-- Grep!
|
||||
local match, last = 1
|
||||
local from, to = string_find(raw, grep, 0, grep_raw)
|
||||
|
||||
while from do
|
||||
printToIndex(from -1)
|
||||
printToIndex(to, grep_color)
|
||||
|
||||
last = to +1
|
||||
from, to = string_find(raw, grep, last, grep_raw)
|
||||
end
|
||||
|
||||
printToIndex(len)
|
||||
MsgN()
|
||||
end
|
||||
|
||||
|
||||
local function gCommit()
|
||||
if grep_proximity then
|
||||
-- Check if the line has at least one match
|
||||
local raw, match = gCheckMatch(buffer)
|
||||
|
||||
if match then
|
||||
|
||||
-- Divide matches
|
||||
if history[grep_proximity] then
|
||||
MsgN("...")
|
||||
end
|
||||
|
||||
-- Flush history
|
||||
if grep_proximity ~= 0 then
|
||||
local len = #history
|
||||
|
||||
for index = len -1, 1, -1 do
|
||||
local entry = history[index]
|
||||
history[index] = nil
|
||||
|
||||
gFlushEx( entry[1], entry[2], entry[3], entry[4] )
|
||||
end
|
||||
|
||||
history[len] = nil
|
||||
end
|
||||
|
||||
-- Flush line, allow next X lines to get printed
|
||||
gFlushEx( raw, markers, colors, baseColor )
|
||||
remain = grep_proximity -1
|
||||
|
||||
history[grep_proximity +1] = nil
|
||||
elseif remain > 0 then
|
||||
-- Flush immediately
|
||||
gFlushEx( raw, markers, colors, baseColor )
|
||||
remain = remain -1
|
||||
else
|
||||
-- Store in history
|
||||
table_insert(history, 1, {raw, markers, colors, baseColor})
|
||||
history[grep_proximity +1] = nil
|
||||
end
|
||||
else
|
||||
-- Flush anyway
|
||||
gFlushEx( table_concat(buffer), markers, colors, baseColor )
|
||||
end
|
||||
|
||||
-- Reset state
|
||||
length = 0
|
||||
buffer = {}
|
||||
|
||||
markers = nil
|
||||
colors = nil
|
||||
|
||||
baseColor = nil
|
||||
currColor = nil
|
||||
end
|
||||
|
||||
-- State machine
|
||||
function gBegin( new, prox )
|
||||
grep = isstring(new) and new
|
||||
|
||||
if grep then
|
||||
grep_raw = not pcall(string_find, ' ', grep)
|
||||
grep_proximity = isnumber(prox) and prox
|
||||
|
||||
-- Reset everything
|
||||
buffer = {}
|
||||
history = {}
|
||||
end
|
||||
|
||||
length = 0
|
||||
remain = 0
|
||||
|
||||
baseColor = nil
|
||||
currColor = nil
|
||||
end
|
||||
|
||||
function gFinish()
|
||||
if grep_proximity and history and history[1] then
|
||||
MsgN("...")
|
||||
end
|
||||
|
||||
-- Free memory
|
||||
buffer = nil
|
||||
markers = nil
|
||||
colors = nil
|
||||
|
||||
history = nil
|
||||
end
|
||||
|
||||
|
||||
function gMsgC( color )
|
||||
if grep then
|
||||
|
||||
-- Try to save some memory by not immediately allocating colors
|
||||
if length == 0 then
|
||||
baseColor = color
|
||||
return
|
||||
end
|
||||
|
||||
-- Record color change
|
||||
if color ~= currColor then
|
||||
if not markers then
|
||||
markers = {}
|
||||
colors = {}
|
||||
end
|
||||
|
||||
-- Record color change
|
||||
table_insert(markers, length)
|
||||
table_insert(colors, color)
|
||||
end
|
||||
end
|
||||
|
||||
currColor = color
|
||||
end
|
||||
|
||||
function gMsgF( str )
|
||||
if grep then
|
||||
|
||||
-- Split multiline fragments to separate ones
|
||||
local fragColor = currColor or baseColor
|
||||
|
||||
local last = 1
|
||||
local from, to = string_find(str, '\n')
|
||||
|
||||
while from do
|
||||
local frag = string_sub(str, last, from -1)
|
||||
local len = from - last
|
||||
|
||||
-- Merge fragment to the line
|
||||
length = length + len
|
||||
table_insert(buffer, frag)
|
||||
|
||||
-- Print finished line
|
||||
gCommit()
|
||||
|
||||
-- Assign base color as previous fragColor
|
||||
baseColor = fragColor
|
||||
|
||||
-- Look for more
|
||||
last = to +1
|
||||
from, to = string_find(str, '\n', last)
|
||||
end
|
||||
|
||||
-- Push last fragment
|
||||
local frag = string_sub(str, last)
|
||||
local len = string_len(str) - last +1
|
||||
|
||||
length = length + len
|
||||
table_insert(buffer, frag)
|
||||
else
|
||||
-- Push immediately
|
||||
MsgC(currColor or baseColor or color_neutral, str)
|
||||
end
|
||||
end
|
||||
|
||||
function gMsgN()
|
||||
-- Print everything in the buffer
|
||||
if grep then
|
||||
gCommit()
|
||||
else
|
||||
MsgN()
|
||||
end
|
||||
|
||||
baseColor = nil
|
||||
currColor = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function InternalPrintValue( value )
|
||||
|
||||
-- 'nil' values can also be printed
|
||||
if value == NIL then
|
||||
gMsgC(color_comment)
|
||||
gMsgF("nil")
|
||||
return
|
||||
end
|
||||
|
||||
local color = type_colors[ TypeID(value) ]
|
||||
|
||||
-- For strings, place quotes
|
||||
if isstring(value) then
|
||||
if string_len(value) <= 1 then
|
||||
value = string.format([['%s']], value)
|
||||
else
|
||||
value = string.format([["%s"]], value)
|
||||
end
|
||||
|
||||
gMsgC(color)
|
||||
gMsgF(value)
|
||||
return
|
||||
end
|
||||
|
||||
-- Workaround for userdata not using MetaName
|
||||
if string_sub(tostring(value), 0, 8) == "userdata" then
|
||||
local meta = getmetatable(value)
|
||||
|
||||
if meta and meta.MetaName then
|
||||
value = string.format("%s: %p", meta.MetaName, value)
|
||||
end
|
||||
end
|
||||
|
||||
-- General print
|
||||
gMsgC(color)
|
||||
gMsgF(tostring(value))
|
||||
|
||||
-- For functions append source info
|
||||
if isfunction(value) then
|
||||
local info = debug.getinfo(value, 'S')
|
||||
local aux
|
||||
|
||||
if info.what == 'C' then
|
||||
aux = "\t-- [C]: -1"
|
||||
else
|
||||
if info.linedefined ~= info.lastlinedefined then
|
||||
aux = string.format("\t-- [%s]: %i-%i", info.short_src, info.linedefined, info.lastlinedefined)
|
||||
else
|
||||
aux = string.format("\t-- [%s]: %i", info.short_src, info.linedefined)
|
||||
end
|
||||
end
|
||||
|
||||
gMsgC(color_comment)
|
||||
gMsgF(aux)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Associated to object keys
|
||||
local objID
|
||||
|
||||
local function isprimitive( value )
|
||||
local id = TypeID(value)
|
||||
|
||||
return id <= TYPE_FUNCTION and id ~= TYPE_TABLE
|
||||
end
|
||||
|
||||
local function InternalPrintTable( table, path, prefix, names, todo )
|
||||
|
||||
-- Collect keys and some info about them
|
||||
local keyList = {}
|
||||
local keyStr = {}
|
||||
|
||||
local keyCount = 0
|
||||
|
||||
for key, value in pairs( table ) do
|
||||
-- Add to key list for later sorting
|
||||
table_insert(keyList, key)
|
||||
|
||||
-- Describe key as string
|
||||
if isprimitive(key) then
|
||||
keyStr[key] = tostring(key)
|
||||
else
|
||||
-- Lookup already known name
|
||||
local name = names[key]
|
||||
|
||||
-- Assign a new unique identifier
|
||||
if not name then
|
||||
objID = objID +1
|
||||
name = string.format("%s: obj #%i", tostring(key), objID)
|
||||
|
||||
names[key] = name
|
||||
todo[key] = true
|
||||
end
|
||||
|
||||
-- Substitute object with name
|
||||
keyStr[key] = name
|
||||
end
|
||||
|
||||
keyCount = keyCount +1
|
||||
end
|
||||
|
||||
|
||||
-- Exit early for empty tables
|
||||
if keyCount == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- Determine max key length
|
||||
local keyLen = 4
|
||||
|
||||
for key, str in pairs(keyStr) do
|
||||
keyLen = math.max(keyLen, string.len(str))
|
||||
end
|
||||
|
||||
-- Sort table keys
|
||||
if keyCount > 1 then
|
||||
table_sort( keyList, function( A, B )
|
||||
|
||||
-- Sort numbers numerically correct
|
||||
if isnumber(A) and isnumber(B) then
|
||||
return A < B
|
||||
end
|
||||
|
||||
-- Weight types
|
||||
local wA = type_weight[ TypeID( table[A] ) ] or 0
|
||||
local wB = type_weight[ TypeID( table[B] ) ] or 0
|
||||
|
||||
if wA ~= wB then
|
||||
return wA < wB
|
||||
end
|
||||
|
||||
-- Order by string representation
|
||||
return keyStr[A] < keyStr[B]
|
||||
|
||||
end )
|
||||
end
|
||||
|
||||
-- Determine the next level ident
|
||||
local new_prefix = string.format( "%s║%s", prefix, string.rep(' ', keyLen) )
|
||||
|
||||
-- Mark object as done
|
||||
todo[table] = nil
|
||||
|
||||
-- Start describing table
|
||||
for index, key in ipairs(keyList) do
|
||||
local value = table[key]
|
||||
|
||||
-- Assign names to already described keys/values
|
||||
local kName = names[key]
|
||||
local vName = names[value]
|
||||
|
||||
-- Decide to either fully describe, or print the value
|
||||
local describe = not isprimitive(value) and ( not vName or todo[value] )
|
||||
|
||||
-- Ident
|
||||
gMsgF(prefix)
|
||||
|
||||
-- Fancy table guides
|
||||
local moreLines = (index ~= keyCount) or describe
|
||||
|
||||
if index == 1 then
|
||||
gMsgF(moreLines and '╦ ' or '═ ')
|
||||
else
|
||||
gMsgF(moreLines and '╠ ' or '╚ ')
|
||||
end
|
||||
|
||||
-- Print key
|
||||
local sKey = kName or keyStr[key]
|
||||
|
||||
gMsgC(kName and color_reference or color_name)
|
||||
gMsgF(sKey)
|
||||
|
||||
-- Describe non primitives
|
||||
describe = istable(value) and ( not names[value] or todo[value] ) and value ~= NIL
|
||||
|
||||
-- Print key postfix
|
||||
local padding = keyLen - string.len(sKey)
|
||||
local postfix = string.format(describe and ":%s" or "%s = ", string.rep(' ', padding))
|
||||
|
||||
gMsgC(color_neutral)
|
||||
gMsgF(postfix)
|
||||
|
||||
-- Print the value
|
||||
if describe then
|
||||
gMsgN()
|
||||
|
||||
-- Expand access path
|
||||
local new_path = sKey
|
||||
|
||||
if isnumber(key) or kName then
|
||||
new_path = string.format("%s[%s]", path or '', key)
|
||||
elseif path then
|
||||
new_path = string.format("%s.%s", path, new_path)
|
||||
end
|
||||
|
||||
-- Name the object to mark it done
|
||||
names[value] = names[value] or new_path
|
||||
|
||||
-- Describe
|
||||
InternalPrintTable(value, new_path, new_prefix, names, todo)
|
||||
else
|
||||
-- Print the value (or the reference name)
|
||||
if vName and not todo[value] then
|
||||
gMsgC(color_reference)
|
||||
gMsgF(string.format("ref: %s",vName))
|
||||
else
|
||||
InternalPrintValue(value)
|
||||
end
|
||||
|
||||
gMsgN()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PrintTableGrep( table, grep, proximity )
|
||||
local base = {
|
||||
[_G] = "_G",
|
||||
[table] = "root"
|
||||
}
|
||||
|
||||
gBegin(grep, proximity)
|
||||
objID = 0
|
||||
InternalPrintTable(table, nil, "", base, {})
|
||||
gFinish()
|
||||
end
|
||||
|
||||
function PrintLocals( level )
|
||||
local level = level or 2
|
||||
local hash = {}
|
||||
|
||||
for index = 1, 255 do
|
||||
local name, value = debug.getlocal(2, index)
|
||||
|
||||
if not name then
|
||||
break
|
||||
end
|
||||
|
||||
if value == nil then
|
||||
value = NIL
|
||||
end
|
||||
|
||||
hash[name] = value
|
||||
end
|
||||
|
||||
PrintTableGrep( hash )
|
||||
end
|
||||
|
||||
function show(...)
|
||||
local n = select('#', ...)
|
||||
local tbl = {...}
|
||||
|
||||
for i = 1, n do
|
||||
if istable(tbl[i]) then MsgN(tostring(tbl[i])) PrintTableGrep(tbl[i])
|
||||
else InternalPrintValue(tbl[i]) MsgN() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Hacky way of creating a pretty string from the above code
|
||||
-- because I don't feel like refactoring the entire thing
|
||||
local strResult
|
||||
local toStringMsgF = function(txt)
|
||||
table.insert(strResult, txt)
|
||||
end
|
||||
|
||||
local toStringMsgN = function()
|
||||
table.insert(strResult, "\n")
|
||||
end
|
||||
|
||||
local toStringMsgC = function(_, txt)
|
||||
table.insert(strResult, txt)
|
||||
end
|
||||
|
||||
function showStr(...)
|
||||
local oldF, oldN, oldMsgC, oldMsgN = gMsgF, gMsgN, MsgC, MsgN
|
||||
gMsgF, gMsgN, MsgC, MsgN = toStringMsgF, toStringMsgN, toStringMsgC, toStringMsgN
|
||||
|
||||
strResult = {}
|
||||
show(...)
|
||||
|
||||
gMsgF, gMsgN, MsgC, MsgN = oldF, oldN, oldMsgC, oldMsgN
|
||||
|
||||
return table.concat(strResult, "")
|
||||
end
|
||||
@@ -1,114 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local function getData()
|
||||
local callCounts = FProfiler.Internal.getCallCounts()
|
||||
local inclusiveTimes = FProfiler.Internal.getInclusiveTimes()
|
||||
local funcNames = FProfiler.Internal.getFunctionNames()
|
||||
|
||||
local data = {}
|
||||
for func, called in pairs(callCounts) do
|
||||
local row = {}
|
||||
row.func = func
|
||||
row.info = debug.getinfo(func, "nfS")
|
||||
row.total_called = called
|
||||
row.total_time = inclusiveTimes[func] or 0
|
||||
row.average_time = row.total_time / row.total_called
|
||||
|
||||
row.name, row.namewhat = nil, nil
|
||||
|
||||
row.names = {}
|
||||
for name, namedata in pairs(funcNames[func]) do
|
||||
table.insert(row.names, {name = name, namewhat = namedata.namewhat, nparams = namedata.nparams})
|
||||
end
|
||||
|
||||
table.insert(data, row)
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
local function cull(data, count)
|
||||
if not count then return data end
|
||||
|
||||
for i = count + 1, #data do
|
||||
data[i] = nil
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The functions that are called most often
|
||||
Their implementations are O(n lg n),
|
||||
which is probably suboptimal but not worth my time optimising.
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.Internal.mostOftenCalled(count)
|
||||
local sorted = getData()
|
||||
|
||||
table.SortByMember(sorted, "total_called")
|
||||
|
||||
return cull(sorted, count)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The functions that take the longest time in total
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.Internal.mostTimeInclusive(count)
|
||||
local sorted = getData()
|
||||
|
||||
table.SortByMember(sorted, "total_time")
|
||||
|
||||
return cull(sorted, count)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The functions that take the longest average time
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.Internal.mostTimeInclusiveAverage(count)
|
||||
local sorted = getData()
|
||||
|
||||
table.SortByMember(sorted, "average_time")
|
||||
|
||||
return cull(sorted, count)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Get the top <count> of most often called, time inclusive and average
|
||||
NOTE: This will almost definitely return more than <count> results.
|
||||
Up to three times <count> is possible.
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.Internal.getAggregatedResults(count)
|
||||
count = count or 100
|
||||
|
||||
local dict = {}
|
||||
local mostTime = FProfiler.Internal.mostTimeInclusive(count)
|
||||
for i = 1, #mostTime do dict[mostTime[i].func] = true end
|
||||
|
||||
local mostAvg = FProfiler.Internal.mostTimeInclusiveAverage(count)
|
||||
|
||||
for i = 1, #mostAvg do
|
||||
if dict[mostAvg[i].func] then continue end
|
||||
dict[mostAvg[i].func] = true
|
||||
table.insert(mostTime, mostAvg[i])
|
||||
end
|
||||
|
||||
local mostCalled = FProfiler.Internal.mostOftenCalled(count)
|
||||
|
||||
for i = 1, #mostCalled do
|
||||
if dict[mostCalled[i].func] then continue end
|
||||
dict[mostCalled[i].func] = true
|
||||
table.insert(mostTime, mostCalled[i])
|
||||
end
|
||||
|
||||
table.SortByMember(mostTime, "total_time")
|
||||
|
||||
return mostTime
|
||||
end
|
||||
@@ -1,110 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local get, update, onUpdate = FProfiler.UI.getModelValue, FProfiler.UI.updateModel, FProfiler.UI.onModelUpdate
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
(Re)start clientside profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
local function restartProfiling()
|
||||
if get({"client", "shouldReset"}) then
|
||||
FProfiler.Internal.reset()
|
||||
update({"client", "recordTime"}, 0)
|
||||
end
|
||||
|
||||
local focus = get({"client", "focusObj"})
|
||||
|
||||
update({"client", "sessionStart"}, CurTime())
|
||||
update({"client", "sessionStartSysTime"}, SysTime())
|
||||
FProfiler.Internal.start(focus)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Stop profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
local function stopProfiling()
|
||||
FProfiler.Internal.stop()
|
||||
|
||||
local newTime = get({"client", "recordTime"}) + SysTime() - (get({"client", "sessionStartSysTime"}) or 0)
|
||||
|
||||
-- Get the aggregated data
|
||||
local mostTime = FProfiler.Internal.getAggregatedResults(100)
|
||||
|
||||
update({"client", "bottlenecks"}, mostTime)
|
||||
update({"client", "topLagSpikes"}, FProfiler.Internal.getMostExpensiveSingleCalls())
|
||||
|
||||
update({"client", "recordTime"}, newTime)
|
||||
update({"client", "sessionStart"}, nil)
|
||||
update({"client", "sessionStartSysTime"}, nil)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Start/stop recording when the recording status is changed
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"client", "status"}, function(new, old)
|
||||
if new == old then return end
|
||||
(new == "Started" and restartProfiling or stopProfiling)()
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Update the current selected focus object when data is entered
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"client", "focusStr"}, function(new)
|
||||
update({"client", "focusObj"}, FProfiler.funcNameToObj(new))
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Update info when a different line is selected
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"client", "currentSelected"}, function(new)
|
||||
if not new or not new.info or not new.info.linedefined or not new.info.lastlinedefined or not new.info.short_src then return end
|
||||
|
||||
update({"client", "sourceText"}, FProfiler.readSource(new.info.short_src, new.info.linedefined, new.info.lastlinedefined))
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
When a function is to be printed to console
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"client", "toConsole"}, function(data)
|
||||
if not data then return end
|
||||
|
||||
update({"client", "toConsole"}, nil)
|
||||
show(data)
|
||||
|
||||
file.CreateDir("fprofiler")
|
||||
file.Write("fprofiler/profiledata.txt", showStr(data))
|
||||
MsgC(Color(200, 200, 200), "-----", Color(120, 120, 255), "NOTE", Color(200, 200, 200), "---------------\n")
|
||||
MsgC(Color(200, 200, 200), "If the above function does not fit in console, you can find it in data/fprofiler/profiledata.txt\n\n")
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
API function: start profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.start(focus)
|
||||
update({"client", "focusStr"}, tostring(focus))
|
||||
update({"client", "focusObj"}, focus)
|
||||
update({"client", "shouldReset"}, true)
|
||||
update({"client", "status"}, "Started")
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
API function: stop profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.stop()
|
||||
update({"client", "status"}, "Stopped")
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
API function: continue profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.continueProfiling()
|
||||
update({"client", "shouldReset"}, false)
|
||||
update({"client", "status"}, "Started")
|
||||
end
|
||||
@@ -1,475 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The panel that contains the realm switcher
|
||||
---------------------------------------------------------------------------]]
|
||||
local REALMPANEL = {}
|
||||
|
||||
function REALMPANEL:Init()
|
||||
self:DockPadding(0, 0, 0, 0)
|
||||
self:DockMargin(0, 0, 5, 0)
|
||||
|
||||
self.realmLabel = vgui.Create("DLabel", self)
|
||||
self.realmLabel:SetDark(true)
|
||||
self.realmLabel:SetText("Realm:")
|
||||
|
||||
self.realmLabel:SizeToContents()
|
||||
self.realmLabel:Dock(TOP)
|
||||
|
||||
self.realmbox = vgui.Create("DComboBox", self)
|
||||
self.realmbox:AddChoice("Client")
|
||||
self.realmbox:AddChoice("Server")
|
||||
self.realmbox:Dock(TOP)
|
||||
|
||||
FProfiler.UI.onModelUpdate("realm", function(new)
|
||||
self.realmbox.selected = new == "client" and 1 or 2
|
||||
self.realmbox:SetText(new == "client" and "Client" or "Server")
|
||||
end)
|
||||
|
||||
FProfiler.UI.onModelUpdate("serverAccess", function(hasAccess)
|
||||
self.realmbox:SetDisabled(not hasAccess)
|
||||
|
||||
if not hasAccess and self.realmbox.selected == 2 then
|
||||
FProfiler.UI.updateModel("realm", "client")
|
||||
end
|
||||
end)
|
||||
|
||||
self.realmbox.OnSelect = function(_, _, value) FProfiler.UI.updateModel("realm", string.lower(value)) end
|
||||
end
|
||||
|
||||
function REALMPANEL:PerformLayout()
|
||||
self.realmLabel:SizeToContents()
|
||||
local top = ( self:GetTall() - self.realmLabel:GetTall() - self.realmbox:GetTall()) * 0.5
|
||||
self:DockPadding(0, top, 0, 0)
|
||||
end
|
||||
|
||||
derma.DefineControl("FProfileRealmPanel", "", REALMPANEL, "Panel")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The little red or green indicator that indicates whether the focussing
|
||||
function is correct
|
||||
---------------------------------------------------------------------------]]
|
||||
local FUNCINDICATOR = {}
|
||||
|
||||
function FUNCINDICATOR:Init()
|
||||
self:SetTall(5)
|
||||
self.color = Color(0, 0, 0, 0)
|
||||
end
|
||||
|
||||
function FUNCINDICATOR:Paint()
|
||||
draw.RoundedBox(0, 0, 0, self:GetWide(), self:GetTall(), self.color)
|
||||
end
|
||||
|
||||
derma.DefineControl("FProfileFuncIndicator", "", FUNCINDICATOR, "DPanel")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The panel that contains the focus text entry and the focus indicator
|
||||
---------------------------------------------------------------------------]]
|
||||
local FOCUSPANEL = {}
|
||||
|
||||
function FOCUSPANEL:Init()
|
||||
self:DockPadding(0, 0, 0, 0)
|
||||
self:DockMargin(0, 0, 5, 0)
|
||||
|
||||
self.focusLabel = vgui.Create("DLabel", self)
|
||||
self.focusLabel:SetDark(true)
|
||||
self.focusLabel:SetText("Profiling Focus:")
|
||||
|
||||
self.focusLabel:SizeToContents()
|
||||
self.focusLabel:Dock(TOP)
|
||||
|
||||
self.funcIndicator = vgui.Create("FProfileFuncIndicator", self)
|
||||
self.funcIndicator:Dock(BOTTOM)
|
||||
|
||||
self.focusBox = vgui.Create("DTextEntry", self)
|
||||
self.focusBox:SetText("")
|
||||
self.focusBox:SetWidth(150)
|
||||
self.focusBox:Dock(BOTTOM)
|
||||
self.focusBox:SetTooltip("Focus the profiling on a single function.\nEnter a global function name here (like player.GetAll)\nYou're not allowed to call functions in here (e.g. hook.GetTable() is not allowed)")
|
||||
|
||||
function self.focusBox:OnChange()
|
||||
FProfiler.UI.updateCurrentRealm("focusStr", self:GetText())
|
||||
end
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("focusObj", function(new)
|
||||
self.funcIndicator.color = FProfiler.UI.getCurrentRealmValue("focusStr") == "" and Color(0, 0, 0, 0) or new and Color(80, 255, 80, 255) or Color(255, 80, 80, 255)
|
||||
end)
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("focusStr", function(new, old)
|
||||
if self.focusBox:GetText() == new then return end
|
||||
|
||||
self.focusBox:SetText(tostring(new))
|
||||
end)
|
||||
end
|
||||
|
||||
function FOCUSPANEL:PerformLayout()
|
||||
self.focusBox:SetWide(200)
|
||||
self.focusLabel:SizeToContents()
|
||||
end
|
||||
|
||||
derma.DefineControl("FProfileFocusPanel", "", FOCUSPANEL, "Panel")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The timer that keeps track of for how long the profiling has been going on
|
||||
---------------------------------------------------------------------------]]
|
||||
local TIMERPANEL = {}
|
||||
|
||||
function TIMERPANEL:Init()
|
||||
self:DockPadding(0, 5, 0, 5)
|
||||
self:DockMargin(5, 0, 5, 0)
|
||||
|
||||
self.timeLabel = vgui.Create("DLabel", self)
|
||||
self.timeLabel:SetDark(true)
|
||||
self.timeLabel:SetText("Total profiling time:")
|
||||
|
||||
self.timeLabel:SizeToContents()
|
||||
self.timeLabel:Dock(TOP)
|
||||
|
||||
self.counter = vgui.Create("DLabel", self)
|
||||
self.counter:SetDark(true)
|
||||
self.counter:SetText("00:00:00")
|
||||
self.counter:SizeToContents()
|
||||
self.counter:Dock(RIGHT)
|
||||
|
||||
function self.counter:Think()
|
||||
local recordTime, sessionStart = FProfiler.UI.getCurrentRealmValue("recordTime"), FProfiler.UI.getCurrentRealmValue("sessionStart")
|
||||
|
||||
local totalTime = recordTime + (sessionStart and (CurTime() - sessionStart) or 0)
|
||||
|
||||
self:SetText(string.FormattedTime(totalTime, "%02i:%02i:%02i"))
|
||||
end
|
||||
end
|
||||
|
||||
function TIMERPANEL:PerformLayout()
|
||||
self.timeLabel:SizeToContents()
|
||||
self.counter:SizeToContents()
|
||||
end
|
||||
|
||||
derma.DefineControl("FProfileTimerPanel", "", TIMERPANEL, "Panel")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The top bar
|
||||
---------------------------------------------------------------------------]]
|
||||
local MAGICBAR = {}
|
||||
|
||||
function MAGICBAR:Init()
|
||||
self:DockPadding(5, 5, 5, 5)
|
||||
self.realmpanel = vgui.Create("FProfileRealmPanel", self)
|
||||
|
||||
-- (Re)Start profiling
|
||||
self.restartProfiling = vgui.Create("DButton", self)
|
||||
self.restartProfiling:SetText(" (Re)Start\n Profiling")
|
||||
self.restartProfiling:DockMargin(0, 0, 5, 0)
|
||||
self.restartProfiling:Dock(LEFT)
|
||||
|
||||
self.restartProfiling.DoClick = function()
|
||||
FProfiler.UI.updateCurrentRealm("shouldReset", true)
|
||||
FProfiler.UI.updateCurrentRealm("status", "Started")
|
||||
end
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("status", function(new)
|
||||
self.restartProfiling:SetDisabled(new == "Started")
|
||||
end)
|
||||
|
||||
-- Stop profiling
|
||||
self.stopProfiling = vgui.Create("DButton", self)
|
||||
self.stopProfiling:SetText(" Stop\n Profiling")
|
||||
self.stopProfiling:DockMargin(0, 0, 5, 0)
|
||||
self.stopProfiling:Dock(LEFT)
|
||||
|
||||
self.stopProfiling.DoClick = function()
|
||||
FProfiler.UI.updateCurrentRealm("status", "Stopped")
|
||||
end
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("status", function(new)
|
||||
self.stopProfiling:SetDisabled(new == "Stopped")
|
||||
end)
|
||||
|
||||
-- Continue profiling
|
||||
self.continueProfiling = vgui.Create("DButton", self)
|
||||
self.continueProfiling:SetText(" Continue\n Profiling")
|
||||
self.continueProfiling:DockMargin(0, 0, 5, 0)
|
||||
self.continueProfiling:Dock(LEFT)
|
||||
|
||||
self.continueProfiling.DoClick = function()
|
||||
FProfiler.UI.updateCurrentRealm("shouldReset", false)
|
||||
FProfiler.UI.updateCurrentRealm("status", "Started")
|
||||
end
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("status", function(new)
|
||||
self.continueProfiling:SetDisabled(new == "Started")
|
||||
end)
|
||||
|
||||
self.realmpanel:Dock(LEFT)
|
||||
|
||||
self.focuspanel = vgui.Create("FProfileFocusPanel", self)
|
||||
self.focuspanel:Dock(LEFT)
|
||||
|
||||
-- Timer
|
||||
self.timerpanel = vgui.Create("FProfileTimerPanel", self)
|
||||
self.timerpanel:Dock(RIGHT)
|
||||
end
|
||||
|
||||
function MAGICBAR:PerformLayout()
|
||||
self.realmpanel:SizeToChildren(true, false)
|
||||
self.focuspanel:SizeToChildren(true, false)
|
||||
self.timerpanel:SizeToChildren(true, false)
|
||||
end
|
||||
|
||||
|
||||
derma.DefineControl("FProfileMagicBar", "", MAGICBAR, "DPanel")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
A custom sort by column function to deal with sorting by numeric value
|
||||
--------------------------------------------------------------------------]]
|
||||
local function SortByColumn(self, ColumnID, Desc)
|
||||
table.Copy(self.Sorted, self.Lines)
|
||||
|
||||
table.sort(self.Sorted, function(a, b)
|
||||
if Desc then
|
||||
a, b = b, a
|
||||
end
|
||||
|
||||
local aval = a:GetSortValue(ColumnID) or a:GetColumnText(ColumnID)
|
||||
local bval = b:GetSortValue(ColumnID) or b:GetColumnText(ColumnID)
|
||||
|
||||
local anum = tonumber(aval)
|
||||
local bnum = tonumber(bval)
|
||||
|
||||
if anum and bnum then
|
||||
return anum < bnum
|
||||
end
|
||||
|
||||
return tostring(aval) < tostring(bval)
|
||||
end)
|
||||
|
||||
self:SetDirty(true)
|
||||
self:InvalidateLayout()
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The Bottlenecks tab's contents
|
||||
---------------------------------------------------------------------------]]
|
||||
local BOTTLENECKTAB = {}
|
||||
|
||||
BOTTLENECKTAB.SortByColumn = SortByColumn
|
||||
|
||||
function BOTTLENECKTAB:Init()
|
||||
self:SetMultiSelect(false)
|
||||
self:AddColumn("Name")
|
||||
self:AddColumn("Path")
|
||||
self:AddColumn("Lines")
|
||||
self:AddColumn("Amount of times called")
|
||||
self:AddColumn("Total time in ms (inclusive)")
|
||||
self:AddColumn("Average time in ms (inclusive)")
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("bottlenecks", function(new)
|
||||
self:Clear()
|
||||
|
||||
for _, row in ipairs(new) do
|
||||
local names = {}
|
||||
local path = row.info.short_src
|
||||
local lines = path ~= "[C]" and row.info.linedefined .. " - " .. row.info.lastlinedefined or "N/A"
|
||||
local amountCalled = row.total_called
|
||||
local totalTime = row.total_time * 100
|
||||
local avgTime = row.average_time * 100
|
||||
|
||||
for _, fname in ipairs(row.names or {}) do
|
||||
if fname.namewhat == "" and fname.name == "" then continue end
|
||||
table.insert(names, fname.namewhat .. " " .. fname.name)
|
||||
end
|
||||
|
||||
if #names == 0 then names[1] = "Unknown" end
|
||||
|
||||
local line = self:AddLine(table.concat(names, "/"), path, lines, amountCalled, totalTime, avgTime)
|
||||
line.data = row
|
||||
end
|
||||
end)
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("currentSelected", function(new, old)
|
||||
if new == old then return end
|
||||
|
||||
for _, line in pairs(self.Lines) do
|
||||
line:SetSelected(line.data.func == new.func)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function BOTTLENECKTAB:OnRowSelected(id, line)
|
||||
FProfiler.UI.updateCurrentRealm("currentSelected", line.data)
|
||||
end
|
||||
|
||||
|
||||
derma.DefineControl("FProfileBottleNecks", "", BOTTLENECKTAB, "DListView")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The Top n lag spikes tab's contents
|
||||
---------------------------------------------------------------------------]]
|
||||
local TOPTENTAB = {}
|
||||
|
||||
TOPTENTAB.SortByColumn = SortByColumn
|
||||
|
||||
function TOPTENTAB:Init()
|
||||
self:SetMultiSelect(false)
|
||||
self:AddColumn("Name")
|
||||
self:AddColumn("Path")
|
||||
self:AddColumn("Lines")
|
||||
self:AddColumn("Runtime in ms")
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("topLagSpikes", function(new)
|
||||
self:Clear()
|
||||
|
||||
for _, row in ipairs(new) do
|
||||
if not row.func then break end
|
||||
|
||||
local name = row.info.name and row.info.name ~= "" and (row.info.namewhat .. " " .. row.info.name) or "Unknown"
|
||||
local path = row.info.short_src
|
||||
local lines = path ~= "[C]" and row.info.linedefined .. " - " .. row.info.lastlinedefined or "N/A"
|
||||
local runtime = row.runtime * 100
|
||||
|
||||
local line = self:AddLine(name, path, lines, runtime)
|
||||
line.data = row
|
||||
end
|
||||
end)
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("currentSelected", function(new, old)
|
||||
if new == old then return end
|
||||
|
||||
for _, line in pairs(self.Lines) do
|
||||
line:SetSelected(line.data.func == new.func)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function TOPTENTAB:OnRowSelected(id, line)
|
||||
FProfiler.UI.updateCurrentRealm("currentSelected", line.data)
|
||||
end
|
||||
|
||||
derma.DefineControl("FProfileTopTen", "", TOPTENTAB, "DListView")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The Tab panel of the bottlenecks and top n lag spikes
|
||||
---------------------------------------------------------------------------]]
|
||||
local RESULTSHEET = {}
|
||||
|
||||
function RESULTSHEET:Init()
|
||||
self:DockMargin(0, 10, 0, 0)
|
||||
self:SetPadding(0)
|
||||
|
||||
self.bottlenecksTab = vgui.Create("FProfileBottleNecks")
|
||||
self:AddSheet("Bottlenecks", self.bottlenecksTab)
|
||||
|
||||
self.toptenTab = vgui.Create("FProfileTopTen")
|
||||
self:AddSheet("Top 50 most expensive function calls", self.toptenTab)
|
||||
|
||||
end
|
||||
|
||||
|
||||
derma.DefineControl("FProfileResultSheet", "", RESULTSHEET, "DPropertySheet")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The function details panel
|
||||
---------------------------------------------------------------------------]]
|
||||
local FUNCDETAILS = {}
|
||||
|
||||
function FUNCDETAILS:Init()
|
||||
self.titleLabel = vgui.Create("DLabel", self)
|
||||
self.titleLabel:SetDark(true)
|
||||
self.titleLabel:SetFont("DermaLarge")
|
||||
self.titleLabel:SetText("Function Details")
|
||||
self.titleLabel:SizeToContents()
|
||||
-- self.titleLabel:Dock(TOP)
|
||||
|
||||
self.focus = vgui.Create("DButton", self)
|
||||
self.focus:SetText("Focus")
|
||||
self.focus:SetTall(50)
|
||||
self.focus:SetFont("DermaDefaultBold")
|
||||
self.focus:Dock(BOTTOM)
|
||||
|
||||
function self.focus:DoClick()
|
||||
local sel = FProfiler.UI.getCurrentRealmValue("currentSelected")
|
||||
if not sel then return end
|
||||
|
||||
FProfiler.UI.updateCurrentRealm("focusStr", sel.func)
|
||||
end
|
||||
|
||||
self.source = vgui.Create("DTextEntry", self)
|
||||
self.source:SetKeyboardInputEnabled(false)
|
||||
self.source:DockMargin(0, 40, 0, 0)
|
||||
self.source:SetMultiline(true)
|
||||
self.source:Dock(FILL)
|
||||
|
||||
FProfiler.UI.onCurrentRealmUpdate("sourceText", function(new)
|
||||
self.source:SetText(string.Replace(new, "\t", " "))
|
||||
end)
|
||||
|
||||
self.toConsole = vgui.Create("DButton", self)
|
||||
self.toConsole:SetText("Print Details to Console")
|
||||
self.toConsole:SetTall(50)
|
||||
self.toConsole:SetFont("DermaDefaultBold")
|
||||
self.toConsole:Dock(BOTTOM)
|
||||
|
||||
function self.toConsole:DoClick()
|
||||
FProfiler.UI.updateCurrentRealm("toConsole", FProfiler.UI.getCurrentRealmValue("currentSelected"))
|
||||
end
|
||||
end
|
||||
|
||||
function FUNCDETAILS:PerformLayout()
|
||||
self.titleLabel:CenterHorizontal()
|
||||
end
|
||||
derma.DefineControl("FProfileFuncDetails", "", FUNCDETAILS, "DPanel")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The actual frame
|
||||
---------------------------------------------------------------------------]]
|
||||
local FRAME = {}
|
||||
|
||||
local frameInstance
|
||||
function FRAME:Init()
|
||||
self:SetTitle("FProfiler profiling tool")
|
||||
self:SetSize(ScrW() * 0.8, ScrH() * 0.8)
|
||||
self:Center()
|
||||
self:SetVisible(true)
|
||||
self:MakePopup()
|
||||
self:SetDeleteOnClose(false)
|
||||
|
||||
self.magicbar = vgui.Create("FProfileMagicBar", self)
|
||||
self.magicbar:SetTall(math.max(self:GetTall() * 0.07, 48))
|
||||
self.magicbar:Dock(TOP)
|
||||
|
||||
self.resultsheet = vgui.Create("FProfileResultSheet", self)
|
||||
self.resultsheet:SetWide(self:GetWide() * 0.8)
|
||||
self.resultsheet:Dock(LEFT)
|
||||
|
||||
self.details = vgui.Create("FProfileFuncDetails", self)
|
||||
self.details:SetWide(self:GetWide() * 0.2 - 12)
|
||||
self.details:DockMargin(5, 31, 0, 0)
|
||||
self.details:Dock(RIGHT)
|
||||
end
|
||||
|
||||
function FRAME:OnClose()
|
||||
FProfiler.UI.updateModel("frameVisible", false)
|
||||
end
|
||||
|
||||
derma.DefineControl("FProfileFrame", "", FRAME, "DFrame")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The command to start the profiler
|
||||
---------------------------------------------------------------------------]]
|
||||
concommand.Add("FProfiler",
|
||||
function()
|
||||
frameInstance = frameInstance or vgui.Create("FProfileFrame")
|
||||
frameInstance:SetVisible(true)
|
||||
|
||||
FProfiler.UI.updateModel("frameVisible", true)
|
||||
end,
|
||||
nil, "Starts FProfiler")
|
||||
@@ -1,188 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The model describes the data that the drives the UI
|
||||
Loosely based on the Elm architecture
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
local model =
|
||||
{
|
||||
realm = "client", -- "client" or "server"
|
||||
serverAccess = false, -- Whether the player has access to profile the server
|
||||
frameVisible = false, -- Whether the frame is visible
|
||||
|
||||
client = {
|
||||
status = "Stopped", -- Started or Stopped
|
||||
shouldReset = true, -- Whether profiling should start anew
|
||||
recordTime = 0, -- Total time spent on the last full profiling session
|
||||
sessionStart = nil, -- When the last profiling session was started
|
||||
sessionStartSysTime = nil, -- When the last profiling session was started, measured in SysTime
|
||||
bottlenecks = {}, -- The list of bottleneck functions
|
||||
topLagSpikes = {}, -- Top of lagging functions
|
||||
currentSelected = nil, -- Currently selected function
|
||||
|
||||
focusObj = nil, -- The current function being focussed upon in profiling
|
||||
focusStr = "", -- The current function name being entered
|
||||
|
||||
toConsole = nil, -- Any functions that should be printed to console
|
||||
|
||||
sourceText = "", -- The text of the source function (if available)
|
||||
},
|
||||
|
||||
server = {
|
||||
status = "Stopped", -- Started or Stopped
|
||||
shouldReset = true, -- Whether profiling should start anew
|
||||
bottlenecks = {}, -- The list of bottleneck functions
|
||||
recordTime = 0, -- Total time spent on the last full profiling session
|
||||
sessionStart = nil, -- When the last profiling session was started
|
||||
topLagSpikes = {}, -- Top of lagging functions
|
||||
currentSelected = nil, -- Currently selected function
|
||||
|
||||
focusObj = nil, -- The current function being focussed upon in profiling
|
||||
focusStr = "", -- The current function name
|
||||
|
||||
toConsole = nil, -- Any functions that should be printed to console
|
||||
|
||||
sourceText = "", -- The text of the source function (if available)
|
||||
fromServer = false, -- Whether a change of the model came from the server.
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
local updaters = {}
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Update the model.
|
||||
Automatically calls the registered update hook functions
|
||||
|
||||
e.g. updating the realm would be:
|
||||
FProfiler.UI.updateModel("realm", "server")
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.UI.updateModel(path, value)
|
||||
path = istable(path) and path or {path}
|
||||
|
||||
local updTbl = updaters
|
||||
local mdlTbl = model
|
||||
local key = path[#path]
|
||||
|
||||
for i = 1, #path - 1 do
|
||||
mdlTbl = mdlTbl[path[i]]
|
||||
updTbl = updTbl and updTbl[path[i]]
|
||||
end
|
||||
|
||||
local oldValue = mdlTbl[key]
|
||||
mdlTbl[key] = value
|
||||
|
||||
for _, updFunc in ipairs(updTbl and updTbl[key] or {}) do
|
||||
updFunc(value, oldValue)
|
||||
end
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Update the model of the current realm
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.UI.updateCurrentRealm(path, value)
|
||||
path = istable(path) and path or {path}
|
||||
|
||||
table.insert(path, 1, model.realm)
|
||||
|
||||
FProfiler.UI.updateModel(path, value)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Retrieve a value of the model
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.UI.getModelValue(path)
|
||||
path = istable(path) and path or {path}
|
||||
|
||||
local mdlTbl = model
|
||||
local key = path[#path]
|
||||
|
||||
for i = 1, #path - 1 do
|
||||
mdlTbl = mdlTbl[path[i]]
|
||||
end
|
||||
|
||||
return mdlTbl[key]
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Retrieve a value of the model regardless of realm
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.UI.getCurrentRealmValue(path)
|
||||
path = istable(path) and path or {path}
|
||||
|
||||
table.insert(path, 1, model.realm)
|
||||
|
||||
return FProfiler.UI.getModelValue(path)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Registers a hook that gets triggered when a certain part of the model is updated
|
||||
e.g. FProfiler.UI.onModelUpdate("realm", print) prints when the realm is changed
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.UI.onModelUpdate(path, func)
|
||||
path = istable(path) and path or {path}
|
||||
|
||||
local updTbl = updaters
|
||||
local mdlTbl = model
|
||||
local key = path[#path]
|
||||
|
||||
for i = 1, #path - 1 do
|
||||
mdlTbl = mdlTbl[path[i]]
|
||||
updTbl[path[i]] = updTbl[path[i]] or {}
|
||||
updTbl = updTbl[path[i]]
|
||||
end
|
||||
|
||||
updTbl[key] = updTbl[key] or {}
|
||||
|
||||
table.insert(updTbl[key], func)
|
||||
|
||||
-- Call update with the initial value
|
||||
if mdlTbl[key] ~= nil then
|
||||
func(mdlTbl[key], mdlTbl[key])
|
||||
end
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Registers a hook to both realms
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.UI.onCurrentRealmUpdate(path, func)
|
||||
path = istable(path) and path or {path}
|
||||
|
||||
table.insert(path, 1, "client")
|
||||
FProfiler.UI.onModelUpdate(path, function(...)
|
||||
if FProfiler.UI.getModelValue("realm") == "server" then return end
|
||||
|
||||
func(...)
|
||||
end)
|
||||
|
||||
path[1] = "server"
|
||||
FProfiler.UI.onModelUpdate(path, function(...)
|
||||
if FProfiler.UI.getModelValue("realm") == "client" then return end
|
||||
|
||||
func(...)
|
||||
end)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
When the realm is changed, all update functions of the new realm are to be called
|
||||
---------------------------------------------------------------------------]]
|
||||
FProfiler.UI.onModelUpdate("realm", function(new, old)
|
||||
if not updaters[new] then return end
|
||||
|
||||
for k, funcTbl in pairs(updaters[new]) do
|
||||
for _, func in ipairs(funcTbl) do
|
||||
func(model[new][k], model[new][k])
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
The server is involved in the ui in the sense that it interacts with its model
|
||||
---------------------------------------------------------------------------]]
|
||||
|
||||
-- Net messages
|
||||
util.AddNetworkString("FProfile_startProfiling")
|
||||
util.AddNetworkString("FProfile_stopProfiling")
|
||||
util.AddNetworkString("FProfile_focusObj")
|
||||
util.AddNetworkString("FProfile_getSource")
|
||||
util.AddNetworkString("FProfile_printFunction")
|
||||
util.AddNetworkString("FProfile_fullModelUpdate")
|
||||
util.AddNetworkString("FProfile_focusUpdate")
|
||||
util.AddNetworkString("FProfile_unsubscribe")
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Simplified version of the model
|
||||
Contains only what the server needs to know
|
||||
---------------------------------------------------------------------------]]
|
||||
local model =
|
||||
{
|
||||
focusObj = nil, -- the function currently in focus
|
||||
sessionStart = nil, -- When the last profiling session was started. Used for the live timer.
|
||||
sessionStartSysTime = nil, -- Same as sessionStart, but measured in SysTime. Used to update recordTime
|
||||
recordTime = 0, -- Total time spent on the last full profiling session
|
||||
bottlenecks = {}, -- The list of bottleneck functions
|
||||
topLagSpikes = {}, -- Top of lagging functions
|
||||
subscribers = RecipientFilter(), -- The players that get updates of the profiler model
|
||||
}
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Helper function: receive a net message
|
||||
---------------------------------------------------------------------------]]
|
||||
local function receive(msg, f)
|
||||
net.Receive(msg, function(len, ply)
|
||||
-- Check access.
|
||||
CAMI.PlayerHasAccess(ply, "FProfiler", function(b, _)
|
||||
if not b then return end
|
||||
|
||||
f(len, ply)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Helper function:
|
||||
Write generic row data to a net message
|
||||
---------------------------------------------------------------------------]]
|
||||
local function writeRowData(row)
|
||||
net.WriteString(tostring(row.func))
|
||||
net.WriteString(row.info.short_src)
|
||||
net.WriteUInt(row.info.linedefined, 16)
|
||||
net.WriteUInt(row.info.lastlinedefined, 16)
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Helper function:
|
||||
Send the bottlenecks to the client
|
||||
Only sends the things displayed
|
||||
---------------------------------------------------------------------------]]
|
||||
local function writeBottleNecks()
|
||||
net.WriteUInt(#model.bottlenecks, 16)
|
||||
|
||||
for i, row in ipairs(model.bottlenecks) do
|
||||
writeRowData(row)
|
||||
|
||||
net.WriteUInt(#row.names, 8)
|
||||
|
||||
for j, name in ipairs(row.names) do
|
||||
net.WriteString(name.name)
|
||||
net.WriteString(name.namewhat)
|
||||
end
|
||||
|
||||
net.WriteUInt(row.total_called, 32)
|
||||
net.WriteDouble(row.total_time)
|
||||
net.WriteDouble(row.average_time)
|
||||
end
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Helper function:
|
||||
Sends the top n functions
|
||||
---------------------------------------------------------------------------]]
|
||||
local function writeTopN()
|
||||
local count = #model.topLagSpikes
|
||||
|
||||
-- All top N f
|
||||
for i = count, 0, -1 do
|
||||
if model.topLagSpikes and model.topLagSpikes[i] and model.topLagSpikes[i].info then break end -- Entry exists
|
||||
count = i
|
||||
end
|
||||
|
||||
net.WriteUInt(count, 8)
|
||||
|
||||
for i = 1, count do
|
||||
local row = model.topLagSpikes[i]
|
||||
|
||||
if not row.info then break end
|
||||
|
||||
writeRowData(row)
|
||||
|
||||
net.WriteString(row.info.name or "")
|
||||
net.WriteString(row.info.namewhat or "")
|
||||
net.WriteDouble(row.runtime)
|
||||
end
|
||||
end
|
||||
|
||||
-- Start profiling
|
||||
local function startProfiling()
|
||||
model.sessionStart = CurTime()
|
||||
model.sessionStartSysTime = SysTime()
|
||||
FProfiler.Internal.start(model.focusObj)
|
||||
|
||||
net.Start("FProfile_startProfiling")
|
||||
net.WriteDouble(model.recordTime)
|
||||
net.WriteDouble(model.sessionStart)
|
||||
net.Send(model.subscribers:GetPlayers())
|
||||
end
|
||||
|
||||
-- Stop profiling
|
||||
local function stopProfiling()
|
||||
FProfiler.Internal.stop()
|
||||
|
||||
model.recordTime = model.recordTime + SysTime() - (model.sessionStartSysTime or 0)
|
||||
model.sessionStart = nil
|
||||
model.sessionStartSysTime = nil
|
||||
|
||||
model.bottlenecks = FProfiler.Internal.getAggregatedResults(100)
|
||||
model.topLagSpikes = FProfiler.Internal.getMostExpensiveSingleCalls()
|
||||
|
||||
net.Start("FProfile_stopProfiling")
|
||||
net.WriteDouble(model.recordTime)
|
||||
|
||||
writeBottleNecks()
|
||||
writeTopN()
|
||||
net.Send(model.subscribers:GetPlayers())
|
||||
end
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Receive an update of the function to focus on
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_focusObj", function(_, ply)
|
||||
local funcStr = net.ReadString()
|
||||
|
||||
model.focusObj = FProfiler.funcNameToObj(funcStr)
|
||||
|
||||
net.Start("FProfile_focusObj")
|
||||
net.WriteBool(model.focusObj and true or false)
|
||||
net.Send(ply)
|
||||
|
||||
-- Send a focus update to all other players
|
||||
net.Start("FProfile_focusUpdate")
|
||||
net.WriteString(funcStr)
|
||||
net.WriteBool(model.focusObj and true or false)
|
||||
model.subscribers:RemovePlayer(ply)
|
||||
net.Send(model.subscribers:GetPlayers())
|
||||
model.subscribers:AddPlayer(ply)
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Receive a "start profiling" signal
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_startProfiling", function(_, ply)
|
||||
local shouldReset = net.ReadBool()
|
||||
if shouldReset then
|
||||
FProfiler.Internal.reset()
|
||||
model.recordTime = 0
|
||||
end
|
||||
|
||||
startProfiling()
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Receive a stop profiling signal
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_stopProfiling", function(_, ply)
|
||||
stopProfiling()
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Send the source of a function to a client
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_getSource", function(_, ply)
|
||||
local func = FProfiler.funcNameToObj(net.ReadString())
|
||||
|
||||
if not func then return end
|
||||
|
||||
local info = debug.getinfo(func)
|
||||
|
||||
if not info then return end
|
||||
|
||||
net.Start("FProfile_getSource")
|
||||
net.WriteString(FProfiler.readSource(info.short_src, info.linedefined, info.lastlinedefined) or "")
|
||||
net.Send(ply)
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Print the details of a function
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_printFunction", function(_, ply)
|
||||
local source = net.ReadBool() -- true is from bottlenecks, false is from Top-N
|
||||
local dataSource = source and model.bottlenecks or model.topLagSpikes
|
||||
local func = net.ReadString()
|
||||
|
||||
local data
|
||||
|
||||
for _, row in ipairs(dataSource or {}) do
|
||||
if tostring(row.func) == func then data = row break end
|
||||
end
|
||||
|
||||
if not data then return end
|
||||
|
||||
-- Show the data
|
||||
show(data)
|
||||
local plaintext = showStr(data)
|
||||
|
||||
-- Write to file if necessary
|
||||
file.CreateDir("fprofiler")
|
||||
file.Write("fprofiler/profiledata.txt", plaintext)
|
||||
MsgC(Color(200, 200, 200), "-----", Color(120, 120, 255), "NOTE", Color(200, 200, 200), "---------------\n")
|
||||
MsgC(Color(200, 200, 200), "If the above function does not fit in console, you can find it in data/fprofiler/profiledata.txt\n\n")
|
||||
|
||||
-- Listen server hosts already see the server console
|
||||
if ply:IsListenServerHost() then return end
|
||||
|
||||
-- Send a plaintext version to the client
|
||||
local binary = util.Compress(plaintext)
|
||||
|
||||
net.Start("FProfile_printFunction")
|
||||
net.WriteData(binary, #binary)
|
||||
net.Send(ply)
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Request of a full model update
|
||||
Particularly useful when someone else has done (or is performing) a profiling session
|
||||
and the current player wants to see the results
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_fullModelUpdate", function(_, ply)
|
||||
-- This player is now subscribed to the updates
|
||||
model.subscribers:AddPlayer(ply)
|
||||
|
||||
net.Start("FProfile_fullModelUpdate")
|
||||
net.WriteBool(model.focusObj ~= nil)
|
||||
if model.focusObj ~= nil then net.WriteString(tostring(model.focusObj)) end
|
||||
|
||||
-- Bool also indicates whether it's currently profiling
|
||||
net.WriteBool(model.sessionStart ~= nil)
|
||||
if model.sessionStart ~= nil then net.WriteDouble(model.sessionStart) end
|
||||
|
||||
net.WriteDouble(model.recordTime)
|
||||
|
||||
writeBottleNecks()
|
||||
writeTopN()
|
||||
|
||||
net.Send(ply)
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Unsubscribe from the updates of the profiler
|
||||
---------------------------------------------------------------------------]]
|
||||
receive("FProfile_unsubscribe", function(_, ply)
|
||||
model.subscribers:RemovePlayer(ply)
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
API function: start profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.start(focus)
|
||||
FProfiler.Internal.reset()
|
||||
|
||||
model.recordTime = 0
|
||||
model.focusObj = focus
|
||||
|
||||
startProfiling()
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
API function: stop profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.stop()
|
||||
stopProfiling()
|
||||
end
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
API function: continue profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
function FProfiler.continueProfiling()
|
||||
startProfiling()
|
||||
end
|
||||
@@ -1,238 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local get, update, onUpdate = FProfiler.UI.getModelValue, FProfiler.UI.updateModel, FProfiler.UI.onModelUpdate
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Update the current selected focus object when data is entered
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"server", "focusStr"}, function(new)
|
||||
if not new or get({"server", "fromServer"}) then return end
|
||||
|
||||
net.Start("FProfile_focusObj")
|
||||
net.WriteString(new)
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
net.Receive("FProfile_focusObj", function()
|
||||
update({"server", "focusObj"}, net.ReadBool() and get({"server", "focusStr"}) or nil)
|
||||
end)
|
||||
|
||||
-- A focus update occurs when someone else changes the focus
|
||||
net.Receive("FProfile_focusUpdate", function()
|
||||
update({"server", "fromServer"}, true)
|
||||
|
||||
local focusStr = net.ReadString()
|
||||
update({"server", "focusStr"}, focusStr)
|
||||
update({"server", "focusObj"}, net.ReadBool() and focusStr or nil)
|
||||
|
||||
update({"server", "fromServer"}, false)
|
||||
end)
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
(Re)start profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
local function restartProfiling()
|
||||
local shouldReset = get({"server", "shouldReset"})
|
||||
|
||||
net.Start("FProfile_startProfiling")
|
||||
net.WriteBool(shouldReset)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
net.Receive("FProfile_startProfiling", function()
|
||||
update({"server", "fromServer"}, true)
|
||||
update({"server", "status"}, "Started")
|
||||
update({"server", "recordTime"}, net.ReadDouble())
|
||||
update({"server", "sessionStart"}, net.ReadDouble())
|
||||
update({"server", "fromServer"}, false)
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Stop profiling
|
||||
---------------------------------------------------------------------------]]
|
||||
local function stopProfiling()
|
||||
net.Start("FProfile_stopProfiling")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
-- Read a row from a net message
|
||||
local function readDataRow(countSize, readSpecific)
|
||||
local res = {}
|
||||
|
||||
local count = net.ReadUInt(countSize)
|
||||
|
||||
for i = 1, count do
|
||||
local row = {}
|
||||
row.info = {}
|
||||
|
||||
row.func = net.ReadString()
|
||||
row.info.short_src = net.ReadString()
|
||||
row.info.linedefined = net.ReadUInt(16)
|
||||
row.info.lastlinedefined = net.ReadUInt(16)
|
||||
|
||||
readSpecific(row)
|
||||
|
||||
table.insert(res, row)
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
-- Read a bottleneck row
|
||||
local function readBottleneckRow(row)
|
||||
local nameCount = net.ReadUInt(8)
|
||||
|
||||
row.names = {}
|
||||
for i = 1, nameCount do
|
||||
table.insert(row.names, {
|
||||
name = net.ReadString(),
|
||||
namewhat = net.ReadString()
|
||||
})
|
||||
end
|
||||
|
||||
row.total_called = net.ReadUInt(32)
|
||||
row.total_time = net.ReadDouble()
|
||||
row.average_time = net.ReadDouble()
|
||||
end
|
||||
|
||||
-- Read the top n row
|
||||
local function readTopNRow(row)
|
||||
row.info.name = net.ReadString()
|
||||
row.info.namewhat = net.ReadString()
|
||||
row.runtime = net.ReadDouble()
|
||||
end
|
||||
|
||||
net.Receive("FProfile_stopProfiling", function()
|
||||
update({"server", "fromServer"}, true)
|
||||
update({"server", "status"}, "Stopped")
|
||||
update({"server", "sessionStart"}, nil)
|
||||
update({"server", "recordTime"}, net.ReadDouble())
|
||||
|
||||
update({"server", "bottlenecks"}, readDataRow(16, readBottleneckRow))
|
||||
update({"server", "topLagSpikes"}, readDataRow(8, readTopNRow))
|
||||
update({"server", "fromServer"}, false)
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Start/stop recording when the recording status is changed
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"server", "status"}, function(new, old)
|
||||
if new == old or get({"server", "fromServer"}) then return end
|
||||
(new == "Started" and restartProfiling or stopProfiling)()
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Update info when a different line is selected
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"server", "currentSelected"}, function(new)
|
||||
if not new or not new.info or not new.info.linedefined or not new.info.lastlinedefined or not new.info.short_src then return end
|
||||
|
||||
net.Start("FProfile_getSource")
|
||||
net.WriteString(tostring(new.func))
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
net.Receive("FProfile_getSource", function()
|
||||
update({"server", "sourceText"}, net.ReadString())
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
When a function is to be printed to console
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate({"server", "toConsole"}, function(data)
|
||||
if not data then return end
|
||||
|
||||
update({"server", "toConsole"}, nil)
|
||||
|
||||
net.Start("FProfile_printFunction")
|
||||
net.WriteBool(data.total_called and true or false) -- true for bottleneck function, false for top-n function
|
||||
net.WriteString(tostring(data.func))
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
net.Receive("FProfile_printFunction", function(len)
|
||||
local data = net.ReadData(len)
|
||||
local decompressed = util.Decompress(data)
|
||||
|
||||
-- Print the text line by line, otherwise big parts of big data will not be printed
|
||||
local split = string.Explode("\n", decompressed, false)
|
||||
for _, line in ipairs(split) do
|
||||
MsgN(line)
|
||||
end
|
||||
|
||||
-- Write the thing to a file
|
||||
file.CreateDir("fprofiler")
|
||||
file.Write("fprofiler/profiledata.txt", showStr(data))
|
||||
MsgC(Color(200, 200, 200), "-----", Color(120, 120, 255), "NOTE", Color(200, 200, 200), "---------------\n")
|
||||
MsgC(Color(200, 200, 200), "In the server's console you can find a colour coded version of the above output.\nIf the above function does not fit in console, you can find it in data/fprofiler/profiledata.txt\n\n")
|
||||
end)
|
||||
|
||||
|
||||
--[[-------------------------------------------------------------------------
|
||||
Check access when the frame opens
|
||||
Also request a full serverside model update
|
||||
---------------------------------------------------------------------------]]
|
||||
onUpdate("frameVisible", function(isOpen)
|
||||
-- Don't network if the server doesn't have FProfiler installed
|
||||
if util.NetworkStringToID("FProfile_fullModelUpdate") == 0 then
|
||||
update("serverAccess", false)
|
||||
return
|
||||
end
|
||||
|
||||
-- Update access
|
||||
CAMI.PlayerHasAccess(LocalPlayer(), "FProfiler", function(b, _)
|
||||
update("serverAccess", b)
|
||||
end)
|
||||
|
||||
if not isOpen then
|
||||
net.Start("FProfile_unsubscribe")
|
||||
net.SendToServer()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("FProfile_fullModelUpdate")
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
|
||||
net.Receive("FProfile_fullModelUpdate", function()
|
||||
update({"server", "fromServer"}, true)
|
||||
|
||||
local focusExists = net.ReadBool()
|
||||
if focusExists then
|
||||
local focus = net.ReadString()
|
||||
update({"server", "focusObj"}, focus)
|
||||
update({"server", "focusStr"}, focus)
|
||||
end
|
||||
|
||||
local startingTimeExists = net.ReadBool()
|
||||
|
||||
if startingTimeExists then
|
||||
update({"server", "status"}, "Started")
|
||||
update({"server", "sessionStart"}, net.ReadDouble())
|
||||
else
|
||||
update({"server", "status"}, "Stopped")
|
||||
end
|
||||
|
||||
update({"server", "recordTime"}, net.ReadDouble())
|
||||
|
||||
update({"server", "bottlenecks"}, readDataRow(16, readBottleneckRow))
|
||||
update({"server", "topLagSpikes"}, readDataRow(8, readTopNRow))
|
||||
|
||||
update({"server", "fromServer"}, false)
|
||||
end)
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
-- Try to find the function represented by a string
|
||||
function FProfiler.funcNameToObj(str)
|
||||
if isfunction(str) then return str end
|
||||
|
||||
local times = FProfiler.Internal.getCallCounts()
|
||||
for func, _ in pairs(times) do
|
||||
if tostring(func) == str then return func end
|
||||
end
|
||||
|
||||
local tbl = _G
|
||||
local exploded = string.Explode(".", str, false)
|
||||
if not exploded or not exploded[1] then return end
|
||||
|
||||
for i = 1, #exploded - 1 do
|
||||
tbl = (tbl or {})[exploded[i]]
|
||||
if not istable(tbl) then return end
|
||||
end
|
||||
|
||||
local func = (tbl or {})[exploded[#exploded]]
|
||||
|
||||
if not isfunction(func) then return end
|
||||
|
||||
return func
|
||||
end
|
||||
|
||||
-- Read a file
|
||||
function FProfiler.readSource(fname, startLine, endLine)
|
||||
if not file.Exists(fname, "GAME") then return "" end
|
||||
if startLine < 0 or endLine < 0 or endLine < startLine then return "" end
|
||||
|
||||
local f = file.Open(fname, "r", "GAME")
|
||||
|
||||
for i = 1, startLine - 1 do f:ReadLine() end
|
||||
|
||||
local res = {}
|
||||
for i = startLine, endLine do
|
||||
table.insert(res, f:ReadLine() or "")
|
||||
end
|
||||
|
||||
return table.concat(res, "\n")
|
||||
end
|
||||
@@ -1,4 +0,0 @@
|
||||
Plugin
|
||||
{
|
||||
file "lua/bin/gmsv_gluapack_plugin.so"
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
resource.AddSingleFile("sound/grove.ogg")
|
||||
@@ -1,89 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local groove = StormFox2.Weather.Add( "Groove" )
|
||||
-- Name and Symbol
|
||||
function groove:GetName(nTime, nTemp, nWind, bThunder, nFraction )
|
||||
return "Groove"
|
||||
end
|
||||
local m_def = Material("stormfox2/hud/w_cloudy_night.png")
|
||||
function groove.GetSymbol( nTime ) -- What the menu should show
|
||||
return m_def
|
||||
end
|
||||
function groove.GetIcon( nTime, nTemp, nWind, bThunder, nFraction) -- What symbol the weather should show
|
||||
return m_def
|
||||
end
|
||||
if CLIENT then -- Music
|
||||
local music = StormFox2.Ambience.CreateAmbienceSnd( "grove.ogg", SF_AMB_OUTSIDE, 1 )
|
||||
groove:AddAmbience( music )
|
||||
-- Particles
|
||||
local m = Material("effects/fluttercore_gmod")
|
||||
local boogie = StormFox2.DownFall.CreateTemplate(m, true)
|
||||
boogie:SetRandomAngle(0.2)
|
||||
boogie:SetSpeed(0.1)
|
||||
function boogie:OnHit( vPos, vNormal, nHitType, zPart )
|
||||
if math.random(3) > 1 then return end -- 33% chance to spawn a splash
|
||||
local dlight = DynamicLight(math.random(32, 20))
|
||||
if ( dlight ) then
|
||||
dlight.pos = vPos
|
||||
local c = zPart:GetColor()
|
||||
dlight.r = c.r
|
||||
dlight.g = c.g
|
||||
dlight.b = c.b
|
||||
dlight.brightness = 2
|
||||
dlight.Decay = 1000
|
||||
dlight.Size = math.random(1, 3) * 256
|
||||
dlight.DieTime = CurTime() + 1
|
||||
end
|
||||
end
|
||||
local boogie2 = StormFox2.DownFall.CreateTemplate(m)
|
||||
boogie2:SetSpeed(0.1)
|
||||
function groove.Think()
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate( boogie, 10, 700, 900, 5, vNorm ) or {} ) do
|
||||
local s = math.random(2, 5)
|
||||
v:SetSize( s,s )
|
||||
v:SetColor( HSVToColor(math.random(360), 1,1) )
|
||||
v:SetSpeed(math.Rand(0.1,0.3))
|
||||
end
|
||||
for _,v in ipairs( StormFox2.DownFall.SmartTemplate(boogie2, 10, 1200, 50, 5, vNorm) or {} ) do
|
||||
local s = math.random(2, 5)
|
||||
v:SetSize( s, s * 0.4 )
|
||||
v:SetRoll( math.random(360) )
|
||||
v:SetColor( HSVToColor(math.random(360), 1,1) )
|
||||
v:SetSpeed(math.Rand(0.1,0.3))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if SERVER then return end
|
||||
|
||||
|
||||
local wallp = {}
|
||||
local function GetWallpapers()
|
||||
return wallp
|
||||
end
|
||||
|
||||
local function LoadWallpapers( steamid64 )
|
||||
http.Fetch("https://steamcommunity.com/inventory/" .. steamid64 .. "/753/6", function(code)
|
||||
if not code then return end
|
||||
local t = util.JSONToTable(code)
|
||||
if not t then return end
|
||||
wallp = {} -- empty
|
||||
for k, v in ipairs(t.descriptions or {}) do
|
||||
if not v.actions or not v.actions[1] then continue end
|
||||
if not v.actions[1].link then continue end
|
||||
if not string.match(v.actions[1].link, "%.jpg$") and not string.match(v.actions[1].link, "%.png$") then print(v.actions[1].link) continue end
|
||||
wallp[v.name] = v.actions[1].link
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
LoadWallpapers("76561198009860285")
|
||||
@@ -1 +0,0 @@
|
||||
OggS
|
||||
@@ -1 +0,0 @@
|
||||
- Add delay for player refresh after group change
|
||||
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,27 +0,0 @@
|
||||
**IMPORTANT:** ONLY DOWNLOAD THIS ADDON ON THE RELEASES PAGE: https://github.com/matbyte-com/vyhub-gmod/releases
|
||||
|
||||
# vyhub-gmod: Garry's Mod Community-Management / Donation-System
|
||||
|
||||
This is the [VyHub](https://vyhub.net) addon for Garry's Mod.
|
||||
|
||||
## Features
|
||||
- :shopping_cart: Shop / donation system -> sell items on your gameserver for real money
|
||||
- :no_entry_sign: Bans
|
||||
- :warning: Warnings
|
||||
- :framed_picture: Ingame dashboard of bans and warnings
|
||||
- :repeat: Synchronize player groups with admin mods
|
||||
- :chart_with_upwards_trend: Playtime statistics
|
||||
- :loudspeaker: Periodic adverts
|
||||
|
||||
Create your account for free on our [website](https://vyhub.net).
|
||||
|
||||
The documentation can be found [here](https://docs.vyhub.net/latest/game/gmod/).
|
||||
|
||||
## Supported Admin Mods
|
||||
|
||||
- ULX
|
||||
- SAM
|
||||
- xAdmin 1 & 2
|
||||
- FAdmin
|
||||
- sAdmin
|
||||
- ServerGuard
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"title": "VyHub GMOD",
|
||||
"description": "",
|
||||
"type": "servercontent",
|
||||
"tags": [
|
||||
"fun"
|
||||
],
|
||||
"ignore": [
|
||||
".git/*",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
|
||||
local f = string.format
|
||||
|
||||
VyHub = VyHub or {}
|
||||
VyHub.Config = VyHub.Config or {}
|
||||
VyHub.Lib = VyHub.Lib or {}
|
||||
VyHub.ready = false
|
||||
|
||||
local vyhub_root = "vyhub"
|
||||
|
||||
local color_warn = Color(211, 120, 0)
|
||||
local color_err = Color(255, 0, 0)
|
||||
local color_green = Color(0, 255, 0)
|
||||
|
||||
function VyHub:msg(message, type)
|
||||
type = type or "neutral"
|
||||
|
||||
// Remove color tags
|
||||
message = string.gsub(message, "<([%l]+)>([^<]+)</%1>", "%2")
|
||||
|
||||
if type == "success" then
|
||||
MsgC("[VyHub] ", color_green, message .. "\n")
|
||||
elseif type == "error" then
|
||||
MsgC("[VyHub] [ERROR] ", color_err, message .. "\n")
|
||||
elseif type == "neutral" then
|
||||
MsgC("[VyHub] ", color_white, message .. "\n")
|
||||
elseif type == "warning" then
|
||||
MsgC("[VyHub] [WARN] ", color_warn, message .. "\n")
|
||||
elseif type == "debug" and VyHub.Config.debug then
|
||||
MsgC("[VyHub] [Debug] ", color_white, message .. "\n")
|
||||
end
|
||||
end
|
||||
|
||||
VyHub:msg("Initializing...")
|
||||
|
||||
if SERVER then
|
||||
addon_incomplete = false
|
||||
|
||||
if file.Exists( vyhub_root .. '/lang/en.json', "LUA") then
|
||||
if file.Exists( vyhub_root .. '/config/sv_config.lua', "LUA") then
|
||||
hook.Run("vyhub_loading_start")
|
||||
|
||||
-- libs
|
||||
VyHub:msg("Loading lib files...")
|
||||
local files = file.Find( vyhub_root .."/lib/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
AddCSLuaFile( vyhub_root .. "/lib/" .. file )
|
||||
include( vyhub_root .. "/lib/" .. file )
|
||||
end
|
||||
|
||||
-- Shared Config
|
||||
include( vyhub_root .. '/config/sh_config.lua' )
|
||||
AddCSLuaFile( vyhub_root .. "/config/sh_config.lua" )
|
||||
|
||||
-- Language
|
||||
VyHub:msg('Loading ' .. VyHub.Config.lang .. ' language...')
|
||||
include( vyhub_root .. '/shared/sh_lang.lua' )
|
||||
|
||||
-- Config Files
|
||||
VyHub:msg("Loading config files...")
|
||||
include( vyhub_root .. '/config/sv_config.lua' )
|
||||
|
||||
-- Shared Files
|
||||
VyHub:msg("Loading shared files...")
|
||||
local files = file.Find( vyhub_root .."/shared/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
AddCSLuaFile( vyhub_root .. "/shared/" .. file )
|
||||
include( vyhub_root .. "/shared/" .. file )
|
||||
end
|
||||
|
||||
-- Client Files
|
||||
VyHub:msg("Loading client files...")
|
||||
local files = file.Find( vyhub_root .."/client/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
AddCSLuaFile( vyhub_root .."/client/" .. file )
|
||||
end
|
||||
|
||||
-- Server Files
|
||||
VyHub:msg("Loading server files...")
|
||||
local files = file.Find( vyhub_root .. "/server/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
include( vyhub_root .. "/server/" .. file )
|
||||
end
|
||||
|
||||
game.ConsoleCommand("sv_hibernate_think 1\n")
|
||||
|
||||
file.CreateDir("vyhub")
|
||||
|
||||
VyHub.Config:load_cache_config()
|
||||
|
||||
timer.Simple(2, function()
|
||||
hook.Run("vyhub_loading_finish")
|
||||
end)
|
||||
|
||||
VyHub:msg("Finished loading!")
|
||||
else
|
||||
VyHub:msg("Could not find lua/vyhub/config/sv_config.lua. Please make sure it exists.", "error")
|
||||
end
|
||||
else
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("Could not find language files!!! Please make sure to download a correct vyhub-gmod release here: https://github.com/matbyte-com/vyhub-gmod/releases", "error")
|
||||
VyHub:msg("Cannot proceed with initialization.", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if CLIENT then
|
||||
if file.Exists( vyhub_root .. '/shared/sh_lang.lua', "LUA") then
|
||||
hook.Run("vyhub_loading_start")
|
||||
|
||||
-- libs
|
||||
VyHub:msg("Loading lib files...")
|
||||
local files = file.Find( vyhub_root .."/lib/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
include( vyhub_root .. "/lib/" .. file )
|
||||
end
|
||||
|
||||
-- Language
|
||||
VyHub:msg('Loading language...')
|
||||
include( vyhub_root .. '/shared/sh_lang.lua' )
|
||||
|
||||
-- Config Files
|
||||
VyHub:msg("Loading config files...")
|
||||
local files = file.Find( vyhub_root .."/config/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
if not string.StartWith(file, 'sv_') then
|
||||
include( vyhub_root .. "/config/" .. file )
|
||||
end
|
||||
end
|
||||
|
||||
-- Shared Files
|
||||
VyHub:msg("Loading shared files...")
|
||||
local files = file.Find( vyhub_root .."/shared/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
include( vyhub_root .. "/shared/" .. file )
|
||||
end
|
||||
|
||||
-- Client Files
|
||||
VyHub:msg("Loading client files...")
|
||||
local files = file.Find( vyhub_root .."/client/*.lua", "LUA" )
|
||||
for _, file in ipairs( files ) do
|
||||
include( vyhub_root .."/client/" .. file )
|
||||
end
|
||||
|
||||
timer.Simple(2, function()
|
||||
hook.Run("vyhub_loading_finish")
|
||||
end)
|
||||
|
||||
VyHub:msg("Finished loading!")
|
||||
else
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("VyHub not correctly loaded. Please check the server log.", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
VyHub:msg("!!!", "error")
|
||||
end
|
||||
end
|
||||
@@ -1,34 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
local color_red = Color(255, 0, 0)
|
||||
|
||||
if VyHub.Config.chat_tags and not DarkRP then
|
||||
hook.Add("OnPlayerChat", "vyhub_chattag_OnPlayerChat", function(ply, msg)
|
||||
if IsValid(ply) then
|
||||
local group = VyHub.Group:get(ply:GetUserGroup())
|
||||
|
||||
if group then
|
||||
local teamcolor = team.GetColor(ply:Team())
|
||||
local deadTag = ""
|
||||
|
||||
if not ply:Alive() then
|
||||
deadTag = f("*%s* ", VyHub.lang.other.dead)
|
||||
end
|
||||
|
||||
chat.AddText(VyHub.Util:hex2rgb(group.color), "[", group.name, "]", " ", color_red, deadTag, teamcolor, ply:Nick(), color_white, ": ", msg)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -1,532 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
VyHub.Dashboard = VyHub.Dashboard or {}
|
||||
|
||||
VyHub.Dashboard.ui = VyHub.Dashboard.ui or nil
|
||||
|
||||
VyHub.Dashboard.html_ready = false
|
||||
VyHub.Dashboard.html_generated = VyHub.Dashboard.html_generated or false
|
||||
|
||||
local dashboard_html = dashboard_html or "Loading, please try again. If this does not help, please ask the server owner to check for errors in the server console. In most cases, vyhub-gmod has not been downloaded correctly and is missing files."
|
||||
|
||||
function VyHub.Dashboard:create_ui()
|
||||
VyHub.Dashboard.html_ready = false
|
||||
|
||||
local xsize = ScrW() - ScrW()/4
|
||||
local ysize = ScrH() - ScrH()/4
|
||||
local xpos = ScrW()/2 - xsize/2
|
||||
local ypos = ScrH()/2 - ysize/2
|
||||
local title = "VyHub Server-Dashboard"
|
||||
local box_color = Color(94, 0, 0, 255)
|
||||
|
||||
VyHub.Dashboard.ui = vgui.Create("DFrame")
|
||||
VyHub.Dashboard.ui:SetSize(xsize, ysize)
|
||||
VyHub.Dashboard.ui:SetPos(xpos, ypos)
|
||||
VyHub.Dashboard.ui:SetDraggable(true)
|
||||
VyHub.Dashboard.ui:SetTitle(title)
|
||||
VyHub.Dashboard.ui:SetDeleteOnClose(false)
|
||||
function VyHub.Dashboard.ui.Paint(self, w, h)
|
||||
surface.SetDrawColor(box_color)
|
||||
surface.DrawRect(0, 0, w, 24)
|
||||
end
|
||||
|
||||
VyHub.Dashboard.ui_html = vgui.Create("DHTML", VyHub.Dashboard.ui)
|
||||
VyHub.Dashboard.ui_html:SetSize(xsize, ysize - 24)
|
||||
VyHub.Dashboard.ui_html:SetPos(0, 24)
|
||||
VyHub.Dashboard.ui_html:SetHTML(dashboard_html)
|
||||
|
||||
function VyHub.Dashboard.ui_html:OnDocumentReady()
|
||||
MsgN("VyHub Dashboard: HTML Loaded")
|
||||
VyHub.Dashboard.html_ready = true
|
||||
VyHub.Dashboard.ui_html:RunJavascript('local_steamid64 = ' .. LocalPlayer():SteamID64())
|
||||
end
|
||||
|
||||
VyHub.Dashboard.ui_html:AddFunction("vyhub", "warning_toggle", function (warning_id)
|
||||
LocalPlayer():ConCommand(f("vh_warning_toggle %s", warning_id))
|
||||
end)
|
||||
VyHub.Dashboard.ui_html:AddFunction("vyhub", "warning_delete", function (warning_id)
|
||||
LocalPlayer():ConCommand(f("vh_warning_delete %s", warning_id))
|
||||
end)
|
||||
VyHub.Dashboard.ui_html:AddFunction("vyhub", "ban_set_status", function (ban_id, status)
|
||||
LocalPlayer():ConCommand(f("vh_ban_set_status %s %s", ban_id, status))
|
||||
end)
|
||||
VyHub.Dashboard.ui_html:AddFunction("vyhub", "warning_create", function (steamid, reason)
|
||||
LocalPlayer():ConCommand(f('vh_warn %s "%s"', steamid, VyHub.Util:escape_concommand_str(reason)))
|
||||
end)
|
||||
VyHub.Dashboard.ui_html:AddFunction("vyhub", "ban_create", function (steamid, minutes, reason)
|
||||
LocalPlayer():ConCommand(f('vh_ban %s "%s" "%s"', steamid, minutes, VyHub.Util:escape_concommand_str(reason)))
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Dashboard:load_html()
|
||||
VyHub.Dashboard.html_generated = true
|
||||
dashboard_html = [[
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="http://cdn.vyhub.net/assets/roboto-v30-latin/roboto-v30-latin.css">
|
||||
<link rel="stylesheet" href="http://cdn.vyhub.net/assets/font-awesome-4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="http://cdn.vyhub.net/assets/bootswatch-darkly.min.css">
|
||||
|
||||
<style>
|
||||
::selection {
|
||||
background: #b5b5b5; /* WebKit/Blink Browsers */
|
||||
}
|
||||
|
||||
body{
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
font-family: Roboto !important;
|
||||
}
|
||||
|
||||
.vh-input {
|
||||
background-color: #303030;
|
||||
color: white;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.nav-pills .active {
|
||||
background-color: #303030;
|
||||
font-weight: bold;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.user-tab {
|
||||
padding: 3px 6px 3px 6px;
|
||||
border-radius: 8px;
|
||||
text-overflow: ellipsis;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
#user_name {
|
||||
width: 50%;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="row" style="margin: 10px">
|
||||
<div class="col-xs-4 col-lg-3">
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><i class="fa fa-search"></i></div>
|
||||
<input style="height: 40px;" id="user_search" type="text" class="form-control vh-input" onclick="$('#user_search').val(''); generate_user_list();" onkeyup="generate_user_list()" >
|
||||
</div>
|
||||
<br/>
|
||||
<ul class="nav nav-pills nav-stacked" id="user_list">
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-8 col-lg-9">
|
||||
<div id="user_content_empty">
|
||||
]] .. VyHub.lang.dashboard.select_user .. [[
|
||||
</div>
|
||||
<div class="tab-content" id="user_content" style="display: none;">
|
||||
<h3 style="margin: 5px 0px 0px 0;">
|
||||
<div class="row">
|
||||
<div class="col-xs-9">
|
||||
<span id="user_name">
|
||||
<span class="label label-default" style="background-color: #5E0000; border-radius: .25em 0 0 .25em;">
|
||||
<i class="fa fa-user"></i> <span id="user_content_name"></span>
|
||||
</span>
|
||||
<span class="label label-default" style="border-radius: 0 .25em .25em 0;">
|
||||
<span id="user_content_username"></span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<span id="user_memberships" class="pull-right">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</h3>
|
||||
|
||||
<hr/>
|
||||
|
||||
<h4><span class="label label-default"><i class="fa fa-exclamation-triangle"></i> ]] .. VyHub.lang.other.warnings .. [[</span></h3>
|
||||
|
||||
<div class="row perm-warning_edit">
|
||||
<div class="col-xs-10">
|
||||
<input id="user_warn" type="text" class="form-control vh-input" onclick="$('#user_warn').val('');" placeholder="]] .. VyHub.lang.other.reason .. [[" />
|
||||
</div>
|
||||
<div class="col-xs-2" style="padding-left: 0;">
|
||||
<button style="height: 30px;" onclick="create_warning()" class="btn btn-warning btn-xs btn-block"><i class="fa fa-exclamation-triangle"></i> ]] .. VyHub.lang.dashboard.action_warn .. [[</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<table class="table table-condensed table-hover">
|
||||
<tr>
|
||||
<th width="10px"></th>
|
||||
<th>]] .. VyHub.lang.other.reason .. [[</th>
|
||||
<th>]] .. VyHub.lang.other.admin .. [[</th>
|
||||
<th>]] .. VyHub.lang.other.date .. [[</th>
|
||||
<th class="text-right">]] .. VyHub.lang.other.actions .. [[</th>
|
||||
</tr>
|
||||
|
||||
<tbody id="user_content_warnings">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<span class="label label-success"><i class="fa fa-check"></i> ]] .. VyHub.lang.other.active .. [[</span>
|
||||
<span class="label label-warning"><i class="fa fa-hourglass"></i> ]] .. VyHub.lang.other.inactive .. [[</span>
|
||||
<span class="label label-default"><i class="fa fa-times"></i> ]] .. VyHub.lang.other.disabled .. [[</span>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<h4><span class="label label-default"><i class="fa fa-gavel"></i> ]] .. VyHub.lang.other.bans .. [[</span></h3>
|
||||
|
||||
<div class="row perm-ban_edit">
|
||||
<div class="col-xs-8">
|
||||
<input id="user_ban_reason" type="text" class="form-control vh-input" onclick="$('#user_ban_reason').val('');" placeholder="]] .. VyHub.lang.other.reason .. [[" />
|
||||
</div>
|
||||
<div class="col-xs-2" style="padding-left: 0;">
|
||||
<input id="user_ban_minutes" type="text" class="form-control vh-input" onclick="$('#user_ban_minutes').val('');" placeholder="]] .. VyHub.lang.other.minutes .. [[" />
|
||||
</div>
|
||||
<div class="col-xs-2" style="padding-left: 0;">
|
||||
<button style="height: 30px;" onclick="create_ban()" class="btn btn-danger btn-xs btn-block"><i class="fa fa-gavel"></i> ]] .. VyHub.lang.dashboard.action_ban .. [[</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<table class="table table-condensed table-hover">
|
||||
<tr>
|
||||
<th width="10px"></th>
|
||||
<th>]] .. VyHub.lang.other.reason .. [[</th>
|
||||
<th>]] .. VyHub.lang.other.admin .. [[</th>
|
||||
<th>]] .. VyHub.lang.other.date .. [[</th>
|
||||
<th>]] .. VyHub.lang.other.minutes .. [[</th>
|
||||
<th class="text-right">]] .. VyHub.lang.other.actions .. [[</th>
|
||||
</tr>
|
||||
|
||||
<tbody id="user_content_bans">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<span class="label label-success"><i class="fa fa-check"></i> ]] .. VyHub.lang.other.active .. [[</span>
|
||||
<span class="label label-info"><i class="fa fa-globe"></i> ]] .. VyHub.lang.other.active_global .. [[</span>
|
||||
<span class="label label-warning"><i class="fa fa-times"></i> ]] .. VyHub.lang.other.unbanned .. [[</span>
|
||||
<span class="label label-danger"><i class="fa fa-hourglass"></i> ]] .. VyHub.lang.other.inactive .. [[</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js"></script>
|
||||
<script>
|
||||
var perms = null;
|
||||
var users = [];
|
||||
var users_by_id = {};
|
||||
var current_user = null;
|
||||
var local_steamid64 = null;
|
||||
|
||||
function escape(str) {
|
||||
return $("<div>").text(str).html();
|
||||
}
|
||||
|
||||
function format_date(iso_str) {
|
||||
return moment(iso_str).format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
|
||||
function load_data(new_data) {
|
||||
users = new_data;
|
||||
users_by_id = {};
|
||||
|
||||
new_data.forEach(function(user) {
|
||||
users_by_id[user.id] = user;
|
||||
});
|
||||
|
||||
generate_user_list()
|
||||
}
|
||||
|
||||
function load_perms(new_perms) {
|
||||
perms = new_perms;
|
||||
}
|
||||
|
||||
function enforce_perms() {
|
||||
if (perms == null) { return; }
|
||||
|
||||
Object.keys(perms).forEach(function(perm) {
|
||||
var has_perm = perms[perm];
|
||||
|
||||
if (has_perm) {
|
||||
$('.perm-' + perm).show();
|
||||
} else {
|
||||
$('.perm-' + perm).hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generate_user_list() {
|
||||
$('#user_list').html('');
|
||||
|
||||
var filter = null;
|
||||
|
||||
if ($('#user_search').val()) {
|
||||
filter = $('#user_search').val().toLowerCase();
|
||||
}
|
||||
|
||||
var ids = [];
|
||||
|
||||
var only_local_user = perms == null || (!perms.warning_show && !perms.ban_show);
|
||||
|
||||
users.forEach(function(user) {
|
||||
var activity = user.activities[0];
|
||||
|
||||
if (activity == null) { return; }
|
||||
if (only_local_user && user.identifier !== local_steamid64) { return; }
|
||||
|
||||
if (filter != null) {
|
||||
if (activity.extra.Nickname.toLowerCase().indexOf(filter) == -1 && user.username.toLowerCase().indexOf(filter) == -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var color = 'white';
|
||||
if (user.memberships.length > 0) {
|
||||
color = user.memberships[0].group.color;
|
||||
}
|
||||
|
||||
var warn_badge_color = ((user.warnings.length == 0) ? '#444' : "#f0ad4e");
|
||||
var ban_badge_color = ((user.bans.length == 0) ? '#444' : "#d9534f");
|
||||
|
||||
$('#user_list').append(' \
|
||||
<li class="user-tab" id="user_tab_' + user.id + '" onclick="generate_user_overview(\'' + user.id + '\')" style="cursor:pointer; color: ' + color + ';"> \
|
||||
' + escape(activity.extra.Nickname) + ' \
|
||||
<span class="badge pull-right" style="background-color: ' + ban_badge_color + ';">' + user.bans.length + ' <i class="fa fa-gavel"></i></span> \
|
||||
<span class="badge pull-right" style="background-color: ' + warn_badge_color + '; margin-left: 3px; margin-right: 3px;">' + user.warnings.length + ' <i class="fa fa-exclamation-triangle"></i></span> \
|
||||
</li> \
|
||||
');
|
||||
|
||||
ids.push(user.id);
|
||||
});
|
||||
|
||||
if (ids.length == 1) {
|
||||
generate_user_overview(ids[0]);
|
||||
} else if (ids.length == 0) {
|
||||
$('#user_content_empty').show();
|
||||
$('#user_content').hide();
|
||||
}
|
||||
}
|
||||
|
||||
function generate_user_overview(user_id) {
|
||||
current_user = null;
|
||||
|
||||
$('#user_content_empty').hide();
|
||||
$('#user_content').hide();
|
||||
|
||||
var user = users_by_id[user_id];
|
||||
if (user == null) { return; }
|
||||
|
||||
var activity = user.activities[0];
|
||||
if (activity == null) { return; }
|
||||
|
||||
current_user = user;
|
||||
|
||||
$('#user_content_name').text(activity.extra.Nickname);
|
||||
$('#user_content_username').text(user.username);
|
||||
|
||||
if (activity.extra.Nickname === user.username) {
|
||||
$('#user_content_username').hide();
|
||||
} else {
|
||||
$('#user_content_username').show();
|
||||
}
|
||||
|
||||
$('.user-tab').removeClass("active");
|
||||
$('#user_tab_' + user_id).addClass("active");
|
||||
|
||||
$('#user_content_warnings').html('');
|
||||
user.warnings.forEach(function(warning) {
|
||||
var row_class = "success";
|
||||
|
||||
if (warning.disabled) {
|
||||
row_class = "active";
|
||||
} else if (!warning.active) {
|
||||
row_class = "warning";
|
||||
}
|
||||
|
||||
$('#user_content_warnings').append(' \
|
||||
<tr> \
|
||||
<td class="' + row_class + '"></td> \
|
||||
<td>' + escape(warning.reason) + '</td> \
|
||||
<td>' + escape(warning.creator.username) + '</td> \
|
||||
<td>' + format_date(warning.created_on) + '</td> \
|
||||
<td class="text-right"> \
|
||||
<button class="btn btn-default btn-xs perm-warning_edit" onclick="vyhub.warning_toggle(\'' + warning.id + '\')"><i class="fa fa-play"></i><i class="fa fa-pause"></i></button> \
|
||||
<button class="btn btn-default btn-xs perm-warning_delete" onclick="vyhub.warning_delete(\'' + warning.id + '\')"><i class="fa fa-trash"></i></button> \
|
||||
</td> \
|
||||
</tr> \
|
||||
');
|
||||
});
|
||||
|
||||
$('#user_content_bans').html('');
|
||||
user.bans.forEach(function(ban) {
|
||||
var minutes = '∞';
|
||||
|
||||
if (ban.length != null) {
|
||||
minutes = Math.round(ban.length/60);
|
||||
}
|
||||
|
||||
var row_class = "success";
|
||||
|
||||
if (ban.status == "UNBANNED") {
|
||||
row_class = "warning";
|
||||
} else if (!ban.active) {
|
||||
row_class = "danger";
|
||||
} else if (ban.serverbundle == null) {
|
||||
row_class = "info";
|
||||
}
|
||||
|
||||
var actions = "";
|
||||
|
||||
if (ban.status == "ACTIVE") {
|
||||
actions += '<button class="btn btn-default btn-xs perm-ban_edit" onclick="vyhub.ban_set_status(\'' + ban.id + '\', \'UNBANNED\')"><i class="fa fa-check"></i> ]] .. VyHub.lang.other.unban .. [[</button>';
|
||||
} else if (ban.status == "UNBANNED") {
|
||||
actions += '<button class="btn btn-default btn-xs perm-ban_edit" onclick="vyhub.ban_set_status(\'' + ban.id + '\', \'ACTIVE\')"><i class="fa fa-gavel"></i> ]] .. VyHub.lang.other.reban .. [[</button>';
|
||||
}
|
||||
|
||||
$('#user_content_bans').append(' \
|
||||
<tr> \
|
||||
<td class="' + row_class + '"></td> \
|
||||
<td>' + escape(ban.reason) + '</td> \
|
||||
<td>' + escape(ban.creator.username) + '</td> \
|
||||
<td>' + format_date(ban.created_on) + '</td> \
|
||||
<td>' + minutes + '</td> \
|
||||
<td class="text-right">' + actions + '</td> \
|
||||
</tr> \
|
||||
');
|
||||
});
|
||||
|
||||
$('#user_memberships').html('');
|
||||
|
||||
user.memberships.forEach(function(membership) {
|
||||
$('#user_memberships').append('<span class="label label-default" style="background-color: ' + membership.group.color + ';">' + membership.group.name + '</span>');
|
||||
});
|
||||
|
||||
$('#user_content').show();
|
||||
|
||||
enforce_perms();
|
||||
}
|
||||
|
||||
function reload_current_user() {
|
||||
if (current_user != null) {
|
||||
generate_user_overview(current_user.id);
|
||||
}
|
||||
}
|
||||
|
||||
function create_warning() {
|
||||
if (current_user == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = $('#user_warn').val();
|
||||
|
||||
vyhub.warning_create(current_user.identifier, reason);
|
||||
|
||||
$('#user_warn').val('');
|
||||
}
|
||||
|
||||
function create_ban() {
|
||||
if (current_user == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var reason = $('#user_ban_reason').val();
|
||||
var minutes = $('#user_ban_minutes').val();
|
||||
|
||||
vyhub.ban_create(current_user.identifier, minutes, reason);
|
||||
|
||||
$('#user_ban_reason').val('');
|
||||
$('#user_ban_minutes').val('');
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
]]
|
||||
end
|
||||
|
||||
|
||||
function VyHub.Dashboard:load_users(users_json)
|
||||
VyHub.Dashboard.ui_html:RunJavascript("load_data(" .. users_json .. ");")
|
||||
VyHub.Dashboard.ui_html:RunJavascript("reload_current_user();")
|
||||
end
|
||||
|
||||
function VyHub.Dashboard:load_perms(perms_json)
|
||||
VyHub.Dashboard.ui_html:RunJavascript("load_perms(" .. perms_json .. ");")
|
||||
end
|
||||
|
||||
concommand.Add("vh_dashboard", function ()
|
||||
if VyHub.Dashboard.ui == nil or not VyHub.Dashboard.ui:IsValid() then
|
||||
VyHub.Dashboard:create_ui()
|
||||
VyHub.Dashboard.ui:Show()
|
||||
VyHub.Dashboard.ui:MakePopup()
|
||||
else
|
||||
-- VyHub.Dashboard:create_ui()
|
||||
|
||||
-- if VyHub.Dashboard.ui != nil and VyHub.Dashboard.ui:IsValid() and VyHub.Dashboard.ui:IsVisible() then
|
||||
-- VyHub.Dashboard.ui:Hide()
|
||||
-- else
|
||||
VyHub.Dashboard.ui:Show()
|
||||
VyHub.Dashboard.ui:MakePopup()
|
||||
-- end
|
||||
end
|
||||
|
||||
net.Start("vyhub_dashboard")
|
||||
net.SendToServer()
|
||||
end)
|
||||
|
||||
|
||||
net.Receive("vyhub_dashboard", function()
|
||||
local data_length = net.ReadUInt(16)
|
||||
local data_raw = net.ReadData(data_length)
|
||||
local perms_json = net.ReadString()
|
||||
local users_json = util.Decompress(data_raw)
|
||||
|
||||
timer.Create("vyhub_dashboard_html_ready", 0.3, 20, function ()
|
||||
if not VyHub.Dashboard.html_ready then
|
||||
MsgN("VyHub Dashboard: Waiting for HTML to load.")
|
||||
return
|
||||
end
|
||||
|
||||
timer.Remove("vyhub_dashboard_html_ready")
|
||||
|
||||
VyHub.Dashboard:load_perms(perms_json)
|
||||
VyHub.Dashboard:load_users(users_json)
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
net.Receive("vyhub_dashboard_reload", function()
|
||||
if VyHub.Dashboard.ui and VyHub.Dashboard.ui:IsVisible() then
|
||||
MsgN("Reloading dashboard data, because server told us.")
|
||||
net.Start("vyhub_dashboard")
|
||||
net.SendToServer()
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_lang_loaded", "vyhub_dashboard_vyhub_lang_loaded", function ()
|
||||
VyHub.Dashboard:load_html()
|
||||
end)
|
||||
|
||||
if VyHub.Dashboard.html_generated then
|
||||
VyHub.Dashboard:load_html()
|
||||
end
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
VyHub.groups_mapped = VyHub.groups_mapped or nil
|
||||
|
||||
net.Receive("vyhub_group_data", function()
|
||||
local num = net.ReadUInt(8)
|
||||
local groups_mapped_new = {}
|
||||
|
||||
for i=1, num do
|
||||
-- Currently only the name and color of the group is transferred
|
||||
local name_game = net.ReadString()
|
||||
local name = net.ReadString()
|
||||
local color = net.ReadString()
|
||||
|
||||
groups_mapped_new[name_game] = {
|
||||
name = name,
|
||||
color = color,
|
||||
}
|
||||
end
|
||||
|
||||
VyHub.groups_mapped = groups_mapped_new
|
||||
end)
|
||||
@@ -1,27 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local meta_ply = FindMetaTable("Player")
|
||||
|
||||
VyHub.user_id = VyHub.user_id or nil
|
||||
|
||||
function meta_ply:VyHubID()
|
||||
if IsValid(self) then
|
||||
if self == LocalPlayer() then
|
||||
return VyHub.user_id
|
||||
else
|
||||
MsgN("ERROR: Cannot get VyHubID of other users on the client side.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive("vyhub_user_id", function ()
|
||||
VyHub.user_id = net.ReadString()
|
||||
end)
|
||||
@@ -1,26 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- Language used. Currently only languages in the "lang" directory are supported.
|
||||
VyHub.Config.lang = "en"
|
||||
|
||||
-- Enable chat tags, not working together with DarkRP
|
||||
VyHub.Config.chat_tags = true
|
||||
|
||||
-- Date format to use (for example in ban message)
|
||||
-- See https://tieske.github.io/date/#dateObject.fmt
|
||||
VyHub.Config.date_format = "%Y-%m-%d %H:%M:%S %z"
|
||||
-- Time offset from UTC (+00:00)
|
||||
-- Uses the timezone of the GMOD server when set to nil
|
||||
-- Example: 2 for CEST, -5 for EST
|
||||
VyHub.Config.time_offset = nil
|
||||
|
||||
-- Debug mode. Enable to see all API requests and responses
|
||||
VyHub.Config.debug = false
|
||||
@@ -1,70 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- VyHub Server Config
|
||||
-- BEWARE: Additional config values can be set in data/vyhub/config.json with the `vh_config <key> <value>` console command.
|
||||
-- The configuration in this file is overwritten by the configuration in data/vyhub/config.json
|
||||
|
||||
-- ONLY SET THE 3 FOLLOWING OPTIONS IF YOU KNOW WHAT YOU ARE DOING!
|
||||
-- PLEASE FOLLOW THE INSTALLATION INSTRUCTIONS HERE: https://docs.vyhub.net/latest/game/gmod/#installation
|
||||
VyHub.Config.api_url = "" -- https://api.vyhub.app/<name>/v1
|
||||
VyHub.Config.api_key = "" -- Admin -> Settings -> Server -> Setup
|
||||
VyHub.Config.server_id = "" -- Admin -> Settings -> Server -> Setup
|
||||
|
||||
-- Prevent script execution as reward
|
||||
-- Rewards that want to execute a script will not work if this is enabled.
|
||||
VyHub.Config.reward_disable_scripts = false
|
||||
-- Whitelsit for executed reward commands
|
||||
-- If this table has entries, only commands matching the given patterns are executed
|
||||
-- Patterns: https://wiki.facepunch.com/gmod/Patterns
|
||||
-- Example: { "^ulx adduser %l+ %l+$" } -> Allows a command like "ulx adduser username groupname"
|
||||
VyHub.Config.reward_command_whitelist = {}
|
||||
|
||||
-- Player groups are checked every X seconds
|
||||
VyHub.Config.player_refresh_time = 120
|
||||
-- Groups are refreshed every X seconds
|
||||
VyHub.Config.group_refresh_time = 300
|
||||
-- Every X seconds, an advert message is shown.
|
||||
VyHub.Config.advert_interval = 180
|
||||
|
||||
-- Printed before every advert line
|
||||
VyHub.Config.advert_prefix = "[★] "
|
||||
|
||||
-- Disable group sync
|
||||
VyHub.Config.group_disable_sync = false
|
||||
|
||||
-- Disable override of admin mod bans (ULX, SAM, ServerGuard, xAdmin, ...)
|
||||
VyHub.Config.ban_disable_sync = false
|
||||
-- Replace ULib ban list with VyHub bans
|
||||
VyHub.Config.replace_ulib_bans = false
|
||||
|
||||
-- Commands that open the shop page
|
||||
VyHub.Config.commands_shop = { '!shop' }
|
||||
-- Commands that open the bans page
|
||||
VyHub.Config.commands_bans = { '!bans' }
|
||||
-- Commands that open the warnings page
|
||||
VyHub.Config.commands_warnings = { '!warnings' }
|
||||
-- Commands that open the news page
|
||||
VyHub.Config.commands_news = { '!news' }
|
||||
-- Commands that open the profile page of a user (Usage: !user <user>)
|
||||
VyHub.Config.commands_profile = { '!user' }
|
||||
-- Commands to warn a user (Usage: !warn <user> <reason>)
|
||||
VyHub.Config.commands_warn = { '!warn' }
|
||||
-- Commands to open the dashboard
|
||||
VyHub.Config.commands_dashboard = { '!dashboard' }
|
||||
|
||||
-- Customize the ban message that banned players see when trying to connect
|
||||
VyHub.Config.ban_message = ">>> Ban Message <<<" .. "\n\n"
|
||||
.. VyHub.lang.other.reason .. ": %reason%" .. "\n"
|
||||
.. VyHub.lang.other.ban_date .. ": %ban_date%" .. "\n"
|
||||
.. VyHub.lang.other.unban_date .. ": %unban_date%" .. "\n"
|
||||
.. VyHub.lang.other.admin .. ": %admin%" .. "\n"
|
||||
.. VyHub.lang.other.id .. ": %id%" .. "\n\n"
|
||||
.. VyHub.lang.other.unban_url .. ": %unban_url%" .. "\n\n"
|
||||
@@ -1 +0,0 @@
|
||||
AA==
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2022] [Matbyte UG]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,14 +0,0 @@
|
||||
[](http://translate.matbyte.com/engage/vyhub/)
|
||||
|
||||
# vyhub-lang
|
||||
Translations for VyHub GMOD
|
||||
|
||||
Matbyte Translation Tool (MTT): https://translate.matbyte.com/projects/vyhub/vyhub-lang/
|
||||
|
||||
## Contribute
|
||||
|
||||
1. Create a MTT account (see above)
|
||||
2. Contact us with your username and the language you wish to translate
|
||||
3. After your account has been unlocked, translate as much as you want
|
||||
4. Regularily check for new strings that need to be translated
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"ply": {
|
||||
"no_permissions": "Du hast nicht die nötigen Berechtigungen dafür.",
|
||||
"banned_self": "Du wurdest vom Server gebannt",
|
||||
"group_changed": "Deine Gruppe wurde zu <green>%s</green> geändert.",
|
||||
"error_get": "Spieler konnte nicht gefunden werden. Bitte probiere es später erneut.",
|
||||
"banned": "Spieler <green>%s</green> wurde <red>%s gebannt</red>. Grund: <green>%s</green>",
|
||||
"welcome": "Willkommen auf dem Server, <green>%s</green>"
|
||||
},
|
||||
"warning": {
|
||||
"toggled": "Status der Verwarnung wurde geändert.",
|
||||
"cmd_help": "<red>Ungültiger Spieler!</red> Verwendung: <green>!warn <nick> <reason></green>",
|
||||
"create_error": "Fehler beim hinzufügen einer Verwarnung für <green>%s</green>: <red>%s</red>",
|
||||
"deleted": "Verwarnung gelöscht.",
|
||||
"received": "Du wurdest von <green>%s</green> verwarnt: <red>%s</red>",
|
||||
"toggled_self": "Der Status einer deiner Verwarnungen wurde geändert. Du kannst deiner Verwarnungen unter !dashboard sehen.",
|
||||
"user_warned": "<red>%s</red> wurde von <green>%s</green> verwarnt. Grund: <green>%s</green>",
|
||||
"deleted_self": "Eine deiner Verwarnungen wurde gelöscht. Du kannst deiner Verwarnungen unter !dashboard sehen."
|
||||
},
|
||||
"ban": {
|
||||
"status_changed": "Ban Status von %s erfolgreich auf %s gesetzt.",
|
||||
"user_banned": "<red>%s</red> wurde von <green>%s</green> für <red>%s</red> vom Server gebannt. Grund: <green>%s</green>"
|
||||
},
|
||||
"rslots": {
|
||||
"full": "Der Server ist voll",
|
||||
"full_no_slot": "Der Server ist voll und du hast keinen Zugriff auf einen reservierten Slot",
|
||||
"kick": "Ein Spieler mit einem reservierten Slot ist dem Server beigetreten und du wurdest gekickt um Platz zu schaffen"
|
||||
},
|
||||
"other": {
|
||||
"id": "ID",
|
||||
"reason": "Grund",
|
||||
"dead": "TOT",
|
||||
"ban_date": "Ban Datum",
|
||||
"unban_date": "Ban Ende",
|
||||
"admin": "Admin",
|
||||
"unknown": "Unbekannt",
|
||||
"minutes": "Minuten",
|
||||
"x_minutes": "%i Minuten",
|
||||
"permanently": "Permanent",
|
||||
"unban_url": "Entbannungs URL",
|
||||
"error_api": "Fehler bei der Kommunikation mit der API: %s",
|
||||
"never": "Nie",
|
||||
"active_global": "Aktiv (Global)",
|
||||
"unban": "Entbannen",
|
||||
"actions": "Aktionen",
|
||||
"active": "Aktiv",
|
||||
"inactive": "Inaktiv",
|
||||
"disabled": "Deaktiviert",
|
||||
"unbanned": "Entbannt",
|
||||
"reban": "Bannen",
|
||||
"warnings": "Verwarnungen",
|
||||
"date": "Datum",
|
||||
"warn": "Verwarnung",
|
||||
"ban": "Bann",
|
||||
"bans": "Banns",
|
||||
"welcome": "Willkommen"
|
||||
},
|
||||
"dashboard": {
|
||||
"select_user": "Bitte wähle einen Spieler aus.",
|
||||
"action_ban": "Bannen",
|
||||
"action_warn": "Verwarnen"
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"ply": {
|
||||
"banned": "Player <green>%s</green> has been <red>banned for %s</red>. Reason: <green>%s</green>",
|
||||
"no_permissions": "You don't have all required permissions.",
|
||||
"banned_self": "You are banned from this server",
|
||||
"group_changed": "Your group has been changed to <green>%s</green>.",
|
||||
"error_get": "Could find player. Please try again.",
|
||||
"welcome": "Welcome on the server, <green>%s</green>"
|
||||
},
|
||||
"warning": {
|
||||
"user_warned": "<red>%s</red> has been warned by <green>%s</green>. Reason: <green>%s</green>",
|
||||
"cmd_help": "<red>Invlid player!</red> Usage: <green>!warn <nick> <reason></green>",
|
||||
"toggled_self": "The status of one of your warnings has been changed. Check your warnings with !dashboard.",
|
||||
"create_error": "Error while adding warning for player <green>%s</green>: <red>%s</red>",
|
||||
"toggled": "Warning status changed.",
|
||||
"deleted": "Warning deleted.",
|
||||
"deleted_self": "One of your warnings has been deleted. Check your warnings with !dashboard.",
|
||||
"received": "You have received a warning by <green>%s</green>: <red>%s</red>"
|
||||
},
|
||||
"ban": {
|
||||
"status_changed": "Successfully set ban status of %s to %s.",
|
||||
"user_banned": "<red>%s</red> has been banned by <green>%s</green> for <red>%s</red>. Reason: <green>%s</green>"
|
||||
},
|
||||
"rslots": {
|
||||
"kick": "A player with a reserved slot connected to the server and you got kicked to free up space",
|
||||
"full": "The server is full",
|
||||
"full_no_slot": "The server is full and you do not have access to a reserved slot"
|
||||
},
|
||||
"dashboard": {
|
||||
"select_user": "Please select an user.",
|
||||
"action_ban": "Ban",
|
||||
"action_warn": "Warn"
|
||||
},
|
||||
"other": {
|
||||
"active": "Active",
|
||||
"active_global": "Active (Global)",
|
||||
"inactive": "Inactive",
|
||||
"disabled": "Disabled",
|
||||
"unbanned": "Unbanned",
|
||||
"id": "ID",
|
||||
"reason": "Reason",
|
||||
"dead": "DEAD",
|
||||
"ban_date": "Ban date",
|
||||
"unban_date": "Unban date",
|
||||
"unban": "Unban",
|
||||
"reban": "Reban",
|
||||
"admin": "Admin",
|
||||
"welcome": "Welcome",
|
||||
"actions": "Actions",
|
||||
"never": "Never",
|
||||
"warnings": "Warnings",
|
||||
"date": "Date",
|
||||
"ban": "Ban",
|
||||
"warn": "Warn",
|
||||
"bans": "Bans",
|
||||
"unknown": "Unknown",
|
||||
"minutes": "Minutes",
|
||||
"x_minutes": "%i Minutes",
|
||||
"permanently": "Permanently",
|
||||
"unban_url": "Unban URL",
|
||||
"error_api": "Error while communicating with the API: %s"
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"ply": {
|
||||
"banned": "Le joueur <green>%s</green> a été <red>banni pendant %s</red>. Raison : <green>%s</green>",
|
||||
"no_permissions": "Vous ne disposez pas de toutes les permissions requises.",
|
||||
"banned_self": "Vous êtes banni de ce serveur",
|
||||
"error_get": "Pourrait trouver un joueur. Veuillez réessayer.",
|
||||
"group_changed": "Votre groupe a été changé par <green>%s</green>.",
|
||||
"welcome": "Bienvenue sur le serveur, <green>%s</green>"
|
||||
},
|
||||
"warning": {
|
||||
"user_warned": "<red>%s</red> a été averti par <green>%s</green>. Raison : <green>%s</green>",
|
||||
"toggled_self": "L'état de l'un de vos avertissements a été modifié. Vérifiez vos avertissements avec !dashboard.",
|
||||
"create_error": "Erreur lors de l'ajout d'un avertissement pour le joueur <green>%s</green> : <red>%s</red>",
|
||||
"deleted": "Avertissement supprimé.",
|
||||
"deleted_self": "L'un de vos avertissements a été supprimé. Vérifiez vos avertissements avec !dashboard.",
|
||||
"toggled": "L'état d'avertissement a changé.",
|
||||
"cmd_help": "<red>Joueur invalide !</red> Utilisation : <green>!warn <joueur> <raison></green>",
|
||||
"received": "Vous avez reçu un avertissement de <green>%s</green> : <red>%s</red>"
|
||||
},
|
||||
"ban": {
|
||||
"status_changed": "Le statut de bannissement de %s a été défini avec succès sur %s.",
|
||||
"user_banned": "<red>%s</red> a été banni par <green>%s</green> pour <red>%s</red>. Raison : <green>%s</green>"
|
||||
},
|
||||
"rslots": {
|
||||
"full": "Le serveur est plein",
|
||||
"full_no_slot": "Le serveur est plein et vous n'avez pas accès à un créneau réservé",
|
||||
"kick": "Un joueur avec un emplacement réservé connecté au serveur et vous avez été expulsé pour libérer de l'espace"
|
||||
},
|
||||
"dashboard": {
|
||||
"select_user": "Veuillez sélectionner un utilisateur.",
|
||||
"action_warn": "Avertir",
|
||||
"action_ban": "Bannir"
|
||||
},
|
||||
"other": {
|
||||
"active": "Actif",
|
||||
"active_global": "Actif (Global)",
|
||||
"inactive": "Inactif",
|
||||
"disabled": "Désactivé",
|
||||
"unbanned": "Débannir",
|
||||
"id": "ID",
|
||||
"reason": "Raison",
|
||||
"dead": "Mort",
|
||||
"ban_date": "Date de bannissement",
|
||||
"unban": "Débannir",
|
||||
"unban_date": "Date de débannissement",
|
||||
"reban": "Rebannir",
|
||||
"admin": "Administrateur",
|
||||
"actions": "Actions",
|
||||
"never": "Jamais",
|
||||
"warnings": "Avertissements",
|
||||
"date": "Date",
|
||||
"ban": "Bannir",
|
||||
"warn": "Avertir",
|
||||
"bans": "Bannissements",
|
||||
"minutes": "Minutes",
|
||||
"unknown": "Inconnu",
|
||||
"x_minutes": "%i Minutes",
|
||||
"unban_url": "URL de débannissement",
|
||||
"permanently": "Permanent",
|
||||
"error_api": "Erreur lors de la communication avec l'API : %s",
|
||||
"welcome": "Bienvenue"
|
||||
}
|
||||
}
|
||||
@@ -1,766 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
VyHub.Lib.date = VyHub.Lib.date or {}
|
||||
|
||||
---------------------------------------------------------------------------------------
|
||||
-- Module for date and time calculations
|
||||
--
|
||||
-- Version 2.2
|
||||
-- Copyright (C) 2005-2006, by Jas Latrix (jastejada@yahoo.com)
|
||||
-- Copyright (C) 2013-2021, by Thijs Schreijer
|
||||
-- Licensed under MIT, http://opensource.org/licenses/MIT
|
||||
|
||||
-- https://github.com/Tieske/date
|
||||
|
||||
-- Changes by Matbyte: New function setbias
|
||||
|
||||
--[[ CONSTANTS ]]--
|
||||
local HOURPERDAY = 24
|
||||
local MINPERHOUR = 60
|
||||
local MINPERDAY = 1440 -- 24*60
|
||||
local SECPERMIN = 60
|
||||
local SECPERHOUR = 3600 -- 60*60
|
||||
local SECPERDAY = 86400 -- 24*60*60
|
||||
local TICKSPERSEC = 1000000
|
||||
local TICKSPERDAY = 86400000000
|
||||
local TICKSPERHOUR = 3600000000
|
||||
local TICKSPERMIN = 60000000
|
||||
local DAYNUM_MAX = 365242500 -- Sat Jan 01 1000000 00:00:00
|
||||
local DAYNUM_MIN = -365242500 -- Mon Jan 01 1000000 BCE 00:00:00
|
||||
local DAYNUM_DEF = 0 -- Mon Jan 01 0001 00:00:00
|
||||
local _;
|
||||
--[[ GLOBAL SETTINGS ]]--
|
||||
local centuryflip = 0 -- year >= centuryflip == 1900, < centuryflip == 2000
|
||||
--[[ LOCAL ARE FASTER ]]--
|
||||
local type = type
|
||||
local pairs = pairs
|
||||
local error = error
|
||||
local assert = assert
|
||||
local tonumber = tonumber
|
||||
local tostring = tostring
|
||||
local string = string
|
||||
local math = math
|
||||
local os = os
|
||||
local unpack = unpack or table.unpack
|
||||
local setmetatable = setmetatable
|
||||
local getmetatable = getmetatable
|
||||
--[[ EXTRA FUNCTIONS ]]--
|
||||
local fmt = string.format
|
||||
local lwr = string.lower
|
||||
local rep = string.rep
|
||||
local len = string.len -- luacheck: ignore
|
||||
local sub = string.sub
|
||||
local gsub = string.gsub
|
||||
local gmatch = string.gmatch or string.gfind
|
||||
local find = string.find
|
||||
local ostime = os.time
|
||||
local osdate = os.date
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local abs = math.abs
|
||||
-- removes the decimal part of a number
|
||||
local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end
|
||||
-- returns the modulo n % d;
|
||||
local function mod(n,d) return n - d*floor(n/d) end
|
||||
-- is `str` in string list `tbl`, `ml` is the minimun len
|
||||
local function inlist(str, tbl, ml, tn)
|
||||
local sl = len(str)
|
||||
if sl < (ml or 0) then return nil end
|
||||
str = lwr(str)
|
||||
for k, v in pairs(tbl) do
|
||||
if str == lwr(sub(v, 1, sl)) then
|
||||
if tn then tn[0] = k end
|
||||
return k
|
||||
end
|
||||
end
|
||||
end
|
||||
local function fnil() end
|
||||
--[[ DATE FUNCTIONS ]]--
|
||||
local DATE_EPOCH -- to be set later
|
||||
local sl_weekdays = {
|
||||
[0]="Sunday",[1]="Monday",[2]="Tuesday",[3]="Wednesday",[4]="Thursday",[5]="Friday",[6]="Saturday",
|
||||
[7]="Sun",[8]="Mon",[9]="Tue",[10]="Wed",[11]="Thu",[12]="Fri",[13]="Sat",
|
||||
}
|
||||
local sl_meridian = {[-1]="AM", [1]="PM"}
|
||||
local sl_months = {
|
||||
[00]="January", [01]="February", [02]="March",
|
||||
[03]="April", [04]="May", [05]="June",
|
||||
[06]="July", [07]="August", [08]="September",
|
||||
[09]="October", [10]="November", [11]="December",
|
||||
[12]="Jan", [13]="Feb", [14]="Mar",
|
||||
[15]="Apr", [16]="May", [17]="Jun",
|
||||
[18]="Jul", [19]="Aug", [20]="Sep",
|
||||
[21]="Oct", [22]="Nov", [23]="Dec",
|
||||
}
|
||||
-- added the '.2' to avoid collision, use `fix` to remove
|
||||
local sl_timezone = {
|
||||
[000]="utc", [0.2]="gmt",
|
||||
[300]="est", [240]="edt",
|
||||
[360]="cst", [300.2]="cdt",
|
||||
[420]="mst", [360.2]="mdt",
|
||||
[480]="pst", [420.2]="pdt",
|
||||
}
|
||||
-- set the day fraction resolution
|
||||
local function setticks(t)
|
||||
TICKSPERSEC = t;
|
||||
TICKSPERDAY = SECPERDAY*TICKSPERSEC
|
||||
TICKSPERHOUR= SECPERHOUR*TICKSPERSEC
|
||||
TICKSPERMIN = SECPERMIN*TICKSPERSEC
|
||||
end
|
||||
-- is year y leap year?
|
||||
local function isleapyear(y) -- y must be int!
|
||||
return (mod(y, 4) == 0 and (mod(y, 100) ~= 0 or mod(y, 400) == 0))
|
||||
end
|
||||
-- day since year 0
|
||||
local function dayfromyear(y) -- y must be int!
|
||||
return 365*y + floor(y/4) - floor(y/100) + floor(y/400)
|
||||
end
|
||||
-- day number from date, month is zero base
|
||||
local function makedaynum(y, m, d)
|
||||
local mm = mod(mod(m,12) + 10, 12)
|
||||
return dayfromyear(y + floor(m/12) - floor(mm/10)) + floor((mm*306 + 5)/10) + d - 307
|
||||
--local yy = y + floor(m/12) - floor(mm/10)
|
||||
--return dayfromyear(yy) + floor((mm*306 + 5)/10) + (d - 1)
|
||||
end
|
||||
-- date from day number, month is zero base
|
||||
local function breakdaynum(g)
|
||||
local g = g + 306
|
||||
local y = floor((10000*g + 14780)/3652425)
|
||||
local d = g - dayfromyear(y)
|
||||
if d < 0 then y = y - 1; d = g - dayfromyear(y) end
|
||||
local mi = floor((100*d + 52)/3060)
|
||||
return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1)
|
||||
end
|
||||
--[[ for floats or int32 Lua Number data type
|
||||
local function breakdaynum2(g)
|
||||
local g, n = g + 306;
|
||||
local n400 = floor(g/DI400Y);n = mod(g,DI400Y);
|
||||
local n100 = floor(n/DI100Y);n = mod(n,DI100Y);
|
||||
local n004 = floor(n/DI4Y); n = mod(n,DI4Y);
|
||||
local n001 = floor(n/365); n = mod(n,365);
|
||||
local y = (n400*400) + (n100*100) + (n004*4) + n001 - ((n001 == 4 or n100 == 4) and 1 or 0)
|
||||
local d = g - dayfromyear(y)
|
||||
local mi = floor((100*d + 52)/3060)
|
||||
return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1)
|
||||
end
|
||||
]]
|
||||
-- day fraction from time
|
||||
local function makedayfrc(h,r,s,t)
|
||||
return ((h*60 + r)*60 + s)*TICKSPERSEC + t
|
||||
end
|
||||
-- time from day fraction
|
||||
local function breakdayfrc(df)
|
||||
return
|
||||
mod(floor(df/TICKSPERHOUR),HOURPERDAY),
|
||||
mod(floor(df/TICKSPERMIN ),MINPERHOUR),
|
||||
mod(floor(df/TICKSPERSEC ),SECPERMIN),
|
||||
mod(df,TICKSPERSEC)
|
||||
end
|
||||
-- weekday sunday = 0, monday = 1 ...
|
||||
local function weekday(dn) return mod(dn + 1, 7) end
|
||||
-- yearday 0 based ...
|
||||
local function yearday(dn)
|
||||
return dn - dayfromyear((breakdaynum(dn))-1)
|
||||
end
|
||||
-- parse v as a month
|
||||
local function getmontharg(v)
|
||||
local m = tonumber(v);
|
||||
return (m and fix(m - 1)) or inlist(tostring(v) or "", sl_months, 2)
|
||||
end
|
||||
-- get daynum of isoweek one of year y
|
||||
local function isow1(y)
|
||||
local f = makedaynum(y, 0, 4) -- get the date for the 4-Jan of year `y`
|
||||
local d = weekday(f)
|
||||
d = d == 0 and 7 or d -- get the ISO day number, 1 == Monday, 7 == Sunday
|
||||
return f + (1 - d)
|
||||
end
|
||||
local function isowy(dn)
|
||||
local w1;
|
||||
local y = (breakdaynum(dn))
|
||||
if dn >= makedaynum(y, 11, 29) then
|
||||
w1 = isow1(y + 1);
|
||||
if dn < w1 then
|
||||
w1 = isow1(y);
|
||||
else
|
||||
y = y + 1;
|
||||
end
|
||||
else
|
||||
w1 = isow1(y);
|
||||
if dn < w1 then
|
||||
w1 = isow1(y-1)
|
||||
y = y - 1
|
||||
end
|
||||
end
|
||||
return floor((dn-w1)/7)+1, y
|
||||
end
|
||||
local function isoy(dn)
|
||||
local y = (breakdaynum(dn))
|
||||
return y + (((dn >= makedaynum(y, 11, 29)) and (dn >= isow1(y + 1))) and 1 or (dn < isow1(y) and -1 or 0))
|
||||
end
|
||||
local function makedaynum_isoywd(y,w,d)
|
||||
return isow1(y) + 7*w + d - 8 -- simplified: isow1(y) + ((w-1)*7) + (d-1)
|
||||
end
|
||||
--[[ THE DATE MODULE ]]--
|
||||
local fmtstr = "%x %X";
|
||||
--#if not DATE_OBJECT_AFX then
|
||||
local date = {}
|
||||
setmetatable(date, date)
|
||||
-- Version: VMMMRRRR; V-Major, M-Minor, R-Revision; e.g. 5.45.321 == 50450321
|
||||
do
|
||||
local major = 2
|
||||
local minor = 2
|
||||
local revision = 0
|
||||
date.version = major * 10000000 + minor * 10000 + revision
|
||||
end
|
||||
--#end -- not DATE_OBJECT_AFX
|
||||
--[[ THE DATE OBJECT ]]--
|
||||
local dobj = {}
|
||||
dobj.__index = dobj
|
||||
dobj.__metatable = dobj
|
||||
-- shout invalid arg
|
||||
local function date_error_arg() return error("invalid argument(s)",0) end
|
||||
-- create new date object
|
||||
local function date_new(dn, df)
|
||||
return setmetatable({daynum=dn, dayfrc=df}, dobj)
|
||||
end
|
||||
|
||||
--#if not NO_LOCAL_TIME_SUPPORT then
|
||||
-- magic year table
|
||||
local date_epoch, yt;
|
||||
local function getequivyear(y)
|
||||
assert(not yt)
|
||||
yt = {}
|
||||
local de = date_epoch:copy()
|
||||
local dw, dy
|
||||
for _ = 0, 3000 do
|
||||
de:setyear(de:getyear() + 1, 1, 1)
|
||||
dy = de:getyear()
|
||||
dw = de:getweekday() * (isleapyear(dy) and -1 or 1)
|
||||
if not yt[dw] then yt[dw] = dy end --print(de)
|
||||
if yt[1] and yt[2] and yt[3] and yt[4] and yt[5] and yt[6] and yt[7] and yt[-1] and yt[-2] and yt[-3] and yt[-4] and yt[-5] and yt[-6] and yt[-7] then
|
||||
getequivyear = function(y) return yt[ (weekday(makedaynum(y, 0, 1)) + 1) * (isleapyear(y) and -1 or 1) ] end
|
||||
return getequivyear(y)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- TimeValue from date and time
|
||||
local function totv(y,m,d,h,r,s)
|
||||
return (makedaynum(y, m, d) - DATE_EPOCH) * SECPERDAY + ((h*60 + r)*60 + s)
|
||||
end
|
||||
-- TimeValue from TimeTable
|
||||
local function tmtotv(tm)
|
||||
return tm and totv(tm.year, tm.month - 1, tm.day, tm.hour, tm.min, tm.sec)
|
||||
end
|
||||
-- Returns the bias in seconds of utc time daynum and dayfrc
|
||||
local function getbiasutc2(self)
|
||||
local y,m,d = breakdaynum(self.daynum)
|
||||
local h,r,s = breakdayfrc(self.dayfrc)
|
||||
local tvu = totv(y,m,d,h,r,s) -- get the utc TimeValue of date and time
|
||||
local tml = osdate("*t", tvu) -- get the local TimeTable of tvu
|
||||
if (not tml) or (tml.year > (y+1) or tml.year < (y-1)) then -- failed try the magic
|
||||
y = getequivyear(y)
|
||||
tvu = totv(y,m,d,h,r,s)
|
||||
tml = osdate("*t", tvu)
|
||||
end
|
||||
local tvl = tmtotv(tml)
|
||||
if tvu and tvl then
|
||||
return tvu - tvl, tvu, tvl
|
||||
else
|
||||
return error("failed to get bias from utc time")
|
||||
end
|
||||
end
|
||||
-- Returns the bias in seconds of local time daynum and dayfrc
|
||||
local function getbiasloc2(daynum, dayfrc)
|
||||
local tvu
|
||||
-- extract date and time
|
||||
local y,m,d = breakdaynum(daynum)
|
||||
local h,r,s = breakdayfrc(dayfrc)
|
||||
-- get equivalent TimeTable
|
||||
local tml = {year=y, month=m+1, day=d, hour=h, min=r, sec=s}
|
||||
-- get equivalent TimeValue
|
||||
local tvl = tmtotv(tml)
|
||||
|
||||
local function chkutc()
|
||||
tml.isdst = nil; local tvug = ostime(tml) if tvug and (tvl == tmtotv(osdate("*t", tvug))) then tvu = tvug return end
|
||||
tml.isdst = true; local tvud = ostime(tml) if tvud and (tvl == tmtotv(osdate("*t", tvud))) then tvu = tvud return end
|
||||
tvu = tvud or tvug
|
||||
end
|
||||
chkutc()
|
||||
if not tvu then
|
||||
tml.year = getequivyear(y)
|
||||
tvl = tmtotv(tml)
|
||||
chkutc()
|
||||
end
|
||||
return ((tvu and tvl) and (tvu - tvl)) or error("failed to get bias from local time"), tvu, tvl
|
||||
end
|
||||
--#end -- not NO_LOCAL_TIME_SUPPORT
|
||||
|
||||
--#if not DATE_OBJECT_AFX then
|
||||
-- the date parser
|
||||
local strwalker = {} -- ^Lua regular expression is not as powerful as Perl$
|
||||
strwalker.__index = strwalker
|
||||
local function newstrwalker(s)return setmetatable({s=s, i=1, e=1, c=len(s)}, strwalker) end
|
||||
function strwalker:aimchr() return "\n" .. self.s .. "\n" .. rep(".",self.e-1) .. "^" end
|
||||
function strwalker:finish() return self.i > self.c end
|
||||
function strwalker:back() self.i = self.e return self end
|
||||
function strwalker:restart() self.i, self.e = 1, 1 return self end
|
||||
function strwalker:match(s) return (find(self.s, s, self.i)) end
|
||||
function strwalker:__call(s, f)-- print("strwalker:__call "..s..self:aimchr())
|
||||
local is, ie; is, ie, self[1], self[2], self[3], self[4], self[5] = find(self.s, s, self.i)
|
||||
if is then self.e, self.i = self.i, 1+ie; if f then f(unpack(self)) end return self end
|
||||
end
|
||||
local function date_parse(str)
|
||||
local y,m,d, h,r,s, z, w,u, j, e, x,c, dn,df
|
||||
local sw = newstrwalker(gsub(gsub(str, "(%b())", ""),"^(%s*)","")) -- remove comment, trim leading space
|
||||
--local function error_out() print(y,m,d,h,r,s) end
|
||||
local function error_dup(q) --[[error_out()]] error("duplicate value: " .. (q or "") .. sw:aimchr()) end
|
||||
local function error_syn(q) --[[error_out()]] error("syntax error: " .. (q or "") .. sw:aimchr()) end
|
||||
local function error_inv(q) --[[error_out()]] error("invalid date: " .. (q or "") .. sw:aimchr()) end
|
||||
local function sety(q) y = y and error_dup() or tonumber(q); end
|
||||
local function setm(q) m = (m or w or j) and error_dup(m or w or j) or tonumber(q) end
|
||||
local function setd(q) d = d and error_dup() or tonumber(q) end
|
||||
local function seth(q) h = h and error_dup() or tonumber(q) end
|
||||
local function setr(q) r = r and error_dup() or tonumber(q) end
|
||||
local function sets(q) s = s and error_dup() or tonumber(q) end
|
||||
local function adds(q) s = s + tonumber(q) end
|
||||
local function setj(q) j = (m or w or j) and error_dup() or tonumber(q); end
|
||||
local function setz(q) z = (z ~= 0 and z) and error_dup() or q end
|
||||
local function setzn(zs,zn) zn = tonumber(zn); setz( ((zn<24) and (zn*60) or (mod(zn,100) + floor(zn/100) * 60))*( zs=='+' and -1 or 1) ) end
|
||||
local function setzc(zs,zh,zm) setz( ((tonumber(zh)*60) + tonumber(zm))*( zs=='+' and -1 or 1) ) end
|
||||
|
||||
if not (sw("^(%d%d%d%d)",sety) and (sw("^(%-?)(%d%d)%1(%d%d)",function(_,a,b) setm(tonumber(a)); setd(tonumber(b)) end) or sw("^(%-?)[Ww](%d%d)%1(%d?)",function(_,a,b) w, u = tonumber(a), tonumber(b or 1) end) or sw("^%-?(%d%d%d)",setj) or sw("^%-?(%d%d)",function(a) setm(a);setd(1) end))
|
||||
and ((sw("^%s*[Tt]?(%d%d):?",seth) and sw("^(%d%d):?",setr) and sw("^(%d%d)",sets) and sw("^(%.%d+)",adds))
|
||||
or sw:finish() or (sw"^%s*$" or sw"^%s*[Zz]%s*$" or sw("^%s-([%+%-])(%d%d):?(%d%d)%s*$",setzc) or sw("^%s*([%+%-])(%d%d)%s*$",setzn))
|
||||
) )
|
||||
then --print(y,m,d,h,r,s,z,w,u,j)
|
||||
sw:restart(); y,m,d,h,r,s,z,w,u,j = nil,nil,nil,nil,nil,nil,nil,nil,nil,nil
|
||||
repeat -- print(sw:aimchr())
|
||||
if sw("^[tT:]?%s*(%d%d?):",seth) then --print("$Time")
|
||||
_ = sw("^%s*(%d%d?)",setr) and sw("^%s*:%s*(%d%d?)",sets) and sw("^(%.%d+)",adds)
|
||||
elseif sw("^(%d+)[/\\%s,-]?%s*") then --print("$Digits")
|
||||
x, c = tonumber(sw[1]), len(sw[1])
|
||||
if (x >= 70) or (m and d and (not y)) or (c > 3) then
|
||||
sety( x + ((x >= 100 or c>3) and 0 or x<centuryflip and 2000 or 1900) )
|
||||
else
|
||||
if m then setd(x) else m = x end
|
||||
end
|
||||
elseif sw("^(%a+)[/\\%s,-]?%s*") then --print("$Words")
|
||||
x = sw[1]
|
||||
if inlist(x, sl_months, 2, sw) then
|
||||
if m and (not d) and (not y) then d, m = m, false end
|
||||
setm(mod(sw[0],12)+1)
|
||||
elseif inlist(x, sl_timezone, 2, sw) then
|
||||
c = fix(sw[0]) -- ignore gmt and utc
|
||||
if c ~= 0 then setz(c, x) end
|
||||
elseif not inlist(x, sl_weekdays, 2, sw) then
|
||||
sw:back()
|
||||
-- am pm bce ad ce bc
|
||||
if sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*[Ee]%s*(%2)%s*") or sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*") then
|
||||
e = e and error_dup() or -1
|
||||
elseif sw("^([aA])%s*(%.?)%s*[Dd]%s*(%2)%s*") or sw("^([cC])%s*(%.?)%s*[Ee]%s*(%2)%s*") then
|
||||
e = e and error_dup() or 1
|
||||
elseif sw("^([PApa])%s*(%.?)%s*[Mm]?%s*(%2)%s*") then
|
||||
x = lwr(sw[1]) -- there should be hour and it must be correct
|
||||
if (not h) or (h > 12) or (h < 0) then return error_inv() end
|
||||
if x == 'a' and h == 12 then h = 0 end -- am
|
||||
if x == 'p' and h ~= 12 then h = h + 12 end -- pm
|
||||
else error_syn() end
|
||||
end
|
||||
elseif not(sw("^([+-])(%d%d?):(%d%d)",setzc) or sw("^([+-])(%d+)",setzn) or sw("^[Zz]%s*$")) then -- sw{"([+-])",{"(%d%d?):(%d%d)","(%d+)"}}
|
||||
error_syn("?")
|
||||
end
|
||||
sw("^%s*") until sw:finish()
|
||||
--else print("$Iso(Date|Time|Zone)")
|
||||
end
|
||||
-- if date is given, it must be complete year, month & day
|
||||
if (not y and not h) or ((m and not d) or (d and not m)) or ((m and w) or (m and j) or (j and w)) then return error_inv("!") end
|
||||
-- fix month
|
||||
if m then m = m - 1 end
|
||||
-- fix year if we are on BCE
|
||||
if e and e < 0 and y > 0 then y = 1 - y end
|
||||
-- create date object
|
||||
dn = (y and ((w and makedaynum_isoywd(y,w,u)) or (j and makedaynum(y, 0, j)) or makedaynum(y, m, d))) or DAYNUM_DEF
|
||||
df = makedayfrc(h or 0, r or 0, s or 0, 0) + ((z or 0)*TICKSPERMIN)
|
||||
--print("Zone",h,r,s,z,m,d,y,df)
|
||||
return date_new(dn, df) -- no need to :normalize();
|
||||
end
|
||||
local function date_fromtable(v)
|
||||
local y, m, d = fix(v.year), getmontharg(v.month), fix(v.day)
|
||||
local h, r, s, t = tonumber(v.hour), tonumber(v.min), tonumber(v.sec), tonumber(v.ticks)
|
||||
-- atleast there is time or complete date
|
||||
if (y or m or d) and (not(y and m and d)) then return error("incomplete table") end
|
||||
return (y or h or r or s or t) and date_new(y and makedaynum(y, m, d) or DAYNUM_DEF, makedayfrc(h or 0, r or 0, s or 0, t or 0))
|
||||
end
|
||||
local tmap = {
|
||||
['number'] = function(v) return date_epoch:copy():addseconds(v) end,
|
||||
['string'] = function(v) return date_parse(v) end,
|
||||
['boolean']= function(v) return date_fromtable(osdate(v and "!*t" or "*t")) end,
|
||||
['table'] = function(v) local ref = getmetatable(v) == dobj; return ref and v or date_fromtable(v), ref end
|
||||
}
|
||||
local function date_getdobj(v)
|
||||
local o, r = (tmap[type(v)] or fnil)(v);
|
||||
return (o and o:normalize() or error"invalid date time value"), r -- if r is true then o is a reference to a date obj
|
||||
end
|
||||
--#end -- not DATE_OBJECT_AFX
|
||||
local function date_from(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
|
||||
local y, m, d = fix(arg1), getmontharg(arg2), fix(arg3)
|
||||
local h, r, s, t = tonumber(arg4 or 0), tonumber(arg5 or 0), tonumber(arg6 or 0), tonumber(arg7 or 0)
|
||||
if y and m and d and h and r and s and t then
|
||||
return date_new(makedaynum(y, m, d), makedayfrc(h, r, s, t)):normalize()
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
|
||||
--[[ THE DATE OBJECT METHODS ]]--
|
||||
function dobj:normalize()
|
||||
local dn, df = fix(self.daynum), self.dayfrc
|
||||
self.daynum, self.dayfrc = dn + floor(df/TICKSPERDAY), mod(df, TICKSPERDAY)
|
||||
return (dn >= DAYNUM_MIN and dn <= DAYNUM_MAX) and self or error("date beyond imposed limits:"..self)
|
||||
end
|
||||
|
||||
function dobj:getdate() local y, m, d = breakdaynum(self.daynum) return y, m+1, d end
|
||||
function dobj:gettime() return breakdayfrc(self.dayfrc) end
|
||||
|
||||
function dobj:getclockhour() local h = self:gethours() return h>12 and mod(h,12) or (h==0 and 12 or h) end
|
||||
|
||||
function dobj:getyearday() return yearday(self.daynum) + 1 end
|
||||
function dobj:getweekday() return weekday(self.daynum) + 1 end -- in lua weekday is sunday = 1, monday = 2 ...
|
||||
|
||||
function dobj:getyear() local r,_,_ = breakdaynum(self.daynum) return r end
|
||||
function dobj:getmonth() local _,r,_ = breakdaynum(self.daynum) return r+1 end-- in lua month is 1 base
|
||||
function dobj:getday() local _,_,r = breakdaynum(self.daynum) return r end
|
||||
function dobj:gethours() return mod(floor(self.dayfrc/TICKSPERHOUR),HOURPERDAY) end
|
||||
function dobj:getminutes() return mod(floor(self.dayfrc/TICKSPERMIN), MINPERHOUR) end
|
||||
function dobj:getseconds() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN) end
|
||||
function dobj:getfracsec() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)+(mod(self.dayfrc,TICKSPERSEC)/TICKSPERSEC) end
|
||||
function dobj:getticks(u) local x = mod(self.dayfrc,TICKSPERSEC) return u and ((x*u)/TICKSPERSEC) or x end
|
||||
|
||||
function dobj:getweeknumber(wdb)
|
||||
local wd, yd = weekday(self.daynum), yearday(self.daynum)
|
||||
if wdb then
|
||||
wdb = tonumber(wdb)
|
||||
if wdb then
|
||||
wd = mod(wd-(wdb-1),7)-- shift the week day base
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
return (yd < wd and 0) or (floor(yd/7) + ((mod(yd, 7)>=wd) and 1 or 0))
|
||||
end
|
||||
|
||||
function dobj:getisoweekday() return mod(weekday(self.daynum)-1,7)+1 end -- sunday = 7, monday = 1 ...
|
||||
function dobj:getisoweeknumber() return (isowy(self.daynum)) end
|
||||
function dobj:getisoyear() return isoy(self.daynum) end
|
||||
function dobj:getisodate()
|
||||
local w, y = isowy(self.daynum)
|
||||
return y, w, self:getisoweekday()
|
||||
end
|
||||
function dobj:setisoyear(y, w, d)
|
||||
local cy, cw, cd = self:getisodate()
|
||||
if y then cy = fix(tonumber(y))end
|
||||
if w then cw = fix(tonumber(w))end
|
||||
if d then cd = fix(tonumber(d))end
|
||||
if cy and cw and cd then
|
||||
self.daynum = makedaynum_isoywd(cy, cw, cd)
|
||||
return self:normalize()
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
|
||||
function dobj:setisoweekday(d) return self:setisoyear(nil, nil, d) end
|
||||
function dobj:setisoweeknumber(w,d) return self:setisoyear(nil, w, d) end
|
||||
|
||||
function dobj:setyear(y, m, d)
|
||||
local cy, cm, cd = breakdaynum(self.daynum)
|
||||
if y then cy = fix(tonumber(y))end
|
||||
if m then cm = getmontharg(m) end
|
||||
if d then cd = fix(tonumber(d))end
|
||||
if cy and cm and cd then
|
||||
self.daynum = makedaynum(cy, cm, cd)
|
||||
return self:normalize()
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
|
||||
function dobj:setmonth(m, d)return self:setyear(nil, m, d) end
|
||||
function dobj:setday(d) return self:setyear(nil, nil, d) end
|
||||
|
||||
function dobj:sethours(h, m, s, t)
|
||||
local ch,cm,cs,ck = breakdayfrc(self.dayfrc)
|
||||
ch, cm, cs, ck = tonumber(h or ch), tonumber(m or cm), tonumber(s or cs), tonumber(t or ck)
|
||||
if ch and cm and cs and ck then
|
||||
self.dayfrc = makedayfrc(ch, cm, cs, ck)
|
||||
return self:normalize()
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
|
||||
function dobj:setminutes(m,s,t) return self:sethours(nil, m, s, t) end
|
||||
function dobj:setseconds(s, t) return self:sethours(nil, nil, s, t) end
|
||||
function dobj:setticks(t) return self:sethours(nil, nil, nil, t) end
|
||||
|
||||
function dobj:spanticks() return (self.daynum*TICKSPERDAY + self.dayfrc) end
|
||||
function dobj:spanseconds() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERSEC end
|
||||
function dobj:spanminutes() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERMIN end
|
||||
function dobj:spanhours() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERHOUR end
|
||||
function dobj:spandays() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERDAY end
|
||||
|
||||
function dobj:addyears(y, m, d)
|
||||
local cy, cm, cd = breakdaynum(self.daynum)
|
||||
if y then y = fix(tonumber(y))else y = 0 end
|
||||
if m then m = fix(tonumber(m))else m = 0 end
|
||||
if d then d = fix(tonumber(d))else d = 0 end
|
||||
if y and m and d then
|
||||
self.daynum = makedaynum(cy+y, cm+m, cd+d)
|
||||
return self:normalize()
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
|
||||
function dobj:addmonths(m, d)
|
||||
return self:addyears(nil, m, d)
|
||||
end
|
||||
|
||||
local function dobj_adddayfrc(self,n,pt,pd)
|
||||
n = tonumber(n)
|
||||
if n then
|
||||
local x = floor(n/pd);
|
||||
self.daynum = self.daynum + x;
|
||||
self.dayfrc = self.dayfrc + (n-x*pd)*pt;
|
||||
return self:normalize()
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
function dobj:adddays(n) return dobj_adddayfrc(self,n,TICKSPERDAY,1) end
|
||||
function dobj:addhours(n) return dobj_adddayfrc(self,n,TICKSPERHOUR,HOURPERDAY) end
|
||||
function dobj:addminutes(n) return dobj_adddayfrc(self,n,TICKSPERMIN,MINPERDAY) end
|
||||
function dobj:addseconds(n) return dobj_adddayfrc(self,n,TICKSPERSEC,SECPERDAY) end
|
||||
function dobj:addticks(n) return dobj_adddayfrc(self,n,1,TICKSPERDAY) end
|
||||
local tvspec = {
|
||||
-- Abbreviated weekday name (Sun)
|
||||
['%a']=function(self) return sl_weekdays[weekday(self.daynum) + 7] end,
|
||||
-- Full weekday name (Sunday)
|
||||
['%A']=function(self) return sl_weekdays[weekday(self.daynum)] end,
|
||||
-- Abbreviated month name (Dec)
|
||||
['%b']=function(self) return sl_months[self:getmonth() - 1 + 12] end,
|
||||
-- Full month name (December)
|
||||
['%B']=function(self) return sl_months[self:getmonth() - 1] end,
|
||||
-- Year/100 (19, 20, 30)
|
||||
['%C']=function(self) return fmt("%.2d", fix(self:getyear()/100)) end,
|
||||
-- The day of the month as a number (range 1 - 31)
|
||||
['%d']=function(self) return fmt("%.2d", self:getday()) end,
|
||||
-- year for ISO 8601 week, from 00 (79)
|
||||
['%g']=function(self) return fmt("%.2d", mod(self:getisoyear() ,100)) end,
|
||||
-- year for ISO 8601 week, from 0000 (1979)
|
||||
['%G']=function(self) return fmt("%.4d", self:getisoyear()) end,
|
||||
-- same as %b
|
||||
['%h']=function(self) return self:fmt0("%b") end,
|
||||
-- hour of the 24-hour day, from 00 (06)
|
||||
['%H']=function(self) return fmt("%.2d", self:gethours()) end,
|
||||
-- The hour as a number using a 12-hour clock (01 - 12)
|
||||
['%I']=function(self) return fmt("%.2d", self:getclockhour()) end,
|
||||
-- The day of the year as a number (001 - 366)
|
||||
['%j']=function(self) return fmt("%.3d", self:getyearday()) end,
|
||||
-- Month of the year, from 01 to 12
|
||||
['%m']=function(self) return fmt("%.2d", self:getmonth()) end,
|
||||
-- Minutes after the hour 55
|
||||
['%M']=function(self) return fmt("%.2d", self:getminutes())end,
|
||||
-- AM/PM indicator (AM)
|
||||
['%p']=function(self) return sl_meridian[self:gethours() > 11 and 1 or -1] end, --AM/PM indicator (AM)
|
||||
-- The second as a number (59, 20 , 01)
|
||||
['%S']=function(self) return fmt("%.2d", self:getseconds()) end,
|
||||
-- ISO 8601 day of the week, to 7 for Sunday (7, 1)
|
||||
['%u']=function(self) return self:getisoweekday() end,
|
||||
-- Sunday week of the year, from 00 (48)
|
||||
['%U']=function(self) return fmt("%.2d", self:getweeknumber()) end,
|
||||
-- ISO 8601 week of the year, from 01 (48)
|
||||
['%V']=function(self) return fmt("%.2d", self:getisoweeknumber()) end,
|
||||
-- The day of the week as a decimal, Sunday being 0
|
||||
['%w']=function(self) return self:getweekday() - 1 end,
|
||||
-- Monday week of the year, from 00 (48)
|
||||
['%W']=function(self) return fmt("%.2d", self:getweeknumber(2)) end,
|
||||
-- The year as a number without a century (range 00 to 99)
|
||||
['%y']=function(self) return fmt("%.2d", mod(self:getyear() ,100)) end,
|
||||
-- Year with century (2000, 1914, 0325, 0001)
|
||||
['%Y']=function(self) return fmt("%.4d", self:getyear()) end,
|
||||
-- Time zone offset, the date object is assumed local time (+1000, -0230)
|
||||
['%z']=function(self) local b = -self:getbias(); local x = abs(b); return fmt("%s%.4d", b < 0 and "-" or "+", fix(x/60)*100 + floor(mod(x,60))) end,
|
||||
-- Time zone name, the date object is assumed local time
|
||||
['%Z']=function(self) return self:gettzname() end,
|
||||
-- Misc --
|
||||
-- Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to "%Y" (1 BCE, 40 BCE)
|
||||
['%\b']=function(self) local x = self:getyear() return fmt("%.4d%s", x>0 and x or (-x+1), x>0 and "" or " BCE") end,
|
||||
-- Seconds including fraction (59.998, 01.123)
|
||||
['%\f']=function(self) local x = self:getfracsec() return fmt("%s%.9f",x >= 10 and "" or "0", x) end,
|
||||
-- percent character %
|
||||
['%%']=function(self) return "%" end,
|
||||
-- Group Spec --
|
||||
-- 12-hour time, from 01:00:00 AM (06:55:15 AM); same as "%I:%M:%S %p"
|
||||
['%r']=function(self) return self:fmt0("%I:%M:%S %p") end,
|
||||
-- hour:minute, from 01:00 (06:55); same as "%I:%M"
|
||||
['%R']=function(self) return self:fmt0("%I:%M") end,
|
||||
-- 24-hour time, from 00:00:00 (06:55:15); same as "%H:%M:%S"
|
||||
['%T']=function(self) return self:fmt0("%H:%M:%S") end,
|
||||
-- month/day/year from 01/01/00 (12/02/79); same as "%m/%d/%y"
|
||||
['%D']=function(self) return self:fmt0("%m/%d/%y") end,
|
||||
-- year-month-day (1979-12-02); same as "%Y-%m-%d"
|
||||
['%F']=function(self) return self:fmt0("%Y-%m-%d") end,
|
||||
-- The preferred date and time representation; same as "%x %X"
|
||||
['%c']=function(self) return self:fmt0("%x %X") end,
|
||||
-- The preferred date representation, same as "%a %b %d %\b"
|
||||
['%x']=function(self) return self:fmt0("%a %b %d %\b") end,
|
||||
-- The preferred time representation, same as "%H:%M:%\f"
|
||||
['%X']=function(self) return self:fmt0("%H:%M:%\f") end,
|
||||
-- GroupSpec --
|
||||
-- Iso format, same as "%Y-%m-%dT%T"
|
||||
['${iso}'] = function(self) return self:fmt0("%Y-%m-%dT%T") end,
|
||||
-- http format, same as "%a, %d %b %Y %T GMT"
|
||||
['${http}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end,
|
||||
-- ctime format, same as "%a %b %d %T GMT %Y"
|
||||
['${ctime}'] = function(self) return self:fmt0("%a %b %d %T GMT %Y") end,
|
||||
-- RFC850 format, same as "%A, %d-%b-%y %T GMT"
|
||||
['${rfc850}'] = function(self) return self:fmt0("%A, %d-%b-%y %T GMT") end,
|
||||
-- RFC1123 format, same as "%a, %d %b %Y %T GMT"
|
||||
['${rfc1123}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end,
|
||||
-- asctime format, same as "%a %b %d %T %Y"
|
||||
['${asctime}'] = function(self) return self:fmt0("%a %b %d %T %Y") end,
|
||||
}
|
||||
function dobj:fmt0(str) return (gsub(str, "%%[%a%%\b\f]", function(x) local f = tvspec[x];return (f and f(self)) or x end)) end
|
||||
function dobj:fmt(str)
|
||||
str = str or self.fmtstr or fmtstr
|
||||
return self:fmt0((gmatch(str, "${%w+}")) and (gsub(str, "${%w+}", function(x)local f=tvspec[x];return (f and f(self)) or x end)) or str)
|
||||
end
|
||||
|
||||
function dobj.__lt(a, b) if (a.daynum == b.daynum) then return (a.dayfrc < b.dayfrc) else return (a.daynum < b.daynum) end end
|
||||
function dobj.__le(a, b) if (a.daynum == b.daynum) then return (a.dayfrc <= b.dayfrc) else return (a.daynum <= b.daynum) end end
|
||||
function dobj.__eq(a, b)return (a.daynum == b.daynum) and (a.dayfrc == b.dayfrc) end
|
||||
function dobj.__sub(a,b)
|
||||
local d1, d2 = date_getdobj(a), date_getdobj(b)
|
||||
local d0 = d1 and d2 and date_new(d1.daynum - d2.daynum, d1.dayfrc - d2.dayfrc)
|
||||
return d0 and d0:normalize()
|
||||
end
|
||||
function dobj.__add(a,b)
|
||||
local d1, d2 = date_getdobj(a), date_getdobj(b)
|
||||
local d0 = d1 and d2 and date_new(d1.daynum + d2.daynum, d1.dayfrc + d2.dayfrc)
|
||||
return d0 and d0:normalize()
|
||||
end
|
||||
function dobj.__concat(a, b) return tostring(a) .. tostring(b) end
|
||||
function dobj:__tostring() return self:fmt() end
|
||||
|
||||
function dobj:copy() return date_new(self.daynum, self.dayfrc) end
|
||||
|
||||
|
||||
function dobj:setbias(bias)
|
||||
self.bias = bias
|
||||
return self
|
||||
end
|
||||
|
||||
--[[ THE LOCAL DATE OBJECT METHODS ]]--
|
||||
function dobj:tolocal()
|
||||
local dn,df = self.daynum, self.dayfrc
|
||||
bias = self.bias or getbiasutc2(self)
|
||||
if bias != nil then
|
||||
-- utc = local + bias; local = utc - bias
|
||||
self.daynum = dn
|
||||
self.dayfrc = df - bias*TICKSPERSEC
|
||||
return self:normalize()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function dobj:toutc()
|
||||
local dn,df = self.daynum, self.dayfrc
|
||||
local bias = self.bias or getbiasloc2(dn, df)
|
||||
if bias then
|
||||
-- utc = local + bias;
|
||||
self.daynum = dn
|
||||
self.dayfrc = df + bias*TICKSPERSEC
|
||||
return self:normalize()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
function dobj:getbias() return self.bias != nil and self.bias/SECPERMIN or (getbiasloc2(self.daynum, self.dayfrc))/SECPERMIN end
|
||||
|
||||
function dobj:gettzname()
|
||||
if self.bias != nil then return "" end
|
||||
|
||||
local _, tvu, _ = getbiasloc2(self.daynum, self.dayfrc)
|
||||
return tvu and osdate("%Z",tvu) or ""
|
||||
end
|
||||
|
||||
--#if not DATE_OBJECT_AFX then
|
||||
function date.time(h, r, s, t)
|
||||
h, r, s, t = tonumber(h or 0), tonumber(r or 0), tonumber(s or 0), tonumber(t or 0)
|
||||
if h and r and s and t then
|
||||
return date_new(DAYNUM_DEF, makedayfrc(h, r, s, t))
|
||||
else
|
||||
return date_error_arg()
|
||||
end
|
||||
end
|
||||
|
||||
function date:__call(arg1, ...)
|
||||
local arg_count = select("#", ...) + (arg1 == nil and 0 or 1)
|
||||
if arg_count > 1 then return (date_from(arg1, ...))
|
||||
elseif arg_count == 0 then return (date_getdobj(false))
|
||||
else local o, r = date_getdobj(arg1); return r and o:copy() or o end
|
||||
end
|
||||
|
||||
date.diff = dobj.__sub
|
||||
|
||||
function date.isleapyear(v)
|
||||
local y = fix(v);
|
||||
if not y then
|
||||
y = date_getdobj(v)
|
||||
y = y and y:getyear()
|
||||
end
|
||||
return isleapyear(y+0)
|
||||
end
|
||||
|
||||
function date.epoch() return date_epoch:copy() end
|
||||
|
||||
function date.isodate(y,w,d) return date_new(makedaynum_isoywd(y + 0, w and (w+0) or 1, d and (d+0) or 1), 0) end
|
||||
function date.setcenturyflip(y)
|
||||
if y ~= floor(y) or y < 0 or y > 100 then date_error_arg() end
|
||||
centuryflip = y
|
||||
end
|
||||
function date.getcenturyflip() return centuryflip end
|
||||
|
||||
-- Internal functions
|
||||
function date.fmt(str) if str then fmtstr = str end; return fmtstr end
|
||||
function date.daynummin(n) DAYNUM_MIN = (n and n < DAYNUM_MAX) and n or DAYNUM_MIN return n and DAYNUM_MIN or date_new(DAYNUM_MIN, 0):normalize()end
|
||||
function date.daynummax(n) DAYNUM_MAX = (n and n > DAYNUM_MIN) and n or DAYNUM_MAX return n and DAYNUM_MAX or date_new(DAYNUM_MAX, 0):normalize()end
|
||||
function date.ticks(t) if t then setticks(t) end return TICKSPERSEC end
|
||||
--#end -- not DATE_OBJECT_AFX
|
||||
|
||||
local tm = osdate("!*t", 0);
|
||||
if tm then
|
||||
date_epoch = date_new(makedaynum(tm.year, tm.month - 1, tm.day), makedayfrc(tm.hour, tm.min, tm.sec, 0))
|
||||
-- the distance from our epoch to os epoch in daynum
|
||||
DATE_EPOCH = date_epoch and date_epoch:spandays()
|
||||
else -- error will be raise only if called!
|
||||
date_epoch = setmetatable({},{__index = function() error("failed to get the epoch date") end})
|
||||
end
|
||||
|
||||
--#if not DATE_OBJECT_AFX then
|
||||
VyHub.Lib.date = date
|
||||
--#else
|
||||
--$return date_from
|
||||
--#end
|
||||
|
||||
@@ -1,399 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
VyHub.Lib.json = VyHub.Lib.json or {}
|
||||
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
VyHub.Lib.json = json
|
||||
@@ -1,57 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
VyHub.Advert = VyHub.Advert or {}
|
||||
VyHub.adverts = VyHub.adverts or {}
|
||||
|
||||
local current_advert = 0
|
||||
|
||||
function VyHub.Advert:refresh()
|
||||
VyHub.API:get("/advert/", nil, { active = "true", serverbundle_id = VyHub.server.serverbundle.id }, function(code, result)
|
||||
VyHub.adverts = result
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Advert:show(advert)
|
||||
if advert then
|
||||
local lines = string.Explode('\n', advert.content)
|
||||
local color = VyHub.Util:hex2rgb(advert.color)
|
||||
|
||||
local prefix = VyHub.Config.advert_prefix
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
VyHub.Util:print_chat_all(line, prefix, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Advert:next()
|
||||
current_advert = current_advert + 1;
|
||||
|
||||
local advert = VyHub.adverts[current_advert];
|
||||
|
||||
if advert then
|
||||
VyHub.Advert:show(advert)
|
||||
else
|
||||
current_advert = 0
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_advert_vyhub_ready", function ()
|
||||
VyHub.Advert:refresh()
|
||||
|
||||
timer.Create("vyhub_advert_next", VyHub.Config.advert_interval, 0, function()
|
||||
VyHub.Advert:next()
|
||||
end)
|
||||
|
||||
timer.Create("vyhub_advert_refresh", 300, 0, function ()
|
||||
VyHub.Advert:refresh()
|
||||
end)
|
||||
end)
|
||||
@@ -1,170 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
VyHub.API = VyHub.API or {}
|
||||
|
||||
local content_type = "application/json; charset=utf-8"
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
function VyHub.API:request(method, url, path_params, query, headers, request_body, type, success, failed, no_error_for)
|
||||
no_error_for = no_error_for or {}
|
||||
|
||||
if path_params != nil then
|
||||
url = f(url, unpack(path_params))
|
||||
end
|
||||
|
||||
if istable(request_body) then
|
||||
request_body = json.encode(request_body)
|
||||
end
|
||||
|
||||
success_func = function(code, body, headers)
|
||||
local result = body
|
||||
|
||||
if headers["Content-Type"] and headers["Content-Type"] == 'application/json' then
|
||||
result = json.decode(body)
|
||||
end
|
||||
|
||||
if code >= 200 and code < 300 then
|
||||
VyHub:msg(f("HTTP %s %s (%s): %s", method, url, json.encode(query), code), "debug")
|
||||
|
||||
if success != nil then
|
||||
-- VyHub:msg(f("Response: %s", body), "debug")
|
||||
|
||||
success(code, result, headers)
|
||||
end
|
||||
else
|
||||
if not table.HasValue(no_error_for, code) then
|
||||
local err_msg = f("HTTP %s %s: %s", method, url, code)
|
||||
|
||||
if query then
|
||||
err_msg = err_msg .. f("\nQuery: %s", json.encode(query))
|
||||
end
|
||||
if request_body then
|
||||
err_msg = err_msg .. f("\nBody: %s", request_body)
|
||||
end
|
||||
|
||||
VyHub:msg(err_msg, "error")
|
||||
|
||||
if code < 500 and string.find( body:lower(), "html>" ) == nil then
|
||||
VyHub:msg(f("Response: %s", body), "error")
|
||||
end
|
||||
end
|
||||
|
||||
if failed != nil then
|
||||
local err_text = json.encode(result)
|
||||
|
||||
if istable(result) and result.detail != nil and result.detail.msg != nil then
|
||||
err_text = f("%s (%s)", result.detail.msg, result.detail.code)
|
||||
end
|
||||
|
||||
failed(code, result, headers, err_text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
failed_func = function(reason)
|
||||
local err_msg = f("HTTP %s %s: %s", method, url, code)
|
||||
|
||||
if query then
|
||||
err_msg = err_msg .. f("\nQuery: %s", json.encode(query))
|
||||
end
|
||||
if request_body then
|
||||
err_msg = err_msg .. f("\nBody: %s", request_body)
|
||||
end
|
||||
|
||||
VyHub:msg(err_msg, "error")
|
||||
|
||||
if failed != nil then
|
||||
failed(0, reason, {})
|
||||
end
|
||||
end
|
||||
|
||||
HTTP({
|
||||
method = method,
|
||||
url = url,
|
||||
parameters = query,
|
||||
headers = headers,
|
||||
body = request_body,
|
||||
type = type,
|
||||
success = success_func,
|
||||
failed = failed_func,
|
||||
})
|
||||
end
|
||||
|
||||
function VyHub.API:get(endpoint, path_params, query, success, failed, no_error_for)
|
||||
local url = f("%s%s", VyHub.API.url, endpoint)
|
||||
|
||||
VyHub.API:request("GET", url, path_params, query, self.headers, nil, content_type, success, failed, no_error_for)
|
||||
end
|
||||
|
||||
function VyHub.API:delete(endpoint, path_params, success, failed)
|
||||
local url = f("%s%s", VyHub.API.url, endpoint)
|
||||
|
||||
self:request("DELETE", url, path_params, nil, self.headers, nil, content_type, success, failed)
|
||||
end
|
||||
|
||||
function VyHub.API:post(endpoint, path_params, body, success, failed, query)
|
||||
local url = f("%s%s", VyHub.API.url, endpoint)
|
||||
|
||||
self:request("POST", url, path_params, query, self.headers, body, content_type, success, failed)
|
||||
end
|
||||
|
||||
function VyHub.API:patch(endpoint, path_params, body, success, failed)
|
||||
local url = f("%s%s", VyHub.API.url, endpoint)
|
||||
|
||||
self:request("PATCH", url, path_params, nil, self.headers, body, content_type, success, failed)
|
||||
end
|
||||
|
||||
function VyHub.API:put(endpoint, path_params, body, success, failed)
|
||||
local url = f("%s%s", VyHub.API.url, endpoint)
|
||||
|
||||
self:request("PUT", url, path_params, nil, self.headers, body, content_type, success, failed)
|
||||
end
|
||||
|
||||
hook.Add("vyhub_loading_finish", "vyhub_api_vyhub_loading_finish", function()
|
||||
if VyHub.Util:invalid_str({VyHub.Config.api_url, VyHub.Config.api_key, VyHub.Config.server_id}) then
|
||||
VyHub:msg("API URL, Server ID or API Key not set! Please follow the manual.", "error")
|
||||
|
||||
timer.Simple(60, function ()
|
||||
hook.Run("vyhub_loading_finish")
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.API.url = VyHub.Config.api_url
|
||||
VyHub.API.headers = {
|
||||
Authorization = f("Bearer %s", VyHub.Config.api_key)
|
||||
}
|
||||
|
||||
if string.EndsWith(VyHub.API.url, "/") then
|
||||
VyHub.API.url = string.sub(VyHub.API.url, 1, -2)
|
||||
end
|
||||
|
||||
VyHub:msg(f("API URL is %s", VyHub.API.url))
|
||||
|
||||
VyHub.API:get("/openapi.json", nil, nil, function(code, result, headers)
|
||||
VyHub:msg(f("Connection to API %s version %s successful!", result.info.title, result.info.version), "success")
|
||||
|
||||
hook.Run("vyhub_api_ready")
|
||||
end, function()
|
||||
VyHub:msg("Connection to API failed! Trying to use cache.", "error")
|
||||
|
||||
hook.Run("vyhub_api_failed")
|
||||
end)
|
||||
end)
|
||||
|
||||
concommand.Add("vh_reinit", function (ply)
|
||||
if not VyHub.Util:is_server(ply) then return end
|
||||
|
||||
hook.Run("vyhub_loading_finish")
|
||||
end)
|
||||
@@ -1,58 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Cache = VyHub.Cache or {}
|
||||
|
||||
function VyHub.Cache:save(key, value)
|
||||
local data = {
|
||||
timestamp = os.time(),
|
||||
data = value
|
||||
}
|
||||
|
||||
local filename = f("vyhub/%s.json", key)
|
||||
local json = json.encode(data)
|
||||
|
||||
VyHub:msg("Write " .. filename .. ": " .. json, "debuga")
|
||||
|
||||
file.Write(filename, json)
|
||||
end
|
||||
|
||||
function VyHub.Cache:get(key, max_age)
|
||||
local path = f("vyhub/%s.json", key)
|
||||
|
||||
if not file.Exists(path, "data") then
|
||||
return nil
|
||||
end
|
||||
|
||||
local data_str = file.Read(path, "data")
|
||||
|
||||
if not string.Trim(data_str) then
|
||||
return nil
|
||||
end
|
||||
|
||||
local success, data = pcall(json.decode, data_str)
|
||||
|
||||
if not success then
|
||||
return nil
|
||||
end
|
||||
|
||||
if istable(data) and data.timestamp and data.data then
|
||||
if max_age != nil and os.time() - data.timestamp > max_age then
|
||||
return nil
|
||||
end
|
||||
|
||||
return data.data
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
@@ -1,75 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_commands_vyhub_ready", function ()
|
||||
VyHub:get_frontend_url(function (url)
|
||||
-- !shop
|
||||
local function open_shop(ply, args)
|
||||
if IsValid(ply) then
|
||||
VyHub.Util:open_url(ply, f('%s/shop', url))
|
||||
end
|
||||
end
|
||||
|
||||
for _, cmd in ipairs(VyHub.Config.commands_shop) do
|
||||
VyHub.Util:register_chat_command(cmd, open_shop)
|
||||
end
|
||||
|
||||
-- !bans
|
||||
local function open_bans(ply, args)
|
||||
if IsValid(ply) then
|
||||
VyHub.Util:open_url(ply, f('%s/bans', url))
|
||||
end
|
||||
end
|
||||
|
||||
for _, cmd in ipairs(VyHub.Config.commands_bans) do
|
||||
VyHub.Util:register_chat_command(cmd, open_bans)
|
||||
end
|
||||
|
||||
-- !warnings
|
||||
local function open_warnings(ply, args)
|
||||
if IsValid(ply) then
|
||||
VyHub.Util:open_url(ply, f('%s/warnings', url))
|
||||
end
|
||||
end
|
||||
|
||||
for _, cmd in ipairs(VyHub.Config.commands_warnings) do
|
||||
VyHub.Util:register_chat_command(cmd, open_warnings)
|
||||
end
|
||||
|
||||
-- !news
|
||||
local function open_news(ply, args)
|
||||
if IsValid(ply) then
|
||||
VyHub.Util:open_url(ply, f('%s/', url))
|
||||
end
|
||||
end
|
||||
|
||||
for _, cmd in ipairs(VyHub.Config.commands_news) do
|
||||
VyHub.Util:register_chat_command(cmd, open_news)
|
||||
end
|
||||
|
||||
-- !user
|
||||
local function open_profile(ply, args)
|
||||
if IsValid(ply) and args[1] then
|
||||
other_ply = VyHub.Util:get_player_by_nick(args[1])
|
||||
|
||||
if IsValid(other_ply) then
|
||||
VyHub.Util:open_url(ply, f('%s/profile/steam/%s', url, other_ply:SteamID64()))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, cmd in ipairs(VyHub.Config.commands_profile) do
|
||||
VyHub.Util:register_chat_command(cmd, open_profile)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
function VyHub.Config:load_cache_config()
|
||||
local ccfg = VyHub.Cache:get("config")
|
||||
|
||||
if ccfg != nil and #table.GetKeys(ccfg) > 0 then
|
||||
VyHub.Config = table.Merge(VyHub.Config, ccfg)
|
||||
VyHub:msg(f("Loaded cache config values: %s", table.concat(table.GetKeys(ccfg), ', ')))
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("vh_setup", function (ply, _, args)
|
||||
if not VyHub.Util:is_server(ply) then return end
|
||||
|
||||
if not args[1] or not args[2] or not args[3] then return end
|
||||
|
||||
local ccfg = VyHub.Cache:get("config")
|
||||
|
||||
if not istable(ccfg) then
|
||||
ccfg = {}
|
||||
end
|
||||
|
||||
ccfg["api_key"] = args[1]
|
||||
ccfg["api_url"] = args[2]
|
||||
ccfg["server_id"] = args[3]
|
||||
|
||||
VyHub.Cache:save("config", ccfg)
|
||||
|
||||
for key, value in pairs(ccfg) do
|
||||
VyHub.Config[key] = value
|
||||
end
|
||||
|
||||
VyHub:msg(f("Successfully set initial config, please wait up to one minute for VyHub to initialize (%s, %s, %s)", args[1], args[2], args[3]))
|
||||
end)
|
||||
|
||||
concommand.Add("vh_config", function (ply, _, args)
|
||||
if not VyHub.Util:is_server(ply) then return end
|
||||
|
||||
local ccfg = VyHub.Cache:get("config")
|
||||
|
||||
if not args[1] or not args[2] then
|
||||
if istable(ccfg) then
|
||||
VyHub:msg("Additional config options:")
|
||||
PrintTable(ccfg)
|
||||
else
|
||||
VyHub:msg("No additional config options set.")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local key = args[1]
|
||||
local value = args[2]
|
||||
|
||||
if not istable(ccfg) then
|
||||
ccfg = {}
|
||||
end
|
||||
|
||||
if value == "false" then
|
||||
value = false
|
||||
elseif value == "true" then
|
||||
value = true
|
||||
end
|
||||
|
||||
ccfg[key] = value
|
||||
VyHub.Cache:save("config", ccfg)
|
||||
|
||||
VyHub.Config[key] = value
|
||||
|
||||
VyHub:msg(f("Successfully set config value %s.", key))
|
||||
end)
|
||||
|
||||
concommand.Add("vh_config_reset", function (ply)
|
||||
if not VyHub.Util:is_server(ply) then return end
|
||||
|
||||
VyHub.Cache:save("config", {})
|
||||
|
||||
VyHub:msg(f("Successfully cleared additional config.", key))
|
||||
end)
|
||||
@@ -1,95 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Dashboard = VyHub.Dashboard or {}
|
||||
VyHub.Dashboard.last_update = VyHub.Dashboard.last_update or {}
|
||||
VyHub.Dashboard.data = VyHub.Dashboard.data or {}
|
||||
|
||||
util.AddNetworkString("vyhub_dashboard")
|
||||
util.AddNetworkString("vyhub_dashboard_reload")
|
||||
|
||||
function VyHub.Dashboard:reset()
|
||||
VyHub.Dashboard.data = {}
|
||||
VyHub.Dashboard.last_update = {}
|
||||
net.Start("vyhub_dashboard_reload")
|
||||
net.Broadcast()
|
||||
end
|
||||
|
||||
function VyHub.Dashboard:fetch_data(user_id, callback)
|
||||
VyHub.API:get("/server/%s/user-activity?morph_user_id=%s", {VyHub.server.id, user_id}, nil, function(code, result)
|
||||
callback(result)
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Dashboard:get_data(steamid64, callback)
|
||||
if VyHub.Dashboard.data[steamid64] == nil or VyHub.Dashboard.last_update[steamid64] == nil or os.time() - VyHub.Dashboard.last_update[steamid64] > 30 then
|
||||
VyHub.Player:get(steamid64, function (user)
|
||||
if user then
|
||||
VyHub.Dashboard:fetch_data(user.id, function (data)
|
||||
VyHub.Dashboard.data[steamid64] = data
|
||||
VyHub.Dashboard.last_update[steamid64] = os.time()
|
||||
|
||||
callback(VyHub.Dashboard.data[steamid64])
|
||||
end)
|
||||
end
|
||||
end)
|
||||
else
|
||||
callback(VyHub.Dashboard.data[steamid64])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function VyHub.Dashboard:get_permissions(ply)
|
||||
return {
|
||||
warning_show = VyHub.Player:check_property(ply, 'warning_show'),
|
||||
warning_edit = VyHub.Player:check_property(ply, 'warning_edit'),
|
||||
warning_delete = VyHub.Player:check_property(ply, 'warning_delete'),
|
||||
ban_show = VyHub.Player:check_property(ply, 'ban_show'),
|
||||
ban_edit = VyHub.Player:check_property(ply, 'ban_edit'),
|
||||
}
|
||||
end
|
||||
|
||||
net.Receive("vyhub_dashboard", function(_, ply)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
VyHub.Dashboard:get_data(ply:SteamID64(), function (users)
|
||||
local users_json = json.encode(users)
|
||||
local users_json_compressed = util.Compress(users_json)
|
||||
local users_json_compressed_len = #users_json_compressed
|
||||
local perms = VyHub.Dashboard:get_permissions(ply)
|
||||
local perms_json = json.encode(perms)
|
||||
|
||||
net.Start("vyhub_dashboard")
|
||||
net.WriteUInt(users_json_compressed_len, 16)
|
||||
net.WriteData(users_json_compressed, users_json_compressed_len)
|
||||
net.WriteString(perms_json)
|
||||
net.Send(ply)
|
||||
end)
|
||||
end)
|
||||
|
||||
local function open_dashboard(ply, args)
|
||||
if ply and IsValid(ply) then
|
||||
ply:ConCommand("vh_dashboard")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_dashboard_vyhub_ready", function ()
|
||||
for _, cmd in ipairs(VyHub.Config.commands_dashboard) do
|
||||
VyHub.Util:register_chat_command(cmd, open_dashboard)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_dashboard_data_changed", "vyhub_dahboard_vyhub_dashboard_data_changed", function ()
|
||||
VyHub.Dashboard:reset()
|
||||
end)
|
||||
@@ -1,375 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Group = VyHub.Group or {}
|
||||
|
||||
VyHub.groups = VyHub.groups or nil
|
||||
VyHub.groups_mapped = VyHub.groups_mapped or nil
|
||||
VyHub.Group.group_changes = VyHub.Group.group_changes or {} -- dict(steamid64, groupname) of the last in-game group change (VyHub -> GMOD). Used to prevent loop.
|
||||
|
||||
util.AddNetworkString("vyhub_group_data")
|
||||
|
||||
local meta_ply = FindMetaTable("Player")
|
||||
|
||||
function VyHub.Group:refresh()
|
||||
VyHub.API:get("/group/", nil, nil, function(code, result)
|
||||
if result != VyHub.groups then
|
||||
VyHub.groups = result
|
||||
|
||||
VyHub:msg(f("Found groups: %s", json.encode(result)), "debug")
|
||||
|
||||
VyHub.groups_mapped = {}
|
||||
|
||||
for _, group in ipairs(VyHub.groups) do
|
||||
for _, mapping in ipairs(group.mappings) do
|
||||
if mapping.serverbundle_id == nil or mapping.serverbundle_id == VyHub.server.serverbundle.id then
|
||||
VyHub.groups_mapped[mapping.name] = group
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
VyHub.Group:send_groups()
|
||||
end
|
||||
end, function (code, reason)
|
||||
VyHub:msg("Could not refresh groups.", "error")
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Group:send_groups(ply)
|
||||
if not VyHub.groups_mapped then return end
|
||||
|
||||
local groups_to_send = VyHub.groups_mapped
|
||||
|
||||
net.Start("vyhub_group_data")
|
||||
net.WriteUInt(table.Count(groups_to_send), 8)
|
||||
|
||||
for name_game, group in pairs(groups_to_send) do
|
||||
net.WriteString(name_game)
|
||||
net.WriteString(group.name)
|
||||
net.WriteString(group.color)
|
||||
end
|
||||
|
||||
if ply != nil and IsValid(ply) then
|
||||
net.Send(ply)
|
||||
else
|
||||
net.Broadcast()
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Group:set(steamid, groupname, seconds, processor_id, callback)
|
||||
if seconds != nil and seconds == 0 then
|
||||
seconds = nil
|
||||
end
|
||||
|
||||
if VyHub.groups_mapped == nil then
|
||||
VyHub:msg("Groups not initialized yet. Please try again later.", "error")
|
||||
if callback then
|
||||
callback(!VyHub.Config.strict_group_sync)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local group = VyHub.groups_mapped[groupname]
|
||||
|
||||
if group == nil then
|
||||
VyHub:msg(f("Could not find VyHub group with name %s", groupname), "debug")
|
||||
|
||||
if callback then
|
||||
callback(!VyHub.Config.strict_group_sync)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.Player:get(steamid, function (user)
|
||||
if user == nil then
|
||||
if callback then
|
||||
callback(false)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local end_date = nil
|
||||
|
||||
if seconds != nil then
|
||||
end_date = VyHub.Util:format_datetime(os.time() + seconds)
|
||||
end
|
||||
|
||||
local url = '/user/%s/membership'
|
||||
|
||||
if processor_id != nil then
|
||||
url = url .. '?morph_user_id=' .. processor_id
|
||||
end
|
||||
|
||||
local ply = player.GetBySteamID64(steamid)
|
||||
|
||||
VyHub.API:post(url, {user.id}, {
|
||||
begin = VyHub.Util.format_datetime(),
|
||||
["end"] = end_date,
|
||||
group_id = group.id,
|
||||
serverbundle_id = VyHub.server.serverbundle.id
|
||||
}, function (code, result)
|
||||
VyHub:msg(f("Added membership in group %s for user %s.", groupname, steamid), "success")
|
||||
|
||||
if IsValid(ply) then
|
||||
timer.Simple(5, function()
|
||||
VyHub.Player:refresh(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(true)
|
||||
end
|
||||
end, function (code, reason)
|
||||
VyHub:msg(f("Could not add membership in group %s for user %s.", groupname, steamid), "error")
|
||||
if callback then
|
||||
callback(false)
|
||||
end
|
||||
|
||||
if IsValid(ply) then
|
||||
timer.Simple(2, function()
|
||||
VyHub.Player:refresh(ply)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Group:remove(steamid, processor_id, callback)
|
||||
VyHub.Player:get(steamid, function (user)
|
||||
if user == nil then
|
||||
if callback then
|
||||
callback(false)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local url = f('/user/%%s/membership?serverbundle_id=%s', VyHub.server.serverbundle.id)
|
||||
|
||||
if processor_id != nil then
|
||||
url = url .. '&morph_user_id=' .. processor_id
|
||||
end
|
||||
|
||||
VyHub.API:delete(url, {user.id}, function (code, result)
|
||||
VyHub:msg(f("Removed %s from all groups.", steamid), "success")
|
||||
|
||||
local ply = player.GetBySteamID64(steamid)
|
||||
|
||||
if IsValid(ply) then
|
||||
VyHub.Player:refresh(ply)
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(true)
|
||||
end
|
||||
end, function (code, reason)
|
||||
VyHub:msg(f("Could not remove %s from all groups.", steamid), "error")
|
||||
|
||||
if callback then
|
||||
callback(false)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Group:override_admin_mods()
|
||||
if VyHub.Config.group_disable_sync then return end
|
||||
|
||||
local _setusergroup = meta_ply.SetUserGroup
|
||||
|
||||
if not ULib and not serverguard and not sam and not (xAdmin and xAdmin.Admin.RegisterBan) and not sAdmin then
|
||||
hook.Remove("PlayerInitialSpawn", "PlayerAuthSpawn")
|
||||
|
||||
meta_ply.SetUserGroup = function(ply, name, ignore_vh)
|
||||
if ply:GetUserGroup() == name then
|
||||
VyHub:msg(ply:SteamID64() .. " already in group " .. name .. ". Ignoring change...")
|
||||
return
|
||||
end
|
||||
|
||||
local steamid = ply:SteamID64()
|
||||
|
||||
if not ignore_vh and VyHub.Group.group_changes[steamid] != name then
|
||||
if VyHub.Group:set(steamid, name) or VyHub.Config.disable_group_check then
|
||||
_setusergroup(ply, name)
|
||||
end
|
||||
else
|
||||
_setusergroup(ply, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if xAdmin and xAdmin.Admin.RegisterBan then
|
||||
local xadmin_setgroup = xAdmin.SetGroup
|
||||
|
||||
xAdmin.SetGroup = function(ply, group, ignore_vh)
|
||||
local steamid32 = isstring(ply) and ply or ply:SteamID()
|
||||
local steamid64 = util.SteamIDTo64(steamid32)
|
||||
|
||||
if not ignore_vh then
|
||||
VyHub.Group:set(steamid64, group, nil, nil, function(success)
|
||||
if success then
|
||||
xadmin_setgroup( ply, group )
|
||||
end
|
||||
end)
|
||||
else
|
||||
xadmin_setgroup( ply, group )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ULib then
|
||||
local ulx_adduser = ULib.ucl.addUser
|
||||
local ulx_removeuser = ULib.ucl.removeUser
|
||||
|
||||
ULib.ucl.addUser = function(steamid32, allow, deny, groupname, ignore_vh)
|
||||
if not ignore_vh then
|
||||
local steamid64 = util.SteamIDTo64(steamid32)
|
||||
VyHub.Group:set(steamid64, groupname, nil, nil, function(success)
|
||||
if success then
|
||||
ulx_adduser( steamid32, allow, deny, groupname )
|
||||
end
|
||||
end)
|
||||
else
|
||||
ulx_adduser( steamid32, allow, deny, groupname )
|
||||
end
|
||||
end
|
||||
|
||||
ULib.ucl.removeUser = function(id)
|
||||
local steamid64 = nil
|
||||
|
||||
if string.find(id, ":") then
|
||||
steamid64 = util.SteamIDTo64(id)
|
||||
else
|
||||
local ply = player.GetByUniqueID(id)
|
||||
|
||||
if IsValid(ply) then
|
||||
steamid64 = ply:SteamID64()
|
||||
end
|
||||
end
|
||||
|
||||
if steamid64 then
|
||||
VyHub.Group:remove(steamid64, nil, function (success)
|
||||
if success then
|
||||
ulx_removeuser( id )
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if serverguard then
|
||||
local servergaurd_setrank = serverguard.player["SetRank"]
|
||||
|
||||
function serverguard.player:SetRank(target, rank, length, ignore_vh)
|
||||
if not ignore_vh then
|
||||
if target then
|
||||
if type(target) == "Player" and IsValid(target) then
|
||||
VyHub.Group:set(target:SteamID64(), rank, nil, nil, function(success)
|
||||
if success then
|
||||
servergaurd_setrank(self, target, rank, length)
|
||||
end
|
||||
end)
|
||||
elseif type(target) == "string" and string.match(target, "STEAM_%d:%d:%d+") then
|
||||
local steamid = util.SteamIDTo64(target)
|
||||
|
||||
VyHub.Group:set(steamid, rank, nil, nil, function(success)
|
||||
if success then
|
||||
servergaurd_setrank(self, target, rank, length)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
else
|
||||
servergaurd_setrank(self, target, rank, length)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if sam then
|
||||
local sam_setrank = sam.player.set_rank
|
||||
|
||||
function sam.player.set_rank(ply, rank, length, ignore_vh)
|
||||
if not ignore_vh then
|
||||
if not sam.isnumber(length) or length < 0 then
|
||||
length = nil
|
||||
end
|
||||
|
||||
local seconds = nil
|
||||
|
||||
if length != nil then
|
||||
seconds = math.Round(length * 60, 0)
|
||||
end
|
||||
|
||||
VyHub.Group:set(ply:SteamID64(), rank, seconds, nil, function(success)
|
||||
if success then
|
||||
sam_setrank(ply, rank, length)
|
||||
end
|
||||
end)
|
||||
else
|
||||
sam_setrank(ply, rank, length)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if sAdmin then
|
||||
local sadmin_setrank = sAdmin.setRank
|
||||
|
||||
sAdmin.setRank = function(ply, rank, expire, noupdate, ignore_vh)
|
||||
rank = rank or "user"
|
||||
|
||||
if not ignore_vh and not noupdate then
|
||||
local seconds = nil
|
||||
|
||||
if isnumber(expire) and expire > 0 then
|
||||
seconds = math.max(expire, 0)
|
||||
end
|
||||
|
||||
VyHub.Group:set(ply:SteamID64(), rank, seconds, nil, function(success)
|
||||
if success then
|
||||
sadmin_setrank(ply, rank, nil, noupdate)
|
||||
end
|
||||
end)
|
||||
else
|
||||
sadmin_setrank(ply, rank, expire, noupdate)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_group_vyhub_ready", function ()
|
||||
VyHub.Group:refresh()
|
||||
|
||||
timer.Create("vyhub_group_refresh", VyHub.Config.group_refresh_time, 0, function ()
|
||||
VyHub.Group:refresh()
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_ply_connected", "vyhub_group_vyhub_ply_connected", function(ply)
|
||||
VyHub.Group:send_groups(ply)
|
||||
end)
|
||||
|
||||
concommand.Add("vh_setgroup", function(ply, _, args)
|
||||
if VyHub.Util:is_server(ply) then
|
||||
local steamid = args[1]
|
||||
local group = args[2]
|
||||
local bundle = args[3]
|
||||
|
||||
if steamid and group then
|
||||
VyHub.Group:set(steamid, group)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
VyHub.Group:override_admin_mods()
|
||||
end)
|
||||
@@ -1,92 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
VyHub.frontend_url = VyHub.frontend_url or nil
|
||||
|
||||
function VyHub:server_data_ready()
|
||||
VyHub:msg(f("I am server %s in bundle %s.", VyHub.server.name, VyHub.server.serverbundle.name))
|
||||
|
||||
VyHub.ready = true
|
||||
|
||||
hook.Run("vyhub_ready")
|
||||
end
|
||||
|
||||
function VyHub:get_frontend_url(callback)
|
||||
if VyHub.frontend_url != nil then
|
||||
if callback then
|
||||
callback(VyHub.frontend_url)
|
||||
end
|
||||
|
||||
return VyHub.frontend_url
|
||||
end
|
||||
|
||||
VyHub.API:get('/general/frontend-url', nil, nil, function (code, result)
|
||||
VyHub.frontend_url = result.frontend_url
|
||||
VyHub.Cache:save('frontend_url', VyHub.frontend_url)
|
||||
|
||||
if callback then
|
||||
callback(VyHub.frontend_url)
|
||||
end
|
||||
end, function ()
|
||||
local frontend_url = VyHub.Cache:get('frontend_url')
|
||||
|
||||
if frontend_url == nil then
|
||||
VyHub:msg("Could not get frontend_url!", "error")
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(frontend_url)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("vyhub_api_ready", "vyhub_main_vyhub_api_ready", function ()
|
||||
VyHub.API:get("/server/%s", { VyHub.Config.server_id }, nil, function(code, result)
|
||||
VyHub.server = result
|
||||
|
||||
VyHub:server_data_ready()
|
||||
|
||||
VyHub.Cache:save("server", VyHub.server)
|
||||
end, function (code, result)
|
||||
VyHub:msg(f("Could not find server with id %s", VyHub.Config.server_id), "error")
|
||||
|
||||
timer.Simple(60, function ()
|
||||
hook.Run("vyhub_loading_finish")
|
||||
end)
|
||||
end)
|
||||
|
||||
VyHub:get_frontend_url()
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_api_failed", "vyhub_main_vyhub_api_failed", function ()
|
||||
local server = VyHub.Cache:get("server", 604800)
|
||||
|
||||
if server != nil then
|
||||
VyHub.server = server
|
||||
|
||||
VyHub:server_data_ready()
|
||||
else
|
||||
VyHub:msg("Could not find cached server data or cached data is too old. Please make sure that the server is able to reach the VyHub API.", "error")
|
||||
|
||||
timer.Simple(60, function ()
|
||||
hook.Run("vyhub_loading_finish")
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
timer.Create("vyhub_not_ready_msg", 30, 0, function ()
|
||||
if VyHub.ready then
|
||||
timer.Remove("vyhub_not_ready_msg")
|
||||
else
|
||||
VyHub.Util:print_chat_all("<green>VyHub</green> is not ready. Please check the server log/console for errors.")
|
||||
end
|
||||
end)
|
||||
@@ -1,350 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
VyHub.Player = VyHub.Player or {}
|
||||
|
||||
VyHub.Player.connect_queue = VyHub.Player.connect_queue or {}
|
||||
VyHub.Player.table = VyHub.Player.table or {}
|
||||
|
||||
util.AddNetworkString("vyhub_user_id")
|
||||
|
||||
local meta_ply = FindMetaTable("Player")
|
||||
|
||||
function VyHub.Player:initialize(ply, retry)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
local steamid = ply:SteamID64()
|
||||
|
||||
VyHub:msg(f("Initializing user %s, %s", ply:Nick(), steamid))
|
||||
|
||||
VyHub.API:get("/user/%s", {steamid}, {type = "STEAM"}, function(code, result)
|
||||
VyHub:msg(f("Found existing user %s for steam id %s (%s).", result.id, steamid, ply:Nick()), "success")
|
||||
|
||||
VyHub.Player.table[steamid] = result
|
||||
|
||||
VyHub.Player:refresh(ply)
|
||||
|
||||
VyHub.Player:send_user_id(ply)
|
||||
|
||||
hook.Run("vyhub_ply_initialized", ply)
|
||||
|
||||
local ply_timer_name = "vyhub_player_" .. steamid
|
||||
|
||||
timer.Create(ply_timer_name, VyHub.Config.player_refresh_time, 0, function()
|
||||
if IsValid(ply) then
|
||||
VyHub.Player:refresh(ply)
|
||||
else
|
||||
timer.Remove(ply_timer_name)
|
||||
end
|
||||
end)
|
||||
end, function(code, reason)
|
||||
if code != 404 then
|
||||
VyHub:msg(f("Could not check if users %s exists. Retrying in a minute..", steamid), "error")
|
||||
|
||||
timer.Simple(60, function ()
|
||||
VyHub.Player:initialize(ply)
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if retry then
|
||||
VyHub:msg(f("Could not create user %s. Retrying in a minute..", steamid), "error")
|
||||
|
||||
timer.Simple(60, function()
|
||||
VyHub.Player:initialize(ply)
|
||||
end)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.Player:create(steamid, function()
|
||||
VyHub.Player:initialize(ply, true)
|
||||
end, function ()
|
||||
VyHub.Player:initialize(ply, true)
|
||||
end)
|
||||
end, { 404 })
|
||||
end
|
||||
|
||||
function VyHub.Player:send_user_id(ply)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
ply:VyHubID(function (user_id)
|
||||
net.Start("vyhub_user_id")
|
||||
net.WriteString(user_id)
|
||||
net.Send(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
local creation_began = {}
|
||||
local creation_success = {}
|
||||
local creation_err = {}
|
||||
|
||||
function VyHub.Player:create(steamid, success, err)
|
||||
-- Creation can take longer. If multiple creation requests occur, merge them to one.
|
||||
if not istable(creation_success[steamid]) then creation_success[steamid] = {} end
|
||||
if not istable(creation_err[steamid]) then creation_err[steamid] = {} end
|
||||
|
||||
table.insert(creation_success[steamid], success)
|
||||
table.insert(creation_err[steamid], err)
|
||||
|
||||
if creation_began[steamid] and os.time() - creation_began[steamid] < 10 then
|
||||
VyHub:msg(f("Queued creation request for steamid %s", steamid), "debug")
|
||||
return
|
||||
end
|
||||
|
||||
VyHub:msg(f("No existing user found for steam id %s. Creating..", steamid))
|
||||
|
||||
creation_began[steamid] = os.time()
|
||||
|
||||
local function reset_queue()
|
||||
creation_began[steamid] = 0
|
||||
creation_success[steamid] = {}
|
||||
creation_err[steamid] = {}
|
||||
end
|
||||
|
||||
VyHub.API:post('/user/', nil, { identifier = steamid, type = 'STEAM' }, function()
|
||||
for _, success_callback in pairs(creation_success[steamid]) do
|
||||
if success_callback then
|
||||
success_callback()
|
||||
end
|
||||
end
|
||||
reset_queue()
|
||||
end, function()
|
||||
for _, err_callback in pairs(creation_err[steamid]) do
|
||||
if err_callback then
|
||||
err_callback()
|
||||
end
|
||||
end
|
||||
reset_queue()
|
||||
end)
|
||||
end
|
||||
|
||||
-- Return nil if steamid is nil or API error
|
||||
-- Return false if steamid is false or could not create user
|
||||
function VyHub.Player:get(steamid, callback, retry)
|
||||
if steamid == nil then
|
||||
callback(nil)
|
||||
return
|
||||
end
|
||||
|
||||
if steamid == false then
|
||||
callback(false)
|
||||
return
|
||||
end
|
||||
|
||||
if VyHub.Player.table[steamid] != nil then
|
||||
callback(VyHub.Player.table[steamid])
|
||||
else
|
||||
VyHub.API:get("/user/%s", {steamid}, {type = "STEAM"}, function(code, result)
|
||||
VyHub:msg(f("Received user %s for steam id %s.", result.id, steamid), "debug")
|
||||
|
||||
VyHub.Player.table[steamid] = result
|
||||
|
||||
callback(result)
|
||||
end, function(code)
|
||||
VyHub:msg(f("Could not receive user %s.", steamid), "error")
|
||||
|
||||
if code == 404 and retry == nil then
|
||||
VyHub.Player:create(steamid, function ()
|
||||
VyHub.Player:get(steamid, callback, true)
|
||||
end, function ()
|
||||
callback(false)
|
||||
end)
|
||||
else
|
||||
callback(nil)
|
||||
end
|
||||
end, {404})
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Player:change_game_group(ply, group)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
local steamid64 = ply:SteamID64()
|
||||
local nick = ply:Nick()
|
||||
|
||||
VyHub.Group.group_changes[steamid64] = group
|
||||
|
||||
if serverguard then
|
||||
serverguard.player:SetRank(ply, group, false, true)
|
||||
elseif ulx then
|
||||
ULib.ucl.addUser( ply:SteamID(), {}, {}, group, true )
|
||||
elseif sam then
|
||||
sam.player.set_rank(ply, group, 0, true)
|
||||
elseif xAdmin and xAdmin.Admin.RegisterBan then
|
||||
xAdmin.SetGroup(ply, group, true)
|
||||
elseif sAdmin then
|
||||
sAdmin.setRank(ply, group, 0, false, true)
|
||||
else
|
||||
ply:SetUserGroup(group, true)
|
||||
end
|
||||
|
||||
VyHub:msg("Added " .. nick .. " to group " .. group, "success")
|
||||
VyHub.Util:print_chat(ply, f(VyHub.lang.ply.group_changed, group))
|
||||
end
|
||||
|
||||
function VyHub.Player:check_group(ply, callback)
|
||||
if VyHub.Config.group_disable_sync then return end
|
||||
|
||||
if ply:VyHubID() == nil then
|
||||
VyHub:msg(f("Could not check groups for user %s, because no VyHub id is available.", ply:SteamID64()), "debug")
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.API:get("/user/%s/group", {ply:VyHubID()}, { serverbundle_id = VyHub.server.serverbundle_id }, function(code, result)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
local steamid64 = ply:SteamID64()
|
||||
local nick = ply:Nick()
|
||||
|
||||
local highest = nil
|
||||
|
||||
for _, group in ipairs(result) do
|
||||
if highest == nil or highest.permission_level < group.permission_level then
|
||||
highest = group
|
||||
end
|
||||
end
|
||||
|
||||
if highest == nil then
|
||||
VyHub:msg(f("Could not find any active group for %s (%s)", nick, steamid64), "debug")
|
||||
return
|
||||
end
|
||||
|
||||
local group = nil
|
||||
|
||||
for _, mapping in ipairs(highest.mappings) do
|
||||
if mapping.serverbundle_id == nil or mapping.serverbundle_id == VyHub.server.serverbundle.id then
|
||||
group = mapping.name
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if group == nil then
|
||||
VyHub:msg(f("Could not find group name mapping for group %s.", highest.name), "debug")
|
||||
return
|
||||
end
|
||||
|
||||
local delay = 0
|
||||
|
||||
if sAdmin then
|
||||
delay = 3
|
||||
end
|
||||
|
||||
timer.Simple(delay, function ()
|
||||
local curr_group = ply:GetUserGroup()
|
||||
|
||||
if curr_group != group then
|
||||
VyHub.Player:change_game_group(ply, group)
|
||||
end
|
||||
end)
|
||||
end, function()
|
||||
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Player:refresh(ply, callback)
|
||||
VyHub.Player:check_group(ply)
|
||||
end
|
||||
|
||||
function VyHub.Player:get_group(ply)
|
||||
if not IsValid(ply) then
|
||||
return nil
|
||||
end
|
||||
|
||||
local group = VyHub.groups_mapped[ply:GetUserGroup()]
|
||||
|
||||
if group == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return group
|
||||
end
|
||||
|
||||
function VyHub.Player:check_property(ply, property)
|
||||
if not IsValid(ply) then return false end
|
||||
|
||||
local group = VyHub.Player:get_group(ply)
|
||||
|
||||
if group != nil then
|
||||
local prop = group.properties[property]
|
||||
|
||||
if prop != nil and prop.granted then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local steamid64 = ply:SteamID64()
|
||||
|
||||
if VyHub.Player.table[steamid64] then
|
||||
return VyHub.Player.table[steamid64].admin
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function meta_ply:VyHubID(callback)
|
||||
if IsValid(self) then
|
||||
local user = VyHub.Player.table[self:SteamID64()]
|
||||
local id = nil
|
||||
|
||||
if user != nil then
|
||||
id = user.id
|
||||
end
|
||||
|
||||
if id == nil or id == "" then
|
||||
VyHub.Player:get(self:SteamID64(), function(user)
|
||||
if user then
|
||||
if callback then
|
||||
callback(user.id)
|
||||
end
|
||||
else
|
||||
if callback then
|
||||
callback(nil)
|
||||
end
|
||||
end
|
||||
end)
|
||||
else
|
||||
if callback then
|
||||
callback(id)
|
||||
end
|
||||
end
|
||||
|
||||
return id
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("vyhub_ply_connected", "vyhub_ply_vyhub_ply_connected", function(ply)
|
||||
VyHub.Player:initialize(ply)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerInitialSpawn","vyhub_ply_PlayerInitialSpawn", function(ply)
|
||||
if IsValid(ply) and not ply:IsBot() then
|
||||
if VyHub.ready then
|
||||
hook.Run("vyhub_ply_connected", ply)
|
||||
else
|
||||
VyHub.Player.connect_queue[#VyHub.Player.connect_queue+1] = ply
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_ply_vyhub_ready", function ()
|
||||
timer.Simple(5, function()
|
||||
for _, ply in ipairs(VyHub.Player.connect_queue) do
|
||||
if IsValid(ply) then
|
||||
hook.Run("vyhub_ply_connected", ply)
|
||||
end
|
||||
end
|
||||
|
||||
VyHub.Player.connect_queue = {}
|
||||
end)
|
||||
end)
|
||||
@@ -1,266 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Reward = VyHub.Reward or {}
|
||||
VyHub.Reward.executed_rewards_queue = VyHub.Reward.executed_rewards_queue or {}
|
||||
VyHub.Reward.executed_rewards = VyHub.Reward.executed_rewards or {}
|
||||
VyHub.rewards = VyHub.rewards or {}
|
||||
|
||||
local RewardEvent = {
|
||||
DIRECT = "DIRECT",
|
||||
CONNECT = "CONNECT",
|
||||
SPAWN = "SPAWN",
|
||||
DEATH = "DEATH",
|
||||
DISCONNECT = "DISCONNECT",
|
||||
DISABLE = "DISABLE",
|
||||
}
|
||||
|
||||
local RewardType = {
|
||||
COMMAND = "COMMAND",
|
||||
SCRIPT = "SCRIPT",
|
||||
CREDITS = "CREDITS",
|
||||
MEMBERSHIP = "MEMBERSHIP",
|
||||
}
|
||||
|
||||
function VyHub.Reward:refresh(callback, limit_players, err)
|
||||
local user_ids = ""
|
||||
local players = limit_players or player.GetHumans()
|
||||
|
||||
for _, ply in ipairs(players) do
|
||||
if IsValid(ply) then
|
||||
local id = ply:VyHubID()
|
||||
|
||||
if id and string.len(id) == 36 then
|
||||
local glue = '&'
|
||||
|
||||
if user_ids == "" then
|
||||
glue = '?'
|
||||
end
|
||||
|
||||
user_ids = user_ids .. glue .. 'user_id=' .. id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if user_ids == "" then
|
||||
VyHub.rewards = {}
|
||||
else
|
||||
local query = f("%s&active=true&serverbundle_id=%s&status=OPEN&for_server_id=%s&foreign_ids=true", user_ids, VyHub.server.serverbundle.id, VyHub.server.id)
|
||||
|
||||
VyHub.API:get('/packet/reward/applied/user' .. query, nil, nil,
|
||||
function(code, result)
|
||||
if limit_players == nil then
|
||||
VyHub.rewards = result
|
||||
VyHub:msg(f("Found %i users with open rewards.", table.Count(result)), "debug")
|
||||
else
|
||||
for steamid, arewards in pairs(result) do
|
||||
VyHub.rewards[steamid] = arewards
|
||||
end
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end, function (code, reason)
|
||||
if err then
|
||||
err()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Reward:set_executed(areward_id)
|
||||
VyHub.Reward.executed_rewards_queue[areward_id] = true
|
||||
table.insert(VyHub.Reward.executed_rewards, areward_id)
|
||||
|
||||
VyHub.Reward:save_executed()
|
||||
end
|
||||
|
||||
function VyHub.Reward:save_executed()
|
||||
VyHub.Cache:save("executed_rewards_queue", VyHub.Reward.executed_rewards_queue)
|
||||
end
|
||||
|
||||
function VyHub.Reward:send_executed()
|
||||
for areward_id, val in pairs(VyHub.Reward.executed_rewards_queue) do
|
||||
if val != nil then
|
||||
VyHub.API:patch('/packet/reward/applied/%s', { areward_id }, { executed_on = { VyHub.server.id } }, function (code, result)
|
||||
VyHub.Reward.executed_rewards_queue[areward_id] = nil
|
||||
VyHub.Reward:save_executed()
|
||||
end, function (code, reason)
|
||||
if code >= 400 and code < 500 then
|
||||
VyHub:msg(f("Could not mark reward %s as executed. Aborting.", areward_id), "error")
|
||||
VyHub.Reward.executed_rewards_queue[areward_id] = nil
|
||||
VyHub.Reward:save_executed()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Reward:exec_rewards(event, steamid)
|
||||
steamid = steamid or nil
|
||||
|
||||
local allowed_events = { event }
|
||||
|
||||
local rewards_by_player = VyHub.rewards
|
||||
|
||||
if steamid != nil then
|
||||
rewards_by_player = {}
|
||||
rewards_by_player[steamid] = VyHub.rewards[steamid]
|
||||
else
|
||||
if event != RewardEvent.DIRECT then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if event == RewardEvent.DIRECT then
|
||||
table.insert(allowed_events, RewardEvent.DISABLE)
|
||||
end
|
||||
|
||||
for steamid, arewards in pairs(rewards_by_player) do
|
||||
local ply = player.GetBySteamID64(steamid)
|
||||
|
||||
if not IsValid(ply) then
|
||||
VyHub:msg(f("Player %s not valid, skipping.", steamid), "debug")
|
||||
continue
|
||||
end
|
||||
|
||||
for _, areward in ipairs(arewards) do
|
||||
local se = true
|
||||
local reward = areward.reward
|
||||
|
||||
if not table.HasValue(allowed_events, reward.on_event) then
|
||||
continue
|
||||
end
|
||||
|
||||
if table.HasValue(VyHub.Reward.executed_rewards, areward.id) then
|
||||
VyHub:msg(f("Skipped reward %s, because it already has been executed.", areward.id), "debug")
|
||||
continue
|
||||
end
|
||||
|
||||
local data = reward.data
|
||||
|
||||
if reward.type == RewardType.COMMAND then
|
||||
if data.command != nil then
|
||||
local cmd = VyHub.Reward:do_string_replacements(data.command, ply, areward)
|
||||
|
||||
if VyHub.Config.reward_command_whitelist and #VyHub.Config.reward_command_whitelist > 0 then
|
||||
local matched = false
|
||||
|
||||
for _, cmd_pattern in ipairs(VyHub.Config.reward_command_whitelist) do
|
||||
if string.match(cmd, cmd_pattern) != nil then
|
||||
matched = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not matched then
|
||||
VyHub:msg(f("Failed to execute reward '%s': Command '%s' does not match a command on the whitelist.", reward.name, cmd), "error")
|
||||
continue
|
||||
end
|
||||
end
|
||||
|
||||
game.ConsoleCommand(cmd.. "\n")
|
||||
end
|
||||
elseif reward.type == RewardType.SCRIPT then
|
||||
if VyHub.Config.reward_disable_scripts then
|
||||
VyHub:msg(f("Failed to execute reward '%s': Scripts are not allowed on this server. You can enable scripts in sv_config.lua or by entering 'vh_config reward_disable_scripts false' in the server console.", reward.name), "error")
|
||||
continue
|
||||
end
|
||||
|
||||
local lua_str = data.script
|
||||
|
||||
if lua_str != nil then
|
||||
lua_str = VyHub.Reward:do_string_replacements(lua_str, ply, areward)
|
||||
|
||||
RunString("local PLAYER = player.GetBySteamID64(\"" .. steamid .. "\") " .. lua_str, "vyhub_reward_script")
|
||||
end
|
||||
else
|
||||
VyHub:msg(f("No implementation for reward type %s", reward.type) "warning")
|
||||
end
|
||||
|
||||
VyHub:msg(f("Executed reward %s for user %s (%s): %s", reward.type, ply:Nick(), ply:SteamID64(), json.encode(data)))
|
||||
|
||||
if se and reward.once then
|
||||
VyHub.Reward:set_executed(areward.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
VyHub.Reward:send_executed()
|
||||
end
|
||||
|
||||
function VyHub.Reward:do_string_replacements(inp_str, ply, areward)
|
||||
local purchase_amount = "-"
|
||||
|
||||
if areward.applied_packet.purchase != nil then
|
||||
purchase_amount = areward.applied_packet.purchase.amount_text
|
||||
end
|
||||
|
||||
local replacements = {
|
||||
["user_id"] = ply:VyHubID(),
|
||||
["nick"] = ply:Nick(),
|
||||
["steamid64"] = ply:SteamID64(),
|
||||
["steamid32"] = ply:SteamID(),
|
||||
["uniqueid"] = ply:UniqueID(),
|
||||
["applied_packet_id"] = areward.applied_packet_id,
|
||||
["packet_title"] = areward.applied_packet.packet.title,
|
||||
["purchase_amount"] = purchase_amount,
|
||||
}
|
||||
|
||||
for k, v in pairs(replacements) do
|
||||
inp_str = string.Replace(tostring(inp_str), "%" .. tostring(k) .. "%", tostring(v))
|
||||
end
|
||||
|
||||
return inp_str
|
||||
end
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_reward_vyhub_ready", function ()
|
||||
VyHub.Reward.executed_rewards_queue = VyHub.Cache:get("executed_rewards_queue") or {}
|
||||
|
||||
VyHub.Reward:refresh(function ()
|
||||
VyHub.Reward:exec_rewards(RewardEvent.DIRECT)
|
||||
end)
|
||||
|
||||
timer.Create("vyhub_reward_refresh", 60, 0, function ()
|
||||
VyHub.Reward:refresh(function ()
|
||||
VyHub.Reward:exec_rewards(RewardEvent.DIRECT)
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_ply_initialized", "vyhub_reward_vyhub_ply_initialized", function(ply)
|
||||
local function exec_ply_rewards()
|
||||
VyHub.Reward:exec_rewards(RewardEvent.CONNECT, tostring(ply:SteamID64()))
|
||||
hook.Run("vyhub_reward_post_connect", ply)
|
||||
end
|
||||
|
||||
VyHub.Reward:refresh(exec_ply_rewards, { ply }, exec_ply_rewards)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawn", "vyhub_reward_PlayerSpawn", function(ply)
|
||||
if ply:Alive() then
|
||||
VyHub.Reward:exec_rewards(RewardEvent.SPAWN, tostring(ply:SteamID64()))
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PostPlayerDeath", "vyhub_reward_PostPlayerDeath", function(ply)
|
||||
VyHub.Reward:exec_rewards(RewardEvent.DEATH, tostring(ply:SteamID64()))
|
||||
end)
|
||||
|
||||
-- Does not work
|
||||
hook.Add("PlayerDisconnect", "vyhub_reward_PlayerDisconnect", function(ply)
|
||||
if IsValid(ply) then
|
||||
VyHub.Reward:exec_rewards(RewardEvent.Disconnect, tostring(ply:SteamID64()))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
@@ -1,157 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Server = VyHub.Server or {}
|
||||
|
||||
VyHub.Server.extra_defaults = {
|
||||
res_slots = 0,
|
||||
res_slots_keep_free = false,
|
||||
res_slots_hide = false,
|
||||
}
|
||||
|
||||
VyHub.Server.reserved_slot_plys = VyHub.Server.reserved_slot_plys or {}
|
||||
|
||||
function VyHub.Server:get_extra(key)
|
||||
if VyHub.server.extra != nil and VyHub.server.extra[key] != nil then
|
||||
return VyHub.server.extra[key]
|
||||
end
|
||||
|
||||
return VyHub.Server.extra_defaults[key]
|
||||
end
|
||||
|
||||
function VyHub.Server:update_status()
|
||||
local user_activities = {}
|
||||
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
local id = ply:VyHubID()
|
||||
|
||||
if id and string.len(id) == 36 then
|
||||
local tt = string.FormattedTime( ply:TimeConnected() )
|
||||
|
||||
table.insert(user_activities, { user_id = id, extra = {
|
||||
Score = ply:Frags(),
|
||||
Deaths = ply:Deaths(),
|
||||
Nickname = ply:Nick(),
|
||||
Playtime = f('%02d:%02d:%02d', tt.h, tt.m, tt.s),
|
||||
Ping = f('%i ms', ply:Ping()),
|
||||
}})
|
||||
end
|
||||
end
|
||||
|
||||
local data = {
|
||||
users_max = VyHub.Server.max_slots_visible,
|
||||
users_current = #player.GetAll(),
|
||||
map = game.GetMap(),
|
||||
is_alive = true,
|
||||
user_activities = user_activities,
|
||||
}
|
||||
|
||||
VyHub:msg(f("Updating status: %s", json.encode(data)), "debug")
|
||||
|
||||
VyHub.API:patch(
|
||||
'/server/%s',
|
||||
{VyHub.server.id},
|
||||
data,
|
||||
function ()
|
||||
hook.Run("vyhub_dashboard_data_changed")
|
||||
end,
|
||||
function ()
|
||||
VyHub:msg("Could not update server status.", "error")
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function VyHub.Server:update_max_slots()
|
||||
RunConsoleCommand("sv_visiblemaxplayers", VyHub.Server.max_slots_visible)
|
||||
end
|
||||
|
||||
function VyHub.Server:init_slots()
|
||||
VyHub.Server.max_slots = game.MaxPlayers() - VyHub.Server:get_extra("res_slots")
|
||||
VyHub.Server.max_slots_visible = VyHub.Server.max_slots
|
||||
|
||||
if VyHub.Server:get_extra("res_slots_hide") then
|
||||
VyHub.Server:update_max_slots()
|
||||
|
||||
hook.Add("PlayerDisconnected", "vyhub_server_PlayerDisconnected", function(ply)
|
||||
timer.Create("vyhub_slots", 0.5, 20, function()
|
||||
if not IsValid(ply) then
|
||||
timer.Remove("vyhub_slots")
|
||||
VyHub.Server:update_max_slots()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
else
|
||||
VyHub.Server.max_slots_visible = game.MaxPlayers()
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Server:can_use_rslot(ply)
|
||||
if not IsValid(ply) or ply:IsBot() then
|
||||
return false
|
||||
end
|
||||
|
||||
if table.HasValue(VyHub.Server.reserved_slot_plys, ply:SteamID64()) then
|
||||
return true
|
||||
end
|
||||
|
||||
local group = VyHub.Player:get_group(ply)
|
||||
|
||||
if group != nil then
|
||||
if group.properties.reserved_slot_use != nil then
|
||||
return group.properties.reserved_slot_use.granted
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function VyHub.Server:handle_ply_connect(ply)
|
||||
if IsValid(ply) then
|
||||
if #player.GetHumans() > VyHub.Server.max_slots then
|
||||
if VyHub.Server:can_use_rslot(ply) then
|
||||
if VyHub.Server:get_extra("res_slots_keep_free") then
|
||||
local tokick = nil
|
||||
|
||||
for _, v in ipairs(player.GetHumans()) do
|
||||
if v:SteamID64() != ply:SteamID64() and not VyHub.Server:can_use_rslot(v) then
|
||||
if tokick == nil or (IsValid(tokick) and v:TimeConnected() < tokick:TimeConnected()) then
|
||||
tokick = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if tokick and IsValid(tokick) then
|
||||
tokick:Kick(VyHub.lang.rslots.kick)
|
||||
else
|
||||
ply:Kick(VyHub.lang.rslots.full)
|
||||
end
|
||||
end
|
||||
else
|
||||
ply:Kick(VyHub.lang.rslots.full_no_slot)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_server_vyhub_ready", function ()
|
||||
VyHub.Server:init_slots()
|
||||
VyHub.Server:update_status()
|
||||
|
||||
timer.Create("vyhub_status_update", 60, 0, function ()
|
||||
VyHub.Server:update_status()
|
||||
end)
|
||||
|
||||
hook.Add("vyhub_reward_post_connect", "vyhub_server_vyhub_reward_post_connect", function (ply)
|
||||
VyHub.Server:handle_ply_connect(ply)
|
||||
end)
|
||||
end)
|
||||
@@ -1,143 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Statistic = VyHub.Statistic or {}
|
||||
VyHub.Statistic.playtime = VyHub.Statistic.playtime or {}
|
||||
VyHub.Statistic.attr_def = VyHub.Statistic.attr_def or nil
|
||||
|
||||
function VyHub.Statistic:save_playtime()
|
||||
VyHub:msg(f("Saved playtime statistics: %s", json.encode(VyHub.Statistic.playtime)), "debug")
|
||||
|
||||
VyHub.Cache:save("playtime", VyHub.Statistic.playtime)
|
||||
end
|
||||
|
||||
function VyHub.Statistic:add_one_minute()
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
local steamid = ply:SteamID64()
|
||||
ply:VyHubID(function (user_id)
|
||||
if user_id == nil or string.len(user_id) < 10 then
|
||||
VyHub:msg(f("Could not add playtime for user %s", steamid))
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.Statistic.playtime[user_id] = VyHub.Statistic.playtime[user_id] or 0
|
||||
VyHub.Statistic.playtime[user_id] = VyHub.Statistic.playtime[user_id] + 60
|
||||
end)
|
||||
end
|
||||
|
||||
VyHub.Statistic:save_playtime()
|
||||
end
|
||||
|
||||
function VyHub.Statistic:send_playtime()
|
||||
VyHub.Statistic:get_or_create_attr_definition(function (attr_def)
|
||||
if attr_def == nil then
|
||||
VyHub:msg("Could not send playtime statistics to API.", "warning")
|
||||
return
|
||||
end
|
||||
|
||||
local user_ids = table.GetKeys(VyHub.Statistic.playtime)
|
||||
|
||||
timer.Create("vyhub_send_stats", 0.3, table.Count(user_ids), function ()
|
||||
local i = table.Count(user_ids)
|
||||
local user_id = user_ids[i]
|
||||
|
||||
if user_id != nil then
|
||||
local seconds = VyHub.Statistic.playtime[user_id]
|
||||
table.remove(user_ids, i)
|
||||
|
||||
if seconds != nil and seconds > 0 then
|
||||
local hours = math.Round(seconds / 60 / 60, 2)
|
||||
|
||||
if hours > 0 then
|
||||
if string.len(user_id) < 10 then
|
||||
VyHub.Statistic.playtime[user_id] = nil
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.API:post("/user/attribute/", nil, {
|
||||
definition_id = attr_def.id,
|
||||
user_id = user_id,
|
||||
serverbundle_id = VyHub.server.serverbundle.id,
|
||||
value = tostring(hours),
|
||||
}, function (code, result)
|
||||
VyHub.Statistic.playtime[user_id] = nil
|
||||
VyHub.Statistic:save_playtime()
|
||||
end, function (code, reason)
|
||||
if code == 404 then
|
||||
VyHub.Statistic.playtime[user_id] = nil
|
||||
VyHub.Statistic:save_playtime()
|
||||
end
|
||||
|
||||
VyHub:msg(f("Could not send %s seconds playtime of %s to API.", seconds, user_id), "warning")
|
||||
end)
|
||||
end
|
||||
else
|
||||
VyHub.Statistic.playtime[user_id] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Statistic:get_or_create_attr_definition(callback)
|
||||
local function cb_wrapper(attr_def)
|
||||
VyHub.Statistic.attr_def = attr_def
|
||||
|
||||
callback(attr_def)
|
||||
end
|
||||
|
||||
if VyHub.Statistic.attr_def != nil then
|
||||
callback(VyHub.Statistic.attr_def)
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.API:get("/user/attribute/definition/%s", { "playtime" }, nil, function (code, result)
|
||||
VyHub.Cache:save("playtime_attr_def", result)
|
||||
cb_wrapper(result)
|
||||
end, function (code, reason)
|
||||
if code != 404 then
|
||||
local attr_def = VyHub.Cache:get("playtime_attr_def")
|
||||
|
||||
cb_wrapper(attr_def)
|
||||
else
|
||||
VyHub.API:post("/user/attribute/definition/", nil, {
|
||||
name = "playtime",
|
||||
title = "Play Time",
|
||||
unit = "Hours",
|
||||
type = "ACCUMULATED",
|
||||
accumulation_interval = "day",
|
||||
unspecific = "true",
|
||||
}, function (code, result)
|
||||
VyHub.Cache:save("playtime_attr_def", result)
|
||||
cb_wrapper(result)
|
||||
end, function (code, reason)
|
||||
cb_wrapper(nil)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_statistic_vyhub_ready", function ()
|
||||
VyHub.Statistic.playtime = VyHub.Cache:get("playtime") or {}
|
||||
|
||||
VyHub.Statistic:send_playtime()
|
||||
|
||||
timer.Create("vyhub_statistic_playtime_tick", 60, 0, function ()
|
||||
VyHub.Statistic:add_one_minute()
|
||||
end)
|
||||
|
||||
timer.Create("vyhub_statistic_send_playtime", 3600, 0, function ()
|
||||
VyHub.Statistic:send_playtime()
|
||||
end)
|
||||
end)
|
||||
@@ -1,183 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
|
||||
VyHub.Warning = VyHub.Warning or {}
|
||||
|
||||
function VyHub.Warning:create(steamid, reason, processor_steamid)
|
||||
processor_steamid = processor_steamid or nil
|
||||
|
||||
VyHub.Player:get(steamid, function (user)
|
||||
if user == nil then
|
||||
VyHub.Util:print_chat_steamid(processor_steamid, f("<red>Cannot find VyHub user with SteamID %s.</red>", steamid))
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.Player:get(processor_steamid, function (processor)
|
||||
if processor_steamid != nil and processor == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local url = '/warning/'
|
||||
|
||||
if processor != nil then
|
||||
url = url .. f('?morph_user_id=%s', processor.id)
|
||||
end
|
||||
|
||||
VyHub.API:post(url, nil, {
|
||||
reason = reason,
|
||||
serverbundle_id = VyHub.server.serverbundle.id,
|
||||
user_id = user.id
|
||||
}, function (code, result)
|
||||
VyHub.Ban:refresh()
|
||||
VyHub:msg(f("Added warning for player %s: %s", user.username, reason))
|
||||
VyHub.Util:print_chat_all(f(VyHub.lang.warning.user_warned, user.username, processor.username, reason))
|
||||
VyHub.Util:print_chat_steamid(steamid, f(VyHub.lang.warning.received, processor.username, reason))
|
||||
VyHub.Util:play_sound_steamid(steamid, "https://cdn.vyhub.net/sound/negativebeep.wav")
|
||||
hook.Run("vyhub_dashboard_data_changed")
|
||||
end, function (code, err_reason, _, err_text)
|
||||
VyHub:msg(f("Error while adding warning for player %s: %s", user.username, err_text), "error")
|
||||
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.warning.create_error, user.username, err_text))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function VyHub.Warning:delete(warning_id, processor_steamid)
|
||||
processor_steamid = processor_steamid or nil
|
||||
|
||||
VyHub.Player:get(processor_steamid, function (processor)
|
||||
if not processor then return end
|
||||
|
||||
local url = '/warning/%s'
|
||||
|
||||
if processor != nil then
|
||||
url = url .. f('?morph_user_id=%s', processor.id)
|
||||
end
|
||||
|
||||
VyHub.API:delete(url, { warning_id }, function (code, result)
|
||||
VyHub:msg(f("%s deleted warning %s.", processor.username, warning_id))
|
||||
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.warning.deleted))
|
||||
VyHub.Util:print_chat_steamid(steamid, VyHub.lang.warning.deleted_self)
|
||||
hook.Run("vyhub_dashboard_data_changed")
|
||||
end, function (code, err_reason, _, err_text)
|
||||
VyHub:msg(f("Error while deleteing warning %s: %s", warning_id, err_text), "error")
|
||||
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.other.error_api, err_text))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
function VyHub.Warning:toggle(warning_id, processor_steamid)
|
||||
processor_steamid = processor_steamid or nil
|
||||
|
||||
VyHub.Player:get(processor_steamid, function (processor)
|
||||
if not processor then return end
|
||||
|
||||
local url = '/warning/%s/toggle'
|
||||
|
||||
if processor != nil then
|
||||
url = url .. f('?morph_user_id=%s', processor.id)
|
||||
end
|
||||
|
||||
VyHub.API:patch(url, { warning_id }, nil, function (code, result)
|
||||
VyHub:msg(f("%s toggled warning %s.", processor.username, warning_id))
|
||||
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.warning.toggled))
|
||||
VyHub.Util:print_chat_steamid(steamid, VyHub.lang.warning.toggled_self)
|
||||
hook.Run("vyhub_dashboard_data_changed")
|
||||
end, function (code, err_reason, _, err_text)
|
||||
VyHub:msg(f("Error while toggling status of warning %s: %s", warning_id, err_text), "error")
|
||||
VyHub.Util:print_chat_steamid(processor_steamid, f(VyHub.lang.other.error_api, err_text))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local function warn_command(ply, args)
|
||||
if not VyHub.Player:check_property(ply, "warning_edit") then
|
||||
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
|
||||
return
|
||||
end
|
||||
|
||||
if args[1] and args[2] then
|
||||
local reason = VyHub.Util:concat_args(args, 2)
|
||||
|
||||
local target = VyHub.Util:get_player_by_nick(args[1])
|
||||
|
||||
if target and IsValid(target) then
|
||||
local nickparts = string.Explode(' ', target:Nick())
|
||||
|
||||
if #nickparts > 1 then
|
||||
nickparts = VyHub.Util:concat_args(nickparts, 2) .. ' '
|
||||
reason = string.Replace(reason, nickparts, '')
|
||||
end
|
||||
|
||||
VyHub.Warning:create(target:SteamID64(), reason, ply:SteamID64())
|
||||
end
|
||||
end
|
||||
|
||||
if IsValid(ply) then
|
||||
VyHub.Util:print_chat(ply, VyHub.lang.warning.cmd_help)
|
||||
end
|
||||
|
||||
return false;
|
||||
end
|
||||
|
||||
hook.Add("vyhub_ready", "vyhub_warning_vyhub_ready", function ()
|
||||
concommand.Add("vh_warn", function(ply, _, args)
|
||||
if not args[1] or not args[2] then return end
|
||||
|
||||
if VyHub.Util:is_server(ply) then
|
||||
VyHub.Warning:create(args[1], args[2])
|
||||
elseif IsValid(ply) then
|
||||
if VyHub.Player:check_property(ply, "warning_edit") then
|
||||
VyHub.Warning:create(args[1], args[2], ply:SteamID64())
|
||||
else
|
||||
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
concommand.Add("vh_warning_toggle", function(ply, _, args)
|
||||
if not args[1] then return end
|
||||
|
||||
local warning_id = args[1]
|
||||
|
||||
if VyHub.Util:is_server(ply) then
|
||||
VyHub.Warning:toggle(warning_id)
|
||||
elseif IsValid(ply) then
|
||||
if VyHub.Player:check_property(ply, "warning_edit") then
|
||||
VyHub.Warning:toggle(warning_id, ply:SteamID64())
|
||||
else
|
||||
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
concommand.Add("vh_warning_delete", function(ply, _, args)
|
||||
if not args[1] then return end
|
||||
|
||||
local warning_id = args[1]
|
||||
|
||||
if VyHub.Util:is_server(ply) then
|
||||
VyHub.Warning:delete(warning_id)
|
||||
elseif IsValid(ply) then
|
||||
if VyHub.Player:check_property(ply, "warning_delete") then
|
||||
VyHub.Warning:delete(warning_id, ply:SteamID64())
|
||||
else
|
||||
VyHub.Util:print_chat(ply, VyHub.lang.ply.no_permissions)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
for _, cmd in ipairs(VyHub.Config.commands_warn) do
|
||||
VyHub.Util:register_chat_command(cmd, warn_command)
|
||||
end
|
||||
end)
|
||||
@@ -1,43 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
-- DEFAULTS
|
||||
VyHub.Config.date_format = VyHub.Config.date_format or "%Y-%m-%d %H:%M:%S %z"
|
||||
|
||||
if SERVER then
|
||||
VyHub.Config.advert_interval = VyHub.Config.advert_interval or 180
|
||||
VyHub.Config.advert_prefix = VyHub.Config.advert_prefix or "[★] "
|
||||
|
||||
-- Do not allow too small refresh intervals
|
||||
if VyHub.Config.player_refresh_time < 5 then
|
||||
VyHub.Config.player_refresh_time = 5
|
||||
end
|
||||
if VyHub.Config.group_refresh_time < 5 then
|
||||
VyHub.Config.group_refresh_time = 5
|
||||
end
|
||||
|
||||
VyHub.Config.ban_message = VyHub.Config.ban_message or ">>> Ban Message <<<" .. "\n\n"
|
||||
.. VyHub.lang.other.reason .. ": %reason%" .. "\n"
|
||||
.. VyHub.lang.other.ban_date .. ": %ban_date%" .. "\n"
|
||||
.. VyHub.lang.other.unban_date .. ": %unban_date%" .. "\n"
|
||||
.. VyHub.lang.other.admin .. ": %admin%" .. "\n"
|
||||
.. VyHub.lang.other.id .. ": %id%" .. "\n\n"
|
||||
.. VyHub.lang.other.unban_url .. ": %unban_url%" .. "\n\n"
|
||||
|
||||
VyHub.Config.commands_shop = VyHub.Config.commands_shop or { '!shop' }
|
||||
VyHub.Config.commands_bans = VyHub.Config.commands_bans or { '!bans' }
|
||||
VyHub.Config.commands_warnings = VyHub.Config.commands_warnings or { '!warnings' }
|
||||
VyHub.Config.commands_news = VyHub.Config.commands_news or { '!news' }
|
||||
VyHub.Config.commands_profile = VyHub.Config.commands_profile or { '!user' }
|
||||
VyHub.Config.commands_warn = VyHub.Config.commands_warn or { '!warn' }
|
||||
VyHub.Config.commands_dashboard = VyHub.Config.commands_dashboard or { '!dashboard' }
|
||||
|
||||
VyHub.Config.strict_group_sync = VyHub.Config.strict_group_sync or false
|
||||
end
|
||||
@@ -1,19 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
VyHub.Group = VyHub.Group or {}
|
||||
|
||||
function VyHub.Group:get(groupname)
|
||||
if VyHub.groups_mapped == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
return VyHub.groups_mapped[groupname]
|
||||
end
|
||||
@@ -1,110 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local json = VyHub.Lib.json
|
||||
|
||||
VyHub.Lang = VyHub.Lang or {}
|
||||
VyHub.lang = VyHub.lang or nil
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("vyhub_lang")
|
||||
|
||||
VyHub.Lang.compressed = VyHub.Lang.compressed or nil
|
||||
|
||||
function VyHub.Lang:load()
|
||||
local f_en = file.Open("vyhub/lang/en.json", "r", "LUA")
|
||||
|
||||
if f_en == nil then
|
||||
VyHub:msg("Missing language file en.json!!! PLEASE MAKE SURE TO DOWNLOAD VYHUB-GMOD ON THE GITHUB RELESES PAGE! https://github.com/matbyte-com/vyhub-gmod/releases", "error")
|
||||
return
|
||||
end
|
||||
|
||||
local en = json.decode(f_en:Read())
|
||||
f_en:Close()
|
||||
|
||||
if not istable(en) then
|
||||
VyHub:msg("Could not load language file en.json!", "error")
|
||||
return
|
||||
end
|
||||
|
||||
VyHub.lang = en
|
||||
|
||||
VyHub:msg("Loaded language en.")
|
||||
|
||||
if VyHub.Config.lang != 'en' then
|
||||
local f_custom = file.Open(f("vyhub/lang/%s.json", VyHub.Config.lang), "r", "LUA")
|
||||
|
||||
if f_custom != nil then
|
||||
local custom = json.decode(f_custom:Read())
|
||||
f_custom:Close()
|
||||
|
||||
if istable(custom) then
|
||||
table.Merge(VyHub.lang, custom)
|
||||
VyHub:msg(f("Loaded language %s.", VyHub.Config.lang))
|
||||
else
|
||||
VyHub:msg(f("Could not load language file %s.json!", VyHub.Config.lang), "warning")
|
||||
end
|
||||
else
|
||||
VyHub:msg(f("Missing language file %s.json.", VyHub.Config.lang), "warning")
|
||||
end
|
||||
end
|
||||
|
||||
VyHub.Lang.compressed = util.Compress(json.encode(VyHub.lang))
|
||||
end
|
||||
|
||||
if VyHub.lang == nil then
|
||||
VyHub.Lang:load()
|
||||
end
|
||||
|
||||
net.Receive("vyhub_lang", function(_, ply)
|
||||
if not IsValid(ply) then return end
|
||||
if not VyHub.Lang.compressed then return end
|
||||
|
||||
local len = #VyHub.Lang.compressed
|
||||
|
||||
net.Start("vyhub_lang")
|
||||
net.WriteUInt(len, 16)
|
||||
net.WriteData(VyHub.Lang.compressed, len)
|
||||
net.Send(ply)
|
||||
end)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function VyHub.Lang:load()
|
||||
net.Start("vyhub_lang")
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
net.Receive("vyhub_lang", function()
|
||||
timer.Remove("vyhub_lang_load")
|
||||
|
||||
local len = net.ReadUInt(16)
|
||||
local lang_compr = net.ReadData(len)
|
||||
|
||||
VyHub.lang = json.decode(util.Decompress(lang_compr))
|
||||
|
||||
VyHub:msg("Loaded language.")
|
||||
|
||||
hook.Run("vyhub_lang_loaded")
|
||||
end)
|
||||
|
||||
hook.Add("Initialize", "vyhub_lang_Initialize", function ()
|
||||
VyHub.Lang:load()
|
||||
|
||||
timer.Create("vyhub_lang_load", 5, 5, function ()
|
||||
if VyHub.lang == nil then
|
||||
VyHub.Lang:load()
|
||||
else
|
||||
timer.Remove("vyhub_lang_load")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
@@ -1,322 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local f = string.format
|
||||
local date = VyHub.Lib.date
|
||||
|
||||
VyHub.Util = VyHub.Util or {}
|
||||
VyHub.Util.chat_commands = VyHub.Util.chat_commands or {}
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString("vyhub_print_chat")
|
||||
util.AddNetworkString("vyhub_play_sound")
|
||||
util.AddNetworkString("vyhub_open_url")
|
||||
end
|
||||
|
||||
function VyHub.Util:format_datetime(unix_timestamp)
|
||||
unix_timestamp = unix_timestamp or os.time()
|
||||
|
||||
local tz_wrong = os.date("%z", unix_timestamp)
|
||||
local timezone = f("%s:%s", string.sub(tz_wrong, 1, 3), string.sub(tz_wrong, 4, 5))
|
||||
|
||||
return os.date("%Y-%m-%dT%H:%M:%S" .. timezone, unix_timestamp)
|
||||
end
|
||||
|
||||
function VyHub.Util:is_server(obj)
|
||||
if type(obj) == "Entity" and (obj.EntIndex and obj:EntIndex() == 0) and !IsValid(obj) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:iso_to_unix_timestamp(datetime)
|
||||
if datetime == nil then return nil end
|
||||
|
||||
local pd = date(datetime)
|
||||
|
||||
if pd == nil then return nil end
|
||||
|
||||
local time = os.time(
|
||||
{
|
||||
year = pd:getyear(),
|
||||
month = pd:getmonth(),
|
||||
day = pd:getday(),
|
||||
hour = pd:gethours(),
|
||||
minute = pd:getminutes(),
|
||||
second = pd:getseconds(),
|
||||
}
|
||||
)
|
||||
|
||||
return time
|
||||
end
|
||||
|
||||
function VyHub.Util:get_ply_by_nick(nick)
|
||||
nick = string.lower(nick);
|
||||
|
||||
for _, v in ipairs(player.GetHumans()) do
|
||||
if(string.find(string.lower(v:Name()), nick, 1, true) != nil)
|
||||
then return v;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:register_chat_command(strCommand, Func)
|
||||
if !strCommand || !Func then return end
|
||||
|
||||
for k, v in pairs( VyHub.Util.chat_commands ) do
|
||||
if( strCommand == k ) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
VyHub.Util.chat_commands[ tostring( strCommand ) ] = Func;
|
||||
end
|
||||
|
||||
function VyHub.Util:concat_args(args, pos)
|
||||
local toconcat = {}
|
||||
|
||||
if pos > 1 then
|
||||
for i = pos, #args, 1 do
|
||||
toconcat[#toconcat+1] = args[i]
|
||||
end
|
||||
end
|
||||
|
||||
return string.Implode(" ", toconcat)
|
||||
end
|
||||
|
||||
|
||||
if SERVER then
|
||||
hook.Add("PlayerSay", "vyhub_util_PlayerSay", function(ply, message)
|
||||
if VyHub.ready then
|
||||
local chat_string = string.Explode(" ", message)
|
||||
local ret = nil
|
||||
|
||||
for k, v in pairs( VyHub.Util.chat_commands ) do
|
||||
if( string.lower(chat_string[1]) == string.lower(k) ) then
|
||||
table.remove(chat_string, 1)
|
||||
ret = v(ply, chat_string)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if ret != nil then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local colors = {
|
||||
red = Color(255, 24, 35),
|
||||
green = Color(45, 170, 0),
|
||||
blue = Color(0, 115, 204),
|
||||
yellow = Color(229, 221, 0),
|
||||
pink = Color(229, 0, 218),
|
||||
}
|
||||
|
||||
-- Takes a str message with colors and returns a table
|
||||
function VyHub.Util:replace_colors(message, no_color)
|
||||
local resultTable = {}
|
||||
local currentIndex = 1
|
||||
|
||||
local function getColor(colorName)
|
||||
if colors[colorName] then
|
||||
return colors[colorName]
|
||||
else
|
||||
return no_color
|
||||
end
|
||||
end
|
||||
|
||||
local function addStringToTable(str, color)
|
||||
table.insert(resultTable, color)
|
||||
table.insert(resultTable, str)
|
||||
end
|
||||
|
||||
local tags = {}
|
||||
|
||||
-- Extract all color tags and their corresponding content
|
||||
for tag, content in string.gmatch(message, "<([%l]+)>([^<]+)</%1>") do
|
||||
table.insert(tags, {tag = tag, content = content})
|
||||
end
|
||||
|
||||
-- Process the string, splitting it based on the color tags
|
||||
for _, tagData in ipairs(tags) do
|
||||
local startIndex, endIndex = string.find(message, f("<(%s)>[^<]+</%s>", string.PatternSafe(tagData.tag), string.PatternSafe(tagData.tag)), currentIndex, false)
|
||||
|
||||
if startIndex then
|
||||
local str = string.sub(message, currentIndex, startIndex - 1)
|
||||
|
||||
addStringToTable(str, no_color)
|
||||
addStringToTable(tagData.content, getColor(tagData.tag))
|
||||
|
||||
currentIndex = endIndex + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Append any remaining part of the string
|
||||
local str = string.sub(message, currentIndex)
|
||||
if str != "" then
|
||||
addStringToTable(str, no_color)
|
||||
end
|
||||
|
||||
return resultTable
|
||||
end
|
||||
|
||||
local color_tag = Color(0, 187, 255)
|
||||
|
||||
function VyHub.Util:print_chat(ply, message, tag, color)
|
||||
color = color or color_white
|
||||
|
||||
if SERVER then
|
||||
if IsValid(ply) then
|
||||
if not VyHub.Config.chat_tag then
|
||||
VyHub.Config.chat_tag = "[VyHub] "
|
||||
end
|
||||
|
||||
if not tag then
|
||||
tag = VyHub.Config.chat_tag
|
||||
end
|
||||
|
||||
message = string.Replace(message, '\r', '')
|
||||
message = string.Replace(message, '\n', '')
|
||||
|
||||
net.Start("vyhub_print_chat")
|
||||
net.WriteString(message)
|
||||
net.WriteString(tag)
|
||||
net.WriteColor(color)
|
||||
net.Send(ply)
|
||||
end
|
||||
elseif CLIENT then
|
||||
msg_table = VyHub.Util:replace_colors(message, color)
|
||||
|
||||
chat.AddText(color_tag, tag, color_white, unpack(msg_table))
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:print_chat_steamid(steamid, message, tag, color)
|
||||
if steamid != nil and steamid != false then
|
||||
local ply = player.GetBySteamID64(steamid)
|
||||
|
||||
if IsValid(ply) then
|
||||
VyHub.Util:print_chat(ply, message, tag, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:play_sound_steamid(steamid, url)
|
||||
if steamid then
|
||||
local ply = player.GetBySteamID64(steamid)
|
||||
|
||||
if IsValid(ply) then
|
||||
return VyHub.Util:play_sound(ply, url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:play_sound(ply, url)
|
||||
if SERVER then
|
||||
if IsValid(ply) then
|
||||
net.Start("vyhub_play_sound")
|
||||
net.WriteString(url)
|
||||
net.Send(ply)
|
||||
end
|
||||
elseif CLIENT then
|
||||
sound.PlayURL ( url, "", function() end)
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:open_url(ply, url)
|
||||
if SERVER then
|
||||
if IsValid(ply) then
|
||||
net.Start("vyhub_open_url")
|
||||
net.WriteString(url)
|
||||
net.Send(ply)
|
||||
end
|
||||
elseif CLIENT then
|
||||
gui.OpenURL(url)
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:print_chat_all(message, tag, color)
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
VyHub.Util:print_chat(ply, message, tag, color)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function VyHub.Util:get_player_by_nick(nick)
|
||||
nick = string.lower(nick);
|
||||
|
||||
for _,v in ipairs(player.GetHumans()) do
|
||||
if(string.find(string.lower(v:Name()), nick, 1, true) != nil)
|
||||
then return v;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function VyHub.Util:hex2rgb(hex)
|
||||
hex = hex:gsub("#","")
|
||||
if(string.len(hex) == 3) then
|
||||
return Color(tonumber("0x"..hex:sub(1,1)) * 17, tonumber("0x"..hex:sub(2,2)) * 17, tonumber("0x"..hex:sub(3,3)) * 17)
|
||||
elseif(string.len(hex) == 6) then
|
||||
return Color(tonumber("0x"..hex:sub(1,2)), tonumber("0x"..hex:sub(3,4)), tonumber("0x"..hex:sub(5,6)))
|
||||
else
|
||||
return color_white
|
||||
end
|
||||
end
|
||||
|
||||
function VyHub.Util:iso_ts_to_local_str(iso_ts)
|
||||
local bias = VyHub.Config.time_offset != nil and -math.Round(VyHub.Config.time_offset * 60 * 60) or nil
|
||||
|
||||
return date(iso_ts):setbias(bias):tolocal():fmt(VyHub.Config.date_format)
|
||||
end
|
||||
|
||||
|
||||
function VyHub.Util:invalid_str(str_list)
|
||||
for _, str in ipairs(str_list) do
|
||||
if str == nil or string.Trim(str) == "" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function VyHub.Util:escape_concommand_str(str)
|
||||
str = string.Replace(str, '"', "'")
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
|
||||
if CLIENT then
|
||||
net.Receive("vyhub_print_chat", function ()
|
||||
local message = net.ReadString()
|
||||
local tag = net.ReadString()
|
||||
local color = net.ReadColor()
|
||||
|
||||
VyHub.Util:print_chat(nil, message, tag, color)
|
||||
end)
|
||||
|
||||
net.Receive("vyhub_play_sound", function ()
|
||||
local url = net.ReadString()
|
||||
|
||||
VyHub.Util:play_sound(nil, url)
|
||||
end)
|
||||
|
||||
net.Receive("vyhub_open_url", function ()
|
||||
local url = net.ReadString()
|
||||
|
||||
VyHub.Util:open_url(nil, url)
|
||||
end)
|
||||
end
|
||||
@@ -1,143 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
__ ___ __ __
|
||||
/ |/ /__ ____/ /__ / / __ __
|
||||
/ /|_/ / _ `/ _ / -_) / _ \/ // /
|
||||
/_/ /_/\_,_/\_,_/\__/ /_.__/\_, /
|
||||
___ __ /___/ ___ __
|
||||
/ _ \___ / /_ _____ ___ / /____ ____ / _ \__ ______/ /__
|
||||
/ ___/ _ \/ / // / -_|_-</ __/ -_) __/ / // / // / __/ '_/
|
||||
/_/ \___/_/\_, /\__/___/\__/\__/_/ /____/\_,_/\__/_/\_\
|
||||
/___/
|
||||
https://steamcommunity.com/profiles/76561198057599363
|
||||
]]
|
||||
CreateClientConVar("cl_weapon_holsters", "1", true, false, "Enable Weapon Holsters (client side)")
|
||||
WepHolster = WepHolster or {}
|
||||
WepHolster.wepInfo = WepHolster.wepInfo or {}
|
||||
|
||||
net.Receive("sendWholeWHData", function(len)
|
||||
WepHolster.wepInfo = net.ReadTable()
|
||||
end)
|
||||
|
||||
net.Receive("sendWHData", function(len)
|
||||
local class = net.ReadString()
|
||||
local tbl = net.ReadTable()
|
||||
WepHolster.wepInfo[class] = tbl.Model and tbl or nil
|
||||
|
||||
if not tbl.Model then
|
||||
for pl, weps in pairs(WepHolster.HolsteredWeps) do
|
||||
local ply = Entity(pl)
|
||||
|
||||
for cls, wep in pairs(weps) do
|
||||
if cls == class then
|
||||
wep:Remove()
|
||||
WepHolster.HolsteredWeps[pl][cls] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
WepHolster.HolsteredWeps = WepHolster.HolsteredWeps or {}
|
||||
|
||||
local function CalcOffset(pos, ang, off)
|
||||
return pos + ang:Right() * off.x + ang:Forward() * off.y + ang:Up() * off.z
|
||||
end
|
||||
|
||||
local function cdwh()
|
||||
return WepHolster.wepInfo and WepHolster.HolsteredWeps and GetConVar("cl_weapon_holsters"):GetBool() and GetConVar("sv_weapon_holsters"):GetBool()
|
||||
end
|
||||
|
||||
hook.Add("PostPlayerDraw", "WeaponHolster", function(ply)
|
||||
if not cdwh() then
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid(ply) and ply:Alive() then
|
||||
for wepclass, model in pairs(WepHolster.HolsteredWeps[ply:EntIndex()] or {}) do
|
||||
if not WepHolster.wepInfo[wepclass] then
|
||||
return
|
||||
end
|
||||
|
||||
local bone = ply:LookupBone(WepHolster.wepInfo[wepclass].Bone)
|
||||
|
||||
if not bone then
|
||||
return
|
||||
end
|
||||
|
||||
local matrix = ply:GetBoneMatrix(bone)
|
||||
|
||||
if not matrix then
|
||||
return
|
||||
end
|
||||
|
||||
local pos = matrix:GetTranslation()
|
||||
local ang = matrix:GetAngles()
|
||||
pos = CalcOffset(pos, ang, WepHolster.wepInfo[wepclass].BoneOffset[1])
|
||||
model:SetRenderOrigin(pos)
|
||||
ang:RotateAroundAxis(ang:Forward(), WepHolster.wepInfo[wepclass].BoneOffset[2].p)
|
||||
ang:RotateAroundAxis(ang:Up(), WepHolster.wepInfo[wepclass].BoneOffset[2].y)
|
||||
ang:RotateAroundAxis(ang:Right(), WepHolster.wepInfo[wepclass].BoneOffset[2].r)
|
||||
model:SetRenderAngles(ang)
|
||||
model:DrawModel()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("Think", "WeaponHolster", function()
|
||||
if not cdwh() then
|
||||
return
|
||||
end
|
||||
|
||||
for _, ply in pairs(player.GetAll()) do
|
||||
if IsValid(ply) and ply:Alive() then
|
||||
for k, v in pairs(ply:GetWeapons()) do
|
||||
local class = v:GetClass()
|
||||
local plyid = ply:EntIndex()
|
||||
WepHolster.HolsteredWeps[plyid] = WepHolster.HolsteredWeps[plyid] or {}
|
||||
|
||||
if WepHolster.wepInfo[class] and ply:GetActiveWeapon() ~= v and not WepHolster.HolsteredWeps[plyid][class] then
|
||||
WepHolster.HolsteredWeps[plyid][class] = ClientsideModel(WepHolster.wepInfo[class].Model, RENDERGROUP_OPAQUE)
|
||||
|
||||
if not IsValid(WepHolster.HolsteredWeps[plyid][class]) then
|
||||
SafeRemoveEntity(WepHolster.HolsteredWeps[plyid][class]) -- just in case.
|
||||
WepHolster.HolsteredWeps[plyid][class] = nil
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
WepHolster.HolsteredWeps[plyid][class]:SetNoDraw(true)
|
||||
|
||||
if WepHolster.wepInfo[class].isEditing then
|
||||
WepHolster.HolsteredWeps[plyid][class]:SetMaterial("models/wireframe")
|
||||
--print("wireframe화 think")
|
||||
--print(WepHolster.wepInfo[class].isEditing)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 필요없는 CSEnt 찾아서 삭제
|
||||
for pl, weps in pairs(WepHolster.HolsteredWeps) do
|
||||
local ply = Entity(pl)
|
||||
|
||||
for class, wep in pairs(weps) do
|
||||
if not IsValid(ply) or not ply:IsPlayer() or not ply:Alive() or (IsValid(ply:GetActiveWeapon()) and ply:GetActiveWeapon():GetClass() == class) or not IsValid(ply:GetWeapon(class)) then
|
||||
wep:Remove() -- 삭제
|
||||
WepHolster.HolsteredWeps[pl][class] = nil
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -1,678 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
__ ___ __ __
|
||||
/ |/ /__ ____/ /__ / / __ __
|
||||
/ /|_/ / _ `/ _ / -_) / _ \/ // /
|
||||
/_/ /_/\_,_/\_,_/\__/ /_.__/\_, /
|
||||
___ __ /___/ ___ __
|
||||
/ _ \___ / /_ _____ ___ / /____ ____ / _ \__ ______/ /__
|
||||
/ ___/ _ \/ / // / -_|_-</ __/ -_) __/ / // / // / __/ '_/
|
||||
/_/ \___/_/\_, /\__/___/\__/\__/_/ /____/\_,_/\__/_/\_\
|
||||
/___/
|
||||
https://steamcommunity.com/profiles/76561198057599363
|
||||
]]
|
||||
local Menu = {
|
||||
Main = {},
|
||||
Editor = {}
|
||||
}
|
||||
|
||||
local playerBones = {"ValveBiped.Bip01_Head1", "ValveBiped.Bip01_Pelvis", "ValveBiped.Bip01_Spine", "ValveBiped.Bip01_Spine1", "ValveBiped.Bip01_Spine2", "ValveBiped.Bip01_Spine4", "ValveBiped.Anim_Attachment_RH", "ValveBiped.Bip01_R_Hand", "ValveBiped.Bip01_R_Forearm", "ValveBiped.Bip01_R_UpperArm", "ValveBiped.Bip01_R_Clavicle", "ValveBiped.Bip01_R_Foot", "ValveBiped.Bip01_R_Toe0", "ValveBiped.Bip01_R_Thigh", "ValveBiped.Bip01_R_Calf", "ValveBiped.Bip01_R_Shoulder", "ValveBiped.Bip01_R_Elbow", "ValveBiped.Bip01_Neck1", "ValveBiped.Anim_Attachment_LH", "ValveBiped.Bip01_L_Hand", "ValveBiped.Bip01_L_Forearm", "ValveBiped.Bip01_L_UpperArm", "ValveBiped.Bip01_L_Clavicle", "ValveBiped.Bip01_L_Foot", "ValveBiped.Bip01_L_Toe0", "ValveBiped.Bip01_L_Thigh", "ValveBiped.Bip01_L_Calf", "ValveBiped.Bip01_L_Shoulder", "ValveBiped.Bip01_L_Elbow"}
|
||||
|
||||
local function KeyboardOn(pnl)
|
||||
if (IsValid(Menu.Main.Frame) and IsValid(pnl) and pnl:HasParent(Menu.Main.Frame)) then
|
||||
Menu.Main.Frame:SetKeyboardInputEnabled(true)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("OnTextEntryGetFocus", "lf_weapon_properties_editor_keyboard_on", KeyboardOn)
|
||||
|
||||
local function KeyboardOff(pnl)
|
||||
if (IsValid(Menu.Main.Frame) and IsValid(pnl) and pnl:HasParent(Menu.Main.Frame)) then
|
||||
Menu.Main.Frame:SetKeyboardInputEnabled(false)
|
||||
|
||||
if pnl.OnValueChange then
|
||||
pnl:OnValueChange()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("OnTextEntryLoseFocus", "lf_weapon_properties_editor_keyboard_off", KeyboardOff)
|
||||
-- Blur Code by: https://facepunch.com/member.php?u=237675
|
||||
local blur = Material("pp/blurscreen")
|
||||
|
||||
local function DrawBlur(panel, amount)
|
||||
local x, y = panel:LocalToScreen(0, 0)
|
||||
local scrW, scrH = ScrW(), ScrH()
|
||||
surface.SetDrawColor(255, 255, 255)
|
||||
surface.SetMaterial(blur)
|
||||
|
||||
for i = 1, 3 do
|
||||
blur:SetFloat("$blur", (i / 3) * (amount or 6))
|
||||
blur:Recompute()
|
||||
render.UpdateScreenEffectTexture()
|
||||
surface.DrawTexturedRect(x * -1, y * -1, scrW, scrH)
|
||||
end
|
||||
end
|
||||
|
||||
local function removeHolstWep(class)
|
||||
for pl, weps in pairs(WepHolster.HolsteredWeps) do
|
||||
for cls, wep in pairs(weps) do
|
||||
if class == "" or cls == class then
|
||||
wep:Remove()
|
||||
WepHolster.HolsteredWeps[pl][cls] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function setEditing(class, bool)
|
||||
if class ~= "" then
|
||||
if WepHolster.wepInfo[class] then
|
||||
WepHolster.wepInfo[class].isEditing = bool
|
||||
end
|
||||
else
|
||||
for k, v in pairs(WepHolster.wepInfo) do
|
||||
WepHolster.wepInfo[k].isEditing = bool
|
||||
end
|
||||
end
|
||||
|
||||
for pl, weps in pairs(WepHolster.HolsteredWeps) do
|
||||
for cls, wep in pairs(weps) do
|
||||
if cls == class or class == "" then
|
||||
if bool then
|
||||
wep:SetMaterial("models/wireframe")
|
||||
--print("wireframe화 gui")
|
||||
--print(WepHolster.wepInfo[cls].isEditing)
|
||||
--print("wireframe 해제 gui")
|
||||
--print(WepHolster.wepInfo[cls].isEditing)
|
||||
else
|
||||
wep:Remove()
|
||||
WepHolster.HolsteredWeps[pl][cls] = nil
|
||||
WepHolster.wepInfo[cls].isEditing = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Menu.Editor:Init(class)
|
||||
local Frame = vgui.Create("DFrame", Menu.Main.Frame)
|
||||
local fw, fh = 600, 350
|
||||
local pw, ph = fw - 10, fh - 34
|
||||
Frame:SetPos(ScrW() - fw - 10, (ScrH() / 2) - (fh / 2))
|
||||
Frame:SetSize(fw, fh)
|
||||
Frame:SetTitle(class)
|
||||
Frame:SetVisible(true)
|
||||
Frame:SetDraggable(true)
|
||||
Frame:SetScreenLock(false)
|
||||
Frame:ShowCloseButton(true)
|
||||
Frame:MakePopup()
|
||||
Frame:SetKeyboardInputEnabled(false)
|
||||
|
||||
function Frame:Paint(w, h)
|
||||
DrawBlur(self, 2)
|
||||
draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 200))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function Frame.lblTitle:Paint(w, h)
|
||||
draw.SimpleTextOutlined(Frame.lblTitle:GetText(), "DermaDefaultBold", 1, 2, Color(255, 255, 255, 255), 0, 0, 1, Color(0, 0, 0, 255))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local pnl = Frame:Add("DPanel")
|
||||
pnl:Dock(FILL)
|
||||
pnl:DockPadding(10, 10, 10, 10)
|
||||
local prop = pnl:Add("DCategoryList")
|
||||
prop:Dock(FILL)
|
||||
|
||||
local function AddLineText(list, text, val, class)
|
||||
local line = list:Add("DPanel")
|
||||
line:DockPadding(5, 2, 5, 2)
|
||||
line:SetDrawBackground(false)
|
||||
local id
|
||||
|
||||
if val then
|
||||
local lbl = line:Add("DLabel")
|
||||
lbl:Dock(LEFT)
|
||||
lbl:SetWide(239)
|
||||
lbl:SetDark(true)
|
||||
lbl:SetText(text)
|
||||
id = line:Add("DTextEntry")
|
||||
id:Dock(FILL)
|
||||
id:SetText(val or "")
|
||||
else
|
||||
local lbl = line:Add("DLabel")
|
||||
lbl:Dock(FILL)
|
||||
lbl:SetText(text)
|
||||
end
|
||||
|
||||
return line, id
|
||||
end
|
||||
|
||||
local function AddLineInt(list, text, val, min, max)
|
||||
local line = list:Add("DPanel")
|
||||
line:DockPadding(5, 2, 5, 2)
|
||||
line:SetPaintBackground(false)
|
||||
local id
|
||||
|
||||
if val then
|
||||
id = line:Add("DNumSlider")
|
||||
id:Dock(FILL)
|
||||
id:SetDark(true)
|
||||
id:SetDecimals(3)
|
||||
id:SetMinMax(min, max)
|
||||
id:SetText(text)
|
||||
id:SetValue(val or 0)
|
||||
else
|
||||
local lbl = line:Add("DLabel")
|
||||
lbl:Dock(FILL)
|
||||
lbl:SetText(text)
|
||||
end
|
||||
|
||||
return line, id
|
||||
end
|
||||
|
||||
local cat = prop:Add(class)
|
||||
local list = vgui.Create("DListLayout")
|
||||
cat:SetContents(list)
|
||||
local line, rModel = AddLineText(list, "Model:", WepHolster.wepInfo[class].Model, class)
|
||||
|
||||
rModel.OnValueChange = function(val)
|
||||
val = rModel:GetValue()
|
||||
WepHolster.wepInfo[class].Model = val
|
||||
setEditing(class, true)
|
||||
removeHolstWep(class)
|
||||
end
|
||||
|
||||
local line, rBone = AddLineText(list, "Bone:", WepHolster.wepInfo[class].Bone)
|
||||
WepHolster.lookingBone = WepHolster.wepInfo[class].Bone
|
||||
|
||||
if rBone then
|
||||
rBone.OnValueChange = function(val)
|
||||
val = rBone:GetValue()
|
||||
|
||||
if table.HasValue(playerBones, val) then
|
||||
WepHolster.wepInfo[class].Bone = val
|
||||
setEditing(class, true)
|
||||
WepHolster.lookingBone = val
|
||||
end
|
||||
end
|
||||
|
||||
local c = line:Add("DComboBox")
|
||||
c:Dock(RIGHT)
|
||||
c:SetWide(40)
|
||||
c:SetSortItems(false)
|
||||
c:SetValue("...")
|
||||
|
||||
for _, v in pairs(playerBones) do
|
||||
c:AddChoice(v)
|
||||
end
|
||||
|
||||
function c:OnSelect(index, value)
|
||||
rBone:SetText(value)
|
||||
rBone:OnValueChange()
|
||||
c:SetValue("...")
|
||||
end
|
||||
end
|
||||
|
||||
local line, rVectorX = AddLineInt(list, "Position x:", WepHolster.wepInfo[class].BoneOffset[1].x, -20, 20, class)
|
||||
|
||||
rVectorX.OnValueChanged = function(value)
|
||||
WepHolster.wepInfo[class].BoneOffset[1].x = rVectorX:GetValue()
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
local line, rVectorY = AddLineInt(list, "Position y:", WepHolster.wepInfo[class].BoneOffset[1].y, -20, 20, class)
|
||||
|
||||
rVectorY.OnValueChanged = function(value)
|
||||
WepHolster.wepInfo[class].BoneOffset[1].y = rVectorY:GetValue()
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
local line, rVectorZ = AddLineInt(list, "Position z:", WepHolster.wepInfo[class].BoneOffset[1].z, -20, 20, class)
|
||||
|
||||
rVectorZ.OnValueChanged = function(value)
|
||||
WepHolster.wepInfo[class].BoneOffset[1].z = rVectorZ:GetValue()
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
local line, rAngleP = AddLineInt(list, "Angle pitch:", WepHolster.wepInfo[class].BoneOffset[2].p, -180, 180, class)
|
||||
|
||||
rAngleP.OnValueChanged = function(value)
|
||||
WepHolster.wepInfo[class].BoneOffset[2].p = rAngleP:GetValue()
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
local line, rAngleY = AddLineInt(list, "Angle yaw:", WepHolster.wepInfo[class].BoneOffset[2].y, -180, 180, class)
|
||||
|
||||
rAngleY.OnValueChanged = function(value)
|
||||
WepHolster.wepInfo[class].BoneOffset[2].y = rAngleY:GetValue()
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
local line, rAngleR = AddLineInt(list, "Angle roll:", WepHolster.wepInfo[class].BoneOffset[2].r, -180, 180, class)
|
||||
|
||||
rAngleR.OnValueChanged = function(value)
|
||||
WepHolster.wepInfo[class].BoneOffset[2].r = rAngleR:GetValue()
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
local subpnl = pnl:Add("DPanel")
|
||||
subpnl:Dock(BOTTOM)
|
||||
subpnl:DockMargin(0, 20, 0, 0)
|
||||
subpnl:SetHeight(20)
|
||||
subpnl:SetDrawBackground(false)
|
||||
local lw = (pw - 10) / 2 - 10
|
||||
local b = subpnl:Add("DComboBox")
|
||||
b:Dock(LEFT)
|
||||
b:SetWide(lw)
|
||||
b:SetValue("Reset")
|
||||
b:SetSortItems(false)
|
||||
b:AddChoice("to saved data if available")
|
||||
b:AddChoice("to default if available")
|
||||
|
||||
b.OnSelect = function(index, value, data)
|
||||
Frame:Close()
|
||||
|
||||
if value == 2 then
|
||||
local oldwhdata = WepHolster.wepInfo[class]
|
||||
net.Start("resetWHDataToDefault")
|
||||
net.WriteString(class)
|
||||
net.SendToServer()
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
local different
|
||||
|
||||
for k, v in pairs(WepHolster.wepInfo[class]) do
|
||||
if (k == "Model" or k == "Bone") and string.lower(oldwhdata[k]) ~= string.lower(v) then
|
||||
different = true
|
||||
print(k .. ": " .. v)
|
||||
-- print(_..": "..vec_ang)
|
||||
elseif k == "BoneOffset" then
|
||||
for _, vec_ang in pairs(v) do
|
||||
if vec_ang ~= oldwhdata[k][_] then
|
||||
different = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- '==' operator is not working on table.. fuck.
|
||||
if different then
|
||||
setEditing(class, true)
|
||||
end
|
||||
end)
|
||||
elseif not WepHolster.wepInfo[class].notSavedYet then
|
||||
net.Start("reloadWH")
|
||||
net.WriteString(class)
|
||||
net.SendToServer()
|
||||
setEditing(class, false)
|
||||
end
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
Menu.Editor:Init(class)
|
||||
removeHolstWep(class)
|
||||
end)
|
||||
end
|
||||
|
||||
local b = subpnl:Add("DComboBox")
|
||||
b:Dock(RIGHT)
|
||||
b:SetWide(lw)
|
||||
b:SetValue("Delete")
|
||||
b:AddChoice("Are you sure?")
|
||||
|
||||
b.OnSelect = function(index, value, data)
|
||||
net.Start("deleteWHData")
|
||||
net.WriteString(class)
|
||||
net.SendToServer()
|
||||
|
||||
timer.Simple(0.1, function()
|
||||
Menu.Main.Frame:Close()
|
||||
Menu.Main:Init()
|
||||
end)
|
||||
end
|
||||
|
||||
local b = pnl:Add("DButton")
|
||||
b:Dock(BOTTOM)
|
||||
b:DockMargin(0, 10, 0, 0)
|
||||
b:SetHeight(30)
|
||||
b:SetText("Apply Changes")
|
||||
|
||||
b.DoClick = function()
|
||||
setEditing(class, false)
|
||||
WepHolster.wepInfo[class].notSavedYet = nil
|
||||
net.Start("applyWepHolsterData")
|
||||
net.WriteString(class)
|
||||
net.WriteTable(WepHolster.wepInfo[class])
|
||||
net.SendToServer()
|
||||
Frame:Close()
|
||||
end
|
||||
|
||||
Frame.OnClose = function()
|
||||
--[[
|
||||
net.Start("reloadWH")
|
||||
net.WriteString(class)
|
||||
net.SendToServer()
|
||||
Menu.Main.Frame:Close()
|
||||
timer.Simple(0.1, function()
|
||||
removeHolstWep(class)
|
||||
Menu.Main:Init()
|
||||
end)
|
||||
]]
|
||||
WepHolster.lookingBone = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function addWeapon(class, model)
|
||||
WepHolster.wepInfo[class] = WepHolster.wepInfo[class] or {
|
||||
Model = model or "models/weapons/w_rif_ak47.mdl",
|
||||
Bone = "ValveBiped.Bip01_R_Clavicle",
|
||||
BoneOffset = {Vector(13, 4, 5), Angle(90, 0, 100)},
|
||||
notSavedYet = true
|
||||
}
|
||||
|
||||
Menu.Main.Frame:Close()
|
||||
Menu.Main:Init()
|
||||
Menu.Editor:Init(class)
|
||||
setEditing(class, true)
|
||||
end
|
||||
|
||||
function Menu.Main:Init()
|
||||
Menu.Main.Frame = vgui.Create("DFrame")
|
||||
local Frame = Menu.Main.Frame
|
||||
local fw, fh = 400, ScrH() - 20
|
||||
local pw, ph = fw - 10, fh - 34
|
||||
Frame:SetPos(10, 10)
|
||||
Frame:SetSize(fw, fh)
|
||||
Frame:SetTitle("Weapon Holsters Editor")
|
||||
Frame:SetVisible(true)
|
||||
Frame:SetDraggable(true)
|
||||
Frame:SetScreenLock(false)
|
||||
Frame:ShowCloseButton(true)
|
||||
Frame:MakePopup()
|
||||
Frame:SetKeyboardInputEnabled(false)
|
||||
|
||||
function Frame:Paint(w, h)
|
||||
DrawBlur(self, 2)
|
||||
draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 200))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function Frame.lblTitle:Paint(w, h)
|
||||
draw.SimpleTextOutlined(Frame.lblTitle:GetText(), "DermaDefaultBold", 1, 2, Color(255, 255, 255, 255), 0, 0, 1, Color(0, 0, 0, 255))
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local pnl = Frame:Add("DPanel")
|
||||
pnl:Dock(FILL)
|
||||
pnl:DockPadding(10, 10, 10, 10)
|
||||
local b = pnl:Add("DButton")
|
||||
b:Dock(TOP)
|
||||
b:DockMargin(0, 0, 0, 1)
|
||||
b:SetHeight(25)
|
||||
b:SetText("Add weapon by class:")
|
||||
|
||||
b.DoClick = function()
|
||||
local name = tostring(Menu.Main.WeaponEntry:GetValue())
|
||||
|
||||
if name == "" then
|
||||
return
|
||||
end
|
||||
|
||||
addWeapon(name)
|
||||
end
|
||||
|
||||
Menu.Main.WeaponEntry = pnl:Add("DTextEntry")
|
||||
Menu.Main.WeaponEntry:Dock(TOP)
|
||||
Menu.Main.WeaponEntry:DockMargin(0, 0, 0, 10)
|
||||
Menu.Main.WeaponEntry:SetHeight(20)
|
||||
Menu.Main.WeaponEntry:SetTooltip("Just right-click on the weapon icon in the spawn menu, click Copy to Clipboard, and paste it here.")
|
||||
--[[
|
||||
local help = pnl:Add("DLabel") -- Fuck you, I'll use SetTooltip instead.
|
||||
help:Dock(TOP)
|
||||
help:DockMargin(0, 0, 0, 10)
|
||||
help:SetHeight(26)
|
||||
help:SetText("Just right-click on the weapon icon in the spawn menu,\nclick Copy to Clipboard, and paste it here.")
|
||||
]]
|
||||
b = pnl:Add("DButton")
|
||||
b:Dock(TOP)
|
||||
b:DockMargin(0, 0, 0, 10)
|
||||
b:SetHeight(24)
|
||||
b:SetText("Add weapon you currently holding")
|
||||
|
||||
b.DoClick = function()
|
||||
local weapon = LocalPlayer():GetActiveWeapon()
|
||||
|
||||
if IsValid(weapon) then
|
||||
addWeapon(weapon:GetClass(), weapon:GetWeaponWorldModel())
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
local help = pnl:Add("DLabel")
|
||||
help:Dock(TOP)
|
||||
help:DockMargin(0, 0, 0, 5)
|
||||
help:SetHeight(13)
|
||||
help:SetText("Click the bar to sort the list, and double click the weapon to edit:")
|
||||
]]
|
||||
Menu.Main.WeaponList = pnl:Add("DListView")
|
||||
Menu.Main.WeaponList:Dock(FILL)
|
||||
Menu.Main.WeaponList:SetMultiSelect(false)
|
||||
Menu.Main.WeaponList:AddColumn("Category")
|
||||
Menu.Main.WeaponList:AddColumn("Name")
|
||||
Menu.Main.WeaponList:AddColumn("Class")
|
||||
Menu.Main.WeaponList:SetTooltip("Click the bar to sort the list, and double click the weapon to edit.")
|
||||
|
||||
function Menu.Main.WeaponList:DoDoubleClick(id, sel)
|
||||
local wepclass = tostring(sel:GetValue(3))
|
||||
|
||||
if WepHolster.wepInfo[wepclass] then
|
||||
Menu.Editor:Init(wepclass)
|
||||
end
|
||||
end
|
||||
|
||||
function Menu.Main.WeaponList:Populate()
|
||||
self:Clear()
|
||||
|
||||
for k, v in pairs(WepHolster.wepInfo) do
|
||||
local swep = weapons.Get(k)
|
||||
self:AddLine(swep and swep.Category or "Other", swep and swep.PrintName or WepHolster.HL2Weps[k] or k, k)
|
||||
end
|
||||
|
||||
self:SortByColumn(1)
|
||||
end
|
||||
|
||||
Menu.Main.WeaponList:Populate()
|
||||
local credit = pnl:Add("DLabel")
|
||||
credit:Dock(BOTTOM)
|
||||
credit:DockMargin(0, 10, 0, 0)
|
||||
credit:SetHeight(13)
|
||||
credit:SetText("Made by Polyester Duck")
|
||||
local b = pnl:Add("DComboBox")
|
||||
b:Dock(BOTTOM)
|
||||
b:DockMargin(0, 10, 0, 0)
|
||||
b:SetHeight(20)
|
||||
b:SetValue("RESET EVERYTHING TO DEFAULT")
|
||||
b:AddChoice("ARE YOU REALLY SURE?")
|
||||
|
||||
b.OnSelect = function(index, value, data)
|
||||
setEditing("", false)
|
||||
net.Start("resetWholeWHDataToDefault")
|
||||
net.SendToServer()
|
||||
Frame:Close()
|
||||
timer.Simple(0.1, Menu.Main.Init)
|
||||
end
|
||||
|
||||
Frame.OnClose = function()
|
||||
--[[
|
||||
net.Start("reloadWholeWH")
|
||||
net.SendToServer()
|
||||
for pl, weps in pairs(WepHolster.HolsteredWeps) do
|
||||
for cls, wep in pairs(weps) do
|
||||
wep:Remove()
|
||||
WepHolster.HolsteredWeps[pl][cls] = nil
|
||||
end
|
||||
end
|
||||
]]
|
||||
WepHolster.lookingBone = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Menu.Toggle()
|
||||
if LocalPlayer():IsSuperAdmin() then
|
||||
if IsValid(Menu.Main.Frame) then
|
||||
Menu.Main.Frame:Close()
|
||||
else
|
||||
Menu.Main:Init()
|
||||
end
|
||||
else
|
||||
if IsValid(Menu.Main.Frame) then
|
||||
Menu.Main.Frame:Close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
concommand.Add("weapon_holsters_editor", Menu.Toggle)
|
||||
|
||||
local function clientTrickBox(panel, con_name, func_onchange)
|
||||
local con = GetConVar(con_name)
|
||||
|
||||
if not con then
|
||||
return
|
||||
end
|
||||
|
||||
local tickbox = vgui.Create("DCheckBoxLabel", panel)
|
||||
tickbox:SetText(con:GetHelpText() or "Unknown setting.")
|
||||
tickbox:SetValue(con:GetBool())
|
||||
tickbox.con_name = con_name
|
||||
tickbox:SetDark(true)
|
||||
|
||||
function tickbox:OnChange(b)
|
||||
RunConsoleCommand(self.con_name, b and "1" or "0")
|
||||
|
||||
if func_onchange then
|
||||
func_onchange()
|
||||
end
|
||||
end
|
||||
|
||||
function tickbox:Think()
|
||||
if not self.con_name then
|
||||
return
|
||||
end
|
||||
|
||||
local ucon = GetConVar(self.con_name)
|
||||
|
||||
if (ucon:GetBool() or true) ~= self:GetValue() then
|
||||
self:SetChecked(ucon:GetBool())
|
||||
end
|
||||
end
|
||||
|
||||
panel:AddItem(tickbox)
|
||||
end
|
||||
|
||||
local function requestSetting(con, arg)
|
||||
if type(arg) == "boolean" then
|
||||
arg = arg and "1" or "0"
|
||||
end
|
||||
|
||||
net.Start("WepHolsters_Settings")
|
||||
net.WriteString(con)
|
||||
net.WriteString(arg)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
local function adminTrickBox(panel, con_name)
|
||||
local con = GetConVar(con_name)
|
||||
|
||||
if not con then
|
||||
return
|
||||
end
|
||||
|
||||
local tickbox = vgui.Create("DCheckBoxLabel", panel)
|
||||
tickbox:SetText(con:GetHelpText() or "Unknown setting.")
|
||||
tickbox:SetValue(con:GetBool())
|
||||
tickbox.con_name = con_name
|
||||
tickbox:SetDark(true)
|
||||
|
||||
function tickbox:OnChange(b)
|
||||
requestSetting(self.con_name, b and "1" or "0")
|
||||
end
|
||||
|
||||
function tickbox:Think()
|
||||
if not self.con_name then
|
||||
return
|
||||
end
|
||||
|
||||
local ucon = GetConVar(self.con_name)
|
||||
|
||||
if (ucon:GetBool() or true) ~= self:GetValue() then
|
||||
self:SetChecked(ucon:GetBool())
|
||||
end
|
||||
end
|
||||
|
||||
panel:AddItem(tickbox)
|
||||
end
|
||||
|
||||
-- Spawn Menu entry.
|
||||
local function SpawnMenu_Entry(panel)
|
||||
panel:AddControl("Label", {
|
||||
Text = "Client Settings:"
|
||||
})
|
||||
|
||||
clientTrickBox(panel, "cl_weapon_holsters", function()
|
||||
removeHolstWep("")
|
||||
end)
|
||||
|
||||
panel:AddControl("Label", {
|
||||
Text = "Administrator Settings:"
|
||||
})
|
||||
|
||||
adminTrickBox(panel, "sv_weapon_holsters")
|
||||
|
||||
local a = panel:AddControl("Button", {
|
||||
Label = "Open Editor",
|
||||
Command = "weapon_holsters_editor"
|
||||
})
|
||||
|
||||
a:SetSize(0, 50)
|
||||
a:SetEnabled(LocalPlayer():IsSuperAdmin())
|
||||
end
|
||||
|
||||
hook.Add("PopulateToolMenu", "weapon_holsters_editor_spawnmenu", function()
|
||||
spawnmenu.AddToolMenuOption("Options", "Player", "weapon_holsters_editor_spawnmenu_entry", "Weapon Holsters", "", "", SpawnMenu_Entry, {})
|
||||
end)
|
||||
|
||||
hook.Add("HUDPaint", "whBoneIndicator", function()
|
||||
if WepHolster.lookingBone then
|
||||
for k, ply in pairs(player.GetAll()) do
|
||||
lp = LocalPlayer()
|
||||
local bone = ply:LookupBone(WepHolster.lookingBone)
|
||||
local matrix = ply:GetBoneMatrix(bone)
|
||||
|
||||
if not matrix then
|
||||
return
|
||||
end
|
||||
|
||||
local pos = matrix:GetTranslation()
|
||||
pos = pos:ToScreen()
|
||||
local wihe = 6
|
||||
draw.RoundedBox(2, pos.x - wihe / 2, pos.y - wihe / 2, wihe, wihe, Color(0, 0, 0, 200))
|
||||
wihe = wihe - 2
|
||||
draw.RoundedBox(2, pos.x - wihe / 2, pos.y - wihe / 2, wihe, wihe, Color(255, 255, 255, 255))
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -1,207 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
--[[
|
||||
__ ___ __ __
|
||||
/ |/ /__ ____/ /__ / / __ __
|
||||
/ /|_/ / _ `/ _ / -_) / _ \/ // /
|
||||
/_/ /_/\_,_/\_,_/\__/ /_.__/\_, /
|
||||
___ __ /___/ ___ __
|
||||
/ _ \___ / /_ _____ ___ / /____ ____ / _ \__ ______/ /__
|
||||
/ ___/ _ \/ / // / -_|_-</ __/ -_) __/ / // / // / __/ '_/
|
||||
/_/ \___/_/\_, /\__/___/\__/\__/_/ /____/\_,_/\__/_/\_\
|
||||
/___/
|
||||
https://steamcommunity.com/profiles/76561198057599363
|
||||
]]
|
||||
WepHolster.wepInfo = WepHolster.wepInfo or {}
|
||||
util.AddNetworkString("sendWHData")
|
||||
util.AddNetworkString("sendWholeWHData")
|
||||
util.AddNetworkString("applyWepHolsterData")
|
||||
util.AddNetworkString("reloadWH")
|
||||
util.AddNetworkString("deleteWHData")
|
||||
util.AddNetworkString("reloadWholeWH")
|
||||
util.AddNetworkString("resetWHDataToDefault")
|
||||
util.AddNetworkString("resetWholeWHDataToDefault")
|
||||
util.AddNetworkString("WepHolsters_Settings")
|
||||
|
||||
function WepHolster.setWHData(wep, tbl)
|
||||
file.Write("wepholster/" .. wep .. ".txt", util.TableToJSON(tbl, true))
|
||||
WepHolster.wepInfo[wep] = tbl
|
||||
end
|
||||
|
||||
--[[
|
||||
local function InitOfInit()
|
||||
for k, v in pairs(WepHolster.defData) do
|
||||
WepHolster.setWHData(k, v)
|
||||
end
|
||||
end
|
||||
hook.Add("Initialize", "wepHolstersInit", function()
|
||||
if file.IsDir("wepholster", "DATA") then
|
||||
local files, dirs = file.Find("wepholster/*.txt", "DATA")
|
||||
|
||||
for k, v in pairs(files) do
|
||||
WepHolster.wepInfo[string.sub(v, 1, #v - 4)] = util.JSONToTable(file.Read("wepholster/" .. v))
|
||||
end
|
||||
else
|
||||
file.CreateDir("wepholster", "DATA")
|
||||
InitOfInit()
|
||||
end
|
||||
end)
|
||||
]]
|
||||
local function checkValidSWEP()
|
||||
if not file.IsDir("wepholster", "DATA") then
|
||||
file.CreateDir("wepholster", "DATA")
|
||||
|
||||
for k, v in pairs(WepHolster.defData) do
|
||||
file.Write("wepholster/" .. k .. ".txt", util.TableToJSON(v, true))
|
||||
end
|
||||
end
|
||||
|
||||
local files, dirs = file.Find("wepholster/*.txt", "DATA")
|
||||
|
||||
for k, v in pairs(files) do
|
||||
local wepclass = string.sub(v, 1, #v - 4)
|
||||
local swep = weapons.Get(wepclass)
|
||||
|
||||
if swep or WepHolster.HL2Weps[wepclass] or not WepHolster.defData[wepclass] then
|
||||
WepHolster.wepInfo[wepclass] = util.JSONToTable(file.Read("wepholster/" .. v))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("Initialize", "Initialize Weapon Holsters", checkValidSWEP)
|
||||
|
||||
function WepHolster.sendWholeWHData(ply)
|
||||
net.Start("sendWholeWHData")
|
||||
net.WriteTable(WepHolster.wepInfo)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "sendWholeWHData", WepHolster.sendWholeWHData)
|
||||
|
||||
net.Receive("resetWholeWHDataToDefault", function(len, ply)
|
||||
if ply:IsSuperAdmin() then
|
||||
local files, dirs = file.Find("wepholster/*", "DATA")
|
||||
|
||||
for k, v in pairs(files) do
|
||||
file.Delete("wepholster/" .. v)
|
||||
--print(v)
|
||||
end
|
||||
file.Delete("wepholster")
|
||||
WepHolster.wepInfo = {}
|
||||
checkValidSWEP()
|
||||
net.Start("sendWholeWHData")
|
||||
net.WriteTable(WepHolster.wepInfo)
|
||||
net.Broadcast()
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("resetWHDataToDefault", function(len, ply)
|
||||
if ply:IsSuperAdmin() then
|
||||
local class = net.ReadString()
|
||||
|
||||
if WepHolster.defData[class] then
|
||||
net.Start("sendWHData")
|
||||
net.WriteString(class)
|
||||
net.WriteTable(WepHolster.defData[class])
|
||||
net.Send(ply)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("reloadWholeWH", function(len, ply)
|
||||
WepHolster.sendWholeWHData(ply)
|
||||
end)
|
||||
|
||||
net.Receive("applyWepHolsterData", function(len, ply)
|
||||
if ply:IsSuperAdmin() then
|
||||
local class = net.ReadString()
|
||||
local weptbl = net.ReadTable()
|
||||
weptbl.notSavedYet = nil
|
||||
weptbl.isEditing = nil
|
||||
WepHolster.setWHData(class, weptbl)
|
||||
net.Start("sendWHData")
|
||||
net.WriteString(class)
|
||||
net.WriteTable(WepHolster.wepInfo[class])
|
||||
net.Broadcast()
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("reloadWH", function(len, ply)
|
||||
if ply:IsSuperAdmin() then
|
||||
local class = net.ReadString()
|
||||
|
||||
if WepHolster.wepInfo[class] then
|
||||
net.Start("sendWHData")
|
||||
net.WriteString(class)
|
||||
net.WriteTable(WepHolster.wepInfo[class])
|
||||
net.Send(ply)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
net.Receive("deleteWHData", function(len, ply)
|
||||
if ply:IsSuperAdmin() then
|
||||
local class = net.ReadString()
|
||||
WepHolster.wepInfo[class] = nil
|
||||
|
||||
if file.Exists("wepholster/" .. class .. ".txt", "DATA") then
|
||||
file.Delete("wepholster/" .. class .. ".txt")
|
||||
end
|
||||
|
||||
net.Start("sendWHData")
|
||||
net.WriteString(class)
|
||||
net.WriteTable({})
|
||||
net.Broadcast()
|
||||
end
|
||||
end)
|
||||
|
||||
function WepHolster.CanEditSetting(ply, con, var)
|
||||
if not ply:IsSuperAdmin() then
|
||||
ply:PrintMessage(HUD_PRINTCENTER, "You don't have access to server settings.")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if not ConVarExists(con) then
|
||||
return
|
||||
end
|
||||
|
||||
print("[Weapon Holsters] " .. ply:Nick() .. " (" .. ply:SteamID() .. ") changed " .. con .. " to " .. tostring(var))
|
||||
con = GetConVar(con)
|
||||
con:SetString(var)
|
||||
end
|
||||
|
||||
local whitelist = {}
|
||||
whitelist["sv_weapon_holsters"] = true
|
||||
|
||||
net.Receive("WepHolsters_Settings", function(len, ply)
|
||||
if not ply then
|
||||
return
|
||||
end
|
||||
|
||||
if (ply.WH_LAST or 0) > SysTime() then
|
||||
return
|
||||
end
|
||||
|
||||
ply.WH_LAST = SysTime() + 0.2
|
||||
local con = net.ReadString()
|
||||
local arg = net.ReadString()
|
||||
|
||||
if not con then
|
||||
return
|
||||
end
|
||||
|
||||
if not whitelist[con] then
|
||||
return
|
||||
end
|
||||
|
||||
WepHolster.CanEditSetting(ply, con, arg or nil)
|
||||
end)
|
||||
@@ -1,29 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
if not ConVarExists("sv_weapon_holsters") then
|
||||
CreateConVar("sv_weapon_holsters",1, { FCVAR_REPLICATED, FCVAR_ARCHIVE,FCVAR_SERVER_CAN_EXECUTE }, "Enable Weapon Holsters (server side)" )
|
||||
end
|
||||
WepHolster = WepHolster or {}
|
||||
WepHolster.HL2Weps = {
|
||||
["weapon_pistol"] = "Pistol",
|
||||
["weapon_357"] = "357",
|
||||
["weapon_frag"] = "Frag Grenade",
|
||||
["weapon_slam"] = "SLAM",
|
||||
["weapon_crowbar"] = "Crowbar",
|
||||
["weapon_stunstick"] = "Stunstick",
|
||||
["weapon_shotgun"] = "Shotgun",
|
||||
["weapon_rpg"] = "RPG Launcher",
|
||||
["weapon_smg1"] = "SMG",
|
||||
["weapon_ar2"] = "AR2",
|
||||
["weapon_crossbow"] = "Crossbow",
|
||||
["weapon_physcannon"] = "Gravity Gun",
|
||||
["weapon_physgun"] = "Physics Gun"
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local domain = CreateConVar("xeon_dev", 0, bit.bor(FCVAR_REPLICATED, FCVAR_UNREGISTERED, FCVAR_UNLOGGED, FCVAR_DONTRECORD), nil, 0, 1):GetInt() == 1 and "http://gmod.sneed" or "https://xeon.network"
|
||||
|
||||
local XEON_AUTH
|
||||
net.Receive("XEON.Auth", function(len)
|
||||
if net.ReadBool() then
|
||||
if IsValid(XEON_AUTH) then
|
||||
XEON_AUTH:Linked()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
net.Start("XEON.Auth")
|
||||
net.SendToServer()
|
||||
|
||||
if IsValid(XEON_AUTH) then return end
|
||||
|
||||
XEON_AUTH = vgui.Create("DFrame")
|
||||
XEON_AUTH:SetSize(ScrW() * .8, ScrH() * .8)
|
||||
XEON_AUTH:SetTitle("XEON DRM by Billy - Server Linking")
|
||||
XEON_AUTH:Center()
|
||||
XEON_AUTH:MakePopup()
|
||||
XEON_AUTH:SetRenderInScreenshots(false)
|
||||
|
||||
local html = vgui.Create("DHTML", XEON_AUTH)
|
||||
html:Dock(FILL)
|
||||
html:OpenURL(domain .. "/link/" .. (LocalPlayer():IsSuperAdmin() and "1" or "0"))
|
||||
html:AddFunction("XEON", "Auth", function(token_bytes)
|
||||
local token_bytes = string.Explode(", ", token_bytes:sub(2, -2), false)
|
||||
local token = {}
|
||||
for i = 1, 32 do
|
||||
token[i] = string.char(token_bytes[i])
|
||||
end
|
||||
token = table.concat(token)
|
||||
|
||||
net.Start("XEON.Auth")
|
||||
net.WriteData(token, 32)
|
||||
net.SendToServer()
|
||||
end)
|
||||
html:AddFunction("XEON", "ReloadMap", function()
|
||||
net.Start("XEON.ReloadMap")
|
||||
net.SendToServer()
|
||||
|
||||
if IsValid(XEON_AUTH) then
|
||||
XEON_AUTH:Close()
|
||||
XEON_AUTH = nil
|
||||
end
|
||||
end)
|
||||
local RunJS = html.RunJavascript
|
||||
function XEON_AUTH:Linked()
|
||||
RunJS(html, "LINKED()")
|
||||
end
|
||||
html.QueueJavascript = nil
|
||||
html.RunJavascript = nil
|
||||
end)
|
||||
|
||||
local function openErrors(errors)
|
||||
if IsValid(XEON_ERRORS) then
|
||||
XEON_ERRORS:Update(errors)
|
||||
return
|
||||
end
|
||||
|
||||
XEON_ERRORS = vgui.Create("DFrame")
|
||||
XEON_ERRORS:SetSize(ScrW() * .8, ScrH() * .8)
|
||||
XEON_ERRORS:SetTitle("XEON DRM by Billy - Error!")
|
||||
XEON_ERRORS:Center()
|
||||
XEON_ERRORS:MakePopup()
|
||||
|
||||
local html = vgui.Create("DHTML", XEON_ERRORS)
|
||||
html:Dock(FILL)
|
||||
html:OpenURL(domain .. "/errors")
|
||||
html:AddFunction("XEON", "ScriptSupport", function()
|
||||
gui.OpenURL("https://support.billy.enterprises")
|
||||
end)
|
||||
|
||||
function XEON_ERRORS:Update(errors)
|
||||
print("XEON Errors: " .. #errors)
|
||||
PrintTable(errors)
|
||||
|
||||
html:QueueJavascript("ShowNetworkedErrors(" .. util.TableToJSON(errors) .. ")")
|
||||
end
|
||||
function html:OnDocumentReady()
|
||||
XEON_ERRORS:Update(errors)
|
||||
self.OnDocumentReady = nil
|
||||
end
|
||||
XEON_ERRORS:Update(errors)
|
||||
|
||||
sound.PlayURL("https://xeon.network/static/media/oof.mp3", "", function() end)
|
||||
end
|
||||
|
||||
net.Receive("XEON.Error", function()
|
||||
local errors = {}
|
||||
for i = 1, net.ReadUInt(16) do
|
||||
errors[i] = net.ReadString()
|
||||
end
|
||||
openErrors(errors)
|
||||
end)
|
||||
|
||||
hook.Add("InitPostEntity", "XEON.Error", function()
|
||||
timer.Simple(2, function()
|
||||
net.Start("XEON.Error")
|
||||
net.SendToServer()
|
||||
end)
|
||||
end)
|
||||
@@ -1,221 +0,0 @@
|
||||
--[[
|
||||
| This file was obtained through the combined efforts
|
||||
| of Madbluntz & Plymouth Antiquarian Society.
|
||||
|
|
||||
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||||
| Maloy, DrPepper10 @ RIP, Atle!
|
||||
|
|
||||
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||||
--]]
|
||||
|
||||
local function JohnLua()
|
||||
local quotes = {"In the beginning was the Statement, and the Statement was with Lua, and the Statement was Lua.","Greater love has no one than Lua, that the compiler lay down his life for our code.","Do not think that I will debug with the Stack Trace; For had ye believed the Compiler Errors, ye would have believed Me, for he wrote of Lua; But if ye believe not his Stack Traces, how shall ye believe My code?","Even though my types remain weak, I fear no error, for Lua is with me.","Lua is the chain that ties our health and bodies together.","Rip scripts, you must not. Let the Lua flow straight from your fingers.","Variables are changed by your example, not by your opinion.","The meaning of Lua is to give Lua a meaning.","Doubt has killed more scripts than errors will.","The hottest places in Hell are for those who never declare local variables.","Crashes and overflows may break my bones; But pcalls will never harm me.","Wise men code because they have something to make; Fools because they want to make something.","Lua is the joy of the good, the wonder of the wise, the amazement of the Gods.","You dump functions. You dump memory regions and crash reports that look like hell. You dump unused variables and collected garbage and tables. But people?","Whoever believes in the Trace is not condemned, but whoever does not believe stands condemned already because they have not believed in the name of John Lua's one and only Function.","It is written: \"Lua shall not live on interpreter alone, but on every instruction that comes from the mouth of Mike Pall.\"","Because of Mike Pall's great love we are not consumed, for his Traces never fail. They are new every loop; great is your Just-In-Time compilation.","I give them eternal life, and they shall never perish; no one will collect them out of my heap. My Allocator, who has given them to me, is greater than all; no one can Mark-and-Sweep them out of my Garbage-Collectors hand. I and the Collector are one."}
|
||||
return "\"" .. quotes[math.random(1, #quotes)] .. "\" - John Lua\n"
|
||||
end
|
||||
|
||||
do
|
||||
local hookId = {}
|
||||
for i = 1, math.random(10, 20) do
|
||||
hookId[i] = string.char(math.random(0, 1) == 0 and math.random(97, 122) or math.random(65, 90))
|
||||
end
|
||||
hookId = table.concat(hookId)
|
||||
|
||||
local ipConVar = GetConVar("ip")
|
||||
local function getIPAddress()
|
||||
local ip = game.GetIPAddress()
|
||||
if ip and #ip > 0 and not ip:StartWith("0.0.0.0") and not ip:StartWith("localhost") and not ip:StartWith("loopback") then
|
||||
return true
|
||||
end
|
||||
|
||||
ip = ipConVar:GetString()
|
||||
if ip and #ip > 0 and not ip:StartWith("0.0.0.0") and not ip:StartWith("localhost") and not ip:StartWith("loopback") then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function init()
|
||||
if not getIPAddress() then return end
|
||||
|
||||
hook.Remove("GetGameDescription", hookId)
|
||||
hook.Remove("PlayerConnect", hookId)
|
||||
|
||||
MsgC(Color(0,255,255), "\n[ Y88b d88P 8888888888 .d88888b. 888b 888 ]\n[ Y88b d88P 888 d88P\" \"Y88b 8888b 888 ]\n[ Y88o88P 888 888 888 88888b 888 ]\n[ Y888P 8888888 888 888 888Y88b 888 ]\n[ d888b 888 888 888 888 Y88b888 ]\n[ d88888b 888 888 888 888 Y88888 ]\n[ d88P Y88b 888 Y88b. .d88P 888 Y8888 ]\n[ d88P Y88b 8888888888 \"Y88888P\" 888 Y888 ]\n\n")
|
||||
MsgC(Color(0,255,0), "DRM (digital rights management) for Billy's scripts\n\n")
|
||||
MsgC(Color(255,255,255), JohnLua())
|
||||
|
||||
MsgC(Color(255,0,255), "\nSupport\n")
|
||||
MsgC(Color(255,255,255), "https://support.billy.enterprises\n")
|
||||
|
||||
MsgC(Color(255,0,255), "\nServer Status\n")
|
||||
MsgC(Color(255,255,255), "https://xeon.network\n")
|
||||
|
||||
MsgC(Color(255,0,255), "\nServer/Licenses Panel\n")
|
||||
MsgC(Color(255,255,255), "https://xeon.network/servers\n")
|
||||
|
||||
if system.IsOSX() then
|
||||
print()
|
||||
for i = 1, 10 do
|
||||
MsgC(Color(255,0,0), "XEON DRM is not compatible with your operating system. You are unable to use Billy's scripts on this computer.\n")
|
||||
end
|
||||
print()
|
||||
hook.Run("XEON.Error", "XEON DRM is not compatible with your operating system. You are unable to use Billy's scripts on this computer.")
|
||||
return
|
||||
end
|
||||
|
||||
local platform = system.IsWindows() and (jit.arch == "x86" and "win32" or "win64") or (jit.arch == "x86" and "linux" or "linux64")
|
||||
if not file.Exists("bin/gmsv_xeon_drm_" .. platform .. ".dll", "LUA") then
|
||||
print()
|
||||
for i = 1, 10 do
|
||||
MsgC(Color(255,0,0), "Your server is running ", Color(255,255,255), (system.IsWindows() and "Windows" or "Linux"), Color(255,0,0), " on the ", Color(255,255,255), (jit.arch == "x86" and "main" or "x86-64"), Color(255,0,0), " branch\n")
|
||||
MsgC(Color(255,0,0), "Couldn't find ", Color(255,255,255), "garrysmod/lua/bin/gmsv_xeon_drm_" .. platform .. ".dll", Color(255,0,0), " on your server!\n")
|
||||
MsgC(Color(255,0,0), "You didn't install the XEON DRM binary module! This is required to use Billy's scripts.\n")
|
||||
MsgC(Color(255,0,0), "Please download it here: ", Color(255,255,255), "https://xeon.network/download\n\n")
|
||||
end
|
||||
print()
|
||||
hook.Run("XEON.Error", "Your server is running " .. (system.IsWindows() and "Windows" or "Linux") .. " on the " .. (jit.arch == "x86" and "main" or "x86-64") .. " branch")
|
||||
hook.Run("XEON.Error", "Couldn't find garrysmod/lua/bin/gmsv_xeon_drm_" .. platform .. ".dll on your server!")
|
||||
hook.Run("XEON.Error", "You didn't install the XEON DRM binary module! This is required to use Billy's scripts.")
|
||||
hook.Run("XEON.Error", "Please download it here: https://xeon.network/download")
|
||||
return
|
||||
end
|
||||
|
||||
print()
|
||||
|
||||
require("xeon_drm")
|
||||
|
||||
if not XEON then
|
||||
print()
|
||||
for i = 1, 10 do
|
||||
MsgC(Color(255,0,0), "XEON DRM failed to load! Please read your server's console for more information.\n")
|
||||
end
|
||||
print()
|
||||
hook.Run("XEON.Error", "XEON DRM failed to load! Please read your server's console for more information.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("GetGameDescription", hookId, init)
|
||||
hook.Add("PlayerConnect", hookId, init)
|
||||
end
|
||||
|
||||
do
|
||||
util.AddNetworkString("XEON.Error")
|
||||
|
||||
local cached, cachedTick
|
||||
local function getSuperAdmins()
|
||||
if cached and cachedTick == engine.TickCount() then
|
||||
return cached
|
||||
end
|
||||
|
||||
local filter = RecipientFilter()
|
||||
for _, ply in ipairs(player.GetHumans()) do
|
||||
if ply:IsSuperAdmin() then
|
||||
filter:AddPlayer(ply)
|
||||
end
|
||||
end
|
||||
|
||||
cached, cachedTick = filter, engine.TickCount()
|
||||
|
||||
return filter
|
||||
end
|
||||
|
||||
local errors = setmetatable({}, (function()
|
||||
local meta = {}
|
||||
meta.__index = meta
|
||||
|
||||
function meta:add(error)
|
||||
table.insert(self, error)
|
||||
end
|
||||
|
||||
function meta:transmit(ply)
|
||||
if #self == 0 then return end
|
||||
if player.GetCount() == 0 then return end
|
||||
if ply ~= nil and (not IsValid(ply) or not ply:IsSuperAdmin()) then return end
|
||||
net.Start("XEON.Error")
|
||||
net.WriteUInt(#self, 16)
|
||||
for _, error in ipairs(self) do
|
||||
net.WriteString(error)
|
||||
end
|
||||
net.Send(ply == nil and getSuperAdmins() or ply)
|
||||
end
|
||||
|
||||
return meta
|
||||
end)())
|
||||
|
||||
hook.Add("XEON.Error", "XEON.Error", function(error)
|
||||
errors:add(error)
|
||||
errors:transmit()
|
||||
end)
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "XEON.Error", function(ply)
|
||||
timer.Simple(5, function()
|
||||
if not IsValid(ply) or not ply:IsSuperAdmin() then return end
|
||||
errors:transmit(ply)
|
||||
end)
|
||||
end)
|
||||
|
||||
net.Receive("XEON.Error", function(_, ply)
|
||||
if ply:IsSuperAdmin() then
|
||||
errors:transmit(ply)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
do
|
||||
local linksRequired = {}
|
||||
|
||||
local function startAuth(ply)
|
||||
local timerName = "XEON.Auth:" .. ply:SteamID64()
|
||||
timer.Create(timerName, 1, 0, function()
|
||||
if IsValid(ply) then
|
||||
net.Start("XEON.Auth")
|
||||
net.WriteBool(false)
|
||||
net.Send(ply)
|
||||
else
|
||||
timer.Remove(timerName)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local function authSpawn(ply)
|
||||
if not linksRequired[ply:SteamID64()] then return end
|
||||
startAuth(ply)
|
||||
end
|
||||
|
||||
net.Receive("XEON.Auth", function(len, ply)
|
||||
if not ply:IsSuperAdmin() then return end
|
||||
local steamid64 = ply:SteamID64()
|
||||
if not linksRequired[steamid64] then return end
|
||||
if len == 0 then
|
||||
timer.Remove("XEON.Auth:" .. ply:SteamID64())
|
||||
else
|
||||
linksRequired[steamid64] = nil
|
||||
if table.IsEmpty(linksRequired) then
|
||||
hook.Remove("PlayerSpawn", "XEON.Auth")
|
||||
end
|
||||
XEON:LinkServer(ply, net.ReadData(32))
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("XEON.Auth", "XEON.Auth", function(steamid64)
|
||||
local ply = player.GetBySteamID64(steamid64)
|
||||
linksRequired[steamid64] = true
|
||||
if IsValid(ply) then
|
||||
startAuth(ply)
|
||||
else
|
||||
hook.Add("PlayerSpawn", "XEON.Auth", authSpawn)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
do
|
||||
util.AddNetworkString("XEON.ReloadMap")
|
||||
net.Receive("XEON.ReloadMap", function(_, ply)
|
||||
if not ply:IsSuperAdmin() then return end
|
||||
RunConsoleCommand("changelevel", game.GetMap())
|
||||
end)
|
||||
end
|
||||
|
||||
MsgC(Color(0,255,255), "XEON DRM is waiting for your server to get an IP address...\n")
|
||||
@@ -1,2 +0,0 @@
|
||||
<EFBFBD>Constraints<74><73>
|
||||
forcelimit<EFBFBD>
|
||||