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...) private applyRoomModes(
if (!Number.isNaN(bo) && bo > 0 && bo % 2 === 1) { source: string,
hostinfo.mode = setWinMatchCountBits(hostinfo.mode, (bo + 1) / 2); 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)) { if (stopOnMatch && matched) {
hostinfo.rule = 0; return true;
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;
} }
} }
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