@rbxts/replecs 0.1.0-alpha5 → 0.1.0-alpha8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -109
- package/out/replecs/client.luau +50 -16
- package/out/replecs/common.luau +6 -4
- package/out/replecs/index.d.ts +42 -41
- package/out/replecs/init.luau +12 -5
- package/out/replecs/masking/init.luau +4 -6
- package/out/replecs/server.luau +49 -7
- package/out/replecs/utils.luau +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Replecs ⚡
|
|
2
|
-
Replecs is a fast, fine controlled JECS replication library
|
|
3
2
|
|
|
4
|
-
Replecs is a
|
|
3
|
+
Replecs is a fast, fine controlled JECS replication library
|
|
4
|
+
|
|
5
|
+
Replecs is a feature rich, fast, flexible, powerful, and runtime/library agnostic replication library for ECS.
|
|
5
6
|
|
|
6
7
|
- Per-entity replication
|
|
7
8
|
- Heavily optimized granular player filtering for entities and components
|
|
@@ -14,110 +15,3 @@ Replecs is a feature rich, fast, flexible, powerful, and runtime/library agnosti
|
|
|
14
15
|
- Unreliable replication, with automatic 1kb buffer size limit
|
|
15
16
|
|
|
16
17
|
Demo place: https://www.roblox.com/games/132070456195381/
|
|
17
|
-
# Getting Started
|
|
18
|
-
### Roblox-TS
|
|
19
|
-
Install `@rbxts/jecs-addons` `@rbxts/replecs` `@rbxts/jecs`.
|
|
20
|
-
|
|
21
|
-
**Make sure you install jecs as v0.8.3, not ^0.9.0** as they are not compatible. You can check the versioning by running `npm ls @rbxts/jecs`
|
|
22
|
-
|
|
23
|
-
## 🔥Setting Up
|
|
24
|
-
|
|
25
|
-
`shared/world.ts`
|
|
26
|
-
```ts
|
|
27
|
-
import { observer_add } from "@rbxts/jecs-addons"
|
|
28
|
-
import { world } from "@rbxts/jecs"
|
|
29
|
-
|
|
30
|
-
// you need to add the observer addon before passing it to the replicator!
|
|
31
|
-
export const world = observer_add(world())
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
`server/runtime.server.ts`
|
|
35
|
-
```ts
|
|
36
|
-
import { world } from "shared/world"
|
|
37
|
-
import { create_server } from "@rbxts/replecs"
|
|
38
|
-
|
|
39
|
-
const replicator = create_server(world)
|
|
40
|
-
|
|
41
|
-
//make sure you call replicator.init before you apply any operations
|
|
42
|
-
replicator.init()
|
|
43
|
-
|
|
44
|
-
//use whatever networking library you want, I am using @rbxts/remo
|
|
45
|
-
InitialReplecsRemotes.requestInitialPackets.connect(player => {
|
|
46
|
-
if (replicator.is_player_ready(player)) return;
|
|
47
|
-
replicator.mark_player_ready(player);
|
|
48
|
-
|
|
49
|
-
const [buf, variants] = replicator.get_full(player);
|
|
50
|
-
|
|
51
|
-
InitialReplecsRemotes.sendInitialPackets(player, buf, variants);
|
|
52
|
-
});
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
`client/runtime.client.ts`
|
|
56
|
-
```ts
|
|
57
|
-
import { world } from "shared/world"
|
|
58
|
-
import { create_client } from "@rbxts/replecs"
|
|
59
|
-
|
|
60
|
-
const replicator = create_server(world)
|
|
61
|
-
|
|
62
|
-
//make sure you call replicator.init before you apply any operations
|
|
63
|
-
replicator.init()
|
|
64
|
-
|
|
65
|
-
InitialReplecsRemotes.requestInitialPackets();
|
|
66
|
-
|
|
67
|
-
InitialReplecsRemotes.sendInitialPackets.connect((buf, variants) => {
|
|
68
|
-
const replicator = HANDLE.getClientReplicator();
|
|
69
|
-
|
|
70
|
-
replicator.apply_full(buf, variants);
|
|
71
|
-
});
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Using in action
|
|
75
|
-
Basic replicated component
|
|
76
|
-
```ts
|
|
77
|
-
const component1 = world.component<string>()
|
|
78
|
-
//you must add shared or Shared component to components you want to replicate
|
|
79
|
-
world.add(component1, shared)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const replicatedEntity = world.entity()
|
|
83
|
-
world.set(replicatedEntity, component1, "string")
|
|
84
|
-
|
|
85
|
-
//you must add replicatedEntity as networked and add the components you want to replicate
|
|
86
|
-
world.add(replicatedEntity, networked)
|
|
87
|
-
|
|
88
|
-
//this makes components appear in collect_updates
|
|
89
|
-
world.add(replicatedEntity, pair(reliable, component1))
|
|
90
|
-
|
|
91
|
-
//this makes components appear in collect_unreliable
|
|
92
|
-
world.add(replicatedEntity, pair(unreliable, component1))
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Replication process
|
|
96
|
-
```ts
|
|
97
|
-
RunService.Heartbeat.Connect(() => {
|
|
98
|
-
//this collects reliable updates
|
|
99
|
-
for (const [player, buf, variants] of serverReplicator.collect_updates()) {
|
|
100
|
-
//here I am using yetanothernet
|
|
101
|
-
reliableRoute.send(buf, variants).to(player)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
//this collects unrealible updates
|
|
105
|
-
for (const [player, buf, variants] serverReplicator.collect_updates()) {
|
|
106
|
-
//here I am using yetanothernet
|
|
107
|
-
unreliableRoute.send(buf, variants).to(player)
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
This is the basics of Replecs. For deeper understanding check out the demo folder files here
|
|
113
|
-
- [player first hydration](https://github.com/PepeElToro41/replecs/blob/main/demo/src/server/init.server.luau#L22)
|
|
114
|
-
- [send replication](https://github.com/PepeElToro41/replecs/blob/main/demo/src/server/systems/replecs_server.luau#L12)
|
|
115
|
-
- [receive replication](https://github.com/PepeElToro41/replecs/blob/main/demo/src/client/systems/replecs_client.luau)
|
|
116
|
-
- [setup an entity to replicate](https://github.com/PepeElToro41/replecs/blob/main/demo/src/server/controllers/spawn_cubes.luau#L10)
|
|
117
|
-
- custom ids: [client](https://github.com/PepeElToro41/replecs/blob/main/demo/src/shared/components/init.luau#L33) - [server](https://github.com/PepeElToro41/replecs/blob/main/demo/src/shared/players.luau#L16)
|
|
118
|
-
- [serdes](https://github.com/PepeElToro41/replecs/blob/main/demo/src/shared/components/init.luau#L37)
|
|
119
|
-
- [how you would add `shared` to all components](https://github.com/PepeElToro41/replecs/blob/main/demo/src/shared/components/init.luau#L52)
|
|
120
|
-
- create the replicators: [client](https://github.com/PepeElToro41/replecs/blob/main/demo/src/client/replicator.luau) - [server](https://github.com/PepeElToro41/replecs/blob/main/demo/src/server/replicator.luau)
|
|
121
|
-
-
|
|
122
|
-
### Luau
|
|
123
|
-
TODO
|
package/out/replecs/client.luau
CHANGED
|
@@ -14,12 +14,13 @@ type World = jecs.World
|
|
|
14
14
|
export type Client = {
|
|
15
15
|
world: World,
|
|
16
16
|
inited: boolean?,
|
|
17
|
+
|
|
17
18
|
is_replicating: boolean,
|
|
18
19
|
after_replication_callbacks: { () -> () },
|
|
19
20
|
|
|
20
21
|
components: common.Components,
|
|
21
22
|
shared: common.Shared,
|
|
22
|
-
|
|
23
|
+
global_handler: ((id: number) -> Entity)?,
|
|
23
24
|
server_ids: { [number]: Entity },
|
|
24
25
|
client_ids: { [Entity]: number },
|
|
25
26
|
ordered_creation: boolean,
|
|
@@ -28,10 +29,13 @@ export type Client = {
|
|
|
28
29
|
init: (self: Client, world: World?) -> (),
|
|
29
30
|
destroy: (self: Client) -> (),
|
|
30
31
|
after_replication: (self: Client, callback: () -> ()) -> (),
|
|
31
|
-
|
|
32
|
+
handle_global: (self: Client, handler: (id: number) -> Entity) -> (),
|
|
32
33
|
get_server_entity: (self: Client, client_entity: Entity) -> number?,
|
|
33
34
|
get_client_entity: (self: Client, server_entity: number) -> Entity?,
|
|
34
35
|
|
|
36
|
+
encode_component: (self: Client, component: Entity) -> number,
|
|
37
|
+
decode_component: (self: Client, component: number) -> Entity,
|
|
38
|
+
|
|
35
39
|
apply_updates: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
|
|
36
40
|
apply_unreliable: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
|
|
37
41
|
apply_full: (self: Client, buf: buffer, all_variants: { { any } }?) -> (),
|
|
@@ -68,6 +72,23 @@ local function get_or_create_entity(client: Client, server_id: number): Entity
|
|
|
68
72
|
end
|
|
69
73
|
end
|
|
70
74
|
|
|
75
|
+
local function encode_component(client: Client, component: Entity): number
|
|
76
|
+
local encoded = client.shared.ids[component]
|
|
77
|
+
if not encoded then
|
|
78
|
+
utils.logerror(`attempted to encode a non-shared component `, utils.logcomponent(client.world, component))
|
|
79
|
+
return 0
|
|
80
|
+
end
|
|
81
|
+
return encoded
|
|
82
|
+
end
|
|
83
|
+
local function decode_component(client: Client, encoded: number): Entity
|
|
84
|
+
local component = client.shared.components[encoded]
|
|
85
|
+
if not component then
|
|
86
|
+
print("NON SHARED COMPONENT", encoded, client.shared.components)
|
|
87
|
+
error "attemped to decode a non shared component"
|
|
88
|
+
end
|
|
89
|
+
return component
|
|
90
|
+
end
|
|
91
|
+
|
|
71
92
|
local function read_component_id(client: Client, c: Cursor)
|
|
72
93
|
local shared_id = cursor.readu8(c)
|
|
73
94
|
local component = client.shared.components[shared_id]
|
|
@@ -86,7 +107,18 @@ local function read_component_value(client: Client, c: Cursor, variants: { any }
|
|
|
86
107
|
local bytespan = client.shared.bytespan[component] or cursor.read_vlq(c)
|
|
87
108
|
|
|
88
109
|
local appended = cursor.read_buffer(c, bytespan)
|
|
89
|
-
local
|
|
110
|
+
local serdes_variants: { any }? = nil
|
|
111
|
+
|
|
112
|
+
if serdes.includes_variants then
|
|
113
|
+
local start_variant = cursor.read_vlq(c)
|
|
114
|
+
|
|
115
|
+
if start_variant > 0 then
|
|
116
|
+
local size = cursor.read_vlq(c)
|
|
117
|
+
serdes_variants = table.move(variants :: { any }, start_variant, start_variant + size - 1, 1, {})
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
local output = serdes.deserialize(appended, serdes_variants)
|
|
90
122
|
return component, output
|
|
91
123
|
else
|
|
92
124
|
local variant_id = cursor.read_vlq(c)
|
|
@@ -98,11 +130,11 @@ local function read_component_value(client: Client, c: Cursor, variants: { any }
|
|
|
98
130
|
end
|
|
99
131
|
end
|
|
100
132
|
|
|
101
|
-
local function
|
|
102
|
-
if not client.
|
|
103
|
-
error "global id parser not set, consider using client:
|
|
133
|
+
local function resolve_global(client: Client, global_id: number): Entity
|
|
134
|
+
if not client.global_handler then
|
|
135
|
+
error "global id parser not set, consider using client:handle_global()"
|
|
104
136
|
end
|
|
105
|
-
local parsed = client.
|
|
137
|
+
local parsed = client.global_handler(global_id)
|
|
106
138
|
return parsed
|
|
107
139
|
end
|
|
108
140
|
|
|
@@ -129,11 +161,11 @@ local function read_entity_id(client: Client, c: Cursor, variants: { any }?): (E
|
|
|
129
161
|
return read_component_id(client, c), nil
|
|
130
162
|
elseif id_type == ENTITY_ID_TYPES.global then
|
|
131
163
|
local global_id = cursor.readu8(c) - GLOBAL_ID_OFFSET
|
|
132
|
-
return
|
|
164
|
+
return resolve_global(client, global_id), nil
|
|
133
165
|
end
|
|
134
166
|
error(`malformed entity id {id_type} ` .. cursor.readu32(c))
|
|
135
167
|
else
|
|
136
|
-
return
|
|
168
|
+
return resolve_global(client, id_type - GLOBAL_ID_OFFSET), nil
|
|
137
169
|
end
|
|
138
170
|
end
|
|
139
171
|
|
|
@@ -184,7 +216,7 @@ local function check_packet_type(c: Cursor, type: string)
|
|
|
184
216
|
local got: string = nil :: any
|
|
185
217
|
for k, v in PACKET_TYPES do
|
|
186
218
|
if v == packet_type then
|
|
187
|
-
got = k
|
|
219
|
+
got = k :: string
|
|
188
220
|
break
|
|
189
221
|
end
|
|
190
222
|
end
|
|
@@ -389,8 +421,8 @@ local function init(client: Client, _world: World?)
|
|
|
389
421
|
|
|
390
422
|
client.shared = utils.create_shared_lookup(world, components)
|
|
391
423
|
|
|
392
|
-
for component,
|
|
393
|
-
client.custom_ids[component] =
|
|
424
|
+
for component, custom_get in world:query(components.custom_handler):with(components.shared, ECS_NAME) do
|
|
425
|
+
client.custom_ids[component] = custom_get
|
|
394
426
|
end
|
|
395
427
|
end
|
|
396
428
|
|
|
@@ -419,8 +451,8 @@ local function destroy(client: Client)
|
|
|
419
451
|
-- right now this is kind of useless
|
|
420
452
|
end
|
|
421
453
|
|
|
422
|
-
local function
|
|
423
|
-
client.
|
|
454
|
+
local function handle_global(client: Client, handler: (id: number) -> Entity)
|
|
455
|
+
client.global_handler = handler
|
|
424
456
|
end
|
|
425
457
|
|
|
426
458
|
local client = {}
|
|
@@ -431,9 +463,11 @@ client.after_replication = after_replication
|
|
|
431
463
|
client.apply_updates = apply_updates
|
|
432
464
|
client.apply_unreliable = apply_unreliable
|
|
433
465
|
client.apply_full = apply_full
|
|
466
|
+
client.encode_component = encode_component
|
|
467
|
+
client.decode_component = decode_component
|
|
434
468
|
client.get_server_entity = get_server_entity
|
|
435
469
|
client.get_client_entity = get_client_entity
|
|
436
|
-
client.
|
|
470
|
+
client.handle_global = handle_global
|
|
437
471
|
|
|
438
472
|
local function create(world: World?, components: common.Components): Client
|
|
439
473
|
local self = {} :: Client
|
|
@@ -443,7 +477,7 @@ local function create(world: World?, components: common.Components): Client
|
|
|
443
477
|
self.custom_ids = {}
|
|
444
478
|
self.server_ids = {}
|
|
445
479
|
self.client_ids = {}
|
|
446
|
-
self.
|
|
480
|
+
self.global_handler = nil
|
|
447
481
|
self.world = world :: any
|
|
448
482
|
self.inited = false
|
|
449
483
|
self.is_replicating = false
|
package/out/replecs/common.luau
CHANGED
|
@@ -5,8 +5,9 @@ export type PlayerFilter = {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export type Serdes = {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
includes_variants: boolean?,
|
|
9
|
+
serialize: (value: any) -> (buffer, { any }?),
|
|
10
|
+
deserialize: (buffer, { any }?) -> any,
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export type Components = {
|
|
@@ -18,8 +19,9 @@ export type Components = {
|
|
|
18
19
|
|
|
19
20
|
serdes: jecs.Entity<Serdes>,
|
|
20
21
|
bytespan: jecs.Entity<number>,
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
custom: jecs.Entity,
|
|
23
|
+
custom_handler: jecs.Entity<(value: any) -> jecs.Entity>,
|
|
24
|
+
global: jecs.Entity<number>,
|
|
23
25
|
__alive_tracking__: jecs.Entity,
|
|
24
26
|
}
|
|
25
27
|
|
package/out/replecs/index.d.ts
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
|
-
import type { Entity,
|
|
1
|
+
import type { Entity, Tag, World } from "@rbxts/jecs";
|
|
2
2
|
|
|
3
3
|
declare namespace Replecs {
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
removed<T>(
|
|
16
|
-
this: ObserverWorld,
|
|
17
|
-
component: Id<T>,
|
|
18
|
-
callback: (e: Entity, id: Id<T>) => void
|
|
19
|
-
): () => void;
|
|
20
|
-
changed<T>(
|
|
21
|
-
this: ObserverWorld,
|
|
22
|
-
component: Id<T>,
|
|
23
|
-
callback: (e: Entity, id: Id<T>, value: T) => void
|
|
24
|
-
): () => void;
|
|
25
|
-
}
|
|
4
|
+
export type SerdesTable =
|
|
5
|
+
| {
|
|
6
|
+
includes_variants?: false;
|
|
7
|
+
serialize: (value: any) => buffer;
|
|
8
|
+
deserialize: (buffer: buffer) => any;
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
includes_variants: true;
|
|
12
|
+
serialize: (value: any) => LuaTuple<[buffer, defined[] | undefined]>;
|
|
13
|
+
deserialize: (buffer: buffer, blobs: defined[] | undefined) => any;
|
|
14
|
+
};
|
|
26
15
|
|
|
27
16
|
type MemberFilterMap = Map<Player, boolean>;
|
|
28
17
|
type MemberFilter = Player | MemberFilterMap | undefined;
|
|
@@ -44,8 +33,9 @@ declare namespace Replecs {
|
|
|
44
33
|
|
|
45
34
|
serdes: Entity<SerdesTable>;
|
|
46
35
|
bytespan: Entity<number>;
|
|
47
|
-
|
|
48
|
-
|
|
36
|
+
custom: Entity;
|
|
37
|
+
custom_handler: Entity<(value: any) => Entity>;
|
|
38
|
+
global: Entity<number>;
|
|
49
39
|
|
|
50
40
|
Shared: Tag;
|
|
51
41
|
Networked: Entity<MemberFilter>;
|
|
@@ -55,39 +45,50 @@ declare namespace Replecs {
|
|
|
55
45
|
|
|
56
46
|
Serdes: Entity<SerdesTable>;
|
|
57
47
|
Bytespan: Entity<number>;
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
Custom: Entity;
|
|
49
|
+
CustomHandler: Entity<(value: any) => Entity>;
|
|
50
|
+
Global: Entity<number>;
|
|
60
51
|
}
|
|
61
52
|
|
|
62
53
|
export interface Client {
|
|
63
|
-
world:
|
|
54
|
+
world: World;
|
|
64
55
|
inited?: boolean;
|
|
65
56
|
|
|
66
|
-
init(world?:
|
|
57
|
+
init(world?: World): void;
|
|
67
58
|
destroy(): void;
|
|
68
59
|
after_replication(callback: () => void): void;
|
|
69
60
|
|
|
70
|
-
apply_updates(buf: buffer, all_variants?:
|
|
71
|
-
apply_unreliable(buf: buffer, all_variants?:
|
|
72
|
-
apply_full(buf: buffer, all_variants?:
|
|
73
|
-
|
|
61
|
+
apply_updates(buf: buffer, all_variants?: unknown[][]): void;
|
|
62
|
+
apply_unreliable(buf: buffer, all_variants?: unknown[][]): void;
|
|
63
|
+
apply_full(buf: buffer, all_variants?: unknown[][]): void;
|
|
64
|
+
|
|
65
|
+
encode_component(component: Entity): number;
|
|
66
|
+
decode_component(encoded: number): Entity;
|
|
67
|
+
|
|
68
|
+
handle_global(handler: (id: number) => Entity): void;
|
|
74
69
|
|
|
75
70
|
get_server_entity(client_entity: Entity): number | undefined;
|
|
76
71
|
get_client_entity(server_entity: number): Entity | undefined;
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
export interface Server {
|
|
80
|
-
world:
|
|
75
|
+
world: World;
|
|
81
76
|
inited?: boolean;
|
|
82
77
|
|
|
83
|
-
init(world?:
|
|
78
|
+
init(world?: World): void;
|
|
84
79
|
destroy(): void;
|
|
85
80
|
|
|
86
|
-
get_full(player: Player): LuaTuple<[buffer,
|
|
87
|
-
collect_updates(): IterableFunction<
|
|
81
|
+
get_full(player: Player): LuaTuple<[buffer, unknown[][]]>;
|
|
82
|
+
collect_updates(): IterableFunction<
|
|
83
|
+
LuaTuple<[Player, buffer, unknown[][]]>
|
|
84
|
+
>;
|
|
88
85
|
collect_unreliable(): IterableFunction<
|
|
89
|
-
LuaTuple<[Player, buffer,
|
|
86
|
+
LuaTuple<[Player, buffer, unknown[][]]>
|
|
90
87
|
>;
|
|
88
|
+
|
|
89
|
+
encode_component(component: Entity): number;
|
|
90
|
+
decode_component(encoded: number): Entity;
|
|
91
|
+
|
|
91
92
|
mark_player_ready(player: Player): void;
|
|
92
93
|
is_player_ready(player: Player): boolean;
|
|
93
94
|
|
|
@@ -102,9 +103,9 @@ declare namespace Replecs {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
export interface Replecs extends Components {
|
|
105
|
-
create: (world?:
|
|
106
|
-
create_server: (world?:
|
|
107
|
-
create_client: (world?:
|
|
106
|
+
create: (world?: World) => ReplecsLib;
|
|
107
|
+
create_server: (world?: World) => Server;
|
|
108
|
+
create_client: (world?: World) => Client;
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
package/out/replecs/init.luau
CHANGED
|
@@ -27,30 +27,37 @@ local replecs = {
|
|
|
27
27
|
|
|
28
28
|
serdes = jecs.component() :: jecs.Entity<common.Serdes>,
|
|
29
29
|
bytespan = jecs.component() :: jecs.Entity<number>,
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
custom = jecs.tag() :: jecs.Entity,
|
|
31
|
+
custom_handler = jecs.component() :: jecs.Entity<(any) -> jecs.Entity>,
|
|
32
|
+
global = jecs.component() :: jecs.Entity<number>,
|
|
32
33
|
__alive_tracking__ = jecs.tag(),
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
replecs.__index = replecs
|
|
36
37
|
replecs.Serdes = replecs.serdes
|
|
37
38
|
replecs.Bytespan = replecs.bytespan
|
|
38
|
-
replecs.
|
|
39
|
+
replecs.Custom = replecs.custom
|
|
40
|
+
replecs.CustomHandler = replecs.custom_handler
|
|
39
41
|
replecs.Networked = replecs.networked
|
|
40
42
|
replecs.Reliable = replecs.reliable
|
|
41
43
|
replecs.Unreliable = replecs.unreliable
|
|
42
44
|
replecs.Pair = replecs.pair
|
|
43
45
|
replecs.Shared = replecs.shared
|
|
44
|
-
replecs.
|
|
46
|
+
replecs.Global = replecs.global
|
|
45
47
|
|
|
46
48
|
jecs.meta(replecs.serdes, jecs.Name, "replecs.serdes")
|
|
47
49
|
jecs.meta(replecs.bytespan, jecs.Name, "replecs.bytespan")
|
|
48
|
-
jecs.meta(replecs.
|
|
50
|
+
jecs.meta(replecs.custom, jecs.Name, "replecs.custom")
|
|
51
|
+
jecs.meta(replecs.custom_handler, jecs.Name, "replecs.custom_handler")
|
|
49
52
|
jecs.meta(replecs.networked, jecs.Name, "replecs.networked")
|
|
50
53
|
jecs.meta(replecs.reliable, jecs.Name, "replecs.reliable")
|
|
51
54
|
jecs.meta(replecs.unreliable, jecs.Name, "replecs.unreliable")
|
|
52
55
|
jecs.meta(replecs.pair, jecs.Name, "replecs.pair")
|
|
53
56
|
jecs.meta(replecs.shared, jecs.Name, "replecs.shared")
|
|
57
|
+
jecs.meta(replecs.global, jecs.Name, "replecs.global")
|
|
58
|
+
|
|
59
|
+
jecs.meta(replecs.custom, jecs.Exclusive)
|
|
60
|
+
jecs.meta(replecs.global, jecs.Exclusive)
|
|
54
61
|
|
|
55
62
|
function replecs.create_server(world: jecs.World?): Server
|
|
56
63
|
return server.create(world, replecs)
|
|
@@ -1136,12 +1136,14 @@ function masking_controller.allocate_component_change(
|
|
|
1136
1136
|
local storage = masking:get_component_storage_group(entity, component, COMPONENT_TYPES.component)
|
|
1137
1137
|
local changes = get_or_set_changes(storage, entity)
|
|
1138
1138
|
changes.component[component] = value
|
|
1139
|
+
changes.removed[component] = nil
|
|
1139
1140
|
end
|
|
1140
1141
|
|
|
1141
1142
|
function masking_controller.allocate_tag_addition(masking: MaskingInternal, entity: Entity, tag: Entity)
|
|
1142
1143
|
local storage = masking:get_component_storage_group(entity, tag, COMPONENT_TYPES.tag)
|
|
1143
1144
|
local changes = get_or_set_changes(storage, entity)
|
|
1144
1145
|
changes.tagged[tag] = true
|
|
1146
|
+
changes.removed[tag] = nil
|
|
1145
1147
|
end
|
|
1146
1148
|
|
|
1147
1149
|
function masking_controller.allocate_pair_addition(
|
|
@@ -1164,9 +1166,7 @@ function masking_controller.allocate_component_removal(masking: MaskingInternal,
|
|
|
1164
1166
|
local storage = masking:get_component_storage_group(entity, component, COMPONENT_TYPES.component)
|
|
1165
1167
|
local changes = get_or_set_changes(storage, entity)
|
|
1166
1168
|
|
|
1167
|
-
|
|
1168
|
-
changes.component[component] = nil
|
|
1169
|
-
end
|
|
1169
|
+
changes.component[component] = nil
|
|
1170
1170
|
changes.removed[component] = true
|
|
1171
1171
|
end
|
|
1172
1172
|
|
|
@@ -1174,9 +1174,7 @@ function masking_controller.allocate_tag_removal(masking: MaskingInternal, entit
|
|
|
1174
1174
|
local storage = masking:get_component_storage_group(entity, tag, COMPONENT_TYPES.tag)
|
|
1175
1175
|
local changes = get_or_set_changes(storage, entity)
|
|
1176
1176
|
|
|
1177
|
-
|
|
1178
|
-
changes.tagged[tag] = nil
|
|
1179
|
-
end
|
|
1177
|
+
changes.tagged[tag] = nil
|
|
1180
1178
|
changes.removed[tag] = true
|
|
1181
1179
|
end
|
|
1182
1180
|
|
package/out/replecs/server.luau
CHANGED
|
@@ -54,6 +54,9 @@ export type Server = {
|
|
|
54
54
|
init: (self: Server, world: World?) -> (),
|
|
55
55
|
destroy: (self: Server) -> (),
|
|
56
56
|
|
|
57
|
+
encode_component: (self: Server, component: Entity) -> number,
|
|
58
|
+
decode_component: (self: Server, component: number) -> Entity,
|
|
59
|
+
|
|
57
60
|
get_full: (self: Server, player: Player) -> (buffer, { { any } }),
|
|
58
61
|
collect_updates: (self: Server) -> () -> (Player, buffer, { { any } }),
|
|
59
62
|
collect_unreliable: (self: Server) -> () -> (Player, buffer, { { any } }),
|
|
@@ -330,6 +333,24 @@ local function untrack_entity_pair(server: Server, entity: Entity, relation: Ent
|
|
|
330
333
|
end
|
|
331
334
|
end
|
|
332
335
|
|
|
336
|
+
local function encode_component(server: Server, component: Entity): number
|
|
337
|
+
local encoded = server.shared.ids[component]
|
|
338
|
+
if not encoded then
|
|
339
|
+
utils.logerror(`attempted to encode a non-shared component `, utils.logcomponent(server.world, component))
|
|
340
|
+
return 0
|
|
341
|
+
end
|
|
342
|
+
return encoded
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
local function decode_component(server: Server, encoded: number): Entity
|
|
346
|
+
local component = server.shared.components[encoded]
|
|
347
|
+
if not component then
|
|
348
|
+
print("NON SHARED COMPONENTS", encoded, server.shared.components)
|
|
349
|
+
error "attemped to decode a non shared component"
|
|
350
|
+
end
|
|
351
|
+
return component
|
|
352
|
+
end
|
|
353
|
+
|
|
333
354
|
local function write_component_id(server: Server, c: Cursor, component: Entity)
|
|
334
355
|
local encoded = server.shared.ids[component]
|
|
335
356
|
if not encoded then
|
|
@@ -342,7 +363,26 @@ end
|
|
|
342
363
|
local function write_component_value(server: Server, c: Cursor, component: Entity, value: any, variants: { any })
|
|
343
364
|
local serdes = server.shared.serdes[component]
|
|
344
365
|
if serdes then
|
|
345
|
-
local output = serdes.serialize(if value == NIL_COMPONENT_VALUE then nil else value)
|
|
366
|
+
local output, serdes_variants = serdes.serialize(if value == NIL_COMPONENT_VALUE then nil else value)
|
|
367
|
+
|
|
368
|
+
if serdes.includes_variants then
|
|
369
|
+
if serdes_variants == nil then
|
|
370
|
+
-- 128 is zero for vlq
|
|
371
|
+
cursor.writeu8(c, 128)
|
|
372
|
+
else
|
|
373
|
+
local start_variants = #variants
|
|
374
|
+
if #serdes_variants > 0 then
|
|
375
|
+
table.move(serdes_variants, 1, #serdes_variants, #variants + 1, variants)
|
|
376
|
+
end
|
|
377
|
+
cursor.write_vlq(c, #serdes_variants)
|
|
378
|
+
cursor.write_vlq(c, start_variants + 1)
|
|
379
|
+
end
|
|
380
|
+
elseif serdes_variants ~= nil then
|
|
381
|
+
utils.logerror(
|
|
382
|
+
"serdes: serializer provided variants yet includes_variants is not set to true for component",
|
|
383
|
+
utils.logcomponent(server.world, component)
|
|
384
|
+
)
|
|
385
|
+
end
|
|
346
386
|
|
|
347
387
|
local bytespan = server.shared.bytespan[component]
|
|
348
388
|
cursor.write_buffer(c, output)
|
|
@@ -1033,9 +1073,9 @@ function init(server: Server, _world: World?)
|
|
|
1033
1073
|
masking:stop_component(entity, component, COMPONENT_TYPES.unreliable)
|
|
1034
1074
|
end))
|
|
1035
1075
|
|
|
1036
|
-
hook(hooks.added(world, components.
|
|
1076
|
+
hook(hooks.added(world, components.custom, function(entity, id)
|
|
1037
1077
|
if not IS_PAIR(id) then
|
|
1038
|
-
utils.logerror "
|
|
1078
|
+
utils.logerror "replecs.custom should be used with a relationship in the server"
|
|
1039
1079
|
end
|
|
1040
1080
|
local target = PAIR_SECOND(world, id)
|
|
1041
1081
|
if server.custom_ids[entity] then
|
|
@@ -1045,23 +1085,23 @@ function init(server: Server, _world: World?)
|
|
|
1045
1085
|
|
|
1046
1086
|
server.custom_ids[entity] = target
|
|
1047
1087
|
end))
|
|
1048
|
-
hook(hooks.removed(world, components.
|
|
1088
|
+
hook(hooks.removed(world, components.custom, function(entity)
|
|
1049
1089
|
server.custom_ids[entity] = nil
|
|
1050
1090
|
end))
|
|
1051
|
-
hook(hooks.added(world, components.
|
|
1091
|
+
hook(hooks.added(world, components.global, function(entity, _, value)
|
|
1052
1092
|
if value > 245 then
|
|
1053
1093
|
utils.logerror "global id size exceeded, max is 245"
|
|
1054
1094
|
end
|
|
1055
1095
|
server.global_ids[entity] = value
|
|
1056
1096
|
end))
|
|
1057
1097
|
|
|
1058
|
-
hook(hooks.changed(world, components.
|
|
1098
|
+
hook(hooks.changed(world, components.global, function(entity, _, value)
|
|
1059
1099
|
if value > 245 then
|
|
1060
1100
|
utils.logerror "global id size exceeded, max is 245"
|
|
1061
1101
|
end
|
|
1062
1102
|
server.global_ids[entity] = value
|
|
1063
1103
|
end))
|
|
1064
|
-
hook(hooks.removed(world, components.
|
|
1104
|
+
hook(hooks.removed(world, components.global, function(entity)
|
|
1065
1105
|
server.global_ids[entity] = nil
|
|
1066
1106
|
end))
|
|
1067
1107
|
|
|
@@ -1116,6 +1156,8 @@ server.destroy = destroy
|
|
|
1116
1156
|
server.get_full = get_full
|
|
1117
1157
|
server.collect_updates = collect_updates
|
|
1118
1158
|
server.collect_unreliable = collect_unreliable
|
|
1159
|
+
server.encode_component = encode_component
|
|
1160
|
+
server.decode_component = decode_component
|
|
1119
1161
|
server.mark_player_ready = mark_player_ready
|
|
1120
1162
|
server.is_player_ready = is_player_ready
|
|
1121
1163
|
|
package/out/replecs/utils.luau
CHANGED
|
@@ -678,7 +678,7 @@ function utils.logwarn(...: string)
|
|
|
678
678
|
end
|
|
679
679
|
|
|
680
680
|
function utils.logcomponent(world: World, component: Entity)
|
|
681
|
-
return ` name: {world:get(component, ECS_NAME) or "(no name)"} id: {component}`
|
|
681
|
+
return ` name: {world:get(component, ECS_NAME) or "(no name)"}, id: {component}`
|
|
682
682
|
end
|
|
683
683
|
|
|
684
684
|
return utils
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rbxts/replecs",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-alpha8",
|
|
4
4
|
"description": "Replication library for Jecs",
|
|
5
5
|
"main": "out/init.lua",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"typescript": "=5.5.3",
|
|
41
41
|
"prettier": "^3.2.5"
|
|
42
42
|
},
|
|
43
|
-
"
|
|
44
|
-
"@rbxts/jecs": "^0.9.0-rc.
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"@rbxts/jecs": "^0.9.0-rc.9"
|
|
45
45
|
}
|
|
46
46
|
}
|