Skip to content

Commit 5186325

Browse files
authored
feat: exec config per map (#315)
* get rid of Tf2Map * update configs to use the MapPoolItem * execute per-map config
1 parent 4e5ca2e commit 5186325

14 files changed

+202
-103
lines changed

configs/queue/6v6.json

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,38 @@
1919
}
2020
],
2121
"maps": [
22-
"cp_process_final",
23-
"cp_snakewater_final1",
24-
"cp_sunshine",
25-
"cp_granary_pro_rc8",
26-
"cp_gullywash_final1",
27-
"cp_reckoner_rc2",
28-
"cp_prolands_rc2t"
29-
],
30-
"execConfigs": [
31-
"etf2l_6v6_5cp"
22+
{
23+
"name": "cp_process_final",
24+
"configName": "5cp"
25+
},
26+
{
27+
"name": "cp_snakewater_final1",
28+
"configName": "5cp"
29+
},
30+
{
31+
"name": "cp_sunshine",
32+
"configName": "5cp"
33+
},
34+
{
35+
"name": "cp_granary_pro_rc8",
36+
"configName": "5cp"
37+
},
38+
{
39+
"name": "cp_gullywash_final1",
40+
"configName": "5cp"
41+
},
42+
{
43+
"name": "cp_reckoner_rc2",
44+
"configName": "5cp"
45+
},
46+
{
47+
"name": "cp_prolands_rc2t",
48+
"configName": "5cp"
49+
}
3250
],
51+
"configs": {
52+
"5cp": "etf2l_6v6_5cp",
53+
"koth": "etf2l_6v6_koth"
54+
},
3355
"whitelistId": "10585"
3456
}

configs/queue/9v9.json

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,51 @@
3939
}
4040
],
4141
"maps": [
42-
"koth_product_rcx",
43-
"koth_warmtic_b6",
44-
"koth_coalplant_b8",
45-
"pl_prowater_b6",
46-
"cp_gullywash_final1",
47-
"pl_upward",
48-
"pl_borneo",
49-
"pl_swiftwater_final1",
50-
"pl_vigil_rc6",
51-
"cp_propaganda_b16",
52-
"pl_barnblitz_pro6"
53-
],
54-
"execConfigs": [
55-
"etf2l_9v9_5cp"
42+
{
43+
"name": "koth_product_rcx",
44+
"configName": "koth"
45+
},
46+
{
47+
"name": "koth_warmtic_b6",
48+
"configName": "koth"
49+
},
50+
{
51+
"name": "koth_coalplant_b8",
52+
"configName": "koth"
53+
},
54+
{
55+
"name": "pl_prowater_b6",
56+
"configName": "stopwatch"
57+
},
58+
{
59+
"name": "cp_gullywash_final1",
60+
"configName": "5cp"
61+
},
62+
{
63+
"name": "pl_upward",
64+
"configName": "stopwatch"
65+
},
66+
{
67+
"name": "pl_borneo",
68+
"configName": "stopwatch"
69+
},
70+
{
71+
"name": "pl_swiftwater_final1",
72+
"configName": "stopwatch"
73+
},
74+
{
75+
"name": "pl_vigil_rc6",
76+
"configName": "stopwatch"
77+
},
78+
{
79+
"name": "pl_barnblitz_pro6",
80+
"configName": "stopwatch"
81+
}
5682
],
83+
"configs": {
84+
"koth": "etf2l_9v9_koth",
85+
"stopwatch": "etf2l_9v9_stopwatch",
86+
"5cp": "etf2l_9v9_5cp"
87+
},
5788
"whitelistId": "10712"
5889
}

configs/queue/test.json

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
}
88
],
99
"maps": [
10-
"cp_process_final",
11-
"cp_snakewater_final1",
12-
"cp_sunshine",
13-
"cp_granary_pro_rc8",
14-
"cp_gullywash_final1",
15-
"cp_reckoner_rc2",
16-
"cp_prolands_rc2t"
10+
{
11+
"name": "cp_process_final",
12+
"configName": "5cp"
13+
},
14+
{
15+
"name": "cp_gullywash_final1",
16+
"configName": "5cp"
17+
}
1718
],
18-
"execConfigs": [
19-
"etf2l_6v6_5cp"
20-
]
19+
"configs": {
20+
"5cp": "etf2l_6v6_5cp"
21+
}
2122
}

