Files
wnsrc/lua/pac3/editor/server/wear.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

456 lines
12 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local next = next
local type = type
local istable = istable
local IsValid = IsValid
local tostring = tostring
local isfunction = isfunction
local ProtectedCall = ProtectedCall
pace.StreamQueue = pace.StreamQueue or {}
timer.Create("pac_check_stream_queue", 0.1, 0, function()
local item = table.remove(pace.StreamQueue)
if not item then return end
local data = item.data
local filter = item.filter
local callback = item.callback
local allowed, reason
local function submitPart()
allowed, reason = pace.SubmitPartNow(data, filter)
end
local success = ProtectedCall(submitPart)
if not isfunction(callback) then return end
if not success then
allowed = false
reason = "Unexpected Error"
end
ProtectedCall(function()
callback(allowed, reason)
end)
end)
local function make_copy(tbl, input)
if tbl.self.UniqueID then
tbl.self.UniqueID = pac.Hash(tbl.self.UniqueID .. input)
end
for _, val in pairs(tbl.children) do
make_copy(val, input)
end
end
local function net_write_table(tbl)
local buffer = pac.StringStream()
buffer:writeTable(tbl)
local data = buffer:getString()
local ok, err = pcall(net.WriteStream, data)
if not ok then
return ok, err
end
return #data
end
pace.dupe_ents = pace.dupe_ents or {}
local uid2key = include("pac3/editor/server/legacy_network_dictionary_translate.lua")
local function translate_old_dupe(tableIn, target)
for key, value2 in pairs(tableIn) do
local value
if istable(value2) then
value = translate_old_dupe(value2, {})
else
value = value2
end
if isnumber(key) and key > 10000 then
local str = uid2key[key] or key
target[str] = value
else
target[key] = value
end
end
return target
end
duplicator.RegisterEntityModifier("pac_config", function(ply, ent, parts)
if parts.json then
parts = util.JSONToTable(parts.json)
parts = translate_old_dupe(parts, {})
end
local id = ent:EntIndex()
if parts.part then
parts = {[parts.part.self.UniqueID] = parts}
end
ent.pac_parts = parts
pace.dupe_ents[ent:EntIndex()] = {owner = ply, ent = ent}
-- give source engine time
timer.Simple(0, function()
for _, data in pairs(parts) do
if istable(data.part) then
make_copy(data.part, id)
data.part.self.Name = tostring(ent)
data.part.self.OwnerName = id
end
data.owner = ply
data.uid = pac.Hash(ply)
data.is_dupe = true
-- clientside sent variables cleanup for sanity
data.wear_filter = nil
data.partID = nil
data.totalParts = nil
data.transmissionID = nil
pace.SubmitPart(data)
end
end)
end)
function pace.SubmitPartNow(data, filter)
local part = data.part
local owner = data.owner
-- last arg "true" is pac3 only in case you need to do your checking differently from pac2
local allowed, reason = hook.Run("PrePACConfigApply", owner, data, true)
if allowed == false then return allowed, reason end
local uid = data.uid
if uid ~= false and pace.IsBanned(owner) then
return false, "you are banned from using pac"
end
if istable(part) then
local ent = Entity(tonumber(part.self.OwnerName) or -1)
if ent:IsValid() then
if not pace.CanPlayerModify(owner, ent) then
local entOwner = ent.CPPIGetOwner and ent:CPPIGetOwner()
entOwner = tostring(entOwner or "world")
return false, "you are not allowed to modify this entity: " .. tostring(ent) .. " owned by: " .. entOwner
else
if not data.is_dupe then
ent.pac_parts = ent.pac_parts or {}
ent.pac_parts[pac.Hash(owner)] = data
pace.dupe_ents[ent:EntIndex()] = {owner = owner, ent = ent}
duplicator.ClearEntityModifier(ent, "pac_config")
-- fresh table copy
duplicator.StoreEntityModifier(ent, "pac_config", {json = util.TableToJSON(table.Copy(ent.pac_parts))})
end
ent:CallOnRemove("pac_config", function(e)
if e.pac_parts then
for _, eData in pairs(e.pac_parts) do
if istable(eData.part) then
eData.part = eData.part.self.UniqueID
end
pace.RemovePart(eData)
end
end
end)
end
end
end
pace.Parts[uid] = pace.Parts[uid] or {}
if istable(part) then
pace.Parts[uid][part.self.UniqueID] = data
else
if part == "__ALL__" then
pace.Parts[uid] = {}
filter = true
for key, v in pairs(pace.dupe_ents) do
if v.owner:IsValid() and v.owner == owner then
if v.ent:IsValid() and v.ent.pac_parts then
v.ent.pac_parts = nil
duplicator.ClearEntityModifier(v.ent, "pac_config")
end
end
pace.dupe_ents[key] = nil
end
elseif part then
pace.Parts[uid][part] = nil
end
end
local players
if IsValid(data.temp_wear_filter) and type(data.temp_wear_filter) == "Player" then
players = {data.temp_wear_filter}
elseif istable(data.wear_filter) then
players = {}
for _, id in ipairs(data.wear_filter) do
local ply = pac.ReverseHash(id, "Player")
if ply:IsValid() then
table.insert(players, ply)
end
end
else
players = player.GetAll()
end
if filter == false then
filter = owner
elseif filter == true then
local tbl = {}
for _, v in pairs(players) do
if v ~= owner then
table.insert(tbl, v)
end
end
filter = tbl
end
if not data.server_only then
if owner:IsValid() then
data.player_uid = pac.Hash(owner)
end
players = filter or players
if istable(players) then
for key = #players, 1, -1 do
local ply = players[key]
if not ply.pac_requested_outfits and ply ~= owner then
table.remove(players, key)
end
end
if pace.GlobalBans and owner:IsValid() then
local owner_steamid = owner:SteamID()
for key, ply in pairs(players) do
local steamid = ply:SteamID()
for var in pairs(pace.GlobalBans) do
if var == steamid or istable(var) and (table.HasValue(var, steamid) or table.HasValue(var, util.CRC(ply:IPAddress():match("(.+):") or ""))) then
table.remove(players, key)
if owner_steamid == steamid then
pac.Message("Dropping data transfer request by '", ply:Nick(), "' due to a global PAC ban.")
return false, "You have been globally banned from using PAC. See global_bans.lua for more info."
end
end
end
end
end
elseif type(players) == "Player" and (not players.pac_requested_outfits and players ~= owner) then
data.transmissionID = nil
return true
end
if not players or istable(players) and not next(players) then return true end
-- Alternative transmission system
local ret = hook.Run("pac_SendData", players, data)
if ret == nil then
net.Start("pac_submit")
local bytes, err = net_write_table(data)
if not bytes then
local errStr = tostring(err)
ErrorNoHalt("[PAC3] Outfit broadcast failed for " .. tostring(owner) .. ": " .. errStr .. '\n')
if owner and owner:IsValid() then
owner:ChatPrint('[PAC3] ERROR: Could not broadcast your outfit: ' .. errStr)
end
else
net.Send(players)
end
end
if istable(part) then
pace.CallHook("OnWoreOutfit", owner, part)
end
end
-- nullify transmission ID
data.transmissionID = nil
return true
end
-- Inserts the given part into the StreamQueue
function pace.SubmitPart(data, filter, callback)
if istable(data.part) then
pac.dprint("queuing part %q from %s", data.part.self.Name, tostring(data.owner))
table.insert(pace.StreamQueue, {
data = data,
filter = filter,
callback = callback
})
return "queue"
end
return pace.SubmitPartNow(data, filter)
end
-- Inserts the given part into the StreamQueue, and notifies when it completes
function pace.SubmitPartNotify(data)
local part = data.part
local partName = part.self.Name or "no name"
pac.dprint("submitted outfit %q from %s with %i children to set on %s", partName, data.owner:GetName(), table.Count(part.children), part.self.OwnerName or "")
local function callback(allowed, reason)
if allowed == "queue" then return end
if not data.owner:IsPlayer() then return end
reason = reason or ""
if not reason and allowed and istable(part) then
reason = string.format("Your part %q has been applied", partName or "<unknown>")
end
net.Start("pac_submit_acknowledged")
net.WriteBool(allowed)
net.WriteString(reason)
net.WriteString(partName)
net.Send(data.owner)
hook.Run("PACSubmitAcknowledged", data.owner, not not allowed, reason, partName, data)
end
pace.SubmitPart(data, nil, callback)
end
function pace.RemovePart(data)
local part = data.part
local owner = data.owner
pac.dprint("%s is removed %q", owner and owner:IsValid() and owner:GetName(), part)
if part == "__ALL__" then
pace.CallHook("RemoveOutfit", owner)
end
pace.SubmitPart(data, data.filter)
end
function pace.HandleReceivedData(ply, data)
data.owner = ply
data.uid = pac.Hash(ply)
if data.wear_filter and #data.wear_filter > game.MaxPlayers() then
pac.Message("Player ", ply, " tried to submit extraordinary wear filter size of ", #data.wear_filter, ", dropping.")
data.wear_filter = nil
end
if istable(data.part) and data.part.self then
if istable(data.part.self) and not data.part.self.UniqueID then return end -- bogus data
pac.Message("Received pac group ", data.partID or 0 , "/", data.totalParts or 0, " from ", ply)
pace.SubmitPartNotify(data)
elseif isstring(data.part) then
local clearing = data.part == "__ALL__"
pac.Message("Clearing ", clearing and "Oufit" or "Part" , " from ", ply)
pace.RemovePart(data)
end
end
util.AddNetworkString("pac_submit")
local pac_submit_spam = CreateConVar('pac_submit_spam', '1', {FCVAR_NOTIFY, FCVAR_ARCHIVE}, 'Prevent users from spamming pac_submit')
local pac_submit_limit = CreateConVar('pac_submit_limit', '30', {FCVAR_NOTIFY, FCVAR_ARCHIVE}, 'pac_submit spam limit')
pace.PCallNetReceive(net.Receive, "pac_submit", function(len, ply)
if len < 64 then return end
if pac_submit_spam:GetBool() and not game.SinglePlayer() then
local allowed = pac.RatelimitPlayer( ply, "pac_submit", pac_submit_limit:GetInt(), 5, {"Player ", ply, " is spamming pac_submit!"} )
if not allowed then return end
end
if pac.CallHook("CanWearParts", ply) == false then
return
end
net.ReadStream(ply, function(data)
if not data then
pac.Message("message from ", ply, " timed out")
return
end
if not ply:IsValid() then
pac.Message("received message from ", ply, " but player is no longer valid!")
return
end
local buffer = pac.StringStream(data)
pace.HandleReceivedData(ply, buffer:readTable())
end)
end)
function pace.ClearOutfit(ply)
local uid = pac.Hash(ply)
pace.SubmitPart({part = "__ALL__", uid = pac.Hash(ply), owner = ply})
pace.CallHook("RemoveOutfit", ply)
end
function pace.RequestOutfits(ply)
if not ply:IsValid() then return end
if ply.pac_requested_outfits_time and ply.pac_requested_outfits_time > RealTime() then return end
ply.pac_requested_outfits_time = RealTime() + 30
ply.pac_requested_outfits = true
ply.pac_gonna_receive_outfits = true
pace.UpdateWearFilters()
timer.Simple(6, function()
if not IsValid(ply) then return end
ply.pac_gonna_receive_outfits = false
for id, outfits in pairs(pace.Parts) do
local owner = pac.ReverseHash(id, "Player")
if owner:IsValid() and owner:IsPlayer() and owner.GetPos and id ~= pac.Hash(ply) then
for key, outfit in pairs(outfits) do
if not outfit.wear_filter or table.HasValue(outfit.wear_filter, pac.Hash(ply)) then
pace.SubmitPart(outfit, ply)
end
end
end
end
end)
end
concommand.Add("pac_request_outfits", pace.RequestOutfits)