How to have a table viewable by both server and client

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

  1. 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.)
  2. 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.
  3. … 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 :slight_smile:

2 Likes

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 :man_shrugging:

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) .

1 Like

@nicemike40 and @Ra_f gave sage advice. You may also want to look at this from @Tomarty:

and take a look at this thread:

1 Like

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

1 Like

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)

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)