Optimization of Making new parts, similar to mining games

I am recreating Minecraft world generation, so I need to render many parts. The max and ideal is 268,435,456 parts. The max I managed to generate was ~300,000 parts before the script timed out. I have implemented Greedymeshing, Client Rendering, LOD, and Data compression for optimizations.

Play with it here: PartsOptimizationV2.rbxl (57.4 KB)

Server Script
local http=game:GetService("HttpService")
--Inputs
local partBasesize=4096
local subdividecount=12
local RenderRadius=5
local BigChunkSize=256
local SmallChunkSize=32
--Derives
----local MegaChunks
local PartSize=partBasesize/math.pow(2,subdividecount)
local Factor=partBasesize/PartSize

local BigChunks=Factor/BigChunkSize
local SmallChunks=BigChunkSize/SmallChunkSize

local bottompos=-((Factor-1)*PartSize)/2
local count=0

print(PartSize,Factor,Factor*Factor,BigChunks,SmallChunks)

local world={}

for i=1,BigChunks do
	table.insert(world,{})
	for o=1,BigChunks do
		table.insert(world[i],{})
		for p=1,SmallChunks do
			table.insert(world[i][o],{})
			for a=1,SmallChunks do
				table.insert(world[i][o][p],{})
				for s=1,SmallChunkSize do
					table.insert(world[i][o][p][a],{})
					for d=1,SmallChunkSize do
						count+=1
						table.insert(world[i][o][p][a][s], if math.random(1,10)==1 then "l" else "o")
						if count%100000==0 then task.wait() print("working") end
					end
				end
			end
		end
		world[i][o]=http:JSONEncode(world[i][o])
	end
end

--local function makeport(size)
--	local Part=Instance.new("Part",game.Workspace)
--	count+=1
--	if count%10000==0 then task.wait() print("working") end
--	Part.Anchored=true
--	Part.Size=Vector3.new(size,1,size)
--	Part.TopSurface=Enum.SurfaceType.Smooth
--	Part.BottomSurface=Enum.SurfaceType.Smooth
--	return Part
--end

--for i,ServerColumn in ipairs(world) do
--	for o,ServerChunk in ipairs(ServerColumn) do
--		local UnzippedChunk=http:JSONDecode(ServerChunk)
--		for p,ChunkColumn in ipairs(UnzippedChunk) do
--			for a,Chunk in ipairs(ChunkColumn) do
--				for s,column in ipairs(Chunk) do
--					for d,part in ipairs(column) do
--						local Part=makeport(PartSize)
--						Part.Position=Vector3.new(
--							bottompos+(i-1)*BigChunkSize*PartSize+(p-1)*SmallChunkSize*PartSize+(s-1)*PartSize
--							,0.5,
--							bottompos+(o-1)*BigChunkSize*PartSize+(a-1)*SmallChunkSize*PartSize+(d-1)*PartSize
--						)
--						if part=="l" then
--							Part.BrickColor=BrickColor.new("Bright green")
--						else
--							Part.BrickColor=BrickColor.new("Bright blue")
--						end
--					end
--				end
--			end
--		end
--	end
--end

game.ReplicatedStorage.StartingValues.OnServerInvoke=function()
	return PartSize,Factor,bottompos,RenderRadius,SmallChunkSize,BigChunkSize
end

local unzippedChunkIndex={0,0}
local unzippedChunk=nil

game.ReplicatedStorage.GetChunks.OnServerInvoke=function(Player,ChunksWanted)
	local ToRender={}
	for i,column in pairs(ChunksWanted) do
		for o,Chunk in pairs(column) do
			local ServerChunkX=math.ceil(i/SmallChunks)
			local InsideChunkX=i-(ServerChunkX-1)*SmallChunks
			local ServerChunkZ=math.ceil(o/SmallChunks)
			local InsideChunkZ=o-(ServerChunkZ-1)*SmallChunks
			if unzippedChunkIndex[1]~=ServerChunkX or unzippedChunkIndex[2]~=ServerChunkZ then
				unzippedChunk=http:JSONDecode(world[ServerChunkX][ServerChunkZ])
				unzippedChunkIndex={ServerChunkX,ServerChunkZ}
			end
			if not ToRender[i] then
				ToRender[i]={}
			end
			ToRender[i][o]=unzippedChunk[InsideChunkX][InsideChunkZ]
		end
	end
	return ToRender
end
Client Script
local player=game.Players.LocalPlayer
local http=game:GetService("HttpService")
local Chunks={}
local PlayerArraypos={0,0}
player.CharacterAdded:Wait()

