Nesting labels inside Lua table structures

Post Reply
Xavious
Posts: 7
Joined: Sat Nov 21, 2015 9:48 pm

Nesting labels inside Lua table structures

Post by Xavious »

Hello, friends. I've been using Mudlet for about a week now and I have to say I really love the client so far. The API for Geyser is really intuitive and I love the functionality. After accomplishing a few simple tasks I started working on things with increasing complexity.

My current project is creating a "galaxy map" window that plots all the planets in the game. Planet locations are set up on an X/Y axis, so I created a trigger to pull this data and put all the planets in a Lua table called systems. The basic structure is something like this:
Code: [show] | [select all] lua
systems{
   0 = { 
      name = "PlanetA",
      x = -25,
      y = 45,
   }
   1 = {
      name = "PlanetB",
      x = 15,
      y = 15,
   }
}
Once I have the actual planet data in a table, I want to create a label for each one of these planets. I actually did accomplish this, but I don't think it's working how I am intending it to. I've tried various versions of code to fit this label inside the actual table structure. For example the following function, which is what actually adds the planets:
Code: [show] | [select all] lua
function addSystem(name, x, y)
	if not table.contains (starmap.systems, name) then
		echo("System not found, adding..") 
		local system = {}
		system.name = name
		system.x = x
		system.y = y
		--system.label = Geyser.Label:new({
			--name = name.."_label",
			--width = "100px",
			--height = "32px",
			--message = [[<center>*<br />]]..name..[[</center>]]
		--}, starmap.planet_container)
	   --system.label:setStyleSheet([[
		--	background-color: rgba(0,0,0,0%);
		--]])
		--system.label:hide()
		checkMinMax(x, y)
		table.insert(starmap.systems, system)
		--updateStarmap()
	else
		echo("System already exits..")
	end
end
Doing this I get the an error when the commented label code is actually not commented out:

LUA: ERROR running script System Grab (Trigger20) ERROR:../src/mudlet-lua/lua/TableUtils.lua:101:
stack overflow

The commented out code is where I have run into issues. I've tried filling up the "Systems" table first, then using a different function later to fill it out with labels rather than do it the moment the planet is inserted, like this:
Code: [show] | [select all] lua
function updateStarmap()
	for k, v in pairs(starmap.systems) do
		echo("K:"..k.."\n")
		echo("V:"..v.name.."\n")
		v["label"] = Geyser.Label:new({
			name = v.name.."_label",
			width = "100px",
			height = "32px",
			message = [[<center>*<br />]]..v.name..[[</center>]]
		}, starmap.planet_container)
	   v["label"]:setStyleSheet([[
			background-color: rgba(0,0,0,0%);
		]])
		local xPosition,yPosition = findPosition(v.x,v.y)
		echo(v.name.." X:"..xPosition.."\n")
		echo(v.name.." Y:"..yPosition.."\n")
		v["label"]:move(xPosition.."%", yPosition.."%")
	end
end
So the error message I receive is actually being thrown from the Lua table function "table.contains". The error is only thrown after labels have been inserted into the table structure. I've noticed some strange behavior when inserting labels into the table hierarchy and I think I may be doing something wrong. If I generate the systems first, without labels, then generate labels for them afterwards in a loop it works fine. However, when I look at my variable list.. only the first index in the system table has the "label" index. Inside this label is an entire hierarchy of all the containers and labels for the ENTIRE script, which I think is causing the recursive function call "table.contains" to get the stack overflow warning.

Does anyone have any advice for how to approach this or where I'm going wrong. In order to dynamically add and remove planets as this project piece grows in complexity, I will need to be able to check the table to prevent duplicate entries. I felt the most elegant way to do this would be to create the labels as soon as the system is created at all, then later using another function to arrange the points on the map based on scale.

I've actually worked out most of the hard stuff, or so I thought. However, I think my inexperience with both Lua and Geyser is holding me back. I managed to create the map itself and it looks pretty decent so far, but I want the functionality to add hidden systems and recalibrate the map as needed.

Here's a picture of what it looks like:

https://dl.dropboxusercontent.com/u/958 ... 5%3A19.png

Drevarr
Posts: 43
Joined: Tue Aug 06, 2013 12:07 am
Location: GA, USA

Re: Nesting labels inside Lua table structures

Post by Drevarr »

This change to the label definition worked for me, maybe it will point you in the right direction. Would love to see the final product when you are finished.
Code: [show] | [select all] lua

starmap = {}
starmap.systems = { }

starmap.planet_container = Geyser.Container:new({
  name = "starmap.planet_container",    -- give it a unique name here
  x="50%", y=0,                   -- have it start at the top-left corner of mudlet
  width = 400, height="100%", -- with a width of 200, and a height of the full screen, hence 100%
})

-------------------------------------------------
--         Put your Lua functions here.        --
--                                             --
-- Note that you can also use external Scripts --
-------------------------------------------------
function addSystem(name, x, y)
        if not table.contains (starmap.systems, name) then
                echo("System not found, adding..")
                local system = {}
                system.name = name
                system.x = x
                system.y = y
                --system.label = Geyser.Label:new({
                  --      name = name.."_label",
                    --    width = "100px",
                      --  height = "32px",
                        --message = [[<center>*<br />]]..name..[[</center>]]
--                }, starmap.planet_container)
--           system.label:setStyleSheet([[
--                      background-color: rgba(0,0,0,0%);
--                ]])
                --system.label:hide()
                --checkMinMax(x, y)
                table.insert(starmap.systems, system)
