Thread Tools Display Modes
11-21-20, 07:04 PM   #1
Shitara
A Murloc Raider
Join Date: Nov 2020
Posts: 7
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 ...
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");
  Reply With Quote
11-21-20, 07:16 PM   #2
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,871
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");
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 11-21-20 at 07:43 PM.
  Reply With Quote
11-21-20, 09:00 PM   #3
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
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.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
11-21-20, 11:08 PM   #4
Shitara
A Murloc Raider
Join Date: Nov 2020
Posts: 7
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.
  Reply With Quote
11-21-20, 11:46 PM   #5
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
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.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
11-22-20, 12:42 AM   #6
Shitara
A Murloc Raider
Join Date: Nov 2020
Posts: 7
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;
  Reply With Quote
11-22-20, 11:32 AM   #7
DahkCeles
A Cliff Giant
 
DahkCeles's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2020
Posts: 73
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

Last edited by DahkCeles : 11-23-20 at 04:48 PM. Reason: "then" on line 22
  Reply With Quote
11-22-20, 01:21 PM   #8
Shitara
A Murloc Raider
Join Date: Nov 2020
Posts: 7
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
  Reply With Quote
11-22-20, 05:47 PM   #9
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
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
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » table index is nil

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off