local PartSize,Factor,bottompos,RenderRadius,SmallChunkSize,BigChunkSize=game.ReplicatedStorage.StartingValues:InvokeServer()

local acceptabledistance=1

local function GetHeight(chunk,startposX,startposZ,PartType)
	local column=chunk[startposX]
	local height=0
	for i=startposZ,#column do
		local nextpart=column[i]
		if nextpart==PartType then
			height+=1
			chunk[startposX][i]="V"
		else
			break
		end
	end
	return chunk,height
end

local function GetWidth(chunk,startposX,startposZ,PartType,height)
	local width=1
	for i=startposX+1,#chunk do
		local column=chunk[i]
		local Freecolumn=true
		for o=startposZ,startposZ+height-1 do
			local newpart=column[o]
			if newpart~=PartType then
				Freecolumn=false
				break
			end
		end
		
		if Freecolumn then
			width+=1
			for o=startposZ,startposZ+height-1 do
				chunk[i][o]="V"
			end
		else
			break
		end
	end
	return chunk,width
end

local function ResetChunks()
	for i, chunkcolumn in pairs(Chunks) do
		for o,chunk in pairs(chunkcolumn) do
			Chunks[i][o]="C"
		end
	end
end

local function FindOverlap(bottomarrayposX,bottomarrayposZ)
	for i=bottomarrayposX,bottomarrayposX+RenderRadius*2 do
		if not Chunks[i] then 
			Chunks[i]={}
		end
		for o=bottomarrayposZ,bottomarrayposZ+RenderRadius*2 do
			if Chunks[i][o] then
				Chunks[i][o]="O"
			else
				Chunks[i][o]="M"
			end
		end
	end
end

local function RemoveOverlap()
	local RenderChunks={}
	for i, chunkcolumn in pairs(Chunks) do
		for o,chunk in pairs(chunkcolumn) do
			if chunk=="M" then
				if not RenderChunks[i] then
					RenderChunks[i]={}
				end
				RenderChunks[i][o]="M"
			end
		end
	end
	return RenderChunks
end

local function GetSacraficialChunks()
	local TakeChunks={}
	for i, chunkcolumn in pairs(Chunks) do
		for o,chunk in pairs(chunkcolumn) do
			if chunk=="C" then
				table.insert(TakeChunks,tostring(i..","..o))

				Chunks[i][o]=nil
				local length=0; for _ in pairs(Chunks[i]) do length+=1 end if length==0 then
					Chunks[i]=nil
				end
			end
		end
	end
	return TakeChunks
end

local function GetPartBank(TakeChunks)
	local SacraficialChunk
	local PossibleParts={}
	if #TakeChunks>0 then
		SacraficialChunk=game.Workspace:FindFirstChild(TakeChunks[1])
		table.remove(TakeChunks,1)
		PossibleParts=SacraficialChunk:GetChildren()
	end
	return SacraficialChunk,PossibleParts
end

local function AdjustPart(Part,partwidth,partheight,folder,chunkposX,chunkposZ,posX,posZ,Type)
	Part.Size=Vector3.new(partwidth*PartSize,1,partheight*PartSize)
	Part.Parent=folder
	Part.Position=Vector3.new(
		bottompos+((chunkposX-1)*SmallChunkSize*PartSize)+((posX-1)*PartSize)+((PartSize*partwidth)/2)-(PartSize/2)
		,0.5,
		bottompos+((chunkposZ-1)*SmallChunkSize*PartSize)+((posZ-1)*PartSize)+((PartSize*partheight)/2)-(PartSize/2)
	)
	if Type=="l" then
		Part.BrickColor=BrickColor.new("Bright green")
	else
		Part.BrickColor=BrickColor.new("Bright blue")
	end
end

local function MakeorFindPart(PossibleParts)
	local Part
	if #PossibleParts>0 then
		Part=PossibleParts[1]
		table.remove(PossibleParts,1)
	else
		Part=Instance.new("Part")
		Part.Anchored=true
		Part.TopSurface=Enum.SurfaceType.Smooth
		Part.BottomSurface=Enum.SurfaceType.Smooth
	end
	return Part
end

