Nope. Seems to be working just fine.
There is one thing tho. If the server removes a tree from the folder, then the remote gets fired. Could there be a way to clean up the local list too?
Nope. Seems to be working just fine.
There is one thing tho. If the server removes a tree from the folder, then the remote gets fired. Could there be a way to clean up the local list too?
Could you send me all the code related to the tree loading again?
Ok.
Server
local event = game:GetService("ReplicatedStorage").Events.positionTrees
game:GetService("Players").PlayerAdded:Connect(function(plr)
wait(15)
event:FireClient(plr)
end)
game:GetService("Workspace").Trees.ChildAdded:Connect(function()
event:FireAllClients()
end)
game:GetService("Workspace").Trees.ChildRemoved:Connect(function()
event:FireAllClients()
end)
Client
if not game.Loaded then game.Loaded:Wait() end
local event = game:GetService("ReplicatedStorage").Events.positionTrees
local trees = {}
event.OnClientEvent:Connect(function()
for i, v in pairs(game:GetService("Workspace").Trees:GetChildren()) do
if v and v.Name == "Tree" and not (table.find(trees, v)) then
table.insert(trees, v)
local pos = v.Position
-- [RAYCAST STUFF] --
local rayOrigin = pos
local rayDirection = Vector3.new(0, -300, 0)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {v, game.Players.LocalPlayer.Character}
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
if raycastResult then
local targetMaterial = raycastResult.Material
local targetPos = raycastResult.Position
local targetInstance = raycastResult.Instance
if targetInstance == game:GetService("Workspace").Terrain and targetMaterial ~= Enum.Material.Water then
v.Position = targetPos + Vector3.new(0, 7, 0)
end
task.wait(.5)
end
-- -- -- -- -- --
end
end
end)
So what are the problems with it now?
If you look at the code, you will definetly see that the list will not remove trees deleted by the server.
How could that work?
Thank You, this game might even work!
Great! If you have any more questions then let me know. I’d also love to see the finished thing if you complete it!
Hey the way i would do it is the localscript that is created by the plugin i would convert into a modulescript
in order to get acces to the GetHeight function
because terrain has a pixel size of 4x4 studs the GetHeight function also requires you to divide your postion into 4
-- Position where the tree will spawn
local positionX = 489534.345
local positionZ = 23445.345
-- We dived by 4 and floor to get the terrain position
local terrainX = math.floor(positionX / 4)
local terrainZ = math.floor(positionZ / 4)
-- We use the terrain module to get the height
local height = module.GetHeight(terrainX, terrainZ)
then on the server side i would crate random invisable parts scartered around the world
then when the client enters the game they loop all invisable parts and place tree models in there place but using the height data to make sure the tree does not spawn under the terrain
this could also be done in a way so that the client only loads tree there standing close to if you want it to be more optmized but will be harder to code or you could use Content Streaming | Roblox Creator Documentation so that only invisable parts the player is standing close to is loaded but then you will need to use Instance.ChildAdded to detect when a new part was streamed to the client
the server will also give these invisable parts attributes
https://developer.roblox.com/en-us/api-reference/function/Instance/SetAttribute
for example
state = 0 (tree does not exist no model will be spawned)
state = 1 (tree is spawned)
state = 2 (tree has fallen over)
then the clients will listen for attribute changed using
https://developer.roblox.com/en-us/api-reference/event/Instance/AttributeChanged
then as soon and the server updates the attribute all clients would update the model
then when a client hits the tree the client will send a event to the server saying i hit this invisable part then the server will change the state of the part from 1 to 2 so it falls down for all clients to see
instead of using state you could also use health as a attribute if you wanted trees to have multiple hits before they fall
please remember that hackers can make and edit there own local scripts
also remeber that the client has ownership of there own humanoid
so what this means is hackers can send events to a server when ever they want with what ever infomation they want
also hackers can move there character around and teleport where ever they want
so how can we prevent hackers from cuting trees
if we simple check the distance from the tree we cant trust this infomation as a hacker could simply teleport there character to a tree and send the event
so we need a loop on the server side and this loop needs to keep track of every clients character position then we need to do a Magnitude check and if the hacker moved to fast we know not to let them cut the tree
Well That did not work in my case
script.Parent.launched.Event:Connect(function()
wait(12.2)
local module = script.ModuleScript
-- Position where the tree will spawn
local positionX = script.Parent.Position.X
local positionZ = script.Parent.Position.Z
-- We dived by 4 and floor to get the terrain position
local terrainX = math.floor(positionX / 4)
local terrainZ = math.floor(positionZ / 4)
-- We use the terrain module to get the height
local height = module.GetHeight(terrainX, terrainZ)
script.Parent.CFrame.Position.Y = height
end)
It tell me
GetHeight is not a valid member of ModuleScript “Workspace.rockey.Launcher.force.timer.ModuleScript”
You need to use the Terrain Module, generated, by the plugin.
If you mean turning the local script into a module script ,Yes Ive done that
Hey so let me show you how to use ModuleScript | Roblox Creator Documentation
so when you create a empty modulescript
you first see this
local module = {}
return module
so what this is doing is creating a table and returning it to who ever requires the module
so what ever you put inside this table other scripts can have access to it
we can put functions inside this table so other scripts can call this same function over and over
and here you can see what it looks like to add a function to the module
local module = {}
module.MyModuleFunction = function()
print("Hello World")
end
return module
and this is what the plugin script should look like when its a module
local module = {}
local dataChild = nil
local heightChild = nil
local materialChild = nil
for i, child in ipairs(script:GetChildren()) do
if child.ClassName == "ModuleScript" then
local data = child:GetAttribute("Data")
if data == "Terrain" then
dataChild = child
end
elseif child.ClassName == "Folder" then
local data = child:GetAttribute("Data")
if data == "Height" then
heightChild = child
elseif data == "Material" then
materialChild = child
end
end
end
if dataChild == nil then return end
local distance = 16
local chunkSize = 16
local positionX = math.huge
local positionZ = math.huge
local heightData = {}
local materialData = {}
local loaded = {}
local data = require(dataChild)
if heightChild then
for i, child in ipairs(heightChild:GetDescendants()) do
if child.ClassName ~= "ModuleScript" then continue end
local data = require(child)
local position = child:GetAttribute("Position")
for i = 1, #data, 2 do
local x = position.X + data[i]
local zData = data[i + 1]
if heightData[x] == nil then heightData[x] = {} end
for j = 1, #zData, 2 do
local z = position.Y + zData[j]
local height = zData[j + 1]
heightData[x][z] = height
end
end
end
end
if materialChild then
for i, child in ipairs(materialChild:GetDescendants()) do
if child.ClassName ~= "ModuleScript" then continue end
local data = require(child)
local position = child:GetAttribute("Position")
for i = 1, #data, 2 do
local x = position.X + data[i]
local zData = data[i + 1]
if materialData[x] == nil then materialData[x] = {} end
for j = 1, #zData, 2 do
local z = position.Y + zData[j]
local material = zData[j + 1]
materialData[x][z] = material
end
end
end
end
module.GetHeight = function(x, z)
if heightData[x] == nil then heightData[x] = {} end
if heightData[x][z] ~= nil then return heightData[x][z] end
local height = 0
for i, data in ipairs(data.noises) do
local noise = math.noise(x * data[3], data[1], z * data[3])
height += math.clamp(noise, data[4], data[5]) * data[2]
end
height += data.shift
height = math.clamp(height, data.minimumHeight, data.maximumHeight)
heightData[x][z] = height
return height
end
Load = function(x, z)
local minimum = math.huge
local maximum = -math.huge
for xx = x-1, x+1 do
for zz = z-1, z+1 do
local height = module.GetHeight(xx, zz)
minimum = math.min(minimum, height)
maximum = math.max(maximum, height)
end
end
local slope = maximum - minimum
local height = heightData[x][z]
local thickness = height - minimum + data.thickness
local cFrame = CFrame.new(x * 4 + 2, height - thickness / 2, z * 4 + 2)
local size = Vector3.new(4, thickness, 4)
if materialData[x] ~= nil and materialData[x][z] ~= nil then
workspace.Terrain:FillBlock(cFrame, size, materialData[x][z])
else
for i, materialData in ipairs(data.materials) do
if height >= materialData[2] and height < materialData[3] and slope >= materialData[4] and slope < materialData[5] then
workspace.Terrain:FillBlock(cFrame, size, materialData[1])
break
end
end
end
if height >= data.waterHeight then return end
thickness = data.waterHeight - height
local cFrame = CFrame.new(x * 4 + 2, height + thickness / 2, z * 4 + 2)
local size = Vector3.new(4, thickness, 4)
workspace.Terrain:FillBlock(cFrame, size, Enum.Material.Water)
end
LoadChunk = function(chunkX, chunkZ)
if loaded[chunkX] == nil then loaded[chunkX] = {} end
if loaded[chunkX][chunkZ] ~= nil then return end
loaded[chunkX][chunkZ] = true
local startX = chunkX * chunkSize
local startZ = chunkZ * chunkSize
local endX = startX + chunkSize - 1
local endZ = startZ + chunkSize - 1
for x = startX, endX do
for z = startZ, endZ do
Load(x, z)
end
end
wait()
end
LoadChunks = function(chunkX, chunkZ, hole)
if hole == nil then hole = 0 LoadChunk(chunkX, chunkZ) end
hole = hole * 2 + 1
local x = chunkX + math.floor(hole/2)
local z = chunkZ - math.floor(hole/2)
local dx, dz = 1, 0
local passed = hole - 1
local length = hole
local amount = (distance * 2 + 1) * (distance * 2 + 1) - hole * hole
for i = 1, amount do
x += dx z += dz
if Vector2.new(chunkX - x, chunkZ - z).Magnitude < distance + 0.5 then LoadChunk(x, z) end
passed += 1
if passed == length then passed = 0 dx, dz = -dz, dx if dz == 0 then length += 1 end end
end
end
workspace.CurrentCamera:GetPropertyChangedSignal("Focus"):Connect(function()
local x = workspace.CurrentCamera.Focus.Position.X
local z = workspace.CurrentCamera.Focus.Position.Z
local magnitude = Vector2.new(positionX - x, positionZ - z).Magnitude
if magnitude < chunkSize * 8 then return end
positionX, positionZ = x, z
x = math.floor(positionX / 4 / chunkSize)
z = math.floor(positionZ / 4 / chunkSize)
if magnitude < distance * chunkSize * 2 then
LoadChunks(x, z, math.floor(distance / 2 - 0.5))
else
LoadChunks(x, z)
end
end)
return module
notice how the
GetHeight = function(x, z)
now looks like this
module.GetHeight = function(x, z)
ok now we have the modulescript place this modulescript inside ReplicatedStorage
now this is how we use the module script in other scripts
-- get the module table into the terrainModule varable
local terrainModule = require(game.ReplicatedStorage.TerrainModuleScript)
-- Position where the tree will spawn
local positionX = 489534.345
local positionZ = 23445.345
-- We dived by 4 and floor to get the terrain position
local terrainX = math.floor(positionX / 4)
local terrainZ = math.floor(positionZ / 4)
-- We use the terrain module to get the height
local height = terrainModule.GetHeight(terrainX, terrainZ)
notice how we use require to get the module
thank you for helping,Im making an exploration game and i make a rocket but terrain always load slowly so the roket go through,But now,I can get height of the terrain at a time and just teleport the rocket there,Thank you!
Make sure you only have 1 terrain script so your not loading the terrain multiple times other then that the terrain is loading as fast as it can unfortunately it’s not a simple operation so it needs some time