├── .gitignore ├── addon.json ├── lua ├── patchprotect │ ├── shared │ │ └── patchprotect.lua │ ├── client │ │ ├── fonts.lua │ │ ├── csettings.lua │ │ ├── buddy.lua │ │ ├── hud.lua │ │ ├── derma.lua │ │ └── panel.lua │ └── server │ │ ├── buddy.lua │ │ ├── config.lua │ │ ├── cleanup.lua │ │ ├── settings.lua │ │ ├── antispam.lua │ │ └── propprotection.lua └── autorun │ ├── pprotect_init.lua │ └── cppi.lua ├── README.md └── pprotect_import_blocked_props.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.bat 2 | .DS_Store 3 | ._* 4 | -------------------------------------------------------------------------------- /addon.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "PatchProtect (PProtect)", 3 | "type": "tool", 4 | "tags": ["build"], 5 | "ignore": ["*.git*", "*.bat", "*.md"] 6 | } 7 | -------------------------------------------------------------------------------- /lua/patchprotect/shared/patchprotect.lua: -------------------------------------------------------------------------------- 1 | -- GET OWNER 2 | function sh_PProtect.GetOwner(ent) 3 | if !ent then return end 4 | return ent:GetNWEntity('pprotect_owner') 5 | end 6 | 7 | -- CHECK SHARED 8 | -- ent: valid entity to check for shared state 9 | -- mode: string value for the mode to check for 10 | function sh_PProtect.IsShared(ent, mode) 11 | return ent:GetNWBool('pprotect_shared_' .. mode) 12 | end 13 | -------------------------------------------------------------------------------- /lua/patchprotect/client/fonts.lua: -------------------------------------------------------------------------------- 1 | local fonts = {} 2 | function cl_PProtect.setFont(f, s, b, a, sh, sy) 3 | b, a, sh, sy = b or 500, a or false, sh or false, sy or false 4 | local fstr = 'pprotect_' .. f .. '_' .. tostring(s) .. '_' .. tostring(b) .. '_' .. string.sub(tostring(a), 1, 1) .. '_' .. string.sub(tostring(sh), 1, 1) 5 | 6 | if table.HasValue(fonts, fstr) then 7 | return fstr 8 | end 9 | 10 | surface.CreateFont(fstr, { 11 | font = f, 12 | size = s, 13 | weight = b, 14 | antialias = a, 15 | shadow = sh, 16 | symbol = sy 17 | }) 18 | 19 | table.insert(fonts, fstr) 20 | 21 | return fstr 22 | end -------------------------------------------------------------------------------- /lua/patchprotect/server/buddy.lua: -------------------------------------------------------------------------------- 1 | ------------------- 2 | -- CHECK BUDDY -- 3 | ------------------- 4 | 5 | function sv_PProtect.IsBuddy(ply, bud, mode) 6 | if !ply or !ply.Buddies or !bud:IsPlayer() or !ply.Buddies[bud:SteamID()] or !ply.Buddies[bud:SteamID()].bud then 7 | return false 8 | end 9 | if (!mode and ply.Buddies[bud:SteamID()].bud == true) or (ply.Buddies[bud:SteamID()].bud == true and ply.Buddies[bud:SteamID()].perm[mode] == true) then 10 | return true 11 | end 12 | return false 13 | end 14 | 15 | -------------------- 16 | -- SEND BUDDIES -- 17 | -------------------- 18 | 19 | -- SEND BUDDY 20 | net.Receive('pprotect_buddy', function(len, ply) 21 | ply.Buddies = net.ReadTable() 22 | end) 23 | 24 | -- NOTIFICATION 25 | net.Receive('pprotect_info_buddy', function(len, ply) 26 | local bud = net.ReadEntity() 27 | sv_PProtect.Notify(bud, ply:Nick() .. ' added you as a buddy.', 'normal') 28 | end) 29 | 30 | -- SEND BUDDIES TO CLIENT 31 | concommand.Add('pprotect_send_buddies', function(ply, cmd, args) 32 | local bud = player.GetByUniqueID(args[1]) 33 | if !bud or !bud.Buddies then return end 34 | net.Start('pprotect_send_buddies') 35 | net.WriteBool(sv_PProtect.IsBuddy(ply, bud)) 36 | net.Send(ply) 37 | end) 38 | -------------------------------------------------------------------------------- /lua/patchprotect/client/csettings.lua: -------------------------------------------------------------------------------- 1 | ----------------------- 2 | -- CLIENT SETTINGS -- 3 | ----------------------- 4 | 5 | -- Delete old settings version 6 | if sql.QueryValue("SELECT value FROM pprotect_csettings WHERE setting = 'OwnerHUD'") == '1' then 7 | sql.Query('DROP TABLE pprotect_csettings') 8 | end 9 | 10 | -- Create SQL-CSettings-Table 11 | if !sql.TableExists('pprotect_csettings') then 12 | sql.Query('CREATE TABLE IF NOT EXISTS pprotect_csettings (setting TEXT, value TEXT)') 13 | end 14 | 15 | -- Set default CSettings 16 | local csettings_default = { 17 | ownerhud = true, 18 | fppmode = false, 19 | notes = true 20 | } 21 | 22 | -- Check/Load SQL-CSettings 23 | table.foreach(csettings_default, function(setting, value) 24 | local v = sql.QueryValue("SELECT value FROM pprotect_csettings WHERE setting = '" .. setting .. "'") 25 | if !v then 26 | sql.Query("INSERT INTO pprotect_csettings (setting, value) VALUES ('" .. setting .. "', '" .. tostring(value) .. "')") 27 | cl_PProtect.Settings.CSettings[setting] = value 28 | else 29 | cl_PProtect.Settings.CSettings[setting] = tobool(v) 30 | end 31 | end) 32 | 33 | -- Update CSettings 34 | function cl_PProtect.update_csetting(setting, value) 35 | sql.Query("UPDATE pprotect_csettings SET value = '" .. tostring(value) .. "' WHERE setting = '" .. setting .. "'") 36 | cl_PProtect.Settings.CSettings[setting] = value 37 | end 38 | 39 | -- Reset CSettings 40 | concommand.Add('pprotect_reset_csettings', function(ply, cmd, args) 41 | sql.Query('DROP TABLE pprotect_csettings') 42 | print('[PProtect-CSettings] Successfully deleted all Client Settings.') 43 | print('[PProtect-CSettings] PLEASE RECONNECT TO GET A NEW TABLE.') 44 | end) 45 | -------------------------------------------------------------------------------- /lua/autorun/pprotect_init.lua: -------------------------------------------------------------------------------- 1 | -------------------------------- 2 | -- LOAD SERVER/CLIENT FILES -- 3 | -------------------------------- 4 | 5 | -- Create shared table 6 | sh_PProtect = {} 7 | 8 | -- Include shared files 9 | include('patchprotect/shared/patchprotect.lua') 10 | 11 | if SERVER then 12 | -- Create server table 13 | sv_PProtect = { 14 | Settings = {} 15 | } 16 | 17 | -- Include server files 18 | include('patchprotect/server/config.lua') 19 | include('patchprotect/server/settings.lua') 20 | include('patchprotect/server/antispam.lua') 21 | include('patchprotect/server/propprotection.lua') 22 | include('patchprotect/server/cleanup.lua') 23 | include('patchprotect/server/buddy.lua') 24 | 25 | -- Force clients to download all client files 26 | AddCSLuaFile() 27 | AddCSLuaFile('patchprotect/client/csettings.lua') 28 | AddCSLuaFile('patchprotect/client/fonts.lua') 29 | AddCSLuaFile('patchprotect/client/hud.lua') 30 | AddCSLuaFile('patchprotect/client/derma.lua') 31 | AddCSLuaFile('patchprotect/client/panel.lua') 32 | AddCSLuaFile('patchprotect/client/buddy.lua') 33 | 34 | -- Force clients to download all shared files 35 | AddCSLuaFile('patchprotect/shared/patchprotect.lua') 36 | else 37 | -- Create client table 38 | cl_PProtect = { 39 | Settings = { 40 | Antispam = {}, 41 | Propprotection = {}, 42 | CSettings = {} 43 | }, 44 | Buddies = {} 45 | } 46 | 47 | -- Include client files 48 | include('patchprotect/client/csettings.lua') 49 | include('patchprotect/client/fonts.lua') 50 | include('patchprotect/client/hud.lua') 51 | include('patchprotect/client/derma.lua') 52 | include('patchprotect/client/panel.lua') 53 | include('patchprotect/client/buddy.lua') 54 | end 55 | -------------------------------------------------------------------------------- /lua/patchprotect/server/config.lua: -------------------------------------------------------------------------------- 1 | ----------------------- 2 | -- NETWORK STRINGS -- 3 | ----------------------- 4 | 5 | -- SETTINGS 6 | util.AddNetworkString('pprotect_new_settings') 7 | util.AddNetworkString('pprotect_save') 8 | 9 | -- CLEANUP 10 | util.AddNetworkString('pprotect_cleanup') 11 | util.AddNetworkString('pprotect_new_counts') 12 | 13 | -- BUDDY 14 | util.AddNetworkString('pprotect_buddy') 15 | util.AddNetworkString('pprotect_info_buddy') 16 | util.AddNetworkString('pprotect_send_buddies') 17 | 18 | -- BLOCKED PROPS/ENTS 19 | util.AddNetworkString('pprotect_request_ents') 20 | util.AddNetworkString('pprotect_send_ents') 21 | util.AddNetworkString('pprotect_save_ents') 22 | util.AddNetworkString('pprotect_save_cent') 23 | 24 | -- ANTISPAMED/BLOCKED TOOLS 25 | util.AddNetworkString('pprotect_request_tools') 26 | util.AddNetworkString('pprotect_send_tools') 27 | util.AddNetworkString('pprotect_save_tools') 28 | 29 | -- NOTIFICATIONS 30 | util.AddNetworkString('pprotect_notify') 31 | 32 | ---------------------- 33 | -- DEFAULT CONFIG -- 34 | ---------------------- 35 | 36 | sv_PProtect.Config = {} 37 | 38 | -- ANTI SPAM 39 | sv_PProtect.Config.Antispam = { 40 | enabled = true, 41 | admins = false, 42 | alert = true, 43 | prop = true, 44 | tool = true, 45 | toolblock = true, 46 | propblock = true, 47 | entblock = true, 48 | propinprop = true, 49 | cooldown = 0.3, 50 | spam = 2, 51 | spamaction = 'Nothing', 52 | bantime = 10, 53 | concommand = 'Put your command here' 54 | } 55 | 56 | -- PROP PROTECTION 57 | sv_PProtect.Config.Propprotection = { 58 | enabled = true, 59 | superadmins = true, 60 | admins = false, 61 | adminscleanup = false, 62 | use = true, 63 | reload = true, 64 | damage = true, 65 | damageinvehicle = true, 66 | gravgun = true, 67 | proppickup = true, 68 | creator = false, 69 | propdriving = false, 70 | worldpick = false, 71 | worlduse = true, 72 | worldtool = false, 73 | worldgrav = true, 74 | propdelete = true, 75 | adminprops = false, 76 | delay = 120 77 | } 78 | -------------------------------------------------------------------------------- /lua/patchprotect/client/buddy.lua: -------------------------------------------------------------------------------- 1 | -------------------------- 2 | -- BUDDY SQL SETTINGS -- 3 | -------------------------- 4 | 5 | -- Load Buddies 6 | hook.Add('InitPostEntity', 'pprotect_load_buddies', function() 7 | if file.Exists('pprotect_buddies.txt', 'DATA') then 8 | local buds = file.Read('pprotect_buddies.txt', 'DATA') 9 | cl_PProtect.Buddies = util.JSONToTable(buds) 10 | 11 | net.Start('pprotect_buddy') 12 | net.WriteTable(cl_PProtect.Buddies) 13 | net.SendToServer() 14 | end 15 | end) 16 | 17 | -- Save Buddies 18 | local function saveBuddies() 19 | file.Write('pprotect_buddies.txt', util.TableToJSON(cl_PProtect.Buddies)) 20 | 21 | net.Start('pprotect_buddy') 22 | net.WriteTable(cl_PProtect.Buddies) 23 | net.SendToServer() 24 | end 25 | 26 | -- Reset Buddies 27 | concommand.Add('pprotect_reset_buddies', function() 28 | cl_PProtect.Buddies = {} 29 | cl_PProtect.saveBuddies() 30 | print('[PProtect-Buddy] Successfully deleted all Buddies.') 31 | end) 32 | 33 | -- Set Buddy 34 | function cl_PProtect.setBuddy(bud, c) 35 | local id = bud:SteamID() 36 | if !cl_PProtect.Buddies[id] then 37 | cl_PProtect.Buddies[id] = { 38 | bud = false, 39 | perm = { 40 | phys = false, 41 | tool = false, 42 | use = false, 43 | prop = false, 44 | dmg = false 45 | } 46 | } 47 | end 48 | 49 | cl_PProtect.Buddies[id].bud = c 50 | 51 | if c then 52 | cl_PProtect.ClientNote('Added ' .. bud:Nick() .. ' to the Buddy-List.', 'info') 53 | 54 | -- Send message to buddy 55 | net.Start('pprotect_info_buddy') 56 | net.WriteEntity(bud) 57 | net.SendToServer() 58 | else 59 | cl_PProtect.ClientNote('Removed ' .. bud:Nick() .. ' from the Buddy-List.', 'info') 60 | end 61 | 62 | saveBuddies() 63 | end 64 | 65 | -- Set Buddy 66 | function cl_PProtect.setBuddyPerm(bud, p, c) 67 | if !bud then return end 68 | local id = bud:SteamID() 69 | if !cl_PProtect.Buddies[id] then 70 | cl_PProtect.Buddies[id] = { 71 | bud = false, 72 | perm = { 73 | phys = false, 74 | tool = false, 75 | use = false, 76 | prop = false, 77 | dmg = false 78 | } 79 | } 80 | end 81 | 82 | cl_PProtect.Buddies[id].perm[p] = c 83 | 84 | saveBuddies() 85 | end 86 | -------------------------------------------------------------------------------- /lua/autorun/cppi.lua: -------------------------------------------------------------------------------- 1 | -- http://ulyssesmod.net/archive/CPPI_v1-3.pdf 2 | 3 | CPPI = CPPI or {} 4 | 5 | CPPI.CPPI_DEFER = 8080 -- PP (PathProtect) 6 | CPPI.CPPI_NOTIMPLEMENTED = 012019 -- month/year of newest version 7 | 8 | local PLAYER = FindMetaTable('Player') 9 | local ENTITY = FindMetaTable('Entity') 10 | 11 | -- Get name of prop protection 12 | function CPPI:GetName() 13 | return 'PatchProtect' 14 | end 15 | 16 | -- Get version of prop protection 17 | function CPPI:GetVersion() 18 | return '1.4.0' 19 | end 20 | 21 | -- Get interface version of CPPI 22 | function CPPI:GetInterfaceVersion() 23 | return 1.3 24 | end 25 | 26 | -- Get name of player from UID 27 | function CPPI:GetNameFromUID(uid) 28 | local ply = player.GetByUniqueID(uid) 29 | if !ply then return end 30 | return ply:Nick() 31 | end 32 | 33 | -- Get friends from a player 34 | function PLAYER:CPPIGetFriends() 35 | if CLIENT then return CPPI.CPPI_NOTIMPLEMENTED end -- TODO add this for client side (maybe only for local player) 36 | return self.Buddies 37 | end 38 | 39 | -- Get the owner of an entity 40 | function ENTITY:CPPIGetOwner() 41 | local ply = sh_PProtect.GetOwner(self) 42 | if !ply or !ply:IsPlayer() then return nil, nil end 43 | return ply, ply:UniqueID() 44 | end 45 | 46 | if CLIENT then return end 47 | 48 | -- Set owner of an entity 49 | function ENTITY:CPPISetOwner(ply) 50 | return sv_PProtect.SetOwner(self, ply) 51 | end 52 | 53 | -- Set owner of an entity by UID 54 | function ENTITY:CPPISetOwnerUID(uid) 55 | return self:CPPISetOwner(player.GetByUniqueID(uid) or nil) 56 | end 57 | 58 | -- Set entity to no world (true) or not even world (false) 59 | -- It is not officially documented, but some addons seem to require this. 60 | function ENTITY:CPPISetOwnerless(bool) 61 | if !IsValid(self) then return false end 62 | 63 | if bool then 64 | self:SetNWEntity('pprotect_owner', nil) 65 | self:SetNWBool('pprotect_world', true) 66 | else 67 | self:SetNWEntity('pprotect_owner', nil) 68 | end 69 | 70 | return true 71 | end 72 | 73 | -- Can tool 74 | function ENTITY:CPPICanTool(ply, tool) 75 | return sv_PProtect.CanTool(ply, self, tool) 76 | end 77 | 78 | -- Can physgun 79 | function ENTITY:CPPICanPhysgun(ply) 80 | return sv_PProtect.CanPhysgun(ply, self) 81 | end 82 | 83 | -- Can pickup 84 | function ENTITY:CPPICanPickup(ply) 85 | return sv_PProtect.CanPickup(ply, self) 86 | end 87 | 88 | -- Can punt 89 | function ENTITY:CPPICanPunt(ply) 90 | return sv_PProtect.CanGravPunt(ply, self) 91 | end 92 | 93 | -- Can use 94 | function ENTITY:CPPICanUse(ply) 95 | return sv_PProtect.CanUse(ply, self) 96 | end 97 | 98 | -- Can damage 99 | function ENTITY:CPPICanDamage(ply) 100 | return sv_PProtect.CanDamage(ply, self) 101 | end 102 | 103 | -- Can drive 104 | function ENTITY:CPPICanDrive(ply) 105 | return sv_PProtect.CanDrive(ply, self) 106 | end 107 | 108 | -- Can property 109 | function ENTITY:CPPICanProperty(ply, property) 110 | return sv_PProtect.CanProperty(ply, property, self) 111 | end 112 | 113 | -- Can edit variable 114 | function ENTITY:CPPICanEditVariable(ply, key, val, edit) 115 | return CPPI.CPPI_NOTIMPLEMENTED -- TODO 116 | end 117 | -------------------------------------------------------------------------------- /lua/patchprotect/server/cleanup.lua: -------------------------------------------------------------------------------- 1 | ------------------- 2 | -- COUNT PROPS -- 3 | ------------------- 4 | 5 | local function countProps(ply, dels) 6 | local result = { 7 | global = 0, 8 | players = {} 9 | } 10 | 11 | table.foreach(ents.GetAll(), function(key, ent) 12 | if !ent or !ent:IsValid() then return end 13 | local o = sh_PProtect.GetOwner(ent) 14 | if ent:GetNWBool('pprotect_world') or !o or isnumber(o) or !o:IsValid() then return end 15 | 16 | -- check deleted entities (which shouldn't be counted, because they shouldn't exist anymore) 17 | if istable(dels) and table.HasValue(dels, ent:EntIndex()) then return end 18 | 19 | -- Global-Count 20 | result.global = result.global + 1 21 | 22 | -- Player-Count 23 | if !result.players[o] then 24 | result.players[o] = 0 25 | end 26 | result.players[o] = result.players[o] + 1 27 | end) 28 | 29 | net.Start('pprotect_new_counts') 30 | net.WriteTable(result) 31 | net.Send(ply) 32 | end 33 | concommand.Add('pprotect_request_new_counts', countProps) 34 | 35 | --------------------- 36 | -- CLEANUP PROPS -- 37 | --------------------- 38 | 39 | -- Cleanup Map 40 | local function cleanupMap(typ, ply) 41 | -- cleanup map 42 | game.CleanUpMap() 43 | 44 | -- set world props 45 | sv_PProtect.setWorldProps() 46 | 47 | -- count props 48 | if typ then 49 | countProps(ply) 50 | end 51 | 52 | -- console exception 53 | if !ply:IsValid() then 54 | print('[PatchProtect - Cleanup] Removed all props.') 55 | return 56 | end 57 | 58 | sv_PProtect.Notify(ply, 'Cleaned Map.', 'info') 59 | print('[PatchProtect - Cleanup] ' .. ply:Nick() .. ' removed all props.') 60 | end 61 | 62 | -- Cleanup Disconnected Players Props 63 | local function cleanupDisc(ply) 64 | local del_ents = {} 65 | table.foreach(ents.GetAll(), function(key, ent) 66 | if ent.pprotect_cleanup != nil then 67 | ent:Remove() 68 | table.insert(del_ents, ent:EntIndex()) 69 | end 70 | end) 71 | 72 | sv_PProtect.Notify(ply, 'Removed all props from disconnected players.', 'info') 73 | print('[PatchProtect - Cleanup] ' .. ply:Nick() .. ' removed all props from disconnected players.') 74 | end 75 | 76 | -- Cleanup Players Props 77 | local function cleanupPly(pl, c, ply) 78 | local del_ents = {} 79 | table.foreach(ents.GetAll(), function(key, ent) 80 | if ent:GetNWEntity('pprotect_owner') == pl then 81 | ent:Remove() 82 | table.insert(del_ents, ent:EntIndex()) 83 | end 84 | end) 85 | 86 | sv_PProtect.Notify(ply, 'Cleaned ' .. pl:Nick() .. "'s props. (" .. tostring(c) .. ')', 'info') 87 | print('[PatchProtect - Cleanup] ' .. ply:Nick() .. ' removed ' .. tostring(c) .. ' props from ' .. pl:Nick() .. '.') 88 | countProps(pl, del_ents) 89 | end 90 | 91 | -- Cleanup Unowned Props 92 | local function cleanupUnowned(ply) 93 | table.foreach(ents.GetAll(), function(key, ent) 94 | if ent:IsValid() and !sh_PProtect.GetOwner(ent) and !ent:GetNWBool('pprotect_world') and string.find(ent:GetClass(), 'prop_') then 95 | ent:Remove() 96 | end 97 | end) 98 | 99 | sv_PProtect.Notify(ply, 'Removed all unowned props.', 'info') 100 | print('[PatchProtect - Cleanup] ' .. ply:Nick() .. ' removed all unowned props.') 101 | end 102 | 103 | -- General Cleanup-Function 104 | function sv_PProtect.Cleanup(typ, ply) 105 | -- check permissions 106 | if ply:IsValid() and (!sv_PProtect.Settings.Propprotection['adminscleanup'] or !ply:IsAdmin()) and !ply:IsSuperAdmin() then 107 | sv_PProtect.Notify(ply, 'You are not allowed to clean the map.') 108 | return 109 | end 110 | 111 | -- get cleanup-type 112 | local d = {} 113 | if !isstring(typ) then 114 | d = net.ReadTable() 115 | typ = d[1] 116 | end 117 | 118 | if typ == 'all' then 119 | cleanupMap(d[1], ply) 120 | return 121 | end 122 | 123 | if typ == 'disc' then 124 | cleanupDisc(ply) 125 | return 126 | end 127 | 128 | if typ == 'ply' then 129 | cleanupPly(d[2], d[3], ply) 130 | return 131 | end 132 | 133 | if typ == 'unowned' then 134 | cleanupUnowned(ply) 135 | end 136 | end 137 | net.Receive('pprotect_cleanup', sv_PProtect.Cleanup) 138 | concommand.Add('gmod_admin_cleanup', function(ply, cmd, args) 139 | sv_PProtect.Cleanup('all', ply) 140 | end) 141 | 142 | ---------------------------------------- 143 | -- CLEAR DISCONNECTED PLAYERS PROPS -- 144 | ---------------------------------------- 145 | 146 | -- PLAYER LEFT SERVER 147 | local function setCleanup(ply) 148 | if !sv_PProtect.Settings.Propprotection['enabled'] or !sv_PProtect.Settings.Propprotection['propdelete'] then return end 149 | if sv_PProtect.Settings.Propprotection['adminprops'] and (ply:IsSuperAdmin() or ply:IsAdmin()) then return end 150 | 151 | print('[PatchProtect - Cleanup] ' .. ply:Nick() .. ' left the server. Props will be deleted in ' .. tostring(sv_PProtect.Settings.Propprotection['delay']) .. ' seconds.') 152 | 153 | table.foreach(ents.GetAll(), function(k, v) 154 | if v:CPPIGetOwner() and v:CPPIGetOwner():UniqueID() == ply:UniqueID() then 155 | v.pprotect_cleanup = ply:Nick() 156 | end 157 | end) 158 | 159 | local nick = ply:Nick() 160 | timer.Create('pprotect_cleanup_' .. nick, sv_PProtect.Settings.Propprotection['delay'], 1, function() 161 | table.foreach(ents.GetAll(), function(k, v) 162 | if v.pprotect_cleanup == nick then 163 | v:Remove() 164 | end 165 | end) 166 | 167 | print('[PatchProtect - Cleanup] Removed ' .. nick .. 's Props. (Reason: Left the Server)') 168 | end) 169 | end 170 | hook.Add('PlayerDisconnected', 'pprotect_playerdisconnected', setCleanup) 171 | 172 | -- PLAYER CAME BACK 173 | local function abortCleanup(ply) 174 | if !timer.Exists('pprotect_cleanup_' .. ply:Nick()) then return end 175 | 176 | print('[PatchProtect - Cleanup] Abort Cleanup. ' .. ply:Nick() .. ' came back.') 177 | timer.Destroy('pprotect_cleanup_' .. ply:Nick()) 178 | 179 | table.foreach(ents.GetAll(), function(k, v) 180 | if v:CPPIGetOwner() and v:CPPIGetOwner():UniqueID() == ply:UniqueID() then 181 | v.pprotect_cleanup = nil 182 | v:CPPISetOwner(ply) 183 | end 184 | end) 185 | end 186 | hook.Add('PlayerSpawn', 'pprotect_abortcleanup', abortCleanup) 187 | -------------------------------------------------------------------------------- /lua/patchprotect/server/settings.lua: -------------------------------------------------------------------------------- 1 | ---------------------- 2 | -- RESET SETTINGS -- 3 | ---------------------- 4 | 5 | local function resetSettings(ply, cmd, args, auto) 6 | -- all valid tables 7 | local tabs = {'all', 'help', 'antispam', 'propprotection', 'blocked_props', 'blocked_ents', 'blocked_tools', 'antispam_tools'} 8 | 9 | -- help for reset command 10 | if args[1] == 'help' then 11 | MsgC(Color(255, 0, 0), '\n[PatchProtect-Reset]', Color(255, 255, 255), ' Use all, antispam, propprotection, blocked_props, blocked_ents, blocked_tools or antispam_tools.\n') 12 | return 13 | end 14 | 15 | -- reset all sql-tables 16 | if args[1] == 'all' then 17 | table.foreach(tabs, function(key, value) 18 | sql.Query('DROP TABLE pprotect_' .. value) 19 | end) 20 | if auto == 'auto' then return end 21 | MsgC(Color(255, 0, 0), '\n[PatchProtect-Reset]', Color(255, 255, 255), ' Successfully deleted all sql-settings.\n', Color(255, 0, 0), '[PatchProtect-Reset]', Color(255, 255, 255), ' PLEASE RESTART YOUR SERVER.\n') 22 | return 23 | end 24 | 25 | -- check argument 26 | if !table.HasValue(tabs, args[1]) then 27 | MsgC(Color(255, 0, 0), '\n[PatchProtect-Reset]', Color(255, 255, 255), ' ' .. args[1] .. ' is not a valid sql-table.\n') 28 | return 29 | end 30 | 31 | -- delete sql-table 32 | sql.Query('DROP TABLE pprotect_' .. args[1]) 33 | MsgC(Color(255, 0, 0), '\n[PatchProtect-Reset]', Color(255, 255, 255), ' Successfully deleted all ' .. args[1] .. '-settings.\n', Color(255, 0, 0), '[PatchProtect-Reset]', Color(255, 255, 255), ' PLEASE RESTART THE SERVER WHEN YOU ARE FINISHED WITH ALL RESETS.\n') 34 | end 35 | concommand.Add('pprotect_reset', resetSettings) 36 | 37 | --------------------- 38 | -- LOAD SETTINGS -- 39 | --------------------- 40 | 41 | -- ANTISPAM AND PROP PROTECTION 42 | function sv_PProtect.loadSettings(name) 43 | local sqltable = 'pprotect_' .. string.lower(name) 44 | sql.Query('CREATE TABLE IF NOT EXISTS ' .. sqltable .. ' (setting TEXT, value TEXT)') 45 | 46 | local sql_settings = {} 47 | 48 | -- Save/Load SQLSettings 49 | table.foreach(sv_PProtect.Config[name], function(setting, value) 50 | if !sql.Query('SELECT value FROM ' .. sqltable .. " WHERE setting = '" .. setting .. "'") then 51 | sql.Query('INSERT INTO ' .. sqltable .. " (setting, value) VALUES ('" .. setting .. "', '" .. tostring(value) .. "')") 52 | end 53 | 54 | sql_settings[setting] = sql.QueryValue('SELECT value FROM ' .. sqltable .. " WHERE setting = '" .. setting .. "'") 55 | end) 56 | 57 | -- Convert strings to numbers and booleans 58 | table.foreach(sql_settings, function(setting, value) 59 | if tonumber(value) != nil then 60 | sql_settings[setting] = tonumber(value) 61 | end 62 | if value == 'true' or value == 'false' then 63 | sql_settings[setting] = tobool(value) 64 | end 65 | end) 66 | 67 | return sql_settings 68 | end 69 | 70 | -- BLOCKED ENTS 71 | function sv_PProtect.loadBlockedEnts(typ) 72 | if !sql.TableExists('pprotect_blocked_' .. typ) or !sql.Query('SELECT * FROM pprotect_blocked_' .. typ) then 73 | return {} 74 | end 75 | 76 | local sql_ents = {} 77 | table.foreach(sql.Query('SELECT * FROM pprotect_blocked_' .. typ), function(id, ent) 78 | sql_ents[ent.name] = ent.model 79 | end) 80 | 81 | return sql_ents 82 | end 83 | 84 | -- ANTISPAMMED/BLOCKED TOOLS 85 | function sv_PProtect.loadBlockedTools(typ) 86 | if !sql.TableExists('pprotect_' .. typ .. '_tools') or !sql.Query('SELECT * FROM pprotect_' .. typ .. '_tools') then 87 | return {} 88 | end 89 | 90 | local sql_tools = {} 91 | table.foreach(sql.Query('SELECT * FROM pprotect_' .. typ .. '_tools'), function(ind, tool) 92 | sql_tools[tool.tool] = tobool(tool.bool) 93 | end) 94 | 95 | return sql_tools 96 | end 97 | 98 | -- LOAD SETTINGS 99 | local sql_version = '2.3' 100 | if !sql.TableExists('pprotect_version') or sql.QueryValue('SELECT * FROM pprotect_version') != sql_version then 101 | resetSettings(nil, nil, {'all'}, 'auto') 102 | sql.Query('DROP TABLE pprotect_version') 103 | sql.Query('CREATE TABLE IF NOT EXISTS pprotect_version (info TEXT)') 104 | sql.Query("INSERT INTO pprotect_version (info) VALUES ('" .. sql_version .. "')") 105 | MsgC(Color(255, 0, 0), '\n[PatchProtect-Reset]', Color(255, 255, 255), " Reset all sql-settings due a new sql-table-version, sry.\nYou don't need to resetart the server, but please check all settings. Thanks.\n") 106 | end 107 | sv_PProtect.Settings = { 108 | Antispam = sv_PProtect.loadSettings('Antispam'), 109 | Propprotection = sv_PProtect.loadSettings('Propprotection') 110 | } 111 | sv_PProtect.Blocked = { 112 | props = sv_PProtect.loadBlockedEnts('props'), 113 | ents = sv_PProtect.loadBlockedEnts('ents'), 114 | atools = sv_PProtect.loadBlockedTools('antispam'), 115 | btools = sv_PProtect.loadBlockedTools('blocked') 116 | } 117 | MsgC(Color(255, 255, 0), '\n[PatchProtect]', Color(255, 255, 255), ' Successfully loaded.\n\n') 118 | 119 | --------------------- 120 | -- SAVE SETTINGS -- 121 | --------------------- 122 | 123 | -- SAVE ANTISPAM/PROP PROTECTION 124 | net.Receive('pprotect_save', function(len, pl) 125 | if !pl:IsSuperAdmin() then return end 126 | 127 | local data = net.ReadTable() 128 | sv_PProtect.Settings[data[1]] = data[2] 129 | sv_PProtect.sendSettings() 130 | 131 | -- SAVE TO SQL TABLES 132 | table.foreach(sv_PProtect.Settings[data[1]], function(setting, value) 133 | sql.Query('UPDATE pprotect_' .. string.lower(data[1]) .. " SET value = '" .. tostring(value) .. "' WHERE setting = '" .. setting .. "'") 134 | end) 135 | 136 | sv_PProtect.Notify(pl, 'Saved new ' .. data[1] .. '-Settings', 'info') 137 | print('[PatchProtect - ' .. data[1] .. '] ' .. pl:Nick() .. ' saved new ' .. data[1] .. '-Settings.') 138 | end) 139 | 140 | -- SAVE BLOCKED PROPS/ENTS 141 | function sv_PProtect.saveBlockedEnts(typ, data) 142 | sql.Query('DROP TABLE pprotect_blocked_' .. typ) 143 | sql.Query('CREATE TABLE IF NOT EXISTS pprotect_blocked_' .. typ .. ' (name TEXT, model TEXT)') 144 | 145 | table.foreach(data, function(n, m) 146 | sql.Query('INSERT INTO pprotect_blocked_' .. typ .. " (name, model) VALUES ('" .. n .. "', '" .. m .. "')") 147 | end) 148 | end 149 | 150 | -- SAVE ANTISPAMED/BLOCKED TOOLS 151 | function sv_PProtect.saveBlockedTools(typ, data) 152 | sql.Query('DROP TABLE pprotect_' .. typ .. '_tools') 153 | sql.Query('CREATE TABLE IF NOT EXISTS pprotect_' .. typ .. '_tools (tool TEXT, bool TEXT)') 154 | 155 | table.foreach(data, function(tool, bool) 156 | sql.Query('INSERT INTO pprotect_' .. typ .. "_tools (tool, bool) VALUES ('" .. tool .. "', '" .. tostring(bool) .. "')") 157 | end) 158 | end 159 | 160 | --------------- 161 | -- NETWORK -- 162 | --------------- 163 | 164 | -- SEND SETTINGS 165 | function sv_PProtect.sendSettings(ply, cmd, args) 166 | local new_settings = {} 167 | new_settings.AntiSpam = sv_PProtect.Settings.Antispam 168 | new_settings.PropProtection = sv_PProtect.Settings.Propprotection 169 | 170 | net.Start('pprotect_new_settings') 171 | net.WriteTable(new_settings) 172 | if args and args[1] then 173 | net.WriteString(args[1]) 174 | end 175 | if ply then 176 | net.Send(ply) 177 | else 178 | net.Broadcast() 179 | end 180 | end 181 | hook.Add('PlayerInitialSpawn', 'pprotect_playersettings', sv_PProtect.sendSettings) 182 | concommand.Add('pprotect_request_new_settings', sv_PProtect.sendSettings) 183 | 184 | -- SEND NOTIFICATION 185 | function sv_PProtect.Notify(ply, text, typ) 186 | net.Start('pprotect_notify') 187 | net.WriteTable({text, typ}) 188 | if ply then 189 | net.Send(ply) 190 | else 191 | net.Broadcast() 192 | end 193 | end 194 | -------------------------------------------------------------------------------- /lua/patchprotect/client/hud.lua: -------------------------------------------------------------------------------- 1 | local Owner 2 | local IsWorld 3 | local IsShared 4 | local IsBuddy 5 | local LastID 6 | local Note = { 7 | msg = '', 8 | typ = '', 9 | time = 0, 10 | alpha = 0 11 | } 12 | local scr_w, scr_h = ScrW(), ScrH() 13 | 14 | ------------------ 15 | -- PROP OWNER -- 16 | ------------------ 17 | 18 | function cl_PProtect.showOwner() 19 | if !cl_PProtect.Settings.Propprotection['enabled'] or !cl_PProtect.Settings.CSettings['ownerhud'] or !LocalPlayer():Alive() then return end 20 | 21 | -- Check Entity 22 | local ent = LocalPlayer():GetEyeTrace().Entity 23 | if !ent or !ent:IsValid() or ent:IsWorld() or ent:IsPlayer() then return end 24 | 25 | if LastID != ent:EntIndex() or (!Owner and !IsWorld) then 26 | Owner, IsWorld, IsShared, IsBuddy = ent:GetNWEntity('pprotect_owner'), ent:GetNWBool('pprotect_world'), false, false 27 | if Owner and Owner:IsValid() and Owner != LocalPlayer() and !IsWorld then 28 | RunConsoleCommand('pprotect_send_buddies', Owner:UniqueID()) 29 | end 30 | table.foreach({'phys', 'tool', 'use', 'dmg'}, function(k, v) 31 | if ent:GetNWBool('pprotect_shared_' .. v) then 32 | IsShared = true 33 | end 34 | end) 35 | 36 | LastID = ent:EntIndex() 37 | end 38 | 39 | local txt = nil 40 | if IsWorld then 41 | txt = 'World' 42 | elseif Owner and Owner:IsValid() and Owner:IsPlayer() then 43 | txt = Owner:Nick() 44 | if !table.HasValue(player.GetAll(), Owner) then 45 | txt = txt .. ' (disconnected)' 46 | elseif IsBuddy then 47 | txt = txt .. ' (Buddy)' 48 | elseif IsShared then 49 | txt = txt .. ' (Shared)' 50 | end 51 | else 52 | txt = 'No Owner' 53 | end 54 | 55 | -- Set Variables 56 | surface.SetFont(cl_PProtect.setFont('roboto', 14, 500, true)) 57 | local w = surface.GetTextSize(txt) 58 | w = w + 10 59 | local l = scr_w - w - 20 60 | local t = scr_h * 0.5 61 | 62 | -- Set color 63 | local col 64 | if Owner == LocalPlayer() or LocalPlayer():IsAdmin() or LocalPlayer():IsSuperAdmin() or IsBuddy or IsShared or (IsWorld and cl_PProtect.Settings.Propprotection['worldpick']) or txt == 'No Owner' then 65 | col = Color(128, 255, 0, 200) 66 | elseif IsWorld and (cl_PProtect.Settings.Propprotection['worlduse'] or cl_PProtect.Settings.Propprotection['worldtool']) then 67 | col = Color(0, 161, 222, 200) 68 | else 69 | col = Color(176, 0, 0, 200) 70 | end 71 | 72 | -- Check Draw-Mode (FPP-Mode or not) 73 | if !cl_PProtect.Settings.CSettings['fppmode'] then 74 | -- Background 75 | draw.RoundedBoxEx(4, l - 5, t - 12, 5, 24, col, true, false, true, false) 76 | draw.RoundedBoxEx(4, l, t - 12, w, 24, Color(240, 240, 240, 200), false, true, false, true) 77 | -- Text 78 | draw.SimpleText(txt, cl_PProtect.setFont('roboto', 14, 500, true), l + 5, t - 6, Color(75, 75, 75)) 79 | else 80 | -- Background 81 | draw.RoundedBox(4, scr_w * 0.5 - (w * 0.5), t + 16, w, 20, Color(0, 0, 0, 150)) 82 | -- Text 83 | draw.SimpleText(txt, cl_PProtect.setFont('roboto', 14, 500, true), scr_w * 0.5, t + 20, col, TEXT_ALIGN_CENTER, 0) 84 | end 85 | end 86 | hook.Add('HUDPaint', 'pprotect_owner', cl_PProtect.showOwner) 87 | 88 | ------------------------ 89 | -- PHYSGUN BEAM FIX -- 90 | ------------------------ 91 | 92 | local function PhysBeam(ply, ent) 93 | return false 94 | end 95 | hook.Add('PhysgunPickup', 'pprotect_physbeam', PhysBeam) 96 | 97 | ---------------------------- 98 | -- ADD BLOCKED PROP/ENT -- 99 | ---------------------------- 100 | 101 | properties.Add('addblockedprop', { 102 | MenuLabel = 'Add to Blocked-List', 103 | Order = 2002, 104 | MenuIcon = 'icon16/page_white_edit.png', 105 | Filter = function(self, ent, ply) 106 | local typ = 'prop' 107 | if ent:GetClass() != 'prop_physics' then 108 | typ = 'ent' 109 | end 110 | if !cl_PProtect.Settings.Antispam['enabled'] or !cl_PProtect.Settings.Antispam[typ .. 'block'] or !LocalPlayer():IsSuperAdmin() or !ent:IsValid() or ent:IsPlayer() then 111 | return false 112 | end 113 | return true 114 | end, 115 | Action = function(self, ent) 116 | net.Start('pprotect_save_cent') 117 | if ent:GetClass() == 'prop_physics' then 118 | net.WriteTable({ 119 | typ = 'props', 120 | name = ent:GetModel(), 121 | model = ent:GetModel() 122 | }) 123 | else 124 | net.WriteTable({ 125 | typ = 'ents', 126 | name = ent:GetClass(), 127 | model = ent:GetModel() 128 | }) 129 | end 130 | net.SendToServer() 131 | end 132 | }) 133 | 134 | --------------------- 135 | -- SHARED ENTITY -- 136 | --------------------- 137 | 138 | properties.Add('shareentity', { 139 | MenuLabel = 'Share entity', 140 | Order = 2003, 141 | MenuIcon = 'icon16/group.png', 142 | Filter = function(self, ent, ply) 143 | if !ent:IsValid() or !cl_PProtect.Settings.Propprotection['enabled'] or ent:IsPlayer() then 144 | return false 145 | end 146 | if LocalPlayer():IsSuperAdmin() or Owner == LocalPlayer() then 147 | return true 148 | else 149 | return false 150 | end 151 | end, 152 | Action = function(self, ent) 153 | local shared_info = {} 154 | table.foreach({'phys', 'tool', 'use', 'dmg'}, function(k, v) 155 | shared_info[v] = ent:GetNWBool('pprotect_shared_' .. v) 156 | end) 157 | 158 | -- Frame 159 | local frm = cl_PProtect.addfrm(180, 165, 'share prop:', false) 160 | 161 | -- Checkboxes 162 | frm:addchk('Physgun', nil, shared_info['phys'], function(c) 163 | ent:SetNWBool('pprotect_shared_phys', c) 164 | end) 165 | frm:addchk('Toolgun', nil, shared_info['tool'], function(c) 166 | ent:SetNWBool('pprotect_shared_tool', c) 167 | end) 168 | frm:addchk('Use', nil, shared_info['use'], function(c) 169 | ent:SetNWBool('pprotect_shared_use', c) 170 | end) 171 | frm:addchk('Damage', nil, shared_info['dmg'], function(c) 172 | ent:SetNWBool('pprotect_shared_dmg', c) 173 | end) 174 | end 175 | }) 176 | 177 | ---------------- 178 | -- MESSAGES -- 179 | ---------------- 180 | 181 | -- DRAW NOTE 182 | local function DrawNote() 183 | -- Check Note 184 | if Note.msg == '' or Note.time + 5 < SysTime() then return end 185 | 186 | -- Animation 187 | if Note.time + 0.5 > SysTime() then 188 | Note.alpha = math.Clamp(Note.alpha + 10, 0, 255) 189 | elseif SysTime() > Note.time + 4.5 then 190 | Note.alpha = math.Clamp(Note.alpha - 10, 0, 255) 191 | end 192 | 193 | surface.SetFont(cl_PProtect.setFont('roboto', 18, 500, true)) 194 | local tw, th = surface.GetTextSize(Note.msg) 195 | local w = tw + 20 196 | local h = th + 20 197 | local x = ScrW() - w - 20 198 | local y = ScrH() - h - 20 199 | local alpha = Note.alpha 200 | local bcol = Color(88, 144, 222, alpha) 201 | 202 | -- Textbox 203 | if Note.typ == 'info' then 204 | bcol = Color(128, 255, 0, alpha) 205 | elseif Note.typ == 'admin' then 206 | bcol = Color(176, 0, 0, alpha) 207 | end 208 | draw.RoundedBox(0, x - h, y, h, h, bcol) 209 | draw.RoundedBox(0, x, y, w, h, Color(240, 240, 240, alpha)) 210 | draw.SimpleText('i', cl_PProtect.setFont('roboto', 36, 1000, true), x - 23, y + 2, Color(255, 255, 255, alpha)) 211 | 212 | local tri = {{ 213 | x = x, 214 | y = y + (h * 0.5) - 6 215 | }, { 216 | x = x + 5, 217 | y = y + (h * 0.5) 218 | }, { 219 | x = x, 220 | y = y + (h * 0.5) + 6 221 | }} 222 | surface.SetDrawColor(bcol) 223 | draw.NoTexture() 224 | surface.DrawPoly(tri) 225 | 226 | -- Text 227 | draw.SimpleText(Note.msg, cl_PProtect.setFont('roboto', 18, 500, true), x + 10, y + 10, Color(75, 75, 75, alpha)) 228 | end 229 | hook.Add('HUDPaint', 'pprotect_drawnote', DrawNote) 230 | 231 | function cl_PProtect.ClientNote(msg, typ) 232 | if !cl_PProtect.Settings.CSettings['notes'] then return end 233 | 234 | local al = 0 235 | if Note.alpha > 0 then 236 | al = 255 237 | end 238 | Note = { 239 | msg = msg, 240 | typ = typ, 241 | time = SysTime(), 242 | alpha = al 243 | } 244 | 245 | if Note.typ == 'info' then 246 | LocalPlayer():EmitSound('buttons/button9.wav', 100, 100) 247 | elseif Note.typ == 'admin' and cl_PProtect.Settings.Antispam['alert'] then 248 | LocalPlayer():EmitSound('ambient/alarms/klaxon1.wav', 100, 100) 249 | end 250 | end 251 | 252 | --------------- 253 | -- NETWORK -- 254 | --------------- 255 | 256 | -- NOTIFY 257 | net.Receive('pprotect_notify', function(len) 258 | local note = net.ReadTable() 259 | if note[2] == 'admin' and !LocalPlayer():IsAdmin() then return end 260 | 261 | cl_PProtect.ClientNote(note[1], note[2]) 262 | end) 263 | 264 | -- BUDDIES 265 | net.Receive('pprotect_send_buddies', function(len) 266 | IsBuddy = net.ReadBool() 267 | end) 268 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PatchProtect 2 | 3 | **PatchProtect** is a **fast**, **simple** and **stable prop protection** for **Garry's Mod 13**. 4 | 5 | #### Usage of PatchProtect: 6 | 7 | You can control the settings in the `Spawn-Menu -> Utilities`. There you can **change all settings** and also **what should happen, if someone is spamming**. _(Don't forget to save!)_ 8 | 9 | ## Advantages of Patchprotect: 10 | 11 | 1. **Lagfree and high performance:** Everybody hates lags, and we too, so it's very important for us, to offer a prop protection, which is fast and saves CPU-resources. 12 | 2. **Simple and functional:** We try to keep PatchProtect easy to understand. Also we are adding new features to give you more freedom to control your server. 13 | 3. **5 Panels for a good overview:** The panels are _AntiSpam, Prop Protection, Buddy, Cleanup and Client-Settings_. 14 | 4. **Modern Design:** Patchprotect is not just fast, simple and functional. It also looks pretty! We gave PatchProtect a modern design, which offers a good and nice-looking overview of all what you can do. 15 | 5. **Bug fixing:** If you found an error, you could inform us on GitHub or Workshop about it. We try to fix it (relatively to its priority) as soon as possible. _Please have in mind, that we are students, so it could happen that changes and bug fixes take some time._ 16 | 17 | ## Features and instructions of PatchProtect: 18 | 19 | > PatchProtect offers a nice range of features to gain more control of your server 20 | 21 | ### SuperAdmins and Admins in PatchProtect: 22 | 23 | We decided to have a simple rank-management in PatchProtect. 24 | 25 | - SuperAdmins 26 | - have no restrictions 27 | - are not affected by the antispam and prop protection feature 28 | - can change settings of PatchProtect 29 | - can interact with props and entities, owned by another SuperAmdin 30 | - Admins 31 | - can be freed from any antispam and prop protection feature 32 | - can change some settings as long as they are allowed to _(configured by SuperAdmins)_ 33 | - can not interact with props and entities, owned by SuperAdmins, as long as they have no special permissions to do it 34 | - every Admin can interact with props and entities, owned by another Admin 35 | 36 | ### AntiSpam: 37 | 38 | > tries to prevent players from spamming the server to death. 39 | 40 | - You can prevent players from **spamming props, tools and entities**! _(duplicator-exception is included)_ 41 | - You can set the **cooldown-time** _(how long to wait until next prop-spawn, tool-fire, entity-spawn)_ 42 | - Players will get informed about the current cooldown time on the right-bottom corner 43 | - Admins will get **informed if someone is spamming** _(if enabled, there is also a short alert sound, so the admin takes attention to it)_ 44 | - You can change the **spam-action** _(what automatically happens, if someone is spamming)_ 45 | - nothing 46 | - message 47 | - kick or ban 48 | - custom console command 49 | 50 | ### Tool Block: 51 | 52 | > prevent players to use specific tools. 53 | 54 | #### Add/Remove blocked tools: 55 | 56 | - Navigate to `Spawn-Menu -> Utilities -> PatchProtect -> AntiSpam` 57 | - Click on `Set blocked tools` 58 | - Check a tool to block it 59 | 60 | ### Tool Antispam: 61 | 62 | > apply cooldown time to specific tools. 63 | 64 | #### Add/Remove antispam-protected tools: 65 | 66 | - Navigate to `Spawn-Menu -> Utilities -> PatchProtect -> AntiSpam` 67 | - Click on `Set antispammed tools` 68 | - Check a tool to enable antispam 69 | 70 | ### Prop/Entity Block: 71 | 72 | > prevent players from spawning specific props and entities. 73 | 74 | #### Add blocked prop: 75 | 76 | - Spawn the prop/entity, which you want to block 77 | - Hold the `c` key and `right-click` on it 78 | - Click on `Add to Blocked-List` 79 | 80 | #### Remove blocked prop: 81 | 82 | - Navigate to `Spawn-Menu -> Utilities -> PatchProtect -> AntiSpam` 83 | - Click on `Set blocked props` or `Set blocked entities` 84 | - Click on the `icon of the prop/entity` 85 | - Select `Remove from Blocked-List` 86 | 87 | #### Import list of blocked props: 88 | 89 | PatchProtect allows to import a list of blocked props from a TXT file. 90 | 91 | - The TXT-file should include **one model-path in each line**, which are seperated with a **;** _(semicolon)_. _(e.g.: `models/props_c17/oildrum001_explosive.mdl;`)_ 92 | - Save the file to the servers `data` folder with the name `pprotect_import_blocked_props.txt`. 93 | - Open the server console and run `pprotect_import_blocked_props`. 94 | - Follow the instructions, which are printed to the console. 95 | 96 | PatchProtect contains a list of default props that you may want to block. It includes every model in the `rails` build category as well as many other large props. Copy the `pprotect_import_blocked_props.txt` file, which is located in this repository and follow the instructions above. 97 | 98 | ### PropProtection: 99 | 100 | > protect your stuff 101 | 102 | - You can **enable/disable many PropProtection-Features**: 103 | - PhysGun-Protection 104 | - Use-Protection 105 | - Reload-Protection _(prevent use of the 'r'-key when using the PhysGun)_ 106 | - Damage-Protection 107 | - GravGun-Protection 108 | - PropPickup-Protection _(prevent pick-up of props with 'use'-key)_ 109 | - You can prevent players from **interacting with world-objects**: 110 | - You can allow players that they can move world-objects 111 | - You can allow players that they can use world-doors and buttons 112 | - You can allow players to use tools on world props 113 | - A **HUD prints the owner** of the currently viewing prop: 114 | - You can **switch between two HUDs**: 115 | - **PatchProtect-design** _(little white box with the owner in it on the right-middle of the screen)_ 116 | - **FPP-design** _(little box with the owner in it under the crosshair)_ You can enable it in the client settings. 117 | - The color shows you, if you are allowed to interact with the viewing prop or not *(green = yes, red = no, blue = partly)* 118 | - Auto-Cleanup: 119 | - If somebody disconnects, you can enable that props are getting removed when `Use Prop-Delete` is `checked` 120 | - You can set the time, after how many seconds the props are getting cleaned-up 121 | - If you rejoin before the deadline, your props won't get deleted _(so no need to rage on client crashes)_ 122 | 123 | ### Cleanup: 124 | 125 | > keep your server clean and lagfree 126 | 127 | - Cleanup the **whole map** _(all world props will be reset)_ 128 | - Cleanup **everything from a specific player** 129 | - **Prop-Count** of the whole map and each player 130 | - Cleanup **disconnected player's props** _(see above for more details)_ 131 | 132 | ### Buddy: 133 | 134 | > you wan't to play with your friend(s) together? No problem! 135 | 136 | - You can add/remove each player to your buddies 137 | - You can set specific rights to each buddy _(pickup, tool, use, damage)_ 138 | 139 | #### Add/Remove a buddy: 140 | 141 | - Navigate to `Spawn-Menu -> Utilities -> PatchProtect -> Buddy` 142 | - Follow the instructions on the top of the menu 143 | 144 | ### Share Props/Entities: 145 | 146 | > allow all other players on the server to have special rights on a specific prop/entity 147 | 148 | - The permissions are pickup, tool, use and damage 149 | 150 | #### Add/Remove prop share-permissions: 151 | 152 | - Be sure that you are the owner of the prop/entity 153 | - Hold the `c` key and `right-click` on the prop/entity 154 | - Click on `Share entity` 155 | - Check permissions, which you want to allow to all other players on the server 156 | 157 | ### Client Settings: 158 | 159 | > we offer some settings to personalize your PatchProtect-experience 160 | 161 | - Enable/Disable the **Owner-HUD** 162 | - Enable/Disable the **FPP-Design** 163 | - Enable/Disable all **incoming notifications** 164 | 165 | ### CPPI: 166 | 167 | We implemented CPPI, so you are able to **use PatchProtect with other Addons/Pugins/Gamemodes**! 168 | 169 | ## Console Commands: 170 | 171 | > some commands, which you (hopefully) won't use that often 172 | 173 | - **Server side:** (type them into the server console) 174 | - `pprotect_reset [arg]` 175 | - This resets serverside-settings. Please follow all printed instructions, after executing this command. 176 | - For `[arg]` you can use `help, all, antispam, propprotection, blocked_props, blocked_ents, blocked_tools` or `antispam_tools` 177 | - **Client side:** (type them into the client console) 178 | - `pprotect_reset_csettings` 179 | - This resets all client settings. Please follow all printed instructions, after executing this command. 180 | - `pprotect_reset_buddies` 181 | - This resets all buddies. 182 | 183 | This is a description of most important features of PatchProtect. We recommend to go through the settings in game to make sure that everything is correctly configured. 184 | 185 | ## Contributors 186 | 187 | Thanks to @azarus, @cardermeister, @zaknesler, @ReturnEnd, @Voperak. 188 | -------------------------------------------------------------------------------- /lua/patchprotect/client/derma.lua: -------------------------------------------------------------------------------- 1 | local pan = FindMetaTable('Panel') 2 | 3 | ------------- 4 | -- FRAME -- 5 | ------------- 6 | 7 | function cl_PProtect.addfrm(w, h, title, hor) 8 | -- Frame 9 | local t = SysTime() 10 | local frm = vgui.Create('DPanel') 11 | frm:SetPos(surface.ScreenWidth() / 2 - (w / 2), surface.ScreenHeight() / 2 - (h / 2)) 12 | frm:SetSize(w, h) 13 | frm:MakePopup() 14 | 15 | function frm:Paint(w, h) 16 | Derma_DrawBackgroundBlur(self, t) 17 | draw.RoundedBox(4, 0, 0, w, h, Color(0, 0, 0, 127.5)) 18 | draw.RoundedBox(4, 1, 1, w - 2, h - 2, Color(255, 150, 30)) 19 | draw.RoundedBoxEx(4, 1, 50, w - 2, h - 51, Color(255, 255, 255), false, false, true, true) 20 | end 21 | 22 | -- Title 23 | frm.title = vgui.Create('DLabel', frm) 24 | frm.title:SetText(title) 25 | frm.title:SetPos(15, 12.5) 26 | frm.title:SetFont(cl_PProtect.setFont('roboto', 25, 750, true)) 27 | frm.title:SetColor(Color(0, 0, 0, 191.25)) 28 | frm.title:SizeToContents() 29 | 30 | -- Close-Button 31 | frm.close = vgui.Create('DButton', frm) 32 | frm.close:SetPos(w - 40, 10) 33 | frm.close:SetSize(30, 30) 34 | frm.close:SetText('') 35 | function frm.close.DoClick() 36 | frm:Remove() 37 | end 38 | 39 | function frm.close:Paint() 40 | if self.Depressed then 41 | draw.RoundedBox(4, 0, 0, 30, 30, Color(135, 50, 50)) 42 | elseif self.Hovered then 43 | draw.RoundedBox(4, 0, 0, 30, 30, Color(200, 60, 60)) 44 | else 45 | draw.RoundedBox(4, 0, 0, 30, 30, Color(200, 80, 80)) 46 | end 47 | draw.SimpleText('r', cl_PProtect.setFont('marlett', 14, 0, false, false, true), 9, 8, Color(255, 255, 255)) 48 | end 49 | 50 | frm.list = vgui.Create('DPanelList', frm) 51 | frm.list:SetPos(10, 60) 52 | frm.list:SetSize(w - 20, h - 70) 53 | frm.list:SetSpacing(5) 54 | frm.list:EnableHorizontal(hor) 55 | frm.list:EnableVerticalScrollbar(true) 56 | frm.list.VBar.btnUp:SetVisible(false) 57 | frm.list.VBar.btnDown:SetVisible(false) 58 | 59 | function frm.list.VBar:Paint() 60 | draw.RoundedBox(0, 0, 0, 20, frm.list.VBar:GetTall(), Color(255, 255, 255)) 61 | end 62 | 63 | function frm.list.VBar.btnGrip:Paint() 64 | draw.RoundedBox(0, 8, 0, 5, frm.list.VBar.btnGrip:GetTall(), Color(0, 0, 0, 150)) 65 | end 66 | 67 | return frm.list 68 | end 69 | 70 | ------------- 71 | -- LABEL -- 72 | ------------- 73 | 74 | function pan:addlbl(text, header) 75 | if header then 76 | header = 750 77 | else 78 | header = 0 79 | end 80 | local lbl = vgui.Create('DLabel') 81 | lbl:SetText(text) 82 | lbl:SetDark(true) 83 | lbl:SetFont(cl_PProtect.setFont('roboto', 14, header, true)) 84 | lbl:SizeToContents() 85 | self:AddItem(lbl) 86 | 87 | return lbl 88 | end 89 | 90 | ---------------- 91 | -- CHECKBOX -- 92 | ---------------- 93 | 94 | function pan:addchk(text, tip, check, cb) 95 | local chk = vgui.Create('DCheckBoxLabel') 96 | chk:SetText(text) 97 | chk:SetDark(true) 98 | chk:SetChecked(check) 99 | if tip then 100 | chk:SetTooltip(tip) 101 | end 102 | chk.Label:SetFont(cl_PProtect.setFont('roboto', 14, 500, true)) 103 | 104 | function chk:OnChange() 105 | cb(chk:GetChecked()) 106 | end 107 | 108 | function chk:PerformLayout() 109 | local x = self.m_iIndent or 0 110 | self:SetHeight(20) 111 | self.Button:SetSize(36, 20) 112 | self.Button:SetPos(x, 0) 113 | if self.Label then 114 | self.Label:SizeToContents() 115 | self.Label:SetPos(x + 35 + 10, self.Button:GetTall() / 2 - 7) 116 | end 117 | end 118 | 119 | local curx = 0 120 | if !chk:GetChecked() then 121 | curx = 2 122 | else 123 | curx = 18 124 | end 125 | local function smooth(goal) 126 | local speed = math.abs(goal - curx) / 3 127 | if curx > goal then 128 | curx = curx - speed 129 | elseif curx < goal then 130 | curx = curx + speed 131 | end 132 | return curx 133 | end 134 | 135 | function chk:PaintOver() 136 | draw.RoundedBox(0, 0, 0, 36, 20, Color(255, 255, 255)) 137 | if !chk:GetChecked() then 138 | draw.RoundedBox(8, 0, 0, 36, 20, Color(100, 100, 100)) 139 | draw.RoundedBox(8, smooth(2), 2, 16, 16, Color(255, 255, 255)) 140 | else 141 | draw.RoundedBox(8, 0, 0, 36, 20, Color(255, 150, 0)) 142 | draw.RoundedBox(8, smooth(18), 2, 16, 16, Color(255, 255, 255)) 143 | end 144 | end 145 | 146 | self:AddItem(chk) 147 | 148 | return chk 149 | end 150 | 151 | ---------------- 152 | -- BUTTON -- 153 | ---------------- 154 | 155 | function pan:addbtn(text, nettext, args) 156 | local btn = vgui.Create('DButton') 157 | btn:Center() 158 | btn:SetTall(25) 159 | btn:SetText(text) 160 | btn:SetDark(true) 161 | btn:SetFont(cl_PProtect.setFont('roboto', 14, 500, true)) 162 | btn:SetColor(Color(50, 50, 50)) 163 | 164 | function btn:DoClick() 165 | if btn:GetDisabled() then return end 166 | 167 | if type(args) == 'function' then 168 | args() 169 | else 170 | net.Start(nettext) 171 | if args != nil and cl_PProtect.Settings[args[1]] then 172 | net.WriteTable({args[1], cl_PProtect.Settings[args[1]]}) 173 | else 174 | net.WriteTable(args or {}) 175 | end 176 | net.SendToServer() 177 | end 178 | 179 | if nettext == 'pprotect_save' then 180 | cl_PProtect.UpdateMenus() 181 | end 182 | end 183 | 184 | function btn:Paint(w, h) 185 | if btn:GetDisabled() then 186 | draw.RoundedBox(0, 0, 0, w, h, Color(240, 240, 240)) 187 | btn:SetCursor('arrow') 188 | elseif btn.Depressed then 189 | draw.RoundedBox(0, 0, 0, w, h, Color(250, 150, 0)) 190 | elseif btn.Hovered then 191 | draw.RoundedBox(0, 0, 0, w, h, Color(220, 220, 220)) 192 | else 193 | draw.RoundedBox(0, 0, 0, w, h, Color(200, 200, 200)) 194 | end 195 | end 196 | 197 | self:AddItem(btn) 198 | 199 | return btn 200 | end 201 | 202 | -------------- 203 | -- SLIDER -- 204 | -------------- 205 | 206 | local sldnum = 0 207 | function pan:addsld(min, max, text, value, t1, t2, decimals) 208 | local sld = vgui.Create('DNumSlider') 209 | sld:SetMin(min) 210 | sld:SetMax(max) 211 | sld:SetDecimals(decimals) 212 | sld:SetText(text) 213 | sld:SetDark(true) 214 | sld:SetValue(value) 215 | sld.TextArea:SetFont(cl_PProtect.setFont('roboto', 14, 500, true)) 216 | sld.Label:SetFont(cl_PProtect.setFont('roboto', 14, 500, true)) 217 | sld.Scratch:SetVisible(false) 218 | 219 | sld.OnValueChanged = function(self, number) 220 | if sldnum != math.Round(number, decimals) then 221 | sldnum = math.Round(number, decimals) 222 | end 223 | cl_PProtect.Settings[t1][t2] = sldnum 224 | end 225 | 226 | function sld.Slider.Knob:Paint() 227 | draw.RoundedBox(6, 2, 2, 12, 12, Color(255, 150, 0)) 228 | end 229 | 230 | function sld.Slider:Paint() 231 | draw.RoundedBox(2, 8, 15, 115, 2, Color(200, 200, 200)) 232 | end 233 | 234 | self:AddItem(sld) 235 | end 236 | 237 | ---------------- 238 | -- COMBOBOX -- 239 | ---------------- 240 | 241 | function pan:addcmb(items, setting, value) 242 | local cmb = vgui.Create('DComboBox') 243 | table.foreach(items, function(key, choice) 244 | cmb:AddChoice(choice) 245 | end) 246 | cmb:SetValue(value) 247 | 248 | function cmb:OnSelect(panel, index, value, data) 249 | cl_PProtect.Settings.Antispam[setting] = index 250 | end 251 | 252 | self:AddItem(cmb) 253 | end 254 | 255 | ---------------- 256 | -- LISTVIEW -- 257 | ---------------- 258 | local pressed = {} 259 | function pan:addplp(ply, bud, cb, cb2) 260 | local plp = vgui.Create('DPanel') 261 | plp:SetHeight(40) 262 | plp:SetCursor('hand') 263 | 264 | plp.av = vgui.Create('AvatarImage', plp) 265 | plp.av:SetSize(32, 32) 266 | plp.av:SetPlayer(ply, 32) 267 | plp.av:SetPos(4, 4) 268 | 269 | plp.lbl = vgui.Create('DLabel', plp) 270 | plp.lbl:SetText(ply:Nick()) 271 | plp.lbl:SetFont(cl_PProtect.setFont('roboto', 25, 750, true)) 272 | plp.lbl:SetPos(40, 9) 273 | plp.lbl:SetColor(Color(50, 50, 50)) 274 | plp.lbl:SetWidth(230) 275 | 276 | plp.chk = vgui.Create('DCheckBox', plp) 277 | plp.chk:SetPos(270, 10) 278 | plp.chk:SetSize(20, 20) 279 | plp.chk:SetChecked(bud) 280 | 281 | function plp:OnMousePressed() 282 | cb(ply) 283 | pressed = {plp, ply} 284 | end 285 | function plp.chk:OnChange(c) 286 | cb2(c) 287 | end 288 | 289 | function plp:Paint(w, h) 290 | if pressed[1] == plp then 291 | draw.RoundedBox(4, 0, 0, w, h, Color(255, 150, 0)) 292 | else 293 | draw.RoundedBox(4, 0, 0, w, h, Color(230, 230, 230)) 294 | end 295 | end 296 | 297 | function plp.chk:Paint(w, h) 298 | if !self:GetChecked() then 299 | draw.RoundedBox(4, 0, 0, w, h, Color(255, 50, 0)) 300 | draw.SimpleText('r', cl_PProtect.setFont('marlett', 16, 750, false, false, true), 2, 3, Color(255, 255, 255)) 301 | else 302 | draw.RoundedBox(4, 0, 0, w, h, Color(0, 200, 75)) 303 | draw.SimpleText('b', cl_PProtect.setFont('marlett', 22, 500, false, false, true), 0, 0, Color(255, 255, 255)) 304 | end 305 | end 306 | 307 | self:AddItem(plp) 308 | 309 | return plp 310 | end 311 | 312 | --------------- 313 | -- TEXTBOX -- 314 | --------------- 315 | 316 | function pan:addtxt(text) 317 | local txt = vgui.Create('DTextEntry') 318 | txt:SetText(text) 319 | txt:SetFont(cl_PProtect.setFont('roboto', 14, 500, true)) 320 | self:AddItem(txt) 321 | 322 | function txt:OnTextChanged() 323 | cl_PProtect.Settings.Antispam['concommand'] = txt:GetValue() 324 | end 325 | end 326 | 327 | ------------ 328 | -- ICON -- 329 | ------------ 330 | 331 | function pan:addico(model, tip, cb) 332 | local ico = vgui.Create('SpawnIcon', self) 333 | ico:SetModel(model) 334 | if tip then 335 | ico:SetTooltip(tip) 336 | end 337 | 338 | ico.DoClick = function() 339 | cb(ico) 340 | end 341 | 342 | function ico:Paint(w, h) 343 | if !self.Hovered then 344 | draw.RoundedBox(4, 0, 0, w, h, Color(200, 200, 200)) 345 | else 346 | draw.RoundedBox(4, 0, 0, w, h, Color(255, 150, 0)) 347 | end 348 | if self.Depressed then 349 | draw.RoundedBox(4, 0, 0, w, h, Color(0, 0, 0, 63.75)) 350 | end 351 | end 352 | 353 | function ico:PaintOver() 354 | end 355 | 356 | self:AddItem(ico) 357 | 358 | return ico 359 | end 360 | -------------------------------------------------------------------------------- /lua/patchprotect/server/antispam.lua: -------------------------------------------------------------------------------- 1 | ---------------------- 2 | -- ANTISPAM SETUP -- 3 | ---------------------- 4 | 5 | -- SET PLAYER VARS 6 | function sv_PProtect.Setup(ply) 7 | -- Props 8 | ply.propcooldown = 0 9 | ply.props = 0 10 | 11 | -- Tools 12 | ply.toolcooldown = 0 13 | ply.tools = 0 14 | 15 | -- Duplicate 16 | ply.duplicate = false 17 | end 18 | hook.Add('PlayerInitialSpawn', 'pprotect_initialspawn', sv_PProtect.Setup) 19 | 20 | -- CHECK ANTISPAM ADMIN 21 | function sv_PProtect.CheckASAdmin(ply) 22 | if not IsValid(ply) then 23 | return false 24 | end 25 | if !sv_PProtect.Settings.Antispam['enabled'] or ply:IsSuperAdmin() then 26 | return true 27 | end 28 | if ply:IsAdmin() and sv_PProtect.Settings.Antispam['admins'] then 29 | return true 30 | end 31 | return false 32 | end 33 | 34 | ------------------- 35 | -- SPAM ACTION -- 36 | ------------------- 37 | 38 | function sv_PProtect.spamaction(ply) 39 | local action = sv_PProtect.Settings.Antispam['spamaction'] 40 | local name = ply:Nick() 41 | 42 | -- Cleanup 43 | if action == 'Cleanup' then 44 | cleanup.CC_Cleanup(ply, '', {}) 45 | sv_PProtect.Notify(ply, 'Cleaned all your props. (Reason: spamming)') 46 | sv_PProtect.Notify(nil, 'Cleaned ' .. name .. 's props. (Reason: spamming)', 'admin') 47 | print('[PatchProtect - AntiSpam] Cleaned ' .. name .. 's props. (Reason: spamming)') 48 | -- Kick 49 | elseif action == 'Kick' then 50 | ply:Kick('Kicked by PatchProtect. (Reason: spamming)') 51 | sv_PProtect.Notify(nil, 'Kicked ' .. name .. '. (Reason: spamming)', 'admin') 52 | print('[PatchProtect - AntiSpam] Kicked ' .. name .. '. (Reason: spamming)') 53 | -- Ban 54 | elseif action == 'Ban' then 55 | local mins = sv_PProtect.Settings.Antispam['bantime'] 56 | ply:Ban(mins, 'Banned by PatchProtect. (Reason: spamming)') 57 | sv_PProtect.Notify(nil, 'Banned ' .. name .. ' for ' .. mins .. ' minutes. (Reason: spamming)', 'admin') 58 | print('[PatchProtect - AntiSpam] Banned ' .. name .. ' for ' .. mins .. ' minutes. (Reason: spamming)') 59 | -- ConCommand 60 | elseif action == 'Command' then 61 | if sv_PProtect.Settings.Antispam['concommand'] == sv_PProtect.Config.Antispam['concommand'] then return end 62 | local rep = string.Replace(sv_PProtect.Settings.Antispam['concommand'], '', ply:SteamID()) 63 | local cmd = string.Explode(' ', rep) 64 | RunConsoleCommand(cmd[1], unpack(cmd, 2)) 65 | print("[PatchProtect - AntiSpam] Ran console command '" .. rep .. "'. (Reason: reached spam limit)") 66 | end 67 | end 68 | 69 | ----------------------- 70 | -- SPAWN ANTI SPAM -- 71 | ----------------------- 72 | 73 | function sv_PProtect.CanSpawn(ply, mdl) 74 | if sv_PProtect.CheckASAdmin(ply) then return end 75 | if ply.duplicate then return end 76 | 77 | -- Block Prop/Entity with /../ 78 | if string.find(mdl, '/../') then return false end 79 | 80 | mdl = string.lower(mdl) 81 | 82 | -- Check if Prop/Entity is blocked 83 | if (sv_PProtect.Settings.Antispam['propblock'] and sv_PProtect.Blocked.props[mdl]) or 84 | (sv_PProtect.Settings.Antispam['entblock'] and sv_PProtect.Blocked.ents[mdl]) then 85 | sv_PProtect.Notify(ply, 'This object is not allowed.') 86 | return false 87 | end 88 | 89 | -- If prop AntiSpam is not enabled, continue. 90 | if !sv_PProtect.Settings.Antispam['prop'] then 91 | return 92 | end 93 | 94 | -- Cooldown 95 | if CurTime() > ply.propcooldown then 96 | ply.props = 0 97 | ply.propcooldown = CurTime() + sv_PProtect.Settings.Antispam['cooldown'] 98 | return 99 | end 100 | 101 | ply.props = ply.props + 1 102 | sv_PProtect.Notify(ply, 'Please wait ' .. math.Round(ply.propcooldown - CurTime(), 1) .. ' seconds', 'normal') 103 | 104 | -- Spamaction 105 | if ply.props >= sv_PProtect.Settings.Antispam['spam'] then 106 | ply.props = 0 107 | sv_PProtect.spamaction(ply) 108 | sv_PProtect.Notify(nil, ply:Nick() .. ' is spamming.', 'admin') 109 | print('[PatchProtect - AntiSpam] ' .. ply:Nick() .. ' is spamming.') 110 | end 111 | 112 | return false 113 | end 114 | hook.Add('PlayerSpawnProp', 'pprotect_spawnprop', sv_PProtect.CanSpawn) 115 | hook.Add('PlayerSpawnEffect', 'pprotect_spawneffect', sv_PProtect.CanSpawn) 116 | hook.Add('PlayerSpawnSENT', 'pprotect_spawnSENT', sv_PProtect.CanSpawn) 117 | hook.Add('PlayerSpawnRagdoll', 'pprotect_spawnragdoll', sv_PProtect.CanSpawn) 118 | hook.Add('PlayerSpawnVehicle', 'pprotect_spawnvehicle', sv_PProtect.CanSpawn) 119 | hook.Add('PlayerSpawnNPC', 'pprotect_spawnNPC', sv_PProtect.CanSpawn) 120 | hook.Add('PlayerSpawnSWEP', 'pprotect_spawnSWEP', sv_PProtect.CanSpawn) 121 | 122 | ---------------------- 123 | -- TOOL ANTI SPAM -- 124 | ---------------------- 125 | 126 | -- TOOL-ANTISPAM 127 | function sv_PProtect.CanUseTool(ply, trace, tool) 128 | if sv_PProtect.CheckASAdmin(ply) then return end 129 | 130 | -- Blocked Tool 131 | if sv_PProtect.Settings.Antispam['toolblock'] and sv_PProtect.Blocked.btools[tool] then 132 | sv_PProtect.Notify(ply, 'This tool is in the blacklist.', 'normal') 133 | return false 134 | end 135 | 136 | -- Check Dupe 137 | if tool == 'duplicator' or tool == 'adv_duplicator' or tool == 'advdupe2' or tool == 'wire_adv' then 138 | ply.duplicate = true 139 | else 140 | ply.duplicate = false 141 | end 142 | 143 | -- If tool AntiSpam is not enabled or the tool is not AntiSpam protected, continue. 144 | if !sv_PProtect.Settings.Antispam['tool'] or !sv_PProtect.Blocked.atools[tool] then 145 | return 146 | end 147 | 148 | -- Cooldown 149 | if CurTime() > ply.toolcooldown then 150 | ply.tools = 0 151 | ply.toolcooldown = CurTime() + sv_PProtect.Settings.Antispam['cooldown'] 152 | return 153 | end 154 | 155 | ply.tools = ply.tools + 1 156 | sv_PProtect.Notify(ply, 'Please wait ' .. math.Round(ply.toolcooldown - CurTime(), 1) .. ' seconds', 'normal') 157 | 158 | -- Spamaction 159 | if ply.tools >= sv_PProtect.Settings.Antispam['spam'] then 160 | ply.tools = 0 161 | sv_PProtect.spamaction(ply) 162 | sv_PProtect.Notify(nil, ply:Nick() .. ' is spamming with ' .. tostring(tool) .. 's.', 'admin') 163 | print('PatchProtect - AntiSpam] ' .. ply:Nick() .. ' is spamming with ' .. tostring(tool) .. 's.') 164 | end 165 | 166 | return false 167 | end 168 | hook.Add('CanTool', 'pprotect_tool_antispam', sv_PProtect.CanUseTool) 169 | 170 | -------------------------- 171 | -- BLOCKED PROPS/ENTS -- 172 | -------------------------- 173 | 174 | -- SEND BLOCKED PROPS/ENTS TABLE 175 | net.Receive('pprotect_request_ents', function(len, pl) 176 | if !pl:IsSuperAdmin() then return end 177 | 178 | local typ = net.ReadTable()[1] 179 | 180 | net.Start('pprotect_send_ents') 181 | net.WriteString(typ) 182 | net.WriteTable(sv_PProtect.Blocked[typ]) 183 | net.Send(pl) 184 | end) 185 | 186 | -- SAVE BLOCKED PROPS/ENTS TABLE 187 | net.Receive('pprotect_save_ents', function(len, pl) 188 | if !pl:IsSuperAdmin() then return end 189 | 190 | local d = net.ReadTable() 191 | local typ, key = d[1], d[2] 192 | 193 | sv_PProtect.Blocked[typ][key] = nil 194 | sv_PProtect.saveBlockedEnts(typ, sv_PProtect.Blocked[typ]) 195 | print('[PatchProtect - AntiSpam] ' .. pl:Nick() .. ' removed ' .. key .. ' from the blocked-' .. typ .. '-list.') 196 | end) 197 | 198 | -- SAVE BLOCKED PROP/ENT FROM CPANEL 199 | net.Receive('pprotect_save_cent', function(len, pl) 200 | if !pl:IsSuperAdmin() then return end 201 | 202 | local ent = net.ReadTable() 203 | 204 | if sv_PProtect.Blocked[ent.typ][ent.name] then 205 | sv_PProtect.Notify(pl, 'This object is already in the ' .. ent.typ .. '-list.', 'info') 206 | return 207 | end 208 | 209 | sv_PProtect.Blocked[ent.typ][string.lower(ent.name)] = string.lower(ent.model) 210 | sv_PProtect.saveBlockedEnts(ent.typ, sv_PProtect.Blocked[ent.typ]) 211 | 212 | sv_PProtect.Notify(pl, 'Saved ' .. ent.name .. ' to blocked-' .. ent.typ .. '-list.', 'info') 213 | print('[PatchProtect - AntiSpam] ' .. pl:Nick() .. ' added ' .. ent.name .. ' to the blocked-' .. ent.typ .. '-list.') 214 | end) 215 | 216 | -- IMPORT BLOCKED PROPS LIST 217 | concommand.Add('pprotect_import_blocked_props', function(ply, cmd, args) 218 | if !pl:IsSuperAdmin() then return end 219 | 220 | if !file.Read('pprotect_import_blocked_props.txt', 'DATA') then 221 | print("Cannot find 'pprotect_import_blocked_props.txt' to import props. Please read the description of patchprotect.") 222 | return 223 | end 224 | 225 | local imp = string.Explode(';', file.Read('pprotect_import_blocked_props.txt', 'DATA')) 226 | table.foreach(imp, function(key, model) 227 | if model == '' then return end 228 | model = string.lower(string.sub(model, string.find(model, 'models/'), string.find(model, ';'))) 229 | if util.IsValidModel(model) and !sv_PProtect.Blocked.props[model] then 230 | sv_PProtect.Blocked.props[model] = model 231 | end 232 | end) 233 | 234 | sv_PProtect.saveBlockedEnts('props', sv_PProtect.Blocked.props) 235 | 236 | print("\n[PatchProtect] Imported all blocked props. If you experience any errors,\nthen use the command to reset the whole blocked-props-list:\n'pprotect_reset blocked_props'\n") 237 | end) 238 | 239 | -------------------------------- 240 | -- ANTISPAMED/BLOCKED TOOLS -- 241 | -------------------------------- 242 | 243 | -- SEND ANTISPAMED/BLOCKED TOOLS TABLE 244 | net.Receive('pprotect_request_tools', function(len, pl) 245 | if !pl:IsSuperAdmin() then return end 246 | 247 | local t = string.sub(net.ReadTable()[1], 1, 1) .. 'tools' 248 | local tools = {} 249 | 250 | table.foreach(weapons.GetList(), function(_, wep) 251 | if wep.ClassName != 'gmod_tool' then return end 252 | table.foreach(wep.Tool, function(name, tool) 253 | tools[name] = false 254 | end) 255 | end) 256 | 257 | table.foreach(sv_PProtect.Blocked[t], function(name, value) 258 | if value == true then 259 | tools[name] = true 260 | end 261 | end) 262 | 263 | net.Start('pprotect_send_tools') 264 | net.WriteString(t) 265 | net.WriteTable(tools) 266 | net.Send(pl) 267 | end) 268 | 269 | -- SAVE BLOCKED/ANTISPAMED TOOLS 270 | net.Receive('pprotect_save_tools', function(len, pl) 271 | if !pl:IsSuperAdmin() then return end 272 | 273 | local d = net.ReadTable() 274 | local t1, t2, k, c = d[1], d[2], d[3], d[4] 275 | 276 | sv_PProtect.Blocked[t1][k] = c 277 | sv_PProtect.saveBlockedTools(t2, sv_PProtect.Blocked[t1]) 278 | 279 | print('[PatchProtect - AntiSpam] ' .. pl:Nick() .. ' set "' .. k .. '" from ' .. t2 .. '-tools-list to "' .. tostring(c) .. '".') 280 | end) 281 | -------------------------------------------------------------------------------- /lua/patchprotect/server/propprotection.lua: -------------------------------------------------------------------------------- 1 | ---------------------- 2 | -- GENERAL CHECKS -- 3 | ---------------------- 4 | 5 | -- CHECK FOR PROP PROTECTION ADMIN CONDITIONS 6 | function sv_PProtect.CheckPPAdmin(ply) 7 | -- allow if PatchProtect is disabled or for SuperAdmins (if enabled) or for Admins (if enabled) 8 | if !sv_PProtect.Settings.Propprotection['enabled'] or (sv_PProtect.Settings.Propprotection['superadmins'] and ply:IsSuperAdmin()) or (sv_PProtect.Settings.Propprotection['admins'] and ply:IsAdmin()) then return true end 9 | 10 | return false 11 | end 12 | 13 | -- Checks if the given entity is a world prop and if players are allowed to interact with them for the given setting 14 | -- ent: valid entity to check 15 | -- sett: PatchProtect setting to use to check for world-premissions 16 | function sv_PProtect.CheckWorld(ent, sett) 17 | -- if an entity has no owner, then set that entity as a world entity 18 | if !sh_PProtect.GetOwner(ent) and !ent:GetNWBool('pprotect_world') then 19 | ent:SetNWBool('pprotect_world', true) 20 | end 21 | 22 | if sv_PProtect.Settings.Propprotection['world' .. sett] and (ent:IsWorld() or ent:GetNWBool('pprotect_world')) then return true end 23 | 24 | return false 25 | end 26 | 27 | ----------------- 28 | -- OWNERSHIP -- 29 | ----------------- 30 | 31 | -- Set the owner for an entity 32 | -- ent: valid entity which probably gets a new owner 33 | -- ply: valid player which probably gets the new owner of ent 34 | function sv_PProtect.SetOwner(ent, ply) 35 | -- Check if another addon may want to block the owner assignment 36 | if hook.Run('CPPIAssignOwnership', ply, ent, ply:UniqueID()) == false then return false end 37 | 38 | ent:SetNWEntity('pprotect_owner', ply) 39 | 40 | -- get all contrained entitis and do the same for them 41 | table.foreach(constraint.GetAllConstrainedEntities(ent), function(_, cent) 42 | -- if there is already an owner on a constrained entity set, don't overwrite it 43 | if sh_PProtect.GetOwner(cent) then return end 44 | 45 | cent:SetNWEntity('pprotect_owner', ply) 46 | end) 47 | 48 | return true 49 | end 50 | 51 | -- Set the owner for multiple entites 52 | -- ply: probably invalid player which probably gets the new owner of all ents 53 | -- typ: defines, why given entites are existing 54 | -- ents: table of valid entities which should get the new owner, or nil 55 | function sv_PProtect.SetOwnerForEnts(ply, typ, ents) 56 | if !ents or !IsValid(ply) or !ply:IsPlayer() then return end 57 | 58 | -- if this is a duplication, we need to temporarily disable the AntiSpam feature 59 | -- as soon as there is not a duplicated entity, disable the duplication exception 60 | if ply.duplicate == true and typ != 'Duplicator' and !string.find(typ, 'AdvDupe') then 61 | ply.duplicate = false 62 | end 63 | 64 | -- set the new owner for all entites 65 | table.foreach(ents, function(k, ent) 66 | sv_PProtect.SetOwner(ent, ply) 67 | 68 | -- if the entity is a duplication or the PropInProp protection is disabled or the spawner is an admin (and accepted by PatchProtect) or it is not a physics prop, then don't check for penetrating props 69 | if ply.duplicate or !sv_PProtect.Settings.Antispam['propinprop'] or sv_PProtect.CheckPPAdmin(ply) or ent:GetClass() != 'prop_physics' then return end 70 | 71 | -- PropInProp-Protection 72 | if ent:GetPhysicsObject():IsPenetrating() then 73 | sv_PProtect.Notify(ply, 'You are not allowed to spawn a prop in an other prop.') 74 | ent:Remove() 75 | end 76 | end) 77 | end 78 | 79 | -- GET DATA 80 | local en, uc, ue, up, uf = nil, undo.Create, undo.AddEntity, undo.SetPlayer, undo.Finish 81 | function undo.Create(typ) 82 | en = { 83 | t = typ, 84 | e = {}, 85 | o = nil 86 | } 87 | uc(typ) 88 | end 89 | function undo.AddEntity(ent) 90 | if IsValid(ent) and ent:GetClass() != 'phys_constraint' then 91 | table.insert(en.e, ent) 92 | end 93 | ue(ent) 94 | end 95 | function undo.SetPlayer(ply) 96 | en.o = ply 97 | up(ply) 98 | end 99 | function undo.Finish() 100 | sv_PProtect.SetOwnerForEnts(en.o, en.t, en.e) 101 | en = nil 102 | uf() 103 | end 104 | 105 | 106 | ------------------------------- 107 | -- PHYSGUN PROP PROTECTION -- 108 | ------------------------------- 109 | 110 | function sv_PProtect.CanPhysgun(ply, ent) 111 | -- Check Entity 112 | if !IsValid(ent) then return false end 113 | 114 | -- Check Admin 115 | if sv_PProtect.CheckPPAdmin(ply) then return end 116 | 117 | -- Check Entity 2 118 | if ent:IsPlayer() then return false end 119 | 120 | -- Check World 121 | if sv_PProtect.CheckWorld(ent, 'pick') then return end 122 | 123 | -- Check Shared 124 | if sh_PProtect.IsShared(ent, 'phys') then return end 125 | 126 | -- Check Owner and Buddy 127 | local owner = sh_PProtect.GetOwner(ent) 128 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'phys') then return end 129 | 130 | sv_PProtect.Notify(ply, 'You are not allowed to hold this object.') 131 | return false 132 | end 133 | hook.Add('PhysgunPickup', 'pprotect_touch', sv_PProtect.CanPhysgun) 134 | 135 | ---------------------------- 136 | -- TOOL PROP PROTECTION -- 137 | ---------------------------- 138 | 139 | function sv_PProtect.CanTool(ply, ent, tool) 140 | -- Check Entity 141 | if !IsValid(ent) then return false end 142 | 143 | -- Check Admin 144 | if sv_PProtect.CheckPPAdmin(ply) then return end 145 | 146 | -- Check Protection 147 | if tool == 'creator' and !sv_PProtect.Settings.Propprotection['creator'] then 148 | sv_PProtect.Notify(ply, 'You are not allowed to use the creator tool on this server.') 149 | return false 150 | end 151 | 152 | -- Check World 153 | if sv_PProtect.CheckWorld(ent, 'tool') then return end 154 | 155 | -- Check Shared 156 | if sh_PProtect.IsShared(ent, 'tool') then return end 157 | 158 | -- Check Owner and Buddy 159 | local owner = sh_PProtect.GetOwner(ent) 160 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'tool') then return end 161 | 162 | sv_PProtect.Notify(ply, 'You are not allowed to use ' .. tool .. ' on this object.') 163 | return false 164 | end 165 | 166 | function sv_PProtect.CanToolTrace(ply, trace, tool) 167 | return sv_PProtect.CanTool(ply, trace.Entity, tool) 168 | end 169 | hook.Add('CanTool', 'pprotect_tool', sv_PProtect.CanToolTrace) 170 | 171 | --------------------------- 172 | -- USE PROP PROTECTION -- 173 | --------------------------- 174 | 175 | function sv_PProtect.CanUse(ply, ent) 176 | -- Check Protection and GameMode 177 | if !sv_PProtect.Settings.Propprotection['use'] or engine.ActiveGamemode() == 'prop_hunt' then return end 178 | 179 | -- Check Entity 180 | if !IsValid(ent) then return false end 181 | 182 | -- Check Admin 183 | if sv_PProtect.CheckPPAdmin(ply) then return end 184 | 185 | -- Check World 186 | if sv_PProtect.CheckWorld(ent, 'use') then return end 187 | 188 | -- Check Shared 189 | if sh_PProtect.IsShared(ent, 'use') then return end 190 | 191 | -- Check Owner and Buddy 192 | local owner = sh_PProtect.GetOwner(ent) 193 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'use') then return end 194 | 195 | sv_PProtect.Notify(ply, 'You are not allowed to use this object.') 196 | return false 197 | end 198 | hook.Add('PlayerUse', 'pprotect_use', sv_PProtect.CanUse) 199 | 200 | ------------------------------ 201 | -- PROP PICKUP PROTECTION -- 202 | ------------------------------ 203 | 204 | function sv_PProtect.CanPickup(ply, ent) 205 | -- Check Protection 206 | if !sv_PProtect.Settings.Propprotection['proppickup'] then return end 207 | 208 | -- Check Entity 209 | if !IsValid(ent) then return false end 210 | 211 | -- Check Admin 212 | if sv_PProtect.CheckPPAdmin(ply) then return end 213 | 214 | -- Check World 215 | if sv_PProtect.CheckWorld(ent, 'use') then return end 216 | 217 | -- Check Shared 218 | if sh_PProtect.IsShared(ent, 'use') then return end 219 | 220 | -- Check Owner and Buddy 221 | local owner = sh_PProtect.GetOwner(ent) 222 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'use') then return end 223 | 224 | sv_PProtect.Notify(ply, 'You are not allowed to pick up this object.') 225 | return false 226 | end 227 | hook.Add('AllowPlayerPickup', 'pprotect_proppickup', sv_PProtect.CanPickup) 228 | 229 | -------------------------------- 230 | -- PROPERTY PROP PROTECTION -- 231 | -------------------------------- 232 | 233 | function sv_PProtect.CanProperty(ply, property, ent) 234 | -- Check Entity 235 | if !IsValid(ent) then return false end 236 | 237 | -- Check Admin 238 | if sv_PProtect.CheckPPAdmin(ply) then return end 239 | 240 | -- Check Persist 241 | if property == 'persist' then 242 | sv_PProtect.Notify(ply, 'You are not allowed to persist this object.') 243 | return false 244 | end 245 | 246 | -- Check World 247 | if sv_PProtect.CheckWorld(ent, 'pick') then return end 248 | 249 | -- Check Owner and Buddy 250 | local owner = sh_PProtect.GetOwner(ent) 251 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'prop') then return end 252 | 253 | sv_PProtect.Notify(ply, 'You are not allowed to change the properties on this object.') 254 | return false 255 | end 256 | hook.Add('CanProperty', 'pprotect_property', sv_PProtect.CanProperty) 257 | 258 | function sv_PProtect.CanDrive(ply, ent) 259 | -- Check Entity 260 | if !IsValid(ent) then return false end 261 | 262 | -- Check Admin 263 | if sv_PProtect.CheckPPAdmin(ply) then return end 264 | 265 | -- Check Protection 266 | if !sv_PProtect.Settings.Propprotection['propdriving'] then 267 | sv_PProtect.Notify(ply, 'Driving objects is not allowed on this server.') 268 | return false 269 | end 270 | 271 | -- Check World 272 | if sv_PProtect.CheckWorld(ent, 'pick') then return end 273 | 274 | -- Check Owner and Buddy 275 | local owner = sh_PProtect.GetOwner(ent) 276 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'prop') then return end 277 | 278 | sv_PProtect.Notify(ply, 'You are not allowed to drive this object.') 279 | return false 280 | end 281 | hook.Add('CanDrive', 'pprotect_drive', sv_PProtect.CanDrive) 282 | 283 | ------------------------------ 284 | -- DAMAGE PROP PROTECTION -- 285 | ------------------------------ 286 | 287 | function sv_PProtect.CanDamage(ply, ent) 288 | -- Check Protection 289 | if !sv_PProtect.Settings.Propprotection['damage'] then return end 290 | 291 | -- Check Entity 292 | if !IsValid(ent) or !ply:IsPlayer() then return false end 293 | 294 | -- Check Admin 295 | if sv_PProtect.CheckPPAdmin(ply) then return end 296 | 297 | -- Check Damage from Player in Vehicle 298 | if ply:InVehicle() and sv_PProtect.Settings.Propprotection['damageinvehicle'] then 299 | sv_PProtect.Notify(ply, 'You are not allowed to damage other players while sitting in a vehicle.') 300 | return true 301 | end 302 | 303 | -- Check World 304 | if sv_PProtect.CheckWorld(ent, 'pick') then return end 305 | 306 | -- Check Shared 307 | if sh_PProtect.IsShared(ent, 'dmg') then return end 308 | 309 | -- Check Owner and Buddy 310 | local owner = sh_PProtect.GetOwner(ent) 311 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'dmg') or ent:IsPlayer() then return end 312 | 313 | sv_PProtect.Notify(ply, 'You are not allowed to damage this object.') 314 | return true 315 | end 316 | 317 | function sv_PProtect.TakeDamage(ent, info) 318 | return sv_PProtect.CanDamage(info:GetAttacker():CPPIGetOwner() or info:GetAttacker(), ent) 319 | end 320 | hook.Add('EntityTakeDamage', 'pprotect_damage', sv_PProtect.TakeDamage) 321 | 322 | --------------------------------- 323 | -- PHYSGUN-RELOAD PROTECTION -- 324 | --------------------------------- 325 | 326 | function sv_PProtect.CanPhysReload(ply, ent) 327 | -- Check Protection 328 | if !sv_PProtect.Settings.Propprotection['reload'] then return end 329 | 330 | -- Check Entity 331 | if !IsValid(ent) then return false end 332 | 333 | -- Check Admin 334 | if sv_PProtect.CheckPPAdmin(ply) then return end 335 | 336 | -- Check World 337 | if sv_PProtect.CheckWorld(ent, 'pick') then return end 338 | 339 | -- Check Owner and Buddy 340 | local owner = sh_PProtect.GetOwner(ent) 341 | if ply == owner or sv_PProtect.IsBuddy(owner, ply, 'phys') then return end 342 | 343 | sv_PProtect.Notify(ply, 'You are not allowed to unfreeze this object.') 344 | return false 345 | end 346 | 347 | function sv_PProtect.PrepareCanPhysReload(weapon, ply) 348 | return sv_PProtect.CanPhysReload(ply, ply:GetEyeTrace().Entity) 349 | end 350 | hook.Add('OnPhysgunReload', 'pprotect_physreload', sv_PProtect.PreparePhysReload) 351 | 352 | ------------------------------- 353 | -- GRAVGUN PUNT PROTECTION -- 354 | ------------------------------- 355 | 356 | function sv_PProtect.CanGravPunt(ply, ent) 357 | -- Check Protection 358 | if !sv_PProtect.Settings.Propprotection['gravgun'] then return end 359 | 360 | -- Check Entity 361 | if !IsValid(ent) then return false end 362 | 363 | -- Check Admin 364 | if sv_PProtect.CheckPPAdmin(ply) then return end 365 | 366 | -- Check World 367 | if sv_PProtect.CheckWorld(ent, 'pick') then return end 368 | -- I assume people don't want to allow both grabing and throwing props using gravity gun 369 | 370 | -- Check Owner 371 | if ply == sh_PProtect.GetOwner(ent) then return end 372 | 373 | sv_PProtect.Notify(ply, 'You are not allowed to punt this object.') 374 | return false 375 | end 376 | hook.Add('GravGunPunt', 'pprotect_gravpunt', sv_PProtect.CanGravPunt) 377 | 378 | function sv_PProtect.CanGravPickup(ply, ent) 379 | -- Check Protection 380 | if !sv_PProtect.Settings.Propprotection['gravgun'] then return end 381 | 382 | -- Check Entity 383 | if !IsValid(ent) then return false end 384 | 385 | -- Check Admin 386 | if sv_PProtect.CheckPPAdmin(ply) then return end 387 | 388 | -- Check World 389 | if sv_PProtect.CheckWorld(ent, 'grav') then return end 390 | 391 | -- Check Owner 392 | if ply == sh_PProtect.GetOwner(ent) then return end 393 | 394 | sv_PProtect.Notify(ply, 'You are not allowed to use the Grav-Gun on this object.') 395 | ply:DropObject() 396 | return false 397 | end 398 | hook.Add('GravGunOnPickedUp', 'pprotect_gravpickup', sv_PProtect.CanGravPickup) 399 | 400 | ----------------------- 401 | -- SET WORLD PROPS -- 402 | ----------------------- 403 | 404 | function sv_PProtect.setWorldProps() 405 | table.foreach(ents:GetAll(), function(id, ent) 406 | if string.find(ent:GetClass(), 'func_') or string.find(ent:GetClass(), 'prop_') then 407 | ent:SetNWBool('pprotect_world', true) 408 | end 409 | end) 410 | end 411 | hook.Add('PersistenceLoad', 'pprotect_worldprops', sv_PProtect.setWorldProps) 412 | -------------------------------------------------------------------------------- /lua/patchprotect/client/panel.lua: -------------------------------------------------------------------------------- 1 | --------------------- 2 | -- ANTISPAM MENU -- 3 | --------------------- 4 | 5 | function cl_PProtect.as_menu(p) 6 | -- clear Panel 7 | p:ClearControls() 8 | 9 | -- Main Settings 10 | p:addlbl('General Settings:', true) 11 | p:addchk('Enable AntiSpam', nil, cl_PProtect.Settings.Antispam['enabled'], function(c) 12 | cl_PProtect.Settings.Antispam['enabled'] = c 13 | end) 14 | 15 | if cl_PProtect.Settings.Antispam['enabled'] then 16 | -- General 17 | p:addchk('Ignore Admins', nil, cl_PProtect.Settings.Antispam['admins'], function(c) 18 | cl_PProtect.Settings.Antispam['admins'] = c 19 | end) 20 | p:addchk('Admin-Alert Sound', nil, cl_PProtect.Settings.Antispam['alert'], function(c) 21 | cl_PProtect.Settings.Antispam['alert'] = c 22 | end) 23 | 24 | -- Anti-Spam features 25 | p:addlbl('\nEnable/Disable antispam features:', true) 26 | p:addchk('Prop-AntiSpam', nil, cl_PProtect.Settings.Antispam['prop'], function(c) 27 | cl_PProtect.Settings.Antispam['prop'] = c 28 | end) 29 | p:addchk('Tool-AntiSpam', nil, cl_PProtect.Settings.Antispam['tool'], function(c) 30 | cl_PProtect.Settings.Antispam['tool'] = c 31 | end) 32 | p:addchk('Tool-Block', nil, cl_PProtect.Settings.Antispam['toolblock'], function(c) 33 | cl_PProtect.Settings.Antispam['toolblock'] = c 34 | end) 35 | p:addchk('Prop-Block', nil, cl_PProtect.Settings.Antispam['propblock'], function(c) 36 | cl_PProtect.Settings.Antispam['propblock'] = c 37 | end) 38 | p:addchk('Entity-Block', nil, cl_PProtect.Settings.Antispam['entblock'], function(c) 39 | cl_PProtect.Settings.Antispam['entblock'] = c 40 | end) 41 | p:addchk('Prop-In-Prop', nil, cl_PProtect.Settings.Antispam['propinprop'], function(c) 42 | cl_PProtect.Settings.Antispam['propinprop'] = c 43 | end) 44 | 45 | -- Tool Protection 46 | if cl_PProtect.Settings.Antispam['tool'] then 47 | p:addbtn('Set antispamed Tools', 'pprotect_request_tools', {'antispam'}) 48 | end 49 | 50 | -- Tool Block 51 | if cl_PProtect.Settings.Antispam['toolblock'] then 52 | p:addbtn('Set blocked Tools', 'pprotect_request_tools', {'blocked'}) 53 | end 54 | 55 | -- Prop Block 56 | if cl_PProtect.Settings.Antispam['propblock'] then 57 | p:addbtn('Set blocked Props', 'pprotect_request_ents', {'props'}) 58 | end 59 | 60 | -- Ent Block 61 | if cl_PProtect.Settings.Antispam['entblock'] then 62 | p:addbtn('Set blocked Entities', 'pprotect_request_ents', {'ents'}) 63 | end 64 | 65 | -- Cooldown 66 | p:addlbl('\nDuration till the next prop-spawn/tool-fire:', true) 67 | p:addsld(0, 10, 'Cooldown (Seconds)', cl_PProtect.Settings.Antispam['cooldown'], 'Antispam', 'cooldown', 1) 68 | p:addlbl('Number of props till admins get warned:') 69 | p:addsld(0, 40, 'Amount', cl_PProtect.Settings.Antispam['spam'], 'Antispam', 'spam', 0) 70 | p:addlbl('Automatic action after spamming:') 71 | p:addcmb({'Nothing', 'Cleanup', 'Kick', 'Ban', 'Command'}, 'spamaction', cl_PProtect.Settings.Antispam['spamaction']) 72 | 73 | -- Spamaction 74 | if cl_PProtect.Settings.Antispam['spamaction'] == 'Ban' then 75 | p:addsld(0, 60, 'Ban (Minutes)', cl_PProtect.Settings.Antispam['bantime'], 'Antispam', 'bantime', 0) 76 | elseif cl_PProtect.Settings.Antispam['spamaction'] == 'Command' then 77 | p:addlbl("Use '' to use the spamming player.") 78 | p:addlbl("Some commands need sv_cheats 1 to run,\nlike 'kill '") 79 | p:addtxt(cl_PProtect.Settings.Antispam['concommand']) 80 | end 81 | end 82 | 83 | -- save Settings 84 | p:addbtn('Save Settings', 'pprotect_save', {'Antispam'}) 85 | end 86 | 87 | -------------- 88 | -- FRAMES -- 89 | -------------- 90 | 91 | cl_PProtect.Blocked = { 92 | props = {}, 93 | ents = {}, 94 | atools = {}, 95 | btools = {} 96 | } 97 | 98 | -- ANTISPAMED/BLOCKED TOOLS 99 | net.Receive('pprotect_send_tools', function() 100 | local t = net.ReadString() 101 | local typ = 'antispam' 102 | if t == 'btools' then 103 | typ = 'blocked' 104 | end 105 | cl_PProtect.Blocked[t] = net.ReadTable() 106 | local frm = cl_PProtect.addfrm(250, 350, typ .. ' tools:', false) 107 | 108 | for key, value in SortedPairs(cl_PProtect.Blocked[t]) do 109 | frm:addchk(key, nil, cl_PProtect.Blocked[t][key], function(c) 110 | net.Start('pprotect_save_tools') 111 | net.WriteTable({t, typ, key, c}) 112 | net.SendToServer() 113 | cl_PProtect.Blocked[t][key] = c 114 | end) 115 | end 116 | end) 117 | 118 | -- BLOCKED PROPS/ENTS 119 | net.Receive('pprotect_send_ents', function() 120 | local typ = net.ReadString() 121 | cl_PProtect.Blocked[typ] = net.ReadTable() 122 | local frm = cl_PProtect.addfrm(800, 600, 'blocked ' .. typ .. ':', true, 'Save ' .. typ, {typ, cl_PProtect.Blocked[typ]}, 'pprotect_save_ents') 123 | 124 | table.foreach(cl_PProtect.Blocked[typ], function(name, model) 125 | frm:addico(model, name, function(icon) 126 | local menu = DermaMenu() 127 | menu:AddOption('Remove from Blocked-List', function() 128 | net.Start('pprotect_save_ents') 129 | net.WriteTable({typ, name}) 130 | net.SendToServer() 131 | icon:Remove() 132 | end) 133 | menu:Open() 134 | end) 135 | end) 136 | end) 137 | 138 | --------------------------- 139 | -- PROPPROTECTION MENU -- 140 | --------------------------- 141 | 142 | function cl_PProtect.pp_menu(p) 143 | -- clear Panel 144 | p:ClearControls() 145 | 146 | -- Main Settings 147 | p:addlbl('General Settings:', true) 148 | p:addchk('Enable PropProtection', nil, cl_PProtect.Settings.Propprotection['enabled'], function(c) 149 | cl_PProtect.Settings.Propprotection['enabled'] = c 150 | end) 151 | 152 | if cl_PProtect.Settings.Propprotection['enabled'] then 153 | -- General 154 | p:addchk('Ignore SuperAdmins', nil, cl_PProtect.Settings.Propprotection['superadmins'], function(c) 155 | cl_PProtect.Settings.Propprotection['superadmins'] = c 156 | end) 157 | p:addchk('Ignore Admins', nil, cl_PProtect.Settings.Propprotection['admins'], function(c) 158 | cl_PProtect.Settings.Propprotection['admins'] = c 159 | end) 160 | p:addchk('Admins can use Cleanup-Menu', nil, cl_PProtect.Settings.Propprotection['adminscleanup'], function(c) 161 | cl_PProtect.Settings.Propprotection['adminscleanup'] = c 162 | end) 163 | 164 | -- Protections 165 | p:addlbl('\nProtection Settings:', true) 166 | p:addchk('Use-Protection', nil, cl_PProtect.Settings.Propprotection['use'], function(c) 167 | cl_PProtect.Settings.Propprotection['use'] = c 168 | end) 169 | p:addchk('Reload-Protection', nil, cl_PProtect.Settings.Propprotection['reload'], function(c) 170 | cl_PProtect.Settings.Propprotection['reload'] = c 171 | end) 172 | p:addchk('Damage-Protection', nil, cl_PProtect.Settings.Propprotection['damage'], function(c) 173 | cl_PProtect.Settings.Propprotection['damage'] = c 174 | end) 175 | p:addchk('GravGun-Protection', nil, cl_PProtect.Settings.Propprotection['gravgun'], function(c) 176 | cl_PProtect.Settings.Propprotection['gravgun'] = c 177 | end) 178 | p:addchk('PropPickup-Protection', "Pick up props with 'use'-key", cl_PProtect.Settings.Propprotection['proppickup'], function(c) 179 | cl_PProtect.Settings.Propprotection['proppickup'] = c 180 | end) 181 | 182 | -- Special damage protection 183 | if cl_PProtect.Settings.Propprotection['damage'] then 184 | p:addchk('In-Vehicle-Damage-Protection', 'Restrict players to kill other players, while sitting in a vehicle', cl_PProtect.Settings.Propprotection['damageinvehicle'], function(c) 185 | cl_PProtect.Settings.Propprotection['damageinvehicle'] = c 186 | end) 187 | end 188 | 189 | -- Restrictions 190 | p:addlbl('\nSpecial User-Restrictions:', true) 191 | p:addchk('Allow Creator-Tool', 'ie. spawning weapons with the toolgun', cl_PProtect.Settings.Propprotection['creator'], function(c) 192 | cl_PProtect.Settings.Propprotection['creator'] = c 193 | end) 194 | p:addchk('Allow Prop-Driving', 'Allow users to drive props over the context menu (c-key)', cl_PProtect.Settings.Propprotection['propdriving'], function(c) 195 | cl_PProtect.Settings.Propprotection['propdriving'] = c 196 | end) 197 | p:addchk('Allow World-Pickup', 'Allow users to pickup world props', cl_PProtect.Settings.Propprotection['worldpick'], function(c) 198 | cl_PProtect.Settings.Propprotection['worldpick'] = c 199 | end) 200 | p:addchk('Allow World-GravPick', 'Allow users to pickup world props using gravity gun', cl_PProtect.Settings.Propprotection['worldgrav'], function(c) 201 | cl_PProtect.Settings.Propprotection['worldgrav'] = c 202 | end) 203 | p:addchk('Allow World-Use', 'Allow users to use World-Buttons/Doors', cl_PProtect.Settings.Propprotection['worlduse'], function(c) 204 | cl_PProtect.Settings.Propprotection['worlduse'] = c 205 | end) 206 | p:addchk('Allow World-Tooling', 'Allow users to use tools on World-Objects', cl_PProtect.Settings.Propprotection['worldtool'], function(c) 207 | cl_PProtect.Settings.Propprotection['worldtool'] = c 208 | end) 209 | 210 | p:addlbl('\nProp-Delete on Disconnect:', true) 211 | p:addchk('Use Prop-Delete', nil, cl_PProtect.Settings.Propprotection['propdelete'], function(c) 212 | cl_PProtect.Settings.Propprotection['propdelete'] = c 213 | end) 214 | 215 | -- Prop-Delete 216 | if cl_PProtect.Settings.Propprotection['propdelete'] then 217 | p:addchk("Keep admin's props", nil, cl_PProtect.Settings.Propprotection['adminsprops'], function(c) 218 | cl_PProtect.Settings.Propprotection['adminsprops'] = c 219 | end) 220 | p:addsld(5, 300, 'Delay (sec.)', cl_PProtect.Settings.Propprotection['delay'], 'Propprotection', 'delay', 0) 221 | end 222 | end 223 | 224 | -- save Settings 225 | p:addbtn('Save Settings', 'pprotect_save', {'Propprotection'}) 226 | end 227 | 228 | ------------------ 229 | -- BUDDY MENU -- 230 | ------------------ 231 | 232 | local txt, perms, sply = 233 | '', 234 | { 235 | phys = false, 236 | tool = false, 237 | use = false, 238 | prop = false, 239 | dmg = false 240 | }, 241 | nil 242 | local function edit_perm(ply, data) 243 | txt:SetText('Permissions (' .. ply:Nick() .. '):') 244 | txt:SetVisible(true) 245 | 246 | table.foreach(data, function(key, perm) 247 | perms[key]:SetChecked(perm) 248 | perms[key]:SetVisible(true) 249 | end) 250 | end 251 | 252 | function cl_PProtect.b_menu(p) 253 | -- clear Panel 254 | p:ClearControls() 255 | 256 | -- add buddies 257 | p:addlbl('Buddies:', true) 258 | p:addlbl('Click on name -> change permissions.', false) 259 | p:addlbl('Change right box -> add/remove buddy.', false) 260 | 261 | table.foreach(player.GetAll(), function(key, ply) 262 | if ply == LocalPlayer() then return end 263 | local chk = false 264 | local id = ply:SteamID() 265 | if istable(cl_PProtect.Buddies[id]) then 266 | chk = cl_PProtect.Buddies[id].bud 267 | end 268 | 269 | p:addplp( 270 | ply, 271 | chk, 272 | function() 273 | sply = ply 274 | local ps = { 275 | phys = false, 276 | tool = false, 277 | use = false, 278 | prop = false, 279 | dmg = false 280 | } 281 | if cl_PProtect.Buddies[id] then 282 | ps = cl_PProtect.Buddies[id].perm 283 | end 284 | edit_perm(ply, ps) 285 | end, 286 | function(c) 287 | cl_PProtect.setBuddy(ply, c) 288 | end 289 | ) 290 | end) 291 | 292 | -- add permissions 293 | txt = p:addlbl('THIS IS JUST A PLACEHOLDER TO KEEP THE LABEL LONG', true) 294 | perms.phys = p:addchk('Physgun', nil, false, function(c) 295 | cl_PProtect.setBuddyPerm(sply, 'phys', c) 296 | end) 297 | perms.tool = p:addchk('Tool', nil, false, function(c) 298 | cl_PProtect.setBuddyPerm(sply, 'tool', c) 299 | end) 300 | perms.use = p:addchk('Use', nil, false, function(c) 301 | cl_PProtect.setBuddyPerm(sply, 'use', c) 302 | end) 303 | perms.prop = p:addchk('Property', nil, false, function(c) 304 | cl_PProtect.setBuddyPerm(sply, 'prop', c) 305 | end) 306 | perms.dmg = p:addchk('Damage', nil, false, function(c) 307 | cl_PProtect.setBuddyPerm(sply, 'dmg', c) 308 | end) 309 | 310 | txt:SetVisible(false) 311 | perms.phys:SetVisible(false) 312 | perms.tool:SetVisible(false) 313 | perms.use:SetVisible(false) 314 | perms.prop:SetVisible(false) 315 | perms.dmg:SetVisible(false) 316 | end 317 | 318 | -------------------- 319 | -- CLEANUP MENU -- 320 | -------------------- 321 | 322 | local o_global, o_players = 0, {} 323 | function cl_PProtect.cu_menu(p) 324 | -- clear Panel 325 | p:ClearControls() 326 | 327 | p:addlbl('Cleanup everything:', true) 328 | p:addbtn('Cleanup everything (' .. tostring(o_global) .. ' Props)', 'pprotect_cleanup', {'all'}) 329 | 330 | p:addlbl('\nCleanup props from disconnected players:', true) 331 | p:addbtn('Cleanup all props from disc. players', 'pprotect_cleanup', {'disc'}) 332 | 333 | p:addlbl('\nCleanup unowned props:', true) 334 | p:addbtn('Cleanup all unowned props', 'pprotect_cleanup', {'unowned'}) 335 | 336 | if o_global == 0 then return end 337 | p:addlbl("\nCleanup player's props:", true) 338 | table.foreach(o_players, function(pl, c) 339 | p:addbtn('Cleanup ' .. pl:Nick() .. ' (' .. tostring(c) .. ' props)', 'pprotect_cleanup', {'ply', pl, tostring(c)}) 340 | end) 341 | end 342 | 343 | ---------------------------- 344 | -- CLIENT SETTINGS MENU -- 345 | ---------------------------- 346 | 347 | function cl_PProtect.cs_menu(p) 348 | -- clear Panel 349 | p:ClearControls() 350 | 351 | p:addlbl('Enable/Disable features:', true) 352 | p:addchk('Use Owner-HUD', 'Allows you to see the owner of a prop.', cl_PProtect.Settings.CSettings['ownerhud'], function(c) 353 | cl_PProtect.update_csetting('ownerhud', c) 354 | end) 355 | p:addchk('FPP-Mode (Owner HUD)', 'Owner will be shown under the crosshair', cl_PProtect.Settings.CSettings['fppmode'], function(c) 356 | cl_PProtect.update_csetting('fppmode', c) 357 | end) 358 | p:addchk('Use Notifications', 'Allows you to see incoming notifications. (right-bottom).', cl_PProtect.Settings.CSettings['notes'], function(c) 359 | cl_PProtect.update_csetting('notes', c) 360 | end) 361 | end 362 | 363 | -------------------- 364 | -- CREATE MENUS -- 365 | -------------------- 366 | 367 | local function CreateMenus() 368 | -- Anti-Spam 369 | spawnmenu.AddToolMenuOption('Utilities', 'PatchProtect', 'PPAntiSpam', 'AntiSpam', '', '', function(p) 370 | cl_PProtect.UpdateMenus('as', p) 371 | end) 372 | 373 | -- Prop-Protection 374 | spawnmenu.AddToolMenuOption('Utilities', 'PatchProtect', 'PPPropProtection', 'PropProtection', '', '', function(p) 375 | cl_PProtect.UpdateMenus('pp', p) 376 | end) 377 | 378 | -- Buddy 379 | spawnmenu.AddToolMenuOption('Utilities', 'PatchProtect', 'PPBuddy', 'Buddy', '', '', function(p) 380 | cl_PProtect.UpdateMenus('b', p) 381 | end) 382 | 383 | -- Cleanup 384 | spawnmenu.AddToolMenuOption('Utilities', 'PatchProtect', 'PPCleanup', 'Cleanup', '', '', function(p) 385 | cl_PProtect.UpdateMenus('cu', p) 386 | end) 387 | 388 | -- Client-Settings 389 | spawnmenu.AddToolMenuOption('Utilities', 'PatchProtect', 'PPClientSettings', 'Client Settings', '', '', function(p) 390 | cl_PProtect.UpdateMenus('cs', p) 391 | end) 392 | end 393 | hook.Add('PopulateToolMenu', 'pprotect_make_menus', CreateMenus) 394 | 395 | -------------------- 396 | -- UPDATE MENUS -- 397 | -------------------- 398 | 399 | local function showErrorMessage(p, msg) 400 | p:ClearControls() 401 | p:addlbl(msg) 402 | end 403 | 404 | local pans = {} 405 | function cl_PProtect.UpdateMenus(p_type, panel) 406 | -- add Panel 407 | if p_type and !pans[p_type] then 408 | pans[p_type] = panel 409 | end 410 | 411 | -- load Panel 412 | table.foreach(pans, function(t, p) 413 | if t == 'as' or t == 'pp' then 414 | if LocalPlayer():IsSuperAdmin() then 415 | RunConsoleCommand('pprotect_request_new_settings', t) 416 | else 417 | showErrorMessage(pans[t], 'Sorry, you need to be a SuperAdmin to change\nthe settings.') 418 | end 419 | elseif t == 'cu' then 420 | if LocalPlayer():IsSuperAdmin() or (LocalPlayer():IsAdmin() and cl_PProtect.Settings.Propprotection['adminscleanup']) then 421 | RunConsoleCommand('pprotect_request_new_counts') 422 | else 423 | showErrorMessage(pans[t], 'Sorry, you need to be a Admin/SuperAdmin to\nchange the settings.') 424 | end 425 | else 426 | cl_PProtect[t .. '_menu'](pans[t]) 427 | end 428 | end) 429 | end 430 | hook.Add('SpawnMenuOpen', 'pprotect_update_menus', cl_PProtect.UpdateMenus) 431 | 432 | --------------- 433 | -- NETWORK -- 434 | --------------- 435 | 436 | -- RECEIVE NEW SETTINGS 437 | net.Receive('pprotect_new_settings', function() 438 | local settings = net.ReadTable() 439 | local typ = net.ReadString() 440 | 441 | cl_PProtect.Settings.Antispam = settings.AntiSpam 442 | cl_PProtect.Settings.Propprotection = settings.PropProtection 443 | 444 | if typ != 'as' and typ != 'pp' then return end 445 | cl_PProtect[typ .. '_menu'](pans[typ]) 446 | end) 447 | 448 | -- RECEIVE NEW PROP-COUNTS 449 | net.Receive('pprotect_new_counts', function() 450 | local counts = net.ReadTable() 451 | 452 | -- set new Count-Data 453 | o_global = counts.global 454 | o_players = counts.players 455 | 456 | -- create new Cleanup-Panel 457 | cl_PProtect.cu_menu(pans.cu) 458 | end) 459 | -------------------------------------------------------------------------------- /pprotect_import_blocked_props.txt: -------------------------------------------------------------------------------- 1 | models/props/de_nuke/ibeams_ctspawnc.mdl; 2 | models/props_vehicles/generatortrailer01.mdl; 3 | models/props_canal/boat001a.mdl; 4 | models/props_phx/gears/rack36.mdl; 5 | models/props/cs_militia/fence_ranch.mdl; 6 | models/props_wasteland/medbridge_strut01.mdl; 7 | models/props/cs_militia/housefence.mdl; 8 | models/props_canal/bridge_pillar02.mdl; 9 | models/props/cs_assault/moneypallet02.mdl; 10 | models/xqm/coastertrack/special_helix_middle_full_3.mdl; 11 | models/xqm/rails/trackball_1.mdl; 12 | models/props/cs_office/awning_short.mdl; 13 | models/props_phx/trains/tracks/track_4x.mdl; 14 | models/props/cs_office/awning_long.mdl; 15 | models/xqm/coastertrack/bank_turn_45_2.mdl; 16 | models/props_phx/mechanics/mech1.mdl; 17 | models/xqm/coastertrack/train_1.mdl; 18 | models/props/cs_assault/moneypallete.mdl; 19 | models/airboat.mdl; 20 | models/buggy.mdl; 21 | models/props/cs_militia/coveredbridge01_top.mdl; 22 | models/props/de_nuke/pipesb_bombsite.mdl; 23 | models/hunter/blocks/cube6x8x1.mdl; 24 | models/props_canal/canal_bridge04.mdl; 25 | models/props/cs_militia/coveredbridge01_left.mdl; 26 | models/props/de_nuke/fuel_cask.mdl; 27 | models/xqm/coastertrack/special_sturn_right_3.mdl; 28 | models/props/de_nuke/car_nuke.mdl; 29 | models/props_c17/utilitypole03a.mdl; 30 | models/props/de_nuke/pipeset.mdl; 31 | models/xqm/coastertrack/bank_start_left_4.mdl; 32 | models/combine_apc.mdl; 33 | models/xqm/coastertrack/turn_slope_down_45_2.mdl; 34 | models/props_wasteland/wheel02b.mdl; 35 | models/xqm/coastertrack/slope_225_1.mdl; 36 | models/props_vehicles/car002a_physics.mdl; 37 | models/props_phx/mechanics/slider1.mdl; 38 | models/props_vehicles/car001b_hatchback.mdl; 39 | models/cranes/crane_frame.mdl; 40 | models/props_trainstation/pole_448connection002b.mdl; 41 | models/xqm/coastertrack/bank_turn_180_3.mdl; 42 | models/xqm/coastertrack/special_full_loop_3.mdl; 43 | models/xqm/coastertrack/turn_180_1.mdl; 44 | models/props_combine/combine_fence01b.mdl; 45 | models/xqm/coastertrack/special_full_corkscrew_right_3.mdl; 46 | models/props_combine/combine_fence01a.mdl; 47 | models/xqm/coastertrack/turn_slope_180_2.mdl; 48 | models/props_docks/dock02_pole02a.mdl; 49 | models/props_docks/dock03_pole01a.mdl; 50 | models/props_c17/utilitypole01a.mdl; 51 | models/props_c17/utilitypole01b.mdl; 52 | models/props_c17/utilitypole01d.mdl; 53 | models/props_c17/utilitypole02b.mdl; 54 | models/xqm/coastertrack/turn_slope_down_45_3.mdl; 55 | models/props_canal/canal_bridge01.mdl; 56 | models/props_canal/canal_bridge02.mdl; 57 | models/props_canal/canal_bridge03a.mdl; 58 | models/hunter/tubes/tube4x4x5.mdl; 59 | models/combine_helicopter.mdl; 60 | models/props_canal/boat002b.mdl; 61 | models/props_canal/boat001b.mdl; 62 | models/props_trainstation/train001.mdl; 63 | models/props_trainstation/train002.mdl; 64 | models/props_trainstation/train005.mdl; 65 | models/props_combine/combine_train02b.mdl; 66 | models/xqm/rails/tunnel_1.mdl; 67 | models/xqm/coastertrack/bank_start_left_3.mdl; 68 | models/props_vehicles/car002b_physics.mdl; 69 | models/xeon133/racewheelskinny/race-wheel-55_s.mdl; 70 | models/props_wasteland/wheel02a.mdl; 71 | models/props_vehicles/car001a_hatchback.mdl; 72 | models/hunter/tubes/tube2x2x4c.mdl; 73 | models/props_vehicles/car005a_physics.mdl; 74 | models/props_vehicles/car004b_physics.mdl; 75 | models/xqm/rails/turn_30.mdl; 76 | models/hunter/tubes/tube2x2x16d.mdl; 77 | models/mechanics/gears2/pinion_40.mdl; 78 | models/props_vehicles/car004a_physics.mdl; 79 | models/xqm/coastertrack/turn_slope_down_180_1.mdl; 80 | models/props_vehicles/car003b_physics.mdl; 81 | models/props_vehicles/car003a_physics.mdl; 82 | models/props_vehicles/car005b_physics.mdl; 83 | models/xqm/rails/tunnel_2.mdl; 84 | models/xqm/coastertrack/special_station.mdl; 85 | models/props_vehicles/trailer002a.mdl; 86 | models/mechanics/gears2/pinion_80t1.mdl; 87 | models/props_vehicles/truck003a.mdl; 88 | models/xqm/coastertrack/special_full_corkscrew_left_2.mdl; 89 | models/xqm/coastertrack/turn_slope_down_90_4.mdl; 90 | models/xqm/coastertrack/slope_90_2.mdl; 91 | models/props/cs_assault/forklift.mdl; 92 | models/xqm/coastertrack/special_full_corkscrew_right_4.mdl; 93 | models/props/cs_assault/box_stack1.mdl; 94 | models/props/de_nuke/ibeams_ctspawnb.mdl; 95 | models/props/cs_assault/moneypalletb.mdl; 96 | models/props/cs_assault/moneypalletc.mdl; 97 | models/props/cs_assault/moneypallet03d.mdl; 98 | models/props/cs_assault/moneypallet03e.mdl; 99 | models/props/cs_assault/moneypalleta.mdl; 100 | models/props/cs_assault/moneypallet03a.mdl; 101 | models/props/cs_assault/moneypallet03b.mdl; 102 | models/xqm/rails/straight_16.mdl; 103 | models/props/cs_assault/moneypallet03c.mdl; 104 | models/props/cs_assault/moneypallet02e.mdl; 105 | models/props/cs_assault/moneypallet03.mdl; 106 | models/props/cs_assault/moneypallet02b.mdl; 107 | models/props/cs_assault/moneypallet02d.mdl; 108 | models/props_phx/trains/track_256.mdl; 109 | models/props/de_nuke/storagetank.mdl; 110 | models/props/cs_assault/moneypallet02a.mdl; 111 | models/props/cs_assault/moneypalletd.mdl; 112 | models/xeon133/racewheelskinny/race-wheel-75_s.mdl; 113 | models/props/cs_militia/boxes_frontroom.mdl; 114 | models/props_vehicles/trailer001a.mdl; 115 | models/xqm/coastertrack/slope_45_down_4.mdl; 116 | models/hunter/blocks/cube4x6x1.mdl; 117 | models/props_c17/oildrum001_explosive.mdl; 118 | models/props/cs_militia/coveredbridge01_bottom.mdl; 119 | models/props/de_nuke/coolingtower.mdl; 120 | models/props/cs_militia/boulderring01.mdl; 121 | models/props_phx/trains/tracks/track_pass.mdl; 122 | models/props_phx/trains/tracks/track_single.mdl; 123 | models/props/cs_militia/car_militia.mdl; 124 | models/props/cs_militia/fencewoodlog04_long.mdl; 125 | models/xqm/rails/twist_45_left.mdl; 126 | models/props/cs_militia/fencewoodlog02_short.mdl; 127 | models/props/de_nuke/ibeams_tunnela.mdl; 128 | models/xqm/rails/gumball_1.mdl; 129 | models/xqm/coastertrack/turn_slope_45_3.mdl; 130 | models/xqm/coastertrack/turn_90_tight_3.mdl; 131 | models/props/cs_militia/van.mdl; 132 | models/xqm/rails/twist_45_right.mdl; 133 | models/hunter/blocks/cube8x8x8.mdl; 134 | models/props/cs_office/address.mdl; 135 | models/props/cs_office/crates_indoor.mdl; 136 | models/xqm/coastertrack/bank_start_right_2.mdl; 137 | models/props/de_nuke/ibeams_bombsitea.mdl; 138 | models/props/de_nuke/ibeams_bombsitec.mdl; 139 | models/props_vehicles/tanker001a.mdl; 140 | models/props/de_nuke/ibeams_ctspawna.mdl; 141 | models/props/cs_assault/box_stack2.mdl; 142 | models/props/de_nuke/ibeams_tspawna.mdl; 143 | models/props/de_nuke/ibeams_tspawnb.mdl; 144 | models/props/de_nuke/crate_extralarge.mdl; 145 | models/xqm/coastertrack/bank_turn_180_1.mdl; 146 | models/props/de_nuke/crate_large.mdl; 147 | models/props/de_nuke/crate_small.mdl; 148 | models/xqm/coastertrack/bank_turn_90_4.mdl; 149 | models/props_phx/trains/tracks/track_turn90.mdl; 150 | models/props/de_nuke/smokestack01.mdl; 151 | models/props/de_nuke/powerplanttank.mdl; 152 | models/hunter/tubes/tube1x1x5.mdl; 153 | models/mechanics/wheels/wheel_smooth_96.mdl; 154 | models/props/de_nuke/pipesa_bombsite.mdl; 155 | models/props/de_nuke/nuklogo.mdl; 156 | models/props_phx/trains/tracks/track_16x.mdl; 157 | models/xqm/rails/tunnel_4.mdl; 158 | models/props/de_nuke/ibeams_warehouseroof.mdl; 159 | models/props/cs_militia/fencewoodlog01_short.mdl; 160 | models/props/de_nuke/ibeams_tunnelb.mdl; 161 | models/props/de_nuke/coolingtank.mdl; 162 | models/props/de_nuke/car_nuke_glass.mdl; 163 | models/props_docks/dock02_pole02a_256.mdl; 164 | models/mechanics/wheels/wheel_smooth_72w.mdl; 165 | models/props/de_nuke/car_nuke_red.mdl; 166 | models/xeon133/racewheelskinny/race-wheel-50_s.mdl; 167 | models/xeon133/racewheelskinny/race-wheel-45_s.mdl; 168 | models/props_vehicles/apc001.mdl; 169 | models/xqm/rails/slope_down_30.mdl; 170 | models/xeon133/racewheelskinny/race-wheel-60_s.mdl; 171 | models/xeon133/racewheelskinny/race-wheel-65_s.mdl; 172 | models/xeon133/racewheelskinny/race-wheel-70_s.mdl; 173 | models/props/cs_assault/moneypallet_washerdryer.mdl; 174 | models/xeon133/racewheelskinny/race-wheel-80_s.mdl; 175 | models/props_phx/trains/tracks/track_225_down.mdl; 176 | models/xeon133/offroad/off-road-60.mdl; 177 | models/xqm/coastertrack/train_2.mdl; 178 | models/xqm/coastertrack/special_sturn_left_2.mdl; 179 | models/props/cs_militia/crate_extralargemill.mdl; 180 | models/xqm/coastertrack/train_car_1.mdl; 181 | models/xqm/coastertrain1seat.mdl; 182 | models/xqm/coastertrack/turn_180_tight_4.mdl; 183 | models/xqm/coastertrain1.mdl; 184 | models/xeon133/offroad/off-road-70.mdl; 185 | models/props/cs_militia/boxes_garage.mdl; 186 | models/props_phx/trains/trackslides_both.mdl; 187 | models/props/cs_assault/moneypallet.mdl; 188 | models/props_phx/trains/tracks/track_crossing.mdl; 189 | models/props/de_nuke/truck_nuke.mdl; 190 | models/props_phx/trains/tracks/track_switch2.mdl; 191 | models/props_phx/trains/track_128.mdl; 192 | models/xqm/rails/turn_180.mdl; 193 | models/props_phx/trains/track_512.mdl; 194 | models/props_phx/trains/trackslides_inner.mdl; 195 | models/hunter/tubes/tube4x4x16.mdl; 196 | models/props_phx/trains/trackslides_outer.mdl; 197 | models/props_phx/trains/tracks/track_switcher.mdl; 198 | models/xqm/coastertrack/slope_45_down_3.mdl; 199 | models/props/cs_militia/militiarock05.mdl; 200 | models/xqm/coastertrack/turn_slope_45_2.mdl; 201 | models/xqm/coastertrack/turn_slope_45_4.mdl; 202 | models/xqm/coastertrack/turn_slope_90_2.mdl; 203 | models/xqm/coastertrack/turn_slope_90_1.mdl; 204 | models/xqm/coastertrack/turn_slope_90_4.mdl; 205 | models/xqm/coastertrack/turn_slope_down_180_4.mdl; 206 | models/xqm/coastertrack/turn_slope_down_180_3.mdl; 207 | models/hunter/blocks/cube6x6x6.mdl; 208 | models/props_foliage/tree_poplar_01.mdl; 209 | models/xqm/coastertrack/turn_slope_down_45_1.mdl; 210 | models/xqm/coastertrack/turn_slope_down_45_4.mdl; 211 | models/xqm/coastertrack/turn_slope_down_90_2.mdl; 212 | models/props_canal/canal_bridge03b.mdl; 213 | models/xqm/rails/turn_15.mdl; 214 | models/hunter/blocks/cube2x2x1.mdl; 215 | models/mechanics/gears2/pinion_40t1.mdl; 216 | models/mechanics/gears2/pinion_80t3.mdl; 217 | models/props_phx/gears/rack70.mdl; 218 | models/xqm/rails/twist_90_left.mdl; 219 | models/props_vehicles/truck002a_cab.mdl; 220 | models/props_phx/gears/rack18.mdl; 221 | models/hunter/tubes/tube4x4x8c.mdl; 222 | models/hunter/tubes/tube1x1x6b.mdl; 223 | models/mechanics/gears2/pinion_80t2.mdl; 224 | models/mechanics/gears2/pinion_20t3.mdl; 225 | models/mechanics/gears2/pinion_20t2.mdl; 226 | models/mechanics/gears2/pinion_40t2.mdl; 227 | models/xqm/coastertrack/turn_slope_down_180_2.mdl; 228 | models/hunter/tubes/tube2x2x8b.mdl; 229 | models/hunter/plates/plate6x7.mdl; 230 | models/hunter/plates/plate6x6.mdl; 231 | models/xqm/coastertrack/special_half_corkscrew_left_4.mdl; 232 | models/xqm/coastertrack/special_helix_middle_4.mdl; 233 | models/xqm/rails/tunnel_16.mdl; 234 | models/xqm/rails/twist_90_right.mdl; 235 | models/props_trainstation/pole_448connection001a.mdl; 236 | models/xqm/coastertrack/turn_slope_90_3.mdl; 237 | models/xqm/coastertrack/bank_turn_180_4.mdl; 238 | models/hunter/blocks/cube2x8x1.mdl; 239 | models/xqm/coastertrack/turn_slope_down_90_3.mdl; 240 | models/hunter/blocks/cube6x6x1.mdl; 241 | models/hunter/blocks/cube4x8x1.mdl; 242 | models/xqm/coastertrack/bank_turn_45_4.mdl; 243 | models/hunter/blocks/cube4x4x1.mdl; 244 | models/hunter/blocks/cube8x8x1.mdl; 245 | models/hunter/blocks/cube4x4x2.mdl; 246 | models/hunter/blocks/cube4x6x2.mdl; 247 | models/hunter/blocks/cube6x6x2.mdl; 248 | models/hunter/blocks/cube8x8x2.mdl; 249 | models/props_phx/trains/tracks/track_45_down.mdl; 250 | models/xqm/coastertrack/slope_90_4.mdl; 251 | models/hunter/blocks/cube8x8x4.mdl; 252 | models/xqm/coastertrack/turn_90_4.mdl; 253 | models/hunter/tubes/tube4x4x5d.mdl; 254 | models/hunter/tubes/tube4x4x16d.mdl; 255 | models/hunter/tubes/tube4x4x5b.mdl; 256 | models/hunter/tubes/tube4x4x5c.mdl; 257 | models/mechanics/gears2/pinion_20t1.mdl; 258 | models/props_phx/trains/tracks/track_45_up.mdl; 259 | models/hunter/tubes/tube4x4x6d.mdl; 260 | models/hunter/blocks/cube4x6x4.mdl; 261 | models/xqm/rails/slope_up_30.mdl; 262 | models/hunter/tubes/tube4x4x6b.mdl; 263 | models/hunter/tubes/tubebend2x2x90square.mdl; 264 | models/hunter/tubes/tube4x4x4d.mdl; 265 | models/hunter/blocks/cube6x8x2.mdl; 266 | models/xqm/coastertrack/turn_slope_down_90_1.mdl; 267 | models/hunter/blocks/cube2x2x2.mdl; 268 | models/props_phx/trains/monorail4.mdl; 269 | models/hunter/tubes/tube4x4x6c.mdl; 270 | models/hunter/tubes/tube4x4x16b.mdl; 271 | models/props_phx/trains/tracks/track_switcher2.mdl; 272 | models/props/cs_militia/bathroomwallhole01_wood_broken.mdl; 273 | models/hunter/tubes/tube4x4x8d.mdl; 274 | models/hunter/tubes/tube4x4x8.mdl; 275 | models/props_wasteland/cargo_container01b.mdl; 276 | models/hunter/tubes/tube1x1x8c.mdl; 277 | models/props_wasteland/medbridge_base01.mdl; 278 | models/hunter/tubes/tube4x4x8b.mdl; 279 | models/hunter/blocks/cube2x4x1.mdl; 280 | models/props_phx/trains/tracks/track_turn45.mdl; 281 | models/props/cs_militia/tree_large_militia.mdl; 282 | models/hunter/tubes/tube4x4x16c.mdl; 283 | models/xqm/coastertrack/bank_start_right_3.mdl; 284 | models/props_phx/trains/track_32.mdl; 285 | models/hunter/tubes/tube2x2x4d.mdl; 286 | models/hunter/tubes/tube2x2x8.mdl; 287 | models/hunter/tubes/tube2x2x8d.mdl; 288 | models/hunter/tubes/tube2x2x2d.mdl; 289 | models/xqm/rails/turn_45.mdl; 290 | models/hunter/tubes/tube2x2x4b.mdl; 291 | models/xqm/rails/funnel.mdl; 292 | models/hunter/tubes/tube2x2x4.mdl; 293 | models/hunter/tubes/tube1x1x4c.mdl; 294 | models/hunter/tubes/tube2x2x8c.mdl; 295 | models/hunter/tubes/tube2x2x1d.mdl; 296 | models/xqm/coastertrack/turn_90_2.mdl; 297 | models/hunter/tubes/tube2x2x2c.mdl; 298 | models/props_wasteland/cargo_container01c.mdl; 299 | models/hunter/tubes/tube1x1x6d.mdl; 300 | models/hunter/tubes/tube1x1x8.mdl; 301 | models/hunter/tubes/tube1x1x8b.mdl; 302 | models/hunter/tubes/tube1x1x4.mdl; 303 | models/hunter/tubes/tube1x1x8d.mdl; 304 | models/hunter/tubes/tube1x1x6.mdl; 305 | models/hunter/tubes/tube1x1x5b.mdl; 306 | models/hunter/tubes/tube1x1x5d.mdl; 307 | models/hunter/tubes/tube1x1x5c.mdl; 308 | models/hunter/tubes/tube1x1x4d.mdl; 309 | models/hunter/tubes/tube1x1x3d.mdl; 310 | models/hunter/tubes/tube1x1x3c.mdl; 311 | models/hunter/tubes/tube1x1x4b.mdl; 312 | models/hunter/tubes/tube1x1x3b.mdl; 313 | models/props_phx/mechanics/slider2.mdl; 314 | models/xqm/coastertrack/turn_slope_180_4.mdl; 315 | models/xqm/coastertrack/turn_slope_180_3.mdl; 316 | models/props/de_nuke/car_nuke_black.mdl; 317 | models/xqm/coastertrack/turn_90_tight_4.mdl; 318 | models/xqm/coastertrack/special_half_corkscrew_right_4.mdl; 319 | models/xqm/coastertrack/turn_slope_180_1.mdl; 320 | models/hunter/plates/plate8x8.mdl; 321 | models/xqm/coastertrack/special_full_loop_4.mdl; 322 | models/xqm/coastertrack/turn_90_tight_1.mdl; 323 | models/xqm/coastertrack/special_full_corkscrew_right_2.mdl; 324 | models/xqm/coastertrack/turn_90_3.mdl; 325 | models/props_wasteland/cargo_container01.mdl; 326 | models/xqm/coastertrack/track_guide.mdl; 327 | models/xqm/coastertrack/turn_90_1.mdl; 328 | models/xqm/coastertrack/turn_45_3.mdl; 329 | models/xqm/coastertrack/turn_45_4.mdl; 330 | models/xqm/coastertrack/turn_45_2.mdl; 331 | models/xqm/coastertrack/slope_90_down_3.mdl; 332 | models/xqm/coastertrain2seat.mdl; 333 | models/xqm/coastertrack/turn_180_tight_3.mdl; 334 | models/xqm/coastertrack/bank_start_left_2.mdl; 335 | models/xqm/coastertrack/turn_180_3.mdl; 336 | models/xqm/coastertrack/turn_180_tight_2.mdl; 337 | models/props_phx/trains/tracks/track_225_up.mdl; 338 | models/xqm/coastertrack/turn_180_2.mdl; 339 | models/xqm/coastertrack/straight_4.mdl; 340 | models/xqm/coastertrack/bank_start_right_4.mdl; 341 | models/xqm/coastertrack/straight_3.mdl; 342 | models/xqm/coastertrack/straight_2.mdl; 343 | models/xqm/coastertrack/straight_1.mdl; 344 | models/xqm/coastertrack/special_sturn_right_4.mdl; 345 | models/xqm/coastertrack/special_sturn_right_2.mdl; 346 | models/xqm/coastertrack/special_sturn_left_4.mdl; 347 | models/xqm/coastertrack/special_helix_middle_full_4.mdl; 348 | models/props/de_nuke/ibeams_bombsite_d.mdl; 349 | models/hunter/plates/plate7x7.mdl; 350 | models/xqm/coastertrack/special_helix_middle_3.mdl; 351 | models/xqm/coastertrack/special_helix_middle_full_2.mdl; 352 | models/props/cs_militia/van_glass.mdl; 353 | models/xqm/coastertrack/special_half_corkscrew_right_3.mdl; 354 | models/hunter/blocks/cube2x6x1.mdl; 355 | models/xqm/coastertrack/special_half_corkscrew_right_1.mdl; 356 | models/xqm/coastertrack/special_half_corkscrew_right_2.mdl; 357 | models/hunter/plates/plate6x8.mdl; 358 | models/xqm/coastertrack/turn_90_tight_2.mdl; 359 | models/props/cs_assault/billboard.mdl; 360 | models/xqm/coastertrack/special_full_corkscrew_right_1.mdl; 361 | models/mechanics/gears2/pinion_40t3.mdl; 362 | models/props_c17/column02a.mdl; 363 | models/xqm/coastertrack/special_full_corkscrew_left_4.mdl; 364 | models/props_vehicles/truck001a.mdl; 365 | models/xqm/coastertrack/special_full_corkscrew_left_3.mdl; 366 | models/props/cs_militia/fencewoodlog03_long.mdl; 367 | models/xqm/coastertrack/turn_45_1.mdl; 368 | models/xqm/coastertrack/slope_90_down_4.mdl; 369 | models/hunter/tubes/tubebend4x4x90.mdl; 370 | models/xqm/coastertrack/slope_90_down_2.mdl; 371 | models/xqm/coastertrack/slope_90_down_1.mdl; 372 | models/xqm/coastertrack/slope_90_3.mdl; 373 | models/props_vehicles/van001a_physics.mdl; 374 | models/xqm/coastertrack/slope_90_1.mdl; 375 | models/props/cs_assault/wirepipe.mdl; 376 | models/xqm/rails/loop_left.mdl; 377 | models/xqm/coastertrack/bank_turn_90_1.mdl; 378 | models/props_phx/trains/tracks/track_x.mdl; 379 | models/xqm/coastertrack/bank_turn_90_2.mdl; 380 | models/props/cs_assault/moneypallet02c.mdl; 381 | models/xqm/coastertrack/bank_turn_90_3.mdl; 382 | models/props/de_nuke/cinderblock_stack.mdl; 383 | models/xqm/coastertrack/bank_turn_45_1.mdl; 384 | models/xqm/coastertrack/bank_turn_45_3.mdl; 385 | models/props_wasteland/horizontalcoolingtank04.mdl; 386 | models/xqm/coastertrack/bank_turn_180_2.mdl; 387 | models/props_phx/gears/rack9.mdl; 388 | models/hunter/tubes/tube1x1x6c.mdl; 389 | models/xqm/coastertrack/special_helix_middle_2.mdl; 390 | models/xqm/coastertrack/special_full_corkscrew_left_1.mdl; 391 | models/props/de_nuke/crate_extrasmall.mdl; 392 | models/xqm/coastertrack/turn_180_4.mdl; 393 | models/props_combine/combine_train02a.mdl; 394 | models/xqm/coastertrack/turn_slope_45_1.mdl; 395 | models/xqm/coastertrack/bank_start_left_1.mdl; 396 | models/xqm/coastertrack/bank_start_right_1.mdl; 397 | models/props/cs_office/crates_outdoor.mdl; 398 | models/xqm/coastertrack/slope_45_down_2.mdl; 399 | models/hunter/plates/plate7x8.mdl; 400 | models/xqm/rails/loop_right.mdl; 401 | models/hunter/tubes/tube2x2x2b.mdl; 402 | models/xqm/rails/turn_90.mdl; 403 | models/hunter/blocks/cube4x4x4.mdl; 404 | models/xqm/rails/slope_up_45.mdl; 405 | models/xqm/rails/slope_up_15.mdl; 406 | models/xqm/rails/slope_up_90.mdl; 407 | models/props_combine/combinetrain01a.mdl; 408 | models/xqm/rails/slope_down_45.mdl; 409 | models/xqm/rails/slope_down_90.mdl; 410 | models/xqm/rails/slope_down_15.mdl; 411 | models/xqm/rails/tunnel_8.mdl; 412 | models/props/de_nuke/nuclearfuelcontainer.mdl; 413 | models/props_trainstation/train003.mdl; 414 | models/xqm/rails/straight_4.mdl; 415 | models/xqm/rails/straight_8.mdl; 416 | models/xqm/rails/straight_1.mdl; 417 | models/xqm/rails/straight_2.mdl; 418 | models/xqm/coastertrack/special_sturn_left_3.mdl; 419 | models/props_phx/trains/tracks/track_1x.mdl; 420 | models/props_phx/trains/tracks/track_2x.mdl; 421 | models/props_phx/trains/tracks/track_8x.mdl; 422 | --------------------------------------------------------------------------------