diff options
Diffstat (limited to 'gamemode/core/pac/sv_pac.lua')
| -rw-r--r-- | gamemode/core/pac/sv_pac.lua | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/gamemode/core/pac/sv_pac.lua b/gamemode/core/pac/sv_pac.lua new file mode 100644 index 0000000..5a82607 --- /dev/null +++ b/gamemode/core/pac/sv_pac.lua @@ -0,0 +1,167 @@ +--[[ + The server side lazy loader for pac3 costumes + PAC3 outfits are not downloaded until they are needed, we can keep the inital download to join the server pretty small this way. + The downside is that the game might lag a little when someone wears something that is rare/new and everyone has to download it. + + Console Commands: + artery_reload_pacs + The server will cache PAC's so it dosen't need to read form disk every time. If you're live editing pac's on the server, and are wondering why your changes aren't showing, use this command. There is no command to clear client cache, because applying a pac also sends a hash of the pac to the client, and the client re-downloads if the hashes are different. + + + Functions: + entity:ApplyPac(String costume) :: nil + Find the file /data/pacs/<costume>.pac (on the server) and tell clients to apply it to ent. This will automatically download the pac to any clients nessessary. + + entity:RemovePac(String costume) :: nil + Remove the pac from the entity. + + entity:GetPacs() :: table + Retreives a list of the pacs an entity is wearing, as strings + + Network Strings: + "artery_getworldpacs", + "artery_giveworldpacs", + "artery_applypac", + "artery_removepac", + "artery_requestpac", + "artery_downloadpac" +]] + +local p3 = {} + +local nwstrings = { + "artery_getworldpacs", + "artery_giveworldpacs", + "artery_applypac", + "artery_removepac", + "artery_requestpac", + "artery_downloadpac" +} +for _,v in pairs(nwstrings) do + util.AddNetworkString(v) +end + +--If the server has pac installed, restrict player's from putting on their own pacs +hook.Add("PrePACConfigApply", "stoppacs", function(ply, outfit_data) + if not ply:IsAdmin() then + return false, "You don't have permission to do that!" + end +end) + +--When the server starts, get all the pacs and calculate their hashes so we can index them quickly without haveing to read from disk each time. +local pachashes = {} +local function loadhashes() + local files,_ = file.Find("artery/pacs/*","DATA") + for _,v in ipairs(files) do + local filepath = string.format("artery/pacs/%s",v) + local filetext = file.Read(filepath,"DATA") + local filehash = util.CRC(filetext) + pachashes[string.StripExtension(v)] = tonumber(filehash) + end +end +loadhashes() + +local appliedpacs = {} + +function p3.ApplyPac(what, name) + appliedpacs[what] = appliedpacs[what] or {} + appliedpacs[what][name] = pachashes[name] + net.Start("artery_applypac") + net.WriteEntity(what) + net.WriteString(name) + net.WriteUInt(pachashes[name],32) + net.Broadcast() +end + +function p3.RemovePac(what, name) + assert(appliedpacs[what][name],"Attempted to remove a pac that an entity is not wearing!") + appliedpacs[what][name] = nil + if #appliedpacs[what] == 0 then + appliedpacs[what] = nil + end + net.Start("artery_removepac") + net.WriteEntity(what) + net.WriteString(name) + net.WriteUInt(pachashes[name],32) + net.Broadcast() +end + +function p3.GetPacs(what) + return appliedpacs[what] or {} +end + +--If a player joins the server, tell them all about the pacs that are applied +net.Receive("artery_getworldpacs",function(ln,ply) + net.Start("artery_giveworldpacs") + net.WriteTable(appliedpacs) + net.Send(ply) +end) + +local max_pacs_in_cache = 10 +local pacs_in_cache = 0 +local pac_cache = {} + +--Load something from our cache +local function cacheload(key) + --If it's already in the cache, just update the time it was last used and return the pac. + if pac_cache[key] ~= nil then + pac_cache[key].time = CurTime() + return pac_cache[key].pac + end + + --Otherwise, we need to load it. + local pacpath = string.format("artery/pacs/%s.txt",key) + local pacfile = file.Read(pacpath,"LUA") + + --If we haven't reached max cache yet, just put it in + if pacs_in_cache < max_pacs_in_cache then + pac_cache[key] = { + ["pac"] = pacfile, + ["time"] = CurTime() + } + pacs_in_cache = pacs_in_cache + 1 + return pacfile + else + --We have max pac's, delete the oldest one, and put the new one in. + local oldest,oldstr = CurTime(),"" + for k,v in pairs(pac_cache) do + if v.time < oldest then + oldest = v.time + oldstr = k + end + end + pac_cache[oldstr] = nil + pac_cache[key] = { + ["pac"] = pacfile, + ["time"] = CurTime() + } + return pacfile + end +end + +net.Receive("artery_requestpac",function(ln,ply) + local pac_name = net.ReadString() + + --Double check that we're not executing a directory traversal attack https://www.owasp.org/index.php/Path_Traversal + if string.find(pac_name,"..",1,true) then + Report(string.format("Directory traversal attack attempted by %s:%s using artery_requestpac string %q",ply:Nick(),ply:SteamID64(),pac_name)) + end + + local pac_txt = cacheload(pac_name) + + net.Start("artery_downloadpac") + net.WriteString(pac_name) + net.WriteString(pac_txt) + net.WriteUInt(pachashes[pac_name],32) + net.Send(ply) + +end) + +--Does all the things needed to edit pac's live +concommand.Add("artery_reload_pacs",function() + pac_cache = {} + pacs_in_cache = 0 + loadhashes() +end) + +return p3 |
