Commit 1b386346 authored by nanahira's avatar nanahira

handle multiple ocgcore process messages

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