--[[
	Title: Adv. Duplicator 2 Module
	Desc: Provides advanced duplication functionality for the Adv. Dupe 2 tool.
	Author: TB
	Version: 1.0
]]

require( "duplicator" )

AdvDupe2.duplicator = {}

AdvDupe2.JobManager = {}
AdvDupe2.JobManager.PastingHook = false
AdvDupe2.JobManager.Queue = {}

local debugConvar = GetConVar("AdvDupe2_DebugInfo")

local gtSetupTable = {
	SERIAL = {
		[TYPE_BOOL]   = true,
		[TYPE_ANGLE]  = true,
		[TYPE_TABLE]  = true,
		[TYPE_NUMBER] = true,
		[TYPE_VECTOR] = true,
		[TYPE_STRING] = true
	},
	CONSTRAINT = {
		Weld       = true,
		Axis       = true,
		Rope       = true,
		Motor      = true,
		Winch      = true,
		Muscle     = true,
		Pulley     = true,
		Slider     = true,
		Elastic    = true,
		Hydraulic  = true,
		Ballsocket = true
	},
	COMPARE = {
		V1 = Vector(1, 1, 1),
		A0 = Angle (0, 0, 0),
		V0 = Vector(0, 0, 0)
	},
	POS = {
		pos      = true,
		Pos      = true,
		position = true,
		Position = true
	},
	ANG = {
		ang   = true,
		Ang   = true,
		angle = true,
		Angle = true
	},
	MODEL = {
		model = true,
		Model = true
	},
	PLAYER = {
		pl  = true,
		ply = true
	},
	ENT1 = {
		Ent  = true,
		Ent1 = true,
	},
	TVEHICLE = {
		VehicleTable = true
	},
	SPECIAL = {
		Data = true
	}
}

local function CopyClassArgTable(tab)
	local done = {}
	local function recursiveCopy(oldtable)
		local newtable = {}
		done[oldtable] = newtable
		for k, v in pairs(oldtable) do
			local varType = TypeID(v)
			if gtSetupTable.SERIAL[varType] then
				if varType == TYPE_TABLE then
					if done[v] then
						newtable[k] = done[v]
					else
						newtable[k] = recursiveCopy(v)
					end
				else
					newtable[k] = v
				end
			else
				if debugConvar:GetBool() then
					print("[AdvDupe2] ClassArg table with key \"" .. tostring(k) .. "\" has unsupported value of type \"".. type(v) .."\"!")
				end
			end
		end
		return newtable
	end
	return recursiveCopy(tab)
end

--[[
	Name: CopyEntTable
	Desc: Returns a copy of the passed entity's table
	Params: <entity> Ent
	Returns: <table> enttable
]]

--[[---------------------------------------------------------
	Returns a copy of the passed entity's table
---------------------------------------------------------]]

function AdvDupe2.duplicator.IsCopyable(ent)
	local class = ent:GetClass()
	if not duplicator.IsAllowed(class) or ent.DoNotDuplicate then return false end

	if not ent:GetPhysicsObject():IsValid() then
		-- Support for rare cases when an entity does not have a physical object but still can be copied
		if scripted_ents.GetMember(class, "Spawnable") == false and not duplicator.FindEntityClass(class) then return false end
		if ent:IsWeapon() and ent:GetOwner():IsValid() then return false end
	end

	return true
end

local function CopyEntTable(Ent, Offset)
	-- Filter duplicator blocked entities out.
	if not AdvDupe2.duplicator.IsCopyable(Ent) then return nil end

	local Tab = {}

	if Ent.PreEntityCopy then
		local status, valid = pcall(Ent.PreEntityCopy, Ent)
		if (not status) then print("AD2 PreEntityCopy Error: " .. tostring(valid)) end
	end

	local EntityClass = duplicator.FindEntityClass(Ent:GetClass())

	local EntTable = Ent:GetTable()

	if EntityClass then
		for iNumber, Key in pairs(EntityClass.Args) do
			if gtSetupTable.SPECIAL[Key] then
				Tab = CopyClassArgTable(EntTable)
			end
			-- Ignore keys from old system
			if (not gtSetupTable.POS[Key] and
					not gtSetupTable.ANG[Key] and
					not gtSetupTable.MODEL[Key]) then
				local varType = TypeID(EntTable[Key])
				if gtSetupTable.SERIAL[varType] then
					if varType == TYPE_TABLE then
						Tab[Key] = CopyClassArgTable(EntTable[Key])
					else
						Tab[Key] = EntTable[Key]
					end
				elseif varType ~= TYPE_NIL and debugConvar:GetBool() then
					print("[AdvDupe2] Entity ClassArg \"" .. Key .. "\" of type \"" .. Ent:GetClass() .. "\" has unsupported value of type \"" .. type(EntTable[Key]) .. "\"!\n")
				end
			end
		end
	end

	Tab.BoneMods = table.Copy(Ent.BoneMods)
	if(Ent.EntityMods)then
		Tab.EntityMods = table.Copy(Ent.EntityMods)
	end

	if Ent.PostEntityCopy then
		local status, valid = pcall(Ent.PostEntityCopy, Ent)
		if(not status)then
			print("AD2 PostEntityCopy Error: "..tostring(valid))
		end
	end

	Tab.Pos   = Ent:GetPos()
	Tab.Class = Ent:GetClass()
	Tab.Model = Ent:GetModel()
	Tab.Skin  = Ent:GetSkin()
	Tab.CollisionGroup = Ent:GetCollisionGroup()
	Tab.ModelScale = Ent:GetModelScale()

	if (Tab.Skin == 0) then Tab.Skin = nil end
	if (Tab.ModelScale == 1) then Tab.ModelScale = nil end

	if(Tab.Class == "gmod_cameraprop")then
		Tab.key = Ent:GetNetworkedInt("key")
	end

	-- Allow the entity to override the class
	-- This is a hack for the jeep, since it's real class is different from the one it reports as
	-- (It reports a different class to avoid compatibility problems)
	if Ent.ClassOverride then Tab.Class = Ent.ClassOverride end

	Tab.PhysicsObjects = {}

	-- Physics Objects
	local PhysObj
	for Bone = 0, Ent:GetPhysicsObjectCount() - 1 do
		PhysObj = Ent:GetPhysicsObjectNum(Bone)
		if IsValid(PhysObj) then
			Tab.PhysicsObjects[Bone] = Tab.PhysicsObjects[Bone] or {}
			if (PhysObj:IsMoveable()) then Tab.PhysicsObjects[Bone].Frozen = true end
			PhysObj:EnableMotion(false)
			Tab.PhysicsObjects[Bone].Pos = PhysObj:GetPos() - Tab.Pos
			Tab.PhysicsObjects[Bone].Angle = PhysObj:GetAngles()
		end
	end

	if not Tab.PhysicsObjects[0] then Tab.PhysicsObjects[0] = {Angle = Ent:GetAngles()} end
	Tab.PhysicsObjects[0].Pos = Tab.Pos - Offset

	Tab.Pos = nil
	if (Tab.Class ~= "prop_physics") then
		if (not Tab.BuildDupeInfo) then Tab.BuildDupeInfo = {} end
		Tab.BuildDupeInfo.IsNPC = Ent:IsNPC()
		Tab.BuildDupeInfo.IsVehicle = Ent:IsVehicle()
	end
	if (IsValid(Ent:GetParent())) then
		if (not Tab.BuildDupeInfo) then Tab.BuildDupeInfo = {} end
		Tab.PhysicsObjects[0].Angle = Ent:GetAngles()
		Tab.BuildDupeInfo.DupeParentID = Ent:GetParent():EntIndex()
	end

	-- Flexes
	local FlexNum = Ent:GetFlexNum()
	Tab.Flex = Tab.Flex or {}
	local weight
	local flexes
	for i = 0, FlexNum do
		weight = Ent:GetFlexWeight(i)
		if (weight ~= 0) then
			Tab.Flex[i] = weight
			flexes = true
		end
	end
	if (flexes or Ent:GetFlexScale() ~= 1) then
		Tab.FlexScale = Ent:GetFlexScale()
	else
		Tab.Flex = nil
	end

	-- Body Groups
	Tab.BodyG = {}
	for k, v in pairs(Ent:GetBodyGroups()) do
		if ( Ent:GetBodygroup( v.id ) > 0 ) then
			Tab.BodyG[ v.id ] = Ent:GetBodygroup( v.id )
		end
	end

	if(next(Tab.BodyG) == nil)then
		Tab.BodyG = nil
	end

	-- Bone Manipulator
	if (Ent:HasBoneManipulations()) then
		Tab.BoneManip = {}
		local c = gtSetupTable.COMPARE
		for i = 0, Ent:GetBoneCount() do
			local s = Ent:GetManipulateBoneScale(i)
			s = ((s ~= c.V1) and s or nil)
			local a = Ent:GetManipulateBoneAngles(i)
			a = ((a ~= c.A0) and a or nil)
			local p = Ent:GetManipulateBonePosition(i)
			p = ((p ~= c.V0) and p or nil)
			-- Avoid making a vector just to compare it
			if (s or a or p) then
				Tab.BoneManip[i] = {s = s, a = a, p = p}
			end
		end
	end

	if Ent.GetNetworkVars then Tab.DT = Ent:GetNetworkVars() end

	-- Make this function on your SENT if you want to modify the
	-- returned table specifically for your entity.
	if Ent.OnEntityCopyTableFinish then
		local status, valid = pcall(Ent.OnEntityCopyTableFinish, Ent, Tab)
		if (not status) then
			print("AD2 OnEntityCopyTableFinish Error: " .. tostring(valid))
		end
	end

	return Tab

