This commit is contained in:
lifestorm
2024-08-04 23:12:27 +03:00
parent 0e770b2b49
commit ba1fc01b16
7084 changed files with 2173495 additions and 14 deletions

116
lua/advdupe2/cl_file.lua Normal file
View File

@@ -0,0 +1,116 @@
--[[
| 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 function AdvDupe2_ReceiveFile(len, ply)
local AutoSave = net.ReadUInt(8) == 1
net.ReadStream(nil, function(data)
AdvDupe2.RemoveProgressBar()
if(!data)then
AdvDupe2.Notify("File was not saved!",NOTIFY_ERROR,5)
return
end
local path
if AutoSave then
if(LocalPlayer():GetInfo("advdupe2_auto_save_overwrite")~="0")then
path = AdvDupe2.GetFilename(AdvDupe2.AutoSavePath, true)
else
path = AdvDupe2.GetFilename(AdvDupe2.AutoSavePath)
end
else
path = AdvDupe2.GetFilename(AdvDupe2.SavePath)
end
local dupefile = file.Open(path, "wb", "DATA")
if(!dupefile)then
AdvDupe2.Notify("File was not saved!",NOTIFY_ERROR,5)
return
end
dupefile:Write(data)
dupefile:Close()
local errored = false
if(LocalPlayer():GetInfo("advdupe2_debug_openfile")=="1")then
if(not file.Exists(path, "DATA"))then AdvDupe2.Notify("File does not exist", NOTIFY_ERROR) return end
local readFile = file.Open(path, "rb", "DATA")
if not readFile then AdvDupe2.Notify("File could not be read", NOTIFY_ERROR) return end
local readData = readFile:Read(readFile:Size())
readFile:Close()
local success,dupe,info,moreinfo = AdvDupe2.Decode(readData)
if(success)then
AdvDupe2.Notify("DEBUG CHECK: File successfully opens. No EOF errors.")
else
AdvDupe2.Notify("DEBUG CHECK: " .. dupe, NOTIFY_ERROR)
errored = true
end
end
local filename = string.StripExtension(string.GetFileFromFilename( path ))
if(AutoSave)then
if(IsValid(AdvDupe2.FileBrowser.AutoSaveNode))then
local add = true
for i=1, #AdvDupe2.FileBrowser.AutoSaveNode.Files do
if(filename==AdvDupe2.FileBrowser.AutoSaveNode.Files[i].Label:GetText())then
add=false
break
end
end
if(add)then
AdvDupe2.FileBrowser.AutoSaveNode:AddFile(filename)
AdvDupe2.FileBrowser.Browser.pnlCanvas:Sort(AdvDupe2.FileBrowser.AutoSaveNode)
end
end
else
AdvDupe2.FileBrowser.Browser.pnlCanvas.ActionNode:AddFile(filename)
AdvDupe2.FileBrowser.Browser.pnlCanvas:Sort(AdvDupe2.FileBrowser.Browser.pnlCanvas.ActionNode)
end
if(!errored)then
AdvDupe2.Notify("File successfully saved!",NOTIFY_GENERIC, 5)
end
end)
end
net.Receive("AdvDupe2_ReceiveFile", AdvDupe2_ReceiveFile)
local uploading = nil
function AdvDupe2.UploadFile(ReadPath, ReadArea)
if uploading then AdvDupe2.Notify("Already opening file, please wait.", NOTIFY_ERROR) return end
if(ReadArea==0)then
ReadPath = AdvDupe2.DataFolder.."/"..ReadPath..".txt"
elseif(ReadArea==1)then
ReadPath = AdvDupe2.DataFolder.."/-Public-/"..ReadPath..".txt"
else
ReadPath = "adv_duplicator/"..ReadPath..".txt"
end
if(not file.Exists(ReadPath, "DATA"))then AdvDupe2.Notify("File does not exist", NOTIFY_ERROR) return end
local read = file.Read(ReadPath)
if not read then AdvDupe2.Notify("File could not be read", NOTIFY_ERROR) return end
local name = string.Explode("/", ReadPath)
name = name[#name]
name = string.sub(name, 1, #name-4)
local success, dupe, info, moreinfo = AdvDupe2.Decode(read)
if(success)then
net.Start("AdvDupe2_ReceiveFile")
net.WriteString(name)
uploading = net.WriteStream(read, function()
uploading = nil
AdvDupe2.File = nil
AdvDupe2.RemoveProgressBar()
end)
net.SendToServer()
AdvDupe2.LoadGhosts(dupe, info, moreinfo, name)
else
AdvDupe2.Notify("File could not be decoded. ("..dupe..") Upload Canceled.", NOTIFY_ERROR)
end
end

353
lua/advdupe2/cl_ghost.lua Normal file
View File

@@ -0,0 +1,353 @@
--[[
| 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/
--]]
function AdvDupe2.LoadGhosts(dupe, info, moreinfo, name, preview)
AdvDupe2.RemoveGhosts()
AdvDupe2.Ghosting = true
AdvDupe2.GhostToSpawn = {}
local count = 0
local time, desc, date, creator
if(info.ad1) then
local z = dupe.HeadEnt.Z
local Pos, Ang
time = moreinfo.Time or ""
desc = info.Description or ""
date = info.Date or ""
creator = info.Creator or ""
AdvDupe2.HeadEnt = dupe.HeadEnt.Index
AdvDupe2.HeadPos = dupe.HeadEnt.Pos
AdvDupe2.HeadZPos = z
AdvDupe2.HeadPos.Z = AdvDupe2.HeadPos.Z + z
for k, v in pairs(dupe.Entities) do
if(v.SavedParentIdx) then
if(not v.BuildDupeInfo) then v.BuildDupeInfo = {} end
v.BuildDupeInfo.DupeParentID = v.SavedParentIdx
Pos = v.LocalPos
Ang = v.LocalAngle
else
Pos, Ang = nil, nil
end
for i, p in pairs(v.PhysicsObjects) do
p.Pos = Pos or p.LocalPos
p.Pos.Z = p.Pos.Z - z
p.Angle = Ang or p.LocalAngle
p.LocalPos = nil
p.LocalAngle = nil
end
v.LocalPos = nil
v.LocalAngle = nil
AdvDupe2.GhostToSpawn[count] =
{
Model = v.Model,
PhysicsObjects = v.PhysicsObjects
}
if(AdvDupe2.HeadEnt == k) then
AdvDupe2.HeadEnt = count
end
count = count + 1
end
AdvDupe2.HeadOffset = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Pos
AdvDupe2.HeadAngle = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Angle
else
time = info.time or ""
desc = dupe.Description or ""
date = info.date or ""
creator = info.name or ""
AdvDupe2.HeadEnt = dupe.HeadEnt.Index
AdvDupe2.HeadZPos = dupe.HeadEnt.Z
AdvDupe2.HeadPos = dupe.HeadEnt.Pos
AdvDupe2.HeadOffset = dupe.Entities[AdvDupe2.HeadEnt].PhysicsObjects[0].Pos
AdvDupe2.HeadAngle = dupe.Entities[AdvDupe2.HeadEnt].PhysicsObjects[0].Angle
for k, v in pairs(dupe.Entities) do
AdvDupe2.GhostToSpawn[count] =
{
Model = v.Model,
PhysicsObjects = v.PhysicsObjects
}
if(AdvDupe2.HeadEnt == k) then
AdvDupe2.HeadEnt = count
end
count = count + 1
end
end
if(not preview) then
AdvDupe2.Info.File:SetText("File: "..name)
AdvDupe2.Info.Creator:SetText("Creator: "..creator)
AdvDupe2.Info.Date:SetText("Date: "..date)
AdvDupe2.Info.Time:SetText("Time: "..time)
AdvDupe2.Info.Size:SetText("Size: "..string.NiceSize(tonumber(info.size) or 0))
AdvDupe2.Info.Desc:SetText("Desc: "..(desc or ""))
AdvDupe2.Info.Entities:SetText("Entities: "..table.Count(dupe.Entities))
AdvDupe2.Info.Constraints:SetText("Constraints: "..table.Count(dupe.Constraints))
end
AdvDupe2.StartGhosting()
AdvDupe2.Preview = preview
end
function AdvDupe2.RemoveGhosts()
if(AdvDupe2.Ghosting) then
hook.Remove("Tick", "AdvDupe2_SpawnGhosts")
AdvDupe2.Ghosting = false
if(not AdvDupe2.BusyBar) then
AdvDupe2.RemoveProgressBar()
end
end
if(AdvDupe2.GhostEntities) then
for k, v in pairs(AdvDupe2.GhostEntities) do
if(IsValid(v))then
v:Remove()
end
end
end
if(IsValid(AdvDupe2.HeadGhost))then
AdvDupe2.HeadGhost:Remove()
end
AdvDupe2.CurrentGhost = 1
AdvDupe2.HeadGhost = nil
AdvDupe2.GhostEntities = nil
AdvDupe2.Preview = false
end
--Creates a ghost from the given entity's table
local function MakeGhostsFromTable(EntTable)
if(not EntTable) then return end
if(not EntTable.Model or EntTable.Model:sub(-4,-1) ~= ".mdl") then
EntTable.Model = "models/error.mdl"
end
local GhostEntity = ClientsideModel(EntTable.Model, RENDERGROUP_TRANSLUCENT)
-- If there are too many entities we might not spawn..
if not IsValid(GhostEntity) then
AdvDupe2.RemoveGhosts()
AdvDupe2.Notify("Too many entities to spawn ghosts!", NOTIFY_ERROR)
return
end
GhostEntity:SetRenderMode( RENDERMODE_TRANSALPHA ) --Was broken, making ghosts invisible
GhostEntity:SetColor( Color(255, 255, 255, 150) )
GhostEntity.Phys = EntTable.PhysicsObjects[0]
if util.IsValidRagdoll(EntTable.Model) then
local ref, parents, angs = {}, {}, {}
GhostEntity:SetupBones()
for k, v in pairs(EntTable.PhysicsObjects) do
local bone = GhostEntity:TranslatePhysBoneToBone(k)
local bonp = GhostEntity:GetBoneParent(bone)
if bonp == -1 then
ref[bone] = GhostEntity:GetBoneMatrix(bone):GetInverseTR()
else
bonp = GhostEntity:TranslatePhysBoneToBone(GhostEntity:TranslateBoneToPhysBone(bonp))
parents[bone] = bonp
ref[bone] = GhostEntity:GetBoneMatrix(bone):GetInverseTR() * GhostEntity:GetBoneMatrix(bonp)
end
local m = Matrix() m:SetAngles(v.Angle)
angs[bone] = m
end
for bone, ang in pairs( angs ) do
if parents[bone] and angs[parents[bone]] then
local localrotation = angs[parents[bone]]:GetInverseTR() * ang
local m = ref[bone] * localrotation
GhostEntity:ManipulateBoneAngles(bone, m:GetAngles())
else
local pos = GhostEntity:GetBonePosition(bone)
GhostEntity:ManipulateBonePosition(bone, -pos)
GhostEntity:ManipulateBoneAngles(bone, ref[bone]:GetAngles())
end
end
end
return GhostEntity
end
local function SpawnGhosts()
if AdvDupe2.CurrentGhost == AdvDupe2.HeadEnt then AdvDupe2.CurrentGhost = AdvDupe2.CurrentGhost + 1 end
local g = AdvDupe2.GhostToSpawn[AdvDupe2.CurrentGhost]
if g and AdvDupe2.CurrentGhost / AdvDupe2.TotalGhosts * 100 <= GetConVar("advdupe2_limit_ghost"):GetFloat() then
AdvDupe2.GhostEntities[AdvDupe2.CurrentGhost] = MakeGhostsFromTable(g)
if(not AdvDupe2.BusyBar) then
AdvDupe2.ProgressBar.Percent = AdvDupe2.CurrentGhost / AdvDupe2.TotalGhosts * 100
end
AdvDupe2.CurrentGhost = AdvDupe2.CurrentGhost + 1
AdvDupe2.UpdateGhosts(true)
else
AdvDupe2.Ghosting = false
hook.Remove("Tick", "AdvDupe2_SpawnGhosts")
if(not AdvDupe2.BusyBar) then
AdvDupe2.RemoveProgressBar()
end
end
end
net.Receive("AdvDupe2_SendGhosts", function(len, ply, len2)
AdvDupe2.RemoveGhosts()
AdvDupe2.GhostToSpawn = {}
AdvDupe2.HeadEnt = net.ReadInt(16)
AdvDupe2.HeadZPos = net.ReadFloat()
AdvDupe2.HeadPos = net.ReadVector()
local cache = {}
for i = 1, net.ReadInt(16) do
cache[i] = net.ReadString()
end
for i = 1, net.ReadInt(16) do
AdvDupe2.GhostToSpawn[i] =
{
Model = cache[net.ReadInt(16)],
PhysicsObjects = {}
}
for k = 0, net.ReadInt(8) do
AdvDupe2.GhostToSpawn[i].PhysicsObjects[k] =
{
Angle = net.ReadAngle(),
Pos = net.ReadVector()
}
end
end
AdvDupe2.CurrentGhost = 1
AdvDupe2.GhostEntities = {}
AdvDupe2.HeadGhost = MakeGhostsFromTable(AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt])
AdvDupe2.HeadOffset = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Pos
AdvDupe2.HeadAngle = AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt].PhysicsObjects[0].Angle
AdvDupe2.GhostEntities[AdvDupe2.HeadEnt] = AdvDupe2.HeadGhost
AdvDupe2.TotalGhosts = #AdvDupe2.GhostToSpawn
if(AdvDupe2.TotalGhosts > 1) then
AdvDupe2.Ghosting = true
if(not AdvDupe2.BusyBar) then
AdvDupe2.InitProgressBar("Ghosting: ")
AdvDupe2.BusyBar = false
end
hook.Add("Tick", "AdvDupe2_SpawnGhosts", SpawnGhosts)
else
AdvDupe2.Ghosting = false
end
end)
net.Receive("AdvDupe2_AddGhost", function(len, ply, len2)
local ghost = {Model = net.ReadString(), PhysicsObjects = {}}
for k = 0, net.ReadInt(8) do
ghost.PhysicsObjects[k] = {Angle = net.ReadAngle(), Pos = net.ReadVector()}
end
AdvDupe2.GhostEntities[AdvDupe2.CurrentGhost] = MakeGhostsFromTable(ghost)
AdvDupe2.CurrentGhost = AdvDupe2.CurrentGhost + 1
end)
function AdvDupe2.StartGhosting()
AdvDupe2.RemoveGhosts()
if(not AdvDupe2.GhostToSpawn) then return end
AdvDupe2.CurrentGhost = 1
AdvDupe2.GhostEntities = {}
AdvDupe2.Ghosting = true
AdvDupe2.HeadGhost = MakeGhostsFromTable(AdvDupe2.GhostToSpawn[AdvDupe2.HeadEnt])
AdvDupe2.GhostEntities[AdvDupe2.HeadEnt] = AdvDupe2.HeadGhost
AdvDupe2.TotalGhosts = #AdvDupe2.GhostToSpawn
if AdvDupe2.TotalGhosts > 1 then
if not AdvDupe2.BusyBar then
AdvDupe2.InitProgressBar("Ghosting: ")
AdvDupe2.BusyBar = false
end
hook.Add("Tick", "AdvDupe2_SpawnGhosts", SpawnGhosts)
else
AdvDupe2.Ghosting = false
end
end
net.Receive("AdvDupe2_StartGhosting", function()
AdvDupe2.StartGhosting()
end)
net.Receive("AdvDupe2_RemoveGhosts", AdvDupe2.RemoveGhosts)
--Update the ghost's postion and angles based on where the player is looking and the offsets
local Lheadpos, Lheadang = Vector(), Angle()
function AdvDupe2.UpdateGhosts(force)
if not IsValid(AdvDupe2.HeadGhost) then
AdvDupe2.RemoveGhosts()
AdvDupe2.Notify("Invalid ghost parent!", NOTIFY_ERROR)
return
end
local trace = LocalPlayer():GetEyeTrace()
if (not trace.Hit) then return end
local originpos, originang, headpos, headang
local worigin = GetConVar("advdupe2_offset_world"):GetBool()
if(GetConVar("advdupe2_original_origin"):GetBool())then
originang = Angle()
originpos = Vector(AdvDupe2.HeadPos)
headpos = AdvDupe2.HeadPos + AdvDupe2.HeadOffset
headang = AdvDupe2.HeadAngle
else
local hangle = worigin and Angle(0,0,0) or AdvDupe2.HeadAngle
local pz = math.Clamp(AdvDupe2.HeadZPos + GetConVar("advdupe2_offset_z"):GetFloat() or 0, -16000, 16000)
local ap = math.Clamp(GetConVar("advdupe2_offset_pitch"):GetFloat() or 0, -180, 180)
local ay = math.Clamp(GetConVar("advdupe2_offset_yaw" ):GetFloat() or 0, -180, 180)
local ar = math.Clamp(GetConVar("advdupe2_offset_roll" ):GetFloat() or 0, -180, 180)
originang = Angle(ap, ay, ar)
originpos = Vector(trace.HitPos); originpos.z = originpos.z + pz
headpos, headang = LocalToWorld(AdvDupe2.HeadOffset, hangle, originpos, originang)
end
if math.abs(Lheadpos.x - headpos.x) > 0.01 or
math.abs(Lheadpos.y - headpos.y) > 0.01 or
math.abs(Lheadpos.z - headpos.z) > 0.01 or
math.abs(Lheadang.p - headang.p) > 0.01 or
math.abs(Lheadang.y - headang.y) > 0.01 or
math.abs(Lheadang.r - headang.r) > 0.01 or force then
Lheadpos = headpos
Lheadang = headang
AdvDupe2.HeadGhost:SetPos(headpos)
AdvDupe2.HeadGhost:SetAngles(headang)
for k, ghost in ipairs(AdvDupe2.GhostEntities) do
local phys = ghost.Phys
local pos, ang = LocalToWorld(phys.Pos, phys.Angle, originpos, originang)
ghost:SetPos(pos)
ghost:SetAngles(ang)
end
end
end

File diff suppressed because it is too large Load Diff

570
lua/advdupe2/sh_codec.lua Normal file
View File

@@ -0,0 +1,570 @@
--[[
| 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/
--]]
--[[
Title: Adv. Dupe 2 Codec
Desc: Dupe encoder/decoder.
Author: emspike
Version: 2.0
]]
local REVISION = 5
AdvDupe2.CodecRevision = REVISION
AdvDupe2.MaxDupeSize = 32e6 -- 32 MB
include( "sh_codec_legacy.lua" )
AddCSLuaFile( "sh_codec_legacy.lua" )
local pairs = pairs
local type = type
local error = error
local Vector = Vector
local Angle = Angle
local format = string.format
local char = string.char
local byte = string.byte
local sub = string.sub
local gsub = string.gsub
local find = string.find
local gmatch = string.gmatch
local match = string.match
local concat = table.concat
local compress = util.Compress
local decompress = util.Decompress
--[[
Name: GenerateDupeStamp
Desc: Generates an info table.
Params: <player> ply
Return: <table> stamp
]]
function AdvDupe2.GenerateDupeStamp(ply)
local stamp = {}
stamp.name = ply:GetName()
stamp.time = os.date("%I:%M %p")
stamp.date = os.date("%d %B %Y")
stamp.timezone = os.date("%z")
hook.Call("AdvDupe2_StampGenerated",GAMEMODE,stamp)
return stamp
end
local function makeInfo(tbl)
local info = ""
for k, v in pairs(tbl) do
info = concat{info,k,"\1",v,"\1"}
end
return info.."\2"
end
local AD2FF = "AD2F%s\n%s\n%s"
local tables, buff
local function noserializer() end
local enc = {}
for i = 1, 255 do enc[i] = noserializer end
local function isArray(tbl)
local ret = true
local m = 0
for k, v in pairs(tbl) do
m = m + 1
if k ~= m or enc[TypeID(v)] == noserializer then
ret = false
break
end
end
return ret
end
local function write(obj)
enc[TypeID(obj)](obj)
end
local len, tables, tablesLookup
enc[TYPE_TABLE] = function(obj) --table
if not tablesLookup[obj] then
tables = tables + 1
tablesLookup[obj] = tables
else
buff:WriteByte(247)
buff:WriteShort(tablesLookup[obj])
return
end
if isArray(obj) then
buff:WriteByte(254)
for i, v in pairs(obj) do
write(v)
end
else
buff:WriteByte(255)
for k, v in pairs(obj) do
if(enc[TypeID(k)] ~= noserializer and enc[TypeID(v)] ~= noserializer) then
write(k)
write(v)
end
end
end
buff:WriteByte(246)
end
enc[TYPE_BOOL] = function(obj) --boolean
buff:WriteByte(obj and 253 or 252)
end
enc[TYPE_NUMBER] = function(obj) --number
buff:WriteByte(251)
buff:WriteDouble(obj)
end
enc[TYPE_VECTOR] = function(obj) --vector
buff:WriteByte(250)
buff:WriteDouble(obj.x)
buff:WriteDouble(obj.y)
buff:WriteDouble(obj.z)
end
enc[TYPE_ANGLE] = function(obj) --angle
buff:WriteByte(249)
buff:WriteDouble(obj.p)
buff:WriteDouble(obj.y)
buff:WriteDouble(obj.r)
end
enc[TYPE_STRING] = function(obj) --string
len = #obj
if len < 246 then
buff:WriteByte(len)
buff:Write(obj)
else
buff:WriteByte(248)
buff:WriteULong(len)
buff:Write(obj)
end
end
local function error_nodeserializer()
buff:Seek(buff:Tell()-1)
error(format("Couldn't find deserializer for type {typeid:%d}!", buff:ReadByte()))
end
local reference = 0
local read4, read5
do --Version 4
local dec = {}
for i = 1, 255 do dec[i] = error_nodeserializer end
local function read()
local tt = buff:ReadByte()
if not tt then
error("Expected value, got EOF!")
end
if tt == 0 then
return nil
end
return dec[tt]()
end
read4 = read
dec[255] = function() --table
local t = {}
local k
reference = reference + 1
local ref = reference
repeat
k = read()
if k ~= nil then
t[k] = read()
end
until (k == nil)
tables[ref] = t
return t
end
dec[254] = function() --array
local t = {}
local k = 0
local v
reference = reference + 1
local ref = reference
repeat
k = k + 1
v = read()
if(v ~= nil) then
t[k] = v
end
until (v == nil)
tables[ref] = t
return t
end
dec[253] = function()
return true
end
dec[252] = function()
return false
end
dec[251] = function()
return buff:ReadDouble()
end
dec[250] = function()
return Vector(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
end
dec[249] = function()
return Angle(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
end
dec[248] = function() --null-terminated string
local start = buff:Tell()
local slen = 0
while buff:ReadByte() ~= 0 do
slen = slen + 1
end
buff:Seek(start)
local retv = buff:Read(slen)
if(not retv) then retv="" end
buff:ReadByte()
return retv
end
dec[247] = function() --table reference
reference = reference + 1
return tables[buff:ReadShort()]
end
for i = 1, 246 do dec[i] = function() return buff:Read(i) end end
end
do --Version 5
local dec = {}
for i = 1, 255 do dec[i] = error_nodeserializer end
local function read()
local tt = buff:ReadByte()
if not tt then
error("Expected value, got EOF!")
end
return dec[tt]()
end
read5 = read
dec[255] = function() --table
local t = {}
reference = reference + 1
tables[reference] = t
for k in read do
t[k] = read()
end
return t
end
dec[254] = function() --array
local t = {}
reference = reference + 1
tables[reference] = t
local k = 1
for v in read do
t[k] = v
k = k + 1
end
return t
end
dec[253] = function()
return true
end
dec[252] = function()
return false
end
dec[251] = function()
return buff:ReadDouble()
end
dec[250] = function()
return Vector(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
end
dec[249] = function()
return Angle(buff:ReadDouble(),buff:ReadDouble(),buff:ReadDouble())
end
dec[248] = function() -- Length>246 string
local slen = buff:ReadULong()
local retv = buff:Read(slen)
if(not retv) then retv = "" end
return retv
end
dec[247] = function() --table reference
return tables[buff:ReadShort()]
end
dec[246] = function() --nil
return
end
for i = 1, 245 do dec[i] = function() return buff:Read(i) end end
dec[0] = function() return "" end
end
local function serialize(tbl)
tables = 0
tablesLookup = {}
buff = file.Open("ad2temp.txt", "wb", "DATA")
if not buff then error("Failed to open file data/ad2temp.txt for writing!") end
write(tbl)
buff:Close()
buff = file.Open("ad2temp.txt","rb","DATA")
if not buff then error("Failed to open file data/ad2temp.txt for reading!") end
local ret = buff:Read(buff:Size())
buff:Close()
return ret
end
local function deserialize(str, read)
if(str == nil) then
error("File could not be decompressed!")
return {}
end
tables = {}
reference = 0
buff = file.Open("ad2temp.txt","wb","DATA")
if not buff then error("Failed to open file data/ad2temp.txt for writing!") end
buff:Write(str)
buff:Flush()
buff:Close()
buff = file.Open("ad2temp.txt","rb", "DATA")
if not buff then error("Failed to open file data/ad2temp.txt for reading!") end
local success, tbl = pcall(read)
buff:Close()
if success then
return tbl
else
error(tbl)
end
end
--[[
Name: Encode
Desc: Generates the string for a dupe file with the given data.
Params: <table> dupe, <table> info, <function> callback, <...> args
Return: runs callback(<string> encoded_dupe, <...> args)
]]
function AdvDupe2.Encode(dupe, info, callback, ...)
local encodedTable = compress(serialize(dupe))
info.check = "\r\n\t\n"
info.size = #encodedTable
callback(AD2FF:format(char(REVISION), makeInfo(info), encodedTable),...)
end
--seperates the header and body and converts the header to a table
local function getInfo(str)
local last = str:find("\2")
if not last then
error("Attempt to read AD2 file with malformed info block!")
end
local info = {}
local ss = str:sub(1, last - 1)
for k, v in ss:gmatch("(.-)\1(.-)\1") do
info[k] = v
end
if info.check ~= "\r\n\t\n" then
if info.check == "\10\9\10" then
error("Detected AD2 file corrupted in file transfer (newlines homogenized)(when using FTP, transfer AD2 files in image/binary mode, not ASCII/text mode)!")
else
error("Attempt to read AD2 file with malformed info block!")
end
end
return info, str:sub(last+2)
end
--decoders for individual versions go here
local versions = {}
versions[1] = AdvDupe2.LegacyDecoders[1]
versions[2] = AdvDupe2.LegacyDecoders[2]
versions[3] = function(encodedDupe)
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n", "\n")
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
return versions[4](encodedDupe)
end
versions[4] = function(encodedDupe)
local info, dupestring = getInfo(encodedDupe:sub(7))
return deserialize(decompress(dupestring, AdvDupe2.MaxDupeSize), read4), info
end
versions[5] = function(encodedDupe)
local info, dupestring = getInfo(encodedDupe:sub(7))
return deserialize(decompress(dupestring, AdvDupe2.MaxDupeSize), read5), info
end
function AdvDupe2.CheckValidDupe(dupe, info)
if not dupe.HeadEnt then return false, "Missing HeadEnt table" end
if not dupe.Entities then return false, "Missing Entities table" end
if not dupe.Constraints then return false, "Missing Constraints table" end
if not dupe.HeadEnt.Z then return false, "Missing HeadEnt.Z" end
if not dupe.HeadEnt.Pos then return false, "Missing HeadEnt.Pos" end
if not dupe.HeadEnt.Index then return false, "Missing HeadEnt.Index" end
if not dupe.Entities[dupe.HeadEnt.Index] then return false, "Missing HeadEnt index ["..dupe.HeadEnt.Index.."] from Entities table" end
for key, data in pairs(dupe.Entities) do
if not data.PhysicsObjects then return false, "Missing PhysicsObject table from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
if not data.PhysicsObjects[0] then return false, "Missing PhysicsObject[0] table from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
if info.ad1 then -- Advanced Duplicator 1
if not data.PhysicsObjects[0].LocalPos then return false, "Missing PhysicsObject[0].LocalPos from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
if not data.PhysicsObjects[0].LocalAngle then return false, "Missing PhysicsObject[0].LocalAngle from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
else -- Advanced Duplicator 2
if not data.PhysicsObjects[0].Pos then return false, "Missing PhysicsObject[0].Pos from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
if not data.PhysicsObjects[0].Angle then return false, "Missing PhysicsObject[0].Angle from Entity ["..key.."]["..data.Class.."]["..data.Model.."]" end
end
end
return true, dupe
end
--[[
Name: Decode
Desc: Generates the table for a dupe from the given string. Inverse of Encode
Params: <string> encodedDupe, <function> callback, <...> args
Return: runs callback(<boolean> success, <table/string> tbl, <table> info)
]]
function AdvDupe2.Decode(encodedDupe)
local sig, rev = encodedDupe:match("^(....)(.)")
if not rev then
return false, "Malformed dupe (wtf <5 chars long)!"
end
rev = rev:byte()
if sig ~= "AD2F" then
if sig == "[Inf" then --legacy support, ENGAGE (AD1 dupe detected)
local success, tbl, info, moreinfo = pcall(AdvDupe2.LegacyDecoders[0], encodedDupe)
if success then
info.ad1 = true
info.size = #encodedDupe
info.revision = 0
local index = tonumber(moreinfo.Head) or (istable(tbl.Entities) and next(tbl.Entities))
if not index then return false, "Missing head index" end
local pos
if isstring(moreinfo.StartPos) then
local spx,spy,spz = moreinfo.StartPos:match("^(.-),(.-),(.+)$")
pos = Vector(tonumber(spx) or 0, tonumber(spy) or 0, tonumber(spz) or 0)
else
pos = Vector()
end
local z
if isstring(moreinfo.HoldPos) then
z = (tonumber(moreinfo.HoldPos:match("^.-,.-,(.+)$")) or 0)*-1
else
z = 0
end
tbl.HeadEnt = {
Index = index,
Pos = pos,
Z = z
}
else
ErrorNoHalt(tbl)
end
if success then
success, tbl = AdvDupe2.CheckValidDupe(tbl, info)
end
return success, tbl, info, moreinfo
else
return false, "Unknown duplication format!"
end
elseif rev > REVISION then
return false, format("Newer codec needed. (have rev %u, need rev %u) Update Advdupe2.",REVISION,rev)
elseif rev < 1 then
return false, format("Attempt to use an invalid format revision (rev %d)!", rev)
else
local success, tbl, info = pcall(versions[rev], encodedDupe)
if success then
success, tbl = AdvDupe2.CheckValidDupe(tbl, info)
end
if success then
info.revision = rev
end
return success, tbl, info
end
end
if CLIENT then
concommand.Add("advdupe2_to_json", function(_,_,arg)
if not arg[1] then print("Need AdvDupe2 file name argument!") return end
local readFileName = "advdupe2/"..arg[1]
local writeFileName = "advdupe2/"..string.StripExtension(arg[1])..".json"
local readFile = file.Open(readFileName, "rb", "DATA")
if not readFile then print("File could not be read or found! ("..readFileName..")") return end
local readData = readFile:Read(readFile:Size())
readFile:Close()
local ok, tbl = AdvDupe2.Decode(readData)
local writeFile = file.Open(writeFileName, "wb", "DATA")
if not writeFile then print("File could not be written! ("..writeFileName..")") return end
writeFile:Write(util.TableToJSON(tbl))
writeFile:Close()
print("File written! ("..writeFileName..")")
end)
concommand.Add("advdupe2_from_json", function(_,_,arg)
if not arg[1] then print("Need json file name argument!") return end
local readFileName = "advdupe2/"..arg[1]
local writeFileName = "advdupe2/"..string.StripExtension(arg[1])..".txt"
local readFile = file.Open(readFileName, "rb", "DATA")
if not readFile then print("File could not be read or found! ("..readFileName..")") return end
local readData = readFile:Read(readFile:Size())
readFile:Close()
AdvDupe2.Encode(util.JSONToTable(readData), {}, function(data)
local writeFile = file.Open(writeFileName, "wb", "DATA")
if not writeFile then print("File could not be written! ("..writeFileName..")") return end
writeFile:Write(data)
writeFile:Close()
print("File written! ("..writeFileName..")")
end)
end)
end

View File

@@ -0,0 +1,569 @@
--[[
| 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/
--]]
--[[
Title: Adv. Dupe 2 Codec Legacy Support
Desc: Facilitates opening of dupes from AD1 and earlier AD2 versions.
Author: emspike
Version: 2.0
]]
local pairs = pairs
local type = type
local tonumber = tonumber
local error = error
local Vector = Vector
local Angle = Angle
local format = string.format
local char = string.char
local byte = string.byte
local sub = string.sub
local gsub = string.gsub
local find = string.find
local gmatch = string.gmatch
local match = string.match
local concat = table.concat
--[[
Name: GenerateDupeStamp
Desc: Generates an info table.
Params: <player> ply
Return: <table> stamp
]]
function AdvDupe2.GenerateDupeStamp(ply)
local stamp = {}
stamp.name = ply:GetName()
stamp.time = os.date("%I:%M %p")
stamp.date = os.date("%d %B %Y")
stamp.timezone = os.date("%z")
hook.Call("AdvDupe2_StampGenerated",GAMEMODE,stamp)
return stamp
end
local AD2FF = "AD2F%s\n%s\n%s"
local decode_types_v1, decode_types_v2
local tables = 0
local str,pos
local a,b,c,m,n,w,tblref
local function read_v2()
local t = byte(str, pos+1)
if t then
local dt = decode_types_v2[t]
if dt then
pos = pos + 1
return dt()
else
error(format("encountered invalid data type (%u)\n",t))
end
else
error("expected value, got EOF\n")
end
end
decode_types_v2 = {
[1 ] = function()
error("expected value, got terminator\n")
end,
[2 ] = function() -- table
m = find(str, "\1", pos)
if m then
w = sub(str, pos+1, m-1)
pos = m
else
error("expected table identifier, got EOF\n")
end
local t = {}
tables[w] = t
while true do
if byte(str, pos+1) == 1 then
pos = pos + 1
return t
else
t[read_v2()] = read_v2()
end
end
end,
[3 ] = function() -- array
m = find(str, "\1", pos)
if m then
w = sub(str, pos+1, m-1)
pos = m
else
error("expected table identifier, got EOF\n")
end
local t, i = {}, 1
tables[w] = t
while true do
if byte(str,pos+1) == 1 then
pos = pos+1
return t
else
t[i] = read_v2()
i = i + 1
end
end
end,
[4 ] = function() -- false boolean
return false
end,
[5 ] = function() -- true boolean
return true
end,
[6 ] = function() -- number
m = find(str, "\1", pos)
if m then
a = tonumber(sub(str, pos+1, m-1)) or 0
pos = m
return a
else
error("expected number, got EOF\n")
end
end,
[7 ] = function() -- string
m = find(str,"\1",pos)
if m then
w = sub(str, pos+1, m-1)
pos = m
return w
else
error("expected string, got EOF\n")
end
end,
[8 ] = function() -- Vector
m,n = find(str,".-\1.-\1.-\1", pos)
if m then
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Vector(tonumber(a), tonumber(b), tonumber(c))
else
error("expected vector, got EOF\n")
end
end,
[9 ] = function() -- Angle
m,n = find(str, ".-\1.-\1.-\1", pos)
if m then
a,b,c = match(str, "^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Angle(tonumber(a), tonumber(b), tonumber(c))
else
error("expected angle, got EOF\n")
end
end,
[10 ] = function() -- Table Reference
m = find(str,"\1",pos)
if m then
w = sub(str,pos+1,m-1)
pos = m
else
error("expected table identifier, got EOF\n")
end
tblref = tables[w]
if tblref then
return tblref
else
error(format("table identifier %s points to nil\n", w))
end
end
}
local function read_v1()
local t = byte(str,pos+1)
if t then
local dt = decode_types_v1[t]
if dt then
pos = pos + 1
return dt()
else
error(format("encountered invalid data type (%u)\n",t))
end
else
error("expected value, got EOF\n")
end
end
decode_types_v1 = {
[1 ] = function()
error("expected value, got terminator\n")
end,
[2 ] = function() -- table
local t = {}
while true do
if byte(str,pos+1) == 1 then
pos = pos+1
return t
else
t[read_v1()] = read_v1()
end
end
end,
[3 ] = function() -- array
local t, i = {}, 1
while true do
if byte(str,pos+1) == 1 then
pos = pos+1
return t
else
t[i] = read_v1()
i = i + 1
end
end
end,
[4 ] = function() -- false boolean
return false
end,
[5 ] = function() -- true boolean
return true
end,
[6 ] = function() -- number
m = find(str,"\1",pos)
if m then
a = tonumber(sub(str,pos+1,m-1)) or 0
pos = m
return a
else
error("expected number, got EOF\n")
end
end,
[7 ] = function() -- string
m = find(str,"\1",pos)
if m then
w = sub(str,pos+1,m-1)
pos = m
return w
else
error("expected string, got EOF\n")
end
end,
[8 ] = function() -- Vector
m,n = find(str,".-\1.-\1.-\1",pos)
if m then
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Vector(tonumber(a), tonumber(b), tonumber(c))
else
error("expected vector, got EOF\n")
end
end,
[9 ] = function() -- Angle
m,n = find(str,".-\1.-\1.-\1",pos)
if m then
a,b,c = match(str,"^(.-)\1(.-)\1(.-)\1",pos+1)
pos = n
return Angle(tonumber(a), tonumber(b), tonumber(c))
else
error("expected angle, got EOF\n")
end
end
}
local function deserialize_v1(data)
str = data
pos = 0
tables = {}
return read_v1()
end
local function deserialize_v2(data)
str = data
pos = 0
tables = {}
return read_v2()
end
local function lzwDecode(encoded)
local dictionary_length = 256
local dictionary = {}
for i = 0, 255 do
dictionary[i] = char(i)
end
local pos = 2
local decompressed = {}
local decompressed_length = 1
local index = byte(encoded)
local word = dictionary[index]
decompressed[decompressed_length] = dictionary[index]
local entry
local encoded_length = #encoded
local firstbyte --of an index
while pos <= encoded_length do
firstbyte = byte(encoded,pos)
if firstbyte > 252 then --now we know it's a length indicator for a multibyte index
index = 0
firstbyte = 256 - firstbyte
--[[if pos+firstbyte > encoded_length then --will test for performance impact
error("expected index got EOF")
end]]
for i = pos+firstbyte, pos+1, -1 do
index = bit.bor(bit.lshift(index, 8), byte(encoded,i))
end
pos = pos + firstbyte + 1
else
index = firstbyte
pos = pos + 1
end
entry = dictionary[index] or (word..sub(word,1,1))
decompressed_length = decompressed_length + 1
decompressed[decompressed_length] = entry
dictionary[dictionary_length] = word..sub(entry,1,1)
dictionary_length = dictionary_length + 1
word = entry
end
return concat(decompressed)
end
--http://en.wikipedia.org/wiki/Huffman_coding#Decompression
local invcodes = {[2]={[0]="\254"},[5]={[22]="\1",[11]="\2"},[6]={[13]="\7",[35]="\6",[37]="\5",[58]="\3",[31]="\8",[9]="\13",[51]="\9",[55]="\10",[57]="\4",[59]="\15"},[7]={[1]="\14",[15]="\16",[87]="\31",[89]="\30",[62]="\26",[17]="\27",[97]="\19",[19]="\43",[10]="\12",[39]="\33",[41]="\24",[82]="\40",[3]="\32",[46]="\41",[47]="\38",[94]="\25",[65]="\23",[50]="\39",[26]="\11",[7]="\28",[33]="\18",[61]="\17",[25]="\42"},[8]={[111]="\101",[162]="\29",[2]="\34",[133]="\21",[142]="\36",[5]="\20",[21]="\37",[170]="\44",[130]="\22",[66]="\35"},[9]={[241]="\121",[361]="\104",[365]="\184",[125]="\227",[373]="\198",[253]="\117",[381]="\57",[270]="\49",[413]="\80",[290]="\47",[294]="\115",[38]="\112",[429]="\74",[433]="\0",[437]="\48",[158]="\183",[453]="\107",[166]="\111",[469]="\182",[477]="\241",[45]="\86",[489]="\69",[366]="\100",[497]="\61",[509]="\76",[49]="\53",[390]="\78",[279]="\196",[283]="\70",[414]="\98",[53]="\55",[422]="\109",[233]="\79",[349]="\89",[369]="\52",[14]="\105",[238]="\56",[319]="\162",[323]="\83",[327]="\63",[458]="\65",[335]="\231",[339]="\225",[337]="\114",[347]="\193",[493]="\139",[23]="\209",[359]="\250",[490]="\68",[42]="\54",[63]="\91",[286]="\97",[254]="\50",[510]="\108",[109]="\73",[67]="\103",[255]="\122",[69]="\170",[70]="\110",[407]="\176",[411]="\119",[110]="\120",[83]="\146",[149]="\163",[151]="\224",[85]="\51",[155]="\177",[79]="\251",[27]="\118",[447]="\159",[451]="\228",[455]="\175",[383]="\174",[463]="\243",[467]="\157",[173]="\210",[475]="\167",[177]="\84",[90]="\45",[487]="\206",[93]="\226",[495]="\245",[207]="\64",[127]="\147",[191]="\155",[511]="\153",[195]="\208",[197]="\85",[199]="\178",[181]="\82",[102]="\116",[103]="\71",[285]="\144",[105]="\102",[211]="\199",[213]="\123",[301]="\66",[305]="\46",[219]="\137",[81]="\67",[91]="\88",[157]="\130",[325]="\95",[29]="\58",[231]="\201",[117]="\99",[341]="\222",[237]="\77",[239]="\211",[71]="\223"},[10]={[710]="\149",[245]="\60",[742]="\172",[774]="\81",[134]="\151",[917]="\145",[274]="\216",[405]="\242",[146]="\194",[838]="\246",[298]="\248",[870]="\189",[1013]="\150",[894]="\190",[326]="\244",[330]="\166",[334]="\217",[465]="\179",[346]="\59",[354]="\180",[966]="\212",[974]="\143",[370]="\148",[998]="\154",[625]="\138",[382]="\161",[194]="\141",[198]="\126",[402]="\96",[206]="\185",[586]="\129",[721]="\187",[610]="\135",[618]="\181",[626]="\72",[226]="\62",[454]="\127",[658]="\113",[462]="\164",[234]="\214",[474]="\140",[242]="\106",[714]="\188",[730]="\87",[498]="\237",[746]="\125",[754]="\229",[786]="\128",[202]="\93",[18]="\255",[810]="\173",[846]="\131",[74]="\192",[842]="\142",[977]="\252",[858]="\235",[78]="\134",[874]="\234",[882]="\90",[646]="\92",[1006]="\160",[126]="\165",[914]="\221",[718]="\94",[738]="\238",[638]="\197",[482]="\230",[34]="\220",[962]="\133",[6]="\213",[706]="\219",[986]="\171",[994]="\233",[866]="\200",[1010]="\247",[98]="\169",[518]="\236",[494]="\207",[230]="\205",[542]="\191",[501]="\202",[530]="\203",[450]="\204",[209]="\158",[106]="\186",[590]="\136",[218]="\232",[733]="\124",[309]="\168",[221]="\152",[757]="\240",[113]="\215",[114]="\156",[362]="\239",[486]="\132",[358]="\249",[262]="\75",[30]="\218",[821]="\195",[546]="\253"}}
local function huffmanDecode(encoded)
local h1,h2,h3 = byte(encoded, 1, 3)
if (not h3) or (#encoded < 4) then
error("invalid input")
end
local original_length = bit.bor(bit.lshift(h3,16), bit.lshift(h2,8), h1)
local encoded_length = #encoded+1
local decoded = {}
local decoded_length = 0
local buffer = 0
local buffer_length = 0
local code
local code_len = 2
local temp
local pos = 4
while decoded_length < original_length do
if code_len <= buffer_length then
temp = invcodes[code_len]
code = bit.band(buffer, bit.lshift(1, code_len)-1)
if temp and temp[code] then --most of the time temp is nil
decoded_length = decoded_length + 1
decoded[decoded_length] = temp[code]
buffer = bit.rshift(buffer, code_len)
buffer_length = buffer_length - code_len
code_len = 2
else
code_len = code_len + 1
if code_len > 10 then
error("malformed code")
end
end
else
buffer = bit.bor(buffer, bit.lshift(byte(encoded, pos), buffer_length))
buffer_length = buffer_length + 8
pos = pos + 1
if pos > encoded_length then
error("malformed code")
end
end
end
return concat(decoded)
end
local function invEscapeSub(str)
local escseq,body = match(str,"^(.-)\n(.-)$")
if not escseq then error("invalid input") end
return gsub(body,escseq,"\26")
end
local dictionary
local subtables
local function deserializeChunk(chunk)
local ctype,val = byte(chunk),sub(chunk,3)
if ctype == 89 then return dictionary[ val ]
elseif ctype == 86 then
local a,b,c = match(val,"^(.-),(.-),(.+)$")
return Vector( tonumber(a), tonumber(b), tonumber(c) )
elseif ctype == 65 then
local a,b,c = match(val,"^(.-),(.-),(.+)$")
return Angle( tonumber(a), tonumber(b), tonumber(c) )
elseif ctype == 84 then
local t = {}
local tv = subtables[val]
if not tv then
tv = {}
subtables[ val ] = tv
end
tv[#tv+1] = t
return t
elseif ctype == 78 then return tonumber(val)
elseif ctype == 83 then return gsub(sub(val,2,-2),"<EFBFBD>",";")
elseif ctype == 66 then return val == "t"
elseif ctype == 80 then return 1
end
error(format("AD1 deserialization failed: invalid chunk (%u:%s)",ctype,val))
end
local function deserializeAD1(dupestring)
dupestring = dupestring:Replace("\r\n", "\n")
local header, extraHeader, dupeBlock, dictBlock = dupestring:match("%[Info%]\n(.+)\n%[More Information%]\n(.+)\n%[Save%]\n(.+)\n%[Dict%]\n(.+)")
if not header then
error("unknown duplication format")
end
local info = {}
for k,v in header:gmatch("([^\n:]+):([^\n]+)") do
info[k] = v
end
local moreinfo = {}
for k,v in extraHeader:gmatch("([^\n:]+):([^\n]+)") do
moreinfo[k] = v
end
dictionary = {}
for k,v in dictBlock:gmatch("(.-):\"(.-)\"\n") do
dictionary[k] = v
end
local dupe = {}
for key,block in dupeBlock:gmatch("([^\n:]+):([^\n]+)") do
local tables = {}
subtables = {}
local head
for id,chunk in block:gmatch('(%w+){(.-)}') do
--check if this table is the trunk
if byte(id) == 72 then
id = sub(id,2)
head = id
end
tables[id] = {}
for kv in gmatch(chunk,'[^;]+') do
local k,v = match(kv,'(.-)=(.+)')
if k then
k = deserializeChunk( k )
v = deserializeChunk( v )
tables[id][k] = v
else
v = deserializeChunk( kv )
local tid = tables[id]
tid[#tid+1]=v
end
end
end
--Restore table references
for id,tbls in pairs( subtables ) do
for _,tbl in pairs( tbls ) do
if not tables[id] then error("attempt to reference a nonexistent table") end
for k,v in pairs(tables[id]) do
tbl[k] = v
end
end
end
dupe[key] = tables[ head ]
end
return dupe, info, moreinfo
end
--seperates the header and body and converts the header to a table
local function getInfo(str)
local last = str:find("\2")
if not last then
error("attempt to read AD2 file with malformed info block error 1")
end
local info = {}
local ss = str:sub(1,last-1)
for k,v in ss:gmatch("(.-)\1(.-)\1") do
info[k] = v
end
if info.check ~= "\r\n\t\n" then
if info.check == "\10\9\10" then
error("detected AD2 file corrupted in file transfer (newlines homogenized)(when using FTP, transfer AD2 files in image/binary mode, not ASCII/text mode)")
else
error("attempt to read AD2 file with malformed info block error 2")
end
end
return info, str:sub(last+2)
end
--decoders for individual versions go here
local versions = {}
versions[2] = function(encodedDupe)
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n", "\n")
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
local info, dupestring = getInfo(encodedDupe:sub(7))
return deserialize_v2(
lzwDecode(
huffmanDecode(
invEscapeSub(dupestring)
)
)
), info
end
versions[1] = function(encodedDupe)
encodedDupe = encodedDupe:Replace("\r\r\n\t\r\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n\t\n", "\t\t\t\t")
encodedDupe = encodedDupe:Replace("\r\n", "\n")
encodedDupe = encodedDupe:Replace("\t\t\t\t", "\r\n\t\n")
local info, dupestring = getInfo(encodedDupe:sub(7))
return deserialize_v1(
lzwDecode(
huffmanDecode(
invEscapeSub(dupestring)
)
)
), info
end
versions[0] = deserializeAD1
AdvDupe2.LegacyDecoders = versions

File diff suppressed because it is too large Load Diff

153
lua/advdupe2/sv_file.lua Normal file
View File

@@ -0,0 +1,153 @@
--[[
| 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/
--]]
--Save a file to the client
local function SaveFile(ply, cmd, args)
if(not ply.AdvDupe2 or not ply.AdvDupe2.Entities or next(ply.AdvDupe2.Entities)==nil)then AdvDupe2.Notify(ply,"Duplicator is empty, nothing to save.", NOTIFY_ERROR) return end
if(not game.SinglePlayer() and CurTime()-(ply.AdvDupe2.FileMod or 0) < 0)then
AdvDupe2.Notify(ply,"Cannot save at the moment. Please Wait...", NOTIFY_ERROR)
return
end
if(ply.AdvDupe2.Pasting || ply.AdvDupe2.Downloading)then
AdvDupe2.Notify(ply,"Advanced Duplicator 2 is busy.",NOTIFY_ERROR)
return false
end
ply.AdvDupe2.FileMod = CurTime()+tonumber(GetConVarString("AdvDupe2_FileModificationDelay")+2)
local name = string.Explode("/", args[1])
ply.AdvDupe2.Name = name[#name]
net.Start("AdvDupe2_SetDupeInfo")
net.WriteString(ply.AdvDupe2.Name)
net.WriteString(ply:Nick())
net.WriteString(os.date("%d %B %Y"))
net.WriteString(os.date("%I:%M %p"))
net.WriteString("")
net.WriteString(args[2] or "")
net.WriteString(table.Count(ply.AdvDupe2.Entities))
net.WriteString(#ply.AdvDupe2.Constraints)
net.Send(ply)
local Tab = {Entities = ply.AdvDupe2.Entities, Constraints = ply.AdvDupe2.Constraints, HeadEnt = ply.AdvDupe2.HeadEnt, Description=args[2]}
AdvDupe2.Encode( Tab, AdvDupe2.GenerateDupeStamp(ply), function(data)
AdvDupe2.SendToClient(ply, data, 0)
end)
end
concommand.Add("AdvDupe2_SaveFile", SaveFile)
function AdvDupe2.SendToClient(ply, data, autosave)
if(not IsValid(ply))then return end
if #data > AdvDupe2.MaxDupeSize then
AdvDupe2.Notify(ply,"Copied duplicator filesize is too big!",NOTIFY_ERROR)
return
end
ply.AdvDupe2.Downloading = true
AdvDupe2.InitProgressBar(ply,"Saving:")
net.Start("AdvDupe2_ReceiveFile")
net.WriteUInt(autosave, 8)
net.WriteStream(data, function()
ply.AdvDupe2.Downloading = false
end)
net.Send(ply)
end
function AdvDupe2.LoadDupe(ply,success,dupe,info,moreinfo)
if(not IsValid(ply))then return end
if not success then
AdvDupe2.Notify(ply,"Could not open "..dupe,NOTIFY_ERROR)
return
end
if(not game.SinglePlayer())then
if(tonumber(GetConVarString("AdvDupe2_MaxConstraints"))~=0 and #dupe["Constraints"]>tonumber(GetConVarString("AdvDupe2_MaxConstraints")))then
AdvDupe2.Notify(ply,"Amount of constraints is greater than "..GetConVarString("AdvDupe2_MaxConstraints"),NOTIFY_ERROR)
return false
end
end
ply.AdvDupe2.Entities = {}
ply.AdvDupe2.Constraints = {}
ply.AdvDupe2.HeadEnt={}
ply.AdvDupe2.Revision = info.revision
if(info.ad1)then
ply.AdvDupe2.HeadEnt.Index = tonumber(moreinfo.Head)
local spx,spy,spz = moreinfo.StartPos:match("^(.-),(.-),(.+)$")
ply.AdvDupe2.HeadEnt.Pos = Vector(tonumber(spx) or 0, tonumber(spy) or 0, tonumber(spz) or 0)
local z = (tonumber(moreinfo.HoldPos:match("^.-,.-,(.+)$")) or 0)*-1
ply.AdvDupe2.HeadEnt.Z = z
ply.AdvDupe2.HeadEnt.Pos.Z = ply.AdvDupe2.HeadEnt.Pos.Z + z
local Pos
local Ang
for k,v in pairs(dupe["Entities"])do
Pos = nil
Ang = nil
if(v.SavedParentIdx)then
if(not v.BuildDupeInfo)then v.BuildDupeInfo = {} end
v.BuildDupeInfo.DupeParentID = v.SavedParentIdx
Pos = v.LocalPos*1
Ang = v.LocalAngle*1
end
for i,p in pairs(v.PhysicsObjects)do
p.Pos = Pos or (p.LocalPos*1)
p.Pos.Z = p.Pos.Z - z
p.Angle = Ang or (p.LocalAngle*1)
p.LocalPos = nil
p.LocalAngle = nil
p.Frozen = not p.Frozen -- adv dupe 2 does this wrong way
end
v.LocalPos = nil
v.LocalAngle = nil
end
ply.AdvDupe2.Entities = dupe["Entities"]
ply.AdvDupe2.Constraints = dupe["Constraints"]
else
ply.AdvDupe2.Entities = dupe["Entities"]
ply.AdvDupe2.Constraints = dupe["Constraints"]
ply.AdvDupe2.HeadEnt = dupe["HeadEnt"]
end
AdvDupe2.ResetOffsets(ply, true)
end
local function AdvDupe2_ReceiveFile(len, ply)
if not IsValid(ply) then return end
if not ply.AdvDupe2 then ply.AdvDupe2 = {} end
ply.AdvDupe2.Name = string.match(net.ReadString(), "([%w_ ]+)") or "Advanced Duplication"
local stream = net.ReadStream(ply, function(data)
if data then
AdvDupe2.LoadDupe(ply, AdvDupe2.Decode(data))
else
AdvDupe2.Notify(ply, "Duplicator Upload Failed!", NOTIFY_ERROR, 5)
end
ply.AdvDupe2.Uploading = false
end)
if ply.AdvDupe2.Uploading then
if stream then
stream:Remove()
end
AdvDupe2.Notify(ply, "Duplicator is Busy!", NOTIFY_ERROR, 5)
elseif stream then
ply.AdvDupe2.Uploading = true
AdvDupe2.InitProgressBar(ply, "Opening: ")
end
end
net.Receive("AdvDupe2_ReceiveFile", AdvDupe2_ReceiveFile)

80
lua/advdupe2/sv_ghost.lua Normal file
View File

@@ -0,0 +1,80 @@
--[[
| 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/
--]]
util.AddNetworkString("AdvDupe2_SendGhosts")
util.AddNetworkString("AdvDupe2_AddGhost")
function AdvDupe2.SendGhost(ply, AddOne)
net.Start("AdvDupe2_AddGhost")
net.WriteString(AddOne.Model)
net.WriteInt(#AddOne.PhysicsObjects, 8)
for i=0, #AddOne.PhysicsObjects do
net.WriteAngle(AddOne.PhysicsObjects[i].Angle)
net.WriteVector(AddOne.PhysicsObjects[i].Pos)
end
net.Send(ply)
end
function AdvDupe2.SendGhosts(ply)
if(not ply.AdvDupe2.Entities)then return end
local cache = {}
local temp = {}
local mdls = {}
local cnt = 1
local add = true
local head
for k,v in pairs(ply.AdvDupe2.Entities)do
temp[cnt] = v
for i=1,#cache do
if(cache[i]==v.Model)then
mdls[cnt] = i
add=false
break
end
end
if(add)then
mdls[cnt] = table.insert(cache, v.Model)
else
add = true
end
if(k==ply.AdvDupe2.HeadEnt.Index)then
head = cnt
end
cnt = cnt+1
end
if(!head)then
AdvDupe2.Notify(ply, "Invalid head entity for ghosts.", NOTIFY_ERROR);
return
end
net.Start("AdvDupe2_SendGhosts")
net.WriteInt(head, 16)
net.WriteFloat(ply.AdvDupe2.HeadEnt.Z)
net.WriteVector(ply.AdvDupe2.HeadEnt.Pos)
net.WriteInt(#cache, 16)
for i=1,#cache do
net.WriteString(cache[i])
end
net.WriteInt(cnt-1, 16)
for i=1, #temp do
net.WriteInt(mdls[i], 16)
net.WriteInt(#temp[i].PhysicsObjects, 8)
for k=0, #temp[i].PhysicsObjects do
net.WriteAngle(temp[i].PhysicsObjects[k].Angle)
net.WriteVector(temp[i].PhysicsObjects[k].Pos)
end
end
net.Send(ply)
end

114
lua/advdupe2/sv_misc.lua Normal file
View File

@@ -0,0 +1,114 @@
--[[
| 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/
--]]
--[[
Title: Miscellaneous
Desc: Contains miscellaneous (serverside) things AD2 needs to function that don't fit anywhere else.
Author: TB
Version: 1.0
]]
--[[
Name: SavePositions
Desc: Save the position of the entities to prevent sagging on dupe.
Params: <entity> Constraint
Returns: nil
]]
local function SavePositions( Constraint )
if IsValid(Constraint) then
if Constraint.BuildDupeInfo then return end
local BuildDupeInfo = {}
Constraint.BuildDupeInfo = BuildDupeInfo
local Ent1, Ent2
if IsValid(Constraint.Ent) then
if Constraint.Ent:GetPhysicsObjectCount()>1 then
BuildDupeInfo.Ent1Ang = Constraint.Ent:GetAngles()
else
BuildDupeInfo.Ent1Ang = Constraint.Ent:GetPhysicsObject():GetAngles()
end
end
if IsValid(Constraint.Ent1) then
if Constraint.Ent1:GetPhysicsObjectCount()>1 then
local Bone = Constraint.Ent1:GetPhysicsObjectNum(Constraint.Bone1)
BuildDupeInfo.Ent1Ang = Constraint.Ent1:GetAngles()
BuildDupeInfo.Ent1Pos = Constraint.Ent1:GetPos()
BuildDupeInfo.Bone1 = Constraint.Bone1
BuildDupeInfo.Bone1Pos = Bone:GetPos() - Constraint.Ent1:GetPos()
BuildDupeInfo.Bone1Angle = Bone:GetAngles()
else
local Bone = Constraint.Ent1:GetPhysicsObject()
BuildDupeInfo.Ent1Ang = Bone:GetAngles()
BuildDupeInfo.Ent1Pos = Bone:GetPos()
end
if IsValid(Constraint.Ent2) then
if Constraint.Ent2:GetPhysicsObjectCount()>1 then
local Bone = Constraint.Ent2:GetPhysicsObjectNum(Constraint.Bone2)
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Constraint.Ent2:GetPos()
BuildDupeInfo.Ent2Ang = Constraint.Ent2:GetAngles()
BuildDupeInfo.Bone2 = Constraint.Bone2
BuildDupeInfo.Bone2Pos = Bone:GetPos() - Constraint.Ent2:GetPos()
BuildDupeInfo.Bone2Angle = Bone:GetAngles()
else
local Bone = Constraint.Ent2:GetPhysicsObject()
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Bone:GetPos()
BuildDupeInfo.Ent2Ang = Bone:GetAngles()
end
elseif IsValid(Constraint.Ent4) then
if Constraint.Ent4:GetPhysicsObjectCount()>1 then
local Bone = Constraint.Ent4:GetPhysicsObjectNum(Constraint.Bone4)
BuildDupeInfo.Bone2 = Constraint.Bone4
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Constraint.Ent4:GetPos()
BuildDupeInfo.Ent2Ang = Constraint.Ent4:GetAngles()
BuildDupeInfo.Bone2Pos = Bone:GetPos() - Constraint.Ent4:GetPos()
BuildDupeInfo.Bone2Angle = Bone:GetAngles()
else
local Bone = Constraint.Ent4:GetPhysicsObject()
BuildDupeInfo.EntityPos = BuildDupeInfo.Ent1Pos - Bone:GetPos()
BuildDupeInfo.Ent2Ang = Bone:GetAngles()
end
end
end
end
end
local function monitorConstraint(name)
local oldFunc = constraint[name]
constraint[name] = function(...)
local Constraint, b, c = oldFunc(...)
if Constraint and Constraint:IsValid() then
SavePositions(Constraint)
end
return Constraint, b, c
end
end
monitorConstraint("AdvBallsocket")
monitorConstraint("Axis")
monitorConstraint("Ballsocket")
monitorConstraint("Elastic")
monitorConstraint("Hydraulic")
monitorConstraint("Keepupright")
monitorConstraint("Motor")
monitorConstraint("Muscle")
monitorConstraint("Pulley")
monitorConstraint("Rope")
monitorConstraint("Slider")
monitorConstraint("Weld")
monitorConstraint("Winch")