I'm having a huge problem when I've added content to a table (Lua), where all content in the table suddenly disappears.
The table in question holds the data for a banlist (608 entries at present) and is placed within the table by using table.insert, however the lead entry for the user is done as Umbra.Banlist[profileId] = {};. The Umbra.Banlist[profileId] = {}; table alone should mean that there's content within the Umbra.Banlist table.
I get no errors when running the code, and when using
if (#Umbra.Banlist == 0) then
System.LogAlways(Umbra.Tag.." The Banlist seems to be empty. It seems that some part of loading has failed.");
end
I get this:
I've looked on the web and on this site but there doesn't seem to be any related questions, so I'm posting it here.
This code is part of a server mod for Crysis Wars (see the whole function below), but has the entire Lua library at hand (so don't worry if you don't think your answer won't solve the problem).
The Code:
Umbra.ReadBans = function()
self = Umbra;
System.LogAlways(Umbra.Tag.." Starting banlist read.");
FileHnd, err = io.open(Root().."Mods/Infinity/System/Read/Banlst.lua", "r");
if (not FileHnd) then
System.LogAlways(Umbra.Tag.." Unable to find file 'Banlst.lua'.");
return;
end
for line in FileHnd:lines() do
local name, profile, ip, domain, reason, date, bannedby = line:match([[Umbra.BanSystem:Add%('(.-)', '(.+)', '(.+)', '(.+)', '(.+)', '(.+)', '(.-)'%);]]);
if (not name) then
System.LogAlways(Umbra.Tag.." Failed to read the banlist at line "..count or 0);
break;
end
System.LogAlways(Umbra.Tag.." Banlist; Name: [ "..name.." ], For: [ "..reason.." ], By: [ "..bannedby.." ]");
--local Msg, Date, Reason, Type, Domain = line:match([[User:Read%( "(.-)", { Date="(.+)"; Reason="(.+)"; Typ="(.+)"; Info="(.+)"; } %);]]);
--User:Read( "Banned", { Date="31.03.2011"; Reason="WEBSTREAM"; Typ="Inetnum"; Info="COMPUTER.SED.gg"; } );
--Umbra.BanSystem:Add('patryk', '258132298', '178.183.243.163', '178.183.243.163.dsl.dynamic.t-mobile.pl', 'flyhack', '08/11/2012 | 21:39:53', 'Anti-Noob');
Umbra.Banlist[profile] = {};
table.insert(Umbra.Banlist[profile], name);
table.insert(Umbra.Banlist[profile], ip);
table.insert(Umbra.Banlist[profile], domain);
table.insert(Umbra.Banlist[profile], reason);
table.insert(Umbra.Banlist[profile], date);
table.insert(Umbra.Banlist[profile], bannedby);
--[[Umbra.Banlist[profile].name = name;
Umbra.Banlist[profile].ip = ip;
Umbra.Banlist[profile].domain = domain;
Umbra.Banlist[profile].reason = reason;
Umbra.Banlist[profile].date = date;
Umbra.Banlist[profile].bannedby = bannedby;--]]
if not count then count = 0; end
count = count + 1;
end
Umbra.Bans = {};
Umbra.Bans.cnt = count;
System.LogAlways(Umbra.Tag.." Read "..count.." banned players (added into the Umbra Global Banlist)");
if (#Umbra.Banlist == 0) then
System.LogAlways(Umbra.Tag.." The Banlist seems to be empty. It seems that some part of loading has failed.");
end
count = nil; --Purge this one as well, again!
end
Edit:
I don't get the message that the code below should print if their profile doesn't exist in the table, so their profile does actually exist.
if (not Umbra.Banlist[profile]) then
System.LogAlways(Umbra.Tag.." Error in 'Umbra.Banlist': The profile does not exist.)");
break;
end
Another Edit:
Proof that the system can in fact get the 'ID' variable:
Near the end of your code you have #Umbra.Banlist == 0 but I don't see any item insertions into it by numeric keys. Here are some things to check to see if you're assumptions matches up with reality.
Check that profile isn't nil. It looks like in your use-case you're assuming it's a number. You can check this easily with:
assert(profile)
assert(type(profile) == 'number')
If profile is actually not a number type then checking Umbra.Banlist with # operator is faulty. Note # does not count the associative part of the table. If you only care whether Umbra.Banlist is empty or not you can use if next(Umbra.Banlist) then to check for it.
Just try to use this function to get count of items in table:
function count(t)
local c=0;
for i in pairs(t) do c=c+1; end
return c;
end
--...
if(count(Umbra.Banlist)==0)then
--...
end
The thing is, that # counts an array/table with number indexes, but you have table with string indexes. # is same like when you would replace "pairs" with "ipairs" in that count function. So and when you do #Umbra.Banlist, where all indexes are strings, it returns 0, because there are 0 integer indexes, but it does not mean, that table is empty.
Related
I'm currently starting work on a text adventure game in Lua--no addons, just pure Lua for my first project. In essence, here is my problem; I'm trying to find out how I can do a "reverse lookup" of a table using one of its variables. Here's an example of what I've tried to do:
print("What are you trying to take?")
bag = {}
gold = {name="Gold",ap=3}
x = io.read("*l")
if x == "Gold" then
table.insert(bag,gold)
print("You took the " .. gold.name .. ".")
end
Obviously, writing a line like this with every single object in the game would be very... exhausting--especially since I think I'll be able to use this solution for not just taking items but movement from room to room using a reverse lookup with each room's (x,y) coordinates. Anyone have any ideas on how to make a more flexible system that can find a table by the player typing in one of its variables? Thanks in advance!
-blockchainporter
This doesn't directly answer your question as you asked it, but I think it would serve the purpose of what you are trying to do. I create a table called 'loot' which can hold many objects, and the player can place any of these in their 'bag' by typing the name.
bag = {}
loot = {
{name="Gold", qty=3},
{name="Axe", qty=1},
}
print("What are you trying to take?")
x = io.read("*l")
i = 1
while loot[i] do
if (x == loot[i].name) then
table.insert(bag, table.remove(loot,i))
else
i = i + 1
end
end
For bonus points, you could check 'bag' to see if the player has some of that item already and then just update the quantity...
while loot[i] do
if (x == loot[i].name) then
j, found = 1, nil
while bag[j] do
if (x == bag[j].name) then
found = true
bag[j].qty = bag[j].qty + loot[i].qty
table.remove(loot,i)
end
j = j + 1
end
if (not found) then
table.insert(bag, table.remove(loot,i))
end
else
i = i + 1
end
end
Again, this isn't a 'reverse lookup' solution like you asked for... but I think it is closer to what you are trying to do by letting a user choose to loot something.
My disclaimer is that I don't use IO functions in my own lua usage, so I have to assume that your x = io.read("*l") is correct.
PS. If you only ever want objects to have a name and qty, and never any other properties (like condition, enchantment, or whatever) then you could also simplify my solution by using key/val pairs:
bag = {}
loot = { ["Gold"] = 3, ["Axe"] = 1 }
print("What are you trying to take?")
x = io.read("*l")
for name, qty in pairs(loot) do
if x == name then
bag.name = (bag.name or 0) + qty
loot.name = nil
end
end
I have a few notes to start before I specifically address your question. (I just want to do this before I forget, so please bear with me!)
I recommend printing to the terminal using stderr instead of stdout--the Lua function print uses the latter. When I am writing a Lua script, I often create a C-style function called eprintf to print formatted output to stderr. I implement it like this:
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
Just be aware that, unlike print, this function does not automatically append a newline character to the output string; to do so, remember to put \n at the end of your fmt string.
Next, it may be useful to define a helper function that calls io.read("*l") to get an entire line of input. In writing some example code to help answer your question, I called my function getline--like the C++ function that has similar behavior--and defined it like this:
local function getline()
local read = tostring(io.read("*l"))
return read
end
If I correctly understand what it is you are trying to do, the player will have an inventory--which you have called bag--and he can put items into it by entering item names into stdin. So, for instance, if the player found a treasure chest with gold, a sword, and a potion in it and he wanted to take the gold, he would type Gold into stdin and it would be placed in his inventory.
Based on what you have so far, it looks like you are using Lua tables to create these items: each table has a name index and another called ap; and, if a player's text input matches an item's name, the player picks that up item.
I would recommend creating an Item class, which you could abstract nicely by placing it in its own script and then loading it as needed with require. This is a very basic Item class module I wrote:
----------------
-- Item class --
----------------
local Item = {__name = "Item"}
Item.__metatable = "metatable"
Item.__index = Item
-- __newindex metamethod.
function Item.__newindex(self, k, v)
local err = string.format(
"type `Item` does not have member `%s`",
tostring(k)
)
return error(err, 2)
end
-- Item constructor
function Item.new(name_in, ap_in)
assert((name_in ~= nil) and (ap_in ~= nil))
local self = {
name = name_in,
ap = ap_in
}
return setmetatable(self, Item)
end
return Item
From there, I wrote a main driver to encapsulate some of the behavior you described in your question. (Yes, I know my Lua code looks more like C.)
#!/usr/bin/lua
-------------
-- Modules --
-------------
local Item = assert(require("Item"))
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
local function printf(fmt, ...)
io.stdout:write(string.format(fmt, ...))
return
end
local function getline()
local read = tostring(io.read("*l"))
return read
end
local function main(argc, argv)
local gold = Item.new("Gold", 3)
printf("gold.name = %s\ngold.ap = %i\n", gold.name, gold.ap)
return 0
end
main(#arg, arg)
Now, as for the reverse search which you described, at this point all you should have to do is check the user's input against an Item's name. Here it is in the main function:
local function main(argc, argv)
local gold = Item.new("Gold", 3)
local bag = {}
eprintf("What are you trying to take? ")
local input = getline()
if (input == gold.name) then
table.insert(bag, gold)
eprintf("You took the %s.\n", gold.name)
else
eprintf("Unrecognized item `%s`.\n", input)
end
return 0
end
I hope this helps!
I want to parse a config file which has information like:
[MY_WINDOW_0]
Address = 0xA0B0C0D0
Size = 0x100
Type = cpu0
[MY_WINDOW_1]
Address = 0xB0C0D0A0
Size = 0x200
Type = cpu0
[MY_WINDOW_2]
Address = 0xC0D0A0B0
Size = 0x100
Type = cpu1
into a LUA table as follows
CPU_TRACE_WINDOWS =
{
["cpu0"] = {{address = 0xA0B0C0D0, size = 0x100},{address = 0xB0C0D0A0, size = 0x200},}
["cpu1"] = {{address = 0xC0D0A0B0, size = 0x100},...}
}
I tried my best with some basic LUA string manipulation functions but couldn't get the output that I'm looking for due to repetition of strings in each sections like 'Address',' Size', 'Type' etc. Also my actual config file is huge with 20 such sections.
I got so far, this is basically one section of the code, rest would be just repetition of the logic.
OriginalConfigFile = "test.cfg"
os.execute("cls")
CPU_TRACE_WINDOWS = {}
local bus
for line in io.lines(OriginalConfigFile) do
if string.find(line, "Type") ~= nil then
bus = string.gsub(line, "%a=%a", "")
k,v = string.match(bus, "(%w+) = (%w+)")
table.insert(CPU_TRACE_WINDOWS, v)
end
end
Basically I'm having trouble with coming up with the FINAL TABLE STRUCTURE that I need. v here is the different rvalues of the string "Type". I'm having issues with arranging it in the table. I'm currently working to find a solution but I thought I could ask for help meanwhile.
This should work for you. Just change the filename to wherever you have your config file stored.
f, Address, Size, Type = io.input("configfile"), "", "", ""
CPU_TRACE_WINDOWS = {}
for line in f:lines() do
if line:find("MY_WINDOW") then
Type = ""
Address = ""
Size = ""
elseif line:find("=") then
_G[line:match("^%a+")] = line:match("[%d%a]+$")
if line:match("Type") then
if not CPU_TRACE_WINDOWS[Type] then
CPU_TRACE_WINDOWS[Type] = {}
end
table.insert(CPU_TRACE_WINDOWS[Type], {address = Address, size = Size})
end
end
end
end
It searches for the MY_WINDOW phrase and resets the variable. If the table exists within CPU_TRACE_WINDOWS, then it just appends a new table value, otherwise it just creates it. Note that this is dependent upon Type always being the last entry. If it switches up anywhere, then it will not have all the required information. There may be a cleaner way to do it, but this works (tested on my end).
Edit: Whoops, forgot to change the variables in the middle there if MY_WINDOW matched. That needed to be corrected.
Edit 2: Cleaned up the redundancy with table.insert. Only need it once, just need to make sure the table is created first.
As you can tell I'm a beginner in lua. I am trying to understand a function I'm stuck at what the following code segment does?
It is used in the following code snippet in the last line:
function classify(txt_dir, img_dir, cls_list)
local acc = 0.0
local total = 0.0
local fea_img = {}
local fea_txt = {}
for fname in io.lines(cls_list) do
local imgpath = img_dir .. '/' .. fname .. '.t7'
local txtpath = txt_dir .. '/' .. fname .. '.t7'
fea_img[#fea_img + 1] = extract_img(imgpath)
fea_txt[#fea_txt + 1] = extract_txt(txtpath)
end
for i = 1,#fea_img do
-- loop over individual images.
for k = 1,fea_img[i]:size(1) do
local best_match = 1
local best_score = -math.huge
for j = 1,#fea_txt do
local cur_score = torch.dot(fea_img[i][{k,{}}], fea_txt[j])
From my understanding, fea_img is a lua table. Is the line fea_img[i][{k,{}}] some sort of slicing for the value for the key 'i' in the table fea_img?
I tried searching for more examples and found this being used here too (last line):
for i = 1,nsamples,batchsize do
-- indices
local lasti = math.min(i+batchsize-1,nsamples)
local m = lasti - i + 1
-- k-means step, on minibatch
local batch = x[{ {i,lasti},{} }]
Any help on this would be really appreciated. Thank you!
In lua you can access a specific index on a table in multiple ways. Like these two examples
local myValue = someTable.theIndex
-- or
local myOtherValue = someTable[2]
So the construct you see here is to access some values from a (nested) table.
Also in lua you can use anything except nil as a index, so even tables are possible.
The line
fea_img[i][{k,{}}]
Can be extended to this:
local index1 = i -- i in this case is your loop variable
local index2 = { k , { } } -- This creates a table with 2 values, the first one will be the vaule of the var k, the second one is an empty table
local value1 = fea_img[index1] -- This will get you a table
local value2 = value1[index2] -- This will get the same as: fea_img[i][{k,{}}]
Correction and Addition:
As Nicol Bolas already said in the comments: The index must be an exact match. Which means it literally has to be the same table, which is not the case for the presented code from you. Either you dropped code you thought is unnecessary or fea_img has some some kind of metatable on it.
In the case of
local k = 2
local table1 = {k, { } }
local table2 = {k, { } }
table2 and table1 do have the exact same content. But they are not the same table. Which will lead to nil always being retrieved if one is used to store data in a table and the other is used to get it back.
Syntactically, t[k] is indexing a table with a key. Normally, if there is a record in the table with the key k, its value is returned. Nothing more, nothing less.
If fea_img[i] was a normal table, {k,{}} would always return nil, since table indices are resolved based on their identity ({k,{}} is always a new table). Based on your code, I have to conclude that the elements of fea_img (i.e. what extract_img returns) are not normal tables.
In Lua, you can override the indexing operation using a metatable. If you index a value that has a metatable with __index, it will be used if there is no matching record in the table:
local t = {}
setmetatable(t, {
__index = function(t, k)
return k
end
})
print(t[{}])
This table has a metatable associated with it, which is used in the indexing operation. In this case __index returns the key, but whatever library you are using might provide more complex behaviour.
This is specific to the library you are using, not something related to the Lua syntax.
I'd like to re-initialize a table without losing references to it.
What I want to achieve is defining tables in files, and when a file is changed (with a text editor) the file is reloaded, changing the table. Of course this doesn't change the table but creates a new instance, old references will still point to the old table.
Any suggestions?
EDIT: I want to elaborate on what I want to achieve. An example with game characters and weapons. I want to modify the weapons.lua and so affect the characters.
-- weapons.lua
sword = { damage = 3 }
-- characters.lua
character = { weapon = sword }
Adding a level of indirection (putting "sword" inside "weapons") like suggested by JWT doesn't help, unless I split character into { weaponTable = weapons, weaponKey = "sword" } but I don't see this as an option.
Anchor everything that needs to survive in the global environment. Nesting is fine, and this doesn't have to be your primary reference. (You can still local things, but make sure to initialize those local variables from the global environment and update the global if you change the local.)
To initialize the global values, say
foo = foo or value -- if foo is always true-ish
bar = (bar == nil) and value or bar -- if bar may be `false`
To initialize or update tables, you can
foo = foo or { }
foo.bar = foo.bar or 23
foo.baz = foo.baz or 42
-- and so on...
but that's kinda icky, so maybe say
function reinit( new, old ) -- (re)initialize one level, prefer old
if old == nil then return new end
if type( old ) ~= "table" then return old end
for k, v in pairs( new ) do
if old[k] == nil then old[k] = v end
end
return old
end
function reset( new, old ) -- (re)initialize one level, prefer new
if old == nil then return new end
if type( old ) ~= "table" then return new end
for k, v in pairs( new ) do old[k] = v end
return old
end
and then just
foo = reinit( { bar = 23, baz = 42 }, foo ) -- only setting if not defined
-- or
foo = reset( { bar = 23, baz = 42 }, foo ) -- always overriding given fields
or maybe make it even more fancy and say
function traverse( t, path )
local here, last, lastk, created = t
-- follow path of several keys starting from t, creating tables as needed
for k in path:gmatch "[^.]+" do
k = tonumber( k ) or k -- convert all-number keys to integer (for arrays)
local next = here[k]
if not next then
next, created = { }, true
here[k] = next
else
created = false
end
lastk, last, here = k, here, next
end
return here, last, lastk, created
end
function repopulate( path, value, update )
update = update or reinit -- pass 'reset' as 'update' for the other behavior
-- or something entirely different if that's what you need
local here, last, lastk, created = traverse( _G, path )
if type( value ) == "table" then
update( value, here )
else
if created then last[lastk] = nil end -- created one level too much
update( { [lastk] = value }, last )
end
end
and then (with arbitrary nesting)
-- No need to create 'state' first if it doesn't exist yet!
-- (If it exists, it will get updated, otherwise it's created)
repopulate( "state.player.weapon", { kind = "sword", damage = 11 } )
-- Do keep in mind that generally update order is relevant -- you may want to
-- give a newly created player a default inventory, but you may not want to
-- "refill" the player's inventory on every reload. So generally `repopulate`
-- with the parent and all child nodes for from-scratch creation, then
-- `repopulate` the child nodes that need to be checked & potentially updated
-- as well.
-- (So here you'd probably repopulate `state.player` first and then
-- `state.player.weapon` or other fields only if they should be updated anyway.)
-- e.g.:
repopulate( "state.player", {
x = 0, y = 0, hp = 100, mp = 100, level = 0, -- and so on
weapon = { kind = "sword", damage = 11 }, -- etc. etc.
} )
-- on reload always force a sword-kind weapon, leave rest (damage, ...) as-is
repopulate( "state.player.weapon", { kind = "sword" }, reset )
-- or alternatively: only if player has no weapon, give them a sword
repopulate( "state.player.weapon", { kind = "sword", damage = 3 } )
And you can go further, add metamethods to hide some of that shuffling, define different update policies, ... – you've seen some of the possibilities, now go and build your own version that fits your style and your code.
(While you're free to use the above code in any way, please note that it was written ad-hoc in the browser. I did some testing, fixed some glitches, and it seems to work now, but don't be surprised if there's still one or two bugs hiding in there. So play with this, change it, break it (and see how/why it breaks), adapt and extend it, ... – but unless you completely understand what it does and can fix any bugs, I strongly suggest you write your own version, or just stick to the basics. You probably don't need everything that this does, and you're likely to need other things that this doesn't do. As this is a central part of the reloading/live-coding infrastructure and everything has to be adapted to be reload-compatible, any mismatch between your tooling and what you actually need will result in a lot of pain everywhere in your code. So if you need something like this, put in a day or two to make it work the way you need it to, or you will regret it.)
(Free bonus warning: If you do OOP, you'll probably have to store and retrieve your classes instead of creating them every time, otherwise old objects from previous iterations will miss code updates and still run their old methods. I've forgotten about that more than just a couple of times and wasted several hours pondering "why isn't it fixed now?!?" after repeatedly re-loading code... So remember to anchor your metatables, anchor your classes!)
You could nest the tables in another table.
Before:
local a = { 1, 2, 3 }
local b = { 7, 8, 9 }
print(a[2] + b[2]) -- #=> 10
After:
local lookup = {
a = { 1, 2, 3 },
b = { 7, 8, 9 }
}
print(lookup.a[2] + lookup.b[2]) -- #=> 10
Then you can fully replace (or just update) a table in the lookup table and any dependent statements will use that updated value:
lookup.a = { 100, 50, 0 }
print(lookup.a[2] + lookup.b[2]) -- #=> 58
I don't know if it's exactly what you needed (As an ID is necessary) but I hope it will fit your needs.
meta = {
tables = {},
__call = function(arg, t)
for k, v in pairs(t) do
arg[k] = v
end
end,
__bnot = function(arg)
return arg.__key
end,
__newindex = function(arg, key, val)
meta.tables[arg.__key][key] = val
end,
__index = function(arg, key)
return meta.tables[arg.__key][key]
end
}
function RefTable(arg)
local newtable = {}
if arg ~= nil then
newtable.__key = arg
setmetatable(newtable, meta)
if meta.tables[arg] == nil then
meta.tables[arg] = {}
end
else
error("RefTable can't have nil key")
end
return newtable
end
-- Using the RefTable
sword = RefTable("Sword")
sword({damage = 3})
sword.cooldown = 10
character = {sword = sword}
print("base", sword.damage, sword.cooldown)
print("embed", character.sword.damage, character.sword.cooldown)
sword = RefTable("Sword")
sword({damage = 8, cooldown = 50})
print("embed2", character.sword.damage, character.sword.cooldown)
print(sword.__key, sword.cooldown)
ref = RefTable("Sword")
ref.cooldown = 1000
print(sword.cooldown)
What I'm currently trying to do is make a table of email addresses (as keys) that hold person_records (as values). Where the person_record holds 6 or so things in it. The problem I'm getting is that when I try to assign the email address as a key to a table it complains and says table index is nil... This is what I have so far:
random_record = split(line, ",")
person_record = {first_name = random_record[1], last_name = random_record[2], email_address = random_record[3], street_address = random_record[4], city = random_record[5], state = random_record[6]}
email_table[person_record.email_address] = person_record
I wrote my own split function that basically takes a line of input and pulls out the 6 comma seperated values and stores them in a table (random_record)
I get an error when I try to say email_table[person_record.email_address] = person_record.
But when I print out person_record.email_address it's NOT nil, it prints out the string I stored in it.. I'm so confused.
function split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
The following code is copy and pasted from your example and runs just fine:
email_table = {}
random_record = {"first", "second", "third"}
person_record = {first_name = random_record[1], last_name = random_record[1], email_address = random_record[1]}
email_table[person_record.email_address] = person_record
So your problem is in your split function.
BTW, Lua doesn't have "hashtables". It simply has "tables" which store key/value pairs. Whether these happen to use hashes or not is an implementation detail.
It looks like you iterating over some lines that have comma-separated data.
Looking at your split function, it stops as soon as there's no more separator (,) symbols in particular line to find. So feeding it anything with less than 3 ,-separated fields (for very common example: an empty line at end of file) will produce a table that doesn't go up to [3]. Addressing any empty table value will return you a nil, so person_record.email_address will be set to nil as well on the 2nd line of your code. Then, when you attempt to use this nil stored in person_record.email_address as an index to email_table in 3rd line, you will get the exact error you've mentioned.