Skip to content

Instantly share code, notes, and snippets.

@balaam
Created October 4, 2016 07:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save balaam/e6158422ca165629535a7e0309781e7b to your computer and use it in GitHub Desktop.
Save balaam/e6158422ca165629535a7e0309781e7b to your computer and use it in GitHub Desktop.
A Lua project for Dinodeck that lets the user explore the global memory space
Watch = {}
Watch.__index = Watch
function Watch:Create()
local this =
{
key = {},
keyRoot =
{
id = "_G",
cursor = 1,
windowCursor = 1
},
windowSize = 5,
repeatSeconds = 0.2, -- down and up will repeat
repeatCount = 0,
-- a list of tables used to filter
-- common libraries out of the global space
activeFilters = {},
view =
{
textColor = Vector.Create(1,1,1,1),
backgroundKeyColor = Vector.Create(0, 0, 135.0/255.0, 1),
backgroundValueColor = Vector.Create(0, 0, 105.0/255.0, 1),
}
}
setmetatable(this, self)
return this
end
function Watch:Render(renderer, x, y)
local validKey = self:MakeValidKey(self.key)
-- We'll start by print the key
local strKey = "_G"
if next(self.key) then
strKey = self:KeyToString()
end
local xCursor = x + 16
local yCursor = y
local lineHeight = 22
local lineWidth = 128
-- Draw the key
renderer:ScaleText(1.03, 1.03)
renderer:AlignText("left", "top")
renderer:DrawText2d(x, yCursor, strKey, self.view.textColor)
yCursor = yCursor - lineHeight
-- If the key resolves to a table draw all the children
local keyData, t = self:GetKeyValue(validKey)
if (type(t) == "table") then
keyData.cursor = self:GetValidCursor(t, keyData.cursor)
keyData.windowCursor = self:GetValidCursor(t, keyData.windowCursor)
renderer:AlignText("left", "center")
local index = 1
for k, v in pairs(t) do
if index >= keyData.windowCursor then
local textY = math.floor(yCursor - ((lineHeight - 1)/2))
if index == keyData.cursor then
renderer:DrawText2d(x, textY, ">")
end
-- Key background
renderer:DrawRect2d(xCursor, yCursor,
xCursor + lineWidth,
yCursor - (lineHeight - 1),
self.view.backgroundKeyColor)
-- Value background
renderer:DrawRect2d(xCursor + lineWidth + 1, yCursor,
xCursor + (lineWidth*2) + 1,
yCursor - (lineHeight - 1),
self.view.backgroundValueColor)
-- Drawing the key, value pairs
renderer:DrawText2d(xCursor, textY, tostring(k))
local valueX = xCursor + lineWidth + 1
renderer:DrawText2d(valueX, textY, self:ValueAsStr(v))
yCursor = yCursor - lineHeight
end
if index >= self:WindowBottom(keyData) then
break
end
index = index + 1
end
local textY = math.floor(yCursor - ((lineHeight - 1)/2))
renderer:AlignText("right", "center")
local progressStr = string.format("%d/%d", keyData.cursor, self:TableSize())
renderer:DrawText2d(xCursor + (lineWidth*2) + 1, textY, progressStr)
end
end
function Watch:KeyToString(key)
local idList = {"_G"}
for k, v in ipairs(self.key) do
table.insert(idList, v.id)
end
return table.concat(idList, ".")
end
-- the bottom of the view into the list
function Watch:WindowBottom(keyData)
return keyData.windowCursor + self.windowSize
end
function Watch:ValueAsStr(v)
local vtype = type(v)
if vtype == "number" then
return string.format("%d", v)
elseif vtype == "table" then
return "{...}"
elseif vtype == "function" then
return "function"
elseif vtype == "userdata" then
local meta = getmetatable(v)
if Vector ~= nil and meta == Vector then
return tostring(v)
elseif Renderer ~= nil and meta == Renderer then
return "Renderer"
end
return "userdata"
else
return tostring(v)
end
end
--
-- Structures can change as code is modified
-- The bit of code you're looking at may no longer exist
-- Therefore keys are trimmed to remove invalid parts
--
function Watch:MakeValidKey(key)
local validKey = {}
local cursor = _G
for k, v in ipairs(key) do
validKey[k] = v
if type(cursor[v.id]) ~= "table" then
return validKey
end
cursor = cursor[v.id]
end
return validKey
end
-- Clamps the cusor to the current table
function Watch:GetValidCursor(t, cursor)
if cursor <= 0 then
return 1
end
-- 1. Count the keys
local keyCount = 0
for k, v in pairs(t) do
keyCount = keyCount + 1
end
return math.max(0, math.min(cursor, keyCount))
end
function Watch:GetKeyValue(key)
local value = _G
local keyData = self.keyRoot
for k, v in ipairs(key) do
value = value[v.id]
keyData = v
end
if value == _G and next(self.activeFilters or {}) then
value = self:FilterAndClone(_G)
end
return keyData, value
end
function Watch:FilterAndClone(value)
local gClone = {}
for k, v in pairs(value) do
gClone[k] = v
end
-- nil out everything in the filters
for k, v in ipairs(self.activeFilters) do
for _, f in ipairs(v) do
gClone[f] = nil
end
end
return gClone
end
function Watch:Update()
if Keyboard.JustPressed(KEY_DOWN) then
self:MoveDown()
self.repeatCount = 0
return
elseif Keyboard.JustPressed(KEY_UP) then
self:MoveUp()
self.repeatCount = 0
return
elseif Keyboard.JustPressed(KEY_SPACE) then
self:Select()
elseif Keyboard.JustPressed(KEY_BACKSPACE) then
self:Back()
end
--
-- Code to let you hold down the up/down key
--
if Keyboard.Held(KEY_DOWN) then
if self.repeatCount >= self.repeatSeconds then
self:MoveDown()
self.repeatCount = 0
end
self.repeatCount = self.repeatCount + GetDeltaTime()
elseif Keyboard.Held(KEY_UP) then
if self.repeatCount >= self.repeatSeconds then
self:MoveUp()
self.repeatCount = 0
end
self.repeatCount = self.repeatCount + GetDeltaTime()
end
end
function Watch:MoveDown()
local keyData, t = self:GetKeyValue(self:MakeValidKey(self.key))
if type(t) == "table" then
keyData.cursor = self:GetValidCursor(t, keyData.cursor + 1)
if keyData.cursor > self:WindowBottom(keyData) then
self:PushWindowDown(keyData)
end
end
end
function Watch:MoveUp()
local keyData, t = self:GetKeyValue(self:MakeValidKey(self.key))
if type(t) == "table" then
keyData.cursor = self:GetValidCursor(t, keyData.cursor - 1)
if keyData.cursor < keyData.windowCursor then
self:PushWindowUp(keyData)
end
end
end
function Watch:Select()
local keyData, t = self:GetKeyValue(self:MakeValidKey(self.key))
if type(t) == "table" then
-- run through the table until the index matches the cursor
local index = 1
for k, v in pairs(t) do
if index == keyData.cursor and type(v) == "table" then
table.insert(self.key,
{
id = k,
cursor = 1,
windowCursor = 1,
})
end
index = index + 1
end
end
end
function Watch:Back()
table.remove(self.key)
end
function Watch:TableSize()
local keyData, t = self:GetKeyValue(self:MakeValidKey(self.key))
local count = 0
for k, v in pairs(t) do
count = count + 1
end
return count
end
function Watch:PushWindowDown(keyData)
-- Don't let the view area drop off the end of the
-- table
if self:WindowBottom(keyData) >= self:TableSize() then
return
end
keyData.windowCursor = keyData.windowCursor + 1
end
function Watch:PushWindowUp(keyData)
if keyData.windowCursor <= 1 then
keyData.windowCursor = 1
return
end
keyData.windowCursor = keyData.windowCursor - 1
end
function Watch:AddFilter(filter)
table.insert(self.activeFilters, filter)
end
-- Filters are only applied to the _G level
Watch.Filters =
{
-- Filter out the watch library
Watch =
{
"Watch"
},
-- Filter out the LuaJIT keywords and libraries
Lua =
{
"_G",
"assert",
"tostring",
"Asset",
"tonumber",
"io",
"rawget",
"ipairs",
"print",
"pcall",
"gcinfo",
"module",
"rawset",
"require",
"rawequal",
"setmetatable",
"_VERSION",
"next",
"setfenv",
"os",
"load",
"math",
"string",
"debug",
"coroutine",
"newproxy",
"jit",
"getfenv",
"package",
"loadfile",
"bit",
"loadstring",
"pairs",
"type",
"select",
"unpack",
"getmetatable",
"error",
"dofile",
"table",
"xpcall",
"collectgarbage",
},
Dinodeck =
{
"Renderer",
"Vector",
"Keyboard",
"System",
-- Special functions
"GetDeltaTime",
"GetTime",
"LoadLibrary",
-- Blend modes from the renderer
"BLEND_ADDITIVE",
"BLEND_BLEND",
-- Keys from the keyboard
"KEY_BACKSPACE",
"KEY_TAB",
"KEY_CLEAR",
"KEY_RETURN",
"KEY_PAUSE",
"KEY_ESCAPE",
"KEY_SPACE",
"KEY_EXCLAIM",
"KEY_QUOTEDBL",
"KEY_HASH",
"KEY_DOLLAR",
"KEY_AMPERSAND",
"KEY_QUOTE",
"KEY_LEFTPAREN",
"KEY_RIGHTPAREN",
"KEY_ASTERISK",
"KEY_A",
"KEY_B",
"KEY_C",
"KEY_D",
"KEY_E",
"KEY_F",
"KEY_G",
"KEY_H",
"KEY_I",
"KEY_J",
"KEY_K",
"KEY_L",
"KEY_M",
"KEY_N",
"KEY_O",
"KEY_P",
"KEY_Q",
"KEY_R",
"KEY_S",
"KEY_T",
"KEY_U",
"KEY_V",
"KEY_W",
"KEY_X",
"KEY_Y",
"KEY_Z",
"KEY_UP",
"KEY_DOWN",
"KEY_RIGHT",
"KEY_LEFT",
"KEY_INSERT",
"KEY_HOME",
"KEY_END",
"KEY_PAGEUP",
"KEY_PAGEDOWN",
"KEY_RSHIFT",
"KEY_LSHIFT",
"KEY_RCTRL",
"KEY_LCTRL",
"KEY_RALT",
"KEY_LALT",
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment