local v=require((game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\078\101\118\101\114\109\111\114\101"))local E=game:GetService("\080\104\121\115\105\099\115\083\101\114\118\105\099\101")local F=game:GetService("\080\108\097\121\101\114\115")local N=v("\077\097\105\100")local Q={}Q.ClassName="\067\108\105\112\067\104\097\114\097\099\116\101\114\115"Q.__index=Q Q.CollisionGroupName="\067\108\105\112\067\104\097\114\097\099\116\101\114\115"function Q.initServer()local F=E:CreateCollisionGroup(Q.CollisionGroupName)E:CollisionGroupSetCollidable(Q.CollisionGroupName,"\068\101\102\097\117\108\116",false)local N=v.GetRemoteFunction("\071\101\116\067\108\105\112\067\104\097\114\097\099\116\101\114\115\073\100")function N.OnServerInvoke(v)return F end end function Q.new()local E=setmetatable({},Q)E._remoteFunction=v.GetRemoteFunction("\071\101\116\067\108\105\112\067\104\097\114\097\099\116\101\114\115\073\100")E._maid=N.new()E:_bindUpdatesYielding()return E end function Q._onDescendantAdded(F,v,E)if not v[E]and E:IsA("\066\097\115\101\080\097\114\116")then v[E]=E.CollisionGroupId E.CollisionGroupId=F._collisionGroupId end end function Q._onDescendantRemoving(F,v,E)if v[E]then E.CollisionGroupId=v[E]v[E]=nil end end function Q._onCharacterAdd(F,v,E)local Q=N.new()local c={}Q:GiveTask(E.DescendantAdded:Connect(function(v)F:_onDescendantAdded(c,v)end))Q:GiveTask(E.DescendantRemoving:Connect(function(v)F:_onDescendantRemoving(c,v)end))Q:GiveTask(function()for v,E in pairs(c)do F:_onDescendantRemoving(c,v)end end)for v,E in pairs(E:GetDescendants())do F:_onDescendantAdded(c,E)end v._characterMaid=Q end function Q._onPlayerAdded(E,v)if v==F.LocalPlayer then return end local Q=N.new()Q:GiveTask(v.CharacterAdded:Connect(function(v)E:_onCharacterAdd(Q,v)end))if v.Character then E:_onCharacterAdd(Q,v.Character)end E._maid[v]=Q end function Q._bindUpdatesYielding(v)v._collisionGroupId=v._remoteFunction:InvokeServer()if not v._collisionGroupId then warn("\091\067\108\105\112\067\104\097\114\097\099\116\101\114\115\093 - \078\111 \115\101\108\102\046\095\099\111\108\108\105\115\105\111\110\071\114\111\117\112\073\100")end for E,F in pairs(F:GetPlayers())do v:_onPlayerAdded(F)end v._maid:GiveTask(F.PlayerAdded:Connect(function(E)v:_onPlayerAdded(E)end))v._maid:GiveTask(F.PlayerRemoving:Connect(function(E)v._maid[E]=nil end))end function Q.Destroy(v)v._maid:DoCleaning()v._maid=nil setmetatable({},nil)end return Q
--- Clip characters locally on the client of other clients so they don't interfer with physics.
-- @classmod ClipCharacters

local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Nevermore"))

local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")

local Maid = require("Maid")

local ClipCharacters = {}
ClipCharacters.ClassName = "ClipCharacters"
ClipCharacters.__index = ClipCharacters
ClipCharacters.CollisionGroupName = "ClipCharacters"

--- Initialize on server
-- @constructor
-- @treturn nil
function ClipCharacters.initServer()
	local groupId = PhysicsService:CreateCollisionGroup(ClipCharacters.CollisionGroupName)
	PhysicsService:CollisionGroupSetCollidable(ClipCharacters.CollisionGroupName, "Default", false)

	local remoteFunction = require.GetRemoteFunction("GetClipCharactersId")
	function remoteFunction.OnServerInvoke(player)
		return groupId
	end
end

--- Initialize clipping on the client. Returns a new inst
-- @constructor
-- @treturn ClipCharacters
function ClipCharacters.new()
	local self = setmetatable({}, ClipCharacters)

	self._remoteFunction = require.GetRemoteFunction("GetClipCharactersId")

	self._maid = Maid.new()
	self:_bindUpdatesYielding()

	return self
end

function ClipCharacters:_onDescendantAdded(originalTable, descendant)
	if not originalTable[descendant] and descendant:IsA("BasePart") then
		originalTable[descendant] = descendant.CollisionGroupId
		descendant.CollisionGroupId = self._collisionGroupId
	end
end

function ClipCharacters:_onDescendantRemoving(originalTable, descendant)
	if originalTable[descendant] then
		descendant.CollisionGroupId = originalTable[descendant]
		originalTable[descendant] = nil
	end
end

function ClipCharacters:_onCharacterAdd(playerMaid, character)
	local maid = Maid.new()

	local originalTable = {}

	maid:GiveTask(character.DescendantAdded:Connect(function(descendant)
		self:_onDescendantAdded(originalTable, descendant)
	end))

	maid:GiveTask(character.DescendantRemoving:Connect(function(descendant)
		self:_onDescendantRemoving(originalTable, descendant)
	end))

	-- Cleanup
	maid:GiveTask(function()
		for descendant, _ in pairs(originalTable) do
			self:_onDescendantRemoving(originalTable, descendant)
		end
	end)

	-- Initialize
	for _, descendant in pairs(character:GetDescendants()) do
		self:_onDescendantAdded(originalTable, descendant)
	end

	playerMaid._characterMaid = maid
end

function ClipCharacters:_onPlayerAdded(player)
	if player == Players.LocalPlayer then
		return
	end

	local maid = Maid.new()

	maid:GiveTask(player.CharacterAdded:Connect(function(character)
		self:_onCharacterAdd(maid, character)
	end))

	if player.Character then
		self:_onCharacterAdd(maid, player.Character)
	end

	self._maid[player] = maid
end

function ClipCharacters:_bindUpdatesYielding()
	self._collisionGroupId = self._remoteFunction:InvokeServer()

	if not self._collisionGroupId then
		warn("[ClipCharacters] - No self._collisionGroupId")
	end

	for _, player in pairs(Players:GetPlayers()) do
		self:_onPlayerAdded(player)
	end

	self._maid:GiveTask(Players.PlayerAdded:Connect(function(player)
		self:_onPlayerAdded(player)
	end))

	self._maid:GiveTask(Players.PlayerRemoving:Connect(function(player)
		self._maid[player] = nil
	end))
end

--- Stop clipping on client
-- @treturn nil
function ClipCharacters:Destroy()
	self._maid:DoCleaning()
	self._maid = nil

	setmetatable({}, nil)
end

return ClipCharacters