Skip to content

Getting started

How to install Lync, declare a schema, and wire the server and client.

Install

[dependencies]
Lync = "axp3cter/lync@4.0.3"
npm install @axpecter/lync

Then see roblox-ts for the tsconfig.json change.

Take Lync.rbxm from the latest release and place it in ReplicatedStorage.

Declare a namespace

Every definition lives in a namespace. Declare it with define, in a module the server and every client require.

ReplicatedStorage/Net.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Lync = require(ReplicatedStorage.Lync)

return Lync.define("lobby", {
    Ping = Lync.packet(Lync.empty()),
    Notice = Lync.packet(Lync.str(1, 200)),
})
  • The name is how the two sides find each other. Each namespace gets its own remotes.
  • define returns the same table, frozen.
  • When a client joins, its schema is compared with the server's. A client whose schema differs is refused with an error.

A game can have one namespace or several. Separate namespaces can be flushed on their own schedules.

Wire both sides

Each side attaches its listeners, calls start once, and calls flush every frame.

ServerScriptService/Net.server.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Lync = require(ReplicatedStorage.Lync)
local Net = require(ReplicatedStorage.Net)

Net.Ping:onServer(function(_, player)
    Net.Notice:fireClient(player, "pong")
end)

Lync.start()
RunService.PostSimulation:Connect(function()
    Lync.flush()
end)
game:BindToClose(Lync.close)
StarterPlayerScripts/Net.client.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Lync = require(ReplicatedStorage.Lync)
local Net = require(ReplicatedStorage.Net)

Net.Notice:onClient(function(text)
    print(text)
end)

Lync.start()
RunService.PostSimulation:Connect(function()
    Lync.flush()
end)

Net.Ping:fireServer()

The order of things

Rule Why
Require every module that calls define before start. start opens the namespaces that exist. A define after it throws.
Register query responders before start. start seals them. Packet and set listeners can attach at any time.
Send after start. Fires, requests, set writes and audiences throw before it.
Flush every frame, from your own function. Nothing is sent until a flush. A signal connected to flush directly passes the frame time, which flush reads as a byte budget and refuses.
Bind close on the server only. BindToClose is a server API. A client needs no shutdown call.

Lifecycle covers start, flush and close in full. The rest of the guide assumes both sides are wired as above, and marks the lines that must run after start.