function ContentsManager._getTouchingScenery(o,B,K)local U={}local m=o:getCell(B)local a=Vector3.new(0,-o.grid.CELL_DIMENSIONS.Y,0)local H={m.center,m.center+Vector3.new(o.grid.CELL_DIMENSIONS.X/2,0,0),m.center-Vector3.new(o.grid.CELL_DIMENSIONS.X/2,0,0),m.center+Vector3.new(0,0,o.grid.CELL_DIMENSIONS.Z/2);m.center-Vector3.new(0,0,o.grid.CELL_DIMENSIONS.Z/2)}for B,K in ipairs(H)do local o=workspace:FindPartOnRayWithWhitelist(Ray.new(K,a),Util.getTableKeys(SceneryManager.scenery),true)if o then U[o]=true end end return U end function ContentsManager.addForced(o,B,K)return o:add(B,K,nil,true)end function ContentsManager.add(m,B,K,o,U)if U or m:canPlace(B,K)then if o~=nil then m:_addToContents(o,B,K)else local o=B:render(m,m:getCell(K))m:_addToContents(o,B,K)if isServer then local U=m:getTouchingObjects(o,B,m:getCell(K))weldTouchingObjects(o,U)o.Anchored=false end end return true else return false end end function ContentsManager._allocateCell(K,B)local o=tostring(B)K.contents[o]=K:getCell(B)return K.contents[o]end function ContentsManager._deallocateCell(K,B)local o=tostring(B)K.contents[o]=nil end function ContentsManager._allocateConnectivityMapForIndex(o,B,K)local U=o:_allocateCell(B)local m=tostring(K)if U.connectivityMap[m]==nil then U.connectivityMap[m]={}end return U.connectivityMap[m]end function ContentsManager._deallocateConnectivityMapForIndex(o,B,K)local U=o:getCell(B)local m=tostring(K)U.connectivityMap[m]=nil end function ContentsManager._addToContents(U,B,K,o)local m=U:_allocateCell(o)m.contents.objects[K]=B m.occupancy=bit32.bor(K.occupancy,m.occupancy)for o=0,13,1 do if bit32.extract(K.occupancy,o)==1 then m.occupancyMap[tostring(o)]=B end end for m,a in pairs(BitOps.FACES)do local H=U:_allocateCell(o+a)H.occupancy=BitOps.applyFaceOccupancy(H.occupancy,-1*a,K.occupancy,a)if bit32.btest(BitOps.OCCUPANCY_FACE_MASK[tostring(BitOps.normalizeVec3(a))],K.occupancy)then local o,m,a=K.fromString(K.name)local S=(m+180)%360 local Z=U.grid.objectTypes[o..("\036"..(tostring(S)..("\036"..tostring(a))))]H.contents.objects[Z]=B end end for m,a in pairs(BitOps.FACES)do local H=BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(a))]if bit32.extract(K.occupancy,H)==1 then local K=U:_allocateCell(o+a)local m=BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(-1*a))]K.occupancyMap[tostring(m)]=B end end m.connectivity=bit32.bor(K.connectivity,m.connectivity)for m=0,23,1 do if bit32.extract(K.connectivity,m)==1 then local K=U:_allocateConnectivityMapForIndex(o,m)K[B]=true end end for B,m in pairs(BitOps.FACES)do local a=U:_allocateCell(o+m)a.connectivity=BitOps.applyFaceConnectivity(a.connectivity,-1*m,K.connectivity,m)end for m,a in pairs(BitOps.FACES)do for m,H in ipairs(BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(a))])do if bit32.extract(K.connectivity,H)==1 then local K=BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(-1*a))]local H=K[m]local S=U:_allocateConnectivityMapForIndex(o+a,H)S[B]=true end end end for m,a in pairs(BitOps.CONNECTIVITY_INDEXES_FOR_EDGE)do local H=Vector3FromString(m)local S=#a for m,a in ipairs(a)do if bit32.extract(K.connectivity,a)~=0 then local K=U:_allocateCell(o+H)local a=BitOps.normalizeVec3(-1*H)local Z=BitOps.CONNECTIVITY_INDEXES_FOR_EDGE[tostring(a)]local c=m if H.Y~=0 then c=(S+1)-c end K.connectivity=bit32.replace(K.connectivity,1,Z[c])local q=U:_allocateConnectivityMapForIndex(K.id,Z[c])q[B]=true end end end if DEBUG then local B="\067\076\073\069\078\084 - "if isServer then B="\083\069\082\086\069\082 - "end print(B..("\067\111\110\116\101\110\116\115\077\097\110\097\103\101\114 \099\111\110\116\101\110\116\115 \102\111\114 \102\111\108\100\101\114 "..(U.folder.Name..(" \097\102\116\101\114 \097\100\100\105\110\103 "..(K.name..(" \116\111 "..tostring(o)))))))print(Util.debugPrint(U.contents))end U:broadcastContentsChange({type=ADDED_OBJECT,cellId=o,objectType=K,object=B})end function ContentsManager.broadcastContentsChange(K,B)if K.contentsListenerCount>0 then for K in pairs(K.contentsListeners)do K(B)end end end function ContentsManager.getTouchingObjects(U,B,K,o)local m={}local a={}for B=0,23,1 do local U=tostring(B)if bit32.extract(K.connectivity,B)==1 and o.connectivityMap[U]~=nil then for B in pairs(o.connectivityMap[U])do if a[B]then m[B]=true else a[B]=true end end end end for B in pairs(U:_getTouchingScenery(o.id,K))do m[B]=true end return m end function ContentsManager.remove(m,B,K,o,U)local a=m:getCell(Vector3FromString(K))local H=a.contents.objects[B]if H==nil then if not o then warn("\084\114\105\101\100 \116\111 \114\101\109\111\118\101 "..(B.name..(" \105\110 \099\101\108\108 "..(tostring(a.id).." \119\104\101\114\101 \110\111\110\101 \101\120\105\115\116\101\100"))))end return end a.occupancy=bit32.band(bit32.bnot(B.occupancy),a.occupancy)for K=0,13,1 do if bit32.extract(B.occupancy,K)==1 then a.occupancyMap[tostring(K)]=nil end end for K,o in pairs(BitOps.FACES)do local U=m:getCell(a.id+o)U.occupancy=BitOps.removeFaceOccupancy(U.occupancy,-1*o,B.occupancy,o)end for K,o in pairs(BitOps.FACES)do local U=BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(o))]if bit32.extract(B.occupancy,U)==1 then local K=m:getCell(a.id+o)local U=BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(-1*o))]K.occupancyMap[tostring(U)]=nil local H,S,Z=B.fromString(B.name)local c=(S+180)%360 local q=m.grid.objectTypes[H..("\036"..(tostring(c)..("\036"..tostring(Z))))]K.contents.objects[q]=nil end end for K=0,23,1 do if bit32.extract(B.connectivity,K)==1 then local B=m:_allocateConnectivityMapForIndex(a.id,K)B[H]=nil if next(B)==nil then m:_deallocateConnectivityMapForIndex(a.id,K)a.connectivity=bit32.replace(a.connectivity,0,K)end end end for K,o in pairs(BitOps.FACES)do for K,U in ipairs(BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(o))])do if bit32.extract(B.connectivity,U)==1 then local B=BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(-1*o))]local U=B[K]local S=m:_allocateConnectivityMapForIndex(a.id+o,U)S[H]=nil if next(S)==nil then local B=m:getCell(a.id+o)m:_deallocateConnectivityMapForIndex(B.id,U)B.connectivity=bit32.replace(B.connectivity,0,U)end end end end for K,o in pairs(BitOps.CONNECTIVITY_INDEXES_FOR_EDGE)do local U=Vector3FromString(K)local S=#o for K,o in ipairs(o)do if bit32.extract(B.connectivity,o)~=0 then local B=m:getCell(a.id+U)local o=BitOps.normalizeVec3(-1*U)local Z=BitOps.CONNECTIVITY_INDEXES_FOR_EDGE[tostring(o)]local c=K if U.Y~=0 then c=(S+1)-c end local q=m:_allocateConnectivityMapForIndex(B.id,Z[c])q[H]=nil if next(q)==nil then B.connectivity=bit32.replace(B.connectivity,0,Z[c])end end end end a.contents.objects[B]=nil for K,o in pairs(BitOps.FACES)do local U=BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(o))]if bit32.extract(B.occupancy,U)==1 then local K=m:getCell(a.id+o)local U,H,S=B.fromString(B.name)local Z=(H+180)%360 local c=m.grid.objectTypes[U..("\036"..(tostring(Z)..("\036"..tostring(S))))]K.contents.objects[c]=nil end end if not U then H:Destroy()end local S={a}for B,K in pairs(BitOps.FACES)do table.insert(S,m:getCell(a.id+K))end for B in pairs(BitOps.CONNECTIVITY_INDEXES_FOR_EDGE)do table.insert(S,m:getCell(a.id+Vector3FromString(B)))end for B,K in ipairs(S)do if K.occupancy==0 and K.connectivity==0 then m:_deallocateCell(K.id)end end if DEBUG then local o="\067\076\073\069\078\084 - "if isServer then o="\083\069\082\086\069\082 - "end print(o..("\067\111\110\116\101\110\116\115\077\097\110\097\103\101\114 \099\111\110\116\101\110\116\115 \102\111\114 \102\111\108\100\101\114 "..(m.folder.Name..(" \097\102\116\101\114 \114\101\109\111\118\105\110\103 "..(B.name..(" \116\111 "..K))))))print(Util.debugPrint(m.contents))end m:broadcastContentsChange({type=REMOVED_OBJECT;cellId=a.id,objectType=B;doNotDestroy=U})if isServer then RemoveGridObject:FireAllClients(B:toString(a),U)end end function ContentsManager.removeByName(U,B,K,o)local m={}for B in B:gmatch("\091\094\124\093\043")do table.insert(m,B)end local a=m[1]local H=m[2]if U.grid.objectTypes[a]then U:remove(U.grid.objectTypes[a],H,K,o)else warn("\067\111\117\108\100 \110\111\116 \102\105\110\100 \111\098\106\101\099\116 \116\121\112\101 "..a)end end function ContentsManager.destroy(B) end return ContentsManager

