diff --git a/lua/autorun/client/cl_nadmodpp.lua b/lua/autorun/client/cl_nadmodpp.lua index fed71a5..36a9dc7 100644 --- a/lua/autorun/client/cl_nadmodpp.lua +++ b/lua/autorun/client/cl_nadmodpp.lua @@ -6,23 +6,71 @@ if !NADMOD then NADMOD = {} NADMOD.PropOwners = {} + NADMOD.PropNames = {} NADMOD.PPConfig = {} NADMOD.Friends = {} end local Props = NADMOD.PropOwners -net.Receive("nadmod_propowners",function(len) - local num = net.ReadUInt(16) - for k=1,num do - local id,str = net.ReadUInt(16), net.ReadString() - if str == "-" then Props[id] = nil - elseif str == "W" then Props[id] = "World" - elseif str == "O" then Props[id] = "Ownerless" - else Props[id] = str +local PropNames = NADMOD.PropNames +net.Receive("nadmod_propowners",function(len) + local nameMap = {} + for i=1, net.ReadUInt(8) do + nameMap[i] = net.ReadString() + end + for i=1, net.ReadUInt(32) do + local id, str = net.ReadUInt(16), nameMap[net.ReadUInt(8)] + if id==0 then break end + if str == "-" then Props[id] = nil PropNames[id] = nil + elseif str == "W" then PropNames[id] = "World" + elseif str == "O" then PropNames[id] = "Ownerless" + else + Props[id] = str + local ply = player.GetBySteamID(str) + PropNames[id] = ply and ply:IsValid() and ply:Nick() or "N/A" end end end) +function NADMOD.GetPropOwner(ent) + local id = Props[ent:EntIndex()] + return id and player.GetBySteamID(id) +end + +function NADMOD.PlayerCanTouch(ply, ent) + -- If PP is off or the ent is worldspawn, let them touch it + if not tobool(NADMOD.PPConfig["toggle"]) then return true end + if ent:IsWorld() then return ent:GetClass()=="worldspawn" end + if !IsValid(ent) or !IsValid(ply) or ent:IsPlayer() or !ply:IsPlayer() then return false end + + local index = ent:EntIndex() + if not Props[index] then + return false + end + + -- Ownerless props can be touched by all + if PropNames[index] == "Ownerless" then return true end + -- Admins can touch anyones props + world + if NADMOD.PPConfig["adminall"] and NADMOD.IsPPAdmin(ply) then return true end + -- Players can touch their own props + local plySteam = ply:SteamID() + if Props[index] == plySteam then return true end + -- Friends can touch LocalPlayer()'s props + if Props[index] == LocalPlayer():SteamID() and NADMOD.Friends[plySteam] then return true end + + return false +end + +-- Does your admin mod not seem to work with Nadmod PP? Try overriding this function! +function NADMOD.IsPPAdmin(ply) + if NADMOD.HasPermission then + return NADMOD.HasPermission(ply, "PP_All") + else + -- If the admin mod NADMOD isn't present, just default to using IsAdmin + return ply:IsAdmin() + end +end + local nadmod_overlay_convar = CreateConVar("nadmod_overlay", 2, {FCVAR_NOTIFY, FCVAR_ARCHIVE, FCVAR_REPLICATED}, "0 - Disables NPP Overlay. 1 - Minimal overlay of just owner info. 2 - Includes model, entityID, class") local font = "ChatFont" hook.Add("HUDPaint", "NADMOD.HUDPaint", function() @@ -32,7 +80,7 @@ hook.Add("HUDPaint", "NADMOD.HUDPaint", function() if !tr.HitNonWorld then return end local ent = tr.Entity if ent:IsValid() && !ent:IsPlayer() then - local text = "Owner: " .. (Props[ent:EntIndex()] or "N/A") + local text = "Owner: " .. (PropNames[ent:EntIndex()] or "N/A") surface.SetFont(font) local Width, Height = surface.GetTextSize(text) local boxWidth = Width + 25 @@ -138,8 +186,9 @@ function NADMOD.AdminPanel(Panel, runByNetReceive) end for k, ply in pairs(player.GetAll()) do if IsValid(ply) then - Panel:Button( ply:Nick().." ("..(counts[ply:Nick()] or 0)..")", "nadmod_cleanupprops", ply:EntIndex() ) - dccount = dccount - (counts[ply:Nick()] or 0) + local steamid = ply:SteamID() + Panel:Button( ply:Nick().." ("..(counts[steamid] or 0)..")", "nadmod_cleanupprops", ply:EntIndex() ) + dccount = dccount - (counts[steamid] or 0) end end @@ -217,3 +266,16 @@ net.Receive("nadmod_notify", function(len) surface.PlaySound("ambient/water/drip"..math.random(1, 4)..".wav") print(text) end) + +CPPI = {} +local metaent = FindMetaTable("Entity") +local metaply = FindMetaTable("Player") + +function CPPI:GetName() return "Nadmod Prop Protection" end +function CPPI:GetVersion() return "" end +function metaply:CPPIGetFriends() return {} end +function metaent:CPPIGetOwner() return NADMOD.GetPropOwner(self) end +function metaent:CPPICanTool(ply,mode) return NADMOD.PlayerCanTouch(ply,self) end +function metaent:CPPICanPhysgun(ply) return NADMOD.PlayerCanTouch(ply,self) end +function metaent:CPPICanPickup(ply) return NADMOD.PlayerCanTouch(ply,self) end +function metaent:CPPICanPunt(ply) return NADMOD.PlayerCanTouch(ply,self) end diff --git a/lua/autorun/server/nadmod_pp.lua b/lua/autorun/server/nadmod_pp.lua index aa0d1cf..e7bca80 100644 --- a/lua/autorun/server/nadmod_pp.lua +++ b/lua/autorun/server/nadmod_pp.lua @@ -80,6 +80,41 @@ function NADMOD.IsPPAdmin(ply) end end +function NADMOD.SendPropOwners(props, ply) + net.Start("nadmod_propowners") + local nameMap = {} + local nameMapi = 0 + local count = 0 + for k,v in pairs(props) do + if not nameMap[v] then + nameMapi = nameMapi + 1 + nameMap[v] = nameMapi + nameMap[nameMapi] = v + end + count = count + 1 + end + net.WriteUInt(nameMapi,8) + for i=1, nameMapi do + net.WriteString(nameMap[i]) + end + + net.WriteUInt(count,32) + for k,v in pairs(props) do + net.WriteUInt(k,16) + net.WriteUInt(nameMap[v],8) + end + if ply then net.Send(ply) else net.Broadcast() end +end + +function NADMOD.RefreshOwners() + if not timer.Exists("NADMOD.RefreshOwners") then + timer.Create("NADMOD.RefreshOwners", 0, 1, function() + NADMOD.SendPropOwners(NADMOD.PropOwnersSmall) + NADMOD.PropOwnersSmall = {} + end) + end +end + function NADMOD.PPInitPlayer(ply) local steamid = ply:SteamID() for _,v in pairs(NADMOD.Props) do @@ -89,13 +124,11 @@ function NADMOD.PPInitPlayer(ply) if v.Ent.SetPlayer then v.Ent:SetPlayer(ply) end end end - net.Start("nadmod_propowners") - net.WriteUInt(table.Count(NADMOD.Props),16) - for k,v in pairs(NADMOD.Props) do - net.WriteUInt(k,16) - net.WriteString(v.Name) - end - net.Send(ply) + local tbl = {} + for k,v in pairs(NADMOD.Props) do + tbl[k] = v.SteamID + end + NADMOD.SendPropOwners(tbl, ply) end hook.Add("PlayerInitialSpawn", "NADMOD.PPInitPlayer", NADMOD.PPInitPlayer) @@ -110,21 +143,6 @@ function NADMOD.PPOwnWeapons(ply) end hook.Add("PlayerSpawn", "NADMOD.PPOwnWeapons", NADMOD.PPOwnWeapons) -function NADMOD.RefreshOwners() - if next(NADMOD.PropOwnersSmall) then - net.Start("nadmod_propowners") - net.WriteUInt(table.Count(NADMOD.PropOwnersSmall),16) - for k,v in pairs(NADMOD.PropOwnersSmall) do - net.WriteUInt(k,16) - net.WriteString(v) - end - //net.WriteTable(NADMOD.PropOwnersSmall) - net.Broadcast() - table.Empty(NADMOD.PropOwnersSmall) - end -end -timer.Create("NADMOD.RefreshOwners", 1, 0, NADMOD.RefreshOwners) - function NADMOD.IsFriendProp(ply, ent) if IsValid(ent) && IsValid(ply) && ply:IsPlayer() && NADMOD.Props[ent:EntIndex()] then local ownerSteamID = NADMOD.Props[ent:EntIndex()].SteamID @@ -138,39 +156,45 @@ end function NADMOD.PlayerCanTouch(ply, ent) -- If PP is off or the ent is worldspawn, let them touch it - if not tobool(NADMOD.PPConfig["toggle"]) or ent:IsWorld() then return true end - + if not tobool(NADMOD.PPConfig["toggle"]) then return true end + if ent:IsWorld() then return ent:GetClass()=="worldspawn" end if !IsValid(ent) or !IsValid(ply) or ent:IsPlayer() or !ply:IsPlayer() then return false end - if not NADMOD.Props[ent:EntIndex()] then + local index = ent:EntIndex() + if not NADMOD.Props[index] then + if index == 0 then + -- Players cannot take ownership of EntIndex 0 ents (constraints, func_'s, map lights) + NADMOD.SetOwnerWorld(ent) + return false + end + local class = ent:GetClass() - if(string.find(class, "stone_") == 1 or string.find(class, "rock_") == 1 or string.find(class, "stargate_") == 0 or string.find(class, "dhd_") == 0 or class == "flag" or class == "item" or class == "predicted_viewmodel" or class == "gmod_hands" or class == "physgun_beam") then + if(class == "predicted_viewmodel" or class == "gmod_hands" or class == "physgun_beam") then NADMOD.SetOwnerWorld(ent) elseif ent.GetPlayer and IsValid(ent:GetPlayer()) then NADMOD.PlayerMakePropOwner(ent:GetPlayer(), ent) elseif ent.GetOwner and (IsValid(ent:GetOwner()) or ent:GetOwner():IsWorld()) then NADMOD.PlayerMakePropOwner(ent:GetOwner(), ent) - elseif ent:EntIndex() == 0 then - -- Players cannot take ownership of EntIndex 0 ents (constraints, func_'s, map lights) - return false else NADMOD.PlayerMakePropOwner(ply, ent) NADMOD.Notify(ply, "You now own this " .. class .. " (" .. string.sub(table.remove(string.Explode("/", ent:GetModel() or "?")), 1,-5) .. ")" ) return true end + + if !NADMOD.Props[index] then + -- To get here implies the ent has a 'valid' GetPlayer()/GetOwner(), but still couldn't get set properly + -- For example, if an NPC is sitting in jeep (??), the jeep's GetPlayer returns the driver? or something + ent:CPPISetOwnerless(true) + if !NADMOD.Props[index] then return false end + end end - if !NADMOD.Props[ent:EntIndex()] then - -- To get here implies the ent has a 'valid' GetPlayer()/GetOwner(), but still couldn't get set properly - -- For example, if an NPC is sitting in jeep (??), the jeep's GetPlayer returns the driver? or something - ent:CPPISetOwnerless(true) - if !NADMOD.Props[ent:EntIndex()] then return end - end + -- Ownerless props can be touched by all - if NADMOD.Props[ent:EntIndex()].Name == "O" then return true end + if NADMOD.Props[index].Name == "O" then return true end -- Admins can touch anyones props + world if NADMOD.PPConfig["adminall"] and NADMOD.IsPPAdmin(ply) then return true end -- Players can touch their own props and friends - if NADMOD.Props[ent:EntIndex()].SteamID == ply:SteamID() or NADMOD.IsFriendProp(ply, ent) then return true end + if NADMOD.Props[index].SteamID == ply:SteamID() or NADMOD.IsFriendProp(ply, ent) then return true end return false end @@ -264,9 +288,11 @@ function NADMOD.PlayerMakePropOwner(ply,ent) SteamID = ply:SteamID(), Name = ply:Nick() } - NADMOD.PropOwnersSmall[ent:EntIndex()] = ply:Nick() + NADMOD.PropOwnersSmall[ent:EntIndex()] = ply:SteamID() ent.SPPOwner = ply + NADMOD.RefreshOwners() end + -- Hook into the cleanup and sbox-limit adding functions to catch most props if(cleanup) then local backupcleanupAdd = cleanup.Add @@ -299,6 +325,7 @@ function metaent:CPPISetOwnerless(bool) } NADMOD.PropOwnersSmall[self:EntIndex()] = "O" self.SPPOwner = game.GetWorld() + NADMOD.RefreshOwners() else NADMOD.EntityRemoved(self) end @@ -313,14 +340,15 @@ function NADMOD.SetOwnerWorld(ent) } NADMOD.PropOwnersSmall[ent:EntIndex()] = "W" ent.SPPOwner = game.GetWorld() + NADMOD.RefreshOwners() end -- Loop through all entities that exist when the map is loaded, these are all "world owned" entities function NADMOD.WorldOwner() local WorldEnts = 0 - for k,v in pairs(ents.FindByClass("*")) do + for k,v in pairs(ents.GetAll()) do if(!v:IsPlayer() and (v:EntIndex() == 0 or !NADMOD.Props[v:EntIndex()])) and not IsValid(v.SPPOwner) then - if game.GetMap() == "gm_construct" and v:GetClass() == "func_brush" then + if v:GetClass() == "func_brush" and game.GetMap() == "gm_construct" then v:CPPISetOwnerless(true) else NADMOD.SetOwnerWorld(v) @@ -331,12 +359,15 @@ function NADMOD.WorldOwner() print("Nadmod Prop Protection: "..WorldEnts.." props belong to world") end if CurTime() < 5 then timer.Create("NADMOD.PPFindWorldProps",7,1,NADMOD.WorldOwner) end -hook.Add("PostCleanupMap","NADMOD.PPFindWorldProps",NADMOD.WorldOwner) +hook.Add("PostCleanupMap","NADMOD.MapCleaned",function() + timer.Simple(0,function() NADMOD.WorldOwner() end) +end) function NADMOD.EntityRemoved(ent) NADMOD.Props[ent:EntIndex()] = nil NADMOD.PropOwnersSmall[ent:EntIndex()] = "-" + NADMOD.RefreshOwners() if ent:IsValid() and ent:IsPlayer() and not ent:IsBot() then -- This is more reliable than PlayerDisconnect local steamid, nick = ent:SteamID(), ent:Nick() @@ -382,10 +413,10 @@ function NADMOD.CleanupPlayerProps(steamid) return count end -function NADMOD.CleanPlayer(tar) +function NADMOD.CleanPlayer(ply, tar) if IsValid(tar) and tar:IsPlayer() then local count = NADMOD.CleanupPlayerProps(tar:SteamID()) - NADMOD.Notify(tar:Nick().."'s props have been cleaned up ("..count..")") + NADMOD.Notify((ply:IsValid() and ply:Nick() or "Console") .. " cleaned up " ..tar:Nick().."'s props ("..count..")") end end @@ -395,14 +426,14 @@ function NADMOD.CleanupProps(ply, cmd, args) local count = NADMOD.CleanupPlayerProps(ply:SteamID()) NADMOD.Notify(ply,"Your props have been cleaned up ("..count..")") elseif !ply:IsValid() or NADMOD.IsPPAdmin(ply) then - NADMOD.CleanPlayer(Entity(EntIndex)) + NADMOD.CleanPlayer(ply, Entity(EntIndex)) end end concommand.Add("nadmod_cleanupprops", NADMOD.CleanupProps) function NADMOD.CleanPlayerConCommand(ply, cmd, args, fullstr) if ply:IsValid() and not NADMOD.IsPPAdmin(ply) then return end - NADMOD.CleanPlayer(NADMOD.FindPlayer(fullstr)) + NADMOD.CleanPlayer(ply, NADMOD.FindPlayer(fullstr)) end concommand.Add("nadmod_cleanplayer", NADMOD.CleanPlayerConCommand) @@ -418,7 +449,7 @@ function NADMOD.CleanName(ply, cmd, args, fullstr) count = count + 1 end end - NADMOD.Notify(fullstr.."'s props ("..count..") have been cleaned up") + NADMOD.Notify((ply:IsValid() and ply:Nick() or "Console") .. " cleaned up " ..fullstr.."'s props ("..count..")") end concommand.Add("nadmod_cleanname",NADMOD.CleanName)