end

--[[
	Name: CopyConstraintTable
	Desc: Create a table for constraints
	Params: <table> Constraints
	Returns: <table> Constraints, <table> Entities
]]
local function CopyConstraintTable(Const, Offset)
	if (Const == nil) then return nil, {} end

	-- Filter duplicator blocked constraints out.
	if Const.DoNotDuplicate then return nil, {} end

	local Type = duplicator.ConstraintType[Const.Type]
	if (not Type) then return nil, {} end
	local Constraint = {}
	local Entities = {}

	Const.Constraint = nil
	Const.OnDieFunctions = nil
	Constraint.Entity = {}
	for k, key in pairs(Type.Args) do
		if (key ~= "pl" and not string.find(key, "Ent") and not string.find(key, "Bone")) then
			Constraint[key] = Const[key]
		end
	end

	if ((Const["Ent"] and Const["Ent"]:IsWorld()) or IsValid(Const["Ent"])) then
		Constraint.Entity[1] = {}
		Constraint.Entity[1].Index = Const["Ent"]:EntIndex()
		if (not Const["Ent"]:IsWorld()) then table.insert(Entities, Const["Ent"]) end
		Constraint.Type = Const.Type
		if (Const.BuildDupeInfo) then Constraint.BuildDupeInfo = table.Copy(Const.BuildDupeInfo) end
	else
		for i = 1, 4 do
			local ent  = "Ent"  .. i
			local lpos = "LPos" .. i
			local wpos = "WPos" .. i

			if ((Const[ent] and Const[ent]:IsWorld()) or IsValid(Const[ent])) then
				Constraint.Entity[i] = {}
				Constraint.Entity[i].Index  = Const[ent]:EntIndex()
				Constraint.Entity[i].Bone   = Const["Bone"   .. i]
				Constraint.Entity[i].Length = Const["Length" .. i]
				Constraint.Entity[i].World  = Const["World"  .. i]

				if Const[ent]:IsWorld() then
					Constraint.Entity[i].World = true
					if (Const[lpos]) then
						if (i ~= 4 and i ~= 2) then
							if (Const["Ent2"]) then
								Constraint.Entity[i].LPos = Const[lpos] - Const["Ent2"]:GetPos()
								Constraint[lpos]   = Const[lpos] - Const["Ent2"]:GetPos()
							elseif (Const["Ent4"]) then
								Constraint.Entity[i].LPos = Const[lpos] - Const["Ent4"]:GetPos()
								Constraint[lpos]   = Const[lpos] - Const["Ent4"]:GetPos()
							end
						elseif (Const["Ent1"]) then
							Constraint.Entity[i].LPos = Const[lpos] - Const["Ent1"]:GetPos()
							Constraint[lpos]   = Const[lpos] - Const["Ent1"]:GetPos()
						end
					else
						Constraint.Entity[i].LPos = Offset
						Constraint[lpos]   = Offset
					end
				else
					Constraint.Entity[i].LPos = Const[lpos]
					Constraint.Entity[i].WPos = Const[wpos]
				end

				if (not Const[ent]:IsWorld()) then table.insert(Entities, Const[ent]) end
			end

			if (Const[wpos]) then
				if (not Const["Ent1"]:IsWorld()) then
					Constraint[wpos] = Const[wpos] - Const["Ent1"]:GetPos()
				else
					Constraint[wpos] = Const[wpos] - Const["Ent4"]:GetPos()
				end
			end
		end

		Constraint.Type = Const.Type
		if (Const.BuildDupeInfo) then
			Constraint.BuildDupeInfo = table.Copy(Const.BuildDupeInfo)
		end
	end
	return Constraint, Entities
end

--[[
	Name: Copy
	Desc: Copy an entity and all entities constrained
	Params: <entity> Entity
	Returns: <table> Entities, <table> Constraints
]]
local function Copy(ply, Ent, EntTable, ConstraintTable, Offset)

	local function RecursiveCopy(Ent)
		local index = Ent:EntIndex()
		if EntTable[index] then return end

		local cantool = Ent.CPPICanTool
		if cantool and not cantool(Ent, ply, "advdupe2") then return end

		local EntData = CopyEntTable(Ent, Offset)
		if EntData == nil then return end
		EntTable[index] = EntData

		if Ent.Constraints then
			for k, Constraint in pairs(Ent.Constraints) do
				if Constraint:IsValid() then
					index = Constraint:GetCreationID()
					if index and not ConstraintTable[index] then
						local ConstTable, EntTab = CopyConstraintTable(table.Copy(Constraint:GetTable()), Offset)
						ConstraintTable[index] = ConstTable
						for j, e in pairs(EntTab) do
							if e and (e:IsWorld() or e:IsValid()) then
								RecursiveCopy(e)
							end
						end
					end
				end
			end
		end

		do -- Wiremod Wire Connections
			if istable(Ent.Inputs) then
				for k, v in pairs(Ent.Inputs) do
					if isentity(v.Src) and v.Src:IsValid() then
						RecursiveCopy(v.Src)
					end
				end
			end

			if istable(Ent.Outputs) then
				for k, v in pairs(Ent.Outputs) do
					if istable(v.Connected) then
						for k, v in pairs(v.Connected) do
							if isentity(v.Entity) and v.Entity:IsValid() then
								RecursiveCopy(v.Entity)
							end
						end
					end
				end
			end
		end

		do -- Parented stuff
			local parent = Ent:GetParent()
			if IsValid(parent) then RecursiveCopy(parent) end
			for k, child in pairs(Ent:GetChildren()) do
				RecursiveCopy(child)
			end
		end

		for k, v in pairs(EntData.PhysicsObjects) do
			local phys = Ent:GetPhysicsObjectNum(k)

			if IsValid(phys) then
				phys:EnableMotion(v.Frozen)
			end
		end
	end
	RecursiveCopy(Ent)

	return EntTable, ConstraintTable
