summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /scripts
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'scripts')
-rw-r--r--scripts/README.md52
-rw-r--r--scripts/binds.lua77
-rw-r--r--scripts/granger/lib/init.lua5
-rw-r--r--scripts/granger/lib/os.lua222
-rw-r--r--scripts/granger/lib/path.lua137
-rw-r--r--scripts/granger/lib/string.lua50
-rw-r--r--scripts/granger/lib/table.lua181
-rw-r--r--scripts/granger/main.lua63
-rw-r--r--scripts/granger/util.lua28
-rw-r--r--scripts/http.lua40
-rw-r--r--scripts/inspect.lua218
-rw-r--r--scripts/sample-httpjson-client.lua37
-rw-r--r--scripts/test-nettle.lua24
13 files changed, 1134 insertions, 0 deletions
diff --git a/scripts/README.md b/scripts/README.md
new file mode 100644
index 0000000..1da04b7
--- /dev/null
+++ b/scripts/README.md
@@ -0,0 +1,52 @@
+# Lua Scripts
+
+Scripts packaged with Tremulous. This folder will likely fill up with
+not so useful examples.
+
+Even more likely this will be the only source of documentation in the
+short term. Likely documentation will happen after the first large API
+re-organization.
+
+
+Available APIs
+==============
+
+HTTP Client
+-----------
+ - `http.del()`
+ - `http.get()`
+ - `http.post()`
+ - `http.put()`
+
+ Returns a `HttpResponse` object.
+
+```lua
+ HttpResponse::code -- HTTP status code from server
+ HttpResponse::body -- Raw response body
+```
+
+### Example
+```lua
+ print(http.get('www.google.com/search?q=' .. 'Hello World').body)
+```
+
+JSON
+----
+
+ - `rapidjson.encode()`
+ - `rapidjson.decode()`
+
+Cvar
+----
+
+ - `cvar.new(a, b, c)`
+ - `cvar.new(a, b)`
+ - `cvar.new(a)`
+
+Nettle
+------
+TBD
+
+Client
+------
+TBD
diff --git a/scripts/binds.lua b/scripts/binds.lua
new file mode 100644
index 0000000..9194b7d
--- /dev/null
+++ b/scripts/binds.lua
@@ -0,0 +1,77 @@
+--[[
+-- Key binds specified in Lua
+--]]
+
+
+player = {
+ team = cvar.new('team_teamname'),
+ stage = cvar.new('team_stage'),
+ hp = cvar.new('player_hp'),
+ kns = cvar.new('team_kns'),
+ spawns = cvar.new('team_spawns'),
+ bp = cvar.new('team_bp'),
+ maxbp = cvar.new('player_maxbp'),
+ credits = cvar.new('player_credits'),
+ score = cvar.new('player_score'),
+ deaths = cvar.new('player_deaths')
+}
+
+alien = {
+ kns = cvar.new('alien_kns'),
+ score = cvar.new('alien_score')
+}
+
+human = {
+ kns = cvar.new('human_kns'),
+ score = cvar.new('human_score')
+}
+
+function TeamSay(text)
+ client.addReliableCommand('say_team ' .. text)
+end
+
+function Screenshot()
+ draw2D = cvar.new('cg_draw2d')
+ drawGun = cvar.new('cg_drawgun')
+ _a = draw2D.value
+ _b = drawGun.value
+
+ draw2D.value = 0
+ drawGun.value = 0
+
+ client.addReliableCommand('wait 2; screenshotJPEG silent')
+
+ draw2D.value = _a
+ drawGun.value = _b
+end
+
+binds = {
+ a = bind.new('a', "teamstatus"),
+ b = bind.new('b', "script TeamSay('^5Humans have ^1' .. human.score.value .. ' ^2Aliens have ^1' .. alien.score.value)"),
+ c = bind.new('c', "script TeamSay('^5MOVE!!! I only have ^1' .. player.hp.value .. '^5HP Available')"),
+ d = bind.new('d'),
+ e = bind.new('e'),
+ f = bind.new('f'),
+ g = bind.new('g'),
+ h = bind.new('h', "buy ammo"),
+ i = bind.new('i', "+forward"),
+ j = bind.new('j', "+moveleft"),
+ k = bind.new('k', "+back"),
+ l = bind.new('l', "+moveright"),
+ m = bind.new('m', "itemact medkit"),
+ n = bind.new('n', "+button8"),
+ o = bind.new('o'),
+ p = bind.new('p'),
+ q = bind.new('q'),
+ r = bind.new('r'),
+ s = bind.new('s'),
+ t = bind.new('t'),
+ u = bind.new('u'),
+ v = bind.new('v'),
+ w = bind.new('w'),
+ x = bind.new('x'),
+ y = bind.new('y'),
+ z = bind.new('z', "screenshotJPEG silent"),
+
+ f12 = bind.new('f12', "cg_draw2d ")
+}
diff --git a/scripts/granger/lib/init.lua b/scripts/granger/lib/init.lua
new file mode 100644
index 0000000..9158d0b
--- /dev/null
+++ b/scripts/granger/lib/init.lua
@@ -0,0 +1,5 @@
+require "lib.os"
+require "lib.path"
+require "lib.string"
+require "lib.table"
+require "lib.log"
diff --git a/scripts/granger/lib/os.lua b/scripts/granger/lib/os.lua
new file mode 100644
index 0000000..66941a7
--- /dev/null
+++ b/scripts/granger/lib/os.lua
@@ -0,0 +1,222 @@
+--
+-- os.lua
+-- Additions to the OS namespace.
+-- Copyright (c) 2002-2011 Jason Perkins and the Premake project
+-- Copyright (c) 2015 Jeff Kent <jeff@jkent.net>
+--
+
+
+--
+-- Retrieve the current operating system ID string.
+--
+
+ function os.get()
+ return _OS
+ end
+
+
+
+--
+-- Check the current operating system.
+--
+
+ function os.is(id)
+ return (os.get():lower() == id:lower())
+ end
+
+
+
+--
+-- Determine if the current system is running a 64-bit architecture
+--
+
+ local _64BitHostTypes = {
+ "x86_64",
+ "ia64",
+ "amd64",
+ "ppc64",
+ "powerpc64",
+ "sparc64"
+ }
+
+ function os.is64bit()
+ -- Call the native code implementation. If this returns true then
+ -- we're 64-bit, otherwise do more checking locally
+ if (os._is64bit()) then
+ return true
+ end
+
+ -- Identify the system
+ local arch
+ if _OS == "windows" then
+ arch = os.getenv("PROCESSOR_ARCHITECTURE")
+ elseif _OS == "macosx" then
+ arch = os.outputof("echo $HOSTTYPE")
+ else
+ arch = os.outputof("uname -m")
+ end
+
+ -- Check our known 64-bit identifiers
+ arch = arch:lower()
+ for _, hosttype in ipairs(_64BitHostTypes) do
+ if arch:find(hosttype) then
+ return true
+ end
+ end
+ return false
+ end
+
+
+
+--
+-- The os.matchdirs() and os.matchfiles() functions
+--
+
+ local function domatch(result, mask, wantfiles)
+ -- need to remove extraneous path info from the mask to ensure a match
+ -- against the paths returned by the OS. Haven't come up with a good
+ -- way to do it yet, so will handle cases as they come up
+ if mask:startswith("./") then
+ mask = mask:sub(3)
+ end
+
+ -- strip off any leading directory information to find out
+ -- where the search should take place
+ local basedir = mask
+ local starpos = mask:find("%*")
+ if starpos then
+ basedir = basedir:sub(1, starpos - 1)
+ end
+ basedir = path.getdirectory(basedir)
+ if (basedir == ".") then basedir = "" end
+
+ -- recurse into subdirectories?
+ local recurse = mask:find("**", nil, true)
+
+ -- convert mask to a Lua pattern
+ mask = path.wildcards(mask)
+
+ local function matchwalker(basedir)
+ local wildcard = path.join(basedir, "*")
+
+ -- retrieve files from OS and test against mask
+ local m = os.matchstart(wildcard)
+ while (os.matchnext(m)) do
+ local isfile = os.matchisfile(m)
+ if ((wantfiles and isfile) or (not wantfiles and not isfile)) then
+ local basename = os.matchname(m)
+ local fullname = path.join(basedir, basename)
+ if basename ~= ".." and fullname:match(mask) == fullname then
+ table.insert(result, fullname)
+ end
+ end
+ end
+ os.matchdone(m)
+
+ -- check subdirectories
+ if recurse then
+ m = os.matchstart(wildcard)
+ while (os.matchnext(m)) do
+ if not os.matchisfile(m) then
+ local dirname = os.matchname(m)
+ if (not dirname:startswith(".")) then
+ matchwalker(path.join(basedir, dirname))
+ end
+ end
+ end
+ os.matchdone(m)
+ end
+ end
+
+ matchwalker(basedir)
+ end
+
+ function os.matchdirs(...)
+ local result = { }
+ for _, mask in ipairs(arg) do
+ domatch(result, mask, false)
+ end
+ return result
+ end
+
+ function os.matchfiles(...)
+ local result = { }
+ for _, mask in ipairs(arg) do
+ domatch(result, mask, true)
+ end
+ return result
+ end
+
+
+
+--
+-- An overload of the os.mkdir() function, which will create any missing
+-- subdirectories along the path.
+--
+
+ local builtin_mkdir = os.mkdir
+ function os.mkdir(p)
+ local dir = iif(p:startswith("/"), "/", "")
+ for part in p:gmatch("[^/]+") do
+ dir = dir .. part
+
+ if (part ~= "" and not path.isabsolute(part) and not os.isdir(dir)) then
+ local ok, err = builtin_mkdir(dir)
+ if (not ok) then
+ return nil, err
+ end
+ end
+
+ dir = dir .. "/"
+ end
+
+ return true
+ end
+
+
+--
+-- Run a shell command and return the output.
+--
+
+ function os.outputof(cmd)
+ local pipe = io.popen(cmd)
+ local result = pipe:read('*a')
+ pipe:close()
+ return result
+ end
+
+
+--
+-- Remove a directory, along with any contained files or subdirectories.
+--
+
+ local builtin_rmdir = os.rmdir
+ function os.rmdir(p)
+ -- recursively remove subdirectories
+ local dirs = os.matchdirs(p .. "/*")
+ for _, dname in ipairs(dirs) do
+ os.rmdir(dname)
+ end
+
+ -- remove any files
+ local files = os.matchfiles(p .. "/*")
+ for _, fname in ipairs(files) do
+ os.remove(fname)
+ end
+
+ -- remove this directory
+ builtin_rmdir(p)
+ end
+
+
+--
+-- Elevate and set _ELEVATED global
+--
+
+ _ENV._ELEVATED = false
+ local builtin_elevate = os.elevate
+ function os.elevate()
+ _ENV._ELEVATED = builtin_elevate()
+ return _ENV._ELEVATED
+ end
+
diff --git a/scripts/granger/lib/path.lua b/scripts/granger/lib/path.lua
new file mode 100644
index 0000000..7bf4b2b
--- /dev/null
+++ b/scripts/granger/lib/path.lua
@@ -0,0 +1,137 @@
+--
+-- path.lua
+-- Path manipulation functions.
+-- Copyright (c) 2002-2010 Jason Perkins and the Premake project
+--
+
+
+--
+-- Retrieve the filename portion of a path, without any extension.
+--
+
+ function path.getbasename(p)
+ local name = path.getname(p)
+ local i = name:findlast(".", true)
+ if (i) then
+ return name:sub(1, i - 1)
+ else
+ return name
+ end
+ end
+
+
+--
+-- Retrieve the directory portion of a path, or an empty string if
+-- the path does not include a directory.
+--
+
+ function path.getdirectory(p)
+ local i = p:findlast("/", true)
+ if (i) then
+ if i > 1 then i = i - 1 end
+ return p:sub(1, i)
+ else
+ return "."
+ end
+ end
+
+
+--
+-- Retrieve the drive letter, if a Windows path.
+--
+
+ function path.getdrive(p)
+ local ch1 = p:sub(1,1)
+ local ch2 = p:sub(2,2)
+ if ch2 == ":" then
+ return ch1
+ end
+ end
+
+
+
+--
+-- Retrieve the file extension.
+--
+
+ function path.getextension(p)
+ local i = p:findlast(".", true)
+ if (i) then
+ return p:sub(i)
+ else
+ return ""
+ end
+ end
+
+
+
+--
+-- Retrieve the filename portion of a path.
+--
+
+ function path.getname(p)
+ local i = p:findlast("[/\\]")
+ if (i) then
+ return p:sub(i + 1)
+ else
+ return p
+ end
+ end
+
+
+--
+-- Takes a path which is relative to one location and makes it relative
+-- to another location instead.
+--
+
+ function path.rebase(p, oldbase, newbase)
+ p = path.getabsolute(path.join(oldbase, p))
+ p = path.getrelative(newbase, p)
+ return p
+ end
+
+
+--
+-- Convert the separators in a path from one form to another. If `sep`
+-- is nil, then a platform-specific separator is used.
+--
+
+ local builtin_translate = path.translate
+
+ function path.translate(p, sep)
+ if not sep then
+ if os.is("windows") then
+ sep = "\\"
+ else
+ sep = "/"
+ end
+ end
+ return builtin_translate(p, sep)
+ end
+
+
+--
+-- Converts from a simple wildcard syntax, where * is "match any"
+-- and ** is "match recursive", to the corresponding Lua pattern.
+--
+-- @param pattern
+-- The wildcard pattern to convert.
+-- @returns
+-- The corresponding Lua pattern.
+--
+
+ function path.wildcards(pattern)
+ -- Escape characters that have special meanings in Lua patterns
+ pattern = pattern:gsub("([%+%.%-%^%$%(%)%%])", "%%%1")
+
+ -- Replace wildcard patterns with special placeholders so I don't
+ -- have competing star replacements to worry about
+ pattern = pattern:gsub("%*%*", "\001")
+ pattern = pattern:gsub("%*", "\002")
+
+ -- Replace the placeholders with their Lua patterns
+ pattern = pattern:gsub("\001", ".*")
+ pattern = pattern:gsub("\002", "[^/]*")
+
+ return pattern
+ end
diff --git a/scripts/granger/lib/string.lua b/scripts/granger/lib/string.lua
new file mode 100644
index 0000000..56fae4d
--- /dev/null
+++ b/scripts/granger/lib/string.lua
@@ -0,0 +1,50 @@
+--
+-- string.lua
+-- Additions to Lua's built-in string functions.
+-- Copyright (c) 2002-2008 Jason Perkins and the Premake project
+--
+
+
+--
+-- Returns an array of strings, each of which is a substring of s
+-- formed by splitting on boundaries formed by `pattern`.
+--
+
+ function string.explode(s, pattern, plain)
+ if (pattern == '') then return false end
+ local pos = 0
+ local arr = { }
+ for st,sp in function() return s:find(pattern, pos, plain) end do
+ table.insert(arr, s:sub(pos, st-1))
+ pos = sp + 1
+ end
+ table.insert(arr, s:sub(pos))
+ return arr
+ end
+
+
+
+--
+-- Find the last instance of a pattern in a string.
+--
+
+ function string.findlast(s, pattern, plain)
+ local curr = 0
+ repeat
+ local next = s:find(pattern, curr + 1, plain)
+ if (next) then curr = next end
+ until (not next)
+ if (curr > 0) then
+ return curr
+ end
+ end
+
+
+
+--
+-- Returns true if `haystack` starts with the sequence `needle`.
+--
+
+ function string.startswith(haystack, needle)
+ return (haystack:find(needle, 1, true) == 1)
+ end
diff --git a/scripts/granger/lib/table.lua b/scripts/granger/lib/table.lua
new file mode 100644
index 0000000..3076cb0
--- /dev/null
+++ b/scripts/granger/lib/table.lua
@@ -0,0 +1,181 @@
+--
+-- table.lua
+-- Additions to Lua's built-in table functions.
+-- Copyright (c) 2002-2008 Jason Perkins and the Premake project
+--
+
+
+--
+-- Returns true if the table contains the specified value.
+--
+
+ function table.contains(t, value)
+ for _,v in pairs(t) do
+ if (v == value) then
+ return true
+ end
+ end
+ return false
+ end
+
+
+--
+-- Enumerates an array of objects and returns a new table containing
+-- only the value of one particular field.
+--
+
+ function table.extract(arr, fname)
+ local result = { }
+ for _,v in ipairs(arr) do
+ table.insert(result, v[fname])
+ end
+ return result
+ end
+
+
+
+--
+-- Flattens a hierarchy of tables into a single array containing all
+-- of the values.
+--
+
+ function table.flatten(arr)
+ local result = { }
+
+ local function flatten(arr)
+ for _, v in ipairs(arr) do
+ if type(v) == "table" then
+ flatten(v)
+ else
+ table.insert(result, v)
+ end
+ end
+ end
+
+ flatten(arr)
+ return result
+ end
+
+
+--
+-- Merges an array of items into a string.
+--
+
+ function table.implode(arr, before, after, between)
+ local result = ""
+ for _,v in ipairs(arr) do
+ if (result ~= "" and between) then
+ result = result .. between
+ end
+ result = result .. before .. v .. after
+ end
+ return result
+ end
+
+
+--
+-- Inserts a value of array of values into a table. If the value is
+-- itself a table, its contents are enumerated and added instead. So
+-- these inputs give these outputs:
+--
+-- "x" -> { "x" }
+-- { "x", "y" } -> { "x", "y" }
+-- { "x", { "y" }} -> { "x", "y" }
+--
+
+ function table.insertflat(tbl, values)
+ if type(values) == "table" then
+ for _, value in ipairs(values) do
+ table.insertflat(tbl, value)
+ end
+ else
+ table.insert(tbl, values)
+ end
+ end
+
+
+--
+-- Returns true if the table is empty, and contains no indexed or keyed values.
+--
+
+ function table.isempty(t)
+ return next(t) == nil
+ end
+
+
+--
+-- Adds the values from one array to the end of another and
+-- returns the result.
+--
+
+ function table.join(...)
+ local result = { }
+ for _,t in ipairs(arg) do
+ if type(t) == "table" then
+ for _,v in ipairs(t) do
+ table.insert(result, v)
+ end
+ else
+ table.insert(result, t)
+ end
+ end
+ return result
+ end
+
+
+--
+-- Return a list of all keys used in a table.
+--
+
+ function table.keys(tbl)
+ local keys = {}
+ for k, _ in pairs(tbl) do
+ table.insert(keys, k)
+ end
+ return keys
+ end
+
+
+--
+-- Adds the key-value associations from one table into another
+-- and returns the resulting merged table.
+--
+
+ function table.merge(...)
+ local result = { }
+ for _,t in ipairs(arg) do
+ if type(t) == "table" then
+ for k,v in pairs(t) do
+ result[k] = v
+ end
+ else
+ error("invalid value")
+ end
+ end
+ return result
+ end
+
+
+
+--
+-- Translates the values contained in array, using the specified
+-- translation table, and returns the results in a new array.
+--
+
+ function table.translate(arr, translation)
+ local result = { }
+ for _, value in ipairs(arr) do
+ local tvalue
+ if type(translation) == "function" then
+ tvalue = translation(value)
+ else
+ tvalue = translation[value]
+ end
+ if (tvalue) then
+ table.insert(result, tvalue)
+ end
+ end
+ return result
+ end
+
+ \ No newline at end of file
diff --git a/scripts/granger/main.lua b/scripts/granger/main.lua
new file mode 100644
index 0000000..304f45a
--- /dev/null
+++ b/scripts/granger/main.lua
@@ -0,0 +1,63 @@
+--
+-- main.lua
+-- Granger main
+-- Copyright (c) 2016 Jeff Kent <jeff@jkent.net>
+--
+
+require 'scripts/granger/lib'
+
+local install_files = {}
+
+if os.is('windows') then
+ install_files = {
+ "tremulous.exe"
+ "tremded.exe"
+ "granger.exe"
+ "SDL264.dll"
+ "renderer_opengl1.dll"
+ "renderer_opengl2.dll"
+ }
+elseif os.is('linux') then
+ install_files = {
+ "tremulous"
+ "tremded"
+ "granger"
+ "renderer_opengl1.so"
+ "renderer_opengl2.so"
+ }
+elseif os.is('macosx') then
+ install_files = {
+ "tremulous"
+ "tremded"
+ "granger"
+ "libSDL2-2.0.0.dylib"
+ "renderer_opengl1.dylib"
+ "renderer_opengl2.dylib"
+ }
+else
+ os.exit(1)
+end
+
+local dst_dir = path.getdirectory('.')
+local dst_dir = path.getdirectory(_EXE_PATH)
+
+local privs = false
+for file in ipairs(install_files) do
+ local src = path.join(src_dir, file)
+ local dst = path.join(dst_dir, file)
+ if not os.access(dst, 'w') then
+ privs = true
+ end
+end
+
+if privs then
+ os.elevate()
+end
+
+for file in ipairs(install_files) do
+ local src = path.join(src_dir, file)
+ local dst = path.join(dst_dir, file)
+ os.rename(src, dst)
+end
+
+--- Copyright (C) 2015-2019 GrangerHub
diff --git a/scripts/granger/util.lua b/scripts/granger/util.lua
new file mode 100644
index 0000000..77dc476
--- /dev/null
+++ b/scripts/granger/util.lua
@@ -0,0 +1,28 @@
+--
+-- util.lua
+-- various utility functions
+-- Copyright (c) 2016 Jeff Kent <jeff@jkent.net>
+--
+
+local function hash_file(file, ctx)
+ local f = io.open(file, "r")
+ if f == nil then
+ return nil
+ end
+ repeat
+ local buf = f:read(0x10000)
+ ctx:update(buf)
+ until buf == nil
+ f:close()
+ return tostring(ctx)
+end
+
+function sha256_file(file)
+ local ctx = nettle.sha256()
+ return hash_file(file, ctx)
+end
+
+function md5_file(file)
+ local ctx = nettle.md5()
+ return hash_file(file, ctx)
+end
diff --git a/scripts/http.lua b/scripts/http.lua
new file mode 100644
index 0000000..adc565e
--- /dev/null
+++ b/scripts/http.lua
@@ -0,0 +1,40 @@
+
+
+cl_latestRelease = cvar.new("cl_latestRelease")
+
+dlurl = ""
+
+Releases = {
+ url='https://api.github.com/repos/GrangerHub/tremulous/releases'
+
+ refresh = function()
+ r = http.get(url)
+ if r.code != 200 then
+ cl_latestRelease = "ERROR:\n Server did not return OK status code"
+ return false
+ end
+ releases = rapidjson.decode(r.body)
+ most_recent = releases[1]
+ cl_latestRelease = most_recent.tag;
+ for i,asset in ipairs(most_recent.assets) do
+ dlurl = cvar.new("download_url", "", 256)
+ dlurl = asset.browser_download_url
+ end
+ return true
+ end
+
+ download = function()
+ r = http.get(download_url)
+ if r.code != 200 then
+ cvar.new("com_error") = "Download failed"
+ return false
+ end
+
+ io.open(path, "w+")
+ io.write(r.body)
+ io.close()
+
+ args = "path-to-tremulous-binary"
+ os.execute(path .. args)
+ end
+}
diff --git a/scripts/inspect.lua b/scripts/inspect.lua
new file mode 100644
index 0000000..892d91b
--- /dev/null
+++ b/scripts/inspect.lua
@@ -0,0 +1,218 @@
+-----------------------------------------------------------------------------------------------------------------------
+-- inspect.lua - v1.1.1 (2011-01)
+-- Enrique GarcĂ­a Cota - enrique.garcia.cota [AT] gmail [DOT] com
+-- human-readable representations of tables.
+-- inspired by http://lua-users.org/wiki/TableSerialization
+-----------------------------------------------------------------------------------------------------------------------
+
+-- Apostrophizes the string if it has quotes, but not aphostrophes
+-- Otherwise, it returns a regular quoted string
+local function smartQuote(str)
+ if string.match( string.gsub(str,"[^'\"]",""), '^"+$' ) then
+ return "'" .. str .. "'"
+ end
+ return string.format("%q", str )
+end
+
+local controlCharsTranslation = {
+ ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
+ ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\\"] = "\\\\"
+}
+
+local function unescapeChar(c) return controlCharsTranslation[c] end
+
+local function unescape(str)
+ local result, _ = string.gsub( str, "(%c)", unescapeChar )
+ return result
+end
+
+local function isIdentifier(str)
+ return string.match( str, "^[_%a][_%a%d]*$" )
+end
+
+local function isArrayKey(k, length)
+ return type(k)=='number' and 1 <= k and k <= length
+end
+
+local function isDictionaryKey(k, length)
+ return not isArrayKey(k, length)
+end
+
+local sortOrdersByType = {
+ ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
+ ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+}
+
+local function sortKeys(a,b)
+ local ta, tb = type(a), type(b)
+ if ta ~= tb then return sortOrdersByType[ta] < sortOrdersByType[tb] end
+ if ta == 'string' or ta == 'number' then return a < b end
+ return false
+end
+
+local function getDictionaryKeys(t)
+ local length = #t
+ local keys = {}
+ for k,_ in pairs(t) do
+ if isDictionaryKey(k, length) then table.insert(keys,k) end
+ end
+ table.sort(keys, sortKeys)
+ return keys
+end
+
+local function getToStringResultSafely(t, mt)
+ local __tostring = type(mt) == 'table' and mt.__tostring
+ local string, status
+ if type(__tostring) == 'function' then
+ status, string = pcall(__tostring, t)
+ string = status and string or 'error: ' .. tostring(string)
+ end
+ return string
+end
+
+local Inspector = {}
+
+function Inspector:new(v, depth)
+ local inspector = {
+ buffer = {},
+ depth = depth,
+ level = 0,
+ counters = {
+ ['function'] = 0,
+ ['userdata'] = 0,
+ ['thread'] = 0,
+ ['table'] = 0
+ },
+ pools = {
+ ['function'] = setmetatable({}, {__mode = "kv"}),
+ ['userdata'] = setmetatable({}, {__mode = "kv"}),
+ ['thread'] = setmetatable({}, {__mode = "kv"}),
+ ['table'] = setmetatable({}, {__mode = "kv"})
+ }
+ }
+
+ setmetatable( inspector, {
+ __index = Inspector,
+ __tostring = function(instance) return table.concat(instance.buffer) end
+ } )
+ return inspector:putValue(v)
+end
+
+function Inspector:puts(...)
+ local args = {...}
+ for i=1, #args do
+ table.insert(self.buffer, tostring(args[i]))
+ end
+ return self
+end
+
+function Inspector:tabify()
+ self:puts("\n", string.rep(" ", self.level))
+ return self
+end
+
+function Inspector:up()
+ self.level = self.level - 1
+end
+
+function Inspector:down()
+ self.level = self.level + 1
+end
+
+function Inspector:putComma(comma)
+ if comma then self:puts(',') end
+ return true
+end
+
+function Inspector:putTable(t)
+ if self:alreadySeen(t) then
+ self:puts('<table ', self:getOrCreateCounter(t), '>')
+ elseif self.level >= self.depth then
+ self:puts('{...}')
+ else
+ self:puts('<',self:getOrCreateCounter(t),'>{')
+ self:down()
+
+ local length = #t
+ local mt = getmetatable(t)
+
+ local string = getToStringResultSafely(t, mt)
+ if type(string) == 'string' and #string > 0 then
+ self:puts(' -- ', unescape(string))
+ if length >= 1 then self:tabify() end -- tabify the array values
+ end
+
+ local comma = false
+ for i=1, length do
+ comma = self:putComma(comma)
+ self:puts(' '):putValue(t[i])
+ end
+
+ local dictKeys = getDictionaryKeys(t)
+
+ for _,k in ipairs(dictKeys) do
+ comma = self:putComma(comma)
+ self:tabify():putKey(k):puts(' = '):putValue(t[k])
+ end
+
+ if mt then
+ comma = self:putComma(comma)
+ self:tabify():puts('<metatable> = '):putValue(mt)
+ end
+ self:up()
+
+ if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
+ self:tabify()
+ elseif length > 0 then -- array tables have one extra space before closing }
+ self:puts(' ')
+ end
+ self:puts('}')
+ end
+ return self
+end
+
+function Inspector:alreadySeen(v)
+ local tv = type(v)
+ return self.pools[tv][v] ~= nil
+end
+
+function Inspector:getOrCreateCounter(v)
+ local tv = type(v)
+ local current = self.pools[tv][v]
+ if not current then
+ current = self.counters[tv] + 1
+ self.counters[tv] = current
+ self.pools[tv][v] = current
+ end
+ return current
+end
+
+function Inspector:putValue(v)
+ local tv = type(v)
+
+ if tv == 'string' then
+ self:puts(smartQuote(unescape(v)))
+ elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
+ self:puts(tostring(v))
+ elseif tv == 'table' then
+ self:putTable(v)
+ else
+ self:puts('<',tv,' ',self:getOrCreateCounter(v),'>')
+ end
+ return self
+end
+
+function Inspector:putKey(k)
+ if type(k) == "string" and isIdentifier(k) then
+ return self:puts(k)
+ end
+ return self:puts( "[" ):putValue(k):puts("]")
+end
+
+local function inspect(t, depth)
+ depth = depth or 4
+ return tostring(Inspector:new(t, depth))
+end
+
+return inspect
+
diff --git a/scripts/sample-httpjson-client.lua b/scripts/sample-httpjson-client.lua
new file mode 100644
index 0000000..a733f95
--- /dev/null
+++ b/scripts/sample-httpjson-client.lua
@@ -0,0 +1,37 @@
+--[[
+ _____ _ _
+|_ _| __ ___ _ __ ___ _ _| | ___ _ _ ___ | | _ _ __ _
+ | || '__/ _ \ '_ ` _ \| | | | |/ _ \| | | / __|_____| | | | | |/ _` |
+ | || | | __/ | | | | | |_| | | (_) | |_| \__ \_____| |__| |_| | (_| |
+ |_||_| \___|_| |_| |_|\__,_|_|\___/ \__,_|___/ |_____\__,_|\__,_|
+ Victor Roemer [WTFBBQHAX]
+ Nov. 03, 2016 10:53:27AM EST
+ A sample Restful JSON Client API.
+ This sample demonstrates how to comunicate with an HTTP JSON 3rdparty
+ API in Lua script.
+ APIs demonstrated:
+ * HTTP RestClient
+ * JSON
+--]]
+
+-- GitHub API URL
+url='https://api.github.com/repos/GrangerHub/tremulous/releases'
+
+-- HTTP Get request- retrieve the raw JSON response
+txt = http.get(url)
+assert(txt.code == 200)
+
+-- Decode raw JSON response into a Lua table
+releases = rapidjson.decode(txt.body)
+
+-- GitHub returned an array of releases- The "most_recent" is item 1
+-- NOTE: Lua array indexing starts at `1` not `0`!!
+most_recent = releases[1]
+
+-- FIXME: Remove hardcoded tag_name in this test
+assert(most_recent.tag_name == "Oct-22-2016")
+
+
+for i,asset in ipairs(most_recent.assets) do
+ print(asset.browser_download_url)
+end
diff --git a/scripts/test-nettle.lua b/scripts/test-nettle.lua
new file mode 100644
index 0000000..09a96be
--- /dev/null
+++ b/scripts/test-nettle.lua
@@ -0,0 +1,24 @@
+print "nettle tests begin"
+
+empty_hash = tostring(nettle.sha256())
+
+ctx = nettle.sha256()
+ctx:update(nil)
+assert(empty_hash == tostring(ctx))
+
+ctx = nettle.sha256()
+ctx:update("Hello World!")
+ctx:update("Hello World!")
+hash = tostring(ctx)
+assert(hash == "95a5a79bf6218dd0938950acb61bca24d5809172fe6cfd7f1af4b059449e52f8")
+
+ctx = nettle.sha256()
+ctx:update("Hello World!Hello World!")
+hash = tostring(ctx)
+assert(hash == "95a5a79bf6218dd0938950acb61bca24d5809172fe6cfd7f1af4b059449e52f8")
+
+require "util"
+hash = hash_file("../../COPYING")
+assert(hash == "8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643")
+
+print "nettle tests completed"