Implemented queueing system for rollWorker to limit number of concurrent workers
Moved some consts to the config.ts file Added flag to disable logging while rolling for a massive speed increase and to increase stability of the bot
This commit is contained in:
parent
b58cf31edb
commit
bb10d52506
|
@ -5,6 +5,11 @@ export const config = {
|
|||
"localtoken": "local_testing_token", // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
|
||||
"prefix": "[[", // Prefix for all commands
|
||||
"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
|
||||
"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
|
||||
},
|
||||
"api": { // Setting for the built-in API
|
||||
"enable": false, // Leave this off if you have no intention of using this/supporting it
|
||||
"port": 8080, // Port for the API to listen on
|
||||
|
|
|
@ -268,7 +268,7 @@ ${details}`,
|
|||
details = 'Details have been ommitted from this message for being over 2000 characters.';
|
||||
if (b.size > 8388290) {
|
||||
details +=
|
||||
'Full 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,
|
||||
|
@ -283,7 +283,7 @@ ${details}`,
|
|||
},
|
||||
};
|
||||
} else {
|
||||
details += 'Full 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,
|
||||
|
|
|
@ -11,60 +11,33 @@ import {
|
|||
sendDirectMessage,
|
||||
} from '../../deps.ts';
|
||||
import { SolvedRoll } from '../solver/solver.d.ts';
|
||||
import { RollModifiers } from '../mod.d.ts';
|
||||
import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed, infoColor1, warnColor } from '../commandUtils.ts';
|
||||
import { QueuedRoll, RollModifiers } from '../mod.d.ts';
|
||||
import { generateCountDetailsEmbed, generateDMFailed, generateRollEmbed, infoColor1, infoColor2, warnColor } from '../commandUtils.ts';
|
||||
import rollFuncs from './roll/_index.ts';
|
||||
|
||||
export const roll = async (message: DiscordenoMessage, args: string[], command: string) => {
|
||||
// Light telemetry to see how many times a command is being run
|
||||
dbClient.execute(`CALL INC_CNT("roll");`).catch((e) => {
|
||||
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
|
||||
});
|
||||
let currentWorkers = 0;
|
||||
const rollQueue: Array<QueuedRoll> = [];
|
||||
|
||||
// If DEVMODE is on, only allow this command to be used in the devServer
|
||||
if (DEVMODE && message.guildId !== config.devServer) {
|
||||
message.send({
|
||||
embeds: [{
|
||||
color: warnColor,
|
||||
title: 'Command is in development, please try again later.',
|
||||
}],
|
||||
}).catch((e) => {
|
||||
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Rest of this command is in a try-catch to protect all sends/edits from erroring out
|
||||
try {
|
||||
const originalCommand = `${config.prefix}${command} ${args.join(' ')}`;
|
||||
|
||||
const m = await message.reply({
|
||||
const rollingEmbed = {
|
||||
embeds: [{
|
||||
color: infoColor1,
|
||||
title: 'Rolling . . .',
|
||||
}],
|
||||
});
|
||||
};
|
||||
|
||||
// Get modifiers from command
|
||||
const modifiers = rollFuncs.getModifiers(m, args, command, originalCommand);
|
||||
// Handle setting up and calling the rollWorker
|
||||
const handleRollWorker = async (m: DiscordenoMessage, message: DiscordenoMessage, originalCommand: String, rollCmd: String, modifiers: RollModifiers) => {
|
||||
currentWorkers++;
|
||||
|
||||
// gmModifiers used to create gmEmbed (basically just turn off the gmRoll)
|
||||
const gmModifiers = JSON.parse(JSON.stringify(modifiers));
|
||||
gmModifiers.gmRoll = false;
|
||||
|
||||
// Return early if the modifiers were invalid
|
||||
if (!modifiers.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) || <SolvedRoll>{ error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message' };
|
||||
|
||||
const rollWorker = new Worker(new URL('../solver/rollWorker.ts', import.meta.url).href, { type: 'module' });
|
||||
|
||||
const workerTimeout = setTimeout(async () => {
|
||||
rollWorker.terminate();
|
||||
currentWorkers--;
|
||||
m.edit({
|
||||
embeds: [
|
||||
(await generateRollEmbed(
|
||||
|
@ -72,13 +45,13 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
|
|||
<SolvedRoll> {
|
||||
error: true,
|
||||
errorCode: 'TooComplex',
|
||||
errorMsg: 'Error: Roll Too Complex, try breaking roll down into simpler parts',
|
||||
errorMsg: 'Error: Roll took too long to process, try breaking roll down into simpler parts',
|
||||
},
|
||||
<RollModifiers> {},
|
||||
)).embed,
|
||||
],
|
||||
});
|
||||
}, 60000);
|
||||
}, config.limits.workerTimeout);
|
||||
|
||||
rollWorker.postMessage({
|
||||
rollCmd,
|
||||
|
@ -87,6 +60,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
|
|||
|
||||
rollWorker.addEventListener('message', async (workerMessage) => {
|
||||
try {
|
||||
currentWorkers--;
|
||||
clearTimeout(workerTimeout);
|
||||
const returnmsg = workerMessage.data;
|
||||
const pubEmbedDetails = await generateRollEmbed(message.authorId, returnmsg, modifiers);
|
||||
|
@ -145,6 +119,79 @@ export const roll = async (message: DiscordenoMessage, args: string[], command:
|
|||
log(LT.ERROR, `Unddandled Error: ${JSON.stringify(e)}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Runs the roll or queues it depending on how many workers are currently running
|
||||
const queueRoll = async (m: DiscordenoMessage, message: DiscordenoMessage, originalCommand: String, rollCmd: String, modifiers: RollModifiers) => {
|
||||
if (!rollQueue.length && currentWorkers < config.limits.maxWorkers) {
|
||||
handleRollWorker(m, message, originalCommand, rollCmd, modifiers);
|
||||
} else {
|
||||
m.edit({
|
||||
embeds: [{
|
||||
color: infoColor2,
|
||||
title: `${config.name} currently has its hands full and has queued your roll.`,
|
||||
description: `There are currently ${currentWorkers + rollQueue.length} rolls ahead of this roll.
|
||||
|
||||
The results for this roll will replace this message when it is done.`,
|
||||
}],
|
||||
}).catch((e) => {
|
||||
log(LT.ERROR, `Failed to send message: ${JSON.stringify(m)} | ${JSON.stringify(e)}`);
|
||||
});
|
||||
rollQueue.push({ m, message, originalCommand, rollCmd, modifiers });
|
||||
}
|
||||
};
|
||||
|
||||
// Checks the queue constantly to make sure the queue stays empty
|
||||
setInterval(async () => {
|
||||
log(LT.LOG, `Checking rollQueue for items, rollQueue length: ${rollQueue.length}, currentWorkers: ${currentWorkers}, config.limits.maxWorkers: ${config.limits.maxWorkers}`);
|
||||
if (rollQueue.length && currentWorkers < config.limits.maxWorkers) {
|
||||
const temp = rollQueue.shift();
|
||||
if (temp) {
|
||||
temp.m.edit(rollingEmbed).catch((e) => {
|
||||
log(LT.ERROR, `Failed to send message: ${JSON.stringify(temp.m)} | ${JSON.stringify(e)}`);
|
||||
});
|
||||
handleRollWorker(temp.m, temp.message, temp.originalCommand, temp.rollCmd, temp.modifiers);
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
export const roll = async (message: DiscordenoMessage, args: string[], command: string) => {
|
||||
// Light telemetry to see how many times a command is being run
|
||||
dbClient.execute(`CALL INC_CNT("roll");`).catch((e) => {
|
||||
log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`);
|
||||
});
|
||||
|
||||
// If DEVMODE is on, only allow this command to be used in the devServer
|
||||
if (DEVMODE && message.guildId !== config.devServer) {
|
||||
message.send({
|
||||
embeds: [{
|
||||
color: warnColor,
|
||||
title: 'Command is in development, please try again later.',
|
||||
}],
|
||||
}).catch((e) => {
|
||||
log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Rest of this command is in a try-catch to protect all sends/edits from erroring out
|
||||
try {
|
||||
const originalCommand = `${config.prefix}${command} ${args.join(' ')}`;
|
||||
|
||||
const m = await message.reply(rollingEmbed);
|
||||
|
||||
// Get modifiers from command
|
||||
const modifiers = rollFuncs.getModifiers(m, args, command, originalCommand);
|
||||
|
||||
// Return early if the modifiers were invalid
|
||||
if (!modifiers.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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(' ')}`;
|
||||
|
||||
queueRoll(m, message, originalCommand, rollCmd, modifiers);
|
||||
} catch (e) {
|
||||
log(LT.ERROR, `Undandled Error: ${JSON.stringify(e)}`);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// mod.d.ts custom types
|
||||
import { DiscordenoMessage } from '../deps.ts';
|
||||
|
||||
// EmojiConf is used as a structure for the emojis stored in config.ts
|
||||
export type EmojiConf = {
|
||||
|
@ -22,3 +23,12 @@ export type RollModifiers = {
|
|||
valid: boolean;
|
||||
count: boolean;
|
||||
};
|
||||
|
||||
// QueuedRoll is the structure to track rolls we could not immediately handle
|
||||
export type QueuedRoll = {
|
||||
m: DiscordenoMessage;
|
||||
message: DiscordenoMessage;
|
||||
originalCommand: String;
|
||||
rollCmd: String;
|
||||
modifiers: RollModifiers;
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ import config from '../../config.ts';
|
|||
|
||||
import { RollModifiers } from '../mod.d.ts';
|
||||
import { CountDetails, ReturnData, SolvedRoll, SolvedStep } from './solver.d.ts';
|
||||
import { compareTotalRolls, escapeCharacters } from './rollUtils.ts';
|
||||
import { compareTotalRolls, escapeCharacters, loggingEnabled } from './rollUtils.ts';
|
||||
import { formatRoll } from './rollFormatter.ts';
|
||||
import { fullSolver } from './solver.ts';
|
||||
|
||||
|
@ -49,7 +49,7 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
|
|||
|
||||
// Loop thru all roll/math ops
|
||||
for (const sepRoll of sepRolls) {
|
||||
log(LT.LOG, `Parsing roll ${fullCmd} | Working ${sepRoll}`);
|
||||
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
|
||||
const [tempConf, tempFormat] = sepRoll.split(config.postfix);
|
||||
|
||||
|
@ -59,7 +59,7 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
|
|||
// 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;
|
||||
mathConf.forEach((e) => {
|
||||
log(LT.LOG, `Parsing roll ${fullCmd} | Checking parenthesis balance ${e}`);
|
||||
loggingEnabled && log(LT.LOG, `Parsing roll ${fullCmd} | Checking parenthesis balance ${e}`);
|
||||
if (e === '(') {
|
||||
parenCnt++;
|
||||
} else if (e === ')') {
|
||||
|
@ -74,7 +74,7 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
|
|||
|
||||
// Evaluate all rolls into stepSolve format and all numbers into floats
|
||||
for (let i = 0; i < mathConf.length; i++) {
|
||||
log(LT.LOG, `Parsing roll ${fullCmd} | Evaluating rolls into mathable items ${JSON.stringify(mathConf[i])}`);
|
||||
loggingEnabled && log(LT.LOG, `Parsing roll ${fullCmd} | Evaluating rolls into mathable items ${JSON.stringify(mathConf[i])}`);
|
||||
if (mathConf[i].toString().length === 0) {
|
||||
// If its an empty string, get it out of here
|
||||
mathConf.splice(i, 1);
|
||||
|
@ -198,7 +198,7 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll
|
|||
|
||||
// Fill out all of the details and results now
|
||||
tempReturnData.forEach((e) => {
|
||||
log(LT.LOG, `Parsing roll ${fullCmd} | Making return text ${JSON.stringify(e)}`);
|
||||
loggingEnabled && log(LT.LOG, `Parsing roll ${fullCmd} | Making return text ${JSON.stringify(e)}`);
|
||||
let preFormat = '';
|
||||
let postFormat = '';
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
import { roll } from './roller.ts';
|
||||
import { rollCounter } from './counter.ts';
|
||||
import { RollFormat } from './solver.d.ts';
|
||||
import { loggingEnabled } from './rollUtils.ts';
|
||||
|
||||
// formatRoll(rollConf, maximiseRoll, nominalRoll) returns one SolvedStep
|
||||
// formatRoll handles creating and formatting the completed rolls into the SolvedStep format
|
||||
|
@ -21,7 +22,7 @@ export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll:
|
|||
|
||||
// Loop thru all parts of the roll to document everything that was done to create the total roll
|
||||
tempRollSet.forEach((e) => {
|
||||
log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`);
|
||||
loggingEnabled && log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`);
|
||||
let preFormat = '';
|
||||
let postFormat = '';
|
||||
|
||||
|
|
|
@ -6,10 +6,7 @@ import {
|
|||
|
||||
import { ReturnData, RollSet } from './solver.d.ts';
|
||||
|
||||
// MAXLOOPS determines how long the bot will attempt a roll
|
||||
// Default is 5000000 (5 million), which results in at most a 10 second delay before the bot calls the roll infinite or too complex
|
||||
// Increase at your own risk
|
||||
export const MAXLOOPS = 5000000;
|
||||
export const loggingEnabled = false;
|
||||
|
||||
// genRoll(size) returns number
|
||||
// genRoll rolls a die of size size and returns the result
|
||||
|
@ -63,7 +60,7 @@ export const compareOrigidx = (a: RollSet, b: RollSet): number => {
|
|||
export const escapeCharacters = (str: string, esc: string): string => {
|
||||
// Loop thru each esc char one at a time
|
||||
for (const e of esc) {
|
||||
log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
|
||||
loggingEnabled && log(LT.LOG, `Escaping character ${e} | ${str}, ${esc}`);
|
||||
// Create a new regex to look for that char that needs replaced and escape it
|
||||
const temprgx = new RegExp(`[${e}]`, 'g');
|
||||
str = str.replace(temprgx, `\\${e}`);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import config from '../../config.ts';
|
||||
import {
|
||||
log,
|
||||
// Log4Deno deps
|
||||
|
@ -5,7 +6,7 @@ import {
|
|||
} from '../../deps.ts';
|
||||
|
||||
import { RollSet } from './solver.d.ts';
|
||||
import { compareOrigidx, compareRolls, genRoll, MAXLOOPS } from './rollUtils.ts';
|
||||
import { compareOrigidx, compareRolls, genRoll, loggingEnabled } from './rollUtils.ts';
|
||||
|
||||
// roll(rollStr, maximiseRoll, nominalRoll) returns RollSet
|
||||
// roll parses and executes the rollStr, if needed it will also make the roll the maximum or average
|
||||
|
@ -87,8 +88,8 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
|
|||
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}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsed Die Count: ${rollConf.dieCount}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsed Die Size: ${rollConf.dieSize}`);
|
||||
|
||||
// Finish parsing the roll
|
||||
if (remains.length > 0) {
|
||||
|
@ -99,7 +100,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
|
|||
|
||||
// Loop until all remaining args are parsed
|
||||
while (remains.length > 0) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing remains ${remains}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${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) {
|
||||
|
@ -157,7 +158,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
|
|||
// 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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing r> ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing r> ${i}`);
|
||||
rollConf.reroll.nums.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -168,7 +169,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
|
|||
// 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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing r< ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing r< ${i}`);
|
||||
rollConf.reroll.nums.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -182,7 +183,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing cs> ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing cs> ${i}`);
|
||||
rollConf.critScore.range.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -190,7 +191,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing cs< ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing cs< ${i}`);
|
||||
rollConf.critScore.range.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -204,7 +205,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing cf> ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing cf> ${i}`);
|
||||
rollConf.critFail.range.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -212,7 +213,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing cf< ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing cf< ${i}`);
|
||||
rollConf.critFail.range.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -245,7 +246,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing !> ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing !> ${i}`);
|
||||
rollConf.exploding.nums.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -256,7 +257,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Parsing !< ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Parsing !< ${i}`);
|
||||
rollConf.exploding.nums.push(i);
|
||||
}
|
||||
break;
|
||||
|
@ -279,7 +280,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) => {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Checking if drop/keep is on ${e}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Checking if drop/keep is on ${e}`);
|
||||
if (e) {
|
||||
dkdkCnt++;
|
||||
}
|
||||
|
@ -344,9 +345,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++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`);
|
||||
// If loopCount gets too high, stop trying to calculate infinity
|
||||
if (loopCount > MAXLOOPS) {
|
||||
if (loopCount > config.limits.maxLoops) {
|
||||
throw new Error('MaxLoopsExceeded');
|
||||
}
|
||||
|
||||
|
@ -378,9 +379,9 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea
|
|||
// If needed, handle rerolling and exploding dice now
|
||||
if (rollConf.reroll.on || rollConf.exploding.on) {
|
||||
for (let i = 0; i < rollSet.length; i++) {
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`);
|
||||
// If loopCount gets too high, stop trying to calculate infinity
|
||||
if (loopCount > MAXLOOPS) {
|
||||
if (loopCount > config.limits.maxLoops) {
|
||||
throw new Error('MaxLoopsExceeded');
|
||||
}
|
||||
|
||||
|
@ -451,11 +452,11 @@ 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
|
||||
if (loopCount > MAXLOOPS) {
|
||||
if (loopCount > config.limits.maxLoops) {
|
||||
throw new Error('MaxLoopsExceeded');
|
||||
}
|
||||
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[j])}`);
|
||||
rollSet[j].origidx = j;
|
||||
|
||||
if (rollSet[j].rerolled) {
|
||||
|
@ -505,11 +506,11 @@ 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
|
||||
if (loopCount > MAXLOOPS) {
|
||||
if (loopCount > config.limits.maxLoops) {
|
||||
throw new Error('MaxLoopsExceeded');
|
||||
}
|
||||
|
||||
log(LT.LOG, `Handling roll ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
|
||||
loggingEnabled && log(LT.LOG, `Handling roll ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`);
|
||||
// Skip all rolls that were rerolled
|
||||
if (!rollSet[i].rerolled) {
|
||||
rollSet[i].dropped = true;
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
} from '../../deps.ts';
|
||||
|
||||
import { SolvedStep } from './solver.d.ts';
|
||||
import { loggingEnabled } from './rollUtils.ts';
|
||||
|
||||
// fullSolver(conf, wrapDetails) returns one condensed SolvedStep
|
||||
// fullSolver is a function that recursively solves the full roll and math
|
||||
|
@ -32,7 +33,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
|
|||
|
||||
// Evaluate all parenthesis
|
||||
while (conf.indexOf('(') > -1) {
|
||||
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
|
||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`);
|
||||
// Get first open parenthesis
|
||||
const openParen = conf.indexOf('(');
|
||||
let closeParen = -1;
|
||||
|
@ -40,7 +41,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
|
|||
|
||||
// Using nextParen to count the opening/closing parens, find the matching paren to openParen above
|
||||
for (let i = openParen; i < conf.length; i++) {
|
||||
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for matching ) openIdx: ${openParen} checking: ${i}`);
|
||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for matching ) openIdx: ${openParen} checking: ${i}`);
|
||||
// If we hit an open, add one (this includes the openParen we start with), if we hit a close, subtract one
|
||||
if (conf[i] === '(') {
|
||||
nextParen++;
|
||||
|
@ -83,10 +84,10 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
|
|||
// Evaluate all EMMDAS by looping thru each teir of operators (exponential is the higehest teir, addition/subtraction the lowest)
|
||||
const allCurOps = [['^'], ['*', '/', '%'], ['+', '-']];
|
||||
allCurOps.forEach((curOps) => {
|
||||
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
|
||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`);
|
||||
// Iterate thru all operators/operands in the conf
|
||||
for (let i = 0; i < conf.length; i++) {
|
||||
log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`);
|
||||
loggingEnabled && log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)} | Checking ${JSON.stringify(conf[i])}`);
|
||||
// Check if the current index is in the active teir of operators
|
||||
if (curOps.indexOf(conf[i].toString()) > -1) {
|
||||
// Grab the operands from before and after the operator
|
||||
|
@ -172,7 +173,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails:
|
|||
|
||||
// If we somehow have more than one item left in conf at this point, something broke, throw an error
|
||||
if (conf.length > 1) {
|
||||
log(LT.LOG, `ConfWHAT? ${JSON.stringify(conf)}`);
|
||||
loggingEnabled && log(LT.LOG, `ConfWHAT? ${JSON.stringify(conf)}`);
|
||||
throw new Error('ConfWhat');
|
||||
} else if (singleNum && (typeof (conf[0]) === 'number')) {
|
||||
// If we are only left with a number, populate the stepSolve with it
|
||||
|
|
Loading…
Reference in New Issue