end
AdvDupe2.duplicator.Copy = Copy

--[[
	Name: AreaCopy
	Desc: Copy based on a box
	Returns: <table> Entities, <table> Constraints
]]
function AdvDupe2.duplicator.AreaCopy(ply, Entities, Offset, CopyOutside)
	local Constraints, EntTable, ConstraintTable = {}, {}, {}
	local index, add, AddEnts, AddConstrs, ConstTable, EntTab

	for _, Ent in pairs(Entities) do
		index = Ent:EntIndex()
		EntTable[index] = CopyEntTable(Ent, Offset)
		if (EntTable[index] ~= nil) then

			if (not constraint.HasConstraints(Ent)) then
				for k, v in pairs(EntTable[Ent:EntIndex()].PhysicsObjects) do
					local phys = Ent:GetPhysicsObjectNum(k)

					if IsValid(phys) then
						phys:EnableMotion(v.Frozen)
					end
				end
			else
				for k, v in pairs(Ent.Constraints) do
					-- Filter duplicator blocked constraints out.
					if not v.DoNotDuplicate then
						index = v:GetCreationID()
						if (index and not Constraints[index]) then
							Constraints[index] = v
						end
					end
				end

				for k, v in pairs(EntTable[Ent:EntIndex()].PhysicsObjects) do
					Ent:GetPhysicsObjectNum(k):EnableMotion(v.Frozen)
				end
			end
		end
	end

	for _, Constraint in pairs(Constraints) do
		ConstTable, EntTab = CopyConstraintTable(table.Copy(Constraint:GetTable()), Offset)
		-- If the entity is constrained to an entity outside of the area box, don't copy the constraint.
		if (not CopyOutside) then
			add = true
			for k, v in pairs(EntTab) do
				if (not Entities[v:EntIndex()]) then add = false end
			end
			if (add) then ConstraintTable[_] = ConstTable end
		else -- Copy entities and constraints outside of the box that are constrained to entities inside the box
			ConstraintTable[_] = ConstTable
			for k, v in pairs(EntTab) do
				Copy(ply, v, EntTable, ConstraintTable, Offset)
			end
		end
	end

	return EntTable, ConstraintTable
end

