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