@takaro/gameserver 0.0.0-next.0da151e

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 +16 -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 +9 -0
  105. package/tsconfig.json +9 -0
  106. package/typedoc.json +3 -0
@@ -0,0 +1,424 @@
1
+ import { expect, sandbox } from '@takaro/test';
2
+ import {
3
+ EventChatMessage,
4
+ EventEntityKilled,
5
+ EventPlayerConnected,
6
+ EventPlayerDeath,
7
+ GameEvents,
8
+ IGamePlayer,
9
+ } from '@takaro/modules';
10
+ import { SdtdConnectionInfo } from '../connectionInfo.js';
11
+ import { SevenDaysToDieEmitter } from '../emitter.js';
12
+ import { SevenDaysToDie } from '../index.js';
13
+ import { describe, beforeEach, it } from 'node:test';
14
+
15
+ const mockConnectionInfo = async (overrides?: Partial<SdtdConnectionInfo>) =>
16
+ new SdtdConnectionInfo({
17
+ adminToken: 'aaa',
18
+ adminUser: 'aaa',
19
+ useTls: false,
20
+ host: 'localhost',
21
+ ...overrides,
22
+ });
23
+
24
+ const mockGamePlayer = new IGamePlayer({
25
+ name: 'Catalysm',
26
+ ping: undefined,
27
+ gameId: '0002b5d970954287afdcb5dc35af0424',
28
+ steamId: '76561198028175941',
29
+ });
30
+
31
+ const mockGamePlayerWithBrackets = new IGamePlayer({
32
+ name: '[ptz]stoun',
33
+ ping: undefined,
34
+ gameId: '0002b5d970954287afdcb5dc35af0425',
35
+ steamId: '76561198013829487',
36
+ });
37
+
38
+ describe('7d2d event detection', () => {
39
+ let emitStub = sandbox.stub(SevenDaysToDieEmitter.prototype, 'emit');
40
+
41
+ beforeEach(() => {
42
+ sandbox.restore();
43
+ emitStub = sandbox.stub(SevenDaysToDieEmitter.prototype, 'emit');
44
+ sandbox.stub(SevenDaysToDie.prototype, 'steamIdOrXboxToGameId').callsFake(async (id: string) => {
45
+ if (id === '76561198013829487' || id === 'Steam_76561198013829487') {
46
+ return mockGamePlayerWithBrackets;
47
+ }
48
+ return mockGamePlayer;
49
+ });
50
+ });
51
+
52
+ it('[PlayerConnected]: Can detect simple player connected', async () => {
53
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
54
+ await emitter.parseMessage({
55
+ msg: `PlayerSpawnedInWorld (reason: JoinMultiplayer, position: 1873, 66, 347): EntityID=171, PltfmId='Steam_76561198028175941', CrossId='EOS_0002b5d970954287afdcb5dc35af0424', OwnerID='Steam_76561198028175941', PlayerName='Catalysm'`,
56
+ });
57
+ expect(emitStub).to.have.been.calledTwice;
58
+
59
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_CONNECTED);
60
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerConnected).player).to.deep.equal({
61
+ name: 'Catalysm',
62
+
63
+ gameId: '0002b5d970954287afdcb5dc35af0424',
64
+ steamId: '76561198028175941',
65
+ epicOnlineServicesId: '0002b5d970954287afdcb5dc35af0424',
66
+ xboxLiveId: undefined,
67
+ });
68
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
69
+ });
70
+
71
+ it('[PlayerConnected]: Can detect simple player connected when player connects for the first time', async () => {
72
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
73
+ await emitter.parseMessage({
74
+ msg: `PlayerSpawnedInWorld (reason: EnterMultiplayer, position: 1080, 61, 1089): EntityID=3812, PltfmId='Steam_76561198028175941', CrossId='EOS_0002b5d970954287afdcb5dc35af0424', OwnerID='Steam_76561198028175941', PlayerName='Catalysm', ClientNumber='6'`,
75
+ });
76
+ expect(emitStub).to.have.been.calledTwice;
77
+
78
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_CONNECTED);
79
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerConnected).player).to.deep.equal({
80
+ name: 'Catalysm',
81
+
82
+ gameId: '0002b5d970954287afdcb5dc35af0424',
83
+ steamId: '76561198028175941',
84
+ epicOnlineServicesId: '0002b5d970954287afdcb5dc35af0424',
85
+ xboxLiveId: undefined,
86
+ });
87
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
88
+ });
89
+
90
+ it('[PlayerConnected]: Can detect Xbox player connected', async () => {
91
+ await new SevenDaysToDieEmitter(await mockConnectionInfo()).parseMessage({
92
+ msg: `PlayerSpawnedInWorld (reason: JoinMultiplayer, position: 1873, 66, 347): EntityID=171, PltfmId='XBL_123456abcdef', CrossId='EOS_0002b5d970954287afdcb5dc35af0424', OwnerID='Steam_76561198028175941', PlayerName='Catalysm'`,
93
+ });
94
+ expect(emitStub).to.have.been.calledTwice;
95
+
96
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_CONNECTED);
97
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerConnected).player).to.deep.equal({
98
+ name: 'Catalysm',
99
+
100
+ gameId: '0002b5d970954287afdcb5dc35af0424',
101
+ steamId: undefined,
102
+ xboxLiveId: '123456abcdef',
103
+ epicOnlineServicesId: '0002b5d970954287afdcb5dc35af0424',
104
+ });
105
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
106
+ });
107
+
108
+ it('[PlayerConnected]: Can detect player connected with space', async () => {
109
+ await new SevenDaysToDieEmitter(await mockConnectionInfo()).parseMessage({
110
+ msg: `PlayerSpawnedInWorld (reason: JoinMultiplayer, position: 1873, 66, 347): EntityID=171, PltfmId='Steam_76561198028175941', CrossId='EOS_0002b5d970954287afdcb5dc35af0424', OwnerID='Steam_76561198028175941', PlayerName='Cata lysm'`,
111
+ });
112
+ expect(emitStub).to.have.been.calledTwice;
113
+
114
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_CONNECTED);
115
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerConnected).player).to.deep.equal({
116
+ name: 'Cata lysm',
117
+
118
+ gameId: '0002b5d970954287afdcb5dc35af0424',
119
+ steamId: '76561198028175941',
120
+ epicOnlineServicesId: '0002b5d970954287afdcb5dc35af0424',
121
+ xboxLiveId: undefined,
122
+ });
123
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
124
+ });
125
+
126
+ it('[PlayerDisconnected]: Can detect simple player disconnected', async () => {
127
+ await new SevenDaysToDieEmitter(await mockConnectionInfo()).parseMessage({
128
+ msg: `Player disconnected: EntityID=171, PltfmId='Steam_76561198028175941', CrossId='EOS_0002b5d970954287afdcb5dc35af0424', OwnerID='Steam_76561198028175941', PlayerName='Catalysm'`,
129
+ });
130
+ expect(emitStub).to.have.been.calledTwice;
131
+
132
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_DISCONNECTED);
133
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerConnected).player).to.deep.equal({
134
+ name: 'Catalysm',
135
+
136
+ gameId: '0002b5d970954287afdcb5dc35af0424',
137
+ steamId: '76561198028175941',
138
+ xboxLiveId: undefined,
139
+ });
140
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
141
+ });
142
+
143
+ it('[ChatMessage] Can detect chat message', async () => {
144
+ // Chat handled by mod 'CSMM Patrons': Chat (from 'Steam_76561198028175941', entity id '549', to 'Global'): /ping
145
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
146
+
147
+ await emitter.parseMessage({
148
+ msg: `Chat handled by mod 'CSMM Patrons': Chat (from 'Steam_76561198028175941', entity id '549', to 'Global'): fsafsafasf`,
149
+ });
150
+
151
+ expect(emitStub).to.have.been.calledTwice;
152
+
153
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.CHAT_MESSAGE);
154
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
155
+
156
+ expect((emitStub.getCalls()[0].args[1] as EventChatMessage).player).to.deep.equal({
157
+ name: 'Catalysm',
158
+ ping: undefined,
159
+ gameId: '0002b5d970954287afdcb5dc35af0424',
160
+ steamId: '76561198028175941',
161
+ });
162
+ });
163
+
164
+ it('[ChatMessage] Can deduplicate messages when a mod handles it', async () => {
165
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
166
+
167
+ await emitter.parseMessage({
168
+ msg: `"Chat (from '-non-player-', entity id '-1', to 'Global'): 'Cata': a"`,
169
+ });
170
+
171
+ await emitter.parseMessage({
172
+ msg: `Chat handled by mod '1CSMM_Patrons': Chat (from 'Steam_76561198028175941', entity id '546', to 'Global'): &ping`,
173
+ });
174
+
175
+ expect(emitStub).to.have.been.calledThrice;
176
+
177
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.LOG_LINE);
178
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.CHAT_MESSAGE);
179
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
180
+
181
+ expect((emitStub.getCalls()[1].args[1] as EventChatMessage).player).to.deep.equal({
182
+ name: 'Catalysm',
183
+ ping: undefined,
184
+ gameId: '0002b5d970954287afdcb5dc35af0424',
185
+ steamId: '76561198028175941',
186
+ });
187
+ });
188
+
189
+ it('[ChatMessage] Can deduplicate messages when a mod handles it (bulk test)', async () => {
190
+ const messages = ['Hello', 'Testing', 'If the', 'chat is', 'working'];
191
+
192
+ const logsToSend = messages
193
+ .map((message) => {
194
+ return [
195
+ {
196
+ msg: `Chat handled by mod 'ServerTools': Chat (from 'Steam_76561198028175941', entity id '2196446', to 'Global'): ${message}`,
197
+ },
198
+ {
199
+ msg: `Chat (from 'Steam_76561198028175941', entity id '-1', to 'Global'): ${message}`,
200
+ },
201
+ ];
202
+ })
203
+ .flat();
204
+
205
+ const logsToSendRandomized = logsToSend.sort(() => Math.random() - 0.5);
206
+
207
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
208
+
209
+ await Promise.all(logsToSendRandomized.map(emitter.parseMessage));
210
+
211
+ expect(emitStub).to.have.callCount(messages.length * 3);
212
+
213
+ for (const msg of messages) {
214
+ expect(
215
+ emitStub.getCalls().some((call) => {
216
+ return call.args[0] === GameEvents.CHAT_MESSAGE && (call.args[1] as EventChatMessage).msg === msg;
217
+ }),
218
+ ).to.equal(true);
219
+ }
220
+ });
221
+
222
+ it('[PlayerDeath] Can detect player death', async () => {
223
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
224
+
225
+ await emitter.parseMessage({
226
+ msg: `GMSG: Player '${mockGamePlayer.name}' died`,
227
+ });
228
+ await emitter.parseMessage({
229
+ msg: `[CSMM_Patrons]playerDied: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) died @ 702 34 -2938`,
230
+ });
231
+
232
+ expect(emitStub).to.have.been.calledThrice;
233
+
234
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_DEATH);
235
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
236
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
237
+
238
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerDeath).player.name).to.equal(mockGamePlayer.name);
239
+
240
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerDeath).position).to.deep.equal({
241
+ x: NaN,
242
+ y: NaN,
243
+ z: NaN,
244
+ });
245
+ });
246
+
247
+ it('[PlayerDeath] Can detect CPM player death', async () => {
248
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
249
+
250
+ await emitter.parseMessage({
251
+ msg: `GMSG: Player '${mockGamePlayer.name}' died`,
252
+ });
253
+ await emitter.parseMessage({
254
+ msg: `[CSMM_Patrons]playerDied: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) died @ 702 34 -2938`,
255
+ });
256
+
257
+ expect(emitStub).to.have.been.calledThrice;
258
+
259
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.LOG_LINE);
260
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.PLAYER_DEATH);
261
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
262
+
263
+ expect((emitStub.getCalls()[1].args[1] as EventPlayerDeath).player.name).to.equal(mockGamePlayer.name);
264
+
265
+ expect((emitStub.getCalls()[1].args[1] as EventPlayerDeath).position).to.deep.equal({
266
+ x: 702,
267
+ y: 34,
268
+ z: -2938,
269
+ });
270
+ });
271
+
272
+ it('[EntityKilled] Can detect entity killed', async () => {
273
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
274
+
275
+ await emitter.parseMessage({
276
+ msg: `Entity zombieNurse 613814 killed by ${mockGamePlayer.name} 54854`,
277
+ });
278
+ await emitter.parseMessage({
279
+ msg: `[CSMM_Patrons]entityKilled: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) killed zombie zombieNurse with S.H.I.E.L.D. Auto Shotgun`,
280
+ });
281
+
282
+ expect(emitStub).to.have.been.calledThrice;
283
+
284
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.ENTITY_KILLED);
285
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
286
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
287
+
288
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).entity).to.equal('zombieNurse');
289
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).player.name).to.equal(mockGamePlayer.name);
290
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).weapon).to.equal(undefined);
291
+ });
292
+
293
+ it('[EntityKilled] Can detect CPM entity killed', async () => {
294
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
295
+
296
+ await emitter.parseMessage({
297
+ msg: `Entity zombieNurse 613814 killed by ${mockGamePlayer.name} 54854`,
298
+ });
299
+ await emitter.parseMessage({
300
+ msg: `[CSMM_Patrons]entityKilled: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) killed zombie zombieNurse with S.H.I.E.L.D. Auto Shotgun`,
301
+ });
302
+
303
+ expect(emitStub).to.have.been.calledThrice;
304
+
305
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.LOG_LINE);
306
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.ENTITY_KILLED);
307
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
308
+
309
+ expect((emitStub.getCalls()[1].args[1] as EventEntityKilled).entity).to.equal('zombieNurse');
310
+ expect((emitStub.getCalls()[1].args[1] as EventEntityKilled).player.name).to.equal(mockGamePlayer.name);
311
+ expect((emitStub.getCalls()[1].args[1] as EventEntityKilled).weapon).to.equal('S.H.I.E.L.D. Auto Shotgun');
312
+ });
313
+
314
+ it('[EntityKilled] Can detect entity killed with spaces in name', async () => {
315
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo());
316
+
317
+ await emitter.parseMessage({
318
+ msg: `Entity Zombie cheerleader 613814 killed by ${mockGamePlayer.name} 54854`,
319
+ });
320
+ await emitter.parseMessage({
321
+ msg: `[CSMM_Patrons]entityKilled: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) killed zombie Zombie cheerleader with Compound Bow`,
322
+ });
323
+
324
+ expect(emitStub).to.have.been.calledThrice;
325
+
326
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.ENTITY_KILLED);
327
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
328
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
329
+
330
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).entity).to.equal('Zombie cheerleader');
331
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).player.name).to.equal(mockGamePlayer.name);
332
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).weapon).to.equal(undefined);
333
+ });
334
+
335
+ it('[EntityKilled] Can detect entity killed with special accents', async () => {
336
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
337
+
338
+ await emitter.parseMessage({
339
+ msg: `Entity zombieFrançaise 613814 killed by ${mockGamePlayer.name} 54854`,
340
+ });
341
+ await emitter.parseMessage({
342
+ msg: `[CSMM_Patrons]entityKilled: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) killed zombie zombieFrançaise with Molotov Cocktail`,
343
+ });
344
+
345
+ expect(emitStub).to.have.been.calledThrice;
346
+
347
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.LOG_LINE);
348
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.ENTITY_KILLED);
349
+ expect(emitStub.getCalls()[2].args[0]).to.equal(GameEvents.LOG_LINE);
350
+
351
+ expect((emitStub.getCalls()[1].args[1] as EventEntityKilled).entity).to.equal('zombieFrançaise');
352
+ expect((emitStub.getCalls()[1].args[1] as EventEntityKilled).player.name).to.equal(mockGamePlayer.name);
353
+ expect((emitStub.getCalls()[1].args[1] as EventEntityKilled).weapon).to.equal('Molotov Cocktail');
354
+ });
355
+
356
+ it('[EntityKilled] Can detect entity killed with color codes in name', async () => {
357
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
358
+
359
+ await emitter.parseMessage({
360
+ msg: `[CSMM_Patrons]entityKilled: [ptz]stoun (Steam_76561198013829487) killed zombie [94CBFF]ELITE[-] Hazmat with Stun Baton`,
361
+ });
362
+
363
+ expect(emitStub).to.have.been.calledTwice;
364
+
365
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.ENTITY_KILLED);
366
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
367
+
368
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).entity).to.equal('[94CBFF]ELITE[-] Hazmat');
369
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).player.name).to.equal('[ptz]stoun');
370
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).weapon).to.equal('Stun Baton');
371
+ });
372
+
373
+ it('[EntityKilled] Can detect PrismaCore entity killed', async () => {
374
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
375
+
376
+ await emitter.parseMessage({
377
+ msg: `[PrismaCore]entityKilled: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) killed zombie zombieNurse with S.H.I.E.L.D. Auto Shotgun`,
378
+ });
379
+
380
+ expect(emitStub).to.have.been.calledTwice;
381
+
382
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.ENTITY_KILLED);
383
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
384
+
385
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).entity).to.equal('zombieNurse');
386
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).player.name).to.equal(mockGamePlayer.name);
387
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).weapon).to.equal('S.H.I.E.L.D. Auto Shotgun');
388
+ });
389
+
390
+ it('[EntityKilled] Can detect PrismaCore entity killed with legendary zombie', async () => {
391
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
392
+
393
+ await emitter.parseMessage({
394
+ msg: `[PrismaCore]entityKilled: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) killed zombie Frostclaw with Thor Hammer`,
395
+ });
396
+
397
+ expect(emitStub).to.have.been.calledTwice;
398
+
399
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.ENTITY_KILLED);
400
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
401
+
402
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).entity).to.equal('Frostclaw');
403
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).player.name).to.equal(mockGamePlayer.name);
404
+ expect((emitStub.getCalls()[0].args[1] as EventEntityKilled).weapon).to.equal('Thor Hammer');
405
+ });
406
+
407
+ it('[PlayerDeath] Can detect PrismaCore player death', async () => {
408
+ const emitter = new SevenDaysToDieEmitter(await mockConnectionInfo({ useCPM: true }));
409
+
410
+ await emitter.parseMessage({
411
+ msg: `[PrismaCore]playerDied: ${mockGamePlayer.name} (Steam_${mockGamePlayer.steamId}) died @ 702 34 -2938`,
412
+ });
413
+
414
+ expect(emitStub).to.have.been.calledTwice;
415
+
416
+ expect(emitStub.getCalls()[0].args[0]).to.equal(GameEvents.PLAYER_DEATH);
417
+ expect(emitStub.getCalls()[1].args[0]).to.equal(GameEvents.LOG_LINE);
418
+
419
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerDeath).player.name).to.equal(mockGamePlayer.name);
420
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerDeath).position.x).to.equal(702);
421
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerDeath).position.y).to.equal(34);
422
+ expect((emitStub.getCalls()[0].args[1] as EventPlayerDeath).position.z).to.equal(-2938);
423
+ });
424
+ });
@@ -0,0 +1,213 @@
1
+ export interface GameTime {
2
+ days: number;
3
+ hours: number;
4
+ minutes: number;
5
+ }
6
+
7
+ export interface StatsResponse {
8
+ gametime: GameTime;
9
+ players: number;
10
+ hostiles: number;
11
+ animals: number;
12
+ }
13
+
14
+ export interface Position {
15
+ x: number;
16
+ y: number;
17
+ z: number;
18
+ }
19
+
20
+ export interface OnlinePlayerResponse {
21
+ steamid: string;
22
+ crossplatformid: string;
23
+ entityid: number;
24
+ ip: string;
25
+ name: string;
26
+ online: boolean;
27
+ position: Position;
28
+ level: number;
29
+ health: number;
30
+ stamina: number;
31
+ zombiekills: number;
32
+ playerkills: number;
33
+ playerdeaths: number;
34
+ score: number;
35
+ totalplaytime: number;
36
+ lastonline: string;
37
+ ping: number;
38
+ }
39
+
40
+ export interface EntityLocation {
41
+ id: number;
42
+ name: string;
43
+ position: Position;
44
+ }
45
+
46
+ export interface LandClaimsResponse {
47
+ claimsize: number;
48
+ claimowners: Array<ClaimOwner>;
49
+ }
50
+
51
+ export interface ClaimOwner {
52
+ steamid: string;
53
+ claimactive: boolean;
54
+ playername: string;
55
+ claims: Array<Position>;
56
+ }
57
+
58
+ export interface CommandResponse {
59
+ command: string;
60
+ parameters: string;
61
+ result: string;
62
+ }
63
+
64
+ export interface AllowedCommands {
65
+ commands: Array<CommandEntry>;
66
+ }
67
+
68
+ export interface CommandEntry {
69
+ command: string;
70
+ description: string;
71
+ help: string;
72
+ }
73
+
74
+ export interface InventoryResponse {
75
+ playername: string;
76
+ userid: string;
77
+ crossplatformid: string;
78
+ bag: Array<InventoryItem>;
79
+ belt: Array<InventoryItem>;
80
+ equipment: PlayerEquipment;
81
+ }
82
+
83
+ export interface InventoryItem {
84
+ count: number;
85
+ name: string;
86
+ icon: string;
87
+ iconcolor: string;
88
+ quality: string;
89
+ }
90
+
91
+ export interface PlayerEquipment extends Record<string, InventoryItem> {
92
+ head: InventoryItem;
93
+ eyes: InventoryItem;
94
+ face: InventoryItem;
95
+ armor: InventoryItem;
96
+ jacket: InventoryItem;
97
+ shirt: InventoryItem;
98
+ legarmor: InventoryItem;
99
+ pants: InventoryItem;
100
+ boots: InventoryItem;
101
+ gloves: InventoryItem;
102
+ }
103
+
104
+ export interface PlayerListResponse {
105
+ total: number;
106
+ totalUnfiltered: number;
107
+ firstResult: number;
108
+ players: Array<PlayerNotOnline>;
109
+ }
110
+
111
+ export interface PlayerNotOnline {
112
+ steamid: string;
113
+ entityid: number;
114
+ ip: string;
115
+ name: string;
116
+ online: boolean;
117
+ position: Position;
118
+ totalplaytime: number;
119
+ lastonline: string;
120
+ ping: number;
121
+ banned: boolean;
122
+ }
123
+
124
+ export interface PlayerLocation {
125
+ steamid: string;
126
+ crossplatformid: string;
127
+ name: string;
128
+ online: boolean;
129
+ position: Position;
130
+ }
131
+
132
+ export interface GetServerInfo {
133
+ GameType: GetServerInfoEntry;
134
+ GameName: GetServerInfoEntry;
135
+ GameHost: GetServerInfoEntry;
136
+ ServerDescription: GetServerInfoEntry;
137
+ ServerWebsiteURL: GetServerInfoEntry;
138
+ LevelName: GetServerInfoEntry;
139
+ GameMode: GetServerInfoEntry;
140
+ Version: GetServerInfoEntry;
141
+ IP: GetServerInfoEntry;
142
+ CountryCode: GetServerInfoEntry;
143
+ SteamID: GetServerInfoEntry;
144
+ CompatibilityVersion: GetServerInfoEntry;
145
+ Platform: GetServerInfoEntry;
146
+ Port: GetServerInfoEntry;
147
+ CurrentPlayers: GetServerInfoEntry;
148
+ MaxPlayers: GetServerInfoEntry;
149
+ GameDifficulty: GetServerInfoEntry;
150
+ DayNightLength: GetServerInfoEntry;
151
+ ZombiesRun: GetServerInfoEntry;
152
+ DayCount: GetServerInfoEntry;
153
+ Ping: GetServerInfoEntry;
154
+ DropOnDeath: GetServerInfoEntry;
155
+ DropOnQuit: GetServerInfoEntry;
156
+ BloodMoonEnemyCount: GetServerInfoEntry;
157
+ EnemyDifficulty: GetServerInfoEntry;
158
+ PlayerKillingMode: GetServerInfoEntry;
159
+ CurrentServerTime: GetServerInfoEntry;
160
+ DayLightLength: GetServerInfoEntry;
161
+ BlockDurabilityModifier: GetServerInfoEntry;
162
+ AirDropFrequency: GetServerInfoEntry;
163
+ LootAbundance: GetServerInfoEntry;
164
+ LootRespawnDays: GetServerInfoEntry;
165
+ MaxSpawnedZombies: GetServerInfoEntry;
166
+ LandClaimSize: GetServerInfoEntry;
167
+ LandClaimDeadZone: GetServerInfoEntry;
168
+ LandClaimExpiryTime: GetServerInfoEntry;
169
+ LandClaimDecayMode: GetServerInfoEntry;
170
+ LandClaimOnlineDurabilityModifier: GetServerInfoEntry;
171
+ LandClaimOfflineDurabilityModifier: GetServerInfoEntry;
172
+ MaxSpawnedAnimals: GetServerInfoEntry;
173
+ IsDedicated: GetServerInfoEntry;
174
+ IsPasswordProtected: GetServerInfoEntry;
175
+ ShowFriendPlayerOnMap: GetServerInfoEntry;
176
+ BuildCreate: GetServerInfoEntry;
177
+ EACEnabled: GetServerInfoEntry;
178
+ Architecture64: GetServerInfoEntry;
179
+ StockSettings: GetServerInfoEntry;
180
+ StockFiles: GetServerInfoEntry;
181
+ RequiresMod: GetServerInfoEntry;
182
+ AirDropMarker: GetServerInfoEntry;
183
+ EnemySpawnMode: GetServerInfoEntry;
184
+ IsPublic: GetServerInfoEntry;
185
+ }
186
+
187
+ export interface GetServerInfoEntry {
188
+ type: string;
189
+ value: any;
190
+ }
191
+
192
+ export interface GetWebUIUpdatesResponse {
193
+ gametime: GameTime;
194
+ players: number;
195
+ hostiles: number;
196
+ animals: number;
197
+ newlogs: number;
198
+ }
199
+
200
+ export interface LogLine {
201
+ date: string;
202
+ time: string;
203
+ uptime: string;
204
+ msg: string;
205
+ trace: string;
206
+ type: string;
207
+ }
208
+
209
+ export interface GetLog {
210
+ firstLine: number;
211
+ lastLine: number;
212
+ entries: Array<LogLine>;
213
+ }
@@ -0,0 +1,46 @@
1
+ import { IsString, IsBoolean } from 'class-validator';
2
+ import { TakaroDTO } from '@takaro/util';
3
+
4
+ export class SdtdConnectionInfo extends TakaroDTO<SdtdConnectionInfo> {
5
+ @IsString()
6
+ public readonly host!: string;
7
+ @IsString()
8
+ public readonly adminUser!: string;
9
+ @IsString()
10
+ public readonly adminToken!: string;
11
+ @IsBoolean()
12
+ public readonly useTls!: boolean;
13
+ @IsBoolean()
14
+ public readonly useCPM!: boolean;
15
+ @IsBoolean()
16
+ public readonly useLegacy!: boolean;
17
+ }
18
+
19
+ export const sdtdJsonSchema = {
20
+ $schema: 'http://json-schema.org/draft-07/schema#',
21
+ title: 'SdtdConnectionInfo',
22
+ type: 'object',
23
+ properties: {
24
+ host: {
25
+ type: 'string',
26
+ },
27
+ adminUser: {
28
+ type: 'string',
29
+ },
30
+ adminToken: {
31
+ type: 'string',
32
+ },
33
+ useTls: {
34
+ type: 'boolean',
35
+ },
36
+ useCPM: {
37
+ type: 'boolean',
38
+ default: false,
39
+ },
40
+ useLegacy: {
41
+ type: 'boolean',
42
+ default: false,
43
+ },
44
+ },
45
+ required: ['host', 'adminUser', 'adminToken', 'useTls'],
46
+ };