--[[
	Name: CreateConstraintFromTable
	Desc: Creates a constraint from a given table
	Params: <table>Constraint, <table> EntityList, <table> EntityTable
	Returns: <entity> CreatedConstraint
]]
local function CreateConstraintFromTable(Constraint, EntityList, EntityTable, Player, DontEnable)
	local Factory = duplicator.ConstraintType[Constraint.Type]
	if not Factory then return end

	if Player and not Player:CheckLimit( "constraints" ) then return end
	local first, firstindex -- Ent1 or Ent in the constraint's table
	local second, secondindex -- Any other Ent that is not Ent1 or Ent
	local Args = {} -- Build the argument list for the Constraint's spawn function
	for k, Key in ipairs(Factory.Args) do

		local Val = Constraint[Key]

		if gtSetupTable.PLAYER[Key] then Val = Player end

		for i = 1, 4 do
			if (Constraint.Entity and Constraint.Entity[i]) then
				if Key == "Ent" .. i or Key == "Ent" then
					if (Constraint.Entity[i].World) then
						Val = game.GetWorld()
					else
						Val = EntityList[Constraint.Entity[i].Index]

						if not IsValid(Val) then
							if (Player) then
								Player:ChatPrint("DUPLICATOR: ERROR, " .. Constraint.Type .. " Constraint could not find an entity!")
							else
								print("DUPLICATOR: ERROR, " .. Constraint.Type .. " Constraint could not find an entity!")
							end
							return
						else
							if (IsValid(Val:GetPhysicsObject())) then
								Val:GetPhysicsObject():EnableMotion(false)
							end
							-- Important for perfect duplication
							-- Get which entity is which so we can reposition them before constraining
							if (gtSetupTable.ENT1[Key]) then
								first = Val
								firstindex = Constraint.Entity[i].Index
							else
								second = Val
								secondindex = Constraint.Entity[i].Index
							end

						end
					end

				end

				if Key == "Bone" .. i or Key == "Bone" then
					Val = Constraint.Entity[i].Bone or 0
				end

				if Key == "LPos" .. i then
					if (Constraint.Entity[i].World and Constraint.Entity[i].LPos) then
						if (i == 2 or i == 4) then
							Val = Constraint.Entity[i].LPos + EntityList[Constraint.Entity[1].Index]:GetPos()
						elseif (i == 1) then
							if (Constraint.Entity[2]) then
								Val = Constraint.Entity[i].LPos + EntityList[Constraint.Entity[2].Index]:GetPos()
							else
								Val = Constraint.Entity[i].LPos + EntityList[Constraint.Entity[4].Index]:GetPos()
							end
						end
					elseif (Constraint.Entity[i].LPos) then
						Val = Constraint.Entity[i].LPos
					end
				end

				if Key == "Length" .. i then
					Val = Constraint.Entity[i].Length
				end
			end
			if Key == "WPos" .. i then
				if (not Constraint.Entity[1].World) then
					Val = Constraint[Key] + EntityList[Constraint.Entity[1].Index]:GetPos()
				else
					Val = Constraint[Key] + EntityList[Constraint.Entity[4].Index]:GetPos()
				end
			end

		end

		Args[k] = Val
	end

	local Bone1, Bone1Index, ReEnableFirst
	local Bone2, Bone2Index, ReEnableSecond
	local buildInfo = Constraint.BuildDupeInfo
	if (buildInfo) then

		if first ~= nil and second ~= nil and not second:IsWorld() and buildInfo.EntityPos ~= nil then
			local SecondPhys = second:GetPhysicsObject()
			if IsValid(SecondPhys) then
				if not DontEnable then ReEnableSecond = SecondPhys:IsMoveable() end
				SecondPhys:EnableMotion(false)
				second:SetPos(first:GetPos() - buildInfo.EntityPos)
				if (buildInfo.Bone2) then
					Bone2Index = buildInfo.Bone2
					Bone2 = second:GetPhysicsObjectNum(Bone2Index)
					if IsValid(Bone2) then
						Bone2:EnableMotion(false)
						Bone2:SetPos(second:GetPos() + buildInfo.Bone2Pos)
						Bone2:SetAngles(buildInfo.Bone2Angle)
					end
				end
			end
		end

		if first ~= nil and not first:IsWorld() and buildInfo.Ent1Ang ~= nil then
			local FirstPhys = first:GetPhysicsObject()
			if IsValid(FirstPhys) then
				if not DontEnable then ReEnableFirst = FirstPhys:IsMoveable() end
				FirstPhys:EnableMotion(false)
				first:SetAngles(buildInfo.Ent1Ang)
				if (buildInfo.Bone1) then
					Bone1Index = buildInfo.Bone1
					Bone1 = first:GetPhysicsObjectNum(Bone1Index)
					if IsValid(Bone1) then
						Bone1:EnableMotion(false)
						Bone1:SetPos(first:GetPos() + buildInfo.Bone1Pos)
						Bone1:SetAngles(buildInfo.Bone1Angle)
					end
				end
			end
		end

		if second ~= nil and not second:IsWorld() then
			if buildInfo.Ent2Ang ~= nil then
				second:SetAngles(buildInfo.Ent2Ang)
			elseif buildInfo.Ent4Ang ~= nil then
				second:SetAngles(buildInfo.Ent4Ang)
			end
		end
	end

	local ok, Ent = pcall(Factory.Func, unpack(Args, 1, #Factory.Args))

	if not ok or not Ent then
		if (Player) then
			AdvDupe2.Notify(Player, "ERROR, Failed to create " .. Constraint.Type .. " Constraint!", NOTIFY_ERROR)
		else
			print("DUPLICATOR: ERROR, Failed to create " .. Constraint.Type .. " Constraint!")
		end
		return
	end

	if Player then
		Player:AddCount( "constraints", Ent )
	end

	Ent.BuildDupeInfo = table.Copy(buildInfo)

	-- Move the entities back after constraining them. No point in moving the world though.
	if (EntityTable) then
		local fEnt = EntityTable[firstindex]
		local sEnt = EntityTable[secondindex]

		if (first ~= nil and not first:IsWorld()) then
			first:SetPos(fEnt.BuildDupeInfo.PosReset)
			first:SetAngles(fEnt.BuildDupeInfo.AngleReset)
			if (IsValid(Bone1) and Bone1Index ~= 0) then
				Bone1:SetPos(fEnt.BuildDupeInfo.PosReset +
										 fEnt.BuildDupeInfo.PhysicsObjects[Bone1Index].Pos)
				Bone1:SetAngles(fEnt.PhysicsObjects[Bone1Index].Angle)
			end

			local FirstPhys = first:GetPhysicsObject()
			if IsValid(FirstPhys) then
				if ReEnableFirst then
					FirstPhys:EnableMotion(true)
				end
			end
		end

		if (second ~= nil and not second:IsWorld()) then
			second:SetPos(sEnt.BuildDupeInfo.PosReset)
			second:SetAngles(sEnt.BuildDupeInfo.AngleReset)
			if (IsValid(Bone2) and Bone2Index ~= 0) then
				Bone2:SetPos(sEnt.BuildDupeInfo.PosReset +
										 sEnt.BuildDupeInfo.PhysicsObjects[Bone2Index].Pos)
				Bone2:SetAngles(sEnt.PhysicsObjects[Bone2Index].Angle)
			end

			local SecondPhys = second:GetPhysicsObject()
			if IsValid(SecondPhys) then
				if ReEnableSecond then
					SecondPhys:EnableMotion(true)
				end
			end
		end
	end

	if (Ent and Ent.length) then
		Ent.length = Constraint["length"]
	end -- Fix for weird bug with ropes

	return Ent
end

local function ApplyEntityModifiers(Player, Ent)
	if not Ent.EntityMods then return end
	if Ent.EntityMods.trail then
		Ent.EntityMods.trail.EndSize = math.Clamp(tonumber(Ent.EntityMods.trail.EndSize) or 0, 0, 1024)
		Ent.EntityMods.trail.StartSize = math.Clamp(tonumber(Ent.EntityMods.trail.StartSize) or 0, 0, 1024)
	end

	for Type, Data in SortedPairs(Ent.EntityMods) do
		local ModFunction = duplicator.EntityModifiers[Type]
		if (ModFunction) then
			local ok, err = pcall(ModFunction, Player, Ent, Data)
			if (not ok) then
				if (Player) then
					Player:ChatPrint('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
				else
					print('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
				end
			end
		end
	end
	if (Ent.EntityMods["mass"] and duplicator.EntityModifiers["mass"]) then
		local ok, err = pcall(duplicator.EntityModifiers["mass"], Player, Ent, Ent.EntityMods["mass"])
		if (not ok) then
			if (Player) then
				Player:ChatPrint('Error applying entity modifer, "mass". ERROR: ' .. err)
			else
				print('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
			end
		end
	end
	if(Ent.EntityMods["buoyancy"] and duplicator.EntityModifiers["buoyancy"]) then
		local ok, err = pcall(duplicator.EntityModifiers["buoyancy"], Player, Ent, Ent.EntityMods["buoyancy"])
		if (not ok) then
			if (Player) then
				Player:ChatPrint('Error applying entity modifer, "buoyancy". ERROR: ' .. err)
			else
				print('Error applying entity modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
			end
		end
	end
end

local function ApplyBoneModifiers(Player, Ent)
	if (not Ent.BoneMods or not Ent.PhysicsObjects) then return end

	for Type, ModFunction in pairs(duplicator.BoneModifiers) do
		for Bone, Args in pairs(Ent.PhysicsObjects) do
			if (Ent.BoneMods[Bone] and Ent.BoneMods[Bone][Type]) then
				local PhysObj = Ent:GetPhysicsObjectNum(Bone)
				if (Ent.PhysicsObjects[Bone]) then
					local ok, err = pcall(ModFunction, Player, Ent, Bone, PhysObj, Ent.BoneMods[Bone][Type])
					if (not ok) then
						Player:ChatPrint('Error applying bone modifer, "' .. tostring(Type) .. '". ERROR: ' .. err)
					end
				end
			end
		end
	end
end

--[[
	Name: DoGenericPhysics
	Desc: Applies bone data, generically.
	Params: <player> Player, <table> data
	Returns: <entity> Entity, <table> data
]]
local function DoGenericPhysics(Entity, data, Player)

	if (not data) then return end
	if (not data.PhysicsObjects) then return end
	local Phys
	if (Player) then
		for Bone, Args in pairs(data.PhysicsObjects) do
			Phys = Entity:GetPhysicsObjectNum(Bone)
			if (IsValid(Phys)) then
				Phys:SetPos(Args.Pos)
				Phys:SetAngles(Args.Angle)
				Phys:EnableMotion(false)
				Player:AddFrozenPhysicsObject(Entity, Phys)
			end
		end
	else
		for Bone, Args in pairs(data.PhysicsObjects) do
			Phys = Entity:GetPhysicsObjectNum(Bone)
			if (IsValid(Phys)) then
				Phys:SetPos(Args.Pos)
				Phys:SetAngles(Args.Angle)
				Phys:EnableMotion(false)
			end
		end
	end
end

local function reportclass(ply, class)
	net.Start("AdvDupe2_ReportClass")
	net.WriteString(class)
	net.Send(ply)
end

local function reportmodel(ply, model)
	net.Start("AdvDupe2_ReportModel")
	net.WriteString(model)
	net.Send(ply)
end

local strictConvar = GetConVar("AdvDupe2_Strict")

--[[
	Name: GenericDuplicatorFunction
	Desc: Override the default duplicator's GenericDuplicatorFunction function
	Params: <table> data, <player> Player
	Returns: <entity> Entity
]]
local function GenericDuplicatorFunction(data, Player)

	local Entity = ents.Create(data.Class)
	if (not IsValid(Entity)) then
		if (Player) then
			reportclass(Player, data.Class)
		else
			print("Advanced Duplicator 2 Invalid Class: " .. data.Class)
		end
		return nil
	end

	if (not util.IsValidModel(data.Model) and not file.Exists(data.Model, "GAME")) then
		if (Player) then
			reportmodel(Player, data.Model)
		else
			print("Advanced Duplicator 2 Invalid Model: " .. data.Model)
		end
		return nil
	end

	duplicator.DoGeneric(Entity, data)
	if (Player) then Entity:SetCreator(Player) end
	Entity:Spawn()
	Entity:Activate()
	DoGenericPhysics(Entity, data, Player)

	if (not strictConvar:GetBool()) then
		table.Add(Entity:GetTable(), data)
	end
	return Entity
end

--[[
	Name: MakeProp
	Desc: Make prop without spawn effects
	Params: <player> Player, <vector> Pos, <angle> Ang, <string> Model, <table> PhysicsObject, <table> Data
	Returns: <entity> Prop
]]
local function MakeProp(Player, Pos, Ang, Model, PhysicsObject, Data)

    if Data.ModelScale then Data.ModelScale = math.Clamp(Data.ModelScale, 1e-5, 1e5) end

	if (not util.IsValidModel(Model) and not file.Exists(Data.Model, "GAME")) then
		if (Player) then
			reportmodel(Player, Data.Model)
		else
			print("Advanced Duplicator 2 Invalid Model: " .. Model)
		end
		return nil
	end

	Data.Pos = Pos
	Data.Angle = Ang
	Data.Model = Model
	Data.Frozen = true
	-- Make sure this is allowed
	if (Player) then
		if (not gamemode.Call("PlayerSpawnProp", Player, Model)) then
			return false
		end
	end

	local Prop = ents.Create("prop_physics")
	if not IsValid(Prop) then return false end

	duplicator.DoGeneric(Prop, Data)
	if (Player) then Prop:SetCreator(Player) end
	Prop:Spawn()
	Prop:Activate()
	DoGenericPhysics(Prop, Data, Player)
	if (Data.Flex) then
		duplicator.DoFlex(Prop, Data.Flex, Data.FlexScale)
	end

	return Prop
end

local function RestoreBodyGroups(ent, BodyG)
	for k, v in pairs(BodyG) do
		ent:SetBodygroup(k, v)
	end
end

--[[
	Name: CreateEntityFromTable
	Desc: Creates an entity from a given table
	Params: <table> EntTable, <player> Player
	Returns: nil
]]
local function IsAllowed(Player, Class, EntityClass)
	if (scripted_ents.GetMember(Class, "DoNotDuplicate")) then return false end

	if (IsValid(Player) and not Player:IsAdmin()) then
		if not duplicator.IsAllowed(Class) then return false end

		local weapon = list.GetForEdit("Weapon")[Class]

		if weapon then
			if (not weapon.Spawnable) then return false end
			if (weapon.AdminOnly) then return false end
		else
			if (not scripted_ents.GetMember(Class, "Spawnable") and not EntityClass) then return false end
			if (scripted_ents.GetMember(Class, "AdminOnly")) then return false end
		end
	end

	return true
end

local function CreateEntityFromTable(EntTable, Player)
	hook.Run("AdvDupe2_PreCreateEntity", EntTable, Player)

	local EntityClass = duplicator.FindEntityClass(EntTable.Class)
	if not IsAllowed(Player, EntTable.Class, EntityClass) then
		Player:ChatPrint([[Entity Class Black listed, "]] .. EntTable.Class .. [["]])
		return nil
	end

	local canCreate, blockReason = hook.Run( "AdvDupe2_CanCreateEntity", Player, EntTable.Class )
	if canCreate == false then
		local msg = [[Entity Class, "]] .. EntTable.Class .. [[" is Blocked! ]]
		if isstring( blockReason ) then -- allow nil blockReason
			msg = msg .. blockReason

		end
		Player:ChatPrint( msg )
		return nil
	end

	local sent = false
	local status, valid
	local GENERIC = false
	local CreatedEntities = {}

	-- This class is unregistered. Instead of failing try using a generic
	-- Duplication function to make a new copy.
	if (not EntityClass) then
		GENERIC = true
		sent = true

		if Player then
			if(EntTable.Class=="prop_effect")then
				sent = gamemode.Call( "PlayerSpawnEffect", Player, EntTable.Model)
			else
				local weapon = list.Get("Weapon")[EntTable.Class]

				if weapon then
					sent = gamemode.Call("PlayerSpawnSWEP", Player, EntTable.Class, weapon)
				else
					sent = gamemode.Call("PlayerSpawnSENT", Player, EntTable.Class)
				end
			end
		else
			sent = true
		end

		if (sent == false) then
			print("Advanced Duplicator 2: Creation rejected for class, : " .. EntTable.Class)
			return nil
		else
			sent = true
		end

		if IsAllowed(Player, EntTable.Class, EntityClass) then
			status, valid = pcall(GenericDuplicatorFunction, EntTable, Player)
		else
			print("Advanced Duplicator 2: ENTITY CLASS IS BLACKLISTED, CLASS NAME: " .. EntTable.Class)
			return nil
		end
	end

	if (not GENERIC) then

		-- Build the argument list for the Entitie's spawn function
		local ArgList, Arg = {}

		for iNumber, Key in pairs(EntityClass.Args) do

			Arg = nil
			-- Translate keys from old system
			if (gtSetupTable.POS[Key]) then Key = "Pos" end
			if (gtSetupTable.ANG[Key]) then Key = "Angle" end
			if (gtSetupTable.MODEL[Key]) then Key = "Model" end
			if (gtSetupTable.TVEHICLE[Key] and EntTable[Key] and EntTable[Key].KeyValues) then
				EntTable[Key].KeyValues = {
					limitview     = EntTable[Key].KeyValues.limitview,
					vehiclescript = EntTable[Key].KeyValues.vehiclescript
				}
			end

			Arg = EntTable[Key]

			-- Special keys
			if (gtSetupTable.SPECIAL[Key]) then
				Arg = EntTable
			end

			ArgList[iNumber] = Arg

		end

		-- Create and return the entity
		if (EntTable.Class == "prop_physics") then
			valid = MakeProp(Player, unpack(ArgList, 1, #EntityClass.Args)) -- Create prop_physics like this because if the model doesn't exist it will cause
		elseif IsAllowed(Player, EntTable.Class, EntityClass) then
			-- Create sents using their spawn function with the arguments we stored earlier
			sent = true

			if Player then
				if (not EntTable.BuildDupeInfo.IsVehicle and not EntTable.BuildDupeInfo.IsNPC and EntTable.Class ~= "prop_ragdoll" and EntTable.Class ~= "prop_effect") then
					local weapon = list.Get("Weapon")[EntTable.Class]

					if weapon then
						sent = gamemode.Call("PlayerSpawnSWEP", Player, EntTable.Class, weapon)
					else
						sent = gamemode.Call("PlayerSpawnSENT", Player, EntTable.Class)
					end
				end
			else
				sent = true
			end

			if (sent == false) then
				print("Advanced Duplicator 2: Creation rejected for class, : " .. EntTable.Class)
				return nil
			else
				sent = true
			end

			hook.Add( "OnEntityCreated", "AdvDupe2_GetLastEntitiesCreated", function( ent )
				table.insert( CreatedEntities, ent )
			end )

			status, valid = xpcall(EntityClass.Func, ErrorNoHaltWithStack, Player, unpack(ArgList, 1, #EntityClass.Args))

			hook.Remove( "OnEntityCreated", "AdvDupe2_GetLastEntitiesCreated" )
		else
			print("Advanced Duplicator 2: ENTITY CLASS IS BLACKLISTED, CLASS NAME: " .. EntTable.Class)
			return nil
		end
	end

	-- If its a valid entity send it back to the entities list so we can constrain it
	if (status ~= false and IsValid(valid)) then
		if (sent) then
			local iNumPhysObjects = valid:GetPhysicsObjectCount()
			local PhysObj
			if (Player) then
				for Bone = 0, iNumPhysObjects - 1 do
					PhysObj = valid:GetPhysicsObjectNum(Bone)
					if IsValid(PhysObj) then
						PhysObj:EnableMotion(false)
						Player:AddFrozenPhysicsObject(valid, PhysObj)
					end
				end
			else
				for Bone = 0, iNumPhysObjects - 1 do
					PhysObj = valid:GetPhysicsObjectNum(Bone)
					if IsValid(PhysObj) then
						PhysObj:EnableMotion(false)
					end
				end
			end
			if (EntTable.Skin) then valid:SetSkin(EntTable.Skin) end
			if (EntTable.BodyG) then RestoreBodyGroups(valid, EntTable.BodyG) end

			if valid.RestoreNetworkVars then
				valid:RestoreNetworkVars(EntTable.DT)
			end

			if GENERIC and Player then
				if(EntTable.Class=="prop_effect")then
					gamemode.Call("PlayerSpawnedEffect", Player, valid:GetModel(), valid)
				elseif valid:IsWeapon() then
					gamemode.Call("PlayerSpawnedSWEP", Player, valid)
				else
					gamemode.Call("PlayerSpawnedSENT", Player, valid)
				end
			end

		elseif (Player) then
			gamemode.Call("PlayerSpawnedProp", Player, valid:GetModel(), valid)
		end

		return valid
	else
		if (status == false) then
			print("Advanced Duplicator 2: Error creating entity, removing last created entities")
			for _, CreatedEntity in pairs(CreatedEntities) do
				SafeRemoveEntity(CreatedEntity)
			end
		end

		if (valid == false) then
			return false
		else
			return nil
		end
	end
end

--[[
	Name: Paste
	Desc: Override the default duplicator's paste function
	Params: <player> Player, <table> Entities, <table> Constraints
	Returns: <table> Entities, <table> Constraints
]]
function AdvDupe2.duplicator.Paste(Player, EntityList, ConstraintList, Position, AngleOffset, OrigPos, Parenting)

	local CreatedEntities = {}
	--
	-- Create entities
	--
	local proppos
	DisablePropCreateEffect = true
	for k, v in pairs(EntityList) do
		if (not v.BuildDupeInfo) then v.BuildDupeInfo = {} end
		v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects)
		proppos = v.PhysicsObjects[0].Pos
		v.BuildDupeInfo.PhysicsObjects[0].Pos = Vector(0, 0, 0)
		if (OrigPos) then
			for i, p in pairs(v.BuildDupeInfo.PhysicsObjects) do
				v.PhysicsObjects[i].Pos = p.Pos + proppos + OrigPos
				v.PhysicsObjects[i].Frozen = true
			end
			v.Pos = v.PhysicsObjects[0].Pos
			v.Angle = v.PhysicsObjects[0].Angle
			v.BuildDupeInfo.PosReset = v.Pos
			v.BuildDupeInfo.AngleReset = v.Angle
		else
			for i, p in pairs(v.BuildDupeInfo.PhysicsObjects) do
				v.PhysicsObjects[i].Pos, v.PhysicsObjects[i].Angle =
					LocalToWorld(p.Pos + proppos, p.Angle, Position, AngleOffset)
				v.PhysicsObjects[i].Frozen = true
			end
			v.Pos = v.PhysicsObjects[0].Pos
			v.BuildDupeInfo.PosReset = v.Pos
			v.Angle = v.PhysicsObjects[0].Angle
			v.BuildDupeInfo.AngleReset = v.Angle
		end

		AdvDupe2.SpawningEntity = true
		local Ent = CreateEntityFromTable(v, Player)
		AdvDupe2.SpawningEntity = false

		if Ent then
			if (Player) then Player:AddCleanup("AdvDupe2", Ent) end
			Ent.BoneMods = table.Copy(v.BoneMods)
			Ent.EntityMods = table.Copy(v.EntityMods)
			Ent.PhysicsObjects = table.Copy(v.PhysicsObjects)
			if (v.CollisionGroup) then Ent:SetCollisionGroup(v.CollisionGroup) end
			if (Ent.OnDuplicated) then Ent:OnDuplicated(v) end
			ApplyEntityModifiers(Player, Ent)
			ApplyBoneModifiers(Player, Ent)
			Ent.SolidMod = not Ent:IsSolid()
			Ent:SetNotSolid(true)
		elseif (Ent == false) then
			Ent = nil
			-- ConstraintList = {}
			-- break
		else
			Ent = nil
		end
		CreatedEntities[k] = Ent
	end

	local CreatedConstraints, Entity = {}
	--
	-- Create constraints
	--
	for k, Constraint in pairs(ConstraintList) do
		Entity = CreateConstraintFromTable(Constraint, CreatedEntities, EntityList, Player)
		if (IsValid(Entity)) then
			table.insert(CreatedConstraints, Entity)
		end
	end

	if (Player) then
		local undotxt = "AdvDupe2"..(Player.AdvDupe2.Name and (": ("..tostring(Player.AdvDupe2.Name)..")") or "")

		undo.Create(undotxt)
		for _, v in pairs(CreatedEntities) do
			-- If the entity has a PostEntityPaste function tell it to use it now
			if v.PostEntityPaste then
				local status, valid = pcall(v.PostEntityPaste, v, Player, v, CreatedEntities)
				if (not status) then
					print("AD2 PostEntityPaste Error: " .. tostring(valid))
				end
			end

			if IsValid(v:GetPhysicsObject()) then
				v:GetPhysicsObject():EnableMotion(false)
			end

			if (EntityList[_].BuildDupeInfo.DupeParentID and Parenting) then
				v:SetParent(CreatedEntities[EntityList[_].BuildDupeInfo.DupeParentID])
			end
			v:SetNotSolid(v.SolidMod)
			undo.AddEntity(v)
		end
		undo.SetPlayer(Player)
		undo.Finish()

		-- if(Tool)then AdvDupe2.FinishPasting(Player, true) end
	else

		for _, v in pairs(CreatedEntities) do
			-- If the entity has a PostEntityPaste function tell it to use it now
			if v.PostEntityPaste then
				local status, valid = pcall(v.PostEntityPaste, v, Player, v, CreatedEntities)
				if (not status) then
					print("AD2 PostEntityPaste Error: " .. tostring(valid))
				end
			end

			if IsValid(v:GetPhysicsObject()) then
				v:GetPhysicsObject():EnableMotion(false)
			end

			if (EntityList[_].BuildDupeInfo.DupeParentID and Parenting) then
				v:SetParent(CreatedEntities[EntityList[_].BuildDupeInfo.DupeParentID])
			end

			v:SetNotSolid(v.SolidMod)
		end
	end
	DisablePropCreateEffect = nil
	hook.Call("AdvDupe_FinishPasting", nil, {
		{
			EntityList = EntityList,
			CreatedEntities = CreatedEntities,
			ConstraintList = ConstraintList,
			CreatedConstraints = CreatedConstraints,
			HitPos = OrigPos or Position,
			Player = Player
		}
	}, 1)

	return CreatedEntities, CreatedConstraints
end

local function AdvDupe2_Spawn()

	local Queue = AdvDupe2.JobManager.Queue[AdvDupe2.JobManager.CurrentPlayer]

	if (not Queue or not IsValid(Queue.Player)) then
		if Queue then
			table.remove(AdvDupe2.JobManager.Queue, AdvDupe2.JobManager.CurrentPlayer)
		end

		if (#AdvDupe2.JobManager.Queue == 0) then
			hook.Remove("Tick", "AdvDupe2_Spawning")
			DisablePropCreateEffect = nil
			AdvDupe2.JobManager.PastingHook = false
		end
		return
	end

	if (Queue.Entity) then
		if (Queue.Current == 1) then
			AdvDupe2.InitProgressBar(Queue.Player, "Pasting:")
			Queue.Player.AdvDupe2.Queued = false
		end
		if (Queue.Current > #Queue.SortedEntities) then
			Queue.Entity = false
			Queue.Constraint = true
			Queue.Current = 1
			return
		end
		if (not Queue.SortedEntities[Queue.Current]) then
			Queue.Current = Queue.Current + 1
			return
		end

		local k = Queue.SortedEntities[Queue.Current]
		local v = Queue.EntityList[k]

		if (not v.BuildDupeInfo) then v.BuildDupeInfo = {} end
		if Queue.Revision < 1 and v.LocalPos then
			for i, _ in pairs(v.PhysicsObjects) do
				v.PhysicsObjects[i] = {Pos = v.LocalPos, Angle = v.LocalAngle}
			end
		end

		v.BuildDupeInfo.PhysicsObjects = table.Copy(v.PhysicsObjects)
		local proppos = v.PhysicsObjects[0].Pos
		v.BuildDupeInfo.PhysicsObjects[0].Pos = Vector(0, 0, 0)
		if (Queue.OrigPos) then
			for i, p in pairs(v.BuildDupeInfo.PhysicsObjects) do
				v.PhysicsObjects[i].Pos = p.Pos + proppos + Queue.OrigPos
				v.PhysicsObjects[i].Frozen = true
			end
			v.Pos = v.PhysicsObjects[0].Pos
			v.Angle = v.PhysicsObjects[0].Angle
			v.BuildDupeInfo.PosReset = v.Pos
			v.BuildDupeInfo.AngleReset = v.Angle
		else
			for i, p in pairs(v.BuildDupeInfo.PhysicsObjects) do
				v.PhysicsObjects[i].Pos, v.PhysicsObjects[i].Angle =
					LocalToWorld(p.Pos + proppos, p.Angle, Queue.PositionOffset, Queue.AngleOffset)
				v.PhysicsObjects[i].Frozen = true
			end
			v.Pos = v.PhysicsObjects[0].Pos
			v.BuildDupeInfo.PosReset = v.Pos
			v.Angle = v.PhysicsObjects[0].Angle
			v.BuildDupeInfo.AngleReset = v.Angle
		end

		AdvDupe2.SpawningEntity = true
		local Ent = CreateEntityFromTable(v, Queue.Player)
		AdvDupe2.SpawningEntity = false

		if Ent then
			Queue.Player:AddCleanup("AdvDupe2", Ent)
			Ent.BoneMods = table.Copy(v.BoneMods)
			Ent.EntityMods = table.Copy(v.EntityMods)
			Ent.PhysicsObjects = table.Copy(v.PhysicsObjects)
			Ent.SolidMod = not Ent:IsSolid()

			local Phys = Ent:GetPhysicsObject()
			if (IsValid(Phys)) then Phys:EnableMotion(false) end
			if (not Queue.DisableProtection) then Ent:SetNotSolid(true) end
			if (v.CollisionGroup) then Ent:SetCollisionGroup(v.CollisionGroup) end
			if (Ent.OnDuplicated) then Ent:OnDuplicated(v) end
		elseif (Ent == false) then
			Ent = nil
		else
			Ent = nil
		end
		Queue.CreatedEntities[k] = Ent

		AdvDupe2.UpdateProgressBar(Queue.Player, math.floor((Queue.Percent * Queue.Current) * 100))
		Queue.Current = Queue.Current + 1
		if (Queue.Current > #Queue.SortedEntities) then

			for _, Ent in pairs(Queue.CreatedEntities) do
				ApplyEntityModifiers(Queue.Player, Ent)
				ApplyBoneModifiers(Queue.Player, Ent)

				-- If the entity has a PostEntityPaste function tell it to use it now
				if Ent.PostEntityPaste then
					local status, valid = pcall(Ent.PostEntityPaste, Ent, Queue.Player, Ent, Queue.CreatedEntities)
					if (not status) then
						print("AD2 PostEntityPaste Error: " .. tostring(valid))
					end
				end
			end

			Queue.Entity = false
			Queue.Constraint = true
			Queue.Current = 1
		end

		if (#AdvDupe2.JobManager.Queue >= AdvDupe2.JobManager.CurrentPlayer + 1) then
			AdvDupe2.JobManager.CurrentPlayer = AdvDupe2.JobManager.CurrentPlayer + 1
		else
			AdvDupe2.JobManager.CurrentPlayer = 1
		end
	else
		if (#Queue.ConstraintList > 0) then

			if (#AdvDupe2.JobManager.Queue == 0) then
				hook.Remove("Tick", "AdvDupe2_Spawning")
				DisablePropCreateEffect = nil
				AdvDupe2.JobManager.PastingHook = false
			end
			if (not Queue.ConstraintList[Queue.Current]) then
				Queue.Current = Queue.Current + 1
				return
			end

			local Entity = CreateConstraintFromTable(Queue.ConstraintList[Queue.Current], Queue.CreatedEntities,
																							 Queue.EntityList, Queue.Player, true)
			if IsValid(Entity) then
				table.insert(Queue.CreatedConstraints, Entity)
			end
		elseif (next(Queue.ConstraintList) ~= nil) then
			local tbl = {}
			for k, v in pairs(Queue.ConstraintList) do
				table.insert(tbl, v)
			end
			Queue.ConstraintList = tbl
			Queue.Current = 0
		end

		AdvDupe2.UpdateProgressBar(Queue.Player, math.floor((Queue.Percent * (Queue.Current + Queue.Plus)) * 100))
		Queue.Current = Queue.Current + 1

		if (Queue.Current > #Queue.ConstraintList) then

			local unfreeze = tobool(Queue.Player:GetInfo("advdupe2_paste_unfreeze")) or false
			local preservefrozenstate = tobool(Queue.Player:GetInfo("advdupe2_preserve_freeze")) or false

			-- Remove the undo for stopping pasting
			local undotxt = "AdvDupe2"..(Queue.Name and (": ("..tostring(Queue.Name)..")") or "")
			local undos = undo.GetTable()[Queue.Player:UniqueID()]
			for i = #undos, 1, -1 do
				if (undos[i] and undos[i].Name == undotxt) then
					undos[i] = nil
					-- Undo module netmessage
					net.Start("Undo_Undone")
					net.WriteInt(i, 16)
					net.Send(Queue.Player)
					break
				end
			end

			undo.Create(undotxt)
			local phys, edit, mass
			for k, v in pairs(Queue.CreatedEntities) do
				if (not IsValid(v)) then
					v = nil
				else
					edit = true
					if (Queue.EntityList[k].BuildDupeInfo.DupeParentID ~= nil and Queue.Parenting) then
						v:SetParent(Queue.CreatedEntities[Queue.EntityList[k].BuildDupeInfo.DupeParentID])
						if (v.Constraints ~= nil) then
							for i, c in pairs(v.Constraints) do
								if (c and gtSetupTable.CONSTRAINT[c.Type]) then
									edit = false
									break
								end
							end
						end
						if (edit and IsValid(v:GetPhysicsObject())) then
							mass = v:GetPhysicsObject():GetMass()
							v:PhysicsInitShadow(false, false)
							v:SetCollisionGroup(COLLISION_GROUP_WORLD)
							v:GetPhysicsObject():EnableMotion(false)
							v:GetPhysicsObject():Sleep()
							v:GetPhysicsObject():SetMass(mass)
						end
					else
						edit = false
					end

					local physCount = v:GetPhysicsObjectCount() - 1

					if (unfreeze) then
						for i = 0, physCount do
							phys = v:GetPhysicsObjectNum(i)
							if (IsValid(phys)) then
								phys:EnableMotion(true) -- Unfreeze the entitiy and all of its objects
								phys:Wake()
							end
						end
					elseif (preservefrozenstate) then
						for i = 0, physCount do
							phys = v:GetPhysicsObjectNum(i)
							if (IsValid(phys)) then
								if (Queue.EntityList[k].BuildDupeInfo.PhysicsObjects[i].Frozen) then
									phys:EnableMotion(true) -- Restore the entity and all of its objects to their original frozen state
									phys:Wake()
								else
									Queue.Player:AddFrozenPhysicsObject(v, phys)
								end
							end
						end
					else
						for i = 0, physCount do
							phys = v:GetPhysicsObjectNum(i)
							if (IsValid(phys)) then
								if (phys:IsMoveable()) then
									phys:EnableMotion(false) -- Freeze the entitiy and all of its objects
									Queue.Player:AddFrozenPhysicsObject(v, phys)
								end
							end
						end
					end

					if (not edit or not Queue.DisableParents) then
						v:SetNotSolid(v.SolidMod)
					end

					undo.AddEntity(v)
				end
			end
			undo.SetPlayer(Queue.Player)
			undo.Finish()

			hook.Call("AdvDupe_FinishPasting", nil, {
				{
					EntityList = Queue.EntityList,
					CreatedEntities = Queue.CreatedEntities,
					ConstraintList = Queue.ConstraintList,
					CreatedConstraints = Queue.CreatedConstraints,
					HitPos = Queue.PositionOffset,
					Player = Queue.Player
				}
			}, 1)
			AdvDupe2.FinishPasting(Queue.Player, true)

			table.remove(AdvDupe2.JobManager.Queue, AdvDupe2.JobManager.CurrentPlayer)
			if (#AdvDupe2.JobManager.Queue == 0) then
				hook.Remove("Tick", "AdvDupe2_Spawning")
				DisablePropCreateEffect = nil
				AdvDupe2.JobManager.PastingHook = false
			end
		end
		if (#AdvDupe2.JobManager.Queue >= AdvDupe2.JobManager.CurrentPlayer + 1) then
			AdvDupe2.JobManager.CurrentPlayer = AdvDupe2.JobManager.CurrentPlayer + 1
		else
			AdvDupe2.JobManager.CurrentPlayer = 1
		end
	end
end

local ticktotal = 0
local function ErrorCatchSpawning()

	ticktotal = ticktotal + math.max(GetConVarNumber("AdvDupe2_SpawnRate"), 0.01)
	while ticktotal >= 1 do
		ticktotal = ticktotal - 1
		local status, err = pcall(AdvDupe2_Spawn)

		if (not status) then
			-- PUT ERROR LOGGING HERE

			if (not AdvDupe2.JobManager.Queue) then
				print("[AdvDupe2Notify]\t" .. err)
				AdvDupe2.JobManager.Queue = {}
				return
			end

			local Queue = AdvDupe2.JobManager.Queue[AdvDupe2.JobManager.CurrentPlayer]
			if (not Queue) then
				print("[AdvDupe2Notify]\t" .. err)
				return
			end

			if (IsValid(Queue.Player)) then
				AdvDupe2.Notify(Queue.Player, err)

				local undos = undo.GetTable()[Queue.Player:UniqueID()]
				local undotxt = Queue.Name and ("AdvDupe2 ("..Queue.Name..")") or "AdvDupe2"
				for i = #undos, 1, -1 do
					if (undos[i] and undos[i].Name == undotxt) then
						undos[i] = nil
						-- Undo module netmessage
						net.Start("Undo_Undone")
						net.WriteInt(i, 16)
						net.Send(Queue.Player)
						break
					end
				end
			else
				print("[AdvDupe2Notify]\t" .. err)
			end

			for k, v in pairs(Queue.CreatedEntities) do
				if (IsValid(v)) then v:Remove() end
			end

			if (IsValid(Queue.Player)) then
				AdvDupe2.FinishPasting(Queue.Player, true)
			end

			table.remove(AdvDupe2.JobManager.Queue, AdvDupe2.JobManager.CurrentPlayer)

			if (#AdvDupe2.JobManager.Queue == 0) then
				hook.Remove("Tick", "AdvDupe2_Spawning")
				DisablePropCreateEffect = nil
				AdvDupe2.JobManager.PastingHook = false
			else
				if (#Queue < AdvDupe2.JobManager.CurrentPlayer) then
					AdvDupe2.JobManager.CurrentPlayer = 1
				end
			end

		end
	end
end

local function RemoveSpawnedEntities(tbl, i)
	if (not AdvDupe2.JobManager.Queue[i]) then return end -- Without this some errors come up, double check the errors without this line

	for k, v in pairs(AdvDupe2.JobManager.Queue[i].CreatedEntities) do
		if (IsValid(v)) then v:Remove() end
	end

	AdvDupe2.FinishPasting(AdvDupe2.JobManager.Queue[i].Player, false)
	table.remove(AdvDupe2.JobManager.Queue, i)
	if (#AdvDupe2.JobManager.Queue == 0) then
		hook.Remove("Tick", "AdvDupe2_Spawning")
		DisablePropCreateEffect = nil
		AdvDupe2.JobManager.PastingHook = false
	end
end

function AdvDupe2.InitPastingQueue(Player, PositionOffset, AngleOffset, OrigPos, Constrs, Parenting, DisableParents, DisableProtection)
	local i = #AdvDupe2.JobManager.Queue + 1

	local Queue = {
		Player = Player,
		SortedEntities = {},
		EntityList = table.Copy(Player.AdvDupe2.Entities),
		Current = 1,
		Name = Player.AdvDupe2.Name,
		Entity = true,
		Constraint = false,
		Parenting = Parenting,
		DisableParents = DisableParents,
		DisableProtection = DisableProtection,
		CreatedEntities = {},
		CreatedConstraints = {},
		PositionOffset = PositionOffset or Vector(0, 0, 0),
		AngleOffset = AngleOffset or Angle(0, 0, 0),
		Revision = Player.AdvDupe2.Revision,
	}
	AdvDupe2.JobManager.Queue[i] = Queue

	if (Constrs) then
		Queue.ConstraintList = table.Copy(Player.AdvDupe2.Constraints)
	else
		Queue.ConstraintList = {}
	end
	Queue.OrigPos = OrigPos
	for k, v in pairs(Player.AdvDupe2.Entities) do
		table.insert(Queue.SortedEntities, k)
	end

	if (Player.AdvDupe2.Name) then
		print(
			"[AdvDupe2NotifyPaste]\t Player: " .. Player:Nick() .. " Pasted File, " .. Player.AdvDupe2.Name .. " with, " ..
				#Queue.SortedEntities .. " Entities and " .. #Player.AdvDupe2.Constraints .. " Constraints.")
	else
		print("[AdvDupe2NotifyPaste]\t Player: " .. Player:Nick() .. " Pasted, " .. #Queue.SortedEntities ..
						" Entities and " .. #Player.AdvDupe2.Constraints .. " Constraints.")
	end

	Queue.Plus = #Queue.SortedEntities
	Queue.Percent = 1 / (#Queue.SortedEntities + #Queue.ConstraintList)
	AdvDupe2.InitProgressBar(Player, "Queued:")
	Player.AdvDupe2.Queued = true
	if (not AdvDupe2.JobManager.PastingHook) then
		DisablePropCreateEffect = true
		hook.Add("Tick", "AdvDupe2_Spawning", ErrorCatchSpawning)
		AdvDupe2.JobManager.PastingHook = true
		AdvDupe2.JobManager.CurrentPlayer = 1
	end

	local undotxt = "AdvDupe2"..(Player.AdvDupe2.Name and (": ("..tostring(Player.AdvDupe2.Name)..")") or "")
	undo.Create(undotxt)
	undo.SetPlayer(Player)
	undo.AddFunction(RemoveSpawnedEntities, i)
	undo.Finish()
end