function ContentsManager:_getTouchingScenery(cellId, objectType)
	local scenerySet = {}
	local cell = self:getCell(cellId)
	local rayDirection = Vector3.new(0, -self.grid.CELL_DIMENSIONS.Y, 0)
	local rayOrigins = {
		cell.center,
		cell.center + Vector3.new(self.grid.CELL_DIMENSIONS.X / 2, 0, 0),
		cell.center - Vector3.new(self.grid.CELL_DIMENSIONS.X / 2, 0, 0),
		cell.center + Vector3.new(0, 0, self.grid.CELL_DIMENSIONS.Z / 2),
		cell.center - Vector3.new(0, 0, self.grid.CELL_DIMENSIONS.Z / 2)
	}

	for _, rayOrigin in ipairs(rayOrigins) do
		local part = workspace:FindPartOnRayWithWhitelist(Ray.new(rayOrigin, rayDirection), Util.getTableKeys(SceneryManager.scenery), true)
		if part then
			scenerySet[part] = true
		end
	end

	return scenerySet
end

function ContentsManager:addForced(objectType, cell)
	return self:add(objectType, cell, nil, true)
end

function ContentsManager:add(objectType, cellId, instance, forced)
	if forced or self:canPlace(objectType, cellId) then
		if instance ~= nil then
			self:_addToContents(instance, objectType, cellId)
		else
			local gridObject = objectType:render(self, self:getCell(cellId))
			self:_addToContents(gridObject, objectType, cellId)

			-- TODO: Move this check into Grid/init.lua
			if isServer then
				local touchingObjects = self:getTouchingObjects(gridObject, objectType, self:getCell(cellId))
				weldTouchingObjects(gridObject, touchingObjects)
				gridObject.Anchored = false
			end
		end

		return true
	else
		return false
	end
