Added rollWorker so that multiple dice rolls can happen simultaneously

This commit is contained in:
Ean Milligan (Bastion) 2022-05-28 03:37:11 -04:00
parent 7391a0fd68
commit 1f4d1e3ef6
5 changed files with 140 additions and 45 deletions

View File

@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"allowJs": true, "allowJs": true,
"lib": ["deno.window"], "lib": ["deno.worker"],
"strict": true "strict": true
}, },
"lint": { "lint": {

View File

@ -12,6 +12,7 @@ import {
} from '../../deps.ts'; } from '../../deps.ts';
import solver from '../solver/_index.ts'; import solver from '../solver/_index.ts';
import { SolvedRoll } from '../solver/solver.d.ts'; import { SolvedRoll } from '../solver/solver.d.ts';
import { RollModifiers } from '../mod.d.ts';
import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed, infoColor1, warnColor } from '../commandUtils.ts'; import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed, infoColor1, warnColor } from '../commandUtils.ts';
import rollFuncs from './roll/_index.ts'; import rollFuncs from './roll/_index.ts';
@ -59,60 +60,92 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
// Rejoin all of the args and send it into the solver, if solver returns a falsy item, an error object will be substituded in // Rejoin all of the args and send it into the solver, if solver returns a falsy item, an error object will be substituded in
const rollCmd = `${command} ${args.join(' ')}`; const rollCmd = `${command} ${args.join(' ')}`;
const returnmsg = solver.parseRoll(rollCmd, modifiers) || <SolvedRoll> { error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message' }; // const returnmsg = solver.parseRoll(rollCmd, modifiers) || <SolvedRoll>{ error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message' };
const pubEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, modifiers); const rollWorker = new Worker(new URL('../solver/rollWorker.ts', import.meta.url).href, { type: 'module' });
const gmEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, gmModifiers);
const countEmbed = generateCountDetailsEmbed(returnmsg.counts);
// If there was an error, report it to the user in hopes that they can determine what they did wrong const workerTimeout = setTimeout(async () => {
if (returnmsg.error) { rollWorker.terminate();
m.edit({ embeds: [pubEmbedDetails.embed] }); m.edit({
embeds: [
(await generateRollEmbed(
message.authorId,
<SolvedRoll>{
error: true,
errorCode: 'TooComplex',
errorMsg: 'Error: Roll Too Complex, try breaking roll down into simpler parts',
},
<RollModifiers>{},
)).embed,
],
});
}, 60000);
if (DEVMODE && config.logRolls) { rollWorker.postMessage({
// If enabled, log rolls so we can see what went wrong rollCmd,
dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, returnmsg.errorCode, m.id]).catch((e) => { modifiers,
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); });
});
}
} else {
// Determine if we are to send a GM roll or a normal roll
if (modifiers.gmRoll) {
// Send the public embed to correct channel
m.edit({ embeds: [pubEmbedDetails.embed] });
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged rollWorker.addEventListener('message', async (e) => {
modifiers.gms.forEach(async (gm) => { try {
log(LT.LOG, `Messaging GM ${gm}`); clearTimeout(workerTimeout);
// Attempt to DM the GM and send a warning if it could not DM a GM const returnmsg = e.data;
await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), { const pubEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, modifiers);
embeds: modifiers.count ? [gmEmbedDetails.embed, countEmbed] : [gmEmbedDetails.embed], const gmEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, gmModifiers);
}).then(async () => { const countEmbed = generateCountDetailsEmbed(returnmsg.counts);
// Check if we need to attach a file and send it after the initial details sent
if (gmEmbedDetails.hasAttachment) { // If there was an error, report it to the user in hopes that they can determine what they did wrong
if (returnmsg.error) {
m.edit({ embeds: [pubEmbedDetails.embed] });
if (DEVMODE && config.logRolls) {
// If enabled, log rolls so we can see what went wrong
dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, returnmsg.errorCode, m.id]).catch((e) => {
log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`);
});
}
} else {
// Determine if we are to send a GM roll or a normal roll
if (modifiers.gmRoll) {
// Send the public embed to correct channel
m.edit({ embeds: [pubEmbedDetails.embed] });
// And message the full details to each of the GMs, alerting roller of every GM that could not be messaged
modifiers.gms.forEach(async (gm) => {
log(LT.LOG, `Messaging GM ${gm}`);
// Attempt to DM the GM and send a warning if it could not DM a GM
await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), { await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), {
file: gmEmbedDetails.attachment, embeds: modifiers.count ? [gmEmbedDetails.embed, countEmbed] : [gmEmbedDetails.embed],
}).then(async () => {
// Check if we need to attach a file and send it after the initial details sent
if (gmEmbedDetails.hasAttachment) {
await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), {
file: gmEmbedDetails.attachment,
}).catch(() => {
message.reply(generateDMFailed(gm));
});
}
}).catch(() => { }).catch(() => {
message.reply(generateDMFailed(gm)); message.reply(generateDMFailed(gm));
}); });
});
} else {
// Not a gm roll, so just send normal embed to correct channel
const n = await m.edit({
embeds: modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed],
});
if (pubEmbedDetails.hasAttachment) {
// Attachment requires you to send a new message
n.reply({
file: pubEmbedDetails.attachment,
});
} }
}).catch(() => { }
message.reply(generateDMFailed(gm));
});
});
} else {
// Not a gm roll, so just send normal embed to correct channel
await m.edit({
embeds: modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed],
});
if (pubEmbedDetails.hasAttachment) {
// Attachment requires you to send a new message
message.send({
file: pubEmbedDetails.attachment,
});
} }
} catch (e) {
log(LT.ERROR, `Unddandled Error: ${JSON.stringify(e)}`);
} }
} });
} catch (e) { } catch (e) {
log(LT.ERROR, `Undandled Error: ${JSON.stringify(e)}`); log(LT.ERROR, `Undandled Error: ${JSON.stringify(e)}`);
} }

View File

@ -17,6 +17,19 @@ import { fullSolver } from './solver.ts';
export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => { export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => {
const returnmsg = <SolvedRoll> { const returnmsg = <SolvedRoll> {
error: false, error: false,
errorCode: '',
errorMsg: '',
line1: '',
line2: '',
line3: '',
counts: {
total: 0,
successful: 0,
failed: 0,
rerolled: 0,
dropped: 0,
exploded: 0,
},
}; };
// Whole function lives in a try-catch to allow safe throwing of errors on purpose // Whole function lives in a try-catch to allow safe throwing of errors on purpose
@ -25,7 +38,14 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
const sepRolls = fullCmd.split(config.prefix); const sepRolls = fullCmd.split(config.prefix);
const tempReturnData: ReturnData[] = []; const tempReturnData: ReturnData[] = [];
const tempCountDetails: CountDetails[] = []; const tempCountDetails: CountDetails[] = [{
total: 0,
successful: 0,
failed: 0,
rerolled: 0,
dropped: 0,
exploded: 0,
}];
// Loop thru all roll/math ops // Loop thru all roll/math ops
for (const sepRoll of sepRolls) { for (const sepRoll of sepRolls) {
@ -75,6 +95,21 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
containsCrit: false, containsCrit: false,
containsFail: false, containsFail: false,
}; };
} else if (mathConf[i].toString().toLowerCase() === 'fart' || mathConf[i].toString().toLowerCase() === '💩') {
mathConf[i] = {
total: 7,
details: '💩',
containsCrit: false,
containsFail: false,
};
} else if (mathConf[i].toString().toLowerCase() === 'inf' || mathConf[i].toString().toLowerCase() === 'infinity' || mathConf[i].toString().toLowerCase() === '∞') {
// If the operand is the constant Infinity, create a SolvedStep for it
mathConf[i] = {
total: Infinity,
details: '∞',
containsCrit: false,
containsFail: false,
};
} else if (mathConf[i].toString().toLowerCase() === 'pi' || mathConf[i].toString().toLowerCase() === '𝜋') { } else if (mathConf[i].toString().toLowerCase() === 'pi' || mathConf[i].toString().toLowerCase() === '𝜋') {
// If the operand is the constant pi, create a SolvedStep for it // If the operand is the constant pi, create a SolvedStep for it
mathConf[i] = { mathConf[i] = {

23
src/solver/rollWorker.ts Normal file
View File

@ -0,0 +1,23 @@
import { parseRoll } from './parser.ts';
self.onmessage = async (e: any) => {
const payload = e.data;
const returnmsg = parseRoll(payload.rollCmd, payload.modifiers) || {
error: true,
errorCode: 'EmptyMessage',
errorMsg: 'Error: Empty message',
line1: '',
line2: '',
line3: '',
counts: {
total: 0,
successful: 0,
failed: 0,
rerolled: 0,
dropped: 0,
exploded: 0,
},
};
self.postMessage(returnmsg);
self.close();
};

View File

@ -83,6 +83,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
rollConf.dieSize = parseInt(remains.slice(0, afterDieIdx)); rollConf.dieSize = parseInt(remains.slice(0, afterDieIdx));
remains = remains.slice(afterDieIdx); remains = remains.slice(afterDieIdx);
if (!rollConf.dieCount || !rollConf.dieSize) {
throw new Error('YouNeedAD');
}
log(LT.LOG, `Handling roll ${rollStr} | Parsed Die Count: ${rollConf.dieCount}`); log(LT.LOG, `Handling roll ${rollStr} | Parsed Die Count: ${rollConf.dieCount}`);
log(LT.LOG, `Handling roll ${rollStr} | Parsed Die Size: ${rollConf.dieSize}`); log(LT.LOG, `Handling roll ${rollStr} | Parsed Die Size: ${rollConf.dieSize}`);