--                updateStarmap()
        else
                echo("System already exits..")
        end
end

function updateStarmap()
        for k, v in pairs(starmap.systems) do
                echo("K:"..k.."\n")
                echo("V:"..v.name.."\n")
-- Changed the label creation
                starmap["label"..k] = Geyser.Label:new({
                        name = v.name.."_label",
                        width = "100px",
                        height = "32px",
                        message = [[<center>*<br />]]..v.name..[[</center>]]
                }, starmap.planet_container)
           starmap["label"..k]:setStyleSheet([[
                        background-color: rgba(0,0,0,0%);
                ]])
--                local xPosition,yPosition = findPosition(v.x,v.y)
--                echo(v.name.." X:"..xPosition.."\n")
--                echo(v.name.." Y:"..yPosition.."\n")
                starmap["label"..k]:move(v.x.."%", v.y.."%")
        end
end

Added two planets via addSystem(name, x, y) then called updateStarmap()
Image

Xavious
Posts: 7
Joined: Sat Nov 21, 2015 9:48 pm

Re: Nesting labels inside Lua table structures

Post by Xavious »

Thanks for the response Drevarr. I eventually stumbled on to implementing the labels much in the way that you did. By keeping them completely outside the "systems" table structure it prevents the recursive table function, table.contains, from spiraling out of control and throwing the overflow error.

It still leaves me with a mystery I haven't completely unraveled. Throughout all of the development I've been doing, I often look at the variable list to make sure things are retaining the proper values. I've noticed that, generally speaking, containers, labels, and other Geyser objects don't show up on the variable list. However, they do show up, though inconsistently, when you put them into a table. When I insert the Geyser labels into a Lua table structure, the labels do not appear in the hierarchy that you would expect.

Using my previous implementation as an example, you would expect every system entry to have its own label entry, much like this structure:
Code: [show] | [select all] lua
systems {
     0 {
          name = "Planet A",
          x = 10,
          y = 20,
          label = { (Attributes of this Label) }
      }
     1{
          name = "Planet B",
          x = -10,
          y = 40
          label  = { (Attributes of this label) }
     }
}
However, what ACTUALLY happens, and the reason why I believe the table.contains function crashes, is all of the labels get dumped on the very first table entry, regardless of key or value reference. When I go to my variable list and click on this label it then branches on and on into a nested structure that eventually contains all of the other labels you would expect to see associated with other system table indexes.

An example of what actually happens:
Code: [show] | [select all] lua
systems {
     0 {
          name = "Planet A",
          x = 10,
          y = 20,
          label = { 
               label = {
                        (LABEL STUFF on and on and on)
               }
           }
      }
     1{
          name = "Planet B",
          x = -10,
          y = 40
          -- Label is missing?   
     }
}
In any case, the root of this behavior is just an unsolved mystery for me. I have found the way to work around it, so I will continue my work. Not knowing why this is happening doesn't prevent me from moving forward, but it does leave me a bit curious..

I am also curious if there is a setting, feature, or reason why Geyser objects don't show up on the variable list, except in certain situations. For instance, when I tried to nest them in the manner I did on my first example, I could see them on the variable list. However, normally I wouldn't see them at all and even in the new implementation I don't see them on the variable list hierarchy.

User avatar
Oneymus
Posts: 321
Joined: Thu Sep 17, 2009 5:24 am

Re: Nesting labels inside Lua table structures

Post by Oneymus »

As far as I can tell, the issue you're running into exists because a Geyser object can have a crazy deep structure. Testing your code locally, I was able to add the first system without issue, but as soon as I tried to add another, table.contains overflows.

If you want to see what I mean, at the end of addSystem, call display(starmap). You should see a mammoth tree. I don't know the internals of Geyser all that well, but it looks like the Label (in this case specifically) object contains functions and tables for the whole host of Geyser functionality.
Xavious wrote:It still leaves me with a mystery I haven't completely unraveled. Throughout all of the development I've been doing, I often look at the variable list to make sure things are retaining the proper values. I've noticed that, generally speaking, containers, labels, and other Geyser objects don't show up on the variable list. However, they do show up, though inconsistently, when you put them into a table. When I insert the Geyser labels into a Lua table structure, the labels do not appear in the hierarchy that you would expect.
The most likely reason that the Variables UI can't display a Geyser object is that it overflows and gives up.

If you look at the definition of table.contains, you can see that it recursively iterates through a table looking for a given value. Because the iteration is using pairs, order of iteration isn't guaranteed. Since table.contains only stops when it finds the value it's looking for, if it happens to run into the label field before it finds the name field, it's going to try and walk the entire Geyser object's tree.

Therefore, I would recommend you do not store the Geyser object in starmap.systems. Use a much shallower table that holds the necessary fields (which it looks like you're doing now), and build your Labels on demand or store them in a separate table you won't recurse through; something like a dictionary keyed by Label name for direct lookup.

SilverDragon
Posts: 23
Joined: Tue Mar 01, 2011 10:00 pm

Re: Nesting labels inside Lua table structures

Post by SilverDragon »

another possibility to test to see if a table contains a value is to just do
Code: [show] | [select all] lua
if not starmap.systems[name] then
  echo(name .. " not found! Creating system...")
end -- if

Post Reply