Bug JavaScript bugs

To gausie: Thanks, see discord for further discussion

@ikzann: Thanks. I tried to make a relay script in JS but attempting to launch it from the top menu results in an error message printed to the gCLI:

Code:
JavaScript exception: Error: Module "file:/C:/Users/Phil/Documents/KoL/relay/relay_test.js" is not contained in sandbox.

This script only contains this:
JavaScript:
const { write } = require("kolmafia");

module.exports.main = function () {
  write("Hello, world!");
};

I tested it with r20534, r20531, r20527, r20524 (with necessary modifications to make it work) but all of them failed.
 
Huh, that's odd. Might be a Windows issue specifically since I have been doing all my development on a Mac...

If you have time, it would be helpful to see where in the Rhino code that exception is coming from. It's supposed to be restricting execution only to scripts that reside within the Mafia directories, either via symlink or actual presence. I assume your Mafia directory is just C:/Users/Phil/Documents/KoL? Maybe ROOT_LOCATION in KoLConstants is not set correctly?
 
Once I realized that the test was to copy the code into something in the relay subdirectory and name it relay-...js I got the same error on Windows,

JavaScript exception: Error: Module "file:/D:/Dropbox/dist/relay/relay_qt.js" is not contained in sandbox.

I think this shows my ROOT_LOCATION and AFAIK everything else works :)

Capture.JPG
 
Okay. I'll set up the dev environment on my Windows computer this evening and see if I can come up with a fix. Thanks for flagging this.
 
Okay. I'll set up the dev environment on my Windows computer this evening and see if I can come up with a fix. Thanks for flagging this.
If someone posts a file I can just download and place in he right subdirectory I have no problems running a test on Windows. It's when I have to paste into a properly named text file that I get lazy and am less inclined to help.
 
I get the same error with phil's code on my mac.

JavaScript exception: Error: Module "file:/Users/mcroft/Library/Application%20Support/KoLmafia/relay/relay_test.js" is not contained in sandbox.

The Internet tells me that that error comes from Rhino, but the rabbit hole starts me looking at bugs from 2012, so I'm going to leave that to you.
 
Putzing around:

  1. If no predicate, return help
  2. seems correct
  3. would be better to throw an error like #2 rather than an NPE (even if no one should ever do what I do IRL).

Code:
> js
Returned: org.mozilla.javascript.Undefined@0

> js nothing at all
JavaScript evaluator exception: missing ; before statement (command line#1)
Returned: null

> js require("kolmafia")
Unexpected error, debug log printed.

class java.lang.NullPointerException: null
java.lang.NullPointerException
    at net.sourceforge.kolmafia.textui.parsetree.AggregateType.toString(AggregateType.java:146)
    at net.sourceforge.kolmafia.textui.parsetree.AggregateValue.toString(AggregateValue.java:65)
    at java.base/java.lang.String.valueOf(String.java:2949)
    at java.base/java.lang.StringBuilder.append(StringBuilder.java:166)
    at net.sourceforge.kolmafia.textui.command.JavaScriptCommand.run(JavaScriptCommand.java:63)
    at net.sourceforge.kolmafia.KoLmafiaCLI.doExecuteCommand(KoLmafiaCLI.java:594)
    at net.sourceforge.kolmafia.KoLmafiaCLI.executeCommand(KoLmafiaCLI.java:547)
    at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:449)
    at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:317)
    at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.handleQueue(CommandDisplayFrame.java:199)
    at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.run(CommandDisplayFrame.java:168)

Method threw 'java.lang.NullPointerException' exception. Cannot evaluate net.sourceforge.kolmafia.textui.parsetree.MapValue.toString()
 
Alright, this patch fixes both the sandbox issue and the issue with trying to print require("kolmafia"). I've left the behavior with no input unchanged for now as we should fix the ASH command as well. The sandboxing rule is now that any file in the Mafia directories can be run by the JS interpreter. If that is a problem let me know, and I can think about how to tweak it. require() now tries to resolve first against KoLmafia/ and then against KoLmafia/scripts/. I expect people will not want to use scripts/whatever/whatever for a require path, though.

It also has a fix to an issue where running a nested JS interpreter did not work due to the incorrect assumption I had made that there would be only one interpreter per thread. This can happen when, e.g. a JS script calls another one via the CLI, or a JS script uses a JS consult script.
 

Attachments

I don't actually mind #1, but you may want special casing around the undefined return object.

Code:
> ash
Script parsing error ()
Returned: void

