Hi, I have a ModuleScript in ReplicatedStorage that is intended to house a bunch of dictionaries with different values. The purpose of the module is to be a config that the client would use while the server can live update the ModuleScript.
The ideal goal of this “live-updating-server-to-client-config” system is shown:
Server making changes to a config → table in ModuleScript that holds the current config → client reads the config when needed
The only issue is, as we all know, you can’t communicate server to client via ModuleScripts. That complicates things a bit.
How should I go about creating a system like this to hold complex tables with many values that the server can edit anytime to have the client able to see the changes?
Honestly, folders and instances are the simplest way to go because when changing these, it replicates to the client as opposed to a module. Now you can just simply devise a method to do what you want using a module script and remote events.
I've attempted a system like this here within minutes if this could work for you
Keep in mind that I did not test this and is just something I created quickly
local module = {
Remote = script:FindFirstChild("RemoteEvent") or Instance.new("RemoteEvent", script),
Data = nil -- set to nil so you can read it while scripting and accessing the module
}
local isServer = game:GetService("RunService"):IsServer()
local submodule = isServer and {} or nil
if isServer then
setmetatable(submodule, {
__index = module,
__newindex = function(_, i, v) -- i = index/key, v = value
module[i] = v
module.Remote:FireAllClients(i, v)
end
})
else
module.Data = {}
module.Remote.OnClientEvent:Connect(function(i, v)
module.Data[i] = v
end)
end
return submodule or module
So if you still want to try to create a modular version of this dictionaries housing, then you’d just simply use metamethods on a fake table (fake as in that it is not the original table) then __index and __newindex can be connected to functions that affect the original table.
In the __newindex function, you can do whatever you want like a remote call to change the client-side, which I use :FireAllClients() for. The __index one can just be connected to the original module table instead of a function. (__index = module)
You could also just whenever wanting to access information, send a key to the server then the server looks in its data for whatever is linked to that key via the remote. This way keeps exploitation out of hands since when doing a modular way that the client can access, they could affect the dictionaries data housing.
Doesn’t requiring a module reserve a permanent space in memory for the module? If it does, it would just keep adding stuff that would’ve been deleted to the memory a lot.
That metamethods solution sounds pretty cool. I would only worry about the server constantly changing the config and thus constantly firing a remote which is bad news for the networking. In terms of exploiting, this config is only applied and seen by the client so I think exploitation is pointless for people who try.
As you said folders and values might do the trick. I would assume it would work nicely if I wrote up a function to convert tables and values to tables and variables?
If you want real-time changes, you unfortunately will have to do a remote request for every single change. The ideally best way is just to ask the server with a key of information and that the server will authorize this request to search for whatever data the client requests.
If you decide to use Instances instead of modules, you’re no longer using a modular form and you’re just using server-to-client replication.
Even if sending requests from the client just for every bit of information might be as network expensive, it’s the way everyone does it when they want to give information from the server, plus it’s the safest route.
You can reduce the network expenses of using one remote by making remotes for individual named dictionaries (if you have multiple dictionaries in one).
It’s hard to spam the remotes unintentionally enough that network traffic is a problem so I doubt you’ll have any problems unless you will change data at very high speeds.
If you were to be really worried about exploiters they couldn’t affect the instances on the server. RemoteEvents might not be the best choice purely just because if a person for some reason winds up trying to request data from the server they can spam remotes and swamp the server with requests. Not that this is a problem or a specific vulnerability with this exact system, it might just end up being ““safer”” to use instances.
The client can intentionally manipulate anything that they are given access to, this includes instances and client-sided code. I don’t mean to say it isn’t a safe route but the client can affect data that the server might not touch for a while thus giving you falsified data on the client whenever they look to access this information. This is the same for the modular form.
So why is just giving a key for information from the server the most safest? Because you can authorize whether accessing this information based on if the player sending the request is allowed to and they’ll never be able to change that information on their side when requesting it because they directly request the server for the data instead of having the server update the client-sided data.
However, they could albeit harder, still possibly modify the information from the server but they’ll never receive a falsified value, they have to modify a value somehow received by the server.
In all three scenarios, the server is unaffected by client-sided exploitation.
I’m only saying here that you have a choice to pick whichever method and I just pointed the pros & cons of those methods. They might all work similarly to be honest.
Luckily the issue you presented with instance-based data structures isn’t really affected by what I have planned. It’s just config that the client can read to show client sided stuff only to them. Falsifying the data would only really affect them, so why bother? gotem
With this in mind, do you think that instances will be the best solution because false data doesn’t really matter and it would save on networking?
Networking isn’t a problem until you start having heavily spammed remotes. Instances definitely removes the problem on networking but don’t worry about networking as you have plenty of data you can send over very fast.
However, there’s still a cost to constantly adding/removing instances anyways. I don’t know how in script and instances would be in comparison for performance but the script’s side sounds more cleaner and better optimized.
I’d recommend going for setting a remote event that the client can access when requesting data using a key (that key is then used for the dictionaries housing to find whatever via index) on the server, but you can try instances since it won’t hurt.
Seeing as I would hope the instances being created wouldn’t be spammed (that’s a no-go for Roblox to begin with) I’m going to try to mess around with instances because I’m not keen on the extra delay that would come with using remotes and the slight annoyance of not being able to see the full data set. Thanks for your input though, it was really helpful!
The extra delay is very short probably just tens or hundreds of milliseconds for something that is just data that will be indexed on request by the client, are you sure you really want the networking alternative just to save a negligible amount of time?
Well the reason is mostly because having the fire the remote event just to get one item would be a bit annoying. If you wanted two values is completely different locations in the table you would need to fire the remote twice, which just becomes less viable when just compared to instances which can just be referenced at any time.
Then you could use variadic functions (functions using ...) in order to make as many requests as you want. You still might reference multiple instances otherwise.