@esengine/server 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- export { createHttpRouter, createServer } from './chunk-M7VONMZJ.js';
1
+ export { ECSRoom } from './chunk-ZUTL4RI7.js';
2
+ export { DistributedRoomManager, MemoryAdapter, RoomManager, createHttpRouter, createServer } from './chunk-NWZLKNGV.js';
2
3
  import './chunk-I4QQSQ72.js';
3
- export { Player, Room, onMessage } from './chunk-FACTBKJ3.js';
4
- import { __name } from './chunk-T626JPC7.js';
4
+ export { Player, Room, onMessage } from './chunk-T3QJOPNG.js';
5
+ import { __name, __publicField } from './chunk-T626JPC7.js';
5
6
  export { ErrorCode, RpcError } from '@esengine/rpc';
6
7
 
7
8
  // src/helpers/define.ts
@@ -9,15 +10,1512 @@ function defineApi(definition) {
9
10
  return definition;
10
11
  }
11
12
  __name(defineApi, "defineApi");
13
+ function defineApiWithSchema(schema, definition) {
14
+ return {
15
+ ...definition,
16
+ schema
17
+ };
18
+ }
19
+ __name(defineApiWithSchema, "defineApiWithSchema");
12
20
  function defineMsg(definition) {
13
21
  return definition;
14
22
  }
15
23
  __name(defineMsg, "defineMsg");
24
+ function defineMsgWithSchema(schema, definition) {
25
+ return {
26
+ ...definition,
27
+ schema
28
+ };
29
+ }
30
+ __name(defineMsgWithSchema, "defineMsgWithSchema");
16
31
  function defineHttp(definition) {
17
32
  return definition;
18
33
  }
19
34
  __name(defineHttp, "defineHttp");
20
35
 
21
- export { defineApi, defineHttp, defineMsg };
36
+ // src/schema/base.ts
37
+ var _BaseValidator = class _BaseValidator {
38
+ constructor() {
39
+ __publicField(this, "_options", {});
40
+ }
41
+ validate(value, path = []) {
42
+ if (value === void 0) {
43
+ if (this._options.isOptional) {
44
+ if (this._options.defaultValue !== void 0) {
45
+ return {
46
+ success: true,
47
+ data: this._options.defaultValue
48
+ };
49
+ }
50
+ return {
51
+ success: true,
52
+ data: void 0
53
+ };
54
+ }
55
+ return {
56
+ success: false,
57
+ error: {
58
+ path,
59
+ message: "Required",
60
+ expected: this.typeName,
61
+ received: void 0
62
+ }
63
+ };
64
+ }
65
+ if (value === null) {
66
+ if (this._options.isNullable) {
67
+ return {
68
+ success: true,
69
+ data: null
70
+ };
71
+ }
72
+ return {
73
+ success: false,
74
+ error: {
75
+ path,
76
+ message: "Expected non-null value",
77
+ expected: this.typeName,
78
+ received: null
79
+ }
80
+ };
81
+ }
82
+ return this._validate(value, path);
83
+ }
84
+ is(value) {
85
+ return this.validate(value).success;
86
+ }
87
+ optional() {
88
+ const clone = this._clone();
89
+ clone._options.isOptional = true;
90
+ return clone;
91
+ }
92
+ default(defaultValue) {
93
+ const clone = this._clone();
94
+ clone._options.isOptional = true;
95
+ clone._options.defaultValue = defaultValue;
96
+ return clone;
97
+ }
98
+ nullable() {
99
+ const clone = this._clone();
100
+ clone._options.isNullable = true;
101
+ return clone;
102
+ }
103
+ };
104
+ __name(_BaseValidator, "BaseValidator");
105
+ var BaseValidator = _BaseValidator;
106
+
107
+ // src/schema/primitives.ts
108
+ var _StringValidator = class _StringValidator extends BaseValidator {
109
+ constructor() {
110
+ super(...arguments);
111
+ __publicField(this, "typeName", "string");
112
+ __publicField(this, "_stringOptions", {});
113
+ }
114
+ _validate(value, path) {
115
+ if (typeof value !== "string") {
116
+ return {
117
+ success: false,
118
+ error: {
119
+ path,
120
+ message: `Expected string, received ${typeof value}`,
121
+ expected: "string",
122
+ received: value
123
+ }
124
+ };
125
+ }
126
+ const { minLength, maxLength, pattern } = this._stringOptions;
127
+ if (minLength !== void 0 && value.length < minLength) {
128
+ return {
129
+ success: false,
130
+ error: {
131
+ path,
132
+ message: `String must be at least ${minLength} characters`,
133
+ expected: `string(minLength: ${minLength})`,
134
+ received: value
135
+ }
136
+ };
137
+ }
138
+ if (maxLength !== void 0 && value.length > maxLength) {
139
+ return {
140
+ success: false,
141
+ error: {
142
+ path,
143
+ message: `String must be at most ${maxLength} characters`,
144
+ expected: `string(maxLength: ${maxLength})`,
145
+ received: value
146
+ }
147
+ };
148
+ }
149
+ if (pattern && !pattern.test(value)) {
150
+ return {
151
+ success: false,
152
+ error: {
153
+ path,
154
+ message: `String does not match pattern ${pattern}`,
155
+ expected: `string(pattern: ${pattern})`,
156
+ received: value
157
+ }
158
+ };
159
+ }
160
+ return {
161
+ success: true,
162
+ data: value
163
+ };
164
+ }
165
+ _clone() {
166
+ const clone = new _StringValidator();
167
+ clone._options = {
168
+ ...this._options
169
+ };
170
+ clone._stringOptions = {
171
+ ...this._stringOptions
172
+ };
173
+ return clone;
174
+ }
175
+ /**
176
+ * @zh 设置最小长度
177
+ * @en Set minimum length
178
+ */
179
+ min(length) {
180
+ const clone = this._clone();
181
+ clone._stringOptions.minLength = length;
182
+ return clone;
183
+ }
184
+ /**
185
+ * @zh 设置最大长度
186
+ * @en Set maximum length
187
+ */
188
+ max(length) {
189
+ const clone = this._clone();
190
+ clone._stringOptions.maxLength = length;
191
+ return clone;
192
+ }
193
+ /**
194
+ * @zh 设置长度范围
195
+ * @en Set length range
196
+ */
197
+ length(min, max) {
198
+ const clone = this._clone();
199
+ clone._stringOptions.minLength = min;
200
+ clone._stringOptions.maxLength = max;
201
+ return clone;
202
+ }
203
+ /**
204
+ * @zh 设置正则模式
205
+ * @en Set regex pattern
206
+ */
207
+ regex(pattern) {
208
+ const clone = this._clone();
209
+ clone._stringOptions.pattern = pattern;
210
+ return clone;
211
+ }
212
+ /**
213
+ * @zh 邮箱格式验证
214
+ * @en Email format validation
215
+ */
216
+ email() {
217
+ return this.regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
218
+ }
219
+ /**
220
+ * @zh URL 格式验证
221
+ * @en URL format validation
222
+ */
223
+ url() {
224
+ return this.regex(/^https?:\/\/.+/);
225
+ }
226
+ };
227
+ __name(_StringValidator, "StringValidator");
228
+ var StringValidator = _StringValidator;
229
+ var _NumberValidator = class _NumberValidator extends BaseValidator {
230
+ constructor() {
231
+ super(...arguments);
232
+ __publicField(this, "typeName", "number");
233
+ __publicField(this, "_numberOptions", {});
234
+ }
235
+ _validate(value, path) {
236
+ if (typeof value !== "number" || Number.isNaN(value)) {
237
+ return {
238
+ success: false,
239
+ error: {
240
+ path,
241
+ message: `Expected number, received ${typeof value}`,
242
+ expected: "number",
243
+ received: value
244
+ }
245
+ };
246
+ }
247
+ const { min, max, integer } = this._numberOptions;
248
+ if (integer && !Number.isInteger(value)) {
249
+ return {
250
+ success: false,
251
+ error: {
252
+ path,
253
+ message: "Expected integer",
254
+ expected: "integer",
255
+ received: value
256
+ }
257
+ };
258
+ }
259
+ if (min !== void 0 && value < min) {
260
+ return {
261
+ success: false,
262
+ error: {
263
+ path,
264
+ message: `Number must be >= ${min}`,
265
+ expected: `number(min: ${min})`,
266
+ received: value
267
+ }
268
+ };
269
+ }
270
+ if (max !== void 0 && value > max) {
271
+ return {
272
+ success: false,
273
+ error: {
274
+ path,
275
+ message: `Number must be <= ${max}`,
276
+ expected: `number(max: ${max})`,
277
+ received: value
278
+ }
279
+ };
280
+ }
281
+ return {
282
+ success: true,
283
+ data: value
284
+ };
285
+ }
286
+ _clone() {
287
+ const clone = new _NumberValidator();
288
+ clone._options = {
289
+ ...this._options
290
+ };
291
+ clone._numberOptions = {
292
+ ...this._numberOptions
293
+ };
294
+ return clone;
295
+ }
296
+ /**
297
+ * @zh 设置最小值
298
+ * @en Set minimum value
299
+ */
300
+ min(value) {
301
+ const clone = this._clone();
302
+ clone._numberOptions.min = value;
303
+ return clone;
304
+ }
305
+ /**
306
+ * @zh 设置最大值
307
+ * @en Set maximum value
308
+ */
309
+ max(value) {
310
+ const clone = this._clone();
311
+ clone._numberOptions.max = value;
312
+ return clone;
313
+ }
314
+ /**
315
+ * @zh 设置范围
316
+ * @en Set range
317
+ */
318
+ range(min, max) {
319
+ const clone = this._clone();
320
+ clone._numberOptions.min = min;
321
+ clone._numberOptions.max = max;
322
+ return clone;
323
+ }
324
+ /**
325
+ * @zh 要求为整数
326
+ * @en Require integer
327
+ */
328
+ int() {
329
+ const clone = this._clone();
330
+ clone._numberOptions.integer = true;
331
+ return clone;
332
+ }
333
+ /**
334
+ * @zh 要求为正数
335
+ * @en Require positive
336
+ */
337
+ positive() {
338
+ return this.min(0);
339
+ }
340
+ /**
341
+ * @zh 要求为负数
342
+ * @en Require negative
343
+ */
344
+ negative() {
345
+ return this.max(0);
346
+ }
347
+ };
348
+ __name(_NumberValidator, "NumberValidator");
349
+ var NumberValidator = _NumberValidator;
350
+ var _BooleanValidator = class _BooleanValidator extends BaseValidator {
351
+ constructor() {
352
+ super(...arguments);
353
+ __publicField(this, "typeName", "boolean");
354
+ }
355
+ _validate(value, path) {
356
+ if (typeof value !== "boolean") {
357
+ return {
358
+ success: false,
359
+ error: {
360
+ path,
361
+ message: `Expected boolean, received ${typeof value}`,
362
+ expected: "boolean",
363
+ received: value
364
+ }
365
+ };
366
+ }
367
+ return {
368
+ success: true,
369
+ data: value
370
+ };
371
+ }
372
+ _clone() {
373
+ const clone = new _BooleanValidator();
374
+ clone._options = {
375
+ ...this._options
376
+ };
377
+ return clone;
378
+ }
379
+ };
380
+ __name(_BooleanValidator, "BooleanValidator");
381
+ var BooleanValidator = _BooleanValidator;
382
+ var _LiteralValidator = class _LiteralValidator extends BaseValidator {
383
+ constructor(literal2) {
384
+ super();
385
+ __publicField(this, "typeName");
386
+ __publicField(this, "_literal");
387
+ this._literal = literal2;
388
+ this.typeName = `literal(${JSON.stringify(literal2)})`;
389
+ }
390
+ _validate(value, path) {
391
+ if (value !== this._literal) {
392
+ return {
393
+ success: false,
394
+ error: {
395
+ path,
396
+ message: `Expected ${JSON.stringify(this._literal)}, received ${JSON.stringify(value)}`,
397
+ expected: this.typeName,
398
+ received: value
399
+ }
400
+ };
401
+ }
402
+ return {
403
+ success: true,
404
+ data: value
405
+ };
406
+ }
407
+ _clone() {
408
+ const clone = new _LiteralValidator(this._literal);
409
+ clone._options = {
410
+ ...this._options
411
+ };
412
+ return clone;
413
+ }
414
+ };
415
+ __name(_LiteralValidator, "LiteralValidator");
416
+ var LiteralValidator = _LiteralValidator;
417
+ var _AnyValidator = class _AnyValidator extends BaseValidator {
418
+ constructor() {
419
+ super(...arguments);
420
+ __publicField(this, "typeName", "any");
421
+ }
422
+ _validate(value) {
423
+ return {
424
+ success: true,
425
+ data: value
426
+ };
427
+ }
428
+ _clone() {
429
+ const clone = new _AnyValidator();
430
+ clone._options = {
431
+ ...this._options
432
+ };
433
+ return clone;
434
+ }
435
+ };
436
+ __name(_AnyValidator, "AnyValidator");
437
+ var AnyValidator = _AnyValidator;
438
+ function string() {
439
+ return new StringValidator();
440
+ }
441
+ __name(string, "string");
442
+ function number() {
443
+ return new NumberValidator();
444
+ }
445
+ __name(number, "number");
446
+ function boolean() {
447
+ return new BooleanValidator();
448
+ }
449
+ __name(boolean, "boolean");
450
+ function literal(value) {
451
+ return new LiteralValidator(value);
452
+ }
453
+ __name(literal, "literal");
454
+ function any() {
455
+ return new AnyValidator();
456
+ }
457
+ __name(any, "any");
458
+
459
+ // src/schema/composites.ts
460
+ var _ObjectValidator = class _ObjectValidator extends BaseValidator {
461
+ constructor(shape) {
462
+ super();
463
+ __publicField(this, "typeName", "object");
464
+ __publicField(this, "_shape");
465
+ __publicField(this, "_objectOptions", {});
466
+ this._shape = shape;
467
+ }
468
+ _validate(value, path) {
469
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
470
+ return {
471
+ success: false,
472
+ error: {
473
+ path,
474
+ message: `Expected object, received ${Array.isArray(value) ? "array" : typeof value}`,
475
+ expected: "object",
476
+ received: value
477
+ }
478
+ };
479
+ }
480
+ const result = {};
481
+ const obj = value;
482
+ for (const [key, validator] of Object.entries(this._shape)) {
483
+ const fieldValue = obj[key];
484
+ const fieldPath = [
485
+ ...path,
486
+ key
487
+ ];
488
+ const fieldResult = validator.validate(fieldValue, fieldPath);
489
+ if (!fieldResult.success) {
490
+ return fieldResult;
491
+ }
492
+ result[key] = fieldResult.data;
493
+ }
494
+ if (this._objectOptions.strict) {
495
+ const knownKeys = new Set(Object.keys(this._shape));
496
+ for (const key of Object.keys(obj)) {
497
+ if (!knownKeys.has(key)) {
498
+ return {
499
+ success: false,
500
+ error: {
501
+ path: [
502
+ ...path,
503
+ key
504
+ ],
505
+ message: `Unknown key "${key}"`,
506
+ expected: "known key",
507
+ received: key
508
+ }
509
+ };
510
+ }
511
+ }
512
+ }
513
+ return {
514
+ success: true,
515
+ data: result
516
+ };
517
+ }
518
+ _clone() {
519
+ const clone = new _ObjectValidator(this._shape);
520
+ clone._options = {
521
+ ...this._options
522
+ };
523
+ clone._objectOptions = {
524
+ ...this._objectOptions
525
+ };
526
+ return clone;
527
+ }
528
+ /**
529
+ * @zh 严格模式(不允许额外字段)
530
+ * @en Strict mode (no extra fields allowed)
531
+ */
532
+ strict() {
533
+ const clone = this._clone();
534
+ clone._objectOptions.strict = true;
535
+ return clone;
536
+ }
537
+ /**
538
+ * @zh 部分模式(所有字段可选)
539
+ * @en Partial mode (all fields optional)
540
+ */
541
+ partial() {
542
+ const partialShape = {};
543
+ for (const [key, validator] of Object.entries(this._shape)) {
544
+ partialShape[key] = validator.optional();
545
+ }
546
+ return new _ObjectValidator(partialShape);
547
+ }
548
+ /**
549
+ * @zh 选择部分字段
550
+ * @en Pick specific fields
551
+ */
552
+ pick(...keys) {
553
+ const pickedShape = {};
554
+ for (const key of keys) {
555
+ pickedShape[key] = this._shape[key];
556
+ }
557
+ return new _ObjectValidator(pickedShape);
558
+ }
559
+ /**
560
+ * @zh 排除部分字段
561
+ * @en Omit specific fields
562
+ */
563
+ omit(...keys) {
564
+ const keySet = new Set(keys);
565
+ const omittedShape = {};
566
+ for (const [key, validator] of Object.entries(this._shape)) {
567
+ if (!keySet.has(key)) {
568
+ omittedShape[key] = validator;
569
+ }
570
+ }
571
+ return new _ObjectValidator(omittedShape);
572
+ }
573
+ /**
574
+ * @zh 扩展对象 Schema
575
+ * @en Extend object schema
576
+ */
577
+ extend(shape) {
578
+ const extendedShape = {
579
+ ...this._shape,
580
+ ...shape
581
+ };
582
+ return new _ObjectValidator(extendedShape);
583
+ }
584
+ };
585
+ __name(_ObjectValidator, "ObjectValidator");
586
+ var ObjectValidator = _ObjectValidator;
587
+ var _ArrayValidator = class _ArrayValidator extends BaseValidator {
588
+ constructor(element) {
589
+ super();
590
+ __publicField(this, "typeName", "array");
591
+ __publicField(this, "_element");
592
+ __publicField(this, "_arrayOptions", {});
593
+ this._element = element;
594
+ }
595
+ _validate(value, path) {
596
+ if (!Array.isArray(value)) {
597
+ return {
598
+ success: false,
599
+ error: {
600
+ path,
601
+ message: `Expected array, received ${typeof value}`,
602
+ expected: "array",
603
+ received: value
604
+ }
605
+ };
606
+ }
607
+ const { minLength, maxLength } = this._arrayOptions;
608
+ if (minLength !== void 0 && value.length < minLength) {
609
+ return {
610
+ success: false,
611
+ error: {
612
+ path,
613
+ message: `Array must have at least ${minLength} items`,
614
+ expected: `array(minLength: ${minLength})`,
615
+ received: value
616
+ }
617
+ };
618
+ }
619
+ if (maxLength !== void 0 && value.length > maxLength) {
620
+ return {
621
+ success: false,
622
+ error: {
623
+ path,
624
+ message: `Array must have at most ${maxLength} items`,
625
+ expected: `array(maxLength: ${maxLength})`,
626
+ received: value
627
+ }
628
+ };
629
+ }
630
+ const result = [];
631
+ for (let i = 0; i < value.length; i++) {
632
+ const itemPath = [
633
+ ...path,
634
+ String(i)
635
+ ];
636
+ const itemResult = this._element.validate(value[i], itemPath);
637
+ if (!itemResult.success) {
638
+ return itemResult;
639
+ }
640
+ result.push(itemResult.data);
641
+ }
642
+ return {
643
+ success: true,
644
+ data: result
645
+ };
646
+ }
647
+ _clone() {
648
+ const clone = new _ArrayValidator(this._element);
649
+ clone._options = {
650
+ ...this._options
651
+ };
652
+ clone._arrayOptions = {
653
+ ...this._arrayOptions
654
+ };
655
+ return clone;
656
+ }
657
+ /**
658
+ * @zh 设置最小长度
659
+ * @en Set minimum length
660
+ */
661
+ min(length) {
662
+ const clone = this._clone();
663
+ clone._arrayOptions.minLength = length;
664
+ return clone;
665
+ }
666
+ /**
667
+ * @zh 设置最大长度
668
+ * @en Set maximum length
669
+ */
670
+ max(length) {
671
+ const clone = this._clone();
672
+ clone._arrayOptions.maxLength = length;
673
+ return clone;
674
+ }
675
+ /**
676
+ * @zh 设置长度范围
677
+ * @en Set length range
678
+ */
679
+ length(min, max) {
680
+ const clone = this._clone();
681
+ clone._arrayOptions.minLength = min;
682
+ clone._arrayOptions.maxLength = max;
683
+ return clone;
684
+ }
685
+ /**
686
+ * @zh 要求非空数组
687
+ * @en Require non-empty array
688
+ */
689
+ nonempty() {
690
+ return this.min(1);
691
+ }
692
+ };
693
+ __name(_ArrayValidator, "ArrayValidator");
694
+ var ArrayValidator = _ArrayValidator;
695
+ var _TupleValidator = class _TupleValidator extends BaseValidator {
696
+ constructor(elements) {
697
+ super();
698
+ __publicField(this, "typeName", "tuple");
699
+ __publicField(this, "_elements");
700
+ this._elements = elements;
701
+ }
702
+ _validate(value, path) {
703
+ if (!Array.isArray(value)) {
704
+ return {
705
+ success: false,
706
+ error: {
707
+ path,
708
+ message: `Expected tuple, received ${typeof value}`,
709
+ expected: "tuple",
710
+ received: value
711
+ }
712
+ };
713
+ }
714
+ if (value.length !== this._elements.length) {
715
+ return {
716
+ success: false,
717
+ error: {
718
+ path,
719
+ message: `Expected tuple of length ${this._elements.length}, received length ${value.length}`,
720
+ expected: `tuple(length: ${this._elements.length})`,
721
+ received: value
722
+ }
723
+ };
724
+ }
725
+ const result = [];
726
+ for (let i = 0; i < this._elements.length; i++) {
727
+ const itemPath = [
728
+ ...path,
729
+ String(i)
730
+ ];
731
+ const itemResult = this._elements[i].validate(value[i], itemPath);
732
+ if (!itemResult.success) {
733
+ return itemResult;
734
+ }
735
+ result.push(itemResult.data);
736
+ }
737
+ return {
738
+ success: true,
739
+ data: result
740
+ };
741
+ }
742
+ _clone() {
743
+ const clone = new _TupleValidator(this._elements);
744
+ clone._options = {
745
+ ...this._options
746
+ };
747
+ return clone;
748
+ }
749
+ };
750
+ __name(_TupleValidator, "TupleValidator");
751
+ var TupleValidator = _TupleValidator;
752
+ var _UnionValidator = class _UnionValidator extends BaseValidator {
753
+ constructor(variants) {
754
+ super();
755
+ __publicField(this, "typeName");
756
+ __publicField(this, "_variants");
757
+ this._variants = variants;
758
+ this.typeName = `union(${variants.map((v) => v.typeName).join(" | ")})`;
759
+ }
760
+ _validate(value, path) {
761
+ const errors = [];
762
+ for (const variant of this._variants) {
763
+ const result = variant.validate(value, path);
764
+ if (result.success) {
765
+ return result;
766
+ }
767
+ errors.push(variant.typeName);
768
+ }
769
+ return {
770
+ success: false,
771
+ error: {
772
+ path,
773
+ message: `Expected one of: ${errors.join(", ")}`,
774
+ expected: this.typeName,
775
+ received: value
776
+ }
777
+ };
778
+ }
779
+ _clone() {
780
+ const clone = new _UnionValidator(this._variants);
781
+ clone._options = {
782
+ ...this._options
783
+ };
784
+ return clone;
785
+ }
786
+ };
787
+ __name(_UnionValidator, "UnionValidator");
788
+ var UnionValidator = _UnionValidator;
789
+ var _RecordValidator = class _RecordValidator extends BaseValidator {
790
+ constructor(valueValidator) {
791
+ super();
792
+ __publicField(this, "typeName", "record");
793
+ __publicField(this, "_valueValidator");
794
+ this._valueValidator = valueValidator;
795
+ }
796
+ _validate(value, path) {
797
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
798
+ return {
799
+ success: false,
800
+ error: {
801
+ path,
802
+ message: `Expected object, received ${Array.isArray(value) ? "array" : typeof value}`,
803
+ expected: "record",
804
+ received: value
805
+ }
806
+ };
807
+ }
808
+ const result = {};
809
+ const obj = value;
810
+ for (const [key, val] of Object.entries(obj)) {
811
+ const fieldPath = [
812
+ ...path,
813
+ key
814
+ ];
815
+ const fieldResult = this._valueValidator.validate(val, fieldPath);
816
+ if (!fieldResult.success) {
817
+ return fieldResult;
818
+ }
819
+ result[key] = fieldResult.data;
820
+ }
821
+ return {
822
+ success: true,
823
+ data: result
824
+ };
825
+ }
826
+ _clone() {
827
+ const clone = new _RecordValidator(this._valueValidator);
828
+ clone._options = {
829
+ ...this._options
830
+ };
831
+ return clone;
832
+ }
833
+ };
834
+ __name(_RecordValidator, "RecordValidator");
835
+ var RecordValidator = _RecordValidator;
836
+ var _EnumValidator = class _EnumValidator extends BaseValidator {
837
+ constructor(values) {
838
+ super();
839
+ __publicField(this, "typeName");
840
+ __publicField(this, "_values");
841
+ __publicField(this, "_valuesArray");
842
+ this._valuesArray = values;
843
+ this._values = new Set(values);
844
+ this.typeName = `enum(${values.map((v) => JSON.stringify(v)).join(", ")})`;
845
+ }
846
+ _validate(value, path) {
847
+ if (!this._values.has(value)) {
848
+ return {
849
+ success: false,
850
+ error: {
851
+ path,
852
+ message: `Expected one of: ${this._valuesArray.map((v) => JSON.stringify(v)).join(", ")}`,
853
+ expected: this.typeName,
854
+ received: value
855
+ }
856
+ };
857
+ }
858
+ return {
859
+ success: true,
860
+ data: value
861
+ };
862
+ }
863
+ _clone() {
864
+ const clone = new _EnumValidator(this._valuesArray);
865
+ clone._options = {
866
+ ...this._options
867
+ };
868
+ return clone;
869
+ }
870
+ };
871
+ __name(_EnumValidator, "EnumValidator");
872
+ var EnumValidator = _EnumValidator;
873
+ function object(shape) {
874
+ return new ObjectValidator(shape);
875
+ }
876
+ __name(object, "object");
877
+ function array(element) {
878
+ return new ArrayValidator(element);
879
+ }
880
+ __name(array, "array");
881
+ function tuple(elements) {
882
+ return new TupleValidator(elements);
883
+ }
884
+ __name(tuple, "tuple");
885
+ function union(variants) {
886
+ return new UnionValidator(variants);
887
+ }
888
+ __name(union, "union");
889
+ function record(valueValidator) {
890
+ return new RecordValidator(valueValidator);
891
+ }
892
+ __name(record, "record");
893
+ function nativeEnum(values) {
894
+ return new EnumValidator(values);
895
+ }
896
+ __name(nativeEnum, "nativeEnum");
897
+
898
+ // src/schema/index.ts
899
+ var s = {
900
+ // Primitives
901
+ string,
902
+ number,
903
+ boolean,
904
+ literal,
905
+ any,
906
+ // Composites
907
+ object,
908
+ array,
909
+ tuple,
910
+ union,
911
+ record,
912
+ /**
913
+ * @zh 创建枚举验证器
914
+ * @en Create enum validator
915
+ *
916
+ * @example
917
+ * ```typescript
918
+ * const RoleSchema = s.enum(['admin', 'user', 'guest'] as const);
919
+ * type Role = s.infer<typeof RoleSchema>; // 'admin' | 'user' | 'guest'
920
+ * ```
921
+ */
922
+ enum: nativeEnum,
923
+ /**
924
+ * @zh 类型推断辅助(仅用于类型层面)
925
+ * @en Type inference helper (type-level only)
926
+ *
927
+ * @zh 这是一个类型辅助,用于从验证器推断类型
928
+ * @en This is a type helper to infer types from validators
929
+ */
930
+ infer: void 0
931
+ };
932
+ function parse(validator, value) {
933
+ const result = validator.validate(value);
934
+ if (!result.success) {
935
+ const pathStr = result.error.path.length > 0 ? ` at "${result.error.path.join(".")}"` : "";
936
+ throw new Error(`Validation failed${pathStr}: ${result.error.message}`);
937
+ }
938
+ return result.data;
939
+ }
940
+ __name(parse, "parse");
941
+ function safeParse(validator, value) {
942
+ return validator.validate(value);
943
+ }
944
+ __name(safeParse, "safeParse");
945
+ function createGuard(validator) {
946
+ return (value) => validator.is(value);
947
+ }
948
+ __name(createGuard, "createGuard");
949
+
950
+ // src/distributed/adapters/RedisAdapter.ts
951
+ var RELEASE_LOCK_SCRIPT = `
952
+ if redis.call("get", KEYS[1]) == ARGV[1] then
953
+ return redis.call("del", KEYS[1])
954
+ else
955
+ return 0
956
+ end
957
+ `;
958
+ var EXTEND_LOCK_SCRIPT = `
959
+ if redis.call("get", KEYS[1]) == ARGV[1] then
960
+ return redis.call("pexpire", KEYS[1], ARGV[2])
961
+ else
962
+ return 0
963
+ end
964
+ `;
965
+ var _RedisAdapter = class _RedisAdapter {
966
+ constructor(config) {
967
+ __publicField(this, "_config");
968
+ __publicField(this, "_client", null);
969
+ __publicField(this, "_subscriber", null);
970
+ __publicField(this, "_connected", false);
971
+ // 锁的 owner token(用于安全释放)
972
+ __publicField(this, "_lockTokens", /* @__PURE__ */ new Map());
973
+ // 事件处理器
974
+ __publicField(this, "_handlers", /* @__PURE__ */ new Map());
975
+ __publicField(this, "_messageHandler", null);
976
+ this._config = {
977
+ prefix: "dist:",
978
+ serverTtl: 30,
979
+ roomTtl: 0,
980
+ snapshotTtl: 86400,
981
+ channel: "distributed:events",
982
+ ...config,
983
+ factory: config.factory
984
+ };
985
+ }
986
+ // =========================================================================
987
+ // Key 生成器 | Key Generators
988
+ // =========================================================================
989
+ _key(type, id) {
990
+ return id ? `${this._config.prefix}${type}:${id}` : `${this._config.prefix}${type}`;
991
+ }
992
+ _serverKey(serverId) {
993
+ return this._key("server", serverId);
994
+ }
995
+ _roomKey(roomId) {
996
+ return this._key("room", roomId);
997
+ }
998
+ _snapshotKey(roomId) {
999
+ return this._key("snapshot", roomId);
1000
+ }
1001
+ _lockKey(key) {
1002
+ return this._key("lock", key);
1003
+ }
1004
+ _serversSetKey() {
1005
+ return this._key("servers");
1006
+ }
1007
+ _roomsSetKey() {
1008
+ return this._key("rooms");
1009
+ }
1010
+ _serverRoomsKey(serverId) {
1011
+ return this._key("server-rooms", serverId);
1012
+ }
1013
+ // =========================================================================
1014
+ // 生命周期 | Lifecycle
1015
+ // =========================================================================
1016
+ async connect() {
1017
+ if (this._connected) return;
1018
+ this._client = await this._config.factory();
1019
+ this._subscriber = this._client.duplicate();
1020
+ this._messageHandler = (channel, message) => {
1021
+ if (channel !== this._config.channel) return;
1022
+ try {
1023
+ const event = JSON.parse(message);
1024
+ this._dispatchEvent(event);
1025
+ } catch (error) {
1026
+ console.error("[RedisAdapter] Failed to parse event:", error);
1027
+ }
1028
+ };
1029
+ this._subscriber.on("message", this._messageHandler);
1030
+ await this._subscriber.subscribe(this._config.channel);
1031
+ this._connected = true;
1032
+ }
1033
+ async disconnect() {
1034
+ if (!this._connected) return;
1035
+ if (this._subscriber) {
1036
+ if (this._messageHandler) {
1037
+ this._subscriber.off("message", this._messageHandler);
1038
+ }
1039
+ await this._subscriber.unsubscribe(this._config.channel);
1040
+ this._subscriber.disconnect();
1041
+ this._subscriber = null;
1042
+ }
1043
+ if (this._client) {
1044
+ await this._client.quit();
1045
+ this._client = null;
1046
+ }
1047
+ this._handlers.clear();
1048
+ this._lockTokens.clear();
1049
+ this._connected = false;
1050
+ }
1051
+ isConnected() {
1052
+ return this._connected;
1053
+ }
1054
+ _ensureConnected() {
1055
+ if (!this._connected || !this._client) {
1056
+ throw new Error("RedisAdapter is not connected");
1057
+ }
1058
+ return this._client;
1059
+ }
1060
+ // =========================================================================
1061
+ // 服务器注册 | Server Registry
1062
+ // =========================================================================
1063
+ async registerServer(server) {
1064
+ const client = this._ensureConnected();
1065
+ const key = this._serverKey(server.serverId);
1066
+ await client.hmset(key, "serverId", server.serverId, "address", server.address, "port", String(server.port), "roomCount", String(server.roomCount), "playerCount", String(server.playerCount), "capacity", String(server.capacity), "status", server.status, "lastHeartbeat", String(Date.now()), "metadata", JSON.stringify(server.metadata ?? {}));
1067
+ await client.expire(key, this._config.serverTtl);
1068
+ await client.sadd(this._serversSetKey(), server.serverId);
1069
+ await this.publish({
1070
+ type: "server:online",
1071
+ serverId: server.serverId,
1072
+ payload: server,
1073
+ timestamp: Date.now()
1074
+ });
1075
+ }
1076
+ async unregisterServer(serverId) {
1077
+ const client = this._ensureConnected();
1078
+ const key = this._serverKey(serverId);
1079
+ await client.del(key);
1080
+ await client.srem(this._serversSetKey(), serverId);
1081
+ const roomIds = await client.smembers(this._serverRoomsKey(serverId));
1082
+ for (const roomId of roomIds) {
1083
+ await this.unregisterRoom(roomId);
1084
+ }
1085
+ await client.del(this._serverRoomsKey(serverId));
1086
+ await this.publish({
1087
+ type: "server:offline",
1088
+ serverId,
1089
+ payload: {
1090
+ serverId
1091
+ },
1092
+ timestamp: Date.now()
1093
+ });
1094
+ }
1095
+ async heartbeat(serverId) {
1096
+ const client = this._ensureConnected();
1097
+ const key = this._serverKey(serverId);
1098
+ await client.hset(key, "lastHeartbeat", String(Date.now()));
1099
+ await client.expire(key, this._config.serverTtl);
1100
+ }
1101
+ async getServers() {
1102
+ const client = this._ensureConnected();
1103
+ const serverIds = await client.smembers(this._serversSetKey());
1104
+ const servers = [];
1105
+ for (const serverId of serverIds) {
1106
+ const server = await this.getServer(serverId);
1107
+ if (server && server.status === "online") {
1108
+ servers.push(server);
1109
+ }
1110
+ }
1111
+ return servers;
1112
+ }
1113
+ async getServer(serverId) {
1114
+ const client = this._ensureConnected();
1115
+ const key = this._serverKey(serverId);
1116
+ const data = await client.hgetall(key);
1117
+ if (!data || !data.serverId) return null;
1118
+ return {
1119
+ serverId: data.serverId,
1120
+ address: data.address,
1121
+ port: parseInt(data.port, 10),
1122
+ roomCount: parseInt(data.roomCount, 10),
1123
+ playerCount: parseInt(data.playerCount, 10),
1124
+ capacity: parseInt(data.capacity, 10),
1125
+ status: data.status,
1126
+ lastHeartbeat: parseInt(data.lastHeartbeat, 10),
1127
+ metadata: data.metadata ? JSON.parse(data.metadata) : {}
1128
+ };
1129
+ }
1130
+ async updateServer(serverId, updates) {
1131
+ const client = this._ensureConnected();
1132
+ const key = this._serverKey(serverId);
1133
+ const args = [];
1134
+ if (updates.address !== void 0) args.push("address", updates.address);
1135
+ if (updates.port !== void 0) args.push("port", String(updates.port));
1136
+ if (updates.roomCount !== void 0) args.push("roomCount", String(updates.roomCount));
1137
+ if (updates.playerCount !== void 0) args.push("playerCount", String(updates.playerCount));
1138
+ if (updates.capacity !== void 0) args.push("capacity", String(updates.capacity));
1139
+ if (updates.status !== void 0) args.push("status", updates.status);
1140
+ if (updates.metadata !== void 0) args.push("metadata", JSON.stringify(updates.metadata));
1141
+ if (args.length > 0) {
1142
+ await client.hmset(key, ...args);
1143
+ }
1144
+ if (updates.status === "draining") {
1145
+ await this.publish({
1146
+ type: "server:draining",
1147
+ serverId,
1148
+ payload: {
1149
+ serverId
1150
+ },
1151
+ timestamp: Date.now()
1152
+ });
1153
+ }
1154
+ }
1155
+ // =========================================================================
1156
+ // 房间注册 | Room Registry
1157
+ // =========================================================================
1158
+ async registerRoom(room) {
1159
+ const client = this._ensureConnected();
1160
+ const key = this._roomKey(room.roomId);
1161
+ await client.hmset(key, "roomId", room.roomId, "roomType", room.roomType, "serverId", room.serverId, "serverAddress", room.serverAddress, "playerCount", String(room.playerCount), "maxPlayers", String(room.maxPlayers), "isLocked", room.isLocked ? "1" : "0", "metadata", JSON.stringify(room.metadata), "createdAt", String(room.createdAt), "updatedAt", String(room.updatedAt));
1162
+ if (this._config.roomTtl > 0) {
1163
+ await client.expire(key, this._config.roomTtl);
1164
+ }
1165
+ await client.sadd(this._roomsSetKey(), room.roomId);
1166
+ await client.sadd(this._serverRoomsKey(room.serverId), room.roomId);
1167
+ const roomCount = (await client.smembers(this._serverRoomsKey(room.serverId))).length;
1168
+ await client.hset(this._serverKey(room.serverId), "roomCount", String(roomCount));
1169
+ await this.publish({
1170
+ type: "room:created",
1171
+ serverId: room.serverId,
1172
+ roomId: room.roomId,
1173
+ payload: {
1174
+ roomType: room.roomType
1175
+ },
1176
+ timestamp: Date.now()
1177
+ });
1178
+ }
1179
+ async unregisterRoom(roomId) {
1180
+ const client = this._ensureConnected();
1181
+ const room = await this.getRoom(roomId);
1182
+ if (!room) return;
1183
+ const key = this._roomKey(roomId);
1184
+ await client.del(key);
1185
+ await client.srem(this._roomsSetKey(), roomId);
1186
+ await client.srem(this._serverRoomsKey(room.serverId), roomId);
1187
+ const roomCount = (await client.smembers(this._serverRoomsKey(room.serverId))).length;
1188
+ await client.hset(this._serverKey(room.serverId), "roomCount", String(roomCount));
1189
+ await this.deleteSnapshot(roomId);
1190
+ await this.publish({
1191
+ type: "room:disposed",
1192
+ serverId: room.serverId,
1193
+ roomId,
1194
+ payload: {},
1195
+ timestamp: Date.now()
1196
+ });
1197
+ }
1198
+ async updateRoom(roomId, updates) {
1199
+ const client = this._ensureConnected();
1200
+ const room = await this.getRoom(roomId);
1201
+ if (!room) return;
1202
+ const key = this._roomKey(roomId);
1203
+ const args = [];
1204
+ if (updates.playerCount !== void 0) args.push("playerCount", String(updates.playerCount));
1205
+ if (updates.maxPlayers !== void 0) args.push("maxPlayers", String(updates.maxPlayers));
1206
+ if (updates.isLocked !== void 0) args.push("isLocked", updates.isLocked ? "1" : "0");
1207
+ if (updates.metadata !== void 0) args.push("metadata", JSON.stringify(updates.metadata));
1208
+ args.push("updatedAt", String(Date.now()));
1209
+ if (args.length > 0) {
1210
+ await client.hmset(key, ...args);
1211
+ }
1212
+ await this.publish({
1213
+ type: "room:updated",
1214
+ serverId: room.serverId,
1215
+ roomId,
1216
+ payload: updates,
1217
+ timestamp: Date.now()
1218
+ });
1219
+ if (updates.isLocked !== void 0) {
1220
+ await this.publish({
1221
+ type: updates.isLocked ? "room:locked" : "room:unlocked",
1222
+ serverId: room.serverId,
1223
+ roomId,
1224
+ payload: {},
1225
+ timestamp: Date.now()
1226
+ });
1227
+ }
1228
+ }
1229
+ async getRoom(roomId) {
1230
+ const client = this._ensureConnected();
1231
+ const key = this._roomKey(roomId);
1232
+ const data = await client.hgetall(key);
1233
+ if (!data || !data.roomId) return null;
1234
+ return {
1235
+ roomId: data.roomId,
1236
+ roomType: data.roomType,
1237
+ serverId: data.serverId,
1238
+ serverAddress: data.serverAddress,
1239
+ playerCount: parseInt(data.playerCount, 10),
1240
+ maxPlayers: parseInt(data.maxPlayers, 10),
1241
+ isLocked: data.isLocked === "1",
1242
+ metadata: data.metadata ? JSON.parse(data.metadata) : {},
1243
+ createdAt: parseInt(data.createdAt, 10),
1244
+ updatedAt: parseInt(data.updatedAt, 10)
1245
+ };
1246
+ }
1247
+ async queryRooms(query) {
1248
+ const client = this._ensureConnected();
1249
+ const roomIds = await client.smembers(this._roomsSetKey());
1250
+ let results = [];
1251
+ for (const roomId of roomIds) {
1252
+ const room = await this.getRoom(roomId);
1253
+ if (room) results.push(room);
1254
+ }
1255
+ if (query.roomType) {
1256
+ results = results.filter((r) => r.roomType === query.roomType);
1257
+ }
1258
+ if (query.hasSpace) {
1259
+ results = results.filter((r) => r.playerCount < r.maxPlayers);
1260
+ }
1261
+ if (query.notLocked) {
1262
+ results = results.filter((r) => !r.isLocked);
1263
+ }
1264
+ if (query.metadata) {
1265
+ results = results.filter((r) => {
1266
+ for (const [key, value] of Object.entries(query.metadata)) {
1267
+ if (r.metadata[key] !== value) return false;
1268
+ }
1269
+ return true;
1270
+ });
1271
+ }
1272
+ if (query.offset) {
1273
+ results = results.slice(query.offset);
1274
+ }
1275
+ if (query.limit) {
1276
+ results = results.slice(0, query.limit);
1277
+ }
1278
+ return results;
1279
+ }
1280
+ async findAvailableRoom(roomType) {
1281
+ const rooms = await this.queryRooms({
1282
+ roomType,
1283
+ hasSpace: true,
1284
+ notLocked: true,
1285
+ limit: 1
1286
+ });
1287
+ return rooms[0] ?? null;
1288
+ }
1289
+ async getRoomsByServer(serverId) {
1290
+ const client = this._ensureConnected();
1291
+ const roomIds = await client.smembers(this._serverRoomsKey(serverId));
1292
+ const rooms = [];
1293
+ for (const roomId of roomIds) {
1294
+ const room = await this.getRoom(roomId);
1295
+ if (room) rooms.push(room);
1296
+ }
1297
+ return rooms;
1298
+ }
1299
+ // =========================================================================
1300
+ // 房间状态 | Room State
1301
+ // =========================================================================
1302
+ async saveSnapshot(snapshot) {
1303
+ const client = this._ensureConnected();
1304
+ const key = this._snapshotKey(snapshot.roomId);
1305
+ await client.set(key, JSON.stringify(snapshot));
1306
+ await client.expire(key, this._config.snapshotTtl);
1307
+ }
1308
+ async loadSnapshot(roomId) {
1309
+ const client = this._ensureConnected();
1310
+ const key = this._snapshotKey(roomId);
1311
+ const data = await client.get(key);
1312
+ return data ? JSON.parse(data) : null;
1313
+ }
1314
+ async deleteSnapshot(roomId) {
1315
+ const client = this._ensureConnected();
1316
+ const key = this._snapshotKey(roomId);
1317
+ await client.del(key);
1318
+ }
1319
+ // =========================================================================
1320
+ // 发布/订阅 | Pub/Sub
1321
+ // =========================================================================
1322
+ async publish(event) {
1323
+ const client = this._ensureConnected();
1324
+ await client.publish(this._config.channel, JSON.stringify(event));
1325
+ }
1326
+ async subscribe(pattern, handler) {
1327
+ if (!this._handlers.has(pattern)) {
1328
+ this._handlers.set(pattern, /* @__PURE__ */ new Set());
1329
+ }
1330
+ this._handlers.get(pattern).add(handler);
1331
+ return () => {
1332
+ const handlers = this._handlers.get(pattern);
1333
+ if (handlers) {
1334
+ handlers.delete(handler);
1335
+ if (handlers.size === 0) {
1336
+ this._handlers.delete(pattern);
1337
+ }
1338
+ }
1339
+ };
1340
+ }
1341
+ async sendToRoom(roomId, messageType, data, playerId) {
1342
+ const room = await this.getRoom(roomId);
1343
+ if (!room) return;
1344
+ await this.publish({
1345
+ type: "room:message",
1346
+ serverId: room.serverId,
1347
+ roomId,
1348
+ payload: {
1349
+ messageType,
1350
+ data,
1351
+ playerId
1352
+ },
1353
+ timestamp: Date.now()
1354
+ });
1355
+ }
1356
+ _dispatchEvent(event) {
1357
+ const wildcardHandlers = this._handlers.get("*");
1358
+ if (wildcardHandlers) {
1359
+ for (const handler of wildcardHandlers) {
1360
+ try {
1361
+ handler(event);
1362
+ } catch (error) {
1363
+ console.error("[RedisAdapter] Event handler error:", error);
1364
+ }
1365
+ }
1366
+ }
1367
+ const typeHandlers = this._handlers.get(event.type);
1368
+ if (typeHandlers) {
1369
+ for (const handler of typeHandlers) {
1370
+ try {
1371
+ handler(event);
1372
+ } catch (error) {
1373
+ console.error("[RedisAdapter] Event handler error:", error);
1374
+ }
1375
+ }
1376
+ }
1377
+ }
1378
+ // =========================================================================
1379
+ // 分布式锁 | Distributed Lock
1380
+ // =========================================================================
1381
+ async acquireLock(key, ttlMs) {
1382
+ const client = this._ensureConnected();
1383
+ const lockKey = this._lockKey(key);
1384
+ const token = `${Date.now()}_${Math.random().toString(36).substring(2)}`;
1385
+ const ttlSeconds = Math.ceil(ttlMs / 1e3);
1386
+ const result = await client.set(lockKey, token, "NX", "EX", ttlSeconds);
1387
+ if (result === "OK") {
1388
+ this._lockTokens.set(key, token);
1389
+ return true;
1390
+ }
1391
+ return false;
1392
+ }
1393
+ async releaseLock(key) {
1394
+ const client = this._ensureConnected();
1395
+ const lockKey = this._lockKey(key);
1396
+ const token = this._lockTokens.get(key);
1397
+ if (!token) return;
1398
+ await client.eval(RELEASE_LOCK_SCRIPT, 1, lockKey, token);
1399
+ this._lockTokens.delete(key);
1400
+ }
1401
+ async extendLock(key, ttlMs) {
1402
+ const client = this._ensureConnected();
1403
+ const lockKey = this._lockKey(key);
1404
+ const token = this._lockTokens.get(key);
1405
+ if (!token) return false;
1406
+ const result = await client.eval(EXTEND_LOCK_SCRIPT, 1, lockKey, token, String(ttlMs));
1407
+ return result === 1;
1408
+ }
1409
+ };
1410
+ __name(_RedisAdapter, "RedisAdapter");
1411
+ var RedisAdapter = _RedisAdapter;
1412
+ function createRedisAdapter(config) {
1413
+ return new RedisAdapter(config);
1414
+ }
1415
+ __name(createRedisAdapter, "createRedisAdapter");
1416
+
1417
+ // src/distributed/routing/LoadBalancedRouter.ts
1418
+ var _LoadBalancedRouter = class _LoadBalancedRouter {
1419
+ constructor(config = {}) {
1420
+ __publicField(this, "_config");
1421
+ __publicField(this, "_roundRobinIndex", 0);
1422
+ this._config = {
1423
+ strategy: config.strategy ?? "least-rooms",
1424
+ preferLocal: config.preferLocal ?? true,
1425
+ localPreferenceThreshold: config.localPreferenceThreshold ?? 0.8
1426
+ };
1427
+ }
1428
+ /**
1429
+ * @zh 选择最优服务器
1430
+ * @en Select optimal server
1431
+ *
1432
+ * @param servers - 可用服务器列表 | Available servers
1433
+ * @param localServerId - 本地服务器 ID | Local server ID
1434
+ * @returns 最优服务器,如果没有可用服务器返回 null | Optimal server, or null if none available
1435
+ */
1436
+ selectServer(servers, localServerId) {
1437
+ const availableServers = servers.filter((s2) => s2.status === "online" && s2.roomCount < s2.capacity);
1438
+ if (availableServers.length === 0) {
1439
+ return null;
1440
+ }
1441
+ if (this._config.preferLocal && localServerId) {
1442
+ const localServer = availableServers.find((s2) => s2.serverId === localServerId);
1443
+ if (localServer) {
1444
+ const loadRatio = localServer.roomCount / localServer.capacity;
1445
+ if (loadRatio < this._config.localPreferenceThreshold) {
1446
+ return localServer;
1447
+ }
1448
+ }
1449
+ }
1450
+ switch (this._config.strategy) {
1451
+ case "round-robin":
1452
+ return this._selectRoundRobin(availableServers);
1453
+ case "least-rooms":
1454
+ return this._selectLeastRooms(availableServers);
1455
+ case "least-players":
1456
+ return this._selectLeastPlayers(availableServers);
1457
+ case "random":
1458
+ return this._selectRandom(availableServers);
1459
+ case "weighted":
1460
+ return this._selectWeighted(availableServers);
1461
+ default:
1462
+ return this._selectLeastRooms(availableServers);
1463
+ }
1464
+ }
1465
+ /**
1466
+ * @zh 选择创建房间的最优服务器
1467
+ * @en Select optimal server for room creation
1468
+ */
1469
+ selectServerForCreation(servers, localServerId) {
1470
+ return this.selectServer(servers, localServerId);
1471
+ }
1472
+ /**
1473
+ * @zh 重置轮询索引
1474
+ * @en Reset round-robin index
1475
+ */
1476
+ resetRoundRobin() {
1477
+ this._roundRobinIndex = 0;
1478
+ }
1479
+ // =========================================================================
1480
+ // 私有方法 | Private Methods
1481
+ // =========================================================================
1482
+ _selectRoundRobin(servers) {
1483
+ const server = servers[this._roundRobinIndex % servers.length];
1484
+ this._roundRobinIndex++;
1485
+ return server;
1486
+ }
1487
+ _selectLeastRooms(servers) {
1488
+ return servers.reduce((best, current) => current.roomCount < best.roomCount ? current : best);
1489
+ }
1490
+ _selectLeastPlayers(servers) {
1491
+ return servers.reduce((best, current) => current.playerCount < best.playerCount ? current : best);
1492
+ }
1493
+ _selectRandom(servers) {
1494
+ return servers[Math.floor(Math.random() * servers.length)];
1495
+ }
1496
+ _selectWeighted(servers) {
1497
+ const weights = servers.map((s2) => ({
1498
+ server: s2,
1499
+ weight: (s2.capacity - s2.roomCount) / s2.capacity
1500
+ }));
1501
+ const totalWeight = weights.reduce((sum, w) => sum + w.weight, 0);
1502
+ let random = Math.random() * totalWeight;
1503
+ for (const { server, weight } of weights) {
1504
+ random -= weight;
1505
+ if (random <= 0) {
1506
+ return server;
1507
+ }
1508
+ }
1509
+ return servers[0];
1510
+ }
1511
+ };
1512
+ __name(_LoadBalancedRouter, "LoadBalancedRouter");
1513
+ var LoadBalancedRouter = _LoadBalancedRouter;
1514
+ function createLoadBalancedRouter(config) {
1515
+ return new LoadBalancedRouter(config);
1516
+ }
1517
+ __name(createLoadBalancedRouter, "createLoadBalancedRouter");
1518
+
1519
+ export { AnyValidator, ArrayValidator, BooleanValidator, EnumValidator, LiteralValidator, LoadBalancedRouter, NumberValidator, ObjectValidator, RecordValidator, RedisAdapter, StringValidator, TupleValidator, UnionValidator, any, array, boolean, createGuard, createLoadBalancedRouter, createRedisAdapter, defineApi, defineApiWithSchema, defineHttp, defineMsg, defineMsgWithSchema, literal, nativeEnum, number, object, parse, record, s, safeParse, string, tuple, union };
22
1520
  //# sourceMappingURL=index.js.map
23
1521
  //# sourceMappingURL=index.js.map