Player Filtering
Filtering Entities
Section titled “Filtering Entities”By default, entities will replicate to all active players. You can decide to which players you want to replicate certain entities.
This can be done by passing a filter to set_networked. This filter can be a set of players or a single player.
local replicator = require("@server/replicator")
local entity1 = world:entity()local entity2 = world:entity()local entity3 = world:entity()
replicator:set_networked(entity1) -- replicate to all playersreplicator:set_networked(entity3, Player3) -- replicate to player3 onlyreplicator:set_networked(entity2, { -- replicate to player1 and player2 [Player1] = true, [Player2] = true,})You can also convert this filter to a blacklist by setting it to false.
local entity = world:entity()
replicator:set_networked(entity, { -- replicate to all players except player3 [Player3] = false,})You can add a non-ready player to the filter, but it wont replicate until the player is marked as ready.
Non-ready players are always excluded, both from updates and from get_full, no matter what the filter says.
local entity = world:entity()
replicator:set_networked(entity, { -- replicates to no players [Player1] = true,})
replicator:mark_player_ready(Player1) -- finally will replicate to player1Filtering Components
Section titled “Filtering Components”By default, components will be replicated to the same players that the entity is replicated to. Components can also have player filters. This will make a specific component replicate to certain players, while the rest of the entity will replicate to all other players.
You can pass a filter as the last argument of any set_* call. Filters can be set to any type of replication.
local entity = world:entity()
replicator:set_reliable(entity, component, { [Player1] = true,})replicator:set_relation(entity, relation, { [Player2] = true,})replicator:set_unreliable(entity, component, { [Player3] = true,})Example:
local entity = world:entity()local position = world:component() :: Entity<Vector3>local password = world:component() :: Entity<string>
replicator:set_networked(entity, { [Player1] = true, [Player2] = true,})-- position will replicate to player1 and player2replicator:set_reliable(entity, position)-- password will replicate only to player1replicator:set_reliable(entity, password, { [Player1] = true,})Component filters are limited to the entity filter. This means that if you set a filter that includes a player that the entity does not include, the component will not replicate to that player.
local entity = world:entity()local password = world:component() :: Entity<string>
replicator:set_networked(entity, { [Player1] = true, [Player2] = true,})replicator:set_reliable(entity, password, { [Player1] = true, -- only player1 will be able to see the password [Player3] = true,})Modifying Filters
Section titled “Modifying Filters”Filters can be modified at any time, but you need to call the set_* function again to apply the changes.
local entity = world:entity()
replicator:set_networked(entity, { [Player1] = true, [Player2] = true,})
-- now it only replicates to player1replicator:set_networked(entity, { [Player1] = true,})Modifying a filter will send deletions/additions to the difference between both filters. So removing a player from the filter will cause that player’s entity to get deleted. This also works for components, removing a player from the filter will cause that player to remove the component.
local entity = world:entity()
replicator:set_networked(entity, { [Player1] = true, [Player2] = true,})
-- now it only replicates to player1replicator:set_networked(entity, { [Player1] = true, [Player3] = true, -- Player3 gets a create event. -- Player2 gets a deletion event,})Filter Predicators
Section titled “Filter Predicators”You can use predicators as filters. Filter functions are recomputed every collect_updates cycle.
These are convenient because you dont need to update anything, as changes get picked up automatically the next cycle, allowing you to use them in an immediate-mode.
Because these filters are called every frame, they scale worse, and they have a O(n) per-entity complexity, so you should use them sparringly
local server_info = world:entity()local banned_list = world:component()
replicator:set_networked(server_info)replicator:set_reliable(server_info, banned_list, function(player) return get_rank(player) > 200end)Player Aliasing
Section titled “Player Aliasing”You can add aliases to refer to players with a different key. This is useful if you mainly work with player entities, rather than with player objects.
To set aliases you would call server_replicator:add_player_alias(player, alias)
local Players = game:GetService("Players")local replicator = require("@server/replicator")
local player = Players.Player1local player_entity = world:entity()
replicator:add_player_alias(player, player_entity)
local entity = world:entity()
replicator:set_networked(entity, { [player] = true, -- works as normal})replicator:set_networked(entity, { [player_entity] = true, -- works too :D})You can also remove aliases by calling server_replicator:remove_player_alias(alias).
This is important when a player leaves the game.
local Players = game:GetService("Players")local replicator = require("@server/replicator")
Players.PlayerRemoving:Connect(function(player) local player_entity = ref(player) replicator:remove_player_alias(player_entity)end)Filter Groups
Section titled “Filter Groups”The filters are groups of players that can behave reactively, these are useful to create a single filter that represents something (your friends, your team, your faction), and combine it with other filters. There are created with replicator:filter(initial?)
-- initial filter can be provided, optional, defaults to an empty filterlocal match = replicator:filter({ ... })
-- use it like a normal filterreplicator:set_networked(entity, match)
-- you can use add/remove to incrementally add more players
match:add(player2) -- player2 is added to match filtermatch:remove(player1) -- player1 is removed from match filter.
match:set({ -- replaces the whole filter [player4] = true, [player5] = true})
match:set(player2) -- single player filters work too.match:clear() -- removes all players (empty filter now)Deriving filters
Section titled “Deriving filters”Combinators return a new filter whose value follows its inputs. The graph is fixed when you build it; only the values change.
-- who can see your inventory, starts as only your player.local inventory_filter = replicator:filter(player)local admins_filter = replicator:filter()
-- ... plus adminslocal with_admins = inventory_filter:extend(admins_filter)
replicator:set_reliable(player, Inventory, with_admins)| Method | Result |
|---|---|
a:filter(b, ...) | further limits the filter, a & b |
a:extend(b, ...) | adds more players to filter, a | b |
a:exclude(b, ...) | excludes the player, a & !b |
Operands can be other filter groups, a derived filter, a single player or a { [Player]: boolean } set.
Only sources can be mutated; calling set/add/remove/clear on a derived filter errors.
Nothing is recomputed when you mutate a source. Filters are only marked as dirty, and get resolved in the next collect_updates cycle.
Reading a filter
Section titled “Reading a filter”You can read the final resolved filter. Both force the filter to resolve right away, so they reflect the latest mutations to sources. Predicate filters are the exception, they are only re-evaluated once per collect, so reading them between collects gives the result of the last collect.
local players = final:members() -- { [Player]: true }if final:contains(Player1) then ...endBlacklists, everyone and predicates
Section titled “Blacklists, everyone and predicates”A set of false values makes a blacklist source: every registered player except its members.
Note that, to exclude players from a filter, you should use filter:exclude() with an include filter, [Player] = false only derives from all the players in the server
Players that join later are included automatically.
You can also use filter_predicator to use a function filter, this function recalculates and updates filters once per collect_updates cycle, force-updating every single entity that depends on it, so use this carefully if you need performance.
local not_banned = replicator:filter({ [Player3] = false })local everyone = replicator:everyone() -- same as an empty blacklist, includes all players
-- re-evaluated over the registered players once per collectlocal admins = replicator:filter_predicator(function(player) return get_rank(player) > 200end)add and remove edit the member set literally, so on a blacklist not_banned:add(player) bans them.
Filters without a server
Section titled “Filters without a server”replecs.filter() creates a source that is not bound to any server yet, so you can build
your filter graph in a module that has no replicator in scope. It binds to the server the
first time a set_* call receives it, directly or through a derived filter. A filter
belongs to one server: passing it to another errors, and so does combining filters of two
servers.
-- filters.luaureturn { match = replecs.filter(), admins = replecs.filter(),}Pair API
Section titled “Pair API”Every filter in this guide can also be set declaratively. The filter is the value of the replecs.networked component,
or the value of the replication pair. Calling world:set again updates the filter, just like calling set_* again.
local entity = world:entity()
-- same as replicator:set_networked(entity, { [Player1] = true, [Player2] = true })world:set(entity, replecs.networked, { [Player1] = true, [Player2] = true,})
-- same as replicator:set_reliable(entity, password, Player1)world:set(entity, jecs.pair(replecs.reliable, password), Player1)
-- same as replicator:set_relation(entity, relation, match)world:set(entity, jecs.pair(replecs.relation, relation), match)
-- same as replicator:set_reliable(entity, banned_list, function(player) ... end)world:set(entity, jecs.pair(replecs.reliable, banned_list), function(player) return get_rank(player) > 200end)