WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   table index is nil (https://www.wowinterface.com/forums/showthread.php?t=58398)

Shitara 11-21-20 07:04 PM

table index is nil
 
Hi, I have been trying to fix this addon and my knowledge of lua is limited at best. It still works, but throws an error first time it's called then is fine. It started throwing the error in pre-patch shadowlands. And thank you in advance for help to fix this.

This is the lua error ...
Quote:

Date: 2020-10-12 12:58:10
ID: 1
Error occured in: Global
Count: 1
Message: ..\AddOns\BattleDex\BattleDex.lua line 69:
table index is nil
Debug:
[string "@BattleDex\BattleDex.lua"]:69: RecordPet()
[string "@BattleDex\BattleDex.lua"]:61: RecordBattle()
[string "@BattleDex\BattleDex.lua"]:34:
BattleDex\BattleDex.lua:23
Locals:
species = nil
level = 1
quality = 1
primary = 379
(*temporary) = <table> {
440 = <table> {
}
441 = <table> {
}
442 = <table> {
}
387 = <table> {
}
417 = <table> {
}
419 = <table> {
}
1162 = <table> {
}
675 = <table> {
}
437 = <table> {
}
378 = <table> {
}
424 = <table> {
}
379 = <table> {
}
}
(*temporary) = <table> {
}
(*temporary) = nil
(*temporary) = nil
(*temporary) = nil
(*temporary) = "table index is nil"
It only has 2 files, a TOC and Lua file
TOC file - The only thing I've changed in here is the Interface number
Code:

## Interface: 90001
## Title : BattleDex
## Notes: Track Pet Battles
## Author: Cal Henderson (Bees on Hyjal-US)
## Version: 1.1
## Dependencies:
## SavedVariablesPerCharacter: BattleDexPrefs
## SavedVariables: BattleDexDB

BattleDex.lua

Lua file
Code:

BattleDex = {};
BattleDex.default_options = {
};

function BattleDex.OnReady()

        -- init database
        _G.BattleDexDB = _G.BattleDexDB or {};
        _G.BattleDexDB.pets = _G.BattleDexDB.pets or {};

        -- set up default options
        _G.BattleDexPrefs = _G.BattleDexPrefs or {};
        local k,v;
        for k,v in pairs(BattleDex.default_options) do
                if (not _G.BattleDexPrefs[k]) then
                        _G.BattleDexPrefs[k] = v;
                end
        end

        GameTooltip:HookScript("OnTooltipSetUnit", BattleDex.AlterTooltip);
end

function BattleDex.OnEvent(frame, event, ...)

        if (event == 'ADDON_LOADED') then
                local name = ...;
                if name == 'BattleDex' then
                        BattleDex.OnReady();
                end
                return;
        end

        if (event == 'PET_BATTLE_OPENING_DONE') then
                BattleDex.RecordBattle();
        end
end

function BattleDex.RecordBattle()

        if (not C_PetBattles.IsWildBattle()) then
                return;
        end

        -- get pet info

        local s1 = C_PetBattles.GetPetSpeciesID(2, 1);
        local s2 = C_PetBattles.GetPetSpeciesID(2, 2);
        local s3 = C_PetBattles.GetPetSpeciesID(2, 3);

        local l1 = C_PetBattles.GetLevel(2, 1);
        local l2 = C_PetBattles.GetLevel(2, 2);
        local l3 = C_PetBattles.GetLevel(2, 3);

        local r1 = C_PetBattles.GetBreedQuality(2, 1);
        local r2 = C_PetBattles.GetBreedQuality(2, 2);
        local r3 = C_PetBattles.GetBreedQuality(2, 3);

        -- record each pet

        BattleDex.RecordPet(s1, l1, r1, 0);
        BattleDex.RecordPet(s2, l2, r2, s1);
        BattleDex.RecordPet(s3, l3, r3, s1);
end

function BattleDex.RecordPet(species, level, quality, primary)

        --print(string.format("s=%d, l=%d, q=%d, p=%d", species, level, quality, primary));

        _G.BattleDexDB.pets[species] = _G.BattleDexDB.pets[species] or {};

        local key = primary.."_"..level.."_"..quality;

        _G.BattleDexDB.pets[species][key] = _G.BattleDexDB.pets[species][key] or 0;
        _G.BattleDexDB.pets[species][key] = _G.BattleDexDB.pets[species][key] + 1;
end

function BattleDex.AlterTooltip()

        local _, unit = GameTooltip:GetUnit();
        if (not unit) then return; end;
        if (not UnitIsWildBattlePet(unit)) then return; end;

        local species = UnitBattlePetSpeciesID(unit);

        -- is this pet in our DB at all?
        if (not _G.BattleDexDB.pets[species]) then
                GameTooltip:AddLine("|cFF9999FFNever battled");
                GameTooltip:Show();
                return;
        end

        -- make a new data structure of [primary -> {quality: count, quality:count}]
        local counts = {};
        local k,v;
        for k,v in pairs(_G.BattleDexDB.pets[species]) do
                local itr = string.gmatch(k, "%d+");
                local pri = tonumber(itr());
                local lvl = tonumber(itr());
                local qul = tonumber(itr());

                --GameTooltip:AddLine(string.format("%d / %d / %d", pri, qul, v));

                counts[pri] = counts[pri] or {};
                counts[pri][qul] = counts[pri][qul] or 0;
                counts[pri][qul] = counts[pri][qul] + v;
        end

        -- colors
        local _, _, _, col0 = GetItemQualityColor(0);
        local _, _, _, col1 = GetItemQualityColor(1);
        local _, _, _, col2 = GetItemQualityColor(2);
        local _, _, _, col3 = GetItemQualityColor(3);

        -- output
        for k,v in pairs(counts) do
                local pri = k;
                local num1 = v[1] or 0;
                local num2 = v[2] or 0;
                local num3 = v[3] or 0;
                local num4 = v[4] or 0;

                local nums = string.format("|c%s%d|r/|c%s%d|r/|c%s%d|r/|c%s%d|r", col0,num1,col1,num2,col2,num3,col3,num4);

                if (pri == 0) then
                        GameTooltip:AddLine("Primary: "..nums);
                else
                        local name = C_PetJournal.GetPetInfoBySpeciesID(pri);
                        GameTooltip:AddLine(name..": "..nums);
                end
        end

        GameTooltip:Show();
end


-- ############################# Slash Commands #############################

SLASH_BattleDex1 = '/bd';
SLASH_BattleDex2 = '/battledex';

function SlashCmdList.BattleDex(msg, editBox)
end

-- ############################# Event Frame #############################

BattleDex.EventFrame = CreateFrame("Frame");
BattleDex.EventFrame:Show();
BattleDex.EventFrame:SetScript("OnEvent", BattleDex.OnEvent);
BattleDex.EventFrame:SetScript("OnUpdate", BattleDex.OnUpdate);
BattleDex.EventFrame:RegisterEvent("ADDON_LOADED");
BattleDex.EventFrame:RegisterEvent("PET_BATTLE_OPENING_DONE");


Fizzlemizz 11-21-20 07:16 PM

Try moving OnReady from ADDON_LOADED to PLAYER_LOGIN. Your SavedVariables will have been loaded by the game when this event fires and it only fires once.

Don't forget to remove your old SV .lua file(s) before trying.

Lua Code:
  1. function BattleDex.OnReady()
  2.     -- init database
  3.     if not BattleDexDB then
  4.         BattleDexDB = {
  5.             pets = {},
  6.         }
  7.     end
  8.     if not BattleDexPrefs then
  9.         BattleDexPrefs = {}
  10.         for k,v in pairs(BattleDex.default_options) do
  11.             if not BattleDexPrefs[k] then
  12.                 BattleDexPrefs[k] = v
  13.             end
  14.         end
  15.     end
  16.     GameTooltip:HookScript("OnTooltipSetUnit", BattleDex.AlterTooltip);
  17. end
  18.  
  19. function BattleDex.OnEvent(frame, event, ...)
  20.     if (event == 'PLAYER_LOGIN') then
  21.         BattleDex.OnReady();
  22.     end
  23.  
  24.     if (event == 'PET_BATTLE_OPENING_DONE') then
  25.         BattleDex.RecordBattle();
  26.     end
  27. end
  28.  
  29. -- ...
  30.  
  31. BattleDex.EventFrame:RegisterEvent("PLAYER_LOGIN");
  32. BattleDex.EventFrame:RegisterEvent("PET_BATTLE_OPENING_DONE");

SDPhantom 11-21-20 09:00 PM

SavedVars should be ready by the time ADDON_LOADED fires. It's part of the addon loading process. The error isn't complaining about the table itself being nil, there's a different error for that. The problem is when you use nil as a key.

The code only works if the opponent has 3 battle pets. If they don't have a full team, this error occurs. One or more empty spots causes species in BattleDex.RecordPet() (along with most of the other arguments) to be nil. This is what the error means by "table index is nil".

Again, if this was a load issue with the SavedVar, this would be "attempt to index nil" instead. It's a common mistake to confuse these two.

Shitara 11-21-20 11:08 PM

Thank you for your help.
I changed to your code, still get the error, slightly different, but same error, when starting the first battle, and it doesn't record the first battle, but does record later battles. I removed the SV before I went in game.

I have a full team, but the wild pets don't always, testing in Darnassus where pets are single, low level. I'm fairly sure it was throwing the error on 3 wild pets, full team, too, but will go test the theory. ...

Theory tested, and No, no error when starting with wild pets that have a full team with changed lua file. Also tested my original lua file and no it doesn't error with the other team being full either. Both versions of lua recorded the first battle.

SDPhantom 11-21-20 11:46 PM

That's the spot it's complaining about. Line 69 in function BattleDex.RecordPet(). The only line there that would generate that error (there are other operations like it, but it would throw a different error generating the second key) is this.
Code:

_G.BattleDexDB.pets[species] = _G.BattleDexDB.pets[species] or {};
The error is specifically saying species is nil.



PS: The theory was about the enemy team having less than 3 pets. The only other explanation is if BattleDex.RecordBattle() is running before pet data is available.

Shitara 11-22-20 12:42 AM

So is there a way I can get it to realize that when the pet doesn't exist it's 0, zero, not nil? or that it's ok that the pet doesn't exist? and just record the existing pet/s

a couple of lines down it tells it to either record/add 0 or 1
I'm presuming that's not enough?

Code:

        _G.BattleDexDB.pets[species][key] = _G.BattleDexDB.pets[species][key] or 0;
        _G.BattleDexDB.pets[species][key] = _G.BattleDexDB.pets[species][key] + 1;


DahkCeles 11-22-20 11:32 AM

Option 1: Add a safeguard to RecordPet() so that it doesn't try to execute when information is missing.

Lua Code:
  1. function BattleDex.RecordPet(species, level, quality, primary)
  2.     if (species) then
  3.         --print(string.format("s=%d, l=%d, q=%d, p=%d", species, level, quality, primary));
  4.  
  5.         _G.BattleDexDB.pets[species] = _G.BattleDexDB.pets[species] or {};
  6.  
  7.         local key = primary.."_"..level.."_"..quality;
  8.  
  9.         _G.BattleDexDB.pets[species][key] = _G.BattleDexDB.pets[species][key] or 0;
  10.         _G.BattleDexDB.pets[species][key] = _G.BattleDexDB.pets[species][key] + 1;
  11.     end
  12. end

Option 2: Change RecordBattle() so it is smart enough to know you sometimes have fewer than three pets.

I think you can do it using C_PetBattles.GetNumPets() but its probably also doable with a simple if (s2) then ... end

Lua Code:
  1. function BattleDex.RecordBattle()
  2.  
  3.     if (not C_PetBattles.IsWildBattle()) then
  4.         return;
  5.     end
  6.  
  7.     -- first pet
  8.     local s1 = C_PetBattles.GetPetSpeciesID(2, 1);
  9.     local l1 = C_PetBattles.GetLevel(2, 1);
  10.     local r1 = C_PetBattles.GetBreedQuality(2, 1);
  11.     BattleDex.RecordPet(s1, l1, r1, 0); -- 4th arg means self is primary
  12.    
  13.     -- second pet
  14.     if (C_PetBattles.GetNumPets(2) >= 2) then
  15.         local s2 = C_PetBattles.GetPetSpeciesID(2, 2);
  16.         local l2 = C_PetBattles.GetLevel(2, 2);    
  17.         local r2 = C_PetBattles.GetBreedQuality(2, 2);
  18.         BattleDex.RecordPet(s2, l2, r2, s1);    -- 4th arg points to first pet
  19.     end
  20.    
  21.     -- third pet
  22.     if (C_PetBattles.GetNumPets(2) >= 3) then
  23.         local s3 = C_PetBattles.GetPetSpeciesID(2, 3);
  24.         local l3 = C_PetBattles.GetLevel(2, 3);
  25.         local r3 = C_PetBattles.GetBreedQuality(2, 3); 
  26.         BattleDex.RecordPet(s3, l3, r3, s1);    -- 4th arg points to first pet
  27.     end
  28. end

Shitara 11-22-20 01:21 PM

Awesome, I went with option 2. It errored on login, but putting 'then' on line 22 fixed that, and it's now recording the low level pets without error.

Thank you very much everyone for your help :D

SDPhantom 11-22-20 05:47 PM

I'd honestly convert it to a loop.
Lua Code:
  1. function BattleDex.RecordBattle()
  2.     if not C_PetBattles.IsWildBattle() then return; end
  3.  
  4.     local primary=0;
  5.     for i=1,C_PetBattles.GetNumPets(2) do
  6.         local species=C_PetBattles.GetPetSpeciesID(2,i);
  7.         BattleDex.RecordPet(
  8.             species
  9.             ,C_PetBattles.GetLevel(2,i)
  10.             ,C_PetBattles.GetBreedQuality(2,i)
  11.             ,primary--  Passes zero for the first iteration
  12.         );
  13.         if i==1 then primary=species; end-- Sets the primary species for following iterations
  14.     end
  15. end


All times are GMT -6. The time now is 11:39 PM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI