@takaro/gameserver 0.0.1
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/README.md +36 -0
- package/dist/TakaroEmitter.d.ts +26 -0
- package/dist/TakaroEmitter.js +97 -0
- package/dist/TakaroEmitter.js.map +1 -0
- package/dist/gameservers/7d2d/apiResponses.d.ts +193 -0
- package/dist/gameservers/7d2d/apiResponses.js +2 -0
- package/dist/gameservers/7d2d/apiResponses.js.map +1 -0
- package/dist/gameservers/7d2d/connectionInfo.d.ts +32 -0
- package/dist/gameservers/7d2d/connectionInfo.js +58 -0
- package/dist/gameservers/7d2d/connectionInfo.js.map +1 -0
- package/dist/gameservers/7d2d/emitter.d.ts +30 -0
- package/dist/gameservers/7d2d/emitter.js +261 -0
- package/dist/gameservers/7d2d/emitter.js.map +1 -0
- package/dist/gameservers/7d2d/index.d.ts +28 -0
- package/dist/gameservers/7d2d/index.js +267 -0
- package/dist/gameservers/7d2d/index.js.map +1 -0
- package/dist/gameservers/7d2d/itemWorker.d.ts +1 -0
- package/dist/gameservers/7d2d/itemWorker.js +31 -0
- package/dist/gameservers/7d2d/itemWorker.js.map +1 -0
- package/dist/gameservers/7d2d/items-7d2d.json +17705 -0
- package/dist/gameservers/7d2d/sdtdAPIClient.d.ts +14 -0
- package/dist/gameservers/7d2d/sdtdAPIClient.js +60 -0
- package/dist/gameservers/7d2d/sdtdAPIClient.js.map +1 -0
- package/dist/gameservers/mock/connectionInfo.d.ts +20 -0
- package/dist/gameservers/mock/connectionInfo.js +37 -0
- package/dist/gameservers/mock/connectionInfo.js.map +1 -0
- package/dist/gameservers/mock/emitter.d.ts +12 -0
- package/dist/gameservers/mock/emitter.js +33 -0
- package/dist/gameservers/mock/emitter.js.map +1 -0
- package/dist/gameservers/mock/index.d.ts +31 -0
- package/dist/gameservers/mock/index.js +135 -0
- package/dist/gameservers/mock/index.js.map +1 -0
- package/dist/gameservers/rust/connectionInfo.d.ts +28 -0
- package/dist/gameservers/rust/connectionInfo.js +51 -0
- package/dist/gameservers/rust/connectionInfo.js.map +1 -0
- package/dist/gameservers/rust/emitter.d.ts +30 -0
- package/dist/gameservers/rust/emitter.js +160 -0
- package/dist/gameservers/rust/emitter.js.map +1 -0
- package/dist/gameservers/rust/index.d.ts +29 -0
- package/dist/gameservers/rust/index.js +189 -0
- package/dist/gameservers/rust/index.js.map +1 -0
- package/dist/gameservers/rust/items-rust.json +20771 -0
- package/dist/getGame.d.ts +8 -0
- package/dist/getGame.js +26 -0
- package/dist/getGame.js.map +1 -0
- package/dist/interfaces/GameServer.d.ts +57 -0
- package/dist/interfaces/GameServer.js +95 -0
- package/dist/interfaces/GameServer.js.map +1 -0
- package/dist/main.d.ts +9 -0
- package/dist/main.js +10 -0
- package/dist/main.js.map +1 -0
- package/package.json +26 -0
- package/src/TakaroEmitter.ts +138 -0
- package/src/TakaroEmitter.unit.test.ts +125 -0
- package/src/__tests__/gameEventEmitter.test.ts +36 -0
- package/src/gameservers/7d2d/__tests__/7d2dActions.unit.test.ts +91 -0
- package/src/gameservers/7d2d/__tests__/7d2dEventDetection.unit.test.ts +324 -0
- package/src/gameservers/7d2d/apiResponses.ts +214 -0
- package/src/gameservers/7d2d/connectionInfo.ts +40 -0
- package/src/gameservers/7d2d/emitter.ts +325 -0
- package/src/gameservers/7d2d/index.ts +318 -0
- package/src/gameservers/7d2d/itemWorker.ts +34 -0
- package/src/gameservers/7d2d/items-7d2d.json +17705 -0
- package/src/gameservers/7d2d/sdtdAPIClient.ts +82 -0
- package/src/gameservers/mock/connectionInfo.ts +25 -0
- package/src/gameservers/mock/emitter.ts +37 -0
- package/src/gameservers/mock/index.ts +156 -0
- package/src/gameservers/rust/__tests__/rustActions.unit.test.ts +140 -0
- package/src/gameservers/rust/connectionInfo.ts +35 -0
- package/src/gameservers/rust/emitter.ts +198 -0
- package/src/gameservers/rust/index.ts +230 -0
- package/src/gameservers/rust/items-rust.json +20771 -0
- package/src/getGame.ts +32 -0
- package/src/interfaces/GameServer.ts +95 -0
- package/src/main.ts +14 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +9 -0
- package/typedoc.json +3 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { logger, traceableClass } from '@takaro/util';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { IGamePlayer, IPosition } from '@takaro/modules';
|
|
4
|
+
import {
|
|
5
|
+
BanDTO,
|
|
6
|
+
CommandOutput,
|
|
7
|
+
IGameServer,
|
|
8
|
+
IItemDTO,
|
|
9
|
+
IPlayerReferenceDTO,
|
|
10
|
+
TestReachabilityOutputDTO,
|
|
11
|
+
} from '../../interfaces/GameServer.js';
|
|
12
|
+
import { RustConnectionInfo } from './connectionInfo.js';
|
|
13
|
+
import { RustEmitter } from './emitter.js';
|
|
14
|
+
import { Settings } from '@takaro/apiclient';
|
|
15
|
+
|
|
16
|
+
const itemsJson = (await import('./items-rust.json', { assert: { type: 'json' } })).default;
|
|
17
|
+
|
|
18
|
+
@traceableClass('game:rust')
|
|
19
|
+
export class Rust implements IGameServer {
|
|
20
|
+
private log = logger('rust');
|
|
21
|
+
connectionInfo: RustConnectionInfo;
|
|
22
|
+
private client: WebSocket | null;
|
|
23
|
+
|
|
24
|
+
constructor(config: RustConnectionInfo, private settings: Partial<Settings> = {}) {
|
|
25
|
+
this.connectionInfo = config;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private getRequestId(): number {
|
|
29
|
+
return Math.floor(Math.random() * 100000000);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private async getClient() {
|
|
33
|
+
if (this.client && this.client.readyState === WebSocket.OPEN) {
|
|
34
|
+
return this.client;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.client = await RustEmitter.getClient(this.connectionInfo);
|
|
38
|
+
return this.client;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getEventEmitter() {
|
|
42
|
+
const emitter = new RustEmitter(this.connectionInfo);
|
|
43
|
+
return emitter;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async getPlayer(player: IPlayerReferenceDTO): Promise<IGamePlayer | null> {
|
|
47
|
+
const players = await this.getPlayers();
|
|
48
|
+
return players.find((p) => p.gameId === player.gameId) || null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getPlayers(): Promise<IGamePlayer[]> {
|
|
52
|
+
const response = await this.executeConsoleCommand('playerlist');
|
|
53
|
+
const rustPlayers = JSON.parse(response.rawResult);
|
|
54
|
+
return Promise.all(
|
|
55
|
+
rustPlayers.map((player: any) => {
|
|
56
|
+
return new IGamePlayer({
|
|
57
|
+
gameId: player.SteamID,
|
|
58
|
+
steamId: player.SteamID,
|
|
59
|
+
ip: player.Address.split(':')[0],
|
|
60
|
+
name: player.DisplayName,
|
|
61
|
+
ping: player.Ping,
|
|
62
|
+
});
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async giveItem(player: IPlayerReferenceDTO, item: string, amount: number): Promise<void> {
|
|
68
|
+
await this.executeConsoleCommand(`inventory.giveid ${player.gameId} ${item} ${amount}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getPlayerLocation(player: IPlayerReferenceDTO): Promise<IPosition | null> {
|
|
72
|
+
const rawResponse = await this.executeConsoleCommand('playerlistpos');
|
|
73
|
+
const lines = rawResponse.rawResult.split('\n');
|
|
74
|
+
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
const matches = /(\d{17}) \w+\s{4}\(([-\d\.]+), ([-\d\.]+), ([-\d\.]+)\)/.exec(line);
|
|
77
|
+
|
|
78
|
+
if (matches) {
|
|
79
|
+
const steamId = matches[1];
|
|
80
|
+
const x = matches[2].replace('(', '');
|
|
81
|
+
const y = matches[3].replace(',', '');
|
|
82
|
+
const z = matches[4].replace(')', '');
|
|
83
|
+
|
|
84
|
+
if (steamId === player.gameId) {
|
|
85
|
+
return {
|
|
86
|
+
x: parseInt(x, 10),
|
|
87
|
+
y: parseInt(y, 10),
|
|
88
|
+
z: parseInt(z, 10),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async testReachability(): Promise<TestReachabilityOutputDTO> {
|
|
98
|
+
try {
|
|
99
|
+
await this.executeConsoleCommand('serverinfo');
|
|
100
|
+
return new TestReachabilityOutputDTO({ connectable: true });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.log.warn('testReachability', error);
|
|
103
|
+
return new TestReachabilityOutputDTO({ connectable: false });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async executeConsoleCommand(rawCommand: string) {
|
|
108
|
+
const client = await this.getClient();
|
|
109
|
+
return new Promise<CommandOutput>(async (resolve, reject) => {
|
|
110
|
+
const command = rawCommand.trim();
|
|
111
|
+
const requestId = this.getRequestId();
|
|
112
|
+
|
|
113
|
+
const timeout = setTimeout(() => reject(), 5000);
|
|
114
|
+
|
|
115
|
+
client.on('message', (data) => {
|
|
116
|
+
const parsed = JSON.parse(data.toString());
|
|
117
|
+
|
|
118
|
+
if (parsed.Identifier !== requestId) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const commandResult = parsed.Message;
|
|
123
|
+
clearTimeout(timeout);
|
|
124
|
+
return resolve(new CommandOutput({ rawResult: commandResult }));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.log.debug('executeConsoleCommand - sending command', { command });
|
|
128
|
+
client.send(
|
|
129
|
+
JSON.stringify({
|
|
130
|
+
Message: command,
|
|
131
|
+
Identifier: requestId,
|
|
132
|
+
Name: 'Takaro',
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async sendMessage(message: string) {
|
|
139
|
+
await this.executeConsoleCommand(`say "${message}"`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async teleportPlayer(player: IGamePlayer, x: number, y: number, z: number) {
|
|
143
|
+
await this.executeConsoleCommand(`teleportplayer.pos ${player.gameId} ${x} ${y} ${z}`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async kickPlayer(player: IGamePlayer, reason: string) {
|
|
147
|
+
await this.executeConsoleCommand(`kick "${player.gameId}" "${reason}"`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async banPlayer(options: BanDTO) {
|
|
151
|
+
// 'find banid'
|
|
152
|
+
// Variables: Commands: global.banid( ) banid <steamid> <username> <reason> [optional duration]
|
|
153
|
+
// This optional duration is an integer, seems to be hours.
|
|
154
|
+
|
|
155
|
+
if (!options.expiresAt) {
|
|
156
|
+
await this.executeConsoleCommand(`banid ${options.player.gameId} "" "${options.reason}"`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const timeDiff = new Date(options.expiresAt).valueOf() - Date.now();
|
|
161
|
+
const hours = Math.floor(timeDiff / 1000 / 60 / 60);
|
|
162
|
+
|
|
163
|
+
await this.executeConsoleCommand(`banid ${options.player.gameId} "" "${options.reason}" ${hours}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async unbanPlayer(player: IPlayerReferenceDTO) {
|
|
167
|
+
await this.executeConsoleCommand(`unban ${player.gameId}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async listBans(): Promise<BanDTO[]> {
|
|
171
|
+
const response = await this.executeConsoleCommand('banlistex');
|
|
172
|
+
|
|
173
|
+
if (!response.success || !response.rawResult) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const lines = response.rawResult.split('\n');
|
|
178
|
+
const pattern =
|
|
179
|
+
/(?:'|^)(?<number>\d+)\s+(?<gameId>\d+)\s+"(?<username>.*?)"\s+"(?<reason>.*?)"\s+(?<expiration>-?\d+)\s*(?:'|\n|$)/g;
|
|
180
|
+
const bans = [];
|
|
181
|
+
|
|
182
|
+
for (const line of lines) {
|
|
183
|
+
let match;
|
|
184
|
+
while ((match = pattern.exec(line)) !== null) {
|
|
185
|
+
if (!match.groups) {
|
|
186
|
+
this.log.warn('listBans - line did not match regex', { match, line });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const { gameId, expiration } = match.groups;
|
|
190
|
+
|
|
191
|
+
let expiresAt = null;
|
|
192
|
+
if (expiration !== '-1') {
|
|
193
|
+
expiresAt = new Date(parseInt(expiration) * 1000).toISOString();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const ban = new BanDTO({
|
|
197
|
+
reason: match.groups.reason,
|
|
198
|
+
player: new IPlayerReferenceDTO({
|
|
199
|
+
gameId,
|
|
200
|
+
}),
|
|
201
|
+
expiresAt,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
bans.push(ban);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return bans;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async listItems(): Promise<IItemDTO[]> {
|
|
212
|
+
return Promise.all(
|
|
213
|
+
Object.values(itemsJson).map((item) => {
|
|
214
|
+
return new IItemDTO({
|
|
215
|
+
code: item.shortname,
|
|
216
|
+
name: item.Name,
|
|
217
|
+
description: item.Description,
|
|
218
|
+
});
|
|
219
|
+
})
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async getPlayerInventory(player: IPlayerReferenceDTO): Promise<IItemDTO[]> {
|
|
224
|
+
const res = await this.executeConsoleCommand(`viewinventory ${player.gameId}`);
|
|
225
|
+
const toParse = res.rawResult.replace('[ViewInventory] ', '');
|
|
226
|
+
const parsed = JSON.parse(toParse);
|
|
227
|
+
const items = JSON.parse(parsed.Inventory);
|
|
228
|
+
return await Promise.all(items.map((i: any) => new IItemDTO({ code: i.itemName, amount: i.amount })));
|
|
229
|
+
}
|
|
230
|
+
}
|