Commit 76b99077 authored by nanahira's avatar nanahira

wip

parent 9a20f3ef
...@@ -42,3 +42,4 @@ Dockerfile ...@@ -42,3 +42,4 @@ Dockerfile
.dockerignore .dockerignore
/tests /tests
/ssl /ssl
/ygopro
...@@ -37,3 +37,4 @@ lerna-debug.log* ...@@ -37,3 +37,4 @@ lerna-debug.log*
/output /output
/config.yaml /config.yaml
/ssl /ssl
/ygopro
...@@ -16,6 +16,7 @@ import { ...@@ -16,6 +16,7 @@ import {
firstValueFrom, firstValueFrom,
merge, merge,
map, map,
take,
} from 'rxjs'; } from 'rxjs';
import { YGOProCtosDisconnect } from '../utility/ygopro-ctos-disconnect'; import { YGOProCtosDisconnect } from '../utility/ygopro-ctos-disconnect';
...@@ -52,8 +53,8 @@ export class ClientHandler { ...@@ -52,8 +53,8 @@ export class ClientHandler {
YGOProCtosExternalAddress, YGOProCtosExternalAddress,
YGOProCtosPlayerInfo, YGOProCtosPlayerInfo,
YGOProCtosJoinGame, YGOProCtosJoinGame,
].some((allowed) => msg instanceof allowed); ].some((allowed) => (msg instanceof allowed) as boolean);
if (client.established !== isPreHandshakeMsg) { if (client.established === isPreHandshakeMsg) {
// disallow any messages before handshake is complete, except for the ones needed for handshake // disallow any messages before handshake is complete, except for the ones needed for handshake
return undefined; return undefined;
} }
...@@ -85,7 +86,7 @@ export class ClientHandler { ...@@ -85,7 +86,7 @@ export class ClientHandler {
await this.ctx.dispatch(msg, client); await this.ctx.dispatch(msg, client);
} catch (e) { } catch (e) {
this.logger.warn( this.logger.warn(
`Error dispatching message ${msg.constructor.name} from ${client.loggingIp()}: ${(e as Error).message}`, `Error dispatching message ${msg.constructor.name} from ${client.loggingIp()}: ${(e as Error).stack}`,
); );
} }
}); });
...@@ -93,16 +94,19 @@ export class ClientHandler { ...@@ -93,16 +94,19 @@ export class ClientHandler {
const handshake$ = forkJoin([ const handshake$ = forkJoin([
client.receive$.pipe( client.receive$.pipe(
filter((msg) => msg instanceof YGOProCtosPlayerInfo), filter((msg) => msg instanceof YGOProCtosPlayerInfo),
take(1),
takeUntil(client.disconnect$), takeUntil(client.disconnect$),
), ),
client.receive$.pipe( client.receive$.pipe(
filter((msg) => msg instanceof YGOProCtosJoinGame), filter((msg) => msg instanceof YGOProCtosJoinGame),
take(1),
takeUntil(client.disconnect$), takeUntil(client.disconnect$),
), ),
]).pipe(timeout(5000), takeUntil(client.disconnect$)); ]).pipe(timeout(5000), takeUntil(client.disconnect$));
firstValueFrom(handshake$) firstValueFrom(handshake$)
.then(() => { .then(() => {
this.logger.debug({ client: client.name }, 'Handshake completed');
client.established = true; client.established = true;
}) })
.catch(() => { .catch(() => {
......
...@@ -3,11 +3,13 @@ import { Context } from '../app'; ...@@ -3,11 +3,13 @@ import { Context } from '../app';
import { RoomManager } from '../room/room-manager'; import { RoomManager } from '../room/room-manager';
export class JoinRoom { export class JoinRoom {
private logger = this.ctx.createLogger(this.constructor.name);
constructor(private ctx: Context) { constructor(private ctx: Context) {
this.ctx.middleware(YGOProCtosJoinGame, async (msg, client, next) => { this.ctx.middleware(YGOProCtosJoinGame, async (msg, client, next) => {
if (!msg.pass) { if (!msg.pass) {
return next(); return next();
} }
this.logger.debug({ name: client.name, pass: msg.pass }, 'Joining room');
const roomManager = this.ctx.get(() => RoomManager); const roomManager = this.ctx.get(() => RoomManager);
const room = await roomManager.findOrCreateByName(msg.pass); const room = await roomManager.findOrCreateByName(msg.pass);
return room.join(client); return room.join(client);
......
...@@ -10,10 +10,12 @@ const REPLAY_ID_YRP2 = 0x32707279; ...@@ -10,10 +10,12 @@ const REPLAY_ID_YRP2 = 0x32707279;
const PRO_VERSION = 0x1362; const PRO_VERSION = 0x1362;
export class DuelRecord { export class DuelRecord {
seed: number[]; constructor(
players: { name: string; deck: YGOProDeck }[]; public seed: number[],
public players: { name: string; deck: YGOProDeck }[],
) {}
winPosition?: number; winPosition?: number;
responses: Buffer[]; responses: Buffer[] = [];
toYrp(room: Room) { toYrp(room: Room) {
const isTag = room.isTag; const isTag = room.isTag;
......
...@@ -27,6 +27,8 @@ import { ...@@ -27,6 +27,8 @@ import {
YGOProStocDuelEnd, YGOProStocDuelEnd,
YGOProStocChangeSide, YGOProStocChangeSide,
YGOProStocWaitingSide, YGOProStocWaitingSide,
YGOProCtosTpResult,
TurnPlayerResult,
} from 'ygopro-msg-encode'; } from 'ygopro-msg-encode';
import { DefaultHostInfoProvider } from './default-hostinfo-provder'; import { DefaultHostInfoProvider } from './default-hostinfo-provder';
import { CardReaderFinalized } from 'koishipro-core.js'; import { CardReaderFinalized } from 'koishipro-core.js';
...@@ -45,11 +47,12 @@ import { OnRoomLeavePlayer } from './room-event/on-room-leave-player'; ...@@ -45,11 +47,12 @@ import { OnRoomLeavePlayer } from './room-event/on-room-leave-player';
import { OnRoomLeaveObserver } from './room-event/on-room-leave-observer'; import { OnRoomLeaveObserver } from './room-event/on-room-leave-observer';
import { OnRoomMatchStart } from './room-event/on-room-match-start'; import { OnRoomMatchStart } from './room-event/on-room-match-start';
import { OnRoomGameStart } from './room-event/on-room-game-start'; import { OnRoomGameStart } from './room-event/on-room-game-start';
// import { OnRoomDuelStart } from './room-event/on-room-duel-start'; // 备用事件,暂未使用
import YGOProDeck from 'ygopro-deck-encode'; import YGOProDeck from 'ygopro-deck-encode';
import { checkDeck, checkChangeSide } from '../utility/check-deck'; import { checkDeck, checkChangeSide } from '../utility/check-deck';
import { DuelRecord } from './duel-record'; import { DuelRecord } from './duel-record';
import { last } from 'rxjs'; import { RoomEvent } from './room-event/room-event';
import { generateSeed } from '../utility/generate-seed';
import { OnRoomDuelStart } from './room-event/on-room-duel-start';
export type RoomFinalizor = (self: Room) => Awaitable<any>; export type RoomFinalizor = (self: Room) => Awaitable<any>;
...@@ -336,6 +339,7 @@ export class Room { ...@@ -336,6 +339,7 @@ export class Room {
.forEach((p) => p.send(new YGOProStocDuelStart())); .forEach((p) => p.send(new YGOProStocDuelStart()));
} }
const duelPos = this.getSwappedDuelPosByDuelPos(winMsg.player!); const duelPos = this.getSwappedDuelPosByDuelPos(winMsg.player!);
this.isPosSwapped = false;
await Promise.all( await Promise.all(
this.allPlayers.map((p) => this.allPlayers.map((p) =>
p.send( p.send(
...@@ -513,15 +517,21 @@ export class Room { ...@@ -513,15 +517,21 @@ export class Room {
const oldPos = client.pos; const oldPos = client.pos;
// 从当前位置的下一个位置开始循环查找空位
let nextPos = (oldPos + 1) % 4;
while (this.players[nextPos]) {
nextPos = (nextPos + 1) % 4;
}
// 移动到新位置 // 移动到新位置
this.players[oldPos] = undefined; this.players[oldPos] = undefined;
this.players[firstEmptyPlayerSlot] = client; this.players[nextPos] = client;
client.pos = firstEmptyPlayerSlot; client.pos = nextPos;
// 发送 PlayerChange 给所有人 // 发送 PlayerChange 给所有人
const changeMsg = new YGOProStocHsPlayerChange().fromPartial({ const changeMsg = new YGOProStocHsPlayerChange().fromPartial({
playerPosition: oldPos, playerPosition: oldPos,
playerState: firstEmptyPlayerSlot, playerState: nextPos,
}); });
this.allPlayers.forEach((p) => p.send(changeMsg)); this.allPlayers.forEach((p) => p.send(changeMsg));
...@@ -713,7 +723,7 @@ export class Room { ...@@ -713,7 +723,7 @@ export class Room {
if (![DuelStage.Finger, DuelStage.Siding].includes(this.duelStage)) { if (![DuelStage.Finger, DuelStage.Siding].includes(this.duelStage)) {
return false; return false;
} }
if (this.winnerPositions.length === 0) { if (this.duelRecords.length === 0) {
this.allPlayers.forEach((p) => p.send(new YGOProStocDuelStart())); this.allPlayers.forEach((p) => p.send(new YGOProStocDuelStart()));
const displayCountDecks = [0, 1].map( const displayCountDecks = [0, 1].map(
(p) => this.getDuelPosPlayers(p)[0].deck!, (p) => this.getDuelPosPlayers(p)[0].deck!,
...@@ -754,7 +764,7 @@ export class Room { ...@@ -754,7 +764,7 @@ export class Room {
} }
// 触发事件 // 触发事件
if (this.winnerPositions.length === 0) { if (this.duelRecords.length === 0) {
// 触发比赛开始事件(第一局) // 触发比赛开始事件(第一局)
await this.ctx.dispatch( await this.ctx.dispatch(
new OnRoomMatchStart(this), new OnRoomMatchStart(this),
...@@ -767,4 +777,37 @@ export class Room { ...@@ -767,4 +777,37 @@ export class Room {
return true; return true;
} }
@RoomMethod()
private async onDuelStart(client: Client, msg: YGOProCtosTpResult) {
if (this.duelStage !== DuelStage.FirstGo) {
return;
}
if (client !== this.firstgoPlayer) {
return;
}
this.isPosSwapped =
(msg.res === TurnPlayerResult.FIRST) !== (this.getDuelPos(client) === 0);
const duelRecord = new DuelRecord(
generateSeed(),
this.playingPlayers.map((p) => ({ name: p.name, deck: p.deck! })),
);
if (this.isPosSwapped) {
this.playingPlayers.forEach((p) => {
duelRecord.players[this.getSwappedDuelPos(p)] = {
name: p.name,
deck: p.deck!,
};
});
}
this.duelRecords.push(duelRecord);
await this.ctx.dispatch(
new OnRoomDuelStart(this),
this.getDuelPosPlayers(this.getSwappedDuelPos(0))[0],
);
this.allPlayers.forEach((p) => p.sendChat('TODO: duel start'));
return this.finalize();
}
} }
...@@ -13,7 +13,13 @@ export class YGOProResourceLoader { ...@@ -13,7 +13,13 @@ export class YGOProResourceLoader {
]); ]);
extraScriptPaths = loadPaths(this.ctx.getConfig('EXTRA_SCRIPT_PATH')); extraScriptPaths = loadPaths(this.ctx.getConfig('EXTRA_SCRIPT_PATH'));
private logger = this.ctx.createLogger(this.constructor.name);
async getCardReader() { async getCardReader() {
this.logger.debug(
{ ygoproPaths: this.ygoproPaths, sql: typeof this.ctx.SQL.Database },
'Getting card reader',
);
return DirCardReader(this.ctx.SQL, ...this.ygoproPaths); return DirCardReader(this.ctx.SQL, ...this.ygoproPaths);
} }
......
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