end

function ContentsManager:_allocateCell(cellId)
	local cellName = tostring(cellId)
	self.contents[cellName] = self:getCell(cellId)
	return self.contents[cellName]
end

function ContentsManager:_deallocateCell(cellId)
	local cellName = tostring(cellId)
	self.contents[cellName] = nil
end

function ContentsManager:_allocateConnectivityMapForIndex(cellId, index)
	local cell = self:_allocateCell(cellId)
	local indexAsString = tostring(index)

	if cell.connectivityMap[indexAsString] == nil then
		cell.connectivityMap[indexAsString] = {}
	end

	return cell.connectivityMap[indexAsString]
end

function ContentsManager:_deallocateConnectivityMapForIndex(cellId, index)
	local cell = self:getCell(cellId)
	local indexAsString = tostring(index)

	cell.connectivityMap[indexAsString] = nil
end

function ContentsManager:_addToContents(object, objectType, cellId)
	local cell = self:_allocateCell(cellId)

	cell.contents.objects[objectType] = object

	-- Update occupancy for cell
	cell.occupancy = bit32.bor(objectType.occupancy, cell.occupancy)

	-- Update occupancyMap for cell
	for index = 0, 13 do
		if bit32.extract(objectType.occupancy, index) == 1 then
			cell.occupancyMap[tostring(index)] = object
		end
	end

	-- Update occupancy and contents for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		local adjacentCell = self:_allocateCell(cellId + face)
		adjacentCell.occupancy =
			BitOps.applyFaceOccupancy(adjacentCell.occupancy, -1 * face, objectType.occupancy, face)

		if bit32.btest(BitOps.OCCUPANCY_FACE_MASK[tostring(BitOps.normalizeVec3(face))], objectType.occupancy) then
			local simpleObjectType, objectTypeRotation, objectFaceRotation = objectType.fromString(objectType.name)
			local adjacentObjectTypeRotation = (objectTypeRotation + 180) % 360
			local adjacentObjectType = self.grid.objectTypes[simpleObjectType .. "$" .. tostring(adjacentObjectTypeRotation) .. "$" .. tostring(objectFaceRotation)]
			adjacentCell.contents.objects[adjacentObjectType] = object
		end
	end

	-- Update occupancyMap for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		local occupancyFaceIndex = BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(face))]
		if bit32.extract(objectType.occupancy, occupancyFaceIndex) == 1 then
			-- TODO: Don't allocate cell unless you have to
			local adjacentCell = self:_allocateCell(cellId + face)
			local occupancyIndexForAdjacentFace = BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(-1 * face))]
			adjacentCell.occupancyMap[tostring(occupancyIndexForAdjacentFace)] = object
		end
	end

	-- Update connectivity for cell
	cell.connectivity = bit32.bor(objectType.connectivity, cell.connectivity)

	-- Update connectivityMap for cell
	for index = 0, 23 do
		if bit32.extract(objectType.connectivity, index) == 1 then
			local setOfObjectsConnectedToPoint = self:_allocateConnectivityMapForIndex(cellId, index)
			setOfObjectsConnectedToPoint[object] = true
		end
	end

	-- Update connectivity for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		-- TODO: Don't allocate cell unless you have to
		local adjacentCell = self:_allocateCell(cellId + face)
		adjacentCell.connectivity =
			BitOps.applyFaceConnectivity(adjacentCell.connectivity, -1 * face, objectType.connectivity, face)
	end

	-- Update connectivityMap for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		for listIndex, bitmaskIndex in ipairs(BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(face))]) do
			if bit32.extract(objectType.connectivity, bitmaskIndex) == 1 then
				local connectivityIndexesForAdjacentFace = BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(-1 * face))]
				local adjacentBitmaskIndex = connectivityIndexesForAdjacentFace[listIndex]
				local setOfObjectsConnectedToAdjacentPoint = self:_allocateConnectivityMapForIndex(cellId + face, adjacentBitmaskIndex)
				setOfObjectsConnectedToAdjacentPoint[object] = true
			end
		end
	end

	-- Update connectivity and connectivityMap for diagonally adjacent cells
	for edgeAsString, connectivityIndexes in pairs(BitOps.CONNECTIVITY_INDEXES_FOR_EDGE) do
		local edge = Vector3FromString(edgeAsString)
		local connectivityIndexesSize = #connectivityIndexes
		for i, connectivityIndex in ipairs(connectivityIndexes) do
			if bit32.extract(objectType.connectivity, connectivityIndex) ~= 0 then
				-- TODO: Don't allocate cell unless you have to
				local diagonallyAdjacentCell = self:_allocateCell(cellId + edge)
				local diagonallyAdjacentEdge = BitOps.normalizeVec3(-1 * edge)
				local diagonallyAdjacentEdgeConnectivityIndexes = BitOps.CONNECTIVITY_INDEXES_FOR_EDGE[tostring(diagonallyAdjacentEdge)]

				-- Flip the order you traverse the diagonally adjacent edge's
				-- connectivity indexes if the edge is perpendicular to the Y axis
				local j = i
				if edge.Y ~= 0 then
					j = connectivityIndexesSize + 1 - j
				end

				diagonallyAdjacentCell.connectivity =
					bit32.replace(diagonallyAdjacentCell.connectivity, 1, diagonallyAdjacentEdgeConnectivityIndexes[j])

				local setOfObjectsConnectedToAdjacentPoint = self:_allocateConnectivityMapForIndex(
					diagonallyAdjacentCell.id,
					diagonallyAdjacentEdgeConnectivityIndexes[j]
				)
				setOfObjectsConnectedToAdjacentPoint[object] = true
			end
		end
	end

	if DEBUG then
		local system = "CLIENT - "
		if isServer then
			system = "SERVER - "
		end
		print(system .. "ContentsManager contents for folder " .. self.folder.Name .. " after adding " .. objectType.name .. " to " .. tostring(cellId))
		print(Util.debugPrint(self.contents))
	end

	self:broadcastContentsChange({
		type = ADDED_OBJECT,
		cellId = cellId,
		objectType = objectType,
		object = object,
	})
