Commit 5e48de4d authored by nanahira's avatar nanahira

Merge branch 'v3-refactor3' into v3.1-refresh-ygopro

parents 4966752b 5b96a05c
......@@ -4,6 +4,7 @@ import ini from 'ini';
import fs from 'fs';
import child_process from 'child_process';
import Mustache from 'mustache';
import _ from 'lodash-es';
export enum Category {
game,
......@@ -68,7 +69,9 @@ export class AppStatus {
export interface YGOProDistroData {
deckPath: string;
replayPath: string;
systemConf: string;
systemConf?: string;
lastDeckFormat?: string;
lastCategoryFormat?: string;
}
export class App {
......@@ -366,24 +369,24 @@ export class App {
return this.data.ygopro || undefined;
}
get ygoproDeckPath(): string | undefined {
const distroData = this.ygoproDistroData;
ygoproDeckPath(_distroData: YGOProDistroData): string | undefined {
const distroData = _distroData || this.ygoproDistroData;
if (!distroData) {
return;
}
return path.join(this.local!.path, distroData.deckPath);
}
get ygoproReplayPath(): string | undefined {
const distroData = this.ygoproDistroData;
ygoproReplayPath(_distroData: YGOProDistroData): string | undefined {
const distroData = _distroData || this.ygoproDistroData;
if (!distroData || !distroData.replayPath) {
return;
}
return path.join(this.local!.path, distroData.replayPath);
}
get systemConfPath(): string | undefined {
const distroData = this.ygoproDistroData;
systemConfPath(_distroData: YGOProDistroData): string | undefined {
const distroData = _distroData || this.ygoproDistroData;
if (!distroData || !distroData.systemConf) {
return;
}
......
......@@ -26,16 +26,13 @@
</div>
<div class="col-sm-8 input-group input-group-sm">
<label i18n class="input-group-text" id="basic-addon1">卡组</label>
<select class="form-select form-select-sm" id="exampleSelect1" name="deck" [(ngModel)]="current_deck">
<option *ngFor="let deck of decks_grouped['.']" [value]="deck">{{deck}}</option>
<ng-container *ngFor="let group of decks_grouped | keyvalue">
<optgroup *ngIf="group.key !== '.'" [label]='group.key'>
<option *ngFor="let deck of group.value" [value]="group.key + '/' + deck">{{deck}}</option>
<select class="form-select form-select-sm" id="exampleSelect1" name="deck" [(ngModel)]="currentDeckSymbol" (change)="onDeckChange()">
<optgroup *ngFor="let group of decks_grouped" [label]="group[0] === '.' ? '未分类卡组' : group[0]">
<option *ngFor="let deck of group[1]" [value]="deck.symbol">{{deck.deck}}</option>
</optgroup>
</ng-container>
</select>
<span class="input-group-btn">
<button id="edit_deck_button" i18n [disabled]="!appsService.allReady(app)" class="btn btn-secondary btn-sm" (click)="edit_deck(current_deck)">编辑</button>
<button id="edit_deck_button" i18n [disabled]="!appsService.allReady(app)" class="btn btn-secondary btn-sm" (click)="edit_deck()">编辑</button>
</span>
</div>
</div>
......@@ -124,10 +121,10 @@
<small i18n class="form-text text-muted">最多 12 个字</small>
</ng-container>
<ng-template #private>
<label *ngIf="room.private" for="game-create-title"><i class="fa fa-key" aria-hidden="true"></i>
<label *ngIf="room.private" for="game-create-password"><i class="fa fa-key" aria-hidden="true"></i>
<span i18n>房间密码</span></label>
<div class="input-group input-group-sm">
<input type="text" maxlength="12" class="form-control" id="game-create-title" name="title" [(ngModel)]="host_password" readonly>
<input type="text" maxlength="12" class="form-control" id="game-create-password" name="title" [(ngModel)]="host_password" readonly>
<span i18n-title id="copy-wrapper" class="input-group-btn" data-bs-toggle="tooltip" title="房间密码已复制到剪贴板">
<button i18n-title class="btn btn-secondary fa fa-clipboard" type="button" title="复制" (click)="copy(host_password, $event)"></button>
</span>
......
......@@ -20,7 +20,7 @@ import * as remote from '@electron/remote';
import fs from 'fs-extra';
// import $ from 'jquery';
import path from 'path';
import { App } from '../shared/app';
import {App, YGOProDistroData} from '../shared/app';
import { AppsService } from '../apps.service';
import { LoginService } from '../login/login.service';
import { SettingsService } from '../settings.service';
......@@ -108,13 +108,6 @@ interface Options {
// ratio: number;
// }
interface YGOProDistroData {
deckPath: string;
replayPath: string;
systemConf?: string;
lastDeckFormat?: string;
}
interface YGOProData {
ygopro: YGOProDistroData;
servers: Server[];
......@@ -124,6 +117,27 @@ let matching: Subscription | undefined;
let matching_arena: string | undefined;
let match_started_at: Date;
export class DeckAndCategory {
category: string;
deck: string;
get param() {
return {
category: this.category === '.' ? '': this.category,
deck: this.deck,
deckPath: this.symbol
}
}
get symbol() {
return path.join(this.category, this.deck);
}
set symbol(str: string) {
this.category = path.dirname(str);
this.deck = path.basename(str, '.ydk');
}
}
@Component({
moduleId: module.id,
selector: 'ygopro',
......@@ -138,10 +152,12 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
@Output()
points: EventEmitter<any> = new EventEmitter();
decks: string[] = [];
decks_grouped: Record<string, string[]> = {};
decks: DeckAndCategory[] = [];
decks_grouped: [string, DeckAndCategory[]][];
replays: string[] = [];
current_deck: string;
currentDeck: DeckAndCategory;
currentDeckSymbol: string;
system_conf?: string;
numfont: string[];
textfont: string[];
......@@ -158,8 +174,8 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
ygoproMatchResultModel: Modal;
// points: Points;
servers: Server[];
selectableServers: Server[];
servers: Server[] = [];
selectableServers: Server[] = [];
// selectingServerId: string;
currentServer: Server;
// tslint:disable-next-line:member-ordering
......@@ -169,6 +185,7 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
this.currentServer = this.servers.find(s => s.id === this.selectingServerId);
}*/
lastDeckFormat?: RegExp;
lastCategoryFormat?: RegExp;
default_options: Options = {
mode: 1,
rule: this.locale.startsWith('zh') ? 0 : 1,
......@@ -320,22 +337,30 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
return ygoproData;
}
ygoproData: YGOProData;
async refreshYGOProData() {
console.log(`Refreshing YGOPro Data.`);
const ygoproData = this.getYGOProData(this.app);
this.servers = ygoproData.servers;
this.ygoproData = this.getYGOProData(this.app);
this.servers = this.ygoproData.servers;
this.selectableServers = this.servers.filter((s) => !s.hidden);
this.currentServer = this.selectableServers[0];
// this.reloadCurrentServer();
if (ygoproData.ygopro.lastDeckFormat) {
if (this.ygoproData.ygopro.lastDeckFormat) {
// console.log(`Deck format pattern: ${ygoproData.ygopro.lastDeckFormat}`)
this.lastDeckFormat = new RegExp(ygoproData.ygopro.lastDeckFormat);
this.lastDeckFormat = new RegExp(this.ygoproData.ygopro.lastDeckFormat);
} else {
this.lastDeckFormat = undefined;
}
this.system_conf = this.app.systemConfPath;
if (this.ygoproData.ygopro.lastCategoryFormat) {
this.lastCategoryFormat = new RegExp(this.ygoproData.ygopro.lastCategoryFormat);
} else {
this.lastCategoryFormat = undefined;
}
this.system_conf = this.app.systemConfPath(this.ygoproData.ygopro);
console.log(`Will load system conf file from ${this.system_conf}`);
await this.refresh(true);
}
......@@ -477,13 +502,15 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
this.watchDropdownMenuShow = false;
}
}
onDeckChange() {
this.currentDeck = this.getDeckObject(this.currentDeckSymbol);
console.log(`Current deck changed to ${this.currentDeck.symbol}`);
}
async refresh(init?: boolean) {
this.decks = await this.get_decks();
this.decks_grouped = _.mapValues(
_.groupBy(this.decks, (p) => path.dirname(p)),
(g) => g.map((p) => path.basename(p, '.ydk'))
);
this.decks_grouped = this.deckGroup();
const allDecks = this.decks;
if (this.lastDeckFormat) {
const systemConfString = await this.load_system_conf();
......@@ -493,22 +520,41 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
const lastDeckMatch = systemConfString.match(this.lastDeckFormat);
if (lastDeckMatch) {
lastDeck = lastDeckMatch[1];
// console.log(`Last deck ${lastDeck} read from ${this.system_conf}.`);
console.log(`Last deck ${lastDeck} read from ${this.system_conf}.`);
} else {
// console.error(`Deck pattern not found from pattern ${this.system_conf}: ${lastDeckMatch}`);
console.error(`Deck pattern not found from pattern ${this.system_conf}: ${lastDeckMatch}`);
}
} else {
// console.error(`System conf ${this.system_conf} not found.`);
console.error(`System conf ${this.system_conf} not found.`);
}
let lastCategory = '.';
if(systemConfString && this.lastCategoryFormat) {
const lastCategoryMatch = systemConfString.match(this.lastCategoryFormat);
if (lastCategoryMatch) {
lastCategory = lastCategoryMatch[1] || '.';
if (lastCategory === '未分类卡组' || lastCategory === '人机卡组') {
lastCategory = '.';
}
console.log(`Last category ${lastCategory} read from ${this.system_conf}.`);
} else {
console.error(`Category pattern not found from pattern ${this.system_conf}: ${lastCategoryMatch}`);
}
}
if (lastDeck && this.decks.includes(lastDeck)) {
// console.log(`Got last deck ${lastDeck}.`);
this.current_deck = lastDeck;
const matchingDeck = allDecks.find(d => d.deck === lastDeck && d.category === lastCategory);
if (matchingDeck) {
console.log(`Got last deck ${matchingDeck.category}/${matchingDeck.deck}.`);
this.currentDeck = matchingDeck;
} else if (init) {
this.current_deck = this.decks[0];
console.log(`Last deck ${lastCategory}/${lastDeck} not found.`);
this.currentDeck = allDecks[0];
}
} else if (init) {
this.currentDeck = allDecks[0];
}
console.log(this.current_deck);
if(this.currentDeck) {
this.currentDeckSymbol = this.currentDeck.symbol;
}
this.replays = await this.get_replays();
......@@ -528,18 +574,29 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
}
}
async get_decks(): Promise<string[]> {
async get_decks(): Promise<DeckAndCategory[]> {
try {
return (await fg('**/*.ydk', { cwd: this.app.ygoproDeckPath })).map((d) => d.slice(0, -4));
return (await fg('**/*.ydk', { cwd: this.app.ygoproDeckPath((this.ygoproData.ygopro)) })).map(path => this.getDeckObject(path));
} catch (error) {
console.error(`Load deck fail: ${error.toString()}`);
return [];
}
}
getDeckObject(deckPath: string) {
const deck = new DeckAndCategory();
deck.symbol = deckPath;
return deck;
}
deckGroup(): [string, DeckAndCategory[]][] {
const categorizedDecks = this.decks;
return Object.entries(_.groupBy(categorizedDecks, d => d.category));
}
async get_replays(): Promise<string[]> {
try {
let files: string[] = await fs.readdir(this.app.ygoproReplayPath!);
let files: string[] = await fs.readdir(this.app.ygoproReplayPath(this.ygoproData.ygopro)!);
return files.filter((file) => path.extname(file) === '.yrp').map((file) => path.basename(file, '.yrp'));
} catch (error) {
console.error(`Load replay fail: ${error.toString()}`);
......@@ -559,7 +616,7 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
async delete_deck(deck: string) {
if (confirm('确认删除?')) {
try {
await fs.unlink(path.join(this.app.ygoproDeckPath!, deck + '.ydk'));
await fs.unlink(path.join(this.app.ygoproDeckPath(this.ygoproData.ygopro)!, deck + '.ydk'));
} catch (error) {
}
return this.refresh();
......@@ -601,32 +658,33 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
return fs.writeFile(this.system_conf, ini.unsafe(ini.stringify(data, <EncodeOptions>{whitespace: true})));
};*/
getDeckAndCategoryParam() {
const deck = this.currentDeck;
console.log(`Deck: ${deck.symbol}`)
return deck.param;
}
async join(name: string, server: Server) {
/*let system_conf = await this.load_system_conf();
await this.fix_fonts(system_conf);
system_conf.lastdeck = this.current_deck;
system_conf.lastdeck = this.currentDeck;
system_conf.lastip = server.address;
system_conf.lasthost = server.address;
system_conf.lastport = server.port.toString();
system_conf.roompass = name;
system_conf.nickname = this.loginService.user.username;
await this.save_system_conf(system_conf);*/
// return this.start_game(['-h', server.address, '-p', server.port.toString(), '-w', name, '-n', this.loginService.user.username, '-d', this.current_deck, '-j']);
return this.start_game('main', {
server,
password: name,
username: this.loginService.user.username,
deck: this.current_deck
});
}
// return this.start_game(['-h', server.address, '-p', server.port.toString(), '-w', name, '-n', this.loginService.user.username, '-d', this.currentDeck, '-j']);
return this.start_game('main', { server, password: name, username: this.loginService.user.username, ...this.getDeckAndCategoryParam() });
};
async edit_deck(deck: string) {
async edit_deck() {
/*let system_conf = await this.load_system_conf();
await this.fix_fonts(system_conf);
system_conf.lastdeck = deck;
await this.save_system_conf(system_conf);*/
// return this.start_game(['-d', deck]);
return this.start_game('deck', { deck });
return this.start_game('deck', this.getDeckAndCategoryParam());
}
async watch_replay(replay: string) {
......@@ -634,7 +692,7 @@ export class YGOProComponent implements OnInit, OnDestroy, AfterViewInit {
await this.fix_fonts(system_conf);
await this.save_system_conf(system_conf);*/
// return this.start_game(['-r', path.join('replay', replay + '.yrp')]);
return this.start_game('replay', { replay: path.join(this.app.ygoproReplayPath!, `${replay}.yrp`) });
return this.start_game('replay', { replay: path.join(this.app.ygoproReplayPath(this.ygoproData.ygopro)!, `${replay}.yrp`) });
}
join_windbot(name?: string) {
......
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