@takaro/gameserver 0.0.29 → 0.1.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.
Files changed (76) hide show
  1. package/dist/TakaroEmitter.d.ts +2 -1
  2. package/dist/TakaroEmitter.d.ts.map +1 -1
  3. package/dist/TakaroEmitter.js.map +1 -1
  4. package/dist/config.d.ts +9 -0
  5. package/dist/config.d.ts.map +1 -0
  6. package/dist/config.js +13 -0
  7. package/dist/config.js.map +1 -0
  8. package/dist/gameservers/7d2d/index.d.ts +5 -3
  9. package/dist/gameservers/7d2d/index.d.ts.map +1 -1
  10. package/dist/gameservers/7d2d/index.js +11 -5
  11. package/dist/gameservers/7d2d/index.js.map +1 -1
  12. package/dist/gameservers/7d2d/sdtdAPIClient.d.ts +1 -1
  13. package/dist/gameservers/7d2d/sdtdAPIClient.d.ts.map +1 -1
  14. package/dist/gameservers/7d2d/sdtdAPIClient.js +2 -1
  15. package/dist/gameservers/7d2d/sdtdAPIClient.js.map +1 -1
  16. package/dist/gameservers/generic/connectionInfo.d.ts +16 -0
  17. package/dist/gameservers/generic/connectionInfo.d.ts.map +1 -0
  18. package/dist/gameservers/{mock → generic}/connectionInfo.js +6 -14
  19. package/dist/gameservers/generic/connectionInfo.js.map +1 -0
  20. package/dist/gameservers/generic/connectorClient.d.ts +7 -0
  21. package/dist/gameservers/generic/connectorClient.d.ts.map +1 -0
  22. package/dist/gameservers/generic/connectorClient.js +84 -0
  23. package/dist/gameservers/generic/connectorClient.js.map +1 -0
  24. package/dist/gameservers/generic/emitter.d.ts +13 -0
  25. package/dist/gameservers/generic/emitter.d.ts.map +1 -0
  26. package/dist/gameservers/generic/emitter.js +31 -0
  27. package/dist/gameservers/generic/emitter.js.map +1 -0
  28. package/dist/gameservers/generic/index.d.ts +36 -0
  29. package/dist/gameservers/generic/index.d.ts.map +1 -0
  30. package/dist/gameservers/generic/index.js +170 -0
  31. package/dist/gameservers/generic/index.js.map +1 -0
  32. package/dist/gameservers/rust/index.d.ts +5 -3
  33. package/dist/gameservers/rust/index.d.ts.map +1 -1
  34. package/dist/gameservers/rust/index.js +12 -5
  35. package/dist/gameservers/rust/index.js.map +1 -1
  36. package/dist/getGame.d.ts +4 -3
  37. package/dist/getGame.d.ts.map +1 -1
  38. package/dist/getGame.js +7 -6
  39. package/dist/getGame.js.map +1 -1
  40. package/dist/interfaces/GameServer.d.ts +28 -2
  41. package/dist/interfaces/GameServer.d.ts.map +1 -1
  42. package/dist/interfaces/GameServer.js +97 -2
  43. package/dist/interfaces/GameServer.js.map +1 -1
  44. package/dist/main.d.ts +4 -2
  45. package/dist/main.d.ts.map +1 -1
  46. package/dist/main.js +4 -2
  47. package/dist/main.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/TakaroEmitter.ts +2 -1
  50. package/src/__tests__/gameEventEmitter.test.ts +4 -9
  51. package/src/config.ts +20 -0
  52. package/src/gameservers/7d2d/__tests__/7d2dEventDetection.unit.test.ts +0 -1
  53. package/src/gameservers/7d2d/index.ts +14 -4
  54. package/src/gameservers/7d2d/sdtdAPIClient.ts +3 -2
  55. package/src/gameservers/generic/connectionInfo.ts +19 -0
  56. package/src/gameservers/generic/connectorClient.ts +107 -0
  57. package/src/gameservers/generic/emitter.ts +36 -0
  58. package/src/gameservers/generic/index.ts +193 -0
  59. package/src/gameservers/rust/index.ts +16 -5
  60. package/src/getGame.ts +7 -5
  61. package/src/interfaces/GameServer.ts +91 -3
  62. package/src/main.ts +5 -3
  63. package/dist/gameservers/mock/connectionInfo.d.ts +0 -21
  64. package/dist/gameservers/mock/connectionInfo.d.ts.map +0 -1
  65. package/dist/gameservers/mock/connectionInfo.js.map +0 -1
  66. package/dist/gameservers/mock/emitter.d.ts +0 -13
  67. package/dist/gameservers/mock/emitter.d.ts.map +0 -1
  68. package/dist/gameservers/mock/emitter.js +0 -34
  69. package/dist/gameservers/mock/emitter.js.map +0 -1
  70. package/dist/gameservers/mock/index.d.ts +0 -35
  71. package/dist/gameservers/mock/index.d.ts.map +0 -1
  72. package/dist/gameservers/mock/index.js +0 -154
  73. package/dist/gameservers/mock/index.js.map +0 -1
  74. package/src/gameservers/mock/connectionInfo.ts +0 -25
  75. package/src/gameservers/mock/emitter.ts +0 -41
  76. package/src/gameservers/mock/index.ts +0 -182