local function Render(ArrayposX,ArrayposZ)

	local bottomarrayposX=ArrayposX-RenderRadius
	local bottomarrayposZ=ArrayposZ-RenderRadius

	ResetChunks()

	FindOverlap(bottomarrayposX,bottomarrayposZ)


	local RenderChunks=RemoveOverlap()
	
	print(RenderChunks)


	local TakeChunks=GetSacraficialChunks()
	
	local ChunkValues=game.ReplicatedStorage.GetChunks:InvokeServer(RenderChunks)

	local count=0
	for i,chunkcolumn in pairs(ChunkValues) do
		for o,chunk in pairs(chunkcolumn) do
			local folder=Instance.new("Folder",game.Workspace)
			folder.Name=tostring(i..","..o)
			
			local SacraficialChunk,PossibleParts=GetPartBank(TakeChunks)

			for p=1,#chunk do
				local column=chunk[p]
				for a=1,#column do
					local partype=column[a]
					if partype=="V" then continue end
					count+=1
					
					local partheight
					chunk,partheight=GetHeight(chunk,p,a,partype)
					column=chunk[p]
					
					local partwidth
					chunk,partwidth=GetWidth(chunk,p,a,partype,partheight)
					
					local Part=MakeorFindPart(PossibleParts)
					
					AdjustPart(Part,partwidth,partheight,folder,i,o,p,a,partype)
				end
			end
			
			if SacraficialChunk then
				SacraficialChunk:Destroy()
			end
		end
	end
	print(count)
end

while wait(1) do
	local PlayerPos=player.Character:GetPivot().Position

	local ArrayposX=math.ceil(math.ceil((PlayerPos.X)-bottompos)/PartSize/SmallChunkSize)
	local ArrayposZ=math.ceil(math.ceil((PlayerPos.Z)-bottompos)/PartSize/SmallChunkSize)


	if ArrayposX>=PlayerArraypos[1]+acceptabledistance or ArrayposX<=PlayerArraypos[1]-acceptabledistance or 
	   ArrayposZ>=PlayerArraypos[2]+acceptabledistance or ArrayposZ<=PlayerArraypos[2]-acceptabledistance then
		PlayerArraypos={ArrayposX,ArrayposZ}
		Render(ArrayposX,ArrayposZ)
	end
end

I want to optimize my code further for larger rendering sizes and faster rendering speed. Also, implement any best coding practices to make my code more readable.

Explanations

The Large Comment section in the server script is the total world generator, which helps show errors between the rendered world and the data.
The purpose of the Big Chunks is to act as individual packets of data that will be compressed. Instead of compressing the entire array, the big chunks allow less data to be decompressed when the script tries to get data.

Small Chunks must fit inside a big chunk and serve as the individual data that will be sent to the client to render. They allow for many optimizations. Each part in the chunk is also inside a folder of the same name, which is the resolution the greedymesher runs on.
The Client Script handles an array of the chunks currently visible. It works as such: Every second, it gets the chunk the player stands on; if that chunk has moved, it renders new chunks. Discarding chunks that don’t need to be changed, it takes parts from chunks that will no longer be visible to use in new chunks. After rendering the new chunk, it destroys the old chunk.
I can’t have the greedymesher run on the whole rendered area, as then that would require parts to extend outside of their chunk. If I can’t rely on parts only being inside their chunk, I can’t throw away chunks that don’t need to change, as they might contain a part that extends to another chunk. Having the greedymesher run on a single chunk limits the greedymesher’s efficiency as it can’t combine parts across chunks.

A problem with this is that in my actual game, there may be large swaths of blue cut into small chunk sizes as the greedymesher can’t combine two identical chunks; this may be a problem.
The greedymesher works by looping over the chunk to find the width and height of a group of parts; finding the width requires a lot of looping. I don’t know how badly this affects performance.
To reiterate, I want to make my code better and faster to render. The primary sources of lag I see are the :JSONDecode()s that unzip the data for each render, and the simple moving around of so many parts.

I talked to BrokenBone (the creator of mining tech, a game that also needs to render large amounts of individual parts) on this matter. He said it was possible to get “up to billions of blocks.” I don’t know if these are virtual parts or rendered parts.

RenderRadius=19 world (largest possible)


When walking to the side, the renderer rendered 7788 new parts, some taken from chunks moved out of render range and others made.

I have thought of implementing a system where the renderer will render each chunk as one part for chunks far away, potentially increasing render size for little cost. I have also thought of making a spiral renderer that will render chunks closer to the player faster and then gradually go outwards, perhaps better than the current system of generating a whole pass in one go. I don’t know how this will work with the player moving around.
Sorry if this is very long and complicated.

2 Likes

I have implemented Parallel Processing. It has cut down the loading time from around 379.2 ms in the post above, to 221.2 ms.

There should be no changes to the client script, though the server script is changed substancially.