> js
Returned: org.mozilla.javascript.Undefined@0

> js let a = 12
Returned: org.mozilla.javascript.Undefined@0

> js a =12
Returned: 12

> ash a =12
Unknown variable 'a' ()
Returned: void

> ash int a =12
Returned: 12
 
The sandboxing rule is now that any file in the Mafia directories can be run by the JS interpreter.

The right answer is that JS files can run from any directory that ash or "cli" files can run from. I thought that was /relay, /scripts and /planting only but I just made KoLmafia try and run /data/batfactors.txt and got an expected and reasonable error. So that might be the right answer.
 
That is not Mafia's current behavior when loading cli/ash files. If that's an issue we should probably take this to PM
 
Alright, this patch fixes both the sandbox issue and the issue with trying to print require("kolmafia"). I've left the behavior with no input unchanged for now as we should fix the ASH command as well. The sandboxing rule is now that any file in the Mafia directories can be run by the JS interpreter. If that is a problem let me know, and I can think about how to tweak it. require() now tries to resolve first against KoLmafia/ and then against KoLmafia/scripts/. I expect people will not want to use scripts/whatever/whatever for a require path, though.

It also has a fix to an issue where running a nested JS interpreter did not work due to the incorrect assumption I had made that there would be only one interpreter per thread. This can happen when, e.g. a JS script calls another one via the CLI, or a JS script uses a JS consult script.
Test results of patch:

