Skip to content

Instantly share code, notes, and snippets.

@janssen-io
Created July 27, 2020 07:22
Show Gist options
  • Save janssen-io/385eea66613a43366dbb8a1a31d81831 to your computer and use it in GitHub Desktop.
Save janssen-io/385eea66613a43366dbb8a1a31d81831 to your computer and use it in GitHub Desktop.
FoundryVTT: Find which (combination) of modules is causing issues
/*
The idea is to prevent testing every possible combination (2 to the power of the number of modules) to
see which combination of modules is causing issues.
TODO: try to find a smaller group using binary search
Halfway done: toggle every module to see if it's needed to reproduce the issues.
*/
declare var game: any;
declare function duplicate<T>(arg: T): T;
type TroubleshooterFlag = {
iteration: number,
troublemakers: number,
modules: string[],
lastFailure: number
}
// create a mask with a 1 for every module
const max = modules => Math.pow(2, modules.length) - 1;
// create a mask for the current iteration. That is:
// 0: 1111
// 1: 1110
// 2: 1101
// 3: 1011
// 4: 0111
const attempt = (flag: TroubleshooterFlag) =>
max(flag.modules) - Math.floor(Math.pow(2, flag.iteration - 1)) | flag.troublemakers;
// if there is no problem in the current attempt, then the module that was
// switched off is causing issues.
function calculateTroublemakers(flag: TroubleshooterFlag, hasProblem: boolean) {
const currentAttempt = attempt(flag);
if (hasProblem)
flag.lastFailure = currentAttempt;
return (flag.lastFailure ^ currentAttempt) | flag.troublemakers;
}
// Show a simple dialog with yes/no/reset buttons
// yes: true, no: false, reset: reset (reloads)
function prompt(flag: TroubleshooterFlag): Promise<boolean> {
return new Promise((resolve, reject) => {
new Dialog({
buttons: {
yes: {
label: 'yes',
callback: () => resolve(true)
},
no: {
label: 'no',
callback: () => resolve(false)
},
reset: {
label: 'reset',
callback: () => reset(flag)
}
}
}).render(true);
})
}
function updateFlags(flag: TroubleshooterFlag): Promise<unknown> {
return game.user.setFlag('world', 'troubleshooter', flag)
}
// duplicate the flags, so Foundry can properly diff.
function getData(): TroubleshooterFlag {
return duplicate(game.user.getFlag('world', 'troubleshooter') || {});
}
// transform decimal number into binary with appropriate padding
const toBits = (num, modules) => num.toString(2).padStart(modules.length, '0');
// enable/disable the appropriate modules
// ! reloads the game !
function updateModules(next, modules) {
const allModules = game.settings.get('core', 'moduleConfiguration');
const bits = toBits(next, modules);
const updates = {};
for(let i = 0; i < bits.length; i++) {
updates[modules[i]] = bits[i] === "1";
}
game.settings.set('core', 'moduleConfiguration', Object.assign({}, allModules, updates));
}
// tidy-ui = love
const EXCLUDED = [ 'tidy-ui_game-settings' ]
function getSortedEnabledModules() {
const modules = game.settings.get('core', 'moduleConfiguration');
return Object.entries(modules)
.filter(([name, active]) => active && !EXCLUDED.includes(name))
.map(([name, active]) => name)
.sort((a, b) => a.toLowerCase() - b.toLowerCase());
}
// re-enable all modules that were initially enabled
// and clear troubleshooter flags
async function reset(flag: TroubleshooterFlag) {
const modules = flag.modules;
let enableAll = max(modules);
await game.user.unsetFlag('world', 'troubleshooter');
updateModules(enableAll, modules);
}
async function run() {
const modules = getSortedEnabledModules();
let lastAttempt = getData();
if (!lastAttempt.iteration) {
lastAttempt = {
iteration: 0,
lastFailure: max(modules),
troublemakers: 0,
modules
};
}
console.log(lastAttempt);
console.log('mask', toBits(lastAttempt.troublemakers, lastAttempt.modules));
console.log('lastFailure', toBits(lastAttempt.lastFailure, lastAttempt.modules));
const hasProblem = await prompt(lastAttempt);
const troublemakers = calculateTroublemakers(lastAttempt, hasProblem);
const nextAttempt: TroubleshooterFlag = {
...lastAttempt,
iteration: lastAttempt.iteration + 1,
lastFailure: hasProblem ? attempt(lastAttempt) : lastAttempt.lastFailure,
troublemakers
}
await updateFlags(nextAttempt);
updateModules(attempt(nextAttempt), nextAttempt.modules);
}
run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment