mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
Upload
This commit is contained in:
116
lua/advdupe2/cl_file.lua
Normal file
116
lua/advdupe2/cl_file.lua
Normal 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
353
lua/advdupe2/cl_ghost.lua
Normal 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
|
||||
1415
lua/advdupe2/file_browser.lua
Normal file
1415
lua/advdupe2/file_browser.lua
Normal file
File diff suppressed because it is too large
Load Diff
570
lua/advdupe2/sh_codec.lua
Normal file
570
lua/advdupe2/sh_codec.lua
Normal 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
|
||||
|
||||
|
||||
569
lua/advdupe2/sh_codec_legacy.lua
Normal file
569
lua/advdupe2/sh_codec_legacy.lua
Normal 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
|
||||
1625
lua/advdupe2/sv_clipboard.lua
Normal file
1625
lua/advdupe2/sv_clipboard.lua
Normal file
File diff suppressed because it is too large
Load Diff
153
lua/advdupe2/sv_file.lua
Normal file
153
lua/advdupe2/sv_file.lua
Normal 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
80
lua/advdupe2/sv_ghost.lua
Normal 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
114
lua/advdupe2/sv_misc.lua
Normal 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")
|
||||
Reference in New Issue
Block a user