Commit b2c66f0a authored by purerosefallen's avatar purerosefallen

refa hostinfo

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