improved xrfragment imports + initial commit for gazecontrol extension
This commit is contained in:
parent
4c72581493
commit
14500b44a2
15 changed files with 671 additions and 603 deletions
|
|
@ -1,16 +1,8 @@
|
||||||
-- sync lovr/love positional cli arguments
|
-- sync lovr/love positional cli arguments
|
||||||
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
||||||
|
|
||||||
package.path = package.path .. ';' ..
|
package.path = package.path .. ';' .. '?.lua;' .. 'lib/?.lua'
|
||||||
'?.lua;' ..
|
|
||||||
'?/init.lua;' ..
|
|
||||||
'lib/?.lua;'
|
|
||||||
|
|
||||||
local runtime = require("runtime/detect")
|
local runtime = require("runtime/detect")
|
||||||
|
|
||||||
package.path = package.path .. ';' ..
|
|
||||||
arg[0] .. '/' .. runtime.path .. '/?.lua;' ..
|
|
||||||
arg[0] .. '/' .. runtime.path .. '/?/init.lua;' ..
|
|
||||||
arg[0] .. '/lib/?.lua'
|
|
||||||
|
|
||||||
require( runtime.path .. "/conf")
|
require( runtime.path .. "/conf")
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,21 @@ return {
|
||||||
obj.root = true
|
obj.root = true
|
||||||
api.ext.URI.current = obj.URL -- set current url
|
api.ext.URI.current = obj.URL -- set current url
|
||||||
end
|
end
|
||||||
obj.x = obj.x or 0
|
if type(obj.URI.target) == 'table' and obj.URI.target.obj ~= nil then
|
||||||
obj.y = obj.y or 0
|
local parent = obj.URI.target.obj
|
||||||
obj.z = obj.z or 0
|
-- TODO: the runtime/lovr/render/model code should calculate parent offset
|
||||||
obj.scale = obj.scale or 1
|
obj.x = parent.x
|
||||||
|
obj.y = parent.y
|
||||||
|
obj.z = parent.z
|
||||||
|
obj.scale = parent.scale
|
||||||
|
else
|
||||||
|
obj.x = obj.x or 0
|
||||||
|
obj.y = obj.y or 0
|
||||||
|
obj.z = obj.z or 0
|
||||||
|
obj.scale = obj.scale or 1
|
||||||
|
end
|
||||||
if obj.URL.protocol == 'file' then
|
if obj.URL.protocol == 'file' then
|
||||||
obj.model = api.graphics.newModel( obj.URL.string )
|
obj.model = api.graphics.newModel( obj.URL.file )
|
||||||
else
|
else
|
||||||
obj.model = api.graphics.newModel( api.data.newBlob( obj.URLResponse.data) )
|
obj.model = api.graphics.newModel( api.data.newBlob( obj.URLResponse.data) )
|
||||||
end
|
end
|
||||||
|
|
|
||||||
35
xurfer/ext/gazecontrol/main.lua
Normal file
35
xurfer/ext/gazecontrol/main.lua
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
local api = ...
|
||||||
|
local ecs = api.ecs
|
||||||
|
|
||||||
|
local gctl
|
||||||
|
|
||||||
|
gctl = {
|
||||||
|
|
||||||
|
name = "gctl",
|
||||||
|
enabled = true,
|
||||||
|
dtmax = 1,
|
||||||
|
dt = 0,
|
||||||
|
|
||||||
|
init = function()
|
||||||
|
lovr.mouse.setRelativeMode(true)
|
||||||
|
|
||||||
|
camera = {
|
||||||
|
transform = lovr.math.newMat4(),
|
||||||
|
position = vector(),
|
||||||
|
movespeed = 10,
|
||||||
|
pitch = 0,
|
||||||
|
yaw = 0
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
|
||||||
|
update = function(dt)
|
||||||
|
end,
|
||||||
|
|
||||||
|
load = function() end,
|
||||||
|
|
||||||
|
draw = function(pass,dt)
|
||||||
|
end,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return gctl
|
||||||
|
|
@ -7,18 +7,27 @@ return {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
|
||||||
onURI = function(obj)
|
onURI = function(obj)
|
||||||
if obj.URL ~= nil and obj.URLResponse ~= nil and obj.URLResponse.ok then
|
if obj.URL ~= nil and (obj.URLResponse ~= nil or obj.URL.protocol == 'file') then
|
||||||
local res = obj.URLResponse
|
if obj.URL.protocol == 'file' and obj.URLResponse.ok == nil then
|
||||||
-- JML heuristic
|
local data = util.readfile(obj.URL.file)
|
||||||
if res.data:lower():match("<fireboxroom>") or res.data:lower():match("<room[ >]") then
|
obj.URLResponse = { data = data, ok = (data ~= nil) }
|
||||||
local JML0
|
end
|
||||||
local JML
|
if obj.URLResponse.ok then
|
||||||
res.xml = api.parser.xml.newParser()
|
local res = obj.URLResponse
|
||||||
local xmlstr = res.data
|
-- JML heuristic
|
||||||
-- cleanup JML0
|
if res.data:lower():match("<fireboxroom>") or res.data:lower():match("<room[ >]") then
|
||||||
local JML0 = xmlstr:gsub(".*<[Rr]oom","<room")
|
local JML0
|
||||||
JML0 = JML0:gsub("</[Rr]oom>.*","</room>")
|
local JML
|
||||||
api.ext.janusxr.loadXML( JML0, res, obj )
|
print_r(api.parser)
|
||||||
|
res.xml = api.parser.xml.newParser()
|
||||||
|
local xmlstr = res.data
|
||||||
|
-- cleanup JML0
|
||||||
|
local JML0 = xmlstr:gsub(".*<[Rr]oom","<room")
|
||||||
|
JML0 = JML0:gsub("</[Rr]oom>.*","</room>")
|
||||||
|
api.ext.janusxr.loadXML( JML0, res, obj )
|
||||||
|
end
|
||||||
|
else
|
||||||
|
print("[janusxr] error: could not load " .. obj.URI.url .. ": " .. (obj.URLResponse.data or '') )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,11 @@ return {
|
||||||
--api.ext.skeleton.extendECS()
|
--api.ext.skeleton.extendECS()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
update = function()
|
update = function(dt) end,
|
||||||
local iui = api.iui
|
|
||||||
local backend = api.backend
|
|
||||||
local mainWindow = api.mainWindow
|
|
||||||
end,
|
|
||||||
|
|
||||||
load = function() end,
|
load = function() end,
|
||||||
|
|
||||||
draw = function(pass)
|
draw = function(pass,dt) end,
|
||||||
if api.iui.idiom == "vr" then
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc
|
-- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc
|
||||||
extendECS = function()
|
extendECS = function()
|
||||||
|
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
--
|
|
||||||
-- json.lua
|
|
||||||
--
|
|
||||||
-- Copyright (c) 2020 rxi
|
|
||||||
--
|
|
||||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
-- this software and associated documentation files (the "Software"), to deal in
|
|
||||||
-- the Software without restriction, including without limitation the rights to
|
|
||||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
-- so, subject to the following conditions:
|
|
||||||
--
|
|
||||||
-- The above copyright notice and this permission notice shall be included in all
|
|
||||||
-- copies or substantial portions of the Software.
|
|
||||||
--
|
|
||||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
-- SOFTWARE.
|
|
||||||
--
|
|
||||||
|
|
||||||
local json = { _version = "0.1.2" }
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Encode
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local encode
|
|
||||||
|
|
||||||
local escape_char_map = {
|
|
||||||
[ "\\" ] = "\\",
|
|
||||||
[ "\"" ] = "\"",
|
|
||||||
[ "\b" ] = "b",
|
|
||||||
[ "\f" ] = "f",
|
|
||||||
[ "\n" ] = "n",
|
|
||||||
[ "\r" ] = "r",
|
|
||||||
[ "\t" ] = "t",
|
|
||||||
}
|
|
||||||
|
|
||||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
|
||||||
for k, v in pairs(escape_char_map) do
|
|
||||||
escape_char_map_inv[v] = k
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function escape_char(c)
|
|
||||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_nil(val)
|
|
||||||
return "null"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_table(val, stack)
|
|
||||||
local res = {}
|
|
||||||
stack = stack or {}
|
|
||||||
|
|
||||||
-- Circular reference?
|
|
||||||
if stack[val] then error("circular reference") end
|
|
||||||
|
|
||||||
stack[val] = true
|
|
||||||
|
|
||||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
|
||||||
-- Treat as array -- check keys are valid and it is not sparse
|
|
||||||
local n = 0
|
|
||||||
for k in pairs(val) do
|
|
||||||
if type(k) ~= "number" then
|
|
||||||
error("invalid table: mixed or invalid key types")
|
|
||||||
end
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
if n ~= #val then
|
|
||||||
error("invalid table: sparse array")
|
|
||||||
end
|
|
||||||
-- Encode
|
|
||||||
for i, v in ipairs(val) do
|
|
||||||
table.insert(res, encode(v, stack))
|
|
||||||
end
|
|
||||||
stack[val] = nil
|
|
||||||
return "[" .. table.concat(res, ",") .. "]"
|
|
||||||
|
|
||||||
else
|
|
||||||
-- Treat as an object
|
|
||||||
for k, v in pairs(val) do
|
|
||||||
if type(k) ~= "string" then
|
|
||||||
error("invalid table: mixed or invalid key types")
|
|
||||||
end
|
|
||||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
|
||||||
end
|
|
||||||
stack[val] = nil
|
|
||||||
return "{" .. table.concat(res, ",") .. "}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_string(val)
|
|
||||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_number(val)
|
|
||||||
-- Check for NaN, -inf and inf
|
|
||||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
|
||||||
error("unexpected number value '" .. tostring(val) .. "'")
|
|
||||||
end
|
|
||||||
return string.format("%.14g", val)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local type_func_map = {
|
|
||||||
[ "nil" ] = encode_nil,
|
|
||||||
[ "table" ] = encode_table,
|
|
||||||
[ "string" ] = encode_string,
|
|
||||||
[ "number" ] = encode_number,
|
|
||||||
[ "boolean" ] = tostring,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
encode = function(val, stack)
|
|
||||||
local t = type(val)
|
|
||||||
local f = type_func_map[t]
|
|
||||||
if f then
|
|
||||||
return f(val, stack)
|
|
||||||
end
|
|
||||||
error("unexpected type '" .. t .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function json.encode(val)
|
|
||||||
return ( encode(val) )
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Decode
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local parse
|
|
||||||
|
|
||||||
local function create_set(...)
|
|
||||||
local res = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
res[ select(i, ...) ] = true
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
|
||||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
|
||||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
|
||||||
local literals = create_set("true", "false", "null")
|
|
||||||
|
|
||||||
local literal_map = {
|
|
||||||
[ "true" ] = true,
|
|
||||||
[ "false" ] = false,
|
|
||||||
[ "null" ] = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function next_char(str, idx, set, negate)
|
|
||||||
for i = idx, #str do
|
|
||||||
if set[str:sub(i, i)] ~= negate then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return #str + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function decode_error(str, idx, msg)
|
|
||||||
local line_count = 1
|
|
||||||
local col_count = 1
|
|
||||||
for i = 1, idx - 1 do
|
|
||||||
col_count = col_count + 1
|
|
||||||
if str:sub(i, i) == "\n" then
|
|
||||||
line_count = line_count + 1
|
|
||||||
col_count = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function codepoint_to_utf8(n)
|
|
||||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
|
||||||
local f = math.floor
|
|
||||||
if n <= 0x7f then
|
|
||||||
return string.char(n)
|
|
||||||
elseif n <= 0x7ff then
|
|
||||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
|
||||||
elseif n <= 0xffff then
|
|
||||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
|
||||||
elseif n <= 0x10ffff then
|
|
||||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
|
||||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
|
||||||
end
|
|
||||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_unicode_escape(s)
|
|
||||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
|
||||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
|
||||||
-- Surrogate pair?
|
|
||||||
if n2 then
|
|
||||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
|
||||||
else
|
|
||||||
return codepoint_to_utf8(n1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_string(str, i)
|
|
||||||
local res = ""
|
|
||||||
local j = i + 1
|
|
||||||
local k = j
|
|
||||||
|
|
||||||
while j <= #str do
|
|
||||||
local x = str:byte(j)
|
|
||||||
|
|
||||||
if x < 32 then
|
|
||||||
decode_error(str, j, "control character in string")
|
|
||||||
|
|
||||||
elseif x == 92 then -- `\`: Escape
|
|
||||||
res = res .. str:sub(k, j - 1)
|
|
||||||
j = j + 1
|
|
||||||
local c = str:sub(j, j)
|
|
||||||
if c == "u" then
|
|
||||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
|
||||||
or str:match("^%x%x%x%x", j + 1)
|
|
||||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
|
||||||
res = res .. parse_unicode_escape(hex)
|
|
||||||
j = j + #hex
|
|
||||||
else
|
|
||||||
if not escape_chars[c] then
|
|
||||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
|
||||||
end
|
|
||||||
res = res .. escape_char_map_inv[c]
|
|
||||||
end
|
|
||||||
k = j + 1
|
|
||||||
|
|
||||||
elseif x == 34 then -- `"`: End of string
|
|
||||||
res = res .. str:sub(k, j - 1)
|
|
||||||
return res, j + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
j = j + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
decode_error(str, i, "expected closing quote for string")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_number(str, i)
|
|
||||||
local x = next_char(str, i, delim_chars)
|
|
||||||
local s = str:sub(i, x - 1)
|
|
||||||
local n = tonumber(s)
|
|
||||||
if not n then
|
|
||||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
|
||||||
end
|
|
||||||
return n, x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_literal(str, i)
|
|
||||||
local x = next_char(str, i, delim_chars)
|
|
||||||
local word = str:sub(i, x - 1)
|
|
||||||
if not literals[word] then
|
|
||||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
|
||||||
end
|
|
||||||
return literal_map[word], x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_array(str, i)
|
|
||||||
local res = {}
|
|
||||||
local n = 1
|
|
||||||
i = i + 1
|
|
||||||
while 1 do
|
|
||||||
local x
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
-- Empty / end of array?
|
|
||||||
if str:sub(i, i) == "]" then
|
|
||||||
i = i + 1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- Read token
|
|
||||||
x, i = parse(str, i)
|
|
||||||
res[n] = x
|
|
||||||
n = n + 1
|
|
||||||
-- Next token
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
local chr = str:sub(i, i)
|
|
||||||
i = i + 1
|
|
||||||
if chr == "]" then break end
|
|
||||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
|
||||||
end
|
|
||||||
return res, i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_object(str, i)
|
|
||||||
local res = {}
|
|
||||||
i = i + 1
|
|
||||||
while 1 do
|
|
||||||
local key, val
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
-- Empty / end of object?
|
|
||||||
if str:sub(i, i) == "}" then
|
|
||||||
i = i + 1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- Read key
|
|
||||||
if str:sub(i, i) ~= '"' then
|
|
||||||
decode_error(str, i, "expected string for key")
|
|
||||||
end
|
|
||||||
key, i = parse(str, i)
|
|
||||||
-- Read ':' delimiter
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
if str:sub(i, i) ~= ":" then
|
|
||||||
decode_error(str, i, "expected ':' after key")
|
|
||||||
end
|
|
||||||
i = next_char(str, i + 1, space_chars, true)
|
|
||||||
-- Read value
|
|
||||||
val, i = parse(str, i)
|
|
||||||
-- Set
|
|
||||||
res[key] = val
|
|
||||||
-- Next token
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
local chr = str:sub(i, i)
|
|
||||||
i = i + 1
|
|
||||||
if chr == "}" then break end
|
|
||||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
|
||||||
end
|
|
||||||
return res, i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local char_func_map = {
|
|
||||||
[ '"' ] = parse_string,
|
|
||||||
[ "0" ] = parse_number,
|
|
||||||
[ "1" ] = parse_number,
|
|
||||||
[ "2" ] = parse_number,
|
|
||||||
[ "3" ] = parse_number,
|
|
||||||
[ "4" ] = parse_number,
|
|
||||||
[ "5" ] = parse_number,
|
|
||||||
[ "6" ] = parse_number,
|
|
||||||
[ "7" ] = parse_number,
|
|
||||||
[ "8" ] = parse_number,
|
|
||||||
[ "9" ] = parse_number,
|
|
||||||
[ "-" ] = parse_number,
|
|
||||||
[ "t" ] = parse_literal,
|
|
||||||
[ "f" ] = parse_literal,
|
|
||||||
[ "n" ] = parse_literal,
|
|
||||||
[ "[" ] = parse_array,
|
|
||||||
[ "{" ] = parse_object,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse = function(str, idx)
|
|
||||||
local chr = str:sub(idx, idx)
|
|
||||||
local f = char_func_map[chr]
|
|
||||||
if f then
|
|
||||||
return f(str, idx)
|
|
||||||
end
|
|
||||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function json.decode(str)
|
|
||||||
if type(str) ~= "string" then
|
|
||||||
error("expected argument of type string, got " .. type(str))
|
|
||||||
end
|
|
||||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
|
||||||
idx = next_char(str, idx, space_chars, true)
|
|
||||||
if idx <= #str then
|
|
||||||
decode_error(str, idx, "trailing garbage")
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
return json
|
|
||||||
|
|
@ -107,7 +107,7 @@ xrf.makeClickable = function( physicsWorld, cb)
|
||||||
scale[3] * (d + 0.01) -- planes e.g.
|
scale[3] * (d + 0.01) -- planes e.g.
|
||||||
)
|
)
|
||||||
print("making node " .. node['name'] .. " clickable: " .. node.extras.href )
|
print("making node " .. node['name'] .. " clickable: " .. node.extras.href )
|
||||||
collider:setUserData( {name = node['name'], node = node, model = model, mesh = mesh, onclick = cb })
|
collider:setUserData( {name = node['name'], node = node, model = model, mesh = mesh, onclick = cb, obj = obj })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ xrfimpl = {
|
||||||
init = function() end,
|
init = function() end,
|
||||||
|
|
||||||
on3DFile = function(obj)
|
on3DFile = function(obj)
|
||||||
|
local referer = obj.URI.url
|
||||||
if lovr ~= nil then
|
if lovr ~= nil then
|
||||||
xrf.traverseNodesContaining('href', obj,
|
xrf.traverseNodesContaining('href', obj,
|
||||||
xrf.makeClickable( api.ecs.worldPhysics,
|
xrf.makeClickable( api.ecs.worldPhysics,
|
||||||
|
|
@ -18,10 +19,9 @@ xrfimpl = {
|
||||||
local href = obj.node.extras.href
|
local href = obj.node.extras.href
|
||||||
trace("\n[xrf] href was clicked: " .. href)
|
trace("\n[xrf] href was clicked: " .. href)
|
||||||
api.ext.exec("onClick", obj, collider)
|
api.ext.exec("onClick", obj, collider)
|
||||||
|
|
||||||
local ok = xrf.level2.load( href, obj, api.ext.URI.current, xrfimpl.onLoad)
|
local ok = xrf.level2.load( href, obj, api.ext.URI.current, xrfimpl.onLoad)
|
||||||
or
|
or
|
||||||
xrf.level4.import( href, obj, api.ext.URI.current, xrfimpl.onImport)
|
xrf.level4.import( href, obj, referer, xrfimpl.onImport)
|
||||||
|
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
@ -51,10 +51,12 @@ xrfimpl = {
|
||||||
local filter = function(o)
|
local filter = function(o)
|
||||||
return (o.URL ~= nil and o.URL.URN:gsub("#.*","") == absoluteHref:gsub("#.*",""))
|
return (o.URL ~= nil and o.URL.URN:gsub("#.*","") == absoluteHref:gsub("#.*",""))
|
||||||
end
|
end
|
||||||
local obj = api.world.get(filter)
|
local newobj = api.world.get(filter)
|
||||||
if obj then
|
if newobj then
|
||||||
obj.URL = url.parse(absoluteHref) -- re-parse to detect media fragment change
|
if newobj.URL.audio ~= nil then
|
||||||
xrfimpl.onAudioFile(obj)
|
newobj.URL = url.parse(absoluteHref) -- re-parse to detect media fragment change
|
||||||
|
xrfimpl.onAudioFile(obj)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
api.ecs.add( api.world, { URI = { url = absoluteHref, method = 'GET', target = obj } })
|
api.ecs.add( api.world, { URI = { url = absoluteHref, method = 'GET', target = obj } })
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
-- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved.
|
|
||||||
--
|
|
||||||
-- Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
-- permitted provided that the following conditions are met:
|
|
||||||
--
|
|
||||||
-- 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
-- conditions and the following disclaimer.
|
|
||||||
--
|
|
||||||
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
-- of conditions and the following disclaimer in the documentation and/or other materials
|
|
||||||
-- provided with the distribution.
|
|
||||||
--
|
|
||||||
-- THIS SOFTWARE IS PROVIDED BY AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR
|
|
||||||
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
||||||
--
|
|
||||||
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
--
|
|
||||||
-- The views and conclusions contained in the software and documentation are those of the
|
|
||||||
-- authors and should not be interpreted as representing official policies, either expressed
|
|
||||||
-- or implied, of
|
|
||||||
|
|
||||||
-- below is a lua adaptation of Godot's URI.gd
|
|
||||||
-- https://gist.github.com/coderofsalvation/b2b111a2631fbdc8e76d6cab3bea8f17
|
|
||||||
|
|
||||||
local url = {}
|
|
||||||
|
|
||||||
-- Helper function to split a string by a delimiter
|
|
||||||
local function split(str, delimiter)
|
|
||||||
local result = {}
|
|
||||||
if delimiter == "" then return {str} end
|
|
||||||
local pattern = string.format("([^%s]+)", delimiter)
|
|
||||||
for match in string.gmatch(str, pattern) do
|
|
||||||
table.insert(result, match)
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper for guess_type (converts numbers/booleans from strings)
|
|
||||||
local function guess_type(val)
|
|
||||||
if tonumber(val) then return tonumber(val) end
|
|
||||||
if val == "true" then return true end
|
|
||||||
if val == "false" then return false end
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper to parse query parameters and fragments
|
|
||||||
local function parseArgs(fragment)
|
|
||||||
local ARG = {}
|
|
||||||
if not fragment or fragment == "" then return ARG end
|
|
||||||
|
|
||||||
local items = split(fragment, "&")
|
|
||||||
for _, item in ipairs(items) do
|
|
||||||
local key_value = split(item, "=")
|
|
||||||
if #key_value > 1 then
|
|
||||||
local k = key_value[1]
|
|
||||||
ARG[k] = guess_type(key_value[2]) -- xr fragment operators preserved
|
|
||||||
ARG[k:gsub("[^%w]","") ] = guess_type(key_value[2]) -- alphanumeric only
|
|
||||||
elseif #key_value == 1 then
|
|
||||||
ARG[key_value[1]] = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ARG
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Main URL Parser function attached to the module object
|
|
||||||
function url.parse(url_string)
|
|
||||||
if type(url_string) == 'table' then return url_string end -- already parsed
|
|
||||||
-- Setup initial URI table structure
|
|
||||||
local URI = {
|
|
||||||
domain = "",
|
|
||||||
fragment = {},
|
|
||||||
file = "",
|
|
||||||
extension = "",
|
|
||||||
string = url_string,
|
|
||||||
protocol = "",
|
|
||||||
path = "",
|
|
||||||
query = {},
|
|
||||||
hash = "",
|
|
||||||
URN = "",
|
|
||||||
isLocal = false
|
|
||||||
}
|
|
||||||
local remaining = url_string
|
|
||||||
local protocol = string.match(remaining, "^(%w+://)") -- protocol
|
|
||||||
if protocol then
|
|
||||||
URI.protocol = protocol:gsub("://", "")
|
|
||||||
remaining = remaining:sub(#protocol + 1)
|
|
||||||
else
|
|
||||||
URI.protocol = "file"
|
|
||||||
end
|
|
||||||
local hash = string.match(remaining, "(#.*)$") -- fragment
|
|
||||||
if hash then
|
|
||||||
URI.hash = hash
|
|
||||||
remaining = remaining:sub(1, #remaining - #hash)
|
|
||||||
end
|
|
||||||
local query = string.match(remaining, "(%?[^#]+)$")
|
|
||||||
if query then
|
|
||||||
URI.query = query
|
|
||||||
remaining = remaining:sub(1, #remaining - #query)
|
|
||||||
end
|
|
||||||
URI.path = remaining
|
|
||||||
-- Process Path, Domain, and File
|
|
||||||
if URI.path and URI.path ~= "" then
|
|
||||||
local pathParts = split(URI.path, "/")
|
|
||||||
|
|
||||||
if #pathParts > 1 then
|
|
||||||
local firstPart = pathParts[1]
|
|
||||||
-- Check if the first part looks like a domain (contains '.' or ':')
|
|
||||||
if string.find(firstPart, "%.") or string.find(firstPart, ":") then
|
|
||||||
URI.domain = table.remove(pathParts, 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Reconstruct path
|
|
||||||
URI.path = table.concat(pathParts, "/")
|
|
||||||
-- Extract file if present
|
|
||||||
if #pathParts > 0 then
|
|
||||||
local lastPart = pathParts[#pathParts]
|
|
||||||
if string.find(lastPart, "%.") then
|
|
||||||
URI.file = lastPart
|
|
||||||
URI.extension = URI.file:match("%.([^%.]+)$"):upper()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if URI.hash and URI.hash ~= "" then
|
|
||||||
URI.fragment = parseArgs(URI.hash:sub(2)) -- sub(2) skips the '#'
|
|
||||||
else
|
|
||||||
URI.fragment = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
if URI.query and type(URI.query) == "string" and URI.query ~= "" then
|
|
||||||
URI.query = parseArgs(URI.query:sub(2)) -- sub(2) skips the '?'
|
|
||||||
else
|
|
||||||
URI.query = {}
|
|
||||||
end
|
|
||||||
if URI.domain ~= "" then
|
|
||||||
URI.URN = URI.string:gsub("%?.*", "")
|
|
||||||
else
|
|
||||||
URI.URN = ""
|
|
||||||
end
|
|
||||||
URI.isLocal = (URI.domain == "")
|
|
||||||
return URI
|
|
||||||
end
|
|
||||||
|
|
||||||
url.getAbsolute = function(href,referer,keephash)
|
|
||||||
local URL = url.parse(href)
|
|
||||||
local URLref = url.parse(referer)
|
|
||||||
if referer ~= nil and URL.protocol == 'file' and URLref.protocol ~= 'file' then
|
|
||||||
URL.protocol = URLref.protocol
|
|
||||||
URL.domain = URLref.domain
|
|
||||||
URL.string = URLref.protocol .. '://' .. URLref.domain .. '/' .. URL.path .. URL.hash
|
|
||||||
if keephash then
|
|
||||||
URL.URN = URL.string
|
|
||||||
else
|
|
||||||
URL.URN = URL.string:gsub("#.*", "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return URL
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return the module table so it can be assigned via require()
|
|
||||||
return url
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
../ext/xrfragments/json.lua
|
|
||||||
386
xurfer/lib/json.lua
Normal file
386
xurfer/lib/json.lua
Normal file
|
|
@ -0,0 +1,386 @@
|
||||||
|
--
|
||||||
|
-- json.lua
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2020 rxi
|
||||||
|
--
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
-- this software and associated documentation files (the "Software"), to deal in
|
||||||
|
-- the Software without restriction, including without limitation the rights to
|
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
-- so, subject to the following conditions:
|
||||||
|
--
|
||||||
|
-- The above copyright notice and this permission notice shall be included in all
|
||||||
|
-- copies or substantial portions of the Software.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
-- SOFTWARE.
|
||||||
|
--
|
||||||
|
|
||||||
|
local json = { _version = "0.1.2" }
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Encode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local encode
|
||||||
|
|
||||||
|
local escape_char_map = {
|
||||||
|
[ "\\" ] = "\\",
|
||||||
|
[ "\"" ] = "\"",
|
||||||
|
[ "\b" ] = "b",
|
||||||
|
[ "\f" ] = "f",
|
||||||
|
[ "\n" ] = "n",
|
||||||
|
[ "\r" ] = "r",
|
||||||
|
[ "\t" ] = "t",
|
||||||
|
}
|
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||||
|
for k, v in pairs(escape_char_map) do
|
||||||
|
escape_char_map_inv[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c)
|
||||||
|
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val)
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack)
|
||||||
|
local res = {}
|
||||||
|
stack = stack or {}
|
||||||
|
|
||||||
|
-- Circular reference?
|
||||||
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
|
stack[val] = true
|
||||||
|
|
||||||
|
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||||
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(val) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if n ~= #val then
|
||||||
|
error("invalid table: sparse array")
|
||||||
|
end
|
||||||
|
-- Encode
|
||||||
|
for i, v in ipairs(val) do
|
||||||
|
table.insert(res, encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "[" .. table.concat(res, ",") .. "]"
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Treat as an object
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if type(k) ~= "string" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "{" .. table.concat(res, ",") .. "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val)
|
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val)
|
||||||
|
-- Check for NaN, -inf and inf
|
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
|
end
|
||||||
|
return string.format("%.14g", val)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = {
|
||||||
|
[ "nil" ] = encode_nil,
|
||||||
|
[ "table" ] = encode_table,
|
||||||
|
[ "string" ] = encode_string,
|
||||||
|
[ "number" ] = encode_number,
|
||||||
|
[ "boolean" ] = tostring,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack)
|
||||||
|
local t = type(val)
|
||||||
|
local f = type_func_map[t]
|
||||||
|
if f then
|
||||||
|
return f(val, stack)
|
||||||
|
end
|
||||||
|
error("unexpected type '" .. t .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val)
|
||||||
|
return ( encode(val) )
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Decode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local parse
|
||||||
|
|
||||||
|
local function create_set(...)
|
||||||
|
local res = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
res[ select(i, ...) ] = true
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||||
|
local literals = create_set("true", "false", "null")
|
||||||
|
|
||||||
|
local literal_map = {
|
||||||
|
[ "true" ] = true,
|
||||||
|
[ "false" ] = false,
|
||||||
|
[ "null" ] = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate)
|
||||||
|
for i = idx, #str do
|
||||||
|
if set[str:sub(i, i)] ~= negate then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #str + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg)
|
||||||
|
local line_count = 1
|
||||||
|
local col_count = 1
|
||||||
|
for i = 1, idx - 1 do
|
||||||
|
col_count = col_count + 1
|
||||||
|
if str:sub(i, i) == "\n" then
|
||||||
|
line_count = line_count + 1
|
||||||
|
col_count = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n)
|
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||||
|
local f = math.floor
|
||||||
|
if n <= 0x7f then
|
||||||
|
return string.char(n)
|
||||||
|
elseif n <= 0x7ff then
|
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||||
|
elseif n <= 0xffff then
|
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
elseif n <= 0x10ffff then
|
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
end
|
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s)
|
||||||
|
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||||
|
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||||
|
-- Surrogate pair?
|
||||||
|
if n2 then
|
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||||
|
else
|
||||||
|
return codepoint_to_utf8(n1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i)
|
||||||
|
local res = ""
|
||||||
|
local j = i + 1
|
||||||
|
local k = j
|
||||||
|
|
||||||
|
while j <= #str do
|
||||||
|
local x = str:byte(j)
|
||||||
|
|
||||||
|
if x < 32 then
|
||||||
|
decode_error(str, j, "control character in string")
|
||||||
|
|
||||||
|
elseif x == 92 then -- `\`: Escape
|
||||||
|
res = res .. str:sub(k, j - 1)
|
||||||
|
j = j + 1
|
||||||
|
local c = str:sub(j, j)
|
||||||
|
if c == "u" then
|
||||||
|
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||||
|
or str:match("^%x%x%x%x", j + 1)
|
||||||
|
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||||
|
res = res .. parse_unicode_escape(hex)
|
||||||
|
j = j + #hex
|
||||||
|
else
|
||||||
|
if not escape_chars[c] then
|
||||||
|
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||||
|
end
|
||||||
|
res = res .. escape_char_map_inv[c]
|
||||||
|
end
|
||||||
|
k = j + 1
|
||||||
|
|
||||||
|
elseif x == 34 then -- `"`: End of string
|
||||||
|
res = res .. str:sub(k, j - 1)
|
||||||
|
return res, j + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local s = str:sub(i, x - 1)
|
||||||
|
local n = tonumber(s)
|
||||||
|
if not n then
|
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||||
|
end
|
||||||
|
return n, x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local word = str:sub(i, x - 1)
|
||||||
|
if not literals[word] then
|
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||||
|
end
|
||||||
|
return literal_map[word], x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i)
|
||||||
|
local res = {}
|
||||||
|
local n = 1
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local x
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of array?
|
||||||
|
if str:sub(i, i) == "]" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read token
|
||||||
|
x, i = parse(str, i)
|
||||||
|
res[n] = x
|
||||||
|
n = n + 1
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "]" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i)
|
||||||
|
local res = {}
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local key, val
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of object?
|
||||||
|
if str:sub(i, i) == "}" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read key
|
||||||
|
if str:sub(i, i) ~= '"' then
|
||||||
|
decode_error(str, i, "expected string for key")
|
||||||
|
end
|
||||||
|
key, i = parse(str, i)
|
||||||
|
-- Read ':' delimiter
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
if str:sub(i, i) ~= ":" then
|
||||||
|
decode_error(str, i, "expected ':' after key")
|
||||||
|
end
|
||||||
|
i = next_char(str, i + 1, space_chars, true)
|
||||||
|
-- Read value
|
||||||
|
val, i = parse(str, i)
|
||||||
|
-- Set
|
||||||
|
res[key] = val
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "}" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = {
|
||||||
|
[ '"' ] = parse_string,
|
||||||
|
[ "0" ] = parse_number,
|
||||||
|
[ "1" ] = parse_number,
|
||||||
|
[ "2" ] = parse_number,
|
||||||
|
[ "3" ] = parse_number,
|
||||||
|
[ "4" ] = parse_number,
|
||||||
|
[ "5" ] = parse_number,
|
||||||
|
[ "6" ] = parse_number,
|
||||||
|
[ "7" ] = parse_number,
|
||||||
|
[ "8" ] = parse_number,
|
||||||
|
[ "9" ] = parse_number,
|
||||||
|
[ "-" ] = parse_number,
|
||||||
|
[ "t" ] = parse_literal,
|
||||||
|
[ "f" ] = parse_literal,
|
||||||
|
[ "n" ] = parse_literal,
|
||||||
|
[ "[" ] = parse_array,
|
||||||
|
[ "{" ] = parse_object,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx)
|
||||||
|
local chr = str:sub(idx, idx)
|
||||||
|
local f = char_func_map[chr]
|
||||||
|
if f then
|
||||||
|
return f(str, idx)
|
||||||
|
end
|
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
error("expected argument of type string, got " .. type(str))
|
||||||
|
end
|
||||||
|
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||||
|
idx = next_char(str, idx, space_chars, true)
|
||||||
|
if idx <= #str then
|
||||||
|
decode_error(str, idx, "trailing garbage")
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
return json
|
||||||
|
|
@ -695,7 +695,9 @@ function tiny_manageEntities(world)
|
||||||
system.modified = true
|
system.modified = true
|
||||||
local tmpEntity = ses[#ses]
|
local tmpEntity = ses[#ses]
|
||||||
ses[index] = tmpEntity
|
ses[index] = tmpEntity
|
||||||
seis[tmpEntity] = index
|
if tmpEntity ~= nil then
|
||||||
|
seis[tmpEntity] = index
|
||||||
|
end
|
||||||
seis[entity] = nil
|
seis[entity] = nil
|
||||||
ses[#ses] = nil
|
ses[#ses] = nil
|
||||||
local onRemove = system.onRemove
|
local onRemove = system.onRemove
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
../ext/xrfragments/url.lua
|
|
||||||
167
xurfer/lib/url.lua
Normal file
167
xurfer/lib/url.lua
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
-- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved.
|
||||||
|
--
|
||||||
|
-- Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
-- permitted provided that the following conditions are met:
|
||||||
|
--
|
||||||
|
-- 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
-- conditions and the following disclaimer.
|
||||||
|
--
|
||||||
|
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
-- of conditions and the following disclaimer in the documentation and/or other materials
|
||||||
|
-- provided with the distribution.
|
||||||
|
--
|
||||||
|
-- THIS SOFTWARE IS PROVIDED BY AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR
|
||||||
|
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
--
|
||||||
|
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
--
|
||||||
|
-- The views and conclusions contained in the software and documentation are those of the
|
||||||
|
-- authors and should not be interpreted as representing official policies, either expressed
|
||||||
|
-- or implied, of
|
||||||
|
|
||||||
|
-- below is a lua adaptation of Godot's URI.gd
|
||||||
|
-- https://gist.github.com/coderofsalvation/b2b111a2631fbdc8e76d6cab3bea8f17
|
||||||
|
|
||||||
|
local url = {}
|
||||||
|
|
||||||
|
-- Helper function to split a string by a delimiter
|
||||||
|
local function split(str, delimiter)
|
||||||
|
local result = {}
|
||||||
|
if delimiter == "" then return {str} end
|
||||||
|
local pattern = string.format("([^%s]+)", delimiter)
|
||||||
|
for match in string.gmatch(str, pattern) do
|
||||||
|
table.insert(result, match)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper for guess_type (converts numbers/booleans from strings)
|
||||||
|
local function guess_type(val)
|
||||||
|
if tonumber(val) then return tonumber(val) end
|
||||||
|
if val == "true" then return true end
|
||||||
|
if val == "false" then return false end
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper to parse query parameters and fragments
|
||||||
|
local function parseArgs(fragment)
|
||||||
|
local ARG = {}
|
||||||
|
if not fragment or fragment == "" then return ARG end
|
||||||
|
|
||||||
|
local items = split(fragment, "&")
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
local key_value = split(item, "=")
|
||||||
|
if #key_value > 1 then
|
||||||
|
local k = key_value[1]
|
||||||
|
ARG[k] = guess_type(key_value[2]) -- xr fragment operators preserved
|
||||||
|
ARG[k:gsub("[^%w]","") ] = guess_type(key_value[2]) -- alphanumeric only
|
||||||
|
elseif #key_value == 1 then
|
||||||
|
ARG[key_value[1]] = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ARG
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Main URL Parser function attached to the module object
|
||||||
|
function url.parse(url_string)
|
||||||
|
if type(url_string) == 'table' then return url_string end -- already parsed
|
||||||
|
-- Setup initial URI table structure
|
||||||
|
local URI = {
|
||||||
|
domain = "",
|
||||||
|
fragment = {},
|
||||||
|
file = "",
|
||||||
|
extension = "",
|
||||||
|
string = url_string,
|
||||||
|
protocol = "",
|
||||||
|
path = "",
|
||||||
|
query = {},
|
||||||
|
hash = "",
|
||||||
|
URN = "",
|
||||||
|
isLocal = false
|
||||||
|
}
|
||||||
|
local remaining = url_string
|
||||||
|
local protocol = string.match(remaining, "^(%w+://)") -- protocol
|
||||||
|
if protocol then
|
||||||
|
URI.protocol = protocol:gsub("://", "")
|
||||||
|
remaining = remaining:sub(#protocol + 1)
|
||||||
|
else
|
||||||
|
URI.protocol = "file"
|
||||||
|
end
|
||||||
|
local hash = string.match(remaining, "(#.*)$") -- fragment
|
||||||
|
if hash then
|
||||||
|
URI.hash = hash
|
||||||
|
remaining = remaining:sub(1, #remaining - #hash)
|
||||||
|
end
|
||||||
|
local query = string.match(remaining, "(%?[^#]+)$")
|
||||||
|
if query then
|
||||||
|
URI.query = query
|
||||||
|
remaining = remaining:sub(1, #remaining - #query)
|
||||||
|
end
|
||||||
|
URI.path = remaining
|
||||||
|
-- Process Path, Domain, and File
|
||||||
|
if URI.path and URI.path ~= "" then
|
||||||
|
local pathParts = split(URI.path, "/")
|
||||||
|
|
||||||
|
if #pathParts > 1 then
|
||||||
|
local firstPart = pathParts[1]
|
||||||
|
-- Check if the first part looks like a domain (contains '.' or ':')
|
||||||
|
if string.find(firstPart, "%.") or string.find(firstPart, ":") then
|
||||||
|
URI.domain = table.remove(pathParts, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Reconstruct path
|
||||||
|
URI.path = table.concat(pathParts, "/")
|
||||||
|
-- Extract file if present
|
||||||
|
if #pathParts > 0 then
|
||||||
|
local lastPart = pathParts[#pathParts]
|
||||||
|
if string.find(lastPart, "%.") then
|
||||||
|
URI.file = lastPart
|
||||||
|
URI.extension = URI.file:match("%.([^%.]+)$"):upper()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if URI.hash and URI.hash ~= "" then
|
||||||
|
URI.fragment = parseArgs(URI.hash:sub(2)) -- sub(2) skips the '#'
|
||||||
|
else
|
||||||
|
URI.fragment = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if URI.query and type(URI.query) == "string" and URI.query ~= "" then
|
||||||
|
URI.query = parseArgs(URI.query:sub(2)) -- sub(2) skips the '?'
|
||||||
|
else
|
||||||
|
URI.query = {}
|
||||||
|
end
|
||||||
|
if URI.domain ~= "" then
|
||||||
|
URI.URN = URI.string:gsub("%?.*", "")
|
||||||
|
else
|
||||||
|
URI.URN = ""
|
||||||
|
end
|
||||||
|
URI.isLocal = (URI.domain == "")
|
||||||
|
return URI
|
||||||
|
end
|
||||||
|
|
||||||
|
url.getAbsolute = function(href,referer,keephash)
|
||||||
|
local URL = url.parse(href)
|
||||||
|
local URLref = url.parse(referer)
|
||||||
|
if referer ~= nil and URL.protocol == 'file' and URLref.protocol ~= 'file' then
|
||||||
|
URL.protocol = URLref.protocol or ''
|
||||||
|
URL.domain = URLref.domain or ''
|
||||||
|
URL.string = URL.protocol .. '://' .. URL.domain .. '/' .. URL.path .. URL.hash
|
||||||
|
if keephash then
|
||||||
|
URL.URN = URL.string
|
||||||
|
else
|
||||||
|
URL.URN = URL.string:gsub("#.*", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
URL.referer = referer
|
||||||
|
return URL
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return the module table so it can be assigned via require()
|
||||||
|
return url
|
||||||
|
|
@ -167,3 +167,8 @@ function newNode(name)
|
||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
newNode = newNode,
|
||||||
|
newParser = newParser
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,22 @@
|
||||||
|
local runtime = require("runtime/detect")
|
||||||
|
-- sync lovr/love positional cli arguments
|
||||||
|
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
||||||
|
|
||||||
|
local runtime = require("runtime/detect")
|
||||||
|
|
||||||
|
-- setup path
|
||||||
|
print("!!!!!!!")
|
||||||
|
package.path = package.path .. ';' ..
|
||||||
|
'./runtime/lovr/?.lua;' ..
|
||||||
|
'./runtime/lovr/?/init.lua;' ..
|
||||||
|
'./lib/?.lua;' ..
|
||||||
|
'./?.lua'
|
||||||
|
--arg[0] .. 'runtime/lovr/?.lua;' ..
|
||||||
|
--arg[0] .. 'runtime/lovr/?/init.lua;' ..
|
||||||
|
--arg[0] .. 'lib/?.lua;' ..
|
||||||
|
|
||||||
local util = require("util")
|
local util = require("util")
|
||||||
local launch = require "launch"
|
local launch = require "runtime/lovr/launch"
|
||||||
|
|
||||||
local function isVR()
|
local function isVR()
|
||||||
return launch.mode == "vr" or util.match(arg,"--desktop") == false
|
return launch.mode == "vr" or util.match(arg,"--desktop") == false
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
local util = { ext = {} }
|
local util = { ext = {} }
|
||||||
|
|
||||||
|
util.readfile = function(filename, mode)
|
||||||
|
local file, err = io.open(filename, mode or "r")
|
||||||
|
if not file then return print("Error opening file:", err) end
|
||||||
|
local content = file:read("*a")
|
||||||
|
file:close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
util.loaddir = function( path, api, target)
|
util.loaddir = function( path, api, target)
|
||||||
local dir = api.filesystem.getDirectoryItems(path)
|
local dir = api.filesystem.getDirectoryItems(path)
|
||||||
foreach( dir, function(k,v)
|
foreach( dir, function(k,v)
|
||||||
|
|
@ -153,8 +161,8 @@ end
|
||||||
-- entity.commit() -- this recalculates the filtercache of systems
|
-- entity.commit() -- this recalculates the filtercache of systems
|
||||||
util.commit = function(world, obj, api)
|
util.commit = function(world, obj, api)
|
||||||
return function(extCb)
|
return function(extCb)
|
||||||
for j = 1, #world.entities do
|
for i = 1, #world.systems do
|
||||||
local system = world.systems[j]
|
local system = world.systems[i]
|
||||||
system.entities = {}
|
system.entities = {}
|
||||||
for j = 1, #world.entities do
|
for j = 1, #world.entities do
|
||||||
local entity = world.entities[j]
|
local entity = world.entities[j]
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue