mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 13:23:46 +03:00
Upload
This commit is contained in:
158
addons/sam/lua/autorun/sam.lua
Normal file
158
addons/sam/lua/autorun/sam.lua
Normal file
@@ -0,0 +1,158 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local version = tonumber("148") or 148
|
||||
|
||||
sam = {}
|
||||
sam.config = {}
|
||||
sam.language = {}
|
||||
sam.player = {}
|
||||
sam.ranks = {}
|
||||
sam.permissions = {}
|
||||
sam.author = "Srlion"
|
||||
sam.version = version
|
||||
|
||||
function sam.print(...)
|
||||
MsgC(
|
||||
Color(236, 240, 241), "(",
|
||||
Color(244, 67, 54), "SAM",
|
||||
Color(236, 240, 241), ") ",
|
||||
Color(236, 240, 241), ...
|
||||
) Msg("\n")
|
||||
end
|
||||
|
||||
local FAILED = false
|
||||
do
|
||||
local types = {
|
||||
sv_ = SERVER and include or function() end,
|
||||
cl_ = SERVER and AddCSLuaFile or include,
|
||||
sh_ = function(name)
|
||||
if SERVER then
|
||||
AddCSLuaFile(name)
|
||||
end
|
||||
return include(name)
|
||||
end
|
||||
}
|
||||
|
||||
sam.load_file = function(name, type)
|
||||
if FAILED then return end
|
||||
|
||||
if type and not type:EndsWith("_") then
|
||||
type = type .. "_"
|
||||
end
|
||||
|
||||
local func = types[type] or types[name:GetFileFromFilename():sub(1, 3)] or types["sh_"]
|
||||
if func then
|
||||
local rets = {func(name)}
|
||||
if rets[1] == false then
|
||||
FAILED = true
|
||||
sam.print("Failed to load!")
|
||||
end
|
||||
return unpack(rets)
|
||||
end
|
||||
end
|
||||
end
|
||||
local load_file = sam.load_file
|
||||
|
||||
sam.print("Loading...")
|
||||
|
||||
load_file("sam/libs/sh_types.lua")
|
||||
load_file("sam/libs/sh_pon.lua")
|
||||
load_file("sam/libs/sh_mp.lua")
|
||||
load_file("sam/libs/sh_netstream.lua")
|
||||
load_file("sam/libs/sh_async_netstream.lua")
|
||||
load_file("sam/libs/sh_globals.lua")
|
||||
load_file("sam/libs/sql/sv_init.lua")
|
||||
sam.Promise = load_file("sam/libs/sh_promises.lua")
|
||||
|
||||
load_file("sam/sh_colors.lua")
|
||||
|
||||
load_file("sam/sh_util.lua")
|
||||
load_file("sam/sh_lang.lua")
|
||||
load_file("sam/sv_sql.lua")
|
||||
load_file("sam/sh_permissions.lua")
|
||||
|
||||
load_file("sam/ranks/sh_ranks.lua")
|
||||
load_file("sam/ranks/sv_ranks.lua")
|
||||
|
||||
load_file("sam/config/sh_config.lua")
|
||||
load_file("sam/config/sv_config.lua")
|
||||
load_file("sam/config/cl_config.lua")
|
||||
|
||||
load_file("sam/player/sh_player.lua")
|
||||
load_file("sam/player/sh_nw_vars.lua")
|
||||
load_file("sam/player/sv_player.lua")
|
||||
load_file("sam/player/cl_player.lua")
|
||||
load_file("sam/player/sv_ranks.lua")
|
||||
load_file("sam/player/sv_auth.lua")
|
||||
load_file("sam/player/sv_bans.lua")
|
||||
|
||||
load_file("sam/command/sh_command.lua")
|
||||
load_file("sam/command/sv_command.lua")
|
||||
load_file("sam/command/cl_command.lua")
|
||||
|
||||
for _, f in ipairs(file.Find("sam/command/arguments/*.lua", "LUA")) do
|
||||
load_file("sam/command/arguments/" .. f, "sh")
|
||||
end
|
||||
|
||||
load_file("sam/sh_restrictions.lua")
|
||||
|
||||
load_file("sam/menu/sh_init.lua")
|
||||
load_file("sam/menu/cl_init.lua")
|
||||
|
||||
load_file("sam/sh_motd.lua")
|
||||
|
||||
local modules = file.Find("sam/modules/*.lua", "LUA")
|
||||
for _, module in ipairs(modules) do
|
||||
load_file("sam/modules/" .. module)
|
||||
end
|
||||
|
||||
load_file("sam/reports/cl_reports.lua")
|
||||
load_file("sam/reports/sv_reports.lua")
|
||||
|
||||
do
|
||||
if SERVER then
|
||||
hook.Add("SAM.LoadedConfig", "SAM.AdvertsMain", function(config)
|
||||
if not config.Adverts then
|
||||
sam.config.set("Adverts", {})
|
||||
end
|
||||
end)
|
||||
end
|
||||
load_file("sam/cl_adverts.lua")
|
||||
end
|
||||
|
||||
if not FAILED then
|
||||
sam.print("Loaded!")
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local path = "sam/importers/"
|
||||
|
||||
concommand.Add("sam_import", function(ply, _, args)
|
||||
if IsValid(ply) then return end
|
||||
|
||||
local admin_mod = args[1]
|
||||
if not admin_mod then
|
||||
sam.print("You need to provide an admin mod to import!")
|
||||
return
|
||||
end
|
||||
|
||||
if not file.Exists(path .. admin_mod, "LUA") then
|
||||
sam.print("There is no importer for '" .. admin_mod .. "'")
|
||||
return
|
||||
end
|
||||
|
||||
CompileFile(path .. admin_mod .. "/main.lua")()
|
||||
end, nil, nil, FCVAR_PROTECTED)
|
||||
end
|
||||
|
||||
SAM_LOADED = true
|
||||
136
addons/sam/lua/sam/cl_adverts.lua
Normal file
136
addons/sam/lua/sam/cl_adverts.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
|
||||
local times = {}
|
||||
|
||||
local entry_OnValueChange = function(s)
|
||||
s:SetTall(s:GetNumLines() * (sam.SUI.Scale(16) --[[font size]] + 1) + 1 + 2)
|
||||
end
|
||||
|
||||
local entry_OnEnter = function(s)
|
||||
local ads = config.get("Adverts")
|
||||
local txt = s:GetText()
|
||||
if txt == "" then
|
||||
s:Remove()
|
||||
if s.i then
|
||||
table.remove(ads, s.i)
|
||||
end
|
||||
else
|
||||
if txt == s.ad then return end
|
||||
ads[s.i] = txt
|
||||
s.ad = txt
|
||||
end
|
||||
config.set("Adverts", ads, true)
|
||||
end
|
||||
|
||||
local entry_OnKeyCodeTyped = function(s, code)
|
||||
if code == KEY_ENTER then
|
||||
s:old_OnKeyCodeTyped(code)
|
||||
return true
|
||||
else
|
||||
return s:old_OnKeyCodeTyped(code)
|
||||
end
|
||||
end
|
||||
|
||||
config.add_menu_setting("Adverts", function(body)
|
||||
local adverts_body
|
||||
|
||||
local adverts = body:Add("SAM.LabelPanel")
|
||||
adverts:Dock(TOP)
|
||||
adverts:DockMargin(8, 6, 8, 0)
|
||||
adverts:SetLabel("Adverts\n- Random adverts print every 60 seconds\n- Timed adverts can be done like this: {1m} This advert prints every 1 minute")
|
||||
|
||||
local add_advert = adverts:Add("SAM.Button")
|
||||
add_advert:SetText("+")
|
||||
add_advert:SetSize(25, 25)
|
||||
|
||||
local zpos = 0
|
||||
local add_func = function(ad, ad_i)
|
||||
zpos = zpos + 1
|
||||
|
||||
local entry = adverts_body:Add("SAM.TextEntry")
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetMultiline(true)
|
||||
entry:SetNoBar(true)
|
||||
entry:Dock(TOP)
|
||||
entry:DockMargin(8, 6, 8, 0)
|
||||
entry:SetZPos(zpos)
|
||||
entry.ad = ad
|
||||
entry.no_scale = true
|
||||
|
||||
if not sam.ispanel(ad) then
|
||||
entry.i = ad_i
|
||||
entry:SetValue(ad)
|
||||
else
|
||||
entry.i = #config.get("Adverts") + 1
|
||||
end
|
||||
|
||||
entry.OnValueChange = entry_OnValueChange
|
||||
entry.OnEnter = entry_OnEnter
|
||||
entry.old_OnKeyCodeTyped = entry.OnKeyCodeTyped
|
||||
entry.OnKeyCodeTyped = entry_OnKeyCodeTyped
|
||||
end
|
||||
add_advert:On("DoClick", add_func)
|
||||
|
||||
adverts_body = body:Add("Panel")
|
||||
adverts_body:Dock(TOP)
|
||||
|
||||
function adverts_body:PerformLayout(w, h)
|
||||
for k, v in ipairs(self:GetChildren()) do
|
||||
entry_OnValueChange(v)
|
||||
end
|
||||
self:SizeToChildren(false, true)
|
||||
end
|
||||
|
||||
sam.config.hook({"Adverts"}, function()
|
||||
if not IsValid(adverts_body) then return end
|
||||
adverts_body:Clear()
|
||||
|
||||
for k, v in ipairs(config.get("Adverts")) do
|
||||
add_func(v, k)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
local random = {}
|
||||
|
||||
timer.Create("SAM.Advert.RandomAdverts", 60, 0, function()
|
||||
local ad = random[math.random(1, #random)]
|
||||
if not ad then return end
|
||||
sam.player.send_message(nil, ad)
|
||||
end)
|
||||
|
||||
sam.config.hook({"Adverts"}, function()
|
||||
for i = #times, 1, -1 do
|
||||
times[i] = nil
|
||||
timer.Remove("SAM.Adverts." .. i)
|
||||
end
|
||||
|
||||
random = {}
|
||||
for k, v in ipairs(config.get("Adverts")) do
|
||||
if v:sub(1, 1) == "{" then
|
||||
local time
|
||||
time, v = v:match("(%b{}) *(.*)")
|
||||
time = sam.parse_length(time)
|
||||
if time then
|
||||
timer.Create("SAM.Adverts." .. table.insert(times, true), time * 60, 0, function()
|
||||
sam.player.send_message(nil, v)
|
||||
end)
|
||||
end
|
||||
else
|
||||
table.insert(random, v)
|
||||
end
|
||||
end
|
||||
end)
|
||||
58
addons/sam/lua/sam/command/arguments/dropdown.lua
Normal file
58
addons/sam/lua/sam/command/arguments/dropdown.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
command.new_argument("dropdown")
|
||||
:OnExecute(function(arg, input, ply, _, result)
|
||||
if not arg.options or table.Empty(arg.options) then
|
||||
ply:sam_send_message("no data", {S = "dropdown", S_2 = input})
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(result, input)
|
||||
end)
|
||||
:Menu(function(set_result, body, buttons, arg)
|
||||
local default = arg.hint or "select"
|
||||
|
||||
local cbo = buttons:Add("SAM.ComboBox")
|
||||
cbo:SetValue(default)
|
||||
cbo:SetTall(25)
|
||||
|
||||
function cbo:OnSelect(_, value)
|
||||
set_result(value)
|
||||
default = value
|
||||
end
|
||||
|
||||
function cbo:DoClick()
|
||||
if self:IsMenuOpen() then
|
||||
return self:CloseMenu()
|
||||
end
|
||||
|
||||
self:Clear()
|
||||
self:SetValue(default)
|
||||
|
||||
if not arg.options then
|
||||
LocalPlayer():sam_send_message("dropdown has no options data")
|
||||
return
|
||||
end
|
||||
|
||||
for k, v in pairs(arg.options) do
|
||||
self:AddChoice(v)
|
||||
end
|
||||
|
||||
self:OpenMenu()
|
||||
end
|
||||
|
||||
return cbo
|
||||
end)
|
||||
:End()
|
||||
72
addons/sam/lua/sam/command/arguments/length.lua
Normal file
72
addons/sam/lua/sam/command/arguments/length.lua
Normal file
@@ -0,0 +1,72 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
local get_length = function(arg, input)
|
||||
if (input == "" or input == nil) and arg.optional then
|
||||
if arg.default ~= nil then
|
||||
return arg.default
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
return sam.parse_length(input)
|
||||
end
|
||||
|
||||
command.new_argument("length")
|
||||
:OnExecute(function(arg, input, ply, _, result, i)
|
||||
local length = get_length(arg, input)
|
||||
if length == "" then
|
||||
result[i] = nil
|
||||
elseif not length then
|
||||
ply:sam_send_message("invalid", {
|
||||
S = "length", S_2 = input
|
||||
})
|
||||
return false
|
||||
else
|
||||
if arg.min and length ~= 0 then
|
||||
length = math.max(length, arg.min)
|
||||
end
|
||||
|
||||
if arg.max then
|
||||
if length == 0 then
|
||||
length = arg.max
|
||||
else
|
||||
length = math.min(length, arg.max)
|
||||
end
|
||||
end
|
||||
|
||||
result[i] = length
|
||||
end
|
||||
end)
|
||||
|
||||
:Menu(function(set_result, body, buttons, argument)
|
||||
local length_input = buttons:Add("SAM.TextEntry")
|
||||
length_input:SetTall(25)
|
||||
|
||||
length_input:SetCheck(function(new_limit)
|
||||
new_limit = get_length(argument, new_limit) or nil
|
||||
set_result(new_limit)
|
||||
return new_limit or false
|
||||
end)
|
||||
|
||||
local hint = argument.hint or "length"
|
||||
if argument.default then
|
||||
hint = hint .. " = " .. tostring(argument.default)
|
||||
end
|
||||
|
||||
length_input:SetPlaceholder(hint)
|
||||
return length_input
|
||||
end)
|
||||
:End()
|
||||
60
addons/sam/lua/sam/command/arguments/map.lua
Normal file
60
addons/sam/lua/sam/command/arguments/map.lua
Normal file
@@ -0,0 +1,60 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
command.new_argument("map")
|
||||
:OnExecute(function(argument, input, ply, _, result)
|
||||
local map_name = sam.is_valid_map(input)
|
||||
if not map_name and not (argument.optional and input == "None") then
|
||||
ply:sam_send_message("invalid", {
|
||||
S = "map", S_2 = input
|
||||
})
|
||||
return false
|
||||
end
|
||||
|
||||
table.insert(result, map_name)
|
||||
end)
|
||||
|
||||
:Menu(function(set_result, _, buttons, argument)
|
||||
local maps = buttons:Add("SAM.ComboBox")
|
||||
maps:SetTall(25)
|
||||
|
||||
if argument.optional then
|
||||
maps:AddChoice("None", nil, true)
|
||||
end
|
||||
|
||||
for _, map_name in ipairs(sam.get_global("Maps")) do
|
||||
if not (argument.exclude_current and map_name == game.GetMap()) then
|
||||
maps:AddChoice(map_name)
|
||||
end
|
||||
end
|
||||
|
||||
function maps:OnSelect(_, value)
|
||||
set_result(value)
|
||||
end
|
||||
|
||||
local value = argument.optional and "None" or maps:GetOptionText(1)
|
||||
maps:SetValue(value)
|
||||
maps:OnSelect(nil, value)
|
||||
|
||||
return maps
|
||||
end)
|
||||
|
||||
:AutoComplete(function(_, result, name)
|
||||
for _, map_name in ipairs(sam.get_global("Maps")) do
|
||||
if map_name:lower():find(name, 1, true) then
|
||||
table.insert(result, map_name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
77
addons/sam/lua/sam/command/arguments/number.lua
Normal file
77
addons/sam/lua/sam/command/arguments/number.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
local get_number = function(argument, input, gsub)
|
||||
if (input == "" or input == nil) and argument.optional then
|
||||
if argument.default ~= nil then
|
||||
return argument.default
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local number = tonumber(input)
|
||||
if gsub ~= false and not isnumber(number) then
|
||||
number = tonumber(input:gsub("%D", ""), 10 /*gsub returns two args*/)
|
||||
end
|
||||
|
||||
return number
|
||||
end
|
||||
|
||||
command.new_argument("number")
|
||||
:OnExecute(function(argument, input, ply, _, result, i)
|
||||
local number = get_number(argument, input)
|
||||
if number == "" then
|
||||
result[i] = nil
|
||||
elseif not number then
|
||||
ply:sam_send_message("invalid", {
|
||||
S = argument.hint or "number", S_2 = input
|
||||
})
|
||||
return false
|
||||
else
|
||||
if argument.min then
|
||||
number = math.max(number, argument.min)
|
||||
end
|
||||
|
||||
if argument.max then
|
||||
number = math.min(number, argument.max)
|
||||
end
|
||||
|
||||
if argument.round then
|
||||
number = math.Round(number)
|
||||
end
|
||||
|
||||
result[i] = number
|
||||
end
|
||||
end)
|
||||
:Menu(function(set_result, body, buttons, argument)
|
||||
local number_entry = buttons:Add("SAM.TextEntry")
|
||||
number_entry:SetUpdateOnType(true)
|
||||
number_entry:SetNumeric(true)
|
||||
number_entry:SetTall(25)
|
||||
|
||||
number_entry:SetCheck(function(number)
|
||||
number = get_number(argument, number, false)
|
||||
set_result(number)
|
||||
return number or false
|
||||
end)
|
||||
|
||||
local hint = argument.hint or "number"
|
||||
if argument.default then
|
||||
hint = hint .. " = " .. tostring(argument.default)
|
||||
end
|
||||
number_entry:SetPlaceholder(hint)
|
||||
|
||||
return number_entry
|
||||
end)
|
||||
:End()
|
||||
456
addons/sam/lua/sam/command/arguments/player.lua
Normal file
456
addons/sam/lua/sam/command/arguments/player.lua
Normal file
@@ -0,0 +1,456 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
local can_target_player = function(arg, admin, target, cmd, input)
|
||||
if not IsValid(target) or not target:IsPlayer() or not target:sam_get_nwvar("is_authed") then
|
||||
if input then
|
||||
admin:sam_send_message("cant_find_target", {
|
||||
S = input
|
||||
})
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if not arg.allow_higher_target and not admin:CanTarget(target) then
|
||||
if cmd then
|
||||
admin:sam_send_message("cant_target_player", {
|
||||
S = target:Name()
|
||||
})
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if arg.cant_target_self and admin == target then
|
||||
if cmd then
|
||||
admin:sam_send_message("cant_target_self", {
|
||||
S = cmd.name
|
||||
})
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local check_text_match = function(text, ply)
|
||||
if ply:Name():lower():find(text, 1, true) then return true end
|
||||
if ply:sam_getrank():lower():find(text, 1, true) then return true end
|
||||
if team.GetName(ply:Team()):lower():find(text, 1, true) then return true end
|
||||
|
||||
if not ply:IsBot() then
|
||||
return ply:SteamID():lower():find(text, 1, true) or ply:SteamID64():lower():find(text, 1, true)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
command.new_argument("player")
|
||||
:OnExecute(function(arg, input, ply, cmd, result, n)
|
||||
if input == nil and arg.optional then
|
||||
if sam.isconsole(ply) then
|
||||
ply:sam_send_message("cant_target_self", {
|
||||
S = cmd.name
|
||||
})
|
||||
return false
|
||||
end
|
||||
result[n] = {ply, admin = ply, input = input}
|
||||
return
|
||||
end
|
||||
|
||||
local single_target = arg.single_target
|
||||
local targets = {admin = ply, input = input}
|
||||
|
||||
if input == "*" then
|
||||
if single_target then
|
||||
ply:sam_send_message("cant_target_multi_players")
|
||||
return false
|
||||
end
|
||||
local players = player.GetAll()
|
||||
for i = 1, #players do
|
||||
local v = players[i]
|
||||
if can_target_player(arg, ply, v) then
|
||||
table.insert(targets, v)
|
||||
end
|
||||
end
|
||||
elseif input:sub(1, 1) == "#" and not single_target then
|
||||
local tmp = {}
|
||||
for _, v in ipairs(input:sub(2):Trim():Split(",")) do
|
||||
v = tonumber(v)
|
||||
if not sam.isnumber(v) then continue end
|
||||
local target = Entity(v)
|
||||
if not tmp[target] and IsValid(target) and target:IsPlayer() then
|
||||
tmp[target] = true
|
||||
if can_target_player(arg, ply, target) then
|
||||
table.insert(targets, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local target
|
||||
if input == "^" then
|
||||
target = ply
|
||||
elseif input == "@" and not sam.isconsole(ply) then
|
||||
target = ply:GetEyeTrace().Entity
|
||||
elseif sam.is_steamid(input) then
|
||||
target = player.GetBySteamID(input)
|
||||
elseif sam.is_steamid64(input) then
|
||||
target = player.GetBySteamID64(input)
|
||||
elseif input:sub(1, 1) == "#" then
|
||||
local index = input:sub(2):Trim()
|
||||
index = tonumber(index)
|
||||
|
||||
if not isnumber(index) then
|
||||
ply:sam_send_message("invalid_id", {
|
||||
S = input
|
||||
})
|
||||
return false
|
||||
end
|
||||
|
||||
target = Entity(index)
|
||||
|
||||
if not IsValid(target) or not target:IsPlayer() then
|
||||
ply:sam_send_message("player_id_not_found", {
|
||||
S = index
|
||||
})
|
||||
return false
|
||||
end
|
||||
else
|
||||
if input:sub(1, 1) == "%" and #input > 1 then
|
||||
input = input:sub(2)
|
||||
end
|
||||
|
||||
target = sam.player.find_by_name(input)
|
||||
if sam.istable(target) then
|
||||
if single_target then
|
||||
ply:sam_send_message("found_multi_players", {T = target})
|
||||
return false
|
||||
else
|
||||
for k, v in ipairs(target) do
|
||||
if can_target_player(arg, ply, v) then
|
||||
table.insert(targets, v)
|
||||
end
|
||||
end
|
||||
goto _end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not can_target_player(arg, ply, target, cmd, input) then
|
||||
return false
|
||||
end
|
||||
|
||||
table.insert(targets, target)
|
||||
end
|
||||
|
||||
::_end::
|
||||
|
||||
if #targets == 0 then
|
||||
ply:sam_send_message("cant_find_target", {
|
||||
S = input
|
||||
})
|
||||
return false
|
||||
end
|
||||
result[n] = targets
|
||||
end)
|
||||
|
||||
-- Do NOT ask me about this code at all please because I feel shit about it but I'm not gonna make
|
||||
-- a file specially for this one
|
||||
:Menu(function(set_result, body, buttons, argument, childs)
|
||||
if body.ply_list then
|
||||
local ply_list = body.ply_list
|
||||
ply_list.argument = argument
|
||||
ply_list.set_result = set_result
|
||||
ply_list.multi_select = argument.single_target ~= true
|
||||
|
||||
if argument.single_target == true and #ply_list:GetSelected() > 1 then
|
||||
ply_list:ClearSelection()
|
||||
end
|
||||
|
||||
ply_list:OnRowSelected()
|
||||
ply_list:GetParent():Show()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local SUI = sam.SUI
|
||||
|
||||
local SetVisible = FindMetaTable("Panel").SetVisible
|
||||
|
||||
local left_body = body:Add("SAM.Panel")
|
||||
left_body:Dock(LEFT)
|
||||
left_body:DockMargin(0, 0, 5, 0)
|
||||
left_body:SetWide(0)
|
||||
left_body.no_remove = true
|
||||
left_body.no_change = "player"
|
||||
|
||||
SetVisible(left_body, false)
|
||||
left_body.SetVisible = function(s, visible)
|
||||
if visible == s:IsVisible() or visible == s.visible_state then return end
|
||||
|
||||
if visible then
|
||||
SetVisible(s, true)
|
||||
s:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
s.visible_state = visible
|
||||
s:Stop()
|
||||
|
||||
s:SizeTo(visible and SUI.Scale(320) or 0, -1, 0.2, 0, 0, function()
|
||||
SetVisible(s, visible)
|
||||
s:InvalidateParent(true)
|
||||
end)
|
||||
end
|
||||
left_body:Show()
|
||||
|
||||
table.insert(childs, left_body)
|
||||
|
||||
local ply_list = left_body:Add("SAM.ScrollPanel")
|
||||
ply_list:Dock(FILL)
|
||||
ply_list:Background(Color(34, 34, 34), 3)
|
||||
ply_list.argument = argument
|
||||
ply_list.set_result = set_result
|
||||
ply_list.multi_select = argument.single_target ~= true
|
||||
ply_list.Paint = function(s, w, h)
|
||||
s:RoundedBox("Background", 3, 0, 0, w, h, SUI.GetColor("text_entry_bg"))
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
function ply_list:OnClickLine(line, clear)
|
||||
local multi_select = ply_list.multi_select
|
||||
if not multi_select and not clear then return end
|
||||
|
||||
if multi_select and input.IsKeyDown(KEY_LCONTROL) then
|
||||
if line.Selected then
|
||||
line.Selected = false
|
||||
self.main_selected_line = nil
|
||||
self:OnRowSelected()
|
||||
return
|
||||
end
|
||||
clear = false
|
||||
end
|
||||
|
||||
if multi_select and input.IsKeyDown(KEY_LSHIFT) then
|
||||
local selected = self:GetSelectedLine()
|
||||
if selected then
|
||||
self.main_selected_line = self.main_selected_line or selected
|
||||
|
||||
if clear then
|
||||
self:ClearSelection()
|
||||
end
|
||||
|
||||
local first = math.min(self.main_selected_line.id, line.id)
|
||||
local last = math.max(self.main_selected_line.id, line.id)
|
||||
|
||||
for id = first, last do
|
||||
local line_2 = lines[id]
|
||||
local was_selected = line_2.Selected
|
||||
|
||||
line_2.Selected = true
|
||||
|
||||
if not was_selected then
|
||||
self:OnRowSelected(line_2.id, line_2)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not multi_select or clear then
|
||||
self:ClearSelection()
|
||||
end
|
||||
|
||||
line.Selected = true
|
||||
|
||||
self.main_selected_line = line
|
||||
self:OnRowSelected(line.id, line)
|
||||
end
|
||||
|
||||
function ply_list:GetSelected()
|
||||
local ret = {}
|
||||
for _, v in ipairs(lines) do
|
||||
if v.Selected then
|
||||
table.insert(ret, v)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function ply_list:GetSelectedLine()
|
||||
for _, line in ipairs(lines) do
|
||||
if line.Selected then return line end
|
||||
end
|
||||
end
|
||||
|
||||
function ply_list:ClearSelection()
|
||||
for _, line in ipairs(lines) do
|
||||
line.Selected = false
|
||||
end
|
||||
self:OnRowSelected()
|
||||
end
|
||||
|
||||
function ply_list:OnRowSelected()
|
||||
local plys = {}
|
||||
for k, v in ipairs(ply_list:GetSelected()) do
|
||||
plys[k] = v.ply:EntIndex()
|
||||
end
|
||||
if #plys == 0 then
|
||||
self.set_result(nil)
|
||||
else
|
||||
self.set_result("#" .. table.concat(plys, ","))
|
||||
end
|
||||
end
|
||||
|
||||
function ply_list:OnRowRightClick(_, line)
|
||||
local dmenu = vgui.Create("SAM.Menu")
|
||||
dmenu:SetInternal(line)
|
||||
|
||||
local name = line.ply:Name()
|
||||
dmenu:AddOption("Copy Name", function()
|
||||
SetClipboardText(name)
|
||||
end)
|
||||
|
||||
dmenu:AddSpacer()
|
||||
|
||||
local steamid = line.ply:SteamID()
|
||||
dmenu:AddOption("Copy SteamID", function()
|
||||
SetClipboardText(steamid)
|
||||
end)
|
||||
|
||||
dmenu:AddOption("Copy SteamID64", function()
|
||||
SetClipboardText(util.SteamIDTo64(steamid))
|
||||
end)
|
||||
|
||||
dmenu:Open()
|
||||
dmenu:SetPos(input.GetCursorPos())
|
||||
end
|
||||
|
||||
local item_click = function(s)
|
||||
ply_list:OnClickLine(s, true)
|
||||
end
|
||||
|
||||
local item_rightclick = function(s)
|
||||
if not s.Selected then
|
||||
ply_list:OnClickLine(s, true)
|
||||
end
|
||||
ply_list:OnRowRightClick(s.id, s)
|
||||
end
|
||||
|
||||
local item_cursor = function(s)
|
||||
if input.IsMouseDown(MOUSE_LEFT) then
|
||||
ply_list:OnClickLine(s)
|
||||
end
|
||||
end
|
||||
|
||||
local added_players = {}
|
||||
|
||||
local add_player = function(ply, i)
|
||||
if can_target_player(ply_list.argument, LocalPlayer(), ply) then
|
||||
local player_button = ply_list:Add("SAM.Button")
|
||||
player_button:Dock(TOP)
|
||||
player_button:DockMargin(0, 0, 0, 2)
|
||||
player_button:DockPadding(4, 4, 4, 4)
|
||||
player_button:SetContained(false)
|
||||
player_button:SetText("")
|
||||
player_button:SetZPos(i)
|
||||
player_button.DoClick = item_click
|
||||
player_button.DoRightClick = item_rightclick
|
||||
player_button.OnCursorMoved = item_cursor
|
||||
|
||||
local line = player_button:Add("SAM.PlayerLine")
|
||||
line:SetMouseInputEnabled(false)
|
||||
line:SetInfo({
|
||||
steamid = ply:IsBot() and "BOT" or ply:SteamID(),
|
||||
name = ply:Name(),
|
||||
rank = ply:sam_getrank()
|
||||
})
|
||||
|
||||
player_button:InvalidateLayout(true)
|
||||
player_button:SizeToChildren(false, true)
|
||||
|
||||
player_button.ply = ply
|
||||
player_button.line = line
|
||||
player_button.id = table.insert(lines, player_button)
|
||||
body.search_entry:OnValueChange()
|
||||
|
||||
added_players[ply] = true
|
||||
end
|
||||
end
|
||||
|
||||
ply_list:On("Think", function()
|
||||
local players = player.GetAll()
|
||||
for i = 1, #players do
|
||||
local ply = players[i]
|
||||
if not added_players[ply] then
|
||||
add_player(ply, i)
|
||||
end
|
||||
end
|
||||
|
||||
local argument = ply_list.argument
|
||||
for i = 1, #lines do
|
||||
local line = lines[i]
|
||||
local ply = line.ply
|
||||
|
||||
if not can_target_player(argument, LocalPlayer(), ply) then
|
||||
line:Remove()
|
||||
table.remove(lines, i)
|
||||
added_players[ply] = nil
|
||||
ply_list:OnRowSelected()
|
||||
break
|
||||
end
|
||||
|
||||
line = line.line
|
||||
line:SetName(ply:Name())
|
||||
line:SetRank(ply:sam_getrank())
|
||||
end
|
||||
end)
|
||||
|
||||
local search_entry = left_body:Add("SAM.TextEntry")
|
||||
search_entry:Dock(TOP)
|
||||
search_entry:DockMargin(0, 0, 0, 5)
|
||||
search_entry:SetPlaceholder("Search... (name/steamid/rank/job)")
|
||||
search_entry:SetBackground(Color(34, 34, 34))
|
||||
search_entry:SetTall(25)
|
||||
search_entry:SetNoBar(true)
|
||||
|
||||
function search_entry:OnValueChange(text)
|
||||
if text == nil then
|
||||
text = self:GetValue()
|
||||
end
|
||||
if text ~= "" then
|
||||
ply_list:ClearSelection()
|
||||
end
|
||||
text = text:lower()
|
||||
for i, line in ipairs(lines) do
|
||||
local ply = line.ply
|
||||
if IsValid(ply) then
|
||||
line:SetVisible(check_text_match(text, ply))
|
||||
end
|
||||
end
|
||||
ply_list:GetCanvas():InvalidateLayout(true)
|
||||
end
|
||||
|
||||
body.ply_list = ply_list
|
||||
body.search_entry = search_entry
|
||||
end)
|
||||
|
||||
:AutoComplete(function(arg, result, name)
|
||||
local ply = LocalPlayer()
|
||||
for k, v in ipairs(player.GetAll()) do
|
||||
if can_target_player(arg, ply, v) and v:Name():lower():find(name, 1, true) then
|
||||
table.insert(result, "%" .. v:Name())
|
||||
end
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
78
addons/sam/lua/sam/command/arguments/rank.lua
Normal file
78
addons/sam/lua/sam/command/arguments/rank.lua
Normal file
@@ -0,0 +1,78 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
local is_good_rank = function(rank, arg, ply)
|
||||
if arg.check and not arg.check(rank, ply) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
command.new_argument("rank")
|
||||
:OnExecute(function(arg, input, ply, _, result, i)
|
||||
if not input and arg.optional then
|
||||
result[i] = nil
|
||||
return
|
||||
end
|
||||
|
||||
if not sam.ranks.is_rank(input) or not is_good_rank(input, arg, ply) then
|
||||
ply:sam_send_message("invalid", {
|
||||
S = arg.hint or "rank", S_2 = input
|
||||
})
|
||||
return false
|
||||
end
|
||||
|
||||
result[i] = input
|
||||
end)
|
||||
|
||||
:Menu(function(set_result, body, buttons, arg)
|
||||
local current_rank = arg.hint or "select rank"
|
||||
|
||||
local ranks = buttons:Add("SAM.ComboBox")
|
||||
ranks:SetValue(current_rank)
|
||||
ranks:SetTall(25)
|
||||
|
||||
function ranks:OnSelect(_, value)
|
||||
set_result(value)
|
||||
current_rank = value
|
||||
end
|
||||
|
||||
function ranks:DoClick()
|
||||
if self:IsMenuOpen() then
|
||||
return self:CloseMenu()
|
||||
end
|
||||
|
||||
self:Clear()
|
||||
self:SetValue(current_rank)
|
||||
|
||||
for rank_name in SortedPairsByMemberValue(sam.ranks.get_ranks(), "immunity", true) do
|
||||
if is_good_rank(rank_name, arg, LocalPlayer()) then
|
||||
self:AddChoice(rank_name)
|
||||
end
|
||||
end
|
||||
|
||||
self:OpenMenu()
|
||||
end
|
||||
|
||||
return ranks
|
||||
end)
|
||||
|
||||
:AutoComplete(function(arg, result, name)
|
||||
for rank_name in SortedPairsByMemberValue(sam.ranks.get_ranks(), "immunity", true) do
|
||||
if rank_name:lower():find(name, 1, true) and is_good_rank(rank_name, arg, LocalPlayer()) then
|
||||
table.insert(result, rank_name)
|
||||
end
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
121
addons/sam/lua/sam/command/arguments/steamid.lua
Normal file
121
addons/sam/lua/sam/command/arguments/steamid.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
local cached_ranks = {}
|
||||
local targeting_offline = {}
|
||||
|
||||
local check_steamid = function(steamid)
|
||||
if not sam.is_steamid(steamid) then
|
||||
if sam.is_steamid64(steamid) then
|
||||
return util.SteamIDFrom64(steamid)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
return steamid
|
||||
end
|
||||
|
||||
local can_target_steamid_callback = function(data, promise)
|
||||
local ply, steamid = promise.ply, promise.steamid
|
||||
|
||||
if not data or sam.ranks.can_target(promise.rank, data.rank) then
|
||||
promise:resolve({steamid})
|
||||
elseif IsValid(ply) then
|
||||
ply:sam_send_message("cant_target_player", {
|
||||
S = steamid
|
||||
})
|
||||
end
|
||||
|
||||
targeting_offline[ply] = nil
|
||||
cached_ranks[steamid] = data ~= nil and data or false
|
||||
end
|
||||
|
||||
command.new_argument("steamid")
|
||||
:OnExecute(function(argument, input, ply, _, result, i)
|
||||
local steamid = check_steamid(input)
|
||||
if not steamid then
|
||||
ply:sam_send_message("invalid", {
|
||||
S = "steamid/steamid64", S_2 = input
|
||||
})
|
||||
return false
|
||||
end
|
||||
|
||||
if argument.allow_higher_target then
|
||||
result[i] = steamid
|
||||
return
|
||||
end
|
||||
|
||||
local promise = sam.Promise.new()
|
||||
promise.ply = ply
|
||||
promise.rank = ply:sam_getrank()
|
||||
promise.steamid = steamid
|
||||
|
||||
local target = player.GetBySteamID(steamid)
|
||||
if sam.isconsole(ply) then
|
||||
promise:resolve({steamid})
|
||||
elseif target then
|
||||
if ply:CanTarget(target) then
|
||||
promise:resolve({steamid, target})
|
||||
else
|
||||
ply:sam_send_message("cant_target_player", {
|
||||
S = steamid
|
||||
})
|
||||
end
|
||||
elseif cached_ranks[steamid] ~= nil then
|
||||
can_target_steamid_callback(cached_ranks[steamid], promise)
|
||||
else
|
||||
targeting_offline[ply] = true
|
||||
|
||||
sam.SQL.FQuery([[
|
||||
SELECT
|
||||
`rank`
|
||||
FROM
|
||||
`sam_players`
|
||||
WHERE
|
||||
`steamid` = {1}
|
||||
]], {steamid}, can_target_steamid_callback, true, promise)
|
||||
end
|
||||
|
||||
result[i] = promise
|
||||
end)
|
||||
:Menu(function(set_result, body, buttons, argument)
|
||||
local steamid_entry = buttons:Add("SAM.TextEntry")
|
||||
steamid_entry:SetTall(25)
|
||||
steamid_entry:SetUpdateOnType(true)
|
||||
steamid_entry:SetPlaceholder("steamid/steamid64")
|
||||
|
||||
steamid_entry:SetCheck(function(steamid)
|
||||
steamid = check_steamid(steamid)
|
||||
set_result(steamid)
|
||||
return steamid or false
|
||||
end)
|
||||
|
||||
return steamid_entry
|
||||
end)
|
||||
:End()
|
||||
|
||||
timer.Create("SAM.ClearCachedRanks", 60 * 2.5, 0, function()
|
||||
table.Empty(cached_ranks)
|
||||
end)
|
||||
|
||||
hook.Add("SAM.ChangedSteamIDRank", "RemoveIfCached", function(steamid)
|
||||
cached_ranks[steamid] = nil
|
||||
end)
|
||||
|
||||
hook.Add("SAM.CanRunCommand", "StopIfTargetingOffline", function(ply)
|
||||
if targeting_offline[ply] then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
70
addons/sam/lua/sam/command/arguments/text.lua
Normal file
70
addons/sam/lua/sam/command/arguments/text.lua
Normal file
@@ -0,0 +1,70 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
command.new_argument("text")
|
||||
:OnExecute(function(argument, input, ply, _, result, i)
|
||||
if sam.isstring(input) then
|
||||
input = input:sub(1, 255)
|
||||
end
|
||||
|
||||
local invalid = false
|
||||
if input == nil then
|
||||
if not argument.optional then
|
||||
invalid = true
|
||||
end
|
||||
elseif argument.check and not argument.check(input, ply) then
|
||||
invalid = true
|
||||
end
|
||||
|
||||
if invalid then
|
||||
ply:sam_send_message("invalid", {
|
||||
S = argument.hint or "text", S_2 = input
|
||||
})
|
||||
return false
|
||||
end
|
||||
|
||||
result[i] = input
|
||||
end)
|
||||
:Menu(function(set_result, body, buttons, argument)
|
||||
local text_entry = buttons:Add("SAM.TextEntry")
|
||||
text_entry:SetTall(25)
|
||||
|
||||
local default = argument.default
|
||||
text_entry:SetCheck(function(text)
|
||||
local valid = true
|
||||
if text == "" then
|
||||
if default then
|
||||
text = default
|
||||
elseif not argument.optional then
|
||||
valid = false
|
||||
end
|
||||
elseif argument.check and not argument.check(text, LocalPlayer()) then
|
||||
valid = false
|
||||
end
|
||||
|
||||
set_result(valid and text or nil)
|
||||
|
||||
return valid
|
||||
end)
|
||||
|
||||
local hint = argument.hint or "text"
|
||||
if default then
|
||||
hint = hint .. " = " .. tostring(default)
|
||||
end
|
||||
|
||||
text_entry:SetPlaceholder(hint)
|
||||
|
||||
return text_entry
|
||||
end)
|
||||
:End()
|
||||
128
addons/sam/lua/sam/command/cl_command.lua
Normal file
128
addons/sam/lua/sam/command/cl_command.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local command = sam.command
|
||||
|
||||
local get_syntax = function(args, cmd_args, cmd_str)
|
||||
for i = 1, #cmd_args do
|
||||
cmd_str = cmd_str .. " "
|
||||
|
||||
local cmd_arg = cmd_args[i]
|
||||
local arg = args[i]
|
||||
|
||||
if arg == "" then
|
||||
arg = nil
|
||||
end
|
||||
|
||||
local optional = cmd_arg.optional
|
||||
local c_1, c_2 = "<", ">"
|
||||
if optional then
|
||||
c_1, c_2 = "[", "]"
|
||||
end
|
||||
|
||||
cmd_str = cmd_str .. (arg and "\"" or c_1)
|
||||
|
||||
cmd_str = cmd_str .. (arg or cmd_arg.hint or cmd_arg.name)
|
||||
|
||||
if not arg then
|
||||
local default = cmd_arg.default
|
||||
if default then
|
||||
cmd_str = cmd_str .. " = " .. tostring(default)
|
||||
end
|
||||
end
|
||||
|
||||
cmd_str = cmd_str .. (arg and "\"" or c_2)
|
||||
end
|
||||
|
||||
return cmd_str
|
||||
end
|
||||
|
||||
--
|
||||
-- Auto Complete
|
||||
--
|
||||
concommand.Add("sam", function(_, _, _, text)
|
||||
LocalPlayer():ConCommand("sam_run " .. text)
|
||||
end, function(_, text)
|
||||
local ply = LocalPlayer()
|
||||
local result = {}
|
||||
|
||||
local new_arg = text:EndsWith(" ")
|
||||
local args = sam.parse_args(text)
|
||||
|
||||
local cmd_name = (args[1] or ""):lower()
|
||||
local cmd = command.get_command(cmd_name)
|
||||
|
||||
if not cmd or (#args == 1 and not new_arg) then
|
||||
local commands = command.get_commands()
|
||||
|
||||
for _, v in ipairs(commands) do
|
||||
local name = v.name
|
||||
if name:find(cmd_name, nil, true) and ply:HasPermission(name) then
|
||||
table.insert(result, "sam " .. name)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
if not ply:HasPermission(cmd_name) then return end
|
||||
|
||||
table.remove(args, 1)
|
||||
|
||||
if new_arg then
|
||||
local syntax = get_syntax(args, cmd.args, "sam " .. cmd.name)
|
||||
if #args == 0 then
|
||||
print(syntax)
|
||||
end
|
||||
table.insert(result, syntax)
|
||||
return result
|
||||
end
|
||||
|
||||
local arg_index = new_arg and #args + 1 or #args
|
||||
|
||||
local cmd_args = cmd.args
|
||||
local cmd_args_n = #cmd_args
|
||||
if cmd_args_n == 0 then return end
|
||||
|
||||
if arg_index >= cmd_args_n then
|
||||
arg_index = cmd_args_n
|
||||
|
||||
if cmd.get_rest_args then
|
||||
local arg = table.concat(args, " ", cmd_args_n)
|
||||
if arg ~= "" then
|
||||
args[cmd_args_n] = arg
|
||||
for i = cmd_args_n + 1, #args do
|
||||
args[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local arguments = command.get_arguments()
|
||||
local cmd_arg = cmd_args[arg_index]
|
||||
local func = arguments[cmd_arg.name].auto_complete
|
||||
if func then
|
||||
func(cmd_arg, result, args[arg_index] or "")
|
||||
end
|
||||
|
||||
local cmd_str = "sam " .. cmd_name .. " "
|
||||
if arg_index - 1 > 0 then
|
||||
cmd_str = cmd_str .. "\"" .. table.concat(args, "\" ", 1, arg_index - 1) .. "\" "
|
||||
end
|
||||
|
||||
for k, v in ipairs(result) do
|
||||
result[k] = cmd_str .. "\"" .. v .. "\""
|
||||
end
|
||||
|
||||
return result
|
||||
end)
|
||||
289
addons/sam/lua/sam/command/sh_command.lua
Normal file
289
addons/sam/lua/sam/command/sh_command.lua
Normal file
@@ -0,0 +1,289 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local istable, isstring = sam.istable, sam.isstring
|
||||
|
||||
local commands = {}
|
||||
local arguments = {}
|
||||
|
||||
do
|
||||
local command = {}
|
||||
|
||||
local current_category = "other"
|
||||
function command.set_category(category)
|
||||
if isstring(category) then
|
||||
current_category = category
|
||||
end
|
||||
end
|
||||
|
||||
function command.get_commands()
|
||||
return commands
|
||||
end
|
||||
|
||||
function command.get_command(name)
|
||||
for i = 1, #commands do
|
||||
local cmd = commands[i]
|
||||
if cmd.name == name then
|
||||
return cmd, i
|
||||
end
|
||||
|
||||
local aliases = cmd.aliases
|
||||
for i2 = 1, #aliases do
|
||||
local alias = aliases[i2]
|
||||
if alias == name then
|
||||
return cmd, i
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function command.remove_command(name)
|
||||
local cmd, index = command.get_command(name)
|
||||
if index then
|
||||
table.remove(commands, index)
|
||||
hook.Call("SAM.CommandRemoved", nil, cmd.name, cmd, index)
|
||||
return index
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function command.get_arguments()
|
||||
return arguments
|
||||
end
|
||||
|
||||
do
|
||||
local argument_methods = {
|
||||
OnExecute = function(self, func)
|
||||
if isfunction(func) and SERVER then
|
||||
self.on_execute = func
|
||||
end
|
||||
return self
|
||||
end,
|
||||
Menu = function(self, func)
|
||||
if isfunction(func) and CLIENT then
|
||||
self.menu = func
|
||||
end
|
||||
return self
|
||||
end,
|
||||
AutoComplete = function(self, func)
|
||||
if isfunction(func) and CLIENT then
|
||||
self.auto_complete = func
|
||||
end
|
||||
return self
|
||||
end,
|
||||
End = function(self)
|
||||
if SERVER then
|
||||
arguments[self.name] = self.on_execute
|
||||
else
|
||||
arguments[self.name] = self
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local argument_meta = {__index = argument_methods}
|
||||
function command.new_argument(name)
|
||||
if isstring(name) then
|
||||
return setmetatable({name = name}, argument_meta)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function command.run_commands(to_run)
|
||||
local time = 0
|
||||
for i = 1, #to_run do
|
||||
timer.Simple(time, function()
|
||||
RunConsoleCommand("sam", unpack(to_run[i]))
|
||||
end)
|
||||
time = time + 0.76
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Methods
|
||||
--
|
||||
local Command_Methods = {}
|
||||
local Command_meta = {__index = Command_Methods}
|
||||
|
||||
function command.new(cmd)
|
||||
if not isstring(cmd) then return end
|
||||
|
||||
local new_command = setmetatable({}, Command_meta)
|
||||
new_command.can_console_run = true
|
||||
new_command.args = {}
|
||||
new_command.name = cmd:lower()
|
||||
new_command.aliases = {}
|
||||
new_command.category = current_category
|
||||
|
||||
return new_command
|
||||
end
|
||||
|
||||
local AddMethod = function(name, func)
|
||||
Command_Methods[name] = func
|
||||
end
|
||||
|
||||
AddMethod("Aliases", function(self, ...)
|
||||
for k, v in ipairs({...}) do
|
||||
table.insert(self.aliases, v)
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("AddArg", function(self, name, data)
|
||||
if not isstring(name) then return end
|
||||
if not istable(data) then
|
||||
data = {}
|
||||
end
|
||||
data.name = name
|
||||
table.insert(self.args, data)
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("DisallowConsole", function(self, disallow)
|
||||
self.can_console_run = isbool(disallow) and disallow or false
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("SetCategory", function(self, category)
|
||||
if isstring(category) then
|
||||
self.category = category
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("Help", function(self, help)
|
||||
if isstring(help) then
|
||||
self.help = sam.language.get(help) or help
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("OnExecute", function(self, func)
|
||||
if isfunction(func) and SERVER then
|
||||
self.on_execute = func
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("SetPermission", function(self, perm, default_rank)
|
||||
if isstring(perm) then
|
||||
self.permission = perm
|
||||
self.default_rank = default_rank
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("GetRestArgs", function(self, get)
|
||||
if not isbool(get) then
|
||||
get = true
|
||||
end
|
||||
self.get_rest_args = get
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("MenuHide", function(self, should_hide)
|
||||
if isbool(should_hide) then
|
||||
self.menu_hide = should_hide
|
||||
else
|
||||
self.menu_hide = true
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("DisableNotify", function(self, disable)
|
||||
if isbool(disable) then
|
||||
self.disable_notify = disable
|
||||
else
|
||||
self.disable_notify = true
|
||||
end
|
||||
return self
|
||||
end)
|
||||
|
||||
AddMethod("End", function(self)
|
||||
local name = self.name
|
||||
if SERVER and not self.on_execute then
|
||||
sam.print("need an OnExecute function for the command!")
|
||||
debug.Trace()
|
||||
return
|
||||
end
|
||||
|
||||
if self.permission then
|
||||
sam.permissions.add(self.permission, "Commands - " .. self.category, self.default_rank)
|
||||
end
|
||||
|
||||
local _, index = command.get_command(name)
|
||||
if index then
|
||||
commands[index] = self
|
||||
hook.Call("SAM.CommandModified", nil, name, self, index)
|
||||
else
|
||||
hook.Call("SAM.CommandAdded", nil, name, self, table.insert(commands, self))
|
||||
end
|
||||
end)
|
||||
|
||||
AddMethod("GetRequiredArgs", function(self)
|
||||
local required_args = {}
|
||||
local args = self.args
|
||||
for i = 1, #args do
|
||||
local v = args[i]
|
||||
if not v.optional then
|
||||
table.insert(required_args, v)
|
||||
end
|
||||
end
|
||||
return required_args
|
||||
end)
|
||||
|
||||
AddMethod("GetOptionalArgs", function(self)
|
||||
local optional_args = {}
|
||||
local args = self.args
|
||||
for i = 1, #args do
|
||||
local v = args[i]
|
||||
if v.optional then
|
||||
table.insert(optional_args, v)
|
||||
end
|
||||
end
|
||||
return optional_args
|
||||
end)
|
||||
|
||||
AddMethod("ArgsToString", function(self, return_table)
|
||||
local str_table = {}
|
||||
local args = self.args
|
||||
for i = 1, #self.args do
|
||||
local v = args[i]
|
||||
if not v.optional then
|
||||
table.insert(str_table, "<" .. (v.hint or v.name) .. ">")
|
||||
else
|
||||
table.insert(str_table, "[" .. (v.hint or v.name) .. "]")
|
||||
end
|
||||
end
|
||||
return return_table and str_table or table.concat(str_table, " ")
|
||||
end)
|
||||
|
||||
AddMethod("HasArg", function(self, arg)
|
||||
local args = self.args
|
||||
for i = 1, #self.args do
|
||||
if args[i].name == arg then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
command.add_method = AddMethod
|
||||
|
||||
sam.command = command
|
||||
end
|
||||
|
||||
sam.__commands = commands
|
||||
sam.__arguments = arguments
|
||||
186
addons/sam/lua/sam/command/sv_command.lua
Normal file
186
addons/sam/lua/sam/command/sv_command.lua
Normal file
@@ -0,0 +1,186 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local command = sam.command
|
||||
|
||||
local sub, match, lower = string.sub, string.match, string.lower
|
||||
local WHITE, RED, BLUE, GREEN = Color(236, 240, 241), Color(244, 67, 54), Color(13, 130, 223), Color(0, 230, 64)
|
||||
|
||||
local prefix
|
||||
local send_syntax = function(ply, args, cmd_name, cmd_args, cmd_args_n, from_console)
|
||||
local tbl = {
|
||||
WHITE,
|
||||
from_console and "sam " or prefix,
|
||||
cmd_name
|
||||
}
|
||||
|
||||
for i = 1, cmd_args_n do
|
||||
table.insert(tbl, " ")
|
||||
|
||||
local cmd_arg = cmd_args[i]
|
||||
local arg = args[i]
|
||||
|
||||
if arg == "" then
|
||||
arg = nil
|
||||
end
|
||||
|
||||
local optional = cmd_arg.optional
|
||||
local c_1, c_2 = "<", ">"
|
||||
if optional then
|
||||
c_1, c_2 = "[", "]"
|
||||
end
|
||||
|
||||
table.insert(tbl, WHITE)
|
||||
table.insert(tbl, arg and "\"" or c_1)
|
||||
|
||||
table.insert(tbl, cmd_arg.optional and BLUE or RED)
|
||||
table.insert(tbl, arg or cmd_arg.hint or cmd_arg.name)
|
||||
|
||||
if not arg then
|
||||
local default = cmd_arg.default
|
||||
if default then
|
||||
table.insert(tbl, WHITE)
|
||||
table.insert(tbl, " = ")
|
||||
|
||||
table.insert(tbl, GREEN)
|
||||
table.insert(tbl, tostring(default))
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(tbl, WHITE)
|
||||
table.insert(tbl, arg and "\"" or c_2)
|
||||
end
|
||||
|
||||
sam.player.add_text(ply, unpack(tbl))
|
||||
return ""
|
||||
end
|
||||
|
||||
local run_command = function(ply, text, from_console)
|
||||
local args = sam.parse_args(text)
|
||||
local cmd_name = args[1]
|
||||
if not cmd_name then return end
|
||||
|
||||
cmd_name = lower(cmd_name)
|
||||
|
||||
local cmd = command.get_command(cmd_name)
|
||||
if not cmd then return end
|
||||
|
||||
if not cmd.can_console_run and sam.isconsole(ply) then
|
||||
ply:sam_send_message("cant_use_as_console", {
|
||||
S = cmd_name
|
||||
})
|
||||
return ""
|
||||
end
|
||||
|
||||
if cmd.permission and not sam.isconsole(ply) and not ply:HasPermission(cmd.permission) then
|
||||
ply:sam_send_message("no_permission", {
|
||||
S = cmd_name
|
||||
})
|
||||
return ""
|
||||
end
|
||||
|
||||
local can_run = hook.Call("SAM.CanRunCommand", nil, ply, cmd_name, args, cmd)
|
||||
if can_run == false then return "" end
|
||||
|
||||
table.remove(args, 1)
|
||||
|
||||
local cmd_args = cmd.args
|
||||
local cmd_args_n = #cmd_args
|
||||
|
||||
-- !kick srlion fuck off > !kick "srlion" "fuck off"
|
||||
if cmd.get_rest_args then
|
||||
local arg = table.concat(args, " ", cmd_args_n)
|
||||
if arg ~= "" then
|
||||
args[cmd_args_n] = arg
|
||||
for i = cmd_args_n + 1, #args do
|
||||
args[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- we need to make sure that all required arguments are there
|
||||
for i = 1, cmd_args_n do
|
||||
if not cmd_args[i].optional then
|
||||
local arg = args[i]
|
||||
if arg == nil or arg == "" then
|
||||
send_syntax(ply, args, cmd_name, cmd_args, cmd_args_n, from_console)
|
||||
return ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local result = {}
|
||||
local args_count = 0
|
||||
local arguments = command.get_arguments()
|
||||
for i = 1, cmd_args_n do
|
||||
local cmd_arg = cmd_args[i]
|
||||
local arg = args[i]
|
||||
|
||||
if arg == nil or arg == "" then
|
||||
arg = cmd_arg.default
|
||||
else
|
||||
args_count = args_count + 1
|
||||
end
|
||||
|
||||
if arguments[cmd_arg.name](cmd_arg, arg, ply, cmd, result, i) == false then
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
cmd.on_execute(ply, unpack(result, 1, table.maxn(result)))
|
||||
|
||||
if not cmd.disable_notify then
|
||||
sam.print(
|
||||
RED, ply:Name(),
|
||||
WHITE, "(",
|
||||
BLUE, ply:SteamID(),
|
||||
WHITE, ") ran command '",
|
||||
RED, cmd_name,
|
||||
WHITE,
|
||||
args_count > 0
|
||||
and "' with arguments: \"" .. table.concat(args, "\" \"") .. "\""
|
||||
or "'"
|
||||
)
|
||||
end
|
||||
|
||||
hook.Call("SAM.RanCommand", nil, ply, cmd_name, args, cmd, result)
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
hook.Add("PlayerSay", "SAM.Command.RunCommand", function(ply, text)
|
||||
prefix = sub(text, 1, 1)
|
||||
if prefix ~= "!" then return end
|
||||
if match(sub(text, 2, 2), "%S") == nil then return end
|
||||
|
||||
return run_command(ply, sub(text, 2), false)
|
||||
end)
|
||||
|
||||
local console_run_command = function(ply, _, _, text)
|
||||
if match(sub(text, 2, 2), "%S") == nil then return end
|
||||
|
||||
if not IsValid(ply) then
|
||||
ply = sam.console
|
||||
else
|
||||
-- making it same as PlayerSay delay
|
||||
-- https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/sp/src/game/server/client.cpp#L747
|
||||
-- no delay for server console
|
||||
if not ply:sam_check_cooldown("RunCommand", 0.66) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
run_command(ply, text, true, false)
|
||||
end
|
||||
concommand.Add("sam", console_run_command)
|
||||
concommand.Add("sam_run", console_run_command) -- for some dumb reason i cant make "sam" command clientside just for auto-complete
|
||||
63
addons/sam/lua/sam/config/cl_config.lua
Normal file
63
addons/sam/lua/sam/config/cl_config.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local mp = sam.mp
|
||||
local config = sam.config
|
||||
|
||||
function config.set(key, value, force)
|
||||
if not sam.isstring(key) then
|
||||
error("invalid setting name")
|
||||
end
|
||||
|
||||
if not mp.packers[sam.type(value)] then
|
||||
error("not supported value type")
|
||||
end
|
||||
|
||||
if not force and config.get(key) == value then return end
|
||||
sam.netstream.Start("Config.Set", key, value)
|
||||
end
|
||||
|
||||
function config.get(key, default)
|
||||
local value = sam.get_global("Config")[key]
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
return default
|
||||
end
|
||||
|
||||
local menu_settings = {}
|
||||
function config.add_menu_setting(title, func)
|
||||
local i = #menu_settings + 1
|
||||
for k, v in ipairs(menu_settings) do
|
||||
if v.title == title then
|
||||
i = k
|
||||
break
|
||||
end
|
||||
end
|
||||
menu_settings[i] = {
|
||||
title = title,
|
||||
func = func,
|
||||
}
|
||||
end
|
||||
|
||||
function config.get_menu_settings()
|
||||
return menu_settings
|
||||
end
|
||||
|
||||
hook.Add("SAM.ChangedGlobalVar", "SAM.CheckLoadedConfig", function(key, value)
|
||||
if key == "Config" then
|
||||
config.loaded = true
|
||||
hook.Call("SAM.LoadedConfig", nil, value)
|
||||
hook.Remove("SAM.ChangedGlobalVar", "SAM.CheckLoadedConfig")
|
||||
end
|
||||
end)
|
||||
61
addons/sam/lua/sam/config/sh_config.lua
Normal file
61
addons/sam/lua/sam/config/sh_config.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
|
||||
sam.permissions.add("manage_config", nil, "superadmin")
|
||||
|
||||
local updates = {}
|
||||
function config.hook(keys, func)
|
||||
for i = #keys, 1, -1 do
|
||||
keys[keys[i]] = true
|
||||
keys[i] = nil
|
||||
end
|
||||
|
||||
local id = table.insert(updates, {
|
||||
keys = keys,
|
||||
func = func
|
||||
})
|
||||
|
||||
if config.loaded then
|
||||
func()
|
||||
end
|
||||
|
||||
return id
|
||||
end
|
||||
|
||||
function config.get_updated(key, default)
|
||||
local setting = {}
|
||||
config.hook({key}, function()
|
||||
setting.value = config.get(key, default)
|
||||
end)
|
||||
return setting
|
||||
end
|
||||
|
||||
function config.remove_hook(key)
|
||||
updates[key] = nil
|
||||
end
|
||||
|
||||
hook.Add("SAM.LoadedConfig", "RunHooks", function()
|
||||
for k, v in pairs(updates) do
|
||||
v.func()
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("SAM.UpdatedConfig", "RunHooks", function(key, value, old)
|
||||
for k, v in pairs(updates) do
|
||||
if v.keys[key] then
|
||||
v.func(value, old)
|
||||
end
|
||||
end
|
||||
end)
|
||||
109
addons/sam/lua/sam/config/sv_config.lua
Normal file
109
addons/sam/lua/sam/config/sv_config.lua
Normal file
@@ -0,0 +1,109 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local SQL = sam.SQL
|
||||
local mp = sam.mp
|
||||
local config = sam.config
|
||||
|
||||
local _config = {}
|
||||
|
||||
function config.sync()
|
||||
sam.set_global("Config", _config, true)
|
||||
end
|
||||
|
||||
local to_hex
|
||||
do
|
||||
local byte = string.byte
|
||||
local gsub = string.gsub
|
||||
local format = string.format
|
||||
|
||||
local hex = function(c)
|
||||
return format("%02X", byte(c))
|
||||
end
|
||||
|
||||
function to_hex(text)
|
||||
return (gsub(text, ".", hex))
|
||||
end
|
||||
end
|
||||
|
||||
function config.set(key, value, force)
|
||||
if not sam.isstring(key) then
|
||||
error("invalid setting name")
|
||||
end
|
||||
|
||||
if not mp.packers[sam.type(value)] then
|
||||
error("not supported value type")
|
||||
end
|
||||
|
||||
local old = _config[key]
|
||||
if not force and value == old then return end
|
||||
|
||||
SQL.FQuery([[
|
||||
REPLACE INTO
|
||||
`sam_config`(
|
||||
`key`,
|
||||
`value`
|
||||
)
|
||||
VALUES
|
||||
({1}, X{2})
|
||||
]], {key, to_hex(mp.pack(value))})
|
||||
|
||||
_config[key] = value
|
||||
config.sync()
|
||||
sam.hook_call("SAM.UpdatedConfig", key, value, old)
|
||||
end
|
||||
|
||||
function config.get(key, default)
|
||||
local value = _config[key]
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
return default
|
||||
end
|
||||
|
||||
config.sync()
|
||||
|
||||
hook.Add("SAM.DatabaseLoaded", "LoadConfig", function()
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
`sam_config`
|
||||
]], function(sam_config)
|
||||
for _, v in ipairs(sam_config) do
|
||||
_config[v.key] = mp.unpack(v.value)
|
||||
end
|
||||
|
||||
config.loaded = true
|
||||
config.sync()
|
||||
hook.Call("SAM.LoadedConfig", nil, _config)
|
||||
end):wait()
|
||||
end)
|
||||
|
||||
sam.netstream.Hook("Config.Set", function(ply, key, value)
|
||||
config.set(key, value)
|
||||
|
||||
value = tostring(value)
|
||||
|
||||
if value == "" then
|
||||
sam.player.send_message(nil, "{A} changed {S Blue} setting to: {S_2 Red}", {
|
||||
A = ply, S = key, S_2 = "none"
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "{A} changed {S Blue} setting to: {S_2}", {
|
||||
A = ply, S = key, S_2 = value
|
||||
})
|
||||
end
|
||||
end, function(ply)
|
||||
return ply:HasPermission("manage_config")
|
||||
end)
|
||||
190
addons/sam/lua/sam/importers/serverguard/main.lua
Normal file
190
addons/sam/lua/sam/importers/serverguard/main.lua
Normal file
@@ -0,0 +1,190 @@
|
||||
--[[
|
||||
| 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 sam, SQL = sam, sam.SQL
|
||||
|
||||
local von = include("sam/importers/serverguard/sam_von.lua")
|
||||
if not von then
|
||||
return sam.print("Download the importer folder again from gmodstore.")
|
||||
end
|
||||
|
||||
local no_bans_msg = "No bans to import from serverguard."
|
||||
local import_bans = function()
|
||||
SQL.TableExists("serverguard_bans", function(exists)
|
||||
if not exists then
|
||||
return sam.print(no_bans_msg)
|
||||
end
|
||||
|
||||
SQL.Query([[
|
||||
DELETE FROM `sam_bans`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`id`,
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
SELECT
|
||||
`id`,
|
||||
`steam_id`,
|
||||
`reason`,
|
||||
'Console',
|
||||
`end_time`
|
||||
FROM
|
||||
`serverguard_bans`
|
||||
]], function()
|
||||
sam.print("Imported bans from serverguard.")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local no_ranks_msg = "No ranks to import from serverguard."
|
||||
local import_ranks = function()
|
||||
SQL.TableExists("serverguard_ranks", function(exists)
|
||||
if not exists then
|
||||
return sam.print(no_ranks_msg)
|
||||
end
|
||||
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
`serverguard_ranks`
|
||||
]], function(serverguard_ranks)
|
||||
for _, v in pairs(serverguard_ranks) do
|
||||
local name = v.unique_id
|
||||
|
||||
if not sam.ranks.is_rank(name) then
|
||||
sam.ranks.add_rank(name, "user", tonumber(v.immunity), tonumber(v.banlimit))
|
||||
end
|
||||
|
||||
local data = sam.isstring(v.data) and util.JSONToTable(v.data)
|
||||
local tools = sam.istable(data) and sam.istable(data.Restrictions) and sam.istable(data.Restrictions.Tools) and data.Restrictions.Tools
|
||||
|
||||
if not tools then continue end
|
||||
|
||||
for tool_name, value in pairs(tools) do
|
||||
if value == false then
|
||||
sam.ranks.take_permission(name, tool_name)
|
||||
else
|
||||
sam.ranks.give_permission(name, tool_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sam.print("Imported ranks from serverguard.")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local no_users_msg = "No users to import from serverguard."
|
||||
|
||||
local import_expires = function(data)
|
||||
if data and #data > 0 then
|
||||
local began = false
|
||||
for _, v in ipairs(data) do
|
||||
local ply_data = v.data and von.deserialize(v.data) or {}
|
||||
if not sam.isnumber(tonumber(ply_data.groupExpire)) then continue end
|
||||
|
||||
if not began then
|
||||
began = true
|
||||
SQL.Begin()
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`expiry_date` = {1}
|
||||
WHERE
|
||||
`steamid` = {2}
|
||||
]], {tonumber(ply_data.groupExpire), v.steam_id})
|
||||
end
|
||||
SQL.Commit(function()
|
||||
sam.print("Imported users from serverguard.")
|
||||
end)
|
||||
else
|
||||
sam.print("Imported users from serverguard.")
|
||||
end
|
||||
end
|
||||
|
||||
local import_users = function()
|
||||
SQL.TableExists("serverguard_users", function(exists)
|
||||
if not exists then
|
||||
return sam.print(no_users_msg)
|
||||
end
|
||||
|
||||
SQL.Query([[
|
||||
DELETE FROM `sam_players`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`id`,
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
SELECT
|
||||
`id`,
|
||||
`steam_id`,
|
||||
`name`,
|
||||
`rank`,
|
||||
0,
|
||||
`last_played`,
|
||||
`last_played`,
|
||||
0
|
||||
FROM
|
||||
`serverguard_users`
|
||||
]], function()
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
`steam_id`,
|
||||
`data`
|
||||
FROM
|
||||
`serverguard_users`
|
||||
]], import_expires)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
if not sam.ranks.ranks_loaded() then
|
||||
return sam.print("Server is connecting to the Database, try again when it connects.")
|
||||
end
|
||||
|
||||
for k, v in ipairs(player.GetAll()) do
|
||||
v:Kick("Importing data.")
|
||||
end
|
||||
|
||||
for k, v in pairs(sam.get_connected_players()) do
|
||||
sam.player.kick_id(k, "Importing data.")
|
||||
end
|
||||
|
||||
hook.Add("CheckPassword", "SAM.ImportingData", function()
|
||||
return false, "Importing data."
|
||||
end)
|
||||
|
||||
sam.print("Starting to import data from serverguard...")
|
||||
timer.Simple(0, function()
|
||||
import_bans()
|
||||
import_ranks()
|
||||
import_users()
|
||||
|
||||
hook.Remove("CheckPassword", "SAM.ImportingData")
|
||||
end)
|
||||
828
addons/sam/lua/sam/importers/serverguard/sam_von.lua
Normal file
828
addons/sam/lua/sam/importers/serverguard/sam_von.lua
Normal file
@@ -0,0 +1,828 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--[[ vON 1.3.4
|
||||
|
||||
Copyright 2012-2014 Alexandru-Mihai Maftei
|
||||
aka Vercas
|
||||
|
||||
GitHub Repository:
|
||||
https://github.com/vercas/vON
|
||||
|
||||
You may use this for any purpose as long as:
|
||||
- You don't remove this copyright notice.
|
||||
- You don't claim this to be your own.
|
||||
- You properly credit the author (Vercas) if you publish your work based on (and/or using) this.
|
||||
|
||||
If you modify the code for any purpose, the above obligations still apply.
|
||||
If you make any interesting modifications, try forking the GitHub repository instead.
|
||||
|
||||
Instead of copying this code over for sharing, rather use the link:
|
||||
https://github.com/vercas/vON/blob/master/von.lua
|
||||
|
||||
The author may not be held responsible for any damage or losses directly or indirectly caused by
|
||||
the use of vON.
|
||||
|
||||
If you disagree with the above, don't use the code.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Thanks to the following people for their contribution:
|
||||
- Divran Suggested improvements for making the code quicker.
|
||||
Suggested an excellent new way of deserializing strings.
|
||||
Lead me to finding an extreme flaw in string parsing.
|
||||
- pennerlord Provided some performance tests to help me improve the code.
|
||||
- Chessnut Reported bug with handling of nil values when deserializing array components.
|
||||
|
||||
- People who contributed on the GitHub repository by reporting bugs, posting fixes, etc.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
The vanilla types supported in this release of vON are:
|
||||
- table
|
||||
- number
|
||||
- boolean
|
||||
- string
|
||||
- nil
|
||||
|
||||
The Garry's Mod-specific types supported in this release are:
|
||||
- Vector
|
||||
- Angle
|
||||
+ Entities:
|
||||
- Entity
|
||||
- Vehicle
|
||||
- Weapon
|
||||
- NPC
|
||||
- Player
|
||||
- NextBot
|
||||
|
||||
These are the types one would normally serialize.
|
||||
|
||||
-----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
New in this version:
|
||||
- Fixed addition of extra entity types. I messed up really badly.
|
||||
--]]
|
||||
|
||||
|
||||
|
||||
local _deserialize, _serialize, _d_meta, _s_meta, d_findVariable, s_anyVariable
|
||||
local sub, gsub, find, insert, concat, error, tonumber, tostring, type, next = string.sub, string.gsub, string.find, table.insert, table.concat, error, tonumber, tostring, type, next
|
||||
|
||||
|
||||
|
||||
--[[ This section contains localized functions which (de)serialize
|
||||
variables according to the types found. ]]
|
||||
|
||||
|
||||
|
||||
-- This is kept away from the table for speed.
|
||||
function d_findVariable(s, i, len, lastType, jobstate)
|
||||
local i, c, typeRead, val = i or 1
|
||||
|
||||
-- Keep looping through the string.
|
||||
while true do
|
||||
-- Stop at the end. Throw an error. This function MUST NOT meet the end!
|
||||
if i > len then
|
||||
error("vON: Reached end of string, cannot form proper variable.")
|
||||
end
|
||||
|
||||
-- Cache the character. Nobody wants to look for the same character ten times.
|
||||
c = sub(s, i, i)
|
||||
|
||||
-- If it just read a type definition, then a variable HAS to come after it.
|
||||
if typeRead then
|
||||
-- Attempt to deserialize a variable of the freshly read type.
|
||||
val, i = _deserialize[lastType](s, i, len, false, jobstate)
|
||||
-- Return the value read, the index of the last processed character, and the type of the last read variable.
|
||||
return val, i, lastType
|
||||
|
||||
-- @ means nil. It should not even appear in the output string of the serializer. Nils are useless to store.
|
||||
elseif c == "@" then
|
||||
return nil, i, lastType
|
||||
|
||||
-- $ means a table reference will follow - a number basically.
|
||||
elseif c == "$" then
|
||||
lastType = "table_reference"
|
||||
typeRead = true
|
||||
|
||||
-- n means a number will follow. Base 10... :C
|
||||
elseif c == "n" then
|
||||
lastType = "number"
|
||||
typeRead = true
|
||||
|
||||
-- b means boolean flags.
|
||||
elseif c == "b" then
|
||||
lastType = "boolean"
|
||||
typeRead = true
|
||||
|
||||
-- ' means the start of a string.
|
||||
elseif c == "'" then
|
||||
lastType = "string"
|
||||
typeRead = true
|
||||
|
||||
-- " means the start of a string prior to version 1.2.0.
|
||||
elseif c == "\"" then
|
||||
lastType = "oldstring"
|
||||
typeRead = true
|
||||
|
||||
-- { means the start of a table!
|
||||
elseif c == "{" then
|
||||
lastType = "table"
|
||||
typeRead = true
|
||||
|
||||
|
||||
--[[ Garry's Mod types go here ]]
|
||||
|
||||
-- e means an entity ID will follow.
|
||||
elseif c == "e" then
|
||||
lastType = "Entity"
|
||||
typeRead = true
|
||||
--[[
|
||||
-- c means a vehicle ID will follow.
|
||||
elseif c == "c" then
|
||||
lastType = "Vehicle"
|
||||
typeRead = true
|
||||
|
||||
-- w means a weapon entity ID will follow.
|
||||
elseif c == "w" then
|
||||
lastType = "Weapon"
|
||||
typeRead = true
|
||||
|
||||
-- x means a NPC ID will follow.
|
||||
elseif c == "x" then
|
||||
lastType = "NPC"
|
||||
typeRead = true
|
||||
--]]
|
||||
-- p means a player ID will follow.
|
||||
-- Kept for backwards compatibility.
|
||||
elseif c == "p" then
|
||||
lastType = "Entity"
|
||||
typeRead = true
|
||||
|
||||
-- v means a vector will follow. 3 numbers.
|
||||
elseif c == "v" then
|
||||
lastType = "Vector"
|
||||
typeRead = true
|
||||
|
||||
-- a means an Euler angle will follow. 3 numbers.
|
||||
elseif c == "a" then
|
||||
lastType = "Angle"
|
||||
typeRead = true
|
||||
|
||||
--[[ Garry's Mod types end here ]]
|
||||
|
||||
|
||||
-- If no type has been found, attempt to deserialize the last type read.
|
||||
elseif lastType then
|
||||
val, i = _deserialize[lastType](s, i, len, false, jobstate)
|
||||
return val, i, lastType
|
||||
|
||||
-- This will occur if the very first character in the vON code is wrong.
|
||||
else
|
||||
error("vON: Malformed data... Can't find a proper type definition. Char#" .. i .. ":" .. c)
|
||||
end
|
||||
|
||||
-- Move the pointer one step forward.
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- This is kept away from the table for speed.
|
||||
-- Yeah, ton of parameters.
|
||||
function s_anyVariable(data, lastType, isNumeric, isKey, isLast, jobstate)
|
||||
local tp = type(data)
|
||||
|
||||
if jobstate[1] and jobstate[2][data] then
|
||||
tp = "table_reference"
|
||||
end
|
||||
|
||||
-- Basically, if the type changes.
|
||||
if lastType ~= tp then
|
||||
-- Remember the new type. Caching the type is useless.
|
||||
lastType = tp
|
||||
|
||||
if _serialize[lastType] then
|
||||
-- Return the serialized data and the (new) last type.
|
||||
-- The second argument, which is true now, means that the data type was just changed.
|
||||
return _serialize[lastType](data, true, isNumeric, isKey, isLast, false, jobstate), lastType
|
||||
else
|
||||
error("vON: No serializer defined for type \"" .. lastType .. "\"!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Otherwise, simply serialize the data.
|
||||
return _serialize[lastType](data, false, isNumeric, isKey, isLast, false, jobstate), lastType
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[ This section contains the tables with the functions necessary
|
||||
for decoding basic Lua data types. ]]
|
||||
|
||||
|
||||
|
||||
_deserialize = {
|
||||
-- Well, tables are very loose...
|
||||
-- The first table doesn't have to begin and end with { and }.
|
||||
["table"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local ret, numeric, i, c, lastType, val, ind, expectValue, key = {}, true, i or 1, nil, nil, nil, 1
|
||||
-- Locals, locals, locals, locals, locals, locals, locals, locals and locals.
|
||||
|
||||
if sub(s, i, i) == "#" then
|
||||
local e = find(s, "#", i + 2, true)
|
||||
|
||||
if e then
|
||||
local id = tonumber(sub(s, i + 1, e - 1))
|
||||
|
||||
if id then
|
||||
if jobstate[1][id] and not jobstate[2] then
|
||||
error("vON: There already is a table of reference #" .. id .. "! Missing an option maybe?")
|
||||
end
|
||||
|
||||
jobstate[1][id] = ret
|
||||
|
||||
i = e + 1
|
||||
else
|
||||
error("vON: Malformed table! Reference ID starting at char #" .. i .. " doesn't contain a number!")
|
||||
end
|
||||
else
|
||||
error("vON: Malformed table! Cannot find end of reference ID start at char #" .. i .. "!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Keep looping.
|
||||
while true do
|
||||
-- Until it meets the end.
|
||||
if i > len then
|
||||
-- Yeah, if the end is unnecessary, it won't spit an error. The main chunk doesn't require an end, for example.
|
||||
if unnecessaryEnd then
|
||||
return ret, i
|
||||
|
||||
-- Otherwise, the data has to be damaged.
|
||||
else
|
||||
error("vON: Reached end of string, incomplete table definition.")
|
||||
end
|
||||
end
|
||||
|
||||
-- Cache the character.
|
||||
c = sub(s, i, i)
|
||||
--print(i, "table char:", c, tostring(unnecessaryEnd))
|
||||
|
||||
-- If it's the end of a table definition, return.
|
||||
if c == "}" then
|
||||
return ret, i
|
||||
|
||||
-- If it's the component separator, switch to key:value pairs.
|
||||
elseif c == "~" then
|
||||
numeric = false
|
||||
|
||||
elseif c == ";" then
|
||||
-- Lol, nothing!
|
||||
-- Remenant from numbers, for faster parsing.
|
||||
|
||||
-- OK, now, if it's on the numeric component, simply add everything encountered.
|
||||
elseif numeric then
|
||||
-- Find a variable and it's value
|
||||
val, i, lastType = d_findVariable(s, i, len, lastType, jobstate)
|
||||
-- Add it to the table.
|
||||
ret[ind] = val
|
||||
|
||||
ind = ind + 1
|
||||
|
||||
-- Otherwise, if it's the key:value component...
|
||||
else
|
||||
-- If a value is expected...
|
||||
if expectValue then
|
||||
-- Read it.
|
||||
val, i, lastType = d_findVariable(s, i, len, lastType, jobstate)
|
||||
-- Add it?
|
||||
ret[key] = val
|
||||
-- Clean up.
|
||||
expectValue, key = false, nil
|
||||
|
||||
-- If it's the separator...
|
||||
elseif c == ":" then
|
||||
-- Expect a value next.
|
||||
expectValue = true
|
||||
|
||||
-- But, if there's a key read already...
|
||||
elseif key then
|
||||
-- Then this is malformed.
|
||||
error("vON: Malformed table... Two keys declared successively? Char#" .. i .. ":" .. c)
|
||||
|
||||
-- Otherwise the key will be read.
|
||||
else
|
||||
-- I love multi-return and multi-assignement.
|
||||
key, i, lastType = d_findVariable(s, i, len, lastType, jobstate)
|
||||
end
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return nil, i
|
||||
end,
|
||||
|
||||
-- Just a number which points to a table.
|
||||
["table_reference"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local i, a = i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
a = find(s, "[;:}~]", i)
|
||||
|
||||
if a then
|
||||
local n = tonumber(sub(s, i, a - 1))
|
||||
|
||||
if n then
|
||||
return jobstate[1][n] or error("vON: Table reference does not point to a (yet) known table!"), a - 1
|
||||
else
|
||||
error("vON: Table reference definition does not contain a valid number!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Using %D breaks identification of negative numbers. :(
|
||||
|
||||
error("vON: Number definition started... Found no end.")
|
||||
end,
|
||||
|
||||
|
||||
-- Numbers are weakly defined.
|
||||
-- The declaration is not very explicit. It'll do it's best to parse the number.
|
||||
-- Has various endings: \n, }, ~, : and ;, some of which will force the table deserializer to go one char backwards.
|
||||
["number"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local i, a = i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
a = find(s, "[;:}~]", i)
|
||||
|
||||
if a then
|
||||
return tonumber(sub(s, i, a - 1)) or error("vON: Number definition does not contain a valid number!"), a - 1
|
||||
end
|
||||
|
||||
-- Using %D breaks identification of negative numbers. :(
|
||||
|
||||
error("vON: Number definition started... Found no end.")
|
||||
end,
|
||||
|
||||
|
||||
-- A boolean is A SINGLE CHARACTER, either 1 for true or 0 for false.
|
||||
-- Any other attempt at boolean declaration will result in a failure.
|
||||
["boolean"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local c = sub(s,i,i)
|
||||
-- Only one character is needed.
|
||||
|
||||
-- If it's 1, then it's true
|
||||
if c == "1" then
|
||||
return true, i
|
||||
|
||||
-- If it's 0, then it's false.
|
||||
elseif c == "0" then
|
||||
return false, i
|
||||
end
|
||||
|
||||
-- Any other supposely "boolean" is just a sign of malformed data.
|
||||
error("vON: Invalid value on boolean type... Char#" .. i .. ": " .. c)
|
||||
end,
|
||||
|
||||
|
||||
-- Strings prior to 1.2.0
|
||||
["oldstring"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local res, i, a = "", i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
while true do
|
||||
a = find(s, "\"", i, true)
|
||||
|
||||
if a then
|
||||
if sub(s, a - 1, a - 1) == "\\" then
|
||||
res = res .. sub(s, i, a - 2) .. "\""
|
||||
i = a + 1
|
||||
else
|
||||
return res .. sub(s, i, a - 2), a
|
||||
end
|
||||
else
|
||||
error("vON: Old string definition started... Found no end.")
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
-- Strings after 1.2.0
|
||||
["string"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local res, i, a = "", i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
while true do
|
||||
a = find(s, "\"", i, true)
|
||||
|
||||
if a then
|
||||
if sub(s, a - 1, a - 1) == "\\" then
|
||||
res = res .. sub(s, i, a - 2) .. "\""
|
||||
i = a + 1
|
||||
else
|
||||
return res .. sub(s, i, a - 1), a
|
||||
end
|
||||
else
|
||||
error("vON: String definition started... Found no end.")
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
_serialize = {
|
||||
-- Uh. Nothing to comment.
|
||||
-- Ton of parameters.
|
||||
-- Makes stuff faster than simply passing it around in locals.
|
||||
-- table.concat works better than normal concatenations WITH LARGE-ISH STRINGS ONLY.
|
||||
["table"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
--print(string.format("data: %s; mustInitiate: %s; isKey: %s; isLast: %s; nice: %s; indent: %s; first: %s", tostring(data), tostring(mustInitiate), tostring(isKey), tostring(isLast), tostring(nice), tostring(indent), tostring(first)))
|
||||
|
||||
local result, keyvals, len, keyvalsLen, keyvalsProgress, val, lastType, newIndent, indentString = {}, {}, #data, 0, 0
|
||||
-- Locals, locals, locals, locals, locals, locals, locals, locals, locals and locals.
|
||||
|
||||
-- First thing to be done is separate the numeric and key:value components of the given table in two tables.
|
||||
-- pairs(data) is slower than next, data as far as my tests tell me.
|
||||
for k, v in next, data do
|
||||
-- Skip the numeric keyz.
|
||||
if type(k) ~= "number" or k < 1 or k > len or (k % 1 ~= 0) then -- k % 1 == 0 is, as proven by personal benchmarks,
|
||||
keyvals[#keyvals + 1] = k -- the quickest way to check if a number is an integer.
|
||||
end -- k % 1 ~= 0 is the fastest way to check if a number
|
||||
end -- is NOT an integer. > is proven slower.
|
||||
|
||||
keyvalsLen = #keyvals
|
||||
|
||||
-- Main chunk - no initial character.
|
||||
if not first then
|
||||
result[#result + 1] = "{"
|
||||
end
|
||||
|
||||
if jobstate[1] and jobstate[1][data] then
|
||||
if jobstate[2][data] then
|
||||
error("vON: Table #" .. jobstate[1][data] .. " written twice..?")
|
||||
end
|
||||
|
||||
result[#result + 1] = "#"
|
||||
result[#result + 1] = jobstate[1][data]
|
||||
result[#result + 1] = "#"
|
||||
|
||||
jobstate[2][data] = true
|
||||
end
|
||||
|
||||
-- Add numeric values.
|
||||
if len > 0 then
|
||||
for i = 1, len do
|
||||
val, lastType = s_anyVariable(data[i], lastType, true, false, i == len and not first, jobstate)
|
||||
result[#result + 1] = val
|
||||
end
|
||||
end
|
||||
|
||||
-- If there are key:value pairs.
|
||||
if keyvalsLen > 0 then
|
||||
-- Insert delimiter.
|
||||
result[#result + 1] = "~"
|
||||
|
||||
-- Insert key:value pairs.
|
||||
for _i = 1, keyvalsLen do
|
||||
keyvalsProgress = keyvalsProgress + 1
|
||||
|
||||
val, lastType = s_anyVariable(keyvals[_i], lastType, false, true, false, jobstate)
|
||||
|
||||
result[#result + 1] = val..":"
|
||||
|
||||
val, lastType = s_anyVariable(data[keyvals[_i]], lastType, false, false, keyvalsProgress == keyvalsLen and not first, jobstate)
|
||||
|
||||
result[#result + 1] = val
|
||||
end
|
||||
end
|
||||
|
||||
-- Main chunk needs no ending character.
|
||||
if not first then
|
||||
result[#result + 1] = "}"
|
||||
end
|
||||
|
||||
return concat(result)
|
||||
end,
|
||||
|
||||
-- Number which points to table.
|
||||
["table_reference"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
data = jobstate[1][data]
|
||||
|
||||
-- If a number hasn't been written before, add the type prefix.
|
||||
if mustInitiate then
|
||||
if isKey or isLast then
|
||||
return "$"..data
|
||||
else
|
||||
return "$"..data..";"
|
||||
end
|
||||
end
|
||||
|
||||
if isKey or isLast then
|
||||
return data
|
||||
else
|
||||
return data..";"
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Normal concatenations is a lot faster with small strings than table.concat
|
||||
-- Also, not so branched-ish.
|
||||
["number"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
-- If a number hasn't been written before, add the type prefix.
|
||||
if mustInitiate then
|
||||
if isKey or isLast then
|
||||
return "n"..data
|
||||
else
|
||||
return "n"..data..";"
|
||||
end
|
||||
end
|
||||
|
||||
if isKey or isLast then
|
||||
return data
|
||||
else
|
||||
return data..";"
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- I hope gsub is fast enough.
|
||||
["string"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
if sub(data, #data, #data) == "\\" then -- Hah, old strings fix this best.
|
||||
return "\"" .. gsub(data, "\"", "\\\"") .. "v\""
|
||||
end
|
||||
|
||||
return "'" .. gsub(data, "\"", "\\\"") .. "\""
|
||||
end,
|
||||
|
||||
|
||||
-- Fastest.
|
||||
["boolean"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
-- Prefix if we must.
|
||||
if mustInitiate then
|
||||
if data then
|
||||
return "b1"
|
||||
else
|
||||
return "b0"
|
||||
end
|
||||
end
|
||||
|
||||
if data then
|
||||
return "1"
|
||||
else
|
||||
return "0"
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Fastest.
|
||||
["nil"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
return "@"
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
--[[ This section handles additions necessary for Garry's Mod. ]]
|
||||
|
||||
|
||||
|
||||
if gmod then -- Luckily, a specific table named after the game is present in Garry's Mod.
|
||||
local Entity = Entity
|
||||
|
||||
|
||||
|
||||
local extra_deserialize = {
|
||||
-- Entities are stored simply by the ID. They're meant to be transfered, not stored anyway.
|
||||
-- Exactly like a number definition, except it begins with "e".
|
||||
["Entity"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local i, a = i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
a = find(s, "[;:}~]", i)
|
||||
|
||||
if a then
|
||||
return Entity(tonumber(sub(s, i, a - 1))), a - 1
|
||||
end
|
||||
|
||||
error("vON: Entity ID definition started... Found no end.")
|
||||
end,
|
||||
|
||||
|
||||
-- A pair of 3 numbers separated by a comma (,).
|
||||
["Vector"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local i, a, x, y, z = i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
a = find(s, ",", i)
|
||||
|
||||
if a then
|
||||
x = tonumber(sub(s, i, a - 1))
|
||||
i = a + 1
|
||||
end
|
||||
|
||||
a = find(s, ",", i)
|
||||
|
||||
if a then
|
||||
y = tonumber(sub(s, i, a - 1))
|
||||
i = a + 1
|
||||
end
|
||||
|
||||
a = find(s, "[;:}~]", i)
|
||||
|
||||
if a then
|
||||
z = tonumber(sub(s, i, a - 1))
|
||||
end
|
||||
|
||||
if x and y and z then
|
||||
return Vector(x, y, z), a - 1
|
||||
end
|
||||
|
||||
error("vON: Vector definition started... Found no end.")
|
||||
end,
|
||||
|
||||
|
||||
-- A pair of 3 numbers separated by a comma (,).
|
||||
["Angle"] = function(s, i, len, unnecessaryEnd, jobstate)
|
||||
local i, a, p, y, r = i or 1
|
||||
-- Locals, locals, locals, locals
|
||||
|
||||
a = find(s, ",", i)
|
||||
|
||||
if a then
|
||||
p = tonumber(sub(s, i, a - 1))
|
||||
i = a + 1
|
||||
end
|
||||
|
||||
a = find(s, ",", i)
|
||||
|
||||
if a then
|
||||
y = tonumber(sub(s, i, a - 1))
|
||||
i = a + 1
|
||||
end
|
||||
|
||||
a = find(s, "[;:}~]", i)
|
||||
|
||||
if a then
|
||||
r = tonumber(sub(s, i, a - 1))
|
||||
end
|
||||
|
||||
if p and y and r then
|
||||
return Angle(p, y, r), a - 1
|
||||
end
|
||||
|
||||
error("vON: Angle definition started... Found no end.")
|
||||
end,
|
||||
}
|
||||
|
||||
local extra_serialize = {
|
||||
-- Same as numbers, except they start with "e" instead of "n".
|
||||
["Entity"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
data = data:EntIndex()
|
||||
|
||||
if mustInitiate then
|
||||
if isKey or isLast then
|
||||
return "e"..data
|
||||
else
|
||||
return "e"..data..";"
|
||||
end
|
||||
end
|
||||
|
||||
if isKey or isLast then
|
||||
return data
|
||||
else
|
||||
return data..";"
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- 3 numbers separated by a comma.
|
||||
["Vector"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
if mustInitiate then
|
||||
if isKey or isLast then
|
||||
return "v"..data.x..","..data.y..","..data.z
|
||||
else
|
||||
return "v"..data.x..","..data.y..","..data.z..";"
|
||||
end
|
||||
end
|
||||
|
||||
if isKey or isLast then
|
||||
return data.x..","..data.y..","..data.z
|
||||
else
|
||||
return data.x..","..data.y..","..data.z..";"
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- 3 numbers separated by a comma.
|
||||
["Angle"] = function(data, mustInitiate, isNumeric, isKey, isLast, first, jobstate)
|
||||
if mustInitiate then
|
||||
if isKey or isLast then
|
||||
return "a"..data.p..","..data.y..","..data.r
|
||||
else
|
||||
return "a"..data.p..","..data.y..","..data.r..";"
|
||||
end
|
||||
end
|
||||
|
||||
if isKey or isLast then
|
||||
return data.p..","..data.y..","..data.r
|
||||
else
|
||||
return data.p..","..data.y..","..data.r..";"
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
for k, v in pairs(extra_serialize) do
|
||||
_serialize[k] = v
|
||||
end
|
||||
|
||||
for k, v in pairs(extra_deserialize) do
|
||||
_deserialize[k] = v
|
||||
end
|
||||
|
||||
local extraEntityTypes = { "Vehicle", "Weapon", "NPC", "Player", "NextBot" }
|
||||
|
||||
for i = 1, #extraEntityTypes do
|
||||
_serialize[extraEntityTypes[i]] = _serialize.Entity
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[ This section exposes the functions of the library. ]]
|
||||
|
||||
|
||||
|
||||
local function checkTableForRecursion(tab, checked, assoc)
|
||||
local id = checked.ID
|
||||
|
||||
if not checked[tab] and not assoc[tab] then
|
||||
assoc[tab] = id
|
||||
checked.ID = id + 1
|
||||
else
|
||||
checked[tab] = true
|
||||
end
|
||||
|
||||
for k, v in pairs(tab) do
|
||||
if type(k) == "table" and not checked[k] then
|
||||
checkTableForRecursion(k, checked, assoc)
|
||||
end
|
||||
|
||||
if type(v) == "table" and not checked[v] then
|
||||
checkTableForRecursion(v, checked, assoc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local _s_table = _serialize.table
|
||||
local _d_table = _deserialize.table
|
||||
|
||||
_d_meta = {
|
||||
__call = function(self, str, allowIdRewriting)
|
||||
if type(str) == "string" then
|
||||
return _d_table(str, nil, #str, true, {{}, allowIdRewriting})
|
||||
end
|
||||
|
||||
error("vON: You must deserialize a string, not a "..type(str))
|
||||
end
|
||||
}
|
||||
_s_meta = {
|
||||
__call = function(self, data, checkRecursion)
|
||||
if type(data) == "table" then
|
||||
if checkRecursion then
|
||||
local assoc, checked = {}, {ID = 1}
|
||||
|
||||
checkTableForRecursion(data, checked, assoc)
|
||||
|
||||
return _s_table(data, nil, nil, nil, nil, true, {assoc, {}})
|
||||
end
|
||||
|
||||
return _s_table(data, nil, nil, nil, nil, true, {false})
|
||||
end
|
||||
|
||||
error("vON: You must serialize a table, not a "..type(data))
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
|
||||
local von = {
|
||||
version = "1.3.4",
|
||||
versionNumber = 1003004, -- Reserving 3 digits per version component.
|
||||
|
||||
deserialize = setmetatable(_deserialize,_d_meta),
|
||||
serialize = setmetatable(_serialize,_s_meta)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return von
|
||||
429
addons/sam/lua/sam/importers/ulx/main.lua
Normal file
429
addons/sam/lua/sam/importers/ulx/main.lua
Normal file
@@ -0,0 +1,429 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
-- PLEASE READ
|
||||
-- PLEASE READ
|
||||
-- PLEASE READ
|
||||
-- PLEASE READ
|
||||
|
||||
-- Set the timing module to import times from when importing users
|
||||
-- Supported modules: "utime"
|
||||
-- If your timing module is not here then STOP and make a support ticket so I can add support for it before you import your users
|
||||
local TIME_MODULE = "utime" -- Set this to nil if you don't want to import times
|
||||
|
||||
-- PLEASE READ
|
||||
-- PLEASE READ
|
||||
-- PLEASE READ
|
||||
-- PLEASE READ
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local sam, SQL = sam, sam.SQL
|
||||
|
||||
local ulib_unescape_backslash = function(s)
|
||||
return s:gsub("\\\\", "\\")
|
||||
end
|
||||
|
||||
local ulib_explode = function(separator, str, limit)
|
||||
local t = {}
|
||||
local curpos = 1
|
||||
while true do -- We have a break in the loop
|
||||
local newpos, endpos = str:find(separator, curpos) -- find the next separator in the string
|
||||
if newpos ~= nil then -- if found then..
|
||||
table.insert(t, str:sub(curpos, newpos - 1)) -- Save it in our table.
|
||||
curpos = endpos + 1 -- save just after where we found it for searching next time.
|
||||
else
|
||||
if limit and #t > limit then
|
||||
return t -- Reached limit
|
||||
end
|
||||
table.insert(t, str:sub(curpos)) -- Save what's left in our array.
|
||||
break
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local ulib_split_args = function(args, start_token, end_token)
|
||||
args = args:Trim()
|
||||
local argv = {}
|
||||
local curpos = 1 -- Our current position within the string
|
||||
local in_quote = false -- Is the text we're currently processing in a quote?
|
||||
start_token = start_token or "\""
|
||||
end_token = end_token or "\""
|
||||
local args_len = args:len()
|
||||
|
||||
while in_quote or curpos <= args_len do
|
||||
local quotepos = args:find(in_quote and end_token or start_token, curpos, true)
|
||||
|
||||
-- The string up to the quote, the whole string if no quote was found
|
||||
local prefix = args:sub(curpos, (quotepos or 0) - 1)
|
||||
if not in_quote then
|
||||
local trimmed = prefix:Trim()
|
||||
if trimmed ~= "" then -- Something to be had from this...
|
||||
local t = ulib_explode("%s+", trimmed)
|
||||
table.Add(argv, t)
|
||||
end
|
||||
else
|
||||
table.insert(argv, prefix)
|
||||
end
|
||||
|
||||
-- If a quote was found, reduce our position and note our state
|
||||
if quotepos ~= nil then
|
||||
curpos = quotepos + 1
|
||||
in_quote = not in_quote
|
||||
else -- Otherwise we've processed the whole string now
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return argv, in_quote
|
||||
end
|
||||
|
||||
local ulib_parse_key_values = function(str, convert)
|
||||
local lines = ulib_explode("\r?\n", str)
|
||||
local parent_tables = {} -- Traces our way to root
|
||||
local current_table, n = {}, 0
|
||||
local is_insert_last_op = false
|
||||
local tmp_string = string.char(01, 02, 03) -- Replacement
|
||||
|
||||
for i, line in ipairs(lines) do
|
||||
local tokens = ulib_split_args((line:gsub("\\\"", tmp_string)))
|
||||
|
||||
for i, token in ipairs(tokens) do
|
||||
tokens[ i ] = ulib_unescape_backslash(token):gsub(tmp_string, "\"")
|
||||
end
|
||||
|
||||
local num_tokens = #tokens
|
||||
|
||||
if num_tokens == 1 then
|
||||
local token = tokens[ 1 ]
|
||||
if token == "{" then
|
||||
local new_table = {}
|
||||
if is_insert_last_op then
|
||||
current_table[ table.remove(current_table) ] = new_table
|
||||
else
|
||||
table.insert(current_table, new_table)
|
||||
end
|
||||
is_insert_last_op = false
|
||||
table.insert(parent_tables, current_table)
|
||||
current_table = new_table
|
||||
|
||||
elseif token == "}" then
|
||||
is_insert_last_op = false
|
||||
current_table = table.remove(parent_tables)
|
||||
if current_table == nil then
|
||||
return nil, "Mismatched recursive tables on line " .. i
|
||||
end
|
||||
|
||||
else
|
||||
is_insert_last_op = true
|
||||
table.insert(current_table, tokens[ 1 ])
|
||||
end
|
||||
|
||||
elseif num_tokens == 2 then
|
||||
is_insert_last_op = false
|
||||
if convert and tonumber(tokens[ 1 ]) then
|
||||
tokens[ 1 ] = tonumber(tokens[ 1 ])
|
||||
end
|
||||
|
||||
current_table[ tokens[ 1 ] ] = tokens[ 2 ]
|
||||
|
||||
elseif num_tokens > 2 then
|
||||
return nil, "Bad input on line " .. i
|
||||
end
|
||||
end
|
||||
|
||||
if #parent_tables ~= 0 then
|
||||
return nil, "Mismatched recursive tables"
|
||||
end
|
||||
|
||||
if convert and n == 1 and
|
||||
type(current_table.Out) == "table" then -- If we caught a stupid garry-wrapper
|
||||
|
||||
current_table = current_table.Out
|
||||
end
|
||||
|
||||
return current_table
|
||||
end
|
||||
|
||||
local ulib_remove_comment_header = function(data, comment_char)
|
||||
comment_char = comment_char or ";"
|
||||
local lines = ulib_explode("\r?\n", data)
|
||||
local end_comment_line = 0
|
||||
for _, line in ipairs(lines) do
|
||||
local trimmed = line:Trim()
|
||||
if trimmed == "" or trimmed:sub(1, 1) == comment_char then
|
||||
end_comment_line = end_comment_line + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local not_comment = table.concat(lines, "\n", end_comment_line + 1)
|
||||
return not_comment:Trim()
|
||||
end
|
||||
|
||||
local no_bans_msg = "No bans to import from ulx."
|
||||
local import_bans = function()
|
||||
sam.print("Importing bans...")
|
||||
|
||||
if not sql.TableExists("ulib_bans") then
|
||||
return sam.print(no_bans_msg)
|
||||
end
|
||||
|
||||
local bans = sql.Query("SELECT `steamid`, `unban`, `reason`, `admin` FROM `ulib_bans`")
|
||||
|
||||
if not sam.istable(bans) then
|
||||
return sam.print(no_bans_msg)
|
||||
end
|
||||
|
||||
local bans_count = #bans
|
||||
|
||||
if bans_count == 0 then
|
||||
return sam.print(no_bans_msg)
|
||||
end
|
||||
|
||||
local began, imported_count = false, 0
|
||||
|
||||
for i = 1, bans_count do
|
||||
local ban = bans[i]
|
||||
local steamid = ban.steamid
|
||||
|
||||
if sam.is_steamid64(steamid) then
|
||||
steamid = util.SteamIDFrom64(ban.steamid)
|
||||
end
|
||||
|
||||
if sam.is_steamid(steamid) then
|
||||
if not began then
|
||||
began = true
|
||||
SQL.Begin()
|
||||
SQL.Query([[
|
||||
DELETE FROM `sam_bans`
|
||||
]])
|
||||
end
|
||||
|
||||
local name, reason, admin, unban_date = ban.name, ban.reason, ban.admin, tonumber(ban.unban)
|
||||
|
||||
if name == "NULL" then
|
||||
name = ""
|
||||
end
|
||||
|
||||
if reason == "NULL" then
|
||||
reason = "none"
|
||||
end
|
||||
|
||||
if sam.isstring(admin) and admin ~= "NULL" and admin ~= "(Console)" then
|
||||
admin = admin:match("%(STEAM_%w:%w:%w*%)"):sub(2, -2)
|
||||
else
|
||||
admin = "Console"
|
||||
end
|
||||
|
||||
if not sam.isnumber(unban_date) or unban_date < 0 then
|
||||
unban_date = 0
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4})
|
||||
]], {steamid, reason, admin, unban_date})
|
||||
|
||||
imported_count = imported_count + 1
|
||||
end
|
||||
end
|
||||
|
||||
if began then
|
||||
SQL.Commit(function()
|
||||
sam.print("Imported " .. imported_count .. " ban(s).")
|
||||
end)
|
||||
else
|
||||
sam.print(no_bans_msg)
|
||||
end
|
||||
end
|
||||
|
||||
local groups_path = "ulib/groups.txt"
|
||||
local no_ranks_msg = "No ranks to import from ulx."
|
||||
|
||||
local imported_ranks
|
||||
|
||||
local function add_rank(rank, info, ranks)
|
||||
if not sam.ranks.is_rank(rank) then
|
||||
local inherit = info.inherit_from
|
||||
if inherit and ranks[inherit] and not sam.ranks.is_rank(inherit) then
|
||||
add_rank(inherit, ranks[inherit], ranks)
|
||||
end
|
||||
imported_ranks = imported_ranks + 1
|
||||
sam.ranks.add_rank(rank, inherit)
|
||||
end
|
||||
end
|
||||
|
||||
local import_ranks = function()
|
||||
sam.print("Importing ranks...")
|
||||
|
||||
if not file.Exists(groups_path, "DATA") then
|
||||
return sam.print(no_ranks_msg)
|
||||
end
|
||||
|
||||
local ranks_text = file.Read(groups_path, "DATA")
|
||||
|
||||
if not sam.isstring(ranks_text) then
|
||||
return sam.print(no_ranks_msg)
|
||||
end
|
||||
|
||||
local ranks, err = ulib_parse_key_values(ulib_remove_comment_header(ranks_text, "/"))
|
||||
if not ranks then
|
||||
return sam.print("ULX ranks file was not formatted correctly, make a support ticket. (include the error message if there is one)" .. (err and (" | error: " .. err) or ""))
|
||||
end
|
||||
|
||||
imported_ranks = 0
|
||||
|
||||
for rank, info in pairs(ranks) do
|
||||
if not sam.ranks.is_rank(rank) then
|
||||
add_rank(rank, info, ranks)
|
||||
end
|
||||
end
|
||||
|
||||
if imported_ranks == 0 then
|
||||
sam.print(no_ranks_msg)
|
||||
else
|
||||
sam.print("Imported " .. imported_ranks .. " rank(s).")
|
||||
end
|
||||
end
|
||||
|
||||
local users_path = "ulib/users.txt"
|
||||
local no_users_msg = "No users to import from ulx."
|
||||
|
||||
local import_users = function()
|
||||
sam.print("Importing users...")
|
||||
|
||||
if not file.Exists(users_path, "DATA") then
|
||||
return sam.print(no_users_msg)
|
||||
end
|
||||
|
||||
local users_text = file.Read(users_path, "DATA")
|
||||
|
||||
if not sam.isstring(users_text) then
|
||||
return sam.print(no_users_msg)
|
||||
end
|
||||
|
||||
local users, err = ulib_parse_key_values(ulib_remove_comment_header(users_text, "/"))
|
||||
if not users then
|
||||
return sam.print("ULX users file was not formatted correctly, make a support ticket. (include the error message if there is one)" .. (err and (" | error: " .. err) or ""))
|
||||
end
|
||||
|
||||
local current_time = os.time()
|
||||
local imported_users = 0
|
||||
|
||||
local times = false
|
||||
|
||||
if TIME_MODULE == "utime" then
|
||||
local utime = sql.Query("SELECT `player`, `lastvisit`, `totaltime` FROM `utime`")
|
||||
if sam.istable(utime) then
|
||||
times = {}
|
||||
for i = 1, #utime do
|
||||
local v = utime[i]
|
||||
times[v.player] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local began = false
|
||||
|
||||
for steamid, info in pairs(users) do
|
||||
if sam.is_steamid64(steamid) then
|
||||
steamid = util.SteamIDFrom64(steamid)
|
||||
end
|
||||
|
||||
if sam.is_steamid(steamid) then
|
||||
if not began then
|
||||
began = true
|
||||
SQL.Begin()
|
||||
SQL.Query([[
|
||||
DELETE FROM `sam_players`
|
||||
]])
|
||||
end
|
||||
|
||||
local first_join, last_join, play_time = current_time, current_time, 0
|
||||
|
||||
if times and TIME_MODULE == "utime" then
|
||||
local utime_user = times[util.CRC("gm_" .. steamid .. "_gm")]
|
||||
if utime_user then
|
||||
last_join, play_time = utime_user.lastvisit, math.floor(utime_user.totaltime)
|
||||
first_join = last_join
|
||||
end
|
||||
end
|
||||
|
||||
local rank = info.group
|
||||
if not rank or not sam.ranks.is_rank(rank) then
|
||||
rank = "user"
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}, {5}, {6}, {7})
|
||||
]], {steamid, info.name or "", rank, 0, first_join, last_join, play_time})
|
||||
|
||||
imported_users = imported_users + 1
|
||||
end
|
||||
end
|
||||
|
||||
if began then
|
||||
SQL.Commit(function()
|
||||
sam.print("Imported " .. imported_users .. " user(s).")
|
||||
end)
|
||||
else
|
||||
sam.print(no_users_msg)
|
||||
end
|
||||
end
|
||||
|
||||
if not sam.ranks.ranks_loaded() then
|
||||
return sam.print("Server is connecting to the Database, try again when it connects.")
|
||||
end
|
||||
|
||||
for k, v in ipairs(player.GetAll()) do
|
||||
v:Kick("Importing data.")
|
||||
end
|
||||
|
||||
for k, v in pairs(sam.get_connected_players()) do
|
||||
sam.player.kick_id(k, "Importing data.")
|
||||
end
|
||||
|
||||
hook.Add("CheckPassword", "SAM.ImportingData", function()
|
||||
return false, "Importing data."
|
||||
end)
|
||||
|
||||
sam.print("Starting to import data from ulx...")
|
||||
timer.Simple(0, function()
|
||||
import_bans()
|
||||
import_ranks()
|
||||
import_users()
|
||||
|
||||
hook.Remove("CheckPassword", "SAM.ImportingData")
|
||||
end)
|
||||
213
addons/sam/lua/sam/importers/xadmin/main.lua
Normal file
213
addons/sam/lua/sam/importers/xadmin/main.lua
Normal file
@@ -0,0 +1,213 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--
|
||||
-- CONFIG
|
||||
--
|
||||
|
||||
local UsersTableName = "xadminusers"
|
||||
local GroupsTableName = "xadmingroups"
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local sam, SQL = sam, sam.SQL
|
||||
|
||||
local no_bans_msg = "No bans to import from xadmin."
|
||||
local import_bans = function()
|
||||
SQL.TableExists("xadminbans", function(exists)
|
||||
if not exists then
|
||||
return sam.print(no_bans_msg)
|
||||
end
|
||||
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
`SteamID`,
|
||||
`Admin`,
|
||||
`Reason`,
|
||||
`EndTime`
|
||||
FROM
|
||||
`xadminbans`
|
||||
]], function(xadmin_bans)
|
||||
SQL.Begin()
|
||||
|
||||
SQL.Query([[
|
||||
DELETE FROM `sam_bans`
|
||||
]])
|
||||
|
||||
local args, steamid, admin = {}, nil, nil
|
||||
for _, v in ipairs(xadmin_bans) do
|
||||
steamid, admin = util.SteamIDFrom64(v.SteamID), v.Admin
|
||||
|
||||
if admin == "CONSOLE" then
|
||||
admin = "Console"
|
||||
else
|
||||
admin = util.SteamIDFrom64(admin)
|
||||
end
|
||||
|
||||
args[1] = steamid
|
||||
args[2] = v.Reason
|
||||
args[3] = admin
|
||||
args[4] = v.EndTime
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4})
|
||||
]], args)
|
||||
end
|
||||
|
||||
SQL.Commit(function()
|
||||
sam.print("Imported bans from xadmin.")
|
||||
end)
|
||||
end)
|
||||
end):wait()
|
||||
end
|
||||
|
||||
local get_rank = function(rank)
|
||||
if rank == "Super Admin" then
|
||||
return "superadmin"
|
||||
elseif rank == "Admin" then
|
||||
return "admin"
|
||||
elseif rank == "Moderator" then
|
||||
return "moderator"
|
||||
elseif rank == "User" then
|
||||
return "user"
|
||||
end
|
||||
return rank
|
||||
end
|
||||
|
||||
local no_ranks_msg = "No ranks to import from xadmin."
|
||||
local import_ranks = function()
|
||||
SQL.TableExists(GroupsTableName, function(exists)
|
||||
if not exists then
|
||||
return sam.print(no_ranks_msg)
|
||||
end
|
||||
|
||||
SQL.Query(([[
|
||||
SELECT
|
||||
`Name`,
|
||||
`Power`
|
||||
FROM
|
||||
`%s`
|
||||
]]):format(GroupsTableName), function(xadmin_ranks)
|
||||
for _, v in ipairs(xadmin_ranks) do
|
||||
local name = get_rank(v.Name)
|
||||
|
||||
if not sam.ranks.is_rank(name) then
|
||||
sam.ranks.add_rank(name, "user", tonumber(v.Power), 20160)
|
||||
end
|
||||
end
|
||||
|
||||
sam.print("Imported ranks from xadmin.")
|
||||
end):wait()
|
||||
end):wait()
|
||||
end
|
||||
|
||||
local no_users_msg = "No users to import from xadmin."
|
||||
local import_users = function()
|
||||
SQL.TableExists(UsersTableName, function(exists)
|
||||
if not exists then
|
||||
return sam.print(no_users_msg)
|
||||
end
|
||||
|
||||
SQL.Query(([[
|
||||
SELECT
|
||||
`%s`.`SteamID`,
|
||||
`%s`.`AccessLevel`,
|
||||
IFNULL(SUM(`time`), 0) `total`
|
||||
FROM
|
||||
`%s`
|
||||
|
||||
LEFT OUTER JOIN `xadmintimetracking` `times`
|
||||
on `%s`.`SteamID` = `times`.`SteamID`
|
||||
|
||||
GROUP BY
|
||||
`%s`.`SteamID`
|
||||
]]):format(UsersTableName, UsersTableName, UsersTableName, UsersTableName, UsersTableName), function(xadmin_players)
|
||||
SQL.Begin()
|
||||
|
||||
SQL.Query([[
|
||||
DELETE FROM `sam_players`
|
||||
]])
|
||||
|
||||
local args = {}
|
||||
args[2] = ""
|
||||
args[4] = 0
|
||||
args[5] = os.time()
|
||||
args[6] = os.time()
|
||||
|
||||
for k, v in ipairs(xadmin_players) do
|
||||
args[1] = v.SteamID
|
||||
|
||||
local rank = get_rank(v.AccessLevel)
|
||||
if not sam.ranks.is_rank(rank) then
|
||||
rank = "user"
|
||||
end
|
||||
|
||||
args[3] = rank
|
||||
args[7] = v.total
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}, {5}, {6}, {7})
|
||||
]], args)
|
||||
end
|
||||
|
||||
SQL.Commit(function()
|
||||
sam.print("Imported users from xadmin.")
|
||||
end)
|
||||
end):wait()
|
||||
end):wait()
|
||||
end
|
||||
|
||||
if not sam.ranks.ranks_loaded() then
|
||||
return sam.print("Server is connecting to the Database, try again when it connects.")
|
||||
end
|
||||
|
||||
for k, v in ipairs(player.GetAll()) do
|
||||
v:Kick("Importing data.")
|
||||
end
|
||||
|
||||
for k, v in pairs(sam.get_connected_players()) do
|
||||
sam.player.kick_id(k, "Importing data.")
|
||||
end
|
||||
|
||||
hook.Add("CheckPassword", "SAM.ImportingData", function()
|
||||
return false, "Importing data."
|
||||
end)
|
||||
|
||||
sam.print("Starting to import data from xadmin...")
|
||||
timer.Simple(0, function()
|
||||
import_bans()
|
||||
import_ranks()
|
||||
import_users()
|
||||
|
||||
hook.Remove("CheckPassword", "SAM.ImportingData")
|
||||
end)
|
||||
30
addons/sam/lua/sam/libs/message_pack/COPYRIGHT
Normal file
30
addons/sam/lua/sam/libs/message_pack/COPYRIGHT
Normal file
@@ -0,0 +1,30 @@
|
||||
lua-MessagePack License
|
||||
--------------------------
|
||||
|
||||
lua-MessagePack is licensed under the terms of the MIT/X11 license reproduced below.
|
||||
|
||||
===============================================================================
|
||||
|
||||
Copyright (C) 2012-2019 Francois Perrad.
|
||||
|
||||
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.
|
||||
|
||||
===============================================================================
|
||||
|
||||
(end of COPYRIGHT)
|
||||
874
addons/sam/lua/sam/libs/message_pack/sh_messagepack.lua
Normal file
874
addons/sam/lua/sam/libs/message_pack/sh_messagepack.lua
Normal file
@@ -0,0 +1,874 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
--
|
||||
-- lua-MessagePack : <https://fperrad.frama.io/lua-MessagePack/>
|
||||
--
|
||||
local SIZEOF_NUMBER = string.pack and #string.pack('n', 0.0) or 8
|
||||
local maxinteger
|
||||
local mininteger
|
||||
local assert = assert
|
||||
local error = error
|
||||
local pairs = pairs
|
||||
local pcall = pcall
|
||||
local setmetatable = setmetatable
|
||||
local tostring = tostring
|
||||
local char = string.char
|
||||
local format = string.format
|
||||
local floor = math.floor
|
||||
local tointeger = floor
|
||||
local frexp = math.frexp
|
||||
local ldexp = math.ldexp
|
||||
local huge = math.huge
|
||||
local tconcat = table.concat
|
||||
local type = sam.type
|
||||
local isnumber = sam.isnumber
|
||||
local _ENV = nil
|
||||
local m = {}
|
||||
|
||||
--[[ debug only
|
||||
local function hexadump (s)
|
||||
return (s:gsub('.', function (c) return format('%02X ', c:byte()) end))
|
||||
end
|
||||
m.hexadump = hexadump
|
||||
--]]
|
||||
local function argerror(caller, narg, extramsg)
|
||||
error("bad argument #" .. tostring(narg) .. " to " .. caller .. " (" .. extramsg .. ")")
|
||||
end
|
||||
|
||||
local function typeerror(caller, narg, arg, tname)
|
||||
argerror(caller, narg, tname .. " expected, got " .. type(arg))
|
||||
end
|
||||
|
||||
local function checktype(caller, narg, arg, tname)
|
||||
if type(arg) ~= tname then
|
||||
typeerror(caller, narg, arg, tname)
|
||||
end
|
||||
end
|
||||
|
||||
local packers = setmetatable({}, {
|
||||
__index = function(t, k)
|
||||
if k == 1 then return end -- allows ipairs
|
||||
error("pack '" .. k .. "' is unimplemented")
|
||||
end
|
||||
})
|
||||
|
||||
m.packers = packers
|
||||
|
||||
packers["nil"] = function(buffer)
|
||||
buffer[#buffer + 1] = char(0xC0) -- nil
|
||||
end
|
||||
|
||||
packers["boolean"] = function(buffer, bool)
|
||||
if bool then
|
||||
buffer[#buffer + 1] = char(0xC3) -- true
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xC2) -- false
|
||||
end
|
||||
end
|
||||
|
||||
packers["string_compat"] = function(buffer, str)
|
||||
local n = #str
|
||||
|
||||
if n <= 0x1F then
|
||||
buffer[#buffer + 1] = char(0xA0 + n) -- fixstr
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xDA, floor(n / 0x100), n % 0x100) -- str16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xDB, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- str32
|
||||
else
|
||||
error"overflow in pack 'string_compat'"
|
||||
end
|
||||
|
||||
buffer[#buffer + 1] = str
|
||||
end
|
||||
|
||||
packers["_string"] = function(buffer, str)
|
||||
local n = #str
|
||||
|
||||
if n <= 0x1F then
|
||||
buffer[#buffer + 1] = char(0xA0 + n) -- fixstr
|
||||
elseif n <= 0xFF then
|
||||
buffer[#buffer + 1] = char(0xD9, n) -- str8
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xDA, floor(n / 0x100), n % 0x100) -- str16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xDB, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- str32
|
||||
else
|
||||
error("overflow in pack 'string'")
|
||||
end
|
||||
|
||||
buffer[#buffer + 1] = str
|
||||
end
|
||||
|
||||
packers["binary"] = function(buffer, str)
|
||||
local n = #str
|
||||
|
||||
if n <= 0xFF then
|
||||
buffer[#buffer + 1] = char(0xC4, n) -- bin8
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xC5, floor(n / 0x100), n % 0x100) -- bin16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xC6, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- bin32
|
||||
else
|
||||
error("overflow in pack 'binary'")
|
||||
end
|
||||
|
||||
buffer[#buffer + 1] = str
|
||||
end
|
||||
|
||||
local set_string = function(str)
|
||||
if str == "string_compat" then
|
||||
packers["string"] = packers["string_compat"]
|
||||
elseif str == "string" then
|
||||
packers["string"] = packers["_string"]
|
||||
elseif str == "binary" then
|
||||
packers["string"] = packers["binary"]
|
||||
else
|
||||
argerror("set_string", 1, "invalid option '" .. str .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
m.set_string = set_string
|
||||
|
||||
packers["map"] = function(buffer, tbl, n)
|
||||
if n <= 0x0F then
|
||||
buffer[#buffer + 1] = char(0x80 + n) -- fixmap
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xDE, floor(n / 0x100), n % 0x100) -- map16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xDF, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- map32
|
||||
else
|
||||
error("overflow in pack 'map'")
|
||||
end
|
||||
|
||||
for k, v in pairs(tbl) do
|
||||
packers[type(k)](buffer, k)
|
||||
packers[type(v)](buffer, v)
|
||||
end
|
||||
end
|
||||
|
||||
packers["array"] = function(buffer, tbl, n)
|
||||
if n <= 0x0F then
|
||||
buffer[#buffer + 1] = char(0x90 + n) -- fixarray
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xDC, floor(n / 0x100), n % 0x100) -- array16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xDD, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- array32
|
||||
else
|
||||
error("overflow in pack 'array'")
|
||||
end
|
||||
|
||||
for i = 1, n do
|
||||
local v = tbl[i]
|
||||
packers[type(v)](buffer, v)
|
||||
end
|
||||
end
|
||||
|
||||
local set_array = function(array)
|
||||
if array == "without_hole" then
|
||||
packers["_table"] = function(buffer, tbl)
|
||||
local is_map, n, max = false, 0, 0
|
||||
|
||||
for k in pairs(tbl) do
|
||||
if isnumber(k) and k > 0 then
|
||||
if k > max then
|
||||
max = k
|
||||
end
|
||||
else
|
||||
is_map = true
|
||||
end
|
||||
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
-- there are holes
|
||||
if max ~= n then
|
||||
is_map = true
|
||||
end
|
||||
|
||||
if is_map then
|
||||
packers["map"](buffer, tbl, n)
|
||||
else
|
||||
packers["array"](buffer, tbl, n)
|
||||
end
|
||||
end
|
||||
elseif array == "with_hole" then
|
||||
packers["_table"] = function(buffer, tbl)
|
||||
local is_map, n, max = false, 0, 0
|
||||
|
||||
for k in pairs(tbl) do
|
||||
if isnumber(k) and k > 0 then
|
||||
if k > max then
|
||||
max = k
|
||||
end
|
||||
else
|
||||
is_map = true
|
||||
end
|
||||
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
if is_map then
|
||||
packers["map"](buffer, tbl, n)
|
||||
else
|
||||
packers["array"](buffer, tbl, max)
|
||||
end
|
||||
end
|
||||
elseif array == "always_as_map" then
|
||||
packers["_table"] = function(buffer, tbl)
|
||||
local n = 0
|
||||
|
||||
for k in pairs(tbl) do
|
||||
n = n + 1
|
||||
end
|
||||
|
||||
packers["map"](buffer, tbl, n)
|
||||
end
|
||||
else
|
||||
argerror("set_array", 1, "invalid option '" .. array .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
m.set_array = set_array
|
||||
|
||||
packers["table"] = function(buffer, tbl)
|
||||
packers["_table"](buffer, tbl)
|
||||
end
|
||||
|
||||
packers["unsigned"] = function(buffer, n)
|
||||
if n >= 0 then
|
||||
if n <= 0x7F then
|
||||
buffer[#buffer + 1] = char(n) -- fixnum_pos
|
||||
elseif n <= 0xFF then
|
||||
buffer[#buffer + 1] = char(0xCC, n) -- uint8
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xCD, floor(n / 0x100), n % 0x100) -- uint16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xCE, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- uint32
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xCF, 0, floor(n / 0x1000000000000) % 0x100, floor(n / 0x10000000000) % 0x100, floor(n / 0x100000000) % 0x100, floor(n / 0x1000000) % 0x100, floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- uint64 -- only 53 bits from double
|
||||
end
|
||||
else
|
||||
if n >= -0x20 then
|
||||
buffer[#buffer + 1] = char(0x100 + n) -- fixnum_neg
|
||||
elseif n >= -0x80 then
|
||||
buffer[#buffer + 1] = char(0xD0, 0x100 + n) -- int8
|
||||
elseif n >= -0x8000 then
|
||||
n = 0x10000 + n
|
||||
buffer[#buffer + 1] = char(0xD1, floor(n / 0x100), n % 0x100) -- int16
|
||||
elseif n >= -0x80000000 then
|
||||
n = 4294967296.0 + n
|
||||
buffer[#buffer + 1] = char(0xD2, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- int32
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xD3, 0xFF, floor(n / 0x1000000000000) % 0x100, floor(n / 0x10000000000) % 0x100, floor(n / 0x100000000) % 0x100, floor(n / 0x1000000) % 0x100, floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- int64 -- only 53 bits from double
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
packers["signed"] = function(buffer, n)
|
||||
if n >= 0 then
|
||||
if n <= 0x7F then
|
||||
buffer[#buffer + 1] = char(n) -- fixnum_pos
|
||||
elseif n <= 0x7FFF then
|
||||
buffer[#buffer + 1] = char(0xD1, floor(n / 0x100), n % 0x100) -- int16
|
||||
elseif n <= 0x7FFFFFFF then
|
||||
buffer[#buffer + 1] = char(0xD2, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- int32
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xD3, 0, floor(n / 0x1000000000000) % 0x100, floor(n / 0x10000000000) % 0x100, floor(n / 0x100000000) % 0x100, floor(n / 0x1000000) % 0x100, floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- int64 -- only 53 bits from double
|
||||
end
|
||||
else
|
||||
if n >= -0x20 then
|
||||
buffer[#buffer + 1] = char(0xE0 + 0x20 + n) -- fixnum_neg
|
||||
elseif n >= -0x80 then
|
||||
buffer[#buffer + 1] = char(0xD0, 0x100 + n) -- int8
|
||||
elseif n >= -0x8000 then
|
||||
n = 0x10000 + n
|
||||
buffer[#buffer + 1] = char(0xD1, floor(n / 0x100), n % 0x100) -- int16
|
||||
elseif n >= -0x80000000 then
|
||||
n = 4294967296.0 + n
|
||||
buffer[#buffer + 1] = char(0xD2, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- int32
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xD3, 0xFF, floor(n / 0x1000000000000) % 0x100, floor(n / 0x10000000000) % 0x100, floor(n / 0x100000000) % 0x100, floor(n / 0x1000000) % 0x100, floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100) -- int64 -- only 53 bits from double
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local set_integer = function(integer)
|
||||
if integer == "unsigned" then
|
||||
packers["integer"] = packers["unsigned"]
|
||||
elseif integer == "signed" then
|
||||
packers["integer"] = packers["signed"]
|
||||
else
|
||||
argerror("set_integer", 1, "invalid option '" .. integer .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
m.set_integer = set_integer
|
||||
|
||||
packers["float"] = function(buffer, n)
|
||||
local sign = 0
|
||||
|
||||
if n < 0.0 then
|
||||
sign = 0x80
|
||||
n = -n
|
||||
end
|
||||
|
||||
local mant, expo = frexp(n)
|
||||
|
||||
if mant ~= mant then
|
||||
buffer[#buffer + 1] = char(0xCA, 0xFF, 0x88, 0x00, 0x00) -- nan
|
||||
elseif mant == huge or expo > 0x80 then
|
||||
if sign == 0 then
|
||||
buffer[#buffer + 1] = char(0xCA, 0x7F, 0x80, 0x00, 0x00) -- inf
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xCA, 0xFF, 0x80, 0x00, 0x00) -- -inf
|
||||
end
|
||||
elseif (mant == 0.0 and expo == 0) or expo < -0x7E then
|
||||
buffer[#buffer + 1] = char(0xCA, sign, 0x00, 0x00, 0x00) -- zero
|
||||
else
|
||||
expo = expo + 0x7E
|
||||
mant = floor((mant * 2.0 - 1.0) * ldexp(0.5, 24))
|
||||
buffer[#buffer + 1] = char(0xCA, sign + floor(expo / 0x2), (expo % 0x2) * 0x80 + floor(mant / 0x10000), floor(mant / 0x100) % 0x100, mant % 0x100)
|
||||
end
|
||||
end
|
||||
|
||||
packers["double"] = function(buffer, n)
|
||||
local sign = 0
|
||||
|
||||
if n < 0.0 then
|
||||
sign = 0x80
|
||||
n = -n
|
||||
end
|
||||
|
||||
local mant, expo = frexp(n)
|
||||
|
||||
if mant ~= mant then
|
||||
buffer[#buffer + 1] = char(0xCB, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) -- nan
|
||||
elseif mant == huge or expo > 0x400 then
|
||||
if sign == 0 then
|
||||
buffer[#buffer + 1] = char(0xCB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) -- inf
|
||||
else
|
||||
buffer[#buffer + 1] = char(0xCB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) -- -inf
|
||||
end
|
||||
elseif (mant == 0.0 and expo == 0) or expo < -0x3FE then
|
||||
buffer[#buffer + 1] = char(0xCB, sign, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) -- zero
|
||||
else
|
||||
expo = expo + 0x3FE
|
||||
mant = floor((mant * 2.0 - 1.0) * ldexp(0.5, 53))
|
||||
buffer[#buffer + 1] = char(0xCB, sign + floor(expo / 0x10), (expo % 0x10) * 0x10 + floor(mant / 0x1000000000000), floor(mant / 0x10000000000) % 0x100, floor(mant / 0x100000000) % 0x100, floor(mant / 0x1000000) % 0x100, floor(mant / 0x10000) % 0x100, floor(mant / 0x100) % 0x100, mant % 0x100)
|
||||
end
|
||||
end
|
||||
|
||||
local set_number = function(number)
|
||||
if number == "float" then
|
||||
packers["number"] = function(buffer, n)
|
||||
if floor(n) == n and n < maxinteger and n > mininteger then
|
||||
packers["integer"](buffer, n)
|
||||
else
|
||||
packers["float"](buffer, n)
|
||||
end
|
||||
end
|
||||
elseif number == "double" then
|
||||
packers["number"] = function(buffer, n)
|
||||
if floor(n) == n and n < maxinteger and n > mininteger then
|
||||
packers["integer"](buffer, n)
|
||||
else
|
||||
packers["double"](buffer, n)
|
||||
end
|
||||
end
|
||||
else
|
||||
argerror("set_number", 1, "invalid option '" .. number .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
m.set_number = set_number
|
||||
|
||||
for k = 0, 4 do
|
||||
local n = tointeger(2 ^ k)
|
||||
local fixext = 0xD4 + k
|
||||
|
||||
packers["fixext" .. tostring(n)] = function(buffer, tag, data)
|
||||
assert(#data == n, "bad length for fixext" .. tostring(n))
|
||||
buffer[#buffer + 1] = char(fixext, tag < 0 and tag + 0x100 or tag)
|
||||
buffer[#buffer + 1] = data
|
||||
end
|
||||
end
|
||||
|
||||
packers["ext"] = function(buffer, tag, data)
|
||||
local n = #data
|
||||
|
||||
if n <= 0xFF then
|
||||
buffer[#buffer + 1] = char(0xC7, n, tag < 0 and tag + 0x100 or tag) -- ext8
|
||||
elseif n <= 0xFFFF then
|
||||
buffer[#buffer + 1] = char(0xC8, floor(n / 0x100), n % 0x100, tag < 0 and tag + 0x100 or tag) -- ext16
|
||||
elseif n <= 4294967295.0 then
|
||||
buffer[#buffer + 1] = char(0xC9, floor(n / 0x1000000), floor(n / 0x10000) % 0x100, floor(n / 0x100) % 0x100, n % 0x100, tag < 0 and tag + 0x100 or tag) -- ext&32
|
||||
else
|
||||
error("overflow in pack 'ext'")
|
||||
end
|
||||
|
||||
buffer[#buffer + 1] = data
|
||||
end
|
||||
|
||||
function m.pack(data)
|
||||
local buffer = {}
|
||||
packers[type(data)](buffer, data)
|
||||
|
||||
return tconcat(buffer)
|
||||
end
|
||||
|
||||
local unpackers -- forward declaration
|
||||
|
||||
local function unpack_cursor(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i > j then
|
||||
c:underflow(i)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local val = s:byte(i)
|
||||
c.i = i + 1
|
||||
|
||||
return unpackers[val](c, val)
|
||||
end
|
||||
|
||||
m.unpack_cursor = unpack_cursor
|
||||
|
||||
local function unpack_str(c, n)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
local e = i + n - 1
|
||||
|
||||
if e > j or n < 0 then
|
||||
c:underflow(e)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
e = i + n - 1
|
||||
end
|
||||
|
||||
c.i = i + n
|
||||
|
||||
return s:sub(i, e)
|
||||
end
|
||||
|
||||
local function unpack_array(c, n)
|
||||
local t = {}
|
||||
|
||||
for i = 1, n do
|
||||
t[i] = unpack_cursor(c)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
local function unpack_map(c, n)
|
||||
local t = {}
|
||||
|
||||
for i = 1, n do
|
||||
local k = unpack_cursor(c)
|
||||
local val = unpack_cursor(c)
|
||||
|
||||
if k == nil or k ~= k then
|
||||
k = m.sentinel
|
||||
end
|
||||
|
||||
if k ~= nil then
|
||||
t[k] = val
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
local function unpack_float(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 3 > j then
|
||||
c:underflow(i + 3)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4 = s:byte(i, i + 3)
|
||||
local sign = b1 > 0x7F
|
||||
local expo = (b1 % 0x80) * 0x2 + floor(b2 / 0x80)
|
||||
local mant = ((b2 % 0x80) * 0x100 + b3) * 0x100 + b4
|
||||
|
||||
if sign then
|
||||
sign = -1
|
||||
else
|
||||
sign = 1
|
||||
end
|
||||
|
||||
local n
|
||||
|
||||
if mant == 0 and expo == 0 then
|
||||
n = sign * 0.0
|
||||
elseif expo == 0xFF then
|
||||
if mant == 0 then
|
||||
n = sign * huge
|
||||
else
|
||||
n = 0.0 / 0.0
|
||||
end
|
||||
else
|
||||
n = sign * ldexp(1.0 + mant / 0x800000, expo - 0x7F)
|
||||
end
|
||||
|
||||
c.i = i + 4
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
local function unpack_double(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 7 > j then
|
||||
c:underflow(i + 7)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4, b5, b6, b7, b8 = s:byte(i, i + 7)
|
||||
local sign = b1 > 0x7F
|
||||
local expo = (b1 % 0x80) * 0x10 + floor(b2 / 0x10)
|
||||
local mant = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||
|
||||
if sign then
|
||||
sign = -1
|
||||
else
|
||||
sign = 1
|
||||
end
|
||||
|
||||
local n
|
||||
|
||||
if mant == 0 and expo == 0 then
|
||||
n = sign * 0.0
|
||||
elseif expo == 0x7FF then
|
||||
if mant == 0 then
|
||||
n = sign * huge
|
||||
else
|
||||
n = 0.0 / 0.0
|
||||
end
|
||||
else
|
||||
n = sign * ldexp(1.0 + mant / 4503599627370496.0, expo - 0x3FF)
|
||||
end
|
||||
|
||||
c.i = i + 8
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
local function unpack_uint8(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i > j then
|
||||
c:underflow(i)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1 = s:byte(i)
|
||||
c.i = i + 1
|
||||
|
||||
return b1
|
||||
end
|
||||
|
||||
local function unpack_uint16(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 1 > j then
|
||||
c:underflow(i + 1)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2 = s:byte(i, i + 1)
|
||||
c.i = i + 2
|
||||
|
||||
return b1 * 0x100 + b2
|
||||
end
|
||||
|
||||
local function unpack_uint32(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 3 > j then
|
||||
c:underflow(i + 3)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4 = s:byte(i, i + 3)
|
||||
c.i = i + 4
|
||||
|
||||
return ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4
|
||||
end
|
||||
|
||||
local function unpack_uint64(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 7 > j then
|
||||
c:underflow(i + 7)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4, b5, b6, b7, b8 = s:byte(i, i + 7)
|
||||
c.i = i + 8
|
||||
|
||||
return ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||
end
|
||||
|
||||
local function unpack_int8(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i > j then
|
||||
c:underflow(i)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1 = s:byte(i)
|
||||
c.i = i + 1
|
||||
|
||||
if b1 < 0x80 then
|
||||
return b1
|
||||
else
|
||||
return b1 - 0x100
|
||||
end
|
||||
end
|
||||
|
||||
local function unpack_int16(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 1 > j then
|
||||
c:underflow(i + 1)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2 = s:byte(i, i + 1)
|
||||
c.i = i + 2
|
||||
|
||||
if b1 < 0x80 then
|
||||
return b1 * 0x100 + b2
|
||||
else
|
||||
return ((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) - 1
|
||||
end
|
||||
end
|
||||
|
||||
local function unpack_int32(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 3 > j then
|
||||
c:underflow(i + 3)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4 = s:byte(i, i + 3)
|
||||
c.i = i + 4
|
||||
|
||||
if b1 < 0x80 then
|
||||
return ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4
|
||||
else
|
||||
return ((((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) * 0x100 + (b3 - 0xFF)) * 0x100 + (b4 - 0xFF)) - 1
|
||||
end
|
||||
end
|
||||
|
||||
local function unpack_int64(c)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
|
||||
if i + 7 > j then
|
||||
c:underflow(i + 7)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
end
|
||||
|
||||
local b1, b2, b3, b4, b5, b6, b7, b8 = s:byte(i, i + 7)
|
||||
c.i = i + 8
|
||||
|
||||
if b1 < 0x80 then
|
||||
return ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||
else
|
||||
return ((((((((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) * 0x100 + (b3 - 0xFF)) * 0x100 + (b4 - 0xFF)) * 0x100 + (b5 - 0xFF)) * 0x100 + (b6 - 0xFF)) * 0x100 + (b7 - 0xFF)) * 0x100 + (b8 - 0xFF)) - 1
|
||||
end
|
||||
end
|
||||
|
||||
function m.build_ext(tag, data)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function unpack_ext(c, n, tag)
|
||||
local s, i, j = c.s, c.i, c.j
|
||||
local e = i + n - 1
|
||||
|
||||
if e > j or n < 0 then
|
||||
c:underflow(e)
|
||||
s, i, j = c.s, c.i, c.j
|
||||
e = i + n - 1
|
||||
end
|
||||
|
||||
c.i = i + n
|
||||
|
||||
return m.build_ext(tag, s:sub(i, e))
|
||||
end
|
||||
|
||||
local fn_1 = function(c, val) return val end
|
||||
local fn_2 = function(c, val) return unpack_map(c, val % 0x10) end
|
||||
local fn_3 = function(c, val) return unpack_array(c, val % 0x10) end
|
||||
local fn_4 = function(c, val) return unpack_str(c, val % 0x20) end
|
||||
local fn_5 = function(c, val) return val - 0x100 end
|
||||
|
||||
unpackers = setmetatable({
|
||||
[0xC0] = function() return nil end,
|
||||
[0xC2] = function() return false end,
|
||||
[0xC3] = function() return true end,
|
||||
[0xC4] = function(c) return unpack_str(c, unpack_uint8(c)) end, -- bin8
|
||||
[0xC5] = function(c) return unpack_str(c, unpack_uint16(c)) end, -- bin16
|
||||
[0xC6] = function(c) return unpack_str(c, unpack_uint32(c)) end, -- bin32
|
||||
[0xC7] = function(c) return unpack_ext(c, unpack_uint8(c), unpack_int8(c)) end,
|
||||
[0xC8] = function(c) return unpack_ext(c, unpack_uint16(c), unpack_int8(c)) end,
|
||||
[0xC9] = function(c) return unpack_ext(c, unpack_uint32(c), unpack_int8(c)) end,
|
||||
[0xCA] = unpack_float,
|
||||
[0xCB] = unpack_double,
|
||||
[0xCC] = unpack_uint8,
|
||||
[0xCD] = unpack_uint16,
|
||||
[0xCE] = unpack_uint32,
|
||||
[0xCF] = unpack_uint64,
|
||||
[0xD0] = unpack_int8,
|
||||
[0xD1] = unpack_int16,
|
||||
[0xD2] = unpack_int32,
|
||||
[0xD3] = unpack_int64,
|
||||
[0xD4] = function(c) return unpack_ext(c, 1, unpack_int8(c)) end,
|
||||
[0xD5] = function(c) return unpack_ext(c, 2, unpack_int8(c)) end,
|
||||
[0xD6] = function(c) return unpack_ext(c, 4, unpack_int8(c)) end,
|
||||
[0xD7] = function(c) return unpack_ext(c, 8, unpack_int8(c)) end,
|
||||
[0xD8] = function(c) return unpack_ext(c, 16, unpack_int8(c)) end,
|
||||
[0xD9] = function(c) return unpack_str(c, unpack_uint8(c)) end,
|
||||
[0xDA] = function(c) return unpack_str(c, unpack_uint16(c)) end,
|
||||
[0xDB] = function(c) return unpack_str(c, unpack_uint32(c)) end,
|
||||
[0xDC] = function(c) return unpack_array(c, unpack_uint16(c)) end,
|
||||
[0xDD] = function(c) return unpack_array(c, unpack_uint32(c)) end,
|
||||
[0xDE] = function(c) return unpack_map(c, unpack_uint16(c)) end,
|
||||
[0xDF] = function(c) return unpack_map(c, unpack_uint32(c)) end
|
||||
}, {
|
||||
__index = function(t, k)
|
||||
if k < 0xC0 then
|
||||
if k < 0x80 then
|
||||
return fn_1
|
||||
elseif k < 0x90 then
|
||||
return fn_2
|
||||
elseif k < 0xA0 then
|
||||
return fn_3
|
||||
else
|
||||
return fn_4
|
||||
end
|
||||
elseif k > 0xDF then
|
||||
return fn_5
|
||||
else
|
||||
return function()
|
||||
error("unpack '" .. format("%#x", k) .. "' is unimplemented")
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local function cursor_string(str)
|
||||
return {
|
||||
s = str,
|
||||
i = 1,
|
||||
j = #str,
|
||||
underflow = function()
|
||||
error"missing bytes"
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local function cursor_loader(ld)
|
||||
return {
|
||||
s = '',
|
||||
i = 1,
|
||||
j = 0,
|
||||
underflow = function(self, e)
|
||||
self.s = self.s:sub(self.i)
|
||||
e = e - self.i + 1
|
||||
self.i = 1
|
||||
self.j = 0
|
||||
|
||||
while e > self.j do
|
||||
local chunk = ld()
|
||||
|
||||
if not chunk then
|
||||
error"missing bytes"
|
||||
end
|
||||
|
||||
self.s = self.s .. chunk
|
||||
self.j = #self.s
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
function m.unpack(s)
|
||||
checktype("unpack", 1, s, "string")
|
||||
local cursor = cursor_string(s)
|
||||
local data = unpack_cursor(cursor)
|
||||
|
||||
if cursor.i <= cursor.j then
|
||||
error("extra bytes")
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
function m.unpacker(src)
|
||||
if type(src) == "string" then
|
||||
local cursor = cursor_string(src)
|
||||
|
||||
return function()
|
||||
if cursor.i <= cursor.j then return cursor.i, unpack_cursor(cursor) end
|
||||
end
|
||||
elseif type(src) == "function" then
|
||||
local cursor = cursor_loader(src)
|
||||
|
||||
return function()
|
||||
if cursor.i > cursor.j then
|
||||
pcall(cursor.underflow, cursor, cursor.i)
|
||||
end
|
||||
|
||||
if cursor.i <= cursor.j then return true, unpack_cursor(cursor) end
|
||||
end
|
||||
else
|
||||
argerror("unpacker", 1, "string or function expected, got " .. type(src))
|
||||
end
|
||||
end
|
||||
|
||||
set_string("string")
|
||||
set_integer("unsigned")
|
||||
|
||||
if SIZEOF_NUMBER == 4 then
|
||||
maxinteger = 16777215
|
||||
mininteger = -maxinteger
|
||||
m.small_lua = true
|
||||
unpackers[0xCB] = nil -- double
|
||||
unpackers[0xCF] = nil -- uint64
|
||||
unpackers[0xD3] = nil -- int64
|
||||
set_number("float")
|
||||
else
|
||||
maxinteger = 9007199254740991
|
||||
mininteger = -maxinteger
|
||||
set_number("double")
|
||||
|
||||
if SIZEOF_NUMBER > 8 then
|
||||
m.long_double = true
|
||||
end
|
||||
end
|
||||
|
||||
set_array("always_as_map")
|
||||
m._VERSION = "0.5.2"
|
||||
m._DESCRIPTION = "lua-MessagePack : a pure Lua implementation"
|
||||
m._COPYRIGHT = "Copyright (c) 2012-2019 Francois Perrad"
|
||||
|
||||
return m
|
||||
--
|
||||
-- This library is licensed under the terms of the MIT/X11 license,
|
||||
-- like Lua itself.
|
||||
--
|
||||
53
addons/sam/lua/sam/libs/sh_async_netstream.lua
Normal file
53
addons/sam/lua/sam/libs/sh_async_netstream.lua
Normal file
@@ -0,0 +1,53 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local netstream = sam.netstream
|
||||
netstream.async = {}
|
||||
|
||||
if SERVER then
|
||||
local IsValid = IsValid
|
||||
function netstream.async.Hook(name, fn, check)
|
||||
netstream.Hook(name, function(ply, i, ...)
|
||||
if not sam.isnumber(i) then return end
|
||||
local res = function(...)
|
||||
if IsValid(ply) then
|
||||
netstream.Start(ply, name, i, ...)
|
||||
end
|
||||
end
|
||||
fn(res, ply, ...)
|
||||
end, check)
|
||||
end
|
||||
else
|
||||
local count = 0
|
||||
local receivers = {}
|
||||
|
||||
local hook_fn = function(i, ...)
|
||||
local receiver = receivers[i]
|
||||
if receiver[2] then
|
||||
receiver[2]()
|
||||
end
|
||||
receiver[1]:resolve(...)
|
||||
receivers[i] = nil
|
||||
end
|
||||
|
||||
function netstream.async.Start(name, func_to_call, ...)
|
||||
local promise = sam.Promise.new()
|
||||
count = count + 1
|
||||
receivers[count] = {promise, func_to_call}
|
||||
netstream.Hook(name, hook_fn)
|
||||
if func_to_call then
|
||||
func_to_call()
|
||||
end
|
||||
netstream.Start(name, count, ...)
|
||||
return promise
|
||||
end
|
||||
end
|
||||
111
addons/sam/lua/sam/libs/sh_globals.lua
Normal file
111
addons/sam/lua/sam/libs/sh_globals.lua
Normal file
@@ -0,0 +1,111 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, netstream = sam, sam.netstream
|
||||
|
||||
local globals
|
||||
|
||||
if SERVER then
|
||||
globals = {}
|
||||
local order = {}
|
||||
|
||||
local get_order_key = function(key)
|
||||
for i = 1, #order do
|
||||
if order[i] == key then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sam.set_global(key, value, force)
|
||||
if force or globals[key] ~= value then
|
||||
globals[key] = value
|
||||
|
||||
if value ~= nil then
|
||||
if not get_order_key(key) then
|
||||
table.insert(order, key)
|
||||
end
|
||||
else
|
||||
local i = get_order_key(key)
|
||||
if i then
|
||||
table.remove(order, i)
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Start(nil, "SetGlobal", key, value)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("OnEntityCreated", "SAM.Globals", function(ent)
|
||||
if ent:IsPlayer() and ent:IsValid() then
|
||||
local delay = FrameTime() * 5
|
||||
local first = true
|
||||
for _, key in ipairs(order) do
|
||||
if (key == "Ranks") then
|
||||
for rank, value in pairs(globals[key]) do
|
||||
timer.Simple(delay, function() netstream.Start(ent, "SendRank", rank, value) end)
|
||||
delay = delay + FrameTime() * 2
|
||||
end
|
||||
timer.Simple(delay, function() netstream.Start(ent, "SendRank", "Last") end)
|
||||
delay = delay + FrameTime() * 5
|
||||
else
|
||||
if (first) then
|
||||
timer.Simple(delay, function() netstream.Start(ent, "SendGlobals", {[key] = globals[key]}, {key}) end)
|
||||
first = false
|
||||
else
|
||||
timer.Simple(delay, function() netstream.Start(ent, "SetGlobal", key, globals[key]) end)
|
||||
end
|
||||
delay = delay + FrameTime() * 5
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function sam.set_global(key, value)
|
||||
if globals then
|
||||
globals[key] = value
|
||||
hook.Call("SAM.ChangedGlobalVar", nil, key, value)
|
||||
end
|
||||
end
|
||||
netstream.Hook("SetGlobal", sam.set_global)
|
||||
|
||||
netstream.Hook("SendGlobals", function(vars, order)
|
||||
globals = vars
|
||||
|
||||
for _, key in ipairs(order) do
|
||||
hook.Call("SAM.ChangedGlobalVar", nil, key, vars[key])
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("SendRank", function(rank, value)
|
||||
if (rank ~= "Last" and value) then
|
||||
globals = globals or {}
|
||||
globals["Ranks"] = globals["Ranks"] or {}
|
||||
globals["Ranks"][rank] = value
|
||||
elseif (rank == "Last") then
|
||||
hook.Call("SAM.ChangedGlobalVar", nil, "Ranks", globals["Ranks"])
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.get_global(key, default)
|
||||
if globals then
|
||||
local value = globals[key]
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
return default
|
||||
end
|
||||
157
addons/sam/lua/sam/libs/sh_mp.lua
Normal file
157
addons/sam/lua/sam/libs/sh_mp.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local mp = sam.load_file("sam/libs/message_pack/sh_messagepack.lua")
|
||||
local EXT_VECTOR = 1
|
||||
local EXT_ANGLE = 2
|
||||
local EXT_ENTITY = 3
|
||||
local EXT_PLAYER = 4
|
||||
local EXT_COLOR = 5
|
||||
local EXT_CONSOLE = 6
|
||||
|
||||
mp.packers["Entity"] = function(buffer, ent)
|
||||
local buf = {}
|
||||
mp.packers["number"](buf, ent:EntIndex())
|
||||
mp.packers["ext"](buffer, EXT_ENTITY, buf[1])
|
||||
end
|
||||
mp.packers["Vehicle"] = mp.packers["Entity"]
|
||||
mp.packers["Weapon"] = mp.packers["Entity"]
|
||||
mp.packers["NPC"] = mp.packers["Entity"]
|
||||
mp.packers["NextBot"] = mp.packers["Entity"]
|
||||
mp.packers["PhysObj"] = mp.packers["Entity"]
|
||||
|
||||
mp.packers["Player"] = function(buffer, ply)
|
||||
local buf = {}
|
||||
mp.packers["number"](buf, ply:UserID())
|
||||
mp.packers["ext"](buffer, EXT_PLAYER, buf[1])
|
||||
end
|
||||
|
||||
local VECTOR = {}
|
||||
mp.packers["Vector"] = function(buffer, vec)
|
||||
VECTOR[1] = vec.x
|
||||
VECTOR[2] = vec.y
|
||||
VECTOR[3] = vec.z
|
||||
|
||||
local buf = {}
|
||||
mp.packers["_table"](buf, VECTOR)
|
||||
mp.packers["ext"](buffer, EXT_VECTOR, table.concat(buf))
|
||||
end
|
||||
|
||||
local ANGLE = {}
|
||||
mp.packers["Angle"] = function(buffer, ang)
|
||||
ANGLE[1] = ang.p
|
||||
ANGLE[2] = ang.y
|
||||
ANGLE[3] = ang.r
|
||||
|
||||
local buf = {}
|
||||
mp.packers["_table"](buf, ANGLE)
|
||||
mp.packers["ext"](buffer, EXT_ANGLE, table.concat(buf))
|
||||
end
|
||||
|
||||
local COLOR = {}
|
||||
mp.packers["Color"] = function(buffer, col)
|
||||
COLOR[1] = col.r
|
||||
COLOR[2] = col.g
|
||||
COLOR[3] = col.b
|
||||
COLOR[4] = col.a
|
||||
|
||||
local buf = {}
|
||||
mp.packers["_table"](buf, COLOR)
|
||||
mp.packers["ext"](buffer, EXT_COLOR, table.concat(buf))
|
||||
end
|
||||
|
||||
mp.packers["console"] = function(buffer)
|
||||
mp.packers["ext"](buffer, EXT_CONSOLE, "")
|
||||
end
|
||||
|
||||
local Entity = Entity
|
||||
local _Player
|
||||
local Color = Color
|
||||
local Vector = Vector
|
||||
local Angle = Angle
|
||||
local unpackers = {
|
||||
[EXT_ENTITY] = function(v)
|
||||
return Entity(v)
|
||||
end,
|
||||
[EXT_PLAYER] = function(v)
|
||||
return _Player(v)
|
||||
end,
|
||||
[EXT_VECTOR] = function(v)
|
||||
return Vector(v[1], v[2], v[3])
|
||||
end,
|
||||
[EXT_ANGLE] = function(v)
|
||||
return Angle(v[1], v[2], v[3])
|
||||
end,
|
||||
[EXT_COLOR] = function(v)
|
||||
return Color(v[1], v[2], v[3], v[4])
|
||||
end,
|
||||
[EXT_CONSOLE] = function(v)
|
||||
return sam.console
|
||||
end
|
||||
}
|
||||
|
||||
local Player = Player
|
||||
if CLIENT then
|
||||
local players = {}
|
||||
|
||||
local Name = function(s)
|
||||
return s.name
|
||||
end
|
||||
|
||||
_Player = function(id)
|
||||
local ply = Player(id)
|
||||
|
||||
if not IsValid(ply) then
|
||||
local name = players[id]
|
||||
if name then
|
||||
return {
|
||||
name = name,
|
||||
Name = Name
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return ply
|
||||
end
|
||||
|
||||
hook.Add("OnEntityCreated", "SAM.GetPlayerName", function(ent)
|
||||
if ent:IsPlayer() and ent:IsValid() then
|
||||
ent.sam_userid = ent:UserID() -- userid is -1 in EntityRemoved?????
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("EntityRemoved", "SAM.GetPlayerName", function(ent)
|
||||
if not ent:IsPlayer() then return end
|
||||
|
||||
local id = ent.sam_userid
|
||||
if not id then return end
|
||||
|
||||
players[id] = ent:Name()
|
||||
|
||||
timer.Simple(60, function()
|
||||
if not IsValid(ent) then
|
||||
players[id] = nil
|
||||
end
|
||||
end)
|
||||
end)
|
||||
else
|
||||
_Player = Player
|
||||
end
|
||||
|
||||
mp.build_ext = function(tag, data)
|
||||
local f = mp.unpacker(data)
|
||||
local _, v = f()
|
||||
return unpackers[tag](v)
|
||||
end
|
||||
|
||||
sam.mp = mp
|
||||
177
addons/sam/lua/sam/libs/sh_netstream.lua
Normal file
177
addons/sam/lua/sam/libs/sh_netstream.lua
Normal file
@@ -0,0 +1,177 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
--[[
|
||||
NetStream - 2.0.1
|
||||
https://github.com/alexgrist/NetStream/blob/master/netstream2.lua
|
||||
|
||||
Alexander Grist-Hucker
|
||||
http://www.revotech.org
|
||||
]]--
|
||||
|
||||
--[[
|
||||
if SERVER then
|
||||
netstream.Hook("Hi", function(ply, ...) -- Third argument is called to check if the player has permission to send the net message before decoding
|
||||
print(...)
|
||||
end, function(ply)
|
||||
if not ply:IsAdmin() then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
-- OR
|
||||
netstream.Hook("Hi", function(ply, ...)
|
||||
print(...)
|
||||
end)
|
||||
netstream.Start(Entity(1), "Hi", "a", 1, {}, true, false, nil, "!") -- First argument player or table of players or any other argument to send to all players
|
||||
netstream.Start({Entity(1), Entity(2)}, "Hi", "a", 1, {}, true, false, nil, "!")
|
||||
netstream.Start(nil, "Hi", "a", 1, {}, true, false, nil, "!")
|
||||
end
|
||||
if CLIENT then
|
||||
netstream.Hook("Hi", function(...)
|
||||
print(...)
|
||||
end)
|
||||
netstream.Start("Hi", "a", 1, {}, true, false, nil, "!")
|
||||
end
|
||||
]]--
|
||||
|
||||
-- Config
|
||||
|
||||
local addonName = "SAM"
|
||||
local mainTable = sam -- _G.netstream = netstream
|
||||
|
||||
local mp = sam.mp
|
||||
|
||||
--
|
||||
|
||||
local type = sam.type
|
||||
local pcall = pcall
|
||||
local unpack = unpack
|
||||
|
||||
local net = net
|
||||
local table_maxn = table.maxn
|
||||
|
||||
local netStreamSend = addonName .. ".NetStreamDS.Sending"
|
||||
|
||||
local netstream = {}
|
||||
if istable(mainTable) then
|
||||
mainTable.netstream = netstream
|
||||
end
|
||||
|
||||
local checks = {}
|
||||
local receivers = {}
|
||||
|
||||
local concat = table.concat
|
||||
local pack = function(t, n)
|
||||
local buffer = {}
|
||||
mp.packers["array"](buffer, t, n)
|
||||
return concat(buffer)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
util.AddNetworkString(netStreamSend)
|
||||
|
||||
-- local str_sub = string.sub
|
||||
-- local function Split(str, buffer, result)
|
||||
-- if not result then
|
||||
-- result = {}
|
||||
-- end
|
||||
|
||||
-- if not buffer then
|
||||
-- buffer = 32768
|
||||
-- end
|
||||
|
||||
-- local len = #str
|
||||
-- if len >= buffer then
|
||||
-- result[#result + 1] = str_sub(str, 1, buffer - 1)
|
||||
-- str = str_sub(str, buffer, len)
|
||||
-- else
|
||||
-- result[#result + 1] = str
|
||||
-- return result
|
||||
-- end
|
||||
|
||||
-- return Split(str, buffer, result)
|
||||
-- end
|
||||
|
||||
local player_GetAll = player.GetAll
|
||||
function netstream.Start(ply, name, ...)
|
||||
local ply_type = type(ply)
|
||||
if ply_type ~= "Player" and ply_type ~= "table" then
|
||||
ply = player_GetAll()
|
||||
end
|
||||
|
||||
local encoded_data = pack({...}, select("#", ...))
|
||||
local length = #encoded_data
|
||||
|
||||
net.Start(netStreamSend)
|
||||
net.WriteString(name)
|
||||
net.WriteUInt(length, 17)
|
||||
net.WriteData(encoded_data, length)
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
function netstream.Hook(name, callback, check)
|
||||
receivers[name] = callback
|
||||
if type(check) == "function" then
|
||||
checks[name] = check
|
||||
end
|
||||
end
|
||||
|
||||
net.Receive(netStreamSend, function(_, ply)
|
||||
local name = net.ReadString()
|
||||
|
||||
local callback = receivers[name]
|
||||
if not callback then return end
|
||||
|
||||
local length = net.ReadUInt(17)
|
||||
|
||||
local check = checks[name]
|
||||
if check and check(ply, length) == false then return end
|
||||
|
||||
local data = net.ReadData(length)
|
||||
|
||||
local status
|
||||
status, data = pcall(mp.unpack, data)
|
||||
if not status or not sam.istable(data) then return end
|
||||
|
||||
callback(ply, unpack(data, 1, table_maxn(data)))
|
||||
end)
|
||||
else
|
||||
checks = nil
|
||||
|
||||
function netstream.Start(name, ...)
|
||||
local encoded_data = pack({...}, select("#", ...))
|
||||
local length = #encoded_data
|
||||
|
||||
net.Start(netStreamSend)
|
||||
net.WriteString(name)
|
||||
net.WriteUInt(length, 17)
|
||||
net.WriteData(encoded_data, length)
|
||||
net.SendToServer()
|
||||
end
|
||||
|
||||
function netstream.Hook(name, callback)
|
||||
receivers[name] = callback
|
||||
end
|
||||
|
||||
net.Receive(netStreamSend, function()
|
||||
local callback = receivers[net.ReadString()]
|
||||
if not callback then return end
|
||||
|
||||
local length = net.ReadUInt(17)
|
||||
local data = net.ReadData(length)
|
||||
|
||||
data = mp.unpack(data)
|
||||
callback(unpack(data, 1, table_maxn(data)))
|
||||
end)
|
||||
end
|
||||
|
||||
return netstream
|
||||
458
addons/sam/lua/sam/libs/sh_pon.lua
Normal file
458
addons/sam/lua/sam/libs/sh_pon.lua
Normal file
@@ -0,0 +1,458 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
--[[
|
||||
|
||||
DEVELOPMENTAL VERSION;
|
||||
|
||||
VERSION 1.2.2
|
||||
Copyright thelastpenguin™
|
||||
|
||||
You may use this for any purpose as long as:
|
||||
- You don't remove this copyright notice.
|
||||
- You don't claim this to be your own.
|
||||
- You properly credit the author, thelastpenguin™, if you publish your work based on (and/or using) this.
|
||||
|
||||
If you modify the code for any purpose, the above still applies to the modified code.
|
||||
|
||||
The author is not held responsible for any damages incured from the use of pon, you use it at your own risk.
|
||||
|
||||
DATA TYPES SUPPORTED:
|
||||
- tables - k,v - pointers
|
||||
- strings - k,v - pointers
|
||||
- numbers - k,v
|
||||
- booleans- k,v
|
||||
|
||||
- Vectors - k,v
|
||||
- Angles - k,v
|
||||
- Entities- k,v
|
||||
- Players - k,v
|
||||
|
||||
CHANGE LOG
|
||||
V 1.1.0
|
||||
- Added Vehicle, NPC, NextBot, Player, Weapon
|
||||
V 1.2.0
|
||||
- Added custom handling for k,v tables without any array component.
|
||||
V 1.2.1
|
||||
- fixed deserialization bug.
|
||||
|
||||
THANKS TO...
|
||||
- VERCAS for the inspiration.
|
||||
]]
|
||||
local pon = {}
|
||||
sam.pon = pon
|
||||
|
||||
do
|
||||
local type = sam.type
|
||||
local IsColor = IsColor
|
||||
local tonumber = tonumber
|
||||
local format = string.format
|
||||
local encode = {}
|
||||
local cacheSize = 0
|
||||
|
||||
encode['table'] = function(self, tbl, output, cache)
|
||||
if cache[tbl] then
|
||||
output[#output + 1] = format('(%x)', cache[tbl])
|
||||
|
||||
return
|
||||
else
|
||||
cacheSize = cacheSize + 1
|
||||
cache[tbl] = cacheSize
|
||||
end
|
||||
|
||||
local first = next(tbl, nil)
|
||||
local predictedNumeric = 1
|
||||
|
||||
-- starts with a numeric dealio
|
||||
if first == 1 then
|
||||
output[#output + 1] = '{'
|
||||
|
||||
for k, v in next, tbl do
|
||||
if k == predictedNumeric then
|
||||
predictedNumeric = predictedNumeric + 1
|
||||
local tv = type(v)
|
||||
|
||||
if tv == 'string' then
|
||||
local pid = cache[v]
|
||||
|
||||
if pid then
|
||||
output[#output + 1] = format('(%x)', pid)
|
||||
else
|
||||
cacheSize = cacheSize + 1
|
||||
cache[v] = cacheSize
|
||||
self.string(self, v, output, cache)
|
||||
end
|
||||
elseif IsColor(v) then
|
||||
self.Color(self, v, output, cache)
|
||||
else
|
||||
self[tv](self, v, output, cache)
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
predictedNumeric = predictedNumeric - 1
|
||||
else
|
||||
predictedNumeric = nil
|
||||
end
|
||||
|
||||
if predictedNumeric == nil then
|
||||
output[#output + 1] = '[' -- no array component
|
||||
else
|
||||
output[#output + 1] = '~' -- array component came first so shit needs to happen
|
||||
end
|
||||
|
||||
for k, v in next, tbl, predictedNumeric do
|
||||
local tk, tv = type(k), type(v)
|
||||
if not self[tk] or not self[tv] then continue end
|
||||
|
||||
-- WRITE KEY
|
||||
if tk == 'string' then
|
||||
local pid = cache[k]
|
||||
|
||||
if pid then
|
||||
output[#output + 1] = format('(%x)', pid)
|
||||
else
|
||||
cacheSize = cacheSize + 1
|
||||
cache[k] = cacheSize
|
||||
self.string(self, k, output, cache)
|
||||
end
|
||||
elseif IsColor(v) then
|
||||
self.Color(self, v, output, cache)
|
||||
else
|
||||
self[tk](self, k, output, cache)
|
||||
end
|
||||
|
||||
-- WRITE VALUE
|
||||
if tv == 'string' then
|
||||
local pid = cache[v]
|
||||
|
||||
if pid then
|
||||
output[#output + 1] = format('(%x)', pid)
|
||||
else
|
||||
cacheSize = cacheSize + 1
|
||||
cache[v] = cacheSize
|
||||
self.string(self, v, output, cache)
|
||||
end
|
||||
elseif IsColor(v) then
|
||||
self.Color(self, v, output, cache)
|
||||
else
|
||||
self[tv](self, v, output, cache)
|
||||
end
|
||||
end
|
||||
|
||||
output[#output + 1] = '}'
|
||||
end
|
||||
|
||||
-- ENCODE STRING
|
||||
local gsub = string.gsub
|
||||
|
||||
encode['string'] = function(self, str, output)
|
||||
--if tryCache(str, output then return end
|
||||
local estr, count = gsub(str, ';', "\\;")
|
||||
|
||||
if count == 0 then
|
||||
output[#output + 1] = '\'' .. str .. ';'
|
||||
else
|
||||
output[#output + 1] = '"' .. estr .. '";'
|
||||
end
|
||||
end
|
||||
|
||||
-- ENCODE NUMBER
|
||||
encode['number'] = function(self, num, output)
|
||||
if num % 1 == 0 then
|
||||
if num < 0 then
|
||||
output[#output + 1] = format('x%x;', -num)
|
||||
else
|
||||
output[#output + 1] = format('X%x;', num)
|
||||
end
|
||||
else
|
||||
output[#output + 1] = tonumber(num) .. ';'
|
||||
end
|
||||
end
|
||||
|
||||
-- ENCODE BOOLEAN
|
||||
encode['boolean'] = function(self, val, output)
|
||||
output[#output + 1] = val and 't' or 'f'
|
||||
end
|
||||
|
||||
-- ENCODE VECTOR
|
||||
encode['Vector'] = function(self, val, output)
|
||||
output[#output + 1] = ('v' .. val.x .. ',' .. val.y) .. (',' .. val.z .. ';')
|
||||
end
|
||||
|
||||
-- ENCODE ANGLE
|
||||
encode['Angle'] = function(self, val, output)
|
||||
output[#output + 1] = ('a' .. val.p .. ',' .. val.y) .. (',' .. val.r .. ';')
|
||||
end
|
||||
|
||||
encode['Entity'] = function(self, val, output)
|
||||
output[#output + 1] = 'E' .. (IsValid(val) and (val:EntIndex() .. ';') or '#')
|
||||
end
|
||||
|
||||
encode['Player'] = encode['Entity']
|
||||
encode['Vehicle'] = encode['Entity']
|
||||
encode['Weapon'] = encode['Entity']
|
||||
encode['NPC'] = encode['Entity']
|
||||
encode['NextBot'] = encode['Entity']
|
||||
encode['PhysObj'] = encode['Entity']
|
||||
|
||||
encode['Color'] = function(self, val, output)
|
||||
output[#output + 1] = ('C' .. val.r .. ',' .. val.g .. ',' .. val.b) .. (',' .. val.a .. ';')
|
||||
end
|
||||
|
||||
encode['console'] = function(self, val, output)
|
||||
output[#output + 1] = 's'
|
||||
end
|
||||
|
||||
encode['nil'] = function(self, val, output)
|
||||
output[#output + 1] = '?'
|
||||
end
|
||||
|
||||
encode.__index = function(key)
|
||||
ErrorNoHalt('Type: ' .. key .. ' can not be encoded. Encoded as as pass-over value.')
|
||||
|
||||
return encode['nil']
|
||||
end
|
||||
|
||||
do
|
||||
local concat = table.concat
|
||||
|
||||
function pon.encode(tbl)
|
||||
local output = {nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
cacheSize = 0
|
||||
encode['table'](encode, tbl, output, {})
|
||||
|
||||
return concat(output)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local tonumber = tonumber
|
||||
local find, sub, gsub, Explode = string.find, string.sub, string.gsub, string.Explode
|
||||
local Vector, Angle, Entity = Vector, Angle, Entity
|
||||
local decode = {}
|
||||
|
||||
decode['{'] = function(self, index, str, cache)
|
||||
local cur = {}
|
||||
cache[#cache + 1] = cur
|
||||
local k, v, tk, tv = 1, nil, nil, nil
|
||||
|
||||
while (true) do
|
||||
tv = sub(str, index, index)
|
||||
|
||||
if not tv or tv == '~' then
|
||||
index = index + 1
|
||||
break
|
||||
end
|
||||
|
||||
if tv == '}' then return index + 1, cur end
|
||||
-- READ THE VALUE
|
||||
index = index + 1
|
||||
index, v = self[tv](self, index, str, cache)
|
||||
cur[k] = v
|
||||
k = k + 1
|
||||
end
|
||||
|
||||
while (true) do
|
||||
tk = sub(str, index, index)
|
||||
|
||||
if not tk or tk == '}' then
|
||||
index = index + 1
|
||||
break
|
||||
end
|
||||
|
||||
-- READ THE KEY
|
||||
index = index + 1
|
||||
index, k = self[tk](self, index, str, cache)
|
||||
-- READ THE VALUE
|
||||
tv = sub(str, index, index)
|
||||
index = index + 1
|
||||
index, v = self[tv](self, index, str, cache)
|
||||
cur[k] = v
|
||||
end
|
||||
|
||||
return index, cur
|
||||
end
|
||||
|
||||
decode['['] = function(self, index, str, cache)
|
||||
local cur = {}
|
||||
cache[#cache + 1] = cur
|
||||
local k, v, tk, tv = 1, nil, nil, nil
|
||||
|
||||
while (true) do
|
||||
tk = sub(str, index, index)
|
||||
|
||||
if not tk or tk == '}' then
|
||||
index = index + 1
|
||||
break
|
||||
end
|
||||
|
||||
-- READ THE KEY
|
||||
index = index + 1
|
||||
index, k = self[tk](self, index, str, cache)
|
||||
if not k then continue end
|
||||
-- READ THE VALUE
|
||||
tv = sub(str, index, index)
|
||||
index = index + 1
|
||||
|
||||
if not self[tv] then
|
||||
print('did not find type: ' .. tv)
|
||||
end
|
||||
|
||||
index, v = self[tv](self, index, str, cache)
|
||||
cur[k] = v
|
||||
end
|
||||
|
||||
return index, cur
|
||||
end
|
||||
|
||||
-- STRING
|
||||
decode['"'] = function(self, index, str, cache)
|
||||
local finish = find(str, '";', index, true)
|
||||
local res = gsub(sub(str, index, finish - 1), '\\;', ';')
|
||||
index = finish + 2
|
||||
cache[#cache + 1] = res
|
||||
|
||||
return index, res
|
||||
end
|
||||
|
||||
-- STRING NO ESCAPING NEEDED
|
||||
decode['\''] = function(self, index, str, cache)
|
||||
local finish = find(str, ';', index, true)
|
||||
local res = sub(str, index, finish - 1)
|
||||
index = finish + 1
|
||||
cache[#cache + 1] = res
|
||||
|
||||
return index, res
|
||||
end
|
||||
|
||||
-- NUMBER
|
||||
decode['n'] = function(self, index, str)
|
||||
index = index - 1
|
||||
local finish = find(str, ';', index, true)
|
||||
local num = tonumber(sub(str, index, finish - 1))
|
||||
index = finish + 1
|
||||
|
||||
return index, num
|
||||
end
|
||||
|
||||
decode['0'] = decode['n']
|
||||
decode['1'] = decode['n']
|
||||
decode['2'] = decode['n']
|
||||
decode['3'] = decode['n']
|
||||
decode['4'] = decode['n']
|
||||
decode['5'] = decode['n']
|
||||
decode['6'] = decode['n']
|
||||
decode['7'] = decode['n']
|
||||
decode['8'] = decode['n']
|
||||
decode['9'] = decode['n']
|
||||
decode['-'] = decode['n']
|
||||
|
||||
-- positive hex
|
||||
decode['X'] = function(self, index, str)
|
||||
local finish = find(str, ';', index, true)
|
||||
local num = tonumber(sub(str, index, finish - 1), 16)
|
||||
index = finish + 1
|
||||
|
||||
return index, num
|
||||
end
|
||||
|
||||
-- negative hex
|
||||
decode['x'] = function(self, index, str)
|
||||
local finish = find(str, ';', index, true)
|
||||
local num = -tonumber(sub(str, index, finish - 1), 16)
|
||||
index = finish + 1
|
||||
|
||||
return index, num
|
||||
end
|
||||
|
||||
-- POINTER
|
||||
decode['('] = function(self, index, str, cache)
|
||||
local finish = find(str, ')', index, true)
|
||||
local num = tonumber(sub(str, index, finish - 1), 16)
|
||||
index = finish + 1
|
||||
|
||||
return index, cache[num]
|
||||
end
|
||||
|
||||
-- BOOLEAN. ONE DATA TYPE FOR YES, ANOTHER FOR NO.
|
||||
decode['t'] = function(self, index) return index, true end
|
||||
decode['f'] = function(self, index) return index, false end
|
||||
|
||||
-- VECTOR
|
||||
decode['v'] = function(self, index, str)
|
||||
local finish = find(str, ';', index, true)
|
||||
local vecStr = sub(str, index, finish - 1)
|
||||
index = finish + 1 -- update the index.
|
||||
local segs = Explode(',', vecStr, false)
|
||||
|
||||
return index, Vector(tonumber(segs[1]), tonumber(segs[2]), tonumber(segs[3]))
|
||||
end
|
||||
|
||||
-- ANGLE
|
||||
decode['a'] = function(self, index, str)
|
||||
local finish = find(str, ';', index, true)
|
||||
local angStr = sub(str, index, finish - 1)
|
||||
index = finish + 1 -- update the index.
|
||||
local segs = Explode(',', angStr, false)
|
||||
|
||||
return index, Angle(tonumber(segs[1]), tonumber(segs[2]), tonumber(segs[3]))
|
||||
end
|
||||
|
||||
-- ENTITY
|
||||
decode['E'] = function(self, index, str)
|
||||
if str[index] == '#' then
|
||||
index = index + 1
|
||||
|
||||
return index, NULL
|
||||
else
|
||||
local finish = find(str, ';', index, true)
|
||||
local num = tonumber(sub(str, index, finish - 1))
|
||||
index = finish + 1
|
||||
|
||||
return index, Entity(num)
|
||||
end
|
||||
end
|
||||
|
||||
-- COLOR
|
||||
decode['C'] = function(self, index, str)
|
||||
local finish = find(str, ';', index, true)
|
||||
local colStr = sub(str, index, finish - 1)
|
||||
index = finish + 1 -- update the index.
|
||||
local segs = Explode(',', colStr, false)
|
||||
|
||||
return index, Color(segs[1], segs[2], segs[3], segs[4])
|
||||
end
|
||||
|
||||
-- PLAYER
|
||||
decode['P'] = function(self, index, str)
|
||||
local finish = find(str, ';', index, true)
|
||||
local num = tonumber(sub(str, index, finish - 1))
|
||||
index = finish + 1
|
||||
|
||||
return index, Entity(num) or NULL
|
||||
end
|
||||
|
||||
-- NIL
|
||||
decode['?'] = function(self, index) return index + 1, nil end
|
||||
-- SAM CONSOLE
|
||||
decode['s'] = function(self, index) return index, sam.console end
|
||||
|
||||
function pon.decode(data)
|
||||
local _, res = decode[sub(data, 1, 1)](decode, 2, data, {})
|
||||
|
||||
return res
|
||||
end
|
||||
end
|
||||
76
addons/sam/lua/sam/libs/sh_promises.lua
Normal file
76
addons/sam/lua/sam/libs/sh_promises.lua
Normal file
@@ -0,0 +1,76 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
-- not real promises, just really simple one
|
||||
|
||||
local isfunction = sam and sam.isfunction or isfunction
|
||||
|
||||
local null = {}
|
||||
|
||||
local Promise = {}
|
||||
|
||||
local PromiseMethods = {}
|
||||
local Promise_meta = {__index = PromiseMethods}
|
||||
|
||||
function Promise.new()
|
||||
return setmetatable({
|
||||
value = null,
|
||||
null = null
|
||||
}, Promise_meta)
|
||||
end
|
||||
|
||||
function Promise.IsPromise(v)
|
||||
return getmetatable(v) == Promise_meta
|
||||
end
|
||||
|
||||
function PromiseMethods:resolve(v)
|
||||
if self.value ~= null then return end
|
||||
if self.done_callback then
|
||||
self.done_callback(v)
|
||||
else
|
||||
self.value = v
|
||||
self.callback = 0
|
||||
end
|
||||
end
|
||||
|
||||
function PromiseMethods:reject(v)
|
||||
if self.value ~= null then return end
|
||||
if self.catch_callback then
|
||||
self.catch_callback(v)
|
||||
else
|
||||
self.value = v
|
||||
self.callback = 1
|
||||
end
|
||||
end
|
||||
|
||||
function PromiseMethods:done(func)
|
||||
if isfunction(func) then
|
||||
if self.value ~= null and self.callback == 0 then
|
||||
func(self.value)
|
||||
else
|
||||
self.done_callback = func
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function PromiseMethods:catch(func)
|
||||
if isfunction(func) then
|
||||
if self.value ~= null and self.callback == 1 then
|
||||
func(self.value)
|
||||
else
|
||||
self.catch_callback = func
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
return Promise
|
||||
94
addons/sam/lua/sam/libs/sh_types.lua
Normal file
94
addons/sam/lua/sam/libs/sh_types.lua
Normal file
@@ -0,0 +1,94 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
-- https://gist.github.com/CapsAdmin/0d9c1e77d0fc22d910e182bfeb9812e5
|
||||
local getmetatable = getmetatable
|
||||
|
||||
do
|
||||
local types = {
|
||||
["string"] = "",
|
||||
["boolean"] = true,
|
||||
["number"] = 0,
|
||||
["function"] = function() end,
|
||||
["thread"] = coroutine.create(getmetatable),
|
||||
["Color"] = Color(0, 0, 0),
|
||||
}
|
||||
|
||||
for k, v in pairs(types) do
|
||||
if not getmetatable(v) then
|
||||
debug.setmetatable(v, {MetaName = k})
|
||||
else
|
||||
getmetatable(v).MetaName = k
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sam.type(value)
|
||||
if value == nil then
|
||||
return "nil"
|
||||
end
|
||||
local meta = getmetatable(value)
|
||||
if meta then
|
||||
meta = meta.MetaName
|
||||
if meta then
|
||||
return meta
|
||||
end
|
||||
end
|
||||
return "table"
|
||||
end
|
||||
|
||||
do
|
||||
local function add(name)
|
||||
local new_name = name
|
||||
if name == "bool" then
|
||||
new_name = "boolean"
|
||||
end
|
||||
sam["is" .. name:lower()] = function(value)
|
||||
local meta = getmetatable(value)
|
||||
if meta and meta.MetaName == new_name then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
add("string")
|
||||
add("number")
|
||||
add("bool")
|
||||
add("function")
|
||||
|
||||
add("Angle")
|
||||
add("Vector")
|
||||
add("Panel")
|
||||
add("Matrix")
|
||||
end
|
||||
|
||||
function sam.isentity(value)
|
||||
local meta = getmetatable(value)
|
||||
if meta then
|
||||
if meta.MetaName == "Entity" then
|
||||
return true
|
||||
end
|
||||
meta = meta.MetaBaseClass
|
||||
if meta then
|
||||
return meta.MetaName == "Entity"
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
sam.IsEntity = sam.isentity
|
||||
|
||||
local type = sam.type
|
||||
function sam.istable(value)
|
||||
return type(value) == "table"
|
||||
end
|
||||
191
addons/sam/lua/sam/libs/sql/databases/mysql.lua
Normal file
191
addons/sam/lua/sam/libs/sql/databases/mysql.lua
Normal file
@@ -0,0 +1,191 @@
|
||||
--[[
|
||||
| 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 _SQL = sam.SQL
|
||||
local _error = _SQL.Error
|
||||
local traceback = debug.traceback
|
||||
|
||||
local _mysqloo, database = nil, nil
|
||||
|
||||
local SQL = {}
|
||||
|
||||
function SQL.Connect(callback, failed_callback, config)
|
||||
if database then
|
||||
local status = database:status()
|
||||
if status == _mysqloo.DATABASE_CONNECTING or status == _mysqloo.DATABASE_CONNECTED then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
_SQL.SetConnected(false)
|
||||
|
||||
require("mysqloo")
|
||||
|
||||
if not mysqloo then
|
||||
_error("mysqloo module doesn't exist, get it from https://github.com/FredyH/MySQLOO")
|
||||
return false
|
||||
end
|
||||
|
||||
_mysqloo = mysqloo
|
||||
|
||||
database = _mysqloo.connect(
|
||||
config.Host,
|
||||
config.Username,
|
||||
config.Password,
|
||||
config.Database,
|
||||
config.Port
|
||||
)
|
||||
|
||||
function database.onConnected()
|
||||
callback()
|
||||
end
|
||||
|
||||
function database.onConnectionFailed(_, error_text)
|
||||
failed_callback(error_text)
|
||||
end
|
||||
|
||||
database:connect()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
local transaction
|
||||
|
||||
local add_transaction = function(query)
|
||||
transaction:addQuery(database:query(query))
|
||||
end
|
||||
|
||||
function SQL.Begin()
|
||||
transaction = database:createTransaction()
|
||||
return add_transaction
|
||||
end
|
||||
|
||||
function SQL.Commit(callback)
|
||||
transaction.SQL_traceback = traceback("", 2)
|
||||
|
||||
transaction.onSuccess = callback
|
||||
transaction.onError = transaction_onError
|
||||
|
||||
transaction:start()
|
||||
|
||||
transaction = nil
|
||||
end
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
local on_query_success = function(query, data)
|
||||
if query.SQL_first_row then
|
||||
data = data[1]
|
||||
end
|
||||
query.SQL_callback(data, query.SQL_callback_obj)
|
||||
end
|
||||
|
||||
local on_query_fail = function(query, error_text)
|
||||
local status = database:status()
|
||||
|
||||
-- https://github.com/Kamshak/LibK/blob/master/lua/libk/server/sv_libk_database.lua#L129
|
||||
if status == _mysqloo.DATABASE_NOT_CONNECTED or status == _mysqloo.DATABASE_CONNECTING or error_text:find("Lost connection to MySQL server during query", 1, true) then
|
||||
_SQL.SetConnected(false)
|
||||
SQL.Query(query.SQL_query_string, query.SQL_callback, query.SQL_first_row, query.SQL_callback_obj)
|
||||
else
|
||||
-- 3cb9b992975d0cc0ba1b28f92ab5d1b700a08080a59b058f1424736060a73552
|
||||
_error("Query error: " .. error_text, query.SQL_traceback)
|
||||
end
|
||||
end
|
||||
|
||||
function SQL.Query(query, callback, first_row, callback_obj)
|
||||
local status = database:status()
|
||||
if status == _mysqloo.DATABASE_NOT_CONNECTED or status == _mysqloo.DATABASE_INTERNAL_ERROR then
|
||||
_SQL.Connect()
|
||||
database:wait()
|
||||
end
|
||||
|
||||
local query_string = query
|
||||
query = database:query(query)
|
||||
|
||||
query.SQL_query_string = query_string
|
||||
|
||||
if callback then
|
||||
query.onSuccess = on_query_success
|
||||
query.SQL_callback = callback
|
||||
query.SQL_first_row = first_row
|
||||
query.SQL_callback_obj = callback_obj
|
||||
end
|
||||
|
||||
query.SQL_traceback = traceback("", 2)
|
||||
query.onError = on_query_fail
|
||||
|
||||
query:start()
|
||||
|
||||
return query
|
||||
end
|
||||
|
||||
-- local prepared_set_values = function(prepared_query, values)
|
||||
-- for i = 1, #values do
|
||||
-- local v = values[i]
|
||||
-- local value_type = type(v)
|
||||
-- if value_type == "string" then
|
||||
-- prepared_query:setString(i, v)
|
||||
-- elseif value_type == "number" then
|
||||
-- prepared_query:setNumber(i, v)
|
||||
-- else
|
||||
-- error(
|
||||
-- string.format(
|
||||
-- "%s invalid type '%s' was passed to escape '%s'",
|
||||
-- "(" .. SQL.GetAddonName() .. " | MySQL)",
|
||||
-- value_type,
|
||||
-- v
|
||||
-- )
|
||||
-- )
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- function SQL.Prepare(query, callback, first_row, callback_obj)
|
||||
-- local prepared_query = database:prepare(query)
|
||||
-- prepared_query.SetValues = prepared_set_values
|
||||
|
||||
-- if callback then
|
||||
-- prepared_query.onSuccess = on_query_success
|
||||
-- prepared_query.SQL_callback = callback
|
||||
-- prepared_query.SQL_first_row = first_row
|
||||
-- prepared_query.SQL_callback_obj = callback_obj
|
||||
-- end
|
||||
|
||||
-- prepared_query.SQL_traceback = traceback("", 2)
|
||||
-- prepared_query.onError = on_query_fail
|
||||
|
||||
-- return prepared_query
|
||||
-- end
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
function SQL.EscapeString(value, no_quotes)
|
||||
if no_quotes then
|
||||
return database:escape(value)
|
||||
else
|
||||
return "'" .. database:escape(value) .. "'"
|
||||
end
|
||||
end
|
||||
|
||||
function SQL.TableExistsQuery(name)
|
||||
return "SHOW TABLES LIKE " .. SQL.EscapeString(name)
|
||||
end
|
||||
|
||||
return SQL
|
||||
148
addons/sam/lua/sam/libs/sql/databases/sqlite.lua
Normal file
148
addons/sam/lua/sam/libs/sql/databases/sqlite.lua
Normal file
@@ -0,0 +1,148 @@
|
||||
--[[
|
||||
| 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 _SQL = sam.SQL
|
||||
local _error = sam.SQL.Error
|
||||
local sql_query = sql.Query
|
||||
|
||||
local SQL = {}
|
||||
|
||||
function SQL.Connect(callback)
|
||||
timer.Simple(0, callback)
|
||||
return true
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
local transactions
|
||||
|
||||
local add_transaction = function(query)
|
||||
table.insert(transactions, query)
|
||||
end
|
||||
|
||||
function SQL.Begin()
|
||||
transactions = {}
|
||||
sql_query("BEGIN TRANSACTION")
|
||||
return add_transaction
|
||||
end
|
||||
|
||||
function SQL.Commit(callback)
|
||||
for i = 1, #transactions do
|
||||
if sql_query(transactions[i]) == false then
|
||||
sql_query("ROLLBACK TRANSACTION")
|
||||
transactions = nil
|
||||
_error("Transaction error: " .. sql.LastError())
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
transactions = nil
|
||||
|
||||
sql_query("COMMIT TRANSACTION")
|
||||
|
||||
if callback then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
local query_obj = {
|
||||
wait = function() end -- mysqloo has query:wait()
|
||||
}
|
||||
|
||||
function SQL.Query(query, callback, first_row, callback_obj)
|
||||
local data = sql_query(query)
|
||||
if data == false then
|
||||
_error("Query error: " .. sql.LastError())
|
||||
elseif callback then
|
||||
if data == nil then
|
||||
if not first_row then
|
||||
data = {}
|
||||
end
|
||||
elseif first_row then
|
||||
data = data[1]
|
||||
end
|
||||
|
||||
callback(data, callback_obj)
|
||||
end
|
||||
|
||||
return query_obj
|
||||
end
|
||||
|
||||
-- local concat = table.concat
|
||||
-- local prepared_set_values = function(prepared_query, values)
|
||||
-- for i = 1, prepared_query.args_n do
|
||||
-- prepared_query[prepared_query[-i]] = _SQL.Escape(values[i])
|
||||
-- end
|
||||
-- return concat(prepared_query, "", 1, prepared_query.n)
|
||||
-- end
|
||||
|
||||
-- local prepared_start = function()
|
||||
-- end
|
||||
|
||||
-- local sub, find = string.sub, string.find
|
||||
-- function SQL.Prepare(query, callback, first_row, callback_obj)
|
||||
-- local prepared_query = {}
|
||||
-- prepared_query.wait = query_obj.wait
|
||||
-- prepared_query.Start = prepared_start
|
||||
-- prepared_query.SetValues = prepared_set_values
|
||||
|
||||
-- local count, args_n = 0, 0
|
||||
-- local pos, start, _end = 0, nil, 0
|
||||
-- while true do
|
||||
-- start, _end = find(query, "?", _end + 1, true)
|
||||
|
||||
-- if not start then
|
||||
-- break
|
||||
-- end
|
||||
|
||||
-- if pos ~= start then
|
||||
-- count = count + 1; prepared_query[count] = sub(query, pos, start - 1)
|
||||
-- end
|
||||
|
||||
-- count = count + 1; prepared_query[count] = "NULL"
|
||||
-- args_n = args_n - 1; prepared_query[args_n] = count
|
||||
|
||||
-- pos = _end + 1
|
||||
-- end
|
||||
|
||||
-- if pos <= #query then
|
||||
-- count = count + 1; prepared_query[count] = sub(query, pos)
|
||||
-- end
|
||||
|
||||
-- prepared_query.n = count
|
||||
-- prepared_query.args_n = abs(args_n)
|
||||
|
||||
-- return prepared_query
|
||||
-- end
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
local SQLStr = SQLStr
|
||||
function SQL.EscapeString(value, no_quotes)
|
||||
return SQLStr(value, no_quotes)
|
||||
end
|
||||
|
||||
function SQL.TableExistsQuery(name)
|
||||
return "SELECT `name` FROM `sqlite_master` WHERE `name` = " .. SQL.EscapeString(name) .. " AND `type` = 'table'"
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
--
|
||||
|
||||
return SQL
|
||||
152
addons/sam/lua/sam/libs/sql/sv_init.lua
Normal file
152
addons/sam/lua/sam/libs/sql/sv_init.lua
Normal file
@@ -0,0 +1,152 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local format, isstring, istable, tonumber = string.format, sam.isstring, sam.istable, tonumber
|
||||
|
||||
local config = {}
|
||||
|
||||
local SQL, _SQL = {}, nil
|
||||
|
||||
function SQL.Print(...)
|
||||
MsgC(
|
||||
Color(255, 255, 255), "(",
|
||||
Color(244, 67, 54), SQL.GetAddonName(),
|
||||
Color(255, 255, 255), " | ",
|
||||
Color(244, 67, 54), SQL.IsMySQL() and "MySQL" or "SQLite",
|
||||
Color(255, 255, 255), ") ",
|
||||
...
|
||||
)
|
||||
Msg("\n")
|
||||
end
|
||||
|
||||
function SQL.Error(err, trace)
|
||||
SQL.Print(err, trace or debug.traceback("", 2))
|
||||
end
|
||||
|
||||
function SQL.Connect()
|
||||
return _SQL.Connect(SQL.OnConnected, SQL.OnConnectionFailed, config)
|
||||
end
|
||||
|
||||
do
|
||||
local in_transaction, old_query = false, nil
|
||||
|
||||
function SQL.Begin()
|
||||
if in_transaction then
|
||||
return SQL.Error("transaction on going!")
|
||||
end
|
||||
in_transaction = true
|
||||
|
||||
SQL.Query, old_query = _SQL.Begin(), SQL.Query
|
||||
end
|
||||
|
||||
function SQL.Commit(callback)
|
||||
if not in_transaction then return end
|
||||
|
||||
in_transaction = false
|
||||
SQL.Query, old_query = old_query, nil
|
||||
|
||||
return _SQL.Commit(callback)
|
||||
end
|
||||
end
|
||||
|
||||
local gsub = string.gsub
|
||||
function SQL.FQuery(query, args, callback, first_row, callback_obj)
|
||||
query = gsub(query, "{(%d)(f?)}", function(i, no_escape)
|
||||
return SQL.Escape(args[tonumber(i)], no_escape ~= "")
|
||||
end)
|
||||
|
||||
return SQL.Query(query, callback, first_row, callback_obj)
|
||||
end
|
||||
|
||||
do
|
||||
local table_exists = function(data, callback)
|
||||
callback(data and true or false)
|
||||
end
|
||||
|
||||
function SQL.TableExists(name, callback)
|
||||
return SQL.Query(_SQL.TableExistsQuery(name), table_exists, true, callback)
|
||||
end
|
||||
end
|
||||
|
||||
function SQL.IsMySQL()
|
||||
return config.MySQL == true
|
||||
end
|
||||
|
||||
do
|
||||
local connected = false
|
||||
function SQL.IsConnected()
|
||||
return connected
|
||||
end
|
||||
function SQL.SetConnected(is_connected)
|
||||
connected = is_connected
|
||||
end
|
||||
end
|
||||
|
||||
function SQL.Escape(value, no_quotes)
|
||||
local value_type = type(value)
|
||||
if value_type == "string" then
|
||||
return _SQL.EscapeString(value, no_quotes)
|
||||
elseif value_type == "number" then
|
||||
return value
|
||||
else
|
||||
error(
|
||||
format(
|
||||
"%s invalid type '%s' was passed to escape '%s'",
|
||||
"(" .. SQL.GetAddonName() .. " | " .. (SQL.IsMySQL() and "MySQL" or "SQLite") .. ")",
|
||||
value_type,
|
||||
value
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function SQL.OnConnected()
|
||||
SQL.SetConnected(true)
|
||||
hook.Call(SQL.GetAddonName() .. ".DatabaseConnected")
|
||||
end
|
||||
|
||||
function SQL.OnConnectionFailed(error_text)
|
||||
SQL.Error("Failed to connect to the server: " .. error_text)
|
||||
hook.Call(SQL.GetAddonName() .. ".DatabaseConnectionFailed", nil, error_text)
|
||||
end
|
||||
|
||||
function SQL.SetConfig(new_config)
|
||||
if not istable(new_config) then return end
|
||||
if new_config.MySQL == true then
|
||||
for _, v in ipairs({"Host", "Username", "Password", "Database"}) do
|
||||
if not isstring(new_config[v]) then
|
||||
return SQL.Error(
|
||||
format("config value for '%s' is invalid '%s' needs to be a string!", v, config[v])
|
||||
)
|
||||
end
|
||||
end
|
||||
new_config.Port = tonumber(new_config.Port) or 3306
|
||||
_SQL = sam.load_file("sam/libs/sql/databases/mysql.lua", "sv_")
|
||||
else
|
||||
_SQL = sam.load_file("sam/libs/sql/databases/sqlite.lua", "sv_")
|
||||
end
|
||||
|
||||
SQL.Query = _SQL.Query
|
||||
config = new_config
|
||||
end
|
||||
|
||||
do
|
||||
local addon_name = "NO NAME"
|
||||
function SQL.SetAddonName(name)
|
||||
addon_name = name
|
||||
end
|
||||
function SQL.GetAddonName()
|
||||
return addon_name
|
||||
end
|
||||
end
|
||||
|
||||
sam.SQL = SQL
|
||||
265
addons/sam/lua/sam/menu/cl_init.lua
Normal file
265
addons/sam/lua/sam/menu/cl_init.lua
Normal file
@@ -0,0 +1,265 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local vgui = vgui
|
||||
local draw = draw
|
||||
|
||||
local sam = sam
|
||||
local sui = sui
|
||||
local TDLib = sui.TDLib
|
||||
|
||||
local config = sam.config
|
||||
|
||||
do
|
||||
local funcs = {
|
||||
["SAM.ComboBox"] = {
|
||||
event = "OnSelect",
|
||||
function(s, _, value)
|
||||
config.set(s.config_key, value)
|
||||
end
|
||||
},
|
||||
["SAM.TextEntry"] = {
|
||||
event = "OnEnter",
|
||||
function(s)
|
||||
local v = s:GetText()
|
||||
if s:GetNumeric() then
|
||||
v = tonumber(v)
|
||||
end
|
||||
config.set(s.config_key, v)
|
||||
end
|
||||
},
|
||||
["SAM.ToggleButton"] = {
|
||||
event = "OnChange",
|
||||
function(s, v)
|
||||
config.set(s.config_key, v)
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
sam.SUI = sam.SUI or sui.new("SAM", true, {
|
||||
SetConfig = function(s, key, default)
|
||||
s.config_key = key
|
||||
|
||||
local i = config.hook({key}, function(value, old)
|
||||
local v = config.get(key, default)
|
||||
s:SetValue(v)
|
||||
end)
|
||||
|
||||
local t = funcs[s:GetName()]
|
||||
s[t.event] = t[1]
|
||||
|
||||
s:On("OnRemove", function()
|
||||
config.remove_hook(i)
|
||||
end)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local SUI = sam.SUI
|
||||
local GetColor = SUI.GetColor
|
||||
|
||||
sam.menu = {}
|
||||
|
||||
local tabs = {}
|
||||
function sam.menu.add_tab(icon, func, check, pos)
|
||||
local tab = {
|
||||
icon = icon,
|
||||
func = func,
|
||||
check = check,
|
||||
pos = pos
|
||||
}
|
||||
for k, v in ipairs(tabs) do
|
||||
if v.icon == icon then
|
||||
tabs[k] = tab
|
||||
return
|
||||
end
|
||||
end
|
||||
table.insert(tabs, tab)
|
||||
end
|
||||
|
||||
function sam.menu.remove_tab(name)
|
||||
for k, v in ipairs(tabs) do
|
||||
if v.name == name then
|
||||
table.remove(tabs, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SAM_TAB_TITLE_FONT = SUI.CreateFont("TabTitle", "Roboto Bold", 22)
|
||||
SAM_TAB_DESC_FONT = SUI.CreateFont("TabDesc", "Roboto Medium", 15)
|
||||
|
||||
local MENU_LOADING = SUI.CreateFont("MenuLoading", "Roboto", 30)
|
||||
|
||||
SUI.AddToTheme("Dark", {
|
||||
frame = "#181818",
|
||||
|
||||
scroll_panel = "#181818",
|
||||
|
||||
menu_tabs_title = "#ffffff",
|
||||
|
||||
--=--
|
||||
player_list_titles = "#f2f1ef",
|
||||
|
||||
player_list_names = "#eeeeee",
|
||||
player_list_names_2 = "#ff6347",
|
||||
player_list_data = "#e8e8e8",
|
||||
|
||||
player_list_rank = "#41b9ff",
|
||||
player_list_console = "#00c853",
|
||||
player_list_rank_text = "#2c3e50",
|
||||
|
||||
player_list_steamid = "#a4a4a4",
|
||||
--=--
|
||||
|
||||
--=--
|
||||
actions_button = Color(0, 0, 0, 0),
|
||||
actions_button_hover = Color(200, 200, 200, 60),
|
||||
|
||||
actions_button_icon = "#aaaaaa",
|
||||
actions_button_icon_hover = "#ffffff",
|
||||
--=--
|
||||
|
||||
--=--
|
||||
page_switch_bg = "#222222",
|
||||
--=--
|
||||
})
|
||||
|
||||
SUI.SetTheme("Dark")
|
||||
|
||||
function SUI.panels.Frame:Paint(w, h)
|
||||
if GetColor("frame_blur") then
|
||||
TDLib.BlurPanel(self)
|
||||
end
|
||||
|
||||
draw.RoundedBox(8, 0, 0, w, h, GetColor("frame"))
|
||||
end
|
||||
|
||||
function SUI.panels.Frame:HeaderPaint(w, h)
|
||||
draw.RoundedBoxEx(8, 0, 0, w, h, GetColor("header"), true, true, false, false)
|
||||
draw.RoundedBox(0, 0, h - 1, w, 1, GetColor("line"))
|
||||
end
|
||||
|
||||
do
|
||||
function sam.menu.add_loading_panel(parent)
|
||||
local is_loading = false
|
||||
|
||||
local loading_panel = parent:Add("Panel")
|
||||
loading_panel:SetVisible(false)
|
||||
loading_panel:SetZPos(999999)
|
||||
loading_panel:SetMouseInputEnabled(false)
|
||||
|
||||
function loading_panel:Paint(w, h)
|
||||
draw.RoundedBox(3, 0, 0, w, h, Color(50, 50, 50, 200))
|
||||
draw.SimpleText(string.rep(".", (CurTime() * 3) % 3), MENU_LOADING, w/2, h/2, Color(200, 200, 200, 200), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER)
|
||||
end
|
||||
|
||||
parent:SUI_TDLib()
|
||||
parent:On("PerformLayout", function(s, w, h)
|
||||
loading_panel:SetSize(w, h)
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local toggle_loading = function(bool)
|
||||
if not IsValid(loading_panel) then return end
|
||||
|
||||
is_loading = bool or not is_loading
|
||||
if is_loading and not first then
|
||||
loading_panel:SetVisible(is_loading and true or false)
|
||||
loading_panel:SetMouseInputEnabled(is_loading)
|
||||
else
|
||||
timer.Simple(0.2, function()
|
||||
if not IsValid(loading_panel) then return end
|
||||
loading_panel:SetVisible(is_loading and true or false)
|
||||
loading_panel:SetMouseInputEnabled(is_loading)
|
||||
end)
|
||||
end
|
||||
|
||||
first = false
|
||||
end
|
||||
|
||||
return toggle_loading, function()
|
||||
return is_loading
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local sam_menu
|
||||
function sam.menu.open_menu()
|
||||
if IsValid(sam_menu) then
|
||||
return sam_menu:IsVisible() and sam_menu:Hide() or sam_menu:Show()
|
||||
-- sam_menu:Remove()
|
||||
end
|
||||
|
||||
sam_menu = vgui.Create("SAM.Frame")
|
||||
sam_menu:Center()
|
||||
sam_menu:MakePopup()
|
||||
sam_menu:SetTitle("SAM")
|
||||
|
||||
sam_menu:AddAnimations(800, 600)
|
||||
|
||||
sam_menu.close.DoClick = function()
|
||||
sam_menu:Hide()
|
||||
end
|
||||
|
||||
local sheet = sam_menu:Add("SAM.ColumnSheet")
|
||||
sheet:Dock(FILL)
|
||||
sheet:InvalidateParent(true)
|
||||
sheet:InvalidateLayout(true)
|
||||
sheet.Paint = nil
|
||||
|
||||
local tab_scroller = sheet.tab_scroller
|
||||
tab_scroller:DockMargin(0, 1, 0, 1)
|
||||
|
||||
function tab_scroller:Paint(w, h)
|
||||
draw.RoundedBoxEx(8, 0, 0, w, h, GetColor("column_sheet_bar"), false, false, true, false)
|
||||
end
|
||||
|
||||
local sheets = {}
|
||||
for _, v in SortedPairsByMemberValue(tabs, "pos") do
|
||||
sheets[v.icon] = sheet:AddSheet(v.icon, v.func)
|
||||
end
|
||||
|
||||
tab_scroller = tab_scroller:GetCanvas()
|
||||
sam_menu:On("Think", function()
|
||||
for _, v in ipairs(tabs) do
|
||||
local tab = sheets[v.icon]
|
||||
if v.check and not v.check() then
|
||||
if tab:IsVisible() then
|
||||
tab:SetVisible(false)
|
||||
if sheet:GetActiveTab() == tab then
|
||||
sheet:SetActiveTab(sheet.tabs[1])
|
||||
end
|
||||
tab_scroller:InvalidateLayout()
|
||||
end
|
||||
elseif not tab:IsVisible() then
|
||||
tab:SetVisible(true)
|
||||
tab_scroller:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.menu.get()
|
||||
return sam_menu
|
||||
end
|
||||
|
||||
hook.Add("GUIMouseReleased", "SAM.CloseMenu", function(mouse_code)
|
||||
local panel = vgui.GetHoveredPanel()
|
||||
if mouse_code == MOUSE_LEFT and panel == vgui.GetWorldPanel() and IsValid(sam_menu) and sam_menu:HasHierarchicalFocus() then
|
||||
sam_menu:Hide()
|
||||
end
|
||||
end)
|
||||
|
||||
for _, f in ipairs(file.Find("sam/menu/tabs/*.lua", "LUA")) do
|
||||
sam.load_file("sam/menu/tabs/" .. f, "sh")
|
||||
end
|
||||
34
addons/sam/lua/sam/menu/sh_init.lua
Normal file
34
addons/sam/lua/sam/menu/sh_init.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
require("sui")
|
||||
|
||||
sam.command.new("menu")
|
||||
:Help("Open admin mod menu")
|
||||
:MenuHide()
|
||||
:DisableNotify()
|
||||
:OnExecute(function(ply)
|
||||
sam.netstream.Start(ply, "OpenMenu")
|
||||
end)
|
||||
:End()
|
||||
|
||||
if CLIENT then
|
||||
sam.netstream.Hook("OpenMenu", function()
|
||||
sam.menu.open_menu()
|
||||
end)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
for _, f in ipairs(file.Find("sam/menu/tabs/*.lua", "LUA")) do
|
||||
sam.load_file("sam/menu/tabs/" .. f)
|
||||
end
|
||||
end
|
||||
419
addons/sam/lua/sam/menu/tabs/bans.lua
Normal file
419
addons/sam/lua/sam/menu/tabs/bans.lua
Normal file
@@ -0,0 +1,419 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local SQL = sam.SQL
|
||||
local SUI = sam.SUI
|
||||
local netstream = sam.netstream
|
||||
|
||||
sam.permissions.add("manage_bans", nil, "superadmin")
|
||||
|
||||
local get_pages_count = function(bans_count)
|
||||
bans_count = bans_count / 35
|
||||
local i2 = math.floor(bans_count)
|
||||
return bans_count ~= i2 and i2 + 1 or bans_count
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local check = function(ply)
|
||||
return ply:HasPermission("manage_bans") and ply:sam_check_cooldown("MenuManageBans", 0.1)
|
||||
end
|
||||
|
||||
local limit = 35
|
||||
|
||||
local get_page_count = function(res, callback, page, order_by, keyword)
|
||||
local current_time = os.time()
|
||||
local query = [[
|
||||
SELECT
|
||||
COUNT(`steamid`) AS `count`
|
||||
FROM
|
||||
`sam_bans`
|
||||
WHERE
|
||||
(`unban_date` >= %d OR `unban_date` = 0)]]
|
||||
|
||||
query = query:format(current_time)
|
||||
|
||||
if keyword then
|
||||
query = query .. " AND `steamid` LIKE " .. SQL.Escape("%" .. keyword .. "%")
|
||||
end
|
||||
|
||||
SQL.Query(query, callback, true, {res, page, order_by, keyword, current_time})
|
||||
end
|
||||
|
||||
local resolve_promise = function(data, arguments)
|
||||
local res = arguments[1]
|
||||
arguments[1] = data
|
||||
res(arguments)
|
||||
end
|
||||
|
||||
local get_bans = function(count_data, arguments)
|
||||
local res, page, order_by, keyword, current_time = unpack(arguments)
|
||||
local count = count_data.count
|
||||
|
||||
local current_page
|
||||
if page < 1 then
|
||||
page, current_page = 1, 1
|
||||
end
|
||||
|
||||
local pages_count = get_pages_count(count)
|
||||
if page > pages_count then
|
||||
page, current_page = pages_count, pages_count
|
||||
end
|
||||
|
||||
local query = [[
|
||||
SELECT
|
||||
`sam_bans`.*,
|
||||
IFNULL(`p1`.`name`, '') AS `name`,
|
||||
IFNULL(`p2`.`name`, '') AS `admin_name`
|
||||
FROM
|
||||
`sam_bans`
|
||||
LEFT OUTER JOIN
|
||||
`sam_players` AS `p1`
|
||||
ON
|
||||
`sam_bans`.`steamid` = `p1`.`steamid`
|
||||
LEFT OUTER JOIN
|
||||
`sam_players` AS `p2`
|
||||
ON
|
||||
`sam_bans`.`admin` = `p2`.`steamid`
|
||||
WHERE
|
||||
(`sam_bans`.`unban_date` >= %d OR `sam_bans`.`unban_date` = 0)]]
|
||||
|
||||
query = query:format(current_time)
|
||||
|
||||
if keyword then
|
||||
query = query .. " AND `sam_bans`.`steamid` LIKE " .. SQL.Escape("%" .. keyword .. "%")
|
||||
end
|
||||
|
||||
local offset = math.abs(limit * (page - 1))
|
||||
query = query .. ([[
|
||||
ORDER BY
|
||||
`sam_bans`.`id` %s
|
||||
LIMIT
|
||||
%d OFFSET %d]]):format(order_by, limit, offset)
|
||||
|
||||
SQL.Query(query, resolve_promise, nil, {res, count, current_page})
|
||||
end
|
||||
|
||||
netstream.async.Hook("SAM.GetBans", function(res, ply, page, order_by, keyword)
|
||||
if not isnumber(page) then return end
|
||||
if order_by ~= "ASC" and order_by ~= "DESC" then return end
|
||||
if keyword ~= nil and not sam.isstring(keyword) then return end
|
||||
|
||||
get_page_count(res, get_bans, page, order_by, keyword)
|
||||
end, check)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
local Line = sui.TDLib.LibClasses.Line
|
||||
|
||||
local COLUMN_FONT = SUI.CreateFont("Column", "Roboto", 18)
|
||||
local LINE_FONT = SUI.CreateFont("Line", "Roboto", 16)
|
||||
local NEXT_FONT = SUI.CreateFont("NextButton", "Roboto", 18)
|
||||
|
||||
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/ban-user.png", function(column_sheet)
|
||||
local refresh, pages
|
||||
local current_page, current_order, keyword = nil, "DESC", nil
|
||||
|
||||
local bans_body = column_sheet:Add("Panel")
|
||||
bans_body:Dock(FILL)
|
||||
bans_body:DockMargin(0, 1, 0, 0)
|
||||
bans_body:DockPadding(10, 10, 10, 10)
|
||||
|
||||
local toggle_loading, is_loading = sam.menu.add_loading_panel(bans_body)
|
||||
|
||||
local title = bans_body:Add("SAM.Label")
|
||||
title:Dock(TOP)
|
||||
title:SetFont(SAM_TAB_TITLE_FONT)
|
||||
title:SetText("Bans")
|
||||
title:SetTextColor(GetColor("menu_tabs_title"))
|
||||
title:SizeToContents()
|
||||
|
||||
local total = bans_body:Add("SAM.Label")
|
||||
total:Dock(TOP)
|
||||
total:DockMargin(0, 6, 0, 0)
|
||||
total:SetFont(SAM_TAB_DESC_FONT)
|
||||
total:SetText("60 total bans")
|
||||
total:SetTextColor(GetColor("menu_tabs_title"))
|
||||
total:SetPos(10, SUI.Scale(40))
|
||||
total:SizeToContents()
|
||||
|
||||
do
|
||||
local container = bans_body:Add("SAM.Panel")
|
||||
container:Dock(TOP)
|
||||
container:DockMargin(0, 6, 10, 0)
|
||||
container:SetTall(30)
|
||||
|
||||
local sort_order = container:Add("SAM.ComboBox")
|
||||
sort_order:Dock(RIGHT)
|
||||
sort_order:SetWide(96)
|
||||
sort_order:SetValue("Desc")
|
||||
sort_order:AddChoice("Desc")
|
||||
sort_order:AddChoice("Asc")
|
||||
|
||||
function sort_order:OnSelect(_, value)
|
||||
value = value:upper()
|
||||
if current_order ~= value then
|
||||
current_order = value
|
||||
refresh()
|
||||
end
|
||||
end
|
||||
|
||||
local search_entry = container:Add("SAM.TextEntry")
|
||||
search_entry:Dock(LEFT)
|
||||
search_entry:SetNoBar(true)
|
||||
search_entry:SetPlaceholder("Search...")
|
||||
search_entry:SetRadius(4)
|
||||
search_entry:SetWide(220)
|
||||
|
||||
function search_entry:OnEnter()
|
||||
local value = self:GetValue()
|
||||
if keyword ~= value then
|
||||
keyword = value ~= "" and value or nil
|
||||
refresh()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Line(bans_body, nil, -5, 15, -5, 0)
|
||||
|
||||
do
|
||||
local columns = bans_body:Add("Panel")
|
||||
columns:Dock(TOP)
|
||||
columns:DockMargin(0, 10, 0, 0)
|
||||
|
||||
local info = columns:Add("SAM.Label")
|
||||
info:Dock(LEFT)
|
||||
info:DockMargin(4, 0, 0, 0)
|
||||
info:SetFont(COLUMN_FONT)
|
||||
info:SetText("Player")
|
||||
info:SetTextColor(GetColor("player_list_titles"))
|
||||
info:SetWide(SUI.Scale(280) + SUI.Scale(34))
|
||||
info:SizeToContentsY(3)
|
||||
|
||||
local time_left = columns:Add("SAM.Label")
|
||||
time_left:Dock(LEFT)
|
||||
time_left:DockMargin(-4, 0, 0, 0)
|
||||
time_left:SetFont(COLUMN_FONT)
|
||||
time_left:SetText("Time Left")
|
||||
time_left:SetTextColor(GetColor("player_list_titles"))
|
||||
time_left:SetWide(SUI.Scale(180))
|
||||
time_left:SizeToContentsY(3)
|
||||
|
||||
local reason = columns:Add("SAM.Label")
|
||||
reason:Dock(LEFT)
|
||||
reason:DockMargin(-4, 0, 0, 0)
|
||||
reason:SetFont(COLUMN_FONT)
|
||||
reason:SetText("Reason")
|
||||
reason:SetTextColor(GetColor("player_list_titles"))
|
||||
reason:SetWide(SUI.Scale(280))
|
||||
reason:SizeToContentsY(3)
|
||||
|
||||
columns:SizeToChildren(false, true)
|
||||
end
|
||||
|
||||
local body = bans_body:Add("SAM.ScrollPanel")
|
||||
body:Dock(FILL)
|
||||
body:DockMargin(0, 10, 0, 0)
|
||||
body:SetVBarPadding(6)
|
||||
|
||||
local set_data = function(data)
|
||||
body:GetCanvas():Clear()
|
||||
body.VBar.Scroll = 0
|
||||
|
||||
local bans, bans_count, current_page_2 = unpack(data)
|
||||
total:SetText(bans_count .. " total bans")
|
||||
|
||||
pages = get_pages_count(bans_count)
|
||||
current_page.i = pages == 0 and 0 or current_page_2 or current_page.i
|
||||
current_page:SetText(current_page.i .. "/" .. pages)
|
||||
|
||||
body:Line()
|
||||
|
||||
for k, v in ipairs(bans) do
|
||||
local line = body:Add("SAM.PlayerLine")
|
||||
line:DockMargin(0, 0, 0, 10)
|
||||
|
||||
local name = v.name ~= "" and v.name or nil
|
||||
local admin_name = v.admin_name ~= "" and v.admin_name or nil
|
||||
line:SetInfo({
|
||||
steamid = v.steamid,
|
||||
name = name,
|
||||
rank = admin_name or (v.admin == "Console" and "Console"),
|
||||
rank_bg = not admin_name and GetColor("player_list_console")
|
||||
})
|
||||
|
||||
local unban_date = tonumber(v.unban_date)
|
||||
local time_left = line:Add("SAM.Label")
|
||||
time_left:Dock(LEFT)
|
||||
time_left:DockMargin(-3, 0, 0, 0)
|
||||
time_left:SetFont(LINE_FONT)
|
||||
time_left:SetText(unban_date == 0 and "Never" or sam.reverse_parse_length((unban_date - os.time()) / 60))
|
||||
time_left:SetTextColor(GetColor("player_list_data"))
|
||||
time_left:SetContentAlignment(4)
|
||||
time_left:SetWide(SUI.Scale(180))
|
||||
|
||||
local reason = line:Add("SAM.Label")
|
||||
reason:Dock(LEFT)
|
||||
reason:DockMargin(4, 0, 0, 0)
|
||||
reason:SetFont(LINE_FONT)
|
||||
reason:SetText(v.reason)
|
||||
reason:SetTextColor(GetColor("player_list_data"))
|
||||
reason:SetContentAlignment(4)
|
||||
reason:SetWrap(true)
|
||||
reason:SetWide(SUI.Scale(200))
|
||||
|
||||
local old_tall = line.size
|
||||
function reason:PerformLayout()
|
||||
local _, h = self:GetTextSize()
|
||||
if old_tall < h then
|
||||
line:SetTall(h)
|
||||
end
|
||||
end
|
||||
|
||||
local but = line:Actions()
|
||||
but:On("DoClick", function()
|
||||
local dmenu = vgui.Create("SAM.Menu")
|
||||
dmenu:SetInternal(but)
|
||||
if name then
|
||||
dmenu:AddOption("Copy Name", function()
|
||||
SetClipboardText(name)
|
||||
end)
|
||||
end
|
||||
dmenu:AddOption("Copy SteamID", function()
|
||||
SetClipboardText(v.steamid)
|
||||
end)
|
||||
dmenu:AddOption("Copy Reason", function()
|
||||
SetClipboardText(v.reason)
|
||||
end)
|
||||
dmenu:AddOption("Copy Time Left", function()
|
||||
SetClipboardText(time_left:GetText())
|
||||
end)
|
||||
|
||||
if v.admin ~= "Console" then
|
||||
dmenu:AddSpacer()
|
||||
|
||||
if admin_name then
|
||||
dmenu:AddOption("Copy Admin Name", function()
|
||||
SetClipboardText(admin_name)
|
||||
end)
|
||||
end
|
||||
|
||||
dmenu:AddOption("Copy Admin SteamID", function()
|
||||
SetClipboardText(v.admin)
|
||||
end)
|
||||
end
|
||||
|
||||
if LocalPlayer():HasPermission("unban") then
|
||||
dmenu:AddSpacer()
|
||||
dmenu:AddOption("Unban", function()
|
||||
local user = name and ("%s (%s)"):format(name, v.steamid) or v.steamid
|
||||
local querybox = vgui.Create("SAM.QueryBox")
|
||||
querybox:SetWide(350)
|
||||
querybox:SetTitle(user)
|
||||
|
||||
local check = querybox:Add("SAM.Label")
|
||||
check:SetText(sui.wrap_text("Are you sure that you want to unban\n" .. user, LINE_FONT, SUI.Scale(350)))
|
||||
check:SetFont(LINE_FONT)
|
||||
check:SizeToContents()
|
||||
|
||||
querybox:Done()
|
||||
querybox.save:SetEnabled(true)
|
||||
querybox.save:SetText("UNBAN")
|
||||
|
||||
querybox.save:SetContained(false)
|
||||
querybox.save:SetColors(GetColor("query_box_cancel"), GetColor("query_box_cancel_text"))
|
||||
|
||||
querybox.cancel:SetContained(true)
|
||||
querybox.cancel:SetColors()
|
||||
|
||||
querybox:SetCallback(function()
|
||||
RunConsoleCommand("sam", "unban", v.steamid)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
dmenu:Open()
|
||||
end)
|
||||
|
||||
body:Line()
|
||||
end
|
||||
|
||||
body:InvalidateLayout(true)
|
||||
body:GetCanvas():InvalidateLayout(true)
|
||||
end
|
||||
|
||||
refresh = function()
|
||||
if not is_loading() and LocalPlayer():HasPermission("manage_bans") then
|
||||
local refresh_query = netstream.async.Start("SAM.GetBans", toggle_loading, current_page.i, current_order, keyword)
|
||||
refresh_query:done(set_data)
|
||||
end
|
||||
end
|
||||
|
||||
local bottom_panel = bans_body:Add("SAM.Panel")
|
||||
bottom_panel:Dock(BOTTOM)
|
||||
bottom_panel:DockMargin(0, 6, 0, 0)
|
||||
bottom_panel:SetTall(30)
|
||||
bottom_panel:Background(GetColor("page_switch_bg"))
|
||||
|
||||
local previous_page = bottom_panel:Add("SAM.Button")
|
||||
previous_page:Dock(LEFT)
|
||||
previous_page:SetWide(30)
|
||||
previous_page:SetText("<")
|
||||
previous_page:SetFont(NEXT_FONT)
|
||||
|
||||
previous_page:On("DoClick", function()
|
||||
if current_page.i <= 1 then return end
|
||||
|
||||
current_page.i = current_page.i - 1
|
||||
refresh()
|
||||
end)
|
||||
|
||||
current_page = bottom_panel:Add("SAM.Label")
|
||||
current_page:Dock(FILL)
|
||||
current_page:SetContentAlignment(5)
|
||||
current_page:SetFont(SAM_TAB_DESC_FONT)
|
||||
current_page:SetText("loading...")
|
||||
current_page.i = 1
|
||||
|
||||
local next_page = bottom_panel:Add("SAM.Button")
|
||||
next_page:Dock(RIGHT)
|
||||
next_page:SetWide(30)
|
||||
next_page:SetText(">")
|
||||
next_page:SetFont(NEXT_FONT)
|
||||
|
||||
next_page:On("DoClick", function()
|
||||
if current_page.i == pages then return end
|
||||
|
||||
current_page.i = current_page.i + 1
|
||||
refresh()
|
||||
end)
|
||||
|
||||
function bottom_panel:Think()
|
||||
next_page:SetEnabled(current_page.i ~= pages)
|
||||
previous_page:SetEnabled(current_page.i > 1)
|
||||
end
|
||||
|
||||
for k, v in ipairs({"SAM.BannedPlayer", "SAM.BannedSteamID", "SAM.EditedBan", "SAM.UnbannedSteamID"}) do
|
||||
hook.Add(v, "SAM.MenuBans", function()
|
||||
if IsValid(body) then
|
||||
refresh()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
refresh()
|
||||
|
||||
return bans_body
|
||||
end, function()
|
||||
return LocalPlayer():HasPermission("manage_bans")
|
||||
end, 4)
|
||||
233
addons/sam/lua/sam/menu/tabs/commands.lua
Normal file
233
addons/sam/lua/sam/menu/tabs/commands.lua
Normal file
@@ -0,0 +1,233 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
if SERVER then return end
|
||||
|
||||
local sam = sam
|
||||
local SUI = sam.SUI
|
||||
local type = sam.type
|
||||
|
||||
local Line = sui.TDLib.LibClasses.Line
|
||||
|
||||
local COMMAND_HELP = SUI.CreateFont("CommandHelp", "Roboto", 14)
|
||||
local COMMAND_RUN = SUI.CreateFont("CommandRun", "Roboto Medium", 14)
|
||||
|
||||
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/command_window.png", function(column_sheet)
|
||||
local tab_body = column_sheet:Add("Panel")
|
||||
tab_body:Dock(FILL)
|
||||
tab_body:DockMargin(0, 1, 0, 0)
|
||||
|
||||
do
|
||||
local title = tab_body:Add("SAM.Label")
|
||||
title:Dock(TOP)
|
||||
title:DockMargin(10, 10, 0, 0)
|
||||
title:SetFont(SAM_TAB_TITLE_FONT)
|
||||
title:SetText("Commands")
|
||||
title:SetTextColor(SUI.GetColor("menu_tabs_title"))
|
||||
title:SizeToContents()
|
||||
end
|
||||
|
||||
local body = tab_body:Add("Panel")
|
||||
body:Dock(FILL)
|
||||
body:DockMargin(10, 5, 10, 10)
|
||||
|
||||
Line(body)
|
||||
|
||||
local left_body = body:Add("SAM.Panel")
|
||||
left_body:Dock(LEFT)
|
||||
left_body:SetWide(148)
|
||||
|
||||
local search_entry = left_body:Add("SAM.TextEntry")
|
||||
search_entry:Dock(TOP)
|
||||
search_entry:SetNoBar(true)
|
||||
search_entry:SetPlaceholder("Search...")
|
||||
search_entry:SetRadius(4)
|
||||
search_entry:SetTall(27)
|
||||
|
||||
local category_list = left_body:Add("SAM.CollapseCategory")
|
||||
category_list:Dock(FILL)
|
||||
category_list:DockMargin(0, SUI.Scale(10), 0, 0)
|
||||
|
||||
local canvas = category_list:GetCanvas()
|
||||
|
||||
local commands_refresh = function()
|
||||
if not IsValid(category_list) then return end
|
||||
|
||||
canvas:Clear()
|
||||
table.Empty(category_list.items)
|
||||
table.Empty(category_list.categories)
|
||||
|
||||
for k, v in ipairs(sam.command.get_commands()) do
|
||||
if (v.permission and not LocalPlayer():HasPermission(v.permission)) or v.menu_hide then
|
||||
continue
|
||||
end
|
||||
|
||||
local item = category_list:add_item(v.name, v.category)
|
||||
item:InvalidateParent(true)
|
||||
item.help = v.help
|
||||
item.command = v
|
||||
|
||||
item.names = {v.name:lower()}
|
||||
for _, aliase in ipairs(v.aliases) do
|
||||
table.insert(item.names, aliase:lower())
|
||||
end
|
||||
end
|
||||
end
|
||||
commands_refresh()
|
||||
|
||||
do
|
||||
local hooks = {
|
||||
"SAM.CommandAdded", "SAM.CommandModified", "SAM.CommandRemoved",
|
||||
"SAM.RemovedPermission",
|
||||
{"SAM.ChangedPlayerRank", func = function(ply, rank, old_rank)
|
||||
if rank == old_rank then return end
|
||||
if ply == LocalPlayer() then
|
||||
commands_refresh()
|
||||
end
|
||||
end},
|
||||
{
|
||||
"SAM.RankPermissionGiven", "SAM.RankPermissionTaken", "SAM.ChangedInheritRank",
|
||||
func = function(rank)
|
||||
if rank == LocalPlayer():sam_getrank() then
|
||||
commands_refresh()
|
||||
end
|
||||
end
|
||||
},
|
||||
{
|
||||
"SAM.AddedPermission", "SAM.PermissionModified",
|
||||
func = function(_, _, rank)
|
||||
if rank == LocalPlayer():sam_getrank() then
|
||||
commands_refresh()
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
for _, v in ipairs(hooks) do
|
||||
if type(v) == "table" then
|
||||
for _, v2 in ipairs(v) do
|
||||
hook.Add(v2, "SAM.Menu.RefreshCommands", v.func)
|
||||
end
|
||||
else
|
||||
hook.Add(v, "SAM.Menu.RefreshCommands", commands_refresh)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function search_entry:OnValueChange(text)
|
||||
category_list:Search(text:lower())
|
||||
end
|
||||
|
||||
do
|
||||
local line = Line(body, LEFT)
|
||||
line:DockMargin(10, 0, 10, 0)
|
||||
line:SetWide(1)
|
||||
end
|
||||
|
||||
local buttons = body:Add("SAM.ScrollPanel")
|
||||
buttons:Dock(FILL)
|
||||
|
||||
local childs = {}
|
||||
local pos = 0
|
||||
buttons:GetCanvas():On("OnChildAdded", function(s, child)
|
||||
child:Dock(TOP)
|
||||
child:DockMargin(0, 0, 0, 5)
|
||||
child:SetAlpha(0)
|
||||
child:SetVisible(false)
|
||||
table.insert(childs, child)
|
||||
|
||||
pos = pos + 1
|
||||
child:SetZPos(pos)
|
||||
end)
|
||||
|
||||
local run_command = buttons:Add("SAM.Button")
|
||||
run_command:Dock(TOP)
|
||||
run_command:SetTall(25)
|
||||
run_command:SetFont(COMMAND_RUN)
|
||||
run_command:SetZPos(100)
|
||||
run_command:SetEnabled(false)
|
||||
|
||||
run_command:On("DoClick", function(self)
|
||||
LocalPlayer():ConCommand("sam\"" .. self:GetText() .. "\"\"" .. table.concat(self.input_arguments, "\"\"") .. "\"")
|
||||
end)
|
||||
|
||||
local help = buttons:Add("SAM.Label")
|
||||
help:Dock(TOP)
|
||||
help:SetFont(COMMAND_HELP)
|
||||
help:SetZPos(101)
|
||||
help:SetWrap(true)
|
||||
help:SetAutoStretchVertical(true)
|
||||
|
||||
sam.menu.get():On("OnKeyCodePressed", function(s, key_code)
|
||||
if key_code == KEY_ENTER and IsValid(run_command) and run_command:IsEnabled() and run_command:IsMouseInputEnabled() and tab_body:IsVisible() then
|
||||
run_command:DoClick()
|
||||
end
|
||||
end)
|
||||
|
||||
function category_list:item_selected(item)
|
||||
local arguments = sam.command.get_arguments()
|
||||
local command = item.command
|
||||
local command_arguments = command.args
|
||||
local input_arguments = {}
|
||||
|
||||
for i = #childs, 3, -1 do
|
||||
local v = childs[i]
|
||||
if not v.no_change or not command:HasArg(v.no_change) then
|
||||
if v.no_remove ~= true then
|
||||
v:Remove()
|
||||
else
|
||||
v:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local NIL = {}
|
||||
for _, v in ipairs(command_arguments) do
|
||||
local func = arguments[v.name]["menu"]
|
||||
if not func then continue end
|
||||
|
||||
local i = table.insert(input_arguments, NIL)
|
||||
local p = func(function(allow)
|
||||
if not IsValid(run_command) then return end
|
||||
input_arguments[i] = allow == nil and NIL or allow
|
||||
for i_2 = 1, #input_arguments do
|
||||
if input_arguments[i_2] == NIL then
|
||||
run_command:SetEnabled(false)
|
||||
return
|
||||
end
|
||||
end
|
||||
run_command:SetEnabled(true)
|
||||
end, body, buttons, v, childs)
|
||||
if p then
|
||||
p:AnimatedSetVisible(true)
|
||||
end
|
||||
end
|
||||
|
||||
if #command_arguments == 0 then
|
||||
run_command:SetEnabled(true)
|
||||
end
|
||||
|
||||
run_command:SetText(command.name)
|
||||
run_command:AnimatedSetVisible(true)
|
||||
run_command.input_arguments = input_arguments
|
||||
|
||||
if command.help then
|
||||
help:SetText(command.help)
|
||||
help:AnimatedSetVisible(true)
|
||||
help:SizeToContents()
|
||||
else
|
||||
help:AnimatedSetVisible(false)
|
||||
end
|
||||
|
||||
buttons:InvalidateLayout(true)
|
||||
end
|
||||
|
||||
return tab_body
|
||||
end, nil, 1)
|
||||
107
addons/sam/lua/sam/menu/tabs/config.lua
Normal file
107
addons/sam/lua/sam/menu/tabs/config.lua
Normal file
@@ -0,0 +1,107 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
|
||||
local tabs = {}
|
||||
if CLIENT then
|
||||
function config.add_tab(name, func, check, pos)
|
||||
local tab = {
|
||||
name = name,
|
||||
func = func,
|
||||
check = check,
|
||||
pos = pos
|
||||
}
|
||||
for k, v in ipairs(tabs) do
|
||||
if v.name == name then
|
||||
tabs[k] = tab
|
||||
return
|
||||
end
|
||||
end
|
||||
table.insert(tabs, tab)
|
||||
end
|
||||
end
|
||||
|
||||
for _, f in ipairs(file.Find("sam/menu/tabs/config/*.lua", "LUA")) do
|
||||
sam.load_file("sam/menu/tabs/config/" .. f, "cl_")
|
||||
end
|
||||
|
||||
if SERVER then return end
|
||||
|
||||
local SUI = sam.SUI
|
||||
local GetColor = SUI.GetColor
|
||||
local Line = sui.TDLib.LibClasses.Line
|
||||
|
||||
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/config.png", function(column_sheet)
|
||||
local tab_body = column_sheet:Add("Panel")
|
||||
tab_body:Dock(FILL)
|
||||
tab_body:DockMargin(0, 1, 0, 0)
|
||||
|
||||
do
|
||||
local title = tab_body:Add("SAM.Label")
|
||||
title:Dock(TOP)
|
||||
title:DockMargin(10, 10, 0, 0)
|
||||
title:SetFont(SAM_TAB_TITLE_FONT)
|
||||
title:SetText("Config")
|
||||
title:SetTextColor(GetColor("menu_tabs_title"))
|
||||
title:SizeToContents()
|
||||
|
||||
local total = tab_body:Add("SAM.Label")
|
||||
total:Dock(TOP)
|
||||
total:DockMargin(10, 6, 0, 0)
|
||||
total:SetFont(SAM_TAB_DESC_FONT)
|
||||
total:SetText("Some settings may require a server restart")
|
||||
total:SetTextColor(GetColor("menu_tabs_title"))
|
||||
total:SetPos(10, SUI.Scale(40))
|
||||
total:SizeToContents()
|
||||
end
|
||||
|
||||
local body = tab_body:Add("Panel")
|
||||
body:Dock(FILL)
|
||||
body:DockMargin(10, 5, 10, 10)
|
||||
|
||||
Line(body, nil, 0, 0, 0, 10)
|
||||
|
||||
local sheet = body:Add("SAM.PropertySheet")
|
||||
sheet:Dock(FILL)
|
||||
sheet:InvalidateParent(true)
|
||||
sheet:InvalidateLayout(true)
|
||||
|
||||
local sheets = {}
|
||||
for _, v in SortedPairsByMemberValue(tabs, "pos") do
|
||||
sheets[v.name] = sheet:AddSheet(v.name, v.func)
|
||||
end
|
||||
|
||||
local tab_scroller = sheet.tab_scroller:GetCanvas()
|
||||
function tab_body.Think()
|
||||
for _, v in ipairs(tabs) do
|
||||
local tab = sheets[v.name]
|
||||
if v.check and not v.check() then
|
||||
if tab:IsVisible() then
|
||||
tab:SetVisible(false)
|
||||
if sheet:GetActiveTab() == tab then
|
||||
sheet:SetActiveTab(sheet.tabs[1])
|
||||
end
|
||||
tab_scroller:InvalidateLayout()
|
||||
end
|
||||
elseif not tab:IsVisible() then
|
||||
tab:SetVisible(true)
|
||||
tab_scroller:InvalidateLayout()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return tab_body
|
||||
end, function()
|
||||
return LocalPlayer():HasPermission("manage_config")
|
||||
end, 5)
|
||||
171
addons/sam/lua/sam/menu/tabs/config/reports.lua
Normal file
171
addons/sam/lua/sam/menu/tabs/config/reports.lua
Normal file
@@ -0,0 +1,171 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
|
||||
local not_empty = function(s)
|
||||
return s and s ~= ""
|
||||
end
|
||||
|
||||
local number_entry = function(setting, config_key, default)
|
||||
local entry = setting:Add("SAM.TextEntry")
|
||||
entry:SetWide(50)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetBackground(Color(34, 34, 34))
|
||||
entry:SetNumeric(true)
|
||||
entry:DisallowFloats()
|
||||
entry:DisallowNegative()
|
||||
entry:SetCheck(not_empty)
|
||||
entry:SetConfig(config_key, default)
|
||||
|
||||
return entry
|
||||
end
|
||||
|
||||
config.add_tab("Reports", function(parent)
|
||||
local body = parent:Add("SAM.ScrollPanel")
|
||||
body:Dock(FILL)
|
||||
body:LineMargin(0, 6, 0, 0)
|
||||
|
||||
local i = 0
|
||||
body:GetCanvas():On("OnChildAdded", function(s, child)
|
||||
i = i + 1
|
||||
child:SetZPos(i)
|
||||
|
||||
if not body.making_line then
|
||||
body:Line()
|
||||
end
|
||||
end)
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Enable")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
local enable = setting:Add("SAM.ToggleButton")
|
||||
enable:SetConfig("Reports", true)
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Commands")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
local entry = setting:Add("SAM.TextEntry")
|
||||
entry:SetWide(200)
|
||||
entry:SetNoBar(true)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetMultiline(true)
|
||||
entry:SetConfig("Reports.Commands")
|
||||
entry.no_scale = true
|
||||
|
||||
function entry:OnValueChange()
|
||||
self:SetTall(self:GetNumLines() * (sam.SUI.Scale(16) --[[font size]] + 1) + 1 + 2)
|
||||
end
|
||||
entry:OnValueChange()
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Max Reports (Number of reports that can show on your screen)")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
number_entry(setting, "Reports.MaxReports", 4)
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Auto Close Time (Time to wait before automatically closing claimed reports)")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
local entry = setting:Add("SAM.TextEntry")
|
||||
entry:SetWide(70)
|
||||
entry:SetNoBar(false)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetCheck(function(time)
|
||||
time = sam.parse_length(time)
|
||||
if not time then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
entry:SetConfig("Reports.AutoCloseTime", "10m")
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Always Show (Show the popups even if you are not on duty)")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
local enable = setting:Add("SAM.ToggleButton")
|
||||
enable:SetConfig("Reports.AlwaysShow", true)
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("On Duty Jobs")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
local entry = setting:Add("SAM.TextEntry")
|
||||
entry:SetWide(300)
|
||||
entry:SetNoBar(true)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetMultiline(true)
|
||||
entry:SetConfig("Reports.DutyJobs", "")
|
||||
entry.no_scale = true
|
||||
|
||||
function entry:OnValueChange()
|
||||
self:SetTall(self:GetNumLines() * (sam.SUI.Scale(16) --[[font size]] + 1) + 1 + 2)
|
||||
end
|
||||
entry:OnValueChange()
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Position")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
local combo = setting:Add("SAM.ComboBox")
|
||||
combo:SetWide(60)
|
||||
combo:AddChoice("Left", nil, true)
|
||||
combo:AddChoice("Right")
|
||||
combo:SetConfig("Reports.Position", "Left")
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("X Padding")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
number_entry(setting, "Reports.XPadding", 5)
|
||||
end
|
||||
|
||||
do
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel("Y Padding")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
|
||||
number_entry(setting, "Reports.YPadding", 5)
|
||||
end
|
||||
|
||||
return body
|
||||
end, function()
|
||||
return LocalPlayer():HasPermission("manage_config")
|
||||
end, 2)
|
||||
42
addons/sam/lua/sam/menu/tabs/config/server.lua
Normal file
42
addons/sam/lua/sam/menu/tabs/config/server.lua
Normal file
@@ -0,0 +1,42 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
|
||||
config.add_tab("Server", function(parent)
|
||||
local server_body = parent:Add("SAM.ScrollPanel")
|
||||
server_body:Dock(FILL)
|
||||
server_body:LineMargin(0, 6, 0, 0)
|
||||
|
||||
local i = 0
|
||||
server_body:GetCanvas():On("OnChildAdded", function(s, child)
|
||||
i = i + 1
|
||||
child:SetZPos(i)
|
||||
end)
|
||||
|
||||
for k, v in ipairs(sam.config.get_menu_settings()) do
|
||||
local panel = v.func(server_body)
|
||||
if ispanel(panel) then
|
||||
local setting = server_body:Add("SAM.LabelPanel")
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
setting:SetLabel(v.title)
|
||||
setting:SetPanel(panel)
|
||||
end
|
||||
|
||||
server_body:Line()
|
||||
end
|
||||
|
||||
return server_body
|
||||
end, function()
|
||||
return LocalPlayer():HasPermission("manage_config")
|
||||
end, 1)
|
||||
462
addons/sam/lua/sam/menu/tabs/players.lua
Normal file
462
addons/sam/lua/sam/menu/tabs/players.lua
Normal file
@@ -0,0 +1,462 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local SQL = sam.SQL
|
||||
local SUI = sam.SUI
|
||||
local netstream = sam.netstream
|
||||
|
||||
sam.permissions.add("manage_players", nil, "superadmin")
|
||||
|
||||
local get_pages_count = function(count)
|
||||
count = count / 35
|
||||
local i2 = math.floor(count)
|
||||
return count ~= i2 and i2 + 1 or count
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local check = function(ply)
|
||||
return ply:HasPermission("manage_players") and ply:sam_check_cooldown("MenuViewPlayers", 0.1)
|
||||
end
|
||||
|
||||
local limit = 35
|
||||
|
||||
local get_page_count = function(callback, res, page, column, order_by, sort_by, keyword)
|
||||
local query = [[
|
||||
SELECT
|
||||
COUNT(`steamid`) AS `count`
|
||||
FROM
|
||||
`sam_players`]]
|
||||
if keyword then
|
||||
if column == "steamid" and sam.is_steamid64(keyword) then
|
||||
keyword = util.SteamIDFrom64(keyword)
|
||||
end
|
||||
|
||||
query = string.format("%s WHERE `%s` LIKE %s", query, column, SQL.Escape("%" .. keyword .. "%"))
|
||||
end
|
||||
SQL.Query(query, callback, true, {res, page, column, order_by, sort_by, keyword})
|
||||
end
|
||||
|
||||
local valid_columns = {
|
||||
steamid = true,
|
||||
name = true,
|
||||
rank = true
|
||||
}
|
||||
|
||||
local valid_sorts = {
|
||||
id = true,
|
||||
name = true,
|
||||
rank = true,
|
||||
play_time = true,
|
||||
last_join = true
|
||||
}
|
||||
|
||||
local resolve_promise = function(data, arguments)
|
||||
local res = arguments[1]
|
||||
arguments[1] = data
|
||||
res(arguments)
|
||||
end
|
||||
|
||||
local get_players = function(count_data, arguments)
|
||||
local res, page, column, order_by, sort_by, keyword = unpack(arguments)
|
||||
local count = count_data.count
|
||||
|
||||
local current_page
|
||||
if page < 1 then
|
||||
page, current_page = 1, 1
|
||||
end
|
||||
|
||||
local pages_count = get_pages_count(count)
|
||||
if page > pages_count then
|
||||
page, current_page = pages_count, pages_count
|
||||
end
|
||||
|
||||
local query = [[
|
||||
SELECT
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
FROM
|
||||
`sam_players`
|
||||
]]
|
||||
|
||||
local args = {}
|
||||
|
||||
if keyword then
|
||||
args[1] = column
|
||||
args[2] = "%" .. keyword .. "%"
|
||||
|
||||
query = query .. [[
|
||||
WHERE
|
||||
`{1f}` LIKE {2}
|
||||
]]
|
||||
end
|
||||
|
||||
args[3] = sort_by
|
||||
if order_by == "DESC" then
|
||||
query = query .. [[
|
||||
ORDER BY `{3f}` DESC
|
||||
]]
|
||||
else
|
||||
query = query .. [[
|
||||
ORDER BY `{3f}` ASC
|
||||
]]
|
||||
end
|
||||
|
||||
args[4] = limit
|
||||
args[5] = math.abs(limit * (page - 1))
|
||||
|
||||
query = query .. [[
|
||||
LIMIT {4} OFFSET {5}
|
||||
]]
|
||||
|
||||
SQL.FQuery(query, args, resolve_promise, false, {res, count, current_page})
|
||||
end
|
||||
|
||||
netstream.async.Hook("SAM.GetPlayers", function(res, ply, page, column, order_by, sort_by, keyword)
|
||||
if not isnumber(page) then return end
|
||||
if not valid_columns[column] then return end
|
||||
if order_by ~= "ASC" and order_by ~= "DESC" then return end
|
||||
if not valid_sorts[sort_by] then return end
|
||||
if keyword ~= nil and not sam.isstring(keyword) then return end
|
||||
|
||||
get_page_count(get_players, res, page, column, order_by, sort_by, keyword)
|
||||
end, check)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
local Line = sui.TDLib.LibClasses.Line
|
||||
|
||||
local COLUMN_FONT = SUI.CreateFont("Column", "Roboto", 18)
|
||||
local LINE_FONT = SUI.CreateFont("Line", "Roboto", 16)
|
||||
local NEXT_FONT = SUI.CreateFont("NextButton", "Roboto", 18)
|
||||
|
||||
local button_click = function(s)
|
||||
local v = s.v
|
||||
|
||||
local dmenu = vgui.Create("SAM.Menu")
|
||||
dmenu:SetInternal(s)
|
||||
if v.name and v.name ~= "" then
|
||||
dmenu:AddOption("Copy Name", function()
|
||||
SetClipboardText(v.name)
|
||||
end)
|
||||
end
|
||||
|
||||
dmenu:AddOption("Copy SteamID", function()
|
||||
SetClipboardText(v.steamid)
|
||||
end)
|
||||
|
||||
dmenu:AddOption("Copy Rank", function()
|
||||
SetClipboardText(v.rank)
|
||||
end)
|
||||
|
||||
dmenu:AddOption("Copy Play Time", function()
|
||||
SetClipboardText(sam.reverse_parse_length(tonumber(v.play_time) / 60))
|
||||
end)
|
||||
|
||||
dmenu:AddSpacer()
|
||||
|
||||
dmenu:AddOption("Change Rank", function()
|
||||
local querybox = vgui.Create("SAM.QueryBox")
|
||||
querybox:SetTitle(string.format("Change rank for '%s'", v.name or v.steamid))
|
||||
querybox:SetWide(360)
|
||||
|
||||
local ranks = querybox:Add("SAM.ComboBox")
|
||||
ranks:SetTall(28)
|
||||
|
||||
for rank_name in SortedPairsByMemberValue(sam.ranks.get_ranks(), "immunity", true) do
|
||||
if v.rank ~= rank_name then
|
||||
ranks:AddChoice(rank_name, nil, true)
|
||||
end
|
||||
end
|
||||
|
||||
querybox:Done()
|
||||
querybox.save:SetEnabled(true)
|
||||
|
||||
querybox:SetCallback(function()
|
||||
RunConsoleCommand("sam", "setrankid", v.steamid, ranks:GetValue())
|
||||
end)
|
||||
end)
|
||||
|
||||
dmenu:Open()
|
||||
end
|
||||
|
||||
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/user.png", function(column_sheet)
|
||||
local refresh, pages
|
||||
local current_page, current_column, current_order, current_sort, keyword = nil, "steamid", "DESC", "id", nil
|
||||
|
||||
local players_body = column_sheet:Add("Panel")
|
||||
players_body:Dock(FILL)
|
||||
players_body:DockMargin(0, 1, 0, 0)
|
||||
players_body:DockPadding(10, 10, 10, 10)
|
||||
|
||||
local toggle_loading, is_loading = sam.menu.add_loading_panel(players_body)
|
||||
|
||||
local title = players_body:Add("SAM.Label")
|
||||
title:Dock(TOP)
|
||||
title:SetFont(SAM_TAB_TITLE_FONT)
|
||||
title:SetText("Players")
|
||||
title:SetTextColor(GetColor("menu_tabs_title"))
|
||||
title:SizeToContents()
|
||||
|
||||
local total = players_body:Add("SAM.Label")
|
||||
total:Dock(TOP)
|
||||
total:DockMargin(0, 6, 0, 0)
|
||||
total:SetFont(SAM_TAB_DESC_FONT)
|
||||
total:SetText("60 total players")
|
||||
total:SetTextColor(GetColor("menu_tabs_title"))
|
||||
total:SetPos(10, SUI.Scale(40))
|
||||
total:SizeToContents()
|
||||
|
||||
local search_entry
|
||||
do
|
||||
local container = players_body:Add("SAM.Panel")
|
||||
container:Dock(TOP)
|
||||
container:DockMargin(0, 6, 10, 0)
|
||||
container:SetTall(30)
|
||||
|
||||
local sort_by = container:Add("SAM.ComboBox")
|
||||
sort_by:Dock(RIGHT)
|
||||
sort_by:DockMargin(4, 0, 0, 0)
|
||||
sort_by:SetWide(106)
|
||||
sort_by:SetValue("Sort By (ID)")
|
||||
sort_by:AddChoice("ID")
|
||||
sort_by:AddChoice("Name")
|
||||
sort_by:AddChoice("Rank")
|
||||
sort_by:AddChoice("Play Time")
|
||||
|
||||
function sort_by:OnSelect(_, value)
|
||||
value = value:lower():gsub(" ", "_")
|
||||
if current_sort ~= value then
|
||||
current_sort = value
|
||||
refresh()
|
||||
end
|
||||
end
|
||||
|
||||
local sort_order = container:Add("SAM.ComboBox")
|
||||
sort_order:Dock(RIGHT)
|
||||
sort_order:SetWide(96)
|
||||
sort_order:SetValue("Desc")
|
||||
sort_order:AddChoice("Desc")
|
||||
sort_order:AddChoice("Asc")
|
||||
|
||||
function sort_order:OnSelect(_, value)
|
||||
value = value:upper()
|
||||
if current_order ~= value then
|
||||
current_order = value
|
||||
refresh()
|
||||
end
|
||||
end
|
||||
|
||||
local column = container:Add("SAM.ComboBox")
|
||||
column:Dock(RIGHT)
|
||||
column:DockMargin(0, 0, 4, 0)
|
||||
column:SetWide(140)
|
||||
|
||||
column:SetValue("Search (SteamID)")
|
||||
column:AddChoice("SteamID")
|
||||
column:AddChoice("Name")
|
||||
column:AddChoice("Rank")
|
||||
|
||||
function column:OnSelect(_, value)
|
||||
value = value:lower()
|
||||
if current_column ~= value then
|
||||
current_column = value
|
||||
refresh()
|
||||
end
|
||||
end
|
||||
|
||||
search_entry = container:Add("SAM.TextEntry")
|
||||
search_entry:Dock(LEFT)
|
||||
search_entry:SetNoBar(true)
|
||||
search_entry:SetPlaceholder("Search...")
|
||||
search_entry:SetRadius(4)
|
||||
search_entry:SetWide(220)
|
||||
|
||||
function search_entry:OnEnter(no_refresh)
|
||||
local value = self:GetValue()
|
||||
if keyword ~= value then
|
||||
keyword = value ~= "" and value or nil
|
||||
if not no_refresh then
|
||||
refresh()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Line(players_body, nil, -5, SUI.Scale(15), -5, 0)
|
||||
|
||||
do
|
||||
local columns = players_body:Add("Panel")
|
||||
columns:Dock(TOP)
|
||||
columns:DockMargin(0, 10, 0, 0)
|
||||
|
||||
local info = columns:Add("SAM.Label")
|
||||
info:Dock(LEFT)
|
||||
info:DockMargin(4, 0, 0, 0)
|
||||
info:SetFont(COLUMN_FONT)
|
||||
info:SetText("Player")
|
||||
info:SetTextColor(GetColor("player_list_titles"))
|
||||
info:SetWide(SUI.Scale(280) + SUI.Scale(34))
|
||||
info:SizeToContentsY(3)
|
||||
|
||||
local play_time = columns:Add("SAM.Label")
|
||||
play_time:Dock(LEFT)
|
||||
play_time:DockMargin(-4, 0, 0, 0)
|
||||
play_time:SetFont(COLUMN_FONT)
|
||||
play_time:SetText("Play Time")
|
||||
play_time:SetTextColor(GetColor("player_list_titles"))
|
||||
play_time:SetWide(SUI.Scale(180))
|
||||
play_time:SizeToContentsY(3)
|
||||
|
||||
local rank_expiry = columns:Add("SAM.Label")
|
||||
rank_expiry:Dock(LEFT)
|
||||
rank_expiry:DockMargin(-4, 0, 0, 0)
|
||||
rank_expiry:SetFont(COLUMN_FONT)
|
||||
rank_expiry:SetText("Rank Expiry")
|
||||
rank_expiry:SetTextColor(GetColor("player_list_titles"))
|
||||
rank_expiry:SetWide(SUI.Scale(280))
|
||||
rank_expiry:SizeToContentsY(3)
|
||||
|
||||
columns:SizeToChildren(false, true)
|
||||
end
|
||||
|
||||
local body = players_body:Add("SAM.ScrollPanel")
|
||||
body:Dock(FILL)
|
||||
body:DockMargin(0, 10, 0, 0)
|
||||
body:SetVBarPadding(6)
|
||||
|
||||
local set_data = function(data)
|
||||
body:GetCanvas():Clear()
|
||||
body.VBar.Scroll = 0
|
||||
|
||||
local players, players_count, current_page_2 = unpack(data)
|
||||
total:SetText(players_count .. " total players")
|
||||
|
||||
pages = get_pages_count(players_count)
|
||||
current_page.i = pages == 0 and 0 or current_page_2 or current_page.i
|
||||
current_page:SetText(current_page.i .. "/" .. pages)
|
||||
|
||||
body:Line()
|
||||
|
||||
for k, v in ipairs(players) do
|
||||
local line = body:Add("SAM.PlayerLine")
|
||||
line:DockMargin(0, 0, 0, 10)
|
||||
|
||||
local name = v.name ~= "" and v.name or nil
|
||||
line:SetInfo({
|
||||
steamid = v.steamid,
|
||||
name = name,
|
||||
rank = v.rank
|
||||
})
|
||||
|
||||
local play_time = line:Add("SAM.Label")
|
||||
play_time:Dock(LEFT)
|
||||
play_time:DockMargin(4, 0, 0, 0)
|
||||
play_time:SetFont(LINE_FONT)
|
||||
play_time:SetText(sam.reverse_parse_length(tonumber(v.play_time) / 60))
|
||||
play_time:SetTextColor(GetColor("player_list_data"))
|
||||
play_time:SetContentAlignment(4)
|
||||
play_time:SetWide(SUI.Scale(180))
|
||||
|
||||
local expiry_date = tonumber(v.expiry_date)
|
||||
local rank_expiry = line:Add("SAM.Label")
|
||||
rank_expiry:Dock(LEFT)
|
||||
rank_expiry:DockMargin(-3, 0, 0, 0)
|
||||
rank_expiry:SetFont(LINE_FONT)
|
||||
rank_expiry:SetText(expiry_date == 0 and "Never" or sam.reverse_parse_length((expiry_date - os.time()) / 60))
|
||||
rank_expiry:SetTextColor(GetColor("player_list_data"))
|
||||
rank_expiry:SetContentAlignment(4)
|
||||
rank_expiry:SizeToContents()
|
||||
|
||||
local but = line:Actions()
|
||||
but.v = v
|
||||
but:On("DoClick", button_click)
|
||||
|
||||
body:Line()
|
||||
end
|
||||
end
|
||||
|
||||
refresh = function()
|
||||
if not is_loading() and LocalPlayer():HasPermission("manage_players") then
|
||||
search_entry:OnEnter(true)
|
||||
local refresh_query = netstream.async.Start("SAM.GetPlayers", toggle_loading, current_page.i, current_column, current_order, current_sort, keyword)
|
||||
refresh_query:done(set_data)
|
||||
end
|
||||
end
|
||||
|
||||
local bottom_panel = players_body:Add("SAM.Panel")
|
||||
bottom_panel:Dock(BOTTOM)
|
||||
bottom_panel:DockMargin(0, 6, 0, 0)
|
||||
bottom_panel:SetTall(30)
|
||||
bottom_panel:Background(GetColor("page_switch_bg"))
|
||||
|
||||
local previous_page = bottom_panel:Add("SAM.Button")
|
||||
previous_page:Dock(LEFT)
|
||||
previous_page:SetWide(30)
|
||||
previous_page:SetText("<")
|
||||
previous_page:SetFont(NEXT_FONT)
|
||||
|
||||
previous_page:On("DoClick", function()
|
||||
if current_page.i <= 1 then return end
|
||||
|
||||
current_page.i = current_page.i - 1
|
||||
refresh()
|
||||
end)
|
||||
|
||||
current_page = bottom_panel:Add("SAM.Label")
|
||||
current_page:Dock(FILL)
|
||||
current_page:SetContentAlignment(5)
|
||||
current_page:SetFont(SAM_TAB_DESC_FONT)
|
||||
current_page:SetText("loading...")
|
||||
current_page.i = 1
|
||||
|
||||
local next_page = bottom_panel:Add("SAM.Button")
|
||||
next_page:Dock(RIGHT)
|
||||
next_page:SetWide(30)
|
||||
next_page:SetText(">")
|
||||
next_page:SetFont(NEXT_FONT)
|
||||
|
||||
next_page:On("DoClick", function()
|
||||
if current_page.i == pages then return end
|
||||
|
||||
current_page.i = current_page.i + 1
|
||||
refresh()
|
||||
end)
|
||||
|
||||
function bottom_panel:Think()
|
||||
next_page:SetEnabled(current_page.i ~= pages)
|
||||
previous_page:SetEnabled(current_page.i > 1)
|
||||
end
|
||||
|
||||
do
|
||||
local refresh_2 = function()
|
||||
timer.Simple(1, refresh)
|
||||
end
|
||||
|
||||
for k, v in ipairs({"SAM.AuthedPlayer", "SAM.ChangedPlayerRank", "SAM.ChangedSteamIDRank"}) do
|
||||
hook.Add(v, "SAM.MenuPlayers", refresh_2)
|
||||
end
|
||||
end
|
||||
|
||||
refresh()
|
||||
|
||||
return players_body
|
||||
end, function()
|
||||
return LocalPlayer():HasPermission("manage_players")
|
||||
end, 2)
|
||||
637
addons/sam/lua/sam/menu/tabs/ranks.lua
Normal file
637
addons/sam/lua/sam/menu/tabs/ranks.lua
Normal file
@@ -0,0 +1,637 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
if SERVER then return end
|
||||
|
||||
local sam = sam
|
||||
local SUI = sam.SUI
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
local Line = sui.TDLib.LibClasses.Line
|
||||
local AnimatedSetVisible = sui.TDLib.LibClasses.AnimatedSetVisible
|
||||
|
||||
local RANK_NAME = SUI.CreateFont("RankName", "Roboto Bold", 18)
|
||||
local RANK_INFO = SUI.CreateFont("RankInfo", "Roboto Medium", 12)
|
||||
|
||||
local CREATE_RANK = SUI.CreateFont("CreateRank", "Roboto Bold", 16, 200)
|
||||
local RANK_TITLE = SUI.CreateFont("RankTitle", "Roboto Bold", 20)
|
||||
|
||||
local rank_menu = function(rank, data)
|
||||
local valid = sui.valid_options()
|
||||
|
||||
local imm, banlim
|
||||
if rank then
|
||||
imm, banlim = data.immunity, data.ban_limit
|
||||
end
|
||||
|
||||
local edit_rank = vgui.Create("SAM.QueryBox")
|
||||
edit_rank:SetWide(470)
|
||||
edit_rank:SetTitle(rank and string.format("Edit Rank '%s'", rank) or "Create Rank")
|
||||
|
||||
local new_name = rank
|
||||
if not sam.ranks.is_default_rank(rank) then
|
||||
local name = edit_rank:Add("SAM.LabelPanel")
|
||||
name:SetLabel("Rank Name")
|
||||
|
||||
local entry = name:Add("SAM.TextEntry")
|
||||
entry:SetSize(210, 28)
|
||||
entry:SetNoBar(false)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetValue(rank or "")
|
||||
entry:SetCheck(function(_name)
|
||||
new_name = _name
|
||||
|
||||
if _name == rank then return end
|
||||
if _name == "" or sam.ranks.is_rank(_name) then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
valid.Add(entry)
|
||||
end
|
||||
|
||||
local new_immunity = imm
|
||||
do
|
||||
local immunity = edit_rank:Add("SAM.LabelPanel")
|
||||
immunity:SetLabel("Immunity (2~99)")
|
||||
immunity:DockMargin(0, 5, 0, 0)
|
||||
|
||||
local entry = immunity:Add("SAM.TextEntry")
|
||||
entry:SetSize(210, 28)
|
||||
entry:SetNumeric(true)
|
||||
entry:DisallowFloats(true)
|
||||
entry:DisallowNegative(true)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetValue(imm or "2")
|
||||
entry:SetCheck(function(_immunity)
|
||||
new_immunity = _immunity
|
||||
|
||||
if _immunity == "" then
|
||||
return false
|
||||
end
|
||||
|
||||
_immunity = tonumber(_immunity)
|
||||
new_immunity = _immunity
|
||||
if _immunity < 2 or _immunity > 99 then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
valid.Add(entry)
|
||||
end
|
||||
|
||||
local new_banlimit = banlim
|
||||
do
|
||||
local banlimit = edit_rank:Add("SAM.LabelPanel")
|
||||
banlimit:SetLabel("Ban Limit (1y 1mo 1w 1d 1h 1m)")
|
||||
banlimit:DockMargin(0, 5, 0, 0)
|
||||
|
||||
local entry = banlimit:Add("SAM.TextEntry")
|
||||
entry:SetSize(210, 28)
|
||||
entry:SetNoBar(false)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetValue(banlim and sam.reverse_parse_length(banlim) or "2w")
|
||||
entry:SetCheck(function(_banlimit)
|
||||
new_banlimit = sam.parse_length(_banlimit)
|
||||
if not new_banlimit and _banlimit ~= banlim then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
valid.Add(entry)
|
||||
end
|
||||
|
||||
local inherit = rank and sam.ranks.get_rank(rank).inherit or "user"
|
||||
local new_inherit = inherit
|
||||
do
|
||||
local inherits_from = edit_rank:Add("SAM.LabelPanel")
|
||||
inherits_from:SetLabel("Inherits From")
|
||||
inherits_from:DockMargin(0, 5, 0, 0)
|
||||
|
||||
local entry = inherits_from:Add("SAM.ComboBox")
|
||||
entry:SetSize(210, 28)
|
||||
entry:SetValue(inherit)
|
||||
|
||||
for name in SortedPairsByMemberValue(sam.ranks.get_ranks(), "immunity", true) do
|
||||
if name ~= rank and not sam.ranks.inherits_from(name, rank) then
|
||||
entry:AddChoice(name)
|
||||
end
|
||||
end
|
||||
|
||||
function entry:OnSelect(_, value)
|
||||
new_inherit = value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
edit_rank:Done()
|
||||
edit_rank.save:SetEnabled(true)
|
||||
edit_rank.save:SetText("SAVE")
|
||||
|
||||
if rank then
|
||||
edit_rank:SetCallback(function()
|
||||
local to_run = {}
|
||||
|
||||
if new_immunity ~= imm then
|
||||
table.insert(to_run, {"changerankimmunity", rank, new_immunity})
|
||||
end
|
||||
|
||||
if new_banlimit ~= banlim then
|
||||
table.insert(to_run, {"changerankbanlimit", rank, new_banlimit})
|
||||
end
|
||||
|
||||
if new_inherit ~= inherit then
|
||||
table.insert(to_run, {"changeinherit", rank, new_inherit})
|
||||
end
|
||||
|
||||
if new_name ~= rank then
|
||||
table.insert(to_run, {"renamerank", rank, new_name})
|
||||
end
|
||||
sam.command.run_commands(to_run)
|
||||
end)
|
||||
else
|
||||
edit_rank:SetCallback(function()
|
||||
RunConsoleCommand("sam", "addrank", new_name, new_inherit, new_immunity, new_banlimit)
|
||||
end)
|
||||
end
|
||||
|
||||
function edit_rank.save:Think()
|
||||
self:SetEnabled(valid.IsValid())
|
||||
end
|
||||
end
|
||||
|
||||
sam.menu.add_tab("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/military_rank.png", function(column_sheet)
|
||||
local current_rank
|
||||
|
||||
local parent = column_sheet:Add("Panel")
|
||||
parent:Dock(FILL)
|
||||
parent:DockMargin(0, 1, 0, 0)
|
||||
|
||||
local title = parent:Add("SAM.Label")
|
||||
title:Dock(TOP)
|
||||
title:DockMargin(10, 10, 0, 0)
|
||||
title:SetFont(SAM_TAB_TITLE_FONT)
|
||||
title:SetText("Ranks")
|
||||
title:SetTextColor(GetColor("menu_tabs_title"))
|
||||
title:SizeToContents()
|
||||
|
||||
local total = parent:Add("SAM.Label")
|
||||
total:Dock(TOP)
|
||||
total:DockMargin(10, 6, 0, 0)
|
||||
total:SetFont(SAM_TAB_DESC_FONT)
|
||||
total:SetText(table.Count(sam.ranks.get_ranks()) .. " total ranks")
|
||||
total:SetTextColor(GetColor("menu_tabs_title"))
|
||||
total:SizeToContents()
|
||||
|
||||
local search_entry
|
||||
do
|
||||
local container = parent:Add("SAM.Panel")
|
||||
container:Dock(TOP)
|
||||
container:DockMargin(10, 6, 10, SUI.Scale(15))
|
||||
container:SetTall(30)
|
||||
|
||||
search_entry = container:Add("SAM.TextEntry")
|
||||
search_entry:Dock(LEFT)
|
||||
search_entry:SetNoBar(true)
|
||||
search_entry:SetPlaceholder("Search...")
|
||||
search_entry:SetRadius(4)
|
||||
search_entry:SetWide(220)
|
||||
end
|
||||
|
||||
local create_rank = parent:Add("SAM.Button")
|
||||
create_rank:SetFont(CREATE_RANK)
|
||||
create_rank:SetText("Create Rank")
|
||||
create_rank:Dock(BOTTOM)
|
||||
create_rank:DockMargin(10, 0, 10, 10)
|
||||
|
||||
create_rank:On("DoClick", function()
|
||||
rank_menu()
|
||||
end)
|
||||
|
||||
local right_body = parent:Add("Panel")
|
||||
right_body:Dock(RIGHT)
|
||||
right_body:DockMargin(0, 5, 10, 10)
|
||||
right_body:SetWide(0)
|
||||
right_body:SetZPos(-1)
|
||||
|
||||
local rank_title = right_body:Add("SAM.Label")
|
||||
rank_title:Dock(TOP)
|
||||
rank_title:DockMargin(0, 0, 0, 5)
|
||||
rank_title:SetFont(RANK_TITLE)
|
||||
rank_title:SetTextColor(GetColor("menu_tabs_title"))
|
||||
|
||||
local permissions_body = right_body:Add("SAM.CollapseCategory")
|
||||
permissions_body:Dock(FILL)
|
||||
permissions_body:GetCanvas():DockPadding(0, 0, 5, 0)
|
||||
|
||||
local function refresh_access()
|
||||
if not IsValid(current_rank) then return end
|
||||
|
||||
for k, v in ipairs(permissions_body.items) do
|
||||
AnimatedSetVisible(v.img, sam.ranks.has_permission(current_rank.name, v.name))
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in ipairs({"SAM.ChangedInheritRank", "SAM.RankPermissionGiven", "SAM.RankPermissionTaken"}) do
|
||||
hook.Add(v, "SAM.Menu.RefreshPermissions ", refresh_access)
|
||||
end
|
||||
|
||||
local function sortFunc(a, b)
|
||||
if (a.category == b.category) then return a.name < b.name end
|
||||
|
||||
return a.category < b.category
|
||||
end
|
||||
|
||||
local function refresh_permissions()
|
||||
permissions_body:GetCanvas():Clear()
|
||||
table.Empty(permissions_body.items)
|
||||
table.Empty(permissions_body.categories)
|
||||
|
||||
local item_click = function(s)
|
||||
local rank = current_rank.name
|
||||
if not sam.ranks.has_permission(rank, s.name) then
|
||||
RunConsoleCommand("sam", "givepermission", rank, s.name)
|
||||
else
|
||||
RunConsoleCommand("sam", "takepermission", rank, s.name)
|
||||
end
|
||||
end
|
||||
|
||||
local permissions = sam.permissions.get()
|
||||
table.sort(permissions, sortFunc)
|
||||
for k, v in ipairs(permissions) do
|
||||
local item = permissions_body:add_item(v.name, v.category)
|
||||
item:SetContentAlignment(4)
|
||||
item:SetTextInset(6, 0)
|
||||
item:SizeToContentsY(SUI.Scale(10))
|
||||
item:SetZPos(k)
|
||||
item.name = v.name
|
||||
item.DoClick = item_click
|
||||
|
||||
local img = item:Add("SAM.Image")
|
||||
img:Dock(RIGHT)
|
||||
img:DockMargin(4, 4, 4, 4)
|
||||
img:InvalidateParent(true)
|
||||
img:SetWide(img:GetTall())
|
||||
img:SetImageColor(Color(52, 161, 224))
|
||||
img:SetImage("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/check_mark.png")
|
||||
|
||||
item.img = img
|
||||
end
|
||||
end
|
||||
|
||||
local limits_body
|
||||
|
||||
do
|
||||
local permissions_search = right_body:Add("SAM.TextEntry")
|
||||
permissions_search:Dock(TOP)
|
||||
permissions_search:DockMargin(0, 0, 5, 10)
|
||||
permissions_search:SetNoBar(true)
|
||||
permissions_search:SetPlaceholder("Search...")
|
||||
permissions_search:SetRadius(4)
|
||||
permissions_search:SetTall(30)
|
||||
|
||||
function permissions_search:OnValueChange(text)
|
||||
if limits_body and limits_body:IsVisible() then
|
||||
local children = limits_body:GetCanvas():GetChildren()
|
||||
for k, v in ipairs(children) do
|
||||
v:AnimatedSetVisible(v.title:find(text, nil, true) ~= nil)
|
||||
end
|
||||
limits_body:InvalidateLayout(true)
|
||||
else
|
||||
permissions_body:Search(text)
|
||||
end
|
||||
end
|
||||
|
||||
Line(right_body):SetZPos(2)
|
||||
end
|
||||
|
||||
local function load_limits()
|
||||
if sam.limit_types then
|
||||
if limits_body then return end
|
||||
else
|
||||
if limits_body then
|
||||
limits_body:SetVisible(false)
|
||||
permissions_body:AnimatedSetVisible(true)
|
||||
limits_body:Remove()
|
||||
limits_body = nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
limits_body = right_body:Add("SAM.ScrollPanel")
|
||||
limits_body:Dock(FILL)
|
||||
limits_body:GetCanvas():DockPadding(0, 0, 5, 0)
|
||||
limits_body:SetVisible(false)
|
||||
|
||||
local item_enter = function(s)
|
||||
if not IsValid(current_rank) then return end
|
||||
|
||||
local rank = current_rank.name
|
||||
|
||||
local limit = math.Clamp(s:GetValue(), -1, 1000)
|
||||
if limit ~= sam.ranks.get_limit(rank, s.limit_type) then
|
||||
RunConsoleCommand("sam", "changeranklimit", rank, s.limit_type, limit)
|
||||
else
|
||||
s:SetText(tostring(sam.ranks.get_limit(rank, s.limit_type)))
|
||||
end
|
||||
end
|
||||
|
||||
local not_empty = function(s)
|
||||
return s and s ~= ""
|
||||
end
|
||||
|
||||
local limit_values = {}
|
||||
for k, v in ipairs(sam.limit_types) do
|
||||
local immunity = limits_body:Add("SAM.LabelPanel")
|
||||
immunity:SetLabel(v)
|
||||
immunity:DockMargin(5, 0, 0, 5)
|
||||
|
||||
local entry = immunity:Add("SAM.TextEntry")
|
||||
entry:SetSize(60, 26)
|
||||
entry:SetNumeric(true)
|
||||
entry:DisallowFloats(true)
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetCheck(not_empty)
|
||||
entry.limit_type = v
|
||||
entry.OnEnter = item_enter
|
||||
|
||||
table.insert(limit_values, entry)
|
||||
end
|
||||
|
||||
function limits_body:Refresh()
|
||||
if not IsValid(current_rank) then return end
|
||||
|
||||
local rank = current_rank.name
|
||||
for k, v in ipairs(limit_values) do
|
||||
v:SetValue(tostring(sam.ranks.get_limit(rank, v.limit_type)))
|
||||
end
|
||||
end
|
||||
|
||||
local right_current_rank = right_body:Add("SAM.Button")
|
||||
right_current_rank:Dock(BOTTOM)
|
||||
right_current_rank:DockMargin(0, 5, 0, 0)
|
||||
right_current_rank:SetFont(CREATE_RANK)
|
||||
right_current_rank:SetText("Switch to Limits")
|
||||
right_current_rank:On("DoClick", function()
|
||||
limits_body:AnimatedToggleVisible()
|
||||
permissions_body:AnimatedToggleVisible()
|
||||
|
||||
if permissions_body:AnimatedIsVisible() then
|
||||
right_current_rank:SetText("Switch to Limits")
|
||||
else
|
||||
right_current_rank:SetText("Switch to Permissions")
|
||||
end
|
||||
end)
|
||||
|
||||
limits_body:On("OnRemove", function()
|
||||
right_current_rank:Remove()
|
||||
end)
|
||||
limits_body:Refresh()
|
||||
end
|
||||
|
||||
local function refresh_all()
|
||||
timer.Create("SAM.Menu.Ranks.Refresh", 1, 1, function()
|
||||
load_limits()
|
||||
refresh_permissions()
|
||||
refresh_access()
|
||||
end)
|
||||
end
|
||||
|
||||
sam.config.hook({"Restrictions.Limits"}, refresh_all)
|
||||
|
||||
for k, v in ipairs({"SAM.AddedPermission", "SAM.PermissionModified", "SAM.RemovedPermission"}) do
|
||||
hook.Add(v, "SAM.Menu.RefreshPermissions", refresh_all)
|
||||
end
|
||||
|
||||
local body = parent:Add("SAM.ScrollPanel")
|
||||
body:Dock(FILL)
|
||||
body:DockMargin(10, 0, 5, 10)
|
||||
body:SetVBarPadding(6)
|
||||
|
||||
body:Line():SetZPos(-101)
|
||||
|
||||
local select_rank = function(s)
|
||||
if not IsValid(s) then
|
||||
current_rank = nil
|
||||
right_body:SizeTo(0, -1, 0.3)
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid(current_rank) then
|
||||
current_rank.Selected = false
|
||||
|
||||
if current_rank == s then
|
||||
current_rank = nil
|
||||
right_body:SizeTo(0, -1, 0.3)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
s.Selected = true
|
||||
current_rank = s
|
||||
refresh_access()
|
||||
if limits_body then
|
||||
limits_body:Refresh()
|
||||
end
|
||||
right_body:SizeTo(SUI.Scale(300), -1, 0.3)
|
||||
|
||||
rank_title:SetText(s.name)
|
||||
rank_title:SizeToContents()
|
||||
end
|
||||
|
||||
local ranks = {}
|
||||
|
||||
function search_entry:OnValueChange()
|
||||
local value = self:GetValue()
|
||||
for k, v in pairs(ranks) do
|
||||
local show = k:find(value, nil, true)
|
||||
show = show ~= nil
|
||||
v.line:AnimatedSetVisible(show)
|
||||
v:GetParent():AnimatedSetVisible(show)
|
||||
end
|
||||
end
|
||||
|
||||
local add_rank = function(rank_name, data)
|
||||
if rank_name == "superadmin" then return end
|
||||
if not IsValid(body) then return end
|
||||
|
||||
local line = body:Add("SAM.Panel")
|
||||
line:Dock(TOP)
|
||||
line:DockMargin(0, 0, 0, 10)
|
||||
line:SetTall(34)
|
||||
line:SetZPos(-data.immunity)
|
||||
line:InvalidateLayout(true)
|
||||
|
||||
local container = line:Add("SAM.Button")
|
||||
container:Dock(FILL)
|
||||
container:DockMargin(0, 0, 5, 0)
|
||||
container:DockPadding(5, 5, 0, 5)
|
||||
container:SetText("")
|
||||
container:SetContained(false)
|
||||
container.name = rank_name
|
||||
|
||||
ranks[rank_name] = container
|
||||
|
||||
container:On("DoClick", select_rank)
|
||||
|
||||
function container:DoRightClick()
|
||||
rank_name = container.name
|
||||
|
||||
if rank_name == "user" then return end
|
||||
|
||||
local dmenu = vgui.Create("SAM.Menu")
|
||||
dmenu:SetSize(w, h)
|
||||
dmenu:SetInternal(container)
|
||||
|
||||
dmenu:AddOption("Edit Rank", function()
|
||||
rank_menu(rank_name, sam.ranks.get_rank(rank_name))
|
||||
end)
|
||||
|
||||
if not sam.ranks.is_default_rank(rank_name) then
|
||||
dmenu:AddSpacer()
|
||||
|
||||
dmenu:AddOption("Remove Rank", function()
|
||||
local remove_rank = vgui.Create("SAM.QueryBox")
|
||||
remove_rank:SetWide(350)
|
||||
|
||||
local check = remove_rank:Add("SAM.Label")
|
||||
check:SetText("Are you sure that you want to remove '" .. rank_name .. "'?")
|
||||
check:SetFont("SAMLine")
|
||||
check:SetWrap(true)
|
||||
check:SetAutoStretchVertical(true)
|
||||
|
||||
remove_rank:Done()
|
||||
remove_rank.save:SetEnabled(true)
|
||||
remove_rank.save:SetText("REMOVE")
|
||||
remove_rank.save:SetContained(false)
|
||||
remove_rank.save:SetColors(GetColor("query_box_cancel"), GetColor("query_box_cancel_text"))
|
||||
|
||||
remove_rank.cancel:SetContained(true)
|
||||
remove_rank.cancel:SetColors()
|
||||
|
||||
remove_rank:SetCallback(function()
|
||||
RunConsoleCommand("sam", "removerank", rank_name)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
dmenu:Open()
|
||||
dmenu:SetPos(input.GetCursorPos())
|
||||
end
|
||||
|
||||
do
|
||||
local name = container:Add("SAM.Label")
|
||||
name:Dock(TOP)
|
||||
name:DockMargin(0, 0, 0, 2)
|
||||
name:SetTextColor(GetColor("player_list_names"))
|
||||
name:SetFont(RANK_NAME)
|
||||
name:SetText(rank_name)
|
||||
name:SizeToContents()
|
||||
|
||||
local immunity = container:Add("SAM.Label")
|
||||
immunity:Dock(TOP)
|
||||
immunity:SetTextColor(GetColor("player_list_steamid"))
|
||||
immunity:SetFont(RANK_INFO)
|
||||
immunity:SetText("Immunity: " .. data.immunity)
|
||||
immunity:SizeToContents()
|
||||
|
||||
local banlimit = container:Add("SAM.Label")
|
||||
banlimit:Dock(TOP)
|
||||
banlimit:SetTextColor(GetColor("player_list_steamid"))
|
||||
banlimit:SetFont(RANK_INFO)
|
||||
banlimit:SetText("Ban limit: " .. sam.reverse_parse_length(sam.parse_length(data.ban_limit)))
|
||||
banlimit:SizeToContents()
|
||||
|
||||
local inherit = container:Add("SAM.Label")
|
||||
inherit:Dock(TOP)
|
||||
inherit:SetTextColor(GetColor("player_list_steamid"))
|
||||
inherit:SetFont(RANK_INFO)
|
||||
inherit:SetText("Inherits from: " .. (sam.isstring(data.inherit) and data.inherit or "none"))
|
||||
inherit:SizeToContents()
|
||||
end
|
||||
|
||||
container:InvalidateLayout(true)
|
||||
container:SizeToChildren(false, true)
|
||||
line:SizeToChildren(false, true)
|
||||
|
||||
local _line = body:Line()
|
||||
_line:SetZPos(-data.immunity)
|
||||
|
||||
container.line = _line
|
||||
container.data = data
|
||||
end
|
||||
|
||||
for rank_name, v in pairs(sam.ranks.get_ranks()) do
|
||||
add_rank(rank_name, v)
|
||||
end
|
||||
|
||||
hook.Add("SAM.AddedRank", "SAM.RefreshRanksList", function(name, rank)
|
||||
add_rank(name, rank)
|
||||
end)
|
||||
|
||||
hook.Add("SAM.RemovedRank", "SAM.RefreshRanksList", function(name)
|
||||
local line = ranks[name]
|
||||
if not IsValid(line) then return end
|
||||
|
||||
line.line:Remove()
|
||||
line:GetParent():Remove()
|
||||
ranks[name] = nil
|
||||
|
||||
if line == current_rank then
|
||||
select_rank()
|
||||
end
|
||||
end)
|
||||
|
||||
-- This is just better than caching panels for stuff that ain't gonna be called a lot
|
||||
hook.Add("SAM.RankNameChanged", "SAM.RefreshRanksList", function(name, new_name)
|
||||
local line = ranks[name]
|
||||
if not IsValid(line) then return end
|
||||
|
||||
-- if current_rank == name then
|
||||
-- rank_name:SetText(new_name)
|
||||
-- end
|
||||
|
||||
line:GetChildren()[1]:SetText(new_name)
|
||||
|
||||
ranks[new_name], ranks[name] = line, nil
|
||||
line.name = new_name
|
||||
end)
|
||||
|
||||
hook.Add("SAM.RankImmunityChanged", "SAM.RefreshRanksList", function(name, immunity)
|
||||
local line = ranks[name]
|
||||
if not IsValid(line) then return end
|
||||
|
||||
line:GetChildren()[2]:SetText("Immunity: " .. immunity)
|
||||
line:GetParent():SetZPos(-immunity)
|
||||
|
||||
-- SetZPos is kinda weird to deal with
|
||||
line.line:SetZPos(-immunity + 1)
|
||||
line.line:SetZPos(-immunity)
|
||||
end)
|
||||
|
||||
hook.Add("SAM.RankBanLimitChanged", "SAM.RefreshRanksList", function(name, new_limit)
|
||||
local line = ranks[name]
|
||||
if IsValid(line) then
|
||||
line:GetChildren()[3]:SetText("Ban limit: " .. sam.reverse_parse_length(new_limit))
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("SAM.ChangedInheritRank", "SAM.RefreshRanksList", function(name, new_inherit)
|
||||
local line = ranks[name]
|
||||
if IsValid(line) then
|
||||
line:GetChildren()[4]:SetText("Inherits from: " .. new_inherit)
|
||||
end
|
||||
end)
|
||||
|
||||
return parent
|
||||
end, function()
|
||||
return LocalPlayer():HasPermission("manage_ranks")
|
||||
end, 3)
|
||||
367
addons/sam/lua/sam/modules/cami.lua
Normal file
367
addons/sam/lua/sam/modules/cami.lua
Normal file
@@ -0,0 +1,367 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
--[[
|
||||
CAMI - Common Admin Mod Interface.
|
||||
Copyright 2020 CAMI Contributors
|
||||
|
||||
Makes admin mods intercompatible and provides an abstract privilege interface
|
||||
for third party addons.
|
||||
|
||||
Follows the specification on this page:
|
||||
https://github.com/glua/CAMI/blob/master/README.md
|
||||
|
||||
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.
|
||||
]]
|
||||
|
||||
-- Version number in YearMonthDay format.
|
||||
local version = 20201130
|
||||
|
||||
if CAMI and CAMI.Version >= version then return end
|
||||
|
||||
CAMI = CAMI or {}
|
||||
CAMI.Version = version
|
||||
|
||||
|
||||
--- @class CAMI_USERGROUP
|
||||
--- defines the charactaristics of a usergroup
|
||||
--- @field Name string @The name of the usergroup
|
||||
--- @field Inherits string @The name of the usergroup this usergroup inherits from
|
||||
|
||||
--- @class CAMI_PRIVILEGE
|
||||
--- defines the charactaristics of a privilege
|
||||
--- @field Name string @The name of the privilege
|
||||
--- @field MinAccess "'user'" | "'admin'" | "'superadmin'" @Default group that should have this privilege
|
||||
--- @field Description string | nil @Optional text describing the purpose of the privilege
|
||||
local CAMI_PRIVILEGE = {}
|
||||
--- Optional function to check if a player has access to this privilege
|
||||
--- (and optionally execute it on another player)
|
||||
---
|
||||
--- ⚠ **Warning**: This function may not be called by all admin mods
|
||||
--- @param actor GPlayer @The player
|
||||
--- @param target GPlayer | nil @Optional - the target
|
||||
--- @return boolean @If they can or not
|
||||
--- @return string | nil @Optional reason
|
||||
function CAMI_PRIVILEGE:HasAccess(actor, target)
|
||||
end
|
||||
|
||||
--- Contains the registered CAMI_USERGROUP usergroup structures.
|
||||
--- Indexed by usergroup name.
|
||||
--- @type CAMI_USERGROUP[]
|
||||
local usergroups = CAMI.GetUsergroups and CAMI.GetUsergroups() or {
|
||||
user = {
|
||||
Name = "user",
|
||||
Inherits = "user"
|
||||
},
|
||||
admin = {
|
||||
Name = "admin",
|
||||
Inherits = "user"
|
||||
},
|
||||
superadmin = {
|
||||
Name = "superadmin",
|
||||
Inherits = "admin"
|
||||
}
|
||||
}
|
||||
|
||||
--- Contains the registered CAMI_PRIVILEGE privilege structures.
|
||||
--- Indexed by privilege name.
|
||||
--- @type CAMI_PRIVILEGE[]
|
||||
local privileges = CAMI.GetPrivileges and CAMI.GetPrivileges() or {}
|
||||
|
||||
--- Registers a usergroup with CAMI.
|
||||
---
|
||||
--- Use the source parameter to make sure CAMI.RegisterUsergroup function and
|
||||
--- the CAMI.OnUsergroupRegistered hook don't cause an infinite loop
|
||||
--- @param usergroup CAMI_USERGROUP @The structure for the usergroup you want to register
|
||||
--- @param source any @Identifier for your own admin mod. Can be anything.
|
||||
--- @return CAMI_USERGROUP @The usergroup given as an argument
|
||||
function CAMI.RegisterUsergroup(usergroup, source)
|
||||
usergroups[usergroup.Name] = usergroup
|
||||
|
||||
hook.Call("CAMI.OnUsergroupRegistered", nil, usergroup, source)
|
||||
return usergroup
|
||||
end
|
||||
|
||||
--- Unregisters a usergroup from CAMI. This will call a hook that will notify
|
||||
--- all other admin mods of the removal.
|
||||
---
|
||||
--- ⚠ **Warning**: Call only when the usergroup is to be permanently removed.
|
||||
---
|
||||
--- Use the source parameter to make sure CAMI.UnregisterUsergroup function and
|
||||
--- the CAMI.OnUsergroupUnregistered hook don't cause an infinite loop
|
||||
--- @param usergroupName string @The name of the usergroup.
|
||||
--- @param source any @Identifier for your own admin mod. Can be anything.
|
||||
--- @return boolean @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
|
||||
|
||||
--- Retrieves all registered usergroups.
|
||||
--- @return CAMI_USERGROUP[] @Usergroups indexed by their names.
|
||||
function CAMI.GetUsergroups()
|
||||
return usergroups
|
||||
end
|
||||
|
||||
--- Receives information about a usergroup.
|
||||
--- @param usergroupName string
|
||||
--- @return CAMI_USERGROUP | nil @Returns nil when the usergroup does not exist.
|
||||
function CAMI.GetUsergroup(usergroupName)
|
||||
return usergroups[usergroupName]
|
||||
end
|
||||
|
||||
--- Checks to see if potentialAncestor is an ancestor of usergroupName.
|
||||
--- All usergroups are ancestors of themselves.
|
||||
---
|
||||
--- Examples:
|
||||
--- * `user` is an ancestor of `admin` and also `superadmin`
|
||||
--- * `admin` is an ancestor of `superadmin`, but not `user`
|
||||
--- @param usergroupName string @The usergroup to query
|
||||
--- @param potentialAncestor string @The ancestor to query
|
||||
--- @return boolean @Whether usergroupName inherits potentialAncestor.
|
||||
function CAMI.UsergroupInherits(usergroupName, potentialAncestor)
|
||||
repeat
|
||||
if usergroupName == potentialAncestor then return true end
|
||||
|
||||
usergroupName = usergroups[usergroupName] and
|
||||
usergroups[usergroupName].Inherits or
|
||||
usergroupName
|
||||
until not usergroups[usergroupName] or
|
||||
usergroups[usergroupName].Inherits == usergroupName
|
||||
|
||||
-- One can only be sure the usergroup inherits from user if the
|
||||
-- usergroup isn't registered.
|
||||
return usergroupName == potentialAncestor or potentialAncestor == "user"
|
||||
end
|
||||
|
||||
--- Find the base group a usergroup inherits from.
|
||||
---
|
||||
--- This function traverses down the inheritence chain, so for example if you have
|
||||
--- `user` -> `group1` -> `group2`
|
||||
--- this function will return `user` if you pass it `group2`.
|
||||
---
|
||||
--- ℹ **NOTE**: All usergroups must eventually inherit either user, admin or superadmin.
|
||||
--- @param usergroupName string @The name of the usergroup
|
||||
--- @return "'user'" | "'admin'" | "'superadmin'" @The name of the root usergroup
|
||||
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
|
||||
|
||||
--- Registers an addon privilege with CAMI.
|
||||
---
|
||||
--- ⚠ **Warning**: This should only be used by addons. Admin mods must *NOT*
|
||||
--- register their privileges using this function.
|
||||
--- @param privilege CAMI_PRIVILEGE
|
||||
--- @return CAMI_PRIVILEGE @The privilege given as argument.
|
||||
function CAMI.RegisterPrivilege(privilege)
|
||||
privileges[privilege.Name] = privilege
|
||||
|
||||
hook.Call("CAMI.OnPrivilegeRegistered", nil, privilege)
|
||||
|
||||
return privilege
|
||||
end
|
||||
|
||||
--- Unregisters a privilege from CAMI.
|
||||
--- This will call a hook that will notify any admin mods of the removal.
|
||||
---
|
||||
--- ⚠ **Warning**: Call only when the privilege is to be permanently removed.
|
||||
--- @param privilegeName string @The name of the privilege.
|
||||
--- @return boolean @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
|
||||
|
||||
--- Retrieves all registered privileges.
|
||||
--- @return CAMI_PRIVILEGE[] @All privileges indexed by their names.
|
||||
function CAMI.GetPrivileges()
|
||||
return privileges
|
||||
end
|
||||
|
||||
--- Receives information about a privilege.
|
||||
--- @param privilegeName string
|
||||
--- @return CAMI_PRIVILEGE | nil
|
||||
function CAMI.GetPrivilege(privilegeName)
|
||||
return privileges[privilegeName]
|
||||
end
|
||||
|
||||
-- Default access handler
|
||||
local defaultAccessHandler = {["CAMI.PlayerHasAccess"] =
|
||||
function(_, actorPly, privilegeName, callback, targetPly, 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
|
||||
|
||||
local hasAccess =
|
||||
priv.MinAccess == "user" or
|
||||
priv.MinAccess == "admin" and actorPly:IsAdmin() or
|
||||
priv.MinAccess == "superadmin" and actorPly:IsSuperAdmin()
|
||||
|
||||
if hasAccess and priv.HasAccess then
|
||||
hasAccess = priv:HasAccess(actorPly, targetPly)
|
||||
end
|
||||
|
||||
callback(hasAccess, "Fallback.")
|
||||
end,
|
||||
["CAMI.SteamIDHasAccess"] =
|
||||
function(_, _, _, callback)
|
||||
callback(false, "No information available.")
|
||||
end
|
||||
}
|
||||
|
||||
--- @class CAMI_ACCESS_EXTRA_INFO
|
||||
--- @field Fallback "'user'" | "'admin'" | "'superadmin'" @Fallback status for if the privilege doesn't exist. Defaults to `admin`.
|
||||
--- @field IgnoreImmunity boolean @Ignore any immunity mechanisms an admin mod might have.
|
||||
--- @field CommandArguments table @Extra arguments that were given to the privilege command.
|
||||
|
||||
--- Checks if a player has access to a privilege
|
||||
--- (and optionally can execute it on targetPly)
|
||||
---
|
||||
--- This function is designed to be asynchronous but will be invoked
|
||||
--- synchronously if no callback is passed.
|
||||
---
|
||||
--- ⚠ **Warning**: If the currently installed admin mod does not support
|
||||
--- synchronous queries, this function will throw an error!
|
||||
--- @param actorPly GPlayer @The player to query
|
||||
--- @param privilegeName string @The privilege to query
|
||||
--- @param callback fun(hasAccess: boolean, reason: string|nil) @Callback to receive the answer, or nil for synchronous
|
||||
--- @param targetPly GPlayer | nil @Optional - target for if the privilege effects another player (eg kick/ban)
|
||||
--- @param extraInfoTbl CAMI_ACCESS_EXTRA_INFO | nil @Table of extra information for the admin mod
|
||||
--- @return boolean | nil @Synchronous only - if the player has the privilege
|
||||
--- @return string | nil @Synchronous only - optional reason from admin mod
|
||||
function CAMI.PlayerHasAccess(actorPly, privilegeName, callback, targetPly,
|
||||
extraInfoTbl)
|
||||
local hasAccess, reason = nil, nil
|
||||
local callback_ = callback or function(hA, r) hasAccess, reason = hA, r end
|
||||
|
||||
hook.Call("CAMI.PlayerHasAccess", defaultAccessHandler, actorPly,
|
||||
privilegeName, callback_, targetPly, extraInfoTbl)
|
||||
|
||||
if callback ~= nil then return end
|
||||
|
||||
if hasAccess == nil then
|
||||
local err = [[The function CAMI.PlayerHasAccess was used to find out
|
||||
whether Player %s has privilege "%s", but an admin mod did not give an
|
||||
immediate answer!]]
|
||||
error(string.format(err,
|
||||
actorPly:IsPlayer() and actorPly:Nick() or tostring(actorPly),
|
||||
privilegeName))
|
||||
end
|
||||
|
||||
return hasAccess, reason
|
||||
end
|
||||
|
||||
--- Get all the players on the server with a certain privilege
|
||||
--- (and optionally who can execute it on targetPly)
|
||||
---
|
||||
--- ℹ **NOTE**: This is an asynchronous function!
|
||||
--- @param privilegeName string @The privilege to query
|
||||
--- @param callback fun(players: GPlayer[]) @Callback to receive the answer
|
||||
--- @param targetPly GPlayer | nil @Optional - target for if the privilege effects another player (eg kick/ban)
|
||||
--- @param extraInfoTbl CAMI_ACCESS_EXTRA_INFO | nil @Table of extra information for the admin mod
|
||||
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 ipairs(allPlys) do
|
||||
CAMI.PlayerHasAccess(ply, privilegeName,
|
||||
function(...) onResult(ply, ...) end,
|
||||
targetPly, extraInfoTbl)
|
||||
end
|
||||
end
|
||||
|
||||
--- @class CAMI_STEAM_ACCESS_EXTRA_INFO
|
||||
--- @field IgnoreImmunity boolean @Ignore any immunity mechanisms an admin mod might have.
|
||||
--- @field CommandArguments table @Extra arguments that were given to the privilege command.
|
||||
|
||||
--- Checks if a (potentially offline) SteamID has access to a privilege
|
||||
--- (and optionally if they can execute it on a target SteamID)
|
||||
---
|
||||
--- ℹ **NOTE**: This is an asynchronous function!
|
||||
--- @param actorSteam string | nil @The SteamID to query
|
||||
--- @param privilegeName string @The privilege to query
|
||||
--- @param callback fun(hasAccess: boolean, reason: string|nil) @Callback to receive the answer
|
||||
--- @param targetSteam string | nil @Optional - target SteamID for if the privilege effects another player (eg kick/ban)
|
||||
--- @param extraInfoTbl CAMI_STEAM_ACCESS_EXTRA_INFO | nil @Table of extra information for the admin mod
|
||||
function CAMI.SteamIDHasAccess(actorSteam, privilegeName, callback,
|
||||
targetSteam, extraInfoTbl)
|
||||
hook.Call("CAMI.SteamIDHasAccess", defaultAccessHandler, actorSteam,
|
||||
privilegeName, callback, targetSteam, extraInfoTbl)
|
||||
end
|
||||
|
||||
--- 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.
|
||||
--- @param ply GPlayer @The player for which the usergroup is changed
|
||||
--- @param old string @The previous usergroup of the player.
|
||||
--- @param new string @The new usergroup of the player.
|
||||
--- @param 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
|
||||
|
||||
--- 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.
|
||||
--- @param steamId string @The steam ID of the player for which the usergroup is changed
|
||||
--- @param old string @The previous usergroup of the player.
|
||||
--- @param new string @The new usergroup of the player.
|
||||
--- @param 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
|
||||
238
addons/sam/lua/sam/modules/chat.lua
Normal file
238
addons/sam/lua/sam/modules/chat.lua
Normal file
@@ -0,0 +1,238 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("Chat")
|
||||
|
||||
command.new("pm")
|
||||
:SetPermission("pm", "user")
|
||||
|
||||
:AddArg("player", {allow_higher_target = true, single_target = true, cant_target_self = true})
|
||||
:AddArg("text", {hint = "message", check = function(str)
|
||||
return str:match("%S") ~= nil
|
||||
end})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("pm_help")
|
||||
|
||||
:OnExecute(function(ply, targets, message)
|
||||
if ply:sam_get_pdata("unmute_time") then
|
||||
return ply:sam_send_message("you_muted")
|
||||
end
|
||||
|
||||
local target = targets[1]
|
||||
|
||||
ply:sam_send_message("pm_to", {
|
||||
T = targets, V = message
|
||||
})
|
||||
|
||||
if ply ~= target then
|
||||
target:sam_send_message("pm_from", {
|
||||
A = ply, V = message
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
sam.permissions.add("see_admin_chat", nil, "admin")
|
||||
|
||||
local reports_enabled = sam.config.get_updated("Reports", true)
|
||||
command.new("asay")
|
||||
:SetPermission("asay", "user")
|
||||
|
||||
:AddArg("text", {hint = "message"})
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("asay_help")
|
||||
|
||||
:OnExecute(function(ply, message)
|
||||
if reports_enabled.value and not ply:HasPermission("see_admin_chat") then
|
||||
local success, time = sam.player.report(ply, message)
|
||||
if success == false then
|
||||
ply:sam_send_message("You need to wait {S Red} seconds.", {
|
||||
S = time
|
||||
})
|
||||
else
|
||||
ply:sam_send_message("to_admins", {
|
||||
A = ply, V = message
|
||||
})
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local targets = {ply}
|
||||
|
||||
local players = player.GetHumans()
|
||||
for i = 1, #players do
|
||||
local v = players[i]
|
||||
if v:HasPermission("see_admin_chat") and v ~= ply then
|
||||
table.insert(targets, v)
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(targets, "to_admins", {
|
||||
A = ply, V = message
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
if SERVER then
|
||||
sam.hook_last("PlayerSay", "SAM.Chat.Asay", function(ply, text)
|
||||
if text:sub(1, 1) == "@" then
|
||||
ply:Say("!asay " .. text:sub(2))
|
||||
return ""
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
command.new("mute")
|
||||
:SetPermission("mute", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("length", {optional = true, default = 0, min = 0})
|
||||
:AddArg("text", {hint = "reason", optional = true, default = sam.language.get("default_reason")})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("mute_help")
|
||||
|
||||
:OnExecute(function(ply, targets, length, reason)
|
||||
local current_time = SysTime()
|
||||
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
target:sam_set_pdata("unmute_time", length ~= 0 and (current_time + length * 60) or 0)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "mute", {
|
||||
A = ply, T = targets, V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unmute")
|
||||
:SetPermission("unmute", "admin")
|
||||
:AddArg("player", {optional = true})
|
||||
:Help("unmute_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:sam_set_pdata("unmute_time", nil)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "unmute", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
if SERVER then
|
||||
sam.hook_first("PlayerSay", "SAM.Chat.Mute", function(ply, text)
|
||||
local unmute_time = ply:sam_get_pdata("unmute_time")
|
||||
if not unmute_time then return end
|
||||
|
||||
if text:sub(1, 1) == "!" and text:sub(2, 2):match("%S") ~= nil then
|
||||
local args = sam.parse_args(text:sub(2))
|
||||
|
||||
local cmd_name = args[1]
|
||||
if not cmd_name then return end
|
||||
|
||||
local cmd = command.get_command(cmd_name)
|
||||
if cmd then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if unmute_time == 0 or unmute_time > SysTime() then
|
||||
return ""
|
||||
else
|
||||
ply:sam_set_pdata("unmute_time", nil)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
command.new("gag")
|
||||
:SetPermission("gag", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("length", {optional = true, default = 0, min = 0})
|
||||
:AddArg("text", {hint = "reason", optional = true, default = sam.language.get("default_reason")})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("gag_help")
|
||||
|
||||
:OnExecute(function(ply, targets, length, reason)
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
target.sam_gagged = true
|
||||
if length ~= 0 then
|
||||
timer.Create("SAM.UnGag" .. target:SteamID64(), length * 60, 1, function()
|
||||
RunConsoleCommand("sam", "ungag", "#" .. target:EntIndex())
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "gag", {
|
||||
A = ply, T = targets, V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("ungag")
|
||||
:SetPermission("ungag", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
:Help("ungag_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
target.sam_gagged = nil
|
||||
timer.Remove("SAM.UnGag" .. target:SteamID64())
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "ungag", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
if SERVER then
|
||||
hook.Add("PlayerCanHearPlayersVoice", "SAM.Chat.Gag", function(_, ply)
|
||||
if ply.sam_gagged then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "SAM.Gag", function(ply)
|
||||
local gag_time = ply:sam_get_pdata("gagged")
|
||||
if gag_time then
|
||||
ply:sam_set_pdata("gagged", nil)
|
||||
RunConsoleCommand("sam", "gag", "#" .. ply:EntIndex(), gag_time / 60, "LTAP")
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerDisconnected", "SAM.Gag", function(ply)
|
||||
if ply.sam_gagged then
|
||||
ply:sam_set_pdata("gagged", timer.TimeLeft("SAM.UnGag" .. ply:SteamID64()) or 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
39
addons/sam/lua/sam/modules/commnad_notify_for_ranks.lua
Normal file
39
addons/sam/lua/sam/modules/commnad_notify_for_ranks.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
--
|
||||
-- Make command notifying only for ranks you select.
|
||||
-- permission is command_notify. (by default admin+ has it)
|
||||
-- You can NOT use this with 'command_hide_admin_name.lua'
|
||||
--
|
||||
if SAM_LOADED then return end
|
||||
|
||||
sam.permissions.add("command_notify", nil, "admin")
|
||||
|
||||
if SERVER then
|
||||
local get_players = function()
|
||||
local players = {}
|
||||
for _, v in ipairs(player.GetAll()) do
|
||||
if v:HasPermission("command_notify") then
|
||||
table.insert(players, v)
|
||||
end
|
||||
end
|
||||
return players
|
||||
end
|
||||
|
||||
sam.player.old_send_message = sam.player.old_send_message or sam.player.send_message
|
||||
function sam.player.send_message(ply, msg, tbl)
|
||||
if ply == nil and debug.traceback():find("lua/sam/command/", 1, true) then
|
||||
sam.player.old_send_message(get_players(), msg, tbl)
|
||||
else
|
||||
sam.player.old_send_message(ply, msg, tbl)
|
||||
end
|
||||
end
|
||||
end
|
||||
324
addons/sam/lua/sam/modules/darkrp.lua
Normal file
324
addons/sam/lua/sam/modules/darkrp.lua
Normal file
@@ -0,0 +1,324 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local add = not GAMEMODE and hook.Add or function(_, _, fn)
|
||||
fn()
|
||||
end
|
||||
|
||||
add("PostGamemodeLoaded", "SAM.DarkRP", function()
|
||||
if not DarkRP then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("DarkRP")
|
||||
|
||||
command.new("arrest")
|
||||
:SetPermission("arrest", "superadmin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "time", optional = true, min = 0, default = 0, round = true})
|
||||
|
||||
:Help("arrest_help")
|
||||
|
||||
:OnExecute(function(ply, targets, time)
|
||||
if time == 0 then
|
||||
time = math.huge
|
||||
end
|
||||
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
if v:isArrested() then
|
||||
v:unArrest()
|
||||
end
|
||||
v:arrest(time, ply)
|
||||
end
|
||||
|
||||
if time == math.huge then
|
||||
sam.player.send_message(nil, "arrest", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "arrest2", {
|
||||
A = ply, T = targets, V = time
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unarrest")
|
||||
:SetPermission("unarrest", "superadmin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("unarrest_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:unArrest()
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "unarrest", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("setmoney")
|
||||
:SetPermission("setmoney", "superadmin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("number", {hint = "amount", min = 0, round = true})
|
||||
|
||||
:Help("setmoney_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
local target = targets[1]
|
||||
|
||||
amount = hook.Call("playerWalletChanged", GAMEMODE, target, amount - target:getDarkRPVar("money"), target:getDarkRPVar("money")) or amount
|
||||
|
||||
DarkRP.storeMoney(target, amount)
|
||||
target:setDarkRPVar("money", amount)
|
||||
|
||||
sam.player.send_message(nil, "setmoney", {
|
||||
A = ply, T = targets, V = amount
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("addmoney")
|
||||
:SetPermission("addmoney", "superadmin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("number", {hint = "amount", min = 0, round = true})
|
||||
|
||||
:Help("addmoney_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
targets[1]:addMoney(amount)
|
||||
|
||||
sam.player.send_message(nil, "addmoney", {
|
||||
A = ply, T = targets, V = DarkRP.formatMoney(amount)
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("selldoor")
|
||||
:SetPermission("selldoor", "superadmin")
|
||||
|
||||
:Help("selldoor_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
local ent = ply:GetEyeTrace().Entity
|
||||
if not IsValid(ent) or not ent.keysUnOwn then
|
||||
return ply:sam_send_message("door_invalid")
|
||||
end
|
||||
local door_owner = ent:getDoorOwner()
|
||||
if not IsValid(door_owner) then
|
||||
return ply:sam_send_message("door_no_owner")
|
||||
end
|
||||
ent:keysUnOwn(ply)
|
||||
|
||||
sam.player.send_message(nil, "selldoor", {
|
||||
A = ply, T = {door_owner, admin = ply}
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("sellall")
|
||||
:SetPermission("sellall", "superadmin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
|
||||
:Help("sellall_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
targets[1]:keysUnOwnAll()
|
||||
|
||||
sam.player.send_message(nil, "sellall", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("setjailpos")
|
||||
:SetPermission("setjailpos", "superadmin")
|
||||
|
||||
:Help("setjailpos_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
DarkRP.storeJailPos(ply, false)
|
||||
|
||||
sam.player.send_message(nil, "s_jail_pos", {
|
||||
A = ply
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("addjailpos")
|
||||
:SetPermission("addjailpos", "superadmin")
|
||||
|
||||
:Help("addjailpos_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
DarkRP.storeJailPos(ply, true)
|
||||
|
||||
sam.player.send_message(nil, "a_jail_pos", {
|
||||
A = ply
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
local RPExtraTeams = RPExtraTeams
|
||||
local job_index = nil
|
||||
|
||||
command.new("setjob")
|
||||
:SetPermission("setjob", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("text", {hint = "job", check = function(job)
|
||||
job = job:lower()
|
||||
|
||||
for i = 1, #RPExtraTeams do
|
||||
local v = RPExtraTeams[i]
|
||||
if v.name:lower() == job or v.command:lower() == job then
|
||||
job_index = v.team
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end})
|
||||
|
||||
:Help("setjob_help")
|
||||
|
||||
:OnExecute(function(ply, targets, job)
|
||||
for i = 1, #targets do
|
||||
targets[i]:changeTeam(job_index, true, true, true)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "setjob", {
|
||||
A = ply, T = targets, V = job
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
local get_shipment = function(name)
|
||||
local found, key = DarkRP.getShipmentByName(name)
|
||||
if found then return found, key end
|
||||
|
||||
name = name:lower()
|
||||
|
||||
local shipments = CustomShipments
|
||||
for i = 1, #shipments do
|
||||
local shipment = shipments[i]
|
||||
if shipment.entity == name then
|
||||
return DarkRP.getShipmentByName(shipment.name)
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local place_entity = function(ent, tr, ply)
|
||||
local ang = ply:EyeAngles()
|
||||
ang.pitch = 0
|
||||
ang.yaw = ang.yaw - 90
|
||||
ang.roll = 0
|
||||
ent:SetAngles(ang)
|
||||
|
||||
local flush_point = tr.HitPos - (tr.HitNormal * 512)
|
||||
flush_point = ent:NearestPoint(flush_point)
|
||||
flush_point = ent:GetPos() - flush_point
|
||||
flush_point = tr.HitPos + flush_point
|
||||
ent:SetPos(flush_point)
|
||||
end
|
||||
|
||||
command.new("shipment")
|
||||
:SetPermission("shipment", "superadmin")
|
||||
|
||||
:AddArg("text", {hint = "weapon", check = get_shipment})
|
||||
|
||||
:Help("shipment_help")
|
||||
|
||||
:OnExecute(function(ply, weapon_name)
|
||||
local trace = {}
|
||||
trace.start = ply:EyePos()
|
||||
trace.endpos = trace.start + ply:GetAimVector() * 85
|
||||
trace.filter = ply
|
||||
local tr = util.TraceLine(trace)
|
||||
|
||||
local shipment_info, shipment_key = get_shipment(weapon_name)
|
||||
|
||||
local crate = ents.Create(shipment_info.shipmentClass or "spawned_shipment")
|
||||
crate.SID = ply.SID
|
||||
|
||||
crate:Setowning_ent(ply)
|
||||
crate:SetContents(shipment_key, shipment_info.amount)
|
||||
|
||||
crate:SetPos(Vector(tr.HitPos.x, tr.HitPos.y, tr.HitPos.z))
|
||||
|
||||
crate.nodupe = true
|
||||
crate.ammoadd = shipment_info.spareammo
|
||||
crate.clip1 = shipment_info.clip1
|
||||
crate.clip2 = shipment_info.clip2
|
||||
|
||||
crate:Spawn()
|
||||
crate:SetPlayer(ply)
|
||||
|
||||
place_entity(crate, tr, ply)
|
||||
|
||||
local phys = crate:GetPhysicsObject()
|
||||
phys:Wake()
|
||||
|
||||
if shipment_info.weight then
|
||||
phys:SetMass(shipment_info.weight)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "shipment", {
|
||||
A = ply, V = weapon_name
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
end
|
||||
|
||||
sam.command.new("forcename")
|
||||
:SetPermission("forcename", "superadmin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("text", {hint = "name"})
|
||||
|
||||
:Help("forcename_help")
|
||||
|
||||
:OnExecute(function(ply, targets, name)
|
||||
local target = targets[1]
|
||||
|
||||
DarkRP.retrieveRPNames(name, function(taken)
|
||||
if not IsValid(target) then return end
|
||||
|
||||
if taken then
|
||||
ply:sam_send_message("forcename_taken", {
|
||||
V = name
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "forcename", {
|
||||
A = ply, T = targets, V = name
|
||||
})
|
||||
|
||||
DarkRP.storeRPName(target, name)
|
||||
target:setDarkRPVar("rpname", name)
|
||||
end)
|
||||
end)
|
||||
:End()
|
||||
end)
|
||||
654
addons/sam/lua/sam/modules/fun.lua
Normal file
654
addons/sam/lua/sam/modules/fun.lua
Normal file
@@ -0,0 +1,654 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("Fun")
|
||||
|
||||
do
|
||||
local sounds = {}
|
||||
for i = 1, 6 do
|
||||
sounds[i] = "physics/body/body_medium_impact_hard" .. i .. ".wav"
|
||||
end
|
||||
|
||||
local slap = function(ply, damage, admin)
|
||||
if not ply:Alive() or ply:sam_get_nwvar("frozen") then return end
|
||||
ply:ExitVehicle()
|
||||
|
||||
ply:SetVelocity(Vector(math.random(-100, 100), math.random(-100, 100), math.random(200, 400)))
|
||||
ply:EmitSound(sounds[math.random(1, 6)], 60, math.random(80, 120))
|
||||
|
||||
if damage > 0 then
|
||||
ply:TakeDamage(damage, admin, DMG_GENERIC)
|
||||
end
|
||||
end
|
||||
|
||||
command.new("slap")
|
||||
:SetPermission("slap", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "damage", round = true, optional = true, min = 0, default = 0})
|
||||
|
||||
:Help("slap_help")
|
||||
|
||||
:OnExecute(function(ply, targets, damage)
|
||||
for i = 1, #targets do
|
||||
slap(targets[i], damage, ply)
|
||||
end
|
||||
|
||||
if damage > 0 then
|
||||
sam.player.send_message(nil, "slap_damage", {
|
||||
A = ply, T = targets, V = damage
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "slap", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
end
|
||||
|
||||
command.new("slay")
|
||||
:SetPermission("slay", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
|
||||
:Help("slay_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
if not v:sam_get_exclusive(ply) then
|
||||
v:Kill()
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "slay", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("hp")
|
||||
:Aliases("sethp", "health", "sethealth")
|
||||
|
||||
:SetPermission("hp", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "amount", min = 1, max = 2147483647, round = true, optional = true, default = 100})
|
||||
|
||||
:Help("hp_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
for i = 1, #targets do
|
||||
targets[i]:SetHealth(amount)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "set_hp", {
|
||||
A = ply, T = targets, V = amount
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("armor")
|
||||
:Aliases("setarmor")
|
||||
|
||||
:SetPermission("armor", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "amount", min = 1, max = 2147483647, round = true, optional = true, default = 100})
|
||||
|
||||
:Help("armor_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
for i = 1, #targets do
|
||||
targets[i]:SetArmor(amount)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "set_armor", {
|
||||
A = ply, T = targets, V = amount
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("ignite")
|
||||
:SetPermission("ignite", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "seconds", optional = true, default = 60, round = true})
|
||||
|
||||
:Help("ignite_help")
|
||||
|
||||
:OnExecute(function(ply, targets, length)
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
|
||||
if target:IsOnFire() then
|
||||
target:Extinguish()
|
||||
end
|
||||
|
||||
target:Ignite(length)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "ignite", {
|
||||
A = ply, T = targets, V = length
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unignite")
|
||||
:Aliases("extinguish")
|
||||
|
||||
:SetPermission("ignite", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("unignite_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:Extinguish()
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "unignite", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("god")
|
||||
:Aliases("invincible")
|
||||
|
||||
:SetPermission("god", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("god_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
target:GodEnable()
|
||||
target.sam_has_god_mode = true
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "god", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("ungod")
|
||||
:Aliases("uninvincible")
|
||||
|
||||
:SetPermission("ungod", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("ungod_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
target:GodDisable()
|
||||
target.sam_has_god_mode = nil
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "ungod", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
command.new("freeze")
|
||||
:SetPermission("freeze", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
|
||||
:Help("freeze_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
v:ExitVehicle()
|
||||
if v:sam_get_nwvar("frozen") then
|
||||
v:UnLock()
|
||||
end
|
||||
v:Lock()
|
||||
v:sam_set_nwvar("frozen", true)
|
||||
v:sam_set_exclusive("frozen")
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "freeze", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unfreeze")
|
||||
:SetPermission("unfreeze", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("unfreeze_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
v:UnLock()
|
||||
v:sam_set_nwvar("frozen", false)
|
||||
v:sam_set_exclusive(nil)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "unfreeze", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
local disallow = function(ply)
|
||||
if ply:sam_get_nwvar("frozen") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
for _, v in ipairs({"SAM.CanPlayerSpawn", "CanPlayerSuicide", "CanTool"}) do
|
||||
hook.Add(v, "SAM.FreezePlayer." .. v, disallow)
|
||||
end
|
||||
end
|
||||
|
||||
command.new("cloak")
|
||||
:SetPermission("cloak", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("cloak_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:sam_cloak()
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "cloak", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("uncloak")
|
||||
:SetPermission("uncloak", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("uncloak_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:sam_uncloak()
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "uncloak", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
local jail_props = {
|
||||
Vector(0, 0, -5), Angle(90, 0, 0);
|
||||
Vector(0, 0, 97), Angle(90, 0, 0);
|
||||
|
||||
Vector(21, 31, 46), Angle(0, 90, 0);
|
||||
Vector(21, -31, 46), Angle(0, 90, 0);
|
||||
Vector(-21, 31, 46), Angle(0, 90, 0);
|
||||
Vector(-21, -31, 46), Angle(0, 90, 0);
|
||||
|
||||
Vector(-52, 0, 46), Angle(0, 0, 0);
|
||||
Vector(52, 0, 46), Angle(0, 0, 0)
|
||||
}
|
||||
|
||||
local remove_jail = function(ply_jail_props)
|
||||
for _, jail_prop in ipairs(ply_jail_props) do
|
||||
if IsValid(jail_prop) then
|
||||
jail_prop:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local unjail = function(ply)
|
||||
if not IsValid(ply) then return end
|
||||
if not ply:sam_get_nwvar("jailed") then return end
|
||||
|
||||
remove_jail(ply.sam_jail_props)
|
||||
|
||||
ply.sam_jail_props = nil
|
||||
ply.sam_jail_pos = nil
|
||||
|
||||
ply:sam_set_nwvar("jailed", nil)
|
||||
ply:sam_set_exclusive(nil)
|
||||
|
||||
timer.Remove("SAM.Unjail." .. ply:SteamID())
|
||||
timer.Remove("SAM.Jail.Watch." .. ply:SteamID())
|
||||
end
|
||||
|
||||
local return_false = function()
|
||||
return false
|
||||
end
|
||||
|
||||
local function jail(ply, time)
|
||||
if not IsValid(ply) then return end
|
||||
if not isnumber(time) or time < 0 then
|
||||
time = 0
|
||||
end
|
||||
|
||||
if ply:sam_get_nwvar("frozen") then
|
||||
RunConsoleCommand("sam", "unfreeze", "#" .. ply:EntIndex())
|
||||
end
|
||||
|
||||
if not ply:sam_get_nwvar("jailed") or (not ply.sam_jail_props or not IsValid(ply.sam_jail_props[1])) then
|
||||
ply:ExitVehicle()
|
||||
ply:SetMoveType(MOVETYPE_WALK)
|
||||
|
||||
ply.sam_jail_pos = ply:GetPos()
|
||||
|
||||
ply:sam_set_nwvar("jailed", true)
|
||||
ply:sam_set_exclusive("in jail")
|
||||
|
||||
if ply.sam_jail_props then
|
||||
for k, v in ipairs(ply.sam_jail_props) do
|
||||
if IsValid(v) then
|
||||
v:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ply_jail_props = {}
|
||||
for i = 1, #jail_props, 2 do
|
||||
local jail_prop = ents.Create("prop_physics")
|
||||
jail_prop:SetModel("models/props_building_details/Storefront_Template001a_Bars.mdl")
|
||||
jail_prop:SetPos(ply.sam_jail_pos + jail_props[i])
|
||||
jail_prop:SetAngles(jail_props[i + 1])
|
||||
jail_prop:SetMoveType(MOVETYPE_NONE)
|
||||
jail_prop:Spawn()
|
||||
jail_prop:GetPhysicsObject():EnableMotion(false)
|
||||
jail_prop.CanTool = return_false
|
||||
jail_prop.PhysgunPickup = return_false
|
||||
jail_prop.jailWall = true
|
||||
table.insert(ply_jail_props, jail_prop)
|
||||
end
|
||||
ply.sam_jail_props = ply_jail_props
|
||||
end
|
||||
|
||||
local steamid = ply:SteamID()
|
||||
|
||||
if time == 0 then
|
||||
timer.Remove("SAM.Unjail." .. steamid)
|
||||
else
|
||||
timer.Create("SAM.Unjail." .. steamid, time, 1, function()
|
||||
if IsValid(ply) then
|
||||
unjail(ply)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
timer.Create("SAM.Jail.Watch." .. steamid, 0.5, 0, function()
|
||||
if not IsValid(ply) then
|
||||
return timer.Remove("SAM.Jail.Watch." .. steamid)
|
||||
end
|
||||
|
||||
if ply:GetPos():DistToSqr(ply.sam_jail_pos) > 4900 then
|
||||
ply:SetPos(ply.sam_jail_pos)
|
||||
end
|
||||
|
||||
if not IsValid(ply.sam_jail_props[1]) then
|
||||
jail(ply, timer.TimeLeft("SAM.Unjail." .. steamid) or 0)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
command.new("jail")
|
||||
:SetPermission("jail", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("length", {optional = true, default = 0, min = 0})
|
||||
:AddArg("text", {hint = "reason", optional = true, default = sam.language.get("default_reason")})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("jail_help")
|
||||
|
||||
:OnExecute(function(ply, targets, length, reason)
|
||||
for i = 1, #targets do
|
||||
jail(targets[i], length * 60)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "jail", {
|
||||
A = ply, T = targets, V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unjail")
|
||||
:SetPermission("unjail", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("unjail_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
unjail(targets[i])
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "unjail", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
sam.hook_first("CanProperty", "SAM.Jail", function(_, property, ent)
|
||||
if ent.jailWall and property == "remover" then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
if SERVER then
|
||||
hook.Add("PlayerSpawn", "SAM.Jail", function(ply)
|
||||
if ply:sam_get_nwvar("jailed") or ply:sam_get_pdata("jailed") then
|
||||
if ply.sam_jail_pos then
|
||||
ply:SetPos(ply.sam_jail_pos)
|
||||
else
|
||||
ply:SetPos(ply:sam_get_pdata("jail_pos"))
|
||||
jail(ply, ply:sam_get_pdata("jail_time_left"))
|
||||
|
||||
ply:sam_set_pdata("jailed", nil)
|
||||
ply:sam_set_pdata("jail_pos", nil)
|
||||
ply:sam_set_pdata("jail_time_left", nil)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerEnteredVehicle", "SAM.Jail", function(ply)
|
||||
if ply:sam_get_nwvar("jailed") then
|
||||
ply:ExitVehicle()
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerDisconnected", "SAM.Jail", function(ply)
|
||||
if ply:sam_get_nwvar("jailed") then
|
||||
remove_jail(ply.sam_jail_props)
|
||||
|
||||
ply:sam_set_pdata("jailed", true)
|
||||
ply:sam_set_pdata("jail_pos", ply.sam_jail_pos)
|
||||
ply:sam_set_pdata("jail_time_left", timer.TimeLeft("SAM.Unjail." .. ply:SteamID()) or 0)
|
||||
|
||||
timer.Remove("SAM.Unjail." .. ply:SteamID())
|
||||
timer.Remove("SAM.Jail.Watch." .. ply:SteamID())
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local disallow = function(ply)
|
||||
if ply:sam_get_nwvar("jailed") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
for _, v in ipairs({"PlayerNoClip", "SAM.CanPlayerSpawn", "CanPlayerEnterVehicle", "CanPlayerSuicide", "CanTool"}) do
|
||||
hook.Add(v, "SAM.Jail", disallow)
|
||||
end
|
||||
end
|
||||
|
||||
command.new("strip")
|
||||
:SetPermission("strip", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
|
||||
:Help("strip_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:StripWeapons()
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "strip", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("respawn")
|
||||
:SetPermission("respawn", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("respawn_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i]:Spawn()
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "respawn", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("setmodel")
|
||||
:SetPermission("setmodel", "superadmin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("text", {hint = "model"})
|
||||
|
||||
:Help("setmodel_help")
|
||||
|
||||
:OnExecute(function(ply, targets, model)
|
||||
for i = 1, #targets do
|
||||
targets[i]:SetModel(model)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "setmodel", {
|
||||
A = ply, T = targets, V = model
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("giveammo")
|
||||
:Aliases("ammo")
|
||||
|
||||
:SetPermission("giveammo", "superadmin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "amount", min = 0, max = 99999})
|
||||
|
||||
:Help("giveammo_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
if amount == 0 then
|
||||
amount = 99999
|
||||
end
|
||||
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
for _, wep in ipairs(target:GetWeapons()) do
|
||||
if wep:GetPrimaryAmmoType() ~= -1 then
|
||||
target:GiveAmmo(amount, wep:GetPrimaryAmmoType(), true)
|
||||
end
|
||||
|
||||
if wep:GetSecondaryAmmoType() ~= -1 then
|
||||
target:GiveAmmo(amount, wep:GetSecondaryAmmoType(), true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "giveammo", {
|
||||
A = ply, T = targets, V = amount
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
command.new("scale")
|
||||
:SetPermission("scale", "superadmin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "amount", optional = true, min = 0, max = 2.5, default = 1})
|
||||
|
||||
:Help("scale_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
v:SetModelScale(amount)
|
||||
|
||||
-- https://github.com/carz1175/More-ULX-Commands/blob/9b142ee4247a84f16e2dc2ec71c879ab76e145d4/lua/ulx/modules/sh/extended.lua#L313
|
||||
v:SetViewOffset(Vector(0, 0, 64 * amount))
|
||||
v:SetViewOffsetDucked(Vector(0, 0, 28 * amount))
|
||||
|
||||
v.sam_scaled = true
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "scale", {
|
||||
A = ply, T = targets, V = amount
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
hook.Add("PlayerSpawn", "SAM.Scale", function(ply)
|
||||
if ply.sam_scaled then
|
||||
ply.sam_scaled = nil
|
||||
ply:SetViewOffset(Vector(0, 0, 64))
|
||||
ply:SetViewOffsetDucked(Vector(0, 0, 28))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
sam.command.new("freezeprops")
|
||||
:SetPermission("freezeprops", "admin")
|
||||
:Help("freezeprops_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
for _, prop in ipairs(ents.FindByClass("prop_physics")) do
|
||||
local physics_obj = prop:GetPhysicsObject()
|
||||
if IsValid(physics_obj) then
|
||||
physics_obj:EnableMotion(false)
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "freezeprops", {
|
||||
A = ply
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
224
addons/sam/lua/sam/modules/murder.lua
Normal file
224
addons/sam/lua/sam/modules/murder.lua
Normal file
@@ -0,0 +1,224 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local add = not GAMEMODE and hook.Add or function(_, _, fn)
|
||||
fn()
|
||||
end
|
||||
|
||||
-- Thanks to https://github.com/boxama/addons/blob/master/addons/ULX_Murder/lua/ulx/modules/sh/murder.lua
|
||||
add("PostGamemodeLoaded", "SAM.Murder", function()
|
||||
if GAMEMODE.Author ~= "MechanicalMind" then return end
|
||||
if not isstring(GAMEMODE.Version) or GAMEMODE.Version < "28" then return end
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
command.set_category("Murder")
|
||||
|
||||
local autoslain_players = {}
|
||||
|
||||
command.new("slaynr")
|
||||
:SetPermission("slaynr", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("number", {hint = "rounds", optional = true, default = 1, min = 1, max = 100, round = true})
|
||||
|
||||
:Help("Slays the target(s) at the beggining of the next round.")
|
||||
|
||||
:OnExecute(function(ply, targets, rounds)
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
v.MurdererChance = 0
|
||||
|
||||
if not v:IsBot() then
|
||||
autoslain_players[v:AccountID()] = rounds
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "{A} set {T} to be autoslain for {V} round(s)", {
|
||||
A = ply, T = targets, V = rounds
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unslaynr")
|
||||
:SetPermission("unslaynr", "admin")
|
||||
|
||||
:AddArg("player")
|
||||
|
||||
:Help("Remove target(s) autoslays.")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
v.MurdererChance = 1
|
||||
|
||||
if not v:IsBot() then
|
||||
autoslain_players[v:AccountID()] = nil
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "Removed all autoslays for {T} ", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
hook.Add("OnStartRound", "SAM.Murder", function()
|
||||
timer.Simple(3, function()
|
||||
local players = team.GetPlayers(2)
|
||||
local targets = {admin = sam.console}
|
||||
for i = 1, #players do
|
||||
local v = players[i]
|
||||
if not v:IsBot() then continue end
|
||||
|
||||
local slays = autoslain_players[v:AccountID()]
|
||||
if not slays then continue end
|
||||
|
||||
v:Kill()
|
||||
|
||||
slays = slays - 1
|
||||
|
||||
targets[1] = v
|
||||
sam.player.send_message(nil, "{A} autoslayed {T}, autoslays left: {V}.", {
|
||||
A = sam.console, T = targets, V = slays
|
||||
})
|
||||
|
||||
autoslain_players[v:AccountID()] = slays > 0 and slays or nil
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("PlayerInitialSpawn", "SAM.Murder", function(ply)
|
||||
if autoslain_players[ply:AccountID()] then
|
||||
ply.MurdererChance = 0
|
||||
end
|
||||
end)
|
||||
|
||||
command.new("respawn")
|
||||
:SetPermission("respawn", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
|
||||
:Help("Respawn a target.")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
local target = targets[1]
|
||||
|
||||
if target:Team() ~= 2 then
|
||||
return ply:sam_add_text("You cannot respawn a spectator!")
|
||||
end
|
||||
|
||||
target:Spectate(OBS_MODE_NONE)
|
||||
target:Spawn()
|
||||
|
||||
sam.player.send_message(nil, "respawn", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
local get_admins = function()
|
||||
local admins = {}
|
||||
|
||||
local players = player.GetHumans()
|
||||
for i = 1, #players do
|
||||
local v = players[i]
|
||||
if v:IsAdmin() then
|
||||
table.insert(admins, v)
|
||||
end
|
||||
end
|
||||
|
||||
return admins
|
||||
end
|
||||
|
||||
command.new("givemagnum")
|
||||
:SetPermission("givemagnum", "superadmin")
|
||||
|
||||
:AddArg("player", {single_target = true, optional = true})
|
||||
|
||||
:Help("Give the target a magnum.")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
local target = targets[1]
|
||||
|
||||
if target:Team() ~= 2 then
|
||||
return ply:sam_add_text("You cannot give spectator a magnum!")
|
||||
end
|
||||
|
||||
target:Give("weapon_mu_magnum")
|
||||
|
||||
sam.player.send_message(get_admins(), "{A} gave {T} a {V}", {
|
||||
A = ply, T = targets, V = "magnum"
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("giveknife")
|
||||
:SetPermission("giveknife", "superadmin")
|
||||
|
||||
:AddArg("player", {single_target = true, optional = true})
|
||||
|
||||
:Help("Give the target a knife.")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
local target = targets[1]
|
||||
|
||||
if target:Team() ~= 2 then
|
||||
return ply:sam_add_text("You cannot give spectator a knife!")
|
||||
end
|
||||
|
||||
target:Give("weapon_mu_knife")
|
||||
|
||||
sam.player.send_message(get_admins(), "{A} gave {T} a {V}", {
|
||||
A = ply, T = targets, V = "knife"
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("forcemurderer")
|
||||
:SetPermission("forcemurderer", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true, optional = true})
|
||||
|
||||
:Help("Force the target to me a murderer next round.")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
GAMEMODE.ForceNextMurderer = targets[1]
|
||||
|
||||
sam.player.send_message(get_admins(), "{A} set {T} to be the Murderer next round!", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("getmurderers")
|
||||
:SetPermission("getmurderers", "admin")
|
||||
|
||||
:Help("Print all murderers in chat.")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
local murderers = {admin = ply}
|
||||
|
||||
local players = team.GetPlayers(2)
|
||||
for i = 1, #players do
|
||||
local v = players[i]
|
||||
if v:GetMurderer() then
|
||||
table.insert(murderers, v)
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.send_message(ply, "Murderers are: {T}", {
|
||||
T = murderers
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
end)
|
||||
136
addons/sam/lua/sam/modules/support_cami.lua
Normal file
136
addons/sam/lua/sam/modules/support_cami.lua
Normal file
@@ -0,0 +1,136 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local ranks_loaded
|
||||
if SERVER then
|
||||
ranks_loaded = sam.ranks.ranks_loaded()
|
||||
else
|
||||
ranks_loaded = sam.ranks.get_ranks() ~= nil
|
||||
end
|
||||
|
||||
do
|
||||
local load_ranks = function()
|
||||
for name, rank in pairs(sam.ranks.get_ranks()) do
|
||||
if not sam.ranks.is_default_rank(name) then
|
||||
CAMI.RegisterUsergroup({Name = name, Inherits = rank.inherit}, "SAM")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ranks_loaded then
|
||||
load_ranks()
|
||||
else
|
||||
hook.Add("SAM.LoadedRanks", "SAM.CAMI.LoadRanksToCAMI", load_ranks)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("SAM.AddedRank", "SAM.CAMI.AddedRank", function(name, rank)
|
||||
if not sam.ranks.is_default_rank(name) then
|
||||
CAMI.RegisterUsergroup({Name = name, Inherits = rank.inherit}, "SAM")
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("SAM.RemovedRank", "SAM.CAMI.RemovedRank", function(name)
|
||||
CAMI.UnregisterUsergroup(name, "SAM")
|
||||
end)
|
||||
|
||||
hook.Add("SAM.RankNameChanged", "SAM.CAMI.RankNameChanged", function(old, new)
|
||||
CAMI.UnregisterUsergroup(old, "SAM")
|
||||
CAMI.RegisterUsergroup({Name = new, Inherits = sam.ranks.get_rank(new).inherit}, "SAM")
|
||||
end)
|
||||
|
||||
hook.Add("SAM.ChangedPlayerRank", "SAM.CAMI.ChangedPlayerRank", function(ply, new_rank, old_rank)
|
||||
CAMI.SignalUserGroupChanged(ply, old_rank, new_rank, "SAM")
|
||||
end)
|
||||
|
||||
hook.Add("SAM.ChangedSteamIDRank", "SAM.CAMI.ChangedSteamIDRank", function(steamid, new_rank, old_rank)
|
||||
CAMI.SignalSteamIDUserGroupChanged(steamid, old_rank, new_rank, "SAM")
|
||||
end)
|
||||
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
if SERVER then
|
||||
do
|
||||
local on_user_group_registered = function(rank, source)
|
||||
if source ~= "SAM" then
|
||||
sam.ranks.add_rank(rank.Name, sam.ranks.is_rank(rank.Inherits) and rank.Inherits or "user")
|
||||
end
|
||||
end
|
||||
|
||||
local load_ranks = function()
|
||||
for _, rank in pairs(CAMI.GetUsergroups()) do
|
||||
on_user_group_registered(rank, "CAMI")
|
||||
end
|
||||
hook.Add("CAMI.OnUsergroupRegistered", "SAM.CAMI.OnUsergroupRegistered", on_user_group_registered)
|
||||
end
|
||||
|
||||
if ranks_loaded then
|
||||
load_ranks()
|
||||
else
|
||||
hook.Add("SAM.LoadedRanks", "SAM.CAMI.LoadRanksFromCAMI", load_ranks)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("CAMI.OnUsergroupUnregistered", "SAM.CAMI.OnUsergroupUnregistered", function(rank, source)
|
||||
if source ~= "SAM" then
|
||||
sam.ranks.remove_rank(rank.Name)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("CAMI.PlayerUsergroupChanged", "SAM.CAMI.PlayerUsergroupChanged", function(ply, _, new_rank, source)
|
||||
if ply and IsValid(ply) and source ~= "SAM" then
|
||||
sam.player.set_rank(ply, new_rank)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("CAMI.SteamIDUsergroupChanged", "SAM.CAMI.SteamIDUsergroupChanged", function(steamid, _, new_rank, source)
|
||||
if sam.is_steamid(steamid) and source ~= "SAM" then
|
||||
sam.player.set_rank_id(steamid, new_rank)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
do
|
||||
local on_privilege_registered = function(privilege)
|
||||
sam.permissions.add(privilege.Name, "CAMI", privilege.MinAccess)
|
||||
end
|
||||
|
||||
local load_privileges = function()
|
||||
for _, privilege in pairs(CAMI.GetPrivileges()) do
|
||||
on_privilege_registered(privilege)
|
||||
end
|
||||
hook.Add("CAMI.OnPrivilegeRegistered", "SAM.CAMI.OnPrivilegeRegistered", on_privilege_registered)
|
||||
end
|
||||
|
||||
if ranks_loaded then
|
||||
load_privileges()
|
||||
else
|
||||
hook.Add("SAM.LoadedRanks", "SAM.CAMI.LoadPrivileges", load_privileges)
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("CAMI.OnPrivilegeUnregistered", "SAM.CAMI.OnPrivilegeUnregistered", function(privilege)
|
||||
sam.permissions.remove(privilege.Name)
|
||||
end)
|
||||
|
||||
hook.Add("CAMI.PlayerHasAccess", "SAM.CAMI.PlayerHasAccess", function(ply, privilege, callback, target)
|
||||
if sam.type(ply) ~= "Player" then return end
|
||||
|
||||
local has_permission = ply:HasPermission(privilege)
|
||||
if sam.type(target) == "Player" then
|
||||
callback(has_permission and ply:CanTarget(target))
|
||||
else
|
||||
callback(has_permission)
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
61
addons/sam/lua/sam/modules/sv_family_sharing.lua
Normal file
61
addons/sam/lua/sam/modules/sv_family_sharing.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
--[[
|
||||
| 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 checks if a player joined your server using a lent account that is banned
|
||||
-- eg. player got banned so he decided to make an alt account and used https://store.steampowered.com/promotion/familysharing
|
||||
--
|
||||
|
||||
--
|
||||
-- Whitelisted players from checking if they have family sharing or not
|
||||
-- You can have steamid/steamid64 here
|
||||
--
|
||||
local Whitelisted_SteamIDs = {
|
||||
}
|
||||
|
||||
local BanMessage = "Bypassing a ban using an alt. (alt: %s)"
|
||||
|
||||
--
|
||||
-- Do you want to kick players using family shared accounts?
|
||||
--
|
||||
local BlockFamilySharing = false
|
||||
local BlockFamilySharingMessage = "This server blocked using shared accounts."
|
||||
|
||||
--
|
||||
--
|
||||
-- DO NOT TOUCH --
|
||||
--
|
||||
--
|
||||
|
||||
for k, v in pairs(Whitelisted_SteamIDs) do
|
||||
Whitelisted_SteamIDs[v] = true
|
||||
Whitelisted_SteamIDs[k] = nil
|
||||
end
|
||||
|
||||
hook.Add("SAM.AuthedPlayer", "CheckSteamFamily", function(ply)
|
||||
local ply_steamid = ply:SteamID()
|
||||
local ply_steamid64 = ply:SteamID64()
|
||||
if Whitelisted_SteamIDs[ply_steamid] or Whitelisted_SteamIDs[ply_steamid64] then return end
|
||||
|
||||
local lender = ply:OwnerSteamID64()
|
||||
|
||||
if (ply_steamid64 == lender) then return end
|
||||
|
||||
if BlockFamilySharing then
|
||||
ply:Kick(BlockFamilySharingMessage)
|
||||
else
|
||||
lender = util.SteamIDFrom64(lender)
|
||||
sam.player.is_banned(lender, function(banned)
|
||||
if banned then
|
||||
RunConsoleCommand("sam", "banid", ply_steamid, "0", BanMessage:format(lender))
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
198
addons/sam/lua/sam/modules/teleport.lua
Normal file
198
addons/sam/lua/sam/modules/teleport.lua
Normal file
@@ -0,0 +1,198 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("Teleport")
|
||||
|
||||
local find_empty_pos -- https://github.com/FPtje/DarkRP/blob/b147d6fa32799136665a9fd52d35c2fe87cf7f78/gamemode/modules/base/sv_util.lua#L149
|
||||
do
|
||||
local is_empty = function(vector, ignore)
|
||||
local point = util.PointContents(vector)
|
||||
local a = point ~= CONTENTS_SOLID
|
||||
and point ~= CONTENTS_MOVEABLE
|
||||
and point ~= CONTENTS_LADDER
|
||||
and point ~= CONTENTS_PLAYERCLIP
|
||||
and point ~= CONTENTS_MONSTERCLIP
|
||||
if not a then return false end
|
||||
|
||||
local ents_found = ents.FindInSphere(vector, 35)
|
||||
for i = 1, #ents_found do
|
||||
local v = ents_found[i]
|
||||
if (v:IsNPC() or v:IsPlayer() or v:GetClass() == "prop_physics" or v.NotEmptyPos) and v ~= ignore then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local distance, step, area = 600, 30, Vector(16, 16, 64)
|
||||
local north_vec, east_vec, up_vec = Vector(0, 0, 0), Vector(0, 0, 0), Vector(0, 0, 0)
|
||||
|
||||
find_empty_pos = function(pos, ignore)
|
||||
if is_empty(pos, ignore) and is_empty(pos + area, ignore) then
|
||||
return pos
|
||||
end
|
||||
|
||||
for j = step, distance, step do
|
||||
for i = -1, 1, 2 do
|
||||
local k = j * i
|
||||
|
||||
-- North/South
|
||||
north_vec.x = k
|
||||
if is_empty(pos + north_vec, ignore) and is_empty(pos + north_vec + area, ignore) then
|
||||
return pos + north_vec
|
||||
end
|
||||
|
||||
-- East/West
|
||||
east_vec.y = k
|
||||
if is_empty(pos + east_vec, ignore) and is_empty(pos + east_vec + area, ignore) then
|
||||
return pos + east_vec
|
||||
end
|
||||
|
||||
-- Up/Down
|
||||
up_vec.z = k
|
||||
if is_empty(pos + up_vec, ignore) and is_empty(pos + up_vec + area, ignore) then
|
||||
return pos + up_vec
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return pos
|
||||
end
|
||||
end
|
||||
|
||||
command.new("bring")
|
||||
:DisallowConsole()
|
||||
:SetPermission("bring", "admin")
|
||||
|
||||
:AddArg("player", {cant_target_self = true})
|
||||
|
||||
:Help("bring_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
if not ply:Alive() then
|
||||
return ply:sam_send_message("dead")
|
||||
end
|
||||
|
||||
if ply:InVehicle() then
|
||||
return ply:sam_send_message("leave_car")
|
||||
end
|
||||
|
||||
if ply:sam_get_exclusive(ply) then
|
||||
return ply:sam_send_message(ply:sam_get_exclusive(ply))
|
||||
end
|
||||
|
||||
local teleported = {admin = ply}
|
||||
local all = targets.input == "*"
|
||||
|
||||
for i = 1, #targets do
|
||||
local target = targets[i]
|
||||
|
||||
if target:sam_get_exclusive(ply) then
|
||||
if not all then
|
||||
ply:sam_send_message(target:sam_get_exclusive(ply))
|
||||
end
|
||||
continue
|
||||
end
|
||||
|
||||
if not target:Alive() then
|
||||
target:Spawn()
|
||||
end
|
||||
|
||||
target.sam_tele_pos, target.sam_tele_ang = target:GetPos(), target:EyeAngles()
|
||||
|
||||
target:ExitVehicle()
|
||||
target:SetVelocity(Vector(0, 0, 0))
|
||||
target:SetPos(find_empty_pos(ply:GetPos(), target))
|
||||
target:SetEyeAngles((ply:EyePos() - target:EyePos()):Angle())
|
||||
|
||||
table.insert(teleported, target)
|
||||
end
|
||||
|
||||
if #teleported > 0 then
|
||||
sam.player.send_message(nil, "bring", {
|
||||
A = ply, T = teleported
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("goto")
|
||||
:DisallowConsole()
|
||||
:SetPermission("goto", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true, allow_higher_target = true, cant_target_self = true})
|
||||
|
||||
:Help("goto_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
if ply:sam_get_exclusive(ply) then
|
||||
return ply:sam_send_message(ply:sam_get_exclusive(ply))
|
||||
end
|
||||
|
||||
if not ply:Alive() then
|
||||
ply:Spawn()
|
||||
end
|
||||
|
||||
local target = targets[1]
|
||||
ply.sam_tele_pos, ply.sam_tele_ang = ply:GetPos(), ply:EyeAngles()
|
||||
|
||||
ply:ExitVehicle()
|
||||
ply:SetVelocity(Vector(0, 0, 0))
|
||||
ply:SetPos(find_empty_pos(target:GetPos(), ply))
|
||||
ply:SetEyeAngles((target:EyePos() - ply:EyePos()):Angle())
|
||||
|
||||
sam.player.send_message(nil, "goto", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("return")
|
||||
:SetPermission("return", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true, optional = true})
|
||||
|
||||
:Help("return_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
local target = targets[1]
|
||||
|
||||
local last_pos, last_ang = target.sam_tele_pos, target.sam_tele_ang
|
||||
if not last_pos then
|
||||
return sam.player.send_message(ply, "no_location", {
|
||||
T = targets
|
||||
})
|
||||
end
|
||||
|
||||
if target:sam_get_exclusive(ply) then
|
||||
return ply:sam_send_message(target:sam_get_exclusive(ply))
|
||||
end
|
||||
|
||||
if not target:Alive() then
|
||||
return ply:sam_send_message(target:Name() .. " is dead!")
|
||||
end
|
||||
|
||||
target:ExitVehicle()
|
||||
target:SetVelocity(Vector(0, 0, 0))
|
||||
target:SetPos(last_pos)
|
||||
target:SetEyeAngles(last_ang)
|
||||
|
||||
target.sam_tele_pos, target.sam_tele_ang = nil, nil
|
||||
|
||||
sam.player.send_message(nil, "returned", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
102
addons/sam/lua/sam/modules/ttt.lua
Normal file
102
addons/sam/lua/sam/modules/ttt.lua
Normal file
@@ -0,0 +1,102 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local run = function(fn)
|
||||
if not GAMEMODE then
|
||||
timer.Simple(0, fn)
|
||||
else
|
||||
fn()
|
||||
end
|
||||
end
|
||||
|
||||
run(function()
|
||||
if engine.ActiveGamemode() ~= "terrortown" then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("TTT")
|
||||
|
||||
command.new("setslays")
|
||||
:SetPermission("setslays", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("number", {hint = "amount", optional = true, min = 1, default = 1, round = true})
|
||||
|
||||
:Help("setslays_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
targets[1]:sam_set_pdata("slays_amount", amount)
|
||||
|
||||
sam.player.send_message(nil, "setslays", {
|
||||
A = ply, T = targets, V = amount
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("removeslays")
|
||||
:SetPermission("removeslays", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
|
||||
:Help("removeslays_help")
|
||||
|
||||
:OnExecute(function(ply, targets, amount)
|
||||
local target = targets[1]
|
||||
target:sam_set_pdata("slays_amount", nil)
|
||||
target:SetForceSpec(false)
|
||||
|
||||
sam.player.send_message(nil, "removeslays", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
OldBeginRound = OldBeginRound or BeginRound
|
||||
function BeginRound(...)
|
||||
local players = player.GetAll()
|
||||
for i = 1, #players do
|
||||
local ply = players[i]
|
||||
|
||||
local slays = ply:sam_get_pdata("slays_amount")
|
||||
if not slays then continue end
|
||||
|
||||
if not ply:IsSpec() then
|
||||
ply:Kill()
|
||||
end
|
||||
|
||||
GAMEMODE:PlayerSpawnAsSpectator(ply)
|
||||
|
||||
ply:SetTeam(TEAM_SPEC)
|
||||
ply:SetForceSpec(true)
|
||||
ply:Spawn()
|
||||
|
||||
ply:SetRagdollSpec(false) -- dying will enable this, we don't want it here
|
||||
|
||||
slays = slays - 1
|
||||
|
||||
if slays == 0 then
|
||||
timer.Simple(0, function()
|
||||
ply:SetForceSpec(false)
|
||||
end)
|
||||
ply:sam_set_pdata("slays_amount", nil)
|
||||
else
|
||||
ply:sam_set_pdata("slays_amount", slays)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "setslays_slayed", {
|
||||
T = {ply}, V = slays
|
||||
})
|
||||
end
|
||||
|
||||
return OldBeginRound(...)
|
||||
end
|
||||
end)
|
||||
273
addons/sam/lua/sam/modules/user.lua
Normal file
273
addons/sam/lua/sam/modules/user.lua
Normal file
@@ -0,0 +1,273 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("User Management")
|
||||
|
||||
command.new("setrank")
|
||||
:Aliases("adduser", "changerank", "giverank")
|
||||
|
||||
:SetPermission("setrank")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("rank", {check = function(rank, ply)
|
||||
return ply:CanTargetRank(rank)
|
||||
end})
|
||||
:AddArg("length", {optional = true, default = 0})
|
||||
|
||||
:Help("setrank_help")
|
||||
|
||||
:OnExecute(function(ply, targets, rank, length)
|
||||
targets[1]:sam_set_rank(rank, length)
|
||||
|
||||
sam.player.send_message(nil, "setrank", {
|
||||
A = ply, T = targets, V = rank, V_2 = sam.format_length(length)
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("setrankid")
|
||||
:Aliases("adduserid", "changerankid", "giverankid")
|
||||
|
||||
:SetPermission("setrankid")
|
||||
|
||||
:AddArg("steamid")
|
||||
:AddArg("rank", {check = function(rank, ply)
|
||||
return ply:CanTargetRank(rank)
|
||||
end})
|
||||
:AddArg("length", {optional = true, default = 0})
|
||||
|
||||
:Help("setrankid_help")
|
||||
|
||||
:OnExecute(function(ply, promise, rank, length)
|
||||
local a_name = ply:Name()
|
||||
|
||||
promise:done(function(data)
|
||||
local steamid, target = data[1], data[2]
|
||||
if target then
|
||||
target:sam_set_rank(rank, length)
|
||||
|
||||
sam.player.send_message(nil, "setrank", {
|
||||
A = ply, T = {target, admin = ply}, V = rank, V_2 = sam.format_length(length)
|
||||
})
|
||||
else
|
||||
sam.player.set_rank_id(steamid, rank, length)
|
||||
|
||||
sam.player.send_message(nil, "setrank", {
|
||||
A = a_name, T = steamid, V = rank, V_2 = sam.format_length(length)
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("addrank")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("text", {hint = "rank name", check = function(rank)
|
||||
return not sam.ranks.is_rank(rank)
|
||||
end})
|
||||
:AddArg("rank", {hint = "inherit from"})
|
||||
:AddArg("number", {hint = "immunity", min = 2, max = 99, optional = true})
|
||||
:AddArg("length", {hint = "ban limit", optional = true})
|
||||
|
||||
:Help("addrank_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, inherit, immunity, ban_limit)
|
||||
sam.ranks.add_rank(rank, inherit, immunity, ban_limit)
|
||||
|
||||
sam.player.send_message(nil, "addrank", {
|
||||
A = ply, V = rank
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("removerank")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank", {check = function(rank)
|
||||
return not sam.ranks.is_default_rank(rank)
|
||||
end})
|
||||
|
||||
:Help("removerank_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank)
|
||||
sam.ranks.remove_rank(rank)
|
||||
|
||||
sam.player.send_message(nil, "removerank", {
|
||||
A = ply, V = rank
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("renamerank")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank", {check = function(rank)
|
||||
return not sam.ranks.is_default_rank(rank)
|
||||
end})
|
||||
:AddArg("text", {hint = "new name", check = function(rank)
|
||||
return not sam.ranks.is_rank(rank)
|
||||
end})
|
||||
|
||||
:Help("renamerank_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, new_name)
|
||||
sam.ranks.rename_rank(rank, new_name)
|
||||
|
||||
sam.player.send_message(nil, "renamerank", {
|
||||
A = ply, T = rank, V = new_name
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("changeinherit")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank", {check = function(rank)
|
||||
return rank ~= "user" and rank ~= "superadmin"
|
||||
end})
|
||||
:AddArg("rank", {hint = "inherits from"})
|
||||
|
||||
:Help("changeinherit_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, inherit)
|
||||
if rank == inherit then return end
|
||||
|
||||
sam.ranks.change_inherit(rank, inherit)
|
||||
|
||||
sam.player.send_message(nil, "changeinherit", {
|
||||
A = ply, T = rank, V = inherit
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("changerankimmunity")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank", {check = function(rank)
|
||||
return rank ~= "user" and rank ~= "superadmin"
|
||||
end})
|
||||
:AddArg("number", {hint = "new immunity", min = 2, max = 99})
|
||||
|
||||
:Help("changerankimmunity_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, new_immunity)
|
||||
sam.ranks.change_immunity(rank, new_immunity)
|
||||
|
||||
sam.player.send_message(nil, "rank_immunity", {
|
||||
A = ply, T = rank, V = new_immunity
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("changerankbanlimit")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank", {check = function(rank)
|
||||
return rank ~= "superadmin"
|
||||
end})
|
||||
:AddArg("length")
|
||||
|
||||
:Help("changerankbanlimit_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, new_limit)
|
||||
sam.ranks.change_ban_limit(rank, new_limit)
|
||||
|
||||
sam.player.send_message(nil, "rank_ban_limit", {
|
||||
A = ply, T = rank, V = sam.format_length(new_limit)
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("givepermission")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank")
|
||||
:AddArg("text", {hint = "permission"})
|
||||
|
||||
:Help("givepermission_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, permission)
|
||||
if rank == "superadmin" then
|
||||
return ply:sam_send_message("super_admin_access")
|
||||
end
|
||||
|
||||
sam.ranks.give_permission(rank, permission)
|
||||
|
||||
sam.player.send_message(nil, "giveaccess", {
|
||||
A = ply, V = permission, T = rank
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("takepermission")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank")
|
||||
:AddArg("text", {hint = "permission"})
|
||||
|
||||
:Help("takepermission_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, permission)
|
||||
if rank == "superadmin" then
|
||||
return ply:sam_send_message("super_admin_access")
|
||||
end
|
||||
|
||||
sam.ranks.take_permission(rank, permission)
|
||||
|
||||
sam.player.send_message(nil, "takeaccess", {
|
||||
A = ply, V = permission, T = rank
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("changeranklimit")
|
||||
:SetPermission("manage_ranks")
|
||||
|
||||
:AddArg("rank")
|
||||
:AddArg("text", {hint = "limit"})
|
||||
:AddArg("number", {hint = "value"})
|
||||
|
||||
:Help("changeranklimit_help")
|
||||
|
||||
:MenuHide()
|
||||
|
||||
:OnExecute(function(ply, rank, limit, value)
|
||||
if rank == "superadmin" then
|
||||
return ply:sam_send_message("super_admin_access")
|
||||
end
|
||||
|
||||
sam.ranks.set_limit(rank, limit, value)
|
||||
|
||||
sam.player.send_message(nil, "changeranklimit", {
|
||||
A = ply, T = rank, V = limit, V_2 = value
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
579
addons/sam/lua/sam/modules/util.lua
Normal file
579
addons/sam/lua/sam/modules/util.lua
Normal file
@@ -0,0 +1,579 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, command, language = sam, sam.command, sam.language
|
||||
|
||||
command.set_category("Utility")
|
||||
|
||||
command.new("map")
|
||||
:SetPermission("map", "admin")
|
||||
|
||||
:AddArg("map")
|
||||
:AddArg("text", {hint = "gamemode", optional = true, check = sam.is_valid_gamemode})
|
||||
|
||||
:Help("map_help")
|
||||
|
||||
:OnExecute(function(ply, map, gamemode)
|
||||
if not gamemode then
|
||||
sam.player.send_message(nil, "map_change", {
|
||||
A = ply, V = map
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "map_change2", {
|
||||
A = ply, V = map, V_2 = gamemode
|
||||
})
|
||||
RunConsoleCommand("gamemode", gamemode)
|
||||
end
|
||||
|
||||
if #player.GetHumans() == 0 then
|
||||
RunConsoleCommand("changelevel", map)
|
||||
else
|
||||
timer.Create("SAM.Command.Map", 10, 1, function()
|
||||
RunConsoleCommand("changelevel", map)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("maprestart")
|
||||
:SetPermission("maprestart", "admin")
|
||||
|
||||
:Help("map_restart_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
if #player.GetHumans() == 0 then
|
||||
RunConsoleCommand("changelevel", game.GetMap())
|
||||
else
|
||||
timer.Create("SAM.Command.MapRestart", 10, 1, function()
|
||||
RunConsoleCommand("changelevel", game.GetMap())
|
||||
end)
|
||||
|
||||
sam.player.send_message(nil, "map_restart", {
|
||||
A = ply
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("mapreset")
|
||||
:SetPermission("mapreset", "admin")
|
||||
|
||||
:Help("mapreset_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
game.CleanUpMap()
|
||||
|
||||
sam.player.send_message(nil, "mapreset", {
|
||||
A = ply
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("kick")
|
||||
:SetPermission("kick", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("text", {hint = "reason", optional = true, default = sam.language.get("default_reason")})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("kick_help")
|
||||
|
||||
:OnExecute(function(ply, targets, reason)
|
||||
local target = targets[1]
|
||||
target:Kick(reason)
|
||||
|
||||
sam.player.send_message(nil, "kick", {
|
||||
A = ply, T = target:Name(), V = reason
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("ban")
|
||||
:SetPermission("ban", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("length", {optional = true, default = 0})
|
||||
:AddArg("text", {hint = "reason", optional = true, default = sam.language.get("default_reason")})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("ban_help")
|
||||
|
||||
:OnExecute(function(ply, targets, length, reason)
|
||||
local target = targets[1]
|
||||
if ply:GetBanLimit() ~= 0 then
|
||||
if length == 0 then
|
||||
length = ply:GetBanLimit()
|
||||
else
|
||||
length = math.Clamp(length, 1, ply:GetBanLimit())
|
||||
end
|
||||
end
|
||||
target:sam_ban(length, reason, ply:SteamID())
|
||||
|
||||
sam.player.send_message(nil, "ban", {
|
||||
A = ply, T = target:Name(), V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("banid")
|
||||
:SetPermission("banid", "admin")
|
||||
|
||||
:AddArg("steamid")
|
||||
:AddArg("length", {optional = true, default = 0})
|
||||
:AddArg("text", {hint = "reason", optional = true, default = sam.language.get("default_reason")})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("banid_help")
|
||||
|
||||
:OnExecute(function(ply, promise, length, reason)
|
||||
local a_steamid, a_name, a_ban_limit = ply:SteamID(), ply:Name(), ply:GetBanLimit()
|
||||
|
||||
promise:done(function(data)
|
||||
local steamid, target = data[1], data[2]
|
||||
|
||||
if a_ban_limit ~= 0 then
|
||||
if length == 0 then
|
||||
length = a_ban_limit
|
||||
else
|
||||
length = math.Clamp(length, 1, a_ban_limit)
|
||||
end
|
||||
end
|
||||
|
||||
if target then
|
||||
target:sam_ban(length, reason, a_steamid)
|
||||
|
||||
sam.player.send_message(nil, "ban", {
|
||||
A = a_name, T = target:Name(), V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
else
|
||||
sam.player.ban_id(steamid, length, reason, a_steamid)
|
||||
|
||||
sam.player.send_message(nil, "banid", {
|
||||
A = a_name, T = steamid, V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unban")
|
||||
:SetPermission("unban", "admin")
|
||||
|
||||
:AddArg("steamid", {allow_higher_target = true})
|
||||
|
||||
:Help("unban_help")
|
||||
|
||||
:OnExecute(function(ply, steamid, reason)
|
||||
sam.player.unban(steamid, ply:SteamID())
|
||||
|
||||
sam.player.send_message(nil, "unban", {
|
||||
A = ply, T = steamid
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
command.new("noclip")
|
||||
:SetPermission("noclip", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("noclip_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
local id
|
||||
for i = 1, #targets do
|
||||
local v = targets[i]
|
||||
v:SetMoveType(v:GetMoveType() == MOVETYPE_WALK and MOVETYPE_NOCLIP or MOVETYPE_WALK)
|
||||
if v == ply then
|
||||
id = i
|
||||
end
|
||||
end
|
||||
|
||||
if id then
|
||||
table.remove(targets, id)
|
||||
if #targets == 0 then return end
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "noclip", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
sam.permissions.add("can_noclip", nil, "admin")
|
||||
|
||||
hook.Add("PlayerNoClip", "SAM.CanNoClip", function(ply)
|
||||
if ply:HasPermission("can_noclip") then
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
do
|
||||
local config = sam.config
|
||||
|
||||
sam.permissions.add("can_physgun_players", nil, "admin")
|
||||
|
||||
if CLIENT then
|
||||
local add_setting = function(body, title, key)
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel(title)
|
||||
|
||||
local enable = setting:Add("SAM.ToggleButton")
|
||||
enable:SetConfig(key, true)
|
||||
|
||||
return setting
|
||||
end
|
||||
|
||||
config.add_menu_setting("Physgun", function(body)
|
||||
local setting_body
|
||||
|
||||
do
|
||||
local p = add_setting(body, "Physgun (Enable/Disable all physgun features except picking up players)", "Physgun.Enabled")
|
||||
p:DockMargin(8, 6, 8, 0)
|
||||
end
|
||||
|
||||
setting_body = body:Add("Panel")
|
||||
setting_body:Dock(TOP)
|
||||
setting_body:DockMargin(8, 6, 8, 0)
|
||||
setting_body:DockPadding(8, 0, 8, 0)
|
||||
|
||||
add_setting(setting_body, "No fall damage on drop", "Physgun.NoFallDamageOnDrop")
|
||||
add_setting(setting_body, "Right click to freeze players", "Physgun.RightClickToFreeze")
|
||||
add_setting(setting_body, "Reset Velocity to fix some issues when players fall", "Physgun.ResetVelocity")
|
||||
|
||||
function setting_body:PerformLayout()
|
||||
setting_body:SizeToChildren(false, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local freeze_player = function(ply)
|
||||
if SERVER then
|
||||
ply:Lock()
|
||||
end
|
||||
ply:SetMoveType(MOVETYPE_NONE)
|
||||
ply:SetCollisionGroup(COLLISION_GROUP_WORLD)
|
||||
end
|
||||
|
||||
sam.hook_first("PhysgunPickup", "SAM.CanPhysgunPlayer", function(ply, target)
|
||||
if sam.type(target) == "Player" and ply:HasPermission("can_physgun_players") and ply:CanTarget(target) then
|
||||
freeze_player(target)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
local load_phygun_settings = function()
|
||||
hook.Remove("PhysgunDrop", "SAM.PhysgunDrop")
|
||||
hook.Remove("OnPlayerHitGround", "SAM.PhysgunDropOnPlayerHitGround")
|
||||
|
||||
if config.get("Physgun.Enabled", true) == false then
|
||||
return
|
||||
end
|
||||
|
||||
local right_click_to_freeze = config.get("Physgun.RightClickToFreeze", true)
|
||||
local reset_velocity = config.get("Physgun.ResetVelocity", true)
|
||||
hook.Add("PhysgunDrop", "SAM.PhysgunDrop", function(ply, target)
|
||||
if sam.type(target) ~= "Player" then return end
|
||||
|
||||
if right_click_to_freeze and ply:KeyPressed(IN_ATTACK2) then
|
||||
freeze_player(target)
|
||||
|
||||
if SERVER then
|
||||
target:sam_set_nwvar("frozen", true)
|
||||
target:sam_set_exclusive("frozen")
|
||||
end
|
||||
else
|
||||
if reset_velocity then
|
||||
target:SetLocalVelocity(Vector(0, 0, 0))
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
target:UnLock()
|
||||
target:sam_set_nwvar("frozen", false)
|
||||
target:sam_set_exclusive(nil)
|
||||
|
||||
if target.sam_has_god_mode then
|
||||
target:GodEnable()
|
||||
end
|
||||
|
||||
target.sam_physgun_drop_was_frozen = not target:IsOnGround()
|
||||
end
|
||||
|
||||
target:SetMoveType(MOVETYPE_WALK)
|
||||
target:SetCollisionGroup(COLLISION_GROUP_PLAYER)
|
||||
end
|
||||
end)
|
||||
|
||||
if config.get("Physgun.NoFallDamageOnDrop", true) then
|
||||
hook.Add("OnPlayerHitGround", "SAM.PhysgunDropOnPlayerHitGround", function(ply)
|
||||
if ply.sam_physgun_drop_was_frozen then
|
||||
ply.sam_physgun_drop_was_frozen = false
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
config.hook({"Physgun.Enabled", "Physgun.RightClickToFreeze", "Physgun.ResetVelocity", "Physgun.NoFallDamageOnDrop"}, load_phygun_settings)
|
||||
end
|
||||
|
||||
do
|
||||
command.new("cleardecals")
|
||||
:SetPermission("cleardecals", "admin")
|
||||
:Help("cleardecals_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
sam.netstream.Start(nil, "cleardecals")
|
||||
|
||||
sam.player.send_message(nil, "cleardecals", {
|
||||
A = ply
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
if CLIENT then
|
||||
sam.netstream.Hook("cleardecals", function()
|
||||
game.RemoveRagdolls()
|
||||
RunConsoleCommand("r_cleardecals")
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
command.new("stopsound")
|
||||
:SetPermission("stopsound", "admin")
|
||||
:Help("stopsound_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
sam.netstream.Start(nil, "stopsound")
|
||||
|
||||
sam.player.send_message(nil, "stopsound", {
|
||||
A = ply
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
if CLIENT then
|
||||
sam.netstream.Hook("stopsound", function()
|
||||
RunConsoleCommand("stopsound")
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
command.new("exit")
|
||||
:SetPermission("exit_vehicle", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
|
||||
:Help("exit_vehicle_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
local target = targets[1]
|
||||
|
||||
if not target:InVehicle() then
|
||||
if ply == target then
|
||||
return ply:sam_send_message("not_in_vehicle")
|
||||
else
|
||||
return ply:sam_send_message("not_in_vehicle2", {
|
||||
S = target:Name()
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
target:ExitVehicle()
|
||||
|
||||
sam.player.send_message(nil, "exit_vehicle", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("time")
|
||||
:SetPermission("time", "user")
|
||||
|
||||
:AddArg("player", {single_target = true, optional = true})
|
||||
|
||||
:Help("time_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
if ply == targets[1] then
|
||||
sam.player.send_message(ply, "time_your", {
|
||||
V = sam.reverse_parse_length(targets[1]:sam_get_play_time() / 60)
|
||||
})
|
||||
else
|
||||
sam.player.send_message(ply, "time_player", {
|
||||
T = targets, V = sam.reverse_parse_length(targets[1]:sam_get_play_time() / 60)
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("admin")
|
||||
:SetPermission("admin_mode", "admin")
|
||||
|
||||
:Help("admin_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
ply:sam_cloak()
|
||||
ply:GodEnable()
|
||||
ply:SetMoveType(MOVETYPE_NOCLIP)
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unadmin")
|
||||
:SetPermission("admin_mode", "admin")
|
||||
|
||||
:Help("unadmin_help")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
ply:sam_uncloak()
|
||||
ply:GodDisable()
|
||||
ply:SetMoveType(MOVETYPE_WALK)
|
||||
end)
|
||||
:End()
|
||||
|
||||
do
|
||||
command.new("buddha")
|
||||
:SetPermission("buddha", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("buddha_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i].sam_buddha = true
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "buddha", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("unbuddha")
|
||||
:SetPermission("buddha", "admin")
|
||||
|
||||
:AddArg("player", {optional = true})
|
||||
|
||||
:Help("unbuddha_help")
|
||||
|
||||
:OnExecute(function(ply, targets)
|
||||
for i = 1, #targets do
|
||||
targets[i].sam_buddha = nil
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "unbuddha", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
if SERVER then
|
||||
hook.Add("EntityTakeDamage", "SAM.BuddhaMode", function(ply, info)
|
||||
if ply.sam_buddha and ply:Health() - info:GetDamage() <= 0 then
|
||||
ply:SetHealth(1)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
command.new("give")
|
||||
:SetPermission("give", "superadmin")
|
||||
|
||||
:AddArg("player")
|
||||
:AddArg("text", {hint = "weapon/entity"})
|
||||
|
||||
:Help("give_help")
|
||||
|
||||
:OnExecute(function(ply, targets, weapon)
|
||||
for i = 1, #targets do
|
||||
targets[i]:Give(weapon)
|
||||
end
|
||||
|
||||
sam.player.send_message(nil, "give", {
|
||||
A = ply, T = targets, V = weapon
|
||||
})
|
||||
end)
|
||||
:End()
|
||||
|
||||
-- do
|
||||
-- if CLIENT then
|
||||
-- sam.netstream.Hook("GetFriends", function()
|
||||
-- local friends = {}
|
||||
-- local humans = player.GetHumans()
|
||||
-- for i = 1, #humans do
|
||||
-- local human = humans[i]
|
||||
-- if human:GetFriendStatus() == "friend" then
|
||||
-- table.insert(friends, human)
|
||||
-- end
|
||||
-- end
|
||||
-- netstream.Start("GetFriends", friends)
|
||||
-- end)
|
||||
-- else
|
||||
-- hook.Add("SAM.AuthedPlayer", "GetPlayerFriends", function(ply)
|
||||
-- timer.Simple(0, function()
|
||||
-- ply.sam_requesting_friends = true
|
||||
-- netstream.Start(ply, "GetFriends")
|
||||
-- end)
|
||||
-- end)
|
||||
|
||||
-- local invalid_friends = function(ply, friends, new_list)
|
||||
-- if not sam.istable(friends) then return true end
|
||||
|
||||
-- local count = #friends
|
||||
-- local max_players = game.MaxPlayers()
|
||||
-- for k, friend in pairs(friends) do
|
||||
-- if not sam.isnumber(k) then return true end
|
||||
-- if not sam.isentity(friend) then return true end
|
||||
-- if k > max_players then return true end
|
||||
-- if k > count then return true end
|
||||
|
||||
-- if IsValid(friend) then
|
||||
-- table.insert(new_list, friend)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- sam.netstream.Hook("GetFriends", function(ply, friends)
|
||||
-- local new_list = {}
|
||||
-- if invalid_friends(ply, friends, new_list) then
|
||||
-- ply.sam_friends_invalid = true
|
||||
-- return
|
||||
-- end
|
||||
-- ply.sam_friends = new_list
|
||||
-- end, function()
|
||||
-- return ply.sam_requesting_friends
|
||||
-- end)
|
||||
-- end
|
||||
|
||||
-- command.new("friends")
|
||||
-- :SetPermission("friends", "superadmin")
|
||||
|
||||
-- :AddArg("player", {single_target = true})
|
||||
|
||||
-- :Help(language.get("friends_help"))
|
||||
|
||||
-- :OnExecute(function(ply, targets)
|
||||
-- local target = targets[1]
|
||||
-- target.sam_friends_requests = target.sam_friends_requests or {}
|
||||
-- target.sam_friends_requests[ply] = true
|
||||
-- end)
|
||||
-- :End()
|
||||
-- end
|
||||
44
addons/sam/lua/sam/modules/utime.lua
Normal file
44
addons/sam/lua/sam/modules/utime.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local PLAYER = FindMetaTable("Player")
|
||||
|
||||
function PLAYER:GetUTime()
|
||||
return self:sam_get_nwvar("TotalUTime")
|
||||
end
|
||||
|
||||
function PLAYER:SetUTime(time)
|
||||
self:sam_set_nwvar("TotalUTime", time)
|
||||
end
|
||||
|
||||
function PLAYER:GetUTimeStart()
|
||||
return self:sam_get_nwvar("UTimeStart")
|
||||
end
|
||||
|
||||
function PLAYER:SetUTimeStart(time)
|
||||
self:sam_set_nwvar("UTimeStart", time)
|
||||
end
|
||||
|
||||
function PLAYER:GetUTimeSessionTime()
|
||||
return CurTime() - self:GetUTimeStart()
|
||||
end
|
||||
|
||||
function PLAYER:GetUTimeTotalTime()
|
||||
return self:GetUTime() + CurTime() - self:GetUTimeStart()
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
hook.Add("SAM.AuthedPlayer", "SAM.UTime", function(ply)
|
||||
ply:SetUTime(ply:sam_get_play_time())
|
||||
ply:SetUTimeStart(CurTime())
|
||||
end)
|
||||
end
|
||||
486
addons/sam/lua/sam/modules/vote.lua
Normal file
486
addons/sam/lua/sam/modules/vote.lua
Normal file
@@ -0,0 +1,486 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
-- DONT EVER TALK TO ME ABOUT THIS CODE
|
||||
|
||||
local sam, command = sam, sam.command
|
||||
|
||||
command.set_category("Voting")
|
||||
|
||||
local start_vote, end_vote
|
||||
if SERVER then
|
||||
local vote_on = false
|
||||
local options, players_voted
|
||||
|
||||
local shuffle = function(tbl) -- https://gist.github.com/Uradamus/10323382
|
||||
for i = #tbl, 2, -1 do
|
||||
local j = math.random(i)
|
||||
tbl[i], tbl[j] = tbl[j], tbl[i]
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
end_vote = function(ply, callback)
|
||||
if not vote_on then
|
||||
return ply:sam_add_text(color_white, "There is no vote to end.")
|
||||
end
|
||||
|
||||
vote_on = false
|
||||
|
||||
sam.set_global("Vote", nil)
|
||||
|
||||
if callback then
|
||||
local tbl = {}
|
||||
local total_count = 0
|
||||
|
||||
for i = 1, #options do
|
||||
local count = sam.get_global("Votings" .. i)
|
||||
total_count = total_count + count
|
||||
table.insert(tbl, {i, count})
|
||||
sam.set_global("Votings" .. i, nil)
|
||||
end
|
||||
|
||||
if total_count == 0 then
|
||||
return sam.player.add_text(nil, color_white, "The vote have been canceled because nobody voted.")
|
||||
end
|
||||
|
||||
table.sort(shuffle(tbl), function(a,b) return a[2] > b[2] end)
|
||||
|
||||
local v = tbl[1]
|
||||
local winner, count = v[1], v[2]
|
||||
|
||||
callback(winner, options[winner], count, total_count)
|
||||
else
|
||||
for i = 1, #options do
|
||||
sam.set_global("Votings" .. i, nil)
|
||||
end
|
||||
end
|
||||
|
||||
options, players_voted = nil, nil
|
||||
|
||||
timer.Remove("SAM.Vote")
|
||||
end
|
||||
|
||||
start_vote = function(ply, callback, title, ...)
|
||||
if vote_on then
|
||||
return ply:sam_add_text(color_white, "There is an active vote, wait for it to finish.")
|
||||
end
|
||||
|
||||
vote_on = true
|
||||
|
||||
options, players_voted = {}, {}
|
||||
|
||||
local args, n = {...}, select("#", ...)
|
||||
for i = 1, n do
|
||||
local v = args[i]
|
||||
if v then
|
||||
table.insert(options, v)
|
||||
end
|
||||
end
|
||||
|
||||
sam.set_global("Vote", {title, options, CurTime()})
|
||||
|
||||
for k in ipairs(options) do
|
||||
sam.set_global("Votings" .. k, 0)
|
||||
end
|
||||
|
||||
timer.Create("SAM.Vote", 25, 1, function()
|
||||
end_vote(ply, callback)
|
||||
end)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
sam.netstream.Hook("Vote", function(ply, index)
|
||||
if not sam.isnumber(index) or index > 5 then return end
|
||||
|
||||
local votings = sam.get_global("Votings" .. index)
|
||||
if not votings then return end
|
||||
|
||||
local old_index = players_voted[ply:AccountID()]
|
||||
if old_index == index then return end
|
||||
|
||||
if old_index then
|
||||
sam.set_global("Votings" .. old_index, sam.get_global("Votings" .. old_index) - 1)
|
||||
end
|
||||
|
||||
sam.set_global("Votings" .. index, votings + 1)
|
||||
|
||||
players_voted[ply:AccountID()] = index
|
||||
end)
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
local SUI = sam.SUI
|
||||
-- https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/extensions/client/player.lua
|
||||
|
||||
local VOTING_TITLE = SUI.CreateFont("VotingTitle", "Roboto Bold", 15)
|
||||
local VOTING_OPTION = SUI.CreateFont("VotingTitle", "Roboto Medium", 14)
|
||||
|
||||
local bind_translation = {}
|
||||
for i = 0, 9 do
|
||||
bind_translation["slot" .. i] = i
|
||||
end
|
||||
|
||||
local voting_frame
|
||||
|
||||
end_vote = function()
|
||||
if IsValid(voting_frame) then
|
||||
voting_frame:Remove()
|
||||
end
|
||||
hook.Remove("PlayerBindPress", "SAM.Voting")
|
||||
hook.Remove("SAM.ChangedGlobalVar", "SAM.VotingCount")
|
||||
end
|
||||
|
||||
hook.Add("SAM.ChangedGlobalVar", "Voting", function(key, value)
|
||||
if key ~= "Vote" then return end
|
||||
|
||||
if not value then
|
||||
end_vote()
|
||||
return
|
||||
end
|
||||
|
||||
local title, options, start_time = value[1], value[2], value[3]
|
||||
|
||||
sui.TDLib.Start()
|
||||
|
||||
voting_frame = vgui.Create("EditablePanel")
|
||||
voting_frame:SetSize(SUI.Scale(165), SUI.Scale(230))
|
||||
voting_frame:SetPos(5, ScrH() * 0.25)
|
||||
voting_frame:DockPadding(4, 4, 4, 4)
|
||||
voting_frame:Blur()
|
||||
:Background(Color(30, 30, 30, 240))
|
||||
|
||||
local voting_title = voting_frame:Add("SAM.Label")
|
||||
voting_title:Dock(TOP)
|
||||
voting_title:SetFont(VOTING_TITLE)
|
||||
voting_title:TextColor(Color(220, 220, 220))
|
||||
voting_title:SetText(title)
|
||||
voting_title:SetWrap(true)
|
||||
voting_title:SetAutoStretchVertical(true)
|
||||
|
||||
local line = voting_frame:Add("SAM.Label")
|
||||
line:Dock(TOP)
|
||||
line:TextColor(Color(220, 220, 220))
|
||||
line:SetText("-")
|
||||
|
||||
local options_added = {}
|
||||
for i, v in ipairs(options) do
|
||||
local option = voting_frame:Add("SAM.Label")
|
||||
option:Dock(TOP)
|
||||
option:SetFont(VOTING_OPTION)
|
||||
option:TextColor(Color(220, 220, 220), true)
|
||||
option:SetText(i .. ". " .. v .. " (0)")
|
||||
option:SetWrap(true)
|
||||
option:SetAutoStretchVertical(true)
|
||||
|
||||
options_added[i] = option
|
||||
end
|
||||
|
||||
function voting_frame:Think() -- fucking gmod
|
||||
self:SizeToChildren(false, true)
|
||||
|
||||
local time_left = math.floor(25 - (CurTime() - start_time))
|
||||
if time_left <= 0 then
|
||||
end_vote()
|
||||
voting_frame.Think = nil
|
||||
return
|
||||
end
|
||||
|
||||
voting_title:SetText(title .. " (" .. time_left .. ")")
|
||||
end
|
||||
|
||||
line = voting_frame:Add("SAM.Label")
|
||||
line:Dock(TOP)
|
||||
line:TextColor(Color(220, 220, 220))
|
||||
line:SetText("-")
|
||||
|
||||
local option = voting_frame:Add("SAM.Label")
|
||||
option:Dock(TOP)
|
||||
option:SetFont(VOTING_OPTION)
|
||||
option:TextColor(Color(220, 220, 220), true)
|
||||
option:SetText("0. Close")
|
||||
option:SetWrap(true)
|
||||
option:SetAutoStretchVertical(true)
|
||||
|
||||
sui.TDLib.End()
|
||||
|
||||
local current_index
|
||||
hook.Add("PlayerBindPress", "SAM.Voting", function(_, bind, down)
|
||||
if not down then return end
|
||||
|
||||
local index = bind_translation[bind]
|
||||
if not index then return end
|
||||
|
||||
if index == 0 then
|
||||
end_vote()
|
||||
return true
|
||||
end
|
||||
|
||||
if not options[index] then return true end
|
||||
|
||||
if current_index then
|
||||
options_added[current_index]:TextColor(Color(220, 220, 220), true)
|
||||
end
|
||||
|
||||
options_added[index]:TextColor(Color(65, 185, 255), true)
|
||||
current_index = index
|
||||
|
||||
sam.netstream.Start("Vote", index)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
hook.Add("SAM.ChangedGlobalVar", "SAM.VotingCount", function(key2, count)
|
||||
if key2:sub(1, 7) ~= "Votings" then return end
|
||||
if not count then return end
|
||||
|
||||
local index = tonumber(key2:sub(8))
|
||||
options_added[index]:SetText(index .. ". " .. options[index] .. " (" .. count .. ")")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
local vote_check = function(str)
|
||||
return str:match("%S") ~= nil
|
||||
end
|
||||
|
||||
command.new("vote")
|
||||
:SetPermission("vote", "admin")
|
||||
|
||||
:AddArg("text", {hint = "title", check = vote_check})
|
||||
:AddArg("text", {hint = "option", check = vote_check})
|
||||
:AddArg("text", {hint = "option", check = vote_check})
|
||||
:AddArg("text", {hint = "option", optional = true, check = vote_check})
|
||||
:AddArg("text", {hint = "option", optional = true, check = vote_check})
|
||||
:AddArg("text", {hint = "option", optional = true, check = vote_check})
|
||||
|
||||
:Help("Start a vote!")
|
||||
|
||||
:OnExecute(function(ply, title, ...)
|
||||
local callback = function(_, option, count, total_count)
|
||||
sam.player.send_message(nil, "Vote {V} for {V_2} has been passed. ({V_3}/{V_4})", {
|
||||
V = title, V_2 = option, V_3 = count, V_4 = total_count
|
||||
})
|
||||
end
|
||||
|
||||
if start_vote(ply, callback, title, ...) then
|
||||
sam.player.send_message(nil, "{A} started a vote with title {V}.", {
|
||||
A = ply, V = title
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("endvote")
|
||||
:SetPermission("endvote", "admin")
|
||||
|
||||
:Help("End current vote.")
|
||||
|
||||
:OnExecute(function(ply)
|
||||
end_vote(ply)
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("votekick")
|
||||
:SetPermission("votekick", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("text", {hint = "reason", optional = true})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("Start a vote to kick a player.")
|
||||
|
||||
:OnExecute(function(ply, targets, reason)
|
||||
local target = targets[1]
|
||||
local target_name = target:Name()
|
||||
|
||||
local callback = function(index, option, count, total_count)
|
||||
if not IsValid(ply) then
|
||||
sam.player.send_message(nil, "Vote was canceled because {T} left.", {
|
||||
T = target_name
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if index == 1 then
|
||||
target:Kick("Vote was successful (" .. (reason or "none") .. ")")
|
||||
|
||||
sam.player.send_message(nil, "Vote was successful, {T} has been kicked. ({V})", {
|
||||
T = targets, V = reason
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "Vote was unsuccessful, {T} won't be kicked.", {
|
||||
T = target_name
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local title = "Kick " .. target_name .. "?"
|
||||
if reason then
|
||||
title = title .. " (" .. reason .. ")"
|
||||
end
|
||||
|
||||
if start_vote(ply, callback, title, "Yes", "No") then
|
||||
if reason then
|
||||
sam.player.send_message(nil, "{A} started a votekick against {T} ({V})", {
|
||||
A = ply, T = targets, V = reason
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "{A} started a votekick against {T}", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("voteban")
|
||||
:SetPermission("voteban", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("length", {optional = true, default = 60, min = 30, max = 120})
|
||||
:AddArg("text", {hint = "reason", optional = true})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("Start a vote to ban a player.")
|
||||
|
||||
:OnExecute(function(ply, targets, length, reason)
|
||||
local target = targets[1]
|
||||
local target_steamid, target_name = target:SteamID(), target:Name()
|
||||
local ply_steamid = ply:SteamID()
|
||||
|
||||
local callback = function(index, option, count, total_count)
|
||||
if index == 1 then
|
||||
sam.player.ban_id(target_steamid, length, "Vote was successful (" .. (reason or "none") .. ")", ply_steamid)
|
||||
|
||||
sam.player.send_message(nil, "Vote was successful, {T} has been banned. ({V})", {
|
||||
T = target_name, V = reason
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "Vote was unsuccessful, {T} won't be banned.", {
|
||||
T = target_name
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local title = "Ban " .. target_name .. "?"
|
||||
if reason then
|
||||
title = title .. " (" .. reason .. ")"
|
||||
end
|
||||
|
||||
if start_vote(ply, callback, title, "Yes", "No") then
|
||||
if reason then
|
||||
sam.player.send_message(nil, "{A} started a voteban against {T} for {V} ({V_2})", {
|
||||
A = ply, T = targets, V = sam.format_length(length), V_2 = reason
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "{A} started a voteban against {T} for {V}", {
|
||||
A = ply, T = targets, V = sam.format_length(length)
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("votemute")
|
||||
:SetPermission("votemute", "admin")
|
||||
|
||||
:AddArg("player", {single_target = true})
|
||||
:AddArg("text", {hint = "reason", optional = true})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("Start a vote to mute and gag a player.")
|
||||
|
||||
:OnExecute(function(ply, targets, reason)
|
||||
local _reason = reason and (" (" .. reason .. ")") or ""
|
||||
|
||||
local target = targets[1]
|
||||
local target_name = target:Name()
|
||||
|
||||
local callback = function(index, option, count, total_count)
|
||||
if not IsValid(target) then
|
||||
sam.player.send_message(nil, "Vote was canceled because {T} left.", {
|
||||
T = target_name
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if index == 1 then
|
||||
RunConsoleCommand("sam", "mute", "#" .. target:EntIndex(), 0, "votemute" .. _reason)
|
||||
RunConsoleCommand("sam", "gag", "#" .. target:EntIndex(), 0, "votemute" .. _reason)
|
||||
|
||||
sam.player.send_message(nil, "Vote was successful, {T} has been muted. ({V})", {
|
||||
T = target_name, V = reason
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "Vote was unsuccessful, {T} won't be muted.", {
|
||||
T = target_name
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local title = "Mute " .. target_name .. "?" .. _reason
|
||||
if start_vote(ply, callback, title, "Yes", "No") then
|
||||
if reason then
|
||||
sam.player.send_message(nil, "{A} started a votemute against {T} ({V}).", {
|
||||
A = ply, T = targets, V = reason
|
||||
})
|
||||
else
|
||||
sam.player.send_message(nil, "{A} started a votemute against {T}.", {
|
||||
A = ply, T = targets
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
|
||||
command.new("votemap")
|
||||
:SetPermission("votemap", "admin")
|
||||
|
||||
:AddArg("map", {exclude_current = true})
|
||||
:AddArg("map", {optional = true, exclude_current = true})
|
||||
:AddArg("map", {optional = true, exclude_current = true})
|
||||
|
||||
:GetRestArgs()
|
||||
|
||||
:Help("Start a vote to change map.")
|
||||
|
||||
:OnExecute(function(ply, ...)
|
||||
local callback = function(_, option, count, total_count)
|
||||
sam.player.send_message(nil, "Map vote for {V} has been passed. ({V_2}/{V_3})", {
|
||||
V = option, V_2 = count, V_3 = total_count
|
||||
})
|
||||
|
||||
if sam.is_valid_map(option) then
|
||||
RunConsoleCommand("sam", "map", option)
|
||||
end
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
for i = select("#", ...), 1, -1 do
|
||||
if args[i] == "None" then
|
||||
args[i] = nil
|
||||
end
|
||||
end
|
||||
table.insert(args, "Extend Current Map")
|
||||
|
||||
if start_vote(ply, callback, "Vote for the next map!", unpack(args)) then
|
||||
sam.player.send_message(nil, "{A} started a map change vote.", {
|
||||
A = ply
|
||||
})
|
||||
end
|
||||
end)
|
||||
:End()
|
||||
18
addons/sam/lua/sam/player/cl_player.lua
Normal file
18
addons/sam/lua/sam/player/cl_player.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local netstream = sam.netstream
|
||||
|
||||
netstream.Hook("PlaySound", function(sound)
|
||||
surface.PlaySound(sound)
|
||||
end)
|
||||
79
addons/sam/lua/sam/player/sh_nw_vars.lua
Normal file
79
addons/sam/lua/sam/player/sh_nw_vars.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local netstream = sam.netstream
|
||||
|
||||
local nwvars = {}
|
||||
|
||||
if SERVER then
|
||||
function sam.player.set_nwvar(ply, key, value, force)
|
||||
local id = ply:EntIndex()
|
||||
if force or nwvars[id][key] ~= value then
|
||||
nwvars[id][key] = value
|
||||
netstream.Start(nil, "SetNWVar", id, key, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
function sam.player.set_nwvar(ply, key, value)
|
||||
local id_vars = nwvars[ply:EntIndex()]
|
||||
id_vars[key] = value
|
||||
end
|
||||
|
||||
netstream.Hook("SetNWVar", function(id, key, value)
|
||||
local id_vars = nwvars[id]
|
||||
if id_vars == nil then
|
||||
nwvars[id] = {
|
||||
[key] = value
|
||||
}
|
||||
else
|
||||
id_vars[key] = value
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("SendNWVars", function(vars)
|
||||
nwvars = vars
|
||||
end)
|
||||
|
||||
netstream.Hook("RemoveNWVar", function(id)
|
||||
nwvars[id] = nil
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.player.get_nwvar(ply, key, default)
|
||||
local value = nwvars[ply:EntIndex()]
|
||||
if value then
|
||||
value = value[key]
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
end
|
||||
return default
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
hook.Add("OnEntityCreated", "SAM.NWVars", function(ent)
|
||||
if ent:IsPlayer() and ent:IsValid() then
|
||||
nwvars[ent:EntIndex()] = {}
|
||||
netstream.Start(ent, "SendNWVars", nwvars)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("EntityRemoved", "SAM.NWVars", function(ent)
|
||||
if ent:IsPlayer() then
|
||||
local id = ent:EntIndex()
|
||||
nwvars[id] = nil
|
||||
netstream.Start(nil, "RemoveNWVar", id)
|
||||
end
|
||||
end)
|
||||
end
|
||||
183
addons/sam/lua/sam/player/sh_player.lua
Normal file
183
addons/sam/lua/sam/player/sh_player.lua
Normal file
@@ -0,0 +1,183 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
|
||||
do
|
||||
local _player = {}
|
||||
sam.player = setmetatable(sam.player, {
|
||||
__index = _player,
|
||||
__newindex = function(_, k, v)
|
||||
_player[k] = v
|
||||
if sam.isfunction(v) and debug.getlocal(v, 1) == "ply" then
|
||||
FindMetaTable("Player")["sam_" .. k] = v
|
||||
sam.console["sam_" .. k] = v
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
function sam.player.find_by_name(name)
|
||||
name = name:lower()
|
||||
local current, players = nil, player.GetAll()
|
||||
for i = 1, #players do
|
||||
local ply = players[i]
|
||||
local found = ply:Name():lower():find(name, 1, true)
|
||||
if found then
|
||||
if current then
|
||||
if not sam.istable(current) then
|
||||
current = {current, ply}
|
||||
else
|
||||
table.insert(current, ply)
|
||||
end
|
||||
else
|
||||
current = ply
|
||||
end
|
||||
end
|
||||
end
|
||||
return current
|
||||
end
|
||||
|
||||
do
|
||||
if CLIENT then
|
||||
config.add_menu_setting("Chat Prefix (Leave empty for no prefix)", function()
|
||||
local entry = vgui.Create("SAM.TextEntry")
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetNoBar(true)
|
||||
entry:SetConfig("ChatPrefix", "")
|
||||
|
||||
return entry
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.player.send_message(ply, msg, tbl)
|
||||
if SERVER then
|
||||
if sam.isconsole(ply) then
|
||||
local result = sam.format_message(msg, tbl)
|
||||
sam.print(unpack(result, 1, result.__cnt))
|
||||
else
|
||||
return sam.netstream.Start(ply, "send_message", msg, tbl)
|
||||
end
|
||||
else
|
||||
local prefix_result = sam.format_message(config.get("ChatPrefix", ""))
|
||||
local prefix_n = #prefix_result
|
||||
|
||||
local result = sam.format_message(msg, tbl, prefix_result, prefix_n)
|
||||
chat.AddText(unpack(result, 1, result.__cnt))
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function sam.player.add_text(ply, ...)
|
||||
if sam.isconsole(ply) then
|
||||
sam.print(...)
|
||||
else
|
||||
sam.netstream.Start(ply, "add_text", ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
sam.netstream.Hook("send_message", function(msg, tbl)
|
||||
sam.player.send_message(nil, msg, tbl)
|
||||
end)
|
||||
|
||||
sam.netstream.Hook("add_text", function(...)
|
||||
chat.AddText(...)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local PLAYER = FindMetaTable("Player")
|
||||
|
||||
function PLAYER:IsAdmin()
|
||||
return self:CheckGroup("admin")
|
||||
end
|
||||
|
||||
function PLAYER:IsSuperAdmin()
|
||||
return self:CheckGroup("superadmin")
|
||||
end
|
||||
|
||||
local inherits_from = sam.ranks.inherits_from
|
||||
function PLAYER:CheckGroup(name)
|
||||
return inherits_from(self:sam_getrank(), name)
|
||||
end
|
||||
|
||||
local has_permission = sam.ranks.has_permission
|
||||
function PLAYER:HasPermission(perm)
|
||||
return has_permission(self:sam_getrank(), perm)
|
||||
end
|
||||
|
||||
local can_target = sam.ranks.can_target
|
||||
function PLAYER:CanTarget(ply)
|
||||
return can_target(self:sam_getrank(), ply:sam_getrank())
|
||||
end
|
||||
|
||||
function PLAYER:CanTargetRank(rank)
|
||||
return can_target(self:sam_getrank(), rank)
|
||||
end
|
||||
|
||||
local get_ban_limit = sam.ranks.get_ban_limit
|
||||
function PLAYER:GetBanLimit(ply)
|
||||
return get_ban_limit(self:sam_getrank())
|
||||
end
|
||||
|
||||
function PLAYER:sam_get_play_time()
|
||||
return self:sam_get_nwvar("play_time", 0) + self:sam_get_session_time()
|
||||
end
|
||||
|
||||
function PLAYER:sam_get_session_time()
|
||||
return os.time() - self:sam_get_nwvar("join_time", 0)
|
||||
end
|
||||
|
||||
function PLAYER:sam_getrank()
|
||||
return self:sam_get_nwvar("rank", "user")
|
||||
end
|
||||
|
||||
function PLAYER:sam_setrank(name)
|
||||
return self:sam_set_nwvar("rank", name)
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
function PLAYER:Ban(length)
|
||||
self:sam_ban(length)
|
||||
end
|
||||
|
||||
hook.Remove("PlayerInitialSpawn", "PlayerAuthSpawn")
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local set_cooldown = function(ply, name, time)
|
||||
if not ply.sam_cool_downs then
|
||||
ply.sam_cool_downs = {}
|
||||
end
|
||||
ply.sam_cool_downs[name] = SysTime() + time
|
||||
return true
|
||||
end
|
||||
|
||||
function sam.player.check_cooldown(ply, name, time)
|
||||
if not ply.sam_cool_downs or not ply.sam_cool_downs[name] then
|
||||
return set_cooldown(ply, name, time)
|
||||
end
|
||||
|
||||
local current_time = SysTime()
|
||||
local cool_down = ply.sam_cool_downs[name]
|
||||
if cool_down > current_time then
|
||||
return false, cool_down - current_time
|
||||
else
|
||||
return set_cooldown(ply, name, time)
|
||||
end
|
||||
end
|
||||
end
|
||||
139
addons/sam/lua/sam/player/sv_auth.lua
Normal file
139
addons/sam/lua/sam/player/sv_auth.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam, SQL = sam, sam.SQL
|
||||
|
||||
local auth_player = function(data, ply)
|
||||
if not ply:IsValid() then return end
|
||||
if ply:sam_get_nwvar("is_authed") then return end
|
||||
|
||||
local steamid = ply:SteamID()
|
||||
|
||||
local rank, expiry_date, play_time
|
||||
local first_join = false
|
||||
if data then
|
||||
rank, expiry_date, play_time = data.rank, tonumber(data.expiry_date), tonumber(data.play_time)
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`name` = {1},
|
||||
`last_join` = {2}
|
||||
WHERE
|
||||
`steamid` = {3}
|
||||
]], {ply:Name(), os.time(), steamid})
|
||||
else
|
||||
rank, expiry_date, play_time = "user", 0, 0
|
||||
first_join = true
|
||||
|
||||
local time = os.time()
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}, {5}, {6}, {7})
|
||||
]], {steamid, ply:Name(), rank, 0, time, time, 0})
|
||||
end
|
||||
|
||||
ply:SetUserGroup(rank)
|
||||
ply:sam_setrank(rank)
|
||||
ply:sam_start_rank_timer(expiry_date)
|
||||
|
||||
ply:sam_set_nwvar("join_time", os.time())
|
||||
ply:sam_set_nwvar("play_time", play_time)
|
||||
ply:sam_set_nwvar("is_authed", true)
|
||||
|
||||
hook.Call("SAM.AuthedPlayer", nil, ply, steamid, first_join)
|
||||
|
||||
timer.Simple(0, function()
|
||||
if IsValid(ply) then
|
||||
sam.client_hook_call("SAM.AuthedPlayer", ply, steamid, first_join)
|
||||
end
|
||||
end)
|
||||
end
|
||||
hook.Add("PlayerInitialSpawn", "SAM.AuthPlayer", function(ply)
|
||||
SQL.FQuery([[
|
||||
SELECT
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`play_time`
|
||||
FROM
|
||||
`sam_players`
|
||||
WHERE
|
||||
`steamid` = {1}
|
||||
]], {ply:SteamID()}, auth_player, true, ply)
|
||||
end)
|
||||
sam.player.auth = auth_player
|
||||
|
||||
hook.Add("SAM.AuthedPlayer", "SetSuperadminToListenServer", function(ply)
|
||||
if game.SinglePlayer() or ply:IsListenServerHost() then
|
||||
ply:sam_set_rank("superadmin")
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("SAM.AuthedPlayer", "CheckIfFullyAuthenticated", function(ply)
|
||||
timer.Simple(0, function()
|
||||
if not IsValid(ply) then return end
|
||||
if ply:IsBot() then return end
|
||||
if not ply.IsFullyAuthenticated or ply:IsFullyAuthenticated() then return end
|
||||
if game.SinglePlayer() or ply:IsListenServerHost() then return end
|
||||
|
||||
ply:Kick("Your SteamID wasn't fully authenticated, try restarting steam.")
|
||||
end)
|
||||
end)
|
||||
|
||||
do
|
||||
local format = string.format
|
||||
local floor = math.floor
|
||||
local SysTime = SysTime
|
||||
local last_save = SysTime()
|
||||
|
||||
local save_play_time = function(ply)
|
||||
if not ply:sam_get_nwvar("is_authed") then return end
|
||||
|
||||
local query = format([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`play_time` = %u
|
||||
WHERE
|
||||
`steamid` = '%s'
|
||||
]], floor(ply:sam_get_play_time()), ply:SteamID())
|
||||
SQL.Query(query)
|
||||
end
|
||||
|
||||
hook.Add("Think", "SAM.Player.SaveTimes", function()
|
||||
if SysTime() - last_save < 60 then return end
|
||||
|
||||
SQL.Begin()
|
||||
local players = player.GetHumans()
|
||||
for i = 1, #players do
|
||||
save_play_time(players[i])
|
||||
end
|
||||
SQL.Commit()
|
||||
|
||||
sam.hook_call("SAM.UpdatedPlayTimes")
|
||||
|
||||
last_save = SysTime()
|
||||
end)
|
||||
|
||||
hook.Add("PlayerDisconnected", "SAM.Player.SaveTime", save_play_time)
|
||||
end
|
||||
291
addons/sam/lua/sam/player/sv_bans.lua
Normal file
291
addons/sam/lua/sam/player/sv_bans.lua
Normal file
@@ -0,0 +1,291 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local SQL = sam.SQL
|
||||
|
||||
local DEFAULT_REASON = sam.language.get("default_reason")
|
||||
|
||||
local set_cached, get_cached do
|
||||
local cached_bans = {}
|
||||
function set_cached(k, v)
|
||||
cached_bans[k] = v
|
||||
end
|
||||
function get_cached(k)
|
||||
return cached_bans[k]
|
||||
end
|
||||
timer.Create("SAM.ClearCachedBans", 60 * 2.5, 0, function()
|
||||
table.Empty(cached_bans)
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.format_ban_message(admin_name, admin_steamid, reason, unban_date)
|
||||
unban_date = unban_date == 0 and "never" or sam.format_length((unban_date - os.time()) / 60)
|
||||
|
||||
local message_tbl
|
||||
if admin_name == "" then
|
||||
message_tbl = sam.format_message("ban_message", {
|
||||
S = admin_steamid, S_2 = reason, S_3 = unban_date
|
||||
})
|
||||
else
|
||||
message_tbl = sam.format_message("ban_message_2", {
|
||||
S = admin_name, S_2 = admin_steamid, S_3 = reason, S_4 = unban_date
|
||||
})
|
||||
end
|
||||
|
||||
local message = ""
|
||||
for i = 1, #message_tbl do
|
||||
local v = message_tbl[i]
|
||||
if sam.isstring(v) then
|
||||
message = message .. v
|
||||
end
|
||||
end
|
||||
|
||||
return message
|
||||
end
|
||||
|
||||
function sam.player.ban(ply, length, reason, admin_steamid)
|
||||
if sam.type(ply) ~= "Player" or not ply:IsValid() then
|
||||
error("invalid player")
|
||||
end
|
||||
|
||||
if ply.sam_is_banned then return end
|
||||
|
||||
local unban_date
|
||||
if not sam.isnumber(length) or length <= 0 then
|
||||
unban_date = 0
|
||||
else
|
||||
unban_date = (math.min(length, 31536000) * 60) + os.time()
|
||||
end
|
||||
|
||||
if not sam.isstring(reason) then
|
||||
reason = DEFAULT_REASON
|
||||
end
|
||||
|
||||
if ply:IsBot() then -- you can't ban bots!
|
||||
return ply:Kick(reason)
|
||||
end
|
||||
|
||||
if not sam.is_steamid(admin_steamid) then -- 4958fc64d974ee5657060aff5c15da1f4f4c5a3de14720e33e3dfbd4622d384a
|
||||
admin_steamid = "Console"
|
||||
end
|
||||
|
||||
local steamid = ply:SteamID()
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4})
|
||||
]], {steamid, reason, admin_steamid, unban_date})
|
||||
|
||||
local admin_name = ""
|
||||
do
|
||||
local admin = player.GetBySteamID(admin_steamid)
|
||||
if admin then
|
||||
admin_name = admin:Name()
|
||||
end
|
||||
end
|
||||
|
||||
ply.sam_is_banned = true
|
||||
set_cached(steamid, nil)
|
||||
sam.hook_call("SAM.BannedPlayer", ply, unban_date, reason, admin_steamid)
|
||||
ply:Kick(sam.format_ban_message(admin_name, admin_steamid, reason, unban_date))
|
||||
end
|
||||
|
||||
function sam.player.ban_id(steamid, length, reason, admin_steamid)
|
||||
sam.is_steamid(steamid, true)
|
||||
|
||||
do
|
||||
local ply = player.GetBySteamID(steamid)
|
||||
if ply then
|
||||
return ply:sam_ban(length, reason, admin_steamid)
|
||||
end
|
||||
end
|
||||
|
||||
local unban_date
|
||||
if not sam.isnumber(length) or length <= 0 then
|
||||
unban_date = 0
|
||||
else
|
||||
unban_date = (math.min(length, 31536000) * 60) + os.time()
|
||||
end
|
||||
|
||||
if not sam.isstring(reason) then
|
||||
reason = DEFAULT_REASON
|
||||
end
|
||||
|
||||
local query
|
||||
if SQL.IsMySQL() then
|
||||
query = [[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}) ON DUPLICATE KEY
|
||||
UPDATE
|
||||
`reason` = VALUES(`reason`),
|
||||
`admin` = VALUES(`admin`),
|
||||
`unban_date` = VALUES(`unban_date`)
|
||||
]]
|
||||
else
|
||||
query = [[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}) ON CONFLICT(`steamid`) DO
|
||||
UPDATE SET
|
||||
`reason` = excluded.`reason`,
|
||||
`admin` = excluded.`admin`,
|
||||
`unban_date` = excluded.`unban_date`
|
||||
]]
|
||||
end
|
||||
|
||||
SQL.FQuery(query, {steamid, reason, admin_steamid, unban_date})
|
||||
|
||||
local admin_name = ""
|
||||
if sam.is_steamid(admin_steamid) then
|
||||
local admin = player.GetBySteamID(admin_steamid)
|
||||
if admin then
|
||||
admin_name = admin:Name()
|
||||
end
|
||||
else
|
||||
admin_steamid = "Console"
|
||||
end
|
||||
|
||||
set_cached(steamid, nil)
|
||||
sam.hook_call("SAM.BannedSteamID", steamid, unban_date, reason, admin_steamid)
|
||||
sam.player.kick_id(steamid, sam.format_ban_message(admin_name, admin_steamid, reason, unban_date))
|
||||
end
|
||||
|
||||
function sam.player.unban(steamid, admin)
|
||||
sam.is_steamid(steamid, true)
|
||||
|
||||
if not sam.is_steamid(admin) then
|
||||
admin = "Console"
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
DELETE FROM
|
||||
`sam_bans`
|
||||
WHERE
|
||||
`steamid` = {1}
|
||||
]], {steamid})
|
||||
|
||||
set_cached(steamid, false)
|
||||
sam.hook_call("SAM.UnbannedSteamID", steamid, admin)
|
||||
end
|
||||
|
||||
local check_for_unban = function(steamid, ban_data, callback)
|
||||
local to_return = ban_data
|
||||
|
||||
local unban_date = tonumber(ban_data.unban_date)
|
||||
if unban_date ~= 0 and os.time() >= unban_date then
|
||||
to_return = false
|
||||
sam.player.unban(steamid)
|
||||
end
|
||||
|
||||
if callback then
|
||||
callback(to_return, steamid)
|
||||
else
|
||||
return to_return
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local query_callback = function(data, arguments)
|
||||
local steamid, callback = arguments[1], arguments[2]
|
||||
if data then
|
||||
set_cached(steamid, data)
|
||||
check_for_unban(steamid, data, callback)
|
||||
else
|
||||
set_cached(steamid, false)
|
||||
callback(false, steamid)
|
||||
end
|
||||
end
|
||||
|
||||
function sam.player.is_banned(steamid, callback)
|
||||
sam.is_steamid(steamid, true)
|
||||
|
||||
local ban_data = get_cached(steamid)
|
||||
if ban_data then
|
||||
check_for_unban(steamid, ban_data, callback)
|
||||
elseif ban_data == false then
|
||||
callback(false, steamid)
|
||||
else
|
||||
local query = [[
|
||||
SELECT
|
||||
`sam_bans`.`steamid`,
|
||||
`sam_bans`.`reason`,
|
||||
`sam_bans`.`admin`,
|
||||
`sam_bans`.`unban_date`,
|
||||
IFNULL(`sam_players`.`name`, '') AS `admin_name`
|
||||
FROM
|
||||
`sam_bans`
|
||||
LEFT OUTER JOIN
|
||||
`sam_players`
|
||||
ON
|
||||
`sam_bans`.`admin` = `sam_players`.`steamid`
|
||||
WHERE
|
||||
`sam_bans`.`steamid` = ]] .. SQL.Escape(steamid)
|
||||
|
||||
SQL.Query(query, query_callback, true, {steamid, callback})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local steamids = {}
|
||||
|
||||
local query_callback = function(ban_data, steamid)
|
||||
steamids[steamid] = nil
|
||||
|
||||
if ban_data then
|
||||
sam.player.kick_id(steamid, sam.format_ban_message(ban_data.admin_name, ban_data.admin, ban_data.reason, tonumber(ban_data.unban_date)))
|
||||
end
|
||||
end
|
||||
|
||||
gameevent.Listen("player_connect")
|
||||
hook.Add("player_connect", "SAM.CheckIfBanned", function(data)
|
||||
local steamid = data.networkid
|
||||
if data.bot == 0 and not steamids[steamid] then
|
||||
steamids[steamid] = true
|
||||
sam.player.is_banned(steamid, query_callback)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("CheckPassword", "SAM.CheckIfBanned", function(steamid64)
|
||||
local steamid = util.SteamIDFrom64(steamid64)
|
||||
local ban_data = get_cached(steamid)
|
||||
if not ban_data then return end
|
||||
|
||||
ban_data = check_for_unban(steamid, ban_data)
|
||||
if not ban_data then return end
|
||||
|
||||
return false, sam.format_ban_message(ban_data.admin_name, ban_data.admin, ban_data.reason, tonumber(ban_data.unban_date))
|
||||
end)
|
||||
end
|
||||
|
||||
sam.player.ban_set_cached, sam.player.ban_get_cached = set_cached, get_cached
|
||||
157
addons/sam/lua/sam/player/sv_player.lua
Normal file
157
addons/sam/lua/sam/player/sv_player.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local netstream = sam.netstream
|
||||
|
||||
do
|
||||
local connected_players = {}
|
||||
|
||||
function sam.player.kick_id(id, reason)
|
||||
reason = sam.isstring(reason) and reason or sam.language.get("default_reason")
|
||||
reason = reason:sub(1, 400)
|
||||
game.KickID(id, reason)
|
||||
end
|
||||
|
||||
function sam.player.is_connected(steamid)
|
||||
return connected_players[steamid] and true or false
|
||||
end
|
||||
|
||||
function sam.get_connected_players()
|
||||
return connected_players
|
||||
end
|
||||
|
||||
gameevent.Listen("player_connect")
|
||||
hook.Add("player_connect", "SAM.ConnectedPlayers", function(data)
|
||||
connected_players[data.networkid] = true
|
||||
end)
|
||||
|
||||
gameevent.Listen("player_disconnect")
|
||||
hook.Add("player_disconnect", "SAM.ConnectedPlayers", function(data)
|
||||
connected_players[data.networkid] = nil
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.player.set_exclusive(ply, reason)
|
||||
ply.sam_exclusive_reason = reason
|
||||
end
|
||||
|
||||
function sam.player.get_exclusive(ply, admin)
|
||||
local reason = ply.sam_exclusive_reason
|
||||
if reason then
|
||||
if ply == admin then
|
||||
return "You are " .. reason .. "!"
|
||||
else
|
||||
return ply:Name() .. " is " .. reason .. "!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local hide_weapons = function(ply, should_hide)
|
||||
for _, v in pairs(ply:GetWeapons()) do
|
||||
v:SetNoDraw(should_hide)
|
||||
end
|
||||
|
||||
local physgun_beams = ents.FindByClassAndParent("physgun_beam", ply)
|
||||
if physgun_beams then
|
||||
for i = 1, #physgun_beams do
|
||||
physgun_beams[i]:SetNoDraw(should_hide)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cloak = function(ply, should_cloak)
|
||||
ply:SetNoDraw(should_cloak)
|
||||
ply:DrawWorldModel(not should_cloak)
|
||||
ply:SetRenderMode(should_cloak and RENDERMODE_TRANSALPHA or RENDERMODE_NORMAL)
|
||||
ply:Fire("alpha", should_cloak and 0 or 255, 0)
|
||||
ply:sam_set_nwvar("cloaked", should_cloak)
|
||||
hide_weapons(ply, should_cloak)
|
||||
end
|
||||
|
||||
function sam.player.cloak(ply)
|
||||
cloak(ply, true)
|
||||
end
|
||||
|
||||
function sam.player.uncloak(ply)
|
||||
cloak(ply, false)
|
||||
end
|
||||
|
||||
hook.Add("PlayerSpawn", "SAM.CloakPlayer", function(ply)
|
||||
if ply:sam_get_nwvar("cloaked") then
|
||||
cloak(ply, true)
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSwitchWeapon", "SAM.CloakPlayer", function(ply)
|
||||
if ply:sam_get_nwvar("cloaked") then
|
||||
timer.Create("SAM.HideWeapons" .. ply:SteamID(), 0, 1, function()
|
||||
if IsValid(ply) and ply:sam_get_nwvar("cloaked") then
|
||||
hide_weapons(ply, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
do
|
||||
local call_hook = function(ply)
|
||||
local can_spawn = hook.Call("SAM.CanPlayerSpawn", nil, ply)
|
||||
if can_spawn ~= nil then
|
||||
return can_spawn
|
||||
end
|
||||
end
|
||||
|
||||
local spawn_hooks = {
|
||||
"Effect", "NPC",
|
||||
"Object", "Prop",
|
||||
"Ragdoll", "SENT",
|
||||
"SWEP", "Vehicle"
|
||||
}
|
||||
|
||||
for k, v in ipairs(spawn_hooks) do
|
||||
hook.Add("PlayerSpawn" .. v, "SAM.CanPlayerSpawn", call_hook)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local persistent_data = {}
|
||||
|
||||
function sam.player.set_pdata(ply, key, value)
|
||||
local ply_pdata = persistent_data[ply:AccountID()]
|
||||
if ply_pdata then
|
||||
ply_pdata[key] = value
|
||||
else
|
||||
persistent_data[ply:AccountID()] = {
|
||||
[key] = value
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function sam.player.get_pdata(ply, key, default)
|
||||
local ply_pdata = persistent_data[ply:AccountID()]
|
||||
if ply_pdata then
|
||||
local value = ply_pdata[key]
|
||||
if value ~= nil then
|
||||
return value
|
||||
end
|
||||
end
|
||||
return default
|
||||
end
|
||||
end
|
||||
|
||||
function sam.player.play_sound(ply, sound)
|
||||
netstream.Start(ply, "PlaySound", sound)
|
||||
end
|
||||
|
||||
--[[/*953ed60e46bbecc33df4049c215b53951c0dfcb21fbe82bbeb63c201211c72dc*/]]
|
||||
215
addons/sam/lua/sam/player/sv_ranks.lua
Normal file
215
addons/sam/lua/sam/player/sv_ranks.lua
Normal file
@@ -0,0 +1,215 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local SQL, Promise = sam.SQL, sam.Promise
|
||||
|
||||
function sam.player.set_rank(ply, rank, length)
|
||||
if sam.type(ply) ~= "Player" or not ply:IsValid() then
|
||||
error("invalid player")
|
||||
elseif not sam.ranks.is_rank(rank) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isnumber(length) or length < 0 then
|
||||
length = 0
|
||||
end
|
||||
|
||||
local expiry_date = 0
|
||||
if length ~= 0 then
|
||||
if rank == "user" then
|
||||
expiry_date = 0
|
||||
else
|
||||
expiry_date = (math.min(length, 31536000) * 60) + os.time()
|
||||
end
|
||||
end
|
||||
|
||||
ply:sam_start_rank_timer(expiry_date)
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`rank` = {1},
|
||||
`expiry_date` = {2}
|
||||
WHERE
|
||||
`steamid` = {3}
|
||||
]], {rank, expiry_date, ply:SteamID()})
|
||||
|
||||
local old_rank = ply:sam_getrank()
|
||||
ply:SetUserGroup(rank)
|
||||
ply:sam_setrank(rank)
|
||||
sam.hook_call("SAM.ChangedPlayerRank", ply, rank, old_rank, expiry_date)
|
||||
end
|
||||
|
||||
do
|
||||
local set_rank_id = function(player_data, arguments)
|
||||
local old_rank = player_data and player_data.rank or false
|
||||
local promise, steamid, rank, length = unpack(arguments, 1, 4)
|
||||
|
||||
local expiry_date = 0
|
||||
if length ~= 0 then
|
||||
if rank == "user" then
|
||||
expiry_date = 0
|
||||
else
|
||||
expiry_date = (math.min(length, 31536000) * 60) + os.time()
|
||||
end
|
||||
end
|
||||
|
||||
local exists = true
|
||||
if old_rank == false then
|
||||
exists, old_rank = false, "user"
|
||||
|
||||
local time = os.time()
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}, {5}, {6}, {7})
|
||||
]], {steamid, "", rank, 0, time, time, 0})
|
||||
else
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`rank` = {1},
|
||||
`expiry_date` = {2}
|
||||
WHERE
|
||||
`steamid` = {3}
|
||||
]], {rank, expiry_date, steamid})
|
||||
end
|
||||
|
||||
promise:resolve()
|
||||
sam.hook_call("SAM.ChangedSteamIDRank", steamid, rank, old_rank, expiry_date, exists)
|
||||
end
|
||||
|
||||
function sam.player.set_rank_id(steamid, rank, length)
|
||||
sam.is_steamid(steamid, true)
|
||||
|
||||
if not sam.ranks.is_rank(rank) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
local promise = Promise.new()
|
||||
|
||||
do
|
||||
local ply = player.GetBySteamID(steamid)
|
||||
if ply then
|
||||
promise:resolve(ply:sam_set_rank(rank, length))
|
||||
return promise
|
||||
end
|
||||
end
|
||||
|
||||
if not sam.isnumber(length) or length < 0 then
|
||||
length = 0
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
SELECT
|
||||
`rank`
|
||||
FROM
|
||||
`sam_players`
|
||||
WHERE
|
||||
`steamid` = {1}
|
||||
]], {steamid}, set_rank_id, true, {promise, steamid, rank, length})
|
||||
|
||||
return promise
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local get_rank = function(data, callback)
|
||||
if not data then
|
||||
callback(false)
|
||||
else
|
||||
callback(data.rank)
|
||||
end
|
||||
end
|
||||
|
||||
function sam.player.get_rank(steamid, callback)
|
||||
sam.is_steamid(steamid, true)
|
||||
|
||||
SQL.FQuery([[
|
||||
SELECT
|
||||
`rank`
|
||||
FROM
|
||||
`sam_players`
|
||||
WHERE
|
||||
`steamid` = {1}
|
||||
]], {steamid}, get_rank, true, callback)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local remove_rank_timer = function(ply)
|
||||
timer.Remove("SAM.RankTimer." .. ply:SteamID())
|
||||
end
|
||||
|
||||
function sam.player.start_rank_timer(ply, expiry_date)
|
||||
ply.sam_rank_expirydate = expiry_date
|
||||
if expiry_date == 0 then -- permanent rank
|
||||
return remove_rank_timer(ply)
|
||||
end
|
||||
expiry_date = expiry_date - os.time()
|
||||
timer.Create("SAM.RankTimer." .. ply:SteamID(), expiry_date, 1, function()
|
||||
sam.player.send_message(nil, "rank_expired", {
|
||||
T = {ply, admin = sam.console}, V = ply:sam_getrank()
|
||||
})
|
||||
ply:sam_set_rank("user")
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("PlayerDisconnected", "SAM.RemoveRankTimer", remove_rank_timer)
|
||||
end
|
||||
|
||||
hook.Add("SAM.OnRankRemove", "ResetPlayerRank", function(name)
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply:sam_getrank() == name then
|
||||
ply:sam_set_rank("user")
|
||||
end
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`rank` = 'user',
|
||||
`expiry_date` = 0
|
||||
WHERE
|
||||
`rank` = {1}
|
||||
]], {name})
|
||||
end)
|
||||
|
||||
hook.Add("SAM.RankNameChanged", "ChangePlayerRankName", function(old, new)
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if ply:sam_getrank() == old then
|
||||
ply:sam_set_rank(new)
|
||||
end
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_players`
|
||||
SET
|
||||
`rank` = {1}
|
||||
WHERE
|
||||
`rank` = {2}
|
||||
]], {new, old})
|
||||
end)
|
||||
124
addons/sam/lua/sam/ranks/sh_ranks.lua
Normal file
124
addons/sam/lua/sam/ranks/sh_ranks.lua
Normal file
@@ -0,0 +1,124 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
SAM_IMMUNITY_SUPERADMIN = 100
|
||||
SAM_IMMUNITY_ADMIN = 50
|
||||
SAM_IMMUNITY_USER = 1
|
||||
|
||||
function sam.ranks.get_ranks()
|
||||
return sam.get_global("Ranks") or {}
|
||||
end
|
||||
|
||||
function sam.ranks.get_rank(rank)
|
||||
local ranks = sam.ranks.get_ranks()
|
||||
return ranks[rank]
|
||||
end
|
||||
|
||||
function sam.ranks.is_rank(rank)
|
||||
if sam.ranks.get_rank(rank) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function sam.ranks.is_default_rank(rank)
|
||||
return rank == "superadmin" or rank == "admin" or rank == "user"
|
||||
end
|
||||
|
||||
function sam.ranks.inherits_from(rank, rank_2)
|
||||
if rank == rank_2 then
|
||||
return true
|
||||
end
|
||||
|
||||
while true do
|
||||
rank = sam.ranks.get_rank(rank)
|
||||
|
||||
if rank then
|
||||
local inherits_from = rank.inherit
|
||||
if inherits_from == rank_2 then
|
||||
return true
|
||||
end
|
||||
|
||||
rank = rank.inherit
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sam.ranks.has_permission(rank, permission)
|
||||
while true do
|
||||
if rank == "superadmin" then
|
||||
return true
|
||||
end
|
||||
|
||||
rank = sam.ranks.get_rank(rank)
|
||||
|
||||
if rank then
|
||||
local rank_permission = rank.data.permissions[permission]
|
||||
if rank_permission ~= nil then
|
||||
return rank_permission
|
||||
end
|
||||
|
||||
rank = rank.inherit
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sam.ranks.get_limit(rank, limit_type)
|
||||
while true do
|
||||
if rank == "superadmin" then return -1 end
|
||||
|
||||
rank = sam.ranks.get_rank(rank)
|
||||
|
||||
if rank then
|
||||
local limit = rank.data.limits[limit_type]
|
||||
if limit ~= nil then
|
||||
return limit
|
||||
end
|
||||
|
||||
rank = rank.inherit
|
||||
else
|
||||
return cvars.Number("sbox_max" .. limit_type, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sam.ranks.get_immunity(rank)
|
||||
rank = sam.ranks.get_rank(rank)
|
||||
return rank and rank.immunity or false
|
||||
end
|
||||
|
||||
function sam.ranks.can_target(rank_1, rank_2)
|
||||
rank_1, rank_2 = sam.ranks.get_rank(rank_1), sam.ranks.get_rank(rank_2)
|
||||
if not rank_1 or not rank_2 then
|
||||
return false
|
||||
end
|
||||
return rank_1.immunity >= rank_2.immunity
|
||||
end
|
||||
|
||||
function sam.ranks.get_ban_limit(rank)
|
||||
rank = sam.ranks.get_rank(rank)
|
||||
return rank and rank.ban_limit or false
|
||||
end
|
||||
|
||||
if CLIENT then
|
||||
hook.Add("SAM.ChangedGlobalVar", "SAM.Ranks.CheckLoadedRanks", function(key, value)
|
||||
if key == "Ranks" then
|
||||
hook.Call("SAM.LoadedRanks", nil, value)
|
||||
hook.Remove("SAM.ChangedGlobalVar", "SAM.Ranks.CheckLoadedRanks")
|
||||
end
|
||||
end)
|
||||
end
|
||||
448
addons/sam/lua/sam/ranks/sv_ranks.lua
Normal file
448
addons/sam/lua/sam/ranks/sv_ranks.lua
Normal file
@@ -0,0 +1,448 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local SQL, util = sam.SQL, util
|
||||
|
||||
local ranks = {}
|
||||
local ranks_loaded = false
|
||||
|
||||
function sam.ranks.sync()
|
||||
sam.set_global("Ranks", ranks, true)
|
||||
end
|
||||
|
||||
function sam.ranks.add_rank(name, inherit, immunity, ban_limit)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank name")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
if ranks[name] then
|
||||
return "rank_exists"
|
||||
end
|
||||
|
||||
if not sam.isstring(inherit) and name ~= "user" then
|
||||
inherit = "user"
|
||||
end
|
||||
|
||||
local data = {
|
||||
permissions = {},
|
||||
limits = {}
|
||||
}
|
||||
|
||||
if name ~= "user" then
|
||||
local inherit_rank = ranks[inherit]
|
||||
if not inherit_rank then
|
||||
error("invalid rank to inherit from")
|
||||
end
|
||||
if not sam.isnumber(immunity) then
|
||||
immunity = inherit_rank.immunity
|
||||
end
|
||||
if not sam.isnumber(ban_limit) then
|
||||
ban_limit = inherit_rank.ban_limit
|
||||
end
|
||||
end
|
||||
|
||||
if name == "superadmin" then
|
||||
immunity = 100
|
||||
elseif name == "user" then
|
||||
immunity = 1
|
||||
else
|
||||
immunity = math.Clamp(immunity, 2, 99)
|
||||
end
|
||||
|
||||
ban_limit = math.Clamp(ban_limit, 0, 31536000)
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_ranks`(
|
||||
`name`,
|
||||
`inherit`,
|
||||
`immunity`,
|
||||
`ban_limit`,
|
||||
`data`
|
||||
)
|
||||
VALUES
|
||||
({1}, {2}, {3}, {4}, {5})
|
||||
]], {name, inherit or "NULL", immunity, ban_limit, util.TableToJSON(data)})
|
||||
|
||||
local rank = {
|
||||
name = name,
|
||||
inherit = inherit,
|
||||
immunity = immunity,
|
||||
ban_limit = ban_limit,
|
||||
data = data
|
||||
}
|
||||
|
||||
ranks[name] = rank
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.AddedRank", name, rank)
|
||||
end
|
||||
|
||||
function sam.ranks.remove_rank(name)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
-- can't just remove default ranks!!
|
||||
if sam.ranks.is_default_rank(name) then
|
||||
return "default_rank"
|
||||
end
|
||||
|
||||
// 13271fc0ed1d2e4e36589c40f54f685d96ba64e69b7c5bc70aa14d4bb236ab18
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
SQL.FQuery([[
|
||||
DELETE FROM
|
||||
`sam_ranks`
|
||||
WHERE
|
||||
`name` = {1}
|
||||
]], {name})
|
||||
|
||||
sam.hook_call("SAM.OnRankRemove", name, rank)
|
||||
ranks[name] = nil
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RemovedRank", name)
|
||||
end
|
||||
|
||||
function sam.ranks.rename_rank(old, new)
|
||||
if not sam.isstring(old) then
|
||||
error("invalid old name")
|
||||
end
|
||||
|
||||
if not sam.isstring(new) then
|
||||
error("invalid new name")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
local old_rank = ranks[old]
|
||||
if not old_rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if sam.ranks.is_default_rank(old) then
|
||||
return "default_rank"
|
||||
end
|
||||
|
||||
if ranks[new] then
|
||||
error("new rank name exists")
|
||||
end
|
||||
|
||||
old_rank.name = new
|
||||
ranks[new], ranks[old] = ranks[old], nil
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`name` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {new, old})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RankNameChanged", old, new)
|
||||
end
|
||||
|
||||
function sam.ranks.change_inherit(name, inherit)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isstring(inherit) then
|
||||
error("invalid rank to inherit from")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if name == "user" or name == "superadmin" then return end
|
||||
|
||||
if not ranks[inherit] then
|
||||
error("invalid rank to inherit from")
|
||||
end
|
||||
|
||||
if name == inherit then
|
||||
error("you can't inherit from the same rank")
|
||||
end
|
||||
|
||||
if rank.inherit == inherit then return end
|
||||
|
||||
local old_inherit = rank.inherit
|
||||
-- e3a33576750e51364148739225303ca04e922554b5b1b0197a7475aa52cc2634!!!
|
||||
rank.inherit = inherit
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`inherit` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {inherit, name})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.ChangedInheritRank", name, inherit, old_inherit)
|
||||
end
|
||||
|
||||
function sam.ranks.change_immunity(name, new_immunity)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isnumber(new_immunity) then
|
||||
error("invalid immunity")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if name == "user" or name == "superadmin" then return end
|
||||
|
||||
new_immunity = math.Clamp(new_immunity, 2, 99) // 8bc01ecde3d91528be5a061c416d9334bd200f6f3f76a0083468334f18f03063!!!
|
||||
|
||||
local old_immunity = rank.immunity
|
||||
rank.immunity = new_immunity
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`immunity` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {new_immunity, name})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RankImmunityChanged", name, new_immunity, old_immunity)
|
||||
end
|
||||
|
||||
function sam.ranks.change_ban_limit(name, new_limit)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isnumber(new_limit) then
|
||||
error("invalid ban limit")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if name == "superadmin" then return end
|
||||
|
||||
new_limit = math.Clamp(new_limit, 0, 31536000)
|
||||
|
||||
local old_limit = rank.ban_limit
|
||||
rank.ban_limit = new_limit
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`ban_limit` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {new_limit, name})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RankBanLimitChanged", name, new_limit, old_limit)
|
||||
end
|
||||
|
||||
function sam.ranks.give_permission(name, permission)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isstring(permission) then
|
||||
error("invalid permission")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
if name == "superadmin" then return end
|
||||
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
local permissions = rank.data.permissions
|
||||
if permissions[permission] then return end
|
||||
|
||||
-- 3db898ddbc4a12ba9980d00541151133f8e09c55bcb7650487608c39edd6c162!!!
|
||||
permissions[permission] = true
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`data` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {util.TableToJSON(rank.data), name})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RankPermissionGiven", name, permission)
|
||||
end
|
||||
|
||||
function sam.ranks.take_permission(name, permission)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isstring(permission) then
|
||||
error("invalid permission")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
if name == "superadmin" then return end
|
||||
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
local permissions = rank.data.permissions
|
||||
if permissions[permission] == false then return end
|
||||
|
||||
permissions[permission] = false
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`data` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {util.TableToJSON(rank.data), name})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RankPermissionTaken", name, permission)
|
||||
end
|
||||
|
||||
function sam.ranks.set_limit(name, type, limit)
|
||||
if not sam.isstring(name) then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
if not sam.isstring(type) then
|
||||
error("invalid limit type")
|
||||
end
|
||||
|
||||
if not sam.isnumber(limit) then
|
||||
error("invalid limit value")
|
||||
end
|
||||
|
||||
if not ranks_loaded then
|
||||
error("loading ranks")
|
||||
end
|
||||
|
||||
if name == "superadmin" then return end
|
||||
|
||||
local rank = ranks[name]
|
||||
if not rank then
|
||||
error("invalid rank")
|
||||
end
|
||||
|
||||
limit = math.Clamp(limit, -1, 1000)
|
||||
local limits = rank.data.limits
|
||||
if limits[type] == limit then return end
|
||||
|
||||
limits[type] = limit
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`data` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {util.TableToJSON(rank.data), name})
|
||||
|
||||
sam.ranks.sync()
|
||||
sam.hook_call("SAM.RankChangedLimit", name, type, limit)
|
||||
end
|
||||
|
||||
function sam.ranks.ranks_loaded()
|
||||
return ranks_loaded
|
||||
end
|
||||
|
||||
sam.ranks.sync()
|
||||
|
||||
hook.Add("SAM.DatabaseLoaded", "LoadRanks", function()
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
`sam_ranks`
|
||||
]], function(sam_ranks)
|
||||
for _, v in ipairs(sam_ranks) do
|
||||
local name = v.name
|
||||
ranks[name] = {
|
||||
name = name,
|
||||
inherit = name ~= "user" and v.inherit,
|
||||
immunity = tonumber(v.immunity),
|
||||
ban_limit = tonumber(v.ban_limit),
|
||||
data = util.JSONToTable(v.data)
|
||||
}
|
||||
end
|
||||
|
||||
ranks_loaded = true
|
||||
|
||||
if #ranks < 3 then
|
||||
sam.ranks.add_rank("user", nil, nil, 0)
|
||||
sam.ranks.add_rank("admin", "user", SAM_IMMUNITY_ADMIN, 20160 --[[2 weeks]])
|
||||
sam.ranks.add_rank("superadmin", "admin", SAM_IMMUNITY_SUPERADMIN, 0)
|
||||
/*a5a6b7626ac8542cad9e421879f6138c874bc3609653dc032c142493caff4203*/
|
||||
end
|
||||
|
||||
sam.ranks.sync()
|
||||
hook.Call("SAM.LoadedRanks", nil, ranks)
|
||||
end):wait()
|
||||
end)
|
||||
480
addons/sam/lua/sam/reports/cl_reports.lua
Normal file
480
addons/sam/lua/sam/reports/cl_reports.lua
Normal file
@@ -0,0 +1,480 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local netstream = sam.netstream
|
||||
local SUI = sam.SUI
|
||||
|
||||
local config = sam.config
|
||||
|
||||
local Trim = string.Trim
|
||||
|
||||
local muted_var = CreateClientConVar("sam_mute_reports", "0", false, false, "", 0, 1)
|
||||
|
||||
local position = config.get_updated("Reports.Position", "Left")
|
||||
local max_reports = config.get_updated("Reports.MaxReports", 4)
|
||||
local always_show = config.get_updated("Reports.AlwaysShow", true)
|
||||
local pad_x = config.get_updated("Reports.XPadding", 5)
|
||||
local pad_y = config.get_updated("Reports.YPadding", 5)
|
||||
|
||||
local duty_jobs = {}
|
||||
config.hook({"Reports.DutyJobs"}, function()
|
||||
local jobs = config.get("Reports.DutyJobs", ""):Split(",")
|
||||
for i = #jobs, 1, -1 do
|
||||
local v = Trim(jobs[i])
|
||||
if v ~= "" then
|
||||
jobs[v] = true
|
||||
end
|
||||
jobs[i] = nil
|
||||
end
|
||||
duty_jobs = jobs
|
||||
end)
|
||||
|
||||
local commands = {}
|
||||
config.hook({"Reports.Commands"}, function()
|
||||
local cmds = config.get("Reports.Commands", ""):Split(",")
|
||||
for i = 1, #cmds do
|
||||
local v = Trim(cmds[i])
|
||||
if v ~= "" then
|
||||
cmds[i] = {
|
||||
name = v,
|
||||
func = function(_, ply)
|
||||
if IsValid(ply) then
|
||||
RunConsoleCommand("sam", v, "#" .. ply:EntIndex())
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
commands = cmds
|
||||
end)
|
||||
|
||||
local reports = {}
|
||||
local queued_reports = {}
|
||||
|
||||
local new_report, remove_report, check_queued, get_report, append_report
|
||||
|
||||
get_report = function(ply, index)
|
||||
for i = 1, #reports do
|
||||
local v = reports[i]
|
||||
local _ply = index and v.index or v.ply
|
||||
if _ply == ply then return v end
|
||||
end
|
||||
|
||||
for i = 1, #queued_reports do
|
||||
local v = queued_reports[i]
|
||||
local _ply = index and v.index or v.ply
|
||||
if _ply == ply then return v, i end
|
||||
end
|
||||
end
|
||||
|
||||
remove_report = function(ply)
|
||||
local report, delayed_i = get_report(ply)
|
||||
|
||||
if delayed_i then
|
||||
return table.remove(queued_reports, delayed_i)
|
||||
end
|
||||
|
||||
local panel = report.panel
|
||||
panel:MoveToNewX(position.value == "Right" and ScrW() or -panel:GetWide(), function()
|
||||
for i = report.pos + 1, #reports do
|
||||
local v = reports[i]
|
||||
v.pos = v.pos - 1
|
||||
v.panel:MoveToNewY(v.panel:GetY())
|
||||
end
|
||||
|
||||
panel:Remove()
|
||||
table.remove(reports, report.pos)
|
||||
|
||||
check_queued()
|
||||
end)
|
||||
end
|
||||
|
||||
check_queued = function()
|
||||
while (max_reports.value - #reports > 0 and #queued_reports > 0) do
|
||||
new_report(table.remove(queued_reports, 1))
|
||||
end
|
||||
end
|
||||
|
||||
append_report = function(ply, text)
|
||||
local report, delayed = get_report(ply)
|
||||
if delayed then
|
||||
table.insert(report.comments, text)
|
||||
else
|
||||
report.panel:AddComment(text)
|
||||
end
|
||||
end
|
||||
|
||||
new_report = function(report)
|
||||
if #reports >= max_reports.value then
|
||||
return table.insert(queued_reports, report)
|
||||
end
|
||||
|
||||
report.pos = table.insert(reports, report)
|
||||
|
||||
local panel = vgui.Create("SAM.Report")
|
||||
panel:SetReport(report)
|
||||
|
||||
for k, v in ipairs(commands) do
|
||||
panel:AddButton(v.name:gsub("^%l", string.upper), v.func)
|
||||
end
|
||||
|
||||
local claim = panel:AddButton("Claim", function(self, ply)
|
||||
if panel:HasReport() then
|
||||
return LocalPlayer():sam_send_message("You have an active case, close it first.")
|
||||
end
|
||||
|
||||
self.DoClick = function()
|
||||
end
|
||||
|
||||
local claim_query = netstream.async.Start("ClaimReport", nil, ply)
|
||||
claim_query:done(function(claimed)
|
||||
if not IsValid(panel) then return end
|
||||
|
||||
if claimed then
|
||||
panel:SetHasReport(ply)
|
||||
|
||||
self:SetText("Close")
|
||||
|
||||
self.background = Color(231, 76, 60, 200)
|
||||
self.hover = Color(255, 255, 255, 25)
|
||||
|
||||
panel:FixWide()
|
||||
|
||||
for k, v in ipairs(panel:GetChildren()[3]:GetChildren()) do
|
||||
v:SetDisabled(false)
|
||||
v:SetCursor("hand")
|
||||
end
|
||||
|
||||
self.DoClick = function()
|
||||
panel:Close()
|
||||
end
|
||||
else
|
||||
panel:SetClaimed()
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
local dismiss = panel:AddButton("Dismiss", function(self, ply)
|
||||
self.DoClick = function()
|
||||
end
|
||||
|
||||
panel:Close()
|
||||
netstream.Start("DismissReport", ply)
|
||||
end)
|
||||
|
||||
panel.claim = claim
|
||||
|
||||
claim:SetCursor("hand")
|
||||
claim:SetDisabled(false)
|
||||
|
||||
claim.background = Color(39, 174, 96, 200)
|
||||
claim.hover = Color(255, 255, 255, 25)
|
||||
|
||||
panel.dismiss = dismiss
|
||||
|
||||
dismiss:SetCursor("hand")
|
||||
dismiss:SetDisabled(false)
|
||||
|
||||
dismiss.background = Color(231, 76, 60, 200)
|
||||
dismiss.hover = Color(255, 255, 255, 25)
|
||||
|
||||
panel:FixWide()
|
||||
|
||||
local x = pad_x.value
|
||||
if position.value == "Right" then
|
||||
x = (ScrW() - panel:GetWide()) - x
|
||||
end
|
||||
|
||||
panel:MoveToNewX(x)
|
||||
panel:MoveToNewY(panel:GetY())
|
||||
|
||||
panel.new = true
|
||||
for k, v in ipairs(report.comments) do
|
||||
panel:AddComment(v)
|
||||
end
|
||||
panel.new = nil
|
||||
end
|
||||
|
||||
netstream.Hook("Report", function(ply, comment)
|
||||
if not IsValid(ply) then return end
|
||||
|
||||
if muted_var:GetBool() then return end
|
||||
|
||||
local report = get_report(ply)
|
||||
if not report then
|
||||
report = {
|
||||
ply = ply,
|
||||
index = ply:EntIndex(),
|
||||
comments = {comment}
|
||||
}
|
||||
|
||||
system.FlashWindow()
|
||||
|
||||
if not always_show.value and not duty_jobs[team.GetName(LocalPlayer():Team())] then
|
||||
LocalPlayer():sam_send_message("({S Blue}) {S_2 Red}: {S_3}", {
|
||||
S = "Report", S_2 = ply:Name(), S_3 = comment
|
||||
})
|
||||
else
|
||||
new_report(report)
|
||||
end
|
||||
else
|
||||
append_report(ply, comment)
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("ReportClaimed", function(ply)
|
||||
local report, delayed = get_report(ply)
|
||||
if not report then return end
|
||||
|
||||
if delayed then
|
||||
table.remove(queued_reports, delayed)
|
||||
else
|
||||
report.panel:SetClaimed()
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("ReportClosed", function(index)
|
||||
local report, delayed = get_report(index, true)
|
||||
if not report then return end
|
||||
|
||||
if delayed then
|
||||
table.remove(queued_reports, delayed)
|
||||
else
|
||||
report.panel:SetClosed()
|
||||
end
|
||||
end)
|
||||
|
||||
do
|
||||
local REPORTS_HEADER = SUI.CreateFont("ReportHeader", "Roboto", 14, 540)
|
||||
local REPORT_COMMENT = SUI.CreateFont("ReportComment", "Roboto", 13, 540)
|
||||
local REPORT_BUTTONS = SUI.CreateFont("ReportButtons", "Roboto", 13, 550)
|
||||
|
||||
local Panel = {}
|
||||
|
||||
function Panel:Init()
|
||||
sui.TDLib.Start()
|
||||
|
||||
self:Blur()
|
||||
:Background(Color(30, 30, 30, 240))
|
||||
|
||||
local p_w, p_h = SUI.Scale(350), SUI.Scale(145)
|
||||
self:SetSize(p_w, p_h)
|
||||
|
||||
local x = p_w * 2
|
||||
|
||||
if position.value == "Right" then
|
||||
x = ScrW() + x
|
||||
else
|
||||
x = -x
|
||||
end
|
||||
|
||||
self:SetPos(x, -p_h)
|
||||
|
||||
local top_panel = self:Add("Panel")
|
||||
top_panel:Dock(TOP)
|
||||
top_panel:SetTall(SUI.Scale(24))
|
||||
top_panel:Background(Color(60, 60, 60, 200))
|
||||
|
||||
local ply_name = top_panel:Add("DLabel")
|
||||
ply_name:Dock(LEFT)
|
||||
ply_name:DockMargin(5, 0, 0, 0)
|
||||
ply_name:SetTextColor(Color(200, 200, 200))
|
||||
ply_name:SetFont(REPORTS_HEADER)
|
||||
self.ply_name = ply_name
|
||||
|
||||
local scroll = self:Add("SAM.ScrollPanel")
|
||||
scroll:Dock(FILL)
|
||||
scroll:DockMargin(5, 5, 5, 5)
|
||||
scroll.Paint = nil
|
||||
self.scroll = scroll
|
||||
|
||||
local comment = scroll:Add("DLabel")
|
||||
comment:Dock(TOP)
|
||||
comment:SetText("")
|
||||
comment:SetTextColor(Color(200, 200, 200))
|
||||
comment:SetFont(REPORT_COMMENT)
|
||||
comment:SetMultiline(true)
|
||||
comment:SetWrap(true)
|
||||
comment:SetAutoStretchVertical(true)
|
||||
self.comment = comment
|
||||
|
||||
local bottom = self:Add("Panel")
|
||||
bottom:Dock(BOTTOM)
|
||||
bottom:SetTall(SUI.Scale(24))
|
||||
self.bottom = bottom
|
||||
|
||||
sui.TDLib.End()
|
||||
end
|
||||
|
||||
function Panel:GetY()
|
||||
return (self:GetTall() + 5) * (self.report.pos - 1) + pad_y.value
|
||||
end
|
||||
|
||||
function Panel:Close()
|
||||
remove_report(self.report.ply)
|
||||
end
|
||||
|
||||
local change_state = function(self, text)
|
||||
self.claim:SetText(text)
|
||||
self.claim.DoClick = function() end
|
||||
|
||||
self.claim:SUI_TDLib()
|
||||
:Background(Color(41, 128, 185, 200))
|
||||
|
||||
timer.Simple(5, function()
|
||||
if IsValid(self) then
|
||||
self:Close()
|
||||
end
|
||||
end)
|
||||
|
||||
if self:HasReport() == self.report.ply then
|
||||
self:SetHasReport()
|
||||
end
|
||||
|
||||
self:FixWide()
|
||||
end
|
||||
|
||||
function Panel:SetClaimed()
|
||||
change_state(self, "Case clamied!")
|
||||
end
|
||||
|
||||
function Panel:SetClosed()
|
||||
change_state(self, "Case closed!")
|
||||
end
|
||||
|
||||
function Panel:SetReport(report)
|
||||
surface.PlaySound("garrysmod/balloon_pop_cute.wav")
|
||||
|
||||
report.panel = self
|
||||
|
||||
self.report = report
|
||||
self.ply_name:SetText(report.ply:Name() .. " (" .. report.ply:SteamName() .. ")")
|
||||
self.ply_name:SetWide(self:GetWide())
|
||||
end
|
||||
|
||||
local disabled = Color(60, 60, 60, 200)
|
||||
local click = Color(255, 255, 255, 30)
|
||||
local button_paint = function(self, w, h)
|
||||
draw.RoundedBox(0, 0, 0, w, h, self.background)
|
||||
|
||||
if self:GetDisabled() then
|
||||
draw.RoundedBox(0, 0, 0, w, h, disabled)
|
||||
else
|
||||
if self:IsHovered() then
|
||||
draw.RoundedBox(0, 0, 0, w, h, self.hover)
|
||||
end
|
||||
|
||||
if self.Depressed then
|
||||
draw.RoundedBox(0, 0, 0, w, h, click)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local button_click = function(self)
|
||||
self.cb(self, self.report.ply)
|
||||
end
|
||||
|
||||
local background = Color(60, 60, 60, 200)
|
||||
local hover = Color(14, 134, 204, 100)
|
||||
function Panel:AddButton(text, cb)
|
||||
local button = self.bottom:Add("DButton")
|
||||
button:Dock(LEFT)
|
||||
button:SetText(text)
|
||||
button:SetTextColor(Color(200, 200, 200))
|
||||
button:SetFont(REPORT_BUTTONS)
|
||||
button:SetDisabled(true)
|
||||
button:SetCursor("arrow")
|
||||
|
||||
button.Paint = button_paint
|
||||
button.DoClick = button_click
|
||||
|
||||
button.background = background
|
||||
button.hover = hover
|
||||
|
||||
button.cb = cb
|
||||
button.report = self.report
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
function Panel:FixWide()
|
||||
local wide = 0
|
||||
|
||||
for _, v in ipairs(self.bottom:GetChildren()) do
|
||||
v:SizeToContents()
|
||||
v:SetWide(v:GetWide() + 6)
|
||||
wide = wide + v:GetWide()
|
||||
end
|
||||
|
||||
self:SetWide(wide)
|
||||
|
||||
return wide
|
||||
end
|
||||
|
||||
function Panel:OnRemove()
|
||||
local reporter = self:HasReport()
|
||||
if reporter then
|
||||
netstream.Start("CloseReport", reporter)
|
||||
self:SetHasReport()
|
||||
end
|
||||
end
|
||||
|
||||
function Panel:AddComment(text)
|
||||
local comment = self.comment
|
||||
|
||||
local old_text = comment:GetText()
|
||||
if old_text ~= "" then
|
||||
old_text = old_text .. "\n"
|
||||
end
|
||||
|
||||
if not self.new then
|
||||
surface.PlaySound("ambient/water/drip4.wav")
|
||||
end
|
||||
|
||||
comment:SetText(old_text .. "- " .. text)
|
||||
comment:SizeToContents()
|
||||
|
||||
self.scroll:ScrollToBottom()
|
||||
end
|
||||
|
||||
function Panel:HasReport()
|
||||
return LocalPlayer().sam_has_report
|
||||
end
|
||||
|
||||
function Panel:SetHasReport(v)
|
||||
LocalPlayer().sam_has_report = v
|
||||
end
|
||||
|
||||
local new_animation = function(panel, name)
|
||||
local new_name = "anim_" .. name
|
||||
panel["MoveToNew" .. name:upper()] = function(self, new, cb)
|
||||
if self[new_name] then
|
||||
table.RemoveByValue(self.m_AnimList, self[new_name])
|
||||
end
|
||||
|
||||
self[new_name] = self:NewAnimation(0.2, 0, -1, function()
|
||||
self[new_name] = nil
|
||||
if cb then cb() end
|
||||
end)
|
||||
|
||||
self[new_name].Think = function(_, _, frac)
|
||||
self[name] = Lerp(frac, self[name], new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
new_animation(Panel, "x")
|
||||
new_animation(Panel, "y")
|
||||
|
||||
vgui.Register("SAM.Report", Panel, "EditablePanel")
|
||||
end
|
||||
202
addons/sam/lua/sam/reports/sv_reports.lua
Normal file
202
addons/sam/lua/sam/reports/sv_reports.lua
Normal file
@@ -0,0 +1,202 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local netstream = sam.netstream
|
||||
|
||||
local config = sam.config
|
||||
|
||||
local auto_close
|
||||
config.hook({"Reports.AutoCloseTime"}, function()
|
||||
auto_close = sam.parse_length(config.get("Reports.AutoCloseTime", "10m")) * 60
|
||||
end)
|
||||
|
||||
hook.Add("SAM.LoadedConfig", "SAM.ReportsMain", function(c)
|
||||
if not c["Reports.Commands"] then
|
||||
sam.config.set("Reports.Commands", "goto, bring, return")
|
||||
end
|
||||
|
||||
if not c["Reports.DutyJobs"] then
|
||||
sam.config.set("Reports.DutyJobs", "Admin On Duty, Hobo, Medic")
|
||||
end
|
||||
end)
|
||||
|
||||
local get_admins = function(ply)
|
||||
local admins = {}
|
||||
|
||||
local players = player.GetHumans()
|
||||
for i = 1, #players do
|
||||
local v = players[i]
|
||||
if v:HasPermission("see_admin_chat") and v ~= ply then
|
||||
table.insert(admins, v)
|
||||
end
|
||||
end
|
||||
|
||||
return admins
|
||||
end
|
||||
|
||||
local remove_report_info = function(ply)
|
||||
local admin = ply.sam_report_admin
|
||||
|
||||
if IsValid(ply) then
|
||||
ply.sam_has_report = nil
|
||||
ply.sam_report_admin = nil
|
||||
netstream.Start(get_admins(), "ReportClosed", ply:EntIndex())
|
||||
end
|
||||
|
||||
if IsValid(admin) then
|
||||
admin.sam_claimed_report = nil
|
||||
end
|
||||
|
||||
timer.Remove("SAM.Reports." .. ply:EntIndex())
|
||||
end
|
||||
|
||||
function sam.player.report(ply, comment)
|
||||
if not IsValid(ply) then
|
||||
error("invalid player")
|
||||
end
|
||||
|
||||
local can_use, time = ply:sam_check_cooldown("NewReport", 4)
|
||||
if can_use == false then return false, time < 1 and 1 or math.Round(time) end
|
||||
|
||||
if not sam.isstring(comment) then
|
||||
error("invalid comment")
|
||||
end
|
||||
|
||||
comment = comment:sub(1, 120)
|
||||
|
||||
local admin = ply.sam_report_admin
|
||||
if admin then
|
||||
if IsValid(admin) then
|
||||
return netstream.Start(admin, "Report", ply, comment)
|
||||
else
|
||||
remove_report_info(ply)
|
||||
end
|
||||
end
|
||||
|
||||
ply.sam_has_report = true
|
||||
netstream.Start(get_admins(), "Report", ply, comment)
|
||||
end
|
||||
|
||||
netstream.async.Hook("ClaimReport", function(res, ply, reporter)
|
||||
if sam.type(reporter) ~= "Player" or not IsValid(reporter) or not reporter.sam_has_report then
|
||||
return res(false)
|
||||
end
|
||||
|
||||
local admin = reporter.sam_report_admin
|
||||
if not IsValid(admin) then
|
||||
reporter.sam_report_admin, admin = nil, nil
|
||||
end
|
||||
|
||||
if admin and admin.sam_claimed_report then
|
||||
return res(false)
|
||||
end
|
||||
|
||||
res(true)
|
||||
|
||||
reporter.sam_report_admin = ply
|
||||
ply.sam_claimed_report = true
|
||||
|
||||
local admins = get_admins(ply)
|
||||
netstream.Start(admins, "ReportClaimed", reporter)
|
||||
table.insert(admins, ply)
|
||||
sam.player.send_message(admins, "report_claimed", {
|
||||
A = ply, T = {reporter, admin = ply}
|
||||
})
|
||||
reporter:sam_send_message("Your report has been {S Green} by {S_2 Blue}.", {
|
||||
S = "claimed", S_2 = ply:SteamName()
|
||||
})
|
||||
|
||||
ix.log.Add(ply, "samReportClaimed", reporter)
|
||||
|
||||
--[[
|
||||
timer.Create("SAM.Reports." .. reporter:EntIndex(), auto_close, 1, function()
|
||||
remove_report_info(reporter)
|
||||
|
||||
if IsValid(reporter) then
|
||||
sam.player.send_message(reporter, "report_aclosed")
|
||||
end
|
||||
end)
|
||||
--]]
|
||||
end, function(ply)
|
||||
return ply:HasPermission("see_admin_chat")
|
||||
end)
|
||||
|
||||
netstream.Hook("DismissReport", function(ply, reporter)
|
||||
if sam.type(reporter) ~= "Player" or not IsValid(reporter) then return end
|
||||
|
||||
remove_report_info(reporter)
|
||||
|
||||
if IsValid(reporter) then
|
||||
sam.player.send_message(get_admins(), "report_dismissed", {
|
||||
A = ply, T = {reporter, admin = ply}
|
||||
})
|
||||
|
||||
reporter:sam_send_message("Your report has been {S Red} by {S_2 Blue}. This may be because it was considered out of staff hands, nonsensical, self-explanatory, not an actual question/issue, or the issue was already dealt with.", {
|
||||
S = "dismissed", S_2 = ply:SteamName()
|
||||
})
|
||||
end
|
||||
end, function(ply)
|
||||
return ply:HasPermission("see_admin_chat")
|
||||
end)
|
||||
|
||||
netstream.Hook("CloseReport", function(ply, reporter)
|
||||
if sam.type(reporter) ~= "Player" or not IsValid(reporter) then return end
|
||||
|
||||
if ply == reporter.sam_report_admin then
|
||||
remove_report_info(reporter)
|
||||
|
||||
if IsValid(reporter) then
|
||||
sam.player.send_message(get_admins(), "report_closed", {
|
||||
A = ply, T = {reporter, admin = ply}
|
||||
})
|
||||
|
||||
reporter:sam_send_message("Your report has been {S Red} by {S_2 Blue}.", {
|
||||
S = "closed", S_2 = ply:SteamName()
|
||||
})
|
||||
end
|
||||
end
|
||||
end, function(ply)
|
||||
return ply:HasPermission("see_admin_chat")
|
||||
end)
|
||||
|
||||
hook.Add("PlayerDisconnected", "SAM.Reports", function(ply)
|
||||
if ply.sam_has_report then
|
||||
remove_report_info(ply)
|
||||
end
|
||||
end)
|
||||
|
||||
local msgs = {
|
||||
"Hey there I need some help",
|
||||
"TP TO ME NOW",
|
||||
"I JUST GOT RDM'D"
|
||||
}
|
||||
concommand.Add("sam_test_reports", function(ply)
|
||||
if IsValid(ply) and not ply:IsSuperAdmin() then return end
|
||||
|
||||
local bots = player.GetBots()
|
||||
if #bots < 2 then
|
||||
for i = 1, 2 - #bots do
|
||||
RunConsoleCommand("bot")
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(1, function()
|
||||
for k, v in ipairs(player.GetBots()) do
|
||||
timer.Create("SAM.TestReports" .. k, k, 3, function()
|
||||
if not IsValid(v) then return end
|
||||
v:sam_set_rank("user")
|
||||
v:Say("!asay srlion " .. table.Random(msgs))
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
29
addons/sam/lua/sam/sh_colors.lua
Normal file
29
addons/sam/lua/sam/sh_colors.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local colors = {
|
||||
Red = Color(244, 67, 54),
|
||||
Blue = Color(13, 130, 223),
|
||||
Green = Color(0, 230, 64),
|
||||
White = Color(236, 240, 241),
|
||||
Black = Color(10, 10, 10)
|
||||
}
|
||||
|
||||
function sam.get_color(name)
|
||||
return colors[name]
|
||||
end
|
||||
|
||||
function sam.add_color(name, color)
|
||||
if isstring(name) and IsColor(color) then
|
||||
colors[name] = color
|
||||
end
|
||||
end
|
||||
184
addons/sam/lua/sam/sh_lang.lua
Normal file
184
addons/sam/lua/sam/sh_lang.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local lang = sam.load_file("sam_language.lua", "sh")
|
||||
|
||||
local original = lang
|
||||
if not isstring(lang) then
|
||||
lang = "english"
|
||||
end
|
||||
|
||||
local lang_path = "sam_languages/" .. lang .. ".lua"
|
||||
|
||||
if not file.Exists(lang_path, "LUA") then
|
||||
lang_path = "sam_languages/english.lua"
|
||||
if not file.Exists(lang_path, "LUA") then
|
||||
-- maybe they deleted english lang????
|
||||
sam.print("SAM is broken!")
|
||||
sam.print("Language '" .. tostring(original) .. "' doesn't exist and 'english' language file doesn't exist")
|
||||
return false
|
||||
else
|
||||
sam.print("Language '" .. tostring(original) .. "' doesn't exist falling back to english")
|
||||
end
|
||||
end
|
||||
|
||||
local Language = sam.load_file(lang_path, "sh_")
|
||||
|
||||
local sub, find = string.sub, string.find
|
||||
|
||||
local white_color = Color(236, 240, 241)
|
||||
|
||||
do
|
||||
local args = {}
|
||||
function sam.add_message_argument(arg, func)
|
||||
if isstring(arg) and isfunction(func) then
|
||||
args[arg] = func
|
||||
end
|
||||
end
|
||||
|
||||
local insert = function(t, v)
|
||||
t.__cnt = t.__cnt + 1
|
||||
t[t.__cnt] = v
|
||||
end
|
||||
|
||||
function sam.format_message(msg, tbl, result, result_n)
|
||||
msg = Language[msg] or msg
|
||||
|
||||
result = result or {}
|
||||
result.__cnt = result_n or 0
|
||||
|
||||
local pos = 0
|
||||
local start, _end, arg, arg2 = nil, 0, nil, nil
|
||||
|
||||
while true do
|
||||
start, _end, arg, arg2 = find(msg, "%{ *([%w_%#]+)([^%{}]-) *%}", _end)
|
||||
if not start then break end
|
||||
|
||||
if pos ~= start then
|
||||
local txt = sub(msg, pos, start - 1)
|
||||
if txt ~= "" then
|
||||
insert(result, white_color)
|
||||
insert(result, txt)
|
||||
end
|
||||
end
|
||||
|
||||
local ma = args[sub(arg, 1, 1)]
|
||||
if not ma then
|
||||
insert(result, "{" .. arg .. " " .. arg2 .. "}")
|
||||
else
|
||||
ma(result, tbl and tbl[arg], arg, unpack(arg2:Trim():Split(" ")))
|
||||
end
|
||||
|
||||
pos = _end + 1
|
||||
end
|
||||
|
||||
if pos <= #msg then
|
||||
insert(result, white_color)
|
||||
insert(result, sub(msg, pos))
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
/*
|
||||
Admin
|
||||
*/
|
||||
sam.add_message_argument("A", function(result, admin)
|
||||
if sam.isconsole(admin) then
|
||||
-- we need to show that it's the real console!!!!!
|
||||
insert(result, Color(236, 240, 241))
|
||||
insert(result, "*")
|
||||
insert(result, Color(13, 130, 223))
|
||||
insert(result, "Console")
|
||||
else
|
||||
if sam.type(admin) == "Player" then
|
||||
if CLIENT and LocalPlayer() == admin then
|
||||
insert(result, Color(255, 215, 0))
|
||||
insert(result, sam.language.get("You"))
|
||||
else
|
||||
insert(result, Color(13, 130, 223))
|
||||
insert(result, admin:Name())
|
||||
end
|
||||
else
|
||||
insert(result, Color(13, 130, 223))
|
||||
insert(result, admin)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
/*
|
||||
Target(s)
|
||||
*/
|
||||
sam.add_message_argument("T", function(result, targets)
|
||||
for k, v in ipairs(sam.get_targets_list(targets)) do
|
||||
insert(result, v)
|
||||
end
|
||||
end)
|
||||
|
||||
/*
|
||||
Value(s)
|
||||
*/
|
||||
sam.add_message_argument("V", function(result, value)
|
||||
insert(result, Color(0, 230, 64))
|
||||
insert(result, tostring(value))
|
||||
end)
|
||||
|
||||
/*
|
||||
Text(s)
|
||||
*/
|
||||
sam.add_message_argument("S", function(result, text, _, color)
|
||||
insert(result, sam.get_color(color) or white_color)
|
||||
insert(result, tostring(text))
|
||||
end)
|
||||
|
||||
-- https://gist.github.com/fernandohenriques/12661bf250c8c2d8047188222cab7e28
|
||||
local hex_rgb = function(hex)
|
||||
local r, g, b
|
||||
if #hex == 4 then
|
||||
r, g, b = tonumber(hex:sub(2, 2), 16) * 17, tonumber(hex:sub(3, 3), 16) * 17, tonumber(hex:sub(4, 4), 16) * 17
|
||||
else
|
||||
r, g, b = tonumber(hex:sub(2, 3), 16), tonumber(hex:sub(4, 5), 16), tonumber(hex:sub(6, 7), 16)
|
||||
end
|
||||
|
||||
if not r or not g or not b then
|
||||
return color_white
|
||||
end
|
||||
|
||||
return Color(r, g, b)
|
||||
end
|
||||
|
||||
/*
|
||||
Colored Text(s)
|
||||
*/
|
||||
sam.add_message_argument("#", function(result, _, color, ...)
|
||||
local text = table.concat({...}, " ")
|
||||
insert(result, hex_rgb(color))
|
||||
insert(result, text)
|
||||
end)
|
||||
end
|
||||
|
||||
function sam.get_message(msg)
|
||||
msg = Language[msg]
|
||||
if not msg then
|
||||
return false
|
||||
else
|
||||
return {Color(236, 240, 241), msg}
|
||||
end
|
||||
end
|
||||
|
||||
function sam.language.get(key)
|
||||
return Language[key]
|
||||
end
|
||||
|
||||
function sam.language.Add(key, value)
|
||||
Language[key] = value
|
||||
end
|
||||
80
addons/sam/lua/sam/sh_motd.lua
Normal file
80
addons/sam/lua/sam/sh_motd.lua
Normal file
@@ -0,0 +1,80 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
local command = sam.command
|
||||
|
||||
if CLIENT then
|
||||
config.add_menu_setting("MOTD URL (Leave empty for no MOTD)", function()
|
||||
local entry = vgui.Create("SAM.TextEntry")
|
||||
entry:SetPlaceholder("")
|
||||
entry:SetNoBar(true)
|
||||
entry:SetConfig("MOTDURL", "")
|
||||
|
||||
return entry
|
||||
end)
|
||||
end
|
||||
|
||||
local motd
|
||||
local load_motd = function()
|
||||
local url = config.get("MOTDURL", "")
|
||||
if url == "" then
|
||||
command.remove_command("motd")
|
||||
hook.Remove("HUDPaint", "SAM.OpenMOTD")
|
||||
return
|
||||
end
|
||||
|
||||
if IsValid(motd) then
|
||||
motd:Remove()
|
||||
end
|
||||
|
||||
command.set_category("Menus")
|
||||
|
||||
command.new("motd")
|
||||
:Help("Open MOTD menu")
|
||||
:OnExecute(function(ply)
|
||||
sam.netstream.Start(ply, "OpenMOTD")
|
||||
end)
|
||||
:End()
|
||||
|
||||
if CLIENT then
|
||||
function sam.menu.open_motd()
|
||||
if IsValid(motd) then
|
||||
motd:Remove()
|
||||
end
|
||||
|
||||
motd = vgui.Create("SAM.Frame")
|
||||
motd:Dock(FILL)
|
||||
motd:DockMargin(40, 40, 40, 40)
|
||||
motd:MakePopup()
|
||||
|
||||
function motd.close.DoClick()
|
||||
motd:Remove()
|
||||
end
|
||||
|
||||
local html = motd:Add("DHTML")
|
||||
html:Dock(FILL)
|
||||
html:OpenURL(url)
|
||||
end
|
||||
|
||||
sam.netstream.Hook("OpenMOTD", function()
|
||||
sam.menu.open_motd()
|
||||
end)
|
||||
|
||||
hook.Add("HUDPaint", "SAM.OpenMOTD", function()
|
||||
sam.menu.open_motd()
|
||||
hook.Remove("HUDPaint", "SAM.OpenMOTD")
|
||||
end)
|
||||
end
|
||||
end
|
||||
config.hook({"MOTDURL"}, load_motd)
|
||||
98
addons/sam/lua/sam/sh_permissions.lua
Normal file
98
addons/sam/lua/sam/sh_permissions.lua
Normal file
@@ -0,0 +1,98 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local permissions = {}
|
||||
|
||||
local give_permission
|
||||
if SERVER then
|
||||
local permissions_to_add = {}
|
||||
|
||||
give_permission = function(name, permission)
|
||||
if sam.ranks.ranks_loaded() then
|
||||
local rank = sam.ranks.get_rank(name)
|
||||
if rank and rank.data.permissions[permission] == nil then
|
||||
sam.ranks.give_permission(name, permission)
|
||||
end
|
||||
else
|
||||
table.insert(permissions_to_add, {name, permission})
|
||||
end
|
||||
end
|
||||
|
||||
hook.Add("SAM.LoadedRanks", "SAM.Command.GivePermissions", function()
|
||||
for k, v in ipairs(permissions_to_add) do
|
||||
give_permission(v[1], v[2])
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local get_next_Other = function()
|
||||
for i, v in ipairs(permissions) do
|
||||
if v.category == "Other" then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #permissions + 1
|
||||
end
|
||||
|
||||
function sam.permissions.add(permission, category, rank)
|
||||
if not sam.isstring(category) then
|
||||
category = "Other"
|
||||
end
|
||||
|
||||
local permission_data = {
|
||||
name = permission,
|
||||
category = category,
|
||||
rank = rank,
|
||||
value = value
|
||||
}
|
||||
|
||||
local index = sam.permissions.get_index(permission)
|
||||
if not index then
|
||||
if category ~= "Other" then
|
||||
table.insert(permissions, get_next_Other(), permission_data)
|
||||
else
|
||||
table.insert(permissions, permission_data)
|
||||
end
|
||||
hook.Call("SAM.AddedPermission", nil, permission, category, rank, value)
|
||||
else
|
||||
permissions[index] = permission_data
|
||||
hook.Call("SAM.PermissionModified", nil, permission, category, rank, value)
|
||||
end
|
||||
|
||||
if SERVER and rank then
|
||||
give_permission(rank, permission)
|
||||
end
|
||||
end
|
||||
|
||||
function sam.permissions.get_index(permission)
|
||||
for i, v in ipairs(permissions) do
|
||||
if v.name == permission then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function sam.permissions.remove(permission)
|
||||
local index = sam.permissions.get_index(permission)
|
||||
if index then
|
||||
table.remove(permissions, index)
|
||||
hook.Call("SAM.RemovedPermission", nil, permission)
|
||||
end
|
||||
end
|
||||
|
||||
function sam.permissions.exists(permission)
|
||||
return sam.permissions.get_index(permission) and true or false
|
||||
end
|
||||
|
||||
function sam.permissions.get()
|
||||
return permissions
|
||||
end
|
||||
251
addons/sam/lua/sam/sh_restrictions.lua
Normal file
251
addons/sam/lua/sam/sh_restrictions.lua
Normal file
@@ -0,0 +1,251 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local loaded = false
|
||||
local load_restrictions = function()
|
||||
local sam = sam
|
||||
local config = sam.config
|
||||
local hook = hook
|
||||
local SERVER = SERVER
|
||||
|
||||
if CLIENT then
|
||||
local add_setting = function(body, title, key)
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:SetLabel(title)
|
||||
|
||||
local enable = setting:Add("SAM.ToggleButton")
|
||||
enable:SetConfig(key, true)
|
||||
|
||||
return setting
|
||||
end
|
||||
|
||||
config.add_menu_setting("Restrictions", function(body)
|
||||
local setting = body:Add("SAM.LabelPanel")
|
||||
setting:Dock(TOP)
|
||||
setting:DockMargin(8, 6, 8, 0)
|
||||
setting:SetLabel("Restrictions (Check these settings in ranks' permissions)")
|
||||
|
||||
local setting_body = body:Add("Panel")
|
||||
setting_body:Dock(TOP)
|
||||
setting_body:DockMargin(8, 6, 8, 0)
|
||||
setting_body:DockPadding(8, 0, 8, 0)
|
||||
|
||||
add_setting(setting_body, "Tool (Eg. using button tool)", "Restrictions.Tool")
|
||||
add_setting(setting_body, "Spawning (Eg. spawning props)", "Restrictions.Spawning")
|
||||
add_setting(setting_body, "Limits (Eg. how many props can you spawn)", "Restrictions.Limits")
|
||||
|
||||
function setting_body:PerformLayout()
|
||||
setting_body:SizeToChildren(false, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local tools = weapons.GetStored("gmod_tool")
|
||||
if sam.istable(tools) then
|
||||
if config.get("Restrictions.Tool", true) then
|
||||
for k, v in pairs(tools.Tool) do
|
||||
sam.permissions.add(v.Mode, "Tools - " .. (v.Category or "Other"), "user")
|
||||
end
|
||||
|
||||
hook.Add("CanTool", "SAM.Module.Restrictions", function(ply, _, tool)
|
||||
if not ply:HasPermission(tool) then
|
||||
if CLIENT and sam.player.check_cooldown(ply, "ToolNoPermission", 0.1) then
|
||||
ply:sam_send_message("You don't have permission to use this tool.")
|
||||
end
|
||||
return false
|
||||
end
|
||||
end)
|
||||
else
|
||||
for k, v in pairs(tools.Tool) do
|
||||
sam.permissions.remove(v.Mode)
|
||||
end
|
||||
|
||||
hook.Remove("CanTool", "SAM.Module.Restrictions")
|
||||
end
|
||||
end
|
||||
|
||||
sam.permissions.add("admin_weapons", "Spawning", "superadmin")
|
||||
|
||||
local function no_permission(ply, name)
|
||||
ply:sam_play_sound("buttons/button10.wav")
|
||||
ply:sam_send_message("You don't have permission to spawn {S Blue}.", {
|
||||
S = name
|
||||
})
|
||||
end
|
||||
|
||||
local spawning = {
|
||||
PlayerSpawnProp = {
|
||||
name = "props",
|
||||
permission = "user",
|
||||
call_gm = true,
|
||||
},
|
||||
PlayerGiveSWEP = {
|
||||
name = "give_weapons",
|
||||
cb = function(_, ply, _, wep)
|
||||
if wep.sam_AdminOnly and not ply:HasPermission("admin_weapons") then
|
||||
no_permission(ply, "admin weapons")
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
hook = sam.hook_first,
|
||||
},
|
||||
PlayerSpawnSWEP = {
|
||||
name = "spawn_weapons",
|
||||
cb = function(_, ply, _, wep)
|
||||
if wep.sam_AdminOnly and not ply:HasPermission("admin_weapons") then
|
||||
no_permission(ply, "admin weapons")
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
hook = sam.hook_first,
|
||||
},
|
||||
-- PlayerSpawnSENT = {
|
||||
-- name = "entities",
|
||||
-- check_limit = "sents"
|
||||
-- },
|
||||
PlayerSpawnNPC = {
|
||||
name = "npcs",
|
||||
check_limit = "npcs",
|
||||
},
|
||||
PlayerSpawnVehicle = {
|
||||
name = "vehicles",
|
||||
check_limit = "vehicles",
|
||||
},
|
||||
PlayerSpawnRagdoll = {
|
||||
name = "ragdolls",
|
||||
permission = "user",
|
||||
}
|
||||
}
|
||||
|
||||
local override_lists = {
|
||||
"Weapon",
|
||||
-- "SpawnableEntities"
|
||||
}
|
||||
|
||||
local function LimitReachedProcess(ply, str)
|
||||
if not IsValid(ply) then return true end
|
||||
return ply:CheckLimit(str)
|
||||
end
|
||||
|
||||
local GAMEMODE = GAMEMODE
|
||||
if config.get("Restrictions.Spawning", true) then
|
||||
for k, v in pairs(spawning) do
|
||||
local name = v
|
||||
local permission = "superadmin"
|
||||
local check
|
||||
local check_limit
|
||||
local hook = sam.hook_last
|
||||
if istable(v) then
|
||||
name = v.name
|
||||
permission = v.permission or permission
|
||||
if v.call_gm then
|
||||
check = GAMEMODE[k]
|
||||
elseif v.cb then
|
||||
check = v.cb
|
||||
end
|
||||
hook = v.hook or hook
|
||||
check_limit = v.check_limit
|
||||
end
|
||||
|
||||
sam.permissions.add(name, "Spawning", permission)
|
||||
|
||||
if SERVER then
|
||||
hook(k, "SAM.Spawning." .. k .. name, function(ply, ...)
|
||||
if not ply:HasPermission(name) then
|
||||
no_permission(ply, name)
|
||||
return false
|
||||
end
|
||||
|
||||
if check_limit then
|
||||
return LimitReachedProcess(ply, check_limit)
|
||||
end
|
||||
|
||||
if check then
|
||||
return check(GAMEMODE, ply, ...)
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #override_lists do
|
||||
for k, v in pairs(list.GetForEdit(override_lists[i])) do
|
||||
v.sam_AdminOnly = v.sam_AdminOnly or v.AdminOnly
|
||||
v.AdminOnly = false
|
||||
end
|
||||
end
|
||||
else
|
||||
sam.permissions.add("admin_weapons")
|
||||
|
||||
for k, v in pairs(spawning) do
|
||||
sam.permissions.remove(istable(v) and v.name or v)
|
||||
|
||||
if SERVER then
|
||||
hook.Remove(k, "SAM.Spawning." .. k)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #override_lists do
|
||||
for k, v in pairs(list.GetForEdit(override_lists[i])) do
|
||||
if v.sam_AdminOnly then
|
||||
v.AdminOnly = v.sam_AdminOnly
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local PLAYER = FindMetaTable("Player")
|
||||
if config.get("Restrictions.Limits", true) then
|
||||
local get_limit = sam.ranks.get_limit
|
||||
function PLAYER:GetLimit(limit_type)
|
||||
return get_limit(self:sam_getrank(), limit_type)
|
||||
end
|
||||
|
||||
sam.hook_first("PlayerCheckLimit", "SAM.PlayerCheckLimit", function(ply, limit_type, count)
|
||||
local ply_limit = ply:GetLimit(limit_type)
|
||||
if ply_limit < 0 then return true end
|
||||
|
||||
if count > ply_limit - 1 then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
sam.limit_types = {}
|
||||
for _, limit_type in SortedPairs(cleanup.GetTable(), true) do
|
||||
local cvar = GetConVar("sbox_max" .. limit_type)
|
||||
if cvar then
|
||||
table.insert(sam.limit_types, limit_type)
|
||||
end
|
||||
end
|
||||
else
|
||||
sam.limit_types = nil
|
||||
PLAYER.GetLimit = nil
|
||||
hook.Remove("PlayerCheckLimit", "SAM.PlayerCheckLimit")
|
||||
end
|
||||
|
||||
if not loaded then
|
||||
loaded = true
|
||||
hook.Call("SAM.LoadedRestrictions")
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(5, function()
|
||||
if GAMEMODE.IsSandboxDerived then
|
||||
sam.config.hook({"Restrictions.Tool", "Restrictions.Spawning", "Restrictions.Limits"}, load_restrictions)
|
||||
end
|
||||
end)
|
||||
350
addons/sam/lua/sam/sh_util.lua
Normal file
350
addons/sam/lua/sam/sh_util.lua
Normal file
@@ -0,0 +1,350 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local sam = sam
|
||||
|
||||
function sam.parse_args(str)
|
||||
local args = {}
|
||||
local tmp, in_quotes = "", false
|
||||
for i = 1, #str do
|
||||
local char = str:sub(i, i)
|
||||
if char == "\"" then
|
||||
-- i could use string.find to find the next double quotes but thats gonna be overkill
|
||||
in_quotes = not in_quotes
|
||||
if tmp ~= "" or not in_quotes then
|
||||
args[#args + 1], tmp = tmp, ""
|
||||
end
|
||||
elseif char ~= " " or in_quotes then
|
||||
tmp = tmp .. char
|
||||
elseif tmp ~= "" then
|
||||
args[#args + 1], tmp = tmp, ""
|
||||
end
|
||||
end
|
||||
if tmp ~= "" then
|
||||
args[#args + 1] = tmp
|
||||
end
|
||||
return args, #args
|
||||
end
|
||||
|
||||
function sam.get_targets_list(targets)
|
||||
if sam.isstring(targets) then
|
||||
return {Color(244, 67, 54), targets}
|
||||
end
|
||||
|
||||
local len = #targets
|
||||
|
||||
if len == player.GetCount() and len > 1 then
|
||||
return {Color(244, 67, 54), sam.language.get("Everyone")}
|
||||
end
|
||||
|
||||
local admin = targets.admin
|
||||
local result = {}
|
||||
local white = Color(236, 240, 241)
|
||||
for i = 1, len do
|
||||
local target = targets[i]
|
||||
|
||||
if CLIENT and LocalPlayer() == target then
|
||||
table.insert(result, Color(255, 215, 0))
|
||||
if admin ~= LocalPlayer() then
|
||||
table.insert(result, sam.language.get("You"))
|
||||
else
|
||||
table.insert(result, sam.language.get("Yourself"))
|
||||
end
|
||||
elseif admin ~= target then
|
||||
local name
|
||||
if sam.isentity(target) and target.Name then
|
||||
name = target:Name()
|
||||
else
|
||||
name = "Unknown"
|
||||
table.insert(result, white)
|
||||
table.insert(result, "*")
|
||||
end
|
||||
|
||||
table.insert(result, Color(244, 67, 54))
|
||||
table.insert(result, name)
|
||||
else
|
||||
table.insert(result, Color(255, 215, 0))
|
||||
table.insert(result, sam.language.get("Themself"))
|
||||
end
|
||||
|
||||
if i ~= len then
|
||||
table.insert(result, white)
|
||||
table.insert(result, ",")
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function sam.is_steamid(id, err) -- https://stackoverflow.com/questions/6724268/check-if-input-matches-steam-id-format
|
||||
if sam.isstring(id) and id:match("^STEAM_[0-5]:[0-1]:[0-9]+$") ~= nil then
|
||||
return true
|
||||
else
|
||||
return err and error("invalid steamid", 2) or false
|
||||
end
|
||||
end
|
||||
|
||||
function sam.is_steamid64(id, err)
|
||||
if sam.isstring(id)
|
||||
and tonumber(id)
|
||||
and id:sub(1, 7) == "7656119"
|
||||
and (#id == 17 or #id == 18) then
|
||||
return true
|
||||
else
|
||||
return err and error("invalid steamid64", 2) or false
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local console = {}
|
||||
|
||||
do
|
||||
local return_console = function()
|
||||
return "Console"
|
||||
end
|
||||
for _, v in ipairs({"SteamID", "SteamID64", "Name", "Nick", "Name"}) do
|
||||
console[v] = return_console
|
||||
end
|
||||
setmetatable(console, {
|
||||
__tostring = return_console,
|
||||
MetaName = "console"
|
||||
})
|
||||
end
|
||||
|
||||
function console.IsAdmin()
|
||||
return true
|
||||
end
|
||||
|
||||
function console.IsSuperAdmin()
|
||||
return true
|
||||
end
|
||||
|
||||
function console:IsUserGroup(name)
|
||||
return name == "superadmin"
|
||||
end
|
||||
|
||||
function console.GetUserGroup()
|
||||
return "superadmin"
|
||||
end
|
||||
|
||||
function console.sam_getrank()
|
||||
return "superadmin"
|
||||
end
|
||||
|
||||
function console.HasPermission()
|
||||
return true
|
||||
end
|
||||
|
||||
function console.CanTarget()
|
||||
return true
|
||||
end
|
||||
|
||||
function console.CanTargetRank()
|
||||
return true
|
||||
end
|
||||
|
||||
function console.GetBanLimit()
|
||||
return 0
|
||||
end
|
||||
|
||||
function console.SetUserGroup()
|
||||
end
|
||||
|
||||
function sam.isconsole(v)
|
||||
return v == console
|
||||
end
|
||||
|
||||
sam.console = console
|
||||
end
|
||||
|
||||
do
|
||||
local times = {
|
||||
"year"; 525600,
|
||||
"month"; 43800,
|
||||
"week"; 10080,
|
||||
"day"; 1440,
|
||||
"hour"; 60,
|
||||
"minute"; 1
|
||||
}
|
||||
|
||||
for i = 1, #times, 2 do
|
||||
times[i] = " " .. times[i]
|
||||
end
|
||||
|
||||
local floor = math.floor
|
||||
function sam.format_length(mins) -- Thanks to this guide https://stackoverflow.com/a/21323783
|
||||
if mins <= 0 then
|
||||
return "Indefinitely"
|
||||
elseif mins <= 1 then
|
||||
return "1 minute"
|
||||
end
|
||||
|
||||
local str = ""
|
||||
for i = 1, #times, 2 do
|
||||
local n1, n2 = times[i + 1]
|
||||
n2, mins = floor(mins / n1), mins % n1
|
||||
|
||||
if n2 > 0 then
|
||||
if str ~= "" then
|
||||
if mins == 0 then
|
||||
str = str .. " and "
|
||||
else
|
||||
str = str .. ", "
|
||||
end
|
||||
end
|
||||
str = str .. n2 .. times[i]
|
||||
if n2 > 1 then
|
||||
str = str .. "s"
|
||||
end
|
||||
end
|
||||
|
||||
if mins == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local times = {
|
||||
m = 1,
|
||||
h = 60,
|
||||
d = 1440,
|
||||
w = 10080,
|
||||
mo = 43800,
|
||||
y = 525600
|
||||
}
|
||||
|
||||
function sam.parse_length(length)
|
||||
local time, found = tonumber(length), false
|
||||
if sam.isnumber(length) then
|
||||
time, found = length, true
|
||||
elseif time then
|
||||
found = true
|
||||
else
|
||||
time = 0
|
||||
for t, u in length:gmatch("(%d+)(%a+)") do
|
||||
u = times[u]
|
||||
if u then
|
||||
time = time + (u * t)
|
||||
found = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if not found then return false end
|
||||
return math.Clamp(time, 0, 31536000)
|
||||
end
|
||||
|
||||
local times2 = {}
|
||||
for k, v in SortedPairsByValue(times, true) do
|
||||
table.insert(times2, k)
|
||||
table.insert(times2, v)
|
||||
end
|
||||
|
||||
local floor = math.floor
|
||||
function sam.reverse_parse_length(mins) -- Thanks to this guide https://stackoverflow.com/a/21323783
|
||||
if mins <= 0 then
|
||||
return "0"
|
||||
elseif mins <= 1 then
|
||||
return "1m"
|
||||
end
|
||||
|
||||
local str = ""
|
||||
for i = 1, #times2, 2 do
|
||||
local n1, n2 = times2[i + 1]
|
||||
n2, mins = floor(mins / n1), mins % n1
|
||||
|
||||
if n2 > 0 then
|
||||
if str ~= "" then
|
||||
str = str .. " "
|
||||
end
|
||||
str = str .. n2 .. times2[i]
|
||||
end
|
||||
|
||||
if mins == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
if SERVER then
|
||||
function sam.hook_call(event, ...)
|
||||
hook.Call(event, nil, ...)
|
||||
sam.netstream.Start(nil, "HookCall", event, ...)
|
||||
end
|
||||
|
||||
function sam.client_hook_call(event, ...)
|
||||
sam.netstream.Start(nil, "HookCall", event, ...)
|
||||
end
|
||||
else
|
||||
local function hook_call(event, ...)
|
||||
hook.Call(event, nil, ...)
|
||||
end
|
||||
sam.netstream.Hook("HookCall", hook_call)
|
||||
end
|
||||
end
|
||||
|
||||
if SERVER then
|
||||
local maps = {}
|
||||
|
||||
for k, v in ipairs(file.Find("maps/*.bsp", "GAME")) do
|
||||
maps[k] = v:sub(1, -5):lower()
|
||||
end
|
||||
|
||||
sam.set_global("Maps", maps)
|
||||
end
|
||||
|
||||
function sam.is_valid_map(name)
|
||||
local maps = sam.get_global("Maps")
|
||||
if name:sub(-4) == ".bsp" then
|
||||
name = name:sub(1, -5)
|
||||
end
|
||||
name = name:lower()
|
||||
for i = 1, #maps do
|
||||
if maps[i] == name then
|
||||
return name
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function sam.is_valid_gamemode(name)
|
||||
name = name:lower()
|
||||
local gamemodes = engine.GetGamemodes()
|
||||
for i = 1, #gamemodes do
|
||||
local gamemode = gamemodes[i]
|
||||
if sam.isstring(gamemode.name) and gamemode.name:lower() == name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function sam.hook_first(event, name, func)
|
||||
if HOOK_HIGH then
|
||||
return hook.Add(event, name, func, HOOK_HIGH)
|
||||
end
|
||||
|
||||
return hook.Add(event, name, func)
|
||||
end
|
||||
|
||||
function sam.hook_last(event, name, func)
|
||||
if HOOK_LOW then
|
||||
return hook.Add(event, name, func, HOOK_LOW)
|
||||
end
|
||||
|
||||
return hook.Add(event, name, func)
|
||||
end
|
||||
425
addons/sam/lua/sam/sv_sql.lua
Normal file
425
addons/sam/lua/sam/sv_sql.lua
Normal file
@@ -0,0 +1,425 @@
|
||||
--[[
|
||||
| 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 SAM_LOADED then return end
|
||||
|
||||
local SQL = sam.SQL
|
||||
SQL.SetAddonName("SAM")
|
||||
|
||||
if file.Exists("sam_sql_config.lua", "LUA") then
|
||||
local config = sam.load_file("sam_sql_config.lua", "sv_")
|
||||
if sam.istable(config) then
|
||||
SQL.SetConfig(config)
|
||||
end
|
||||
end
|
||||
|
||||
local current_version = 0
|
||||
|
||||
local versions = {}
|
||||
|
||||
local add_version = function(version, fn)
|
||||
table.insert(versions, {version, fn})
|
||||
end
|
||||
|
||||
local update = function()
|
||||
for _, v in ipairs(versions) do
|
||||
local version = v[1]
|
||||
if version > current_version then
|
||||
v[2]()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local check_updates = function()
|
||||
-- SQL.Query([[DROP TABLE IF EXISTS `sam_players`]])
|
||||
-- SQL.Query([[DROP TABLE IF EXISTS `sam_bans`]])
|
||||
-- SQL.Query([[DROP TABLE IF EXISTS `sam_ranks`]])
|
||||
-- SQL.Query([[DROP TABLE IF EXISTS `sam_version`]])
|
||||
-- SQL.Query([[DROP TABLE IF EXISTS `sam_config`]])
|
||||
|
||||
SQL.TableExists("sam_version", function(exists)
|
||||
if exists then
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
`version`
|
||||
FROM
|
||||
`sam_version`
|
||||
]], function(version_data)
|
||||
current_version = tonumber(version_data.version)
|
||||
|
||||
if sam.version > current_version then
|
||||
update()
|
||||
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_version`
|
||||
SET
|
||||
`version` = {1}
|
||||
]], {sam.version}):wait()
|
||||
end
|
||||
|
||||
hook.Call("SAM.DatabaseLoaded")
|
||||
end, true):wait()
|
||||
else
|
||||
update()
|
||||
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_version`(
|
||||
`version` SMALLINT(255)
|
||||
)
|
||||
]])
|
||||
|
||||
SQL.FQuery([[
|
||||
INSERT INTO
|
||||
`sam_version`(`version`)
|
||||
VALUES
|
||||
({1})
|
||||
]], {sam.version}):wait()
|
||||
|
||||
hook.Call("SAM.DatabaseLoaded")
|
||||
end
|
||||
end):wait()
|
||||
end
|
||||
|
||||
add_version(100, function()
|
||||
local auto_increment = SQL.IsMySQL() and "AUTO_INCREMENT" or ""
|
||||
|
||||
SQL.FQuery([[
|
||||
CREATE TABLE `sam_players`(
|
||||
`id` INT PRIMARY KEY {1f},
|
||||
`steamid` VARCHAR(32),
|
||||
`name` VARCHAR(255),
|
||||
`rank` VARCHAR(30),
|
||||
`expiry_date` INT UNSIGNED,
|
||||
`first_join` INT UNSIGNED,
|
||||
`last_join` INT UNSIGNED,
|
||||
`play_time` MEDIUMINT UNSIGNED
|
||||
)
|
||||
]], {auto_increment})
|
||||
|
||||
SQL.FQuery([[
|
||||
CREATE TABLE `sam_bans`(
|
||||
`id` INT PRIMARY KEY {1f},
|
||||
`steamid` VARCHAR(32),
|
||||
`name` VARCHAR(255),
|
||||
`reason` VARCHAR(255),
|
||||
`admin` VARCHAR(32),
|
||||
`unban_date` INT UNSIGNED
|
||||
)
|
||||
]], {auto_increment})
|
||||
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_ranks`(
|
||||
`name` VARCHAR(30),
|
||||
`immunity` TINYINT UNSIGNED,
|
||||
`ban_limit` INT UNSIGNED,
|
||||
`data` TEXT
|
||||
)
|
||||
]])
|
||||
end)
|
||||
|
||||
add_version(110, function()
|
||||
SQL.Query([[
|
||||
ALTER TABLE `sam_ranks`
|
||||
ADD `inherit` VARCHAR(30)
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`inherit` = 'user'
|
||||
WHERE
|
||||
`name` != 'user'
|
||||
]])
|
||||
|
||||
if not SQL.IsMySQL() then
|
||||
SQL.Query([[
|
||||
ALTER TABLE
|
||||
`sam_players` RENAME TO `tmp_sam_players`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_players`(
|
||||
`id` INT PRIMARY KEY,
|
||||
`steamid` VARCHAR(32),
|
||||
`name` VARCHAR(255),
|
||||
`rank` VARCHAR(30),
|
||||
`expiry_date` INT UNSIGNED,
|
||||
`first_join` INT UNSIGNED,
|
||||
`last_join` INT UNSIGNED,
|
||||
`play_time` INT UNSIGNED
|
||||
)
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`id`,
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
SELECT
|
||||
`id`,
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time` * 60
|
||||
FROM
|
||||
`tmp_sam_players`
|
||||
]])
|
||||
|
||||
SQL.Query([[DROP TABLE `tmp_sam_players`]])
|
||||
else
|
||||
SQL.Query([[
|
||||
ALTER TABLE
|
||||
`sam_players`
|
||||
MODIFY
|
||||
`play_time` INT UNSIGNED
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
UPDATE `sam_players`
|
||||
SET `play_time` = `play_time` * 60
|
||||
]])
|
||||
end
|
||||
end)
|
||||
|
||||
add_version(112, function()
|
||||
if not SQL.IsMySQL() then
|
||||
SQL.Query([[
|
||||
ALTER TABLE
|
||||
`sam_players` RENAME TO `tmp_sam_players`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
ALTER TABLE
|
||||
`sam_bans` RENAME TO `tmp_sam_bans`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_players`(
|
||||
`id` INTEGER PRIMARY KEY,
|
||||
`steamid` VARCHAR(32),
|
||||
`name` VARCHAR(255),
|
||||
`rank` VARCHAR(30),
|
||||
`expiry_date` INT UNSIGNED,
|
||||
`first_join` INT UNSIGNED,
|
||||
`last_join` INT UNSIGNED,
|
||||
`play_time` INT UNSIGNED
|
||||
)
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_bans`(
|
||||
`id` INTEGER PRIMARY KEY,
|
||||
`steamid` VARCHAR(32),
|
||||
`name` VARCHAR(255),
|
||||
`reason` VARCHAR(255),
|
||||
`admin` VARCHAR(32),
|
||||
`unban_date` INT UNSIGNED
|
||||
)
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
INSERT INTO
|
||||
`sam_players`(
|
||||
`id`,
|
||||
`steamid`,
|
||||
`name`,
|
||||
`rank`,
|
||||
`expiry_date`,
|
||||
`first_join`,
|
||||
`last_join`,
|
||||
`play_time`
|
||||
)
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
`tmp_sam_players`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`id`,
|
||||
`steamid`,
|
||||
`name`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
`tmp_sam_bans`
|
||||
]])
|
||||
|
||||
SQL.Query([[DROP TABLE `tmp_sam_players`]])
|
||||
SQL.Query([[DROP TABLE `tmp_sam_bans`]])
|
||||
end
|
||||
end)
|
||||
|
||||
add_version(114, function()
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
`name`,
|
||||
`data`
|
||||
FROM
|
||||
`sam_ranks`
|
||||
]], function(ranks)
|
||||
for k, v in ipairs(ranks) do
|
||||
local name, permissions = v.name, v.data
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`data` = {1}
|
||||
WHERE
|
||||
`name` == {2}
|
||||
]], {sam.pon.encode({permissions = sam.pon.decode(permissions), limits = {}}), name})
|
||||
end
|
||||
end):wait()
|
||||
end)
|
||||
|
||||
add_version(120, function()
|
||||
if SQL.IsMySQL() then
|
||||
SQL.Query([[ALTER TABLE `sam_bans` ADD UNIQUE (`steamid`)]])
|
||||
SQL.Query([[ALTER TABLE `sam_bans` DROP `name`]])
|
||||
else
|
||||
SQL.Query([[
|
||||
ALTER TABLE
|
||||
`sam_bans` RENAME TO `tmp_sam_bans`
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_bans`(
|
||||
`id` INTEGER PRIMARY KEY,
|
||||
`steamid` VARCHAR(32) UNIQUE,
|
||||
`reason` VARCHAR(255),
|
||||
`admin` VARCHAR(32),
|
||||
`unban_date` INT UNSIGNED
|
||||
)
|
||||
]])
|
||||
|
||||
SQL.Query([[
|
||||
INSERT INTO
|
||||
`sam_bans`(
|
||||
`id`,
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
)
|
||||
SELECT
|
||||
`id`,
|
||||
`steamid`,
|
||||
`reason`,
|
||||
`admin`,
|
||||
`unban_date`
|
||||
FROM
|
||||
`tmp_sam_bans`
|
||||
]])
|
||||
|
||||
SQL.Query([[DROP TABLE `tmp_sam_bans`]])
|
||||
end
|
||||
|
||||
SQL.Query([[
|
||||
SELECT
|
||||
`name`,
|
||||
`data`
|
||||
FROM
|
||||
`sam_ranks`
|
||||
]], function(ranks)
|
||||
for k, v in ipairs(ranks) do
|
||||
SQL.FQuery([[
|
||||
UPDATE
|
||||
`sam_ranks`
|
||||
SET
|
||||
`data` = {1}
|
||||
WHERE
|
||||
`name` = {2}
|
||||
]], {util.TableToJSON(sam.pon.decode(v.data)), v.name})
|
||||
end
|
||||
end
|
||||
):wait()
|
||||
end)
|
||||
|
||||
add_version(135, function()
|
||||
if not SQL.IsMySQL() then return end
|
||||
SQL.Query([[ALTER TABLE `sam_ranks` MODIFY `name` BLOB;]])
|
||||
end)
|
||||
|
||||
add_version(138, function()
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_config`(
|
||||
`key` VARCHAR(40) UNIQUE,
|
||||
`value` TEXT
|
||||
)
|
||||
]])
|
||||
end)
|
||||
|
||||
add_version(141, function()
|
||||
if not SQL.IsMySQL() then return end
|
||||
SQL.Query([[ALTER TABLE `sam_config` MODIFY `value` BLOB;]])
|
||||
end)
|
||||
|
||||
add_version(143, function()
|
||||
SQL.Query([[DROP TABLE IF EXISTS `sam_config`]])
|
||||
SQL.Query([[
|
||||
CREATE TABLE `sam_config`(
|
||||
`key` VARCHAR(40) UNIQUE,
|
||||
`value` BLOB
|
||||
)
|
||||
]])
|
||||
end)
|
||||
|
||||
add_version(147, function()
|
||||
SQL.Query([[CREATE UNIQUE INDEX sam_players_steamid_index ON `sam_players` (`steamid`);]])
|
||||
SQL.Query([[CREATE INDEX sam_bans_steamid_index ON `sam_bans` (`steamid`);]])
|
||||
end)
|
||||
|
||||
hook.Add("SAM.DatabaseConnected", "CheckUpdates", check_updates)
|
||||
|
||||
hook.Add("SAM.DatabaseLoaded", "SAM.DatabaseLoaded", function()
|
||||
sam.DatabaseLoaded = true
|
||||
sam.print("Connected to database and loaded data successfully.")
|
||||
end)
|
||||
|
||||
SQL.Connect()
|
||||
|
||||
timer.Create("SAM.CheckForUpdates", 60 * 20, 0, function()
|
||||
http.Fetch("https://raw.githubusercontent.com/Srlion/SAM-Docs/master/version.txt", function(version, _, _, code)
|
||||
if code ~= 200 then return end
|
||||
|
||||
version = version:gsub("%s", "")
|
||||
|
||||
if sam.version >= (tonumber(version) or 0) then return end
|
||||
|
||||
sam.print(Color(255, 0, 0), "New update is available for SAM to download")
|
||||
|
||||
local superadmins = {}
|
||||
for k, v in ipairs(player.GetAll()) do
|
||||
if v:IsSuperAdmin() then
|
||||
table.insert(superadmins, v)
|
||||
end
|
||||
end
|
||||
|
||||
sam.player.add_text(superadmins, Color(255, 0, 0), "New update is available for SAM to download")
|
||||
end)
|
||||
end)
|
||||
11
addons/sam/lua/sam_language.lua
Normal file
11
addons/sam/lua/sam_language.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return "english"
|
||||
292
addons/sam/lua/sam_languages/english.lua
Normal file
292
addons/sam/lua/sam_languages/english.lua
Normal file
@@ -0,0 +1,292 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
return {
|
||||
You = "You",
|
||||
Yourself = "Yourself",
|
||||
Themself = "Themself",
|
||||
Everyone = "Everyone",
|
||||
|
||||
cant_use_as_console = "You need to be a player to use {S Red} command!",
|
||||
no_permission = "You don't have permission to use '{S Red}'!",
|
||||
|
||||
cant_target_multi_players = "You can't target multiple players using this command!",
|
||||
invalid_id = "Invalid id ({S Red})!",
|
||||
cant_target_player = "You can't target {S Red}!",
|
||||
cant_target_self = "You can't target your self using {S Red} command!",
|
||||
player_id_not_found = "Player with id {S Red} is not found!",
|
||||
found_multi_players = "Found multiple players: {T}!",
|
||||
cant_find_target = "Can't find a player to target ({S Red})!",
|
||||
|
||||
invalid = "Invalid {S} ({S_2 Red})",
|
||||
default_reason = "none",
|
||||
|
||||
menu_help = "Open admin mod menu.",
|
||||
|
||||
-- Chat Commands
|
||||
pm_to = "PM to {T}: {V}",
|
||||
pm_from = "PM from {A}: {V}",
|
||||
pm_help = "Send a personal message (PM) to a player.",
|
||||
|
||||
to_admins = "{A} to admins: {V}",
|
||||
asay_help = "Send a message to admins.",
|
||||
|
||||
mute = "{A} muted {T} for {V}. ({V_2})",
|
||||
mute_help = "Stop player(s) from sending messages in chat.",
|
||||
|
||||
unmute = "{A} unmuted {T}.",
|
||||
unmute_help = "Unmute player(s).",
|
||||
|
||||
you_muted = "You are muted.",
|
||||
|
||||
gag = "{A} gagged {T} for {V}. ({V_2})",
|
||||
gag_help = "Stop player(s) from speaking.",
|
||||
|
||||
ungag = "{A} ungagged {T}.",
|
||||
ungag_help = "Ungag player(s).",
|
||||
|
||||
-- Fun Commands
|
||||
slap = "{A} slapped {T}.",
|
||||
slap_damage = "{A} slapped {T} with {V} damage.",
|
||||
slap_help = "Slap asses.",
|
||||
|
||||
slay = "{A} slayed {T}.",
|
||||
slay_help = "Slay player(s).",
|
||||
|
||||
set_hp = "{A} set the hp for {T} to {V}.",
|
||||
hp_help = "Set health for player(s).",
|
||||
|
||||
set_armor = "{A} set the armor for {T} to {V}.",
|
||||
armor_help = "Set armor for player(s).",
|
||||
|
||||
ignite = "{A} ignited {T} for {V}.",
|
||||
ignite_help = "Ignite player(s).",
|
||||
|
||||
unignite = "{A} extinguished {T}.",
|
||||
unignite_help = "Extinguish player(s).",
|
||||
|
||||
god = "{A} enabled god mode for {T}.",
|
||||
god_help = "Enable god mode for player(s).",
|
||||
|
||||
ungod = "{A} disabled god mode for {T}.",
|
||||
ungod_help = "Disable god mode for player(s).",
|
||||
|
||||
freeze = "{A} froze {T}.",
|
||||
freeze_help = "Freeze player(s).",
|
||||
|
||||
unfreeze = "{A} unfroze {T}.",
|
||||
unfreeze_help = "Unfreeze player(s).",
|
||||
|
||||
cloak = "{A} cloaked {T}.",
|
||||
cloak_help = "Cloak player(s).",
|
||||
|
||||
uncloak = "{A} uncloaked {T}.",
|
||||
uncloak_help = "Uncloak player(s).",
|
||||
|
||||
jail = "{A} jailed {T} for {V}. ({V_2})",
|
||||
jail_help = "Jail player(s).",
|
||||
|
||||
unjail = "{A} unjailed {T}.",
|
||||
unjail_help = "Unjail player(s).",
|
||||
|
||||
strip = "{A} stripped weapons from {T}.",
|
||||
strip_help = "Strip weapons from player(s).",
|
||||
|
||||
respawn = "{A} respawned {T}.",
|
||||
respawn_help = "Respawn player(s).",
|
||||
|
||||
setmodel = "{A} set the model for {T} to {V}.",
|
||||
setmodel_help = "Change player(s)'s model.",
|
||||
|
||||
giveammo = "{A} gave {T} {V} ammo.",
|
||||
giveammo_help = "Give player(s) ammo.",
|
||||
|
||||
scale = "{A} set model scale for {T} to {V}.",
|
||||
scale_help = "Scale player(s).",
|
||||
|
||||
freezeprops = "{A} froze all props.",
|
||||
freezeprops_help = "Freezes all props on the map.",
|
||||
|
||||
-- Teleport Commands
|
||||
dead = "You are dead!",
|
||||
leave_car = "Leave the vehicle first!",
|
||||
|
||||
bring = "{A} teleported {T}.",
|
||||
bring_help = "Bring a player.",
|
||||
|
||||
goto = "{A} teleported to {T}.",
|
||||
goto_help = "Goto a player.",
|
||||
|
||||
no_location = "No previous location to return {T} to.",
|
||||
returned = "{A} returned {T}.",
|
||||
return_help = "Return a player to where he was.",
|
||||
|
||||
-- User Management Commands
|
||||
setrank = "{A} set the rank for {T} to {V} for {V_2}.",
|
||||
setrank_help = "Set a player's rank.",
|
||||
setrankid_help = "Set a player's rank by his steamid/steamid64.",
|
||||
|
||||
addrank = "{A} created a new rank {V}.",
|
||||
addrank_help = "Create a new rank.",
|
||||
|
||||
removerank = "{A} removed rank {V}.",
|
||||
removerank_help = "Remove a rank.",
|
||||
|
||||
super_admin_access = "superadmin has access to everything!",
|
||||
|
||||
giveaccess = "{A} granted access {V} to {T}.",
|
||||
givepermission_help = "Give permission to rank.",
|
||||
|
||||
takeaccess = "{A} taken access {V} from {T}.",
|
||||
takepermission_help = "Take permission from rank.",
|
||||
|
||||
renamerank = "{A} renamed rank {T} to {V}.",
|
||||
renamerank_help = "Rename rank.",
|
||||
|
||||
changeinherit = "{A} changed the rank to inherit from for {T} to {V}.",
|
||||
changeinherit_help = "Change the rank to inherit from.",
|
||||
|
||||
rank_immunity = "{A} changed rank {T}'s immunity to {V}.",
|
||||
changerankimmunity_help = "Change rank immunity.",
|
||||
|
||||
rank_ban_limit = "{A} changed rank {T}'s ban limit to {V}.",
|
||||
changerankbanlimit_help = "Change rank ban limit.",
|
||||
|
||||
changeranklimit = "{A} changed {V} limit for {T} to {V_2}.",
|
||||
changeranklimit_help = "Change rank limits.",
|
||||
|
||||
-- Utility Commands
|
||||
map_change = "{A} changing the map to {V} in 10 seconds.",
|
||||
map_change2 = "{A} changing the map to {V} with gamemode {V_2} in 10 seconds.",
|
||||
map_help = "Change current map and gamemode.",
|
||||
|
||||
map_restart = "{A} restarting the map in 10 seconds.",
|
||||
map_restart_help = "Restart current map.",
|
||||
|
||||
mapreset = "{A} reset the map.",
|
||||
mapreset_help = "Reset the map.",
|
||||
|
||||
kick = "{A} kicked {T} Reason: {V}.",
|
||||
kick_help = "Kick a player.",
|
||||
|
||||
ban = "{A} banned {T} for {V} ({V_2}).",
|
||||
ban_help = "Ban a player.",
|
||||
|
||||
banid = "{A} banned ${T} for {V} ({V_2}).",
|
||||
banid_help = "Ban a player using his steamid.",
|
||||
|
||||
-- ban message when admin name doesn't exists
|
||||
ban_message = [[
|
||||
|
||||
|
||||
You are banned by: {S}
|
||||
|
||||
Reason: {S_2}
|
||||
|
||||
You will be unbanned in: {S_3}]],
|
||||
|
||||
-- ban message when admin name exists
|
||||
ban_message_2 = [[
|
||||
|
||||
|
||||
You are banned by: {S} ({S_2})
|
||||
|
||||
Reason: {S_3}
|
||||
|
||||
You will be unbanned in: {S_4}]],
|
||||
|
||||
unban = "{A} unbanned {T}.",
|
||||
unban_help = "Unban a player using his steamid.",
|
||||
|
||||
noclip = "{A} has toggled noclip for {T}.",
|
||||
noclip_help = "Toggle noclip on player(s).",
|
||||
|
||||
cleardecals = "{A} cleared ragdolls and decals for all players.",
|
||||
cleardecals_help = "Clear ragdolls and decals for all players.",
|
||||
|
||||
stopsound = "{A} stopped all sounds.",
|
||||
stopsound_help = "Stop all sounds for all players.",
|
||||
|
||||
not_in_vehicle = "You are not in a vehicle!",
|
||||
not_in_vehicle2 = "{S Blue} is not in a vehicle!",
|
||||
exit_vehicle = "{A} forced {T} to get out from a vehicle.",
|
||||
exit_vehicle_help = "Force a player out of a vehicle.",
|
||||
|
||||
time_your = "Your total time is {V}.",
|
||||
time_player = "{T} total time is {V}.",
|
||||
time_help = "Check a player's time.",
|
||||
|
||||
admin_help = "Activate admin mode.",
|
||||
unadmin_help = "Deactivate admin mode.",
|
||||
|
||||
buddha = "{A} enabled buddha mode for {T}.",
|
||||
buddha_help = "Make player(s) godmoded when their health is 1.",
|
||||
|
||||
unbuddha = "{A} disabled buddha mode for {T}.",
|
||||
unbuddha_help = "Disable buddha mode for player(s).",
|
||||
|
||||
give = "{A} gave {T} {V}.",
|
||||
give_help = "Give player(s) weapon/entity",
|
||||
|
||||
-- DarkRP Commands
|
||||
arrest = "{A} arrested {T} forever.",
|
||||
arrest2 = "{A} arrested {T} for {V} seconds.",
|
||||
arrest_help = "Arrest player(s).",
|
||||
|
||||
unarrest = "{A} unarrested {T}.",
|
||||
unarrest_help = "Unarrest player(s).",
|
||||
|
||||
setmoney = "{A} set money for {T} to {V}.",
|
||||
setmoney_help = "Set money for a player.",
|
||||
|
||||
addmoney = "{A} added {V} for {T}.",
|
||||
addmoney_help = "Add money for a player.",
|
||||
|
||||
door_invalid = "invalid door to sell.",
|
||||
door_no_owner = "no one owns this door.",
|
||||
|
||||
selldoor = "{A} sold a door/vehicle for {T}.",
|
||||
selldoor_help = "Unown the door/vehicle you are looking at.",
|
||||
|
||||
sellall = "{A} sold every door/vehicle for {T}.",
|
||||
sellall_help = "Sell every door/vehicle owned for a player.",
|
||||
|
||||
s_jail_pos = "{A} set a new jail position.",
|
||||
setjailpos_help = "Resets all jail positions and sets a new one at your location.",
|
||||
|
||||
a_jail_pos = "{A} added a new jail position.",
|
||||
addjailpos_help = "Adds a jail position at your current location.",
|
||||
|
||||
setjob = "{A} set {T}'s job to {V}.",
|
||||
setjob_help = "Change a player's job.",
|
||||
|
||||
shipment = "{A} spawned {V} shipment.",
|
||||
shipment_help = "Spawn a shipment.",
|
||||
|
||||
forcename = "{A} set the name for {T} to {V}.",
|
||||
forcename_taken = "Name already taken. ({V})",
|
||||
forcename_help = "Force name for a player.",
|
||||
|
||||
report_claimed = "{A} claimed a report submitted by {T}.",
|
||||
report_closed = "{A} closed a report submitted by {T}.",
|
||||
report_aclosed = "Your report is closed. (Time expired)",
|
||||
report_dismissed = "{A} dismissed a report submitted by {T}.",
|
||||
|
||||
rank_expired = "{V} rank for {T} expired.",
|
||||
|
||||
-- TTT Commands
|
||||
setslays = "{A} set amount of auto-slays for {T} to {V}.",
|
||||
setslays_help = "Set amount of rounds to auto-slay a player for.",
|
||||
|
||||
setslays_slayed = "{T} got auto-slayed, slays left: {V}.",
|
||||
|
||||
removeslays = "{A} removed auto-slays for {T}.",
|
||||
removeslays_help = "Remove auto-slays for a player."
|
||||
}
|
||||
192
addons/sam/lua/sui/vgui/sam_player_line.lua
Normal file
192
addons/sam/lua/sui/vgui/sam_player_line.lua
Normal file
@@ -0,0 +1,192 @@
|
||||
--[[
|
||||
| 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 SUI, NAME = CURRENT_SUI, CURRENT_SUI.name
|
||||
|
||||
local TDLib = sui.TDLib
|
||||
local draw_material = sui.draw_material
|
||||
local lerp_color = sui.lerp_color
|
||||
|
||||
local GetColor = SUI.GetColor
|
||||
local RoundedBox = TDLib.RoundedBox
|
||||
local CircleAvatar = TDLib.LibClasses.CircleAvatar
|
||||
local CircleClick2 = TDLib.LibClasses.CircleClick2
|
||||
|
||||
local PLAYER_LINE_NAME = SUI.CreateFont("PlayerLineName", "Roboto Bold", 17)
|
||||
local PLAYER_LINE_RANK = SUI.CreateFont("PlayerLineRank", "Roboto Bold", 13)
|
||||
local PLAYER_LINE_STEAMID = SUI.CreateFont("PlayerLineSteamID", "Roboto Medium", 12)
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
function PANEL:Init()
|
||||
local size = SUI.Scale(34)
|
||||
|
||||
self:Dock(TOP)
|
||||
self:SetTall(size)
|
||||
|
||||
self.size = size
|
||||
end
|
||||
|
||||
local rank_Paint = function(s, w, h)
|
||||
RoundedBox(s.rect, SUI.Scale(10), 0, 0, w, h, s.col)
|
||||
end
|
||||
|
||||
function PANEL:SetInfo(info)
|
||||
local size = self.size
|
||||
|
||||
local container
|
||||
do
|
||||
local w = SUI.Scale(280) + size
|
||||
|
||||
local _container = self:Add("Panel")
|
||||
_container:Dock(LEFT)
|
||||
_container:SetMouseInputEnabled(false)
|
||||
_container:SetWide(w)
|
||||
|
||||
container = _container:Add("Panel")
|
||||
container:SetSize(w, size)
|
||||
|
||||
function _container:PerformLayout()
|
||||
container:Center()
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local avatar = container:Add("Panel")
|
||||
avatar:Dock(LEFT)
|
||||
avatar:DockMargin(0, 0, 5, 0)
|
||||
avatar:SetWide(size)
|
||||
avatar:SetMouseInputEnabled(false)
|
||||
CircleAvatar(avatar)
|
||||
|
||||
avatar:SetSteamID(util.SteamIDTo64(info.steamid), size)
|
||||
end
|
||||
|
||||
do
|
||||
local top_container = container:Add("Panel")
|
||||
top_container:Dock(TOP)
|
||||
top_container:DockMargin(0, 0, 0, 2)
|
||||
|
||||
local name = top_container:Add("SAM.Label")
|
||||
name:Dock(LEFT)
|
||||
name:SetFont(PLAYER_LINE_NAME)
|
||||
self.name = name
|
||||
|
||||
local pname = info.name
|
||||
if not pname or pname == "" then
|
||||
name:SetTextColor(GetColor("player_list_names_2"))
|
||||
self:SetName("N/A")
|
||||
else
|
||||
name:SetTextColor(GetColor("player_list_names"))
|
||||
self:SetName(pname)
|
||||
end
|
||||
|
||||
if info.rank then
|
||||
local rank_bg = top_container:Add("Panel")
|
||||
rank_bg:Dock(LEFT)
|
||||
rank_bg:DockMargin(5, 0, 0, 0)
|
||||
|
||||
rank_bg.rect = {}
|
||||
rank_bg.col = info.rank_bg or GetColor("player_list_rank")
|
||||
rank_bg.Paint = rank_Paint
|
||||
|
||||
local rank = rank_bg:Add("SAM.Label")
|
||||
rank:Dock(FILL)
|
||||
rank:DockMargin(SUI.Scale(8), 0, 0, 0)
|
||||
rank:SetTextColor(GetColor("player_list_rank_text"))
|
||||
rank:SetFont(PLAYER_LINE_RANK)
|
||||
rank.bg = rank_bg
|
||||
|
||||
self.rank = rank
|
||||
self:SetRank(info.rank)
|
||||
|
||||
rank_bg:SetSize(rank:GetTextSize() + SUI.Scale(8) * 2)
|
||||
end
|
||||
|
||||
top_container:SizeToChildren(true, true)
|
||||
end
|
||||
|
||||
local steamid = container:Add("SAM.Label")
|
||||
steamid:Dock(TOP)
|
||||
steamid:SetTextColor(GetColor("player_list_steamid"))
|
||||
steamid:SetFont(PLAYER_LINE_STEAMID)
|
||||
steamid:SetText(info.steamid)
|
||||
steamid:SizeToContents()
|
||||
steamid:SetAutoStretchVertical(true)
|
||||
|
||||
self.container = container
|
||||
end
|
||||
|
||||
function PANEL:SetName(new_name)
|
||||
local name = self.name
|
||||
name:SetText(new_name)
|
||||
name:SizeToContents()
|
||||
if name:GetWide() > 160 then
|
||||
name:SetWide(158)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:SetRank(new_rank)
|
||||
local rank = self.rank
|
||||
rank:SetText(new_rank)
|
||||
rank:SizeToContents()
|
||||
rank.bg:SetSize(rank:GetTextSize() + SUI.Scale(8) * 2)
|
||||
end
|
||||
|
||||
function PANEL:Actions()
|
||||
local container
|
||||
do
|
||||
local size = self.size
|
||||
|
||||
local _container = self:Add("Panel")
|
||||
_container:Dock(RIGHT)
|
||||
_container:SetWide(size)
|
||||
|
||||
container = _container:Add("Panel")
|
||||
container:SetSize(size, size)
|
||||
|
||||
function _container:PerformLayout()
|
||||
container:Center()
|
||||
end
|
||||
end
|
||||
|
||||
local actions_button = container:Add("SAM.Button")
|
||||
actions_button:SetText("")
|
||||
actions_button:ClearPaint()
|
||||
|
||||
function container:PerformLayout(w, h)
|
||||
actions_button:SetSize(h, h)
|
||||
actions_button:Center()
|
||||
end
|
||||
|
||||
local image = actions_button:Add("SAM.Image")
|
||||
image:Dock(FILL)
|
||||
image:SetImage("https://raw.githubusercontent.com/Srlion/Addons-Data/main/icons/sam/dots_verticle.png")
|
||||
|
||||
local current_icon_color = Color(GetColor("actions_button_icon"):Unpack())
|
||||
function image:Draw(w, h)
|
||||
if not h then return end
|
||||
|
||||
if actions_button.Hovered then
|
||||
lerp_color(current_icon_color, GetColor("actions_button_icon_hover"))
|
||||
else
|
||||
lerp_color(current_icon_color, GetColor("actions_button_icon"))
|
||||
end
|
||||
|
||||
draw_material(nil, w / 2, h / 2, SUI.ScaleEven(20), current_icon_color)
|
||||
end
|
||||
|
||||
CircleClick2(actions_button, Color(62, 62, 62), 10)
|
||||
actions_button:Center()
|
||||
|
||||
return actions_button
|
||||
end
|
||||
|
||||
sui.register("PlayerLine", PANEL, "Panel")
|
||||
Reference in New Issue
Block a user