@benev/archimedes 0.1.0-1
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/LICENSE +23 -0
- package/README.md +19 -0
- package/package.json +44 -0
- package/s/_archive/GLOSSARY.md +31 -0
- package/s/_archive/README.md +209 -0
- package/s/_archive/core/authority.ts +62 -0
- package/s/_archive/core/contact/codecs.i.ts +3 -0
- package/s/_archive/core/contact/codecs.ts +14 -0
- package/s/_archive/core/contact/contact.test.ts +32 -0
- package/s/_archive/core/contact/contact.ts +63 -0
- package/s/_archive/core/contact/types.ts +24 -0
- package/s/_archive/core/contact/wiring.i.ts +3 -0
- package/s/_archive/core/contact/wiring.ts +212 -0
- package/s/_archive/core/fiber.ts +94 -0
- package/s/_archive/core/liaison.ts +73 -0
- package/s/_archive/core/parcels/inbox.ts +83 -0
- package/s/_archive/core/parcels/parceller.ts +20 -0
- package/s/_archive/core/parcels/parcels.test.ts +136 -0
- package/s/_archive/core/parcels/types.ts +5 -0
- package/s/_archive/core/parcels/utils/nanny.ts +23 -0
- package/s/_archive/core/simulator.ts +11 -0
- package/s/_archive/core/speculator.ts +63 -0
- package/s/_archive/core/types.ts +32 -0
- package/s/_archive/core/utils/handle-telegrams.ts +34 -0
- package/s/_archive/core/utils/is-input-dispatch.ts +10 -0
- package/s/_archive/core/utils/make-telegram-for-inputs.ts +12 -0
- package/s/_archive/core/utils/on-channel-message.ts +11 -0
- package/s/_archive/eureka/eureka.ts +20 -0
- package/s/_archive/eureka/index.ts +0 -0
- package/s/_archive/eureka/integration/context.ts +9 -0
- package/s/_archive/eureka/integration/simulator.ts +75 -0
- package/s/_archive/eureka/integration/types.ts +12 -0
- package/s/_archive/eureka/integration/utils/inputs.ts +24 -0
- package/s/_archive/eureka/integration/utils/relevance.ts +10 -0
- package/s/_archive/eureka/parts/entity.ts +37 -0
- package/s/_archive/eureka/parts/system.ts +38 -0
- package/s/_archive/eureka/parts/types.ts +21 -0
- package/s/_archive/eureka/parts/world.ts +140 -0
- package/s/_archive/eureka/testing/eureka.test.ts +27 -0
- package/s/_archive/eureka/testing/integration.test.ts +40 -0
- package/s/_archive/eureka/testing/situations/health.ts +43 -0
- package/s/_archive/eureka/testing/situations/integration.ts +45 -0
- package/s/_archive/index.ts +6 -0
- package/s/_archive/session/client.ts +56 -0
- package/s/_archive/session/host.ts +71 -0
- package/s/_archive/session/meta/meta-client.ts +5 -0
- package/s/_archive/session/meta/meta-host.ts +18 -0
- package/s/_archive/session/meta/types.ts +15 -0
- package/s/_archive/session/parts/client-on.ts +8 -0
- package/s/_archive/session/parts/fiber-rpc.ts +28 -0
- package/s/_archive/session/parts/host-on.ts +9 -0
- package/s/_archive/session/parts/hub.ts +40 -0
- package/s/_archive/session/parts/netfibers.ts +14 -0
- package/s/_archive/session/parts/seat.ts +35 -0
- package/s/_archive/session/parts/spoke.ts +10 -0
- package/s/_archive/sugar/easy-host.ts +68 -0
- package/s/_archive/tests.test.ts +17 -0
- package/s/_archive/tools/averager.ts +29 -0
- package/s/_archive/tools/bucket.ts +17 -0
- package/s/_archive/tools/chronicle.ts +32 -0
- package/s/_archive/tools/disposers.ts +5 -0
- package/s/_archive/tools/id-counter.ts +13 -0
- package/s/_archive/tools/loop.ts +12 -0
- package/s/_archive/tools/pub.ts +22 -0
- package/s/_archive/tools/ticker.ts +22 -0
- package/s/_archive/tools/u8.ts +7 -0
- package/s/_archive/transports/sparrow/fibers.ts +19 -0
- package/s/_archive/transports/sparrow/start-sparrow-host.ts +26 -0
- package/s/_archive/transports/sparrow/utils/netfibers-from-cable.ts +10 -0
- package/s/ecs/index.ts +7 -0
- package/s/ecs/parts/changers.ts +16 -0
- package/s/ecs/parts/make-id.ts +7 -0
- package/s/ecs/parts/world.ts +68 -0
- package/s/ecs/test/setup-example-world.ts +42 -0
- package/s/ecs/test/setup-lifecycle-counts.ts +17 -0
- package/s/ecs/test.ts +99 -0
- package/s/ecs/types.ts +19 -0
- package/s/ecs/utils/is-match.ts +7 -0
- package/s/ecs/utils/optimizer.ts +38 -0
- package/s/index.ts +3 -0
- package/s/net/test.ts +9 -0
- package/s/net/types.ts +1 -0
- package/s/sim/test.ts +9 -0
- package/s/sim/types.ts +1 -0
- package/s/test.ts +8 -0
- package/x/ecs/index.d.ts +4 -0
- package/x/ecs/index.js +5 -0
- package/x/ecs/index.js.map +1 -0
- package/x/ecs/parts/changers.d.ts +4 -0
- package/x/ecs/parts/changers.js +11 -0
- package/x/ecs/parts/changers.js.map +1 -0
- package/x/ecs/parts/make-id.d.ts +1 -0
- package/x/ecs/parts/make-id.js +5 -0
- package/x/ecs/parts/make-id.js.map +1 -0
- package/x/ecs/parts/world.d.ts +11 -0
- package/x/ecs/parts/world.js +59 -0
- package/x/ecs/parts/world.js.map +1 -0
- package/x/ecs/test/setup-example-world.d.ts +10 -0
- package/x/ecs/test/setup-example-world.js +31 -0
- package/x/ecs/test/setup-example-world.js.map +1 -0
- package/x/ecs/test/setup-lifecycle-counts.d.ts +6 -0
- package/x/ecs/test/setup-lifecycle-counts.js +15 -0
- package/x/ecs/test/setup-lifecycle-counts.js.map +1 -0
- package/x/ecs/test.d.ts +13 -0
- package/x/ecs/test.js +85 -0
- package/x/ecs/test.js.map +1 -0
- package/x/ecs/types.d.ts +12 -0
- package/x/ecs/types.js +2 -0
- package/x/ecs/types.js.map +1 -0
- package/x/ecs/utils/is-match.d.ts +2 -0
- package/x/ecs/utils/is-match.js +4 -0
- package/x/ecs/utils/is-match.js.map +1 -0
- package/x/ecs/utils/optimizer.d.ts +9 -0
- package/x/ecs/utils/optimizer.js +37 -0
- package/x/ecs/utils/optimizer.js.map +1 -0
- package/x/index.d.ts +1 -0
- package/x/index.js +2 -0
- package/x/index.js.map +1 -0
- package/x/net/test.d.ts +4 -0
- package/x/net/test.js +7 -0
- package/x/net/test.js.map +1 -0
- package/x/net/types.d.ts +1 -0
- package/x/net/types.js +2 -0
- package/x/net/types.js.map +1 -0
- package/x/sim/test.d.ts +4 -0
- package/x/sim/test.js +7 -0
- package/x/sim/test.js.map +1 -0
- package/x/sim/types.d.ts +1 -0
- package/x/sim/types.js +2 -0
- package/x/sim/types.js.map +1 -0
- package/x/test.d.ts +1 -0
- package/x/test.js +6 -0
- package/x/test.js.map +1 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
|
|
2
|
+
import {setupEureka} from "../../eureka.js"
|
|
3
|
+
import {EurekaContext} from "../../integration/context.js"
|
|
4
|
+
|
|
5
|
+
export class DemoContext extends EurekaContext {}
|
|
6
|
+
|
|
7
|
+
export type DemoComponents = {
|
|
8
|
+
health: number
|
|
9
|
+
bleeding: number
|
|
10
|
+
mana: number
|
|
11
|
+
manaRegen: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function setupEurekaDemo() {
|
|
15
|
+
const eureka = setupEureka<DemoContext, DemoComponents>()
|
|
16
|
+
const context = new DemoContext()
|
|
17
|
+
const world = eureka.world(context, [
|
|
18
|
+
|
|
19
|
+
eureka.system("mortality")
|
|
20
|
+
.select("health").andMaybe("bleeding")
|
|
21
|
+
.fn((entities, world) => {
|
|
22
|
+
for (const {id, components} of entities) {
|
|
23
|
+
|
|
24
|
+
// process bleeding
|
|
25
|
+
if (components.bleeding)
|
|
26
|
+
components.health -= components.bleeding
|
|
27
|
+
|
|
28
|
+
// process death
|
|
29
|
+
if (components.health <= 0)
|
|
30
|
+
world.delete(id)
|
|
31
|
+
}
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
eureka.system("wizardry")
|
|
35
|
+
.select("mana").andMaybe("manaRegen")
|
|
36
|
+
.fn((entities, _world) => {
|
|
37
|
+
for (const {components} of entities)
|
|
38
|
+
if (components.manaRegen)
|
|
39
|
+
components.mana += components.manaRegen
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
])
|
|
43
|
+
return {context, world}
|
|
44
|
+
}
|
|
45
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
import {Seat} from "./parts/seat.js"
|
|
3
|
+
import {Spoke} from "./parts/spoke.js"
|
|
4
|
+
import {MetaApi} from "./meta/types.js"
|
|
5
|
+
import {Liaison} from "../core/liaison.js"
|
|
6
|
+
import {FiberRpc} from "./parts/fiber-rpc.js"
|
|
7
|
+
import {Simulator} from "../core/simulator.js"
|
|
8
|
+
import {Speculator} from "../core/speculator.js"
|
|
9
|
+
import {makeMetaClientApi} from "./meta/meta-client.js"
|
|
10
|
+
import {InferSimulatorSchema, Telegram} from "../core/types.js"
|
|
11
|
+
|
|
12
|
+
export class SessionClient<xSimulator extends Simulator> {
|
|
13
|
+
static async make<xSimulator extends Simulator>(options: {
|
|
14
|
+
hz: number
|
|
15
|
+
spoke: Spoke
|
|
16
|
+
pastSimulator: xSimulator
|
|
17
|
+
futureSimulator: xSimulator
|
|
18
|
+
}) {
|
|
19
|
+
|
|
20
|
+
const meta = new FiberRpc<MetaApi["host"]>(
|
|
21
|
+
options.spoke.fibers.sub.meta,
|
|
22
|
+
makeMetaClientApi(),
|
|
23
|
+
).remote as MetaApi["host"]
|
|
24
|
+
|
|
25
|
+
const {hostAuthorId, clientAuthorId} = await meta.hello()
|
|
26
|
+
const liaison = new Liaison<Telegram<any>>(hostAuthorId, options.spoke.fibers.sub.primary)
|
|
27
|
+
const seat = new Seat(options.spoke, liaison)
|
|
28
|
+
|
|
29
|
+
const speculator = new Speculator<InferSimulatorSchema<xSimulator>>(
|
|
30
|
+
clientAuthorId,
|
|
31
|
+
liaison,
|
|
32
|
+
options.pastSimulator,
|
|
33
|
+
options.futureSimulator,
|
|
34
|
+
options.hz,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return new this<xSimulator>(
|
|
38
|
+
seat,
|
|
39
|
+
options.pastSimulator,
|
|
40
|
+
options.futureSimulator,
|
|
41
|
+
speculator,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
public seat: Seat,
|
|
47
|
+
public pastSimulator: xSimulator,
|
|
48
|
+
public futureSimulator: xSimulator,
|
|
49
|
+
public speculator: Speculator<InferSimulatorSchema<xSimulator>>,
|
|
50
|
+
) {}
|
|
51
|
+
|
|
52
|
+
disconnect() {
|
|
53
|
+
this.seat.disconnect()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
import {MapG} from "@e280/stz"
|
|
3
|
+
|
|
4
|
+
import {Hub} from "./parts/hub.js"
|
|
5
|
+
import {Seat} from "./parts/seat.js"
|
|
6
|
+
import {MetaApi} from "./meta/types.js"
|
|
7
|
+
import {HostOn} from "./parts/host-on.js"
|
|
8
|
+
import {Liaison} from "../core/liaison.js"
|
|
9
|
+
import {FiberRpc} from "./parts/fiber-rpc.js"
|
|
10
|
+
import {Authority} from "../core/authority.js"
|
|
11
|
+
import {Simulator} from "../core/simulator.js"
|
|
12
|
+
import {makeMetaHostApi} from "./meta/meta-host.js"
|
|
13
|
+
import {AuthorId, InferSimulatorSchema, Telegram} from "../core/types.js"
|
|
14
|
+
|
|
15
|
+
export class SessionHost<xSimulator extends Simulator> {
|
|
16
|
+
authority: Authority<InferSimulatorSchema<xSimulator>>
|
|
17
|
+
seats = new MapG<AuthorId, Seat>()
|
|
18
|
+
on = new HostOn()
|
|
19
|
+
|
|
20
|
+
#cleanup = () => {}
|
|
21
|
+
|
|
22
|
+
constructor(
|
|
23
|
+
public hub: Hub,
|
|
24
|
+
public simulator: xSimulator,
|
|
25
|
+
) {
|
|
26
|
+
|
|
27
|
+
const authority = new Authority(simulator)
|
|
28
|
+
this.authority = authority
|
|
29
|
+
|
|
30
|
+
this.#cleanup = hub.onSpoke(spoke => {
|
|
31
|
+
const authorId = authority.idCounter.next()
|
|
32
|
+
|
|
33
|
+
const liaison = new Liaison<Telegram<any>>(authorId, spoke.fibers.sub.primary)
|
|
34
|
+
authority.liaisons.add(liaison)
|
|
35
|
+
liaison.send(authority.getStateTelegram())
|
|
36
|
+
|
|
37
|
+
new FiberRpc<MetaApi["host"]>(
|
|
38
|
+
spoke.fibers.sub.meta,
|
|
39
|
+
makeMetaHostApi({authority, liaison}),
|
|
40
|
+
).remote as MetaApi["client"]
|
|
41
|
+
|
|
42
|
+
const seat = new Seat(spoke, liaison)
|
|
43
|
+
this.seats.set(authorId, seat)
|
|
44
|
+
this.on.seated.pub(seat)
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
authority.liaisons.delete(liaison)
|
|
48
|
+
this.#unseat(authorId)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#unseat(authorId: AuthorId) {
|
|
54
|
+
const seat = this.seats.get(authorId)
|
|
55
|
+
if (!seat) return undefined
|
|
56
|
+
seat.disconnect()
|
|
57
|
+
this.seats.delete(authorId)
|
|
58
|
+
this.on.unseated.pub(seat)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
disconnectAll() {
|
|
62
|
+
for (const authorId of this.seats.keys())
|
|
63
|
+
this.#unseat(authorId)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
dispose() {
|
|
67
|
+
this.#cleanup()
|
|
68
|
+
this.disconnectAll()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
import {asFns, asMessengerRpc} from "@e280/renraku"
|
|
3
|
+
import {Liaison} from "../../core/liaison.js"
|
|
4
|
+
import {Authority} from "../../core/authority.js"
|
|
5
|
+
|
|
6
|
+
export const makeMetaHostApi = (options: {
|
|
7
|
+
authority: Authority<any>,
|
|
8
|
+
liaison: Liaison<any>
|
|
9
|
+
}) => asMessengerRpc(async _meta => asFns({
|
|
10
|
+
|
|
11
|
+
async hello() {
|
|
12
|
+
return {
|
|
13
|
+
hostAuthorId: options.authority.authorId,
|
|
14
|
+
clientAuthorId: options.liaison.authorId,
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
}))
|
|
18
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import {Fns} from "@e280/renraku"
|
|
3
|
+
import type {makeMetaHostApi} from "./meta-host.js"
|
|
4
|
+
import type {makeMetaClientApi} from "./meta-client.js"
|
|
5
|
+
|
|
6
|
+
export type CustomApi = {
|
|
7
|
+
host: Fns
|
|
8
|
+
client: Fns
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type MetaApi = {
|
|
12
|
+
host: Awaited<ReturnType<ReturnType<typeof makeMetaHostApi>>>
|
|
13
|
+
client: Awaited<ReturnType<ReturnType<typeof makeMetaClientApi>>>
|
|
14
|
+
}
|
|
15
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
import {Fiber} from "../../core/fiber.js"
|
|
3
|
+
import {Conduit, Fns, JsonRpc, Messenger, MessengerRpc, Remote} from "@e280/renraku"
|
|
4
|
+
|
|
5
|
+
export class FiberConduit extends Conduit {
|
|
6
|
+
constructor(public fiber: Fiber<JsonRpc.Bidirectional>) {
|
|
7
|
+
super()
|
|
8
|
+
this.sendRequest.sub(m => fiber.reliable.send(m))
|
|
9
|
+
this.sendResponse.sub(m => fiber.reliable.send(m))
|
|
10
|
+
fiber.reliable.recv.on(m => this.recv.pub(m, {origin: ""}))
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class FiberRpc<RemoteFns extends Fns> {
|
|
15
|
+
remote: Remote<RemoteFns>
|
|
16
|
+
dispose: () => void
|
|
17
|
+
|
|
18
|
+
constructor(public fiber: Fiber<JsonRpc.Bidirectional>, rpc: MessengerRpc<any, RemoteFns>) {
|
|
19
|
+
const messenger = new Messenger<any, RemoteFns>({
|
|
20
|
+
rpc,
|
|
21
|
+
conduit: new FiberConduit(fiber),
|
|
22
|
+
timeout: 60_000,
|
|
23
|
+
})
|
|
24
|
+
this.remote = messenger.remote
|
|
25
|
+
this.dispose = () => {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import {Spoke} from "./spoke.js"
|
|
3
|
+
|
|
4
|
+
export type SpokeListener = (spoke: Spoke) => () => void
|
|
5
|
+
|
|
6
|
+
export class Hub {
|
|
7
|
+
spokes = new Set<Spoke>()
|
|
8
|
+
#listeners = new Set<SpokeListener>()
|
|
9
|
+
|
|
10
|
+
/** an incoming spoke has appeared, dispatch all onSpoke listeners */
|
|
11
|
+
addSpoke(spoke: Spoke) {
|
|
12
|
+
this.spokes.add(spoke)
|
|
13
|
+
|
|
14
|
+
const disconnectedFns: (() => void)[] = []
|
|
15
|
+
|
|
16
|
+
for (const fn of this.#listeners) {
|
|
17
|
+
|
|
18
|
+
// call every spoke listener
|
|
19
|
+
const spokeDisconnected = fn(spoke)
|
|
20
|
+
|
|
21
|
+
// accumulate each listener's disconnected callback
|
|
22
|
+
disconnectedFns.push(spokeDisconnected)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// when this spoke is disconnected, call every listener's disconnected callback
|
|
26
|
+
return () => {
|
|
27
|
+
this.spokes.delete(spoke)
|
|
28
|
+
disconnectedFns.forEach(d => d())
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** add a spoke listener to respond to incoming spokes */
|
|
33
|
+
onSpoke(fn: SpokeListener) {
|
|
34
|
+
this.#listeners.add(fn)
|
|
35
|
+
|
|
36
|
+
// callback to remove this listener
|
|
37
|
+
return () => void this.#listeners.delete(fn)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
import {JsonRpc} from "@e280/renraku"
|
|
3
|
+
import {Fiber} from "../../core/fiber.js"
|
|
4
|
+
|
|
5
|
+
export class Netfibers {
|
|
6
|
+
sub = {
|
|
7
|
+
primary: new Fiber(),
|
|
8
|
+
userland: new Fiber(),
|
|
9
|
+
meta: new Fiber<JsonRpc.Bidirectional>(),
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
megafiber = Fiber.multiplex(this.sub)
|
|
13
|
+
}
|
|
14
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
import {asMessengerRpc, Fns} from "@e280/renraku"
|
|
3
|
+
|
|
4
|
+
import {Spoke} from "./spoke.js"
|
|
5
|
+
import {FiberRpc} from "./fiber-rpc.js"
|
|
6
|
+
import {Liaison} from "../../core/liaison.js"
|
|
7
|
+
|
|
8
|
+
export class Seat {
|
|
9
|
+
constructor(
|
|
10
|
+
private spoke: Spoke,
|
|
11
|
+
private liaison: Liaison<any>,
|
|
12
|
+
) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get authorId() {
|
|
16
|
+
return this.liaison.authorId
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get rtt() {
|
|
20
|
+
return this.liaison.pingponger.averageRtt
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get userland() {
|
|
24
|
+
return this.spoke.fibers.sub.userland
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
disconnect() {
|
|
28
|
+
this.spoke.disconnect()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
userlandApi<RemoteFns extends Fns>(seat: Seat, hostFns: RemoteFns) {
|
|
32
|
+
return new FiberRpc<RemoteFns>(seat.userland, asMessengerRpc(async _meta => ({fns: hostFns}))).remote
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
import {sub} from "@e280/stz"
|
|
3
|
+
import {Ticker} from "../tools/ticker.js"
|
|
4
|
+
import {Hub} from "../session/parts/hub.js"
|
|
5
|
+
import {Fiber} from "../core/fiber.js"
|
|
6
|
+
import {Simulator} from "../core/simulator.js"
|
|
7
|
+
import {SessionHost} from "../session/host.js"
|
|
8
|
+
import {Spoke} from "../session/parts/spoke.js"
|
|
9
|
+
import {SessionClient} from "../session/client.js"
|
|
10
|
+
import {Netfibers} from "../session/parts/netfibers.js"
|
|
11
|
+
import {HubSparrowOptions, startSparrowHost} from "../transports/sparrow/start-sparrow-host.js"
|
|
12
|
+
|
|
13
|
+
export type EasyHostOptions<xSimulator extends Simulator> = {
|
|
14
|
+
hz: number
|
|
15
|
+
makeSimulator: () => xSimulator,
|
|
16
|
+
closed?: () => void
|
|
17
|
+
sparrow?: HubSparrowOptions
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class EasyHost<xSimulator extends Simulator> {
|
|
21
|
+
hub = new Hub()
|
|
22
|
+
onClosed = sub()
|
|
23
|
+
|
|
24
|
+
session: SessionHost<xSimulator>
|
|
25
|
+
ticker: Ticker
|
|
26
|
+
|
|
27
|
+
constructor(public options: EasyHostOptions<xSimulator>) {
|
|
28
|
+
this.session = new SessionHost<xSimulator>(this.hub, options.makeSimulator())
|
|
29
|
+
this.ticker = this.session.authority.makeTicker(options.hz)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async startHostingViaSparrow(sparrow?: HubSparrowOptions) {
|
|
33
|
+
return startSparrowHost({
|
|
34
|
+
sparrow,
|
|
35
|
+
hub: this.hub,
|
|
36
|
+
closed: () => this.onClosed.pub(),
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async localClient() {
|
|
41
|
+
// establish fibers on both sides
|
|
42
|
+
const fibers = {
|
|
43
|
+
host: new Netfibers(),
|
|
44
|
+
client: new Netfibers(),
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// entangle the host and client fibers
|
|
48
|
+
Fiber.entangle(fibers.host.megafiber, fibers.client.megafiber)
|
|
49
|
+
|
|
50
|
+
// create spokes
|
|
51
|
+
const spokes = {
|
|
52
|
+
host: new Spoke(fibers.host, () => {}),
|
|
53
|
+
client: new Spoke(fibers.client, () => {}),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// add the host-side spoke the host hub
|
|
57
|
+
this.hub.addSpoke(spokes.host)
|
|
58
|
+
|
|
59
|
+
// create a session client using the client spoke
|
|
60
|
+
return SessionClient.make<xSimulator>({
|
|
61
|
+
hz: this.options.hz,
|
|
62
|
+
spoke: spokes.client,
|
|
63
|
+
pastSimulator: this.options.makeSimulator(),
|
|
64
|
+
futureSimulator: this.options.makeSimulator(),
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
import {Science} from "@e280/science"
|
|
3
|
+
import eureka from "./eureka/testing/eureka.test.js"
|
|
4
|
+
import parcels from "./core/parcels/parcels.test.js"
|
|
5
|
+
import contact from "./core/contact/contact.test.js"
|
|
6
|
+
import integration from "./eureka/testing/integration.test.js"
|
|
7
|
+
|
|
8
|
+
import ecs from "../alpha/ecs/test.js"
|
|
9
|
+
import sim from "../alpha/sim/test.js"
|
|
10
|
+
|
|
11
|
+
await Science.run({
|
|
12
|
+
alpha: {ecs, sim},
|
|
13
|
+
eureka,
|
|
14
|
+
integration,
|
|
15
|
+
archimedes: {parcels, contact},
|
|
16
|
+
})
|
|
17
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
|
|
2
|
+
export class Averager {
|
|
3
|
+
#memory: number[] = []
|
|
4
|
+
#average: number = 0
|
|
5
|
+
#latest: number = 0
|
|
6
|
+
|
|
7
|
+
constructor(public size: number) {}
|
|
8
|
+
|
|
9
|
+
get average() { return this.#average }
|
|
10
|
+
get latest() { return this.#latest }
|
|
11
|
+
|
|
12
|
+
add(n: number) {
|
|
13
|
+
this.#latest = n
|
|
14
|
+
const memory = this.#memory
|
|
15
|
+
|
|
16
|
+
memory.push(n)
|
|
17
|
+
|
|
18
|
+
while (memory.length > this.size)
|
|
19
|
+
memory.shift()
|
|
20
|
+
|
|
21
|
+
if (memory.length > 0) {
|
|
22
|
+
const sum = memory.reduce((p, c) => p + c, 0)
|
|
23
|
+
this.#average = sum / memory.length
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return this.#average
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
export class Bucket<T> {
|
|
3
|
+
#items: T[] = []
|
|
4
|
+
|
|
5
|
+
/** put an item in the bucket */
|
|
6
|
+
give(item: T) {
|
|
7
|
+
this.#items.push(item)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** extract all items out of the bucket */
|
|
11
|
+
take() {
|
|
12
|
+
const items = this.#items
|
|
13
|
+
this.#items = []
|
|
14
|
+
return items
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
export type Chron<X> = {item: X, time: number}
|
|
3
|
+
|
|
4
|
+
export class Chronicle<X> {
|
|
5
|
+
#chrons: Chron<X>[] = []
|
|
6
|
+
|
|
7
|
+
constructor(public limit = 100) {}
|
|
8
|
+
|
|
9
|
+
add(item: X, time: number) {
|
|
10
|
+
this.#chrons.push({item, time})
|
|
11
|
+
this.#limit()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
at(time: number) {
|
|
15
|
+
return this.#chrons
|
|
16
|
+
.filter(chron => chron.time === time)
|
|
17
|
+
.map(chron => chron.item)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
since(time: number) {
|
|
21
|
+
return this.#chrons
|
|
22
|
+
.filter(chron => chron.time > time)
|
|
23
|
+
.map(chron => chron.item)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#limit() {
|
|
27
|
+
const {limit} = this
|
|
28
|
+
while (this.#chrons.length > limit)
|
|
29
|
+
this.#chrons.shift()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
export function* loop(n: number) {
|
|
3
|
+
for (let i = 0; i < n; i++)
|
|
4
|
+
yield i
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function* loop2d([columns, rows]: [number, number]) {
|
|
8
|
+
for (let y = 0; y < rows; y++)
|
|
9
|
+
for (let x = 0; x < columns; x++)
|
|
10
|
+
yield [x, y] as [number, number]
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
export type Fn<P extends any[]> = (...p: P) => void
|
|
3
|
+
|
|
4
|
+
export function pub<P extends any[]>(fn?: Fn<P>) {
|
|
5
|
+
const fns = new Set<Fn<P>>()
|
|
6
|
+
|
|
7
|
+
if (fn)
|
|
8
|
+
fns.add(fn)
|
|
9
|
+
|
|
10
|
+
function publish(...p: P) {
|
|
11
|
+
for (const fn of fns)
|
|
12
|
+
fn(...p)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
publish.on = (fn: Fn<P>) => {
|
|
16
|
+
fns.add(fn)
|
|
17
|
+
return () => { fns.delete(fn) }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return publish
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
export class Ticker {
|
|
3
|
+
#active = false
|
|
4
|
+
constructor(public hz: number, private fn: () => void) {}
|
|
5
|
+
|
|
6
|
+
start() {
|
|
7
|
+
if (!this.#active) {
|
|
8
|
+
this.#active = true
|
|
9
|
+
const tick = () => {
|
|
10
|
+
if (!this.#active) return undefined
|
|
11
|
+
this.fn()
|
|
12
|
+
setTimeout(tick, 1000 / this.hz)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return () => this.stop()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
stop() {
|
|
19
|
+
this.#active = false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
import {Sparrow} from "sparrow-rtc"
|
|
3
|
+
import {netfibersFromCable} from "./utils/netfibers-from-cable.js"
|
|
4
|
+
|
|
5
|
+
export async function sparrowFibers(options: {
|
|
6
|
+
invite: string,
|
|
7
|
+
disconnected: () => void,
|
|
8
|
+
}) {
|
|
9
|
+
|
|
10
|
+
const sparrow = await Sparrow.join({
|
|
11
|
+
invite: options.invite,
|
|
12
|
+
disconnected: options.disconnected,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const fibers = netfibersFromCable(sparrow.connection.cable)
|
|
16
|
+
|
|
17
|
+
return {sparrow, fibers}
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
import {ConnectOptions, Sparrow, StdCable} from "sparrow-rtc"
|
|
3
|
+
import {Hub} from "../../session/parts/hub.js"
|
|
4
|
+
import {Spoke} from "../../session/parts/spoke.js"
|
|
5
|
+
import {netfibersFromCable} from "./utils/netfibers-from-cable.js"
|
|
6
|
+
|
|
7
|
+
export type HubSparrowOptions = Omit<ConnectOptions<StdCable>, "closed" | "welcome" | "cableConfig">
|
|
8
|
+
|
|
9
|
+
export async function startSparrowHost(options: {
|
|
10
|
+
hub: Hub
|
|
11
|
+
closed: () => void
|
|
12
|
+
sparrow?: HubSparrowOptions
|
|
13
|
+
}) {
|
|
14
|
+
|
|
15
|
+
return Sparrow.host({
|
|
16
|
+
...(options.sparrow ?? {}),
|
|
17
|
+
closed: options.closed,
|
|
18
|
+
welcome: _prospect => connection => {
|
|
19
|
+
const fibers = netfibersFromCable(connection.cable)
|
|
20
|
+
const disconnect = () => connection.disconnect()
|
|
21
|
+
const spoke = new Spoke(fibers, disconnect)
|
|
22
|
+
return options.hub.addSpoke(spoke)
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|