Bug - Fixed divide by zero error when consuming spleen item

Manendra

New member
Debug log is attached. When garbo hit the spleen consumption part of the diet, it errored out. Refreshing the session and re-running hit the same thing. I had just built mafia from source, so functionally running r26986. Same script ran fine this morning on r26983.

I reverted the SpleenItemRequest.java change in PR #1317, rebuilt mafia, and re-ran the script and it got through fine. I don't understand that chunk of code well enough to figure out why it was causing the error, but timing and the log suggested that was the culprit.
 

Attachments

  • DEBUG_20221130.txt
    139.1 KB · Views: 4
I just ran into this, and think I have it narrowed down:

we're using the item name to get the spleenHit, and both Extrovermectin and Breathitin (both of which I've observed this issue for) have special characters, while Agua de Vida and Twinkly Wads (which I haven't observed this issue with) don't.

I think the easiest solution is to just use the item ID instead?
 

Veracity

Developer
Staff member
Code:
> ash $item[Extrovermectin™].spleen

Returned: 2

> ash $item[Extrovermectin™].spleen

Changing "Extrovermectin™" to "Extrovermectin™" would get rid of this message. (char 7 to char 22)
Returned: 2
Notice that if you use KoL's actual name for the item (which is what KoLmafia uses internally), it has an HTML entity - not a "special character".

How is the name with a "special character" getting passed into KoLmafia internals?
I can't reproduce this with ASH.
Is this a JS issue?
 
The issue isn't with the spleen proxy record, but with dailyusesleft, and it does appear to exist with ash:
Code:
> ash $item[extrovermectin].dailyusesleft

Changing "extrovermectin" to "Extrovermectin™" would get rid of this message. (char 7 to char 21)
Returned: 2147483647

What I assume is happening is that at the line in question, ConsumableDatabase is failing to look up the item by name and is thus passing 0 as the value for spleenHit into SpleenItemRequest.maximumUses. I'm not exactly sure why there being an HTML entity (thanks for the correction!) would impact that, but I'm getting the same result for Breathitin
Code:
> ash $item[Breathitin™].dailyusesleft

Returned:    2147483647
but not for Twinkly Wads

Code:
> ash $item[Twinkly Wad].dailyusesleft

Returned: 15

EDIT:
Code:
> js Item.all().filter((i) => i.spleen && i.name.includes("&"))

Returned: aggregate item [8]
0 => Breathetastic™ Premium Canned Air
1 => Five Second Energy™
2 => Party-in-a-Can™
3 => Shantix™
4 => Homebodyl™
5 => Extrovermectin™
6 => Breathitin™
7 => Fleshazole™

> js Item.all().filter((i) => i.spleen && i.name.includes("&")).forEach((i) => print(i.dailyusesleft))

2147483647
2147483647
2147483647
2147483647
2147483647
2147483647
2147483647
2147483647
Returned: org.mozilla.javascript.Undefined@74f8ae57

> js Item.all().filter((i) => i.spleen && i.dailyusesleft === 2147483647)

Returned: aggregate item [9]
0 => Breathetastic™ Premium Canned Air
1 => [5140]astral energy drink
2 => Five Second Energy™
3 => Party-in-a-Can™
4 => Shantix™
5 => Homebodyl™
6 => Extrovermectin™
7 => Breathitin™
8 => Fleshazole™

Looks like the only item that's happening to that isn't using ™ is [5140]astral energy drink, which does have brackets in its name.
 
Last edited:

Veracity

Developer
Staff member
Here's the ASH proxy record access method for .dailyusesleft:

Code:
    public int get_dailyusesleft() {
      return UseItemRequest.maximumUses((int) this.contentLong);
    }
Interestingly, it is calling the internal function with the itemid.

Edit:

I added logging to UseItemRequest.maximumUses:

Code:
    System.out.println("itemId = " + itemId + " itemName = '" + itemName + "'");
    int spleenHit = ConsumablesDatabase.getSpleenHit(itemName);
    System.out.println(spleenHit);

and this:
Code:
ash $item[Extorvermectin™].dailyusesleft

yielded this on my terminal window:

Code:
itemId = 10829 itemName = 'Extrovermectin™'
0

UseItemRequest, given an item id, is doing this:

Code:
  public static final int maximumUses(final int itemId) {
    String itemName = ItemDatabase.getItemName(itemId);
    return UseItemRequest.maximumUses(itemId, itemName, ConsumptionType.NONE, true);
  }

I'd wager it should be calling ItemDatabase.getItemDataName(itemId)
 
That's calling this overload of maximumUses, which passes ConsumptionType.NONE into the full implementation of the function. That does bypass the area of code I highlighted earlier, which only applies to ConsumptionType.SPLEEN, but we still end up using item name to look up organ size here. If we assume that it's then saying that spleenHit is 0, it'll pass on to the next phase, where it tries to determine maximumUses by looking at restoration (none of these items restore hp or mp), and then goes into a long switch statement for various special-cased items. At the very end of that list, we're returning maxint.

So it looks like it is related but not identical to the issue with actually chewing these items--chewing will pass ConsumptionType.SPLEEN, sending us down a different path through UseItemRequest.maximumUses, but it'll still end up looking up spleenHit using the item name. Unless my hunch about the lookup-by-name being the issue is wrong, I suppose.
 

Veracity

Developer
Staff member
Yeah. Try this:

Code:
diff --git a/src/net/sourceforge/kolmafia/request/UseItemRequest.java b/src/net/sourceforge/kolmafia/request/UseItemRequest.java
index 957339de09..cd9c913658 100644
--- a/src/net/sourceforge/kolmafia/request/UseItemRequest.java
+++ b/src/net/sourceforge/kolmafia/request/UseItemRequest.java
@@ -343,17 +343,20 @@ public class UseItemRequest extends GenericRequest {
   }
 
   public static final int maximumUses(final int itemId) {
-    String itemName = ItemDatabase.getItemName(itemId);
+    String itemName = ItemDatabase.getItemDataName(itemId);
     return UseItemRequest.maximumUses(itemId, itemName, ConsumptionType.NONE, true);
   }
 
   public static final int maximumUses(final int itemId, final ConsumptionType consumptionType) {
-    String itemName = ItemDatabase.getItemName(itemId);
+    String itemName = ItemDatabase.getItemDataName(itemId);
     return UseItemRequest.maximumUses(itemId, itemName, consumptionType, true);
   }
 
-  public static final int maximumUses(final String itemName) {
+  public static final int maximumUses(String itemName) {
     int itemId = ItemDatabase.getItemId(itemName);
+    if (itemId > 0) {
+      itemName = ItemDatabase.getItemDataName(itemId);
+    }
     return UseItemRequest.maximumUses(itemId, itemName, ConsumptionType.NONE, false);
   }
 
Last edited:

Veracity

Developer
Staff member
By the way, I assume we use item names in ConsumableDatabase rather than itemId is that there are consumables which are not items.
Sushi, for example. Or hot dogs. Or some Cafe foods.
Which is why I just modified my patch above. :)
 

Irrat

Member
What I figure is that ItemDatabase saves the display name in decoded form, but ConsumableDatabase saves it in encoded form.

It fetched the name from ItemDatabase and tried to use it in ConsumableDatabase.

Edit: Looks like I missed Veracity's comment
 
Top