Commit 1b386346 authored by nanahira's avatar nanahira

handle multiple ocgcore process messages

parent ba2fb78c
......@@ -15,7 +15,7 @@
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"ipaddr.js": "^2.3.0",
"koishipro-core.js": "^1.3.3",
"koishipro-core.js": "^1.3.4",
"nfkit": "^1.0.29",
"p-queue": "6.6.2",
"pino": "^10.3.1",
......@@ -28,7 +28,7 @@
"ygopro-cdb-encode": "^1.0.2",
"ygopro-deck-encode": "^1.0.15",
"ygopro-lflist-encode": "^1.0.3",
"ygopro-msg-encode": "^1.1.18",
"ygopro-msg-encode": "^1.1.20",
"ygopro-yrp-encode": "^1.0.1",
"yuzuthread": "^1.0.8"
},
......@@ -5011,9 +5011,9 @@
}
},
"node_modules/koishipro-core.js": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/koishipro-core.js/-/koishipro-core.js-1.3.3.tgz",
"integrity": "sha512-7CrTOJj3+AIZ4QilYgEqK15SoremQed+HH5mB5wqX5O/ocS+fgensCtSmXtoF/F8Frqq/SCXv5yYTQVH0nJHrQ==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/koishipro-core.js/-/koishipro-core.js-1.3.4.tgz",
"integrity": "sha512-3KCW5JwxH9kvXGYtq1Pr5egwgDCX7UIWIyVipHX+idxz++PDMSLPcUDeXi5urTmc1KjvV2RB+z42pqwaG7oscQ==",
"license": "MIT",
"dependencies": {
"@types/emscripten": "^1.41.5",
......@@ -5023,7 +5023,7 @@
"nfkit": "^1.0.14",
"sql.js": "^1.13.0",
"ygopro-cdb-encode": "^1.0.2",
"ygopro-msg-encode": "^1.1.10",
"ygopro-msg-encode": "^1.1.20",
"ygopro-yrp-encode": "^1.0.1"
}
},
......@@ -7296,9 +7296,9 @@
}
},
"node_modules/ygopro-msg-encode": {
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/ygopro-msg-encode/-/ygopro-msg-encode-1.1.18.tgz",
"integrity": "sha512-zfgHNO7ULeqh/fe6cXIEkmtE2jiLkV10kTSCnHnozAqeYo6awBuZSvdXzwCi8DHxmn+dphoLb92xLKbXgkIKBg==",
"version": "1.1.20",
"resolved": "https://registry.npmjs.org/ygopro-msg-encode/-/ygopro-msg-encode-1.1.20.tgz",
"integrity": "sha512-fP5NHM6WAfFDgCtUQmMdkDUCw/+AU8U0F2JxqLTT9zvuptskGFD3LzqTifqD2ffJ8henQb4YBkfi5T4tiLpBaQ==",
"license": "MIT",
"dependencies": {
"typed-reflector": "^1.0.14",
......
......@@ -46,8 +46,13 @@ interface SerializableProcessResult {
length: number;
raw: Uint8Array;
status: number;
encodeError?: string;
}
export type OcgcoreProcessResultWithEncodeError = OcgcoreProcessResult & {
encodeError?: string;
};
interface SerializableCardQueryResult {
length: number;
raw: Uint8Array;
......@@ -211,7 +216,10 @@ export class OcgcoreWorker {
// Wrapper methods for OcgcoreDuel
@WorkerMethod()
@TransportEncoder<OcgcoreProcessResult, SerializableProcessResult>(
@TransportEncoder<
OcgcoreProcessResultWithEncodeError,
SerializableProcessResult
>(
// serialize in worker: only send raw
(result) => ({
length: result.length,
......@@ -219,26 +227,73 @@ export class OcgcoreWorker {
status: result.status,
}),
// deserialize in main thread: re-parse from raw
(serialized) => ({
length: serialized.length,
raw: serialized.raw,
status: serialized.status,
message:
serialized.raw.length > 0
? (() => {
try {
return YGOProMessages.getInstanceFromPayload(serialized.raw);
} catch {
return undefined;
}
})()
: undefined,
}),
(serialized) => {
let message;
let messages;
let encodeError: string | undefined;
if (serialized.raw.length > 0) {
try {
messages = YGOProMessages.getInstancesFromPayload(serialized.raw);
message = messages[messages.length - 1];
if (!messages.length) {
encodeError = 'failed to decode any game messages';
} else {
const consumed = messages.reduce(
(sum, msg) => sum + msg.toPayload().length,
0,
);
if (consumed < serialized.raw.length) {
const nextIdentifier = serialized.raw[consumed];
encodeError =
`decoded ${messages.length} message(s) but left trailing bytes: ` +
`total=${serialized.raw.length}, consumed=${consumed}, nextIdentifier=${nextIdentifier ?? 'n/a'}`;
}
}
} catch (error) {
message = undefined;
messages = undefined;
encodeError =
error instanceof Error ? error.message : String(error);
}
}
return {
length: serialized.length,
raw: serialized.raw,
status: serialized.status,
message,
messages,
encodeError,
};
},
)
async process(): Promise<OcgcoreProcessResult> {
async process(): Promise<OcgcoreProcessResultWithEncodeError> {
return this.duel.process({ noParse: true });
}
private splitProcessResult(
res: OcgcoreProcessResultWithEncodeError,
): OcgcoreProcessResultWithEncodeError[] {
if (!res.messages || res.messages.length <= 1) {
return [res];
}
const messageCount = res.messages.length;
return res.messages.map((message, index) => {
const raw = message.toPayload();
return {
...res,
length: raw.length,
raw,
status: index === messageCount - 1 ? res.status : 0,
message,
messages: [message],
encodeError: index === messageCount - 1 ? res.encodeError : undefined,
};
});
}
@WorkerMethod()
async setResponseInt(@TransportType() value: number) {
this.duel.setResponseInt(value);
......@@ -317,26 +372,28 @@ export class OcgcoreWorker {
return this.duel.queryFieldInfo({ noParse: true });
}
async *advance() {
async *advance(): AsyncGenerator<OcgcoreProcessResultWithEncodeError> {
while (true) {
const res = await this.process();
const processedResults = this.splitProcessResult(await this.process());
if (res.raw.length === 0) {
continue;
}
for (const res of processedResults) {
if (res.raw.length === 0) {
continue;
}
yield res;
yield res;
if (res.status === 2) {
break;
}
if (res.status === 2) {
return;
}
if (res.message instanceof YGOProMsgRetry) {
break;
}
if (res.message instanceof YGOProMsgRetry) {
return;
}
if (res.message instanceof YGOProMsgResponseBase) {
break;
if (res.message instanceof YGOProMsgResponseBase) {
return;
}
}
}
}
......
......@@ -1607,7 +1607,17 @@ export class Room {
}
try {
for await (const { status, message } of this.ocgcore.advance()) {
for await (const {
status,
message,
encodeError,
} of this.ocgcore.advance()) {
if (encodeError) {
this.logger.warn(
{ encodeError, status },
'Failed to decode game message in worker transport',
);
}
if (!message) {
this.logger.warn({ message }, 'Received empty message from ocgcore');
if (status) {
......
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