Running Phil's "Hello World!" relay/relay_test.js example doesn't give the sandbox error, but still throws an error.
Code:
JavaScript exception: Error: Module "relay/kolmafia" not found. (file:/Users/mcroft/Library/Application%20Support/KoLmafia/relay/relay_test.js#1)
at file:/Users/mcroft/Library/Application%20Support/KoLmafia/relay/relay_test.js:1
 
after patch, require("kolmafia") doesn't throw an error, but it is an unexpected (by me) return...

edit: apparently it's what you should get in this case, so it was just me...
Code:
> js require("kolmafia")

Returned: aggregate string [string]
abort => [function abort]
addItemCondition => [function addItemCondition]
adv1 => [function adv1]
advCost => [function advCost]
adventure => [function adventure]
allMonstersWithId => [function allMonstersWithId]
allNormalOutfits => [function allNormalOutfits]
appearanceRates => [function appearanceRates]
append => [function append]
attack => [function attack]
autosell => [function autosell]
autosellPrice => [function autosellPrice]
availableAmount => [function availableAmount]
availableChoiceOptions => [function availableChoiceOptions]
availableChoiceSelectInputs => [function availableChoiceSelectInputs]
availableChoiceTextInputs => [function availableChoiceTextInputs]
availablePocket => [function availablePocket]
batchClose => [function batchClose]
batchOpen => [function batchOpen]
bjornifyFamiliar => [function bjornifyFamiliar]
blackMarketAvailable => [function blackMarketAvailable]
booleanModifier => [function booleanModifier]
buffedHitStat => [function buffedHitStat]
bufferToFile => [function bufferToFile]
buy => [function buy]
buyPrice => [function buyPrice]
buyUsingStorage => [function buyUsingStorage]
buysItem => [function buysItem]
canDrink => [function canDrink]
canEat => [function canEat]
canEquip => [function canEquip]
canFaxbot => [function canFaxbot]
canInteract => [function canInteract]
canStillSteal => [function canStillSteal]
canadiaAvailable => [function canadiaAvailable]
candyForTier => [function candyForTier]
ceil => [function ceil]
changeMcd => [function changeMcd]
charAt => [function charAt]
chatClan => [function chatClan]
chatMacro => [function chatMacro]
chatNotify => [function chatNotify]
chatPrivate => [function chatPrivate]
chew => [function chew]
choiceFollowsFight => [function choiceFollowsFight]
classModifier => [function classModifier]
clear => [function clear]
cliExecute => [function cliExecute]
cliExecuteOutput => [function cliExecuteOutput]
closetAmount => [function closetAmount]
combatManaCostModifier => [function combatManaCostModifier]
combatRateModifier => [function combatRateModifier]
containsText => [function containsText]
council => [function council]
count => [function count]
craft => [function craft]
craftType => [function craftType]
creatableAmount => [function creatableAmount]
creatableTurns => [function creatableTurns]
create => [function create]
currentHitStat => [function currentHitStat]
currentMcd => [function currentMcd]
currentPvpStances => [function currentPvpStances]
currentRadSickness => [function currentRadSickness]
currentRound => [function currentRound]
dadSeaMonkeeWeakness => [function dadSeaMonkeeWeakness]
dailySpecial => [function dailySpecial]
damageAbsorptionPercent => [function damageAbsorptionPercent]
damageReduction => [function damageReduction]
dateToTimestamp => [function dateToTimestamp]
debugprint => [function debugprint]
delete => [function delete]
descToEffect => [function descToEffect]
descToItem => [function descToItem]
disable => [function disable]
dispensaryAvailable => [function dispensaryAvailable]
displayAmount => [function displayAmount]
drink => [function drink]
drinksilent => [function drinksilent]
dump => [function dump]
eat => [function eat]
eatsilent => [function eatsilent]
effectModifier => [function effectModifier]
effectPockets => [function effectPockets]
elementalResistance => [function elementalResistance]
emptyCloset => [function emptyCloset]
enable => [function enable]
endsWith => [function endsWith]
enthroneFamiliar => [function enthroneFamiliar]
entityDecode => [function entityDecode]
entityEncode => [function entityEncode]
equip => [function equip]
equipAllFamiliars => [function equipAllFamiliars]
equippedAmount => [function equippedAmount]
equippedItem => [function equippedItem]
eudora => [function eudora]
everyCardName => [function everyCardName]
expectedDamage => [function expectedDamage]
experienceBonus => [function experienceBonus]
expressionEval => [function expressionEval]
extractItems => [function extractItems]
extractMeat => [function extractMeat]
familiarEquipment => [function familiarEquipment]
familiarEquippedEquipment => [function familiarEquippedEquipment]
familiarWeight => [function familiarWeight]
favoriteFamiliars => [function favoriteFamiliars]
faxbot => [function faxbot]
fightFollowsChoice => [function fightFollowsChoice]
fileToArray => [function fileToArray]
fileToBuffer => [function fileToBuffer]
fileToMap => [function fileToMap]
floor => [function floor]
floristAvailable => [function floristAvailable]
flushMonsterManuelCache => [function flushMonsterManuelCache]
formField => [function formField]
formFields => [function formFields]
formatDateTime => [function formatDateTime]
friarsAvailable => [function friarsAvailable]
fuelCost => [function fuelCost]
fullnessLimit => [function fullnessLimit]
gamedayToInt => [function gamedayToInt]
gamedayToString => [function gamedayToString]
gametimeToInt => [function gametimeToInt]
getAllProperties => [function getAllProperties]
getAutoAttack => [function getAutoAttack]
getCampground => [function getCampground]
getCcsAction => [function getCcsAction]
getChateau => [function getChateau]
getClanId => [function getClanId]
getClanLounge => [function getClanLounge]
getClanName => [function getClanName]
getClanRumpus => [function getClanRumpus]
getCloset => [function getCloset]
getCounters => [function getCounters]
getCustomOutfits => [function getCustomOutfits]
getDwelling => [function getDwelling]
getFloristPlants => [function getFloristPlants]
getFreePulls => [function getFreePulls]
getFuel => [function getFuel]
getGoals => [function getGoals]
getIgnoreZoneWarnings => [function getIgnoreZoneWarnings]
getIngredients => [function getIngredients]
getInventory => [function getInventory]
getLocationMonsters => [function getLocationMonsters]
getMonsterMapping => [function getMonsterMapping]
getMonsters => [function getMonsters]
getMoods => [function getMoods]
getOutfits => [function getOutfits]
getPath => [function getPath]
getPathFull => [function getPathFull]
getPathVariables => [function getPathVariables]
getPlayerId => [function getPlayerId]
getPlayerName => [function getPlayerName]
getPower => [function getPower]
getProperty => [function getProperty]
getRelated => [function getRelated]
getRevision => [function getRevision]
getShop => [function getShop]
getShopLog => [function getShopLog]
getStackTrace => [function getStackTrace]
getStash => [function getStash]
getStorage => [function getStorage]
getVersion => [function getVersion]
gnomadsAvailable => [function gnomadsAvailable]
goalExists => [function goalExists]
groupString => [function groupString]
guildStoreAvailable => [function guildStoreAvailable]
handlingChoice => [function handlingChoice]
haveBartender => [function haveBartender]
haveChef => [function haveChef]
haveDisplay => [function haveDisplay]
haveEffect => [function haveEffect]
haveEquipped => [function haveEquipped]
haveFamiliar => [function haveFamiliar]
haveMushroomPlot => [function haveMushroomPlot]
haveOutfit => [function haveOutfit]
haveServant => [function haveServant]
haveShop => [function haveShop]
haveSkill => [function haveSkill]
hedgeMaze => [function hedgeMaze]
hermit => [function hermit]
hiddenTempleUnlocked => [function hiddenTempleUnlocked]
hippyStoneBroken => [function hippyStoneBroken]
hippyStoreAvailable => [function hippyStoreAvailable]
historicalAge => [function historicalAge]
historicalPrice => [function historicalPrice]
holiday => [function holiday]
hpCost => [function hpCost]
imageToMonster => [function imageToMonster]
inBadMoon => [function inBadMoon]
inHardcore => [function inHardcore]
inMoxieSign => [function inMoxieSign]
inMultiFight => [function inMultiFight]
inMuscleSign => [function inMuscleSign]
inMysticalitySign => [function inMysticalitySign]
inaccessibleReason => [function inaccessibleReason]
indexOf => [function indexOf]
inebrietyLimit => [function inebrietyLimit]
initiativeModifier => [function initiativeModifier]
insert => [function insert]
isAccessible => [function isAccessible]
isBanished => [function isBanished]
isCoinmasterItem => [function isCoinmasterItem]
isDiscardable => [function isDiscardable]
isDisplayable => [function isDisplayable]
isFamiliarEquipmentLocked => [function isFamiliarEquipmentLocked]
isGiftable => [function isGiftable]
isGoal => [function isGoal]
isInteger => [function isInteger]
isNpcItem => [function isNpcItem]
isOnline => [function isOnline]
isTradeable => [function isTradeable]
isTrendy => [function isTrendy]
isUnrestricted => [function isUnrestricted]
isWearingOutfit => [function isWearingOutfit]
itemAmount => [function itemAmount]
itemDropModifier => [function itemDropModifier]
itemDrops => [function itemDrops]
itemDropsArray => [function itemDropsArray]
itemPockets => [function itemPockets]
itemType => [function itemType]
jokePockets => [function jokePockets]
jumpChance => [function jumpChance]
knollAvailable => [function knollAvailable]
lastChoice => [function lastChoice]
lastDecision => [function lastDecision]
lastIndexOf => [function lastIndexOf]
lastItemMessage => [function lastItemMessage]
lastMonster => [function lastMonster]
lastSkillMessage => [function lastSkillMessage]
leetify => [function leetify]
length => [function length]
lightningCost => [function lightningCost]
limitMode => [function limitMode]
loadHtml => [function loadHtml]
lockFamiliarEquipment => [function lockFamiliarEquipment]
logN => [function logN]
logprint => [function logprint]
makeUrl => [function makeUrl]
mallPrice => [function mallPrice]
mallPrices => [function mallPrices]
manaCostModifier => [function manaCostModifier]
mapToFile => [function mapToFile]
max => [function max]
maximize => [function maximize]
meatDrop => [function meatDrop]
meatDropModifier => [function meatDropModifier]
meatPockets => [function meatPockets]
min => [function min]
minstrelInstrument => [function minstrelInstrument]
minstrelLevel => [function minstrelLevel]
minstrelQuest => [function minstrelQuest]
modifierEval => [function modifierEval]
monsterAttack => [function monsterAttack]
monsterDefense => [function monsterDefense]
monsterElement => [function monsterElement]
monsterEval => [function monsterEval]
monsterFactoidsAvailable => [function monsterFactoidsAvailable]
monsterHp => [function monsterHp]
monsterInitiative => [function monsterInitiative]
monsterLevelAdjustment => [function monsterLevelAdjustment]
monsterManuelText => [function monsterManuelText]
monsterPhylum => [function monsterPhylum]
monsterPockets => [function monsterPockets]
moodExecute => [function moodExecute]
moodList => [function moodList]
moonLight => [function moonLight]
moonPhase => [function moonPhase]
mpCost => [function mpCost]
myAbsorbs => [function myAbsorbs]
myAdventures => [function myAdventures]
myAscensions => [function myAscensions]
myAudience => [function myAudience]
myBasestat => [function myBasestat]
myBjornedFamiliar => [function myBjornedFamiliar]
myBuffedstat => [function myBuffedstat]
myClass => [function myClass]
myClosetMeat => [function myClosetMeat]
myCompanion => [function myCompanion]
myDaycount => [function myDaycount]
myDiscomomentum => [function myDiscomomentum]
myEffectiveFamiliar => [function myEffectiveFamiliar]
myEffects => [function myEffects]
myEnthronedFamiliar => [function myEnthronedFamiliar]
myFamiliar => [function myFamiliar]
myFullness => [function myFullness]
myFury => [function myFury]
myGardenType => [function myGardenType]
myHash => [function myHash]
myHp => [function myHp]
myId => [function myId]
myInebriety => [function myInebriety]
myLevel => [function myLevel]
myLightning => [function myLightning]
myLocation => [function myLocation]
myMask => [function myMask]
myMaxfury => [function myMaxfury]
myMaxhp => [function myMaxhp]
myMaxmp => [function myMaxmp]
myMaxpp => [function myMaxpp]
myMeat => [function myMeat]
myMp => [function myMp]
myName => [function myName]
myPath => [function myPath]
myPathId => [function myPathId]
myPokeFam => [function myPokeFam]
myPp => [function myPp]
myPrimestat => [function myPrimestat]
myRain => [function myRain]
myServant => [function myServant]
mySessionAdv => [function mySessionAdv]
mySessionItems => [function mySessionItems]
mySessionMeat => [function mySessionMeat]
mySign => [function mySign]
mySoulsauce => [function mySoulsauce]
mySpleenUse => [function mySpleenUse]
myStorageMeat => [function myStorageMeat]
myThrall => [function myThrall]
myThunder => [function myThunder]
myTurncount => [function myTurncount]
myVykeaCompanion => [function myVykeaCompanion]
nowToInt => [function nowToInt]
nowToString => [function nowToString]
npcPrice => [function npcPrice]
numberologyPrize => [function numberologyPrize]
numericModifier => [function numericModifier]
outfit => [function outfit]
outfitPieces => [function outfitPieces]
outfitTattoo => [function outfitTattoo]
overdrink => [function overdrink]
pathIdToName => [function pathIdToName]
pathNameToId => [function pathNameToId]
pickPocket => [function pickPocket]
pickedPockets => [function pickedPockets]
pickedScraps => [function pickedScraps]
pocketEffects => [function pocketEffects]
pocketItems => [function pocketItems]
pocketJoke => [function pocketJoke]
pocketMeat => [function pocketMeat]
pocketMonster => [function pocketMonster]
pocketPoem => [function pocketPoem]
pocketScrap => [function pocketScrap]
pocketStats => [function pocketStats]
poemPockets => [function poemPockets]
potentialPockets => [function potentialPockets]
print => [function print]
printHtml => [function printHtml]
propertyDefaultValue => [function propertyDefaultValue]
propertyExists => [function propertyExists]
propertyHasDefault => [function propertyHasDefault]
pullsRemaining => [function pullsRemaining]
putCloset => [function putCloset]
putDisplay => [function putDisplay]
putShop => [function putShop]
putShopUsingStorage => [function putShopUsingStorage]
putStash => [function putStash]
pvpAttacksLeft => [function pvpAttacksLeft]
rainCost => [function rainCost]
random => [function random]
rawDamageAbsorption => [function rawDamageAbsorption]
refreshShop => [function refreshShop]
refreshStash => [function refreshStash]
refreshStatus => [function refreshStatus]
removeItemCondition => [function removeItemCondition]
removeProperty => [function removeProperty]
renameProperty => [function renameProperty]
replace => [function replace]
replaceString => [function replaceString]
repriceShop => [function repriceShop]
restorationPockets => [function restorationPockets]
restoreHp => [function restoreHp]
restoreMp => [function restoreMp]
retrieveItem => [function retrieveItem]
reverseNumberology => [function reverseNumberology]
rollover => [function rollover]
round => [function round]
runChoice => [function runChoice]
runCombat => [function runCombat]
runTurn => [function runTurn]
runaway => [function runaway]
scrapPockets => [function scrapPockets]
sell => [function sell]
sellPrice => [function sellPrice]
sellsItem => [function sellsItem]
sessionLogs => [function sessionLogs]
setAutoAttack => [function setAutoAttack]
setLength => [function setLength]
setLocation => [function setLocation]
setProperty => [function setProperty]
shopAmount => [function shopAmount]
shopLimit => [function shopLimit]
shopPrice => [function shopPrice]
skillModifier => [function skillModifier]
slashCount => [function slashCount]
soulsauceCost => [function soulsauceCost]
spleenLimit => [function spleenLimit]
splitString => [function splitString]
squareRoot => [function squareRoot]
startsWith => [function startsWith]
stashAmount => [function stashAmount]
statBonusToday => [function statBonusToday]
statBonusTomorrow => [function statBonusTomorrow]
statModifier => [function statModifier]
statsPockets => [function statsPockets]
steal => [function steal]
stillsAvailable => [function stillsAvailable]
stopCounter => [function stopCounter]
storageAmount => [function storageAmount]
stringModifier => [function stringModifier]
stunSkill => [function stunSkill]
substring => [function substring]
svnAtHead => [function svnAtHead]
svnExists => [function svnExists]
svnInfo => [function svnInfo]
sweetSynthesis => [function sweetSynthesis]
sweetSynthesisPair => [function sweetSynthesisPair]
sweetSynthesisPairing => [function sweetSynthesisPairing]
sweetSynthesisResult => [function sweetSynthesisResult]
takeCloset => [function takeCloset]
takeDisplay => [function takeDisplay]
takeShop => [function takeShop]
takeStash => [function takeStash]
takeStorage => [function takeStorage]
tavern => [function tavern]
throwItem => [function throwItem]
throwItems => [function throwItems]
thunderCost => [function thunderCost]
timeToString => [function timeToString]
timestampToDate => [function timestampToDate]
toBoolean => [function toBoolean]
toBounty => [function toBounty]
toClass => [function toClass]
toCoinmaster => [function toCoinmaster]
toEffect => [function toEffect]
toElement => [function toElement]
toFamiliar => [function toFamiliar]
toFloat => [function toFloat]
toInt => [function toInt]
toItem => [function toItem]
toJson => [function toJson]
toLocation => [function toLocation]
toLowerCase => [function toLowerCase]
toMonster => [function toMonster]
toPhylum => [function toPhylum]
toPlural => [function toPlural]
toServant => [function toServant]
toSkill => [function toSkill]
toSlot => [function toSlot]
toStat => [function toStat]
toString => [function toString]
toThrall => [function toThrall]
toUpperCase => [function toUpperCase]
toUrl => [function toUrl]
toVykea => [function toVykea]
todayToString => [function todayToString]
totalFreeRests => [function totalFreeRests]
totalTurnsPlayed => [function totalTurnsPlayed]
towerDoor => [function towerDoor]
traceprint => [function traceprint]
truncate => [function truncate]
turnsPerCast => [function turnsPerCast]
turnsPlayed => [function turnsPlayed]
twiddle => [function twiddle]
unusualConstructDisc => [function unusualConstructDisc]
updateCandyPrices => [function updateCandyPrices]
urlDecode => [function urlDecode]
urlEncode => [function urlEncode]
use => [function use]
useFamiliar => [function useFamiliar]
useServant => [function useServant]
useSkill => [function useSkill]
userConfirm => [function userConfirm]
visit => [function visit]
visitUrl => [function visitUrl]
votingBoothInitiatives => [function votingBoothInitiatives]
wait => [function wait]
waitq => [function waitq]
weaponHands => [function weaponHands]
weaponType => [function weaponType]
weightAdjustment => [function weightAdjustment]
whiteCitadelAvailable => [function whiteCitadelAvailable]
whoClan => [function whoClan]
willUsuallyDodge => [function willUsuallyDodge]
willUsuallyMiss => [function willUsuallyMiss]
write => [function write]
writeln => [function writeln]
xpath => [function xpath]
 
Last edited:
That is not Mafia's current behavior when loading cli/ash files. If that's an issue we should probably take this to PM

I probably wasn't clear since I thought you implemented different from ash behavior when I started posting. I ran a test and decided I was wrong about ash behaviour. Bottom line, I think what you did is OK.
 
r20537

Would have done sooner but didn't notice @MCroft 's edit that said the patch was OK.

Tested, if running one relay script constitutes a test :)
Yeah, I was successful and should've been more clear that I had no objections. I was looking for a way to break it and couldn't find one, but I hadn't got around to committing it, so thanks Fronobulax...
 
Yeah, I was successful and should've been more clear that I had no objections. I was looking for a way to break it and couldn't find one, but I hadn't got around to committing it, so thanks Fronobulax...

If there is a lesson to be learned it is that I notice new posts. I often miss edits to existing ones.
 
Error in js Item.get("sword")

Unexpected error, debug log printed.
class net.sourceforge.kolmafia.textui.ScriptException: Bad item value: sword
net.sourceforge.kolmafia.textui.ScriptException: Bad item value: sword
 
Back
Top