-
What do you want to achieve?
I need help on making the terrain generation server-side. -
What is the issue?
The terrain generation that I use in my game is a modified version of Okeanskiy’s Terrain Generation, where instead of trees, random trash generate. Where the “generate tree” function would run, instead, a random model from a folder would be picked, and then cloned onto the terrain. There is a problem, however, with the trash generation system.
The trash on the terrain is generated differently for different players. I discovered this when I showcased the game to a friend of mine, weeks ago. This was confirmed when I tested it with two different players in my Studio:
The left player sees an old billboard, chunks of corroded metal, and a neon cube.
The right player sees stone bricks, a slate block, and a battered bench. -
What solutions have you tried so far?
One option I did was through utilizing remote events. For the client-side generation:
game.StarterPlayer.StarterPlayerScripts.Terrain (LocalScript)
local Chunk = require(game.ReplicatedStorage.Chunk)
TerrainEvents = game.ReplicatedStorage.Terrain
local RENDER_DISTANCE = 12
local CHUNKS_LOADED_PER_TICK = 4
local CAMERA = game.Workspace.CurrentCamera
local centerPosX
local centerPosZ
local chunks = {}
local chunkCount = 0
local fastLoad = true
local function chunkWait()
chunkCount = (chunkCount + 1) % CHUNKS_LOADED_PER_TICK
if chunkCount == 0 and not fastLoad then
wait()
end
end
local function updateCenterPosFromCamera()
local camPos = CAMERA.CFrame.Position
centerPosX = math.floor(camPos.X / Chunk.WIDTH_SIZE_X)
centerPosZ = math.floor(camPos.Z / Chunk.WIDTH_SIZE_Z)
end
local function doesChunkExist(x,z)
for index, chunk in pairs(chunks) do
if chunk.x == x and chunk.z == z then
return true
end
end
return false
end
local function isChunkOutOfRange(chunk)
if math.abs(chunk.x - centerPosX) > RENDER_DISTANCE
or math.abs(chunk.z - centerPosZ) > RENDER_DISTANCE then
return true
end
return false
end
local function makeChunks()
for x = centerPosX - RENDER_DISTANCE, centerPosX + RENDER_DISTANCE do
for z = centerPosZ - RENDER_DISTANCE, centerPosZ + RENDER_DISTANCE do
if not doesChunkExist(x,z) then
table.insert(chunks,Chunk.new(x,z))
chunkWait()
end
end
end
end
local function deleteChunks()
local n = #chunks
for i = 1, n do
local chunk = chunks[i]
if isChunkOutOfRange(chunk) then
chunk:Destroy()
chunkWait()
chunks[i] = nil
end
end
local j = 0
for i = 1, n do
if chunks[i] ~= nil then
j += 1
chunks[j] = chunks[i]
end
end
for i = j+1, n do
chunks[i] = nil
end
end
while true do
TerrainEvents.UpdateCenterPosFromCamera:FireServer()
TerrainEvents.DeleteChunks:FireServer()
TerrainEvents.MakeChunks:FireServer()
fastLoad = false
wait(0.5)
end
While true, the script will activate the RemoteEvents found in game.ReplicatedStorage.TerrainEvents. The followings server-side script will dictate what to do OnServerInvoke. It is nearly the same as the Client-side generation:
game.ServerScriptService.TerrainServer (Script)
local Chunk = require(game.ReplicatedStorage.Chunk)
TerrainEvents = game.ReplicatedStorage.Terrain
local RENDER_DISTANCE = 12
local CHUNKS_LOADED_PER_TICK = 4
local CAMERA = game.Workspace.CurrentCamera
local centerPosX
local centerPosZ
local chunks = {}
local chunkCount = 0
local fastLoad = true
local function chunkWait()
chunkCount = (chunkCount + 1) % CHUNKS_LOADED_PER_TICK
if chunkCount == 0 and not fastLoad then
wait()
end
end
local function updateCenterPosFromCamera()
local camPos = CAMERA.CFrame.Position
centerPosX = math.floor(camPos.X / Chunk.WIDTH_SIZE_X)
centerPosZ = math.floor(camPos.Z / Chunk.WIDTH_SIZE_Z)
end
local function doesChunkExist(x,z)
for index, chunk in pairs(chunks) do
if chunk.x == x and chunk.z == z then
return true
end
end
return false
end
local function isChunkOutOfRange(chunk)
if math.abs(chunk.x - centerPosX) > RENDER_DISTANCE
or math.abs(chunk.z - centerPosZ) > RENDER_DISTANCE then
return true
end
return false
end
local function makeChunks()
for x = centerPosX - RENDER_DISTANCE, centerPosX + RENDER_DISTANCE do
for z = centerPosZ - RENDER_DISTANCE, centerPosZ + RENDER_DISTANCE do
if not doesChunkExist(x,z) then
table.insert(chunks,Chunk.new(x,z))
chunkWait()
end
end
end
end
local function deleteChunks()
local n = #chunks
for i = 1, n do
local chunk = chunks[i]
if isChunkOutOfRange(chunk) then
chunk:Destroy()
chunkWait()
chunks[i] = nil
end
end
local j = 0
for i = 1, n do
if chunks[i] ~= nil then
j += 1
chunks[j] = chunks[i]
end
end
for i = j+1, n do
chunks[i] = nil
end
end
TerrainEvents.UpdateCenterPosFromCamera.OnServerEvent:Connect(updateCenterPosFromCamera)
TerrainEvents.DeleteChunks.OnServerEvent:Connect(deleteChunks)
TerrainEvents.MakeChunks.OnServerEvent:Connect(makeChunks)
When play-tested, the server-side terrain generation just… stops.
While taking a screenshot of the problem, an error occurred twice in the scripts:
Maximum event re-entrancy depth exceeded for BindableEvent.Event
Just by looking at these two scripts, you could see that I am doing something wrong. What am I doing wrong?