How many blocks do you have in a world / is it infinite? What percentage of those blocks are viewable by the client? How is your position data stored?
The amount is infinite, blocks generate around you as you mine, Currently a maximum of around 200 blocks would be loaded in on the client and my position table is stored as example:
BlockData = {
[Y-Cordinate] = {
[X-Co-ordinate] = {[Z-Coordinate] = BlockTYPE}
}
}
Keep in mind im not using roblox co-ordinates, im ijust reprenting each block as 1x1x1 in terms of my positioning data
- You may want to research better data structures for voxel worlds
â a whole table for each y- and x-coordinate block is going to eat your memory as the world expands. Especially since youâre using the dictionary form of lua tables.(edit: I was wrong, I think as long as the keys of the dictionary are contiguous and start at 1, they are stored as an array, not a hash table. Still, you would have one array per block, which will hurt things like caching, and possible GC and access time depending on how you use them.) - 200 blocks is nothing to the server â send all 200 when the client logs in, and then have the client just send deltas to the server and vice-versa.
- ⌠But honestly, I wouldnât try to solve this problem. Replicating bricks and local changes to other clients is what ROBLOX is built to do. Why canât you have the server place all the parts in workspace, throw on
StreamingEnabled
, and have their servers handle it? Then you can just send destroy/create events to the server and it will handle updating the view for everyone.
I really wouldnât try to create a fully-fledged voxel engine in ROBLOX, since theyâve pretty much done all that work for you already!
edit: But if your heart is really set on it, hereâs some links that might lead you to fun places. Again though, this isnât really a simple problem
Have a remote function under game.ReplicatedStorage called âretrieveâ
--server
local r = game.ReplicatedStorage.retrieve
local t = {}
t["i"] = "value";
r.OnServerInvoke = function(_, ind)
return t[ind]
end
-- client
local get = game.ReplicatedStorage.retrieve
local t = {}
setmetatable(t, {__call = function(self, i) return get:InvokeServer(i) end})
print(t("i")) --> prints "value"
you could even use __index to give the illusion of having the table on the client as well but you canât yield within a function bound to it so
Canât think of other ways given changes in modules donât replicate across the boundary, other than remote events (remote functions make it simpler in this case) .
@nicemike40 and @Ra_f gave sage advice. You may also want to look at this from @Tomarty:
and take a look at this thread:
Thatâs not storing for every block,
Itâs storing the square for each Y cord, then the X for each crow then the Z for each individual block.
Besides how does this have any relevance to my issue
Iâd appreciate it if you could also read my issue, I honestly donât care how good roblox streaming is, the whole purpose of this is to make the most effieinct mining system possible
Fire the table to the client using remote events, or all clients, fire again when it needs to be updatedâŚ
Sorry, I worded my first point imprecisely. Having a single table for each block is the issue. Tables are dynamically allocated, so thatâs one allocation for every voxel. I was just suggesting you explore other options that might be faster â but youâre right, itâs probably premature optimization, so you can save that for if you run into issues later on if you like.
I was also saying that if you want to:
make the most [efficient] mining system possible
, you should utilize the tools ROBLOX has created, since youâre never going to match their speed or efficiency in Lua. The most efficient mining system is one that utilizes the network code native to ROBLOX.
Iâd appreciate it if you could also read my issue
I did read your issue, and, unless Iâm missing something, I and others have answered your question (in addition to suggesting a different approach).
To summarize what others in this thread have already said, though, if you still want to do it yourself:
- Store your voxels in some kind of âchunkâ data format (think minecraft chunks)
- Clients request whole chunks at once with a
RemoteFunction
, and save a local copy of the table to render - Clients can update the serverâs table with a
RemoveEvent
that takes(chunk, position)
arguments or something - When the server receives that update, it can broadcast the change to other clients with a
RemoteEvent
and they can update their local copy of the chunks accordingly - The server can handle a few hundred bytes a second of data very easily, you shouldnât need to worry about that â and you donât need to resend the chunks every frame, just when a client moves far enough from their previous position, or logs in, or otherwise requests the data.
- If you really want to, ratelimit the requests from clients (so they can only ask for
x
number of chunks per minute)
- If you really want to, ratelimit the requests from clients (so they can only ask for
Kinda late, but I wanted to put this here just in case anybody else is looking for a solution. If you want to read a table thatâs changed on the server and read on the client without constantly spamming remote events you can try this method.
Make a StringValue on the server then make the table in a server script. Convert that table to a string and set the StringValueâs Value to the table you just converted to a string. Then you can set an event on the client that checks for when the StringValue was changed. Once it detects the value is changed the client will convert it back to a table.
I havenât tried this method myself so I donât know if this would be more effective than using remotes.
Another method you could try is this:
The server script updates the table, which is located inside a modulescript, deleting the old one then creating a new one right after. In the local script, you can make it work via an update function:
local tbl = require(workspace:WaitForChild("TableData"))
workspace.ChildAdded:Connect(function(child) -- update the table on the client when the server script destroys the old one then makes a new one
if child.Name == "TableData" and child:IsA("ModuleScript") then
tbl = require(child)
end
end)