diff --git a/config.example.ts b/config.example.ts index e520fab..07cfbc4 100644 --- a/config.example.ts +++ b/config.example.ts @@ -7,7 +7,7 @@ export const config = { postfix: ']]', // Postfix for rolling command limits: { // Limits for the bot functions - maxLoops: 5000000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk, originally was set to 5 Million before rollWorkers were added, increased to 10 Million since multiple rolls can be handled concurrently + maxLoops: 1000000, // Determines how long the bot will attempt a roll, number of loops before it kills a roll. Increase this at your own risk. maxWorkers: 16, // Maximum number of worker threads to spawn at once (Set this to less than the number of threads your CPU has, Artificer will eat it all if too many rolls happen at once) workerTimeout: 300000, // Maximum time before the bot kills a worker thread in ms }, diff --git a/deno.lock b/deno.lock index 355007f..05a0ca1 100644 --- a/deno.lock +++ b/deno.lock @@ -6,6 +6,7 @@ "jsr:@std/fmt@^1.0.7": "1.0.7", "jsr:@std/html@^1.0.3": "1.0.3", "jsr:@std/http@1.0.15": "1.0.15", + "jsr:@std/io@0.225.2": "0.225.2", "jsr:@std/media-types@^1.1.0": "1.1.0", "jsr:@std/net@^1.0.4": "1.0.4", "jsr:@std/path@^1.0.9": "1.0.9", @@ -37,6 +38,9 @@ "jsr:@std/streams" ] }, + "@std/io@0.225.2": { + "integrity": "3c740cd4ee4c082e6cfc86458f47e2ab7cb353dc6234d5e9b1f91a2de5f4d6c7" + }, "@std/media-types@1.1.0": { "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4" }, @@ -823,6 +827,12 @@ "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.0.0/deps.ts": "9a1b2d559fc8c33ae1aeed899aa821f53f9d094e9df40bd4b51b099c58961cd7", "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.0.0/mod.ts": "d9c38a41a405cf5732c9233c2391a1d7f5a12d0e464aace6f8f596fabf5f21ba", "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.0.0/src/logger.ts": "a1924f1f02b35a7501161349de90b60a3aa329e12f1033fdb212b598542897c4", + "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.0/deps.ts": "3ab026026d146ca5e7160b16146d5665e45487a62749a7970f8e00c0c934874d", + "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.0/mod.ts": "d9c38a41a405cf5732c9233c2391a1d7f5a12d0e464aace6f8f596fabf5f21ba", + "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.0/src/logger.ts": "78072b8257a25b4e6adc03d5b92d64ef68b215159a732832fe6020bdebce2ec7", + "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.1/deps.ts": "3ab026026d146ca5e7160b16146d5665e45487a62749a7970f8e00c0c934874d", + "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.1/mod.ts": "d9c38a41a405cf5732c9233c2391a1d7f5a12d0e464aace6f8f596fabf5f21ba", + "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.1/src/logger.ts": "b3a39724d58102dfbcdcd640a829cbfe1f083065060f68003f9c8fd49fdd658a", "https://unpkg.com/@evan/wasm@0.0.65/target/zlib/deno.js": "36cd3f1edd2f3a6d6fd4c2376f701c2748338c132703810d4866cfa52b5e7bf9" } } diff --git a/deps.ts b/deps.ts index db48b1b..3ef6b33 100644 --- a/deps.ts +++ b/deps.ts @@ -6,6 +6,7 @@ export { DiscordActivityTypes, editBotNickname, editBotStatus, + getMessage, hasGuildPermissions, Intents, sendDirectMessage, @@ -23,6 +24,6 @@ export type { StatusCode } from 'jsr:@std/http@1.0.15'; export { nanoid } from 'https://deno.land/x/nanoid@v3.0.0/mod.ts'; -export { initLog, log, LogTypes as LT } from 'https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.0.0/mod.ts'; +export { initLog, closeLog, log, LogTypes as LT } from 'https://raw.githubusercontent.com/Burn-E99/Log4Deno/V2.1.1/mod.ts'; export * as is from 'https://deno.land/x/imagescript@1.3.0/mod.ts'; diff --git a/src/commandUtils.ts b/src/commandUtils.ts index a0459c8..10f063b 100644 --- a/src/commandUtils.ts +++ b/src/commandUtils.ts @@ -1,6 +1,8 @@ import config from '../config.ts'; import { CountDetails, SolvedRoll } from './solver/solver.d.ts'; import { RollModifiers } from './mod.d.ts'; +import { loggingEnabled } from './solver/rollUtils.ts'; +import { log, LT } from '../deps.ts'; export const failColor = 0xe71212; export const warnColor = 0xe38f28; @@ -9,114 +11,137 @@ export const infoColor1 = 0x313bf9; export const infoColor2 = 0x6805e9; export const rollingEmbed = { - embeds: [{ - color: infoColor1, - title: 'Rolling . . .', - }], + embeds: [ + { + color: infoColor1, + title: 'Rolling . . .', + }, + ], }; export const generatePing = (time: number) => ({ - embeds: [{ - color: infoColor1, - title: time === -1 ? 'Ping?' : `Pong! Latency is ${time}ms.`, - }], + embeds: [ + { + color: infoColor1, + title: time === -1 ? 'Ping?' : `Pong! Latency is ${time}ms.`, + }, + ], }); export const generateReport = (msg: string) => ({ - embeds: [{ - color: infoColor2, - title: 'USER REPORT:', - description: msg || 'No message', - }], + embeds: [ + { + color: infoColor2, + title: 'USER REPORT:', + description: msg || 'No message', + }, + ], }); -export const generateStats = (guildCount: number, channelCount: number, memberCount: number, rollCount: bigint, utilityCount: bigint, rollRate: number, utilityRate: number) => ({ - embeds: [{ - color: infoColor2, - title: "The Artificer's Statistics:", - timestamp: new Date().toISOString(), - fields: [ - { - name: 'Guilds:', - value: `${guildCount}`, - inline: true, - }, - { - name: 'Channels:', - value: `${channelCount}`, - inline: true, - }, - { - name: 'Active Members:', - value: `${memberCount}`, - inline: true, - }, - { - name: 'Roll Commands:', - value: `${rollCount} -(${rollRate.toFixed(2)} per hour)`, - inline: true, - }, - { - name: 'Utility Commands:', - value: `${utilityCount} -(${utilityRate.toFixed(2)} per hour)`, - inline: true, - }, - ], - }], +export const generateStats = ( + guildCount: number, + channelCount: number, + memberCount: number, + rollCount: bigint, + utilityCount: bigint, + rollRate: number, + utilityRate: number +) => ({ + embeds: [ + { + color: infoColor2, + title: "The Artificer's Statistics:", + timestamp: new Date().toISOString(), + fields: [ + { + name: 'Guilds:', + value: `${guildCount}`, + inline: true, + }, + { + name: 'Channels:', + value: `${channelCount}`, + inline: true, + }, + { + name: 'Active Members:', + value: `${memberCount}`, + inline: true, + }, + { + name: 'Roll Commands:', + value: `${rollCount}\n(${rollRate.toFixed(2)} per hour)`, + inline: true, + }, + { + name: 'Utility Commands:', + value: `${utilityCount}\n(${utilityRate.toFixed(2)} per hour)`, + inline: true, + }, + ], + }, + ], }); export const generateApiFailed = (args: string) => ({ - embeds: [{ - color: failColor, - title: `Failed to ${args} API rolls for this guild.`, - description: 'If this issue persists, please report this to the developers.', - }], + embeds: [ + { + color: failColor, + title: `Failed to ${args} API rolls for this guild.`, + description: 'If this issue persists, please report this to the developers.', + }, + ], }); export const generateApiStatus = (banned: boolean, active: boolean) => { const apiStatus = active ? 'allowed' : 'blocked from being used'; return { - embeds: [{ - color: infoColor1, - title: `The Artificer's API is ${config.api.enable ? 'currently enabled' : 'currently disabled'}.`, - description: banned ? 'API rolls are banned from being used in this guild.\n\nThis will not be reversed.' : `API rolls are ${apiStatus} in this guild.`, - }], + embeds: [ + { + color: infoColor1, + title: `The Artificer's API is ${config.api.enable ? 'currently enabled' : 'currently disabled'}.`, + description: banned ? 'API rolls are banned from being used in this guild.\n\nThis will not be reversed.' : `API rolls are ${apiStatus} in this guild.`, + }, + ], }; }; export const generateApiSuccess = (args: string) => ({ - embeds: [{ - color: successColor, - title: `API rolls have successfully been ${args} for this guild.`, - }], + embeds: [ + { + color: successColor, + title: `API rolls have successfully been ${args} for this guild.`, + }, + ], }); export const generateDMFailed = (user: string) => ({ - embeds: [{ - color: failColor, - title: `WARNING: ${user} could not be messaged.`, - description: 'If this issue persists, make sure direct messages are allowed from this server.', - }], + embeds: [ + { + color: failColor, + title: `WARNING: ${user} could not be messaged.`, + description: 'If this issue persists, make sure direct messages are allowed from this server.', + }, + ], }); export const generateApiKeyEmail = (email: string, key: string) => ({ content: `<@${config.api.admin}> A USER HAS REQUESTED AN API KEY`, - embeds: [{ - color: infoColor1, - fields: [ - { - name: 'Send to:', - value: email, - }, - { - name: 'Subject:', - value: 'Artificer API Key', - }, - { - name: 'Body:', - value: `Hello Artificer API User, + embeds: [ + { + color: infoColor1, + fields: [ + { + name: 'Send to:', + value: email, + }, + { + name: 'Subject:', + value: 'Artificer API Key', + }, + { + name: 'Body:', + value: `Hello Artificer API User, Welcome aboard The Artificer's API. You can find full details about the API on the GitHub: https://github.com/Burn-E99/TheArtificer @@ -126,27 +151,29 @@ Guard this well, as there is zero tolerance for API abuse. Welcome aboard, The Artificer Developer - Ean Milligan`, - }, - ], - }], + }, + ], + }, + ], }); export const generateApiDeleteEmail = (email: string, deleteCode: string) => ({ content: `<@${config.api.admin}> A USER HAS REQUESTED A DELETE CODE`, - embeds: [{ - color: infoColor1, - fields: [ - { - name: 'Send to:', - value: email, - }, - { - name: 'Subject:', - value: 'Artificer API Delete Code', - }, - { - name: 'Body:', - value: `Hello Artificer API User, + embeds: [ + { + color: infoColor1, + fields: [ + { + name: 'Send to:', + value: email, + }, + { + name: 'Subject:', + value: 'Artificer API Delete Code', + }, + { + name: 'Body:', + value: `Hello Artificer API User, I am sorry to see you go. If you would like, please respond to this email detailing what I could have done better. @@ -154,20 +181,25 @@ As requested, here is your delete code: ${deleteCode} Sorry to see you go, The Artificer Developer - Ean Milligan`, - }, - ], - }], + }, + ], + }, + ], }); export const generateRollError = (errorType: string, errorMsg: string) => ({ - embeds: [{ - color: failColor, - title: 'Roll command encountered the following error:', - fields: [{ - name: errorType, - value: `${errorMsg}\n\nPlease try again. If the error is repeated, please report the issue using the \`${config.prefix}report\` command.`, - }], - }], + embeds: [ + { + color: failColor, + title: 'Roll command encountered the following error:', + fields: [ + { + name: errorType, + value: `${errorMsg}\n\nPlease try again. If the error is repeated, please report the issue using the \`${config.prefix}report\` command.`, + }, + ], + }, + ], }); export const generateCountDetailsEmbed = (counts: CountDetails) => ({ @@ -221,8 +253,8 @@ export const generateRollEmbed = async (authorId: bigint, returnDetails: SolvedR }, hasAttachment: false, attachment: { - 'blob': await new Blob(['' as BlobPart], { 'type': 'text' }), - 'name': 'rollDetails.txt', + blob: await new Blob(['' as BlobPart], { type: 'text' }), + name: 'rollDetails.txt', }, }; } else { @@ -231,14 +263,12 @@ export const generateRollEmbed = async (authorId: bigint, returnDetails: SolvedR return { embed: { color: infoColor2, - description: `<@${authorId}>${returnDetails.line1} - -Results have been messaged to the following GMs: ${modifiers.gms.join(' ')}`, + description: `<@${authorId}>${returnDetails.line1}\n\nResults have been messaged to the following GMs: ${modifiers.gms.join(' ')}`, }, hasAttachment: false, attachment: { - 'blob': await new Blob(['' as BlobPart], { 'type': 'text' }), - 'name': 'rollDetails.txt', + blob: await new Blob(['' as BlobPart], { type: 'text' }), + name: 'rollDetails.txt', }, }; } else { @@ -248,65 +278,57 @@ Results have been messaged to the following GMs: ${modifiers.gms.join(' ')}`, if (!modifiers.superNoDetails) { if (modifiers.noDetails) { - details = `**Details:** -Suppressed by -nd flag`; + details = `**Details:**\nSuppressed by -nd flag`; } else { - details = `**Details:** -${modifiers.spoiler}${returnDetails.line3}${modifiers.spoiler}`; + details = `**Details:**\n${modifiers.spoiler}${returnDetails.line3}${modifiers.spoiler}`; + loggingEnabled && log(LT.LOG, `${returnDetails.line3} |&| ${details}`); } } - const baseDesc = `<@${authorId}>${returnDetails.line1} -**${line2Details.shift()}:** -${line2Details.join(': ')}`; + const baseDesc = `<@${authorId}>${returnDetails.line1}\n**${line2Details.shift()}:**\n${line2Details.join(': ')}`; + // Embed desc limit is 4096 if (baseDesc.length + details.length < 4090) { return { embed: { color: infoColor2, - description: `${baseDesc} - -${details}`, + description: `${baseDesc}\n\n${details}`, }, hasAttachment: false, attachment: { - 'blob': await new Blob(['' as BlobPart], { 'type': 'text' }), - 'name': 'rollDetails.txt', + blob: await new Blob(['' as BlobPart], { type: 'text' }), + name: 'rollDetails.txt', }, }; } else { // If its too big, collapse it into a .txt file and send that instead. - const b = await new Blob([`${baseDesc}\n\n${details}` as BlobPart], { 'type': 'text' }); - details = 'Details have been ommitted from this message for being over 2000 characters.'; + const b = await new Blob([`${baseDesc}\n\n${details}` as BlobPart], { type: 'text' }); + details = 'Details have been omitted from this message for being over 4000 characters.'; if (b.size > 8388290) { details += - '\n\nFull details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.'; + '\n\nFull details could not be attached to this messaged as a `.txt` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.'; return { embed: { color: infoColor2, - description: `${baseDesc} - - ${details}`, + description: `${baseDesc}\n\n${details}`, }, hasAttachment: false, attachment: { - 'blob': await new Blob(['' as BlobPart], { 'type': 'text' }), - 'name': 'rollDetails.txt', + blob: await new Blob(['' as BlobPart], { type: 'text' }), + name: 'rollDetails.txt', }, }; } else { - details += '\n\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.'; + details += '\n\nFull details have been attached to this messaged as a `.txt` file for verification purposes.'; return { embed: { color: infoColor2, - description: `${baseDesc} - -${details}`, + description: `${baseDesc}\n\n${details}`, }, hasAttachment: true, attachment: { - 'blob': b, - 'name': 'rollDetails.txt', + blob: b, + name: 'rollDetails.txt', }, }; } diff --git a/src/solver/parser.ts b/src/solver/parser.ts index 8c49ef8..cd65705 100644 --- a/src/solver/parser.ts +++ b/src/solver/parser.ts @@ -16,7 +16,7 @@ import { fullSolver } from './solver.ts'; // parseRoll handles converting fullCmd into a computer readable format for processing, and finally executes the solving export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => { const operators = ['^', '*', '/', '%', '+', '-', '(', ')']; - const returnmsg = { + const returnmsg = { error: false, errorCode: '', errorMsg: '', @@ -39,23 +39,25 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll const sepRolls = fullCmd.split(config.prefix); const tempReturnData: ReturnData[] = []; - const tempCountDetails: CountDetails[] = [{ - total: 0, - successful: 0, - failed: 0, - rerolled: 0, - dropped: 0, - exploded: 0, - }]; + 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) { loggingEnabled && log(LT.LOG, `Parsing roll ${fullCmd} | Working ${sepRoll}`); - // Split the current iteration on the command postfix to separate the operation to be parsed and the text formatting after the opertaion + // Split the current iteration on the command postfix to separate the operation to be parsed and the text formatting after the operation const [tempConf, tempFormat] = sepRoll.split(config.postfix); // Remove all spaces from the operation config and split it by any operator (keeping the operator in mathConf for fullSolver to do math on) - const mathConf: (string | number | SolvedStep)[] = <(string | number | SolvedStep)[]> tempConf.replace(/ /g, '').split(/([-+()*/%^])/g); + const mathConf: (string | number | SolvedStep)[] = <(string | number | SolvedStep)[]>tempConf.replace(/ /g, '').split(/([-+()*/%^])/g); // Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens let parenCnt = 0; @@ -98,7 +100,11 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll containsCrit: false, containsFail: false, }; - } else if (mathConf[i].toString().toLowerCase() === 'inf' || mathConf[i].toString().toLowerCase() === 'infinity' || mathConf[i].toString().toLowerCase() === '∞') { + } 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, @@ -122,12 +128,19 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll containsCrit: false, containsFail: false, }; - mathConf.splice(i + 1, 0, ...['*', { - total: Math.E, - details: '*e*', - containsCrit: false, - containsFail: false, - }]); + mathConf.splice( + i + 1, + 0, + ...[ + '*', + { + total: Math.E, + details: '*e*', + containsCrit: false, + containsFail: false, + }, + ] + ); } else if (!operators.includes(mathConf[i].toString())) { // If nothing else has handled it by now, try it as a roll const formattedRoll = formatRoll(mathConf[i].toString(), modifiers.maxRoll, modifiers.nominalRoll); @@ -137,10 +150,10 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll if (mathConf[i - 1] === '-' && ((!mathConf[i - 2] && mathConf[i - 2] !== 0) || mathConf[i - 2] === '(')) { if (typeof mathConf[i] === 'number') { - mathConf[i] = mathConf[i] * -1; + mathConf[i] = mathConf[i] * -1; } else { - ( mathConf[i]).total = ( mathConf[i]).total * -1; - ( mathConf[i]).details = `-${( mathConf[i]).details}`; + (mathConf[i]).total = (mathConf[i]).total * -1; + (mathConf[i]).details = `-${(mathConf[i]).details}`; } mathConf.splice(i - 1, 1); i--; @@ -221,7 +234,10 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll line2 += `${preFormat}${e.rollTotal}${postFormat}, `; } - line2 = line2.replace(/\*\*\*\*/g, '** **').replace(/____/g, '__ __').replace(/~~~~/g, '~~ ~~'); + line2 = line2 + .replace(/\*\*\*\*/g, '** **') + .replace(/____/g, '__ __') + .replace(/~~~~/g, '~~ ~~'); line3 += `\`${e.initConfig}\` = ${e.rollDetails} = ${preFormat}${e.rollTotal}${postFormat}\n`; }); @@ -245,7 +261,8 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll dropped: acc.dropped + cnt.dropped, exploded: acc.exploded + cnt.exploded, })); - } catch (solverError) { + } catch (e) { + const solverError = e as Error; // Welp, the unthinkable happened, we hit an error // Split on _ for the error messages that have more info than just their name diff --git a/src/solver/rollQueue.ts b/src/solver/rollQueue.ts index eaddaa2..67064ae 100644 --- a/src/solver/rollQueue.ts +++ b/src/solver/rollQueue.ts @@ -17,6 +17,7 @@ import { ApiQueuedRoll, DDQueuedRoll, RollModifiers } from '../mod.d.ts'; import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed, infoColor2, rollingEmbed } from '../commandUtils.ts'; import stdResp from '../endpoints/stdResponses.ts'; import utils from '../utils.ts'; +import { loggingEnabled } from './rollUtils.ts'; let currentWorkers = 0; const rollQueue: Array = []; @@ -59,6 +60,7 @@ const handleRollWorker = (rq: ApiQueuedRoll | DDQueuedRoll) => { rollWorker.addEventListener('message', async (workerMessage) => { if (workerMessage.data === 'ready') { + loggingEnabled && log(LT.LOG, `Sending roll to worker: ${rq.rollCmd}, ${JSON.stringify(rq.modifiers)}`); rollWorker.postMessage({ rollCmd: rq.rollCmd, modifiers: rq.modifiers, @@ -70,9 +72,12 @@ const handleRollWorker = (rq: ApiQueuedRoll | DDQueuedRoll) => { currentWorkers--; clearTimeout(workerTimeout); const returnmsg = workerMessage.data; + loggingEnabled && log(LT.LOG, `Roll came back from worker: ${returnmsg.line1.length} |&| ${returnmsg.line2.length} |&| ${returnmsg.line3.length} `); + loggingEnabled && log(LT.LOG, `Roll came back from worker: ${returnmsg.line1} |&| ${returnmsg.line2} |&| ${returnmsg.line3} `); const pubEmbedDetails = await generateRollEmbed(rq.apiRoll ? rq.api.userId : rq.dd.message.authorId, returnmsg, rq.modifiers); const gmEmbedDetails = await generateRollEmbed(rq.apiRoll ? rq.api.userId : rq.dd.message.authorId, returnmsg, gmModifiers); const countEmbed = generateCountDetailsEmbed(returnmsg.counts); + loggingEnabled && log(LT.LOG, `Embeds are generated: ${JSON.stringify(pubEmbedDetails)} |&| ${JSON.stringify(gmEmbedDetails)}`); // If there was an error, report it to the user in hopes that they can determine what they did wrong if (returnmsg.error) { diff --git a/src/solver/rollUtils.ts b/src/solver/rollUtils.ts index ecc4ccd..be5ae01 100644 --- a/src/solver/rollUtils.ts +++ b/src/solver/rollUtils.ts @@ -6,7 +6,7 @@ import { import { ReturnData, RollSet } from './solver.d.ts'; -export const loggingEnabled = false; +export const loggingEnabled = true; // genRoll(size) returns number // genRoll rolls a die of size size and returns the result @@ -15,7 +15,7 @@ export const genRoll = (size: number, maximiseRoll: boolean, nominalRoll: boolea return size; } else { // Math.random * size will return a decimal number between 0 and size (excluding size), so add 1 and floor the result to not get 0 as a result - return nominalRoll ? ((size / 2) + 0.5) : Math.floor((Math.random() * size) + 1); + return nominalRoll ? size / 2 + 0.5 : Math.floor(Math.random() * size + 1); } }; diff --git a/src/solver/rollWorker.ts b/src/solver/rollWorker.ts index f0b29e9..e50b1db 100644 --- a/src/solver/rollWorker.ts +++ b/src/solver/rollWorker.ts @@ -1,10 +1,15 @@ +import { initLog, closeLog } from '../../deps.ts'; +import { DEBUG } from '../../flags.ts'; import { parseRoll } from './parser.ts'; +import { loggingEnabled } from './rollUtils.ts'; + +loggingEnabled && initLog('logs/worker', DEBUG); // Alert rollQueue that this worker is ready self.postMessage('ready'); // Handle the roll -self.onmessage = async (e: any) => { +self.onmessage = async (e) => { const payload = e.data; const returnmsg = parseRoll(payload.rollCmd, payload.modifiers) || { error: true, @@ -23,5 +28,6 @@ self.onmessage = async (e: any) => { }, }; self.postMessage(returnmsg); + loggingEnabled && (await closeLog()); self.close(); }; diff --git a/src/solver/roller.ts b/src/solver/roller.ts index 451261b..9dc2e78 100644 --- a/src/solver/roller.ts +++ b/src/solver/roller.ts @@ -12,10 +12,10 @@ import { compareOrigidx, compareRolls, genFateRoll, genRoll, loggingEnabled } fr // roll parses and executes the rollStr, if needed it will also make the roll the maximum or average export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolean): RollSet[] => { /* Roll Capabilities - * Deciphers and rolls a single dice roll set - * - * Check the README.md of this project for details on the roll options. I gave up trying to keep three places updated at once. - */ + * Deciphers and rolls a single dice roll set + * + * Check the README.md of this project for details on the roll options. I gave up trying to keep three places updated at once. + */ // Make entire roll lowercase for ease of parsing rollStr = rollStr.toLowerCase(); @@ -47,22 +47,22 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea reroll: { on: false, once: false, - nums: [], + nums: [], }, critScore: { on: false, - range: [], + range: [], }, critFail: { on: false, - range: [], + range: [], }, exploding: { on: false, once: false, compounding: false, penetrating: false, - nums: [], + nums: [], }, }; @@ -146,7 +146,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Loop until all remaining args are parsed while (remains.length > 0) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing remains ${remains}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing remains ${remains}`); // Find the next number in the remains to be able to cut out the rule name let afterSepIdx = remains.search(/\d/); if (afterSepIdx < 0) { @@ -190,7 +190,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea case 'ro': case 'ro=': rollConf.reroll.once = true; - // falls through as ro/ro= functions the same as r/r= in this context + // falls through as ro/ro= functions the same as r/r= in this context case 'r': case 'r=': // Configure Reroll (this can happen multiple times) @@ -199,23 +199,23 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea break; case 'ro>': rollConf.reroll.once = true; - // falls through as ro> functions the same as r> in this context + // falls through as ro> functions the same as r> in this context case 'r>': // Configure reroll for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.reroll.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing r> ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing r> ${i}`); rollConf.reroll.nums.push(i); } break; case 'ro<': rollConf.reroll.once = true; - // falls through as ro< functions the same as r< in this context + // falls through as ro< functions the same as r< in this context case 'r<': // Configure reroll for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.reroll.on = true; for (let i = 1; i <= tNum; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing r< ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing r< ${i}`); rollConf.reroll.nums.push(i); } break; @@ -229,7 +229,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.critScore.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing cs> ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing cs> ${i}`); rollConf.critScore.range.push(i); } break; @@ -237,7 +237,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.critScore.on = true; for (let i = 0; i <= tNum; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing cs< ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing cs< ${i}`); rollConf.critScore.range.push(i); } break; @@ -251,7 +251,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.critFail.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing cf> ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing cf> ${i}`); rollConf.critFail.range.push(i); } break; @@ -259,7 +259,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.critFail.on = true; for (let i = 0; i <= tNum; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing cf< ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing cf< ${i}`); rollConf.critFail.range.push(i); } break; @@ -292,7 +292,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Configure Exploding for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.exploding.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing !> ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing !> ${i}`); rollConf.exploding.nums.push(i); } break; @@ -303,7 +303,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Configure Exploding for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.exploding.on = true; for (let i = 1; i <= tNum; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Parsing !< ${i}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Parsing !< ${i}`); rollConf.exploding.nums.push(i); } break; @@ -349,7 +349,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Since only one drop or keep option can be active, count how many are active to throw the right error let dkdkCnt = 0; [rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach((e) => { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Checking if drop/keep is on ${e}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | Checking if drop/keep is on ${e}`); if (e) { dkdkCnt++; } @@ -376,27 +376,27 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Roll the roll const rollSet = []; /* Roll will contain objects of the following format: - * { - * origidx: 0, - * roll: 0, - * dropped: false, - * rerolled: false, - * exploding: false, - * critHit: false, - * critFail: false - * } - * - * Each of these is defined as following: - * { - * origidx: The original index of the roll - * roll: The resulting roll on this die in the set - * dropped: This die is to be dropped as it was one of the dy lowest dice - * rerolled: This die has been rerolled as it matched rz, it is replaced by the very next die in the set - * exploding: This die was rolled as the previous die exploded (was a crit hit) - * critHit: This die matched csq[-u], max die value used if cs not used - * critFail: This die rolled a nat 1, a critical failure - * } - */ + * { + * origidx: 0, + * roll: 0, + * dropped: false, + * rerolled: false, + * exploding: false, + * critHit: false, + * critFail: false + * } + * + * Each of these is defined as following: + * { + * origidx: The original index of the roll + * roll: The resulting roll on this die in the set + * dropped: This die is to be dropped as it was one of the dy lowest dice + * rerolled: This die has been rerolled as it matched rz, it is replaced by the very next die in the set + * exploding: This die was rolled as the previous die exploded (was a crit hit) + * critHit: This die matched csq[-u], max die value used if cs not used + * critFail: This die rolled a nat 1, a critical failure + * } + */ // Initialize a templet rollSet to copy multiple times const templateRoll: RollSet = { @@ -415,8 +415,9 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Initial rolling, not handling reroll or exploding here for (let i = 0; i < rollConf.dieCount; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`); + loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`); // If loopCount gets too high, stop trying to calculate infinity + loopCount++; if (loopCount > config.limits.maxLoops) { throw new Error('MaxLoopsExceeded'); } @@ -447,21 +448,21 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Push the newly created roll and loop again rollSet.push(rolling); - loopCount++; } // If needed, handle rerolling and exploding dice now if (rollConf.reroll.on || rollConf.exploding.on) { for (let i = 0; i < rollSet.length; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`); + loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`); // If loopCount gets too high, stop trying to calculate infinity + loopCount++; if (loopCount > config.limits.maxLoops) { throw new Error('MaxLoopsExceeded'); } // If we need to reroll this roll, flag its been replaced and... // This big boolean statement first checks if reroll is on, if the roll is within the reroll range, and finally if ro is ON, make sure we haven't already rerolled the roll - if (rollConf.reroll.on && rollConf.reroll.nums.indexOf(rollSet[i].roll) >= 0 && (!rollConf.reroll.once || !rollSet[i ? (i - 1) : i].rerolled)) { + if (rollConf.reroll.on && rollConf.reroll.nums.indexOf(rollSet[i].roll) >= 0 && (!rollConf.reroll.once || !rollSet[i ? i - 1 : i].rerolled)) { rollSet[i].rerolled = true; // Copy the template to fill out for this iteration @@ -485,7 +486,9 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Slot this new roll in after the current iteration so it can be processed in the next loop rollSet.splice(i + 1, 0, newReroll); } else if ( - rollConf.exploding.on && !rollSet[i].rerolled && (rollConf.exploding.nums.length ? rollConf.exploding.nums.indexOf(rollSet[i].roll) >= 0 : rollSet[i].critHit) && + rollConf.exploding.on && + !rollSet[i].rerolled && + (rollConf.exploding.nums.length ? rollConf.exploding.nums.indexOf(rollSet[i].roll) >= 0 : rollSet[i].critHit) && (!rollConf.exploding.once || !rollSet[i].exploding) ) { // If we have exploding.nums set, use those to determine the exploding range, and make sure if !o is on, make sure we don't repeatedly explode @@ -514,16 +517,15 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Slot this new roll in after the current iteration so it can be processed in the next loop rollSet.splice(i + 1, 0, newExplodingRoll); } - - loopCount++; } } // If penetrating is on, do the decrements if (rollConf.exploding.penetrating) { for (const penRoll of rollSet) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`); + loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Handling penetrating explosions ${JSON.stringify(penRoll)}`); // If loopCount gets too high, stop trying to calculate infinity + loopCount++; if (loopCount > config.limits.maxLoops) { throw new Error('MaxLoopsExceeded'); } @@ -532,16 +534,15 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (penRoll.exploding) { penRoll.roll--; } - - loopCount++; } } // Handle compounding explosions if (rollConf.exploding.compounding) { for (let i = 0; i < rollSet.length; i++) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`); + loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Handling compounding explosions ${JSON.stringify(rollSet[i])}`); // If loopCount gets too high, stop trying to calculate infinity + loopCount++; if (loopCount > config.limits.maxLoops) { throw new Error('MaxLoopsExceeded'); } @@ -555,8 +556,6 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollSet.splice(i, 1); i--; } - - loopCount++; } } @@ -567,18 +566,17 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (rollConf.reroll.on) { for (let j = 0; j < rollSet.length; j++) { // If loopCount gets too high, stop trying to calculate infinity + loopCount++; if (loopCount > config.limits.maxLoops) { throw new Error('MaxLoopsExceeded'); } - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`); + loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`); rollSet[j].origidx = j; if (rollSet[j].rerolled) { rerollCount++; } - - loopCount++; } } @@ -621,18 +619,18 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea let i = 0; while (dropCount > 0 && i < rollSet.length) { // If loopCount gets too high, stop trying to calculate infinity + loopCount++; if (loopCount > config.limits.maxLoops) { throw new Error('MaxLoopsExceeded'); } - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`); + loggingEnabled && log(LT.LOG, `${loopCount} Handling ${rollType} ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`); // Skip all rolls that were rerolled if (!rollSet[i].rerolled) { rollSet[i].dropped = true; dropCount--; } i++; - loopCount++; } // Finally, return the rollSet to its original order @@ -646,7 +644,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Sum up all rolls for (const ovaRoll of rollSet) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | incrementing rollVals for ${ovaRoll}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | incrementing rollVals for ${ovaRoll}`); rollVals[ovaRoll.roll - 1] += ovaRoll.roll; } @@ -655,7 +653,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Drop all dice that are not a part of the max for (const ovaRoll of rollSet) { - loggingEnabled && log(LT.LOG, `handling ${rollType} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`); + loggingEnabled && log(LT.LOG, `Handling ${rollType} ${rollStr} | checking if this roll should be dropped ${ovaRoll.roll} | to keep: ${maxRoll}`); if (ovaRoll.roll !== maxRoll) { ovaRoll.dropped = true; ovaRoll.critFail = false;