Server Script
local http=game:GetService("HttpService")
local Actors=script.Parent.Parent.Folder:GetChildren()
local Unzipped={}
local CurrentActor=1
--Inputs
local partBasesize=4096
local subdividecount=12
local RenderRadius=19
local BigChunkSize=256
local SmallChunkSize=32
local LOD=math.huge
--Derives
----local MegaChunks
local PartSize=partBasesize/math.pow(2,subdividecount)
local Factor=partBasesize/PartSize

local BigChunks=Factor/BigChunkSize
local SmallChunks=BigChunkSize/SmallChunkSize

local bottompos=-((Factor-1)*PartSize)/2
local count=0

print(PartSize,Factor,Factor*Factor,BigChunks,SmallChunks)

local world={}

for i=1,BigChunks do
	table.insert(world,{})
	for o=1,BigChunks do
		table.insert(world[i],{})
		for p=1,SmallChunks do
			table.insert(world[i][o],{})
			for a=1,SmallChunks do
				table.insert(world[i][o][p],{})
				for s=1,SmallChunkSize do
					table.insert(world[i][o][p][a],{})
					for d=1,SmallChunkSize do
						count+=1
						table.insert(world[i][o][p][a][s], if math.random(1,10)==1 then "l" else "o")
						if count%100000==0 then task.wait() print("working") end
					end
				end
			end
		end
		world[i][o]=http:JSONEncode(world[i][o])
	end
end

--local function makeport(size)
--	local Part=Instance.new("Part",game.Workspace)
--	count+=1
--	if count%10000==0 then task.wait() print("working") end
--	Part.Anchored=true
--	Part.Size=Vector3.new(size,1,size)
--	Part.TopSurface=Enum.SurfaceType.Smooth
--	Part.BottomSurface=Enum.SurfaceType.Smooth
--	return Part
--end

--for i,ServerColumn in ipairs(world) do
--	for o,ServerChunk in ipairs(ServerColumn) do
--		local UnzippedChunk=http:JSONDecode(ServerChunk)
--		for p,ChunkColumn in ipairs(UnzippedChunk) do
--			for a,Chunk in ipairs(ChunkColumn) do
--				for s,column in ipairs(Chunk) do
--					for d,part in ipairs(column) do
--						local Part=makeport(PartSize)
--						Part.Position=Vector3.new(
--							bottompos+(i-1)*BigChunkSize*PartSize+(p-1)*SmallChunkSize*PartSize+(s-1)*PartSize
--							,0.5,
--							bottompos+(o-1)*BigChunkSize*PartSize+(a-1)*SmallChunkSize*PartSize+(d-1)*PartSize
--						)
--						if part=="l" then
--							Part.BrickColor=BrickColor.new("Bright green")
--						else
--							Part.BrickColor=BrickColor.new("Bright blue")
--						end
--					end
--				end
--			end
--		end
--	end
--end

game.ReplicatedStorage.StartingValues.OnServerInvoke=function()
	return PartSize,Factor,bottompos,RenderRadius,SmallChunkSize,BigChunkSize,LOD
end

game.ReplicatedStorage.GetChunks.OnServerInvoke = function(Player,ChunksWanted)
	debug.profilebegin("GetToUnzip")
	table.clear(Unzipped)
	local ToUnzip={}
	for i,column in pairs(ChunksWanted) do
		for o,chunk in pairs(column) do
			local ServerChunkX=math.ceil(i/SmallChunks)
			local ServerChunkZ=math.ceil(o/SmallChunks)
			
			if not ToUnzip[ServerChunkX] then
				ToUnzip[ServerChunkX]={}
			end
			ToUnzip[ServerChunkX][ServerChunkZ]="TU"
		end
	end
	debug.profileend()
	
	debug.profilebegin("Send Messages")
	local Messages=0
	for i,column in pairs(ToUnzip) do
		for o,chunk in pairs(column) do
			Messages+=1
			Actors[CurrentActor]:SendMessage("Unzip",world[i][o],i,o)

			CurrentActor+=1
			if CurrentActor>#Actors then
				CurrentActor=1
			end
		end
	end
	debug.profileend()
	
	local MessageReturns=0
	script.Parent:BindToMessage("ReturningChunk",function(Chunk,posX,posZ)
		MessageReturns+=1
		if not Unzipped[posX] then
			Unzipped[posX]={}
		end
		Unzipped[posX][posZ]=Chunk
	end)
	
	task.wait()
	
	local ToRender={}
	for i,column in pairs(ChunksWanted) do
		for o,chunk in pairs(column) do
			local ServerChunkX=math.ceil(i/SmallChunks)
			local InsideChunkX=i-(ServerChunkX-1)*SmallChunks
			local ServerChunkZ=math.ceil(o/SmallChunks)
			local InsideChunkZ=o-(ServerChunkZ-1)*SmallChunks
			local UnzippedChunk=Unzipped[ServerChunkX][ServerChunkZ][InsideChunkX][InsideChunkZ]
			if not ToRender[i] then
				ToRender[i]={}
			end
			ToRender[i][o]=UnzippedChunk
		end
	end
	return ToRender
