Infinite Scripter Plugin

Hi, thanks a lot for your contribution, it’s an amazing plug-in !
I’ve been using it for a couple of days to create a large map with different biomes having their own set of vegetation. I’m trying to see how far I can go with your plug in in terms of terrain complexity.
While I was doing a « jungle » biome I was messing around with the « model generation ».
I wanted something dense for the jungle so I’ve put a hight amount of models to load on large spaces
By doing so, when I tried the map on studio I got a warning« infinite yield possible on starterplayerscript » at launch probably due to #zdata in LoadMap function (I load the map for each biome with humanoids preset)
The map then never loads and I get an error. (Timeout error → exhaustion of allowed processing time by roblox core logic ? Would it mean that there is too much to load ?)

When I publish the game I get the same error, but the map loads (computer)
When I play the game on my phone some part of the map is missing and I can spot a lot of buggy voxels.
Do you have any idea how to solve this bug ? Or is there nothing to do ?

1 Like

if a thread takes to long to finish its work that thread will be stopped

1 Like

What would you advise me to do if I’m trying to load large maps (lets say around a 1000 modulesScripts) ?
I’m not super familiar with thread management but here’s what I tried so far :

I’ve tried to “wrap” a part of the LoadMap function in a coroutine and yield it every time it loads 100 items → didn’t work

module.LoadMap = function(folder, modules)
	ogTick = tick()
	local map = {}
	local coroutineFunc = coroutine.create(function()
		for n, child in ipairs(folder:GetChildren()) do
			if child.ClassName ~= "ModuleScript" then continue end
			local position = child:GetAttribute("Position")
			if position == nil then continue end
			local data = modules[child]
			for i = 1, #data, 2 do
				local x = position.X + data[i]
				local zData = data[i + 1]
				if map[x] == nil then map[x] = {} end
				for j = 1, #zData, 2 do
					local z = position.Y + zData[j]
					local value = zData[j + 1]
					map[x][z] = value
					if j > 100 then
						coroutine.yield()
					end
				end
				if i > 100 then
					coroutine.yield()
				end
			end
			if n > 100 then
				coroutine.yield()
			end
		end
	end)
	coroutine.resume(coroutineFunc)
	return map
end

I’ve tried to wrap the LoadMap function in a coroutine and yield when the thread took a set amount of time by starting a timer a the beginning of LoadMap and yielding every time the execution is going over 0.0001s → didn’t work

module.LoadMap = function(folder, modules)
	ogTick = tick()
	local map = {}
	local coroutineFunc = coroutine.create(function()
		for _, child in ipairs(folder:GetChildren()) do
			if child.ClassName ~= "ModuleScript" then continue end
			local position = child:GetAttribute("Position")
			if position == nil then continue end
			local data = modules[child]
			for i = 1, #data, 2 do
				local x = position.X + data[i]
				local zData = data[i + 1]
				if map[x] == nil then map[x] = {} end
				for j = 1, #zData, 2 do
					local z = position.Y + zData[j]
					local value = zData[j + 1]
					map[x][z] = value
					if ogTick - tick() > 0.0001 then
						print(tick() - ogTick.."ms")
						coroutine.yield()
					end
				end
				if ogTick - tick() > 0.0001 then
					print(tick() - ogTick.."ms")
					coroutine.yield()
				end
			end
			if ogTick - tick() > 0.0001 then
				print(tick() - ogTick.."ms")
				coroutine.yield()
			end
		end
	end)
	coroutine.resume(coroutineFunc)
	return map
end

I tried to spawn each LoadMap instruction → Didn’t work


module.Initialize = function(instance, modules)
	--Functions
	PerlinNoise = modules[instance.Functions].Noise
	RandomNoise = modules[instance.Functions].Random
	Magnitude = modules[instance.Functions].Magnitude
	LoadMap = modules[instance.Functions].LoadMap
	ScaleModel =  modules[instance.Functions].ScaleModel
	--Biomes
	spawn(function()
		Beachs = LoadMap(instance.Beachs,modules)
	end)
	spawn(function() 
		Cliffs = LoadMap(instance.Cliffs,modules)
	end)
	spawn(function() 
		Desert = LoadMap(instance.Desert,modules)
	end)

	spawn(function()
		Jungle =  LoadMap(instance.Jungle,modules)
	end)
	spawn(function()
		Mountain =  LoadMap(instance.Mountain,modules)	
	end)
...[Rest of the code]

I tried to “yield” with task.wait() before loading the biggest maps → didn’t work

module.Initialize = function(instance, modules)
	--Functions
	PerlinNoise = modules[instance.Functions].Noise
	RandomNoise = modules[instance.Functions].Random
	Magnitude = modules[instance.Functions].Magnitude
	LoadMap = modules[instance.Functions].LoadMap
	ScaleModel =  modules[instance.Functions].ScaleModel
	--Biomes
	Beachs = LoadMap(instance.Beachs,modules)
	Cliffs = LoadMap(instance.Cliffs,modules)
	Desert = LoadMap(instance.Desert,modules)
	task.wait()
	Jungle =  LoadMap(instance.Jungle,modules)
	Mountain =  LoadMap(instance.Mountain,modules)
...[Rest of the code]

I get this at start :

13:53:49.392 Infinite yield possible on ‘StarterPlayer:WaitForChild(“StarterPlayerScripts”)’ - Studio
13:53:49.392 Stack Begin - Studio
13:53:49.392 Script ‘CoreGui.RobloxGui.Modules.Settings.SettingsHub’, Line 1206 - function getOverridesPlayerScripts - CoreScript - SettingsHub:1206
13:53:49.392 Script ‘CoreGui.RobloxGui.Modules.Settings.SettingsHub’, Line 1268 - CoreScript - SettingsHub:1268
13:53:49.392 Stack End - Studio
13:54:03.323 Script timeout: exhausted allowed execution time - Client - -1,-2:65
13:54:03.325 Stack Begin - Studio
13:54:03.331 Script ‘ReplicatedFirst.Terrain.Humanoids.Actor.Humanoids.Jungle.-1,-2’, Line 65 - Studio - -1,-2:65
13:54:03.338 Stack End - Studio
13:54:03.339 Script timeout: exhausted allowed execution time - Client - CoreScripts/CoreScriptErrorReporter:132
13:54:03.339 Stack Begin - Studio
13:54:03.339 Script ‘CoreGui.RobloxGui.CoreScripts/CoreScriptErrorReporter’, Line 132 - CoreScript - CoreScripts/CoreScriptErrorReporter:132
13:54:03.340 Stack End - Studio

I guess it’s because no matter what, replicatedFirst has a limited time set to ends all its threads.

Maybe I should put the whole thing in replicatedStorage instead of ReplicatedFirst and create a custom Loading Screen that would make the player wait for the terrain threads to finish before starting to play ? What do you think ?

You can use task.wait() to wait till the next frame to continue the thread

1 Like

Applied it, worked like a charm.

local function ActorAmount(amount)
		while #actors < amount do
			local actor = Instance.new("Actor", script)
			actorScript:Clone().Parent = actor	
			actor.Initialize:Fire()
			table.insert(actors, actor)
			task.wait() -- Edited
		end
		while #actors > amount do
			local actor = table.remove(actors)
			actor:Destroy()
		end
	end

Thanks a lot, it looks amazing.

Is there a method to get the average slope of an entire chunk, or would I need to add in something to the generation to obtain it?

You would need to add that yourself

1 Like

could you please share how you managed to make it work on the serverside?

Update

image

Update to latest version to fix this error