This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent 9f505a0646
commit c6d9b6f580
8044 changed files with 1853472 additions and 21 deletions

View File

@@ -0,0 +1 @@
4x1374-j382fs-599g87-31j7h3

View File

@@ -0,0 +1 @@
<put https://proxycheck.io proxy key here and remove .example from file name>

View File

@@ -0,0 +1 @@
https://discord.com/api/webhooks/832651486860541973/iYpexHJmA5E6pD6WwUT1GyvLtQ9_8ly-qB0xloIsPu7KmaU0mkQVRE_Kg0qVwGRlwhs_

View File

@@ -0,0 +1 @@
<put discord webhook URL here and remove .example from filename>

View File

@@ -0,0 +1,153 @@
--[[
| 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 CAMI = CAMI
local LocalPlayer = LocalPlayer
local L = L
local SetClipboardText = SetClipboardText
local chat = chat
local net = net
local table = table
local string = string
local tonumber = tonumber
local render = render
local Color = Color
local gui = gui
local hook = hook
local surface = surface
local netstream = netstream
local ipairs = ipairs
local MsgN = MsgN
local ix = ix
local PLUGIN = PLUGIN
PLUGIN.infoIcon = ix.util.GetMaterial("icon16/information.png")
ix.option.Add("staffChat", ix.type.bool, true, {
category = "administration",
hidden = function()
return !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Hear Staff Chat", nil)
end
})
function PLUGIN:PopulateScoreboardPlayerMenu(client, menu)
if (CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")) then
menu:AddOption(L("bastionCopySteamName"), function()
SetClipboardText(client:SteamName())
LocalPlayer():NotifyLocalized("bastionCopiedSteamName")
end)
menu:AddOption(L("bastionCopyCharName"), function()
SetClipboardText(client:Name())
LocalPlayer():NotifyLocalized("bastionCopiedCharName")
end)
end
if (sam and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands") and !LocalPlayer():InVehicle() and client != LocalPlayer()) then
menu:AddOption(L("bastionGoto"), function()
if (LocalPlayer():GetMoveType() != MOVETYPE_NOCLIP) then
LocalPlayer():ConCommand("noclip")
end
LocalPlayer():ConCommand("say !goto "..client:Name())
end)
end
end
function PLUGIN:PrintTarget(target)
if (ix.option.Get("pgi")) then
SetClipboardText(target:SteamID())
end
chat.AddText(self.infoIcon, target:Name(), " (", target:SteamName(), "; ", target:SteamID(),
") | HP: ", target:Health(), " | Armor: ", target:Armor())
end
function PLUGIN:PrintStaffList(amount)
for _ = 1, amount do
local group = net.ReadString()
local members = net.ReadUInt(8)
local memberList = {}
for _ = 1, members do
memberList[#memberList + 1] = net.ReadEntity():SteamName()
end
table.sort(memberList)
chat.AddText(self.infoIcon, "[", string.utf8upper(group), "]: ", table.concat(memberList, ", "))
end
end
function PLUGIN:ShouldDisplayArea(id)
if (LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP and !LocalPlayer():InVehicle()) then
return false
end
end
local commands = {
["playsound"] = 2,
["localevent"] = 2,
["showentsinradius"] = 1,
["localbroadcast"] = 2,
["localbroadcastme"] = 2,
["localbroadcastit"] = 2,
["playsong"] = 3,
["screenshake"] = 4,
["moviebars"] = 1,
["removepersistedprops"] = 1,
["removeclientprops"] = 1
}
function PLUGIN:PostDrawTranslucentRenderables(bDrawingDepth, bDrawingSkybox)
local command = string.utf8lower(ix.chat.currentCommand)
if (commands[command]) then
local range = tonumber(ix.chat.currentArguments[commands[command]])
if (range) then
render.SetColorMaterial()
render.DrawSphere(LocalPlayer():GetPos(), 0 - range, 50, 50, Color(255, 150, 0, 100))
end
end
end
net.Receive("ixOpenURL", function(len)
gui.OpenURL(net.ReadString())
end)
net.Receive("ixPlayerInfo", function(len)
PLUGIN:PrintTarget(net.ReadEntity())
end)
net.Receive("ixStaffList", function(len)
PLUGIN:PrintStaffList(net.ReadUInt(8))
end)
net.Receive("ixPlaySound", function(len)
local sound = net.ReadString()
local isGlobal = net.ReadBool()
if (hook.Run("PrePlaySound", sound, isGlobal) != false) then
surface.PlaySound(sound)
hook.Run("PostPlaySound", sound, isGlobal)
end
end)
netstream.Hook("PrintInfoList", function(list)
for _, v in ipairs(list) do
MsgN(v)
end
end)
function PLUGIN:OnPlayerChat()
if (ix.config.Get("suppressOnPlayerChat", true)) then
return true
end
end

View File

@@ -0,0 +1,137 @@
--[[
| 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 (SERVER) then
util.AddNetworkString("ixBindGrab")
util.AddNetworkString("ixBindGrabList")
end
CAMI.RegisterPrivilege({
Name = "Helix - Check Bind",
MinAccess = "superadmin"
})
ix.command.Add("PlyGetBinds", {
description = "Get a list of all of someone's binds.",
privilege = "Check Bind",
arguments = {ix.type.player, bit.bor(ix.type.optional, ix.type.bool)},
OnRun = function(self, client, target, all)
if (IsValid(target.ixBindGrab) and target.ixBindGrabTime and target.ixBindGrabTime > CurTime()) then
return "Someone else is checking this player's binds already. Please wait a few seconds!"
end
target.ixBindGrab = client
target.ixBindGrabTime = CurTime() + 5
target.ixBindGrabAll = all
net.Start("ixBindGrab")
net.Send(target)
end,
bNoIndicator = true
})
if (CLIENT) then
net.Receive("ixBindGrab", function()
net.Start("ixBindGrab")
for i = 1, BUTTON_CODE_LAST do
net.WriteString(string.Left(input.LookupKeyBinding(i) or "", 255))
end
net.SendToServer()
end)
local blacklist = {
["slot0"] = true,
["slot1"] = true,
["slot2"] = true,
["slot3"] = true,
["slot4"] = true,
["slot5"] = true,
["slot6"] = true,
["slot7"] = true,
["slot8"] = true,
["slot9"] = true,
["+zoom"] = true,
["+forward"] = true,
["+back"] = true,
["+moveleft"] = true,
["+moveright"] = true,
["+jump"] = true,
["+speed"] = true,
["+walk"] = true,
["+duck"] = true,
["+lookup"] = true,
["+left"] = true,
["+lookdown"] = true,
["+right"] = true,
["+attack"] = true,
["+attack2"] = true,
["+reload"] = true,
["+use"] = true,
["invprev"] = true,
["invnext"] = true,
["+menu"] = true,
["+menu_context"] = true,
["gmod_undo"] = true,
["+showscores"] = true,
["gm_showhelp"] = true,
["gm_showteam"] = true,
["gm_showspare1"] = true,
["gm_showspare2"] = true,
["noclip"] = true,
["messagemode"] = true,
["toggleconsole"] = true,
["cancelselect"] = true,
["pause"] = true,
["save quick"] = true,
["load quick"] = true,
["impulse 100"] = true,
["+voicerecord"] = true,
["jpeg"] = true,
}
net.Receive("ixBindGrabList", function()
local all = net.ReadBool()
MsgN(net.ReadString().."'s binds ("..net.ReadString()..")")
for i = 1, BUTTON_CODE_LAST do
local bind = net.ReadString()
if (!all and blacklist[bind]) then continue end
if (bind and bind != "") then
if (#bind == 255) then
bind = bind.."..."
end
MsgN((input.GetKeyName(i) or i)..": ", bind)
end
end
LocalPlayer():Notify("Binds were printed in console!")
end)
else
net.Receive("ixBindGrab", function(len, client)
if (!IsValid(client.ixBindGrab) or !CAMI.PlayerHasAccess(client.ixBindGrab, "Helix - Check Bind")) then return end
net.Start("ixBindGrabList")
net.WriteBool(client.ixBindGrabAll)
net.WriteString(client:SteamName())
net.WriteString(client:SteamID())
for i = 1, BUTTON_CODE_LAST do
net.WriteString(string.Left(net.ReadString(), 255))
end
net.Send(client.ixBindGrab)
client.ixBindGrab = nil
end)
end

View File

@@ -0,0 +1,507 @@
--[[
| 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 pcall = pcall
local util = util
local ix = ix
local string = string
local net = net
local ipairs = ipairs
local IsValid = IsValid
local os = os
local L = L
local math = math
local table = table
local pairs = pairs
local print = print
local CHTTP = CHTTP
local player = player
local CAMI = CAMI
if (!CHTTP) then
pcall(require, "chttp")
end
local PLUGIN = PLUGIN
PLUGIN.TYPE_UNKNOWN = 1
PLUGIN.TYPE_KNOWN = 2
local COOKIE_KEY = "qvFT4QSSST8K4tZF"
local fileChecks = {
{"hl2_misc_001.vpk", "hl2"},
{"ep2_pak_008.vpk", "ep2"},
{"cstrike_pak_003.vpk", "css", "cstrike"},
{"bin/client_panorama.dll", "csgo"},
{"detail.vbsp", "gmod", "garrysmod"}
}
util.AddNetworkString("RecieveDupe")
ix.util.Include("sv_antialt_db.lua")
ix.lang.AddTable("english", {
altSignupProxy = "You are trying to join Willard Networks as a new user while using a proxy or VPN. To combat alt accounts, we do not allow this.\n\nPlease turn off your proxy/VPN and rejoin the server. After you successfully joined the server once, you can turn on your proxy/VPN again for future visits.\n\nIf you are not using a proxy or VPN, please contact the community management on our forums or discord"
})
ix.log.AddType("altNewClient", function(client)
return string.format("[AltChecker] %s joined for the first time, generating new cookie.", client:SteamName())
end)
ix.log.AddType("altKnownNewCookie", function(client)
return string.format("[AltChecker] %s joined with unknown installation, generating new cookie.", client:SteamName())
end)
ix.log.AddType("altKnownCookieMatched", function(client, matched, total)
return string.format("[AltChecker] %s joined without a cookie, but matched installation with existing cookie. Certainty %d/%d", client:SteamName(), matched, total)
end)
hook.Add("PlayerInitialSpawn", "bastionAntiAlt", function(client)
if (client:IsBot()) then return end
client.ixAltData = {
checks = 4 + #fileChecks,
altLogging = 0,
error = false,
received = false,
checkComplete = false,
newAltFound = false,
mode = 0,
localCookie = "",
cookies = false,
ip = false,
timestamps = false,
otherCookies = {},
otherIPs = {},
otherTimestamps = {},
otherTimestampsNZ = {},
otherTimeMatches = {},
discordAlert = {
cookies = {},
timestamps = {},
ips = {},
altIDs = {}
}
}
-- Lookup user data
PLUGIN:AltLoadUserData(client)
if (PLUGIN.API_KEY) then
-- Lookup user IP
PLUGIN:AltLookupIP(client)
else
client.ixAltData.checkes = client.ixAltData.checks - 1
end
-- Check for IP matches
PLUGIN:AltLookupIPMatches(client)
-- Request cookie and install timestamps
PLUGIN:RequestClientData(client)
end)
function PLUGIN:RequestClientData(client)
net.Start("RecieveDupe")
net.WriteUInt(1, 3)
net.WriteString(COOKIE_KEY)
for _, v in ipairs(fileChecks) do
net.WriteString(v[1])
net.WriteString(v[3] or v[2])
end
net.Send(client)
end
net.Receive("RecieveDupe", function(len, client)
if (!IsValid(client) or !client.ixAltData or client.ixAltData.received) then return end
local data = client.ixAltData
data.received = true
-- set cookie
data.localCookie = net.ReadString()
-- set file timestamps
data.timestamps = {}
for i = 1, #fileChecks do
data.timestamps[i] = net.ReadUInt(32)
end
-- Check for cookie matches
if (data.localCookie != "") then
PLUGIN:AltLookupCookieMatches(client, data)
else
PLUGIN:AltPreFinalChecking(client)
end
-- Check for install timestamp matches
data.otherTimestamps = {}
data.otherTimestampsNZ = {}
for i = 1, #fileChecks do
PLUGIN:AltLookupTimestampMatches(client, data, fileChecks[i][2], data.timestamps[i])
end
end)
function PLUGIN:AltPreFinalChecking(client)
if (!IsValid(client)) then return end
local data = client.ixAltData
-- if we errored, don't do anything
-- check will be reexecuted when the client rejoins
if (data.error) then return end
-- check if all queries finished
data.checks = data.checks - 1
if (data.checks != 0) then return end
-- cookie matches (STRONG)
if (#data.otherCookies > 0) then
for _, v in ipairs(data.otherCookies) do
self:AltFound(client, v[2], "cookie", "cookie")
data.discordAlert.cookies[#data.discordAlert.cookies + 1] = (v[2] or "").." ("..(v[1] or "")..")"
data.discordAlert.altIDs[v[2]] = true
end
end
-- IP (WEAK)
if (#data.otherIPs > 0) then
for _, v in ipairs(data.otherIPs) do
data.discordAlert.ips[#data.discordAlert.ips + 1] = v[2]
data.discordAlert.altIDs[v[2]] = true
end
end
-- time matches (STRONG-MEDIUM)
self:AggregateTimestampMatches(data)
-- If no local cookie and player is known, check if a known cookie was time-matched
if (data.localCookie == "" and data.mode == self.TYPE_KNOWN) then
self:FindMatchingCookie(client, data)
end
if (#data.otherTimeMatches > 0) then
-- go looking for the other clients that own our matched timestamps
self:AltLookupCookieForTimestamps(client, data, #fileChecks)
else
-- else we don't need to wait for the lookup above
data.checkComplete = true
self:AltFinalChecking(client)
end
end
function PLUGIN:AltFinalChecking(client)
if (!IsValid(client)) then return end
local data = client.ixAltData
if (!data.checkComplete or data.altLogging != 0) then return end
self:DiscordAlert(client)
-- update IP list
local steamID = client:SteamID64()
local ip = self.GetIPAddress(client)
local query = mysql:Select("bastion_antialt_userips")
query:Where("steamid", steamID)
query:Where("ip", ip)
query:Callback(function(result)
if (!result or #result == 0) then
local query2 = mysql:Insert("bastion_antialt_userips")
query2:Insert("steamid", steamID)
query2:Insert("ip", ip)
query2:Insert("last_seen", os.time())
query2:Execute()
else
local query2 = mysql:Update("bastion_antialt_userips")
query2:Where("id", result[1].id)
query2:Update("last_seen", os.time())
query2:Execute()
end
end)
query:Execute()
-- Kick if new player on proxy/vpn
if (data.mode == self.TYPE_UNKNOWN) then
if (self.API_KEY and (data.ip.proxy == "yes" or (data.ip.risk or 0) > 60)) then
if (ix.config.Get("VPNKick")) then
self:ProxyAlert(client)
client:Kick(L("altSignupProxy", client))
else
if (!self:NotifyProxyJoin(client) or ix.config.Get("ProxyAlwaysAlert")) then
self:ProxyAlert(client)
end
end
elseif (data.localCookie == "") then
ix.log.Add(client, "altNewClient")
self:GenerateCookie(client, data)
else
-- Update the cookie's timestamps
self:StoreCookieInfo(data, fileChecks)
-- Add this cookie to client as well so it can be time matched/timestamps updated
self:StoreClientCookie(client, data)
end
elseif (data.localCookie == "") then
ix.log.Add(client, "altKnownNewCookie")
self:GenerateCookie(client, data)
else
-- Update the cookie's timestamps
self:StoreCookieInfo(data, fileChecks)
-- Add this cookie to client as well so it can be time matched/timestamps updated
self:StoreClientCookie(client, data)
end
if (sam) then
timer.Simple(3, function()
if (!IsValid(client)) then return end
local query1 = mysql:Select("bastion_antialt_alts")
query1:Where("steamid", client:SteamID64())
query1:Select("alt_id")
query1:Callback(function(result)
if (!IsValid(client)) then return end
if (!result or #result == 0) then return end
local query2 = mysql:Select("bastion_antialt_alts")
query2:Where("alt_id", result[1].alt_id)
query2:WhereNotEqual("steamid", client:SteamID64())
query2:Select("steamid")
query2:Callback(function(result2)
if (!IsValid(client)) then return end
if (!result2 or #result2 == 0) then return end
for k, v in ipairs(result2) do
sam.player.is_banned(util.SteamIDFrom64(v.steamid), function(banned)
if (!banned or !IsValid(client)) then return end
client:Kick("You have a banned alt account.")
return
end)
end
end)
query2:Execute()
end)
query1:Execute()
end)
end
end
--[[
COOKIE STUFF
]]
function PLUGIN:WhitelistPlayer(client)
local data = client.ixAltData
if (!data) then return end
if (data.localCookie) then
ix.log.Add(client, "altNewClient")
self:GenerateCookie(client, data)
return true
end
end
function PLUGIN.RandomString()
local result = {} -- The empty table we start with
while (#result != 64) do
local char = string.char(math.random(32, 126))
if (string.find(char, "%w")) then
result[#result + 1] = char
end
end
return table.concat(result)
end
function PLUGIN:GenerateCookie(client, data)
local cookie = self.RandomString()
self:UpdateLocalCookie(client, data, cookie)
local query = mysql:Insert("bastion_antialt_users")
query:Insert("steamid", client:SteamID64())
query:Insert("steam_name", client:SteamName())
query:Insert("cookie", cookie)
query:Execute()
end
function PLUGIN:UpdateLocalCookie(client, data, cookie)
data.localCookie = cookie
net.Start("RecieveDupe")
net.WriteUInt(2, 3)
net.WriteString(COOKIE_KEY)
net.WriteString(cookie)
net.Send(client)
self:StoreCookieInfo(data, fileChecks)
end
function PLUGIN:FindMatchingCookie(client, data)
for _, v in ipairs(data.otherTimeMatches) do
for _, v1 in ipairs(data.cookies) do
if (v1.cookie == v[1]) then
-- found a timestamp match belonging to the client, restore cookie
-- in case of e.g. gmod reinstall
ix.log.Add(client, "altKnownCookieMatched", v[2], #fileChecks)
self:UpdateLocalCookie(client, data, v[1])
return
end
end
end
end
--[[
LOGGING AND ALERTING
]]
local ALT_OFFSET = 0
function PLUGIN:AltFound(client, steamid, type, info)
local ids = {client:SteamID64(), steamid}
local data = client.ixAltData
data.altLogging = data.altLogging + 1
local query = mysql:Select("bastion_antialt_alts")
query:WhereIn("steamid", ids)
query:Callback(function(result)
if (!result or #result == 0) then
local alt_id = os.time() + ALT_OFFSET
ALT_OFFSET = ALT_OFFSET + 1
local text = table.concat(ids,"+")..": "..info.." (alt-id: "..alt_id..")"
self:InsertNewAlt(ids[1], alt_id, type, text)
self:InsertNewAlt(steamid, alt_id, type, text)
data.newAltFound = true
elseif (#result == 1) then
self:InsertNewAlt(
result[1].steamid == steamid and ids[1] or steamid,
result[1].alt_id,
type,
table.concat(ids,"+")..": "..info.." (alt-id: "..result[1].alt_id..")"
)
data.newAltFound = true
elseif (result[2].alt_id != result[1].alt_id) then
self:MergeAlts(
result[2].alt_id,
result[1].alt_id,
table.concat(ids,"+")..": "..info.." (alt-id: "..result[1].alt_id..")"
)
data.newAltFound = true
end
data.altLogging = data.altLogging - 1
self:AltFinalChecking(client)
end)
query:Execute()
end
function PLUGIN:DiscordAlert(client)
if (!self.DISCORD_WEBHOOK_ALTS or self.DISCORD_WEBHOOK_ALTS == "") then return end
local data = client.ixAltData
if (!data.newAltFound) then return end
local tbl = {
embeds = {{
title = "Alt found for "..client:SteamName(),
description = "An alt account match was found for **"..client:SteamName().."** (*"..client:SteamID64().."*)\n\n__COOKIE__: 99.99% certainty via installed cookie\nShown as 'SteamID64 (SteamName)'\n__TIMESTAMP__: check via installation date/time or absense of mounted games (hl2,ep2,css,csgo,gmod)\nMore matches = more certainty, especially if all/most are installed\nShown as 'SteamID64 (SteamName; date/time matches - installed matches)'\n__IP__: users that connected from the same IP address at any point\nShown as 'SteamID64'",
color = 13632027,
timestamp = os.date("%Y-%m-%d %X%z"),
footer = {
text = "Bastion Alt Checker for GMod by Gr4Ss"
},
author = {
name = "Bastion Alt Checker"
},
fields = {
}
}}
}
if (data.discordAlert.cookies and #data.discordAlert.cookies > 0) then
table.insert(tbl.embeds[1].fields, {name = "COOKIE", value = table.concat(data.discordAlert.cookies, "\n")})
end
if (data.discordAlert.timestamps and #data.discordAlert.timestamps > 0) then
table.insert(tbl.embeds[1].fields, {name = "TIMESTAMP", value = table.concat(data.discordAlert.timestamps, "\n")})
end
if (data.discordAlert.ips and #data.discordAlert.ips > 0) then
table.insert(tbl.embeds[1].fields, {
name = "IP",
value = string.format("Address: [%s](https://proxycheck.io/threats/%s)\n", data.ipAddress, data.ipAddress)..
table.concat(data.discordAlert.ips, "\n")
})
end
local ipLinks = {"["..client:SteamID64().."](https://steamidfinder.com/lookup/"..client:SteamID64().."/)"}
for k in pairs(data.discordAlert.altIDs) do
ipLinks[#ipLinks + 1] = "["..k.."](https://steamidfinder.com/lookup/"..k.."/)"
end
table.insert(tbl.embeds[1].fields, {
name = "SteamID Finder Links",
value = table.concat(ipLinks,"\n")
})
for _, field in ipairs(tbl.embeds[1].fields) do
if (string.len(field.value) > 1024) then
field.value = string.sub(field.value, 1, 1024)
end
end
local request = {
failed = function(error) print("discord error", error) end,
success = function(code, body, headers)
if (code != 200) then print("discord error", code, body) end
end,
method = "post",
url = self.DISCORD_WEBHOOK_ALTS,
body = util.TableToJSON(tbl),
type = "application/json; charset=utf-8"
}
CHTTP(request)
end
function PLUGIN:ProxyAlert(client)
if (!self.DISCORD_WEBHOOK_ALTS or self.DISCORD_WEBHOOK_ALTS == "") then return end
local ip, steamID = client.ixAltData.ipAddress, client:SteamID64()
local tbl = {
embeds = {{
title = "New player using VPN - "..client:SteamName(),
description = client:SteamName().." joined WN for the first time, but was using a proxy/VPN. They have been kicked.\n\n"..
string.format("More info: [%s](https://proxycheck.io/threats/%s) & [%s](https://steamidfinder.com/lookup/%s/)", ip, ip, steamID, steamID),
color = 16312092,
timestamp = os.date("%Y-%m-%d %X%z"),
footer = {
text = "Bastion Alt Checker for GMod by Gr4Ss"
},
author = {
name = "Bastion Alt Checker"
}
}}
}
local request = {
failed = function(error) print("discord error", error) end,
method = "post",
url = self.DISCORD_WEBHOOK_ALTS,
body = util.TableToJSON(tbl),
type = "application/json; charset=utf-8"
}
CHTTP(request)
end
function PLUGIN:NotifyProxyJoin(client)
local bSend = false
for _, v in ipairs(player.GetAll()) do
if (CAMI.PlayerHasAccess(v, "Helix - Proxy Notify")) then
bSend = true
v:NotifyLocalized("bastionProxyNotify", client:SteamName())
end
end
return bSend
end

View File

@@ -0,0 +1,461 @@
--[[
| 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 IsValid = IsValid
local string = string
local os = os
local print = print
local util = util
local CHTTP = CHTTP
local ipairs = ipairs
local table = table
local pairs = pairs
local netstream = netstream
local SortedPairsByMemberValue = SortedPairsByMemberValue
local PLUGIN = PLUGIN
local MAX_CACHE_AGE = 7 * 24 * 3600 -- 7 days
PLUGIN.ipCache = {}
hook.Add("DatabaseConnected", "bastionAntiAlt", function()
local query = mysql:Create("bastion_antialt_users")
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
query:Create("steamid", "VARCHAR(20) NOT NULL")
query:Create("steam_name", "VARCHAR(128) NOT NULL")
query:Create("cookie", "VARCHAR(64) NOT NULL")
query:PrimaryKey("id")
query:Execute()
query = mysql:Create("bastion_antialt_cookies")
query:Create("cookie", "VARCHAR(64) NOT NULL")
query:Create("ts_hl2", "INT(11) UNSIGNED DEFAULT NULL")
query:Create("ts_ep2", "INT(11) UNSIGNED DEFAULT NULL")
query:Create("ts_css", "INT(11) UNSIGNED DEFAULT NULL")
query:Create("ts_csgo", "INT(11) UNSIGNED DEFAULT NULL")
query:Create("ts_gmod", "INT(11) UNSIGNED DEFAULT NULL")
query:PrimaryKey("cookie")
query:Execute()
query = mysql:Create("bastion_antialt_userips")
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
query:Create("steamid", "VARCHAR(20) NOT NULL")
query:Create("ip", "VARCHAR(15) NOT NULL")
query:Create("last_seen", "INT(11) UNSIGNED NOT NULL")
query:PrimaryKey("id")
query:Execute()
query = mysql:Create("bastion_antialt_ip")
query:Create("ip", "VARCHAR(15) NOT NULL")
query:Create("updated", "INT(11) UNSIGNED NOT NULL")
query:Create("asn", "VARCHAR(12) NOT NULL")
query:Create("provider", "VARCHAR(128) NOT NULL")
query:Create("isocode", "VARCHAR(2) NOT NULL")
query:Create("proxy", "TINYINT(1) UNSIGNED DEFAULT 0")
query:Create("type", "VARCHAR(32) NOT NULL")
query:Create("risk", "INT UNSIGNED NOT NULL")
query:Create("attack_count", "INT UNSIGNED DEFAULT NULL")
query:Create("attack_history", "TEXT DEFAULT NULL")
query:PrimaryKey("ip")
query:Execute()
query = mysql:Create("bastion_antialt_alts")
query:Create("steamid", "VARCHAR(20) NOT NULL")
query:Create("alt_id", "INT UNSIGNED NOT NULL")
query:Create("type", "VARCHAR(10) NOT NULL")
query:Create("info", "TEXT NOT NULL")
query:PrimaryKey("steamid")
query:Execute()
end)
function PLUGIN:AltLoadUserData(client)
local query = mysql:Select("bastion_antialt_users")
query:Where("steamid", client:SteamID64())
query:Callback(function(result)
if (!IsValid(client)) then return end
if (!result or #result == 0) then
client.ixAltData.mode = self.TYPE_UNKNOWN
else
client.ixAltData.mode = self.TYPE_KNOWN
client.ixAltData.cookies = result
end
self:AltPreFinalChecking(client)
end)
query:Execute()
end
function PLUGIN.GetIPAddress(client)
local ip = client:IPAddress()
return string.gsub(ip, ":%d+$", "", 1)
end
function PLUGIN:AltLookupIP(client)
local ip = self.GetIPAddress(client)
client.ixAltData.ipAddress = ip
if (self.ipCache[ip]) then
-- ip address still in cache
client.ixAltData.ip = self.ipCache[ip]
self:AltPreFinalChecking(client)
else
-- lookup address
local query = mysql:Select("bastion_antialt_ip")
query:Where("ip", ip)
query:Callback(function(result)
if (!result or #result == 0 or (result[1].updated < (os.time() - MAX_CACHE_AGE))) then
self:AltFetchIP(client, ip, !result or #result == 0)
else
-- load in data from the DB
if (result[1].proxy == 1) then
result[1].proxy = "yes"
else
result[1].proxy = "no"
end
client.ixAltData.ip = result[1]
self:AltPreFinalChecking(client)
end
end)
query:Execute()
end
end
function PLUGIN:AltFetchIP(client, ip, bCreateNew)
-- address not found or record too old, look it up
local url = string.format("https://proxycheck.io/v2/%s?key=%s&vpn=1&asn=1&risk=1", ip, self.API_KEY)
local request = {
failed = function(error)
-- error stop matching
print("[BASTION] Alt check IP API call failed with error: "..error)
print("[BASTION] Client: "..client:SteamName().."; ip: "..ip)
client.ixAltData.error = true
end,
success = function(code, body, headers)
if (!IsValid(client)) then return end
local httpResult = util.JSONToTable(body)
if (!httpResult) then
-- error stop matching
print("[BASTION] Alt check IP API call failed to parse httpResult.")
client.ixAltData.error = true
return
end
local status = httpResult.status
if (status == "denied" or status == "error" or status == "warning") then
print("[BASTION] Alt check IP API call failed. Status: "..status.."; Message: "..(httpResult.message or "no message").."\n")
if (status != "warning") then
-- error stop matching
client.ixAltData.error = true
return
end
end
-- we got the data
local lookup = httpResult[ip]
self:StoreIPLookup(ip, lookup, bCreateNew)
client.ixAltData.ip = lookup
self:AltPreFinalChecking(client)
end,
url = url,
method = "GET"
}
CHTTP(request)
end
function PLUGIN:AltLookupCookieMatches(client, data)
local query = mysql:Select("bastion_antialt_users")
query:Where("cookie", data.localCookie)
query:WhereNotEqual("steamid", client:SteamID64())
query:Callback(function(result)
-- we found other cookies
if (result and #result > 0) then
for k, v in ipairs(result) do
data.otherCookies[k] = {v.steam_name, v.steamid}
end
end
self:AltPreFinalChecking(client)
end)
query:Execute()
end
function PLUGIN:AltLookupIPMatches(client)
local query = mysql:Select("bastion_antialt_userips")
query:Where("ip", PLUGIN.GetIPAddress(client))
query:WhereNotEqual("steamid", client:SteamID64())
query:Callback(function(result)
if (!IsValid(client)) then return end
if (result and #result > 0) then
for k, v in ipairs(result) do
client.ixAltData.otherIPs[k] = {v.ip, v.steamid}
end
end
self:AltPreFinalChecking(client)
end)
query:Execute()
end
function PLUGIN:AltLookupTimestampMatches(client, data, game, timestamp)
local query = mysql:Select("bastion_antialt_cookies")
query:Where("ts_"..game, timestamp)
if (data.localCookie != "") then
-- not interested in timestamps that we know for this client
query:WhereNotEqual("cookie", data.localCookie)
end
query:Callback(function(result)
if (result and #result > 0) then
for _, v in ipairs(result) do
data.otherTimestamps[v.cookie] = data.otherTimestamps[v.cookie] or {}
table.insert(data.otherTimestamps[v.cookie], game)
-- track timestamp matches
-- non-zero timestamps are worth a lot more, so tracked separately too
if (timestamp != 0) then
data.otherTimestampsNZ[v.cookie] = data.otherTimestampsNZ[v.cookie] or {}
data.otherTimestampsNZ[v.cookie][game] = true
end
end
end
self:AltPreFinalChecking(client)
end)
query:Execute()
end
function PLUGIN:AggregateTimestampMatches(data)
data.otherTimeMatches = {}
for cookie, matches in pairs(data.otherTimestamps) do
if (#matches == 1) then continue end
data.otherTimeMatches[#data.otherTimeMatches + 1] = {cookie, #matches}
end
table.SortByMember(data.otherTimeMatches, 2, false)
end
function PLUGIN:AltLookupCookieForTimestamps(client, data, maxMatches)
local cookies = {}
for cookie, matches in pairs(data.otherTimestamps) do
-- only find cookies if at least 2 matches of which one is non-zero
if (#matches >= 2 and data.otherTimestampsNZ[cookie]) then
cookies[#cookies + 1] = cookie
end
end
if (#cookies == 0) then
data.checkComplete = true
self:AltFinalChecking(client)
return
end
local query = mysql:Select("bastion_antialt_users")
query:WhereNotEqual("steamid", client:SteamID64())
query:WhereIn("cookie", cookies)
query:Callback(function(result)
if (!IsValid(client)) then return end
if (result and #result > 0) then
for _, v in ipairs(result) do
local installed = table.GetKeys(data.otherTimestampsNZ[v.cookie])
local text = string.format("%d/%d matches - installed: %s)",
#data.otherTimestamps[v.cookie],
maxMatches,
table.concat(installed, "+")
)
self:AltFound(client, v.steamid, "timestamp", text)
data.discordAlert.timestamps[# data.discordAlert.timestamps + 1] = v.steamid.." ("..v.steam_name.."; "..text..")"
data.discordAlert.altIDs[v.steamid] = true
end
end
data.checkComplete = true
self:AltFinalChecking(client)
end)
query:Execute()
end
function PLUGIN:StoreCookieInfo(data, fileChecks)
local query = mysql:Select("bastion_antialt_cookies")
query:Where("cookie", data.localCookie)
query:Callback(function(result)
if (!result or #result == 0) then
local queryInsert = mysql:Insert("bastion_antialt_cookies")
queryInsert:Insert("cookie", data.localCookie)
for k, v in ipairs(fileChecks) do
queryInsert:Insert("ts_"..v[2], data.timestamps[k])
end
queryInsert:Execute()
else
local queryUpdate = mysql:Update("bastion_antialt_cookies")
queryUpdate:Where("cookie", data.localCookie)
for k, v in ipairs(fileChecks) do
queryUpdate:Update("ts_"..v[2], data.timestamps[k])
end
queryUpdate:Execute()
end
end)
query:Execute()
end
function PLUGIN:StoreClientCookie(client, data)
local query = mysql:Select("bastion_antialt_users")
query:Where("cookie", data.localCookie)
query:Where("steamid", client:SteamID64())
query:Callback(function(result)
if (!IsValid(client)) then return end
if (!result or #result == 0) then
local queryInsert = mysql:Insert("bastion_antialt_users")
queryInsert:Insert("steamid", client:SteamID64())
queryInsert:Insert("steam_name", client:SteamName())
queryInsert:Insert("cookie", data.localCookie)
queryInsert:Execute()
end
end)
query:Execute()
end
function PLUGIN:StoreIPLookup(ip, httpResult, bNewEntry)
if (!bNewEntry) then
local query = mysql:Update("bastion_antialt_ip")
query:Where("ip", ip)
query:Update("updated", os.time())
query:Update("asn", httpResult.asn)
query:Update("provider", httpResult.provider)
query:Update("isocode", httpResult.isocode)
query:Update("proxy", httpResult.proxy == "yes" and 1 or 0)
query:Update("type", httpResult.type)
query:Update("risk", httpResult.risk or 0)
if (httpResult["attack history"]) then
query:Update("attack_count", httpResult["attack history"].Total)
query:Update("attack_history", util.TableToJSON(httpResult["attack history"]))
end
query:Execute()
else
local query = mysql:Insert("bastion_antialt_ip")
query:Insert("ip", ip)
query:Insert("updated", os.time())
query:Insert("asn", httpResult.asn)
query:Insert("provider", httpResult.provider)
query:Insert("isocode", httpResult.isocode)
query:Insert("proxy", httpResult.proxy == "yes" and 1 or 0)
query:Insert("type", httpResult.type)
query:Insert("risk", httpResult.risk or 0)
if (httpResult["attack history"]) then
query:Insert("attack_count", httpResult["attack history"].Total)
query:Insert("attack_history", util.TableToJSON(httpResult["attack history"]))
end
query:Execute()
end
end
function PLUGIN:InsertNewAlt(steamid, alt_id, type, text)
local query = mysql:Insert("bastion_antialt_alts")
query:Insert("steamid", steamid)
query:Insert("alt_id", alt_id)
query:Insert("type", type)
query:Insert("info", text)
query:Execute()
end
function PLUGIN:MergeAlts(old_id, new_id, text)
local query = mysql:Select("bastion_antialt_alts")
query:Where("alt_id", old_id)
query:Callback(function(result2)
for _, v in ipairs(result2) do
local queryUpdate = mysql:Update("bastion_antialt_alts")
queryUpdate:Where("steamid", v.steamid)
queryUpdate:Update("alt_id", new_id)
queryUpdate:Update("info", v.info.." - "..text)
queryUpdate:Execute()
end
end)
query:Execute()
end
function PLUGIN:LookupSteamID(client, steamid)
if (string.find(steamid, "^STEAM_")) then
steamid = util.SteamIDTo64()
end
local query = mysql:Select("bastion_antialt_alts")
query:Where("steamid", steamid)
query:Callback(function(result)
if (!IsValid(client)) then return end
if (!result or #result == 0) then
client:NotifyLocalized("bastionNoRecordFound", steamid)
return
end
local querySelect = mysql:Select("bastion_antialt_alts")
querySelect:Where("alt_id", result[1].alt_id)
querySelect:Callback(function(finalResult)
if (!IsValid(client)) then return end
local toReturn = {"Alts for "..steamid..":", "(SteamID64) - (detection trigger type) - (info)"}
for _, v in ipairs(finalResult) do
toReturn[#toReturn + 1] = v.steamid.." - "..v.type.." - info: "..v.info
end
netstream.Start(client, "PrintInfoList", toReturn)
client:NotifyLocalized("bastionResultsPrinted")
end)
querySelect:Execute()
end)
query:Execute()
end
function PLUGIN:LookupIPUsers(client, steamid)
if (string.find(steamid, "^STEAM_")) then
steamid = util.SteamIDTo64(steamid)
end
local query = mysql:query("bastion_antialt_userips")
query:Where("steamid", steamid)
query:Callback(function(result)
if (!IsValid(client)) then return end
if (!result or #result == 0) then
client:NotifyLocalized("bastionNoRecordFound", steamid)
return
end
local list = {}
for k, v in ipairs(result) do
list[k] = v.ip
end
local querySelect = mysql:query("bastion_antialt_userips")
querySelect:WhereIn("ip", list)
querySelect:WhereNotEqual("steamid", steamid)
querySelect:Callback(function(finalResult)
if (!IsValid(client)) then return end
if (!result or #result == 0) then
client:NotifyLocalized("bastionNoRecordFound", steamid)
return
end
local toReturn = {"Other users on same IP as "..steamid, "(SteamID64) - (ip) - (last seen on this ip)"}
for _, v in SortedPairsByMemberValue(finalResult, "steamid") do
toReturn[#toReturn + 1] = v.steamid.." - "..v.ip.." - "..os.date("%Y-%m-%d", v.last_seen)
end
netstream.Start(client, "PrintInfoList", toReturn)
client:NotifyLocalized("bastionResultsPrinted")
end)
querySelect:Execute()
end)
query:Execute()
end

View File

@@ -0,0 +1,96 @@
--[[
| 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 hook = hook
local ix = ix
local util = util
local PLUGIN = PLUGIN
PLUGIN.permaBan = {
-- Crazyman (~Atle/Gr4Ss), code stealing
["STEAM_0:1:120921609"] = true,
["2.73.226.221"] = true,
["2.72.176.41"] = true,
["2.132.96.235"] = true,
["2.132.103.122"] = true,
["2.132.147.172"] = true,
["2.132.150.162"] = true,
["95.57.132.81"] = true,
-- carlsmei (~Atle/Gr4Ss), code stealing
["STEAM_0:0:216726444"] = true,
-- Schwarz Kruppzo (~Atle/Gr4Ss), code stealing
["STEAM_0:1:44629398"] = true,
-- KoC (~Atle/Gr4Ss), toxic, ban evasion
["76561198017957016"] = true,
["76561199123547331"] = true,
["73.121.218.83"] = true,
["136.144.43.116"] = true,
["136.144.43.63"] = true,
-- Kalingi (Staff vote, Hiros/Gr4Ss), toxic, threatening hacks & blackmail
["76561198066620287"] = true,
["STEAM_0:1:53177279"] = true,
["24.197.171.2"] = true,
-- Brando (~Atle/Gr4Ss), pedo
["STEAM_0:1:54660756"] = true,
-- Walter (~Atle/Gr4Ss), none
["STEAM_0:1:43085888"] = true,
-- PrplSckz/The Enemy (~Rad/Gr4Ss), forum DDoS
["STEAM_0:1:68538156"] = true,
-- Hackers (~Gr4Ss)
["STEAM_0:1:13809165"] = true,
["STEAM_0:1:4916602"] = true,
["STEAM_0:1:517232907"] = true,
["STEAM_0:1:17046093"] = true,
-- Exploiters
["STEAM_0:0:549109050"] = true,
["76561199131288084"] = true,
["76561199087140341"] = true,
["76561199206105794"] = true,
["76561198874018211"] = true,
["109.252.109.68"] = true,
["76561199121843993"] = true,
-- Zeroday / Newport Gaming - Sketchy dude + some hacking & exploiting (~M!NT/RAD)
["172.82.32.147"] = true,
["76561199441966033"] = true,
-- Cazo
["82.0.106.136"] = true,
["76561199150421701"] = true,
-- lqut (Translating ISIS Propaganda) (~M!NT/RAD)
["STEAM_0:0:173208852"] = true,
["5.30.219.71"] = true,
["76561198306683432"] = true,
-- madbluntz (doxxing, minging, ddosing, etc etc) (~M!NT)
["176.117.229.107"] = true,
["176.117.229.107"] = true,
["178.214.250.178"] = true,
["46.191.232.69"] = true,
["178.168.94.11"] = true,
["163.182.82.195"] = true,
["104.231.185.125"] = true,
["STEAM_0:0:763201893"] = true,
["STEAM_0:0:629741416"] = true,
["STEAM_0:1:764405213"] = true,
["STEAM_0:1:817531224"] = true,
["STEAM_0:0:785033797"] = true,
["STEAM_0:1:421783352"] = true,
["STEAM_0:1:78544439"] = true,
["STEAM_0:1:178702634"] = true,
["STEAM_0:0:627119036"] = true,
["STEAM_0:0:585787645"] = true,
["STEAM_0:1:43085888"] = true,
}
hook.Add("CheckPassword", "bastionBanList", function(steamid, networkid)
if (PLUGIN.permaBan[steamid] or PLUGIN.permaBan[util.SteamIDFrom64(steamid)] or PLUGIN.permaBan[networkid]) then
ix.log.AddRaw("[BANS] "..steamid.." ("..networkid..") tried to connect but is hard-banned.")
return false
end
end)

View File

@@ -0,0 +1,211 @@
--[[
| 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 hook_Add = hook.Add
local timer_Create = timer.Create
local os_time = os.time
local pairs = pairs
local net_ReadHeader = net.ReadHeader
local util_NetworkIDToString = util.NetworkIDToString
local ix = ix
local IsValid = IsValid
local string_len = string.len
local string_sub = string.sub
-- Code inspired by:
-- Gmod Net Library Debug
-- https://github.com/HexaneNetworks/gmod-netlibrary-debug
-- v2.3
-- October 2020
local PLUGIN = PLUGIN
hook_Add("DatabaseConnected", "bastionNetDB", function()
local query = mysql:Create("bastion_netlog")
query:Create("id", "BIGINT UNSIGNED NOT NULL AUTO_INCREMENT")
query:Create("name", "VARCHAR(50) NOT NULL")
query:Create("length", "INT UNSIGNED NOT NULL")
query:Create("count", "INT UNSIGNED NOT NULL")
query:Create("steamid", "VARCHAR(20) NOT NULL")
query:Create("args", "VARCHAR(200) DEFAULT NULL")
query:PrimaryKey("id")
query:Execute()
query = mysql:Create("bastion_netspam")
query:Create("id", "INT UNSIGNED NOT NULL AUTO_INCREMENT")
query:Create("name", "VARCHAR(50) NOT NULL")
query:Create("type", "BOOL NOT NULL")
query:Create("count", "INT UNSIGNED NOT NULL")
query:Create("steamid", "VARCHAR(20) NOT NULL")
query:Create("steam_name", "VARCHAR(50) NOT NULL")
query:Create("ip", "VARCHAR(25) NOT NULL")
query:Create("time", "INT NOT NULL")
query:PrimaryKey("id")
query:Execute()
end)
local netSpamCount = {}
local netFlagged = {}
local comSpamCount = {}
local comFlagged = {}
local threshold = 20
timer_Create("ixBastionNetSpam.Clear", 1.1, 0, function()
local time = os_time()
for steamID, data in pairs(netFlagged) do
for name in pairs(data.names) do
local query = mysql:Insert("bastion_netspam")
query:Insert("name", name)
query:Insert("type", 0)
query:Insert("count", netSpamCount[steamID][name] or 0)
query:Insert("steamid", steamID)
query:Insert("steam_name", data.steamName)
query:Insert("ip", data.ip)
query:Insert("time", time)
query:Execute()
end
end
netSpamCount = {}
netFlagged = {}
for steamID, data in pairs(comFlagged) do
for name in pairs(data.commands) do
local query = mysql:Insert("bastion_netspam")
query:Insert("name", name)
query:Insert("type", 1)
query:Insert("count", comSpamCount[steamID][name] or 0)
query:Insert("steamid", steamID)
query:Insert("steam_name", data.steamName)
query:Insert("ip", data.ip)
query:Insert("time", time)
query:Execute()
end
end
comSpamCount = {}
comFlagged = {}
end)
local netIgnoreList = {
["76561198002319953"] = 2
}
local netSpamAmount = {
["NetStreamDS"] = 20,
["76561198002319953"] = 30
}
function net.Incoming(len, client)
local i = net_ReadHeader()
local name = util_NetworkIDToString(i)
if (!name) then return end
local func = net.Receivers[name:lower()]
if (!func) then return end
--
-- len includes the 16 bit int which told us the message name
--
len = len - 16
if (ix.config.Get("netAntiSpam")) then
local steamID = IsValid(client) and client:SteamID64() or "UNKNOWN"
netSpamCount[steamID] = netSpamCount[steamID] or {}
netSpamCount[steamID][name] = (netSpamCount[steamID][name] or 0) + 1
if (netSpamCount[steamID][name] > (netSpamAmount[name] or threshold)) then
if (!netFlagged[steamID]) then
netFlagged[steamID] = {
names = {},
steamID = steamID,
steamName = IsValid(client) and (client.SteamName and client:SteamName() or client:Name()) or "UNKNOWN PLAYER NAME",
ip = IsValid(client) and client:IPAddress() or "UNKNOWN IP"
}
if (!ix.config.Get("netLoggingEnabled")) then
local query = mysql:Insert("bastion_netlog")
query:Insert("name", name)
query:Insert("length", len)
query:Insert("count", netSpamCount[steamID][name])
query:Insert("steamid", steamID)
query:Execute()
end
end
netFlagged[steamID].names[name] = true
end
if (ix.config.Get("netLoggingEnabled") and (!netIgnoreList[name] or netIgnoreList[name] < netSpamCount[steamID][name])) then
local query = mysql:Insert("bastion_netlog")
query:Insert("name", name)
query:Insert("length", len)
query:Insert("count", netSpamCount[steamID][name])
query:Insert("steamid", steamID)
query:Execute()
end
end
func(len, client)
end
local conSpamAmount = {
}
local conIgnoreList = {
}
PLUGIN.oldRun = PLUGIN.oldRun or concommand.Run
function concommand.Run(client, command, args, argString)
if (IsValid(client) and command and ix.config.Get("netAntiSpam")) then
local steamID = IsValid(client) and client:SteamID64() or "UNKNOWN"
comSpamCount[steamID] = comSpamCount[steamID] or {}
comSpamCount[steamID][command] = (comSpamCount[steamID][command] or 0) + 1
if (comSpamCount[steamID][command] > (conSpamAmount[command] or threshold)) then
if (!comFlagged[steamID]) then
comFlagged[steamID] = {
commands = {},
steamID = steamID,
steamName = IsValid(client) and (client.SteamName and client:SteamName() or client:Name()) or "UNKNOWN PLAYER NAME",
ip = IsValid(client) and client:IPAddress() or "UNKNOWN IP"
}
if (!ix.config.Get("netLoggingEnabled")) then
local query = mysql:Insert("bastion_netlog")
query:Insert("name", command)
query:Insert("length", #argString)
query:Insert("count", netSpamCount[steamID][command])
query:Insert("steamid", steamID)
query:Insert("args", string_len(argString or "") > 200 and string_sub(argString, 1, 200) or argString or "")
query:Execute()
end
end
comFlagged[steamID].commands[command] = true
end
if (ix.config.Get("netLoggingEnabled") and (!conIgnoreList[command] or conIgnoreList[command] < comSpamCount[steamID][command])) then
local query = mysql:Insert("bastion_netlog")
query:Insert("name", command)
query:Insert("length", #argString)
query:Insert("count", comSpamCount[steamID][command])
query:Insert("steamid", steamID)
query:Insert("args", string_len(argString or "") > 200 and string_sub(argString, 1, 200) or argString or "")
query:Execute()
end
end
return PLUGIN.oldRun(client, command, args, argString)
end

View File

@@ -0,0 +1,82 @@
--[[
| 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 PLUGIN = PLUGIN
--Made by Liquid
PLUGIN.MIN_PLAYER_COUNT = 50
PLUGIN.STOP_AFTER_CONNECTED_FOR = 3600
PLUGIN.MIN_SIZE = 10000
ORIGINAL_NET = ORIGINAL_NET or table.Copy(net)
file.CreateDir("netlogs")
local _net = ORIGINAL_NET
local IsValid = IsValid
local currentMessageName
local function netLog(players)
if player.GetCount() <= PLUGIN.MIN_PLAYER_COUNT then return end
if type(players) == "Player" then
players = { players }
elseif type(players) == "CRecipientFilter" then
players = players:GetPlayers()
end
if #players == 1 then
local ply = players[1]
if (!IsValid(ply)) then return end
if ply:TimeConnected() > PLUGIN.STOP_AFTER_CONNECTED_FOR then return end
if _net.BytesWritten() < PLUGIN.MIN_SIZE then return end
local steamid = ply:SteamID64()
local f = file.Open("netlogs/" .. steamid .. ".txt", "a", "DATA")
f:Write("[" .. os.date("%H:%M:%S - %d/%m/%Y") .. "] [" .. steamid .. "] [" .. currentMessageName .. "] " .. _net.BytesWritten() .. " bytes\n")
f:Close()
end
end
local function netLogBroadcast()
print("[BROADCAST] [" .. currentMessageName .. "] " .. _net.BytesWritten() .. " bytes")
end
net.Start = function(name, unreliable)
currentMessageName = name
return _net.Start(name, unreliable)
end
net.Send = function(players)
netLog(players)
currentMessageName = nil
return _net.Send(players)
end
net.SendOmit = function(players)
netLog(players)
currentMessageName = nil
return _net.SendOmit(players)
end
-- We can probably ignore SendPVS and SendPAS for now
--[[
net.Broadcast = function(pos)
netLogBroadcast()
currentMessageName = nil
return _net.Broadcast()
end
--]]

View File

@@ -0,0 +1,201 @@
--[[
| 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 ix = ix
local Color = Color
local chat = chat
local string = string
local IsValid = IsValid
local LocalPlayer = LocalPlayer
local MsgC = MsgC
local CAMI = CAMI
local surface = surface
local team = team
-- luacheck: globals FACTION_SERVERADMIN
-- Roll information in chat.
ix.chat.Register("gmroll", {
format = "** You have gm-rolled %s out of %s.",
color = Color(155, 111, 176),
CanHear = function(self, speaker, listener)
return speaker == listener
end,
deadCanChat = true,
OnChatAdd = function(self, speaker, text, bAnonymous, data)
chat.AddText(self.color, string.format(self.format, text, data.max or 20))
end
})
ix.chat.Register("localevent", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
OnChatAdd = function(self, speaker, text)
chat.AddText(Color(255, 150, 0), text)
end,
})
local broadcastIcon = ix.util.GetMaterial("willardnetworks/chat/broadcast_icon.png")
ix.chat.Register("localbroadcast", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
CanSay = function(self, speaker, text)
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
speaker:NotifyLocalized("notAllowed")
return false
end
end,
OnChatAdd = function(self, speaker, text)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("%s broadcasts locally \"%s\"", (speaker and speaker.Name and speaker:Name() or ""), text))
else
chat.AddText(Color(151, 161, 255), string.format("%s broadcasts locally \"%s\"", (speaker and speaker.Name and speaker:Name() or ""), text))
end
end
})
ix.chat.Register("localbroadcastme", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
CanSay = function(self, speaker, text)
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
speaker:NotifyLocalized("notAllowed")
return false
end
end,
OnChatAdd = function(self, speaker, text)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("*** %s %s", (speaker and speaker.Name and speaker:Name() or ""), text))
else
chat.AddText(Color(151, 161, 255), string.format("*** %s %s", (speaker and speaker.Name and speaker:Name() or ""), text))
end
end
})
ix.chat.Register("localbroadcastit", {
CanHear = (ix.config.Get("chatRange", 280) * 2),
CanSay = function(self, speaker, text)
if (speaker:Team() != FACTION_ADMIN and !speaker:GetCharacter():GetInventory():HasItem("wireless_microphone")) then
speaker:NotifyLocalized("notAllowed")
return false
end
end,
OnChatAdd = function(self, speaker, text)
if (ix.option.Get("standardIconsEnabled")) then
chat.AddText(broadcastIcon, Color(151, 161, 255), string.format("***' %s", text))
else
chat.AddText(Color(151, 161, 255), string.format("***' %s", text))
end
end
})
ix.chat.Register("announcement", {
OnChatAdd = function(self, speaker, text)
chat.AddText(Color(254, 238, 60), "[ADMIN] ", text)
end,
CanSay = function(self, speaker, text)
return true
end
})
-- STAFF CHAT
do
local CLASS = {}
local icon = ix.util.GetMaterial("icon16/medal_gold_3.png")
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(speaker)) then return end
if (speaker != LocalPlayer() and !ix.option.Get("staffChat")) then
local character = LocalPlayer():GetCharacter()
if (character and character:GetFaction() != FACTION_SERVERADMIN) then
MsgC(Color(255,215,0), "[Staff] ",
Color(128, 0, 255, 255), speaker:Name(), " (", speaker:SteamName(), "): ",
Color(255, 255, 255), text.."\n")
return
end
end
chat.AddText(icon, Color(255,215,0), "[Staff] ",
Color(128, 0, 255, 255), speaker:Name(), " (", speaker:SteamName(), "): ",
Color(255, 255, 255), text)
end
else
function CLASS:CanHear(speaker, listener)
return CAMI.PlayerHasAccess(listener, "Helix - Hear Staff Chat")
end
end
ix.chat.Register("staff_chat", CLASS)
end
-- GM CHAT
do
local CLASS = {}
local icon = ix.util.GetMaterial("icon16/rosette.png")
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(speaker)) then return end
chat.AddText(icon, Color(142, 28, 255), "[GM] ", Color(255, 215, 0, 255), speaker:Name(), " (", speaker:SteamName(), "): ", Color(255, 255, 255), text)
end
else
function CLASS:CanHear(speaker, listener)
return CAMI.PlayerHasAccess(listener, "Helix - Hear GM Chat")
end
end
ix.chat.Register("gm_chat", CLASS)
end
-- MENTOR CHAT
do
local CLASS = {}
local icon = ix.util.GetMaterial("icon16/user_suit.png")
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(speaker)) then return end
chat.AddText(icon, Color(66, 135, 245), "[Mentor] ", Color(66, 245, 191, 255), speaker:Name(), " (", speaker:SteamName(), "): ", Color(255, 255, 255), text)
end
else
function CLASS:CanHear(speaker, listener)
return CAMI.PlayerHasAccess(listener, "Helix - Hear Mentor Chat")
end
end
ix.chat.Register("mentor_chat", CLASS)
end
-- ACHIEVEMENT
do
local CLASS = {}
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!IsValid(data[1])) then return end
if (data[2]) then
surface.PlaySound(data[2])
end
local target = data[1]
chat.AddText(team.GetColor(target:Team()), target:SteamName(), Color(255, 255, 255), " earned the achievement ",
Color( 255, 201, 0, 255 ), text)
end
end
ix.chat.Register("achievement_get", CLASS)
end

View File

@@ -0,0 +1,662 @@
--[[
| 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 bit = bit
local IsValid = IsValid
local pairs = pairs
local table = table
local L = L
local netstream = netstream
local Entity = Entity
local print = print
local math = math
local tostring = tostring
local CAMI = CAMI
local Vector = Vector
local game = game
local RunConsoleCommand = RunConsoleCommand
local net = net
local player = player
local ipairs = ipairs
local ents = ents
local string = string
local timer = timer
local ix = ix
local PLUGIN = PLUGIN
--Links
ix.command.Add("Discord", {
description = "Get a link to the Discord Server",
privilege = "Basic Commands",
OnRun = function(self, client)
net.Start("ixOpenURL")
net.WriteString(ix.config.Get("DiscordLink"))
net.Send(client)
end,
bNoIndicator = true
})
ix.command.Add("Content", {
description = "Get a link to theWorkshop Content Pack",
privilege = "Basic Commands",
OnRun = function(self, client)
net.Start("ixOpenURL")
net.WriteString(ix.config.Get("ContentLink"))
net.Send(client)
end
})
ix.command.Add("Forum", {
description = "Get a link to the Forums",
privilege = "Basic Commands",
OnRun = function(self, client)
net.Start("ixOpenURL")
net.WriteString(ix.config.Get("ForumLink"))
net.Send(client)
end,
bNoIndicator = true
})
--Basic Admin
ix.command.Add("PGI", {
description = "Get someone's basic information and copy their SteamID.",
arguments = {
bit.bor(ix.type.player, ix.type.optional)
},
--alias = "PlyGetInfo",
privilege = "Basic Admin Commands",
OnRun = function(self, client, target)
if (!target) then
target = client:GetEyeTraceNoCursor().Entity
end
if (!IsValid(target) or !target:IsPlayer()) then
client:NotifyLocalized("bastionPGIInvalidTarget")
return
end
net.Start("ixPlayerInfo")
net.WriteEntity(target)
net.Send(client)
end,
bNoIndicator = true
})
ix.command.Add("PrintStaffList", {
alias = "PSL",
description = "Print a list of staff members online.",
OnRun = function(self, client)
local staff = {}
local bFound = false
for _, v in ipairs(player.GetAll()) do
local incognitoSetting = ix.option.Get(v, "iconIncognitoMode", false)
local iconIncognitoMode = !client:IsAdmin() and !client:IsSuperAdmin() and incognitoSetting
if (v:IsAdmin() or v:IsSuperAdmin()) and !iconIncognitoMode then
local userGroup = v:GetUserGroup()
staff[userGroup] = staff[userGroup] or {}
staff[userGroup][#staff[userGroup] + 1] = v
bFound = true
end
end
local toSend = {}
for k, v in pairs(staff) do
toSend[#toSend + 1] = {name = k, players = v}
end
table.SortByMember(toSend, "name", true)
if (bFound) then
net.Start("ixStaffList")
net.WriteUInt(#toSend, 8)
for _, v in ipairs(toSend) do
net.WriteString(v.name)
net.WriteUInt(#v.players, 8)
for i = 1, #v.players do
net.WriteEntity(v.players[i])
end
end
net.Send(client)
else
client:Notify("There are no staff online currently.")
end
end,
bNoIndicator = true
})
ix.command.Add("PrintFactionList", {
alias = "PFL",
description = "Print a list of members of a faction currently online (including on another character).",
arguments = {
ix.type.string
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, name)
if (name == "") then
return "@invalidArg", 2
end
local faction = ix.faction.teams[name]
if (!faction) then
for _, v in ipairs(ix.faction.indices) do
if (ix.util.StringMatches(L(v.name, client), name) or ix.util.StringMatches(v.uniqueID, name)) then
faction = v
break
end
end
end
if (faction) then
local players = {}
for _, v in ipairs(player.GetAll()) do
if (v:HasWhitelist(faction.index)) then
players[#players + 1] = v
end
end
net.Start("ixStaffList")
net.WriteUInt(1, 8)
net.WriteString(faction.name)
net.WriteUInt(#players, 8)
for i = 1, #players do
net.WriteEntity(players[i])
end
net.Send(client)
else
return "@invalidFaction"
end
end,
bNoIndicator = true
})
ix.command.Add("PlaySound", {
description = "Play a sound for all players (when no range is given) or those near you.",
arguments = {
ix.type.string,
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, sound, range)
local targets = range and {} or player.GetAll()
if (range) then
range = range * range
local clientPos = client:EyePos()
for _, target in ipairs(player.GetAll()) do
if (target:EyePos():DistToSqr(clientPos) < range) then
targets[#targets + 1] = target
end
end
end
net.Start("ixPlaySound")
net.WriteString(PLUGIN.soundAlias[sound] or sound)
net.Send(targets)
end,
indicator = "chatPerforming"
})
ix.command.Add("ScreenShake", {
description = "Shakes the screen of everyone in the specified range. Specify the amplitude, the frequency and the radius for it to work.",
arguments = {
ix.type.number,
ix.type.number,
ix.type.number,
ix.type.number
},
argumentNames = {"time(seconds)", "amplitude", "frequency", "radius"},
privilege = "Basic Admin Commands",
OnRun = function(self, client, seconds, strength, frequency, radius)
local vector = client:GetPos()
util.ScreenShake(vector, strength or 5, frequency or 5, seconds, radius, true)
end,
indicator = "chatPerforming"
})
ix.command.Add("PlaySoundGlobal", {
description = "Play a sound for all players.",
arguments = {
ix.type.string,
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, sound)
net.Start("ixPlaySound")
net.WriteString(PLUGIN.soundAlias[sound] or sound)
net.WriteBool(true)
net.Send(player.GetAll())
end,
indicator = "chatPerforming"
})
ix.command.Add("ShowEdicts", {
description = "Returns the amount of networked entities currently on the server.",
privilege = "Basic Admin Commands",
OnRun = function(self, client)
local edictsCount = ents.GetEdictCount()
local edictsLeft = 8192 - edictsCount
return string.format("There are currently %s edicts on the server. You can have up to %s more.", edictsCount, edictsLeft)
end,
bNoIndicator = true
})
ix.command.Add("ShowEntsInRadius", {
description = "Shows a list of entities within a given radius.",
privilege = "Basic Admin Commands",
arguments = {ix.type.number},
OnRun = function(self, client, radius)
local entities = {}
local pos = client:GetPos()
for _, v in pairs(ents.FindInSphere(pos, radius)) do
if (!IsValid(v)) then continue end
entities[#entities + 1] = table.concat({v:EntIndex(), v:GetClass(), v:GetModel() or "no model", v:GetPos():Distance(pos), v:MapCreationID()}, ", ")
end
netstream.Start(client, "ixShowEntsInRadius", table.concat(entities,"\n"))
client:NotifyLocalized("entsPrintedInConsole")
end,
bNoIndicator = true,
})
ix.command.Add("RemoveEntityByID", {
description = "Shows a list of entities within a given radius.",
superAdminOnly = true,
arguments = {ix.type.number},
OnRun = function(self, client, number)
local entity = Entity(number)
if (IsValid(entity)) then
client:NotifyLocalized("entityRemoved", number, entity:GetClass())
entity:Remove()
else
client:NotifyLocalized("entityNotFound", number)
end
end,
bNoIndicator = true,
})
if (CLIENT) then
netstream.Hook("ixShowEntsInRadius", function(text)
print(text)
end)
end
ix.command.Add("LocalEvent", {
description = "@cmdLocalEvent",
privilege = "Event",
arguments = {
ix.type.string,
bit.bor(ix.type.number, ix.type.optional)
},
OnRun = function(self, client, event, range)
local _range = range or (ix.config.Get("chatRange", 280) * 2)
ix.chat.classes.localevent.range = _range * _range
local doubleCommand = false
-- This could probably be done a bit smarter but I'm sure it'll do.
if (string.Left(string.lower(event), 11) == "/localevent") then
doubleCommand = true
event = string.Right(event, #event - 12)
elseif (string.Left(string.lower(event), 10) == "localevent") then
doubleCommand = true
event = string.Right(event, #event - 11)
end
if (doubleCommand) then
client:NotifyLocalized("textDoubleCommand", "/LocalEvent")
end
ix.chat.Send(client, "localevent", event)
end,
indicator = "chatPerforming"
})
ix.command.Add("RemovePersistedProps", {
description = "@cmdRemovePersistedProps",
superAdminOnly = true,
arguments = {
ix.type.number
},
OnRun = function(self, client, radius)
if (radius < 0) then
client:Notify("Radius must be a positive number!")
return
end
for _, entity in ipairs(ents.FindInSphere(client:GetPos(), radius)) do
if (!entity:GetNetVar("Persistent")) then continue end
entity:Remove()
end
client:Notify("Removed all persisted props in a radius of " .. radius .. " units.")
end
})
ix.command.Add("Announce", {
description = "@cmdAnnounce",
arguments = {
ix.type.text
},
OnRun = function(self, client, event)
ix.chat.Send(client, "announcement", event)
end,
OnCheckAccess = function(self, client)
return !client or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands")
end,
indicator = "chatTyping"
})
ix.command.Add("GmRoll", {
description = "Do a roll that only you can see.",
arguments = {
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Basic Admin Commands",
OnRun = function(self, client, maximum)
maximum = math.Clamp(maximum or 20, 0, 1000000)
local value = math.random(0, maximum)
ix.chat.Send(client, "gmroll", tostring(value), nil, nil, {
max = maximum
})
ix.log.Add(client, "roll", value, maximum)
end,
bNoIndicator = true
})
-- Help
ix.command.Add("Help", {
description = "@cmdStaffHelp",
arguments = ix.type.text,
alias = {"GMHelp", "ModHelp"},
OnRun = function(self, client, text)
client:Say("@ " .. text)
end,
OnCheckAccess = function(self, client)
return !CAMI.PlayerHasAccess(client, "Helix - Hear Staff Chat")
end,
indicator = "chatTyping"
})
-- Admin Chat
ix.command.Add("Staff", {
description = "Chat with other staff members.",
arguments = ix.type.text,
privilege = "Hear Staff Chat",
alias = {"Op", "Mod", "ModChat"},
OnRun = function(self, client, text)
ix.chat.Send(client, "staff_chat", text)
end,
indicator = "chatTyping"
})
-- Gamemaster Chat
ix.command.Add("GameMaster", {
description = "Chat with other Gamemasters.",
arguments = ix.type.text,
privilege = "Hear GM Chat",
alias = {"GMChat", "GM"},
OnRun = function(self, client, text)
ix.chat.Send(client, "gm_chat", text)
end,
indicator = "chatTyping"
})
-- Mentor Chat
ix.command.Add("Mentor", {
description = "Chat with other Mentors.",
arguments = ix.type.text,
privilege = "Hear Mentor Chat",
alias = {"MChat", "M"},
OnRun = function(self, client, text)
ix.chat.Send(client, "mentor_chat", text)
end,
indicator = "chatTyping"
})
-- Fun Stuff
ix.command.Add("Achievement", {
description = "Someone has earned a special achievement!",
arguments = {
ix.type.player,
ix.type.text
},
privilege = "Fun Stuff",
OnRun = function(self, client, target, text)
ix.chat.Send(client, "achievement_get", text, false, nil,
{target, "ambient/water/drip" .. math.random( 1, 4 ) .. ".wav"})
end,
indicator = "chatTyping"
})
ix.command.Add("DarwinAward", {
description = "Someone has earned an achievement: he has made the ultimate sacrifice to increase humanity's average IQ.",
arguments = {
ix.type.player
},
privilege = "Fun Stuff",
OnRun = function(self, client, target)
if (!target:Alive()) then
local pos = target:GetPos()
target:Spawn()
target:SetPos(pos)
end
target:SetMoveType(MOVETYPE_WALK)
target:SetVelocity(Vector(0, 0, 4000))
timer.Simple(1, function() PLUGIN:Explode(target) end)
ix.chat.Send(client, "achievement_get", "DARWIN AWARD", false, nil,
{target, "ambient/alarms/razortrain_horn1.wav"})
end,
indicator = "chatTyping"
})
ix.command.Add("PlyRocket", {
description = "To infinity, and beyond!.",
arguments = {
ix.type.player
},
privilege = "Fun Stuff",
OnRun = function(self, client, target)
if (!target:Alive()) then
local pos = target:GetPos()
target:Spawn()
target:SetPos(pos)
end
target:SetMoveType(MOVETYPE_WALK)
target:SetVelocity(Vector(0, 0, 4000))
timer.Simple(1, function() PLUGIN:Explode(target) end)
end,
bNoIndicator = true
})
ix.command.Add("SetTimeScale", {
description = "@cmdTimeScale",
arguments = {
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Fun Stuff",
OnRun = function(self, client, number)
local scale = math.Clamp(number or 1, 0.001, 5)
game.SetTimeScale(scale)
for _, v in ipairs(player.GetAll()) do
if (self:OnCheckAccess(v)) then
v:NotifyLocalized("bastionTimeScale", client:Name(), scale)
end
end
end,
bNoIndicator = true
})
ix.command.Add("SetGravity", {
description = "@cmdGravity",
arguments = {
bit.bor(ix.type.number, ix.type.optional)
},
privilege = "Fun Stuff",
OnRun = function(self, client, number)
RunConsoleCommand("sv_gravity", number)
for _, v in ipairs(player.GetAll()) do
if (self:OnCheckAccess(v)) then
v:NotifyLocalized("bastionGravity", client:Name(), number)
end
end
end,
bNoIndicator = true
})
-- lookup commands
ix.command.Add("LookupSteamID", {
description = "Lookup a SteamID in the Bastion user database",
arguments = {
ix.type.text
},
privilege = "Bastion Lookup",
OnRun = function(self, client, target)
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
PLUGIN:LookupSteamID(client, target)
return
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
PLUGIN:LookupSteamID(client, target)
return
end
target = ix.util.FindPlayer(target, false)
client:NotifyLocalized("bastionTargetSelected", target:Name())
PLUGIN:LookupSteamID(client, target:SteamID64())
end,
bNoIndicator = true
})
ix.command.Add("LookupIPUsers", {
description = "Lookup a SteamID in the Bastion IP database",
arguments = {
ix.type.text
},
privilege = "Bastion Lookup",
OnRun = function(self, client, target)
if (string.find(target, "^STEAM_%d+:%d+:%d+$")) then
PLUGIN:LookupIPUsers(client, target)
return
elseif (string.len(target) == 17 and string.find(target, "^%d+$")) then
PLUGIN:LookupIPUsers(client, target)
return
end
target = ix.util.FindPlayer(target, false)
client:NotifyLocalized("bastionTargetSelected", target:Name())
PLUGIN:LookupIPUsers(client, target:SteamID64())
end,
bNoIndicator = true
})
ix.command.Add("ProxyWhitelist", {
description = "Whitelist a player as trusted to disable future proxy checks.",
arguments = {
ix.type.player
},
privilege = "Bastion Whitelist",
OnRun = function(self, client, target)
if (PLUGIN:WhitelistPlayer(target)) then
client:NotifyLocalized("whitelistDone")
else
client:NotifyLocalized("whitelistError")
end
end,
indicator = "chatTyping"
})
ix.command.Add("PlyGetCharacters", {
description = "Get a list of a player's characters.",
arguments = {
ix.type.player
},
adminOnly = true,
OnRun = function(self, client, target)
client:ChatNotify(target:SteamName() .. "'s characters:")
client:ChatNotify("====================")
for _, character in pairs(target.ixCharList) do
client:ChatNotify(ix.char.loaded[character].vars.name)
end
end
})
ix.command.Add("LTARP", {
description = "Automatically ban a player for leaving to avoid RP after a grace period.",
arguments = {
ix.type.string,
ix.type.number,
bit.bor(ix.type.text, ix.type.optional),
},
adminOnly = true,
OnRun = function(self, client, steamID, days, reason)
local target = player.GetBySteamID64(steamID) or player.GetBySteamID(steamID)
if (target) then
client:Notify(target:SteamName().." is still on the server as '"..target:Name().."!")
return
end
if (days > 100) then
client:Notify("Invalid duration entered. Max ban length is 100 days.")
return
end
if (string.find(string.upper(steamID), "^STEAM")) then
steamID = util.SteamIDTo64(string.upper(steamID))
end
if (!PLUGIN.disconnects[steamID]) then
client:Notify("Could not find a disconnect for "..steamID..", manually ban them if needed.")
return
end
local info = PLUGIN.disconnects[steamID]
if (info.timer) then
client:Notify(steamID.." is already tagged for LTARP by "..info.bannedByName..".")
return
end
local reconnectTime, maxBanDelay = 30, 40
if (info.time + reconnectTime * 60 < os.time()) then
if (info.time + maxBanDelay * 60 < os.time()) then
client:Notify(steamID.." disconnected more than "..maxBanDelay.." minutes ago already. Manually ban them if needed.")
return
end
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..client:SteamName())
return
end
local uniqueID = "ixLTARP"..steamID
local bannedBy = client and client:SteamName() or "console"
timer.Create(uniqueID, info.time - os.time() + reconnectTime * 60, 1, function()
if (IsValid(client)) then
client:Notify(steamID.." did not reconnect and has been banned.")
end
RunConsoleCommand("sam", "banid", steamID, tostring(days * 60 * 24), reason != "" and reason or "Leaving to avoid RP. Appeal on willard.network ~"..bannedBy)
end)
info.timer = uniqueID
info.bannedBy = client
info.bannedByName = client and client:SteamName() or "console"
client:Notify("Timer set! "..steamID.." will be banned in "..math.ceil((info.time - os.time() + reconnectTime * 60) / 60).." minutes.")
end
})

View 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/
--]]
local properties = properties
local IsValid = IsValid
local RunConsoleCommand = RunConsoleCommand
local tostring = tostring
local Derma_StringRequest = Derma_StringRequest
local CAMI = CAMI
local hook = hook
local SetClipboardText = SetClipboardText
local LocalPlayer = LocalPlayer
local net = net
local ix = ix
local PLUGIN = PLUGIN
properties.Add("ixCopyCharName", {
MenuLabel = "#Copy Character Name",
Order = 0,
MenuIcon = "icon16/user.png",
Filter = function(self, target)
return target:IsPlayer()
and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")
and hook.Run("CanProperty", LocalPlayer(), "ixCopyCharName", target) != false
end,
Action = function(self, target)
SetClipboardText(target:Name())
LocalPlayer():NotifyLocalized("bastionCopiedCharName")
end,
})
properties.Add("ixCopySteamName", {
MenuLabel = "#Copy Steam Name",
Order = 1,
MenuIcon = "icon16/user.png",
Filter = function(self, target)
return target:IsPlayer()
and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")
and hook.Run("CanProperty", LocalPlayer(), "ixCopySteamName", target) != false
end,
Action = function(self, target)
SetClipboardText(target:SteamName())
LocalPlayer():NotifyLocalized("bastionCopiedSteamName")
end,
})
properties.Add("ixCopySteamID", {
MenuLabel = "#Copy Steam ID",
Order = 2,
MenuIcon = "icon16/user.png",
Filter = function(self, target)
return target:IsPlayer()
and CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Basic Admin Commands")
and hook.Run("CanProperty", LocalPlayer(), "ixCopySteamID", target) != false
end,
Action = function(self, target)
SetClipboardText(target:SteamID())
LocalPlayer():NotifyLocalized("bastionCopiedSteamID")
end,
})
properties.Add("ixViewInventory", {
MenuLabel = "#View Inventory",
Order = 10,
MenuIcon = "icon16/eye.png",
PrependSpacer = true,
Filter = function(self, target, client)
client = client or LocalPlayer()
return target:IsPlayer()
and CAMI.PlayerHasAccess(client, "Helix - View Inventory")
and hook.Run("CanProperty", client, "ixViewInventory", target) != false
end,
Action = function(self, target)
self:MsgStart()
net.WriteEntity(target)
self:MsgEnd()
end,
Receive = function(self, length, client)
local target = net.ReadEntity()
if (!IsValid(target)) then return end
if (!self:Filter(target, client)) then return end
PLUGIN:OpenInventory(client, target)
end
})
properties.Add("ixSetHealth", {
MenuLabel = "#Health",
Order = 100,
PrependSpacer = true,
MenuIcon = "icon16/heart.png",
Filter = function(self, target, client)
client = client or LocalPlayer()
return target:IsPlayer()
and (sam and (client:HasPermission("hp") or client:HasPermission("slay"))
or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands"))
and hook.Run("CanProperty", client, "ixSetHealth", target) != false
end,
MenuOpen = function(self, option, target)
local submenu = option:AddSubMenu()
local maxHealth = target:GetMaxHealth()
local step = maxHealth > 100 and -50 or -25
if (sam) then
if (LocalPlayer():HasPermission("hp")) then
for i = maxHealth, 1, step do
submenu:AddOption(i, function() RunConsoleCommand("sam", "hp", target:SteamID(), tostring(i)) end)
end
submenu:AddOption("1", function() RunConsoleCommand("sam", "hp", target:SteamID(), "1") end)
end
if (LocalPlayer():HasPermission("slay")) then
submenu:AddOption("Kill", function() RunConsoleCommand("sam", "slay", target:SteamID()) end)
end
else
for i = maxHealth, 1, step do
submenu:AddOption(i, function() self:SetHealth(target, i) end)
end
submenu:AddOption("1", function() self:SetHealth(target, 1) end)
submenu:AddOption("Kill", function() self:SetHealth(target, 0) end)
end
end,
SetHealth = function(self, target, health)
self:MsgStart()
net.WriteEntity(target)
net.WriteUInt(health, 16)
self:MsgEnd()
end,
Receive = function(self, length, client)
local target = net.ReadEntity()
local health = net.ReadUInt(16)
if (!IsValid(target)) then return end
if (!self:Filter(target, client)) then return end
if (health > 0) then
target:SetHealth(health)
ix.log.Add(client, "bastionSetHealth", target)
else
target:Kill()
ix.log.Add(client, "bastionSlay", target)
end
end
})
properties.Add("ixSetArmor", {
MenuLabel = "#Armor",
Order = 110,
MenuIcon = "icon16/heart.png",
Filter = function(self, target, client)
client = client or LocalPlayer()
return target:IsPlayer()
and (sam and client:HasPermission("armor")
or CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands"))
and hook.Run("CanProperty", client, "ixSetArmor", target) != false
end,
MenuOpen = function(self, option, target)
local submenu = option:AddSubMenu()
local maxArmor = 100
local step = maxArmor > 100 and -50 or -10
if (sam) then
for i = maxArmor, 1, step do
submenu:AddOption(i, function() RunConsoleCommand("sam", "armor", target:SteamID(), tostring(i)) end)
end
submenu:AddOption("Remove", function() RunConsoleCommand("sam", "armor", target:SteamID(), "0") end)
else
for i = maxArmor, 1, step do
submenu:AddOption(i, function() self:SetArmor(target, i) end)
end
submenu:AddOption("Remove", function() self:SetArmor(target, 0) end)
end
end,
SetArmor = function(self, target, armor)
self:MsgStart()
net.WriteEntity(target)
net.WriteUInt(armor, 16)
self:MsgEnd()
end,
Receive = function(self, length, client)
local target = net.ReadEntity()
local armor = net.ReadUInt(16)
if (!IsValid(target)) then return end
if (!self:Filter(target, client)) then return end
target:SetArmor(armor)
ix.log.Add(client, "bastionSetArmor", target)
end
})
properties.Add("ixSetCharName", {
MenuLabel = "#Set Name",
Order = 120,
MenuIcon = "icon16/book_edit.png",
PrependSpacer = true,
Filter = function(self, entity, client)
return CAMI.PlayerHasAccess(client, "Helix - CharSetName", nil) and entity:IsPlayer() and entity:GetCharacter()
end,
Action = function(self, entity)
Derma_StringRequest("Set Name", "Set the character's name", entity:Name(), function(text)
if (text == "") then return end
self:MsgStart()
net.WriteEntity(entity)
net.WriteString(text)
self:MsgEnd()
end)
end,
Receive = function(self, length, client)
if (CAMI.PlayerHasAccess(client, "Helix - CharSetName", nil)) then
local entity = net.ReadEntity()
local text = net.ReadString()
if (IsValid(entity) and entity:IsPlayer() and entity:GetCharacter()) then
local oldName = entity:GetCharacter():GetName()
entity:GetCharacter():SetName(text)
ix.log.Add(client, "bastionSetName", entity:GetCharacter(), oldName)
end
end
end
})
properties.Add("ixSetCharDescription", {
MenuLabel = "#Set Description",
Order = 121,
MenuIcon = "icon16/book_edit.png",
Filter = function(self, entity, client)
return CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil) and entity:IsPlayer() and entity:GetCharacter()
end,
Action = function(self, entity)
Derma_StringRequest("Set Description", "Set the character's description", entity:GetCharacter():GetDescription(), function(text)
if (text == "") then return end
self:MsgStart()
net.WriteEntity(entity)
net.WriteString(text)
self:MsgEnd()
end)
end,
Receive = function(self, length, client)
if (CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil)) then
local entity = net.ReadEntity()
local text = net.ReadString()
if (IsValid(entity) and entity:IsPlayer() and entity:GetCharacter()) then
entity:GetCharacter():SetDescription(text)
ix.log.Add(client, "bastionSetDesc", entity:GetCharacter())
end
end
end
})
properties.Add("ixPropViewOwner", {
MenuLabel = "View Owner",
Order = 405,
MenuIcon = "icon16/magnifier.png",
Filter = function(self, entity, client)
if (entity:GetClass() == "prop_physics" and CAMI.PlayerHasAccess(client, "Helix - Basic Admin Commands", nil)) then return true end
end,
Action = function(self, entity)
self:MsgStart()
net.WriteEntity(entity)
self:MsgEnd()
end,
Receive = function(self, length, client)
local entity = net.ReadEntity()
if (!IsValid(entity)) then return end
if (!self:Filter(entity, client)) then return end
local ownerCharacter = entity.ownerCharacter
local ownerName = entity.ownerName
local ownerSteamID = entity.ownerSteamID
if (ownerCharacter and ownerName and ownerSteamID) then
client:ChatNotifyLocalized("bastionPropOwnerInformation", ownerCharacter, ownerName, ownerSteamID)
else
client:ChatNotifyLocalized("bastionPropOwnerUnknown")
end
end
})

View File

@@ -0,0 +1,54 @@
--[[
| 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/
--]]
-- Remake the connect & disconnect chat classes to stop the default ones.
function PLUGIN:InitializedChatClasses()
ix.chat.classes["connect"] = nil
ix.chat.classes["disconnect"] = nil
ix.chat.Register("new_connect", {
CanSay = function(_, speaker, text)
return !IsValid(speaker)
end,
OnChatAdd = function(_, speaker, text)
local icon = ix.util.GetMaterial("willardnetworks/chat/connected_icon.png")
chat.AddText(icon, Color(151, 153, 152), L("playerConnected", text))
end,
noSpaceAfter = true
})
ix.chat.Register("new_disconnect", {
CanSay = function(_, speaker, text)
return !IsValid(speaker)
end,
OnChatAdd = function(_, speaker, text)
local icon = ix.util.GetMaterial("willardnetworks/chat/disconnected_icon.png")
chat.AddText(icon, Color(151, 153, 152), L("playerDisconnected", text))
end,
noSpaceAfter = true
})
ix.chat.Register("bastionPlayerDeath", {
CanSay = function(_, speaker, text)
return true
end,
OnChatAdd = function(_, speaker, text)
local icon = ix.util.GetMaterial("icon16/user_red.png")
chat.AddText(icon, Color(255, 0, 0), text)
end,
CanHear = function(_, speaker, listener)
return listener:IsAdmin() and ix.option.Get(listener, "playerDeathNotification")
end
})
end

View 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/
--]]
local IsValid = IsValid
local ix = ix
PLUGIN.name = "Bastion"
PLUGIN.author = "Gr4Ss"
PLUGIN.description = "Some admin extensions for Helix."
local CAMI = CAMI
CAMI.RegisterPrivilege({
Name = "Helix - Hear Staff Chat",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Hear GM Chat",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Hear Mentor Chat",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Fun Stuff",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Basic Commands",
MinAccess = "user"
})
CAMI.RegisterPrivilege({
Name = "Helix - Basic Admin Commands",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Full Remover Tool",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - HL2 Tools",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Bastion Whitelist",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - View Inventory",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Increase Character Limit",
MinAccess = "admin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Bastion Lookup",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Container Password",
MinAccess = "superadmin"
})
CAMI.RegisterPrivilege({
Name = "Helix - Proxy Notify",
MinAccess = "superadmin"
})
ix.option.Add("pgi", ix.type.bool, true, {category = "administration"})
ix.option.Add("playerDeathNotification", ix.type.bool, true, {category = "administration", bNetworked = true})
ix.config.Add("netLoggingEnabled", false, "Enable or disable net logging into the database (WARNING: PERFORMANCE IMPACT)", nil, {
category = "Bastion"
})
ix.config.Add("netAntiSpam", true, "Enable or disable net anti-spam (WARNING: PERFORMANCE IMPACT)", nil, {
category = "Bastion"
})
ix.config.Add("suppressOnPlayerChat", true, "Suppress the default OnPlayerChat hook (should not be used by helix)", nil, {
category = "Bastion"
})
ix.config.Add("maxCharactersIncreased", 5, "The maximum number of characters a player can have if they have the increased character limit permission.", nil, {
data = {min = 1, max = 50},
category = "characters"
})
ix.config.Add("charCreateInterval", 5, "How many minutes there should be between 2 successful character creations of one player (to avoid character spam).", nil, {
data = {min = 1, max = 50},
category = "characters"
})
ix.config.Add("AllowContainerSpawn", false, "Allow anyone to directly spawn containers by spawning in their prop. Disallowing this will require admins to create containers from a prop using the context menu.", nil, {
category = "containers"
})
ix.config.Add("VPNKick", false, "Kick new users if they use a VPN.", nil, {
category = "Bastion"
})
ix.config.Add("ProxyAlwaysAlert", true, "Always Discord Alert for new joiners with a VPN.", nil, {
category = "Bastion"
})
ix.config.Add("showConnectMessages", true, "Whether or not to show notifications when players connect to the server. When off, only Staff will be notified.", nil, {
category = "server"
})
ix.config.Add("showDisconnectMessages", true, "Whether or not to show notifications when players disconnect from the server. When off, only Staff will be notified.", nil, {
category = "server"
})
ix.config.Add("DiscordLink", "https://discord.gg/HbDjUQd", "Invite link to the discord.", nil, {
category = "Bastion"
})
ix.config.Add("ContentLink", "https://steamcommunity.com/sharedfiles/filedetails/?id=2145501003", "Link to the workshop collection.", nil, {
category = "Bastion"
})
ix.config.Add("ForumLink", "https://willard.network", "Link to the forums", nil, {
category = "Bastion"
})
ix.config.Add("EdictWarningLimit", 1024, "How many edicts can be left before warning messages start to appear.", nil, {
data = {min = 100, max = 1024},
category = "Bastion"
})
ix.flag.Add("a", "Access to the advanced duplicator.")
ix.lang.AddTable("english", {
getPlayerInfo = "Get Player Info",
optStaffChat = "Show Staff Chat on all characters",
optdStaffChat = "Turns on/off staff chat on all characters. When off, will only show staff chat while in observer or on an admin character.",
optPgi = "Copy Steam ID to clipboard on 'PGI'/'View Player'",
optdPgi = "Allows you to turn on/off the automatic copy-to-clipboard of a player's SteamID when using the PGI command or 'View Player' context menu option.",
cmdStaffHelp = "Call for help from all the staff.\n",
cmdAnnounce = "Make an OOC admin announcement to the entire server.",
cmdLocalEvent = "Make an IC admin event that can only be heard within a given radius.",
bastionPGIInvalidTarget = "You must enter or be looking at a valid target.",
bastionTakingItemsTooQuickly = "You are taking items too quickly! Please slow down.",
bastionItemDropSpamKick = "%s was kicked for item drop exploiting.",
bastionItemDropSpamWarn = "%s was warned for item drop exploiting.",
bastionItemDropTooQuick = "You are dropping items too quickly! Please slow down.",
bastionItemTakeWarn = "%s was warned for taking items too quickly.",
bastionItemTakeKick = "%s was kicked for dropping items too quickly.",
charCreateTooFast = "You are creating characters too fast. Please wait at least %d minutes between attempts.",
bastionCopiedCharName = "Character name copied",
bastionCopiedSteamName = "Steam name copied",
bastionCopiedSteamID = "Steam ID copied",
bastionGoto = "Go to player",
bastionCopyCharName = "Copy character name",
bastionCopySteamName = "Copy steam name",
bastionNoRecordFound = "Could not find any records for %s.",
bastionResultsPrinted = "Results were printed in console.",
bastionProxyNotify = "%s connected as new player while using a VPN/Proxy.",
bastionPropOwnerInformation = "The owner of this prop is %s (%s - %s).",
bastionPropOwnerUnknown = "The owner of this prop has not been registered!",
whitelistDone = "Player was whitelisted.",
whitelistError = "Something went wrong whitelisting the player. Ask a dev.",
cmdTimeScale = "Change the timescale (min 0.001, max 5).",
bastionTimeScale = "%s has set the timescale to %d.",
cmdGravity = "Change the gravity.",
bastionGravity = "%s has set the gravity to %d.",
edictWarning = "Only %d edicts are left! Total edict count is currently: %d/8192!",
edictCritical = "Only %d edicts are left! Total edict count is currently: %d/8192! Emergency Cleanup Required!",
entsPrintedInConsole = "Entity list has been printed in console.",
entityRemoved = "Entity %d (%s) was removed!",
entityNotFound = "Entity %d was not found/is not valid.",
optPlayerDeathNotification = "Player Death Notification",
optdPlayerDeathNotification = "Whether to send a chat message when a player dies.",
cmdRemovePersistedProps = "Remove all persisted props in a radius around you."
})
ix.lang.AddTable("spanish", {
bastionCopySteamName = "Copiar nombre de Steam",
bastionPGIInvalidTarget = "Debes especificar o estar mirando a un objetivo válido.",
cmdLocalEvent = "Haz un evento IC de administrador que sólo pueda ser escuchado dentro de un radio determinado.",
bastionNoRecordFound = "No se ha podido encontrar ningún registro para %s.",
cmdAnnounce = "Haz un anuncio OOC de administrador a todo el servidor.",
cmdGravity = "Cambia la gravedad.",
bastionCopiedSteamID = "Steam ID copiada",
bastionProxyNotify = "%s se ha conectado como nuevo jugador mientras usa una VPN/Proxy.",
bastionTakingItemsTooQuickly = "¡Estás agarrando objetos demasiado rápido! Por favor, reduce la velocidad.",
optdStaffChat = "Activa/desactiva el Chat de Staff en todos los personajes. Cuando está desactivado, sólo se mostrará el Chat de Staff mientras se esté en observer o en un personaje administrativo.",
entityNotFound = "La entidad %d no fue encontrada/no es válida.",
whitelistDone = "El jugador estaba en la Whitelist.",
bastionCopiedCharName = "Nombre del personaje copiado",
getPlayerInfo = "Obtener información del jugador",
bastionCopyCharName = "Copiar el nombre del personaje",
charCreateTooFast = "Estás creando personajes demasiado rápido. Por favor, espera al menos %d minutos entre intentos.",
entsPrintedInConsole = "La lista de entidades se ha impreso en la consola.",
bastionItemDropTooQuick = "¡Estás soltando objetos demasiado rápido! Por favor, baja la velocidad.",
bastionGravity = "%s ha fijado la gravedad en %d.",
entityRemoved = "¡La entidad %d (%s) ha sido eliminada!",
cmdStaffHelp = "Pide ayuda a todo el Staff.\n",
bastionCopiedSteamName = "Nombre de Steam copiado",
bastionItemTakeWarn = "%s fue advertido por agarrar objetos demasiado rápido.",
bastionResultsPrinted = "Los resultados se imprimieron en la consola.",
optStaffChat = "Mostrar el Chat de Staff en todos los personajes",
bastionGoto = "Ir al jugador",
cmdTimeScale = "Cambie la escala de tiempo (mínimo 0,001, máximo 5).",
whitelistError = "Algo fue mal cuando se intentó meter al jugador en la whitelist. Contacta con un desarrollador.",
bastionTimeScale = "%s ha fijado la escala de tiempo en %d.",
bastionItemDropSpamKick = "%s ha sido expulsado por exploits relacionados con item-dropping.",
optdPgi = "Te permite activar/desactivar la copia automática al portapapeles del SteamID de un jugador cuando usa el comando PGI o la opción del menú contextual 'Ver Jugador'.",
bastionItemDropSpamWarn = "%s ha sido advertido del uso de exploits relacionados con item-dropping.",
bastionItemTakeKick = "%s ha sido expulsado por soltar objetos demasiado rápido.",
optPgi = "Copiar Steam ID al portapapeles en el 'PGI'/'Ver Jugador'",
edictWarning = "¡Solamente quedan disponibles %d edictos! ¡El conteo total de edictos es de: %d/8192!"
})
PLUGIN.soundAlias = {
["restricted_block_deploy"] = "voices/dispatch/restrictedblock_deployment.wav",
["restricted_block"] = "voices/dispatch/restrictedblock_warning.wav",
["access_restricted"] = "voices/dispatch/access_restricted.wav",
["anticivil_evading"] = "voices/dispatch/anticivil_evading.wav",
["civil_insurrection"] = "voices/dispatch/civil_insurrection.wav",
["victory_music"] = "music/scary_tense/victory_music.mp3",
}
ix.util.Include("cl_plugin.lua")
ix.util.Include("sh_classes.lua")
ix.util.Include("sh_commands.lua")
ix.util.Include("sh_context.lua")
ix.util.Include("sh_hooks.lua")
ix.util.Include("sv_hooks.lua")
ix.util.Include("sv_plugin.lua")
ix.util.Include("modules/sh_bindchecker.lua")
function PLUGIN:GetMaxPlayerCharacter(client)
if (CAMI.PlayerHasAccess(client, "Helix - Increase Character Limit")) then
return ix.config.Get("maxCharactersIncreased", 8)
end
end
function PLUGIN:CanProperty(client, property, entity)
if (property == "container_setpassword" and !CAMI.PlayerHasAccess(client, "Helix - Container Password")) then
return false
end
end
function PLUGIN:CanPlayerSpawnContainer()
if (!ix.config.Get("AllowContainerSpawn")) then
return false
end
end
function PLUGIN:CanPlayerAccessDoor(client)
if (client:GetMoveType() == MOVETYPE_NOCLIP and !client:InVehicle()) then return true end
if (ix.faction.Get(client:Team()).lockAllDoors) then return true end
end
PLUGIN.removeWhitelist = {
["prop_physics"] = true,
["prop_ragdoll"] = true
}
local hl2Tools = {
["env_headcrabcanister"] = true,
["item_ammo_crate"] = true,
["item_item_crate"] = true,
["prop_door"] = true,
["prop_thumper"] = true
}
function PLUGIN:CanTool(client, trace, tool)
if (tool == "remover" and !CAMI.PlayerHasAccess(client, "Helix - Full Remover Tool")) then
if (IsValid(trace.Entity) and !self.removeWhitelist[trace.Entity:GetClass()]) then
return false
end
elseif (tool == "advdupe2" and !client:GetCharacter():HasFlags("a")) then
return false
elseif (hl2Tools[tool] and !CAMI.PlayerHasAccess(client, "Helix - HL2 Tools")) then
return false
end
end

View File

@@ -0,0 +1,193 @@
--[[
| 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 timer = timer
local pairs = pairs
local hook = hook
local CurTime = CurTime
local player = player
local ipairs = ipairs
local ix = ix
local IsValid = IsValid
local PLUGIN = PLUGIN
PLUGIN.disconnects = PLUGIN.disconnects or {}
PLUGIN.takeCounter = {}
timer.Create("ixBastionAntiTakeSpam", 1, 0, function()
for client, amount in pairs(PLUGIN.takeCounter) do
if (amount < 10) then continue end
if (!IsValid(client)) then continue end
for _, admin in ipairs(player.GetAll()) do
if (admin:IsSuperAdmin()) then
admin:NotifyLocalized("bastionItemTakeKick", client:Name())
end
end
client:Kick("Item take spam")
end
PLUGIN.takeCounter = {}
end)
function PLUGIN:CanPlayerInteractItem(client, action, item, data)
if (action == "take") then
if (self.takeCounter[client] and self.takeCounter[client] >= 5) then
if (self.takeCounter[client] == 5) then
for _, v in ipairs(player.GetAll()) do
if (v:IsSuperAdmin()) then
v:NotifyLocalized("bastionItemTakeWarn", client:Name())
end
end
client:NotifyLocalized("bastionTakingItemsTooQuickly")
end
self.takeCounter[client] = self.takeCounter[client] + 1
return false
end
elseif (action == "drop" and client.ixAntiItemSpam and client.ixAntiItemSpam > CurTime()) then
return false
end
end
function PLUGIN:PlayerInteractItem(client, action, item)
if (action == "take") then
self.takeCounter[client] = (self.takeCounter[client] or 0) + 1
end
end
PLUGIN.itemSpawns = {}
function PLUGIN:OnItemSpawned(entity)
if (IsValid(self.itemSpawns[entity.ixItemID])) then
if (self.itemSpawns[entity.ixItemID].ixItemID != entity.ixItemID) then
return -- just in case
end
--Now we are trying to spawn an item which already has an entity!
--Check if it is the same person, in case of weird behaviour
if (entity.ixSteamID == self.itemSpawns[entity.ixItemID]) then
local client = player.GetBySteamID(entity.ixSteamID)
if ((client.ixAntiItemSpam or 0) > CurTime()) then
for _, v in ipairs(player.GetAll()) do
if (v:IsSuperAdmin()) then
v:NotifyLocalized("bastionItemDropSpamKick", client:Name())
end
end
client:Kick("Item drop spam")
else
client.ixAntiItemSpam = CurTime() + 10
for _, v in ipairs(player.GetAll()) do
if (v:IsSuperAdmin()) then
v:NotifyLocalized("bastionItemDropSpamWarn", client:Name())
end
end
client:NotifyLocalized("bastionItemDropTooQuick")
end
end
self.itemSpawns[entity.ixItemID]:Remove()
self.itemSpawns[entity.ixItemID] = entity
else
self.itemSpawns[entity.ixItemID] = entity
end
end
function PLUGIN:CanPlayerCreateCharacter(client)
if (client.ixNextCharCreate and (client.ixNextCharCreate + ix.config.Get("charCreateInterval") * 60) > CurTime()) then
return false, "charCreateTooFast", ix.config.Get("charCreateInterval")
end
end
function PLUGIN:OnCharacterCreated(client)
if (!client:IsAdmin()) then
client.ixNextCharCreate = CurTime()
end
end
function PLUGIN:PlayerSpawnedProp(client, model, entity)
entity.ownerCharacter = client:GetName()
entity.ownerName = client:SteamName()
entity.ownerSteamID = client:SteamID()
end
function PLUGIN:OnPlayerHitGround(client, inWater, onFloater, speed)
local currentVelocity = client:GetVelocity()
client:SetVelocity(-Vector(currentVelocity.x, currentVelocity.y, 0))
end
function PLUGIN:PlayerInitialSpawn(client)
local receivers
if (!ix.config.Get("showConnectMessages", true)) then
receivers = {}
for _, ply in ipairs(player.GetAll()) do
if (ply:IsAdmin()) then
receivers[#receivers + 1] = ply
end
end
end
-- Give some time for the player's data to be loaded, just in case.
timer.Simple(1, function()
ix.chat.Send(nil, "new_connect", client:SteamName(), false, receivers)
end)
end
function PLUGIN:PlayerDisconnected(client)
local receivers
self.disconnects[client:SteamID64()] = {time = os.time(), charID = client:GetCharacter() and client:GetCharacter():GetID()}
if (!ix.config.Get("showDisconnectMessages", true)) then
receivers = {}
for _, ply in ipairs(player.GetAll()) do
if (ply:IsAdmin()) then
receivers[#receivers + 1] = ply
end
end
end
ix.chat.Send(nil, "new_disconnect", client:SteamName(), false, receivers)
end
function PLUGIN:PlayerLoadedCharacter(client, character, lastChar)
if (self.disconnects[client:SteamID64()]) then
local info = self.disconnects[client:SteamID64()]
if (info.timer) then
timer.Remove(info.timer)
if (IsValid(info.bannedBy)) then
if (info.charID) then
if (character:GetID() == info.charID) then
info.bannedBy:Notify(client:SteamName().." has reconnected and is back on their character "..character:GetName()..".")
end
else
info.bannedBy:Notify(client:SteamName().." has reconnected on a different character '"..character:GetName().."'.")
end
end
end
end
end
function PLUGIN:PlayerDeath(client, inflictor, attacker)
if (!client:GetCharacter()) then return end
ix.chat.Send(client, "bastionPlayerDeath", client:GetName() .. " (" .. client:SteamName() .. ") has died at " .. (client.ixArea and client.ixArea != "" and client.ixArea or "an Uncategorized Location") .. ".")
end
hook.Add("SAM.RanCommand", "BastionSamCommandLogs", function(client, cmd_name, args, cmd)
ix.log.Add(client, "bastionSamCommand", cmd_name, args, cmd)
end)

View 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/
--]]
local util = util
local file = file
local table = table
local timer = timer
local ipairs = ipairs
local player = player
local CAMI = CAMI
local string = string
local ents = ents
local IsValid = IsValid
local ix = ix
local PLUGIN = PLUGIN
util.AddNetworkString("ixOpenURL")
util.AddNetworkString("ixPlayerInfo")
util.AddNetworkString("ixStaffList")
util.AddNetworkString("ixPlaySound")
function PLUGIN:InitializedPlugins()
if (!CHTTP) then return end
self.API_KEY = file.Read("gamemodes/helix/plugins/bastion/apikey_proxy.txt", "GAME")
if (self.API_KEY) then
self.API_KEY = string.gsub(self.API_KEY, "[^%w%-]", "")
end
self.DISCORD_WEBHOOK_ALTS = file.Read("gamemodes/helix/plugins/bastion/apiwebhook_disocrd.txt", "GAME")
end
function PLUGIN:InitializedConfig()
ix.config.Set("EdictWarningLimit", 1024)
end
ix.util.Include("modules/sv_banlist.lua")
ix.util.Include("modules/sv_antialt.lua")
--ix.util.Include("modules/sv_netsizelog.lua")
--ix.util.Include("modules/sv_netmonitor.lua") --high performance impact!
ix.log.AddType("bastionCheckInfo", function(client, target)
return string.format("%s has checked %s's info.", client:GetName(), target:GetName())
end)
ix.log.AddType("bastionSetHealth", function(client, target)
return string.format("%s has set %s's health to %d.", client:GetName(), target:GetName(), target:Health())
end)
ix.log.AddType("bastionSetArmor", function(client, target)
return string.format("%s has set %s's armor to %d.", client:GetName(), target:GetName(), target:Armor())
end)
ix.log.AddType("bastionSetName", function(client, target, oldName)
return string.format("%s has set %s's name to %s.", client:GetName(), oldName, target:GetName())
end)
ix.log.AddType("bastionSetDesc", function(client, target)
return string.format("%s has set %s's description to %s.", client:GetName(), target:GetName(), target:GetDescription())
end)
ix.log.AddType("bastionSlay", function(client, target)
return string.format("%s has slayed %s.", client:GetName(), target:GetName())
end, FLAG_DANGER)
ix.log.AddType("bastionInvSearch", function(client, name)
return string.format("%s is admin-searching %s.", client:GetName(), name)
end)
ix.log.AddType("bastionInvClose", function(client, name)
return string.format("%s has closed %s.", client:GetName(), name)
end)
ix.log.AddType("netstreamHoneypot", function(client, hook, bNoAccess)
return string.format("[BANME] %s has just hit the %s HONEYPOT!!! Please ban SteamID: %s (trigger reason: %s)", client:SteamName(), hook, client:SteamID(), bNoAccess and "no access" or "invalid arg")
end, FLAG_DANGER)
ix.log.AddType("luaHack", function(client, name)
return string.format("[BANME] %s just tried to %s through lua-injection!!! Please ban SteamID: %s", client:SteamName(), name, client:SteamID())
end)
ix.log.AddType("bastionSamCommand", function(client, cmd, args)
return string.format("%s ran SAM command '%s' with arguments: '%s'", client:Name(), cmd, table.concat(args, "' '"))
end)
ix.log.AddType("samReportClaimed", function(client, reporter)
return string.format("%s claimed a report by %s (%s).", client:Name(), reporter:Name(), reporter:SteamName())
end)
timer.Create("ixBastionEdictCheck", 60, 0, function()
local edictsCount = ents.GetEdictCount()
local edictsLeft = 8192 - edictsCount
if (edictsLeft < ix.config.Get("EdictWarningLimit")) then
if (edictsLeft < 600) then
for _, v in ipairs(player.GetAll()) do
if (CAMI.PlayerHasAccess(v, "Helix - Basic Admin Commands")) then
v:NotifyLocalized("edictCritical", edictsLeft, edictsCount)
end
end
else
for _, v in ipairs(player.GetAll()) do
if (CAMI.PlayerHasAccess(v, "Helix - Basic Admin Commands")) then
v:NotifyLocalized("edictWarning", edictsLeft, edictsCount)
end
end
end
end
end)
function PLUGIN:Explode(target)
local explosive = ents.Create("env_explosion")
explosive:SetPos(target:GetPos())
explosive:SetOwner(target)
explosive:Spawn()
explosive:SetKeyValue("iMagnitude", "1")
explosive:Fire("Explode", 0, 0)
explosive:EmitSound("ambient/explosions/explode_4.wav", 500, 500)
target:StopParticles()
target:Kill()
end
function PLUGIN:OpenInventory(client, target)
if (!IsValid(client) or !IsValid(target)) then return end
if (!target:IsPlayer()) then return end
local character = target:GetCharacter()
if (!character) then return end
local inventory = character:GetInventory()
if (inventory) then
local name = target:Name().."'s inventory"
ix.storage.Open(client, inventory, {
entity = target,
name = name,
OnPlayerClose = function()
ix.log.Add(client, "bastionInvClose", name)
end
})
ix.log.Add(client, "bastionInvSearch", name)
end
end