Commit b2c66f0a authored by purerosefallen's avatar purerosefallen

refa hostinfo

parent b658bd0a
......@@ -458,6 +458,7 @@ export class CloudReplayService {
...hostInfo,
mode:
hostInfo.mode > 2 ? (this.isTagMode(hostInfo) ? 2 : 1) : hostInfo.mode,
lflist: 0,
};
}
......
......@@ -10,6 +10,7 @@ import { Client } from '../../client';
import { MAX_ROOM_NAME_LENGTH } from '../../constants/room';
import {
DuelStage,
DefaultHostInfoProvider,
OnRoomFinalize,
OnRoomJoinPlayer,
OnRoomLeavePlayer,
......@@ -98,6 +99,7 @@ export class RandomDuelProvider {
private waitForPlayerProvider = this.ctx.get(() => WaitForPlayerProvider);
private clientKeyProvider = this.ctx.get(() => ClientKeyProvider);
private hidePlayerNameProvider = this.ctx.get(() => HidePlayerNameProvider);
private defaultHostInfoProvider = this.ctx.get(() => DefaultHostInfoProvider);
enabled = this.ctx.config.getBoolean('ENABLE_RANDOM_DUEL');
noRematchCheck = this.ctx.config.getBoolean('RANDOM_DUEL_NO_REMATCH_CHECK');
......@@ -120,6 +122,7 @@ export class RandomDuelProvider {
if (!this.enabled) {
return;
}
this.registerRandomRoomModes();
this.waitForPlayerProvider.registerTick({
roomFilter: (room) => !!room.randomType,
raadyTimeoutMs: this.waitForPlayerReadyTimeoutMs,
......@@ -280,6 +283,51 @@ export class RandomDuelProvider {
return uniqModes;
}
private registerRandomRoomModes() {
this.defaultHostInfoProvider
.registerRoomMode('(OOR|OCGONLYRANDOM)', {
rule: 0,
lflist: 0,
})
.registerRoomMode('(OR|OCGRANDOM)', {
rule: 5,
lflist: 0,
})
.registerRoomMode('(CR|CCGRANDOM)', {
rule: 2,
lflist: -1,
})
.registerRoomMode('(TOR|TCGONLYRANDOM)', {
rule: 1,
})
.registerRoomMode('(TR|TCGRANDOM)', {
rule: 5,
})
.registerRoomMode('(OOMR|OCGONLYMATCHRANDOM)', {
rule: 0,
lflist: 0,
mode: 1,
})
.registerRoomMode('(OMR|OCGMATCHRANDOM)', {
rule: 5,
lflist: 0,
mode: 1,
})
.registerRoomMode('(CMR|CCGMATCHRANDOM)', {
rule: 2,
lflist: -1,
mode: 1,
})
.registerRoomMode('(TOMR|TCGONLYMATCHRANDOM)', {
rule: 1,
mode: 1,
})
.registerRoomMode('(TMR|TCGMATCHRANDOM)', {
rule: 5,
mode: 1,
});
}
private resolveSupportedTypes() {
return new Set([...BUILTIN_RANDOM_TYPES, ...this.blankPassModes]);
}
......
import { HostInfo } from 'ygopro-msg-encode';
import { MayBeArray } from 'nfkit';
import { Context } from '../app';
import { DefaultHostinfo } from './default-hostinfo';
......@@ -20,8 +21,44 @@ const setWinMatchCountBits = (mode: number, winMatchCount: number): number => {
return (mode & TAG_MODE_BIT) | nonTagBits;
};
type RoomModeContext = {
hostinfo: HostInfo;
defaultHostinfo: HostInfo;
roomName: string;
roomPrefix: string;
regexResult: RegExpMatchArray;
};
type RoomModeBaseContext = Omit<RoomModeContext, 'regexResult'>;
type RoomModeRegistration = {
patterns: string[];
handler: RoomModeHandler;
};
type RoomModeHandler = (context: RoomModeContext) => Partial<HostInfo>;
type RoomModeHandlerLike = RoomModeHandler | Partial<HostInfo>;
export class DefaultHostInfoProvider {
constructor(private ctx: Context) {}
private roomModeRegistrations: RoomModeRegistration[] = [];
constructor(private ctx: Context) {
this.registerBuiltInRoomModes();
}
registerRoomMode(
pattern: MayBeArray<string>,
handler: RoomModeHandlerLike,
): this {
const normalizedHandler: RoomModeHandler =
typeof handler === 'function' ? handler : () => handler;
const patterns = (Array.isArray(pattern) ? pattern : [pattern]).map(
(item) => this.normalizeRoomModePattern(item),
);
this.roomModeRegistrations.push({
patterns,
handler: normalizedHandler,
});
return this;
}
getHostinfo(): HostInfo {
const hostinfo = { ...DefaultHostinfo };
......@@ -45,22 +82,26 @@ export class DefaultHostInfoProvider {
...partial,
};
if (name.startsWith('M#')) {
const namePrefixMatch = name.match(/(.+)#/);
const namePrefixRaw = namePrefixMatch ? namePrefixMatch[1] : '';
const namePrefix = namePrefixRaw.toUpperCase();
if (namePrefix === 'M') {
hostinfo.mode = 1;
return hostinfo;
}
if (name.startsWith('T#')) {
if (namePrefix === 'T') {
hostinfo.mode = 2;
hostinfo.start_lp = defaultHostinfo.start_lp * 2;
return hostinfo;
}
const compactParam = name.match(
/^(\d)(\d)([12345TF])(T|F)(T|F)(\d+),(\d+),(\d+)/i,
const compactParam = namePrefix.match(
/^([0-5])([0-9])([12345TF])(T|F)(T|F)(\d+),(\d+),(\d+)$/i,
);
if (compactParam) {
const duelRuleNumber = Number.parseInt(compactParam[3], 10);
hostinfo.rule = Number.parseInt(compactParam[1], 10);
hostinfo.mode = Number.parseInt(compactParam[2], 10);
const duelRuleNumber = Number.parseInt(compactParam[3], 10);
hostinfo.duel_rule = Number.isNaN(duelRuleNumber)
? compactParam[3].toUpperCase() === 'T'
? 3
......@@ -74,138 +115,149 @@ export class DefaultHostInfoProvider {
return hostinfo;
}
const rulePrefix = name.match(/(.+)#/);
const rule = rulePrefix ? rulePrefix[1].toUpperCase() : '';
const rule = namePrefix;
if (!rule) {
return hostinfo;
}
if (/(^|,|,)(M|MATCH)(,|,|$)/.test(rule)) {
hostinfo.mode = setWinMatchCountBits(hostinfo.mode, 2);
}
if (/(^|,|,)(T|TAG)(,|,|$)/.test(rule)) {
hostinfo.mode = setTagBit(hostinfo.mode, true);
hostinfo.start_lp = defaultHostinfo.start_lp * 2;
}
const boParam = rule.match(/(^|,|,)BO(\d+)(,|,|$)/);
if (boParam) {
const bo = Number.parseInt(boParam[2], 10);
// only odd BOx is valid (e.g. BO1/3/5...)
if (!Number.isNaN(bo) && bo > 0 && bo % 2 === 1) {
hostinfo.mode = setWinMatchCountBits(hostinfo.mode, (bo + 1) / 2);
this.applyRoomModes(rule, false, {
hostinfo,
defaultHostinfo,
roomName: name,
roomPrefix: namePrefixRaw,
});
return hostinfo;
}
private applyRoomModes(
source: string,
stopOnMatch: boolean,
baseContext: RoomModeBaseContext,
): boolean {
let matched = false;
for (const registration of this.roomModeRegistrations) {
for (const pattern of registration.patterns) {
const regexResult = source.match(new RegExp(pattern, 'i'));
if (!regexResult) {
continue;
}
const context: RoomModeContext = {
...baseContext,
regexResult: this.trimRegexResult(regexResult),
};
Object.assign(baseContext.hostinfo, registration.handler(context));
matched = true;
break;
}
}
if (/(^|,|,)(OOR|OCGONLYRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 0;
hostinfo.lflist = 0;
}
if (/(^|,|,)(OR|OCGRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 5;
hostinfo.lflist = 0;
}
if (/(^|,|,)(CR|CCGRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 2;
hostinfo.lflist = -1;
}
if (/(^|,|,)(TOR|TCGONLYRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 1;
}
if (/(^|,|,)(TR|TCGRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 5;
}
if (/(^|,|,)(OOMR|OCGONLYMATCHRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 0;
hostinfo.lflist = 0;
hostinfo.mode = 1;
}
if (/(^|,|,)(OMR|OCGMATCHRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 5;
hostinfo.lflist = 0;
hostinfo.mode = 1;
}
if (/(^|,|,)(CMR|CCGMATCHRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 2;
hostinfo.lflist = -1;
hostinfo.mode = 1;
}
if (/(^|,|,)(TOMR|TCGONLYMATCHRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 1;
hostinfo.mode = 1;
}
if (/(^|,|,)(TMR|TCGMATCHRANDOM)(,|,|$)/.test(rule)) {
hostinfo.rule = 5;
hostinfo.mode = 1;
}
if (/(^|,|,)(TCGONLY|TO)(,|,|$)/.test(rule)) {
hostinfo.rule = 1;
}
if (/(^|,|,)(OCGONLY|OO)(,|,|$)/.test(rule)) {
hostinfo.rule = 0;
hostinfo.lflist = 0;
}
if (/(^|,|,)(OT|TCG)(,|,|$)/.test(rule)) {
hostinfo.rule = 5;
}
if (/(^|,|,)(SC|CN|CCG|CHINESE)(,|,|$)/.test(rule)) {
hostinfo.rule = 2;
hostinfo.lflist = -1;
}
const lpParam = rule.match(/(^|,|,)LP(\d+)(,|,|$)/);
if (lpParam) {
let startLp = Number.parseInt(lpParam[2], 10);
if (startLp <= 0) startLp = 1;
if (startLp >= 99999) startLp = 99999;
hostinfo.start_lp = startLp;
}
const timeLimitParam = rule.match(/(^|,|,)(TIME|TM|TI)(\d+)(,|,|$)/);
if (timeLimitParam) {
let timeLimit = Number.parseInt(timeLimitParam[3], 10);
if (timeLimit < 0) timeLimit = 180;
if (timeLimit >= 1 && timeLimit <= 60) timeLimit *= 60;
if (timeLimit >= 999) timeLimit = 999;
hostinfo.time_limit = timeLimit;
}
const startHandParam = rule.match(/(^|,|,)(START|ST)(\d+)(,|,|$)/);
if (startHandParam) {
let startHand = Number.parseInt(startHandParam[3], 10);
if (startHand <= 0) startHand = 1;
if (startHand >= 40) startHand = 40;
hostinfo.start_hand = startHand;
}
const drawCountParam = rule.match(/(^|,|,)(DRAW|DR)(\d+)(,|,|$)/);
if (drawCountParam) {
let drawCount = Number.parseInt(drawCountParam[3], 10);
if (drawCount >= 35) drawCount = 35;
hostinfo.draw_count = drawCount;
}
const lflistParam = rule.match(/(^|,|,)(LFLIST|LF)(\d+)(,|,|$)/);
if (lflistParam) {
hostinfo.lflist = Number.parseInt(lflistParam[3], 10) - 1;
}
if (/(^|,|,)(NOLFLIST|NF)(,|,|$)/.test(rule)) {
hostinfo.lflist = -1;
}
if (/(^|,|,)(NOUNIQUE|NU)(,|,|$)/.test(rule)) {
hostinfo.rule = 4;
}
if (/(^|,|,)(CUSTOM|DIY)(,|,|$)/.test(rule)) {
hostinfo.rule = 3;
}
if (/(^|,|,)(NOCHECK|NC)(,|,|$)/.test(rule)) {
hostinfo.no_check_deck = 1;
}
if (/(^|,|,)(NOSHUFFLE|NS)(,|,|$)/.test(rule)) {
hostinfo.no_shuffle_deck = 1;
}
if (/(^|,|,)(IGPRIORITY|PR)(,|,|$)/.test(rule)) {
hostinfo.duel_rule = 4;
}
const duelRuleParam = rule.match(/(^|,|,)(DUELRULE|MR)(\d+)(,|,|$)/);
if (duelRuleParam) {
const duelRule = Number.parseInt(duelRuleParam[3], 10);
if (duelRule > 0 && duelRule <= 5) {
hostinfo.duel_rule = duelRule;
if (stopOnMatch && matched) {
return true;
}
}
return hostinfo;
return matched;
}
private normalizeRoomModePattern(pattern: string): string {
if (
pattern.startsWith('^') ||
pattern.endsWith('$') ||
pattern.includes('(^|,|,') ||
pattern.includes('(,|,|$)')
) {
return pattern;
}
return `(?:^|,|,)${pattern}(?:,|,|$)`;
}
private trimRegexResult(regexResult: RegExpMatchArray): RegExpMatchArray {
const trimmed = regexResult.slice(1) as RegExpMatchArray;
trimmed.index = regexResult.index;
trimmed.input = regexResult.input;
trimmed.groups = regexResult.groups;
return trimmed;
}
private registerBuiltInRoomModes(): void {
this.registerRoomMode('(M|MATCH)', ({ hostinfo }) => ({
mode: setWinMatchCountBits(hostinfo.mode, 2),
}))
.registerRoomMode('(T|TAG)', ({ hostinfo, defaultHostinfo }) => ({
mode: setTagBit(hostinfo.mode, true),
start_lp: defaultHostinfo.start_lp * 2,
}))
.registerRoomMode('BO(\\d+)', ({ regexResult, hostinfo }) => {
const bo = Number.parseInt(regexResult[0], 10);
if (Number.isNaN(bo) || bo <= 0 || bo % 2 !== 1) {
return {};
}
return {
mode: setWinMatchCountBits(hostinfo.mode, (bo + 1) / 2),
};
})
.registerRoomMode('(TCGONLY|TO)', { rule: 1 })
.registerRoomMode('(OCGONLY|OO)', {
rule: 0,
lflist: 0,
})
.registerRoomMode('(OT|TCG)', { rule: 5 })
.registerRoomMode('(SC|CN|CCG|CHINESE)', {
rule: 2,
lflist: -1,
})
.registerRoomMode('LP(\\d+)', ({ regexResult }) => {
let startLp = Number.parseInt(regexResult[0], 10);
if (startLp <= 0) startLp = 1;
if (startLp >= 99999) startLp = 99999;
return { start_lp: startLp };
})
.registerRoomMode('(TIME|TM|TI)(\\d+)', ({ regexResult }) => {
let timeLimit = Number.parseInt(regexResult[1], 10);
if (timeLimit < 0) timeLimit = 180;
if (timeLimit >= 1 && timeLimit <= 60) timeLimit *= 60;
if (timeLimit >= 999) timeLimit = 999;
return { time_limit: timeLimit };
})
.registerRoomMode('(START|ST)(\\d+)', ({ regexResult }) => {
let startHand = Number.parseInt(regexResult[1], 10);
if (startHand <= 0) startHand = 1;
if (startHand >= 40) startHand = 40;
return { start_hand: startHand };
})
.registerRoomMode('(DRAW|DR)(\\d+)', ({ regexResult }) => {
let drawCount = Number.parseInt(regexResult[1], 10);
if (drawCount >= 35) drawCount = 35;
return { draw_count: drawCount };
})
.registerRoomMode('(LFLIST|LF)(\\d+)', ({ regexResult }) => ({
lflist: Number.parseInt(regexResult[1], 10) - 1,
}))
.registerRoomMode('(NOLFLIST|NF)', {
lflist: -1,
})
.registerRoomMode('(NOUNIQUE|NU)', {
rule: 4,
})
.registerRoomMode('(CUSTOM|DIY)', {
rule: 3,
})
.registerRoomMode('(NOCHECK|NC)', {
no_check_deck: 1,
})
.registerRoomMode('(NOSHUFFLE|NS)', {
no_shuffle_deck: 1,
})
.registerRoomMode('(IGPRIORITY|PR)', {
duel_rule: 4,
})
.registerRoomMode('(DUELRULE|MR)(\\d+)', ({ regexResult }) => {
const duelRule = Number.parseInt(regexResult[1], 10);
if (duelRule <= 0 || duelRule > 5) {
return {};
}
return { duel_rule: duelRule };
});
}
}
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