@takaro/gameserver 0.0.0-next.09a7ca1

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.
Files changed (106) hide show
  1. package/README.md +36 -0
  2. package/dist/TakaroEmitter.d.ts +30 -0
  3. package/dist/TakaroEmitter.d.ts.map +1 -0
  4. package/dist/TakaroEmitter.js +101 -0
  5. package/dist/TakaroEmitter.js.map +1 -0
  6. package/dist/config.d.ts +9 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +13 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/gameservers/7d2d/apiResponses.d.ts +194 -0
  11. package/dist/gameservers/7d2d/apiResponses.d.ts.map +1 -0
  12. package/dist/gameservers/7d2d/apiResponses.js +2 -0
  13. package/dist/gameservers/7d2d/apiResponses.js.map +1 -0
  14. package/dist/gameservers/7d2d/connectionInfo.d.ts +38 -0
  15. package/dist/gameservers/7d2d/connectionInfo.d.ts.map +1 -0
  16. package/dist/gameservers/7d2d/connectionInfo.js +66 -0
  17. package/dist/gameservers/7d2d/connectionInfo.js.map +1 -0
  18. package/dist/gameservers/7d2d/emitter.d.ts +32 -0
  19. package/dist/gameservers/7d2d/emitter.d.ts.map +1 -0
  20. package/dist/gameservers/7d2d/emitter.js +273 -0
  21. package/dist/gameservers/7d2d/emitter.js.map +1 -0
  22. package/dist/gameservers/7d2d/index.d.ts +34 -0
  23. package/dist/gameservers/7d2d/index.d.ts.map +1 -0
  24. package/dist/gameservers/7d2d/index.js +304 -0
  25. package/dist/gameservers/7d2d/index.js.map +1 -0
  26. package/dist/gameservers/7d2d/itemWorker.d.ts +2 -0
  27. package/dist/gameservers/7d2d/itemWorker.d.ts.map +1 -0
  28. package/dist/gameservers/7d2d/itemWorker.js +36 -0
  29. package/dist/gameservers/7d2d/itemWorker.js.map +1 -0
  30. package/dist/gameservers/7d2d/items-7d2d.json +25051 -0
  31. package/dist/gameservers/7d2d/sdtdAPIClient.d.ts +19 -0
  32. package/dist/gameservers/7d2d/sdtdAPIClient.d.ts.map +1 -0
  33. package/dist/gameservers/7d2d/sdtdAPIClient.js +57 -0
  34. package/dist/gameservers/7d2d/sdtdAPIClient.js.map +1 -0
  35. package/dist/gameservers/generic/connectionInfo.d.ts +16 -0
  36. package/dist/gameservers/generic/connectionInfo.d.ts.map +1 -0
  37. package/dist/gameservers/generic/connectionInfo.js +29 -0
  38. package/dist/gameservers/generic/connectionInfo.js.map +1 -0
  39. package/dist/gameservers/generic/connectorClient.d.ts +7 -0
  40. package/dist/gameservers/generic/connectorClient.d.ts.map +1 -0
  41. package/dist/gameservers/generic/connectorClient.js +60 -0
  42. package/dist/gameservers/generic/connectorClient.js.map +1 -0
  43. package/dist/gameservers/generic/emitter.d.ts +13 -0
  44. package/dist/gameservers/generic/emitter.d.ts.map +1 -0
  45. package/dist/gameservers/generic/emitter.js +31 -0
  46. package/dist/gameservers/generic/emitter.js.map +1 -0
  47. package/dist/gameservers/generic/index.d.ts +36 -0
  48. package/dist/gameservers/generic/index.d.ts.map +1 -0
  49. package/dist/gameservers/generic/index.js +170 -0
  50. package/dist/gameservers/generic/index.js.map +1 -0
  51. package/dist/gameservers/rust/connectionInfo.d.ts +29 -0
  52. package/dist/gameservers/rust/connectionInfo.d.ts.map +1 -0
  53. package/dist/gameservers/rust/connectionInfo.js +51 -0
  54. package/dist/gameservers/rust/connectionInfo.js.map +1 -0
  55. package/dist/gameservers/rust/emitter.d.ts +32 -0
  56. package/dist/gameservers/rust/emitter.d.ts.map +1 -0
  57. package/dist/gameservers/rust/emitter.js +160 -0
  58. package/dist/gameservers/rust/emitter.js.map +1 -0
  59. package/dist/gameservers/rust/index.d.ts +35 -0
  60. package/dist/gameservers/rust/index.d.ts.map +1 -0
  61. package/dist/gameservers/rust/index.js +217 -0
  62. package/dist/gameservers/rust/index.js.map +1 -0
  63. package/dist/gameservers/rust/items-rust.json +20771 -0
  64. package/dist/getGame.d.ts +10 -0
  65. package/dist/getGame.d.ts.map +1 -0
  66. package/dist/getGame.js +27 -0
  67. package/dist/getGame.js.map +1 -0
  68. package/dist/interfaces/GameServer.d.ts +99 -0
  69. package/dist/interfaces/GameServer.d.ts.map +1 -0
  70. package/dist/interfaces/GameServer.js +235 -0
  71. package/dist/interfaces/GameServer.js.map +1 -0
  72. package/dist/main.d.ts +12 -0
  73. package/dist/main.d.ts.map +1 -0
  74. package/dist/main.js +12 -0
  75. package/dist/main.js.map +1 -0
  76. package/package.json +30 -0
  77. package/src/TakaroEmitter.ts +146 -0
  78. package/src/TakaroEmitter.unit.test.ts +201 -0
  79. package/src/__tests__/gameEventEmitter.test.ts +29 -0
  80. package/src/config.ts +20 -0
  81. package/src/gameservers/7d2d/__tests__/7d2dActions.unit.test.ts +96 -0
  82. package/src/gameservers/7d2d/__tests__/7d2dEventDetection.unit.test.ts +424 -0
  83. package/src/gameservers/7d2d/apiResponses.ts +213 -0
  84. package/src/gameservers/7d2d/connectionInfo.ts +46 -0
  85. package/src/gameservers/7d2d/emitter.ts +334 -0
  86. package/src/gameservers/7d2d/emitter.unit.test.ts +117 -0
  87. package/src/gameservers/7d2d/index.ts +367 -0
  88. package/src/gameservers/7d2d/itemWorker.ts +41 -0
  89. package/src/gameservers/7d2d/items-7d2d.json +25051 -0
  90. package/src/gameservers/7d2d/sdtdAPIClient.ts +82 -0
  91. package/src/gameservers/generic/connectionInfo.ts +19 -0
  92. package/src/gameservers/generic/connectorClient.ts +73 -0
  93. package/src/gameservers/generic/emitter.ts +36 -0
  94. package/src/gameservers/generic/index.ts +193 -0
  95. package/src/gameservers/rust/__tests__/rustActions.unit.test.ts +141 -0
  96. package/src/gameservers/rust/connectionInfo.ts +35 -0
  97. package/src/gameservers/rust/emitter.ts +198 -0
  98. package/src/gameservers/rust/emitter.unit.test.ts +95 -0
  99. package/src/gameservers/rust/index.ts +270 -0
  100. package/src/gameservers/rust/items-rust.json +20771 -0
  101. package/src/getGame.ts +34 -0
  102. package/src/interfaces/GameServer.ts +215 -0
  103. package/src/main.ts +16 -0
  104. package/tsconfig.build.json +16 -0
  105. package/tsconfig.json +9 -0
  106. package/typedoc.json +3 -0
@@ -0,0 +1,201 @@
1
+ import { TakaroEmitter } from './TakaroEmitter.js';
2
+ import { expect, sandbox } from '@takaro/test';
3
+ import { EventLogLine, GameEvents } from '@takaro/modules';
4
+ import { describe, it } from 'node:test';
5
+
6
+ class ExtendedTakaroEmitter extends TakaroEmitter {
7
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
8
+ async start() {}
9
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
10
+ async stop() {}
11
+
12
+ async foo() {
13
+ throw new Error('testing error');
14
+ }
15
+ }
16
+
17
+ describe('TakaroEmitter', () => {
18
+ it('Can listen for events', async () => {
19
+ const emitter = new ExtendedTakaroEmitter();
20
+ const spy = sandbox.spy();
21
+
22
+ emitter.on(GameEvents.LOG_LINE, spy);
23
+
24
+ await emitter.emit(
25
+ GameEvents.LOG_LINE,
26
+ new EventLogLine({
27
+ msg: 'test',
28
+ }),
29
+ );
30
+
31
+ expect(spy).to.have.been.calledOnce;
32
+
33
+ await emitter.emit(
34
+ GameEvents.LOG_LINE,
35
+ new EventLogLine({
36
+ msg: 'test',
37
+ }),
38
+ );
39
+
40
+ expect(spy).to.have.been.calledTwice;
41
+ });
42
+
43
+ it('Can remove a listener, which causes further events to not be received', async () => {
44
+ const emitter = new ExtendedTakaroEmitter();
45
+ const spy = sandbox.spy();
46
+
47
+ emitter.on(GameEvents.LOG_LINE, spy);
48
+
49
+ await emitter.emit(
50
+ GameEvents.LOG_LINE,
51
+ new EventLogLine({
52
+ msg: 'test',
53
+ }),
54
+ );
55
+
56
+ expect(spy).to.have.been.calledOnce;
57
+
58
+ emitter.off(GameEvents.LOG_LINE, spy);
59
+
60
+ await emitter.emit(
61
+ GameEvents.LOG_LINE,
62
+ new EventLogLine({
63
+ msg: 'test',
64
+ }),
65
+ );
66
+
67
+ expect(spy).to.have.been.calledOnce;
68
+ });
69
+
70
+ it('Can have multiple listeners for the same event', async () => {
71
+ const emitter = new ExtendedTakaroEmitter();
72
+ const spy1 = sandbox.spy();
73
+ const spy2 = sandbox.spy();
74
+ const spy3 = sandbox.spy();
75
+
76
+ emitter.on(GameEvents.LOG_LINE, spy1);
77
+ emitter.on(GameEvents.LOG_LINE, spy2);
78
+ emitter.on(GameEvents.LOG_LINE, spy3);
79
+
80
+ await emitter.emit(
81
+ GameEvents.LOG_LINE,
82
+ new EventLogLine({
83
+ msg: 'test',
84
+ }),
85
+ );
86
+
87
+ expect(spy1).to.have.been.calledOnce;
88
+ expect(spy2).to.have.been.calledOnce;
89
+ expect(spy3).to.have.been.calledOnce;
90
+ });
91
+
92
+ it('Can remove one listener without affecting others', async () => {
93
+ const emitter = new ExtendedTakaroEmitter();
94
+ const spy1 = sandbox.spy();
95
+ const spy2 = sandbox.spy();
96
+
97
+ emitter.on(GameEvents.LOG_LINE, spy1);
98
+ emitter.on(GameEvents.LOG_LINE, spy2);
99
+
100
+ await emitter.emit(
101
+ GameEvents.LOG_LINE,
102
+ new EventLogLine({
103
+ msg: 'test',
104
+ }),
105
+ );
106
+
107
+ expect(spy1).to.have.been.calledOnce;
108
+ expect(spy2).to.have.been.calledOnce;
109
+
110
+ emitter.off(GameEvents.LOG_LINE, spy1);
111
+
112
+ await emitter.emit(
113
+ GameEvents.LOG_LINE,
114
+ new EventLogLine({
115
+ msg: 'test',
116
+ }),
117
+ );
118
+
119
+ expect(spy1).to.have.been.calledOnce;
120
+ expect(spy2).to.have.been.calledTwice;
121
+ });
122
+
123
+ it('Errors happening inside extended class do not interrupt flow of events', async () => {
124
+ const emitter = new ExtendedTakaroEmitter();
125
+ const spy = sandbox.spy();
126
+ const errorSpy = sandbox.spy();
127
+
128
+ emitter.on(GameEvents.LOG_LINE, spy);
129
+ emitter.on('error', errorSpy);
130
+
131
+ await expect(emitter.foo()).to.eventually.be.rejectedWith('testing error');
132
+
133
+ expect(errorSpy).to.have.been.calledOnce;
134
+
135
+ await emitter.emit(
136
+ GameEvents.LOG_LINE,
137
+ new EventLogLine({
138
+ msg: 'test',
139
+ }),
140
+ );
141
+
142
+ expect(spy).to.have.been.calledOnce;
143
+ });
144
+
145
+ // it('Validates data on emitting', async (t) => {
146
+ // t.skip();
147
+ // const emitter = new ExtendedTakaroEmitter();
148
+ // const spy = sandbox.spy();
149
+ // const errorSpy = sandbox.spy();
150
+
151
+ // emitter.on(GameEvents.LOG_LINE, spy);
152
+ // emitter.on('error', errorSpy);
153
+
154
+ // await emitter.emit(
155
+ // GameEvents.LOG_LINE,
156
+ // new EventLogLine({
157
+ // msg: 'test',
158
+ // // @ts-expect-error testing validation, our types accurately detect this is invalid
159
+ // unknownProperty: 'this should trip validation',
160
+ // }),
161
+ // );
162
+
163
+ // expect(errorSpy).to.have.been.calledOnce;
164
+ // expect(errorSpy.getCall(0).args[0].message).to.match(
165
+ // /property unknownProperty has failed the following constraints: whitelistValidation/,
166
+ // );
167
+ // });
168
+
169
+ it('Throws when an error occurs and no listeners are attached to the "error" event', async () => {
170
+ const emitter = new ExtendedTakaroEmitter();
171
+ const errorSpy = sandbox.spy();
172
+
173
+ await expect(emitter.foo()).to.eventually.be.rejectedWith('Unhandled error in TakaroEmitter');
174
+
175
+ emitter.on('error', errorSpy);
176
+
177
+ await expect(emitter.foo()).to.eventually.be.rejectedWith('testing error');
178
+ expect(errorSpy).to.have.been.calledOnce;
179
+ });
180
+
181
+ it('Should not process events after stop() is called', async () => {
182
+ const emitter = new ExtendedTakaroEmitter();
183
+ const spy = sandbox.spy();
184
+
185
+ emitter.on(GameEvents.LOG_LINE, spy);
186
+
187
+ // Event before stop - should be processed
188
+ await emitter.emit(GameEvents.LOG_LINE, new EventLogLine({ msg: 'before stop' }));
189
+ expect(spy).to.have.been.calledOnce;
190
+
191
+ // Remove all listeners then call stop (like GameServerManager.remove() does during reconnection)
192
+ emitter.removeAllListeners();
193
+ await emitter.stop();
194
+
195
+ // Event after stop - should NOT be processed
196
+ await emitter.emit(GameEvents.LOG_LINE, new EventLogLine({ msg: 'after stop' }));
197
+
198
+ // Now it should pass - listeners were removed so spy still called once
199
+ expect(spy).to.have.been.calledOnce; // Should still be 1, not 2
200
+ });
201
+ });
@@ -0,0 +1,29 @@
1
+ import { expect, sandbox } from '@takaro/test';
2
+ import { GenericConnectionInfo } from '../gameservers/generic/connectionInfo.js';
3
+ import { Generic } from '../gameservers/generic/index.js';
4
+ import { GameEvents } from '@takaro/modules';
5
+ import { describe, it } from 'node:test';
6
+
7
+ describe('GameEventEmitter', () => {
8
+ /**
9
+ * This test doesn't really do much interesting runtime validation
10
+ * It exists to ensure that the event emitter is safely typed
11
+ * We use @ts-expect-error so that if the compiler fails to mark these as errors, we'll know instantly
12
+ */
13
+ it('Has a typed event emitter', async () => {
14
+ const gameServer = new Generic(new GenericConnectionInfo({}), {}, 'dummy-id');
15
+ const emitter = gameServer.getEventEmitter();
16
+ const listenerSpy = sandbox.spy();
17
+
18
+ emitter.on(GameEvents.PLAYER_CONNECTED, async (e) => {
19
+ expect(() => e.player.name).to.throw("Cannot read properties of undefined (reading 'name')");
20
+ });
21
+ emitter.on(GameEvents.PLAYER_CONNECTED, listenerSpy);
22
+
23
+ // @ts-expect-error Should use the enum here
24
+ emitter.on('non-existent-event', listenerSpy);
25
+
26
+ // But the raw string will also work in runtime when ignoring the compilation error
27
+ emitter.on('player-connected', listenerSpy);
28
+ });
29
+ });
package/src/config.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { Config, IBaseConfig } from '@takaro/config';
2
+
3
+ interface IGameServerLibConfig extends IBaseConfig {
4
+ connector: {
5
+ host: string;
6
+ };
7
+ }
8
+
9
+ const configSchema = {
10
+ connector: {
11
+ host: {
12
+ doc: 'The host of the connector',
13
+ format: String,
14
+ default: 'http://localhost:3003',
15
+ env: 'CONNECTOR_HOST',
16
+ },
17
+ },
18
+ };
19
+
20
+ export const config = new Config<IGameServerLibConfig>([configSchema]);
@@ -0,0 +1,96 @@
1
+ import { expect, sandbox } from '@takaro/test';
2
+ import { SevenDaysToDie } from '../index.js';
3
+ import { CommandOutput } from '../../../interfaces/GameServer.js';
4
+ import { SdtdConnectionInfo } from '../connectionInfo.js';
5
+ import { describe, it } from 'node:test';
6
+
7
+ const testData = {
8
+ oneBan:
9
+ 'Ban list entries:\n Banned until - UserID (name) - Reason\n 2023-06-29 19:39:56 - EOS_00028a9b73bb45b2b74e8f22cda7d225 (-unknown-) - Totally valid testing reason :)\n',
10
+ twoBans:
11
+ 'Ban list entries:\n Banned until - UserID (name) - Reason\n 2028-06-29 17:49:45 - EOS_0002e0daea3b493fa146ce6d06e79a57 (-unknown-) - test\n 2028-06-29 19:19:40 - EOS_00028a9b73bb45b2b74e8f22cda7d225 (-unknown-) - test\n',
12
+ noBans: 'Ban list entries:\n Banned until - UserID (name) - Reason\n',
13
+ oneBanWithDisplayName:
14
+ 'Ban list entries:\n Banned until - UserID (name) - Reason\n 2023-06-29 19:40:59 - EOS_00028a9b73bb45b2b74e8f22cda7d225 (testing display name) - Totally valid testing reason :)\n',
15
+ };
16
+
17
+ const mockSdtdConnectionInfo = new SdtdConnectionInfo({
18
+ adminToken: 'aaa',
19
+ adminUser: 'aaa',
20
+ useTls: false,
21
+ host: 'localhost',
22
+ });
23
+
24
+ describe('7d2d Actions', () => {
25
+ describe('listBans', () => {
26
+ it('Can parse ban list with a single ban', async () => {
27
+ sandbox.stub(SevenDaysToDie.prototype, 'executeConsoleCommand').resolves(
28
+ new CommandOutput({
29
+ rawResult: testData.oneBan,
30
+ success: true,
31
+ }),
32
+ );
33
+
34
+ const result = await new SevenDaysToDie(await mockSdtdConnectionInfo).listBans();
35
+
36
+ expect(result).to.be.an('array');
37
+ expect(result).to.have.lengthOf(1);
38
+ expect(result[0].player.gameId).to.equal('00028a9b73bb45b2b74e8f22cda7d225');
39
+ expect(result[0].player.epicOnlineServicesId).to.equal('00028a9b73bb45b2b74e8f22cda7d225');
40
+ expect(result[0].expiresAt).to.equal('2023-06-29T19:39:56.000Z');
41
+ });
42
+
43
+ it('Can parse ban list with two bans', async () => {
44
+ sandbox.stub(SevenDaysToDie.prototype, 'executeConsoleCommand').resolves(
45
+ new CommandOutput({
46
+ rawResult: testData.twoBans,
47
+ success: true,
48
+ }),
49
+ );
50
+
51
+ const result = await new SevenDaysToDie(await mockSdtdConnectionInfo).listBans();
52
+
53
+ expect(result).to.be.an('array');
54
+ expect(result).to.have.lengthOf(2);
55
+
56
+ expect(result[0].player.gameId).to.equal('0002e0daea3b493fa146ce6d06e79a57');
57
+ expect(result[0].player.epicOnlineServicesId).to.equal('0002e0daea3b493fa146ce6d06e79a57');
58
+ expect(result[0].expiresAt).to.equal('2028-06-29T17:49:45.000Z');
59
+
60
+ expect(result[1].player.gameId).to.equal('00028a9b73bb45b2b74e8f22cda7d225');
61
+ expect(result[1].player.epicOnlineServicesId).to.equal('00028a9b73bb45b2b74e8f22cda7d225');
62
+ expect(result[1].expiresAt).to.equal('2028-06-29T19:19:40.000Z');
63
+ });
64
+
65
+ it('Can parse ban list with no bans', async () => {
66
+ sandbox.stub(SevenDaysToDie.prototype, 'executeConsoleCommand').resolves(
67
+ new CommandOutput({
68
+ rawResult: testData.noBans,
69
+ success: true,
70
+ }),
71
+ );
72
+
73
+ const result = await new SevenDaysToDie(await mockSdtdConnectionInfo).listBans();
74
+
75
+ expect(result).to.be.an('array');
76
+ expect(result).to.have.lengthOf(0);
77
+ });
78
+
79
+ it('Can parse ban list with a single ban with a display name', async () => {
80
+ sandbox.stub(SevenDaysToDie.prototype, 'executeConsoleCommand').resolves(
81
+ new CommandOutput({
82
+ rawResult: testData.oneBanWithDisplayName,
83
+ success: true,
84
+ }),
85
+ );
86
+
87
+ const result = await new SevenDaysToDie(await mockSdtdConnectionInfo).listBans();
88
+
89
+ expect(result).to.be.an('array');
90
+ expect(result).to.have.lengthOf(1);
91
+ expect(result[0].player.gameId).to.equal('00028a9b73bb45b2b74e8f22cda7d225');
92
+ expect(result[0].player.epicOnlineServicesId).to.equal('00028a9b73bb45b2b74e8f22cda7d225');
93
+ expect(result[0].expiresAt).to.equal('2023-06-29T19:40:59.000Z');
94
+ });
95
+ });
96
+ });