src/games/services/server-configurator.service.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ class PlayersServiceStub {
2323

2424
class QueueConfigServiceStub {
2525
queueConfig = {
26-
execConfigs: [ 'etf2l_6v6_5cp' ],
26+
maps: [
27+
{ name: 'cp_badlands', configName: '5cp' },
28+
],
29+
configs: {
30+
'5cp': 'etf2l_6v6_5cp',
31+
},
32+
execConfigs: [ 'test' ],
2733
} as Partial<QueueConfig>;
2834
}
2935

@@ -100,6 +106,7 @@ describe('ServerConfiguratorService', () => {
100106
expect(spy).toHaveBeenCalledWith(kickAll());
101107
expect(spy).toHaveBeenCalledWith(changelevel('cp_badlands'));
102108
expect(spy).toHaveBeenCalledWith(execConfig('etf2l_6v6_5cp'));
109+
expect(spy).toHaveBeenCalledWith(execConfig('test'));
103110
expect(spy).toHaveBeenCalledWith(jasmine.stringMatching(/^sv_password\s.+$/));
104111
expect(spy).toHaveBeenCalledWith(addGamePlayer('PLAYER_1_STEAMID', 'PLAYER_1_NAME', 2, 'soldier'));
105112
expect(spy).toHaveBeenCalledWith(addGamePlayer('PLAYER_2_STEAMID', 'PLAYER_2_NAME', 3, 'soldier'));

src/games/services/server-configurator.service.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,16 @@ export class ServerConfiguratorService {
4141
this.logger.debug(`[${server.name}] changing map to ${game.map}...`);
4242
await rcon.send(changelevel(game.map));
4343

44-
for (const configName of this.queueConfigService.queueConfig.execConfigs) {
45-
this.logger.debug(`[${server.name}] executing ${configName}...`);
46-
await rcon.send(execConfig(configName));
44+
const configName = this.queueConfigService.queueConfig.maps.find(m => m.name === game.map)?.configName;
45+
const config = this.queueConfigService.queueConfig.configs[configName];
46+
if (config) {
47+
this.logger.debug(`[${server.name}] executing ${config}...`);
48+
await rcon.send(execConfig(config));
49+
}
50+
51+
for (const c of this.queueConfigService.queueConfig.execConfigs) {
52+
this.logger.debug(`[${server.name}] executing ${c}...`);
53+
await rcon.send(execConfig(c));
4754
}
4855

4956
if (this.queueConfigService.queueConfig.whitelistId) {

src/queue/gateways/queue.gateway.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { SubscribeMessage, WebSocketGateway, OnGatewayInit } from '@nestjs/webso
22
import { QueueService } from '../services/queue.service';
33
import { WsAuthorized } from '@/auth/decorators/ws-authorized.decorator';
44
import { Socket } from 'socket.io';
5-
import { Tf2Map } from '../tf2-map';
65
import { MapVoteService } from '../services/map-vote.service';
76
import { QueueSlot } from '../queue-slot';
87
import { QueueState } from '../queue-state';
@@ -49,7 +48,7 @@ export class QueueGateway implements OnGatewayInit {
4948

5049
@WsAuthorized()
5150
@SubscribeMessage('vote for map')
52-
voteForMap(client: any, payload: { map: Tf2Map }) {
51+
voteForMap(client: any, payload: { map: string }) {
5352
this.mapVoteService.voteForMap(client.request.user.id, payload.map);
5453
return payload.map;
5554
}

src/queue/map-pool-item.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface MapPoolItem {
2+
name: string;
3+
configName: string;
4+
}

src/queue/map-vote-result.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { Tf2Map } from './tf2-map';
2-
31
export interface MapVoteResult {
4-
map: Tf2Map;
2+
map: string;
53
voteCount: number;
64
}

src/queue/queue-config.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GameClass } from './game-class';
2-
import { Tf2Map } from './tf2-map';
2+
import { MapPoolItem } from './map-pool-item';
33

44
export interface QueueConfig {
55
/* This is always 2 */
@@ -9,11 +9,13 @@ export interface QueueConfig {
99
classes: GameClass[];
1010

1111
/* Map pool */
12-
maps: Tf2Map[];
12+
maps: MapPoolItem[];
1313

14-
/* What configs to execute */
15-
// fixme make this per-map instead of per-gamemode
16-
execConfigs: string[];
14+
/* Configs */
15+
configs: { [configName: string]: string };
16+
17+
/* List of configs to execute */
18+
execConfigs?: string[];
1719

1820
/* Whitelist ID (http://whitelist.tf/) */
1921
whitelistId: string;

src/queue/queue.module.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import { DiscordModule } from '@/discord/discord.module';
1111
import { AutoGameLauncherService } from './services/auto-game-launcher.service';
1212
import { QueueAnnouncementsService } from './services/queue-announcements.service';
1313
import { FriendsService } from './services/friends.service';
14+
import { Environment } from '@/environment/environment';
15+
import { join } from 'path';
16+
import { readFile } from 'fs';
17+
import { promisify } from 'util';
1418

1519
@Module({
1620
imports: [
@@ -27,6 +31,14 @@ import { FriendsService } from './services/friends.service';
2731
AutoGameLauncherService,
2832
QueueAnnouncementsService,
2933
FriendsService,
34+
{
35+
provide: 'QUEUE_CONFIG_JSON',
36+
useFactory: async (environment: Environment) => {
37+
const configFileName = join('configs', 'queue', `${environment.queueConfig}.json`);
38+
return promisify(readFile)(configFileName, 'utf-8');
39+
},
40+
inject: [ Environment ],
41+
},
3042
],
3143
exports: [
3244
QueueService,

src/queue/services/map-vote.service.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
import { Injectable, OnModuleInit, Inject, forwardRef } from '@nestjs/common';
2-
import { Tf2Map } from '../tf2-map';
32
import { QueueConfigService } from './queue-config.service';
43
import { QueueService } from './queue.service';
54
import { maxBy, shuffle } from 'lodash';
6-
import { BehaviorSubject, Observable } from 'rxjs';
5+
import { BehaviorSubject } from 'rxjs';
76
import { MapVoteResult } from '../map-vote-result';
87
import { QueueGateway } from '../gateways/queue.gateway';
98

109
interface MapVote {
1110
playerId: string;
12-
map: Tf2Map;
11+
map: string;
1312
}
1413

1514
@Injectable()
1615
export class MapVoteService implements OnModuleInit {
1716

1817
private _results = new BehaviorSubject<MapVoteResult[]>([]);
1918

20-
public mapOptions: Tf2Map[];
19+
public mapOptions: string[];
2120

2221
get results(): MapVoteResult[] {
2322
return this._results.value;
@@ -40,11 +39,11 @@ export class MapVoteService implements OnModuleInit {
4039
this._results.subscribe(results => this.queueGateway.emitVoteResultsUpdate(results));
4140
}
4241

43-
voteCountForMap(map: Tf2Map): number {
42+
voteCountForMap(map: string): number {
4443
return this.votes.filter(v => v.map === map).length;
4544
}
4645

47-
voteForMap(playerId: string, map: Tf2Map) {
46+
voteForMap(playerId: string, map: string) {
4847
if (!this.mapOptions.includes(map)) {
4948
throw new Error('this map is not an option in the vote');
5049
}
@@ -61,7 +60,7 @@ export class MapVoteService implements OnModuleInit {
6160
this._results.next(this.getResults());
6261
}
6362

64-
playerVote(playerId: string): Tf2Map {
63+
playerVote(playerId: string): string {
6564
return this.votes.find(v => v.playerId === playerId)?.map;
6665
}
6766

@@ -78,8 +77,11 @@ export class MapVoteService implements OnModuleInit {
7877
}
7978

8079
private reset() {
81-
this.mapOptions = shuffle(this.queueConfigService.queueConfig.maps.filter(m => m !== this.lastPlayedMap))
82-
.slice(0, this.mapVoteOptionCount);
80+
this.mapOptions = shuffle(
81+
this.queueConfigService.queueConfig.maps
82+
.filter(m => m.name !== this.lastPlayedMap)
83+
.map(m => m.name)
84+
).slice(0, this.mapVoteOptionCount);
8385
this.votes = [];
8486
this._results.next(this.getResults());
8587
}
Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,46 @@
11
import { Test, TestingModule } from '@nestjs/testing';
22
import { QueueConfigService } from './queue-config.service';
3-
import { Environment } from '@/environment/environment';
4-
5-
class EnvironmentStub {
6-
queueConfig = '6v6';
7-
}
83

94
describe('QueueConfigService', () => {
10-
let service: QueueConfigService;
11-
12-
beforeEach(async () => {
13-
const module: TestingModule = await Test.createTestingModule({
14-
providers: [
15-
QueueConfigService,
16-
{ provide: Environment, useClass: EnvironmentStub },
5+
describe('with a valid config', () => {
6+
const config = `{
7+
"teamCount": 2,
8+
"classes": [
9+
{
10+
"name": "soldier",
11+
"count": 1
12+
}
13+
],
14+
"maps": [
15+
{
16+
"name": "cp_process_final",
17+
"configName": "5cp"
18+
},
19+
{
20+
"name": "cp_gullywash_final1",
21+
"configName": "5cp"
22+
}
1723
],
18-
}).compile();
24+
"configs": {
25+
"5cp": "etf2l_6v6_5cp"
26+
}
27+
}`;
1928

20-
service = module.get<QueueConfigService>(QueueConfigService);
21-
});
29+
let service: QueueConfigService;
30+
31+
beforeEach(async () => {
32+
const module: TestingModule = await Test.createTestingModule({
33+
providers: [
34+
QueueConfigService,
35+
{ provide: 'QUEUE_CONFIG_JSON', useValue: config },
36+
],
37+
}).compile();
38+
39+
service = module.get<QueueConfigService>(QueueConfigService);
40+
});
2241

23-
it('should be defined', () => {
24-
expect(service).toBeDefined();
42+
it('should be defined', () => {
43+
expect(service).toBeDefined();
44+
});
2545
});
2646
});

0 commit comments

Comments
 (0)