end

function ContentsManager:broadcastContentsChange(change)
	if self.contentsListenerCount > 0 then
		for callback in pairs(self.contentsListeners) do
			callback(change)
		end
	end
end

function ContentsManager:getTouchingObjects(object, objectType, cell)
	local result = {}

	-- Must be connected to the same object by at least two connectivity points
	local setOfConnectedObjects = {}
	for i = 0, 23 do
		local key = tostring(i)
		if bit32.extract(objectType.connectivity, i) == 1 and cell.connectivityMap[key] ~= nil then
			for touchingObject in pairs(cell.connectivityMap[key]) do
				if setOfConnectedObjects[touchingObject] then
					result[touchingObject] = true
				else
					setOfConnectedObjects[touchingObject] = true
				end
			end
		end
	end

	for sceneryObject in pairs(self:_getTouchingScenery(cell.id, objectType)) do
		result[sceneryObject] = true
	end

	return result
end

function ContentsManager:remove(objectType, cellName, suppressWarning, doNotDestroy)
	local cell = self:getCell(Vector3FromString(cellName))

	-- Get object
	local object = cell.contents.objects[objectType]
	if object == nil then
		if not suppressWarning then
			warn("Tried to remove " .. objectType.name .. " in cell " .. tostring(cell.id) .. " where none existed")
		end

		return
	end

	-- Update occupancy for cell
	cell.occupancy = bit32.band(bit32.bnot(objectType.occupancy), cell.occupancy)

	-- Update occupancyMap for cell
	for index = 0, 13 do
		if bit32.extract(objectType.occupancy, index) == 1 then
			cell.occupancyMap[tostring(index)] = nil
		end
	end

	-- Update occupancy for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		local adjacentCell = self:getCell(cell.id + face)
		adjacentCell.occupancy =
			BitOps.removeFaceOccupancy(adjacentCell.occupancy, -1 * face, objectType.occupancy, face)
	end

	-- Update occupancyMap for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		local occupancyFaceIndex = BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(face))]
		if bit32.extract(objectType.occupancy, occupancyFaceIndex) == 1 then
			local adjacentCell = self:getCell(cell.id + face)
			local occupancyIndexForAdjacentFace = BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(-1 * face))]
			adjacentCell.occupancyMap[tostring(occupancyIndexForAdjacentFace)] = nil

			local simpleObjectType, objectTypeRotation, objectFaceRotation = objectType.fromString(objectType.name)
			local adjacentObjectTypeRotation = (objectTypeRotation + 180) % 360
			local adjacentObjectType = self.grid.objectTypes[simpleObjectType .. "$" .. tostring(adjacentObjectTypeRotation) .. "$" .. tostring(objectFaceRotation)]
			adjacentCell.contents.objects[adjacentObjectType] = nil
		end
	end

	-- Update connectivity and connectivityMap for cell
	for index = 0, 23 do
		if bit32.extract(objectType.connectivity, index) == 1 then
			local setOfObjectsConnectedToPoint = self:_allocateConnectivityMapForIndex(cell.id, index)
			setOfObjectsConnectedToPoint[object] = nil
			if next(setOfObjectsConnectedToPoint) == nil then
				self:_deallocateConnectivityMapForIndex(cell.id, index)
				cell.connectivity = bit32.replace(cell.connectivity, 0, index)
			end
		end
	end

	-- Update connectivity and connectivityMap for orthogonally adjacent cells
	for _, face in pairs(BitOps.FACES) do
		for listIndex, bitmaskIndex in ipairs(BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(face))]) do
			if bit32.extract(objectType.connectivity, bitmaskIndex) == 1 then
				local connectivityIndexesForAdjacentFace = BitOps.CONNECTIVITY_INDEXES_FOR_FACE[tostring(BitOps.normalizeVec3(-1 * face))]
				local adjacentBitmaskIndex = connectivityIndexesForAdjacentFace[listIndex]
				local setOfObjectsConnectedToAdjacentPoint = self:_allocateConnectivityMapForIndex(cell.id + face, adjacentBitmaskIndex)
				setOfObjectsConnectedToAdjacentPoint[object] = nil
				if next(setOfObjectsConnectedToAdjacentPoint) == nil then
					local adjacentCell = self:getCell(cell.id + face)
					self:_deallocateConnectivityMapForIndex(adjacentCell.id, adjacentBitmaskIndex)
					adjacentCell.connectivity = bit32.replace(adjacentCell.connectivity, 0, adjacentBitmaskIndex)
				end
			end
		end
	end

	-- Update connectivity and connectivityMap for diagonally adjacent cells
	for edgeAsString, connectivityIndexes in pairs(BitOps.CONNECTIVITY_INDEXES_FOR_EDGE) do
		local edge = Vector3FromString(edgeAsString)
		local connectivityIndexesSize = #connectivityIndexes
		for i, connectivityIndex in ipairs(connectivityIndexes) do
			if bit32.extract(objectType.connectivity, connectivityIndex) ~= 0 then
				local diagonallyAdjacentCell = self:getCell(cell.id + edge)
				local diagonallyAdjacentEdge = BitOps.normalizeVec3(-1 * edge)
				local diagonallyAdjacentEdgeConnectivityIndexes = BitOps.CONNECTIVITY_INDEXES_FOR_EDGE[tostring(diagonallyAdjacentEdge)]

				-- Flip the order you traverse the diagonally adjacent edge's
				-- connectivity indexes if the edge is perpendicular to the Y axis
				local j = i
				if edge.Y ~= 0 then
					j = connectivityIndexesSize + 1 - j
				end

				local setOfObjectsConnectedToAdjacentPoint = self:_allocateConnectivityMapForIndex(
					diagonallyAdjacentCell.id,
					diagonallyAdjacentEdgeConnectivityIndexes[j]
				)
				setOfObjectsConnectedToAdjacentPoint[object] = nil
				if next(setOfObjectsConnectedToAdjacentPoint) == nil then
					diagonallyAdjacentCell.connectivity =
						bit32.replace(diagonallyAdjacentCell.connectivity, 0, diagonallyAdjacentEdgeConnectivityIndexes[j])
				end
			end
		end
	end

	-- Remove object from cell contents
	cell.contents.objects[objectType] = nil

	-- Remove object from adjacent cell contents
	for _, face in pairs(BitOps.FACES) do
		local occupancyFaceIndex = BitOps.OCCUPANCY_INDEX_FOR_FACE[tostring(BitOps.normalizeVec3(face))]
		if bit32.extract(objectType.occupancy, occupancyFaceIndex) == 1 then
			local adjacentCell = self:getCell(cell.id + face)
			local simpleObjectType, objectTypeRotation, objectFaceRotation = objectType.fromString(objectType.name)
			local adjacentObjectTypeRotation = (objectTypeRotation + 180) % 360
			local adjacentObjectType = self.grid.objectTypes[simpleObjectType .. "$" .. tostring(adjacentObjectTypeRotation) .. "$" .. tostring(objectFaceRotation)]
			adjacentCell.contents.objects[adjacentObjectType] = nil
		end
	end

	-- Destroy object
	if not doNotDestroy then
		object:Destroy()
	end

	-- Deallocate all affected cells that are empty and disconnected
	local affectedCells = { cell }
	for _, face in pairs(BitOps.FACES) do
		table.insert(affectedCells, self:getCell(cell.id + face))
	end
	for edge in pairs(BitOps.CONNECTIVITY_INDEXES_FOR_EDGE) do
		table.insert(affectedCells, self:getCell(cell.id + Vector3FromString(edge)))
	end

	for _, affectedCell in ipairs(affectedCells) do
		if affectedCell.occupancy == 0 and affectedCell.connectivity == 0 then
			self:_deallocateCell(affectedCell.id)
		end
	end

	if DEBUG then
		local system = "CLIENT - "
		if isServer then
			system = "SERVER - "
		end
		print(system .. "ContentsManager contents for folder " .. self.folder.Name .. " after removing " .. objectType.name .. " to " .. cellName)
		print(Util.debugPrint(self.contents))
	end

	self:broadcastContentsChange({
		type = REMOVED_OBJECT,
		cellId = cell.id,
		objectType = objectType,
		doNotDestroy = doNotDestroy,
	})

	-- TODO: This should be moved into Grid/init.lua
	if isServer then
		RemoveGridObject:FireAllClients(objectType:toString(cell), doNotDestroy)
	end
end

function ContentsManager:removeByName(name, suppressWarning, doNotDestroy)
	local parsedName = {}
	for word in name:gmatch("[^|]+") do
		table.insert(parsedName, word)
	end

	local objectTypeName = parsedName[1]
	local cellName = parsedName[2]

	if self.grid.objectTypes[objectTypeName] then
		self:remove(self.grid.objectTypes[objectTypeName], cellName, suppressWarning, doNotDestroy)
	else
		warn("Could not find object type " .. objectTypeName)
	end
end

function ContentsManager:destroy()
	-- TODO
end

return ContentsManager
