mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
chore: housekeeping
This commit is contained in:
parent
3134136e70
commit
c842926abe
6 changed files with 44 additions and 36 deletions
36
README.md
36
README.md
|
|
@ -640,25 +640,19 @@ return {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
When a player joins on the server, call `server.addSignalsToClient` with the keyed signals that the client should receive updates for. Once they leave, call `server.removeClient` to unsubscribe them from all updates.
|
When a player notifies the server that they're ready to start syncing, call `server.addSignalsToClient` with the signals that the client should receive updates for. Once they leave, call `server.removeClient` to unsubscribe them from all updates.
|
||||||
|
|
||||||
Then, use `server.connect` to specify how state updates should be sent to each client. Pass a callback function that fires a remote with the given target player and the state updates they subscribed to.
|
Then, use `server.connect` to specify how state updates should be sent to each client. Pass a callback function that fires a remote with the given target player and the state updates they subscribed to:
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
local function onPlayerAdded(player: Player)
|
playerReadyEvent.OnServerEvent:Connect(function(player)
|
||||||
-- Add signal getters, computed signals, atoms, or reactive objects
|
-- Sync signal getters, computed signals, atoms, or reactive proxies
|
||||||
server.addSignalsToClient(player, {
|
server.addSignalsToClient(player, {
|
||||||
name = nameStore.getName,
|
name = nameStore.getName,
|
||||||
surname = nameStore.getSurname,
|
surname = nameStore.getSurname,
|
||||||
age = nameStore.ageAtom,
|
age = nameStore.ageAtom,
|
||||||
})
|
})
|
||||||
end
|
end)
|
||||||
|
|
||||||
for _, player in Players:GetPlayers() do
|
|
||||||
onPlayerAdded(player)
|
|
||||||
end
|
|
||||||
|
|
||||||
Players.PlayerAdded:Connect(onPlayerAdded)
|
|
||||||
|
|
||||||
Players.PlayerRemoving:Connect(function(player)
|
Players.PlayerRemoving:Connect(function(player)
|
||||||
server.removeClient(player)
|
server.removeClient(player)
|
||||||
|
|
@ -671,7 +665,7 @@ end)
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> On the server, make sure each key corresponds to the same signal across all players. If two players subscribe to the same key, but were given different signals, Charm will output a warning.
|
> On the server, make sure each key corresponds to the same data across all players. If two players subscribe to the same key, but were given different signals, Charm will output a warning.
|
||||||
|
|
||||||
To sync the client with the server's state, call `client.addSignals` with a table of writable signals (setter functions or atoms) whose keys match their server counterparts.
|
To sync the client with the server's state, call `client.addSignals` with a table of writable signals (setter functions or atoms) whose keys match their server counterparts.
|
||||||
|
|
||||||
|
|
@ -701,8 +695,8 @@ A configuration table that customizes the behavior of Charm Sync on the server.
|
||||||
| --------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| interval | `0` | The frequency at which the server will send patches to the client, in seconds. A value of `0` sends updates on the next frame. Set to a negative value to disable the interval. |
|
| interval | `0` | The frequency at which the server will send patches to the client, in seconds. A value of `0` sends updates on the next frame. Set to a negative value to disable the interval. |
|
||||||
| preserveHistory | `false` | Whether to preserve a full history of state changes since the last sync, at the cost of performance. This is useful if you need to replicate each individual change that occurs between sync events. |
|
| preserveHistory | `false` | Whether to preserve a full history of state changes since the last sync, at the cost of performance. This is useful if you need to replicate each individual change that occurs between sync events. |
|
||||||
| fixArrays | `true` | When `true`, Charm will attempt to work around Roblox remote event limitations regarding array patches. Disable this if your networking library serializes remote arguments (Zap, ByteNet, etc.). |
|
| fixArrays | `true` | When `true`, Charm will attempt to work around remote event limitations that cause data loss in array patches. Disable this if your networking library serializes remote arguments (Zap, ByteNet, etc.). |
|
||||||
| validatePatches | `true` | When `true`, and both `fixArrays` and strict mode are enabled, synced values containing unsafe sparse arrays or mixed tables will throw an error. See the [remote argument limitations](https://create.roblox.com/docs/scripting/events/remote#argument-limitations). |
|
| validatePatches | `true` | When `true`, and both `fixArrays` and strict mode are enabled, synced values containing unsafe sparse arrays or mixed tables will emit a warning. See the [remote argument limitations](https://create.roblox.com/docs/scripting/events/remote#argument-limitations). |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -713,7 +707,7 @@ The `addSignalsToClient` function subscribes a client to updates in the given si
|
||||||
You can pass signal getter functions, computed signals, atoms, and [reactive objects](#reactiveinitialvalue) in the `getters` table. This function can also be called multiple times on the same client to subscribe to new signals.
|
You can pass signal getter functions, computed signals, atoms, and [reactive objects](#reactiveinitialvalue) in the `getters` table. This function can also be called multiple times on the same client to subscribe to new signals.
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
Players.PlayerAdded:Connect(function(player)
|
playerReadyEvent.OnServerEvent:Connect(function(player)
|
||||||
server.addSignalsToClient(player, {
|
server.addSignalsToClient(player, {
|
||||||
name = nameStore.getName,
|
name = nameStore.getName,
|
||||||
surname = nameStore.getSurname,
|
surname = nameStore.getSurname,
|
||||||
|
|
@ -722,14 +716,22 @@ Players.PlayerAdded:Connect(function(player)
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
You're also allowed to create new signals to sync to specific players, as long as the key is unique to that player:
|
You're also allowed to create new signals to sync to specific players, as long as the key is unique to that data:
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
|
playerReadyEvent.OnServerEvent:Connect(function(player)
|
||||||
server.addSignalsToClient(player, {
|
server.addSignalsToClient(player, {
|
||||||
|
-- 🟢 Good: Player-specific data is synced with a unique key
|
||||||
[`data-{player.UserId}`] = computed(function()
|
[`data-{player.UserId}`] = computed(function()
|
||||||
return getPlayerData(player.UserId)
|
return getPlayerData(player.UserId)
|
||||||
end),
|
end),
|
||||||
|
|
||||||
|
-- 🔴 Bad: Syncing different data with the same keys does not work
|
||||||
|
playerData = computed(function()
|
||||||
|
return getPlayerData(player.UserId)
|
||||||
|
end),
|
||||||
})
|
})
|
||||||
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -754,7 +756,7 @@ server.removeSignalsFromClient(player, "surname")
|
||||||
The `removeClient` function unsubscribes a client from receiving all state updates from the server. You should call this function when a player leaves the game.
|
The `removeClient` function unsubscribes a client from receiving all state updates from the server. You should call this function when a player leaves the game.
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
Players.PlayerAdded:Connect(function(player)
|
playerReadyEvent.OnServerEvent:Connect(function(player)
|
||||||
server.addSignalsToClient(player, signals)
|
server.addSignalsToClient(player, signals)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,22 @@ return {
|
||||||
Whether the history of state changes since the client's last update
|
Whether the history of state changes since the client's last update
|
||||||
should be preserved. This is useful for values that change multiple
|
should be preserved. This is useful for values that change multiple
|
||||||
times per frame, where each individual change is important. Defaults
|
times per frame, where each individual change is important. Defaults
|
||||||
to `false`.
|
to `false` for performance reasons.
|
||||||
]]
|
]]
|
||||||
preserveHistory = false,
|
preserveHistory = false,
|
||||||
--[[
|
--[[
|
||||||
When `true`, Charm will apply validation and serialize unsafe arrays
|
When `true`, Charm will modify sparse arrays to prevent data loss from
|
||||||
to address remote event argument limitations. Defaults to `true`.
|
remote event limitations. When an unsafe array is detected, numeric
|
||||||
|
indices will be converted to string keys so that the array can be sent
|
||||||
|
safely and then converted back to an array on the receiving end.
|
||||||
|
|
||||||
This option should be disabled if your network library uses a custom
|
This option should be disabled if your network library uses a custom
|
||||||
serialization method (i.e. Zap, ByteNet) to prevent interference.
|
serialization method (i.e. Zap, ByteNet) to prevent interference.
|
||||||
]]
|
]]
|
||||||
fixArrays = true,
|
fixArrays = true,
|
||||||
--[[
|
--[[
|
||||||
When `true`, synced state containing unsafe sparse arrays or mixed
|
When `true`, synced state containing unsafe sparse arrays or mixed
|
||||||
tables will throw an error. Only checked in strict mode and if
|
tables will emit a warning. Only checked in strict mode and if
|
||||||
`fixArrays` is enabled. Defaults to `true`.
|
`fixArrays` is enabled. Defaults to `true`.
|
||||||
]]
|
]]
|
||||||
validatePatches = true,
|
validatePatches = true,
|
||||||
|
|
|
||||||
13
packages/charm-sync/src/index.d.ts
vendored
13
packages/charm-sync/src/index.d.ts
vendored
|
|
@ -78,14 +78,17 @@ export const config: {
|
||||||
* Whether the history of state changes since the client's last update
|
* Whether the history of state changes since the client's last update
|
||||||
* should be preserved. This is useful for values that change multiple
|
* should be preserved. This is useful for values that change multiple
|
||||||
* times per frame, where each individual change is important. Defaults
|
* times per frame, where each individual change is important. Defaults
|
||||||
* to `false`.
|
* to `false` for performance reasons.
|
||||||
*
|
*
|
||||||
* @server
|
* @server
|
||||||
*/
|
*/
|
||||||
preserveHistory: boolean;
|
preserveHistory: boolean;
|
||||||
/**
|
/**
|
||||||
* When `true`, Charm will apply validation and serialize unsafe arrays
|
* When `true`, Charm will modify sparse arrays to prevent data loss from
|
||||||
* to address remote event argument limitations. Defaults to `true`.
|
* remote event limitations. When an unsafe array is detected, numeric
|
||||||
|
* indices will be converted to string keys so that the array can be sent
|
||||||
|
* safely and then converted back to an array on the receiving end.
|
||||||
|
*
|
||||||
* This option should be disabled if your network library uses a custom
|
* This option should be disabled if your network library uses a custom
|
||||||
* serialization method (i.e. Zap, ByteNet) to prevent interference.
|
* serialization method (i.e. Zap, ByteNet) to prevent interference.
|
||||||
*
|
*
|
||||||
|
|
@ -94,7 +97,7 @@ export const config: {
|
||||||
fixArrays: boolean;
|
fixArrays: boolean;
|
||||||
/**
|
/**
|
||||||
* When `true`, synced state containing unsafe sparse arrays or mixed
|
* When `true`, synced state containing unsafe sparse arrays or mixed
|
||||||
* tables will throw an error. Only checked in strict mode and if
|
* tables will emit a warning. Only checked in strict mode and if
|
||||||
* `fixArrays` is enabled. Defaults to `true`.
|
* `fixArrays` is enabled. Defaults to `true`.
|
||||||
*
|
*
|
||||||
* @server
|
* @server
|
||||||
|
|
@ -242,7 +245,7 @@ export namespace patch {
|
||||||
* @param statePatch The patches to apply.
|
* @param statePatch The patches to apply.
|
||||||
* @returns The new state with the patch applied.
|
* @returns The new state with the patch applied.
|
||||||
*/
|
*/
|
||||||
export function apply<State, FixArrays extends boolean = true>(
|
export function applyImmutable<State, FixArrays extends boolean = true>(
|
||||||
currentState: State,
|
currentState: State,
|
||||||
statePatch: SyncPatch<State, FixArrays>,
|
statePatch: SyncPatch<State, FixArrays>,
|
||||||
): State;
|
): State;
|
||||||
|
|
|
||||||
|
|
@ -497,7 +497,7 @@ end)
|
||||||
|
|
||||||
test("recursive effects should run and cleanup in order", function()
|
test("recursive effects should run and cleanup in order", function()
|
||||||
local a, setA = signal(0)
|
local a, setA = signal(0)
|
||||||
local order = {}
|
local order: { string } = {}
|
||||||
|
|
||||||
effect(function()
|
effect(function()
|
||||||
local value = a()
|
local value = a()
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ end)
|
||||||
|
|
||||||
test("should handle recursive additions and removals", function()
|
test("should handle recursive additions and removals", function()
|
||||||
local items, setItems = signal({ 1 })
|
local items, setItems = signal({ 1 })
|
||||||
local edits = {}
|
local edits: { string } = {}
|
||||||
|
|
||||||
observe(items, function(item)
|
observe(items, function(item)
|
||||||
if item < 5 then
|
if item < 5 then
|
||||||
|
|
@ -114,7 +114,7 @@ end)
|
||||||
|
|
||||||
test("should handle self removal", function()
|
test("should handle self removal", function()
|
||||||
local items, setItems = signal({ 1, 2, 3, 4 })
|
local items, setItems = signal({ 1, 2, 3, 4 })
|
||||||
local edits = {}
|
local edits: { string } = {}
|
||||||
|
|
||||||
observe(items, function(item, index)
|
observe(items, function(item, index)
|
||||||
setItems(function(state: { number })
|
setItems(function(state: { number })
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ test("should run in untracked", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("should ensure correct execution order", function()
|
test("should ensure correct execution order", function()
|
||||||
local order = {}
|
local order: { number } = {}
|
||||||
local n1, setN1 = signal(0)
|
local n1, setN1 = signal(0)
|
||||||
local n2, setN2 = signal(0)
|
local n2, setN2 = signal(0)
|
||||||
local sum = computed(function()
|
local sum = computed(function()
|
||||||
|
|
@ -72,7 +72,7 @@ test("should ensure correct execution order", function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("should skip redundant updates", function()
|
test("should skip redundant updates", function()
|
||||||
local order = {}
|
local order: { "even" | "odd" } = {}
|
||||||
local a, setA = signal(0)
|
local a, setA = signal(0)
|
||||||
|
|
||||||
subscribe(function(): "even" | "odd"
|
subscribe(function(): "even" | "odd"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue