@tsukilabs/nil-ffi 0.7.1 → 0.7.3

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/def.d.ts CHANGED
@@ -16,6 +16,22 @@ export declare const definitions: {
16
16
  readonly arguments: readonly [ffi.types.UINT_32];
17
17
  readonly return: ffi.types.VOID;
18
18
  };
19
+ readonly nil_is_host: {
20
+ readonly arguments: readonly [ffi.types.UINT_32];
21
+ readonly return: ffi.types.VOID;
22
+ };
23
+ readonly nil_start_server: {
24
+ readonly arguments: readonly [ffi.types.UINT_32, ffi.types.POINTER];
25
+ readonly return: ffi.types.VOID;
26
+ };
27
+ readonly nil_start_server_with_savedata: {
28
+ readonly arguments: readonly [ffi.types.UINT_32, ffi.types.POINTER];
29
+ readonly return: ffi.types.VOID;
30
+ };
31
+ readonly nil_stop_server: {
32
+ readonly arguments: readonly [ffi.types.UINT_32];
33
+ readonly return: ffi.types.VOID;
34
+ };
19
35
  readonly nil_client_version: {
20
36
  readonly arguments: readonly [ffi.types.UINT_32];
21
37
  readonly return: ffi.types.VOID;
package/dist/error.d.ts CHANGED
@@ -1,15 +1,20 @@
1
1
  import { Option } from '@tb-dev/utils';
2
- import { ffi_Status } from '@tsukilabs/nil-bindings';
2
+ import { ffi_Response, ffi_Status } from '@tsukilabs/nil-bindings';
3
3
  export interface FfiErrorOptions extends ErrorOptions {
4
4
  readonly status?: Option<ffi_Status>;
5
5
  }
6
- export declare class FfiError extends Error {
7
- readonly name = "FfiError";
6
+ export declare class NilError extends Error {
7
+ readonly name = "NilError";
8
8
  readonly status: ffi_Status;
9
9
  constructor(message: Option<string>, options?: FfiErrorOptions);
10
- static fromStatus(status: ffi_Status): FfiError;
10
+ static fromStatus(status: ffi_Status): NilError;
11
11
  }
12
12
  export declare class HandleClosedError extends Error {
13
13
  readonly name = "HandleClosedError";
14
14
  constructor(options?: ErrorOptions);
15
15
  }
16
+ export declare class UnknownResponseError extends Error {
17
+ readonly name = "UnknownResponseError";
18
+ readonly response: ffi_Response;
19
+ constructor(response: ffi_Response, options?: ErrorOptions);
20
+ }
package/dist/index.d.ts CHANGED
@@ -1,25 +1,72 @@
1
1
  import { definitions } from './def';
2
2
  import { Option } from '@tb-dev/utils';
3
+ import { QueueOptions } from './queue';
4
+ import { AuthorizeRequest, GetPlayerIdsRequest, GetPlayerIdsResponse, GetPlayerRequest, GetPlayerStatusRequest, GetRemoteWorldsResponse, Password, PlayerExistsRequest, PlayerId, SetPlayerStatusRequest, SpawnPlayerRequest, Token, ValidateTokenRequest, ValidateTokenResponse, WorldId, WorldOptions } from '@tsukilabs/nil-bindings';
3
5
  import * as ffi from "node:ffi";
4
6
  export * from './def';
5
7
  export * from './error';
6
8
  export type Handle = ffi.DynamicLibraryResult<typeof definitions>;
9
+ type LocalServer = {
10
+ world: WorldId;
11
+ addr: string;
12
+ };
13
+ type ServerAddr = {
14
+ kind: "remote";
15
+ } | {
16
+ kind: "local";
17
+ addr: string;
18
+ };
19
+ type ffi_UpdateClient = {
20
+ server: ServerAddr;
21
+ world_id?: WorldId | null;
22
+ world_password?: Password | null;
23
+ player_id?: PlayerId | null;
24
+ player_password?: Password | null;
25
+ authorization_token?: Token | null;
26
+ };
7
27
  export declare const VERSION: string;
28
+ export declare const USER_AGENT: string;
8
29
  export declare class Nil implements Disposable {
30
+ readonly path: string;
9
31
  private readonly handle;
10
32
  readonly functions: Handle["functions"];
11
33
  private readonly queue;
12
34
  private disposed;
13
- constructor(dll: string);
35
+ private constructor();
36
+ static init(dll: string, options?: NilOptions): Promise<Nil>;
37
+ static initLatest(outDir?: Option<string>, options?: NilOptions): Promise<Nil>;
38
+ static downloadLatest(outDir?: Option<string>): Promise<string>;
14
39
  [Symbol.dispose](): void;
15
40
  close(): void;
41
+ isClosed(): boolean;
42
+ authorize(req: AuthorizeRequest): Promise<string>;
16
43
  getClientVersion(): Promise<string>;
17
44
  getFfiVersion(): Promise<string>;
18
- getRemoteWorldLimit(): Promise<GetRemoteWorldLimitResponse>;
19
- getRemoteWorldLimitPerUser(): Promise<GetRemoteWorldLimitPerUserResponse>;
45
+ getPlayer(req: GetPlayerRequest): Promise<import('@tsukilabs/nil-bindings').Player>;
46
+ getPlayerIds(req: GetPlayerIdsRequest): Promise<GetPlayerIdsResponse>;
47
+ getPlayerStatus(req: GetPlayerStatusRequest): Promise<import('@tsukilabs/nil-bindings').PlayerStatus>;
48
+ getRemoteWorldLimit(): Promise<number>;
49
+ getRemoteWorldLimitPerUser(): Promise<number>;
20
50
  getRemoteWorlds(): Promise<GetRemoteWorldsResponse>;
51
+ getServerAddr(): Promise<ServerAddr>;
52
+ getServerKind(): Promise<import('@tsukilabs/nil-bindings').ServerKind>;
21
53
  getUserAgent(): Promise<string>;
22
- getWorld(): Promise<Option<string>>;
23
- setUserAgent(userAgent: string): Promise<void>;
24
- private throwIfClosed;
54
+ getWorldId(): Promise<Option<string>>;
55
+ isHost(): Promise<boolean>;
56
+ isLocal(): Promise<boolean>;
57
+ isReady(): Promise<boolean>;
58
+ isRemote(): Promise<boolean>;
59
+ playerExists(req: PlayerExistsRequest): Promise<boolean>;
60
+ setPlayerStatus(req: SetPlayerStatusRequest): Promise<import('@tb-dev/utils').nil>;
61
+ setUserAgent(userAgent: string): Promise<import('@tb-dev/utils').nil>;
62
+ spawnPlayer(req: SpawnPlayerRequest): Promise<import('@tb-dev/utils').nil>;
63
+ startServer(options: WorldOptions): Promise<LocalServer>;
64
+ startServerWithSavedata(path: string): Promise<LocalServer>;
65
+ stopClient(): Promise<import('@tb-dev/utils').nil>;
66
+ stopServer(): Promise<import('@tb-dev/utils').nil>;
67
+ updateClient(options: ffi_UpdateClient): Promise<import('@tb-dev/utils').nil>;
68
+ validateToken(req: ValidateTokenRequest): Promise<ValidateTokenResponse>;
69
+ }
70
+ export interface NilOptions {
71
+ onError?: QueueOptions["onError"];
25
72
  }
package/dist/index.js CHANGED
@@ -1,138 +1,14 @@
1
1
  import * as ffi from "node:ffi";
2
- import { isNil } from "es-toolkit/predicate";
3
2
  import * as process from "node:process";
3
+ import { cwd } from "node:process";
4
4
  import { ffi_Status } from "@tsukilabs/nil-bindings";
5
- //#region src/request.ts
6
- var RequestId = class {
7
- #current = 0;
8
- next() {
9
- if (this.#current >= 4294967295) this.#current = 0;
10
- return ++this.#current;
11
- }
12
- };
13
- //#endregion
14
- //#region src/ptr.ts
15
- function allocBuffer(size) {
16
- return Buffer.alloc(size ?? pointerSize());
17
- }
18
- function readBufferPtr(buffer) {
19
- if (pointerSize() === 8) return buffer.readBigUInt64LE();
20
- else return BigInt(buffer.readUInt32LE());
21
- }
22
- function pointerSize() {
23
- if (process.arch === "x64" || process.arch === "arm64") return 8;
24
- else return 4;
25
- }
26
- //#endregion
27
- //#region src/error.ts
28
- var FfiError = class FfiError extends Error {
29
- constructor(message, options) {
30
- const status = options?.status ?? ffi_Status.ERR_UNKNOWN;
31
- if (!message && status !== ffi_Status.OK) {
32
- for (const [key, value] of Object.entries(ffi_Status)) if (value === status) {
33
- message = key;
34
- break;
35
- }
36
- }
37
- message ??= "ERR_UNKNOWN";
38
- super(message, options);
39
- this.name = "FfiError";
40
- this.status = status;
41
- }
42
- static fromStatus(status) {
43
- return new FfiError(null, { status });
44
- }
45
- };
46
- var HandleClosedError = class extends Error {
47
- constructor(options) {
48
- super("handle is closed", options);
49
- this.name = "HandleClosedError";
50
- }
51
- };
52
- //#endregion
53
- //#region src/queue.ts
54
- var Queue = class Queue {
55
- static {
56
- this.POLL_INTERVAL = 16;
57
- }
58
- static {
59
- this.LIMIT_PER_TICK = 200;
60
- }
61
- constructor(handle) {
62
- this.pending = /* @__PURE__ */ new Map();
63
- this.disposed = false;
64
- this.requestId = new RequestId();
65
- this.functions = handle.functions;
66
- }
67
- [Symbol.dispose]() {
68
- if (this.disposed) return;
69
- this.disposed = true;
70
- this.rejectAll(new HandleClosedError());
71
- }
72
- request(start) {
73
- if (this.disposed) return Promise.reject(new HandleClosedError());
74
- const requestId = this.requestId.next();
75
- return new Promise((resolve, reject) => {
76
- this.pending.set(requestId, {
77
- resolve,
78
- reject
79
- });
80
- try {
81
- start(requestId);
82
- } catch (err) {
83
- this.pending.delete(requestId);
84
- reject(err);
85
- return;
86
- }
87
- this.startPolling();
88
- });
89
- }
90
- startPolling() {
91
- if (isNil(this.timer) && !this.disposed) this.timer = setTimeout(() => this.poll(), 0);
92
- }
93
- poll() {
94
- this.timer = null;
95
- if (!this.disposed && this.pending.size > 0) try {
96
- for (let i = 0; i < Queue.LIMIT_PER_TICK; i++) if (!this.pollOnce()) break;
97
- } catch (err) {
98
- this.rejectAll(err);
99
- return;
100
- }
101
- if (!this.disposed && this.pending.size > 0) this.timer = setTimeout(() => this.poll(), Queue.POLL_INTERVAL);
102
- }
103
- pollOnce() {
104
- const out = allocBuffer();
105
- const status = this.functions.nil_ffi_poll(out);
106
- if (status === ffi_Status.ERR_NOTHING_TO_POLL) return false;
107
- if (status !== ffi_Status.OK) throw FfiError.fromStatus(status);
108
- const ptr = readBufferPtr(out);
109
- if (ptr === 0n) throw new Error("unexpected null pointer");
110
- let json;
111
- try {
112
- json = ffi.toString(ptr);
113
- } finally {
114
- this.functions.nil_ffi_free_str(ptr);
115
- }
116
- const entry = JSON.parse(json);
117
- const response = JSON.parse(entry.json_str);
118
- const pending = this.pending.get(response.id);
119
- if (!pending) return true;
120
- this.pending.delete(response.id);
121
- if (response.kind === "ok") pending.resolve(response.data);
122
- else pending.reject(new FfiError(response.error, { status: response.status }));
123
- return true;
124
- }
125
- rejectAll(error) {
126
- if (!isNil(this.timer)) {
127
- clearTimeout(this.timer);
128
- this.timer = null;
129
- }
130
- for (const { reject } of this.pending.values()) reject(error);
131
- this.pending.clear();
132
- }
133
- };
134
- //#endregion
5
+ import { writeFile } from "node:fs/promises";
6
+ import { EventEmitter } from "node:events";
7
+ import { isNil } from "es-toolkit/predicate";
8
+ import { join, resolve } from "node:path";
135
9
  //#region src/def.ts
10
+ //! Copyright (C) Call of Nil contributors
11
+ //! SPDX-License-Identifier: AGPL-3.0-only
136
12
  var definitions = {
137
13
  nil_ffi_free_str: {
138
14
  arguments: [ffi.types.POINTER],
@@ -150,6 +26,22 @@ var definitions = {
150
26
  arguments: [ffi.types.UINT_32],
151
27
  return: ffi.types.VOID
152
28
  },
29
+ nil_is_host: {
30
+ arguments: [ffi.types.UINT_32],
31
+ return: ffi.types.VOID
32
+ },
33
+ nil_start_server: {
34
+ arguments: [ffi.types.UINT_32, ffi.types.POINTER],
35
+ return: ffi.types.VOID
36
+ },
37
+ nil_start_server_with_savedata: {
38
+ arguments: [ffi.types.UINT_32, ffi.types.POINTER],
39
+ return: ffi.types.VOID
40
+ },
41
+ nil_stop_server: {
42
+ arguments: [ffi.types.UINT_32],
43
+ return: ffi.types.VOID
44
+ },
153
45
  nil_client_version: {
154
46
  arguments: [ffi.types.UINT_32],
155
47
  return: ffi.types.VOID
@@ -680,19 +572,219 @@ var definitions = {
680
572
  }
681
573
  };
682
574
  //#endregion
575
+ //#region src/error.ts
576
+ //! Copyright (C) Call of Nil contributors
577
+ //! SPDX-License-Identifier: AGPL-3.0-only
578
+ var NilError = class NilError extends Error {
579
+ constructor(message, options) {
580
+ const status = options?.status ?? ffi_Status.ERR_UNKNOWN;
581
+ if (!message && status !== ffi_Status.OK) {
582
+ for (const [key, value] of Object.entries(ffi_Status)) if (value === status) {
583
+ message = key;
584
+ break;
585
+ }
586
+ }
587
+ message ??= "ERR_UNKNOWN";
588
+ super(message, options);
589
+ this.name = "NilError";
590
+ this.status = status;
591
+ }
592
+ static fromStatus(status) {
593
+ return new NilError(null, { status });
594
+ }
595
+ };
596
+ var HandleClosedError = class extends Error {
597
+ constructor(options) {
598
+ super("handle is closed", options);
599
+ this.name = "HandleClosedError";
600
+ }
601
+ };
602
+ var UnknownResponseError = class extends Error {
603
+ constructor(response, options) {
604
+ super(`got unknown response with id ${response.id}`, options);
605
+ this.name = "UnknownResponseError";
606
+ this.response = response;
607
+ }
608
+ };
609
+ //#endregion
610
+ //#region src/request.ts
611
+ //! Copyright (C) Call of Nil contributors
612
+ //! SPDX-License-Identifier: AGPL-3.0-only
613
+ var RequestId = class {
614
+ #current = 0;
615
+ next() {
616
+ if (this.#current >= 4294967295) this.#current = 0;
617
+ return ++this.#current;
618
+ }
619
+ };
620
+ //#endregion
621
+ //#region src/ptr.ts
622
+ //! Copyright (C) Call of Nil contributors
623
+ //! SPDX-License-Identifier: AGPL-3.0-only
624
+ function allocBuffer(size) {
625
+ return Buffer.alloc(size ?? pointerSize());
626
+ }
627
+ function readBufferPtr(buffer) {
628
+ if (pointerSize() === 8) return buffer.readBigUInt64LE();
629
+ else return BigInt(buffer.readUInt32LE());
630
+ }
631
+ function pointerSize() {
632
+ if (process.arch === "x64" || process.arch === "arm64") return 8;
633
+ else return 4;
634
+ }
635
+ //#endregion
636
+ //#region src/queue.ts
637
+ //! Copyright (C) Call of Nil contributors
638
+ //! SPDX-License-Identifier: AGPL-3.0-only
639
+ var QueueEmitter = class extends EventEmitter {
640
+ emit(event, ...args) {
641
+ return super.emit(event, ...args);
642
+ }
643
+ on(event, listener) {
644
+ return super.on(event, listener);
645
+ }
646
+ };
647
+ var Queue = class Queue {
648
+ static {
649
+ this.POLL_INTERVAL = 16;
650
+ }
651
+ static {
652
+ this.LIMIT_PER_TICK = 200;
653
+ }
654
+ constructor(handle, options) {
655
+ this.disposed = false;
656
+ this.emitter = new QueueEmitter();
657
+ this.requestId = new RequestId();
658
+ this.pendingRequests = /* @__PURE__ */ new Map();
659
+ this.functions = handle.functions;
660
+ this.errorHandler = options?.onError ?? console.error;
661
+ this.emitter.on("error", this.errorHandler);
662
+ this.emitter.on("response", this.onResponse.bind(this));
663
+ this.timer = setTimeout(() => this.drain(), 0);
664
+ }
665
+ [Symbol.dispose]() {
666
+ if (this.disposed) return;
667
+ this.disposed = true;
668
+ this.rejectAll(new HandleClosedError());
669
+ this.emitter.removeAllListeners();
670
+ }
671
+ request(start) {
672
+ if (this.disposed) return Promise.reject(new HandleClosedError());
673
+ const requestId = this.requestId.next();
674
+ return new Promise((resolve, reject) => {
675
+ this.pendingRequests.set(requestId, {
676
+ resolve,
677
+ reject
678
+ });
679
+ try {
680
+ start(requestId);
681
+ } catch (err) {
682
+ this.pendingRequests.delete(requestId);
683
+ reject(err);
684
+ }
685
+ });
686
+ }
687
+ drain() {
688
+ if (this.disposed) return;
689
+ this.clearTimeout();
690
+ try {
691
+ for (let i = 0; i < Queue.LIMIT_PER_TICK; i++) if (!this.poll()) break;
692
+ } catch (err) {
693
+ this.emitter.emit("error", err);
694
+ }
695
+ this.timer = setTimeout(() => this.drain(), Queue.POLL_INTERVAL);
696
+ }
697
+ poll() {
698
+ const out = allocBuffer();
699
+ const status = this.functions.nil_ffi_poll(out);
700
+ if (status === ffi_Status.ERR_NOTHING_TO_POLL) return false;
701
+ if (status !== ffi_Status.OK) throw NilError.fromStatus(status);
702
+ const ptr = readBufferPtr(out);
703
+ if (ptr === 0n) throw new Error("failed to poll: unexpected null pointer");
704
+ try {
705
+ const json = ffi.toString(ptr) ?? "";
706
+ const entry = JSON.parse(json);
707
+ this.dispatch(entry);
708
+ } finally {
709
+ this.functions.nil_ffi_free_str(ptr);
710
+ }
711
+ return true;
712
+ }
713
+ dispatch(entry) {
714
+ switch (entry.kind) {
715
+ case "response": {
716
+ const response = JSON.parse(entry.json_str);
717
+ this.emitter.emit("response", response);
718
+ break;
719
+ }
720
+ }
721
+ }
722
+ rejectAll(error) {
723
+ this.clearTimeout();
724
+ this.pendingRequests.values().forEach(({ reject }) => reject(error));
725
+ this.pendingRequests.clear();
726
+ }
727
+ clearTimeout() {
728
+ if (!isNil(this.timer)) {
729
+ clearTimeout(this.timer);
730
+ this.timer = null;
731
+ }
732
+ }
733
+ onResponse(response) {
734
+ const pending = this.pendingRequests.get(response.id);
735
+ if (pending) {
736
+ this.pendingRequests.delete(response.id);
737
+ if (response.kind === "ok") pending.resolve(response.data);
738
+ else {
739
+ const { status, error } = response;
740
+ pending.reject(new NilError(error, { status }));
741
+ }
742
+ } else this.emitter.emit("error", new UnknownResponseError(response));
743
+ }
744
+ };
745
+ //#endregion
683
746
  //#region package.json
684
- var version = "0.7.1";
747
+ var version = "0.7.3";
748
+ //#endregion
749
+ //#region \0@oxc-project+runtime@0.139.0/helpers/esm/decorate.js
750
+ function __decorate(decorators, target, key, desc) {
751
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
752
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
753
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
754
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
755
+ }
685
756
  //#endregion
686
757
  //#region src/index.ts
758
+ //! Copyright (C) Call of Nil contributors
759
+ //! SPDX-License-Identifier: AGPL-3.0-only
687
760
  var VERSION = version;
688
- var Nil = class {
689
- constructor(dll) {
761
+ var USER_AGENT = `nil-ffi-node/${VERSION}`;
762
+ var Nil = class Nil {
763
+ constructor(dll, options) {
690
764
  this.disposed = false;
691
765
  if (!dll.endsWith(ffi.suffix)) dll = `${dll}.${ffi.suffix}`;
766
+ this.path = resolve(dll);
692
767
  this.handle = ffi.dlopen(dll, definitions);
693
768
  this.functions = this.handle.functions;
694
- this.queue = new Queue(this.handle);
695
- this.setUserAgent(`nil-ffi-node/${version}`).catch(console.error);
769
+ this.queue = new Queue(this.handle, options);
770
+ }
771
+ static async init(dll, options) {
772
+ const nil = new Nil(dll, options);
773
+ await nil.setUserAgent(USER_AGENT);
774
+ return nil;
775
+ }
776
+ static async initLatest(outDir, options) {
777
+ const dll = await this.downloadLatest(outDir);
778
+ return this.init(dll, options);
779
+ }
780
+ static async downloadLatest(outDir) {
781
+ const file = `libnil.${ffi.suffix}`;
782
+ const response = await fetch(`https://tsukilabs.dev.br/${file}`);
783
+ if (!response.ok) throw new Error(`failed to download ${file}: ${response.status} ${response.statusText}`);
784
+ const bytes = await response.bytes();
785
+ const path = join(outDir ?? cwd(), file);
786
+ await writeFile(path, bytes);
787
+ return resolve(path);
696
788
  }
697
789
  [Symbol.dispose]() {
698
790
  this.close();
@@ -704,58 +796,178 @@ var Nil = class {
704
796
  this.functions.nil_ffi_shutdown();
705
797
  this.handle[Symbol.dispose]();
706
798
  }
799
+ isClosed() {
800
+ return this.disposed;
801
+ }
802
+ async authorize(req) {
803
+ return this.queue.request((requestId) => {
804
+ this.functions.nil_authorize(requestId, JSON.stringify(req));
805
+ });
806
+ }
707
807
  async getClientVersion() {
708
- this.throwIfClosed();
709
808
  return this.queue.request((requestId) => {
710
809
  this.functions.nil_client_version(requestId);
711
810
  });
712
811
  }
713
812
  async getFfiVersion() {
714
- this.throwIfClosed();
715
813
  return this.queue.request((requestId) => {
716
814
  this.functions.nil_ffi_version(requestId);
717
815
  });
718
816
  }
817
+ async getPlayer(req) {
818
+ return this.queue.request((requestId) => {
819
+ this.functions.nil_get_player(requestId, JSON.stringify(req));
820
+ });
821
+ }
822
+ async getPlayerIds(req) {
823
+ return this.queue.request((requestId) => {
824
+ this.functions.nil_get_player_ids(requestId, JSON.stringify(req));
825
+ });
826
+ }
827
+ async getPlayerStatus(req) {
828
+ return this.queue.request((requestId) => {
829
+ this.functions.nil_get_player_status(requestId, JSON.stringify(req));
830
+ });
831
+ }
719
832
  async getRemoteWorldLimit() {
720
- this.throwIfClosed();
721
833
  return this.queue.request((requestId) => {
722
834
  this.functions.nil_get_remote_world_limit(requestId);
723
835
  });
724
836
  }
725
837
  async getRemoteWorldLimitPerUser() {
726
- this.throwIfClosed();
727
838
  return this.queue.request((requestId) => {
728
839
  this.functions.nil_get_remote_world_limit_per_user(requestId);
729
840
  });
730
841
  }
731
842
  async getRemoteWorlds() {
732
- this.throwIfClosed();
733
843
  return this.queue.request((requestId) => {
734
844
  this.functions.nil_get_remote_worlds(requestId);
735
845
  });
736
846
  }
847
+ async getServerAddr() {
848
+ return this.queue.request((requestId) => {
849
+ this.functions.nil_server_addr(requestId);
850
+ });
851
+ }
852
+ async getServerKind() {
853
+ return this.queue.request((requestId) => {
854
+ this.functions.nil_get_server_kind(requestId);
855
+ });
856
+ }
737
857
  async getUserAgent() {
738
- this.throwIfClosed();
739
858
  return this.queue.request((requestId) => {
740
859
  this.functions.nil_user_agent(requestId);
741
860
  });
742
861
  }
743
- async getWorld() {
744
- this.throwIfClosed();
862
+ async getWorldId() {
745
863
  return this.queue.request((requestId) => {
746
864
  this.functions.nil_world(requestId);
747
865
  });
748
866
  }
867
+ async isHost() {
868
+ return this.queue.request((requestId) => {
869
+ this.functions.nil_is_host(requestId);
870
+ });
871
+ }
872
+ async isLocal() {
873
+ return this.queue.request((requestId) => {
874
+ this.functions.nil_is_local(requestId);
875
+ });
876
+ }
877
+ async isReady() {
878
+ return this.queue.request((requestId) => {
879
+ this.functions.nil_is_ready(requestId);
880
+ });
881
+ }
882
+ async isRemote() {
883
+ return this.queue.request((requestId) => {
884
+ this.functions.nil_is_remote(requestId);
885
+ });
886
+ }
887
+ async playerExists(req) {
888
+ return this.queue.request((requestId) => {
889
+ this.functions.nil_player_exists(requestId, JSON.stringify(req));
890
+ });
891
+ }
892
+ async setPlayerStatus(req) {
893
+ return this.queue.request((requestId) => {
894
+ this.functions.nil_set_player_status(requestId, JSON.stringify(req));
895
+ });
896
+ }
749
897
  async setUserAgent(userAgent) {
750
- this.throwIfClosed();
751
- await this.queue.request((requestId) => {
752
- const arg1 = JSON.stringify(userAgent);
753
- this.functions.nil_set_user_agent(requestId, arg1);
898
+ return this.queue.request((requestId) => {
899
+ this.functions.nil_set_user_agent(requestId, JSON.stringify(userAgent));
900
+ });
901
+ }
902
+ async spawnPlayer(req) {
903
+ return this.queue.request((requestId) => {
904
+ this.functions.nil_spawn_player(requestId, JSON.stringify(req));
754
905
  });
755
906
  }
756
- throwIfClosed() {
757
- if (this.disposed) throw new HandleClosedError();
907
+ async startServer(options) {
908
+ return this.queue.request((requestId) => {
909
+ this.functions.nil_start_server(requestId, JSON.stringify(options));
910
+ });
911
+ }
912
+ async startServerWithSavedata(path) {
913
+ return this.queue.request((requestId) => {
914
+ this.functions.nil_start_server_with_savedata(requestId, JSON.stringify(path));
915
+ });
916
+ }
917
+ async stopClient() {
918
+ return this.queue.request((requestId) => {
919
+ this.functions.nil_stop_client(requestId);
920
+ });
921
+ }
922
+ async stopServer() {
923
+ return this.queue.request((requestId) => {
924
+ this.functions.nil_stop_server(requestId);
925
+ });
926
+ }
927
+ async updateClient(options) {
928
+ return this.queue.request((requestId) => {
929
+ this.functions.nil_update_client(requestId, JSON.stringify(options));
930
+ });
931
+ }
932
+ async validateToken(req) {
933
+ return this.queue.request((requestId) => {
934
+ this.functions.nil_validate_token(requestId, JSON.stringify(req));
935
+ });
758
936
  }
759
937
  };
938
+ __decorate([ThrowIfClosed], Nil.prototype, "authorize", null);
939
+ __decorate([ThrowIfClosed], Nil.prototype, "getClientVersion", null);
940
+ __decorate([ThrowIfClosed], Nil.prototype, "getFfiVersion", null);
941
+ __decorate([ThrowIfClosed], Nil.prototype, "getPlayer", null);
942
+ __decorate([ThrowIfClosed], Nil.prototype, "getPlayerIds", null);
943
+ __decorate([ThrowIfClosed], Nil.prototype, "getPlayerStatus", null);
944
+ __decorate([ThrowIfClosed], Nil.prototype, "getRemoteWorldLimit", null);
945
+ __decorate([ThrowIfClosed], Nil.prototype, "getRemoteWorldLimitPerUser", null);
946
+ __decorate([ThrowIfClosed], Nil.prototype, "getRemoteWorlds", null);
947
+ __decorate([ThrowIfClosed], Nil.prototype, "getServerAddr", null);
948
+ __decorate([ThrowIfClosed], Nil.prototype, "getServerKind", null);
949
+ __decorate([ThrowIfClosed], Nil.prototype, "getUserAgent", null);
950
+ __decorate([ThrowIfClosed], Nil.prototype, "getWorldId", null);
951
+ __decorate([ThrowIfClosed], Nil.prototype, "isHost", null);
952
+ __decorate([ThrowIfClosed], Nil.prototype, "isLocal", null);
953
+ __decorate([ThrowIfClosed], Nil.prototype, "isReady", null);
954
+ __decorate([ThrowIfClosed], Nil.prototype, "isRemote", null);
955
+ __decorate([ThrowIfClosed], Nil.prototype, "playerExists", null);
956
+ __decorate([ThrowIfClosed], Nil.prototype, "setPlayerStatus", null);
957
+ __decorate([ThrowIfClosed], Nil.prototype, "setUserAgent", null);
958
+ __decorate([ThrowIfClosed], Nil.prototype, "spawnPlayer", null);
959
+ __decorate([ThrowIfClosed], Nil.prototype, "startServer", null);
960
+ __decorate([ThrowIfClosed], Nil.prototype, "startServerWithSavedata", null);
961
+ __decorate([ThrowIfClosed], Nil.prototype, "stopClient", null);
962
+ __decorate([ThrowIfClosed], Nil.prototype, "stopServer", null);
963
+ __decorate([ThrowIfClosed], Nil.prototype, "updateClient", null);
964
+ __decorate([ThrowIfClosed], Nil.prototype, "validateToken", null);
965
+ function ThrowIfClosed(_target, _key, descriptor) {
966
+ const method = descriptor.value;
967
+ descriptor.value = function(...args) {
968
+ if (this.isClosed()) throw new HandleClosedError();
969
+ return Reflect.apply(method, this, args);
970
+ };
971
+ }
760
972
  //#endregion
761
- export { FfiError, HandleClosedError, Nil, VERSION, definitions };
973
+ export { HandleClosedError, Nil, NilError, USER_AGENT, UnknownResponseError, VERSION, definitions };
package/dist/queue.d.ts CHANGED
@@ -1,20 +1,29 @@
1
1
  import { Handle } from '.';
2
+ import { nil, Option } from '@tb-dev/utils';
2
3
  import { ffi_RequestId } from '@tsukilabs/nil-bindings';
3
- type RequestFn = (requestId: ffi_RequestId) => void;
4
4
  export declare class Queue implements Disposable {
5
5
  private static readonly POLL_INTERVAL;
6
6
  private static readonly LIMIT_PER_TICK;
7
- readonly functions: Handle["functions"];
8
- private timer;
9
- private readonly pending;
10
7
  private disposed;
8
+ private readonly functions;
9
+ private readonly emitter;
11
10
  private readonly requestId;
12
- constructor(handle: Handle);
11
+ private readonly pendingRequests;
12
+ private timer;
13
+ private readonly errorHandler;
14
+ constructor(handle: Handle, options?: QueueOptions);
13
15
  [Symbol.dispose](): void;
14
- request<T = unknown>(start: RequestFn): Promise<T>;
15
- private startPolling;
16
+ request<T = nil>(start: RequestFn): Promise<T>;
17
+ private drain;
16
18
  private poll;
17
- private pollOnce;
19
+ private dispatch;
18
20
  private rejectAll;
21
+ private clearTimeout;
22
+ private onResponse;
23
+ }
24
+ type RequestFn = (requestId: ffi_RequestId) => void;
25
+ type ErrorHandler = (error: unknown) => void;
26
+ export interface QueueOptions {
27
+ onError?: Option<ErrorHandler>;
19
28
  }
20
29
  export {};
package/dist/request.d.ts CHANGED
@@ -1,4 +1,5 @@
1
+ import { ffi_RequestId } from '@tsukilabs/nil-bindings';
1
2
  export declare class RequestId {
2
3
  #private;
3
- next(): number;
4
+ next(): ffi_RequestId;
4
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsukilabs/nil-ffi",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@11.17.0",
6
6
  "license": "AGPL-3.0-only",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@tb-dev/utils": "^8.1.1",
32
- "@tsukilabs/nil-bindings": "workspace:*",
32
+ "@tsukilabs/nil-bindings": "^0.7.2",
33
33
  "es-toolkit": "^1.50.0"
34
34
  },
35
35
  "files": ["dist"],