@benev/tact 0.4.0-8 → 0.4.0-9

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 CHANGED
@@ -72,13 +72,14 @@ i care about the little things. all input types are boiled down to simple number
72
72
  - 💡 *you can call `tact.dispose()` to release resources.*
73
73
  - 🍏 **encoding, decoding, and your game logic.**
74
74
  ```ts
75
- import {Collector} from "@benev/tact"
75
+ import {Players} from "@benev/tact"
76
76
 
77
- const collector = new Collector(bindings)
77
+ const players = new Players(bindings)
78
78
 
79
79
  setInterval(() => {
80
80
  const buffer = tact.encodeAll()
81
- for (const {playerId, inputs} of collector.decodeAll(buffer)) {
81
+ players.decodeAll(buffer)
82
+ for (const [playerId, inputs] of players) {
82
83
 
83
84
  // your game logic can read these handy input values.
84
85
  inputs.jump.value // button-like values are numbers 0 to 1.
@@ -90,8 +91,8 @@ i care about the little things. all input types are boiled down to simple number
90
91
  }
91
92
  }, 16.67)
92
93
  ```
93
- - 💡 *you could stream the buffers across the network to a remote collector, maybe with [renraku](https://github.com/e280/renraku).*
94
- - 💡 *you could have the tact deck on the clientside, and the collector on the serverside (or a web worker!).*
94
+ - 💡 *maybe you'd like to stream the buffers over a network with [renraku](https://github.com/e280/renraku).*
95
+ - 💡 *maybe you'd like the tact deck on the clientside and the players on the serverside.*
95
96
  - 🍏 **put the `<tact-deck>` element in your html page.**
96
97
  ```html
97
98
  <tact-deck></tact-deck>
@@ -165,14 +166,14 @@ i care about the little things. all input types are boiled down to simple number
165
166
  ```
166
167
  ```ts
167
168
  asBindings({
168
- actions: {
169
- special: {
170
- castMagicSpell: ["*", ["mod", "shift", "alt"], "KeyR"],
169
+ special: {
170
+ magic: {
171
+ castSpell: ["*", ["mod", "shift", "alt"], "KeyR"],
171
172
  }
172
173
  }
173
174
  })
174
175
 
175
- inputs.actions.special.castMagicSpell.value // 1
176
+ inputs.special.magic.castSpell.value // 1
176
177
  ```
177
178
  - 🧬 **you can differentiate taps versus holds on the same key.**
178
179
  ```ts
@@ -191,21 +192,24 @@ i care about the little things. all input types are boiled down to simple number
191
192
  lookX: ["accumulate", "pointer.x"],
192
193
  })
193
194
 
194
- inputs.lookX.sum // use sum instead of value, to ensure you don't lose any pointer movement.
195
+ inputs.lookX.sum // use sum instead of value.
195
196
  ```
196
197
  - 🧬 **gamepads thumbstick inputs are best done with some math.**
197
198
  ```ts
198
199
  asBindings({
199
200
  forward: ["*",
200
201
 
201
- // user-customizable sensitivity, equivalent with pointer-pixels-per-second.
202
+ // user-customizable sensitivity,
203
+ // equivalent with pointer-pixels-per-second.
202
204
  1234,
203
205
 
204
- // delta time factor, to ensure sensitivity is framerate-independent.
206
+ // delta time factor,
207
+ // to ensure sensitivity is framerate-independent.
205
208
  ["dt"],
206
209
 
207
- // remapping the ranges because gamepads are naughty and often have jiggly deadzones.
208
- ["remap", -0.1, -0.9, 0, 1, "gamepad.axis.1"],
210
+ // remapping the ranges,
211
+ // because different gamepads represent the values differently.
212
+ ["remap", 0, -1, 0, 1, "gamepad.axis.1"],
209
213
  ],
210
214
  })
211
215
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benev/tact",
3
- "version": "0.4.0-8",
3
+ "version": "0.4.0-9",
4
4
  "description": "🎮 from keypress to couch co-op",
5
5
  "license": "MIT",
6
6
  "author": "Chase Moskal <chasemoskal@gmail.com>",
@@ -21,16 +21,16 @@
21
21
  "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +"
22
22
  },
23
23
  "peerDependencies": {
24
- "@e280/sly": "^0.4.9",
24
+ "@e280/sly": "^0.4.12",
25
25
  "@e280/strata": "^0.5.6",
26
26
  "lit": "^3.3.3"
27
27
  },
28
28
  "dependencies": {
29
29
  "@awasm/noble": "^0.1.4",
30
30
  "@benev/math": "0.4.0-9",
31
- "@e280/kv": "^0.2.4",
31
+ "@e280/kv": "^0.2.6",
32
32
  "@e280/shiny": "^0.1.1",
33
- "@e280/stz": "^0.4.14"
33
+ "@e280/stz": "^0.4.15"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@e280/hottie": "^0.1.7",
package/s/lib/index.ts CHANGED
@@ -19,7 +19,7 @@ export * from "./ui/deck/controls/types.js"
19
19
  export * from "./ui/deck/setup/deck.js"
20
20
  export * from "./ui/deck/setup/tact.js"
21
21
  export * from "./ui/deck/auto-gamepads.js"
22
- export * from "./ui/deck/collector.js"
22
+ export * from "./ui/deck/players.js"
23
23
  export * from "./ui/deck/hub.js"
24
24
  export * from "./ui/deck/types.js"
25
25
  export * from "./ui/views/customize/view.js"
@@ -0,0 +1,73 @@
1
+
2
+ import {RMap} from "@e280/strata"
3
+ import {guarantee} from "@e280/stz"
4
+
5
+ import {PlayerId} from "./types.js"
6
+ import {Bindings} from "../../input/types.js"
7
+ import {Decoder} from "../../input/decoder.js"
8
+ import {decodeParcels} from "../../input/data/parcel/decode.js"
9
+
10
+ export class Players<B extends Bindings> {
11
+ #bindings: B
12
+ #decoders = new RMap<PlayerId, Decoder<B>>()
13
+
14
+ constructor(bindings: B) {
15
+ this.#bindings = bindings
16
+ }
17
+
18
+ decodeAll(...buffers: ArrayBuffer[]) {
19
+ const added: PlayerId[] = []
20
+ const removed: PlayerId[] = []
21
+ const grouped = new Map<PlayerId, ArrayBuffer[]>()
22
+
23
+ for (const buffer of buffers)
24
+ for (const {playerId, intents} of decodeParcels(buffer))
25
+ guarantee(grouped, playerId, () => []).push(intents)
26
+
27
+ for (const [playerId, intents] of grouped) {
28
+ let decoder = this.#decoders.get(playerId)
29
+ if (!decoder) {
30
+ decoder = new Decoder(this.#bindings)
31
+ this.#decoders.set(playerId, decoder)
32
+ added.push(playerId)
33
+ }
34
+ decoder.decode(...intents)
35
+ }
36
+
37
+ for (const playerId of this.#decoders.keys()) {
38
+ if (!grouped.has(playerId)) {
39
+ this.#decoders.delete(playerId)
40
+ removed.push(playerId)
41
+ }
42
+ }
43
+
44
+ return {added, removed}
45
+ }
46
+
47
+ has(playerId: PlayerId) {
48
+ return this.#decoders.has(playerId)
49
+ }
50
+
51
+ get(playerId: PlayerId) {
52
+ return this.#decoders.get(playerId)?.inputs
53
+ }
54
+
55
+ keys() {
56
+ return this.#decoders.keys()
57
+ }
58
+
59
+ *values() {
60
+ for (const {inputs} of this.#decoders.values())
61
+ yield inputs
62
+ }
63
+
64
+ *entries() {
65
+ for (const [playerId, {inputs}] of this.#decoders)
66
+ yield [playerId, inputs] as const
67
+ }
68
+
69
+ [Symbol.iterator]() {
70
+ return this.entries()
71
+ }
72
+ }
73
+