Commit 86b0f261 authored by nanahira's avatar nanahira

tag spawn 3 bots

parent 9c10c96b
......@@ -74,11 +74,10 @@ export class DefaultHostInfoProvider {
}
const rulePrefix = name.match(/(.+)#/);
if (!rulePrefix) {
const rule = rulePrefix ? rulePrefix[1].toUpperCase() : '';
if (!rule) {
return hostinfo;
}
const rule = rulePrefix[1].toUpperCase();
if (/(^|,|,)(M|MATCH)(,|,|$)/.test(rule)) {
hostinfo.mode = setWinMatchCountBits(hostinfo.mode, 2);
}
......
export * from './room';
export * from './room-manager';
export * from './room-event/on-room-finalize';
export * from './default-hostinfo-provder';
......@@ -3,3 +3,4 @@ export * from './windbot-spawner';
export * from './join-windbot-ai';
export * from './join-windbot-token';
export * from './windbot-module';
export * from './utility';
......@@ -3,6 +3,7 @@ import { Context } from '../app';
import { WindBotProvider } from './windbot-provider';
import { RoomManager } from '../room';
import { fillRandomString } from '../utility/fill-random-string';
import { parseWindbotOptions } from './utility';
const getDisplayLength = (text: string) =>
text.replace(/[^\x00-\xff]/g, '00').length;
......@@ -52,16 +53,21 @@ export class JoinWindbotAi {
room.windbot = {
name: '',
deck: '',
}
};
const windbotOptions = parseWindbotOptions(room.name);
await room.join(client);
const requestOk = await this.windbotProvider.requestWindbotJoin(
room,
requestedBotName,
);
if (!requestOk) {
await room.finalize();
return;
const requestCount = room.isTag ? 3 : 1;
for (let i = 0; i < requestCount; i += 1) {
const requestOk = await this.windbotProvider.requestWindbotJoin(
room,
requestedBotName,
windbotOptions,
);
if (!requestOk) {
await room.finalize();
return;
}
}
this.logger.debug(
......@@ -69,6 +75,7 @@ export class JoinWindbotAi {
player: client.name,
roomName: room.name,
botName: room.windbot?.name,
requestCount,
},
'Created windbot room',
);
......
......@@ -20,18 +20,18 @@ export class JoinWindbotToken {
}
const token = msg.pass.slice('AIJOIN#'.length);
const roomName = this.windbotProvider.consumeJoinToken(token);
if (!roomName) {
const tokenData = this.windbotProvider.consumeJoinToken(token);
if (!tokenData) {
return client.die('#{invalid_password_not_found}', ChatColor.RED);
}
const room = this.roomManager.findByName(roomName);
const room = this.roomManager.findByName(tokenData.roomName);
if (!room) {
return client.die('#{invalid_password_not_found}', ChatColor.RED);
}
client.isInternal = true;
client.windbot = room.windbot;
client.windbot = tokenData.windbot;
return room.join(client);
});
}
......
export * from './types';
export * from './parse-rule-prefix';
export * from './parse-windbot-options';
export const parseRulePrefix = (name: string) => {
const rulePrefix = name.match(/(.+)#/);
if (!rulePrefix) {
return '';
}
return rulePrefix[1].toUpperCase();
};
import type { RequestWindbotJoinOptions } from './types';
import { parseRulePrefix } from './parse-rule-prefix';
export const parseWindbotOptions = (name: string): RequestWindbotJoinOptions => {
const rule = parseRulePrefix(name);
const options: RequestWindbotJoinOptions = {};
if (!rule) {
return options;
}
if (/(^|,|,)(SS|SCISSORS)(,|,|$)/.test(rule)) {
options.hand = 1;
} else if (/(^|,|,)(ROCK)(,|,|$)/.test(rule)) {
options.hand = 2;
} else if (/(^|,|,)(PAPER)(,|,|$)/.test(rule)) {
options.hand = 3;
}
return options;
};
export interface WindbotData {
name: string;
deck: string;
dialog?: string;
hidden?: boolean;
deckcode?: string;
}
export interface RequestWindbotJoinOptions {
hand?: 1 | 2 | 3;
}
export interface WindbotJoinTokenData {
roomName: string;
windbot: WindbotData;
}
......@@ -3,14 +3,11 @@ import * as fs from 'node:fs/promises';
import { ChatColor } from 'ygopro-msg-encode';
import { Context } from '../app';
import { OnRoomFinalize, Room } from '../room';
export interface WindbotData {
name: string;
deck: string;
dialog?: string;
hidden?: boolean;
deckcode?: string;
}
import type {
RequestWindbotJoinOptions,
WindbotData,
WindbotJoinTokenData,
} from './utility';
declare module '../client' {
interface Client {
......@@ -36,8 +33,8 @@ export class WindBotProvider {
public botlistPath = this.ctx.config.getString('WINDBOT_BOTLIST');
private bots: WindbotData[] = [];
private tokenRoomMap = new Map<string, string>();
private roomTokenMap = new Map<string, string>();
private tokenDataMap = new Map<string, WindbotJoinTokenData>();
private roomTokenMap = new Map<string, Set<string>>();
constructor(private ctx: Context) {
if (!this.enabled) {
......@@ -73,60 +70,70 @@ export class WindBotProvider {
return this.bots.find((bot) => bot.name === name || bot.deck === name);
}
issueJoinToken(roomName: string) {
const oldToken = this.roomTokenMap.get(roomName);
if (oldToken) {
this.tokenRoomMap.delete(oldToken);
}
issueJoinToken(roomName: string, windbot: WindbotData) {
let token = '';
do {
token = cryptoRandomString({
length: 12,
type: 'alphanumeric',
});
} while (this.tokenRoomMap.has(token));
} while (this.tokenDataMap.has(token));
this.logger.debug(
{ roomName, token },
'Issuing windbot join token for room',
);
this.tokenRoomMap.set(token, roomName);
this.roomTokenMap.set(roomName, token);
this.tokenDataMap.set(token, {
roomName,
windbot: { ...windbot },
});
let roomTokens = this.roomTokenMap.get(roomName);
if (!roomTokens) {
roomTokens = new Set<string>();
this.roomTokenMap.set(roomName, roomTokens);
}
roomTokens.add(token);
return token;
}
consumeJoinToken(token: string) {
const roomName = this.tokenRoomMap.get(token);
this.logger.debug({ roomName, token }, 'Consuming windbot join token');
if (!roomName) {
const data = this.tokenDataMap.get(token);
this.logger.debug({ roomName: data?.roomName, token }, 'Consuming windbot join token');
if (!data) {
return undefined;
}
this.tokenRoomMap.delete(token);
const mappedToken = this.roomTokenMap.get(roomName);
if (mappedToken === token) {
this.roomTokenMap.delete(roomName);
this.tokenDataMap.delete(token);
const roomTokens = this.roomTokenMap.get(data.roomName);
if (roomTokens) {
roomTokens.delete(token);
if (roomTokens.size === 0) {
this.roomTokenMap.delete(data.roomName);
}
}
return roomName;
return data;
}
deleteRoomToken(roomName: string) {
const token = this.roomTokenMap.get(roomName);
if (!token) {
const roomTokens = this.roomTokenMap.get(roomName);
if (!roomTokens) {
return;
}
this.roomTokenMap.delete(roomName);
const mappedRoomName = this.tokenRoomMap.get(token);
if (mappedRoomName === roomName) {
this.tokenRoomMap.delete(token);
for (const token of roomTokens) {
const mappedData = this.tokenDataMap.get(token);
if (mappedData?.roomName === roomName) {
this.tokenDataMap.delete(token);
}
}
}
async requestWindbotJoin(room: Room, botname?: string) {
const roomWindbot =
this.isValidBot(room.windbot) && room.windbot ? room.windbot : undefined;
async requestWindbotJoin(
room: Room,
botname?: string,
options: RequestWindbotJoinOptions = {},
) {
const bot =
(botname && this.getBotByNameOrDeck(botname)) || roomWindbot || this.getRandomBot();
(botname && this.getBotByNameOrDeck(botname)) || this.getRandomBot();
if (!bot) {
await room.sendChat('#{windbot_deck_not_found}', ChatColor.RED);
return false;
......@@ -138,7 +145,7 @@ export class WindBotProvider {
};
}
Object.assign(room.windbot, bot);
const token = this.issueJoinToken(room.name);
const token = this.issueJoinToken(room.name, bot);
let url: URL;
try {
......@@ -164,6 +171,9 @@ export class WindBotProvider {
if (bot.deckcode) {
url.searchParams.set('deckcode', bot.deckcode);
}
if (options.hand) {
url.searchParams.set('hand', options.hand.toString());
}
this.logger.debug(
{ url: url.toString(), roomName: room.name },
......@@ -187,18 +197,6 @@ export class WindBotProvider {
}
}
private isValidBot(
bot: WindbotData | undefined,
): bot is WindbotData {
return !!(
bot &&
typeof bot.name === 'string' &&
bot.name.length &&
typeof bot.deck === 'string' &&
bot.deck.length
);
}
private async loadBotList() {
try {
const text = await fs.readFile(this.botlistPath, 'utf-8');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment