Start typing to search packages!
roblox-net
By @hurreman
Roblox
Mirrored from Wallyroblox-net
One declared set of remotes per game, shared by server and client. It merges two existing approaches:
- Slime Nebula's networking layer: EventsConfig, RemoteBinder, the player and global event queues, and TokenBucket.
- The
Netmodule from Magic Neighborhood and Steal an Egg: one list of remotes, and "only fire to clients that are listening".
# wally.toml
[dependencies]
Net = "hurreman/roblox-net@0.1.0"
-- src/shared/Remotes.luau: required by server and client alike
local Net = require(ReplicatedStorage.Packages.Net)
return Net.new({
remotes = {
DataSync = { kind = "event" },
Notify = { kind = "event", throttle = 10 }, -- s->c: at most 10/s per player
Swing = { kind = "event", limit = { burst = 3, perSecond = 2 } }, -- c->s: token bucket per player
EventStream = { kind = "unreliable" },
AdminList = { kind = "function" },
},
-- layout = "folder" (default: ReplicatedStorage.Remotes) | "byType" (RemoteEvents / RemoteFunctions / UnreliableRemoteEvents)
-- readyRemote = "NetReady" (default) | false
-- onLimited = function(player, name) end -- a refused client request
})
The list form works too: remotes = Net.fromLists({ events = {...}, unreliable = {...}, functions = {...} }).
On the server, requiring the module creates every remote at once, in one replication step. On the client, the accessors wait for them.
Server → client
| Call | Delivery |
|---|---|
net:SendTo(player, name, ...) | Queued until that client is ready, then delivered in order; trailing nils are kept. Returns false if the remote's throttle dropped it, or if the name is unknown or is a function remote. |
net:SendToAll(name, ...) | SendTo for every player. |
net:Broadcast(name, ...) | Ready clients only, immediately, never queued. Use it for cosmetic cues a loading client should simply miss. |
net:QueueGlobal(name, { args }, expiresIn?) / net:ClearGlobal(name) | World state replayed to each client when it becomes ready (default lifetime 30 s). Clear it when a newer state replaces it. |
net:InvokeOn(player, name, ...) | Yields for the client's answer (after it's ready); raises if the client's handler errors. |
A client becomes ready in one of two ways:
- it calls
net:SignalReady()once its listeners are connected; - the server calls
net:MarkReady(player)itself, for example from a data hello.
net:IsReady(player), net:MarkNotReady(player) and net:Queued(player) round this out.
A player's queue, readiness, throttle windows and buckets are dropped when they leave, and a late MarkReady for someone who already left is ignored.
Client → server
net:OnServerEvent("Swing", function(player, targetId) ... end) -- refused calls never reach the handler
net:OnServerInvoke("AdminList", function(player) return list end) -- a refused call returns nil
limit is a token bucket per player and remote: up to burst requests at once, refilling at perSecond. For limits keyed by something else, such as per target or per action, use Net.TokenBucket directly.
On the client, use net:OnClientEvent(name, fn), net:FireServer(name, ...) and net:InvokeServer(name, ...). The raw instances are available from net:Event(name), net:Unreliable(name) and net:Function(name); each checks the name and kind.
Remotes that aren't declared
net:Get(name, className, waitForServer?) creates a remote on demand on the server, or waits for it on the client, in the same folders. This is Slime Nebula's RemoteBinder.get. A remote that exists with the wrong class is replaced on the server.
Net.Binder.new({ clientFallback = true }) keeps RemoteBinder's old client behaviour: after a timeout it creates a local stand-in with no server behind it. This is off by default.
Studio Edit mode
RunService:IsServer() is false in Edit mode, so a plain Net.new there acts as a client. Edit-mode tools and plugins pass server = true to create the remotes. Handlers can't be connected in Edit mode: Roblox refuses OnServerEvent and OnServerInvoke there.
Moving the games over
- Steal an Egg and Magic Neighborhood:
shared/Net.luaubecomesreturn Net.new({ remotes = Net.fromLists({ events = EVENTS, unreliable = UNRELIABLE, functions = FUNCTIONS }) }), with the sameRemotesfolder.- Rename the calls:
Net.event(x)tonet:Event(x),Net.unreliable(x)tonet:Unreliable(x),Net.func(x)tonet:Function(x),Net.markReady(p)tonet:MarkReady(p), andNet.fireReady(Net.event(x), ...)tonet:Broadcast(x, ...). - Handlers that validate rate by hand (ProductService's prompt cooldown, for example) can move to a
limit.
- Slime Nebula:
EventsConfigentries become remote specs:{ type = "RemoteEvent", throttle = n }maps to{ kind = "event", throttle = n }.- Use
layout = "byType"so instance paths stay as they are. PassreadyRemote = falsewhile GameInit still callsMarkPlayerReady. EventsManager.SendTo,SendToAll,MarkPlayerReadyandInvokeOnmap to the same-namednetcalls.QueueGlobalEvent(name, ...)maps tonet:QueueGlobal(name, { ... }, expiresIn).RemoteBinder.get(325 call sites) maps tonet:Get, or keepRemoteBinder.luaas a one-line shim over aNet.Binder.new({ layout = "byType", clientFallback = true }).ThrottlingManagergoes away (throttlecovers it), andTokenBucketisNet.TokenBucket.
Behaviour fixed on the way
- InvokeOn: Slime Nebula's version, for an already-ready client, resumed its own running coroutine and then yielded forever. It now invokes directly.
- Trailing nils: queued sends kept only the arguments up to the first trailing
nil({...}). They now keep them all (table.pack). - Throttle state: throttle windows were never cleared when a player left. They are now.
- Debug prints: the prints on every ready and flush are gone.
Developing
lune run tests/run # specs on hurreman/luau-spec (DevPackages, else ../luau-spec); fake Roblox in tests/fixtures
The Lune specs cover queueing, broadcast, throttle, limits, global replay, InvokeOn, leaving players and the client path, against fake Instances.
In Studio (Slime Nebula's Edit DataModel, under a throwaway folder), these were checked: real remote creation in both layouts, replacement of a wrong-class remote, on-demand Get, and the accessors' classes.
Client-to-server delivery between a real server and client in Play mode has not been run yet.
Package Details
Install command (Click to copy)
Version
0.1.0
License
MIT
Safe for commercial use
Automated license review — not legal advice.
