Created
October 4, 2016 07:45
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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