@utsp/core 0.1.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.
@@ -0,0 +1,4732 @@
1
+ import { Vector2, AxisSource, ButtonSource, InputBindingLoadPacket, AxisBinding, ButtonBinding, UserRenderState } from '@utsp/types';
2
+ export { AxisBinding, AxisSource, ButtonBinding, ButtonSource, InputBindingLoadPacket, RenderState, RenderedCell, UserRenderState, Vector2 } from '@utsp/types';
3
+
4
+ /**
5
+ * Font system for UTSP Core
6
+ * Supports WebFont (CSS-based) and BitmapFont (pixel-based)
7
+ */
8
+ /**
9
+ * Font type enumeration
10
+ */
11
+ declare enum FontType {
12
+ Web = "web",
13
+ Bitmap = "bitmap"
14
+ }
15
+ /**
16
+ * WebFont configuration
17
+ * For CSS-based fonts rendered by the browser
18
+ */
19
+ interface WebFontConfig {
20
+ fontFamily: string;
21
+ fontSize: number;
22
+ offsetX?: number;
23
+ offsetY?: number;
24
+ charSpacing?: number;
25
+ lineHeight?: number;
26
+ fontWeight?: string;
27
+ fontStyle?: string;
28
+ }
29
+ /**
30
+ * WebFont class
31
+ * Represents a CSS-based font for browser rendering
32
+ */
33
+ declare class WebFont {
34
+ private fontId;
35
+ private config;
36
+ constructor(fontId: number, config: WebFontConfig);
37
+ /**
38
+ * Get the unique font ID
39
+ */
40
+ getFontId(): number;
41
+ /**
42
+ * Get the full configuration (copy)
43
+ */
44
+ getConfig(): WebFontConfig;
45
+ /**
46
+ * Get the font family
47
+ */
48
+ getFontFamily(): string;
49
+ /**
50
+ * Get the font size in pixels
51
+ */
52
+ getFontSize(): number;
53
+ /**
54
+ * Get the horizontal offset
55
+ */
56
+ getOffsetX(): number;
57
+ /**
58
+ * Get the vertical offset
59
+ */
60
+ getOffsetY(): number;
61
+ /**
62
+ * Get the character spacing
63
+ */
64
+ getCharSpacing(): number;
65
+ /**
66
+ * Get the line height multiplier
67
+ */
68
+ getLineHeight(): number;
69
+ /**
70
+ * Get the font weight
71
+ */
72
+ getFontWeight(): string;
73
+ /**
74
+ * Get the font style
75
+ */
76
+ getFontStyle(): string;
77
+ /**
78
+ * Generate CSS declaration for this font
79
+ * @returns CSS string (e.g., "font-family: Consolas; font-size: 16px;")
80
+ */
81
+ toCSS(): string;
82
+ }
83
+ /**
84
+ * BitmapFont configuration
85
+ * For pixel-based fonts with custom glyph data
86
+ */
87
+ interface BitmapFontConfig {
88
+ charWidth: number;
89
+ charHeight: number;
90
+ cellWidth?: number;
91
+ cellHeight?: number;
92
+ glyphs: Map<number, Uint8Array>;
93
+ }
94
+ /**
95
+ * BitmapFont class
96
+ * Represents a pixel-based font with custom glyph bitmaps
97
+ * Corresponds to LoadType 0x04 (Charset) in UTSP protocol
98
+ */
99
+ declare class BitmapFont {
100
+ private fontId;
101
+ private config;
102
+ constructor(fontId: number, config: BitmapFontConfig);
103
+ /**
104
+ * Get the unique font ID
105
+ */
106
+ getFontId(): number;
107
+ /**
108
+ * Get the full configuration (deep copy)
109
+ */
110
+ getConfig(): BitmapFontConfig;
111
+ /**
112
+ * Get the glyph width in pixels (actual bitmap size)
113
+ */
114
+ getCharWidth(): number;
115
+ /**
116
+ * Get the glyph height in pixels (actual bitmap size)
117
+ */
118
+ getCharHeight(): number;
119
+ /**
120
+ * Get the target cell width in pixels (rendering size)
121
+ * Defaults to glyph width if not specified
122
+ */
123
+ getCellWidth(): number;
124
+ /**
125
+ * Get the target cell height in pixels (rendering size)
126
+ * Defaults to glyph height if not specified
127
+ */
128
+ getCellHeight(): number;
129
+ /**
130
+ * Get the bitmap data for a specific character
131
+ * @param charCode Character code (0-255)
132
+ * @returns Bitmap data or undefined if not found
133
+ */
134
+ getGlyph(charCode: number): Uint8Array | undefined;
135
+ /**
136
+ * Check if a glyph exists for a character
137
+ * @param charCode Character code (0-255)
138
+ */
139
+ hasGlyph(charCode: number): boolean;
140
+ /**
141
+ * Get the number of glyphs in this font
142
+ */
143
+ getGlyphCount(): number;
144
+ /**
145
+ * Get all character codes that have glyphs
146
+ */
147
+ getCharCodes(): number[];
148
+ }
149
+
150
+ /**
151
+ * Network representation of a display
152
+ * Layers are NO LONGER under displays - they are at User level
153
+ */
154
+ interface NetworkDisplay {
155
+ /** Display ID (0-255) */
156
+ id: number;
157
+ /** Origin X position in virtual world (0-65535) */
158
+ originX: number;
159
+ /** Origin Y position in virtual world (0-65535) */
160
+ originY: number;
161
+ /** Display width in cells (1-256) */
162
+ sizeX: number;
163
+ /** Display height in cells (1-256) */
164
+ sizeY: number;
165
+ }
166
+
167
+ /**
168
+ * Base interface for all network orders
169
+ */
170
+ interface NetworkOrder {
171
+ type: number;
172
+ }
173
+ /**
174
+ * 0x01 - Char: Renders a single character at a specific position
175
+ */
176
+ interface CharOrder extends NetworkOrder {
177
+ type: 0x01;
178
+ posX: number;
179
+ posY: number;
180
+ charCode: number;
181
+ bgColorCode: number;
182
+ fgColorCode: number;
183
+ }
184
+ /**
185
+ * 0x02 - Text: Renders a string of characters with uniform colors
186
+ */
187
+ interface TextOrder extends NetworkOrder {
188
+ type: 0x02;
189
+ posX: number;
190
+ posY: number;
191
+ text: string;
192
+ bgColorCode: number;
193
+ fgColorCode: number;
194
+ }
195
+ /**
196
+ * 0x17 - TextMultiline: Renders multiple lines of text (\n for line breaks)
197
+ */
198
+ interface TextMultilineOrder extends NetworkOrder {
199
+ type: 0x17;
200
+ posX: number;
201
+ posY: number;
202
+ text: string;
203
+ bgColorCode: number;
204
+ fgColorCode: number;
205
+ }
206
+ /**
207
+ * 0x03 - SubFrame: Renders a rectangular region with uniform colors
208
+ */
209
+ interface SubFrameOrder extends NetworkOrder {
210
+ type: 0x03;
211
+ posX: number;
212
+ posY: number;
213
+ sizeX: number;
214
+ sizeY: number;
215
+ bgColorCode: number;
216
+ fgColorCode: number;
217
+ frame: number[];
218
+ }
219
+ /**
220
+ * 0x04 - SubFrameMultiColor: Rectangular region with per-cell colors
221
+ */
222
+ interface SubFrameMultiColorOrder extends NetworkOrder {
223
+ type: 0x04;
224
+ posX: number;
225
+ posY: number;
226
+ sizeX: number;
227
+ sizeY: number;
228
+ frame: Array<{
229
+ charCode: number;
230
+ bgColorCode: number;
231
+ fgColorCode: number;
232
+ }>;
233
+ }
234
+ /**
235
+ * 0x05 - FullFrame: Renders entire screen with uniform colors
236
+ */
237
+ interface FullFrameOrder extends NetworkOrder {
238
+ type: 0x05;
239
+ bgColorCode: number;
240
+ fgColorCode: number;
241
+ frame: number[];
242
+ }
243
+ /**
244
+ * 0x06 - FullFrameMultiColor: Entire screen with per-cell colors
245
+ */
246
+ interface FullFrameMultiColorOrder extends NetworkOrder {
247
+ type: 0x06;
248
+ frame: Array<{
249
+ charCode: number;
250
+ bgColorCode: number;
251
+ fgColorCode: number;
252
+ }>;
253
+ }
254
+ /**
255
+ * 0x07 - Sprite: Renders a preloaded unicolor sprite
256
+ */
257
+ interface SpriteOrder extends NetworkOrder {
258
+ type: 0x07;
259
+ posX: number;
260
+ posY: number;
261
+ spriteIndex: number;
262
+ bgColorCode: number;
263
+ fgColorCode: number;
264
+ }
265
+ /**
266
+ * 0x08 - SpriteMultiColor: Renders a preloaded multicolor sprite
267
+ */
268
+ interface SpriteMultiColorOrder extends NetworkOrder {
269
+ type: 0x08;
270
+ posX: number;
271
+ posY: number;
272
+ spriteIndex: number;
273
+ }
274
+ /**
275
+ * 0x09 - ColorMap: Applies colors to a region without changing characters
276
+ */
277
+ interface ColorMapOrder extends NetworkOrder {
278
+ type: 0x09;
279
+ posX: number;
280
+ posY: number;
281
+ sizeX: number;
282
+ sizeY: number;
283
+ colorData: Array<{
284
+ bgColorCode: number;
285
+ fgColorCode: number;
286
+ }>;
287
+ }
288
+ /**
289
+ * 0x0A - Shape: Renders geometric shapes
290
+ */
291
+ interface ShapeOrder extends NetworkOrder {
292
+ type: 0x0a;
293
+ shapeType: ShapeType;
294
+ shapeData: ShapeData;
295
+ }
296
+ declare enum ShapeType {
297
+ Rectangle = 1,
298
+ Circle = 2,
299
+ Line = 3,
300
+ Ellipse = 4,
301
+ Triangle = 5
302
+ }
303
+ type ShapeData = RectangleShape | CircleShape | LineShape | EllipseShape | TriangleShape;
304
+ interface RectangleShape {
305
+ posX: number;
306
+ posY: number;
307
+ width: number;
308
+ height: number;
309
+ filled: boolean;
310
+ charCode: number;
311
+ bgColorCode: number;
312
+ fgColorCode: number;
313
+ }
314
+ interface CircleShape {
315
+ centerX: number;
316
+ centerY: number;
317
+ radius: number;
318
+ filled: boolean;
319
+ charCode: number;
320
+ bgColorCode: number;
321
+ fgColorCode: number;
322
+ }
323
+ interface LineShape {
324
+ x1: number;
325
+ y1: number;
326
+ x2: number;
327
+ y2: number;
328
+ charCode: number;
329
+ bgColorCode: number;
330
+ fgColorCode: number;
331
+ }
332
+ interface EllipseShape {
333
+ centerX: number;
334
+ centerY: number;
335
+ radiusX: number;
336
+ radiusY: number;
337
+ filled: boolean;
338
+ charCode: number;
339
+ bgColorCode: number;
340
+ fgColorCode: number;
341
+ }
342
+ interface TriangleShape {
343
+ x1: number;
344
+ y1: number;
345
+ x2: number;
346
+ y2: number;
347
+ x3: number;
348
+ y3: number;
349
+ filled: boolean;
350
+ charCode: number;
351
+ bgColorCode: number;
352
+ fgColorCode: number;
353
+ }
354
+ /**
355
+ * 0x0B - DotCloud: Same character at multiple positions (up to 65535)
356
+ */
357
+ interface DotCloudOrder extends NetworkOrder {
358
+ type: 0x0b;
359
+ charCode: number;
360
+ bgColorCode: number;
361
+ fgColorCode: number;
362
+ positions: Array<{
363
+ posX: number;
364
+ posY: number;
365
+ }>;
366
+ }
367
+ /**
368
+ * 0x0C - DotCloudMultiColor: Different characters at multiple positions
369
+ */
370
+ interface DotCloudMultiColorOrder extends NetworkOrder {
371
+ type: 0x0c;
372
+ dots: Array<{
373
+ charCode: number;
374
+ bgColorCode: number;
375
+ fgColorCode: number;
376
+ posX: number;
377
+ posY: number;
378
+ }>;
379
+ }
380
+ /**
381
+ * 0x0D - SpriteCloud: Same unicolor sprite at multiple positions
382
+ */
383
+ interface SpriteCloudOrder extends NetworkOrder {
384
+ type: 0x0d;
385
+ spriteIndex: number;
386
+ bgColorCode: number;
387
+ fgColorCode: number;
388
+ positions: Array<{
389
+ posX: number;
390
+ posY: number;
391
+ }>;
392
+ }
393
+ /**
394
+ * 0x0E - SpriteCloudMultiColor: Same multicolor sprite at multiple positions
395
+ */
396
+ interface SpriteCloudMultiColorOrder extends NetworkOrder {
397
+ type: 0x0e;
398
+ spriteIndex: number;
399
+ positions: Array<{
400
+ posX: number;
401
+ posY: number;
402
+ }>;
403
+ }
404
+ /**
405
+ * 0x0F - SpriteCloudVaried: Different unicolor sprites at multiple positions
406
+ */
407
+ interface SpriteCloudVariedOrder extends NetworkOrder {
408
+ type: 0x0f;
409
+ sprites: Array<{
410
+ spriteIndex: number;
411
+ bgColorCode: number;
412
+ fgColorCode: number;
413
+ posX: number;
414
+ posY: number;
415
+ }>;
416
+ }
417
+ /**
418
+ * 0x10 - SpriteCloudVariedMultiColor: Different multicolor sprites at multiple positions
419
+ */
420
+ interface SpriteCloudVariedMultiColorOrder extends NetworkOrder {
421
+ type: 0x10;
422
+ sprites: Array<{
423
+ spriteIndex: number;
424
+ posX: number;
425
+ posY: number;
426
+ }>;
427
+ }
428
+ /**
429
+ * 0x11 - Bitmask: Renders a rectangular region with bitpacked presence mask
430
+ * Each bit represents presence (1) or absence (0) of the character at that position
431
+ * Ideal for: ore veins, destructible terrain, collision maps, fog of war, etc.
432
+ */
433
+ interface BitmaskOrder extends NetworkOrder {
434
+ type: 0x11;
435
+ posX: number;
436
+ posY: number;
437
+ sizeX: number;
438
+ sizeY: number;
439
+ charCode: number;
440
+ bgColorCode: number;
441
+ fgColorCode: number;
442
+ override: boolean;
443
+ mask: Uint8Array;
444
+ }
445
+ /**
446
+ * 0x13 - Clear: Fills entire layer with single character and colors
447
+ */
448
+ interface ClearOrder extends NetworkOrder {
449
+ type: 0x13;
450
+ charCode: number;
451
+ bgColorCode: number;
452
+ fgColorCode: number;
453
+ }
454
+ /**
455
+ * 0x14 - FillChar: Fills layer with repeating character pattern
456
+ */
457
+ interface FillCharOrder extends NetworkOrder {
458
+ type: 0x14;
459
+ patternWidth: number;
460
+ patternHeight: number;
461
+ bgColorCode: number;
462
+ fgColorCode: number;
463
+ pattern: number[];
464
+ }
465
+ /**
466
+ * 0x15 - FillSprite: Fills layer with repeating unicolor sprite
467
+ */
468
+ interface FillSpriteOrder extends NetworkOrder {
469
+ type: 0x15;
470
+ spriteIndex: number;
471
+ bgColorCode: number;
472
+ fgColorCode: number;
473
+ }
474
+ /**
475
+ * 0x16 - FillSpriteMultiColor: Fills layer with repeating multicolor sprite
476
+ */
477
+ interface FillSpriteMultiColorOrder extends NetworkOrder {
478
+ type: 0x16;
479
+ spriteIndex: number;
480
+ }
481
+ /**
482
+ * 0x20 - TriggerSound: Triggers positional sound effect
483
+ */
484
+ interface TriggerSoundOrder extends NetworkOrder {
485
+ type: 0x20;
486
+ posX: number;
487
+ posY: number;
488
+ soundId: number;
489
+ loop: boolean;
490
+ }
491
+ /**
492
+ * 0x21 - TriggerGlobalSound: Triggers global (non-positional) sound
493
+ */
494
+ interface TriggerGlobalSoundOrder extends NetworkOrder {
495
+ type: 0x21;
496
+ soundId: number;
497
+ loop: boolean;
498
+ }
499
+ type AnyNetworkOrder = CharOrder | TextOrder | TextMultilineOrder | SubFrameOrder | SubFrameMultiColorOrder | FullFrameOrder | FullFrameMultiColorOrder | SpriteOrder | SpriteMultiColorOrder | ColorMapOrder | ShapeOrder | DotCloudOrder | DotCloudMultiColorOrder | BitmaskOrder | SpriteCloudOrder | SpriteCloudMultiColorOrder | SpriteCloudVariedOrder | SpriteCloudVariedMultiColorOrder | ClearOrder | FillCharOrder | FillSpriteOrder | FillSpriteMultiColorOrder | TriggerSoundOrder | TriggerGlobalSoundOrder;
500
+
501
+ interface NetworkLayer {
502
+ id: number;
503
+ updateFlags: number;
504
+ zIndex: number;
505
+ originX: number;
506
+ originY: number;
507
+ width: number;
508
+ height: number;
509
+ orderCount: number;
510
+ orders: AnyNetworkOrder[];
511
+ }
512
+
513
+ /**
514
+ * Update packet according to the new UTSP protocol
515
+ * Layers are at the User level, not under displays
516
+ */
517
+ interface UpdatePacket {
518
+ /** Tick counter (8 bytes) */
519
+ tick: number;
520
+ /** Number of displays (2 bytes) */
521
+ displayCount: number;
522
+ /** List of displays with their origins */
523
+ displays: NetworkDisplay[];
524
+ /** Number of layers (2 bytes) */
525
+ layerCount: number;
526
+ /** List of layers (shared across all displays) */
527
+ layers: NetworkLayer[];
528
+ }
529
+
530
+ /**
531
+ * Represents a display (camera) in the virtual world
532
+ *
533
+ * ARCHITECTURE (new protocol):
534
+ * - Display = camera looking into the virtual world
535
+ * - LAYERS are NO LONGER in Display, they are at User level
536
+ * - Display only defines WHICH PART of the world we see (origin)
537
+ */
538
+ declare class Display {
539
+ private id;
540
+ private origin;
541
+ private size;
542
+ private previousOrigin;
543
+ private previousSize;
544
+ private toDraw;
545
+ constructor(id?: number, sizeX?: number, sizeY?: number);
546
+ /**
547
+ * Gets the display ID (0-255)
548
+ */
549
+ getId(): number;
550
+ /**
551
+ * Gets the origin position in the world
552
+ */
553
+ getOrigin(): Vector2;
554
+ /**
555
+ * Sets the origin position in the world
556
+ */
557
+ setOrigin(origin: Vector2): void;
558
+ /**
559
+ * Moves the origin in the world
560
+ */
561
+ moveOrigin(deltaX: number, deltaY: number): void;
562
+ /**
563
+ * Checks if display origin has changed since last tick
564
+ * @internal - Used to calculate if update should be sent
565
+ */
566
+ hasOriginChanged(): boolean;
567
+ /**
568
+ * Checks if display size has changed since last tick
569
+ * @internal - Used to calculate if update should be sent
570
+ */
571
+ hasSizeChanged(): boolean;
572
+ /**
573
+ * Checks if display has changed (origin OR size)
574
+ * @internal
575
+ */
576
+ hasChanged(): boolean;
577
+ /**
578
+ * Resets change tracking
579
+ * @internal - Called by Core.endTick() after sending updates
580
+ */
581
+ resetChangeTracking(): void;
582
+ /**
583
+ * Gets the display size
584
+ */
585
+ getSize(): Vector2;
586
+ /**
587
+ * Sets the display size
588
+ */
589
+ setSize(size: Vector2): void;
590
+ }
591
+
592
+ interface Cell {
593
+ charCode: number;
594
+ fgColorCode: number;
595
+ bgColorCode: number;
596
+ }
597
+ /**
598
+ * Optimized buffer for 65,536 cells with TypedArray
599
+ * Interleaved structure: [char0, fg0, bg0, char1, fg1, bg1, ...]
600
+ *
601
+ * Performance:
602
+ * - clear(): ~5-10μs (vs 826μs with object array)
603
+ * - Better cache locality
604
+ * - Less GC pressure
605
+ */
606
+ declare class CellBuffer {
607
+ private data;
608
+ private size;
609
+ constructor(size?: number);
610
+ /**
611
+ * Optimized clear: fills with "skip" values
612
+ * - charCode = 0 (no character)
613
+ * - fgColorCode = 255 (COLOR_SKIP = transparent)
614
+ * - bgColorCode = 255 (COLOR_SKIP = transparent)
615
+ * Performance: ~5-10μs (native TypedArray)
616
+ */
617
+ clear(): void;
618
+ /**
619
+ * Optimized clear with uniform color
620
+ * Faster than clear() when clearing to specific char/colors
621
+ * Useful for background layers with uniform color
622
+ *
623
+ * Performance: ~2-5μs (loop unrolling friendly)
624
+ *
625
+ * @param charCode - Character to fill (e.g., 0x20 for space)
626
+ * @param fgColorCode - Foreground color (0-255)
627
+ * @param bgColorCode - Background color (0-255)
628
+ *
629
+ * @example
630
+ * // Clear to black background
631
+ * buffer.clearWithColor(0x20, 15, 0);
632
+ */
633
+ clearWithColor(charCode: number, fgColorCode: number, bgColorCode: number): void;
634
+ /**
635
+ * Sets a cell at given index
636
+ */
637
+ set(index: number, charCode: number, fgColorCode: number, bgColorCode: number): void;
638
+ /**
639
+ * Optimized batch fill for uniform char+colors (DotCloud, SpriteCloud)
640
+ * Writes only characters at multiple positions with uniform fg/bg colors
641
+ *
642
+ * Performance: 2-3x faster than individual set() calls for uniform colors
643
+ * Reduces memory writes by 66% (writes chars only, then batch-fills colors)
644
+ *
645
+ * @param indices - Array of cell indices to fill
646
+ * @param charCode - Character to write at all positions
647
+ * @param fgColorCode - Uniform foreground color
648
+ * @param bgColorCode - Uniform background color
649
+ *
650
+ * @example
651
+ * // Rain drops (1,500 positions with same color)
652
+ * const indices = raindrops.map(d => d.y * width + d.x);
653
+ * buffer.fillCharsUniform(indices, 58, 20, 255); // ':' char, cyan, transparent
654
+ */
655
+ fillCharsUniform(indices: number[], charCode: number, fgColorCode: number, bgColorCode: number): void;
656
+ /**
657
+ * Gets a cell at given index
658
+ * Returns a Cell object for compatibility
659
+ */
660
+ get(index: number): Cell;
661
+ /**
662
+ * Sets only character at given index (keeps existing colors)
663
+ * Useful for operations that modify chars but preserve colors
664
+ *
665
+ * @param index - Cell index
666
+ * @param charCode - Character code to write
667
+ */
668
+ setCharOnly(index: number, charCode: number): void;
669
+ /**
670
+ * Sets only colors at given index (keeps existing char)
671
+ * Useful for ColorMapOrder (updates colors, preserves chars)
672
+ *
673
+ * @param index - Cell index
674
+ * @param fgColorCode - Foreground color
675
+ * @param bgColorCode - Background color
676
+ */
677
+ setColorsOnly(index: number, fgColorCode: number, bgColorCode: number): void;
678
+ /**
679
+ * Direct value access (faster than get())
680
+ * Note: These are called in hot loops, keep as inline as possible
681
+ */
682
+ getCharCode(index: number): number;
683
+ getFgColorCode(index: number): number;
684
+ getBgColorCode(index: number): number;
685
+ /**
686
+ * Direct TypedArray access for batch operations
687
+ */
688
+ getRawData(): Uint8Array;
689
+ /**
690
+ * Buffer size (number of cells)
691
+ */
692
+ getSize(): number;
693
+ }
694
+ /**
695
+ * Optimized buffer to store only charcodes (unicolor sprites)
696
+ * Simple structure: [char0, char1, char2, ...]
697
+ *
698
+ * Performance:
699
+ * - 3x less memory than CellBuffer (1 byte vs 3 bytes per cell)
700
+ * - Ideal for unicolor sprites where colors are defined at render time
701
+ * - Cache-friendly with TypedArray
702
+ */
703
+ declare class CharCodeBuffer {
704
+ private data;
705
+ private size;
706
+ constructor(size: number);
707
+ /**
708
+ * Optimized clear: fills with zeros (no character)
709
+ */
710
+ clear(): void;
711
+ /**
712
+ * Sets a charcode at given index
713
+ */
714
+ set(index: number, charCode: number): void;
715
+ /**
716
+ * Gets a charcode at given index
717
+ */
718
+ get(index: number): number;
719
+ /**
720
+ * Direct TypedArray access for batch operations
721
+ */
722
+ getRawData(): Uint8Array;
723
+ /**
724
+ * Buffer size (number of cells)
725
+ */
726
+ getSize(): number;
727
+ }
728
+
729
+ /**
730
+ * Unicolor sprite - Stores only charcodes
731
+ * Colors (fg/bg) are defined at render time via SpriteOrder
732
+ *
733
+ * Used with LoadType.Sprite (0x02) and SpriteOrder (0x07)
734
+ */
735
+ interface UnicolorSprite {
736
+ id: number;
737
+ sizeX: number;
738
+ sizeY: number;
739
+ data: CharCodeBuffer;
740
+ }
741
+ /**
742
+ * Multicolor sprite - Stores charcodes + fg/bg colors
743
+ * Each cell contains its own color set
744
+ *
745
+ * Used with LoadType.MulticolorSprite (0x03) and SpriteMultiColorOrder (0x08)
746
+ */
747
+ interface MulticolorSprite {
748
+ id: number;
749
+ sizeX: number;
750
+ sizeY: number;
751
+ data: CellBuffer;
752
+ }
753
+
754
+ /**
755
+ * UTSP Load Packet Types
756
+ * Based on UTSP Protocol v0.1 - Section 4: Load Section
757
+ */
758
+ /**
759
+ * LoadType enumeration
760
+ */
761
+ declare enum LoadType {
762
+ ColorPalette = 1,
763
+ Sprite = 2,
764
+ MulticolorSprite = 3,
765
+ BitmapFont = 4,// Formerly "Charset" - pixel-based fonts
766
+ Sound = 5,
767
+ WebFont = 6
768
+ }
769
+ /**
770
+ * Color definition with RGBA+E values
771
+ */
772
+ interface Color {
773
+ colorId: number;
774
+ r: number;
775
+ g: number;
776
+ b: number;
777
+ a: number;
778
+ e?: number;
779
+ }
780
+ /**
781
+ * Color Palette Load (LoadType 0x01)
782
+ * Loads a palette of RGBA+E colors
783
+ */
784
+ interface ColorPaletteLoad {
785
+ loadType: LoadType.ColorPalette;
786
+ colors: Color[];
787
+ }
788
+ /**
789
+ * Simple Sprite Load (LoadType 0x02)
790
+ * Each cell contains only a character code
791
+ */
792
+ interface SpriteLoad {
793
+ loadType: LoadType.Sprite;
794
+ sprites: Array<{
795
+ spriteId: number;
796
+ sizeX: number;
797
+ sizeY: number;
798
+ data: number[];
799
+ }>;
800
+ }
801
+ /**
802
+ * Cell in a multicolor sprite
803
+ */
804
+ interface MulticolorCell {
805
+ charCode: number;
806
+ fgColorId: number;
807
+ bgColorId: number;
808
+ }
809
+ /**
810
+ * Multicolor Sprite Load (LoadType 0x03)
811
+ * Each cell contains charcode + foreground color + background color
812
+ */
813
+ interface MulticolorSpriteLoad {
814
+ loadType: LoadType.MulticolorSprite;
815
+ sprites: Array<{
816
+ spriteId: number;
817
+ sizeX: number;
818
+ sizeY: number;
819
+ data: MulticolorCell[];
820
+ }>;
821
+ }
822
+ /**
823
+ * BitmapFont Load (LoadType 0x04)
824
+ * Custom character definitions (monochrome bitmaps)
825
+ * Formerly known as "Charset" - renamed to BitmapFont for clarity
826
+ */
827
+ interface BitmapFontLoad {
828
+ loadType: LoadType.BitmapFont;
829
+ fontId: number;
830
+ width: number;
831
+ height: number;
832
+ cellWidth: number;
833
+ cellHeight: number;
834
+ characters: Array<{
835
+ charCode: number;
836
+ bitmap: Uint8Array;
837
+ }>;
838
+ }
839
+ /**
840
+ * WebFont Load (LoadType 0x06)
841
+ * CSS-based font definitions for browser rendering
842
+ * Note: The default font is "Courier New" (monospace), but users can specify non-monospace fonts
843
+ */
844
+ interface WebFontLoad {
845
+ loadType: LoadType.WebFont;
846
+ fontId: number;
847
+ fontFamily: string;
848
+ fontSize: number;
849
+ offsetX?: number;
850
+ offsetY?: number;
851
+ charSpacing?: number;
852
+ lineHeight?: number;
853
+ fontWeight?: string;
854
+ fontStyle?: string;
855
+ }
856
+ /**
857
+ * Sound Load (LoadType 0x05)
858
+ * MIDI sound data for audio playback
859
+ */
860
+ interface SoundLoad {
861
+ loadType: LoadType.Sound;
862
+ sounds: Array<{
863
+ soundId: number;
864
+ midiData: Uint8Array;
865
+ }>;
866
+ }
867
+ /**
868
+ * Union type for all load types
869
+ */
870
+ type AnyLoad = ColorPaletteLoad | SpriteLoad | MulticolorSpriteLoad | BitmapFontLoad | SoundLoad | WebFontLoad;
871
+
872
+ /**
873
+ * Central registry to manage unicolor and multicolor sprites
874
+ *
875
+ * Sprites are loaded via LoadPacket and referenced by their ID
876
+ * in Orders (SpriteOrder, SpriteMultiColorOrder, etc.)
877
+ *
878
+ * Architecture:
879
+ * - Two separate registries for unicolor and multicolor
880
+ * - No possible collision between the two types
881
+ * - Memory optimization with CharCodeBuffer for unicolor
882
+ */
883
+ declare class SpriteRegistry {
884
+ private unicolorSprites;
885
+ private multicolorSprites;
886
+ /**
887
+ * Loads unicolor sprites from a LoadPacket
888
+ * Converts network format to optimized internal format
889
+ */
890
+ loadUnicolorSprites(loadData: SpriteLoad): void;
891
+ /**
892
+ * Loads multicolor sprites from a LoadPacket
893
+ * Converts network format to optimized internal format
894
+ */
895
+ loadMulticolorSprites(loadData: MulticolorSpriteLoad): void;
896
+ /**
897
+ * Retrieves a unicolor sprite by its ID
898
+ */
899
+ getUnicolorSprite(id: number): UnicolorSprite | undefined;
900
+ /**
901
+ * Retrieves a multicolor sprite by its ID
902
+ */
903
+ getMulticolorSprite(id: number): MulticolorSprite | undefined;
904
+ /**
905
+ * Checks if a unicolor sprite exists
906
+ */
907
+ hasUnicolorSprite(id: number): boolean;
908
+ /**
909
+ * Checks if a multicolor sprite exists
910
+ */
911
+ hasMulticolorSprite(id: number): boolean;
912
+ /**
913
+ * Removes a unicolor sprite
914
+ */
915
+ unloadUnicolorSprite(id: number): boolean;
916
+ /**
917
+ * Removes a multicolor sprite
918
+ */
919
+ unloadMulticolorSprite(id: number): boolean;
920
+ /**
921
+ * Clears all unicolor sprites
922
+ */
923
+ clearUnicolorSprites(): void;
924
+ /**
925
+ * Clears all multicolor sprites
926
+ */
927
+ clearMulticolorSprites(): void;
928
+ /**
929
+ * Clears all sprites (unicolor and multicolor)
930
+ */
931
+ clearAll(): void;
932
+ /**
933
+ * Number of loaded unicolor sprites
934
+ */
935
+ getUnicolorSpriteCount(): number;
936
+ /**
937
+ * Number of loaded multicolor sprites
938
+ */
939
+ getMulticolorSpriteCount(): number;
940
+ /**
941
+ * Total number of loaded sprites
942
+ */
943
+ getTotalSpriteCount(): number;
944
+ }
945
+
946
+ declare class Layer {
947
+ private id;
948
+ private origin;
949
+ private orders;
950
+ private zOrder;
951
+ private data;
952
+ private width;
953
+ private height;
954
+ private isStatic;
955
+ private spriteRegistry?;
956
+ private mode;
957
+ private previousOrigin;
958
+ private previousZOrder;
959
+ private enabled;
960
+ private useSetMode;
961
+ private needsCommit;
962
+ private static rasterizer;
963
+ constructor(origin: Vector2, zOrder: number, width: number, height: number, isStatic?: boolean);
964
+ /**
965
+ * Configures the layer's execution mode (called by User)
966
+ * @internal
967
+ */
968
+ setMode(mode: CoreMode): void;
969
+ /**
970
+ * Injects the SpriteRegistry into the layer (called by Core)
971
+ * @internal
972
+ */
973
+ setSpriteRegistry(registry: SpriteRegistry): void;
974
+ getOrders(): AnyNetworkOrder[];
975
+ /**
976
+ * Adds orders to the layer (incremental mode)
977
+ * Automatically rasterizes new orders in ADD mode
978
+ */
979
+ addOrders(orders: AnyNetworkOrder[]): void;
980
+ /**
981
+ * Replaces all orders in the layer (reset mode)
982
+ * Automatically rasterizes all orders in SET mode
983
+ */
984
+ setOrders(orders: AnyNetworkOrder[]): void;
985
+ /**
986
+ * Clears all orders and cleans the buffer
987
+ */
988
+ clearOrders(): void;
989
+ getOrigin(): Vector2;
990
+ /**
991
+ * Updates the layer's origin and rasterizes if necessary
992
+ *
993
+ * In client mode, this method automatically rasterizes the layer
994
+ * to reflect the new position.
995
+ *
996
+ * @param origin - New layer origin
997
+ */
998
+ setOrigin(origin: Vector2): void;
999
+ getZOrder(): number;
1000
+ /**
1001
+ * Updates the layer's z-order
1002
+ *
1003
+ * @param zOrder - New z-order
1004
+ */
1005
+ setZOrder(zOrder: number): void;
1006
+ /**
1007
+ * Gets the layer's unique ID
1008
+ *
1009
+ * Used for client-side reconstruction when applying UpdatePackets.
1010
+ * The ID allows associating a NetworkLayer with its corresponding Layer.
1011
+ *
1012
+ * @returns Layer ID (0-65535)
1013
+ */
1014
+ getId(): number;
1015
+ /**
1016
+ * Sets the layer's unique ID
1017
+ *
1018
+ * @internal - Used by User.applyUpdate() during reconstruction
1019
+ * @param id - Unique layer ID (0-65535)
1020
+ */
1021
+ setId(id: number): void;
1022
+ getData(): CellBuffer;
1023
+ getWidth(): number;
1024
+ getHeight(): number;
1025
+ getCellAt(x: number, y: number): Cell | null;
1026
+ /**
1027
+ * Marks this layer as static or dynamic
1028
+ *
1029
+ * Static layer: Sent only once to client via reliable channel,
1030
+ * but continues to be rasterized client-side every frame.
1031
+ * Used for UI elements, backgrounds, elements that never change.
1032
+ *
1033
+ * Dynamic layer: Sent every tick via volatile channel.
1034
+ * Used for animated elements, players, projectiles, etc.
1035
+ *
1036
+ * @param isStatic - true for static, false for dynamic
1037
+ */
1038
+ setStatic(isStatic: boolean): void;
1039
+ /**
1040
+ * Checks if this layer is static
1041
+ */
1042
+ getStatic(): boolean;
1043
+ /**
1044
+ * Enables or disables the layer
1045
+ * A disabled layer will not be rendered by DisplayRasterizer
1046
+ *
1047
+ * @param enabled - true to enable, false to disable
1048
+ */
1049
+ setEnabled(enabled: boolean): void;
1050
+ /**
1051
+ * Checks if layer is enabled
1052
+ */
1053
+ isEnabled(): boolean;
1054
+ /**
1055
+ * Checks if layer origin changed since last tick
1056
+ * @internal - Used to calculate UpdateFlags
1057
+ */
1058
+ hasOriginChanged(): boolean;
1059
+ /**
1060
+ * Checks if layer z-order changed since last tick
1061
+ * @internal - Used to calculate UpdateFlags
1062
+ */
1063
+ hasZOrderChanged(): boolean;
1064
+ /**
1065
+ * Calculates UpdateFlags based on layer state and changes
1066
+ * @internal - Used by Core.endTick()
1067
+ * @returns Bitpacked flags (0x00 to 0x0F)
1068
+ *
1069
+ * Flag structure:
1070
+ * - Bit 0 (0x01): Layer Enabled (1=active, 0=disabled)
1071
+ * - Bit 1 (0x02): SET Mode (1=SET, 0=ADD)
1072
+ * - Bit 2 (0x04): Position Changed
1073
+ * - Bit 3 (0x08): Z-Order Changed
1074
+ */
1075
+ calculateUpdateFlags(): number;
1076
+ /**
1077
+ * Resets change tracking after sending a tick
1078
+ * @internal - Used by Core.endTick()
1079
+ */
1080
+ resetChangeTracking(): void;
1081
+ /**
1082
+ * Marks the layer as needing to be sent on next tick
1083
+ *
1084
+ * Used to optimize bandwidth by sending only modified layers.
1085
+ *
1086
+ * @example
1087
+ * ```typescript
1088
+ * layer.addOrders([...]);
1089
+ * layer.commit(); // Layer will be sent on next tick
1090
+ * ```
1091
+ */
1092
+ commit(): void;
1093
+ /**
1094
+ * Checks if layer needs to be sent
1095
+ * @internal - Used by Core.endTickSplit()
1096
+ */
1097
+ getNeedsCommit(): boolean;
1098
+ /**
1099
+ * Resets commit flag after sending
1100
+ * @internal - Used by Core.endTickSplit()
1101
+ */
1102
+ resetCommit(): void;
1103
+ }
1104
+
1105
+ /**
1106
+ * InputBindingRegistry - Input bindings registry
1107
+ *
1108
+ * Allows the server to define available axes and buttons,
1109
+ * and send them to the client via a JSON LoadPacket.
1110
+ *
1111
+ * Architecture:
1112
+ * 1. Server defines bindings (bindingId ↔ name)
1113
+ * 2. Server generates a JSON LoadPacket
1114
+ * 3. Client receives LoadPacket and configures its mappings
1115
+ * 4. Client sends back compressed inputs (bindingId + value)
1116
+ * 5. Server decodes with registry (bindingId → name → setAxis/setButton)
1117
+ */
1118
+
1119
+ /**
1120
+ * Input bindings registry
1121
+ *
1122
+ * Allows to:
1123
+ * - Define axes and buttons (bindingId → name)
1124
+ * - Generate a JSON LoadPacket to send to client
1125
+ * - Decode compressed inputs received from client (future)
1126
+ */
1127
+ declare class InputBindingRegistry {
1128
+ private axes;
1129
+ private buttons;
1130
+ private axisNameToId;
1131
+ private buttonNameToId;
1132
+ private version;
1133
+ /**
1134
+ * Defines an axis binding
1135
+ *
1136
+ * @param bindingId - Unique axis ID (0-255)
1137
+ * @param name - Axis name (e.g., "MoveHorizontal", "CameraX")
1138
+ * @param sources - Physical sources (keyboard, gamepad, etc.)
1139
+ * @param min - Minimum axis value (default: -1.0)
1140
+ * @param max - Maximum axis value (default: +1.0)
1141
+ * @param defaultValue - Default axis value (default: 0.0)
1142
+ * @throws Error if bindingId or name already exists
1143
+ *
1144
+ * @example
1145
+ * ```typescript
1146
+ * import { InputDeviceType, KeyboardInput, GamepadInput } from '@utsp/types';
1147
+ *
1148
+ * registry.defineAxis(0, "MoveHorizontal", [
1149
+ * { sourceId: 0, type: InputDeviceType.Keyboard, negativeKey: KeyboardInput.ArrowLeft, positiveKey: KeyboardInput.ArrowRight },
1150
+ * { sourceId: 1, type: InputDeviceType.Gamepad, gamepadIndex: 0, axis: GamepadInput.LeftStickX }
1151
+ * ], -1.0, 1.0, 0.0);
1152
+ * ```
1153
+ */
1154
+ defineAxis(bindingId: number, name: string, sources?: AxisSource[], min?: number, max?: number, defaultValue?: number): void;
1155
+ /**
1156
+ * Defines a button binding
1157
+ *
1158
+ * @param bindingId - Unique button ID (0-255)
1159
+ * @param name - Button name (e.g., "Jump", "Attack")
1160
+ * @param sources - Physical sources (keyboard, gamepad, mouse, touch)
1161
+ * @param defaultValue - Default button value (default: false)
1162
+ * @throws Error if bindingId or name already exists
1163
+ *
1164
+ * @example
1165
+ * ```typescript
1166
+ * import { InputDeviceType, KeyboardInput, GamepadInput } from '@utsp/types';
1167
+ *
1168
+ * registry.defineButton(0, "Jump", [
1169
+ * { sourceId: 0, type: InputDeviceType.Keyboard, key: KeyboardInput.Space },
1170
+ * { sourceId: 1, type: InputDeviceType.Gamepad, gamepadIndex: 0, button: GamepadInput.A }
1171
+ * ], false);
1172
+ * ```
1173
+ */
1174
+ defineButton(bindingId: number, name: string, sources?: ButtonSource[], defaultValue?: boolean): void;
1175
+ /**
1176
+ * Evaluates an axis by summing all its sources
1177
+ *
1178
+ * This method takes raw values from each source (received from client)
1179
+ * and sums them according to the following logic:
1180
+ * 1. For each source: apply deadzone, scale, invert, sensitivity
1181
+ * 2. Sum all values
1182
+ * 3. Clamp between min and max
1183
+ *
1184
+ * Note: Raw source values are provided in sourceValues (Map<sourceId, value>)
1185
+ *
1186
+ * @param bindingId - Axis binding ID
1187
+ * @param sourceValues - Map of raw source values (sourceId → value)
1188
+ * @returns Final axis value (clamped), or defaultValue if binding not found
1189
+ *
1190
+ * @example
1191
+ * ```typescript
1192
+ * // Client sends: { sourceId: 0, value: -1.0 }, { sourceId: 1, value: 0.5 }
1193
+ * const sourceValues = new Map([[0, -1.0], [1, 0.5]]);
1194
+ * const axisValue = registry.evaluateAxis(0, sourceValues);
1195
+ * // Result: -1.0 + 0.5 = -0.5 (clamped between min and max)
1196
+ * ```
1197
+ */
1198
+ evaluateAxis(bindingId: number, sourceValues: Map<number, number>): number;
1199
+ /**
1200
+ * Evaluates a button with OR logic on all its sources
1201
+ *
1202
+ * A button is considered pressed if AT LEAST ONE source is pressed.
1203
+ *
1204
+ * @param bindingId - Button binding ID
1205
+ * @param sourceValues - Map of source states (sourceId → pressed)
1206
+ * @returns true if at least one source is pressed, false otherwise
1207
+ *
1208
+ * @example
1209
+ * ```typescript
1210
+ * // Client sends: { sourceId: 0, pressed: false }, { sourceId: 1, pressed: true }
1211
+ * const sourceValues = new Map([[0, false], [1, true]]);
1212
+ * const buttonPressed = registry.evaluateButton(0, sourceValues);
1213
+ * // Result: true (because at least one source is pressed)
1214
+ * ```
1215
+ */
1216
+ evaluateButton(bindingId: number, sourceValues: Map<number, boolean>): boolean;
1217
+ /**
1218
+ * Generates a LoadPacket JSON containing all bindings
1219
+ *
1220
+ * This packet will be sent to the client via the Load channel to indicate
1221
+ * which axes and buttons it should capture and send back.
1222
+ *
1223
+ * JSON format (not binary for now as sent rarely)
1224
+ *
1225
+ * @returns LoadPacket JSON as string
1226
+ *
1227
+ * @example
1228
+ * ```typescript
1229
+ * const packet = registry.toLoadPacket();
1230
+ * websocket.send(packet); // Send to client
1231
+ * ```
1232
+ */
1233
+ toLoadPacket(): string;
1234
+ /**
1235
+ * Generates a LoadPacket JSON as object
1236
+ * (useful for inspection or tests)
1237
+ *
1238
+ * @returns LoadPacket as object
1239
+ */
1240
+ toLoadPacketObject(): InputBindingLoadPacket;
1241
+ /**
1242
+ * Retrieves an axis bindingId from its name
1243
+ *
1244
+ * @param name - Axis name
1245
+ * @returns bindingId or null if not found
1246
+ *
1247
+ * @example
1248
+ * ```typescript
1249
+ * const id = registry.getAxisBindingId("MoveHorizontal");
1250
+ * if (id !== null) {
1251
+ * console.log(`MoveHorizontal has bindingId ${id}`);
1252
+ * }
1253
+ * ```
1254
+ */
1255
+ getAxisBindingId(name: string): number | null;
1256
+ /**
1257
+ * Retrieves a button bindingId from its name
1258
+ *
1259
+ * @param name - Button name
1260
+ * @returns bindingId or null if not found
1261
+ *
1262
+ * @example
1263
+ * ```typescript
1264
+ * const id = registry.getButtonBindingId("Jump");
1265
+ * if (id !== null) {
1266
+ * console.log(`Jump has bindingId ${id}`);
1267
+ * }
1268
+ * ```
1269
+ */
1270
+ getButtonBindingId(name: string): number | null;
1271
+ /**
1272
+ * Retrieves an axis name from its bindingId
1273
+ *
1274
+ * @param bindingId - Binding ID
1275
+ * @returns name or null if not found
1276
+ *
1277
+ * @example
1278
+ * ```typescript
1279
+ * const name = registry.getAxisName(0);
1280
+ * console.log(name); // "MoveHorizontal"
1281
+ * ```
1282
+ */
1283
+ getAxisName(bindingId: number): string | null;
1284
+ /**
1285
+ * Retrieves a button name from its bindingId
1286
+ *
1287
+ * @param bindingId - Binding ID
1288
+ * @returns name or null if not found
1289
+ *
1290
+ * @example
1291
+ * ```typescript
1292
+ * const name = registry.getButtonName(0);
1293
+ * console.log(name); // "Jump"
1294
+ * ```
1295
+ */
1296
+ getButtonName(bindingId: number): string | null;
1297
+ /**
1298
+ * Retrieves a complete axis binding
1299
+ *
1300
+ * @param bindingId - Binding ID
1301
+ * @returns binding or null if not found
1302
+ */
1303
+ getAxisBinding(bindingId: number): AxisBinding | null;
1304
+ /**
1305
+ * Retrieves a complete button binding
1306
+ *
1307
+ * @param bindingId - Binding ID
1308
+ * @returns binding or null if not found
1309
+ */
1310
+ getButtonBinding(bindingId: number): ButtonBinding | null;
1311
+ /**
1312
+ * Checks if an axis is defined
1313
+ *
1314
+ * @param bindingId - Binding ID
1315
+ * @returns true if defined
1316
+ */
1317
+ hasAxis(bindingId: number): boolean;
1318
+ /**
1319
+ * Checks if a button is defined
1320
+ *
1321
+ * @param bindingId - Binding ID
1322
+ * @returns true if defined
1323
+ */
1324
+ hasButton(bindingId: number): boolean;
1325
+ /**
1326
+ * Counts the number of defined axes
1327
+ *
1328
+ * @returns number of axes
1329
+ */
1330
+ getAxisCount(): number;
1331
+ /**
1332
+ * Counts the number of defined buttons
1333
+ *
1334
+ * @returns number of buttons
1335
+ */
1336
+ getButtonCount(): number;
1337
+ /**
1338
+ * Retrieves all axes
1339
+ *
1340
+ * @returns array of all axis bindings
1341
+ */
1342
+ getAllAxes(): AxisBinding[];
1343
+ /**
1344
+ * Retrieves all buttons
1345
+ *
1346
+ * @returns array of all button bindings
1347
+ */
1348
+ getAllButtons(): ButtonBinding[];
1349
+ /**
1350
+ * Retrieves the current binding version
1351
+ *
1352
+ * @returns version (incremented with each modification)
1353
+ */
1354
+ getVersion(): number;
1355
+ /**
1356
+ * Removes an axis
1357
+ *
1358
+ * @param bindingId - Binding ID to remove
1359
+ * @returns true if removed, false if not found
1360
+ */
1361
+ removeAxis(bindingId: number): boolean;
1362
+ /**
1363
+ * Removes a button
1364
+ *
1365
+ * @param bindingId - Binding ID to remove
1366
+ * @returns true if removed, false if not found
1367
+ */
1368
+ removeButton(bindingId: number): boolean;
1369
+ /**
1370
+ * Removes all axes
1371
+ */
1372
+ clearAxes(): void;
1373
+ /**
1374
+ * Removes all buttons
1375
+ */
1376
+ clearButtons(): void;
1377
+ /**
1378
+ * Completely resets the registry
1379
+ */
1380
+ clear(): void;
1381
+ /**
1382
+ * Displays a summary of bindings (for debug)
1383
+ *
1384
+ * @returns formatted string
1385
+ */
1386
+ toString(): string;
1387
+ }
1388
+
1389
+ /**
1390
+ * Per-user performance statistics
1391
+ * Collects user-specific metrics
1392
+ */
1393
+ interface UserTickStats {
1394
+ userId: string;
1395
+ userName: string;
1396
+ tick: number;
1397
+ timestamp: number;
1398
+ displayCount: number;
1399
+ totalDisplayArea: number;
1400
+ totalLayers: number;
1401
+ visibleLayers: number;
1402
+ staticLayers: number;
1403
+ dynamicLayers: number;
1404
+ totalOrders: number;
1405
+ ordersByLayer: Map<number, number>;
1406
+ staticPacketSize: number;
1407
+ dynamicPacketSize: number;
1408
+ totalPacketSize: number;
1409
+ hasInput: boolean;
1410
+ axisCount: number;
1411
+ buttonCount: number;
1412
+ }
1413
+ declare class UserStats {
1414
+ private enabled;
1415
+ private currentStats;
1416
+ private _userId;
1417
+ private _userName;
1418
+ constructor(userId: string, userName: string);
1419
+ /**
1420
+ * Enables or disables statistics collection
1421
+ */
1422
+ setEnabled(enabled: boolean): void;
1423
+ isEnabled(): boolean;
1424
+ /** User ID */
1425
+ get userId(): string;
1426
+ /** User name */
1427
+ get userName(): string;
1428
+ /** Current tick number */
1429
+ get tick(): number;
1430
+ /** Current tick timestamp */
1431
+ get timestamp(): number;
1432
+ /** Number of displays */
1433
+ get displayCount(): number;
1434
+ /** Total display surface area (width × height) */
1435
+ get totalDisplayArea(): number;
1436
+ /** Total number of layers */
1437
+ get totalLayers(): number;
1438
+ /** Visible layers in displays */
1439
+ get visibleLayers(): number;
1440
+ /** Static layers */
1441
+ get staticLayers(): number;
1442
+ /** Dynamic layers */
1443
+ get dynamicLayers(): number;
1444
+ /** Total orders for this user */
1445
+ get totalOrders(): number;
1446
+ /** Orders par layer ID */
1447
+ get ordersByLayer(): Map<number, number>;
1448
+ /** Taille du packet statique (octets) */
1449
+ get staticPacketSize(): number;
1450
+ /** Taille du packet dynamique (octets) */
1451
+ get dynamicPacketSize(): number;
1452
+ /** Taille totale des packets (static + dynamic) */
1453
+ get totalPacketSize(): number;
1454
+ /** Estimated size after gzip compression (25% of original size) */
1455
+ get compressedPacketSize(): number;
1456
+ /** If user sent input this tick */
1457
+ get hasInput(): boolean;
1458
+ /** Number of bound axes */
1459
+ get axisCount(): number;
1460
+ /** Number of bound buttons */
1461
+ get buttonCount(): number;
1462
+ /**
1463
+ * @internal
1464
+ * Starts collection for a new tick
1465
+ */
1466
+ startTick(tickNumber: number): void;
1467
+ /**
1468
+ * @internal
1469
+ * Records display information
1470
+ */
1471
+ recordDisplays(displayCount: number, totalArea: number): void;
1472
+ /**
1473
+ * @internal
1474
+ * Enregistre les informations des layers
1475
+ */
1476
+ recordLayers(total: number, visible: number, staticCount: number, dynamicCount: number): void;
1477
+ /**
1478
+ * @internal
1479
+ * Enregistre les orders d'un layer
1480
+ */
1481
+ recordLayerOrders(layerId: number, orderCount: number): void;
1482
+ /**
1483
+ * @internal
1484
+ * Records network packet sizes
1485
+ */
1486
+ recordPacketSizes(staticSize: number, dynamicSize: number): void;
1487
+ /**
1488
+ * @internal
1489
+ * Records input information
1490
+ */
1491
+ recordInput(hasInput: boolean, axisCount: number, buttonCount: number): void;
1492
+ /**
1493
+ * @internal
1494
+ * Finalizes current tick stats
1495
+ */
1496
+ endTick(): void;
1497
+ /**
1498
+ * Resets statistics
1499
+ */
1500
+ reset(): void;
1501
+ }
1502
+
1503
+ /**
1504
+ * Represents a connected user with their displays and layers
1505
+ *
1506
+ * ARCHITECTURE (new protocol):
1507
+ * - LAYERS are at the USER level (shared across all displays)
1508
+ * - DISPLAYS are origins (cameras) that look into the world
1509
+ * - Each display can see the same layers (if within its origin)
1510
+ *
1511
+ * @template TData - Application-specific data type (default: Record<string, any>)
1512
+ */
1513
+ declare class User<TData = Record<string, any>> {
1514
+ id: string;
1515
+ name: string;
1516
+ private displays;
1517
+ private layers;
1518
+ private spriteRegistry;
1519
+ private mode;
1520
+ private axes;
1521
+ private buttons;
1522
+ private textInputs;
1523
+ private mouseX;
1524
+ private mouseY;
1525
+ private mouseOver;
1526
+ private mouseDisplayId;
1527
+ private touchPositions;
1528
+ private activeTouchId;
1529
+ private inputBindings;
1530
+ private stats;
1531
+ /**
1532
+ * Application-specific data storage
1533
+ * Use this to store game state, player data, or any custom information
1534
+ */
1535
+ data: TData;
1536
+ constructor(id: string, name: string, mode: CoreMode);
1537
+ /**
1538
+ * Injects SpriteRegistry into the user (called by Core)
1539
+ * @internal
1540
+ */
1541
+ setSpriteRegistry(registry: SpriteRegistry): void;
1542
+ getDisplays(): Display[];
1543
+ /**
1544
+ * Adds a display to the user
1545
+ *
1546
+ * @param display - The display to add
1547
+ */
1548
+ addDisplay(display: Display): void;
1549
+ /**
1550
+ * Removes a display from the user
1551
+ *
1552
+ * @param display - The display to remove
1553
+ * @returns true if display was removed, false otherwise
1554
+ */
1555
+ removeDisplay(display: Display): boolean;
1556
+ /**
1557
+ * Removes all displays from the user
1558
+ */
1559
+ clearDisplays(): void;
1560
+ getLayers(): Layer[];
1561
+ /**
1562
+ * Adds a layer to the user
1563
+ * Layers are shared across all displays
1564
+ *
1565
+ * @param layer - The layer to add
1566
+ */
1567
+ addLayer(layer: Layer): void;
1568
+ /**
1569
+ * Removes a layer from the user
1570
+ *
1571
+ * @param layer - The layer to remove
1572
+ * @returns true if layer was removed, false otherwise
1573
+ */
1574
+ removeLayer(layer: Layer): boolean;
1575
+ /**
1576
+ * Removes all layers from the user
1577
+ */
1578
+ clearLayers(): void;
1579
+ /**
1580
+ * Updates mouse position (called by client)
1581
+ *
1582
+ * @param x - X position (0-255)
1583
+ * @param y - Y position (0-255)
1584
+ * @param over - Is mouse over the display?
1585
+ *
1586
+ * @example
1587
+ * ```typescript
1588
+ * user.setMousePosition(128, 64, true);
1589
+ * ```
1590
+ */
1591
+ setMousePosition(x: number, y: number, over?: boolean): void;
1592
+ /**
1593
+ * Sets the position of a touch (multi-touch support)
1594
+ *
1595
+ * @param touchId - Touch ID (0-9)
1596
+ * @param x - X position in display (0-255)
1597
+ * @param y - Y position in display (0-255)
1598
+ * @param over - If touch is active (true) or released (false)
1599
+ * @param displayId - ID of concerned display (0-255)
1600
+ *
1601
+ * @example
1602
+ * ```typescript
1603
+ * // Touch ID 0 at position (128, 64)
1604
+ * user.setTouchPosition(0, 128, 64, true, 0);
1605
+ * ```
1606
+ */
1607
+ setTouchPosition(touchId: number, x: number, y: number, over?: boolean, displayId?: number): void;
1608
+ /**
1609
+ * Sets the value of a virtual axis
1610
+ *
1611
+ * Axes are floating point values between -1.0 and +1.0, typically used for:
1612
+ * - Movement (Horizontal, Vertical)
1613
+ * - Camera rotation (CameraX, CameraY)
1614
+ * - Analog controls (Throttle, Steering)
1615
+ *
1616
+ * Axis name is free-form and defined by the application.
1617
+ * Physical mapping (keyboard, gamepad, etc.) is handled by the client.
1618
+ *
1619
+ * @param axisName - Axis name (e.g., "Horizontal", "Vertical", "CameraX")
1620
+ * @param value - Axis value (-1.0 to +1.0)
1621
+ *
1622
+ * @example
1623
+ * ```typescript
1624
+ * // Client side: map keys to axes
1625
+ * user.setAxis("Horizontal", keyboard.arrowLeft ? -1.0 : keyboard.arrowRight ? 1.0 : 0.0);
1626
+ * user.setAxis("Vertical", keyboard.arrowUp ? -1.0 : keyboard.arrowDown ? 1.0 : 0.0);
1627
+ * user.setAxis("CameraX", gamepad.rightStickX); // -1.0 to +1.0
1628
+ * ```
1629
+ */
1630
+ setAxis(axisName: string, value: number): void;
1631
+ /**
1632
+ * Gets the value of a virtual axis
1633
+ *
1634
+ * @param axisName - Axis name (e.g., "Horizontal", "Vertical")
1635
+ * @returns Axis value (-1.0 to +1.0), or 0.0 if axis doesn't exist
1636
+ *
1637
+ * @example
1638
+ * ```typescript
1639
+ * // Server side: use axes for game logic
1640
+ * const moveX = user.getAxis("Horizontal");
1641
+ * const moveY = user.getAxis("Vertical");
1642
+ * player.move(moveX, moveY);
1643
+ * ```
1644
+ */
1645
+ getAxis(axisName: string): number;
1646
+ /**
1647
+ * Sets the state of a virtual button
1648
+ *
1649
+ * Buttons are boolean values used for actions:
1650
+ * - Point actions (Jump, Fire, Interact)
1651
+ * - Continuous states (Run, Crouch, Aim)
1652
+ *
1653
+ * Button name is free-form and defined by the application.
1654
+ * Physical mapping (keyboard, gamepad, etc.) is handled by the client.
1655
+ *
1656
+ * @param buttonName - Button name (e.g., "Jump", "Fire", "Inventory")
1657
+ * @param pressed - Button state (true = pressed, false = released)
1658
+ *
1659
+ * @example
1660
+ * ```typescript
1661
+ * // Client side: map keys to buttons
1662
+ * user.setButton("Jump", keyboard.space || mouse.leftClick);
1663
+ * user.setButton("Fire", mouse.rightClick);
1664
+ * user.setButton("Inventory", keyboard.i);
1665
+ * user.setButton("Sprint", keyboard.shift);
1666
+ * ```
1667
+ */
1668
+ setButton(buttonName: string, pressed: boolean): void;
1669
+ /**
1670
+ * Checks if a virtual button is pressed
1671
+ *
1672
+ * @param buttonName - Button name (e.g., "Jump", "Fire")
1673
+ * @returns true if button is pressed, false otherwise
1674
+ *
1675
+ * @example
1676
+ * ```typescript
1677
+ * // Server side: use buttons for game logic
1678
+ * if (user.getButton("Jump")) {
1679
+ * player.jump();
1680
+ * }
1681
+ *
1682
+ * if (user.getButton("Fire")) {
1683
+ * player.shoot();
1684
+ * }
1685
+ *
1686
+ * if (user.getButton("Sprint")) {
1687
+ * player.speed = 2.0;
1688
+ * } else {
1689
+ * player.speed = 1.0;
1690
+ * }
1691
+ * ```
1692
+ */
1693
+ getButton(buttonName: string): boolean;
1694
+ /**
1695
+ * Checks if a virtual button was just pressed this frame
1696
+ *
1697
+ * @param buttonName - Button name (e.g., "Jump", "Fire")
1698
+ * @returns true if button just transitioned from false→true
1699
+ *
1700
+ * @example
1701
+ * ```typescript
1702
+ * // Server side: single-shot actions
1703
+ * if (user.getButtonJustPressed("Jump")) {
1704
+ * player.jump(); // Fires once per press, not continuously
1705
+ * }
1706
+ *
1707
+ * if (user.getButtonJustPressed("ToggleInventory")) {
1708
+ * ui.toggleInventory(); // Won't toggle 5 times per click
1709
+ * }
1710
+ * ```
1711
+ */
1712
+ getButtonJustPressed(buttonName: string): boolean;
1713
+ /**
1714
+ * Checks if a virtual button was just released this frame
1715
+ *
1716
+ * @param buttonName - Button name (e.g., "Fire", "ChargeShot")
1717
+ * @returns true if button just transitioned from true→false
1718
+ *
1719
+ * @example
1720
+ * ```typescript
1721
+ * // Server side: charge and release mechanics
1722
+ * if (user.getButton("ChargeShot")) {
1723
+ * weapon.charge(); // Accumulate power while held
1724
+ * }
1725
+ * if (user.getButtonJustReleased("ChargeShot")) {
1726
+ * weapon.fireChargedShot(); // Release when button released
1727
+ * }
1728
+ * ```
1729
+ */
1730
+ getButtonJustReleased(buttonName: string): boolean;
1731
+ /**
1732
+ * Sets text input events for this frame
1733
+ * Called internally by input decoding system
1734
+ *
1735
+ * @param inputs - Array of key strings (e.g., ['a', 'Backspace', 'Enter'])
1736
+ * @internal
1737
+ */
1738
+ setTextInputs(inputs: string[]): void;
1739
+ /**
1740
+ * Gets text input events for this frame
1741
+ * Use this in your update loop to handle input boxes, chat, etc.
1742
+ *
1743
+ * @returns Array of key strings typed this frame
1744
+ *
1745
+ * @example
1746
+ * ```typescript
1747
+ * // Server side: handle input box
1748
+ * const textInputs = user.getTextInputs();
1749
+ * for (const key of textInputs) {
1750
+ * if (key.length === 1) {
1751
+ * inputBox.addChar(key); // Regular character
1752
+ * } else if (key === 'Backspace') {
1753
+ * inputBox.deleteChar();
1754
+ * } else if (key === 'Enter') {
1755
+ * inputBox.submit();
1756
+ * }
1757
+ * }
1758
+ * ```
1759
+ */
1760
+ getTextInputs(): string[];
1761
+ /**
1762
+ * Clears text input events (called after frame processing)
1763
+ * @internal
1764
+ */
1765
+ clearTextInputs(): void;
1766
+ /**
1767
+ * Gets all defined axis names
1768
+ *
1769
+ * @returns Array of axis names
1770
+ *
1771
+ * @example
1772
+ * ```typescript
1773
+ * const axisNames = user.getAxisNames();
1774
+ * console.log(axisNames); // ["Horizontal", "Vertical", "CameraX", "CameraY"]
1775
+ * ```
1776
+ */
1777
+ getAxisNames(): string[];
1778
+ /**
1779
+ * Gets all defined button names
1780
+ *
1781
+ * @returns Array of button names
1782
+ *
1783
+ * @example
1784
+ * ```typescript
1785
+ * const buttonNames = user.getButtonNames();
1786
+ * console.log(buttonNames); // ["Jump", "Fire", "Inventory", "Interact"]
1787
+ * ```
1788
+ */
1789
+ getButtonNames(): string[];
1790
+ /**
1791
+ * Resets all axes to 0.0
1792
+ *
1793
+ * @example
1794
+ * ```typescript
1795
+ * user.clearAxes();
1796
+ * console.log(user.getAxis("Horizontal")); // 0.0
1797
+ * ```
1798
+ */
1799
+ clearAxes(): void;
1800
+ /**
1801
+ * Resets all buttons to false
1802
+ *
1803
+ * @example
1804
+ * ```typescript
1805
+ * user.clearButtons();
1806
+ * console.log(user.getButton("Jump")); // false
1807
+ * ```
1808
+ */
1809
+ clearButtons(): void;
1810
+ /**
1811
+ * Gets detailed information about mouse position
1812
+ *
1813
+ * Returns mouse position in multiple coordinate spaces:
1814
+ * - displayId: ID of the hovered display (or null if none)
1815
+ * - localX/localY: Position in the display (0 to sizeX/sizeY)
1816
+ * - worldX/worldY: Position in the virtual world (origin + local)
1817
+ *
1818
+ * Note: For now, we consider the mouse always hovers display 0
1819
+ * if mouseOver is true and at least one display exists.
1820
+ *
1821
+ * @returns Mouse position information, or null if no display
1822
+ *
1823
+ * @example
1824
+ * ```typescript
1825
+ * const mouseInfo = user.getMouseDisplayInfo();
1826
+ * if (mouseInfo) {
1827
+ * console.log(`Mouse over display ${mouseInfo.displayId}`);
1828
+ * console.log(`Local: ${mouseInfo.localX}, ${mouseInfo.localY}`);
1829
+ * console.log(`World: ${mouseInfo.worldX}, ${mouseInfo.worldY}`);
1830
+ * }
1831
+ * ```
1832
+ */
1833
+ getMouseDisplayInfo(): {
1834
+ displayId: number | null;
1835
+ localX: number;
1836
+ localY: number;
1837
+ worldX: number;
1838
+ worldY: number;
1839
+ } | null;
1840
+ /**
1841
+ * Gets position information for a touch relative to the display
1842
+ *
1843
+ * @param touchId - Touch ID (0-9), default = 0 (first finger)
1844
+ * @returns Touch information (displayId, localX, localY, worldX, worldY) or null
1845
+ *
1846
+ * @example
1847
+ * ```typescript
1848
+ * const touchInfo = user.getTouchDisplayInfo(0);
1849
+ * if (touchInfo) {
1850
+ * console.log(`Touch 0 over display ${touchInfo.displayId}`);
1851
+ * console.log(`Local: ${touchInfo.localX}, ${touchInfo.localY}`);
1852
+ * console.log(`World: ${touchInfo.worldX}, ${touchInfo.worldY}`);
1853
+ * }
1854
+ * ```
1855
+ */
1856
+ getTouchDisplayInfo(touchId?: number): {
1857
+ displayId: number | null;
1858
+ localX: number;
1859
+ localY: number;
1860
+ worldX: number;
1861
+ worldY: number;
1862
+ } | null;
1863
+ /**
1864
+ * Checks if the mouse is currently over a display
1865
+ *
1866
+ * @returns true if mouse is hovering a display, false otherwise
1867
+ *
1868
+ * @example
1869
+ * ```typescript
1870
+ * if (user.getIsMouseOnADisplay()) {
1871
+ * const info = user.getMouseDisplayInfo();
1872
+ * console.log(`Hovering display ${info.displayId}`);
1873
+ * }
1874
+ * ```
1875
+ */
1876
+ getIsMouseOnADisplay(): boolean;
1877
+ /**
1878
+ * Gets the ID of the display currently hovered by the mouse
1879
+ *
1880
+ * @returns Display ID (0-255) or null if no display is hovered
1881
+ *
1882
+ * @example
1883
+ * ```typescript
1884
+ * const hoveredDisplayId = user.getMouseDisplayHover();
1885
+ * if (hoveredDisplayId !== null) {
1886
+ * console.log(`Hovering display ${hoveredDisplayId}`);
1887
+ * }
1888
+ * ```
1889
+ */
1890
+ getMouseDisplayHover(): number | null;
1891
+ /**
1892
+ * Finds a layer by its ID
1893
+ *
1894
+ * @param id - ID of the layer to find
1895
+ * @returns Found layer or null
1896
+ * @internal - Used for UpdatePacket reconstruction
1897
+ */
1898
+ private findLayerById;
1899
+ /**
1900
+ * Applies an UpdatePacket to this user (CLIENT-SIDE RECONSTRUCTION)
1901
+ *
1902
+ * This method reconstructs the user state from an UpdatePacket
1903
+ * received from the server. It updates displays (viewports) and layers
1904
+ * incrementally according to updateFlags.
1905
+ *
1906
+ * IMPORTANT: This method must be called in CLIENT mode only.
1907
+ * The server generates packets, the client applies them.
1908
+ *
1909
+ * @param packet - Decoded UpdatePacket received from server
1910
+ *
1911
+ * @example
1912
+ * ```typescript
1913
+ * // Client side (ClientRuntime)
1914
+ * const decoder = new UpdatePacketDecoder();
1915
+ * const packet = decoder.decode(receivedBuffer);
1916
+ * user.applyUpdate(packet);
1917
+ * ```
1918
+ */
1919
+ applyUpdate(packet: UpdatePacket): void;
1920
+ /**
1921
+ * Updates displays from an UpdatePacket
1922
+ *
1923
+ * Adjusts the number of displays and updates their origins/sizes.
1924
+ *
1925
+ * @private
1926
+ * @param networkDisplays - Displays from packet
1927
+ */
1928
+ private updateDisplaysFromPacket;
1929
+ /**
1930
+ * Updates layers from an UpdatePacket (INCREMENTAL LOGIC)
1931
+ *
1932
+ * This method reconstructs layers according to updateFlags:
1933
+ * - 0x01: Update origin
1934
+ * - 0x02: Update z-order
1935
+ * - 0x04: Update orders (replace or add according to SET/ADD flag)
1936
+ *
1937
+ * If a layer doesn't exist yet, it is created automatically.
1938
+ *
1939
+ * IMPORTANT: In client mode, orders are automatically rasterized
1940
+ * when calling setOrders() / addOrders().
1941
+ *
1942
+ * @private
1943
+ * @param networkLayers - Layers from packet with their updateFlags
1944
+ */
1945
+ private updateLayersFromPacket;
1946
+ /**
1947
+ * Defines an axis binding
1948
+ *
1949
+ * Associates a bindingId (0-255) with an axis name and its physical sources.
1950
+ * The client will receive this mapping via a JSON LoadPacket.
1951
+ *
1952
+ * Sources are summed on the server side after receiving raw values.
1953
+ *
1954
+ * @param bindingId - Unique axis ID (0-255)
1955
+ * @param name - Axis name (e.g., "MoveHorizontal", "CameraX")
1956
+ * @param sources - Physical sources (keyboard, gamepad, mouse, gyro, touch)
1957
+ * @param min - Minimum value (default: -1.0)
1958
+ * @param max - Maximum value (default: +1.0)
1959
+ * @param defaultValue - Default value (default: 0.0)
1960
+ *
1961
+ * @example
1962
+ * ```typescript
1963
+ * import { InputDeviceType, KeyboardInput, GamepadInput } from '@utsp/types';
1964
+ *
1965
+ * user.defineAxisBinding(0, "MoveHorizontal", [
1966
+ * { sourceId: 0, type: InputDeviceType.Keyboard, negativeKey: KeyboardInput.ArrowLeft, positiveKey: KeyboardInput.ArrowRight },
1967
+ * { sourceId: 1, type: InputDeviceType.Gamepad, gamepadIndex: 0, axis: GamepadInput.LeftStickX }
1968
+ * ], -1.0, 1.0, 0.0);
1969
+ * ```
1970
+ */
1971
+ defineAxisBinding(bindingId: number, name: string, sources?: AxisSource[], min?: number, max?: number, defaultValue?: number): void;
1972
+ /**
1973
+ * Defines a button binding
1974
+ *
1975
+ * Associates a bindingId (0-255) with a button name and its physical sources.
1976
+ * Sources use OR logic (at least one pressed source = button pressed).
1977
+ *
1978
+ * @param bindingId - Unique button ID (0-255)
1979
+ * @param name - Button name (e.g., "Jump", "Attack")
1980
+ * @param sources - Physical sources (keyboard, gamepad, mouse, touch)
1981
+ * @param defaultValue - Default value (default: false)
1982
+ *
1983
+ * @example
1984
+ * ```typescript
1985
+ * import { InputDeviceType, KeyboardInput, GamepadInput } from '@utsp/types';
1986
+ *
1987
+ * user.defineButtonBinding(0, "Jump", [
1988
+ * { sourceId: 0, type: InputDeviceType.Keyboard, key: KeyboardInput.Space },
1989
+ * { sourceId: 1, type: InputDeviceType.Gamepad, gamepadIndex: 0, button: GamepadInput.A }
1990
+ * ], false);
1991
+ * ```
1992
+ */
1993
+ defineButtonBinding(bindingId: number, name: string, sources?: ButtonSource[], defaultValue?: boolean): void;
1994
+ /**
1995
+ * Defines multiple axis bindings at once
1996
+ *
1997
+ * @param axes - Array of axis definitions
1998
+ *
1999
+ * @example
2000
+ * ```typescript
2001
+ * import { InputDeviceType, KeyboardInput } from '@utsp/types';
2002
+ *
2003
+ * user.defineAxisBindings([
2004
+ * {
2005
+ * bindingId: 0,
2006
+ * name: "MoveHorizontal",
2007
+ * sources: [
2008
+ * { sourceId: 0, type: InputDeviceType.Keyboard, negativeKey: KeyboardInput.ArrowLeft, positiveKey: KeyboardInput.ArrowRight }
2009
+ * ]
2010
+ * },
2011
+ * { bindingId: 1, name: "MoveVertical", sources: [] },
2012
+ * ]);
2013
+ * ```
2014
+ */
2015
+ defineAxisBindings(axes: Array<{
2016
+ bindingId: number;
2017
+ name: string;
2018
+ sources?: AxisSource[];
2019
+ min?: number;
2020
+ max?: number;
2021
+ defaultValue?: number;
2022
+ }>): void;
2023
+ /**
2024
+ * Defines multiple button bindings at once
2025
+ *
2026
+ * @param buttons - Array of button definitions
2027
+ *
2028
+ * @example
2029
+ * ```typescript
2030
+ * import { InputDeviceType, KeyboardInput } from '@utsp/types';
2031
+ *
2032
+ * user.defineButtonBindings([
2033
+ * {
2034
+ * bindingId: 0,
2035
+ * name: "Jump",
2036
+ * sources: [
2037
+ * { sourceId: 0, type: InputDeviceType.Keyboard, key: KeyboardInput.Space }
2038
+ * ]
2039
+ * },
2040
+ * { bindingId: 1, name: "Attack", sources: [] },
2041
+ * ]);
2042
+ * ```
2043
+ */
2044
+ defineButtonBindings(buttons: Array<{
2045
+ bindingId: number;
2046
+ name: string;
2047
+ sources?: ButtonSource[];
2048
+ defaultValue?: boolean;
2049
+ }>): void;
2050
+ /**
2051
+ * Generates the JSON LoadPacket containing input bindings
2052
+ *
2053
+ * This packet must be sent to the client via the Load channel to indicate
2054
+ * which axes and buttons it should capture and send back.
2055
+ *
2056
+ * Format: JSON string (not binary for now as it's sent rarely)
2057
+ *
2058
+ * @returns JSON LoadPacket as string
2059
+ *
2060
+ * @example
2061
+ * ```typescript
2062
+ * // Server: Define bindings
2063
+ * user.defineAxisBinding(0, "MoveHorizontal");
2064
+ * user.defineAxisBinding(1, "MoveVertical");
2065
+ * user.defineButtonBinding(0, "Jump");
2066
+ *
2067
+ * // Generate and send to client
2068
+ * const packet = user.getInputBindingsLoadPacket();
2069
+ * websocket.send(packet);
2070
+ * ```
2071
+ */
2072
+ getInputBindingsLoadPacket(): string;
2073
+ /**
2074
+ * Applies Input Bindings received from server (CLIENT-SIDE)
2075
+ *
2076
+ * This method parses the bindings JSON and configures them in the registry.
2077
+ * The client can then capture inputs and send them compressed.
2078
+ *
2079
+ * @param json - JSON string of bindings from getInputBindingsLoadPacket()
2080
+ *
2081
+ * @example
2082
+ * ```typescript
2083
+ * // Client side
2084
+ * websocket.on('input-bindings', (json: string) => {
2085
+ * user.applyInputBindingsLoadPacket(json);
2086
+ * console.log('Input bindings configured');
2087
+ * });
2088
+ * ```
2089
+ */
2090
+ applyInputBindingsLoadPacket(json: string): void;
2091
+ /**
2092
+ * Gets this user's InputBindingRegistry
2093
+ *
2094
+ * @returns InputBindingRegistry instance
2095
+ *
2096
+ * @example
2097
+ * ```typescript
2098
+ * const registry = user.getInputBindingRegistry();
2099
+ * console.log(registry.toString());
2100
+ * console.log(`Axes: ${registry.getAxisCount()}`);
2101
+ * console.log(`Buttons: ${registry.getButtonCount()}`);
2102
+ * ```
2103
+ */
2104
+ getInputBindingRegistry(): InputBindingRegistry;
2105
+ /**
2106
+ * Gets the UserStats object to access this user's statistics
2107
+ *
2108
+ * @returns The user's UserStats instance
2109
+ *
2110
+ * @example
2111
+ * ```typescript
2112
+ * // Enable stats
2113
+ * user.getStats().setEnabled(true);
2114
+ *
2115
+ * // After a tick...
2116
+ * const stats = user.getStats();
2117
+ * console.log(`User ${stats.userName}:`);
2118
+ * console.log(` Displays: ${stats.displayCount}`);
2119
+ * console.log(` Layers: ${stats.visibleLayers}/${stats.totalLayers}`);
2120
+ * console.log(` Orders: ${stats.totalOrders}`);
2121
+ * console.log(` Packet size: ${stats.totalPacketSize} bytes`);
2122
+ * ```
2123
+ */
2124
+ getStats(): UserStats;
2125
+ /**
2126
+ * Gets the bindingId of an axis from its name
2127
+ *
2128
+ * @param name - Axis name
2129
+ * @returns bindingId or null if not found
2130
+ */
2131
+ getAxisBindingId(name: string): number | null;
2132
+ /**
2133
+ * Gets the bindingId of a button from its name
2134
+ *
2135
+ * @param name - Button name
2136
+ * @returns bindingId or null if not found
2137
+ */
2138
+ getButtonBindingId(name: string): number | null;
2139
+ /**
2140
+ * Decodes and applies a compressed input buffer received from client (format v3)
2141
+ *
2142
+ * This method:
2143
+ * 1. Decodes the binary buffer (v3 MINIMAL format - without counts)
2144
+ * 2. Stores values in axes/buttons Maps
2145
+ * 3. Updates mouse position + displayId
2146
+ *
2147
+ * Format v3: Buffer does NOT contain axes/buttons counts.
2148
+ * Server uses its internal registry to know the expected structure.
2149
+ *
2150
+ * IMPORTANT: Client and server must have the SAME bindings defined!
2151
+ *
2152
+ * @param buffer - Encoded buffer received from client via WebSocket
2153
+ *
2154
+ * @example
2155
+ * ```typescript
2156
+ * // Server receives buffer from client
2157
+ * websocket.on('message', (data) => {
2158
+ * const buffer = new Uint8Array(data);
2159
+ * user.decodeAndApplyCompressedInput(buffer);
2160
+ *
2161
+ * // Values are now available
2162
+ * const move = user.getAxis("MoveHorizontal");
2163
+ * if (user.getButton("Jump")) { ... }
2164
+ * });
2165
+ * ```
2166
+ */
2167
+ decodeAndApplyCompressedInput(buffer: Uint8Array): void;
2168
+ }
2169
+
2170
+ /**
2171
+ * UTSP engine performance statistics
2172
+ * Collects metrics per tick for analysis and optimization
2173
+ */
2174
+ interface TickStats {
2175
+ tick: number;
2176
+ timestamp: number;
2177
+ totalOrders: number;
2178
+ ordersByLayer: Map<number, number>;
2179
+ ordersByType: Map<number, number>;
2180
+ totalLayers: number;
2181
+ visibleLayers: number;
2182
+ layersPerDisplay: Map<number, number>;
2183
+ updatePacketSize: number;
2184
+ displayHeaderSize: number;
2185
+ layerHeaderSize: number;
2186
+ orderDataSize: number;
2187
+ compressionRatio: number;
2188
+ totalCells: number;
2189
+ nonEmptyCells: number;
2190
+ cellsWithBackground: number;
2191
+ rasterizationTimeMs: number;
2192
+ encodingTimeMs: number;
2193
+ }
2194
+ declare class CoreStats {
2195
+ private enabled;
2196
+ private currentStats;
2197
+ /**
2198
+ * Enables or disables statistics collection
2199
+ */
2200
+ setEnabled(enabled: boolean): void;
2201
+ isEnabled(): boolean;
2202
+ /** Current tick number (0 if no stats) */
2203
+ get tick(): number;
2204
+ /** Current tick timestamp */
2205
+ get timestamp(): number;
2206
+ /** Total orders processed this tick */
2207
+ get totalOrders(): number;
2208
+ /** Orders per layer ID */
2209
+ get ordersByLayer(): Map<number, number>;
2210
+ /** Orders per type (0x01, 0x02, etc.) */
2211
+ get ordersByType(): Map<number, number>;
2212
+ /** Nombre total de layers */
2213
+ get totalLayers(): number;
2214
+ /** Layers visibles (qui intersectent un display) */
2215
+ get visibleLayers(): number;
2216
+ /** Layers visibles par display ID */
2217
+ get layersPerDisplay(): Map<number, number>;
2218
+ /** Total encoded UpdatePacket size (bytes) */
2219
+ get updatePacketSize(): number;
2220
+ /** Display header size (bytes) */
2221
+ get displayHeaderSize(): number;
2222
+ /** Layer header size (bytes) */
2223
+ get layerHeaderSize(): number;
2224
+ /** Order data size (bytes) */
2225
+ get orderDataSize(): number;
2226
+ /** Estimated gzip compression ratio (0.25 = 75% compression) */
2227
+ get compressionRatio(): number;
2228
+ /** Estimated size after gzip compression (bytes) */
2229
+ get compressedPacketSize(): number;
2230
+ /** Total number of rendered cells */
2231
+ get totalCells(): number;
2232
+ /** Non-empty cells (char !== ' ') */
2233
+ get nonEmptyCells(): number;
2234
+ /** Cells with colored background */
2235
+ get cellsWithBackground(): number;
2236
+ /** Rasterization time (ms) */
2237
+ get rasterizationTimeMs(): number;
2238
+ /** Encoding time (ms) */
2239
+ get encodingTimeMs(): number;
2240
+ /** Static/dynamic split information for the last processed user */
2241
+ get packetSplit(): {
2242
+ userId: string;
2243
+ displayCount: number;
2244
+ staticLayerCount: number;
2245
+ dynamicLayerCount: number;
2246
+ } | undefined;
2247
+ /** Details of all layers processed this tick */
2248
+ get layerDetails(): Array<{
2249
+ layerId: number;
2250
+ isStatic: boolean;
2251
+ orderCount: number;
2252
+ updateFlags: number;
2253
+ }> | undefined;
2254
+ /**
2255
+ * Starts collecting statistics for a new tick
2256
+ */
2257
+ startTick(tickNumber: number): void;
2258
+ /**
2259
+ * Records orders for a layer
2260
+ */
2261
+ recordLayerOrders(layerId: number, orders: any[]): void;
2262
+ /**
2263
+ * Records the number of layers
2264
+ */
2265
+ recordLayers(total: number, visible: number): void;
2266
+ /**
2267
+ * Records visible layers per display
2268
+ */
2269
+ recordDisplayLayers(displayId: number, layerCount: number): void;
2270
+ /**
2271
+ * Records the encoded UpdatePacket size
2272
+ */
2273
+ recordUpdatePacketSize(totalBytes: number, displayHeaderBytes: number, layerHeaderBytes: number, orderDataBytes: number): void;
2274
+ /**
2275
+ * Records static/dynamic split information
2276
+ */
2277
+ recordPacketSplit(userId: string, displayCount: number, staticLayerCount: number, dynamicLayerCount: number): void;
2278
+ /**
2279
+ * Records individual layer information
2280
+ */
2281
+ recordLayerInfo(layerId: number, isStatic: boolean, orderCount: number, updateFlags: number): void;
2282
+ /**
2283
+ * Records rendered cell statistics
2284
+ */
2285
+ recordRenderStats(totalCells: number, nonEmptyCells: number, cellsWithBg: number): void;
2286
+ /**
2287
+ * Records rasterization time
2288
+ */
2289
+ recordRasterizationTime(timeMs: number): void;
2290
+ /**
2291
+ * Records encoding time
2292
+ */
2293
+ recordEncodingTime(timeMs: number): void;
2294
+ /**
2295
+ * Finalizes stats for the current tick
2296
+ * Stats remain available until the next startTick()
2297
+ */
2298
+ endTick(): void;
2299
+ /**
2300
+ * Resets statistics
2301
+ */
2302
+ reset(): void;
2303
+ }
2304
+
2305
+ /**
2306
+ * WebFontRegistry - Registry for CSS-based fonts
2307
+ */
2308
+
2309
+ /**
2310
+ * Registry for managing WebFont instances
2311
+ * Each font is identified by a unique fontId (0-255)
2312
+ */
2313
+ declare class WebFontRegistry {
2314
+ private fonts;
2315
+ /**
2316
+ * Load a new WebFont into the registry
2317
+ * @param fontId Unique font identifier (0-255)
2318
+ * @param config WebFont configuration
2319
+ * @throws Error if font ID already exists
2320
+ */
2321
+ loadFont(fontId: number, config: WebFontConfig): void;
2322
+ /**
2323
+ * Get a WebFont by ID
2324
+ * @param fontId Font identifier
2325
+ * @returns WebFont instance or undefined if not found
2326
+ */
2327
+ getFont(fontId: number): WebFont | undefined;
2328
+ /**
2329
+ * Check if a font exists in the registry
2330
+ * @param fontId Font identifier
2331
+ * @returns true if font exists, false otherwise
2332
+ */
2333
+ hasFont(fontId: number): boolean;
2334
+ /**
2335
+ * Remove a WebFont from the registry
2336
+ * @param fontId Font identifier
2337
+ * @returns true if font was removed, false if not found
2338
+ */
2339
+ unloadFont(fontId: number): boolean;
2340
+ /**
2341
+ * Remove all fonts from the registry
2342
+ */
2343
+ clearFonts(): void;
2344
+ /**
2345
+ * Get all font IDs in the registry
2346
+ * @returns Array of font IDs, sorted in ascending order
2347
+ */
2348
+ getFontIds(): number[];
2349
+ /**
2350
+ * Get all WebFont instances in the registry
2351
+ * @returns Array of WebFont instances, sorted by font ID
2352
+ */
2353
+ getAllFonts(): WebFont[];
2354
+ /**
2355
+ * Get the number of fonts in the registry
2356
+ * @returns Number of fonts
2357
+ */
2358
+ getFontCount(): number;
2359
+ }
2360
+
2361
+ /**
2362
+ * BitmapFontRegistry - Registry for pixel-based bitmap fonts
2363
+ */
2364
+
2365
+ /**
2366
+ * Registry for managing BitmapFont instances
2367
+ * Each font is identified by a unique fontId (0-255)
2368
+ * BitmapFonts correspond to LoadType 0x04 (Charset) in UTSP protocol
2369
+ */
2370
+ declare class BitmapFontRegistry {
2371
+ private fonts;
2372
+ /**
2373
+ * Load a new BitmapFont into the registry
2374
+ * @param fontId Unique font identifier (0-255)
2375
+ * @param config BitmapFont configuration
2376
+ * @throws Error if font ID already exists
2377
+ */
2378
+ loadFont(fontId: number, config: BitmapFontConfig): void;
2379
+ /**
2380
+ * Get a BitmapFont by ID
2381
+ * @param fontId Font identifier
2382
+ * @returns BitmapFont instance or undefined if not found
2383
+ */
2384
+ getFont(fontId: number): BitmapFont | undefined;
2385
+ /**
2386
+ * Check if a font exists in the registry
2387
+ * @param fontId Font identifier
2388
+ * @returns true if font exists, false otherwise
2389
+ */
2390
+ hasFont(fontId: number): boolean;
2391
+ /**
2392
+ * Remove a BitmapFont from the registry
2393
+ * @param fontId Font identifier
2394
+ * @returns true if font was removed, false if not found
2395
+ */
2396
+ unloadFont(fontId: number): boolean;
2397
+ /**
2398
+ * Remove all fonts from the registry
2399
+ */
2400
+ clearFonts(): void;
2401
+ /**
2402
+ * Get all font IDs in the registry
2403
+ * @returns Array of font IDs, sorted in ascending order
2404
+ */
2405
+ getFontIds(): number[];
2406
+ /**
2407
+ * Get all BitmapFont instances in the registry
2408
+ * @returns Array of BitmapFont instances, sorted by font ID
2409
+ */
2410
+ getAllFonts(): BitmapFont[];
2411
+ /**
2412
+ * Get the number of fonts in the registry
2413
+ * @returns Number of fonts
2414
+ */
2415
+ getFontCount(): number;
2416
+ }
2417
+
2418
+ /**
2419
+ * Engine execution context type
2420
+ */
2421
+ type CoreMode = 'server' | 'client' | 'standalone';
2422
+ /**
2423
+ * UTSP engine configuration options
2424
+ */
2425
+ interface CoreOptions {
2426
+ /**
2427
+ * Execution mode: "server", "client" or "standalone"
2428
+ *
2429
+ * - "server": Server-side engine
2430
+ * → Marks layers as dirty (change tracking)
2431
+ * → Does NOT rasterize (CPU savings, client-side rasterization)
2432
+ * → Manages multiple users (limited by maxUsers)
2433
+ *
2434
+ * - "client": Client-side engine
2435
+ * → Does NOT mark as dirty (no tracking needed)
2436
+ * → Rasterizes immediately (local rendering for display)
2437
+ * → Manages ONE local user only (fixed limit)
2438
+ *
2439
+ * - "standalone": Standalone engine (preview, tests, debug)
2440
+ * → Marks as dirty AND rasterizes
2441
+ * → Manages multiple users
2442
+ * → Useful for getRenderState(), tests, simulations
2443
+ *
2444
+ * @default "server"
2445
+ */
2446
+ mode?: CoreMode;
2447
+ /**
2448
+ * Maximum number of users allowed (server mode only)
2449
+ *
2450
+ * In client mode, this option is ignored (fixed limit of 1 user)
2451
+ *
2452
+ * @default 64
2453
+ */
2454
+ maxUsers?: number;
2455
+ /**
2456
+ * Enable strict validations (useful in development)
2457
+ *
2458
+ * @default false
2459
+ */
2460
+ strictMode?: boolean;
2461
+ }
2462
+ /**
2463
+ * Core - UTSP data management engine
2464
+ *
2465
+ * Passive API: no loop, no network, no callbacks.
2466
+ * Parent package (utsp-server, utsp-client) controls execution flow.
2467
+ *
2468
+ * Responsibilities:
2469
+ * - Store and manage users
2470
+ * - Provide data manipulation methods
2471
+ * - Pure and predictable API
2472
+ *
2473
+ * What the core does NOT do:
2474
+ * - No tick loop (server/client manages it)
2475
+ * - No network (server/client manages it)
2476
+ * - No callbacks/events (core is a passive slave)
2477
+ */
2478
+ declare class Core {
2479
+ private users;
2480
+ private readonly mode;
2481
+ private readonly maxUsers;
2482
+ private readonly strictMode;
2483
+ private currentTick;
2484
+ private encoder;
2485
+ private displayRasterizer;
2486
+ private colorPalette;
2487
+ private static readonly ANSI_VGA_COLORS;
2488
+ private stats;
2489
+ private spriteRegistry;
2490
+ private webFontRegistry;
2491
+ private bitmapFontRegistry;
2492
+ private activeWebFontId;
2493
+ private _renderCallCount;
2494
+ private onPaletteChangedCallback?;
2495
+ private onBitmapFontChangedCallback?;
2496
+ /**
2497
+ * Creates a new UTSP engine instance
2498
+ *
2499
+ * @param options - Configuration options
2500
+ *
2501
+ * @example
2502
+ * ```typescript
2503
+ * // Server with 100 max users
2504
+ * const engine = new Core({
2505
+ * mode: "server",
2506
+ * maxUsers: 100
2507
+ * });
2508
+ *
2509
+ * // Simple client
2510
+ * const engine = new Core({
2511
+ * mode: "client"
2512
+ * });
2513
+ *
2514
+ * // Default mode (server)
2515
+ * const engine = new Core();
2516
+ * ```
2517
+ */
2518
+ constructor(options?: CoreOptions);
2519
+ /**
2520
+ * Initialize default color palette (15 reserved UI colors + free colors)
2521
+ * @private
2522
+ */
2523
+ private initializeDefaultPalette;
2524
+ /**
2525
+ * Returns current execution mode
2526
+ *
2527
+ * @returns "server", "client" or "standalone"
2528
+ */
2529
+ getMode(): CoreMode;
2530
+ /**
2531
+ * Returns maximum allowed users
2532
+ *
2533
+ * @returns Max number of users
2534
+ */
2535
+ getMaxUsers(): number;
2536
+ /**
2537
+ * Indicates if strict mode is enabled
2538
+ *
2539
+ * @returns true if strict mode is enabled
2540
+ */
2541
+ isStrictMode(): boolean;
2542
+ /**
2543
+ * Checks if core is in server mode
2544
+ *
2545
+ * @returns true if server mode
2546
+ */
2547
+ isServer(): boolean;
2548
+ /**
2549
+ * Checks if core is in client mode
2550
+ *
2551
+ * @returns true if client mode
2552
+ */
2553
+ isClient(): boolean;
2554
+ /**
2555
+ * Checks if core is in standalone mode
2556
+ *
2557
+ * @returns true if standalone mode
2558
+ */
2559
+ isStandalone(): boolean;
2560
+ /**
2561
+ * Creates a new user in the engine
2562
+ *
2563
+ * @param id - Unique user ID (string)
2564
+ * @param name - User name
2565
+ * @returns Created user
2566
+ * @throws Error if ID already exists
2567
+ *
2568
+ * @example
2569
+ * ```typescript
2570
+ * const engine = new Core();
2571
+ * const user = engine.createUser("user1", "Alice");
2572
+ * ```
2573
+ */
2574
+ createUser(id: string, name: string): User;
2575
+ /**
2576
+ * Gets a user by ID
2577
+ *
2578
+ * @param id - User ID (string)
2579
+ * @returns The user or null if not found
2580
+ *
2581
+ * @example
2582
+ * ```typescript
2583
+ * const user = engine.getUser("user1");
2584
+ * if (user) {
2585
+ * console.log(user.name);
2586
+ * }
2587
+ * ```
2588
+ */
2589
+ getUser(id: string): User | null;
2590
+ /**
2591
+ * Gets all users from the engine
2592
+ *
2593
+ * @returns Array of all users
2594
+ *
2595
+ * @example
2596
+ * ```typescript
2597
+ * for (const user of engine.getUsers()) {
2598
+ * console.log(`User ${user.id}: ${user.name}`);
2599
+ * }
2600
+ * ```
2601
+ */
2602
+ getUsers(): User[];
2603
+ /**
2604
+ * Checks if a user exists
2605
+ *
2606
+ * @param id - User ID (string)
2607
+ * @returns true if user exists
2608
+ *
2609
+ * @example
2610
+ * ```typescript
2611
+ * if (engine.hasUser("user1")) {
2612
+ * console.log("User exists");
2613
+ * }
2614
+ * ```
2615
+ */
2616
+ hasUser(id: string): boolean;
2617
+ /**
2618
+ * Removes a user from the engine
2619
+ *
2620
+ * @param id - User ID to remove (string)
2621
+ * @returns true if user was removed, false if not found
2622
+ *
2623
+ * @example
2624
+ * ```typescript
2625
+ * const removed = engine.removeUser("user1");
2626
+ * if (removed) {
2627
+ * console.log("User removed successfully");
2628
+ * }
2629
+ * ```
2630
+ */
2631
+ removeUser(id: string): boolean;
2632
+ /**
2633
+ * Gets total number of users
2634
+ *
2635
+ * @returns Number of users
2636
+ *
2637
+ * @example
2638
+ * ```typescript
2639
+ * console.log(`${engine.getUserCount()} users connected`);
2640
+ * ```
2641
+ */
2642
+ getUserCount(): number;
2643
+ /**
2644
+ * Removes all users from the engine
2645
+ *
2646
+ * Useful for complete reset or cleanup before server shutdown
2647
+ *
2648
+ * @example
2649
+ * ```typescript
2650
+ * engine.clearAllUsers(); // All users removed
2651
+ * console.log(engine.getUserCount()); // 0
2652
+ * ```
2653
+ */
2654
+ clearAllUsers(): void;
2655
+ /**
2656
+ * Sets a color in the palette
2657
+ *
2658
+ * 🎨 RESERVED COLORS:
2659
+ * - ColorIDs 240-254: Standard UI palette (CANNOT be modified)
2660
+ * - ColorID 255: Skip/transparent color (CANNOT be modified)
2661
+ * - ColorIDs 0-239: Free for application use (240 colors available)
2662
+ *
2663
+ * @param colorId - Color ID (0-239 only, 240-255 are reserved)
2664
+ * @param r - Red component (0-255)
2665
+ * @param g - Green component (0-255)
2666
+ * @param b - Blue component (0-255)
2667
+ * @param a - Alpha component (0-255, default: 255 = opaque)
2668
+ * @param e - Emission component (0-255, default: 0 = no emission)
2669
+ * @throws Error if colorId is reserved (240-255) or out of bounds
2670
+ *
2671
+ * @example
2672
+ * ```typescript
2673
+ * // ✅ Set a custom color in free range
2674
+ * engine.setColor(0, 255, 0, 0, 255, 0);
2675
+ *
2676
+ * // ✅ Set a semi-transparent color
2677
+ * engine.setColor(1, 0, 255, 0, 128, 0);
2678
+ *
2679
+ * // ✅ Set an emissive color (bloom/glow)
2680
+ * engine.setColor(2, 0, 255, 255, 255, 255);
2681
+ *
2682
+ * // ❌ ERROR: ColorIDs 240-254 are reserved (UI palette)
2683
+ * // engine.setColor(245, 255, 0, 0); // Throws error
2684
+ *
2685
+ * // ❌ ERROR: ColorID 255 is reserved (skip color)
2686
+ * // engine.setColor(255, 255, 255, 255); // Throws error
2687
+ * ```
2688
+ */
2689
+ setColor(colorId: number, r: number, g: number, b: number, a?: number, e?: number): void;
2690
+ /**
2691
+ * Register a callback to be notified when palette changes
2692
+ *
2693
+ * Use this to update the renderer when palette is modified via loadPalette() or setColor().
2694
+ *
2695
+ * @param callback - Function called with the new palette
2696
+ *
2697
+ * @example
2698
+ * ```typescript
2699
+ * core.onPaletteChanged((palette) => {
2700
+ * const paletteArray = Array.from(palette.values());
2701
+ * renderer.setPalette(paletteArray);
2702
+ * });
2703
+ * ```
2704
+ */
2705
+ onPaletteChanged(callback: (palette: Map<number, {
2706
+ r: number;
2707
+ g: number;
2708
+ b: number;
2709
+ a: number;
2710
+ e: number;
2711
+ }>) => void): void;
2712
+ /**
2713
+ * Register a callback to be notified when a bitmap font is loaded.
2714
+ *
2715
+ * Use this to update the renderer when a font is loaded via loadBitmapFontById().
2716
+ *
2717
+ * @param callback - Function called with the fontId
2718
+ *
2719
+ * @example
2720
+ * ```typescript
2721
+ * core.onBitmapFontChanged((fontId) => {
2722
+ * const font = core.getBitmapFont(fontId);
2723
+ * if (font) renderer.uploadFontToGPU(font);
2724
+ * });
2725
+ * ```
2726
+ */
2727
+ onBitmapFontChanged(callback: (fontId: number) => void): void;
2728
+ /**
2729
+ * Gets a color from the palette
2730
+ *
2731
+ * @param colorId - Color ID (0-255)
2732
+ * @returns RGBA+E color or null if ID doesn't exist
2733
+ *
2734
+ * @example
2735
+ * ```typescript
2736
+ * const color = engine.getColor(16);
2737
+ * if (color) {
2738
+ * console.log(`RGB: ${color.r}, ${color.g}, ${color.b}`);
2739
+ * console.log(`Emission: ${color.e}`);
2740
+ * }
2741
+ * ```
2742
+ */
2743
+ getColor(colorId: number): {
2744
+ r: number;
2745
+ g: number;
2746
+ b: number;
2747
+ a: number;
2748
+ e: number;
2749
+ } | null;
2750
+ /**
2751
+ * Loads a complete color palette
2752
+ *
2753
+ * Replaces existing colors for the specified IDs.
2754
+ *
2755
+ * 🎨 RESERVED COLORS (automatically forced):
2756
+ * - ColorIDs 240-254: Standard UI palette (will be overridden even if provided)
2757
+ * - ColorID 255: Skip/transparent color (will be overridden even if provided)
2758
+ * - ColorIDs 0-239: Free for application use
2759
+ *
2760
+ * @param colors - Array of colors to load (any ColorIDs 0-255)
2761
+ *
2762
+ * @example
2763
+ * ```typescript
2764
+ * engine.loadPalette([
2765
+ * { colorId: 0, r: 255, g: 0, b: 0, a: 255, e: 0 }, // ✅ Custom color (free range)
2766
+ * { colorId: 1, r: 255, g: 255, b: 255, a: 255, e: 0 }, // ✅ Custom white
2767
+ * { colorId: 2, r: 255, g: 0, b: 0, a: 255, e: 0 }, // ✅ Custom red
2768
+ * { colorId: 3, r: 0, g: 255, b: 255, a: 255, e: 255 }, // ✅ Bright cyan
2769
+ * { colorId: 250, r: 255, g: 0, b: 0, a: 255, e: 0 }, // ⚠️ Will be ignored (reserved)
2770
+ * ]);
2771
+ * // ColorIDs 240-254 and 255 will be forced to standard values
2772
+ * ```
2773
+ */
2774
+ loadPalette(colors: Array<{
2775
+ colorId: number;
2776
+ r: number;
2777
+ g: number;
2778
+ b: number;
2779
+ a: number;
2780
+ e?: number;
2781
+ }>): void;
2782
+ /**
2783
+ * Gets the entire color palette
2784
+ *
2785
+ * @returns Map of colors by ID
2786
+ *
2787
+ * @example
2788
+ * ```typescript
2789
+ * const palette = engine.getPalette();
2790
+ * palette.forEach((color, id) => {
2791
+ * console.log(`Color ${id}: rgba(${color.r}, ${color.g}, ${color.b}, ${color.a}) emission: ${color.e}`);
2792
+ * });
2793
+ * ```
2794
+ */
2795
+ getPalette(): Map<number, {
2796
+ r: number;
2797
+ g: number;
2798
+ b: number;
2799
+ a: number;
2800
+ e: number;
2801
+ }>;
2802
+ /**
2803
+ * Resets palette to default VGA colors
2804
+ *
2805
+ * @example
2806
+ * ```typescript
2807
+ * engine.resetPalette(); // Back to VGA 16 colors palette
2808
+ * ```
2809
+ */
2810
+ resetPalette(): void;
2811
+ /**
2812
+ * Converts a color ID to a CSS rgba() string
2813
+ *
2814
+ * Useful for HTML/Canvas rendering
2815
+ *
2816
+ * @param colorId - Color ID (0-255)
2817
+ * @returns CSS string "rgba(r, g, b, a)" or null if ID doesn't exist
2818
+ *
2819
+ * @example
2820
+ * ```typescript
2821
+ * const cssColor = engine.getColorCSS(16);
2822
+ * if (cssColor) {
2823
+ * ctx.fillStyle = cssColor; // "rgba(255, 0, 0, 1)"
2824
+ * }
2825
+ * ```
2826
+ */
2827
+ getColorCSS(colorId: number): string | null;
2828
+ /**
2829
+ * Gets all user IDs
2830
+ *
2831
+ * @returns Array of IDs (strings)
2832
+ *
2833
+ * @example
2834
+ * ```typescript
2835
+ * const ids = engine.getUserIds(); // ["user1", "user2", ...]
2836
+ * ```
2837
+ */
2838
+ getUserIds(): string[];
2839
+ /**
2840
+ * Iterates over all users with a callback
2841
+ *
2842
+ * @param callback - Function called for each user
2843
+ *
2844
+ * @example
2845
+ * ```typescript
2846
+ * engine.forEachUser((user) => {
2847
+ * user.displays.forEach(d => d.clear());
2848
+ * });
2849
+ * ```
2850
+ */
2851
+ forEachUser(callback: (user: User) => void): void;
2852
+ /**
2853
+ * Filters users according to a predicate
2854
+ *
2855
+ * @param predicate - Filter function
2856
+ * @returns Array of matching users
2857
+ *
2858
+ * @example
2859
+ * ```typescript
2860
+ * const activeUsers = engine.filterUsers(user => user.displays.length > 0);
2861
+ * ```
2862
+ */
2863
+ filterUsers(predicate: (user: User) => boolean): User[];
2864
+ /**
2865
+ * Finds a user according to a predicate
2866
+ *
2867
+ * @param predicate - Search function
2868
+ * @returns First matching user or undefined
2869
+ *
2870
+ * @example
2871
+ * ```typescript
2872
+ * const alice = engine.findUser(user => user.name === "Alice");
2873
+ * ```
2874
+ */
2875
+ findUser(predicate: (user: User) => boolean): User | undefined;
2876
+ /**
2877
+ * Gets current tick number
2878
+ *
2879
+ * @returns Current tick number
2880
+ *
2881
+ * @example
2882
+ * ```typescript
2883
+ * console.log(`Current tick: ${engine.getCurrentTick()}`);
2884
+ * ```
2885
+ */
2886
+ getCurrentTick(): number;
2887
+ /**
2888
+ * Gets CoreStats object to access performance statistics
2889
+ *
2890
+ * @returns The engine's CoreStats instance
2891
+ *
2892
+ * @example
2893
+ * ```typescript
2894
+ * // Enable stats
2895
+ * engine.getStats().setEnabled(true);
2896
+ *
2897
+ * // After a few ticks...
2898
+ * const report = engine.getStats().generateReport(10);
2899
+ * console.log(report);
2900
+ *
2901
+ * // Access averages
2902
+ * const avg = engine.getStats().getAverageStats(100);
2903
+ * console.log(`Avg packet size: ${avg.avgPacketSize} bytes`);
2904
+ * ```
2905
+ */
2906
+ getStats(): CoreStats;
2907
+ /**
2908
+ * Ends current tick and generates update packets for all users
2909
+ *
2910
+ * This method:
2911
+ * 1. Encodes displays and layers of each user into binary packets
2912
+ * 2. Increments tick counter
2913
+ * 3. Returns a Map with packets ready to send
2914
+ *
2915
+ * Server/client can then send these packets over the network
2916
+ *
2917
+ * @returns Map<userId, packet> - Encoded packets for each user
2918
+ *
2919
+ * @example
2920
+ * ```typescript
2921
+ * const packets = engine.endTick();
2922
+ * packets.forEach((packet, userId) => {
2923
+ * networkSend(userId, packet);
2924
+ * });
2925
+ * ```
2926
+ */
2927
+ endTick(): Map<string, Uint8Array>;
2928
+ /**
2929
+ * Ends tick and generates SEPARATE packets for static and dynamic layers
2930
+ *
2931
+ * This method allows sending:
2932
+ * - STATIC Layers on a reliable Socket.IO channel (guaranteed delivery)
2933
+ * - DYNAMIC Layers on a volatile Socket.IO channel (can drop packets)
2934
+ *
2935
+ * Optimization: Static layers (background, pattern) must arrive,
2936
+ * but dynamic layers (particles, trail) can skip frames.
2937
+ *
2938
+ * @returns Map<userId, { static: Uint8Array | null, dynamic: Uint8Array | null }>
2939
+ *
2940
+ * @example
2941
+ * ```typescript
2942
+ * const splitPackets = engine.endTickSplit();
2943
+ * splitPackets.forEach(({ static: staticPacket, dynamic: dynamicPacket }, userId) => {
2944
+ * if (staticPacket) {
2945
+ * network.sendToClient(userId, 'update-static', staticPacket); // Reliable
2946
+ * }
2947
+ * if (dynamicPacket) {
2948
+ * network.sendToClientVolatile(userId, 'update-dynamic', dynamicPacket); // Volatile
2949
+ * }
2950
+ * });
2951
+ * ```
2952
+ */
2953
+ endTickSplit(): Map<string, {
2954
+ static: Uint8Array | null;
2955
+ dynamic: Uint8Array | null;
2956
+ }>;
2957
+ /**
2958
+ * Generates a complete snapshot of a user's state
2959
+ *
2960
+ * Unlike endTick() which generates incremental updates
2961
+ * (dynamic layers + static layers not yet sent), getSnapshot()
2962
+ * encodes ALL layers (static + dynamic), ignoring the
2963
+ * hasSentStatic flag.
2964
+ *
2965
+ * The snapshot represents the complete state at time T, while
2966
+ * updates are cumulative deltas.
2967
+ *
2968
+ * Use cases:
2969
+ * - Initial connection: Send complete state to new client
2970
+ * - Reconnection: Resynchronize client after disconnection
2971
+ * - Spectators: Allow observer to join mid-game
2972
+ * - Save: Capture state for persistence
2973
+ * - Migration: Transfer user to another server
2974
+ * - Replay: Record initial state for replay
2975
+ *
2976
+ * @param userId - User ID
2977
+ * @returns Complete binary packet (Uint8Array) or null if user doesn't exist
2978
+ *
2979
+ * @example
2980
+ * ```typescript
2981
+ * // Initial connection
2982
+ * socket.on('connect', (userId) => {
2983
+ * const snapshot = engine.getSnapshot(userId);
2984
+ * socket.emit('initial-state', snapshot);
2985
+ * });
2986
+ *
2987
+ * // Reconnection after disconnect
2988
+ * socket.on('reconnect', (userId) => {
2989
+ * const snapshot = engine.getSnapshot(userId);
2990
+ * socket.emit('resync-state', snapshot);
2991
+ * });
2992
+ *
2993
+ * // Spectator joining during game
2994
+ * socket.on('spectate', (spectatorId, targetUserId) => {
2995
+ * const snapshot = engine.getSnapshot(targetUserId);
2996
+ * socket.to(spectatorId).emit('spectate-state', snapshot);
2997
+ * });
2998
+ * ```
2999
+ */
3000
+ getSnapshot(userId: string): Uint8Array | null;
3001
+ /**
3002
+ * Resets tick counter to 0
3003
+ *
3004
+ * Useful for complete reset or starting a new session
3005
+ *
3006
+ * @example
3007
+ * ```typescript
3008
+ * engine.resetTick();
3009
+ * console.log(engine.getCurrentTick()); // 0
3010
+ * ```
3011
+ */
3012
+ resetTick(): void;
3013
+ /**
3014
+ * Applies a decoded UpdatePacket to a user (CLIENT-SIDE RECONSTRUCTION)
3015
+ *
3016
+ * This method is the main entry point for reconstructing a user's state
3017
+ * on the client side from a packet received from the server.
3018
+ *
3019
+ * It performs:
3020
+ * 1. User validation (must exist)
3021
+ * 2. Update application via user.applyUpdate()
3022
+ * 3. Automatic rasterization in client mode
3023
+ *
3024
+ * IMPORTANT: This method should ONLY be called in CLIENT mode.
3025
+ * The server generates packets via endTick(), the client applies them.
3026
+ *
3027
+ * @param userId - Target user ID
3028
+ * @param packet - Decoded UpdatePacket (via UpdatePacketDecoder)
3029
+ * @returns true if update was applied, false if user doesn't exist
3030
+ *
3031
+ * @example
3032
+ * ```typescript
3033
+ * // Client side (ClientRuntime)
3034
+ * import { UpdatePacketDecoder } from '@utsp/core';
3035
+ *
3036
+ * const decoder = new UpdatePacketDecoder();
3037
+ *
3038
+ * websocket.on('update', (buffer: Uint8Array) => {
3039
+ * // Decode binary packet
3040
+ * const packet = decoder.decode(buffer);
3041
+ *
3042
+ * // Apply to local user
3043
+ * const applied = core.applyUpdatePacket('user1', packet);
3044
+ *
3045
+ * if (applied) {
3046
+ * // Rendering is automatically updated via getRenderState()
3047
+ * console.log(`Update tick ${packet.tick} applied`);
3048
+ * }
3049
+ * });
3050
+ * ```
3051
+ */
3052
+ applyUpdatePacket(userId: string, packet: UpdatePacket): boolean;
3053
+ /**
3054
+ * Applies a raw UpdatePacket buffer (decodes then applies)
3055
+ *
3056
+ * Convenient version that combines decoding + application in one step.
3057
+ *
3058
+ * @param userId - Target user ID
3059
+ * @param buffer - Binary buffer received from server
3060
+ * @returns true if update was applied, false if user doesn't exist
3061
+ *
3062
+ * @example
3063
+ * ```typescript
3064
+ * // Simplified version for runtime
3065
+ * websocket.on('update', (buffer: Uint8Array) => {
3066
+ * core.applyUpdatePacketBuffer('user1', buffer);
3067
+ * });
3068
+ * ```
3069
+ */
3070
+ applyUpdatePacketBuffer(userId: string, buffer: Uint8Array): boolean;
3071
+ /**
3072
+ * Applies a LoadPacket received from the server (CLIENT-SIDE)
3073
+ *
3074
+ * This method automatically decodes the binary buffer and applies
3075
+ * the asset to the Core according to its LoadType:
3076
+ * - ColorPalette → loadPalette()
3077
+ * - Sprite → loadUnicolorSprites()
3078
+ * - MulticolorSprite → loadMulticolorSprites()
3079
+ * - BitmapFont → loadBitmapFontById()
3080
+ * - WebFont → loadWebFontById()
3081
+ * - Sound → (TODO: implement audio system)
3082
+ *
3083
+ * @param buffer - Encoded binary buffer received via WebSocket
3084
+ * @returns true if applied successfully, false on error
3085
+ *
3086
+ * @example
3087
+ * ```typescript
3088
+ * // Client side (ClientRuntime)
3089
+ * websocket.on('load', (buffer: Uint8Array) => {
3090
+ * core.applyLoadPacket(buffer);
3091
+ * });
3092
+ * ```
3093
+ */
3094
+ applyLoadPacket(buffer: Uint8Array): boolean;
3095
+ /**
3096
+ * Generates a LoadPacket for the current color palette (SERVER-SIDE)
3097
+ *
3098
+ * Encodes the complete palette in binary format ready to send to clients.
3099
+ *
3100
+ * @returns Encoded binary buffer (or null if palette empty)
3101
+ *
3102
+ * @example
3103
+ * ```typescript
3104
+ * // Server side
3105
+ * const palettePacket = core.generatePaletteLoadPacket();
3106
+ * if (palettePacket) {
3107
+ * websocket.emit('load', palettePacket);
3108
+ * }
3109
+ * ```
3110
+ */
3111
+ generatePaletteLoadPacket(): Uint8Array | null;
3112
+ /**
3113
+ * Generates a LoadPacket for all unicolor sprites (SERVER-SIDE)
3114
+ *
3115
+ * @returns Encoded binary buffer (or null if no sprites)
3116
+ *
3117
+ * @example
3118
+ * ```typescript
3119
+ * const spritesPacket = core.generateUnicolorSpritesLoadPacket();
3120
+ * if (spritesPacket) {
3121
+ * websocket.emit('load', spritesPacket);
3122
+ * }
3123
+ * ```
3124
+ */
3125
+ generateUnicolorSpritesLoadPacket(): Uint8Array | null;
3126
+ /**
3127
+ * Generates a LoadPacket for all multicolor sprites (SERVER-SIDE)
3128
+ *
3129
+ * @returns Encoded binary buffer (or null if no sprites)
3130
+ *
3131
+ * @example
3132
+ * ```typescript
3133
+ * const spritesPacket = core.generateMulticolorSpritesLoadPacket();
3134
+ * if (spritesPacket) {
3135
+ * websocket.emit('load', spritesPacket);
3136
+ * }
3137
+ * ```
3138
+ */
3139
+ generateMulticolorSpritesLoadPacket(): Uint8Array | null;
3140
+ /**
3141
+ * Generates LoadPackets for all web fonts (SERVER-SIDE)
3142
+ *
3143
+ * Returns an array of buffers (one font = one packet)
3144
+ *
3145
+ * @returns Array of encoded buffers (or empty array if no fonts)
3146
+ *
3147
+ * @example
3148
+ * ```typescript
3149
+ * const fontPackets = core.generateWebFontsLoadPackets();
3150
+ * fontPackets.forEach(packet => {
3151
+ * websocket.emit('load', packet);
3152
+ * });
3153
+ * ```
3154
+ */
3155
+ generateWebFontsLoadPackets(): Uint8Array[];
3156
+ /**
3157
+ * Generates LoadPackets for all bitmap fonts (SERVER-SIDE)
3158
+ *
3159
+ * Returns an array of buffers (one font = one packet)
3160
+ *
3161
+ * @returns Array of encoded buffers (or empty array if no fonts)
3162
+ *
3163
+ * @example
3164
+ * ```typescript
3165
+ * const fontPackets = core.generateBitmapFontsLoadPackets();
3166
+ * fontPackets.forEach(packet => {
3167
+ * websocket.emit('load', packet);
3168
+ * });
3169
+ * ```
3170
+ */
3171
+ generateBitmapFontsLoadPackets(): Uint8Array[];
3172
+ /**
3173
+ * Generates ALL LoadPackets (palette + sprites + fonts) (SERVER-SIDE)
3174
+ *
3175
+ * Useful when a client initially connects to send them
3176
+ * all assets at once.
3177
+ *
3178
+ * @returns Array of all encoded buffers ready to send
3179
+ *
3180
+ * @example
3181
+ * ```typescript
3182
+ * // When a new client connects
3183
+ * socket.on('join', (userId) => {
3184
+ * const packets = core.generateAllLoadPackets();
3185
+ * packets.forEach(packet => {
3186
+ * socket.to(userId).emit('load', packet);
3187
+ * });
3188
+ * });
3189
+ * ```
3190
+ */
3191
+ generateAllLoadPackets(): Uint8Array[];
3192
+ /**
3193
+ * Applies Input Bindings to a user (CLIENT-SIDE)
3194
+ *
3195
+ * This method allows the client to configure input bindings
3196
+ * that it must capture and send back to the server in compressed form.
3197
+ *
3198
+ * @param userId - Target user ID
3199
+ * @param json - JSON string of bindings from user.getInputBindingsLoadPacket()
3200
+ * @returns true if applied successfully, false if user doesn't exist
3201
+ *
3202
+ * @example
3203
+ * ```typescript
3204
+ * // Client side
3205
+ * websocket.on('input-bindings', (json: string) => {
3206
+ * core.applyInputBindingsLoadPacket('user1', json);
3207
+ * });
3208
+ * ```
3209
+ */
3210
+ applyInputBindingsLoadPacket(userId: string, json: string): boolean;
3211
+ /**
3212
+ * Generates render state for a user
3213
+ *
3214
+ * This method converts a user's displays and orders into a readable
3215
+ * format with CSS colors, useful for:
3216
+ * - Render debugging
3217
+ * - Server-side preview
3218
+ * - Automated tests
3219
+ * - Snapshot generation
3220
+ *
3221
+ * @param userId - User ID
3222
+ * @returns Render state or null if user doesn't exist
3223
+ *
3224
+ * @example
3225
+ * ```typescript
3226
+ * const state = engine.getRenderState("user1");
3227
+ * if (state) {
3228
+ * console.log(`Tick: ${state.tick}`);
3229
+ * for (const display of state.displays) {
3230
+ * console.log(`Display ${display.width}x${display.height}`);
3231
+ * // Display cells
3232
+ * for (let y = 0; y < display.height; y++) {
3233
+ * let line = "";
3234
+ * for (let x = 0; x < display.width; x++) {
3235
+ * const cell = display.cells[y * display.width + x];
3236
+ * line += cell.char;
3237
+ * }
3238
+ * console.log(line);
3239
+ * }
3240
+ * }
3241
+ * }
3242
+ * ```
3243
+ */
3244
+ getRenderState(userId: string): UserRenderState | null;
3245
+ /**
3246
+ * Generates render state for all users
3247
+ *
3248
+ * @returns Map of render states by userId
3249
+ *
3250
+ * @example
3251
+ * ```typescript
3252
+ * const allStates = engine.getAllRenderStates();
3253
+ * allStates.forEach((state, userId) => {
3254
+ * console.log(`User ${userId}: ${state.displays.length} displays`);
3255
+ * });
3256
+ * ```
3257
+ */
3258
+ getAllRenderStates(): Map<string, UserRenderState>;
3259
+ /**
3260
+ * Enables or disables statistics collection
3261
+ *
3262
+ * @param enabled - true to enable, false to disable
3263
+ *
3264
+ * @example
3265
+ * ```typescript
3266
+ * engine.enableStats(true);
3267
+ * // ... run game loop ...
3268
+ * const report = engine.getStatsReport();
3269
+ * console.log(report);
3270
+ * ```
3271
+ */
3272
+ enableStats(enabled: boolean): void;
3273
+ /**
3274
+ * Returns the statistics instance
3275
+ *
3276
+ * @returns The CoreStats object
3277
+ */
3278
+ getStatsInstance(): CoreStats;
3279
+ /**
3280
+ * Resets statistics for the current tick
3281
+ *
3282
+ * @example
3283
+ * ```typescript
3284
+ * engine.resetStats();
3285
+ * ```
3286
+ */
3287
+ resetStats(): void;
3288
+ /**
3289
+ * Loads unicolor sprites from a LoadPacket
3290
+ *
3291
+ * Unicolor sprites only store charCode (1 byte per cell).
3292
+ * Colors are applied during rendering via Orders.
3293
+ *
3294
+ * @param loadData - Loading data from a LoadPacket (type 0x02)
3295
+ *
3296
+ * @example
3297
+ * ```typescript
3298
+ * // Load a 4x4 unicolor sprite
3299
+ * engine.loadUnicolorSprites({
3300
+ * sprites: [
3301
+ * {
3302
+ * spriteId: 1,
3303
+ * sizeX: 4,
3304
+ * sizeY: 4,
3305
+ * data: [
3306
+ * 0x2588, 0x2588, 0x2588, 0x2588,
3307
+ * 0x2588, 0x0020, 0x0020, 0x2588,
3308
+ * 0x2588, 0x0020, 0x0020, 0x2588,
3309
+ * 0x2588, 0x2588, 0x2588, 0x2588
3310
+ * ]
3311
+ * }
3312
+ * ]
3313
+ * });
3314
+ * ```
3315
+ */
3316
+ loadUnicolorSprites(loadData: SpriteLoad): void;
3317
+ /**
3318
+ * Loads multicolor sprites from a LoadPacket
3319
+ *
3320
+ * Multicolor sprites store charCode + fgColorId + bgColorId (3 bytes per cell).
3321
+ * Optimized for sprites with lots of visual detail.
3322
+ *
3323
+ * @param loadData - Loading data from a LoadPacket (type 0x03)
3324
+ *
3325
+ * @example
3326
+ * ```typescript
3327
+ * // Load a 2x2 multicolor sprite
3328
+ * engine.loadMulticolorSprites({
3329
+ * sprites: [
3330
+ * {
3331
+ * spriteId: 10,
3332
+ * sizeX: 2,
3333
+ * sizeY: 2,
3334
+ * data: [
3335
+ * { charCode: 0x2588, fgColorId: 12, bgColorId: 0 },
3336
+ * { charCode: 0x2588, fgColorId: 14, bgColorId: 0 },
3337
+ * { charCode: 0x2588, fgColorId: 14, bgColorId: 0 },
3338
+ * { charCode: 0x2588, fgColorId: 12, bgColorId: 0 }
3339
+ * ]
3340
+ * }
3341
+ * ]
3342
+ * });
3343
+ * ```
3344
+ */
3345
+ loadMulticolorSprites(loadData: MulticolorSpriteLoad): void;
3346
+ /**
3347
+ * Unloads a unicolor sprite from memory
3348
+ *
3349
+ * @param spriteId - ID of the sprite to unload
3350
+ * @returns true if sprite was found and removed, false otherwise
3351
+ *
3352
+ * @example
3353
+ * ```typescript
3354
+ * const removed = engine.unloadUnicolorSprite(1);
3355
+ * if (removed) {
3356
+ * console.log("Sprite 1 unloaded");
3357
+ * }
3358
+ * ```
3359
+ */
3360
+ unloadUnicolorSprite(spriteId: number): boolean;
3361
+ /**
3362
+ * Unloads a multicolor sprite from memory
3363
+ *
3364
+ * @param spriteId - ID of the sprite to unload
3365
+ * @returns true if sprite was found and removed, false otherwise
3366
+ *
3367
+ * @example
3368
+ * ```typescript
3369
+ * const removed = engine.unloadMulticolorSprite(10);
3370
+ * if (removed) {
3371
+ * console.log("Sprite 10 unloaded");
3372
+ * }
3373
+ * ```
3374
+ */
3375
+ unloadMulticolorSprite(spriteId: number): boolean;
3376
+ /**
3377
+ * Clears all unicolor sprites from memory
3378
+ *
3379
+ * @example
3380
+ * ```typescript
3381
+ * engine.clearUnicolorSprites();
3382
+ * console.log("All unicolor sprites have been unloaded");
3383
+ * ```
3384
+ */
3385
+ clearUnicolorSprites(): void;
3386
+ /**
3387
+ * Clears all multicolor sprites from memory
3388
+ *
3389
+ * @example
3390
+ * ```typescript
3391
+ * engine.clearMulticolorSprites();
3392
+ * console.log("All multicolor sprites have been unloaded");
3393
+ * ```
3394
+ */
3395
+ clearMulticolorSprites(): void;
3396
+ /**
3397
+ * Clears all sprites (unicolor and multicolor)
3398
+ *
3399
+ * @example
3400
+ * ```typescript
3401
+ * engine.clearAllSprites();
3402
+ * console.log("All sprites have been unloaded");
3403
+ * ```
3404
+ */
3405
+ clearAllSprites(): void;
3406
+ /**
3407
+ * Counts the number of loaded unicolor sprites
3408
+ *
3409
+ * @returns Number of unicolor sprites in memory
3410
+ *
3411
+ * @example
3412
+ * ```typescript
3413
+ * const count = engine.getUnicolorSpriteCount();
3414
+ * console.log(`${count} unicolor sprites loaded`);
3415
+ * ```
3416
+ */
3417
+ getUnicolorSpriteCount(): number;
3418
+ /**
3419
+ * Counts the number of loaded multicolor sprites
3420
+ *
3421
+ * @returns Number of multicolor sprites in memory
3422
+ *
3423
+ * @example
3424
+ * ```typescript
3425
+ * const count = engine.getMulticolorSpriteCount();
3426
+ * console.log(`${count} multicolor sprites loaded`);
3427
+ * ```
3428
+ */
3429
+ getMulticolorSpriteCount(): number;
3430
+ /**
3431
+ * Counts total number of loaded sprites (unicolor + multicolor)
3432
+ *
3433
+ * @returns Total number of sprites in memory
3434
+ *
3435
+ * @example
3436
+ * ```typescript
3437
+ * const total = engine.getTotalSpriteCount();
3438
+ * console.log(`${total} sprites total`);
3439
+ * ```
3440
+ */
3441
+ getTotalSpriteCount(): number;
3442
+ /**
3443
+ * Checks if a unicolor sprite exists
3444
+ *
3445
+ * @param spriteId - ID of the sprite to check
3446
+ * @returns true if sprite exists, false otherwise
3447
+ *
3448
+ * @example
3449
+ * ```typescript
3450
+ * if (engine.hasUnicolorSprite(1)) {
3451
+ * console.log("Sprite 1 available");
3452
+ * }
3453
+ * ```
3454
+ */
3455
+ hasUnicolorSprite(spriteId: number): boolean;
3456
+ /**
3457
+ * Checks if a multicolor sprite exists
3458
+ *
3459
+ * @param spriteId - ID of the sprite to check
3460
+ * @returns true if sprite exists, false otherwise
3461
+ *
3462
+ * @example
3463
+ * ```typescript
3464
+ * if (engine.hasMulticolorSprite(10)) {
3465
+ * console.log("Sprite 10 available");
3466
+ * }
3467
+ * ```
3468
+ */
3469
+ hasMulticolorSprite(spriteId: number): boolean;
3470
+ /**
3471
+ * Gets the sprite registry (for rasterizer only)
3472
+ * @internal
3473
+ */
3474
+ getSpriteRegistry(): SpriteRegistry;
3475
+ /**
3476
+ * Loads the default web font (Courier New, 16px, monospace)
3477
+ *
3478
+ * This font is loaded automatically during engine initialization.
3479
+ * It uses fontId 0 (reserved for the default font).
3480
+ *
3481
+ * @private
3482
+ */
3483
+ private loadDefaultWebFont;
3484
+ /**
3485
+ * Loads a web font into the registry
3486
+ *
3487
+ * Web fonts use the CSS system for rendering (fontFamily, fontSize, etc.)
3488
+ * FontID 0 is reserved for the default font.
3489
+ *
3490
+ * @param font - WebFont instance to load
3491
+ * @throws Error if fontId is already in use
3492
+ *
3493
+ * @example
3494
+ * ```typescript
3495
+ * const customFont = new WebFont(1, {
3496
+ * fontFamily: "Arial",
3497
+ * fontSize: 20,
3498
+ * fontWeight: "bold"
3499
+ * });
3500
+ * engine.loadWebFont(customFont);
3501
+ * ```
3502
+ */
3503
+ loadWebFont(font: WebFont): void;
3504
+ /**
3505
+ * Gets a web font by its ID
3506
+ *
3507
+ * @param fontId - Font ID (0-255)
3508
+ * @returns The WebFont instance or null if not found
3509
+ *
3510
+ * @example
3511
+ * ```typescript
3512
+ * const font = engine.getWebFont(1);
3513
+ * if (font) {
3514
+ * console.log(font.toCSS());
3515
+ * }
3516
+ * ```
3517
+ */
3518
+ getWebFont(fontId: number): WebFont | null;
3519
+ /**
3520
+ * Checks if a web font exists
3521
+ *
3522
+ * @param fontId - Font ID (0-255)
3523
+ * @returns true if font exists
3524
+ *
3525
+ * @example
3526
+ * ```typescript
3527
+ * if (engine.hasWebFont(1)) {
3528
+ * console.log("Font 1 is loaded");
3529
+ * }
3530
+ * ```
3531
+ */
3532
+ hasWebFont(fontId: number): boolean;
3533
+ /**
3534
+ * Unloads a web font from the registry
3535
+ *
3536
+ * Note: Cannot unload the default font (fontId 0)
3537
+ *
3538
+ * @param fontId - Font ID to unload (1-255)
3539
+ * @returns true if font was unloaded
3540
+ * @throws Error if attempting to unload default font (fontId 0)
3541
+ *
3542
+ * @example
3543
+ * ```typescript
3544
+ * const removed = engine.unloadWebFont(1);
3545
+ * if (removed) {
3546
+ * console.log("Font 1 unloaded");
3547
+ * }
3548
+ * ```
3549
+ */
3550
+ unloadWebFont(fontId: number): boolean;
3551
+ /**
3552
+ * Clears all web fonts (except default font)
3553
+ *
3554
+ * @example
3555
+ * ```typescript
3556
+ * engine.clearWebFonts();
3557
+ * console.log("All custom web fonts cleared");
3558
+ * ```
3559
+ */
3560
+ clearWebFonts(): void;
3561
+ /**
3562
+ * Sets the active web font
3563
+ *
3564
+ * The active font is used by default for character rendering.
3565
+ *
3566
+ * @param fontId - Font ID to activate (0-255)
3567
+ * @throws Error if font doesn't exist
3568
+ *
3569
+ * @example
3570
+ * ```typescript
3571
+ * engine.setActiveWebFont(1);
3572
+ * console.log(`Active font: ${engine.getActiveWebFontId()}`);
3573
+ * ```
3574
+ */
3575
+ setActiveWebFont(fontId: number): void;
3576
+ /**
3577
+ * Gets the active web font ID
3578
+ *
3579
+ * @returns Active font ID (0-255)
3580
+ *
3581
+ * @example
3582
+ * ```typescript
3583
+ * const activeFontId = engine.getActiveWebFontId();
3584
+ * console.log(`Current font: ${activeFontId}`);
3585
+ * ```
3586
+ */
3587
+ getActiveWebFontId(): number;
3588
+ /**
3589
+ * Gets the active web font
3590
+ *
3591
+ * @returns The active WebFont instance
3592
+ *
3593
+ * @example
3594
+ * ```typescript
3595
+ * const activeFont = engine.getActiveWebFont();
3596
+ * console.log(activeFont.toCSS());
3597
+ * ```
3598
+ */
3599
+ getActiveWebFont(): WebFont;
3600
+ /**
3601
+ * Counts the number of loaded web fonts
3602
+ *
3603
+ * @returns Number of web fonts in memory
3604
+ *
3605
+ * @example
3606
+ * ```typescript
3607
+ * const count = engine.getWebFontCount();
3608
+ * console.log(`${count} web fonts loaded`);
3609
+ * ```
3610
+ */
3611
+ getWebFontCount(): number;
3612
+ /**
3613
+ * Gets all loaded web font IDs
3614
+ *
3615
+ * @returns Array of fontIds (0-255)
3616
+ *
3617
+ * @example
3618
+ * ```typescript
3619
+ * const fontIds = engine.getWebFontIds();
3620
+ * console.log(`Loaded fonts: ${fontIds.join(", ")}`);
3621
+ * ```
3622
+ */
3623
+ getWebFontIds(): number[];
3624
+ /**
3625
+ * Gets the web font registry
3626
+ *
3627
+ * @returns WebFontRegistry instance
3628
+ *
3629
+ * @example
3630
+ * ```typescript
3631
+ * const registry = engine.getWebFontRegistry();
3632
+ * console.log(`Total fonts: ${registry.getFontCount()}`);
3633
+ * ```
3634
+ */
3635
+ getWebFontRegistry(): WebFontRegistry;
3636
+ /**
3637
+ * Gets the bitmap font registry
3638
+ *
3639
+ * @returns BitmapFontRegistry instance
3640
+ *
3641
+ * @example
3642
+ * ```typescript
3643
+ * const registry = engine.getBitmapFontRegistry();
3644
+ * console.log(`Total fonts: ${registry.getFontCount()}`);
3645
+ * ```
3646
+ */
3647
+ getBitmapFontRegistry(): BitmapFontRegistry;
3648
+ /**
3649
+ * Loads a web font directly with its ID and configuration
3650
+ *
3651
+ * Simplified method that creates and loads the font in one step.
3652
+ *
3653
+ * @param fontId - Unique font ID (0-255, 0 reserved for default font)
3654
+ * @param config - Web font configuration
3655
+ * @throws Error if fontId is already in use
3656
+ *
3657
+ * @example
3658
+ * ```typescript
3659
+ * // Load a custom font
3660
+ * engine.loadWebFontById(1, {
3661
+ * fontFamily: "Arial",
3662
+ * fontSize: 20,
3663
+ * fontWeight: "bold"
3664
+ * });
3665
+ *
3666
+ * // Load a monospace font
3667
+ * engine.loadWebFontById(2, {
3668
+ * fontFamily: "Consolas",
3669
+ * fontSize: 16,
3670
+ * charSpacing: 2
3671
+ * });
3672
+ * ```
3673
+ */
3674
+ loadWebFontById(fontId: number, config: WebFontConfig): void;
3675
+ /**
3676
+ * Loads a bitmap font directly with its ID and configuration
3677
+ *
3678
+ * Simplified method that loads the bitmap font in one step.
3679
+ *
3680
+ * @param fontId - Unique font ID (0-255)
3681
+ * @param config - Bitmap font configuration (dimensions + glyphs)
3682
+ * @throws Error if fontId is already in use
3683
+ *
3684
+ * @example
3685
+ * ```typescript
3686
+ * import { createASCII8x8FontLoad } from "utsp-core";
3687
+ *
3688
+ * // Load the ASCII 8×8 font
3689
+ * const fontLoad = createASCII8x8FontLoad(1);
3690
+ * engine.loadBitmapFontById(1, fontLoad.fontConfig);
3691
+ *
3692
+ * // Load a custom bitmap font
3693
+ * engine.loadBitmapFontById(10, {
3694
+ * charWidth: 16,
3695
+ * charHeight: 16,
3696
+ * glyphs: new Map([
3697
+ * [65, new Uint8Array([...])], // 'A'
3698
+ * [66, new Uint8Array([...])], // 'B'
3699
+ * ])
3700
+ * });
3701
+ * ```
3702
+ */
3703
+ loadBitmapFontById(fontId: number, config: BitmapFontConfig): void;
3704
+ /**
3705
+ * Gets a bitmap font by its ID
3706
+ *
3707
+ * @param fontId - Font ID (0-255)
3708
+ * @returns The BitmapFont instance or null if not found
3709
+ *
3710
+ * @example
3711
+ * ```typescript
3712
+ * const font = engine.getBitmapFont(1);
3713
+ * if (font) {
3714
+ * console.log(`Font dimensions: ${font.getCharWidth()}×${font.getCharHeight()}`);
3715
+ * console.log(`Glyphs: ${font.getGlyphCount()}`);
3716
+ * }
3717
+ * ```
3718
+ */
3719
+ getBitmapFont(fontId: number): BitmapFont | null;
3720
+ /**
3721
+ * Checks if a bitmap font exists
3722
+ *
3723
+ * @param fontId - Font ID (0-255)
3724
+ * @returns true if font exists
3725
+ *
3726
+ * @example
3727
+ * ```typescript
3728
+ * if (engine.hasBitmapFont(1)) {
3729
+ * console.log("Font 1 is loaded");
3730
+ * }
3731
+ * ```
3732
+ */
3733
+ hasBitmapFont(fontId: number): boolean;
3734
+ /**
3735
+ * Unloads a bitmap font from the registry
3736
+ *
3737
+ * @param fontId - Font ID to unload (0-255)
3738
+ * @returns true if font was unloaded
3739
+ *
3740
+ * @example
3741
+ * ```typescript
3742
+ * const removed = engine.unloadBitmapFont(1);
3743
+ * if (removed) {
3744
+ * console.log("Font 1 unloaded");
3745
+ * }
3746
+ * ```
3747
+ */
3748
+ unloadBitmapFont(fontId: number): boolean;
3749
+ /**
3750
+ * Clears all bitmap fonts
3751
+ *
3752
+ * @example
3753
+ * ```typescript
3754
+ * engine.clearBitmapFonts();
3755
+ * console.log("All bitmap fonts cleared");
3756
+ * ```
3757
+ */
3758
+ clearBitmapFonts(): void;
3759
+ /**
3760
+ * Counts the number of loaded bitmap fonts
3761
+ *
3762
+ * @returns Number of bitmap fonts in memory
3763
+ *
3764
+ * @example
3765
+ * ```typescript
3766
+ * const count = engine.getBitmapFontCount();
3767
+ * console.log(`${count} bitmap fonts loaded`);
3768
+ * ```
3769
+ */
3770
+ getBitmapFontCount(): number;
3771
+ /**
3772
+ * Gets all loaded bitmap font IDs
3773
+ *
3774
+ * @returns Array of fontIds (0-255)
3775
+ *
3776
+ * @example
3777
+ * ```typescript
3778
+ * const fontIds = engine.getBitmapFontIds();
3779
+ * console.log(`Loaded bitmap fonts: ${fontIds.join(", ")}`);
3780
+ * ```
3781
+ */
3782
+ getBitmapFontIds(): number[];
3783
+ }
3784
+
3785
+ /**
3786
+ * ASCII 8×8 Bitmap Font (Extended - IBM CP437)
3787
+ * Complete character set including:
3788
+ * - ASCII printable characters (32-126)
3789
+ * - Extended ASCII box drawing and blocks (128-255)
3790
+ *
3791
+ * Each character is 8 pixels wide by 8 pixels tall
3792
+ * 1 bit per pixel, packed into 8 bytes per character
3793
+ *
3794
+ * Box drawing characters support:
3795
+ * - Single line borders: ┌ ─ ┐ │ └ ┘ ├ ┤ ┬ ┴ ┼
3796
+ * - Double line borders: ╔ ═ ╗ ║ ╚ ╝ ╠ ╣ ╦ ╩ ╬
3797
+ * - Block characters: █ ▓ ▒ ░ ▀ ▄ ▌ ▐
3798
+ */
3799
+ /**
3800
+ * 8×8 bitmap font data
3801
+ * Key: ASCII/Extended ASCII character code (32-126, 176-223)
3802
+ * Value: 8 bytes representing the character bitmap
3803
+ */
3804
+ declare const ASCII_8X8_FONT: Map<number, Uint8Array>;
3805
+ /**
3806
+ * Get the bitmap data for a specific ASCII character
3807
+ * @param charCode ASCII character code (32-126) or Extended ASCII (176-223)
3808
+ * @returns 8-byte bitmap or undefined if character not found
3809
+ */
3810
+ declare function getCharBitmap(charCode: number): Uint8Array | undefined;
3811
+ /**
3812
+ * Check if a character has a bitmap defined
3813
+ * @param charCode ASCII character code
3814
+ * @returns true if character is defined
3815
+ */
3816
+ declare function hasChar(charCode: number): boolean;
3817
+ /**
3818
+ * Get all available character codes
3819
+ * @returns Array of ASCII codes (32-126) and Extended ASCII (176-223)
3820
+ */
3821
+ declare function getAllCharCodes(): number[];
3822
+ /**
3823
+ * Create a BitmapFontLoad packet for the complete ASCII font
3824
+ * @param fontId Font identifier (0-255)
3825
+ * @returns BitmapFontLoad object ready for encoding
3826
+ */
3827
+ declare function createASCII8x8FontLoad(fontId: number): {
3828
+ loadType: number;
3829
+ fontId: number;
3830
+ width: number;
3831
+ height: number;
3832
+ cellWidth: number;
3833
+ cellHeight: number;
3834
+ characters: Array<{
3835
+ charCode: number;
3836
+ bitmap: Uint8Array;
3837
+ }>;
3838
+ };
3839
+ /**
3840
+ * Get BitmapFontConfig for the complete ASCII 8×8 font
3841
+ * This is a convenience function to get the config directly
3842
+ * for use with UTSPCore.loadBitmapFontById()
3843
+ * @returns BitmapFontConfig ready to use
3844
+ */
3845
+ declare function getASCII8x8FontConfig(): {
3846
+ charWidth: number;
3847
+ charHeight: number;
3848
+ glyphs: Map<number, Uint8Array>;
3849
+ };
3850
+
3851
+ /**
3852
+ * CompressedInputPacket - Ultra-compact binary format for inputs (V3)
3853
+ *
3854
+ * Architecture:
3855
+ * - CLIENT: Evaluates sources → Compresses → Sends
3856
+ * - SERVER: Receives → Decompresses → Stores in axes/buttons Maps
3857
+ *
3858
+ * MINIMALIST Format (V3):
3859
+ * ┌────────┬───────────┬─────────────┬────────┬──────────┐
3860
+ * │ Tick │ Axes │ Buttons │ Mouse │ Flags │
3861
+ * │ 8 bytes│ N bytes │ M bytes │ 3 bytes│ 1 byte │
3862
+ * └────────┴───────────┴─────────────┴────────┴──────────┘
3863
+ *
3864
+ * Tick (8 bytes) : Frame number (uint64, little-endian)
3865
+ * Axes (N bytes) : 1 byte per axis (int8, -127 to +127)
3866
+ * Buttons (M bytes) : Bitpacking (8 buttons per byte, ⌈ButtonCount/8⌉)
3867
+ * Mouse (3 bytes) : displayId (1) + mouseX (1) + mouseY (1)
3868
+ * Flags (1 byte) : Bit 0 = mouseOverDisplay
3869
+ *
3870
+ * Note: axisCount and buttonCount are known in advance via InputBindingRegistry
3871
+ * Example: 5 axes + 12 buttons = 8 + 5 + 2 + 3 + 1 = 19 bytes
3872
+ */
3873
+ /**
3874
+ * Represents a compressed input on the server side (after decoding)
3875
+ *
3876
+ * This structure contains already decompressed values:
3877
+ * - Axes: float between -1.0 and +1.0
3878
+ * - Buttons: boolean
3879
+ * - Mouse: position + display + flags
3880
+ * - TextInputs: array of key strings for text input (e.g., ['a', 'Backspace', 'Enter'])
3881
+ */
3882
+ interface CompressedInputPacket {
3883
+ /** Frame number (tick) - uint64 */
3884
+ tick: bigint;
3885
+ /** Axis values (bindingId → value between -1.0 and +1.0) */
3886
+ axes: Map<number, number>;
3887
+ /** Button states (bindingId → pressed) */
3888
+ buttons: Map<number, boolean>;
3889
+ /** ID of the display where the mouse is located (0-255) */
3890
+ displayId: number;
3891
+ /** Mouse X position (0-255) */
3892
+ mouseX: number;
3893
+ /** Mouse Y position (0-255) */
3894
+ mouseY: number;
3895
+ /** Is the mouse over a display? (vs off-display area) */
3896
+ mouseOverDisplay: boolean;
3897
+ /** Text input events (e.g., ['a', 'Backspace', 'Enter']) - for input boxes, chat, etc. */
3898
+ textInputs: string[];
3899
+ }
3900
+ /**
3901
+ * Creates an empty CompressedInputPacket
3902
+ *
3903
+ * @param tick - Frame number (bigint)
3904
+ * @returns An empty packet with default values
3905
+ */
3906
+ declare function createEmptyCompressedInputPacket(tick?: bigint): CompressedInputPacket;
3907
+ /**
3908
+ * Helper: Encodes a float (-1.0 to +1.0) to int8 (-128 to +127)
3909
+ *
3910
+ * @param value - Value between -1.0 and +1.0
3911
+ * @returns Value between -128 and +127
3912
+ *
3913
+ * @example
3914
+ * ```typescript
3915
+ * encodeAxisToInt8(-1.0) // -127
3916
+ * encodeAxisToInt8(0.0) // 0
3917
+ * encodeAxisToInt8(1.0) // 127
3918
+ * encodeAxisToInt8(-0.5) // -63
3919
+ * ```
3920
+ */
3921
+ declare function encodeAxisToInt8(value: number): number;
3922
+ /**
3923
+ * Helper: Decodes an int8 (-128 to +127) to float (-1.0 to +1.0)
3924
+ *
3925
+ * @param value - Value between -128 and +127
3926
+ * @returns Value between -1.0 and +1.0
3927
+ *
3928
+ * @example
3929
+ * ```typescript
3930
+ * decodeInt8ToAxis(-127) // -1.0
3931
+ * decodeInt8ToAxis(0) // 0.0
3932
+ * decodeInt8ToAxis(127) // 1.0
3933
+ * decodeInt8ToAxis(-63) // -0.496...
3934
+ * ```
3935
+ */
3936
+ declare function decodeInt8ToAxis(value: number): number;
3937
+ /**
3938
+ * Helper: Calculates the number of bytes needed for N buttons (bitpacking)
3939
+ *
3940
+ * @param buttonCount - Number of buttons
3941
+ * @returns Number of bytes needed
3942
+ *
3943
+ * @example
3944
+ * ```typescript
3945
+ * getButtonByteCount(0) // 0
3946
+ * getButtonByteCount(7) // 1
3947
+ * getButtonByteCount(8) // 1
3948
+ * getButtonByteCount(9) // 2
3949
+ * getButtonByteCount(16) // 2
3950
+ * getButtonByteCount(17) // 3
3951
+ * ```
3952
+ */
3953
+ declare function getButtonByteCount(buttonCount: number): number;
3954
+ /**
3955
+ * Calculates the total size of a compressed packet in bytes (format V3)
3956
+ *
3957
+ * @param axisCount - Number of axes
3958
+ * @param buttonCount - Number of buttons
3959
+ * @returns Size in bytes
3960
+ *
3961
+ * @example
3962
+ * ```typescript
3963
+ * getCompressedPacketSize(5, 12) // 8 + 5 + 2 + 4 = 19 bytes
3964
+ * getCompressedPacketSize(0, 0) // 8 + 0 + 0 + 4 = 12 bytes (minimum)
3965
+ * getCompressedPacketSize(2, 3) // 8 + 2 + 1 + 4 = 15 bytes
3966
+ * ```
3967
+ */
3968
+ declare function getCompressedPacketSize(axisCount: number, buttonCount: number): number;
3969
+
3970
+ /**
3971
+ * CompressedInputEncoder - Encodes inputs in compact binary format
3972
+ *
3973
+ * USED BY CLIENT (utsp-client)
3974
+ *
3975
+ * Workflow:
3976
+ * 1. Client evaluates sources (keyboard + gamepad + touch, etc.)
3977
+ * 2. Client encodes final values in compact binary
3978
+ * 3. Client sends buffer to server via WebSocket
3979
+ *
3980
+ * MINIMALIST V3 Format (without counts):
3981
+ * - Tick: uint64 (8 bytes)
3982
+ * - Axes: float (-1.0 to +1.0) → int8 (-127 to +127) = 1 byte per axis
3983
+ * - Buttons: boolean → bitpacking = 8 buttons per byte
3984
+ * - DisplayId: uint8 (1 byte) = display ID
3985
+ * - Mouse: mouseX/Y (uint8) + flags = 3 bytes
3986
+ *
3987
+ * PREREQUISITE: Client and server must have the SAME bindings defined!
3988
+ *
3989
+ * Example: 5 axes + 12 buttons + mouse = 19 bytes total
3990
+ */
3991
+ /**
3992
+ * Encodes inputs in compact binary format v3 (CLIENT-SIDE)
3993
+ *
3994
+ * @param tick - Frame number (bigint for uint64)
3995
+ * @param axes - Map of axis values (bindingId → float between -1.0 and +1.0)
3996
+ * @param buttons - Map of button states (bindingId → pressed)
3997
+ * @param displayId - ID of the display where the mouse is located (0-255)
3998
+ * @param mouseX - Mouse X position (0-255)
3999
+ * @param mouseY - Mouse Y position (0-255)
4000
+ * @param mouseOverDisplay - Is the mouse over a display?
4001
+ * @returns Encoded buffer (without counts, minimalist format)
4002
+ *
4003
+ * @example
4004
+ * ```typescript
4005
+ * const axes = new Map([[0, -0.7], [1, 0.5]]);
4006
+ * const buttons = new Map([[0, true], [1, false], [2, true]]);
4007
+ * const buffer = encodeCompressedInput(42n, axes, buttons, 0, 128, 64, true);
4008
+ * // buffer.length = 8 + 2 + 1 + 4 = 15 bytes
4009
+ * ```
4010
+ */
4011
+ declare function encodeCompressedInput(tick: bigint, axes: Map<number, number>, buttons: Map<number, boolean>, displayId: number, mouseX: number, mouseY: number, mouseOverDisplay: boolean, textInputs?: string[]): Uint8Array;
4012
+
4013
+ /**
4014
+ * CompressedInputDecoder - Decodes inputs from compact binary format
4015
+ *
4016
+ * USED BY SERVER (utsp-core)
4017
+ *
4018
+ * Workflow:
4019
+ * 1. Server receives binary buffer from client via WebSocket
4020
+ * 2. Server decodes values (MINIMALIST V3 format - without counts)
4021
+ * 3. Server stores in user.axes and user.buttons Maps
4022
+ *
4023
+ * MINIMALIST V3 Format (without counts):
4024
+ * - PREREQUISITE: Client and server must have the SAME bindings defined!
4025
+ * - Buffer does NOT contain AxisCount nor ButtonCount
4026
+ * - Server must pass its registry to know expected structure
4027
+ * - Tick: uint64 (8 bytes)
4028
+ * - Axes: int8 (-127 to +127) → float (-1.0 to +1.0)
4029
+ * - Buttons: bitpacking → boolean (8 buttons per byte)
4030
+ * - DisplayId: uint8 (1 byte) = display ID
4031
+ * - Mouse: uint8 → mouseX/Y + flags (mouseOverDisplay)
4032
+ *
4033
+ * Example (2 axes, 3 buttons):
4034
+ * - Tick (8) + Axes (2) + Buttons (1) + DisplayId (1) + Mouse (3) = 15 bytes
4035
+ */
4036
+
4037
+ /**
4038
+ * Decodes a binary buffer to CompressedInputPacket v3 (SERVER-SIDE)
4039
+ *
4040
+ * MINIMALIST v3 format: buffer does NOT contain counters!
4041
+ * Server MUST pass its registry to know axisCount and buttonCount.
4042
+ *
4043
+ * IMPORTANT: Client and server must have the SAME bindings defined
4044
+ * (same number of axes and buttons, same order after sorting by bindingId).
4045
+ *
4046
+ * @param buffer - Encoded buffer received from client
4047
+ * @param registry - Server registry to know expected structure
4048
+ * @returns Decoded packet with tick (bigint), axes, buttons, displayId, mouse
4049
+ *
4050
+ * @throws Error if buffer is too small or invalid
4051
+ *
4052
+ * @example
4053
+ * ```typescript
4054
+ * const packet = decodeCompressedInput(buffer, serverRegistry);
4055
+ * // packet.tick → 42n (bigint)
4056
+ * // packet.axes.get(0) → -0.7
4057
+ * // packet.buttons.get(0) → true
4058
+ * // packet.displayId → 0
4059
+ * // packet.mouseOverDisplay → true
4060
+ * ```
4061
+ */
4062
+ declare function decodeCompressedInput(buffer: Uint8Array, registry: InputBindingRegistry): CompressedInputPacket;
4063
+
4064
+ /**
4065
+ * OrderBuilder - Factory to create orders easily and type-safely
4066
+ *
4067
+ * Instead of manually writing complex structures, use these helpers:
4068
+ *
4069
+ * @example Before (verbose and error-prone)
4070
+ * ```typescript
4071
+ * const order = {
4072
+ * type: 0x0a,
4073
+ * shapeType: 0x01,
4074
+ * shapeData: {
4075
+ * posX: 10,
4076
+ * posY: 20,
4077
+ * width: 5,
4078
+ * height: 3,
4079
+ * filled: true,
4080
+ * charCode: 0x31,
4081
+ * bgColorCode: 1,
4082
+ * fgColorCode: 15
4083
+ * }
4084
+ * };
4085
+ * ```
4086
+ *
4087
+ * @example After (simple and type-safe with string support!)
4088
+ * ```typescript
4089
+ * // Both syntaxes work:
4090
+ * const order1 = OrderBuilder.rectangle(10, 20, 5, 3, {
4091
+ * charCode: '1', // ✨ NEW: Use string directly!
4092
+ * bgColor: 1,
4093
+ * fgColor: 15,
4094
+ * filled: true
4095
+ * });
4096
+ *
4097
+ * const order2 = OrderBuilder.char(10, 20, '#', 15, 0); // ✨ String support!
4098
+ * const order3 = OrderBuilder.char(10, 20, 0x23, 15, 0); // Number still works
4099
+ * ```
4100
+ */
4101
+
4102
+ /**
4103
+ * Common options for graphical orders
4104
+ */
4105
+ interface ColorOptions {
4106
+ charCode?: string | number;
4107
+ bgColor?: number;
4108
+ fgColor?: number;
4109
+ }
4110
+ /**
4111
+ * OrderBuilder - Static factory to create orders
4112
+ */
4113
+ declare class OrderBuilder {
4114
+ /**
4115
+ * Convert string or number to 8-bit character code
4116
+ * @param char - Either a string (first character used) or a number (0-255)
4117
+ * @returns 8-bit character code (0-255)
4118
+ * @example
4119
+ * toCharCode('#') // → 0x23 (35)
4120
+ * toCharCode(0x23) // → 0x23 (35)
4121
+ * toCharCode('ABC') // → 0x41 (65, first char only)
4122
+ */
4123
+ static toCharCode(char: string | number): number;
4124
+ /**
4125
+ * Remove common leading whitespace from multiline strings (dedent)
4126
+ * Useful for template literals that are indented in source code
4127
+ * @param text - Multiline text to dedent
4128
+ * @returns Dedented text
4129
+ * @example
4130
+ * const text = `
4131
+ * Hello
4132
+ * World
4133
+ * `;
4134
+ * dedent(text) // → "Hello\nWorld"
4135
+ */
4136
+ static dedent(text: string): string;
4137
+ /**
4138
+ * 0x01 - Char: Single character at a position
4139
+ * @param char - Character as string ('#') or charCode (0x23)
4140
+ */
4141
+ static char(x: number, y: number, char: string | number, fgColor?: number, bgColor?: number): CharOrder;
4142
+ /**
4143
+ * 0x02 - Text: Horizontal character string
4144
+ */
4145
+ static text(x: number, y: number, text: string, fgColor?: number, bgColor?: number): TextOrder;
4146
+ /**
4147
+ * 0x17 - TextMultiline: Multiple lines of text (\n for line breaks)
4148
+ * Automatically removes common indentation (dedent) for template literals
4149
+ * @example With explicit \n
4150
+ * OrderBuilder.textMultiline(10, 5, 'Hello\nWorld\n!', 15, 0)
4151
+ *
4152
+ * @example With template literal (auto-dedented)
4153
+ * OrderBuilder.textMultiline(10, 5, `
4154
+ * Score: ${score}
4155
+ * Lives: ${lives}
4156
+ * Level: ${level}
4157
+ * `, 15, 0)
4158
+ */
4159
+ static textMultiline(x: number, y: number, text: string, fgColor?: number, bgColor?: number): TextMultilineOrder;
4160
+ /**
4161
+ * 0x03 - SubFrame: Rectangular area with uniform colors
4162
+ * @param frame - Array of characters as strings or numbers
4163
+ */
4164
+ static subFrame(x: number, y: number, width: number, height: number, frame: (string | number)[], fgColor?: number, bgColor?: number): SubFrameOrder;
4165
+ /**
4166
+ * 0x04 - SubFrameMultiColor: Rectangular area with per-cell colors
4167
+ * @param frame - Array of cells where charCode can be string or number
4168
+ */
4169
+ static subFrameMultiColor(x: number, y: number, width: number, height: number, frame: Array<{
4170
+ charCode: string | number;
4171
+ bgColorCode: number;
4172
+ fgColorCode: number;
4173
+ }>): SubFrameMultiColorOrder;
4174
+ /**
4175
+ * 0x05 - FullFrame: Entire layer (256×256) with uniform colors
4176
+ * @param frame - Array of characters as strings or numbers
4177
+ */
4178
+ static fullFrame(frame: (string | number)[], fgColor?: number, bgColor?: number): FullFrameOrder;
4179
+ /**
4180
+ * 0x06 - FullFrameMultiColor: Entire layer (256×256) with per-cell colors
4181
+ * @param frame - Array of cells where charCode can be string or number
4182
+ */
4183
+ static fullFrameMultiColor(frame: Array<{
4184
+ charCode: string | number;
4185
+ bgColorCode: number;
4186
+ fgColorCode: number;
4187
+ }>): FullFrameMultiColorOrder;
4188
+ /**
4189
+ * 0x07 - Sprite: Single-color sprite at a position
4190
+ */
4191
+ static sprite(x: number, y: number, spriteIndex: number, fgColor?: number, bgColor?: number): SpriteOrder;
4192
+ /**
4193
+ * 0x08 - SpriteMultiColor: Multi-color sprite at a position
4194
+ */
4195
+ static spriteMultiColor(x: number, y: number, spriteIndex: number): SpriteMultiColorOrder;
4196
+ /**
4197
+ * 0x09 - ColorMap: Applies colors without changing characters
4198
+ */
4199
+ static colorMap(x: number, y: number, width: number, height: number, colorData: Array<{
4200
+ fgColorCode: number;
4201
+ bgColorCode: number;
4202
+ }>): ColorMapOrder;
4203
+ /**
4204
+ * 0x0A - Rectangle Shape
4205
+ * @param options.charCode - Character as string ('█') or charCode (0x2588)
4206
+ */
4207
+ static rectangle(x: number, y: number, width: number, height: number, options?: {
4208
+ charCode?: string | number;
4209
+ bgColor?: number;
4210
+ fgColor?: number;
4211
+ filled?: boolean;
4212
+ }): ShapeOrder;
4213
+ /**
4214
+ * 0x0A - Circle Shape
4215
+ * @param options.charCode - Character as string ('█') or charCode (0x2588)
4216
+ */
4217
+ static circle(centerX: number, centerY: number, radius: number, options?: {
4218
+ charCode?: string | number;
4219
+ bgColor?: number;
4220
+ fgColor?: number;
4221
+ filled?: boolean;
4222
+ }): ShapeOrder;
4223
+ /**
4224
+ * 0x0A - Line Shape
4225
+ * @param options.charCode - Character as string ('█') or charCode (0x2588)
4226
+ */
4227
+ static line(x1: number, y1: number, x2: number, y2: number, options?: {
4228
+ charCode?: string | number;
4229
+ bgColor?: number;
4230
+ fgColor?: number;
4231
+ }): ShapeOrder;
4232
+ /**
4233
+ * 0x0A - Ellipse Shape
4234
+ * @param options.charCode - Character as string ('█') or charCode (0x2588)
4235
+ */
4236
+ static ellipse(centerX: number, centerY: number, radiusX: number, radiusY: number, options?: {
4237
+ charCode?: string | number;
4238
+ bgColor?: number;
4239
+ fgColor?: number;
4240
+ filled?: boolean;
4241
+ }): ShapeOrder;
4242
+ /**
4243
+ * 0x0A - Triangle Shape
4244
+ * @param options.charCode - Character as string ('█') or charCode (0x2588)
4245
+ */
4246
+ static triangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, options?: {
4247
+ charCode?: string | number;
4248
+ bgColor?: number;
4249
+ fgColor?: number;
4250
+ filled?: boolean;
4251
+ }): ShapeOrder;
4252
+ /**
4253
+ * 0x0B - DotCloud: Same character at multiple positions
4254
+ * @param char - Character as string ('#') or charCode (0x23)
4255
+ */
4256
+ static dotCloud(positions: Array<{
4257
+ posX: number;
4258
+ posY: number;
4259
+ }>, char: string | number, fgColor?: number, bgColor?: number): DotCloudOrder;
4260
+ /**
4261
+ * 0x0C - DotCloudMultiColor: Different characters at multiple positions
4262
+ * @param dots - Array where charCode can be string or number
4263
+ */
4264
+ static dotCloudMultiColor(dots: Array<{
4265
+ posX: number;
4266
+ posY: number;
4267
+ charCode: string | number;
4268
+ bgColorCode: number;
4269
+ fgColorCode: number;
4270
+ }>): DotCloudMultiColorOrder;
4271
+ /**
4272
+ * 0x11 - Bitmask: Bitpacked presence/absence mask with uniform character
4273
+ * Perfect for ore veins, destructible terrain, collision maps, fog of war
4274
+ * @param mask - Flat array of booleans (row-major order: sizeX × sizeY)
4275
+ * @param char - Character as string ('#') or charCode (0x23)
4276
+ * @param override - true: clear absences (transparent), false: preserve existing cells
4277
+ * @example Create a 3×3 cross pattern
4278
+ * OrderBuilder.bitmask(10, 10, 3, 3, [
4279
+ * false, true, false,
4280
+ * true, true, true,
4281
+ * false, true, false
4282
+ * ], '#', 15, 0, false)
4283
+ */
4284
+ static bitmask(x: number, y: number, width: number, height: number, mask: boolean[], char: string | number, fgColor?: number, bgColor?: number, override?: boolean): BitmaskOrder;
4285
+ /**
4286
+ * 0x0D - SpriteCloud: Same single-color sprite at multiple positions
4287
+ */
4288
+ static spriteCloud(spriteIndex: number, positions: Array<{
4289
+ posX: number;
4290
+ posY: number;
4291
+ }>, fgColor?: number, bgColor?: number): SpriteCloudOrder;
4292
+ /**
4293
+ * 0x0E - SpriteCloudMultiColor: Same multi-color sprite at multiple positions
4294
+ */
4295
+ static spriteCloudMultiColor(spriteIndex: number, positions: Array<{
4296
+ posX: number;
4297
+ posY: number;
4298
+ }>): SpriteCloudMultiColorOrder;
4299
+ /**
4300
+ * 0x0F - SpriteCloudVaried: Different single-color sprites at multiple positions
4301
+ */
4302
+ static spriteCloudVaried(sprites: Array<{
4303
+ posX: number;
4304
+ posY: number;
4305
+ spriteIndex: number;
4306
+ bgColorCode: number;
4307
+ fgColorCode: number;
4308
+ }>): SpriteCloudVariedOrder;
4309
+ /**
4310
+ * 0x10 - SpriteCloudVariedMultiColor: Different multi-color sprites at multiple positions
4311
+ */
4312
+ static spriteCloudVariedMultiColor(sprites: Array<{
4313
+ posX: number;
4314
+ posY: number;
4315
+ spriteIndex: number;
4316
+ }>): SpriteCloudVariedMultiColorOrder;
4317
+ /**
4318
+ * 0x13 - Clear: Fills entire layer with a character
4319
+ * @param char - Character as string (' ') or charCode (0x20)
4320
+ */
4321
+ static clear(char?: string | number, fgColor?: number, bgColor?: number): ClearOrder;
4322
+ /**
4323
+ * 0x14 - FillChar: Fills layer with a repeating pattern
4324
+ * @param pattern - Array of characters as strings or numbers
4325
+ */
4326
+ static fillChar(patternWidth: number, patternHeight: number, pattern: (string | number)[], fgColor?: number, bgColor?: number): FillCharOrder;
4327
+ /**
4328
+ * 0x15 - FillSprite: Fills layer with a repeating single-color sprite
4329
+ */
4330
+ static fillSprite(spriteIndex: number, fgColor?: number, bgColor?: number): FillSpriteOrder;
4331
+ /**
4332
+ * 0x16 - FillSpriteMultiColor: Fills layer with a repeating multi-color sprite
4333
+ */
4334
+ static fillSpriteMultiColor(spriteIndex: number): FillSpriteMultiColorOrder;
4335
+ /**
4336
+ * Helper: Create a rectangle with colored border and different interior
4337
+ * @param borderOptions.charCode - Character as string ('█') or charCode (0x2588)
4338
+ * @param fillOptions.charCode - Character as string ('█') or charCode (0x2588)
4339
+ */
4340
+ static boxWithBorder(x: number, y: number, width: number, height: number, borderOptions: ColorOptions & {
4341
+ charCode?: string | number;
4342
+ }, fillOptions: ColorOptions & {
4343
+ charCode?: string | number;
4344
+ }): ShapeOrder[];
4345
+ /**
4346
+ * Helper: Create a grid of points
4347
+ * @param options.charCode - Character as string ('+') or charCode (0x2b)
4348
+ */
4349
+ static grid(startX: number, startY: number, cellWidth: number, cellHeight: number, rows: number, cols: number, options?: ColorOptions): DotCloudOrder;
4350
+ }
4351
+
4352
+ /**
4353
+ * UTSP Protocol Constants
4354
+ * Fixed and minimum sizes for network packets (in bytes)
4355
+ * Shared between encoding and decoding
4356
+ */
4357
+ /**
4358
+ * Immutable layer size (in cells)
4359
+ * All layers are always 256×256 cells = 65536 cells total
4360
+ */
4361
+ declare const LAYER_SIZE = 256;
4362
+ declare const LAYER_CELL_COUNT = 65536;
4363
+ /**
4364
+ * Sentinel value to indicate "no color" (transparent)
4365
+ * ColorCode 255 = skip this color (look in following layers)
4366
+ * ColorCodes 0-254 = real colors definable by user
4367
+ *
4368
+ * IMPORTANT NOTE:
4369
+ * - ColorID 255 is RESERVED by the engine and cannot be modified via setColor()
4370
+ * - Palette can contain 255 customizable colors (IDs 0-254)
4371
+ * - Total: 256 possible ColorIDs (0-255) but only 0-254 are usable
4372
+ * - Engine forces ColorID 255 = rgba(0,0,0,0) at startup (transparency)
4373
+ */
4374
+ declare const COLOR_SKIP = 255;
4375
+ declare const UPDATE_PACKET_HEADER_SIZE = 9;
4376
+ declare const DISPLAY_HEADER_SIZE = 5;
4377
+ declare const LAYER_HEADER_SIZE = 13;
4378
+ declare const CHAR_ORDER_SIZE = 6;
4379
+ declare const TEXT_ORDER_MIN_SIZE = 6;
4380
+ declare const TEXT_MULTILINE_ORDER_MIN_SIZE = 6;
4381
+ declare const SUBFRAME_ORDER_MIN_SIZE = 7;
4382
+ declare const SUBFRAME_MULTICOLOR_ORDER_MIN_SIZE = 5;
4383
+ declare const FULLFRAME_ORDER_MIN_SIZE = 3;
4384
+ declare const FULLFRAME_MULTICOLOR_ORDER_MIN_SIZE = 1;
4385
+ declare const SPRITE_ORDER_SIZE = 6;
4386
+ declare const SPRITE_MULTICOLOR_ORDER_SIZE = 4;
4387
+ declare const COLORMAP_ORDER_MIN_SIZE = 5;
4388
+ declare const SHAPE_ORDER_MIN_SIZE = 2;
4389
+ declare const RECTANGLE_SHAPE_SIZE = 8;
4390
+ declare const CIRCLE_SHAPE_SIZE = 7;
4391
+ declare const LINE_SHAPE_SIZE = 7;
4392
+ declare const ELLIPSE_SHAPE_SIZE = 8;
4393
+ declare const TRIANGLE_SHAPE_SIZE = 10;
4394
+ declare const DOTCLOUD_ORDER_MIN_SIZE = 6;
4395
+ declare const DOTCLOUD_MULTICOLOR_ORDER_MIN_SIZE = 3;
4396
+ declare const BITMASK_ORDER_MIN_SIZE = 9;
4397
+ declare const SPRITECLOUD_ORDER_MIN_SIZE = 6;
4398
+ declare const SPRITECLOUD_MULTICOLOR_ORDER_MIN_SIZE = 4;
4399
+ declare const SPRITECLOUD_VARIED_ORDER_MIN_SIZE = 3;
4400
+ declare const SPRITECLOUD_VARIED_MULTICOLOR_ORDER_MIN_SIZE = 3;
4401
+ declare const CLEAR_ORDER_SIZE = 4;
4402
+ declare const FILLCHAR_ORDER_MIN_SIZE = 5;
4403
+ declare const FILLSPRITE_ORDER_SIZE = 4;
4404
+ declare const FILLSPRITE_MULTICOLOR_ORDER_SIZE = 2;
4405
+ declare const TRIGGERSOUND_ORDER_SIZE = 5;
4406
+ declare const TRIGGERGLOBALSOUND_ORDER_SIZE = 3;
4407
+
4408
+ /**
4409
+ * UTSP Order Types Enumeration
4410
+ *
4411
+ * All 21 order types defined in the UTSP Protocol v0.1
4412
+ * Use these constants instead of raw hexadecimal values for better readability
4413
+ * and type safety.
4414
+ *
4415
+ * @example
4416
+ * ```typescript
4417
+ * import { OrderType } from 'utsp-core';
4418
+ *
4419
+ * // Instead of: type: 0x01
4420
+ * const order = { type: OrderType.Char, posX: 10, posY: 5, ... };
4421
+ * ```
4422
+ */
4423
+ declare enum OrderType {
4424
+ /**
4425
+ * 0x01 - Char: Renders a single character at a specific position
4426
+ */
4427
+ Char = 1,
4428
+ /**
4429
+ * 0x02 - Text: Renders a string of characters with uniform colors
4430
+ */
4431
+ Text = 2,
4432
+ /**
4433
+ * 0x17 - TextMultiline: Renders multiple lines of text (\n for line breaks)
4434
+ */
4435
+ TextMultiline = 23,
4436
+ /**
4437
+ * 0x03 - SubFrame: Renders a rectangular region with uniform colors
4438
+ */
4439
+ SubFrame = 3,
4440
+ /**
4441
+ * 0x04 - SubFrameMultiColor: Rectangular region with per-cell colors
4442
+ */
4443
+ SubFrameMultiColor = 4,
4444
+ /**
4445
+ * 0x05 - FullFrame: Renders entire screen with uniform colors
4446
+ */
4447
+ FullFrame = 5,
4448
+ /**
4449
+ * 0x06 - FullFrameMultiColor: Entire screen with per-cell colors
4450
+ */
4451
+ FullFrameMultiColor = 6,
4452
+ /**
4453
+ * 0x07 - Sprite: Renders a preloaded unicolor sprite
4454
+ */
4455
+ Sprite = 7,
4456
+ /**
4457
+ * 0x08 - SpriteMultiColor: Renders a preloaded multicolor sprite
4458
+ */
4459
+ SpriteMultiColor = 8,
4460
+ /**
4461
+ * 0x09 - ColorMap: Applies colors to a region without changing characters
4462
+ */
4463
+ ColorMap = 9,
4464
+ /**
4465
+ * 0x0A - Shape: Renders geometric shapes
4466
+ */
4467
+ Shape = 10,
4468
+ /**
4469
+ * 0x0B - DotCloud: Same character at multiple positions (up to 65535)
4470
+ */
4471
+ DotCloud = 11,
4472
+ /**
4473
+ * 0x0C - DotCloudMultiColor: Different characters at multiple positions
4474
+ */
4475
+ DotCloudMultiColor = 12,
4476
+ /**
4477
+ * 0x0D - SpriteCloud: Same unicolor sprite at multiple positions
4478
+ */
4479
+ SpriteCloud = 13,
4480
+ /**
4481
+ * 0x0E - SpriteCloudMultiColor: Same multicolor sprite at multiple positions
4482
+ */
4483
+ SpriteCloudMultiColor = 14,
4484
+ /**
4485
+ * 0x0F - SpriteCloudVaried: Different unicolor sprites at multiple positions
4486
+ */
4487
+ SpriteCloudVaried = 15,
4488
+ /**
4489
+ * 0x10 - SpriteCloudVariedMultiColor: Different multicolor sprites at multiple positions
4490
+ */
4491
+ SpriteCloudVariedMultiColor = 16,
4492
+ /**
4493
+ * 0x11 - Bitmask: Bitpacked presence/absence mask with uniform character and colors
4494
+ */
4495
+ Bitmask = 17,
4496
+ /**
4497
+ * 0x13 - Clear: Fills entire layer with single character and colors
4498
+ */
4499
+ Clear = 19,
4500
+ /**
4501
+ * 0x14 - FillChar: Fills layer with repeating character pattern
4502
+ */
4503
+ FillChar = 20,
4504
+ /**
4505
+ * 0x15 - FillSprite: Fills layer with repeating unicolor sprite
4506
+ */
4507
+ FillSprite = 21,
4508
+ /**
4509
+ * 0x16 - FillSpriteMultiColor: Fills layer with repeating multicolor sprite
4510
+ */
4511
+ FillSpriteMultiColor = 22,
4512
+ /**
4513
+ * 0x20 - TriggerSound: Triggers positional sound effect
4514
+ */
4515
+ TriggerSound = 32,
4516
+ /**
4517
+ * 0x21 - TriggerGlobalSound: Triggers global (non-positional) sound
4518
+ */
4519
+ TriggerGlobalSound = 33
4520
+ }
4521
+ /**
4522
+ * Type guard to check if a number is a valid OrderType
4523
+ */
4524
+ declare function isValidOrderType(type: number): type is OrderType;
4525
+ /**
4526
+ * Get human-readable name for an OrderType
4527
+ */
4528
+ declare function getOrderTypeName(type: OrderType | number): string;
4529
+
4530
+ /**
4531
+ * UpdateFlags to optimize layer transmission
4532
+ *
4533
+ * Bitpacked flags (8 bits) to indicate layer state and changes.
4534
+ * Allows optimizing rendering and network transmission.
4535
+ *
4536
+ * @see UTSP Protocol section 5.4 - Layer UpdateFlags
4537
+ */
4538
+ declare enum UpdateFlags {
4539
+ /**
4540
+ * Bit 0: Layer Enabled
4541
+ *
4542
+ * Indicates if the layer is enabled and should be rendered.
4543
+ * - 1 = Layer enabled, will be rendered by DisplayRasterizer
4544
+ * - 0 = Layer disabled, ignored during rendering
4545
+ *
4546
+ * Use cases:
4547
+ * - Show/hide layers without deleting them
4548
+ * - Toggle UI elements (inventory, menu, etc.)
4549
+ * - Temporarily disable a layer for optimization
4550
+ *
4551
+ * @example
4552
+ * layer.setEnabled(true);
4553
+ * // → UpdateFlags |= LayerEnabled (0x01)
4554
+ */
4555
+ LayerEnabled = 1,
4556
+ /**
4557
+ * Bit 1: SET Mode (vs ADD mode)
4558
+ *
4559
+ * Indicates the order rasterization mode:
4560
+ * - 1 = SET mode → LayerRasterizer clears buffer before rasterizing
4561
+ * - 0 = ADD mode → LayerRasterizer adds to existing buffer
4562
+ *
4563
+ * Use cases:
4564
+ * - SET mode: layer.setOrders() - completely replaces content
4565
+ * - ADD mode: layer.addOrders() - adds to existing content
4566
+ *
4567
+ * IMPORTANT: Corresponds to RasterizeMode.SET vs RasterizeMode.ADD
4568
+ *
4569
+ * @example
4570
+ * layer.setOrders([...newOrders]); // SET mode
4571
+ * // → UpdateFlags |= SetMode (0x02)
4572
+ *
4573
+ * layer.addOrders([...moreOrders]); // ADD mode
4574
+ * // → UpdateFlags &= ~SetMode (bit 1 to 0)
4575
+ */
4576
+ SetMode = 2,
4577
+ /**
4578
+ * Bit 2: Update position (originX/Y)
4579
+ *
4580
+ * Indicates that the layer's origin has changed.
4581
+ * If this bit is not set, the decoder keeps the existing position.
4582
+ *
4583
+ * Use cases:
4584
+ * - Parallax scrolling
4585
+ * - Camera following player
4586
+ * - Move a layer without modifying its content
4587
+ *
4588
+ * @example
4589
+ * layer.setOrigin(new Vector2(100, 50));
4590
+ * // → UpdateFlags |= UpdatePosition (0x04)
4591
+ */
4592
+ UpdatePosition = 4,
4593
+ /**
4594
+ * Bit 3: Update z-index order
4595
+ *
4596
+ * Indicates that the layer's z-index has changed.
4597
+ * If this bit is not set, the decoder keeps the existing z-index.
4598
+ *
4599
+ * Use cases:
4600
+ * - Dynamically reorder layers
4601
+ * - Change rendering priority
4602
+ * - Bring a layer to the foreground
4603
+ *
4604
+ * @example
4605
+ * layer.setZOrder(10);
4606
+ * // → UpdateFlags |= UpdateZOrder (0x08)
4607
+ */
4608
+ UpdateZOrder = 8,
4609
+ /**
4610
+ * No changes, layer disabled
4611
+ * Layer ignored during rendering
4612
+ */
4613
+ Disabled = 0,
4614
+ /**
4615
+ * Layer enabled, ADD mode, no changes
4616
+ * Used to maintain an active layer without modifications
4617
+ */
4618
+ EnabledAddMode = 1,// 0x01
4619
+ /**
4620
+ * Layer enabled, SET mode, no changes
4621
+ * Active layer in SET mode (overwrites buffer)
4622
+ */
4623
+ EnabledSetMode = 3,// 0x03
4624
+ /**
4625
+ * Layer enabled, ADD mode, position changed (parallax scrolling)
4626
+ * Typical use case: scrolling background
4627
+ */
4628
+ EnabledAddMoveOnly = 5,// 0x05
4629
+ /**
4630
+ * Layer enabled, SET mode, position changed
4631
+ */
4632
+ EnabledSetMoveOnly = 7,// 0x07
4633
+ /**
4634
+ * Layer enabled, ADD mode, z-index changed
4635
+ */
4636
+ EnabledAddReorderOnly = 9,// 0x09
4637
+ /**
4638
+ * Layer enabled, SET mode, z-index changed
4639
+ */
4640
+ EnabledSetReorderOnly = 11,// 0x0B
4641
+ /**
4642
+ * Full update: Layer enabled, SET mode, position + z-index changed
4643
+ * Used for a complete change
4644
+ */
4645
+ FullUpdate = 15
4646
+ }
4647
+ /**
4648
+ * Helper functions to manipulate UpdateFlags
4649
+ */
4650
+ declare namespace UpdateFlagsHelper {
4651
+ /**
4652
+ * Checks if a specific flag is enabled
4653
+ */
4654
+ function hasFlag(flags: number, flag: UpdateFlags): boolean;
4655
+ /**
4656
+ * Adds a flag
4657
+ */
4658
+ function addFlag(flags: number, flag: UpdateFlags): number;
4659
+ /**
4660
+ * Removes a flag
4661
+ */
4662
+ function removeFlag(flags: number, flag: UpdateFlags): number;
4663
+ /**
4664
+ * Combines multiple flags
4665
+ */
4666
+ function combine(...flags: UpdateFlags[]): number;
4667
+ /**
4668
+ * Checks if flags are valid (bits 4-7 must be 0)
4669
+ */
4670
+ function isValid(flags: number): boolean;
4671
+ /**
4672
+ * Checks if the layer is enabled (bit 0)
4673
+ */
4674
+ function isEnabled(flags: number): boolean;
4675
+ /**
4676
+ * Checks if the mode is SET (bit 1)
4677
+ */
4678
+ function isSetMode(flags: number): boolean;
4679
+ /**
4680
+ * Checks if the position has changed (bit 2)
4681
+ */
4682
+ function hasPositionChanged(flags: number): boolean;
4683
+ /**
4684
+ * Checks if the z-index has changed (bit 3)
4685
+ */
4686
+ function hasZOrderChanged(flags: number): boolean;
4687
+ /**
4688
+ * Returns a human-readable description of active flags
4689
+ */
4690
+ function describe(flags: number): string;
4691
+ }
4692
+
4693
+ /**
4694
+ * Decoder for UTSP Update Packets
4695
+ * Decodes complete update packets following the UTSP protocol specification (new architecture)
4696
+ */
4697
+ declare class UpdatePacketDecoder {
4698
+ private displayDecoder;
4699
+ private layerDecoder;
4700
+ constructor();
4701
+ /**
4702
+ * Decodes an UpdatePacket from binary buffer
4703
+ *
4704
+ * Structure (NEW PROTOCOL):
4705
+ * - Tick: 8 bytes (big-endian, 64-bit unsigned integer)
4706
+ * - DisplayCount: 1 byte (8-bit unsigned integer, max 255 displays)
4707
+ * - Displays: variable (DisplayId + OriginX + OriginY + SizeX + SizeY = 7 bytes each)
4708
+ * - LayerCount: 2 bytes (big-endian, 16-bit unsigned integer)
4709
+ * - Layers: variable (encoded layers)
4710
+ *
4711
+ * Minimum packet size: 11 bytes (Tick + DisplayCount + LayerCount)
4712
+ */
4713
+ decode(data: Uint8Array, offset?: number): UpdatePacket;
4714
+ /**
4715
+ * Checks if a buffer contains a valid update packet header
4716
+ * Useful for validation before decoding
4717
+ */
4718
+ isValid(data: Uint8Array, offset?: number): boolean;
4719
+ /**
4720
+ * Decodes only the tick and counts without decoding the displays/layers
4721
+ * Useful for quick inspection of packet metadata
4722
+ */
4723
+ decodeHeader(data: Uint8Array, offset?: number): {
4724
+ tick: number;
4725
+ displayCount: number;
4726
+ layerCount: number;
4727
+ };
4728
+ private checkSize;
4729
+ }
4730
+
4731
+ export { ASCII_8X8_FONT, BITMASK_ORDER_MIN_SIZE, BitmapFont, BitmapFontRegistry, CHAR_ORDER_SIZE, CIRCLE_SHAPE_SIZE, CLEAR_ORDER_SIZE, COLORMAP_ORDER_MIN_SIZE, COLOR_SKIP, CellBuffer, CharCodeBuffer, Core, CoreStats, DISPLAY_HEADER_SIZE, DOTCLOUD_MULTICOLOR_ORDER_MIN_SIZE, DOTCLOUD_ORDER_MIN_SIZE, Display, ELLIPSE_SHAPE_SIZE, FILLCHAR_ORDER_MIN_SIZE, FILLSPRITE_MULTICOLOR_ORDER_SIZE, FILLSPRITE_ORDER_SIZE, FULLFRAME_MULTICOLOR_ORDER_MIN_SIZE, FULLFRAME_ORDER_MIN_SIZE, FontType, InputBindingRegistry, LAYER_CELL_COUNT, LAYER_HEADER_SIZE, LAYER_SIZE, LINE_SHAPE_SIZE, Layer, LoadType, OrderBuilder, OrderType, RECTANGLE_SHAPE_SIZE, SHAPE_ORDER_MIN_SIZE, SPRITECLOUD_MULTICOLOR_ORDER_MIN_SIZE, SPRITECLOUD_ORDER_MIN_SIZE, SPRITECLOUD_VARIED_MULTICOLOR_ORDER_MIN_SIZE, SPRITECLOUD_VARIED_ORDER_MIN_SIZE, SPRITE_MULTICOLOR_ORDER_SIZE, SPRITE_ORDER_SIZE, SUBFRAME_MULTICOLOR_ORDER_MIN_SIZE, SUBFRAME_ORDER_MIN_SIZE, ShapeType, SpriteRegistry, TEXT_MULTILINE_ORDER_MIN_SIZE, TEXT_ORDER_MIN_SIZE, TRIANGLE_SHAPE_SIZE, TRIGGERGLOBALSOUND_ORDER_SIZE, TRIGGERSOUND_ORDER_SIZE, UPDATE_PACKET_HEADER_SIZE, UpdateFlags, UpdateFlagsHelper, UpdatePacketDecoder, User, UserStats, WebFont, WebFontRegistry, createASCII8x8FontLoad, createEmptyCompressedInputPacket, decodeCompressedInput, decodeInt8ToAxis, encodeAxisToInt8, encodeCompressedInput, getASCII8x8FontConfig, getAllCharCodes, getButtonByteCount, getCharBitmap, getCompressedPacketSize, getOrderTypeName, hasChar, isValidOrderType };
4732
+ export type { AnyLoad, BitmapFontConfig, BitmapFontLoad, Cell, CircleShape, Color, ColorPaletteLoad, CompressedInputPacket, CoreMode, CoreOptions, EllipseShape, LineShape, MulticolorCell, MulticolorSprite, MulticolorSpriteLoad, NetworkDisplay, NetworkLayer, RectangleShape, ShapeData, SoundLoad, SpriteLoad, TickStats, TriangleShape, UnicolorSprite, UpdatePacket, UserTickStats, WebFontConfig, WebFontLoad };