Merge pull request #649 from MihailRis/generated-pcm-stream
Generated PCM stream (part 1)
This commit is contained in:
@@ -24,10 +24,46 @@ function update_setting(x, id, name, postfix)
|
||||
)
|
||||
end
|
||||
|
||||
local initialized = false
|
||||
|
||||
function on_open()
|
||||
if not initialized then
|
||||
initialized = true
|
||||
local token = audio.input.__get_core_token()
|
||||
document.root:add("<container id='tm' />")
|
||||
local prev_amplitude = 0.0
|
||||
document.tm:setInterval(16, function()
|
||||
audio.input.fetch(token)
|
||||
local amplitude = audio.input.get_max_amplitude()
|
||||
if amplitude > 0.0 then
|
||||
amplitude = math.sqrt(amplitude)
|
||||
end
|
||||
amplitude = math.max(amplitude, prev_amplitude - time.delta())
|
||||
document.input_volume_inner.size = {
|
||||
prev_amplitude *
|
||||
document.input_volume_outer.size[1],
|
||||
document.input_volume_outer.size[2]
|
||||
}
|
||||
prev_amplitude = amplitude
|
||||
end)
|
||||
end
|
||||
create_setting("audio.volume-master", "Master Volume", 0.01)
|
||||
create_setting("audio.volume-regular", "Regular Sounds", 0.01)
|
||||
create_setting("audio.volume-ui", "UI Sounds", 0.01)
|
||||
create_setting("audio.volume-ambient", "Ambient", 0.01)
|
||||
create_setting("audio.volume-music", "Music", 0.01)
|
||||
document.root:add("<label context='settings'>@Microphone</label>")
|
||||
document.root:add("<select id='input_device_select' "..
|
||||
"onselect='function(opt) audio.set_input_device(opt) end'/>")
|
||||
document.root:add("<container id='input_volume_outer' color='#000000' size='4'>"
|
||||
.."<container id='input_volume_inner' color='#00FF00FF' pos='1' size='2'/>"
|
||||
.."</container>")
|
||||
local selectbox = document.input_device_select
|
||||
local devices = {}
|
||||
local names = audio.get_input_devices_names()
|
||||
for i, name in ipairs(names) do
|
||||
table.insert(devices, {value=name, text=name})
|
||||
end
|
||||
selectbox.options = devices
|
||||
selectbox.value = audio.get_input_info().device_specifier
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<container id="%{id}" size="32" tooltip="%{text}"
|
||||
onclick="events.emit('core:open_traceback', '%{traceback}')">
|
||||
<image src="gui/%{type}" size="32"/>
|
||||
<label pos="36,2" sizefunc="-1,-1">%{text}</label>
|
||||
<label pos="36,2" size-func="-1,-1">%{text}</label>
|
||||
<image src="gui/cross" interactive="true" size="16" gravity="top-right"
|
||||
onclick="document['%{id}']:destruct()"></image>
|
||||
</container>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
onclick='%{open_func}("%{filename}")'
|
||||
markup='md'
|
||||
tooltip='%{unit}'
|
||||
sizefunc="-1,-1">
|
||||
size-func="-1,-1">
|
||||
[#FFFFFF80]%{path}[#FFFFFFFF]%{name}
|
||||
</label>
|
||||
</container>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
local _base64_encode_urlsafe = base64.encode_urlsafe
|
||||
local _random_bytes = random.bytes
|
||||
local core_token = _base64_encode_urlsafe(_random_bytes(18))
|
||||
|
||||
local audio_input_tokens_store = {[core_token] = "core"}
|
||||
audio.input = {}
|
||||
|
||||
local _gui_confirm = gui.confirm
|
||||
local _debug_pack_by_frame = debug.get_pack_by_frame
|
||||
local _audio_fetch_input = audio.__fetch_input
|
||||
audio.__fetch_input = nil
|
||||
local MAX_FETCH = 44100 * 4
|
||||
local MAX_AMPLITUDE = 32768
|
||||
|
||||
local total_fetch = Bytearray()
|
||||
local max_amplitude = 0.0
|
||||
|
||||
function audio.__reset_fetch_buffer()
|
||||
total_fetch:clear()
|
||||
max_amplitude = 0.0
|
||||
end
|
||||
|
||||
function audio.input.get_max_amplitude()
|
||||
return max_amplitude / MAX_AMPLITUDE
|
||||
end
|
||||
|
||||
function audio.input.fetch(token, size)
|
||||
size = size or MAX_FETCH
|
||||
if audio_input_tokens_store[token] then
|
||||
if #total_fetch >= size then
|
||||
return total_fetch:slice(1, size)
|
||||
end
|
||||
local fetched = _audio_fetch_input(size - #total_fetch)
|
||||
if not fetched then
|
||||
return
|
||||
end
|
||||
for i, sample in ipairs(I16view(fetched)) do
|
||||
max_amplitude = math.max(math.abs(sample))
|
||||
end
|
||||
total_fetch:append(fetched)
|
||||
return total_fetch:slice()
|
||||
end
|
||||
error("access denied")
|
||||
end
|
||||
|
||||
local GRANT_PERMISSION_MSG = "Grant '%{0}' pack audio recording permission?"
|
||||
|
||||
function audio.input.request_open(callback)
|
||||
local token = _base64_encode_urlsafe(_random_bytes(18))
|
||||
local caller = _debug_pack_by_frame(1)
|
||||
_gui_confirm(gui.str(GRANT_PERMISSION_MSG):gsub("%%{0}", caller), function()
|
||||
audio_input_tokens_store[token] = caller
|
||||
callback(token)
|
||||
menu:reset()
|
||||
end)
|
||||
end
|
||||
|
||||
function audio.input.__get_core_token()
|
||||
local caller = _debug_pack_by_frame(1)
|
||||
if caller == "core" then
|
||||
return core_token
|
||||
end
|
||||
end
|
||||
@@ -14,6 +14,8 @@ FFI.cdef[[
|
||||
|
||||
local malloc = FFI.C.malloc
|
||||
local free = FFI.C.free
|
||||
local FFIBytearray
|
||||
local bytearray_type
|
||||
|
||||
local function grow_buffer(self, elems)
|
||||
local new_capacity = math.ceil(self.capacity / 0.75 + elems)
|
||||
@@ -119,6 +121,23 @@ local function get_capacity(self)
|
||||
return self.capacity
|
||||
end
|
||||
|
||||
local function slice(self, offset, length)
|
||||
offset = offset or 1
|
||||
length = length or (self.size - offset + 1)
|
||||
if offset < 1 or offset > self.size then
|
||||
return FFIBytearray(0)
|
||||
end
|
||||
if offset + length - 1 > self.size then
|
||||
length = self.size - offset + 1
|
||||
end
|
||||
local buffer = malloc(length)
|
||||
if not buffer then
|
||||
error("malloc(" .. length .. ") returned NULL")
|
||||
end
|
||||
FFI.copy(buffer, self.bytes + (offset - 1), length)
|
||||
return bytearray_type(buffer, length, length)
|
||||
end
|
||||
|
||||
local bytearray_methods = {
|
||||
append=append,
|
||||
insert=insert,
|
||||
@@ -127,6 +146,7 @@ local bytearray_methods = {
|
||||
clear=clear,
|
||||
reserve=reserve,
|
||||
get_capacity=get_capacity,
|
||||
slice=slice,
|
||||
}
|
||||
|
||||
local bytearray_mt = {
|
||||
@@ -168,9 +188,9 @@ local bytearray_mt = {
|
||||
}
|
||||
bytearray_mt.__pairs = bytearray_mt.__ipairs
|
||||
|
||||
local bytearray_type = FFI.metatype("bytearray_t", bytearray_mt)
|
||||
bytearray_type = FFI.metatype("bytearray_t", bytearray_mt)
|
||||
|
||||
local FFIBytearray = {
|
||||
FFIBytearray = {
|
||||
__call = function (self, n)
|
||||
local t = type(n)
|
||||
if t == "string" then
|
||||
@@ -210,7 +230,59 @@ local function FFIBytearray_as_string(bytes)
|
||||
end
|
||||
end
|
||||
|
||||
local function create_FFIview_class(name, typename, typesize)
|
||||
local FFIU16view_mt = {
|
||||
__index = function(self, key)
|
||||
if key <= 0 or key > self.size then
|
||||
return
|
||||
end
|
||||
return self.ptr[key - 1]
|
||||
end,
|
||||
__newindex = function(self, key, value)
|
||||
if key == self.size + 1 then
|
||||
return append(self, value)
|
||||
elseif key <= 0 or key > self.size then
|
||||
return
|
||||
end
|
||||
self.ptr[key - 1] = value
|
||||
end,
|
||||
__len = function(self)
|
||||
return self.size
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return string.format(name .. "[%s]{...}", tonumber(self.size))
|
||||
end,
|
||||
__ipairs = function(self)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if i <= self.size then
|
||||
return i, self.ptr[i - 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
return function (bytes)
|
||||
local ptr = FFI.cast(typename .. "*", bytes.bytes)
|
||||
local x = setmetatable({
|
||||
bytes=bytes,
|
||||
ptr=ptr,
|
||||
size=math.floor(bytes.size / typesize),
|
||||
}, FFIU16view_mt)
|
||||
return x
|
||||
end
|
||||
end
|
||||
|
||||
local FFII16view = create_FFIview_class("FFII16view", "int16_t", 2)
|
||||
local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t", 2)
|
||||
local FFII32view = create_FFIview_class("FFII32view", "int32_t", 4)
|
||||
local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t", 4)
|
||||
|
||||
return {
|
||||
FFIBytearray = setmetatable(FFIBytearray, FFIBytearray),
|
||||
FFIBytearray_as_string = FFIBytearray_as_string
|
||||
FFIBytearray_as_string = FFIBytearray_as_string,
|
||||
FFIU16view = FFIU16view,
|
||||
FFII16view = FFII16view,
|
||||
FFIU32view = FFIU32view,
|
||||
FFII32view = FFII32view,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
local breakpoints = {}
|
||||
local dbg_steps_mode = false
|
||||
local dbg_step_into_func = false
|
||||
local hook_lock = false
|
||||
local current_func
|
||||
local current_func_stack_size
|
||||
|
||||
local __parse_path = parse_path
|
||||
local _debug_getinfo = debug.getinfo
|
||||
local _debug_getlocal = debug.getlocal
|
||||
local __pause = debug.pause
|
||||
local __error = error
|
||||
local __sethook = debug.sethook
|
||||
|
||||
-- 'return' hook not called for some functions
|
||||
-- todo: speedup
|
||||
local function calc_stack_size()
|
||||
local s = debug.traceback("", 2)
|
||||
local count = 0
|
||||
for i in s:gmatch("\n") do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local is_debugging = debug.is_debugging()
|
||||
if is_debugging then
|
||||
__sethook(function (e, line)
|
||||
if e == "return" then
|
||||
local info = _debug_getinfo(2)
|
||||
if info.func == current_func then
|
||||
current_func = nil
|
||||
end
|
||||
end
|
||||
if dbg_steps_mode and not hook_lock then
|
||||
hook_lock = true
|
||||
|
||||
if not dbg_step_into_func then
|
||||
local func = _debug_getinfo(2).func
|
||||
if func ~= current_func then
|
||||
return
|
||||
end
|
||||
if current_func_stack_size ~= calc_stack_size() then
|
||||
return
|
||||
end
|
||||
end
|
||||
current_func = func
|
||||
__pause("step")
|
||||
debug.pull_events()
|
||||
end
|
||||
hook_lock = false
|
||||
local bps = breakpoints[line]
|
||||
if not bps then
|
||||
return
|
||||
end
|
||||
local source = _debug_getinfo(2).source
|
||||
if not bps[source] then
|
||||
return
|
||||
end
|
||||
current_func = _debug_getinfo(2).func
|
||||
current_func_stack_size = calc_stack_size()
|
||||
__pause("breakpoint")
|
||||
debug.pull_events()
|
||||
end, "lr")
|
||||
end
|
||||
|
||||
local DBG_EVENT_SET_BREAKPOINT = 1
|
||||
local DBG_EVENT_RM_BREAKPOINT = 2
|
||||
local DBG_EVENT_STEP = 3
|
||||
local DBG_EVENT_STEP_INTO_FUNCTION = 4
|
||||
local DBG_EVENT_RESUME = 5
|
||||
local DBG_EVENT_GET_VALUE = 6
|
||||
local __pull_events = debug.__pull_events
|
||||
local __sendvalue = debug.__sendvalue
|
||||
debug.__pull_events = nil
|
||||
debug.__sendvalue = nil
|
||||
|
||||
function debug.get_pack_by_frame(func)
|
||||
local prefix, _ = __parse_path(_debug_getinfo(func, "S").source)
|
||||
return prefix
|
||||
end
|
||||
|
||||
function debug.pull_events()
|
||||
if not is_debugging then
|
||||
return
|
||||
end
|
||||
if not debug.is_debugging() then
|
||||
is_debugging = false
|
||||
__sethook()
|
||||
end
|
||||
local events = __pull_events()
|
||||
if not events then
|
||||
return
|
||||
end
|
||||
for i, event in ipairs(events) do
|
||||
if event[1] == DBG_EVENT_SET_BREAKPOINT then
|
||||
debug.set_breakpoint(event[2], event[3])
|
||||
elseif event[1] == DBG_EVENT_RM_BREAKPOINT then
|
||||
debug.remove_breakpoint(event[2], event[3])
|
||||
elseif event[1] == DBG_EVENT_STEP then
|
||||
dbg_steps_mode = true
|
||||
dbg_step_into_func = false
|
||||
elseif event[1] == DBG_EVENT_STEP_INTO_FUNCTION then
|
||||
dbg_steps_mode = true
|
||||
dbg_step_into_func = true
|
||||
elseif event[1] == DBG_EVENT_RESUME then
|
||||
dbg_steps_mode = false
|
||||
dbg_step_into_func = false
|
||||
elseif event[1] == DBG_EVENT_GET_VALUE then
|
||||
local _, value = _debug_getlocal(event[2] + 3, event[3])
|
||||
for _, key in ipairs(event[4]) do
|
||||
if value == nil then
|
||||
value = "error: index nil value"
|
||||
break
|
||||
end
|
||||
value = value[key]
|
||||
end
|
||||
__sendvalue(value, event[2], event[3], event[4])
|
||||
__pause()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function debug.set_breakpoint(source, line)
|
||||
local bps = breakpoints[line]
|
||||
if not bps then
|
||||
bps = {}
|
||||
breakpoints[line] = bps
|
||||
end
|
||||
bps[source] = true
|
||||
end
|
||||
|
||||
function debug.remove_breakpoint(source, line)
|
||||
local bps = breakpoints[line]
|
||||
if not bps then
|
||||
return
|
||||
end
|
||||
bps[source] = nil
|
||||
end
|
||||
|
||||
function error(message, level)
|
||||
if is_debugging then
|
||||
__pause("exception", message)
|
||||
end
|
||||
__error(message, level)
|
||||
end
|
||||
@@ -0,0 +1,48 @@
|
||||
-- --------- Deprecated functions ------ --
|
||||
local function wrap_deprecated(func, name, alternatives)
|
||||
return function (...)
|
||||
on_deprecated_call(name, alternatives)
|
||||
return func(...)
|
||||
end
|
||||
end
|
||||
|
||||
block_index = wrap_deprecated(block.index, "block_index", "block.index")
|
||||
block_name = wrap_deprecated(block.name, "block_name", "block.name")
|
||||
blocks_count = wrap_deprecated(block.defs_count, "blocks_count", "block.defs_count")
|
||||
is_solid_at = wrap_deprecated(block.is_solid_at, "is_solid_at", "block.is_solid_at")
|
||||
is_replaceable_at = wrap_deprecated(block.is_replaceable_at, "is_replaceable_at", "block.is_replaceable_at")
|
||||
set_block = wrap_deprecated(block.set, "set_block", "block.set")
|
||||
get_block = wrap_deprecated(block.get, "get_block", "block.get")
|
||||
get_block_X = wrap_deprecated(block.get_X, "get_block_X", "block.get_X")
|
||||
get_block_Y = wrap_deprecated(block.get_Y, "get_block_Y", "block.get_Y")
|
||||
get_block_Z = wrap_deprecated(block.get_Z, "get_block_Z", "block.get_Z")
|
||||
get_block_states = wrap_deprecated(block.get_states, "get_block_states", "block.get_states")
|
||||
set_block_states = wrap_deprecated(block.set_states, "set_block_states", "block.set_states")
|
||||
get_block_rotation = wrap_deprecated(block.get_rotation, "get_block_rotation", "block.get_rotation")
|
||||
set_block_rotation = wrap_deprecated(block.set_rotation, "set_block_rotation", "block.set_rotation")
|
||||
get_block_user_bits = wrap_deprecated(block.get_user_bits, "get_block_user_bits", "block.get_user_bits")
|
||||
set_block_user_bits = wrap_deprecated(block.set_user_bits, "set_block_user_bits", "block.set_user_bits")
|
||||
|
||||
function load_script(path, nocache)
|
||||
on_deprecated_call("load_script", "require or loadstring")
|
||||
return __load_script(path, nocache)
|
||||
end
|
||||
|
||||
_dofile = dofile
|
||||
-- Replaces dofile('*/content/packid/*') with load_script('packid:*')
|
||||
function dofile(path)
|
||||
on_deprecated_call("dofile", "require or loadstring")
|
||||
local index = string.find(path, "/content/")
|
||||
if index then
|
||||
local newpath = string.sub(path, index+9)
|
||||
index = string.find(newpath, "/")
|
||||
if index then
|
||||
local label = string.sub(newpath, 1, index-1)
|
||||
newpath = label..':'..string.sub(newpath, index+1)
|
||||
if file.isfile(newpath) then
|
||||
return __load_script(newpath, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
return _dofile(path)
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
function file.name(path)
|
||||
return path:match("([^:/\\]+)$")
|
||||
end
|
||||
|
||||
function file.stem(path)
|
||||
local name = file.name(path)
|
||||
return name:match("(.+)%.[^%.]+$") or name
|
||||
end
|
||||
|
||||
function file.ext(path)
|
||||
return path:match("%.([^:/\\]+)$")
|
||||
end
|
||||
|
||||
function file.prefix(path)
|
||||
return path:match("^([^:]+)")
|
||||
end
|
||||
|
||||
function file.parent(path)
|
||||
local dir = path:match("(.*)/")
|
||||
if not dir then
|
||||
return file.prefix(path)..":"
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
function file.path(path)
|
||||
local pos = path:find(':')
|
||||
return path:sub(pos + 1)
|
||||
end
|
||||
|
||||
function file.join(a, b)
|
||||
if a[#a] == ':' then
|
||||
return a .. b
|
||||
end
|
||||
return a .. "/" .. b
|
||||
end
|
||||
|
||||
function file.readlines(path)
|
||||
local str = file.read(path)
|
||||
local lines = {}
|
||||
for s in str:gmatch("[^\r\n]+") do
|
||||
table.insert(lines, s)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
@@ -0,0 +1,71 @@
|
||||
function inventory.get_uses(invid, slot)
|
||||
local uses = inventory.get_data(invid, slot, "uses")
|
||||
if uses == nil then
|
||||
return item.uses(inventory.get(invid, slot))
|
||||
end
|
||||
return uses
|
||||
end
|
||||
|
||||
function inventory.use(invid, slot)
|
||||
local itemid, count = inventory.get(invid, slot)
|
||||
if itemid == nil then
|
||||
return
|
||||
end
|
||||
local item_uses = inventory.get_uses(invid, slot)
|
||||
if item_uses == nil then
|
||||
return
|
||||
end
|
||||
if item_uses == 1 then
|
||||
inventory.set(invid, slot, itemid, count - 1)
|
||||
elseif item_uses > 1 then
|
||||
inventory.set_data(invid, slot, "uses", item_uses - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function inventory.decrement(invid, slot, count)
|
||||
count = count or 1
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemcount <= count then
|
||||
inventory.set(invid, slot, 0)
|
||||
else
|
||||
inventory.set_count(invid, slot, itemcount - count)
|
||||
end
|
||||
end
|
||||
|
||||
function inventory.get_caption(invid, slot)
|
||||
local item_id, count = inventory.get(invid, slot)
|
||||
local caption = inventory.get_data(invid, slot, "caption")
|
||||
if not caption then return item.caption(item_id) end
|
||||
|
||||
return caption
|
||||
end
|
||||
|
||||
function inventory.set_caption(invid, slot, caption)
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return
|
||||
end
|
||||
if caption == nil or type(caption) ~= "string" then
|
||||
caption = ""
|
||||
end
|
||||
inventory.set_data(invid, slot, "caption", caption)
|
||||
end
|
||||
|
||||
function inventory.get_description(invid, slot)
|
||||
local item_id, count = inventory.get(invid, slot)
|
||||
local description = inventory.get_data(invid, slot, "description")
|
||||
if not description then return item.description(item_id) end
|
||||
|
||||
return description
|
||||
end
|
||||
|
||||
function inventory.set_description(invid, slot, description)
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return
|
||||
end
|
||||
if description == nil or type(description) ~= "string" then
|
||||
description = ""
|
||||
end
|
||||
inventory.set_data(invid, slot, "description", description)
|
||||
end
|
||||
@@ -0,0 +1,37 @@
|
||||
function math.clamp(_in, low, high)
|
||||
return math.min(math.max(_in, low), high)
|
||||
end
|
||||
|
||||
function math.rand(low, high)
|
||||
return low + (high - low) * math.random()
|
||||
end
|
||||
|
||||
function math.normalize(num, conf)
|
||||
conf = conf or 1
|
||||
|
||||
return (num / conf) % 1
|
||||
end
|
||||
|
||||
function math.round(num, places)
|
||||
places = places or 0
|
||||
|
||||
local mult = 10 ^ places
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
function math.sum(...)
|
||||
local numbers = nil
|
||||
local sum = 0
|
||||
|
||||
if type(...) == "table" then
|
||||
numbers = ...
|
||||
else
|
||||
numbers = {...}
|
||||
end
|
||||
|
||||
for _, v in ipairs(numbers) do
|
||||
sum = sum + v
|
||||
end
|
||||
|
||||
return sum
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
function pack.is_installed(packid)
|
||||
return file.isfile(packid..":package.json")
|
||||
end
|
||||
|
||||
function pack.data_file(packid, name)
|
||||
file.mkdirs("world:data/"..packid)
|
||||
return "world:data/"..packid.."/"..name
|
||||
end
|
||||
|
||||
function pack.shared_file(packid, name)
|
||||
file.mkdirs("config:"..packid)
|
||||
return "config:"..packid.."/"..name
|
||||
end
|
||||
@@ -0,0 +1,126 @@
|
||||
local pattern_escape_replacements = {
|
||||
["("] = "%(",
|
||||
[")"] = "%)",
|
||||
["."] = "%.",
|
||||
["%"] = "%%",
|
||||
["+"] = "%+",
|
||||
["-"] = "%-",
|
||||
["*"] = "%*",
|
||||
["?"] = "%?",
|
||||
["["] = "%[",
|
||||
["]"] = "%]",
|
||||
["^"] = "%^",
|
||||
["$"] = "%$",
|
||||
["\0"] = "%z"
|
||||
}
|
||||
|
||||
function string.pattern_safe(str)
|
||||
return string.gsub(str, ".", pattern_escape_replacements)
|
||||
end
|
||||
|
||||
local string_sub = string.sub
|
||||
local string_find = string.find
|
||||
local string_len = string.len
|
||||
function string.explode(separator, str, withpattern)
|
||||
if (withpattern == nil) then withpattern = false end
|
||||
|
||||
local ret = {}
|
||||
local current_pos = 1
|
||||
|
||||
for i = 1, string_len(str) do
|
||||
local start_pos, end_pos = string_find(
|
||||
str, separator, current_pos, not withpattern)
|
||||
if (not start_pos) then break end
|
||||
ret[i] = string_sub(str, current_pos, start_pos - 1)
|
||||
current_pos = end_pos + 1
|
||||
end
|
||||
|
||||
ret[#ret + 1] = string_sub(str, current_pos)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function string.split(str, delimiter)
|
||||
return string.explode(delimiter, str)
|
||||
end
|
||||
|
||||
function string.formatted_time(seconds, format)
|
||||
if (not seconds) then seconds = 0 end
|
||||
local hours = math.floor(seconds / 3600)
|
||||
local minutes = math.floor((seconds / 60) % 60)
|
||||
local millisecs = (seconds - math.floor(seconds)) * 1000
|
||||
seconds = math.floor(seconds % 60)
|
||||
|
||||
if (format) then
|
||||
return string.format(format, minutes, seconds, millisecs)
|
||||
else
|
||||
return { h = hours, m = minutes, s = seconds, ms = millisecs }
|
||||
end
|
||||
end
|
||||
|
||||
function string.replace(str, tofind, toreplace)
|
||||
local tbl = string.explode(tofind, str)
|
||||
if (tbl[1]) then return table.concat(tbl, toreplace) end
|
||||
return str
|
||||
end
|
||||
|
||||
function string.trim(s, char)
|
||||
if char then char = string.pattern_safe(char) else char = "%s" end
|
||||
return string.match(s, "^" .. char .. "*(.-)" .. char .. "*$") or s
|
||||
end
|
||||
|
||||
function string.trim_right(s, char)
|
||||
if char then char = string.pattern_safe(char) else char = "%s" end
|
||||
return string.match(s, "^(.-)" .. char .. "*$") or s
|
||||
end
|
||||
|
||||
function string.trim_left(s, char)
|
||||
if char then char = string.pattern_safe(char) else char = "%s" end
|
||||
return string.match(s, "^" .. char .. "*(.+)$") or s
|
||||
end
|
||||
|
||||
function string.pad(str, size, char)
|
||||
char = char == nil and " " or char
|
||||
|
||||
local padding = math.floor((size - #str) / 2)
|
||||
local extra_padding = (size - #str) % 2
|
||||
|
||||
return string.rep(char, padding) .. str .. string.rep(char, padding + extra_padding)
|
||||
end
|
||||
|
||||
function string.left_pad(str, size, char)
|
||||
char = char == nil and " " or char
|
||||
|
||||
local left_padding = size - #str
|
||||
return string.rep(char, left_padding) .. str
|
||||
end
|
||||
|
||||
function string.right_pad(str, size, char)
|
||||
char = char == nil and " " or char
|
||||
|
||||
local right_padding = size - #str
|
||||
return str .. string.rep(char, right_padding)
|
||||
end
|
||||
|
||||
string.lower = utf8.lower
|
||||
string.upper = utf8.upper
|
||||
string.escape = utf8.escape
|
||||
|
||||
local meta = getmetatable("")
|
||||
|
||||
function meta:__index(key)
|
||||
local val = string[key]
|
||||
if (val ~= nil) then
|
||||
return val
|
||||
elseif (tonumber(key)) then
|
||||
return string.sub(self, key, key)
|
||||
end
|
||||
end
|
||||
|
||||
function string.starts_with(str, start)
|
||||
return string.sub(str, 1, string.len(start)) == start
|
||||
end
|
||||
|
||||
function string.ends_with(str, endStr)
|
||||
return endStr == "" or string.sub(str, -string.len(endStr)) == endStr
|
||||
end
|
||||
@@ -0,0 +1,179 @@
|
||||
function table.copy(t)
|
||||
local copied = {}
|
||||
|
||||
for k, v in pairs(t) do
|
||||
copied[k] = v
|
||||
end
|
||||
|
||||
return copied
|
||||
end
|
||||
|
||||
function table.deep_copy(t)
|
||||
local copied = {}
|
||||
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
copied[k] = table.deep_copy(v)
|
||||
else
|
||||
copied[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(copied, getmetatable(t))
|
||||
end
|
||||
|
||||
function table.count_pairs(t)
|
||||
local count = 0
|
||||
|
||||
for k, v in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
function table.random(t)
|
||||
return t[math.random(1, #t)]
|
||||
end
|
||||
|
||||
function table.shuffle(t)
|
||||
for i = #t, 2, -1 do
|
||||
local j = math.random(i)
|
||||
t[i], t[j] = t[j], t[i]
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function table.merge(t1, t2)
|
||||
for i, v in pairs(t2) do
|
||||
if type(i) == "number" then
|
||||
t1[#t1 + 1] = v
|
||||
elseif t1[i] == nil then
|
||||
t1[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
return t1
|
||||
end
|
||||
|
||||
function table.map(t, func)
|
||||
for i, v in pairs(t) do
|
||||
t[i] = func(i, v)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function table.filter(t, func)
|
||||
|
||||
for i = #t, 1, -1 do
|
||||
if not func(i, t[i]) then
|
||||
table.remove(t, i)
|
||||
end
|
||||
end
|
||||
|
||||
local size = #t
|
||||
|
||||
for i, v in pairs(t) do
|
||||
local i_type = type(i)
|
||||
if i_type == "number" then
|
||||
if i < 1 or i > size then
|
||||
if not func(i, v) then
|
||||
t[i] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if not func(i, v) then
|
||||
t[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function table.set_default(t, key, default)
|
||||
if t[key] == nil then
|
||||
t[key] = default
|
||||
return default
|
||||
end
|
||||
|
||||
return t[key]
|
||||
end
|
||||
|
||||
function table.flat(t)
|
||||
local flat = {}
|
||||
|
||||
for _, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
table.merge(flat, v)
|
||||
else
|
||||
table.insert(flat, v)
|
||||
end
|
||||
end
|
||||
|
||||
return flat
|
||||
end
|
||||
|
||||
function table.deep_flat(t)
|
||||
local flat = {}
|
||||
|
||||
for _, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
table.merge(flat, table.deep_flat(v))
|
||||
else
|
||||
table.insert(flat, v)
|
||||
end
|
||||
end
|
||||
|
||||
return flat
|
||||
end
|
||||
|
||||
function table.sub(arr, start, stop)
|
||||
local res = {}
|
||||
start = start or 1
|
||||
stop = stop or #arr
|
||||
|
||||
for i = start, stop do
|
||||
table.insert(res, arr[i])
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
function table.has(t, x)
|
||||
for i,v in ipairs(t) do
|
||||
if v == x then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function table.index(t, x)
|
||||
for i,v in ipairs(t) do
|
||||
if v == x then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function table.remove_value(t, x)
|
||||
local index = table.index(t, x)
|
||||
if index ~= -1 then
|
||||
table.remove(t, index)
|
||||
end
|
||||
end
|
||||
|
||||
function table.tostring(t)
|
||||
local s = '['
|
||||
for i,v in ipairs(t) do
|
||||
s = s..tostring(v)
|
||||
if i < #t then
|
||||
s = s..', '
|
||||
end
|
||||
end
|
||||
return s..']'
|
||||
end
|
||||
@@ -0,0 +1,70 @@
|
||||
local rules = {nexid = 1, rules = {}}
|
||||
|
||||
function rules.get_rule(name)
|
||||
local rule = rules.rules[name]
|
||||
if rule == nil then
|
||||
rule = {listeners={}}
|
||||
rules.rules[name] = rule
|
||||
end
|
||||
return rule
|
||||
end
|
||||
|
||||
function rules.get(name)
|
||||
local rule = rules.rules[name]
|
||||
if rule == nil then
|
||||
return nil
|
||||
end
|
||||
return rule.value
|
||||
end
|
||||
|
||||
function rules.set(name, value)
|
||||
local rule = rules.get_rule(name)
|
||||
rule.value = value
|
||||
for _, handler in pairs(rule.listeners) do
|
||||
handler(value)
|
||||
end
|
||||
end
|
||||
|
||||
function rules.reset(name)
|
||||
local rule = rules.get_rule(name)
|
||||
rules.set(rule.default)
|
||||
end
|
||||
|
||||
function rules.listen(name, handler)
|
||||
local rule = rules.get_rule(name)
|
||||
local id = rules.nexid
|
||||
rules.nextid = rules.nexid + 1
|
||||
rule.listeners[utf8.encode(id)] = handler
|
||||
return id
|
||||
end
|
||||
|
||||
function rules.create(name, value, handler)
|
||||
local rule = rules.get_rule(name)
|
||||
rule.default = value
|
||||
|
||||
local handlerid
|
||||
if handler ~= nil then
|
||||
handlerid = rules.listen(name, handler)
|
||||
end
|
||||
if rules.get(name) == nil then
|
||||
rules.set(name, value)
|
||||
elseif handler then
|
||||
handler(rules.get(name))
|
||||
end
|
||||
return handlerid
|
||||
end
|
||||
|
||||
function rules.unlisten(name, id)
|
||||
local rule = rules.rules[name]
|
||||
if rule == nil then
|
||||
return
|
||||
end
|
||||
rule.listeners[utf8.encode(id)] = nil
|
||||
end
|
||||
|
||||
function rules.clear()
|
||||
rules.rules = {}
|
||||
rules.nextid = 1
|
||||
end
|
||||
|
||||
return rules
|
||||
+20
-202
@@ -105,82 +105,10 @@ elseif __vc_app then
|
||||
complete_app_lib(__vc_app)
|
||||
end
|
||||
|
||||
function inventory.get_uses(invid, slot)
|
||||
local uses = inventory.get_data(invid, slot, "uses")
|
||||
if uses == nil then
|
||||
return item.uses(inventory.get(invid, slot))
|
||||
end
|
||||
return uses
|
||||
end
|
||||
|
||||
function inventory.use(invid, slot)
|
||||
local itemid, count = inventory.get(invid, slot)
|
||||
if itemid == nil then
|
||||
return
|
||||
end
|
||||
local item_uses = inventory.get_uses(invid, slot)
|
||||
if item_uses == nil then
|
||||
return
|
||||
end
|
||||
if item_uses == 1 then
|
||||
inventory.set(invid, slot, itemid, count - 1)
|
||||
elseif item_uses > 1 then
|
||||
inventory.set_data(invid, slot, "uses", item_uses - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function inventory.decrement(invid, slot, count)
|
||||
count = count or 1
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemcount <= count then
|
||||
inventory.set(invid, slot, 0)
|
||||
else
|
||||
inventory.set_count(invid, slot, itemcount - count)
|
||||
end
|
||||
end
|
||||
|
||||
function inventory.get_caption(invid, slot)
|
||||
local item_id, count = inventory.get(invid, slot)
|
||||
local caption = inventory.get_data(invid, slot, "caption")
|
||||
if not caption then return item.caption(item_id) end
|
||||
|
||||
return caption
|
||||
end
|
||||
|
||||
function inventory.set_caption(invid, slot, caption)
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return
|
||||
end
|
||||
if caption == nil or type(caption) ~= "string" then
|
||||
caption = ""
|
||||
end
|
||||
inventory.set_data(invid, slot, "caption", caption)
|
||||
end
|
||||
|
||||
function inventory.get_description(invid, slot)
|
||||
local item_id, count = inventory.get(invid, slot)
|
||||
local description = inventory.get_data(invid, slot, "description")
|
||||
if not description then return item.description(item_id) end
|
||||
|
||||
return description
|
||||
end
|
||||
|
||||
function inventory.set_description(invid, slot, description)
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return
|
||||
end
|
||||
if description == nil or type(description) ~= "string" then
|
||||
description = ""
|
||||
end
|
||||
inventory.set_data(invid, slot, "description", description)
|
||||
end
|
||||
|
||||
if enable_experimental then
|
||||
require "core:internal/maths_inline"
|
||||
end
|
||||
|
||||
require "core:internal/maths_inline"
|
||||
require "core:internal/debugging"
|
||||
require "core:internal/audio_input"
|
||||
require "core:internal/extensions/inventory"
|
||||
asserts = require "core:internal/asserts"
|
||||
events = require "core:internal/events"
|
||||
|
||||
@@ -296,11 +224,6 @@ entities.get_all = function(uids)
|
||||
end
|
||||
end
|
||||
|
||||
local bytearray = require "core:internal/bytearray"
|
||||
Bytearray = bytearray.FFIBytearray
|
||||
Bytearray_as_string = bytearray.FFIBytearray_as_string
|
||||
Bytearray_construct = function(...) return Bytearray(...) end
|
||||
|
||||
__vc_scripts_registry = require "core:internal/scripts_registry"
|
||||
|
||||
file.open = require "core:internal/stream_providers/file"
|
||||
@@ -320,86 +243,20 @@ else
|
||||
os.pid = ffi.C.getpid()
|
||||
end
|
||||
|
||||
ffi = nil
|
||||
__vc_lock_internal_modules()
|
||||
|
||||
math.randomseed(time.uptime() * 1536227939)
|
||||
|
||||
rules = {nexid = 1, rules = {}}
|
||||
rules = require "core:internal/rules"
|
||||
local _rules = rules
|
||||
|
||||
function _rules.get_rule(name)
|
||||
local rule = _rules.rules[name]
|
||||
if rule == nil then
|
||||
rule = {listeners={}}
|
||||
_rules.rules[name] = rule
|
||||
end
|
||||
return rule
|
||||
end
|
||||
|
||||
function _rules.get(name)
|
||||
local rule = _rules.rules[name]
|
||||
if rule == nil then
|
||||
return nil
|
||||
end
|
||||
return rule.value
|
||||
end
|
||||
|
||||
function _rules.set(name, value)
|
||||
local rule = _rules.get_rule(name)
|
||||
rule.value = value
|
||||
for _, handler in pairs(rule.listeners) do
|
||||
handler(value)
|
||||
end
|
||||
end
|
||||
|
||||
function _rules.reset(name)
|
||||
local rule = _rules.get_rule(name)
|
||||
_rules.set(rule.default)
|
||||
end
|
||||
|
||||
function _rules.listen(name, handler)
|
||||
local rule = _rules.get_rule(name)
|
||||
local id = _rules.nexid
|
||||
_rules.nextid = _rules.nexid + 1
|
||||
rule.listeners[utf8.encode(id)] = handler
|
||||
return id
|
||||
end
|
||||
|
||||
function _rules.create(name, value, handler)
|
||||
local rule = _rules.get_rule(name)
|
||||
rule.default = value
|
||||
|
||||
local handlerid
|
||||
if handler ~= nil then
|
||||
handlerid = _rules.listen(name, handler)
|
||||
end
|
||||
if _rules.get(name) == nil then
|
||||
_rules.set(name, value)
|
||||
elseif handler then
|
||||
handler(_rules.get(name))
|
||||
end
|
||||
return handlerid
|
||||
end
|
||||
|
||||
function _rules.unlisten(name, id)
|
||||
local rule = _rules.rules[name]
|
||||
if rule == nil then
|
||||
return
|
||||
end
|
||||
rule.listeners[utf8.encode(id)] = nil
|
||||
end
|
||||
|
||||
function _rules.clear()
|
||||
_rules.rules = {}
|
||||
_rules.nextid = 1
|
||||
end
|
||||
|
||||
function __vc_on_hud_open()
|
||||
local _hud_is_content_access = hud._is_content_access
|
||||
local _hud_set_content_access = hud._set_content_access
|
||||
local _hud_set_debug_cheats = hud._set_debug_cheats
|
||||
|
||||
_rules.create("allow-cheats", true)
|
||||
|
||||
_rules.create("allow-content-access", hud._is_content_access(), function(value)
|
||||
hud._set_content_access(value)
|
||||
_rules.create("allow-content-access", _hud_is_content_access(), function(value)
|
||||
_hud_set_content_access(value)
|
||||
end)
|
||||
_rules.create("allow-flight", true, function(value)
|
||||
input.set_enabled("player.flight", value)
|
||||
@@ -420,7 +277,7 @@ function __vc_on_hud_open()
|
||||
input.set_enabled("player.fast_interaction", value)
|
||||
end)
|
||||
_rules.create("allow-debug-cheats", true, function(value)
|
||||
hud._set_debug_cheats(value)
|
||||
_hud_set_debug_cheats(value)
|
||||
end)
|
||||
input.add_callback("devtools.console", function()
|
||||
if menu.page ~= "" then
|
||||
@@ -577,6 +434,9 @@ end
|
||||
|
||||
local __post_runnables = {}
|
||||
|
||||
local fn_audio_reset_fetch_buffer = audio.__reset_fetch_buffer
|
||||
audio.__reset_fetch_buffer = nil
|
||||
|
||||
function __process_post_runnables()
|
||||
if #__post_runnables then
|
||||
for _, func in ipairs(__post_runnables) do
|
||||
@@ -602,6 +462,7 @@ function __process_post_runnables()
|
||||
__vc_named_coroutines[name] = nil
|
||||
end
|
||||
|
||||
fn_audio_reset_fetch_buffer()
|
||||
debug.pull_events()
|
||||
network.__process_events()
|
||||
block.__process_register_events()
|
||||
@@ -621,6 +482,7 @@ local _getinfo = debug.getinfo
|
||||
for i, name in ipairs(removed_names) do
|
||||
debug[name] = nil
|
||||
end
|
||||
|
||||
debug.getinfo = function(lvl, fields)
|
||||
if type(lvl) == "number" then
|
||||
lvl = lvl + 1
|
||||
@@ -630,51 +492,7 @@ debug.getinfo = function(lvl, fields)
|
||||
return debuginfo
|
||||
end
|
||||
|
||||
-- --------- Deprecated functions ------ --
|
||||
local function wrap_deprecated(func, name, alternatives)
|
||||
return function (...)
|
||||
on_deprecated_call(name, alternatives)
|
||||
return func(...)
|
||||
end
|
||||
end
|
||||
require "core:internal/deprecated"
|
||||
|
||||
block_index = wrap_deprecated(block.index, "block_index", "block.index")
|
||||
block_name = wrap_deprecated(block.name, "block_name", "block.name")
|
||||
blocks_count = wrap_deprecated(block.defs_count, "blocks_count", "block.defs_count")
|
||||
is_solid_at = wrap_deprecated(block.is_solid_at, "is_solid_at", "block.is_solid_at")
|
||||
is_replaceable_at = wrap_deprecated(block.is_replaceable_at, "is_replaceable_at", "block.is_replaceable_at")
|
||||
set_block = wrap_deprecated(block.set, "set_block", "block.set")
|
||||
get_block = wrap_deprecated(block.get, "get_block", "block.get")
|
||||
get_block_X = wrap_deprecated(block.get_X, "get_block_X", "block.get_X")
|
||||
get_block_Y = wrap_deprecated(block.get_Y, "get_block_Y", "block.get_Y")
|
||||
get_block_Z = wrap_deprecated(block.get_Z, "get_block_Z", "block.get_Z")
|
||||
get_block_states = wrap_deprecated(block.get_states, "get_block_states", "block.get_states")
|
||||
set_block_states = wrap_deprecated(block.set_states, "set_block_states", "block.set_states")
|
||||
get_block_rotation = wrap_deprecated(block.get_rotation, "get_block_rotation", "block.get_rotation")
|
||||
set_block_rotation = wrap_deprecated(block.set_rotation, "set_block_rotation", "block.set_rotation")
|
||||
get_block_user_bits = wrap_deprecated(block.get_user_bits, "get_block_user_bits", "block.get_user_bits")
|
||||
set_block_user_bits = wrap_deprecated(block.set_user_bits, "set_block_user_bits", "block.set_user_bits")
|
||||
|
||||
function load_script(path, nocache)
|
||||
on_deprecated_call("load_script", "require or loadstring")
|
||||
return __load_script(path, nocache)
|
||||
end
|
||||
|
||||
_dofile = dofile
|
||||
-- Replaces dofile('*/content/packid/*') with load_script('packid:*')
|
||||
function dofile(path)
|
||||
on_deprecated_call("dofile", "require or loadstring")
|
||||
local index = string.find(path, "/content/")
|
||||
if index then
|
||||
local newpath = string.sub(path, index+9)
|
||||
index = string.find(newpath, "/")
|
||||
if index then
|
||||
local label = string.sub(newpath, 1, index-1)
|
||||
newpath = label..':'..string.sub(newpath, index+1)
|
||||
if file.isfile(newpath) then
|
||||
return __load_script(newpath, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
return _dofile(path)
|
||||
end
|
||||
ffi = nil
|
||||
__vc_lock_internal_modules()
|
||||
|
||||
+63
-597
@@ -1,201 +1,8 @@
|
||||
local breakpoints = {}
|
||||
local dbg_steps_mode = false
|
||||
local dbg_step_into_func = false
|
||||
local hook_lock = false
|
||||
local current_func
|
||||
local current_func_stack_size
|
||||
|
||||
local _debug_getinfo = debug.getinfo
|
||||
local _debug_getlocal = debug.getlocal
|
||||
local __pause = debug.pause
|
||||
local __error = error
|
||||
local __sethook = debug.sethook
|
||||
|
||||
-- 'return' hook not called for some functions
|
||||
-- todo: speedup
|
||||
local function calc_stack_size()
|
||||
local s = debug.traceback("", 2)
|
||||
local count = 0
|
||||
for i in s:gmatch("\n") do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local is_debugging = debug.is_debugging()
|
||||
if is_debugging then
|
||||
__sethook(function (e, line)
|
||||
if e == "return" then
|
||||
local info = _debug_getinfo(2)
|
||||
if info.func == current_func then
|
||||
current_func = nil
|
||||
end
|
||||
end
|
||||
if dbg_steps_mode and not hook_lock then
|
||||
hook_lock = true
|
||||
|
||||
if not dbg_step_into_func then
|
||||
local func = _debug_getinfo(2).func
|
||||
if func ~= current_func then
|
||||
return
|
||||
end
|
||||
if current_func_stack_size ~= calc_stack_size() then
|
||||
return
|
||||
end
|
||||
end
|
||||
current_func = func
|
||||
__pause("step")
|
||||
debug.pull_events()
|
||||
end
|
||||
hook_lock = false
|
||||
local bps = breakpoints[line]
|
||||
if not bps then
|
||||
return
|
||||
end
|
||||
local source = _debug_getinfo(2).source
|
||||
if not bps[source] then
|
||||
return
|
||||
end
|
||||
current_func = _debug_getinfo(2).func
|
||||
current_func_stack_size = calc_stack_size()
|
||||
__pause("breakpoint")
|
||||
debug.pull_events()
|
||||
end, "lr")
|
||||
end
|
||||
|
||||
local DBG_EVENT_SET_BREAKPOINT = 1
|
||||
local DBG_EVENT_RM_BREAKPOINT = 2
|
||||
local DBG_EVENT_STEP = 3
|
||||
local DBG_EVENT_STEP_INTO_FUNCTION = 4
|
||||
local DBG_EVENT_RESUME = 5
|
||||
local DBG_EVENT_GET_VALUE = 6
|
||||
local __pull_events = debug.__pull_events
|
||||
local __sendvalue = debug.__sendvalue
|
||||
debug.__pull_events = nil
|
||||
debug.__sendvalue = nil
|
||||
|
||||
function debug.pull_events()
|
||||
if not is_debugging then
|
||||
return
|
||||
end
|
||||
if not debug.is_debugging() then
|
||||
is_debugging = false
|
||||
__sethook()
|
||||
end
|
||||
local events = __pull_events()
|
||||
if not events then
|
||||
return
|
||||
end
|
||||
for i, event in ipairs(events) do
|
||||
if event[1] == DBG_EVENT_SET_BREAKPOINT then
|
||||
debug.set_breakpoint(event[2], event[3])
|
||||
elseif event[1] == DBG_EVENT_RM_BREAKPOINT then
|
||||
debug.remove_breakpoint(event[2], event[3])
|
||||
elseif event[1] == DBG_EVENT_STEP then
|
||||
dbg_steps_mode = true
|
||||
dbg_step_into_func = false
|
||||
elseif event[1] == DBG_EVENT_STEP_INTO_FUNCTION then
|
||||
dbg_steps_mode = true
|
||||
dbg_step_into_func = true
|
||||
elseif event[1] == DBG_EVENT_RESUME then
|
||||
dbg_steps_mode = false
|
||||
dbg_step_into_func = false
|
||||
elseif event[1] == DBG_EVENT_GET_VALUE then
|
||||
local _, value = _debug_getlocal(event[2] + 3, event[3])
|
||||
for _, key in ipairs(event[4]) do
|
||||
if value == nil then
|
||||
value = "error: index nil value"
|
||||
break
|
||||
end
|
||||
value = value[key]
|
||||
end
|
||||
__sendvalue(value, event[2], event[3], event[4])
|
||||
__pause()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function debug.set_breakpoint(source, line)
|
||||
local bps = breakpoints[line]
|
||||
if not bps then
|
||||
bps = {}
|
||||
breakpoints[line] = bps
|
||||
end
|
||||
bps[source] = true
|
||||
end
|
||||
|
||||
function debug.remove_breakpoint(source, line)
|
||||
local bps = breakpoints[line]
|
||||
if not bps then
|
||||
return
|
||||
end
|
||||
bps[source] = nil
|
||||
end
|
||||
|
||||
function error(message, level)
|
||||
if is_debugging then
|
||||
__pause("exception", message)
|
||||
end
|
||||
__error(message, level)
|
||||
end
|
||||
|
||||
-- Lua has no parallelizm, also _set_data does not call any lua functions so
|
||||
-- may be reused one global ffi buffer per lua_State
|
||||
local canvas_ffi_buffer
|
||||
local canvas_ffi_buffer_size = 0
|
||||
|
||||
local ipairs_mt_supported = false
|
||||
for i, _ in ipairs(setmetatable({l={1}}, {
|
||||
__ipairs=function(self) return ipairs(self.l) end})) do
|
||||
ipairs_mt_supported = true
|
||||
end
|
||||
|
||||
if not ipairs_mt_supported then
|
||||
local raw_ipairs = ipairs
|
||||
ipairs = function(t)
|
||||
local metatable = getmetatable(t)
|
||||
if metatable and metatable.__ipairs then
|
||||
return metatable.__ipairs(t)
|
||||
end
|
||||
return raw_ipairs(t)
|
||||
end
|
||||
end
|
||||
|
||||
function await(co)
|
||||
local res, err
|
||||
while coroutine.status(co) ~= 'dead' do
|
||||
coroutine.yield()
|
||||
res, err = coroutine.resume(co)
|
||||
if err then
|
||||
return res, err
|
||||
end
|
||||
end
|
||||
return res, err
|
||||
end
|
||||
|
||||
local _ffi = ffi
|
||||
function __vc_Canvas_set_data(self, data)
|
||||
if type(data) == "cdata" then
|
||||
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
|
||||
end
|
||||
local width = self.width
|
||||
local height = self.height
|
||||
|
||||
local size = width * height * 4
|
||||
if size > canvas_ffi_buffer_size then
|
||||
canvas_ffi_buffer = _ffi.new(
|
||||
string.format("unsigned char[%s]", size)
|
||||
)
|
||||
canvas_ffi_buffer_size = size
|
||||
end
|
||||
for i=0, size - 1 do
|
||||
canvas_ffi_buffer[i] = data[i + 1]
|
||||
end
|
||||
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
|
||||
end
|
||||
local _debug_getinfo = debug.getinfo
|
||||
|
||||
function crc32(bytes, chksum)
|
||||
local chksum = chksum or 0
|
||||
chksum = chksum or 0
|
||||
|
||||
local length = #bytes
|
||||
if type(bytes) == "table" then
|
||||
@@ -231,20 +38,59 @@ function parse_path(path)
|
||||
return string.sub(path, 1, index-1), string.sub(path, index+1, -1)
|
||||
end
|
||||
|
||||
function pack.is_installed(packid)
|
||||
return file.isfile(packid..":package.json")
|
||||
-- Lua has no parallelizm, also _set_data does not call any lua functions so
|
||||
-- may be reused one global ffi buffer per lua_State
|
||||
local canvas_ffi_buffer
|
||||
local canvas_ffi_buffer_size = 0
|
||||
local _ffi = ffi
|
||||
function __vc_Canvas_set_data(self, data)
|
||||
if type(data) == "cdata" then
|
||||
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
|
||||
end
|
||||
local width = self.width
|
||||
local height = self.height
|
||||
|
||||
local size = width * height * 4
|
||||
if size > canvas_ffi_buffer_size then
|
||||
canvas_ffi_buffer = _ffi.new(
|
||||
string.format("unsigned char[%s]", size)
|
||||
)
|
||||
canvas_ffi_buffer_size = size
|
||||
end
|
||||
for i=0, size - 1 do
|
||||
canvas_ffi_buffer[i] = data[i + 1]
|
||||
end
|
||||
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
|
||||
end
|
||||
|
||||
function pack.data_file(packid, name)
|
||||
file.mkdirs("world:data/"..packid)
|
||||
return "world:data/"..packid.."/"..name
|
||||
local ipairs_mt_supported = false
|
||||
for i, _ in ipairs(setmetatable({l={1}}, {
|
||||
__ipairs=function(self) return ipairs(self.l) end})) do
|
||||
ipairs_mt_supported = true
|
||||
end
|
||||
|
||||
function pack.shared_file(packid, name)
|
||||
file.mkdirs("config:"..packid)
|
||||
return "config:"..packid.."/"..name
|
||||
if not ipairs_mt_supported then
|
||||
local raw_ipairs = ipairs
|
||||
ipairs = function(t)
|
||||
local metatable = getmetatable(t)
|
||||
if metatable and metatable.__ipairs then
|
||||
return metatable.__ipairs(t)
|
||||
end
|
||||
return raw_ipairs(t)
|
||||
end
|
||||
end
|
||||
|
||||
function await(co)
|
||||
local res, err
|
||||
while coroutine.status(co) ~= 'dead' do
|
||||
coroutine.yield()
|
||||
res, err = coroutine.resume(co)
|
||||
if err then
|
||||
return res, err
|
||||
end
|
||||
end
|
||||
return res, err
|
||||
end
|
||||
|
||||
function timeit(iters, func, ...)
|
||||
local tm = os.clock()
|
||||
@@ -256,364 +102,6 @@ end
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
function math.clamp(_in, low, high)
|
||||
return math.min(math.max(_in, low), high)
|
||||
end
|
||||
|
||||
function math.rand(low, high)
|
||||
return low + (high - low) * math.random()
|
||||
end
|
||||
|
||||
function math.normalize(num, conf)
|
||||
conf = conf or 1
|
||||
|
||||
return (num / conf) % 1
|
||||
end
|
||||
|
||||
function math.round(num, places)
|
||||
places = places or 0
|
||||
|
||||
local mult = 10 ^ places
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
function math.sum(...)
|
||||
local numbers = nil
|
||||
local sum = 0
|
||||
|
||||
if type(...) == "table" then
|
||||
numbers = ...
|
||||
else
|
||||
numbers = {...}
|
||||
end
|
||||
|
||||
for _, v in ipairs(numbers) do
|
||||
sum = sum + v
|
||||
end
|
||||
|
||||
return sum
|
||||
end
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
function table.copy(t)
|
||||
local copied = {}
|
||||
|
||||
for k, v in pairs(t) do
|
||||
copied[k] = v
|
||||
end
|
||||
|
||||
return copied
|
||||
end
|
||||
|
||||
function table.deep_copy(t)
|
||||
local copied = {}
|
||||
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
copied[k] = table.deep_copy(v)
|
||||
else
|
||||
copied[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(copied, getmetatable(t))
|
||||
end
|
||||
|
||||
function table.count_pairs(t)
|
||||
local count = 0
|
||||
|
||||
for k, v in pairs(t) do
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
function table.random(t)
|
||||
return t[math.random(1, #t)]
|
||||
end
|
||||
|
||||
function table.shuffle(t)
|
||||
for i = #t, 2, -1 do
|
||||
local j = math.random(i)
|
||||
t[i], t[j] = t[j], t[i]
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function table.merge(t1, t2)
|
||||
for i, v in pairs(t2) do
|
||||
if type(i) == "number" then
|
||||
t1[#t1 + 1] = v
|
||||
elseif t1[i] == nil then
|
||||
t1[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
return t1
|
||||
end
|
||||
|
||||
function table.map(t, func)
|
||||
for i, v in pairs(t) do
|
||||
t[i] = func(i, v)
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function table.filter(t, func)
|
||||
|
||||
for i = #t, 1, -1 do
|
||||
if not func(i, t[i]) then
|
||||
table.remove(t, i)
|
||||
end
|
||||
end
|
||||
|
||||
local size = #t
|
||||
|
||||
for i, v in pairs(t) do
|
||||
local i_type = type(i)
|
||||
if i_type == "number" then
|
||||
if i < 1 or i > size then
|
||||
if not func(i, v) then
|
||||
t[i] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if not func(i, v) then
|
||||
t[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function table.set_default(t, key, default)
|
||||
if t[key] == nil then
|
||||
t[key] = default
|
||||
return default
|
||||
end
|
||||
|
||||
return t[key]
|
||||
end
|
||||
|
||||
function table.flat(t)
|
||||
local flat = {}
|
||||
|
||||
for _, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
table.merge(flat, v)
|
||||
else
|
||||
table.insert(flat, v)
|
||||
end
|
||||
end
|
||||
|
||||
return flat
|
||||
end
|
||||
|
||||
function table.deep_flat(t)
|
||||
local flat = {}
|
||||
|
||||
for _, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
table.merge(flat, table.deep_flat(v))
|
||||
else
|
||||
table.insert(flat, v)
|
||||
end
|
||||
end
|
||||
|
||||
return flat
|
||||
end
|
||||
|
||||
function table.sub(arr, start, stop)
|
||||
local res = {}
|
||||
start = start or 1
|
||||
stop = stop or #arr
|
||||
|
||||
for i = start, stop do
|
||||
table.insert(res, arr[i])
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
local pattern_escape_replacements = {
|
||||
["("] = "%(",
|
||||
[")"] = "%)",
|
||||
["."] = "%.",
|
||||
["%"] = "%%",
|
||||
["+"] = "%+",
|
||||
["-"] = "%-",
|
||||
["*"] = "%*",
|
||||
["?"] = "%?",
|
||||
["["] = "%[",
|
||||
["]"] = "%]",
|
||||
["^"] = "%^",
|
||||
["$"] = "%$",
|
||||
["\0"] = "%z"
|
||||
}
|
||||
|
||||
function string.pattern_safe(str)
|
||||
return string.gsub(str, ".", pattern_escape_replacements)
|
||||
end
|
||||
|
||||
local string_sub = string.sub
|
||||
local string_find = string.find
|
||||
local string_len = string.len
|
||||
function string.explode(separator, str, withpattern)
|
||||
if (withpattern == nil) then withpattern = false end
|
||||
|
||||
local ret = {}
|
||||
local current_pos = 1
|
||||
|
||||
for i = 1, string_len(str) do
|
||||
local start_pos, end_pos = string_find(
|
||||
str, separator, current_pos, not withpattern)
|
||||
if (not start_pos) then break end
|
||||
ret[i] = string_sub(str, current_pos, start_pos - 1)
|
||||
current_pos = end_pos + 1
|
||||
end
|
||||
|
||||
ret[#ret + 1] = string_sub(str, current_pos)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function string.split(str, delimiter)
|
||||
return string.explode(delimiter, str)
|
||||
end
|
||||
|
||||
function string.formatted_time(seconds, format)
|
||||
if (not seconds) then seconds = 0 end
|
||||
local hours = math.floor(seconds / 3600)
|
||||
local minutes = math.floor((seconds / 60) % 60)
|
||||
local millisecs = (seconds - math.floor(seconds)) * 1000
|
||||
seconds = math.floor(seconds % 60)
|
||||
|
||||
if (format) then
|
||||
return string.format(format, minutes, seconds, millisecs)
|
||||
else
|
||||
return { h = hours, m = minutes, s = seconds, ms = millisecs }
|
||||
end
|
||||
end
|
||||
|
||||
function string.replace(str, tofind, toreplace)
|
||||
local tbl = string.explode(tofind, str)
|
||||
if (tbl[1]) then return table.concat(tbl, toreplace) end
|
||||
return str
|
||||
end
|
||||
|
||||
function string.trim(s, char)
|
||||
if char then char = string.pattern_safe(char) else char = "%s" end
|
||||
return string.match(s, "^" .. char .. "*(.-)" .. char .. "*$") or s
|
||||
end
|
||||
|
||||
function string.trim_right(s, char)
|
||||
if char then char = string.pattern_safe(char) else char = "%s" end
|
||||
return string.match(s, "^(.-)" .. char .. "*$") or s
|
||||
end
|
||||
|
||||
function string.trim_left(s, char)
|
||||
if char then char = string.pattern_safe(char) else char = "%s" end
|
||||
return string.match(s, "^" .. char .. "*(.+)$") or s
|
||||
end
|
||||
|
||||
function string.pad(str, size, char)
|
||||
char = char == nil and " " or char
|
||||
|
||||
local padding = math.floor((size - #str) / 2)
|
||||
local extra_padding = (size - #str) % 2
|
||||
|
||||
return string.rep(char, padding) .. str .. string.rep(char, padding + extra_padding)
|
||||
end
|
||||
|
||||
function string.left_pad(str, size, char)
|
||||
char = char == nil and " " or char
|
||||
|
||||
local left_padding = size - #str
|
||||
return string.rep(char, left_padding) .. str
|
||||
end
|
||||
|
||||
function string.right_pad(str, size, char)
|
||||
char = char == nil and " " or char
|
||||
|
||||
local right_padding = size - #str
|
||||
return str .. string.rep(char, right_padding)
|
||||
end
|
||||
|
||||
string.lower = utf8.lower
|
||||
string.upper = utf8.upper
|
||||
string.escape = utf8.escape
|
||||
|
||||
local meta = getmetatable("")
|
||||
|
||||
function meta:__index(key)
|
||||
local val = string[key]
|
||||
if (val ~= nil) then
|
||||
return val
|
||||
elseif (tonumber(key)) then
|
||||
return string.sub(self, key, key)
|
||||
end
|
||||
end
|
||||
|
||||
function string.starts_with(str, start)
|
||||
return string.sub(str, 1, string.len(start)) == start
|
||||
end
|
||||
|
||||
function string.ends_with(str, endStr)
|
||||
return endStr == "" or string.sub(str, -string.len(endStr)) == endStr
|
||||
end
|
||||
|
||||
function table.has(t, x)
|
||||
for i,v in ipairs(t) do
|
||||
if v == x then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function table.index(t, x)
|
||||
for i,v in ipairs(t) do
|
||||
if v == x then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
function table.remove_value(t, x)
|
||||
local index = table.index(t, x)
|
||||
if index ~= -1 then
|
||||
table.remove(t, index)
|
||||
end
|
||||
end
|
||||
|
||||
function table.tostring(t)
|
||||
local s = '['
|
||||
for i,v in ipairs(t) do
|
||||
s = s..tostring(v)
|
||||
if i < #t then
|
||||
s = s..', '
|
||||
end
|
||||
end
|
||||
return s..']'
|
||||
end
|
||||
|
||||
function file.readlines(path)
|
||||
local str = file.read(path)
|
||||
local lines = {}
|
||||
for s in str:gmatch("[^\r\n]+") do
|
||||
table.insert(lines, s)
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
function debug.count_frames()
|
||||
local frames = 1
|
||||
while true do
|
||||
@@ -768,7 +256,7 @@ function __vc__error(msg, frame, n, lastn)
|
||||
if events then
|
||||
local frames = debug.get_traceback(1)
|
||||
events.emit(
|
||||
"core:error", msg,
|
||||
"core:error", msg,
|
||||
table.sub(frames, 1 + (n or 0), lastn and #frames-lastn)
|
||||
)
|
||||
end
|
||||
@@ -782,42 +270,20 @@ function __vc_warning(msg, detail, n)
|
||||
end
|
||||
end
|
||||
|
||||
function file.name(path)
|
||||
return path:match("([^:/\\]+)$")
|
||||
end
|
||||
require "core:internal/extensions/pack"
|
||||
require "core:internal/extensions/math"
|
||||
require "core:internal/extensions/file"
|
||||
require "core:internal/extensions/table"
|
||||
require "core:internal/extensions/string"
|
||||
|
||||
function file.stem(path)
|
||||
local name = file.name(path)
|
||||
return name:match("(.+)%.[^%.]+$") or name
|
||||
end
|
||||
|
||||
function file.ext(path)
|
||||
return path:match("%.([^:/\\]+)$")
|
||||
end
|
||||
|
||||
function file.prefix(path)
|
||||
return path:match("^([^:]+)")
|
||||
end
|
||||
|
||||
function file.parent(path)
|
||||
local dir = path:match("(.*)/")
|
||||
if not dir then
|
||||
return file.prefix(path)..":"
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
function file.path(path)
|
||||
local pos = path:find(':')
|
||||
return path:sub(pos + 1)
|
||||
end
|
||||
|
||||
function file.join(a, b)
|
||||
if a[#a] == ':' then
|
||||
return a .. b
|
||||
end
|
||||
return a .. "/" .. b
|
||||
end
|
||||
local bytearray = require "core:internal/bytearray"
|
||||
Bytearray = bytearray.FFIBytearray
|
||||
Bytearray_as_string = bytearray.FFIBytearray_as_string
|
||||
U16view = bytearray.FFIU16view
|
||||
I16view = bytearray.FFII16view
|
||||
U32view = bytearray.FFIU32view
|
||||
I32view = bytearray.FFII32view
|
||||
Bytearray_construct = function(...) return Bytearray(...) end
|
||||
|
||||
bit.compile = require "core:bitwise/compiler"
|
||||
bit.execute = require "core:bitwise/executor"
|
||||
|
||||
+3
-1
@@ -25,7 +25,8 @@ Grant %{0} pack modification permission?=Выдать разрешение на
|
||||
Error at line %{0}=Ошибка на строке %{0}
|
||||
Run=Запустить
|
||||
Filter=Фильтр
|
||||
Are you sure you want to open the link: =Вы уверены, что хотите открыть ссылку:
|
||||
Are you sure you want to open the link: =Вы уверены, что хотите открыть ссылку:
|
||||
Grant '%{0}' pack audio recording permission?=Выдать паку '%{0}' разрешение на запись звука?
|
||||
|
||||
editor.info.tooltip=CTRL+S - Сохранить\nCTRL+R - Запустить\nCTRL+Z - Отменить\nCTRL+Y - Повторить
|
||||
devtools.traceback=Стек вызовов (от последнего)
|
||||
@@ -107,6 +108,7 @@ settings.Shadows quality=Качество теней
|
||||
settings.Conflict=Найдены возможные конфликты
|
||||
settings.Windowed=Оконный
|
||||
settings.Borderless=Безрамочный
|
||||
settings.Microphone=Микрофон
|
||||
|
||||
# Управление
|
||||
chunks.reload=Перезагрузить Чанки
|
||||
|
||||
Reference in New Issue
Block a user