@@ -11,6 +11,7 @@ import {
11
11
  GameEvents,
12
12
  } from '@takaro/modules';
13
13
  import { errors, isTakaroDTO, logger } from '@takaro/util';
14
+ import EventEmitter from 'events';
14
15
  import { isPromise } from 'util/types';
15
16
 
16
17
  const log = logger('TakaroEmitter');
@@ -32,8 +33,8 @@ export interface IEventMap {
32
33
  export abstract class TakaroEmitter {
33
34
  private listenerMap: Map<keyof IEventMap, IEventMap[keyof IEventMap][]> = new Map();
34
35
 
36
+ abstract start(nodeEventEmitter?: EventEmitter): Promise<void>;
35
37
  abstract stop(): Promise<void>;
36
- abstract start(): Promise<void>;
37
38
 
38
39
  constructor() {
39
40
  return getErrorProxyHandler(this);
@@ -1,6 +1,6 @@
1
1
  import { expect, sandbox } from '@takaro/test';
2
- import { MockConnectionInfo } from '../gameservers/mock/connectionInfo.js';
3
- import { Mock } from '../gameservers/mock/index.js';
2
+ import { GenericConnectionInfo } from '../gameservers/generic/connectionInfo.js';
3
+ import { Generic } from '../gameservers/generic/index.js';
4
4
  import { GameEvents } from '@takaro/modules';
5
5
  import { describe, it } from 'node:test';
6
6
 
@@ -11,17 +11,12 @@ describe('GameEventEmitter', () => {
11
11
  * We use @ts-expect-error so that if the compiler fails to mark these as errors, we'll know instantly
12
12
  */
13
13
  it('Has a typed event emitter', async () => {
14
- const gameServer = new Mock(new MockConnectionInfo({}));
14
+ const gameServer = new Generic(new GenericConnectionInfo({}), {}, 'dummy-id');
15
15
  const emitter = gameServer.getEventEmitter();
16
16
  const listenerSpy = sandbox.spy();
17
17
 
18
18
  emitter.on(GameEvents.PLAYER_CONNECTED, async (e) => {
19
- expect(() => e.player.name).to.throw(
20
- // Eslint and prettier disagree on how to format this
21
- // And I cba fixing it for this specific instance :)
22
- // eslint-disable-next-line quotes
23
- "Cannot read properties of undefined (reading 'name')",
24
- );
19
+ expect(() => e.player.name).to.throw("Cannot read properties of undefined (reading 'name')");
25
20
  });
26
21
  emitter.on(GameEvents.PLAYER_CONNECTED, listenerSpy);
27
22
 
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]);
@@ -1,4 +1,3 @@
1
- /* eslint-disable quotes */
2
1
  import { expect, sandbox } from '@takaro/test';
3
2
  import {
4
3
  EventChatMessage,
@@ -3,11 +3,13 @@ import { IGamePlayer, IPosition } from '@takaro/modules';
3
3
  import {
4
4
  BanDTO,
5
5
  CommandOutput,
6
+ IEntityDTO,
6
7
  IGameServer,
7
8
  IItemDTO,
8
9
  IMessageOptsDTO,
9
10
  IPlayerReferenceDTO,
10
11
  TestReachabilityOutputDTO,
12
+ ILocationDTO,
11
13
  } from '../../interfaces/GameServer.js';
12
14
  import { SevenDaysToDieEmitter } from './emitter.js';
13
15
  import { SdtdApiClient } from './sdtdAPIClient.js';
@@ -92,11 +94,11 @@ export class SevenDaysToDie implements IGameServer {
92
94
  return null;
93
95
  }
94
96
 
95
- return {
97
+ return new IPosition({
96
98
  x: playerLocation.position.x,
97
99
  y: playerLocation.position.y,
98
100
  z: playerLocation.position.z,
99
- };
101
+ });
100
102
  }
101
103
 
102
104
  async giveItem(player: IPlayerReferenceDTO, item: string, amount: number = 1, quality?: string): Promise<void> {
@@ -163,7 +165,6 @@ export class SevenDaysToDie implements IGameServer {
163
165
  }
164
166
 
165
167
  async sendMessage(message: string, opts?: IMessageOptsDTO) {
166
- // eslint-disable-next-line quotes
167
168
  const escapedMessage = message.replaceAll(/"/g, "'");
168
169
 
169
170
  let command = `say "${escapedMessage}"`;
@@ -184,7 +185,8 @@ export class SevenDaysToDie implements IGameServer {
184
185
  await this.executeConsoleCommand(command);
185
186
  }
186
187
 
187
- async teleportPlayer(player: IGamePlayer, x: number, y: number, z: number) {
188
+ async teleportPlayer(player: IGamePlayer, x: number, y: number, z: number, _dimension?: string) {
189
+ // 7D2D doesn't support dimensions, so we ignore the dimension parameter
188
190
  const command = `teleportplayer EOS_${player.gameId} ${x} ${y} ${z}`;
189
191
  await this.executeConsoleCommand(command);
190
192
  }
@@ -349,4 +351,12 @@ export class SevenDaysToDie implements IGameServer {
349
351
  async getMapTile(x: number, y: number, z: number) {
350
352
  return this.apiClient.getMapTile(x, y, z);
351
353
  }
354
+
355
+ async listEntities(): Promise<IEntityDTO[]> {
356
+ throw new errors.NotImplementedError();
357
+ }
358
+
359
+ async listLocations(): Promise<ILocationDTO[]> {
360
+ throw new errors.NotImplementedError();
361
+ }
352
362
  }
@@ -92,10 +92,11 @@ export class SdtdApiClient {
92
92
  });
93
93
  }
94
94
 
95
- async getMapTile(x: number, y: number, z: number): Promise<Buffer> {
95
+ async getMapTile(x: number, y: number, z: number): Promise<string> {
96
96
  const res = await this.client.get(`/map/${z}/${x}/${y}.png?t=${Date.now() / 1000}`, {
97
97
  responseType: 'arraybuffer',
98
98
  });
99
- return res.data;
99
+ // Convert to b64
100
+ return Buffer.from(res.data, 'binary').toString('base64');
100
101
  }
101
102
  }
@@ -0,0 +1,19 @@
1
+ import { IsString } from 'class-validator';
2
+ import { TakaroDTO } from '@takaro/util';
3
+
4
+ export class GenericConnectionInfo extends TakaroDTO<GenericConnectionInfo> {
5
+ @IsString()
6
+ public identityToken: string;
7
+ }
8
+
9
+ export const genericJsonSchema = {
10
+ $schema: 'http://json-schema.org/draft-07/schema#',
11
+ title: 'GenericConnectionInfo',
12
+ type: 'object',
13
+ properties: {
14
+ identityToken: {
15
+ type: 'string',
16
+ },
17
+ },
18
+ required: ['identityToken'],
19
+ };
@@ -0,0 +1,107 @@
1
+ import axios, { AxiosError, AxiosInstance, isAxiosError } from 'axios';
2
+ import { config } from '../../config.js';
3
+ import { errors, logger } from '@takaro/util';
4
+
5
+ function getConnectorClient() {
6
+ const log = logger('client:connector');
7
+
8
+ const connectorClient = axios.create({
9
+ baseURL: config.get('connector.host'),
10
+ headers: {
11
+ 'Content-Type': 'application/json',
12
+ 'User-Agent': 'Takaro',
13
+ },
14
+ });
15
+
16
+ connectorClient.interceptors.request.use((request) => {
17
+ log.debug(`➡️ ${request.method?.toUpperCase()} ${request.url}`, {
18
+ method: request.method,
19
+ url: request.url,
20
+ operation: request.data?.operation,
21
+ });
22
+ return request;
23
+ });
24
+
25
+ connectorClient.interceptors.response.use(
26
+ (response) => {
27
+ log.debug(
28
+ `⬅️ ${response.request.method?.toUpperCase()} ${response.request.path} ${response.status} ${
29
+ response.statusText
30
+ }`,
31
+ {
32
+ status: response.status,
33
+ method: response.request.method,
34
+ url: response.request.url,
35
+ operation: response.config.data?.operation,
36
+ },
37
+ );
38
+
39
+ return response;
40
+ },
41
+ (error: AxiosError) => {
42
+ let details = {};
43
+
44
+ if (error.response?.data) {
45
+ const data = error.response.data as Record<string, unknown>;
46
+ details = JSON.stringify(data.error_description);
47
+ }
48
+
49
+ log.warn(`☠️ Request errored: [${error.response?.status}] ${details}`, {
50
+ status: error.response?.status,
51
+ statusText: error.response?.statusText,
52
+ method: error.config?.method,
53
+ url: error.config?.url,
54
+ response: error.response?.data,
55
+ });
56
+
57
+ return Promise.reject(error);
58
+ },
59
+ );
60
+
61
+ return connectorClient;
62
+ }
63
+
64
+ export class TakaroConnector {
65
+ private client: AxiosInstance;
66
+ constructor() {
67
+ this.client = getConnectorClient();
68
+ }
69
+
70
+ async requestFromServer(id: string, operation: string, data: string): Promise<any> {
71
+ try {
72
+ const res = await this.client.post(`/gameserver/${id}/request`, { operation, data });
73
+ return res.data.data;
74
+ } catch (error) {
75
+ if (isAxiosError(error)) {
76
+ if (error.response?.data.meta.error.code === 'ValidationError') {
77
+ throw new errors.BadRequestError(
78
+ 'The gameserver responded with bad data, please verify that the mod is up to date.',
79
+ );
80
+ }
81
+
82
+ if (error.response?.status) {
83
+ if (error.response?.status >= 400 && error.response?.status < 500) {
84
+ throw new errors.BadRequestError(error.response?.data.meta.error.message);
85
+ }
86
+ }
87
+ }
88
+ throw error;
89
+ }
90
+ }
91
+
92
+ async resetConnection(id: string): Promise<any> {
93
+ try {
94
+ await this.client.post(`/gameserver/${id}/reset`);
95
+ return;
96
+ } catch (error) {
97
+ if (isAxiosError(error)) {
98
+ if (error.response?.data.meta.error.code === 'ValidationError') {
99
+ throw new errors.BadRequestError(
100
+ 'The gameserver responded with bad data, please verify that the mod is up to date.',
101
+ );
102
+ }
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,36 @@
1
+ import { logger } from '@takaro/util';
2
+ import { GenericConnectionInfo } from './connectionInfo.js';
3
+ import { TakaroEmitter } from '../../TakaroEmitter.js';
4
+ import { EventMapping, GameEventTypes } from '@takaro/modules';
5
+
6
+ const log = logger('Generic');
7
+ export class GenericEmitter extends TakaroEmitter {
8
+ private scopedListener = this.privateListener.bind(this);
9
+
10
+ constructor(private config: GenericConnectionInfo) {
11
+ super();
12
+ }
13
+
14
+ async start(): Promise<void> {
15
+ // No-op, generic connectors initiate the connection
16
+ }
17
+
18
+ async stop(): Promise<void> {
19
+ // No-op, generic connectors initiate the connection
20
+ }
21
+
22
+ get listener() {
23
+ return this.scopedListener;
24
+ }
25
+
26
+ private async privateListener(event: GameEventTypes, args: any) {
27
+ log.debug(`Transmitting event ${event}`);
28
+ const dto = EventMapping[event];
29
+
30
+ if (dto) {
31
+ this.emit(event, new dto(args));
32
+ } else {
33
+ this.emit(event, args);
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,193 @@
1
+ import { errors, logger, traceableClass } from '@takaro/util';
2
+ import { IGamePlayer, IPosition } from '@takaro/modules';
3
+ import {
4
+ BanDTO,
5
+ CommandOutput,
6
+ IEntityDTO,
7
+ IGameServer,
8
+ IItemDTO,
9
+ ILocationDTO,
10
+ IMessageOptsDTO,
11
+ IPlayerReferenceDTO,
12
+ MapInfoDTO,
13
+ TestReachabilityOutputDTO,
14
+ } from '../../interfaces/GameServer.js';
15
+ import { Settings } from '@takaro/apiclient';
16
+ import { TakaroConnector } from './connectorClient.js';
17
+ import { GenericConnectionInfo } from './connectionInfo.js';
18
+ import { JsonObject } from 'type-fest';
19
+ import { GenericEmitter } from './emitter.js';
20
+
21
+ @traceableClass('game:generic')
22
+ export class Generic implements IGameServer {
23
+ private logger = logger('Generic');
24
+ connectionInfo: GenericConnectionInfo;
25
+ emitter: GenericEmitter;
26
+ private takaroConnector = new TakaroConnector();
27
+
28
+ constructor(
29
+ config: GenericConnectionInfo,
30
+ private settings: Partial<Settings> = {},
31
+ private gameServerId: string,
32
+ ) {
33
+ this.connectionInfo = config;
34
+ this.emitter = new GenericEmitter(config);
35
+ }
36
+
37
+ getEventEmitter() {
38
+ return this.emitter;
39
+ }
40
+
41
+ private async requestFromServer(operation: string, data?: JsonObject) {
42
+ if (!data) data = {};
43
+ const resp = await this.takaroConnector.requestFromServer(this.gameServerId, operation, JSON.stringify(data));
44
+
45
+ if (resp && resp.error) {
46
+ throw new errors.BadRequestError(`Error from server: ${resp.error}`);
47
+ }
48
+
49
+ return resp;
50
+ }
51
+
52
+ async getPlayer(rawPlayer: IPlayerReferenceDTO): Promise<IGamePlayer | null> {
53
+ const player = new IPlayerReferenceDTO({ gameId: rawPlayer.gameId });
54
+ const res = await this.requestFromServer('getPlayer', player.toJSON());
55
+ if (!res) return null;
56
+ const dto = new IGamePlayer(res);
57
+ await dto.validate();
58
+ return dto;
59
+ }
60
+
61
+ async getPlayers(): Promise<IGamePlayer[]> {
62
+ const res = await this.requestFromServer('getPlayers');
63
+ if (!res)
64
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
65
+ const dto: IGamePlayer[] = res.map((p: any) => new IGamePlayer(p));
66
+ await Promise.all(dto.map((p) => p.validate()));
67
+ return dto;
68
+ }
69
+
70
+ async getPlayerLocation(rawPlayer: IPlayerReferenceDTO): Promise<IPosition | null> {
71
+ const player = new IPlayerReferenceDTO({ gameId: rawPlayer.gameId });
72
+ const res = await this.requestFromServer('getPlayerLocation', player.toJSON());
73
+ if (!res) return null;
74
+ const dto = new IPosition(res);
75
+ await dto.validate();
76
+ return dto;
77
+ }
78
+
79
+ async testReachability(): Promise<TestReachabilityOutputDTO> {
80
+ try {
81
+ const response = await this.takaroConnector.requestFromServer(this.gameServerId, 'testReachability', '{}');
82
+ const dto = new TestReachabilityOutputDTO(response);
83
+ await dto.validate();
84
+ return dto;
85
+ } catch (error) {
86
+ return new TestReachabilityOutputDTO({
87
+ connectable: false,
88
+ reason: error instanceof Error ? error.message : 'Unknown error occurred',
89
+ });
90
+ }
91
+ }
92
+
93
+ async executeConsoleCommand(rawCommand: string): Promise<CommandOutput> {
94
+ const res = await this.requestFromServer('executeConsoleCommand', { command: rawCommand });
95
+ if (!res)
96
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
97
+ const dto = new CommandOutput(res);
98
+ await dto.validate();
99
+ return dto;
100
+ }
101
+
102
+ async sendMessage(message: string, opts?: IMessageOptsDTO) {
103
+ if (!opts) opts = new IMessageOptsDTO();
104
+ await this.requestFromServer('sendMessage', {
105
+ message,
106
+ opts: opts.toJSON(),
107
+ });
108
+ }
109
+
110
+ async teleportPlayer(player: IGamePlayer, x: number, y: number, z: number, dimension?: string) {
111
+ await this.requestFromServer('teleportPlayer', { player: player.toJSON(), x, y, z, dimension });
112
+ }
113
+
114
+ async kickPlayer(player: IGamePlayer, reason: string) {
115
+ await this.requestFromServer('kickPlayer', { player: player.toJSON(), reason });
116
+ }
117
+
118
+ async banPlayer(options: BanDTO) {
119
+ await this.requestFromServer('banPlayer', options.toJSON());
120
+ }
121
+
122
+ async unbanPlayer(player: IGamePlayer) {
123
+ await this.requestFromServer('unbanPlayer', player.toJSON());
124
+ }
125
+
126
+ async listBans(): Promise<BanDTO[]> {
127
+ const res = await this.requestFromServer('listBans');
128
+ if (!res)
129
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
130
+ const dto: BanDTO[] = res.map((p: any) => new BanDTO(p));
131
+ await Promise.all(dto.map((p) => p.validate()));
132
+ return dto;
133
+ }
134
+
135
+ async giveItem(rawPlayer: IPlayerReferenceDTO, item: string, amount: number, quality: string): Promise<void> {
136
+ await this.requestFromServer('giveItem', {
137
+ player: { gameId: rawPlayer.gameId },
138
+ item,
139
+ amount,
140
+ quality,
141
+ });
142
+ }
143
+
144
+ async listItems(): Promise<IItemDTO[]> {
145
+ const res = await this.requestFromServer('listItems');
146
+ if (!res)
147
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
148
+ const dto: IItemDTO[] = res.map((p: any) => new IItemDTO(p));
149
+ await Promise.all(dto.map((p) => p.validate()));
150
+ return dto;
151
+ }
152
+
153
+ async getPlayerInventory(rawPlayer: IPlayerReferenceDTO): Promise<IItemDTO[]> {
154
+ const player = new IPlayerReferenceDTO({ gameId: rawPlayer.gameId });
155
+ const res = await this.requestFromServer('getPlayerInventory', player.toJSON());
156
+ if (!res)
157
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
158
+ const dto: IItemDTO[] = res.map((p: any) => new IItemDTO(p));
159
+ await Promise.all(dto.map((p) => p.validate()));
160
+ return dto;
161
+ }
162
+
163
+ async shutdown(): Promise<void> {
164
+ await this.requestFromServer('shutdown');
165
+ }
166
+
167
+ async getMapInfo(): Promise<MapInfoDTO> {
168
+ const res = await this.requestFromServer('getMapInfo');
169
+ if (!res)
170
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
171
+ const dto = new MapInfoDTO(res);
172
+ await dto.validate();
173
+ return dto;
174
+ }
175
+
176
+ async getMapTile(_x: number, _y: number, _z: number): Promise<string> {
177
+ const res = await this.requestFromServer('getMapTile', { x: _x, y: _y, z: _z });
178
+ return res;
179
+ }
180
+
181
+ async listEntities(): Promise<IEntityDTO[]> {
182
+ const res = await this.requestFromServer('listEntities');
183
+ if (!res)
184
+ throw new errors.ValidationError('Nothing returned from server, is the server responding the right data?');
185
+ const dto: IEntityDTO[] = res.map((p: any) => new IEntityDTO(p));
186
+ await Promise.all(dto.map((p) => p.validate()));
187
+ return dto;
188
+ }
189
+
190
+ async listLocations(): Promise<ILocationDTO[]> {
191
+ throw new errors.NotImplementedError();
192
+ }
193
+ }
@@ -1,11 +1,13 @@
1
- import { logger, traceableClass } from '@takaro/util';
1
+ import { errors, logger, traceableClass } from '@takaro/util';
2
2
  import WebSocket from 'ws';
3
3
  import { IGamePlayer, IPosition } from '@takaro/modules';
4
4
  import {
5
5
  BanDTO,
6
6
  CommandOutput,
7
+ IEntityDTO,
7
8
  IGameServer,
8
9
  IItemDTO,
10
+ ILocationDTO,
9
11
  IPlayerReferenceDTO,
10
12
  MapInfoDTO,
11
13
  TestReachabilityOutputDTO,
@@ -86,11 +88,11 @@ export class Rust implements IGameServer {
86
88
  const z = matches[4].replace(')', '');
87
89
 
88
90
  if (steamId === player.gameId) {
89
- return {
91
+ return new IPosition({
90
92
  x: parseInt(x, 10),
91
93
  y: parseInt(y, 10),
92
94
  z: parseInt(z, 10),
93
- };
95
+ });
94
96
  }
95
97
  }
96
98
  }
@@ -145,7 +147,8 @@ export class Rust implements IGameServer {
145
147
  await this.executeConsoleCommand(`say "${message}"`);
146
148
  }
147
149
 
148
- async teleportPlayer(player: IGamePlayer, x: number, y: number, z: number) {
150
+ async teleportPlayer(player: IGamePlayer, x: number, y: number, z: number, _dimension?: string) {
151
+ // Rust doesn't support dimensions, so we ignore the dimension parameter
149
152
  await this.executeConsoleCommand(`teleportplayer.pos ${player.gameId} ${x} ${y} ${z}`);
150
153
  }
151
154
 
@@ -250,7 +253,15 @@ export class Rust implements IGameServer {
250
253
  });
251
254
  }
252
255
 
253
- async getMapTile(_x: number, _y: number, _z: number): Promise<Buffer> {
256
+ async getMapTile(_x: number, _y: number, _z: number): Promise<string> {
254
257
  throw new Error('Not implemented');
255
258
  }
259
+
260
+ async listEntities(): Promise<IEntityDTO[]> {
261
+ throw new errors.NotImplementedError();
262
+ }
263
+
264
+ async listLocations(): Promise<ILocationDTO[]> {
265
+ throw new errors.NotImplementedError();
266
+ }
256
267
  }
package/src/getGame.ts CHANGED
@@ -2,30 +2,32 @@ import { errors } from '@takaro/util';
2
2
  import { GameServerOutputDTOTypeEnum, Settings } from '@takaro/apiclient';
3
3
  import { SdtdConnectionInfo } from './gameservers/7d2d/connectionInfo.js';
4
4
  import { SevenDaysToDie } from './gameservers/7d2d/index.js';
5
- import { MockConnectionInfo } from './gameservers/mock/connectionInfo.js';
6
- import { Mock } from './gameservers/mock/index.js';
7
5
  import { RustConnectionInfo } from './gameservers/rust/connectionInfo.js';
8
6
  import { Rust } from './gameservers/rust/index.js';
9
7
  import { IGameServer } from './interfaces/GameServer.js';
8
+ import { GenericConnectionInfo } from './gameservers/generic/connectionInfo.js';
9
+ import { Generic } from './gameservers/generic/index.js';
10
10
 
11
11
  export enum GAME_SERVER_TYPE {
12
- 'MOCK' = 'MOCK',
13
12
  'SEVENDAYSTODIE' = 'SEVENDAYSTODIE',
14
13
  'RUST' = 'RUST',
14
+ 'GENERIC' = 'GENERIC',
15
+ 'MOCK' = 'MOCK',
15
16
  }
16
17
 
17
18
  export async function getGame(
18
19
  type: GAME_SERVER_TYPE | GameServerOutputDTOTypeEnum,
19
20
  connectionInfo: Record<string, unknown>,
20
21
  settings: Partial<Settings>,
22
+ gameServerId: string,
21
23
  ): Promise<IGameServer> {
22
24
  switch (type) {
23
25
  case GAME_SERVER_TYPE.SEVENDAYSTODIE:
24
26
  return new SevenDaysToDie(new SdtdConnectionInfo(connectionInfo), settings);
25
27
  case GAME_SERVER_TYPE.RUST:
26
28
  return new Rust(new RustConnectionInfo(connectionInfo), settings);
27
- case GAME_SERVER_TYPE.MOCK:
28
- return new Mock(new MockConnectionInfo(connectionInfo), settings);
29
+ case GAME_SERVER_TYPE.GENERIC:
30
+ return new Generic(new GenericConnectionInfo(connectionInfo), settings, gameServerId);
29
31
  default:
30
32
  throw new errors.NotImplementedError();
31
33
  }