diff options
| author | Alexander Pickering <alexandermpickering@gmail.com> | 2017-02-18 21:55:55 -0500 |
|---|---|---|
| committer | Alexander Pickering <alexandermpickering@gmail.com> | 2017-02-18 21:55:55 -0500 |
| commit | a22cbeddc5f8fb61e87a30aa14ba354de5cf4431 (patch) | |
| tree | 297c1dbfb23185c5246e1dd7bdec52253a24ba60 /gamemode/core/pac | |
| parent | f4ee62bb0725a3ae94477b2818071f506e4dfd9f (diff) | |
| download | artery-a22cbeddc5f8fb61e87a30aa14ba354de5cf4431.tar.gz artery-a22cbeddc5f8fb61e87a30aa14ba354de5cf4431.tar.bz2 artery-a22cbeddc5f8fb61e87a30aa14ba354de5cf4431.zip | |
Updates
Diffstat (limited to 'gamemode/core/pac')
| -rw-r--r-- | gamemode/core/pac/cl_pac.lua | 90 | ||||
| -rw-r--r-- | gamemode/core/pac/sv_pac.lua | 167 |
2 files changed, 257 insertions, 0 deletions
diff --git a/gamemode/core/pac/cl_pac.lua b/gamemode/core/pac/cl_pac.lua new file mode 100644 index 0000000..9163d82 --- /dev/null +++ b/gamemode/core/pac/cl_pac.lua @@ -0,0 +1,90 @@ +--[[ + A lazy loader for pac3 costumes, used for weapons/armor/ect. +]] + +--As soon as the client connects, request the names of all the pac's on the server + +--If the player dosen't have PAC3 installed, then overwrite all pac-related network events to display an error. +if pac == nil then + local function no_pac_panic() + error("This gamemode require PAC3 to display armor/cloths, please be sure clients are downloading this addon from somewhere (perferably the workshop!)") + end + local networkmsgs = { + "artery_downloadpac", + "artery_applypac", + "artery_giveworldpacs", + "artery_removepac" + } + for k,v in pairs(networkmsgs) do + net.Receive(v,no_pac_panic) + end + no_pac_panic() + return --Don't execute this file! +end + +timer.Simple(0,function() + net.Start("artery_getworldpacs") + net.SendToServer() +end) + +file.CreateDir("artery/pacs") + +local function loadpac(ent,name,hash) + print("Told to apply pac", name, "to ent", ent) + local filepath = string.format("artery/pacs/%s.txt",name) + local filetext = file.Read(filepath,"DATA") + if ent.AttachPACPart == nil then + pac.SetupENT(ent) + assert(ent.AttachPACPart ~= nil,"Failed to set up ent",ent) + end + if filetext and (tonumber(util.CRC(filetext)) == hash) then + local pactbl = CompileString(string.format("return {%s}",filetext),name)() + ent:AttachPACPart(pactbl) + else--Cache is old, download the new pac! + net.Start("artery_requestpac") + net.WriteString(name) + net.SendToServer() + --1 second is probably long enough to download a pac, right? + timer.Simple(1,function() + loadpac(ent,name,hash) + end) + end +end + +local function unloadpac(ent,name,hash) + local filepath = string.format("artery/pacs/%s.txt",name) + local filetext = file.Read(filepath,"DATA") + local pactbl = CompileString(string.format("return {%s}",filetext),name)() + ent:RemovePACPart(pactbl) +end + +net.Receive("artery_downloadpac",function() + local pac_name = net.ReadString() + local pac_txt = net.ReadString() + local pac_hash = net.ReadUInt(32) + local filepath = string.format("artery/pacs/%s.txt",pac_name) + file.Write(filepath,pac_txt) +end) + +net.Receive("artery_applypac",function() + local pac_ent = net.ReadEntity() + local pac_name = net.ReadString() + local pac_hash = net.ReadUInt(32) + loadpac(pac_ent,pac_name,pac_hash) +end) + +net.Receive("artery_giveworldpacs",function() + local pactbl = net.ReadTable() + for ent,pacnames in pairs(pactbl) do + for name, hash in pairs(pacnames) do + loadpac(ent,name,hash) + end + end +end) + +net.Receive("artery_removepac",function() + local pac_ent = net.ReadEntity() + local pac_name = net.ReadString() + local pac_hash = net.ReadUInt(32) + unloadpac(pac_ent,pac_name,pac_hash) +end) 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 |