end
Client Script
local player=game.Players.LocalPlayer
local http=game:GetService("HttpService")
local Chunks={}
local PlayerArraypos={0,0}
player.CharacterAdded:Wait()

local PartSize,Factor,bottompos,RenderRadius,SmallChunkSize,BigChunkSize=game.ReplicatedStorage.StartingValues:InvokeServer()

local acceptabledistance=1

local function GetHeight(chunk,startposX,startposZ,PartType)
	local column=chunk[startposX]
	local height=0
	for i=startposZ,#column do
		local nextpart=column[i]
		if nextpart==PartType then
			height+=1
			chunk[startposX][i]="V"
		else
			break
		end
	end
	return chunk,height
end

local function GetWidth(chunk,startposX,startposZ,PartType,height)
	local width=1
	for i=startposX+1,#chunk do
		local column=chunk[i]
		local Freecolumn=true
		for o=startposZ,startposZ+height-1 do
			local newpart=column[o]
			if newpart~=PartType then
				Freecolumn=false
				break
			end
		end

		if Freecolumn then
			width+=1
			for o=startposZ,startposZ+height-1 do
				chunk[i][o]="V"
			end
		else
			break
		end
	end
	return chunk,width
end

local function ResetChunks()
	for i, chunkcolumn in pairs(Chunks) do
		for o,chunk in pairs(chunkcolumn) do
			Chunks[i][o]="C"
		end
	end
end

local function FindOverlap(bottomarrayposX,bottomarrayposZ)
	for i=bottomarrayposX,bottomarrayposX+RenderRadius*2 do
		if not Chunks[i] then 
			Chunks[i]={}
		end
		for o=bottomarrayposZ,bottomarrayposZ+RenderRadius*2 do
			if Chunks[i][o] then
				Chunks[i][o]="O"
			else
				Chunks[i][o]="M"
			end
		end
	end
end

local function RemoveOverlap()
	local RenderChunks={}
	for i, chunkcolumn in pairs(Chunks) do
		for o,chunk in pairs(chunkcolumn) do
			if chunk=="M" then
				if not RenderChunks[i] then
					RenderChunks[i]={}
				end
				RenderChunks[i][o]="M"
			end
		end
	end
	return RenderChunks
end

local function GetSacraficialChunks()
	local TakeChunks={}
	for i, chunkcolumn in pairs(Chunks) do
		for o,chunk in pairs(chunkcolumn) do
			if chunk=="C" then
				table.insert(TakeChunks,tostring(i..","..o))

				Chunks[i][o]=nil
				local length=0; for _ in pairs(Chunks[i]) do length+=1 end if length==0 then
					Chunks[i]=nil
				end
			end
		end
	end
	return TakeChunks
end

local function GetPartBank(TakeChunks)
	local SacraficialChunk
	local PossibleParts={}
	if #TakeChunks>0 then
		SacraficialChunk=game.Workspace:FindFirstChild(TakeChunks[1])
		table.remove(TakeChunks,1)
		PossibleParts=SacraficialChunk:GetChildren()
	end
	return SacraficialChunk,PossibleParts
end

local function AdjustPart(Part,partwidth,partheight,folder,chunkposX,chunkposZ,posX,posZ,Type)
	Part.Size=Vector3.new(partwidth*PartSize,1,partheight*PartSize)
	Part.Parent=folder
	Part.Position=Vector3.new(
		bottompos+((chunkposX-1)*SmallChunkSize*PartSize)+((posX-1)*PartSize)+((PartSize*partwidth)/2)-(PartSize/2)
		,0.5,
		bottompos+((chunkposZ-1)*SmallChunkSize*PartSize)+((posZ-1)*PartSize)+((PartSize*partheight)/2)-(PartSize/2)
	)
	if Type=="l" then
		Part.BrickColor=BrickColor.new("Bright green")
	else
		Part.BrickColor=BrickColor.new("Bright blue")
	end
