diff --git a/deno.json b/deno.json index df465f1..6716f30 100644 --- a/deno.json +++ b/deno.json @@ -1,7 +1,7 @@ { "compilerOptions": { "allowJs": true, - "lib": ["deno.window"], + "lib": ["deno.worker"], "strict": true }, "lint": { diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 1506354..45bc840 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -12,6 +12,7 @@ import { } from '../../deps.ts'; import solver from '../solver/_index.ts'; import { SolvedRoll } from '../solver/solver.d.ts'; +import { RollModifiers } from '../mod.d.ts'; import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed, infoColor1, warnColor } from '../commandUtils.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 const rollCmd = `${command} ${args.join(' ')}`; - const returnmsg = solver.parseRoll(rollCmd, modifiers) || { error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message' }; + // const returnmsg = solver.parseRoll(rollCmd, modifiers) || { error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message' }; - const pubEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, modifiers); - const gmEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, gmModifiers); - const countEmbed = generateCountDetailsEmbed(returnmsg.counts); + const rollWorker = new Worker(new URL('../solver/rollWorker.ts', import.meta.url).href, { type: 'module' }); - // 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] }); + const workerTimeout = setTimeout(async () => { + rollWorker.terminate(); + m.edit({ + embeds: [ + (await generateRollEmbed( + message.authorId, + { + error: true, + errorCode: 'TooComplex', + errorMsg: 'Error: Roll Too Complex, try breaking roll down into simpler parts', + }, + {}, + )).embed, + ], + }); + }, 60000); - 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] }); + rollWorker.postMessage({ + rollCmd, + modifiers, + }); - // 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)), { - 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) { + rollWorker.addEventListener('message', async (e) => { + try { + clearTimeout(workerTimeout); + const returnmsg = e.data; + const pubEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, modifiers); + 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 + 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)), { - 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(() => { 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) { log(LT.ERROR, `Undandled Error: ${JSON.stringify(e)}`); } diff --git a/src/solver/parser.ts b/src/solver/parser.ts index bf0e670..ab9bf8f 100644 --- a/src/solver/parser.ts +++ b/src/solver/parser.ts @@ -17,6 +17,19 @@ import { fullSolver } from './solver.ts'; export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => { const returnmsg = { 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 @@ -25,7 +38,14 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll const sepRolls = fullCmd.split(config.prefix); 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 for (const sepRoll of sepRolls) { @@ -75,6 +95,21 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll containsCrit: 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() === '𝜋') { // If the operand is the constant pi, create a SolvedStep for it mathConf[i] = { diff --git a/src/solver/rollWorker.ts b/src/solver/rollWorker.ts new file mode 100644 index 0000000..66062b1 --- /dev/null +++ b/src/solver/rollWorker.ts @@ -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(); +}; diff --git a/src/solver/roller.ts b/src/solver/roller.ts index 13e3631..51b9d4e 100644 --- a/src/solver/roller.ts +++ b/src/solver/roller.ts @@ -83,6 +83,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.dieSize = parseInt(remains.slice(0, 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 Size: ${rollConf.dieSize}`);