[1.3.0] ReplicatedRegistry2 | Insanely simple and easy server-client synchronized table registry

[>>-<<] ReplicatedRegistry2

Github | Wally | Documentation | Donate 80 Robux Donate 1000 Robux

ReplciatedRegistry2 is the successor to ReplicatedRegistry which has a better designed API, more flexibility and increased convenience and transparency.

It is a single module with 0 dependencies, making it very simple to insert and use in your games.

Some examples

Simple ProfileStore Example

--server
local server = ReplicatedRegistry.server

server.set_remote_instances(...)
local ProfileTemplate = {
    Coins = 0,
    Level = 1,
    Inventory = {}
}

local ProfileStore = ProfileService.GetProfileStore(
    "PlayerData",
    ProfileTemplate
)

local Profiles = {}
Players.PlayerAdded:Connect(function(player)
    local profile = ProfileStore:LoadProfileAsync("Player_" .. player.UserId)
    
    if profile ~= nil then
        profile:AddUserId(player.UserId)
        profile:Reconcile()
        
        profile:ListenToRelease(function()
            Profiles[player] = nil
            server.deregister(player.UserId)
            player:Kick("Profile released")
        end)
        
        if player:IsDescendantOf(Players) then
            Profiles[player] = profile
            server.register(
               player.UserId,
               profile.Data,
               ReplicatedRegistry.replication_filters.no_recieve(true)
            )
        else
            profile:Release()
        end
    else
        player:Kick("Failed to load data")
    end
end)

Players.PlayerRemoving:Connect(function(player)
    local profile = Profiles[player]
    if profile ~= nil then
        profile:Release()
        Profiles[player] = nil
        server.deregister(player.UserId)
    end
end)

local function GetProfile(player)
    return Profiles[player]
end

task.wait(5)
for _, player in Players:GetPlayers() do
    local proxy = reg.server.view_as_proxy(player.UserId)
    proxy.incr("Coins") (20) -- This also updates the data stored in ProfileStore since it keeps a reference
    proxy.replicate {player}
end
--client
local client = ReplicatedRegistry.client

client.set_remote_instances(...)

local my_data = client.view(game.Players.LocalPlayer.UserId)
print(my_data.Coins) -- 20

Hope you enjoy this resource, and happy coding!

5 Likes

1.0.1

  • Added a _auto_commit: boolean = true parameter to server.to_clients(), server.to_all_clients() and client.to_server(). This specifies if the aformentioned functions should call ReplicatedRegistry.commit_changes() automatically or not.
  • Changed RegistreeInterface_Server.replicate() to accept an array of Players as the 1st argument instead of only 1 player.

cool, is there dynamic buffer de/serialization here?

Nope, all it does is generate TableChanges which are deltas for tables to be sent over the network. You can make it buffered by calling ReplicatedRegistry.set_change_serde()

Read the docs for more info :slight_smile:

1.0.2

  • Added ReplicatedRegistry.meta.switch_signal_lib(s: typeof(Signal)), keep in mind the new signal library has to have the same structure as the one used by ReplicatedRegistry, which is 2 functions:
    connect: <T...>(tbl: {(T...) -> ()}, item: (T...) -> ()) -> ScriptConnection
    fire: <T...>(tbl: {(T...) -> ()}, ...: T...) -> ()
    Unfortunately using a regular signal library will not work
  • Added the module as a Wally package, @athar-adv/replicated-registry

1.1.0:

  • Changed customEvent arguments in replicator functions such as client.to_server() and server.to_clients() to _sender: Sender? which is a callback you pass that will get called to send the changes.
  • Removed the ReplicatedRegistry.set_change_serde() api as it is no longer necessary because you can just wrap a serde around the sender functions you pass
  • Added set_remote_callbacks() to client and server (kept set_remote_instances()
  • Removed server.to_all_clients() due to the new sender callbacks being implemented, instead you can just pass Players:GetPlayers() into server.to_clients()
  • Removed a few redundant things

1.2.0:

  • Filters now work on a per-changes-basis instead of a per-change-basis
  • Added server.deregister()
  • Updated docs to match 1.1.0 and up
  • Updated wally version to 1.2.0

This module looks great, the syntax is a bit weird however.

My main question is what differentiates this from Replica by loleris? or is it just your own implementation of the same systems.

2 Likes
  1. Its alot simpler (not only to use but also the sourxe code itself) and has 0 dependencies
  2. It doesnt do state management out of the box, pure table replication
  3. It uses registries instead of oop so the syntax is alot simpler
  4. Idk if Replica has this but ReplicatedRegistry2 allows clients to make modifications to the registry with server-authoritive rules, like being able to modify their own settings but not anything else for example (this is via Filters, callbacks.on_recieve_callback, etc)
  5. Middleware isnt implemented out of the box and the way you implement it is via callbacks so its extremely customisable, for example you can pass a Sender callback that serdes TableChanges into buffers before sending and vice versa for ConnectReciever
  6. You arent forced into using a proxy, the default option for table replication is just pure tables which will be diffed with an older copy to generate deltas

Couldnt name more but theres probably more :V U should check the docs to see examples of what kinda applications this module easily integrates into

Wdym by this btw, its pretty explicit

For example on the server you call server.set_remote_instances() to implement middleware as RemoteEvents and RemoteFunctions, then server.register() to register your keys, then either server.view_as_proxy() to view it as a RegistreeInterface to make replications queue per change instead of diff, or if you want replications to use deltas from diffs you can just call server.to_clients() with the _changes parameter passed as nil

1 Like

I’ve ran out of time to see if implementing this would be better for our game. However, I love the fact its not proxied. I’ll definitely come back to this sometime in the future to have a proper look, thank you!

as for my question about the syntax looking weird, I think im just used to pascalcase and oop-style code, apologies.

1 Like

Yea its completely fine, i hope you’ll consider using this module in the future! It integrates nicely with pretty much anything

1.3.0:

  • Changed proxy methods set, get, and incr to accept an array of keys for the path instead of a vararg.
  • Added RegistreeInterface.key_changed() to detect when a key change is done by the opposite context, aka when you call .set on the same context as the proxy, this callback isnt fired.