end

local function MakeorFindPart(PossibleParts)
	local Part
	if #PossibleParts>0 then
		Part=PossibleParts[1]
		table.remove(PossibleParts,1)
	else
		Part=Instance.new("Part")
		Part.Anchored=true
		Part.TopSurface=Enum.SurfaceType.Smooth
		Part.BottomSurface=Enum.SurfaceType.Smooth
	end
	return Part
end

local function Render(ArrayposX,ArrayposZ)

	local bottomarrayposX=ArrayposX-RenderRadius
	local bottomarrayposZ=ArrayposZ-RenderRadius

	ResetChunks()

	FindOverlap(bottomarrayposX,bottomarrayposZ)


	local RenderChunks=RemoveOverlap()

	print(RenderChunks)
	
	local TakeChunks=GetSacraficialChunks()
	local ChunkValues=game.ReplicatedStorage.GetChunks:InvokeServer(RenderChunks)
	
	
	local count=0
	for i,chunkcolumn in pairs(ChunkValues) do
		for o,chunk in pairs(chunkcolumn) do
			local folder=Instance.new("Folder",game.Workspace)
			folder.Name=tostring(i..","..o)

			local SacraficialChunk,PossibleParts=GetPartBank(TakeChunks)

			for p=1,#chunk do
				local column=chunk[p]
				for a=1,#column do
					local partype=column[a]
					if partype=="V" then continue end
					count+=1

					local partheight
					chunk,partheight=GetHeight(chunk,p,a,partype)
					column=chunk[p]

					local partwidth
					chunk,partwidth=GetWidth(chunk,p,a,partype,partheight)

					local Part=MakeorFindPart(PossibleParts)

					AdjustPart(Part,partwidth,partheight,folder,i,o,p,a,partype)
				end
			end

			if SacraficialChunk then
				SacraficialChunk:Destroy()
			end
		end
	end
	print(count)
end

while wait(1) do
	local PlayerPos=player.Character:GetPivot().Position

	local ArrayposX=math.ceil(math.ceil((PlayerPos.X)-bottompos)/PartSize/SmallChunkSize)
	local ArrayposZ=math.ceil(math.ceil((PlayerPos.Z)-bottompos)/PartSize/SmallChunkSize)


	if ArrayposX>=PlayerArraypos[1]+acceptabledistance or ArrayposX<=PlayerArraypos[1]-acceptabledistance or 
		ArrayposZ>=PlayerArraypos[2]+acceptabledistance or ArrayposZ<=PlayerArraypos[2]-acceptabledistance then
		PlayerArraypos={ArrayposX,ArrayposZ}
		Render(ArrayposX,ArrayposZ)
	end
end

I used twelve actors each with this script inside of them.

local http=game:GetService("HttpService")
script.Parent:BindToMessageParallel("Unzip",function(Chunk,PositionX,PositionZ)
	local UnzippedChunk=http:JSONDecode(Chunk)
	
	game.ServerScriptService.Actor:SendMessage("ReturningChunk",UnzippedChunk,PositionX,PositionZ)
end)

I was not able to parallelize the creation of parts as Roblox does not allow for instances to be made in Parallel. I plan to lower the amount of parts needed with LOD, if the chunks are far away, the parts inside them will lower in resolution (combine with their neighbors).

A glaring flaw in my code shows up in the micro profiler as Simulation>deferredThreads. This is the part of serverscript that waits for return messages from the unzippers. I could not get this to be parallel with the unzippers themselves, so it is awkwardly pushed to the end, increasing the time it takes to make the world. (Serverscript, BindToMessage(“ReturningChunk”))
I could have either done this callback with Messages, or used a SharedTable.
The problem with the shared tables is they are too slow. When the unzippers were putting the unzipped chunk into the shared table. It was taking longer than the JSONDecode to just put the chunk in the shared table, many times so. Though I never tested how long it took compared to the messaging system.

If anyone has advice on how to improve my parallelization and/or fix the waiting for responses time sink, It would be much appreciated.

P.S. Render Size of 21 now possible. (~350,000 parts)
PartsOptimizationV3.rbxl (60.2 KB)

2 Likes

I have been trying to use shared tables instead of the message system, but that does not work as putting the tables into sharedtables converts the tables into shared tables. The problem with this is that I need to pass the table through a remote function, and sharedtables don’t do that.
If anyone has any knowledge on how to convert sharedtables to tables in order to pass through a remote function. Or any way to solve the problem with the message system. I would be eternally grateful.