Skip to content

Commit

Permalink
Add possibility to force players by their steam ids into a specific t…
Browse files Browse the repository at this point in the history
…eam.
  • Loading branch information
JensForstmann committed Oct 11, 2024
1 parent 145dca6 commit c803921
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 14 deletions.
25 changes: 18 additions & 7 deletions backend/src/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,13 @@ export const onLog = async (match: Match, body: string) => {
// logBuffer was empty before -> no other onLogLine is in progress right now
while (match.logBuffer.length > 0) {
const oldestLine = match.logBuffer[0]!;
await onLogLine(match, oldestLine);
try {
await onLogLine(match, oldestLine);
} catch (err) {
match.log(`Error processing incoming log line from game server: ${oldestLine}`);
match.log(`Failed line: ${oldestLine}`);
match.log(`Message: ${err}`);
}
match.logBuffer.splice(0, 1);
}
}
Expand Down Expand Up @@ -586,10 +592,10 @@ const onPlayerLogLine = async (
const steamId64 = Player.getSteamID64(steamId);
player = match.data.players.find((p) => p.steamId64 === steamId64);
if (!player) {
player = Player.create(steamId, name);
player = Player.create(match, steamId, name);
match.log(`Player ${player.steamId64} (${name}) created`);
match.data.players.push(player);
player = match.data.players[match.data.players.length - 1]!;
player = match.data.players[match.data.players.length - 1]!; // re-assign to work nicely with changeListener (ProxyHandler)
MatchService.scheduleSave(match);
}
if (player.name !== name) {
Expand Down Expand Up @@ -828,25 +834,29 @@ export const sayWhatTeamToJoin = async (match: Match) => {
const onTeamCommand: commands.CommandHandler = async ({ match, player, parameters }) => {
const firstParameter = parameters[0]?.toUpperCase();
if (firstParameter === 'A' || firstParameter === 'B') {
if (Player.getForcedTeam(match, player.steamId64)) {
await say(match, `PLAYER ${escapeRconString(player.name)} CANNOT CHANGE THEIR TEAM`);
return;
}
player.team = firstParameter === 'A' ? 'TEAM_A' : 'TEAM_B';
MatchService.scheduleSave(match);
const team = getTeamByAB(match, player.team);
say(
await say(
match,
`PLAYER ${escapeRconString(player.name)} JOINED TEAM ${escapeRconString(team.name)}`
);
match.log(`Player ${player.name} joined team ${player.team} (${team.name})`);
} else {
const playerTeam = player.team;
if (playerTeam) {
say(
await say(
match,
`YOU ARE IN TEAM ${playerTeam === 'TEAM_A' ? 'A' : 'B'}: ${escapeRconString(
`PLAYER ${escapeRconString(player.name)} IS IN TEAM ${playerTeam === 'TEAM_A' ? 'A' : 'B'}: ${escapeRconString(
playerTeam === 'TEAM_A' ? match.data.teamA.name : match.data.teamB.name
)}`
);
} else {
say(match, `YOU HAVE NO TEAM`);
await say(match, `PLAYER ${escapeRconString(player.name)} HAS NO TEAM`);
}
}
};
Expand Down Expand Up @@ -1049,6 +1059,7 @@ export const update = async (match: Match, dto: IMatchUpdateDto) => {
}

if (dto.teamA || dto.teamB) {
Player.forcePlayerIntoTeams(match);
await setTeamNames(match);
}

Expand Down
35 changes: 32 additions & 3 deletions backend/src/player.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,46 @@
import SteamID from 'steamid';
import { IPlayer, TTeamSides, TTeamString } from '../../common';
import { IPlayer, TTeamAB, TTeamSides, TTeamString } from '../../common';
import * as Match from './match';

export const create = (steamId: string, name: string): IPlayer => {
export const create = (match: Match.Match, steamId: string, name: string): IPlayer => {
const steamId64 = getSteamID64(steamId);
return {
name: name,
steamId64: getSteamID64(steamId),
steamId64: steamId64,
team: getForcedTeam(match, steamId64),
};
};

export const getSteamID64 = (steamId: string) => {
return new SteamID(steamId).getSteamID64();
};

export const getForcedTeam = (match: Match.Match, steamId64: string): TTeamAB | undefined => {
const isTeamA = match.data.teamA.playerSteamIds64?.includes(steamId64);
const isTeamB = match.data.teamB.playerSteamIds64?.includes(steamId64);
if (isTeamA === isTeamB) {
// either: configured for no teams
// or: configured for both teams
return undefined;
}
return isTeamA ? 'TEAM_A' : 'TEAM_B';
};

export const forcePlayerIntoTeams = (match: Match.Match) => {
match.data.players.forEach((player, index) => {
const prevTeamAB = player.team;
const newTeamAB = getForcedTeam(match, player.steamId64);
if (newTeamAB && prevTeamAB !== newTeamAB) {
const fromTeam = prevTeamAB
? ` from ${prevTeamAB} (${Match.getTeamByAB(match, prevTeamAB).name})`
: '';
const toTeam = ` into team ${newTeamAB} (${Match.getTeamByAB(match, newTeamAB).name})`;
match.log(`Force player ${player.name}${fromTeam}${toTeam}`);
match.data.players[index]!.team = newTeamAB;
}
});
};

export const getSideFromTeamString = (teamString: TTeamString): TTeamSides | null => {
switch (teamString) {
case 'CT':
Expand Down
2 changes: 2 additions & 0 deletions backend/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const models: TsoaRoute.Models = {
passthrough: { dataType: 'string' },
name: { dataType: 'string', required: true },
advantage: { dataType: 'double', required: true },
playerSteamIds64: { dataType: 'array', array: { dataType: 'string' } },
},
additionalProperties: false,
},
Expand Down Expand Up @@ -545,6 +546,7 @@ const models: TsoaRoute.Models = {
name: { dataType: 'string', required: true },
passthrough: { dataType: 'string' },
advantage: { dataType: 'double' },
playerSteamIds64: { dataType: 'array', array: { dataType: 'string' } },
},
additionalProperties: false,
},
Expand Down
16 changes: 15 additions & 1 deletion backend/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
"type": "number",
"format": "double",
"description": "Advantage in map wins, useful for double elemination tournament finals."
},
"playerSteamIds64": {
"items": {
"type": "string"
},
"type": "array",
"description": "Steam ids of players in \"Steam ID 64\" format. Will be forced into this team."
}
},
"required": ["name", "advantage"],
Expand Down Expand Up @@ -524,7 +531,7 @@
},
"team": {
"$ref": "#/components/schemas/TTeamAB",
"description": "Current team as they joined with `.team`."
"description": "Current team as they joined with `.team`.\nIf the player's steam id is in the team's `playerSteamIds64`\nthis cannot be changed and is always set to the team."
},
"side": {
"allOf": [
Expand Down Expand Up @@ -753,6 +760,13 @@
"type": "number",
"format": "double",
"description": "Advantage in map wins, useful for double elemination tournament finals."
},
"playerSteamIds64": {
"items": {
"type": "string"
},
"type": "array",
"description": "Steam ids of players in \"Steam ID 64\" format. Will be forced into this team."
}
},
"required": ["name"],
Expand Down
6 changes: 5 additions & 1 deletion common/types/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ export interface IPlayer {
steamId64: string;
/** Name. */
name: string;
/** Current team as they joined with `.team`. */
/**
* Current team as they joined with `.team`.
* If the player's steam id is in the team's `playerSteamIds64`
* this cannot be changed and is always set to the team.
*/
team?: TTeamAB;
/** Current ingame side. */
side?: TTeamSides | null;
Expand Down
4 changes: 4 additions & 0 deletions common/types/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface ITeam {
name: string;
/** Advantage in map wins, useful for double elemination tournament finals. */
advantage: number;
/** Steam ids of players in "Steam ID 64" format. Will be forced into this team.*/
playerSteamIds64?: string[];
}

/**
Expand All @@ -25,6 +27,8 @@ export interface ITeamCreateDto {
passthrough?: string;
/** Advantage in map wins, useful for double elemination tournament finals. */
advantage?: number;
/** Steam ids of players in "Steam ID 64" format. Will be forced into this team.*/
playerSteamIds64?: string[];
}

/** Possible ingame sides of a player. */
Expand Down
54 changes: 53 additions & 1 deletion frontend/src/components/CreateUpdateMatch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { SelectInput, TextArea, TextInput, ToggleInput } from './Inputs';
import { Modal } from './Modal';

const Presets: Component<{
onSelect: (preset: IPreset) => void;
onSelect: (preset: IMatchCreateDto) => void;
matchCreateDto: IMatchCreateDto;
}> = (props) => {
const fetcher = createFetcher();
Expand Down Expand Up @@ -235,6 +235,10 @@ const minifyMapPool = (maps: string[]) => {
return maps.map((map) => map.trim()).filter((l) => l.length > 0);
};

const minifyPlayerSteamIds64 = (steamIds: string[]) => {
return steamIds.map((steamId) => steamId.trim()).filter((l) => l.length > 0);
};

export const CreateUpdateMatch: Component<
(
| {
Expand Down Expand Up @@ -265,6 +269,12 @@ export const CreateUpdateMatch: Component<
try {
const tempDto = copyObject(dto);
tempDto.mapPool = minifyMapPool(tempDto.mapPool);
tempDto.teamA.playerSteamIds64 = minifyPlayerSteamIds64(
tempDto.teamA.playerSteamIds64 ?? []
);
tempDto.teamB.playerSteamIds64 = minifyPlayerSteamIds64(
tempDto.teamB.playerSteamIds64 ?? []
);
setJson(props.getFinalDto?.(tempDto) ?? JSON.stringify(tempDto, undefined, 4));
} catch (err) {
setJson('ERROR!\n' + err);
Expand Down Expand Up @@ -812,6 +822,27 @@ export const CreateUpdateMatch: Component<
)}
onInput={(e) => setDto('teamA', 'passthrough', e.currentTarget.value)}
/>
<TextArea
label={t('Team A Player Steam IDs 64')}
labelTopRight={t('Force players by their steam id 64 into team A')}
value={dto.teamA.playerSteamIds64?.join('\n')}
class={
'border-2 ' +
getChangedClasses(
props.match.teamA.playerSteamIds64?.join('\n'),
dto.teamA.playerSteamIds64?.join('\n'),
'input-accent'
)
}
onInput={(e) =>
setDto(
'teamA',
'playerSteamIds64',
e.currentTarget.value.split('\n')
)
}
rows="5"
/>
<TextInput
label={t('Team B Advantage')}
type="number"
Expand Down Expand Up @@ -840,6 +871,27 @@ export const CreateUpdateMatch: Component<
)}
onInput={(e) => setDto('teamB', 'passthrough', e.currentTarget.value)}
/>
<TextArea
label={t('Team B Player Steam IDs 64')}
labelTopRight={t('Force players by their steam id 64 into team B')}
value={dto.teamB.playerSteamIds64?.join('\n')}
class={
'border-2 ' +
getChangedClasses(
props.match.teamB.playerSteamIds64?.join('\n'),
dto.teamB.playerSteamIds64?.join('\n'),
'input-accent'
)
}
onInput={(e) =>
setDto(
'teamB',
'playerSteamIds64',
e.currentTarget.value.split('\n')
)
}
rows="5"
/>
<ToggleInput
label={t('Can Clinch')}
labelTopRight={t(
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/copyObject.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const copyObject = <T>(obj: T) => {
export const copyObject = <T>(obj: T): T => {
return JSON.parse(JSON.stringify(obj));
};

0 comments on commit c803921

Please sign in to comment.