mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-12 06:21:50 +00:00
Merge branch 'main' into fix-assets-pipeline
This commit is contained in:
commit
b9b8c6b7da
259 changed files with 5368 additions and 2080 deletions
|
|
@ -1,3 +1,4 @@
|
|||
generator = "base:demo"
|
||||
player-entity = "base:player"
|
||||
hand-skeleton = "base:hand"
|
||||
block-material = "base:stone"
|
||||
|
|
|
|||
|
|
@ -86,12 +86,27 @@ function generate_heightmap(x, y, w, h, s, inputs)
|
|||
|
||||
local rivermap = Heightmap(w, h)
|
||||
rivermap.noiseSeed = SEED
|
||||
rivermap:noise({x+21, y+12}, 0.1*s, 4)
|
||||
rivermap:abs()
|
||||
rivermap:mul(2.0)
|
||||
rivermap:pow(0.15)
|
||||
rivermap:max(0.5)
|
||||
map:mul(rivermap)
|
||||
rivermap:noise({ x + 21, y + 12 }, 0.1 * s, 4)
|
||||
rivermap:pow(4)
|
||||
rivermap:mul(3500)
|
||||
rivermap:add(1)
|
||||
rivermap:pow(-1)
|
||||
rivermap:mul(-1)
|
||||
rivermap:add(1)
|
||||
|
||||
local rivermap_bottom = Heightmap(w, h)
|
||||
rivermap_bottom:add(0.23)
|
||||
rivermap_bottom:noise({ x, y }, 2 * s, 4, 0.005)
|
||||
|
||||
map:sub(rivermap_bottom)
|
||||
|
||||
local map_multiplied = Heightmap(w, h)
|
||||
map_multiplied:add(map)
|
||||
map_multiplied:mul(rivermap)
|
||||
|
||||
map:min(map_multiplied)
|
||||
|
||||
map:add(rivermap_bottom)
|
||||
|
||||
local desertmap = Heightmap(w, h)
|
||||
desertmap.noiseSeed = SEED
|
||||
|
|
|
|||
|
|
@ -1,25 +1,91 @@
|
|||
local util = {}
|
||||
|
||||
function util.drop(ppos, itemid, count, data, pickup_delay)
|
||||
if itemid == 0 or not itemid then
|
||||
return nil
|
||||
util.DROP_FORCE = 8
|
||||
util.DROP_INIT_VEL = { 0, 3, 0 }
|
||||
util.DROP_MAX_ITEM_DIR_SHIFT_DEGREES = 65
|
||||
|
||||
---@param dir_shift? vec2 direction shift for drop; x and y must be clamped to [-0.5, 0.5]
|
||||
local function calculate_item_drop_dir(pid, dir_shift)
|
||||
if not dir_shift then
|
||||
dir_shift = { 0, 0 }
|
||||
end
|
||||
return entities.spawn("base:drop", ppos, {base__drop={
|
||||
id=itemid,
|
||||
count=count,
|
||||
data=data,
|
||||
pickup_delay=pickup_delay
|
||||
}})
|
||||
local yaw, pitch = player.get_rot(pid)
|
||||
local q_yaw = quat.from_euler({ 0, yaw, 0 })
|
||||
local q_pitch = quat.from_euler({ pitch, 0, 0 })
|
||||
local q_rotation = quat.mul(q_yaw, q_pitch)
|
||||
|
||||
local q_shift = quat.from_euler({
|
||||
-dir_shift[2] * util.DROP_MAX_ITEM_DIR_SHIFT_DEGREES * 2,
|
||||
-dir_shift[1] * util.DROP_MAX_ITEM_DIR_SHIFT_DEGREES * 2,
|
||||
0,
|
||||
})
|
||||
quat.mul(q_rotation, q_shift, q_rotation)
|
||||
return quat.mul_vec3(q_rotation, { 0, 0, -1 })
|
||||
end
|
||||
|
||||
local function calc_loot(loot_table)
|
||||
--- Create a drop from slot, set it velocity, and return it
|
||||
---@param mode integer 0 - whole stack, 1 - single item, 2 - dupe whole stack
|
||||
---@param dir_shift? vec2 direction shift for drop; x and y must be clamped to [-0.5, 0.5]
|
||||
---@return table? entity, string? error
|
||||
function util.drop_from_slot(pid, invid, slot, mode, dir_shift)
|
||||
if invid == 0 then
|
||||
return nil, "invid cannot be 0"
|
||||
end
|
||||
if mode == 2 and not player.is_infinite_items(pid) then
|
||||
return nil, "no permission to dupe items"
|
||||
end
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return nil, "no item in slot"
|
||||
end
|
||||
|
||||
local drop_itemcount = 1
|
||||
if mode == 0 then
|
||||
drop_itemcount = itemcount
|
||||
end
|
||||
if mode == 2 then
|
||||
drop_itemcount = item.stack_size(itemid)
|
||||
else
|
||||
inventory.set(invid, slot, itemid, itemcount - drop_itemcount)
|
||||
end
|
||||
|
||||
local data = inventory.get_all_data(invid, slot)
|
||||
local pvel = { player.get_vel(pid) }
|
||||
local ppos = vec3.add({ player.get_pos(pid) }, { 0, 0.7, 0 })
|
||||
local dir = calculate_item_drop_dir(pid, dir_shift)
|
||||
local throw_force = vec3.mul(dir, util.DROP_FORCE)
|
||||
local drop, err = util.drop(ppos, itemid, drop_itemcount, data, 1.5)
|
||||
if not drop then
|
||||
return nil, err
|
||||
end
|
||||
local velocity = vec3.add(throw_force, vec3.add(pvel, util.DROP_INIT_VEL))
|
||||
drop.rigidbody:set_vel(velocity)
|
||||
return drop
|
||||
end
|
||||
|
||||
---@return table? entity, string? error
|
||||
function util.drop(ppos, itemid, count, data, pickup_delay)
|
||||
if itemid == 0 or not itemid then
|
||||
return nil, "item is empty"
|
||||
end
|
||||
return entities.spawn("base:drop", ppos, {
|
||||
base__drop = {
|
||||
id = itemid,
|
||||
count = count,
|
||||
data = data,
|
||||
pickup_delay = pickup_delay
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
function util.calc_loot(loot_table)
|
||||
local results = {}
|
||||
for _, loot in ipairs(loot_table) do
|
||||
local chance = loot.chance or 1
|
||||
local count = loot.count or 1
|
||||
|
||||
|
||||
local roll = math.random()
|
||||
|
||||
|
||||
if roll < chance then
|
||||
if loot.min and loot.max then
|
||||
count = math.random(loot.min, loot.max)
|
||||
|
|
@ -27,7 +93,9 @@ local function calc_loot(loot_table)
|
|||
if count == 0 then
|
||||
goto continue
|
||||
end
|
||||
table.insert(results, {item=item.index(loot.item), count=count})
|
||||
table.insert(results, {
|
||||
item = item.index(loot.item), count = count
|
||||
})
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
|
@ -37,9 +105,9 @@ end
|
|||
function util.block_loot(blockid)
|
||||
local lootscheme = block.properties[blockid]["base:loot"]
|
||||
if lootscheme then
|
||||
return calc_loot(lootscheme)
|
||||
return util.calc_loot(lootscheme)
|
||||
end
|
||||
return {{item=block.get_picking_item(blockid), count=1}}
|
||||
return { { item = block.get_picking_item(blockid), count = 1 } }
|
||||
end
|
||||
|
||||
return util
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ dropitem = ARGS
|
|||
if dropitem.item then
|
||||
dropitem.id = item.index(dropitem.item)
|
||||
end
|
||||
if dropitem then
|
||||
if dropitem then
|
||||
timer = dropitem.pickup_delay or timer
|
||||
end
|
||||
if SAVED_DATA.item then
|
||||
|
|
@ -26,6 +26,7 @@ function on_save()
|
|||
end
|
||||
|
||||
if vc.is_client() then
|
||||
local SCALE = 0.3
|
||||
local scale = {1, 1, 1}
|
||||
local rotation = mat4.rotate({
|
||||
math.random(), math.random(), math.random()
|
||||
|
|
@ -35,7 +36,7 @@ if vc.is_client() then
|
|||
local matrix = mat4.idt()
|
||||
rig:set_model(0, item.model_name(dropitem.id))
|
||||
local bodysize = math.min(scale[1], scale[2], scale[3])
|
||||
body:set_size({scale[1], bodysize, scale[3]})
|
||||
body:set_size(vec3.mul({scale[1], bodysize, scale[3]}, SCALE))
|
||||
mat4.mul(matrix, rotation, matrix)
|
||||
mat4.scale(matrix, scale, matrix)
|
||||
rig:set_matrix(0, matrix)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
local dirtid = block.index("base:dirt")
|
||||
local grass_blockid = block.index("base:grass_block")
|
||||
|
||||
local offsets = {}
|
||||
for lx=-1,1 do
|
||||
for ly=-1,1 do
|
||||
|
|
@ -8,18 +11,17 @@ for lx=-1,1 do
|
|||
end
|
||||
|
||||
function on_random_update(x, y, z)
|
||||
local dirtid = block.index('base:dirt');
|
||||
if block.is_solid_at(x, y+1, z) then
|
||||
block.set(x, y, z, dirtid, 0)
|
||||
return
|
||||
end
|
||||
local grassblockid = block.index('base:grass_block')
|
||||
|
||||
for _, offset in ipairs(offsets) do
|
||||
local nx, ny, nz = x + offset[1], y + offset[2], z + offset[3]
|
||||
|
||||
if block.get(nx, ny, nz) == dirtid and
|
||||
not block.is_solid_at(nx, ny + 1, nz) then
|
||||
block.set(nx, ny, nz, grassblockid, 0)
|
||||
block.set(nx, ny, nz, grass_blockid, 0)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,30 +1,26 @@
|
|||
local base_util = require "util"
|
||||
|
||||
local DROP_FORCE = 8
|
||||
local DROP_INIT_VEL = {0, 3, 0}
|
||||
|
||||
function on_hud_open()
|
||||
input.add_callback("player.drop", function ()
|
||||
events.on("core:drop_outside_inventory", function(mode)
|
||||
local pid = hud.get_player()
|
||||
local invid = hud.get_exchange_inventory()
|
||||
local vp = gui.get_viewport()
|
||||
local dir_shift = table.map(input.get_mouse_pos(), function(i, v)
|
||||
return v / vp[i] - 0.5
|
||||
end)
|
||||
base_util.drop_from_slot(pid, invid, 0, mode, dir_shift)
|
||||
end)
|
||||
input.add_callback("player.drop", function()
|
||||
if hud.is_paused() or hud.is_inventory_open() then
|
||||
return
|
||||
end
|
||||
local pid = hud.get_player()
|
||||
local invid, slot = player.get_inventory(pid)
|
||||
local itemid, itemcount = inventory.get(invid, slot)
|
||||
if itemid == 0 then
|
||||
return
|
||||
local mode = 1
|
||||
if input.is_pressed("key:left-ctrl") or input.is_pressed("key:right-ctrl") then
|
||||
mode = 0
|
||||
end
|
||||
local data = inventory.get_all_data(invid, slot)
|
||||
inventory.set(invid, slot, itemid, itemcount-1)
|
||||
|
||||
local pvel = {player.get_vel(pid)}
|
||||
local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0})
|
||||
local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
|
||||
local drop = base_util.drop(ppos, itemid, 1, data, 1.5)
|
||||
if not drop then
|
||||
return
|
||||
end
|
||||
local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
|
||||
drop.rigidbody:set_vel(velocity)
|
||||
base_util.drop_from_slot(pid, invid, slot, mode)
|
||||
end)
|
||||
rules.create("do-loot-non-player", true)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,6 +16,4 @@ function on_block_broken(id, x, y, z, playerid)
|
|||
spawn_spread=vec3.mul(size, 0.4)
|
||||
})
|
||||
end
|
||||
|
||||
rules.create("do-loot-non-player", true)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
color="#FFFFFF80" size="16" pos="4,6"
|
||||
hover-color="#1080FF"></image>
|
||||
<panel orientation="horizontal" gravity="top-right"
|
||||
size="76,24" padding="8" interval="8" color="0">
|
||||
size="76,24" padding="8,6,8,2" interval="8" color="0">
|
||||
<image id="saveIcon" src="gui/save" tooltip="@Save"
|
||||
enabled="false" interactive="true"
|
||||
hover-color="#1080FF"
|
||||
|
|
@ -31,6 +31,12 @@
|
|||
hover-color="#1080FF"
|
||||
onclick="run_current_file()"
|
||||
color="#FFFFFF80" size="16"></image>
|
||||
<!-- TODO: remove after 0.32-->
|
||||
<image src="gui/item" size="16"
|
||||
enabled="true" interactive="true"
|
||||
hover-color="#1080FF"
|
||||
tooltip="Fix old VCM regions (0.31 bug)"
|
||||
onclick="fix_vcm_regions()"></image>
|
||||
</panel>
|
||||
<label id="title" pos="26,8"></label>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -113,6 +113,78 @@ local function reload_model(filename, name)
|
|||
assets.parse_model(file.ext(filename), document.editor.text, name)
|
||||
end
|
||||
|
||||
-- === TODO: remove after 0.32 ===
|
||||
local pattern =
|
||||
"region %(%s*"
|
||||
.. "([+-]?%d+%.?%d*[eE]?[+-]?%d*)%s*,%s*"
|
||||
.. "([+-]?%d+%.?%d*[eE]?[+-]?%d*)%s*,%s*"
|
||||
.. "([+-]?%d+%.?%d*[eE]?[+-]?%d*)%s*,%s*"
|
||||
.. "([+-]?%d+%.?%d*[eE]?[+-]?%d*)"
|
||||
.. "%s*%)"
|
||||
|
||||
local function replace1(x1, y1, x2, y2)
|
||||
return string.format("region (%s, %s, %s, %s)", x2, y2, x1, y1)
|
||||
end
|
||||
|
||||
local function replace2(x1, y1, x2, y2)
|
||||
return string.format("region (%s, %s, %s, %s)", x2, y1, x1, y2)
|
||||
end
|
||||
|
||||
function convert_vcm(text)
|
||||
local lines = {}
|
||||
local zpairs = {
|
||||
{"%(north%)", "%(south%)", "(TMPS)", "(TMPN)"},
|
||||
{"%,north", "%,south", ",TMPS", ",TMPN"},
|
||||
{"north%,", "south%,", "TMPS,", "TMPN,"},
|
||||
}
|
||||
local xpairs = {
|
||||
{"%(west%)", "%(east%)", "(TMPE)", "(TMPW)"},
|
||||
{"%,west", "%,east", ",TMPE", ",TMPW"},
|
||||
{"west%,", "east%,", "TMPE,", "TMPW,"},
|
||||
}
|
||||
|
||||
for i, pair in ipairs(zpairs) do
|
||||
text = text:gsub(pair[1], pair[3])
|
||||
text = text:gsub(pair[2], pair[4])
|
||||
end
|
||||
text = text:gsub("TMPS", "south")
|
||||
text = text:gsub("TMPN", "north")
|
||||
|
||||
for i, pair in ipairs(xpairs) do
|
||||
text = text:gsub(pair[1], pair[3])
|
||||
text = text:gsub(pair[2], pair[4])
|
||||
end
|
||||
text = text:gsub("TMPW", "west")
|
||||
text = text:gsub("TMPE", "east")
|
||||
|
||||
for line in text:gmatch("[^\n]*\n?") do
|
||||
if line == "" then
|
||||
break
|
||||
end
|
||||
|
||||
if line:find("(top)", 1, true) then
|
||||
line = line:gsub(pattern, replace1)
|
||||
end
|
||||
|
||||
if line:find("(bottom)", 1, true)
|
||||
or line:find("(east)", 1, true) then
|
||||
line = line:gsub(pattern, replace2)
|
||||
end
|
||||
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
return table.concat(lines)
|
||||
end
|
||||
|
||||
function fix_vcm_regions()
|
||||
if not current_file.filename then
|
||||
return
|
||||
end
|
||||
document.editor.text = convert_vcm(document.editor.text)
|
||||
end
|
||||
-- ===============================
|
||||
|
||||
function run_current_file()
|
||||
if not current_file.filename then
|
||||
return
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ local function update_line(line, uptime)
|
|||
end
|
||||
end
|
||||
|
||||
events.on("core:chat", function(message)
|
||||
local core_chat_handler = events.on("core:chat", function(message)
|
||||
while #lines >= max_lines do
|
||||
document[lines[1][1]]:destruct()
|
||||
table.remove(lines, 1)
|
||||
|
|
@ -36,7 +36,7 @@ end)
|
|||
function on_open()
|
||||
if not initialized then
|
||||
initialized = true
|
||||
|
||||
|
||||
document.root:setInterval(1/animation_fps * 1000, function ()
|
||||
local uptime = time.uptime()
|
||||
for _, line in ipairs(lines) do
|
||||
|
|
@ -52,3 +52,7 @@ function on_open()
|
|||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function on_destroy()
|
||||
events.remove("core:chat", core_chat_handler)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<inventory>
|
||||
<slots-grid pos="0, 164" rows="1" count="10" sharefunc="inventory_share_func"/>
|
||||
<slots-grid rows="3" start-index="10" count="30" sharefunc="inventory_share_func"/>
|
||||
<slots-grid pos="0, 164" rows="1" count="10" sharefunc="share_func"/>
|
||||
<slots-grid rows="3" start-index="10" count="30" sharefunc="share_func"/>
|
||||
</inventory>
|
||||
|
|
|
|||
|
|
@ -1,12 +1 @@
|
|||
function inventory_share_func(invid, slotid)
|
||||
local blockinv = hud.get_block_inventory()
|
||||
if blockinv ~= 0 then
|
||||
inventory.move(invid, slotid, blockinv)
|
||||
elseif rules.get("allow-content-access") then
|
||||
inventory.set(invid, slotid, 0, 0)
|
||||
elseif slotid < 10 then
|
||||
inventory.move_range(invid, slotid, invid, 10)
|
||||
else
|
||||
inventory.move_range(invid, slotid, invid, 0, 9)
|
||||
end
|
||||
end
|
||||
share_func = require("core:inventory_utils").share_default_func
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ function on_open(params)
|
|||
mode = params.mode
|
||||
end
|
||||
refresh()
|
||||
document.search_textbox.focused = true
|
||||
end
|
||||
|
||||
-- add - packs to be added to the world (after apply)
|
||||
|
|
@ -178,40 +179,40 @@ function place_pack(panel, packinfo, callback, position_func)
|
|||
end
|
||||
end
|
||||
|
||||
local Version = {};
|
||||
local Version = {}
|
||||
|
||||
function Version.matches_pattern(version)
|
||||
for _, letter in string.gmatch(version, "%.+") do
|
||||
if type(letter) ~= "number" or letter ~= "." then
|
||||
return false;
|
||||
end
|
||||
local t = string.split(version, ".")
|
||||
if #t ~= 2 and #t ~= 3 then return false end
|
||||
|
||||
local t = string.split(version, ".");
|
||||
|
||||
return #t == 2 or #t == 3;
|
||||
for i = 1, #t do
|
||||
local matched = string.match(t[i], "^%d+$")
|
||||
if not matched then return false end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function Version.__equal(ver1, ver2)
|
||||
return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3];
|
||||
return ver1[1] == ver2[1] and ver1[2] == ver2[2] and ver1[3] == ver2[3]
|
||||
end
|
||||
|
||||
function Version.__greater(ver1, ver2)
|
||||
if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end;
|
||||
if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end;
|
||||
return ver1[3] > ver2[3];
|
||||
if ver1[1] ~= ver2[1] then return ver1[1] > ver2[1] end
|
||||
if ver1[2] ~= ver2[2] then return ver1[2] > ver2[2] end
|
||||
return ver1[3] > ver2[3]
|
||||
end
|
||||
|
||||
function Version.__less(ver1, ver2)
|
||||
return Version.__greater(ver2, ver1);
|
||||
return Version.__greater(ver2, ver1)
|
||||
end
|
||||
|
||||
function Version.__greater_or_equal(ver1, ver2)
|
||||
return not Version.__less(ver1, ver2);
|
||||
return not Version.__less(ver2, ver1)
|
||||
end
|
||||
|
||||
function Version.__less_or_equal(ver1, ver2)
|
||||
return not Version.__greater(ver1, ver2);
|
||||
return not Version.__greater(ver1, ver2)
|
||||
end
|
||||
|
||||
Version.operators = {
|
||||
|
|
@ -223,40 +224,39 @@ Version.operators = {
|
|||
}
|
||||
|
||||
function Version.compare(op, ver1, ver2)
|
||||
ver1 = string.split(ver1, ".");
|
||||
ver2 = string.split(ver2, ".");
|
||||
ver1 = string.split(ver1, ".")
|
||||
ver2 = string.split(ver2, ".")
|
||||
|
||||
local comparison_func = Version.operators[op];
|
||||
|
||||
if comparison_func then
|
||||
return comparison_func(ver1, ver2);
|
||||
return comparison_func(ver1, ver2)
|
||||
else
|
||||
return false;
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function Version.parse(version)
|
||||
local op = string.sub(version, 1, 2);
|
||||
local op = string.sub(version, 1, 2)
|
||||
if op == ">=" or op == "<=" then
|
||||
return op, string.sub(version, #op + 1);
|
||||
return op, string.sub(version, #op + 1)
|
||||
end
|
||||
|
||||
op = string.sub(version, 1, 1);
|
||||
if op == ">" or op == "<" then
|
||||
return op, string.sub(version, #op + 1);
|
||||
return op, string.sub(version, #op + 1)
|
||||
end
|
||||
|
||||
return "=", version;
|
||||
return "=", version
|
||||
end
|
||||
|
||||
local function compare_version(dependent_version, actual_version)
|
||||
local function compare_version(op, dependent_version, actual_version)
|
||||
if Version.matches_pattern(dependent_version) and Version.matches_pattern(actual_version) then
|
||||
local op, dep_ver = Version.parse_version(dependent_version);
|
||||
Version.compare(op, dep_ver, actual_version);
|
||||
return Version.compare(op, dep_ver, actual_version)
|
||||
elseif dependent_version == "*" or dependent_version == actual_version then
|
||||
return true;
|
||||
return true
|
||||
else
|
||||
return false;
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -276,15 +276,15 @@ function check_dependencies(packinfo)
|
|||
)
|
||||
end
|
||||
|
||||
local dep_pack = pack.get_info(depid);
|
||||
|
||||
if not compare_version(depver, dep_pack.version) then
|
||||
local op, ver = Version.parse(depver)
|
||||
local dep_pack = pack.get_info(depid)
|
||||
local op, ver = Version.parse(depver)
|
||||
|
||||
if not compare_version(op, ver, dep_pack.version) then
|
||||
return string.format(
|
||||
"%s: %s != %s (%s)",
|
||||
gui.str("error.dependency-version-not-met"),
|
||||
dep_pack.version, ver, depid
|
||||
);
|
||||
)
|
||||
end
|
||||
|
||||
if table.has(packs_installed, packinfo.id) then
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
@World generator
|
||||
</button>
|
||||
<button id='content_btn' onclick='save_state() menu.page="content"'/>
|
||||
<button onclick='create_world()' margin='1,20,1,1'>
|
||||
<button onclick='create_world()' margin='0,20,0,1'>
|
||||
@Create World
|
||||
</button>
|
||||
<button onclick='menu:back()'>@Back</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<container id='pack_%{id}' onclick='%{callback}' size='0,80' color='#00000040' hover-color='#00000080' position-func="%{position_func}" z-index="%{index}">
|
||||
<label color='#FFFFFF80' size='300,25' align='right' gravity='top-right'>
|
||||
[%{id_verbose}]
|
||||
<label color='#FFFFFF80' size='340,25' align='right' gravity='top-right'>
|
||||
[%{id_verbose} %{version}]
|
||||
</label>
|
||||
<label pos='78,6'>%{title}</label>
|
||||
<label if='%{creator}' color='#CCFFE5B2' size='300,20' align='right'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@box from (0,0,0) to (1,0.5,1) delete (top,south)
|
||||
@box from (0,0.5,0) to (1,1,0.5) delete (bottom,south)
|
||||
@box from (0,0,0) to (1,0.5,1) delete (top,north)
|
||||
@box from (0,0.5,0) to (1,1,0.5) delete (bottom,north)
|
||||
@rect from (0,0.5,0.5) right (1,0,0) up (0,0,0.5) texture "$2"
|
||||
@rect from (0,0,0) right (1,0,0) up (0,1,0) texture "$1"
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ local FFI = ffi
|
|||
|
||||
FFI.cdef[[
|
||||
void* malloc(size_t);
|
||||
void* realloc(void*, size_t);
|
||||
void free(void*);
|
||||
typedef struct {
|
||||
unsigned char* bytes;
|
||||
int size;
|
||||
int capacity;
|
||||
} bytearray_t;
|
||||
void* memmove(void*, const void*, size_t);
|
||||
]]
|
||||
|
||||
local free = FFI.C.free
|
||||
|
|
@ -22,12 +24,33 @@ local GC_THRESHOLD = 50 * 1024 * 1024
|
|||
|
||||
local function malloc(size)
|
||||
local raw = FFI.C.malloc(size)
|
||||
if raw == nil then
|
||||
return nil
|
||||
if not raw then
|
||||
error("failed memory allocation of " .. size .. " bytes")
|
||||
end
|
||||
|
||||
allocated_bytes = allocated_bytes + size
|
||||
if allocated_bytes >= GC_THRESHOLD then
|
||||
collectgarbage("step", 200)
|
||||
allocated_bytes = 0
|
||||
end
|
||||
|
||||
return raw
|
||||
end
|
||||
|
||||
local function realloc(buff, size, throwerror)
|
||||
if throwerror == nil then throwerror = true end
|
||||
|
||||
if size <= 0 then
|
||||
free(buff)
|
||||
return
|
||||
end
|
||||
|
||||
local raw = FFI.C.realloc(buff, size)
|
||||
if not raw and throwerror then
|
||||
error("failed memory reallocation of block " .. buff .. " to new size " .. size)
|
||||
end
|
||||
|
||||
allocated_bytes = allocated_bytes + size
|
||||
if allocated_bytes >= GC_THRESHOLD then
|
||||
collectgarbage("step", 200)
|
||||
allocated_bytes = 0
|
||||
|
|
@ -38,23 +61,17 @@ end
|
|||
|
||||
local function grow_buffer(self, elems)
|
||||
local new_capacity = math.ceil(self.capacity / 0.75 + elems)
|
||||
local prev = self.bytes
|
||||
self.bytes = malloc(new_capacity)
|
||||
FFI.copy(self.bytes, prev, self.size)
|
||||
self.bytes = realloc(self.bytes, new_capacity)
|
||||
self.capacity = new_capacity
|
||||
free(prev)
|
||||
end
|
||||
|
||||
local function trim_buffer(self)
|
||||
if self.size == self.capacity then
|
||||
return
|
||||
if self.size == self.capacity then return end
|
||||
local new_bytes = realloc(self.bytes, self.size, false)
|
||||
if new_bytes then
|
||||
self.bytes = new_bytes
|
||||
self.capacity = self.size
|
||||
end
|
||||
local size = self.size
|
||||
local prev = self.bytes
|
||||
self.bytes = malloc(size)
|
||||
FFI.copy(self.bytes, prev, self.size)
|
||||
self.capacity = size
|
||||
free(prev)
|
||||
end
|
||||
|
||||
local function count_elements(b)
|
||||
|
|
@ -125,15 +142,21 @@ local function clear(self)
|
|||
self.size = 0
|
||||
end
|
||||
|
||||
local function reserve(self, new_capacity)
|
||||
if new_capacity <= self.capacity then
|
||||
return
|
||||
local function fill(self, index, size, byte)
|
||||
if index ~= nil and size ~= nil and size > 0 then
|
||||
if index < 1 or index + size - 1 > self.size then
|
||||
error("selected fill range is out of array bounds")
|
||||
end
|
||||
FFI.fill(self.bytes + index - 1, size, byte)
|
||||
else
|
||||
FFI.fill(self.bytes, self.size, byte)
|
||||
end
|
||||
local prev = self.bytes
|
||||
self.bytes = malloc(new_capacity)
|
||||
FFI.copy(self.bytes, prev, self.size)
|
||||
end
|
||||
|
||||
local function reserve(self, new_capacity)
|
||||
if new_capacity <= self.capacity then return end
|
||||
self.bytes = realloc(self.bytes, new_capacity)
|
||||
self.capacity = new_capacity
|
||||
free(prev)
|
||||
end
|
||||
|
||||
local function get_capacity(self)
|
||||
|
|
@ -150,22 +173,46 @@ local function slice(self, offset, length)
|
|||
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 function copy(self, srcindex, dst, dstindex, size)
|
||||
if size <= 0 then error("size of byte range must be positive non-zero integer") end
|
||||
if srcindex < 1 or srcindex + size - 1 > self.size then
|
||||
error("specified source byte range is out of source array bounds")
|
||||
end
|
||||
if dstindex < 1 or dstindex + size - 1 > dst.size then
|
||||
error("specified destination byte range is out of destination array bounds")
|
||||
end
|
||||
|
||||
FFI.copy(dst.bytes + dstindex - 1, self.bytes + srcindex - 1, size)
|
||||
end
|
||||
|
||||
local function move(self, fromindex, toindex, size)
|
||||
if size <= 0 then error("size of byte range must be positive non-zero integer") end
|
||||
if fromindex < 1 or fromindex + size - 1 > self.size then
|
||||
error("specified source byte range is out of array bounds")
|
||||
end
|
||||
if toindex < 1 or toindex + size - 1 > self.size then
|
||||
error("specified destination byte range is out of array bounds")
|
||||
end
|
||||
|
||||
FFI.C.memmove(self.bytes + toindex - 1, self.bytes + fromindex - 1, size)
|
||||
end
|
||||
|
||||
local bytearray_methods = {
|
||||
append=append,
|
||||
insert=insert,
|
||||
remove=remove,
|
||||
trim=trim_buffer,
|
||||
clear=clear,
|
||||
reserve=reserve,
|
||||
get_capacity=get_capacity,
|
||||
slice=slice,
|
||||
append = append,
|
||||
insert = insert,
|
||||
remove = remove,
|
||||
trim = trim_buffer,
|
||||
clear = clear,
|
||||
reserve = reserve,
|
||||
get_capacity = get_capacity,
|
||||
slice = slice,
|
||||
fill = fill,
|
||||
copy = copy,
|
||||
move = move
|
||||
}
|
||||
|
||||
local bytearray_mt = {
|
||||
|
|
@ -256,7 +303,8 @@ local function FFIBytearray_as_string(bytes)
|
|||
end
|
||||
end
|
||||
|
||||
local function create_FFIview_class(name, typename, typesize)
|
||||
local function create_FFIview_class(name, typename)
|
||||
local typesize = FFI.sizeof(typename)
|
||||
local ptrtype = typename .. "*"
|
||||
local FFIview_mt = {
|
||||
__index = function(self, key)
|
||||
|
|
@ -291,27 +339,44 @@ local function create_FFIview_class(name, typename, typesize)
|
|||
return i, ptr[i - 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
return function (bytes)
|
||||
local x = setmetatable({
|
||||
bytes=bytes,
|
||||
}, FFIview_mt)
|
||||
return x
|
||||
end
|
||||
return setmetatable({}, {
|
||||
__call = function (self, bytes)
|
||||
local x = setmetatable({
|
||||
bytes=bytes,
|
||||
}, FFIview_mt)
|
||||
return x
|
||||
end,
|
||||
__index = {
|
||||
typesize = function()
|
||||
return typesize
|
||||
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)
|
||||
local FFII8view = create_FFIview_class("FFII8view", "int8_t")
|
||||
local FFII16view = create_FFIview_class("FFII16view", "int16_t")
|
||||
local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t")
|
||||
local FFII32view = create_FFIview_class("FFII32view", "int32_t")
|
||||
local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t")
|
||||
local FFII64view = create_FFIview_class("FFII64view", "int64_t")
|
||||
local FFIU64view = create_FFIview_class("FFIU64view", "uint64_t")
|
||||
local FFIFLTview = create_FFIview_class("FFIFLTview", "float")
|
||||
local FFIDBLview = create_FFIview_class("FFIDBLview", "double")
|
||||
|
||||
return {
|
||||
FFIBytearray = setmetatable(FFIBytearray, FFIBytearray),
|
||||
FFIBytearray_as_string = FFIBytearray_as_string,
|
||||
FFIBytearray_as_ptr = FFIBytearray_as_ptr,
|
||||
FFII8view = FFII8view,
|
||||
FFIU16view = FFIU16view,
|
||||
FFII16view = FFII16view,
|
||||
FFIU32view = FFIU32view,
|
||||
FFII32view = FFII32view,
|
||||
FFIU64view = FFIU64view,
|
||||
FFII64view = FFII64view,
|
||||
FFIFLTview = FFIFLTview,
|
||||
FFIDBLview = FFIDBLview,
|
||||
}
|
||||
|
|
|
|||
38
res/modules/internal/ctypes.lua
Normal file
38
res/modules/internal/ctypes.lua
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
local FFI = ffi
|
||||
|
||||
FFI.cdef[[
|
||||
unsigned long long strtoull(const char* nptr, char** endptr, int base);
|
||||
long long strtoll(const char* nptr, char** endptr, int base);
|
||||
]]
|
||||
|
||||
local this = {}
|
||||
|
||||
local function create_integral_type(ctype_name, signed)
|
||||
local parse_func = signed and FFI.C.strtoll or FFI.C.strtoull
|
||||
return function(value, base)
|
||||
value = value or 0
|
||||
local typename = type(value)
|
||||
if typename == "number" then
|
||||
return FFI.cast(ctype_name, value)
|
||||
elseif typename == "string" then
|
||||
if type(base) ~= "number" then
|
||||
base = 10
|
||||
end
|
||||
return parse_func(value, nil, base)
|
||||
end
|
||||
error(string.format("invalid value type: %s", typename))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
this.uint8 = create_integral_type("uint8_t", false)
|
||||
this.uint16 = create_integral_type("uint16_t", false)
|
||||
this.uint32 = create_integral_type("uint32_t", false)
|
||||
this.uint64 = create_integral_type("uint64_t", false)
|
||||
|
||||
this.int8 = create_integral_type("int8_t", true)
|
||||
this.int16 = create_integral_type("int16_t", true)
|
||||
this.int32 = create_integral_type("int32_t", true)
|
||||
this.int64 = create_integral_type("int64_t", true)
|
||||
|
||||
return this
|
||||
|
|
@ -148,3 +148,29 @@ function error(message, level)
|
|||
end
|
||||
__error(message, level)
|
||||
end
|
||||
|
||||
local LoggerClass = {
|
||||
__index = {
|
||||
info = function (self, text)
|
||||
debug.log(string.format("[%s] %s", self.name, text))
|
||||
end,
|
||||
warning = function (self, text)
|
||||
debug.warning(string.format("[%s] %s", self.name, text))
|
||||
end,
|
||||
error = function (self, text)
|
||||
debug.error(string.format("[%s] %s", self.name, text))
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
local _getinfo = debug.getinfo
|
||||
|
||||
function debug.Logger(name)
|
||||
if not name then
|
||||
local source = _getinfo(2, "S").source
|
||||
name = file.remove_ext(source)
|
||||
else
|
||||
name = string.format("%s:%s", file.prefix(_getinfo(2, "S").source), name)
|
||||
end
|
||||
return setmetatable({name=name}, LoggerClass)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ function events.on(event, func)
|
|||
events.handlers[event] = {}
|
||||
end
|
||||
table.insert(events.handlers[event], func)
|
||||
return func
|
||||
end
|
||||
|
||||
function events.reset(event, func)
|
||||
|
|
@ -24,6 +25,14 @@ function events.reset(event, func)
|
|||
end
|
||||
end
|
||||
|
||||
function events.remove(event, handler)
|
||||
local handlers = events.handlers[event]
|
||||
if not handlers then
|
||||
return
|
||||
end
|
||||
table.remove_value(handlers, handler)
|
||||
end
|
||||
|
||||
function events.remove_by_prefix(prefix)
|
||||
for name, handlers in pairs(events.handlers) do
|
||||
local actualname = name
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ function file.parent(path)
|
|||
return dir
|
||||
end
|
||||
|
||||
function file.remove_ext(path)
|
||||
return string.format("%s/%s", file.parent(path), file.stem(path))
|
||||
end
|
||||
|
||||
function file.path(path)
|
||||
local pos = path:find(':')
|
||||
return path:sub(pos + 1)
|
||||
|
|
|
|||
|
|
@ -35,3 +35,89 @@ function math.sum(...)
|
|||
|
||||
return sum
|
||||
end
|
||||
|
||||
function math.sign(x)
|
||||
return (x > 0) and 1 or (x < 0 and -1 or 0)
|
||||
end
|
||||
|
||||
local noise_period = 3021
|
||||
local noise_rands = {}
|
||||
|
||||
for i=1,noise_period do
|
||||
table.insert(noise_rands, math.random() * 2.0 - 1.0)
|
||||
end
|
||||
|
||||
local function smoothstep(t)
|
||||
return t * t * (3 - 2 * t)
|
||||
end
|
||||
|
||||
local function sample_noise(x)
|
||||
x = x % noise_period
|
||||
local left_cell = math.floor(x)
|
||||
local right_cell = (left_cell + 1) % noise_period
|
||||
local t = smoothstep(x - left_cell)
|
||||
return noise_rands[left_cell + 1] * (1.0 - t)
|
||||
+ noise_rands[right_cell + 1] * t
|
||||
end
|
||||
|
||||
function math.noise(x, octaves)
|
||||
x = math.abs(x)
|
||||
octaves = octaves or 1
|
||||
local value = 0.0
|
||||
local mul = 1.0
|
||||
for i=1,octaves do
|
||||
value = value * (1.0 - mul) + sample_noise(x + 37 * i) * mul
|
||||
x = x * 2.0
|
||||
mul = mul * 0.5
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function lerp(a, b, t)
|
||||
return a + (b - a) * t
|
||||
end
|
||||
|
||||
local function rand_2d(x, y)
|
||||
return noise_rands[math.floor(x * 317 + y) % noise_period + 1]
|
||||
end
|
||||
|
||||
local function sample_noise2d(x, y)
|
||||
x = math.abs(x)
|
||||
y = math.abs(y)
|
||||
|
||||
local x0 = math.floor(x)
|
||||
local y0 = math.floor(y)
|
||||
local x1 = x0 + 1
|
||||
local y1 = y0 + 1
|
||||
|
||||
local tx = x - x0
|
||||
local ty = y - y0
|
||||
|
||||
local sx = smoothstep(tx)
|
||||
local sy = smoothstep(ty)
|
||||
|
||||
local c00 = rand_2d(x0, y0)
|
||||
local c10 = rand_2d(x1, y0)
|
||||
local c01 = rand_2d(x0, y1)
|
||||
local c11 = rand_2d(x1, y1)
|
||||
|
||||
local bottom = lerp(c00, c10, sx)
|
||||
local top = lerp(c01, c11, sx)
|
||||
|
||||
return lerp(bottom, top, sy)
|
||||
end
|
||||
|
||||
|
||||
function math.noise2d(x, y, octaves)
|
||||
octaves = octaves or 1
|
||||
local value = 0.0
|
||||
local mul = 1.0
|
||||
for i=1,octaves do
|
||||
value = value * (1.0 - mul)
|
||||
+ sample_noise2d(x + 37 * i, y + 73 * i) * mul
|
||||
x = x * 2.0
|
||||
y = y * 2.0
|
||||
mul = mul * 0.5
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,11 +59,7 @@ end
|
|||
|
||||
function table.extend(t1, t2)
|
||||
for i, v in pairs(t2) do
|
||||
if type(i) == "number" then
|
||||
t1[#t1 + 1] = v
|
||||
else
|
||||
t1[i] = v
|
||||
end
|
||||
t1[i] = v
|
||||
end
|
||||
|
||||
return t1
|
||||
|
|
@ -201,3 +197,11 @@ function table.tostring(t)
|
|||
end
|
||||
return s..']'
|
||||
end
|
||||
|
||||
function table.keys(t)
|
||||
local keys = {}
|
||||
for key, _ in pairs(t) do
|
||||
table.insert(keys, key)
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
|
|
|||
|
|
@ -77,15 +77,18 @@ function Element.new(docname, name)
|
|||
end
|
||||
|
||||
-- the engine automatically creates an instance for every ui document (layout)
|
||||
local Document = {}
|
||||
function Document.new(docname)
|
||||
return setmetatable({name=docname}, {
|
||||
__index=function(self, k)
|
||||
local elem = Element.new(self.name, k)
|
||||
rawset(self, k, elem)
|
||||
return elem
|
||||
local Document = {
|
||||
__index=function(self, k)
|
||||
if type(k) ~= "string" then
|
||||
error("element id is not a string")
|
||||
end
|
||||
})
|
||||
local elem = Element.new(self.name, k)
|
||||
rawset(self, k, elem)
|
||||
return elem
|
||||
end
|
||||
}
|
||||
function Document.new(docname)
|
||||
return setmetatable({name=docname}, Document)
|
||||
end
|
||||
|
||||
local RadioGroup = {}
|
||||
|
|
@ -189,13 +192,15 @@ function gui.ask(text, on_yes, on_no, yes_text, no_text)
|
|||
<container id='%s' color='#00000080' size-func='-1,-1' z-index='10'>
|
||||
<panel color='#507090E0' size='600' padding='16'
|
||||
gravity='center-center' interval='4'>
|
||||
<label margin='4'>%s</label>
|
||||
<label margin='4' markup='md'>%s</label>
|
||||
<button onclick='DATA.on_yes()'>@%s</button>
|
||||
<button onclick='DATA.on_no()'>@%s</button>
|
||||
</panel>
|
||||
</container>
|
||||
]], id, string.escape_xml(text)), {on_yes=yes_callback, on_no=no_callback},
|
||||
string.escape_xml(yes_text), string.escape_xml(no_text))
|
||||
]], id,
|
||||
string.escape_xml(text),
|
||||
string.escape_xml(yes_text),
|
||||
string.escape_xml(no_text)), {on_yes=yes_callback, on_no=no_callback})
|
||||
input.add_callback("key:escape", no_callback, gui.root[id])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
local rules = {nexid = 1, rules = {}}
|
||||
local rules = {nextid = 1, rules = {}}
|
||||
|
||||
function rules.get_rule(name)
|
||||
local rule = rules.rules[name]
|
||||
|
|
@ -27,13 +27,13 @@ end
|
|||
|
||||
function rules.reset(name)
|
||||
local rule = rules.get_rule(name)
|
||||
rules.set(rule.default)
|
||||
rules.set(name, rule.default)
|
||||
end
|
||||
|
||||
function rules.listen(name, handler)
|
||||
local rule = rules.get_rule(name)
|
||||
local id = rules.nexid
|
||||
rules.nextid = rules.nexid + 1
|
||||
local id = rules.nextid
|
||||
rules.nextid = rules.nextid + 1
|
||||
rule.listeners[utf8.encode(id)] = handler
|
||||
return id
|
||||
end
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ return {
|
|||
goto continue
|
||||
end
|
||||
for _, component in pairs(entity.components) do
|
||||
local callback = component.on_update
|
||||
local callback = rawget(component, "on_update")
|
||||
if not component.__disabled and callback then
|
||||
local result, err = pcall(callback, tps)
|
||||
if err then
|
||||
|
|
@ -143,7 +143,7 @@ return {
|
|||
physics_update = function(delta)
|
||||
for uid, entity in pairs(entities) do
|
||||
for _, component in pairs(entity.components) do
|
||||
local callback = component.on_physics_update
|
||||
local callback = rawget(component, "on_physics_update")
|
||||
if not component.__disabled and callback then
|
||||
local result, err = pcall(callback, delta)
|
||||
if err then
|
||||
|
|
@ -156,7 +156,7 @@ return {
|
|||
render = function(delta)
|
||||
for _,entity in pairs(entities) do
|
||||
for _, component in pairs(entity.components) do
|
||||
local callback = component.on_render
|
||||
local callback = rawget(component, "on_render")
|
||||
if not component.__disabled and callback then
|
||||
local result, err = pcall(callback, delta)
|
||||
if err then
|
||||
|
|
|
|||
131
res/modules/internal/stream_providers/bytearray.lua
Normal file
131
res/modules/internal/stream_providers/bytearray.lua
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
local io_stream = require "core:io_stream"
|
||||
|
||||
local lib = { }
|
||||
|
||||
local buffers = { }
|
||||
local positions = { }
|
||||
|
||||
local next_descriptor = 0
|
||||
|
||||
local function open_descriptor(buffer)
|
||||
next_descriptor = next_descriptor + 1
|
||||
|
||||
buffers[next_descriptor] = buffer
|
||||
positions[next_descriptor] = 1
|
||||
|
||||
return next_descriptor
|
||||
end
|
||||
|
||||
local function require_descriptor(descriptor)
|
||||
if not buffers[descriptor] then
|
||||
error("unknown descriptor")
|
||||
end
|
||||
end
|
||||
|
||||
function lib.read(descriptor, length)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
local buf = buffers[descriptor]
|
||||
local buf_length = #buf
|
||||
local pos = positions[descriptor]
|
||||
|
||||
local to_read = math.min(buf_length - pos + 1, length)
|
||||
|
||||
if to_read <= 0 then
|
||||
return Bytearray()
|
||||
end
|
||||
|
||||
local segment = buf:slice(pos, to_read)
|
||||
|
||||
positions[descriptor] = pos + to_read
|
||||
|
||||
return segment
|
||||
end
|
||||
|
||||
function lib.write(descriptor, data)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
local buf = buffers[descriptor]
|
||||
local pos = positions[descriptor]
|
||||
|
||||
local buf_length = #buf
|
||||
local data_length = #data
|
||||
|
||||
local end_pos = pos + data_length - 1
|
||||
|
||||
-- size ensuring
|
||||
if end_pos > buf_length then
|
||||
for i = buf_length + 1, end_pos do
|
||||
buf[i] = 0
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, data_length do
|
||||
buf[i + pos - 1] = data[i]
|
||||
end
|
||||
|
||||
positions[descriptor] = pos + data_length
|
||||
end
|
||||
|
||||
function lib.seek(descriptor, mode, offset)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
local buf = buffers[descriptor]
|
||||
local buf_length = #buf
|
||||
|
||||
local base
|
||||
|
||||
if mode == 'b' then
|
||||
base = 1
|
||||
elseif mode == 'c' then
|
||||
base = positions[descriptor]
|
||||
elseif mode == 'e' then
|
||||
base = buf_length + 1
|
||||
else error('invalid seek mode') end
|
||||
|
||||
local new_pos = base + offset
|
||||
|
||||
if new_pos < 1 then
|
||||
error('failed to seek stream')
|
||||
end
|
||||
|
||||
positions[descriptor] = new_pos
|
||||
end
|
||||
|
||||
function lib.tell(descriptor)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
return positions[descriptor]
|
||||
end
|
||||
|
||||
function lib.available(descriptor)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
local buf = buffers[descriptor]
|
||||
local pos = positions[descriptor]
|
||||
|
||||
return math.max(#buf - pos + 1, 0)
|
||||
end
|
||||
|
||||
function lib.is_alive(descriptor)
|
||||
return buffers[descriptor] ~= nil
|
||||
end
|
||||
|
||||
function lib.close(descriptor)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
buffers[descriptor] = nil
|
||||
positions[descriptor] = nil
|
||||
end
|
||||
|
||||
return function(buffer, binary_mode)
|
||||
if binary_mode == nil then
|
||||
binary_mode = true
|
||||
end
|
||||
|
||||
return io_stream.new(
|
||||
open_descriptor(buffer),
|
||||
binary_mode,
|
||||
lib
|
||||
)
|
||||
end
|
||||
|
|
@ -4,15 +4,28 @@ local lib = {
|
|||
read = file.__read_descriptor,
|
||||
write = file.__write_descriptor,
|
||||
seek = file.__seek_descriptor,
|
||||
tell = file.__tell_descriptor,
|
||||
flush = file.__flush_descriptor,
|
||||
available = file.__available_descriptor,
|
||||
is_alive = file.__has_descriptor,
|
||||
close = file.__close_descriptor
|
||||
}
|
||||
|
||||
local __open_descriptor = file.__open_descriptor
|
||||
file.__open_descriptor = nil
|
||||
file.__read_descriptor = nil
|
||||
file.__write_descriptor = nil
|
||||
file.__seek_descriptor = nil
|
||||
file.__tell_descriptor = nil
|
||||
file.__flush_descriptor = nil
|
||||
file.__available_descriptor = nil
|
||||
file.__has_descriptor = nil
|
||||
file.__close_descriptor = nil
|
||||
|
||||
return function(path, mode)
|
||||
return io_stream.new(
|
||||
file.__open_descriptor(path, mode),
|
||||
__open_descriptor(path, mode),
|
||||
mode:find('b') ~= nil,
|
||||
lib
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ int close(int fd);
|
|||
ssize_t read(int fd, void *buf, size_t count);
|
||||
ssize_t write(int fd, const void *buf, size_t count);
|
||||
int fcntl(int fd, int cmd, ...);
|
||||
int ioctl(int fd, unsigned long request, ...);
|
||||
|
||||
const char *strerror(int errnum);
|
||||
]]
|
||||
|
|
@ -20,6 +21,7 @@ local O_WRONLY = 0x1
|
|||
local O_RDWR = 0x2
|
||||
local O_NONBLOCK = 0x800
|
||||
local F_GETFL = 3
|
||||
local FIONREAD = 0x541B
|
||||
|
||||
local function getError()
|
||||
local err = FFI.errno()
|
||||
|
|
@ -58,12 +60,16 @@ function lib.write(fd, bytearray)
|
|||
end
|
||||
end
|
||||
|
||||
function lib.seek(fd, mode, offset)
|
||||
error("cannot seek the named pipe")
|
||||
end
|
||||
function lib.available(fd)
|
||||
if fd == nil or fd < 0 then return 0 end
|
||||
|
||||
function lib.flush(fd)
|
||||
-- no flush on unix
|
||||
local bytes_ready = FFI.new("int[1]", 0)
|
||||
|
||||
if C.ioctl(fd, FIONREAD, bytes_ready) == -1 then
|
||||
return 0
|
||||
end
|
||||
|
||||
return tonumber(bytes_ready[0])
|
||||
end
|
||||
|
||||
function lib.is_alive(fd)
|
||||
|
|
|
|||
|
|
@ -97,14 +97,26 @@ function lib.write(handle, bytearray)
|
|||
end
|
||||
end
|
||||
|
||||
function lib.seek(handle, mode, offset)
|
||||
error("cannot seek the named pipe")
|
||||
end
|
||||
|
||||
function lib.flush(handle)
|
||||
C.FlushFileBuffers(handle)
|
||||
end
|
||||
|
||||
function lib.available(handle)
|
||||
if handle == nil or handle == INVALID_HANDLE_VALUE then
|
||||
return 0
|
||||
end
|
||||
|
||||
local bytes_available = FFI.new("DWORD[1]")
|
||||
|
||||
local success = C.PeekNamedPipe(handle, nil, 0, nil, bytes_available, nil)
|
||||
|
||||
if success == 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
return tonumber(bytes_available[0])
|
||||
end
|
||||
|
||||
function lib.is_alive(handle)
|
||||
if handle == nil or handle == INVALID_HANDLE_VALUE then
|
||||
return false
|
||||
|
|
|
|||
64
res/modules/internal/stream_providers/socket.lua
Normal file
64
res/modules/internal/stream_providers/socket.lua
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
local io_stream = require "core:io_stream"
|
||||
|
||||
local lib = { }
|
||||
|
||||
local sockets = { }
|
||||
|
||||
local next_descriptor = 0
|
||||
|
||||
local function open_descriptor(socket)
|
||||
next_descriptor = next_descriptor + 1
|
||||
|
||||
sockets[next_descriptor] = socket
|
||||
|
||||
return next_descriptor
|
||||
end
|
||||
|
||||
local function require_descriptor(descriptor)
|
||||
if not sockets[descriptor] then
|
||||
error("unknown descriptor")
|
||||
end
|
||||
end
|
||||
|
||||
function lib.read(descriptor, length)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
return sockets[descriptor]:recv(length)
|
||||
end
|
||||
|
||||
function lib.write(descriptor, data)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
sockets[descriptor]:send(data)
|
||||
end
|
||||
|
||||
function lib.available(descriptor)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
return sockets[descriptor]:available()
|
||||
end
|
||||
|
||||
function lib.is_alive(descriptor)
|
||||
local socket = sockets[descriptor]
|
||||
|
||||
return socket ~= nil and socket:is_alive()
|
||||
end
|
||||
|
||||
function lib.close(descriptor)
|
||||
require_descriptor(descriptor)
|
||||
|
||||
sockets[descriptor]:close()
|
||||
sockets[descriptor] = nil
|
||||
end
|
||||
|
||||
return function(socket, binary_mode)
|
||||
if binary_mode == nil then
|
||||
binary_mode = true
|
||||
end
|
||||
|
||||
return io_stream.new(
|
||||
open_descriptor(socket),
|
||||
binary_mode,
|
||||
lib
|
||||
)
|
||||
end
|
||||
139
res/modules/internal/test.lua
Normal file
139
res/modules/internal/test.lua
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
local __app = __vc_app
|
||||
|
||||
local function get_node_center(node)
|
||||
local pos = node.wpos
|
||||
local size = node.size
|
||||
return pos[1] + size[1] / 2, pos[2] + size[2] / 2
|
||||
end
|
||||
|
||||
local function types_check_enum(value, enum)
|
||||
if value == nil then
|
||||
return true, nil
|
||||
end
|
||||
if type(value) ~= "string" then
|
||||
return false, string.format("string expected [%s]", table.concat(table.keys(enum), "|"))
|
||||
end
|
||||
return true, nil
|
||||
end
|
||||
|
||||
local function types_check_number(value)
|
||||
if value == nil then
|
||||
return true, nil
|
||||
end
|
||||
local value_type = type(value)
|
||||
if value_type ~= "number" then
|
||||
return false, string.format("number expected, got %s", value_type)
|
||||
end
|
||||
end
|
||||
|
||||
local function check_option(t, key, func, ...)
|
||||
local value = t[key]
|
||||
if value == nil then
|
||||
return nil
|
||||
end
|
||||
local success, message = func(value, ...)
|
||||
if not success then
|
||||
error(string.format("invalid option '%s': %s", key, message))
|
||||
end
|
||||
end
|
||||
|
||||
local buttons_enum = {
|
||||
left = 0,
|
||||
right = 1,
|
||||
middle = 2,
|
||||
}
|
||||
local no_options = {}
|
||||
|
||||
local _tick = __app.tick
|
||||
local _set_button_pressed = test.set_button_pressed
|
||||
local _set_key_pressed = test.set_key_pressed
|
||||
local _enter_text = test.enter_text
|
||||
test.set_button_pressed = nil
|
||||
test.set_key_pressed = nil
|
||||
test.enter_text = nil
|
||||
|
||||
function test.click(node, options)
|
||||
options = options or no_options
|
||||
|
||||
check_option(options, 'button', types_check_enum, buttons_enum)
|
||||
check_option(options, 'timeout', types_check_number)
|
||||
|
||||
local button = buttons_enum[options.button or 'left']
|
||||
local timeout = options.timeout or 1000
|
||||
|
||||
local tm = time.precise_time()
|
||||
while (time.precise_time() - tm) * 1000 < timeout do
|
||||
if node.exists then
|
||||
break
|
||||
end
|
||||
_tick()
|
||||
end
|
||||
if not node.exists then
|
||||
error(string.format("timeout %s ms exceeded: element %s does not exists",
|
||||
math.floor(timeout), string.escape(rawget(node, 'name'))))
|
||||
end
|
||||
|
||||
local center_x, center_y = get_node_center(node)
|
||||
_set_button_pressed(button, center_x, center_y, true)
|
||||
_tick()
|
||||
_set_button_pressed(button, center_x, center_y, false)
|
||||
_tick()
|
||||
end
|
||||
|
||||
-- TODO: replace with resolving in C++ side
|
||||
function test.find_by_attr(node, attr, value)
|
||||
if getmetatable(node) == Document then
|
||||
return test.find_by_attr(node.root, attr, value)
|
||||
end
|
||||
if node[attr] == value then
|
||||
return node
|
||||
end
|
||||
local i = 1
|
||||
while true do
|
||||
local subnode = node[i]
|
||||
if subnode == nil then
|
||||
break
|
||||
end
|
||||
local found = test.find_by_attr(subnode, attr, value)
|
||||
if found then
|
||||
return found
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function test.find_by_text(node, text)
|
||||
return test.find_by_attr(node, "text", text)
|
||||
end
|
||||
|
||||
function test.find_by_attr_presence(node, attr)
|
||||
if node[attr] then
|
||||
return node
|
||||
end
|
||||
local i = 1
|
||||
while true do
|
||||
local subnode = node[i]
|
||||
if subnode == nil then
|
||||
break
|
||||
end
|
||||
local found = test.find_by_attr_presence(subnode, attr)
|
||||
if found then
|
||||
return found
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function test.fill(node, text, options)
|
||||
test.click(node, options)
|
||||
__app.tick()
|
||||
_enter_text(text)
|
||||
test.press("enter")
|
||||
end
|
||||
|
||||
function test.press(key)
|
||||
_set_key_pressed(key, true)
|
||||
__app.tick()
|
||||
_set_key_pressed(key, false)
|
||||
__app.tick()
|
||||
end
|
||||
21
res/modules/inventory_utils.lua
Normal file
21
res/modules/inventory_utils.lua
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
local M = {}
|
||||
|
||||
function M.share_default_func(invid, slotid)
|
||||
local blockinv = hud.get_block_inventory()
|
||||
local playerinv = player.get_inventory(hud.get_player())
|
||||
if blockinv ~= 0 then
|
||||
if invid == blockinv then
|
||||
inventory.move(blockinv, slotid, playerinv)
|
||||
else
|
||||
inventory.move(invid, slotid, blockinv)
|
||||
end
|
||||
elseif rules.get("allow-content-access") then
|
||||
inventory.set(invid, slotid, 0, 0)
|
||||
elseif slotid < 10 then
|
||||
inventory.move_range(invid, slotid, invid, 10)
|
||||
else
|
||||
inventory.move_range(invid, slotid, invid, 0, 9)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -25,13 +25,13 @@ local ALL_FLUSH_MODES = {
|
|||
local CR = string.byte('\r')
|
||||
local LF = string.byte('\n')
|
||||
|
||||
local function readFully(result, readFunc)
|
||||
local function read_fully(result, read_func)
|
||||
local isTable = type(result) == "table"
|
||||
|
||||
local buf
|
||||
|
||||
repeat
|
||||
buf = readFunc(MAX_BUFFER_SIZE)
|
||||
buf = read_func(MAX_BUFFER_SIZE)
|
||||
|
||||
if isTable then
|
||||
for i = 1, #buf do
|
||||
|
|
@ -42,41 +42,46 @@ local function readFully(result, readFunc)
|
|||
end
|
||||
|
||||
--[[
|
||||
|
||||
descriptor - descriptor of stream for provided I/O library
|
||||
binaryMode - if enabled, most methods will expect bytes instead of strings
|
||||
ioLib - I/O library. Should include the following functions:
|
||||
read(descriptor: int, length: int) -> Bytearray
|
||||
May return bytearray with a smaller size if bytes have not arrived yet or have run out
|
||||
May throw error if descriptor isn't readable
|
||||
write(descriptor: int, data: Bytearray)
|
||||
flush(descriptor: int)
|
||||
May throw error if descriptor isn't writeable
|
||||
[optional] seek(descriptor: int, mode: string, offset: int)
|
||||
Mode may be 'b' (relative begin), 'c' (relative current), 'e' (relative end + 1)
|
||||
[optional] tell(descriptor: int) -> int
|
||||
[optional] flush(descriptor: int)
|
||||
[optional] available(descriptor: int) -> int
|
||||
is_alive(descriptor: int) -> bool
|
||||
close(descriptor: int)
|
||||
--]]
|
||||
|
||||
function io_stream.new(descriptor, binaryMode, ioLib, mode, flushMode)
|
||||
function io_stream.new(descriptor, binary_mode, io_lib, mode, flush_mode)
|
||||
mode = mode or DEFAULT_MODE
|
||||
flushMode = flushMode or FLUSH_MODE_ALL
|
||||
flush_mode = flush_mode or FLUSH_MODE_ALL
|
||||
|
||||
local self = setmetatable({}, io_stream)
|
||||
|
||||
self.descriptor = descriptor
|
||||
self.binaryMode = binaryMode
|
||||
self.maxBufferSize = MAX_BUFFER_SIZE
|
||||
self.ioLib = ioLib
|
||||
self.binary_mode = binary_mode
|
||||
self.max_buffer_size = MAX_BUFFER_SIZE
|
||||
self.io_lib = io_lib
|
||||
|
||||
self:set_mode(mode)
|
||||
self:set_flush_mode(flushMode)
|
||||
self:set_flush_mode(flush_mode)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function io_stream:is_binary_mode()
|
||||
return self.binaryMode
|
||||
return self.binary_mode
|
||||
end
|
||||
|
||||
function io_stream:set_binary_mode(binaryMode)
|
||||
self.binaryMode = binaryMode ~= nil
|
||||
function io_stream:set_binary_mode(binary_mode)
|
||||
self.binary_mode = binary_mode ~= nil
|
||||
end
|
||||
|
||||
function io_stream:get_mode()
|
||||
|
|
@ -88,124 +93,141 @@ function io_stream:set_mode(mode)
|
|||
error("invalid stream mode: "..mode)
|
||||
end
|
||||
|
||||
if self.mode == BUFFERED_MODE then
|
||||
self.writeBuffer:clear()
|
||||
self.readBuffer:clear()
|
||||
if self.write_buffer then
|
||||
self.write_buffer:clear()
|
||||
self.write_buffer = nil
|
||||
end
|
||||
|
||||
if mode == BUFFERED_MODE and not self.writeBuffer then
|
||||
self.writeBuffer = Bytearray()
|
||||
self.readBuffer = Bytearray()
|
||||
if self.read_buffer then
|
||||
self.read_buffer:clear()
|
||||
self.read_buffer = nil
|
||||
end
|
||||
|
||||
if mode == BUFFERED_MODE then
|
||||
self.write_buffer = Bytearray(self.max_buffer_size)
|
||||
self.read_buffer = Bytearray(self.max_buffer_size)
|
||||
end
|
||||
|
||||
self.mode = mode
|
||||
end
|
||||
|
||||
function io_stream:get_flush_mode()
|
||||
return self.flushMode
|
||||
return self.flush_mode
|
||||
end
|
||||
|
||||
function io_stream:set_flush_mode(flushMode)
|
||||
if not table.has(ALL_FLUSH_MODES, flushMode) then
|
||||
error("invalid flush mode: "..flushMode)
|
||||
function io_stream:set_flush_mode(flush_mode)
|
||||
if not table.has(ALL_FLUSH_MODES, flush_mode) then
|
||||
error("invalid flush mode: " .. flush_mode)
|
||||
end
|
||||
|
||||
self.flushMode = flushMode
|
||||
self.flush_mode = flush_mode
|
||||
end
|
||||
|
||||
function io_stream:get_max_buffer_size()
|
||||
return self.maxBufferSize
|
||||
return self.max_buffer_size
|
||||
end
|
||||
|
||||
function io_stream:set_max_buffer_size(maxBufferSize)
|
||||
self.maxBufferSize = maxBufferSize
|
||||
function io_stream:set_max_buffer_size(max_buffer_size)
|
||||
self.max_buffer_size = max_buffer_size
|
||||
|
||||
self.write_buffer = Bytearray(self.max_buffer_size)
|
||||
self.read_buffer = Bytearray(self.max_buffer_size)
|
||||
end
|
||||
|
||||
function io_stream:available(length)
|
||||
local available = self.io_lib.available and self.io_lib.available(self.descriptor) or 0
|
||||
|
||||
if self.mode == BUFFERED_MODE then
|
||||
self:__update_read_buffer()
|
||||
available = available + #self.read_buffer
|
||||
end
|
||||
|
||||
if not length then
|
||||
return #self.readBuffer
|
||||
else
|
||||
return #self.readBuffer >= length
|
||||
end
|
||||
if not length then
|
||||
return available
|
||||
else
|
||||
return available >= length
|
||||
end
|
||||
end
|
||||
|
||||
function io_stream:__update_read_buffer()
|
||||
local readed = Bytearray()
|
||||
|
||||
readFully(readed, function(length) return self.ioLib.read(self.descriptor, length) end)
|
||||
|
||||
self.readBuffer:append(readed)
|
||||
|
||||
if #self.readBuffer > self.maxBufferSize then
|
||||
error "buffer overflow"
|
||||
end
|
||||
end
|
||||
|
||||
function io_stream:__read(length)
|
||||
function io_stream:__read(length, from_read_fully)
|
||||
if self.mode == YIELD_MODE then
|
||||
if from_read_fully then
|
||||
return self.io_lib.read(self.descriptor, length)
|
||||
end
|
||||
|
||||
local buffer = Bytearray()
|
||||
|
||||
while #buffer < length do
|
||||
buffer:append(self.ioLib.read(self.descriptor, length - #buffer))
|
||||
buffer:append(self.io_lib.read(self.descriptor, length - #buffer))
|
||||
|
||||
if #buffer < length then coroutine.yield() end
|
||||
end
|
||||
|
||||
return buffer
|
||||
elseif self.mode == BUFFERED_MODE then
|
||||
self:__update_read_buffer()
|
||||
local buf_len = #self.read_buffer
|
||||
|
||||
if #self.readBuffer < length then
|
||||
error "buffer underflow"
|
||||
if buf_len < length then
|
||||
self.read_buffer:append(
|
||||
self.io_lib.read(self.descriptor, self.max_buffer_size - buf_len)
|
||||
)
|
||||
end
|
||||
|
||||
local copy
|
||||
buf_len = #self.read_buffer
|
||||
|
||||
if #self.readBuffer == length then
|
||||
copy = Bytearray()
|
||||
|
||||
copy:append(self.readBuffer)
|
||||
length = math.min(buf_len, length)
|
||||
|
||||
self.readBuffer:clear()
|
||||
local copy = self.read_buffer:slice(1, length)
|
||||
|
||||
if buf_len == length then
|
||||
self.read_buffer:clear()
|
||||
else
|
||||
copy = Bytearray()
|
||||
|
||||
for i = 1, length do
|
||||
copy[i] = self.readBuffer[i]
|
||||
end
|
||||
|
||||
self.readBuffer:remove(1, length)
|
||||
self.read_buffer:remove(1, length)
|
||||
end
|
||||
|
||||
return copy
|
||||
elseif self.mode == DEFAULT_MODE then
|
||||
return self.ioLib.read(self.descriptor, length)
|
||||
return self.io_lib.read(self.descriptor, length)
|
||||
end
|
||||
end
|
||||
|
||||
function io_stream:__write(data)
|
||||
if self.mode == BUFFERED_MODE then
|
||||
self.writeBuffer:append(data)
|
||||
local data_length = #data
|
||||
|
||||
if #self.writeBuffer > self.maxBufferSize then
|
||||
error "buffer overflow"
|
||||
if #self.write_buffer + data_length > self.max_buffer_size then
|
||||
self:flush()
|
||||
end
|
||||
|
||||
if data_length > self.max_buffer_size then
|
||||
local to_write = math.floor(data_length / self.max_buffer_size) * self.max_buffer_size
|
||||
local to_save = data_length - to_write
|
||||
|
||||
self.io_lib.write(self.descriptor, data:slice(1, to_write))
|
||||
|
||||
self:flush()
|
||||
|
||||
self.write_buffer = data:slice(to_write + 1, to_save)
|
||||
else self.write_buffer:append(data) end
|
||||
elseif self.mode == DEFAULT_MODE or self.mode == YIELD_MODE then
|
||||
return self.ioLib.write(self.descriptor, data)
|
||||
return self.io_lib.write(self.descriptor, data)
|
||||
end
|
||||
end
|
||||
|
||||
function io_stream:read_fully(useTable)
|
||||
if self.binaryMode then
|
||||
local result = useTable and Bytearray() or { }
|
||||
function io_stream:read_fully(use_table)
|
||||
if self.binary_mode then
|
||||
local result = use_table and { } or Bytearray()
|
||||
|
||||
readFully(result, function() return self:__read(self.maxBufferSize) end)
|
||||
local avail = self:available()
|
||||
|
||||
if avail == 0 then
|
||||
avail = self.max_buffer_size
|
||||
end
|
||||
|
||||
read_fully(result, function() return self:__read(avail, true) end)
|
||||
|
||||
return result
|
||||
else
|
||||
if useTable then
|
||||
if use_table then
|
||||
local lines = { }
|
||||
|
||||
local line
|
||||
|
|
@ -220,7 +242,7 @@ function io_stream:read_fully(useTable)
|
|||
else
|
||||
local result = Bytearray()
|
||||
|
||||
readFully(result, function() return self:__read(self.maxBufferSize) end)
|
||||
read_fully(result, function() return self:__read(self.max_buffer_size) end)
|
||||
|
||||
return utf8.tostring(result)
|
||||
end
|
||||
|
|
@ -262,57 +284,61 @@ function io_stream:write_line(str)
|
|||
self:__write(utf8.tobytes(str .. "\n"))
|
||||
end
|
||||
|
||||
function io_stream:read(arg, useTable)
|
||||
local argType = type(arg)
|
||||
function io_stream:read(arg, use_table)
|
||||
local arg_type = type(arg)
|
||||
|
||||
if self.binaryMode then
|
||||
local byteArr
|
||||
if self.binary_mode then
|
||||
local byte_arr
|
||||
|
||||
if argType == "number" then
|
||||
if arg_type == "number" then
|
||||
-- using 'arg' as length
|
||||
|
||||
byteArr = self:__read(arg)
|
||||
byte_arr = self:__read(arg)
|
||||
|
||||
if useTable == true then
|
||||
if use_table == true then
|
||||
local t = { }
|
||||
|
||||
for i = 1, #byteArr do
|
||||
t[i] = byteArr[i]
|
||||
for i = 1, #byte_arr do
|
||||
t[i] = byte_arr[i]
|
||||
end
|
||||
|
||||
return t
|
||||
else
|
||||
return byteArr
|
||||
return byte_arr
|
||||
end
|
||||
elseif argType == "string" then
|
||||
elseif arg_type == "string" then
|
||||
return byteutil.unpack(
|
||||
arg,
|
||||
self:__read(byteutil.get_size(arg))
|
||||
)
|
||||
elseif argType == nil then
|
||||
elseif arg_type == "nil" then
|
||||
error(
|
||||
"in binary mode the first argument must be a string data format"..
|
||||
" for the library \"byteutil\" or the number of bytes to read"
|
||||
)
|
||||
else
|
||||
error("unknown argument type: "..argType)
|
||||
error("unknown argument type: "..arg_type)
|
||||
end
|
||||
else
|
||||
if not arg then
|
||||
return self:read_line()
|
||||
else
|
||||
local linesCount = arg
|
||||
local trimLastEmptyLines = useTable or true
|
||||
local lines_count = arg
|
||||
local trim_last_empty_lines = use_table
|
||||
|
||||
if linesCount < 0 then error "count of lines to read must be positive" end
|
||||
if use_table == nil then
|
||||
trim_last_empty_lines = true
|
||||
end
|
||||
|
||||
if lines_count < 0 then error "count of lines to read must be positive" end
|
||||
|
||||
local result = { }
|
||||
|
||||
for i = 1, linesCount do
|
||||
for i = 1, lines_count do
|
||||
result[i] = self:read_line()
|
||||
end
|
||||
|
||||
if trimLastEmptyLines then
|
||||
if trim_last_empty_lines then
|
||||
local i = #result
|
||||
|
||||
while i >= 0 do
|
||||
|
|
@ -340,41 +366,53 @@ function io_stream:read(arg, useTable)
|
|||
end
|
||||
|
||||
function io_stream:write(arg, ...)
|
||||
local argType = type(arg)
|
||||
local arg_type = type(arg)
|
||||
|
||||
if self.binaryMode then
|
||||
local byteArr
|
||||
if self.binary_mode then
|
||||
local byte_arr
|
||||
|
||||
if argType ~= "string" then
|
||||
if arg_type ~= "string" then
|
||||
-- using arg as bytes table/bytearray
|
||||
|
||||
if argType == "table" then
|
||||
byteArr = Bytearray(arg)
|
||||
if arg_type == "table" then
|
||||
byte_arr = Bytearray(arg)
|
||||
else
|
||||
byteArr = arg
|
||||
byte_arr = arg
|
||||
end
|
||||
else
|
||||
byteArr = byteutil.pack(arg, ...)
|
||||
byte_arr = byteutil.pack(arg, ...)
|
||||
end
|
||||
|
||||
self:__write(byteArr)
|
||||
self:__write(byte_arr)
|
||||
else
|
||||
if argType == "string" then
|
||||
if arg_type == "string" then
|
||||
self:write_line(arg)
|
||||
elseif argType == "table" then
|
||||
elseif arg_type == "table" then
|
||||
for i = 1, #arg do
|
||||
self:write_line(arg[i])
|
||||
end
|
||||
else error("unknown argument type: "..argType) end
|
||||
else error("unknown argument type: "..arg_type) end
|
||||
end
|
||||
end
|
||||
|
||||
function io_stream:seek(mode, offset)
|
||||
self.ioLib.seek(self.descriptor, mode, offset)
|
||||
if not self.io_lib.seek then
|
||||
error("cannot seek this stream")
|
||||
end
|
||||
|
||||
self.io_lib.seek(self.descriptor, mode, offset)
|
||||
end
|
||||
|
||||
function io_stream:tell()
|
||||
if not self.io_lib.tell then
|
||||
error("cannot tell this stream")
|
||||
end
|
||||
|
||||
return self.io_lib.tell(self.descriptor)
|
||||
end
|
||||
|
||||
function io_stream:is_alive()
|
||||
return self.ioLib.is_alive(self.descriptor)
|
||||
return self.io_lib.is_alive(self.descriptor)
|
||||
end
|
||||
|
||||
function io_stream:is_closed()
|
||||
|
|
@ -383,20 +421,24 @@ end
|
|||
|
||||
function io_stream:close()
|
||||
if self.mode == BUFFERED_MODE then
|
||||
self.readBuffer:clear()
|
||||
self.writeBuffer:clear()
|
||||
self.read_buffer:clear()
|
||||
self.write_buffer:clear()
|
||||
end
|
||||
|
||||
return self.ioLib.close(self.descriptor)
|
||||
return self.io_lib.close(self.descriptor)
|
||||
end
|
||||
|
||||
function io_stream:flush()
|
||||
if self.mode == BUFFERED_MODE and #self.writeBuffer > 0 then
|
||||
self.ioLib.write(self.descriptor, self.writeBuffer)
|
||||
self.writeBuffer:clear()
|
||||
if self.mode == BUFFERED_MODE and #self.write_buffer > 0 then
|
||||
self.io_lib.write(self.descriptor, self.write_buffer)
|
||||
self.write_buffer:clear()
|
||||
end
|
||||
|
||||
if self.flushMode ~= FLUSH_MODE_ONLY_BUFFER then self.ioLib.flush(self.descriptor) end
|
||||
if self.flush_mode ~= FLUSH_MODE_ONLY_BUFFER then
|
||||
if self.io_lib.flush then
|
||||
self.io_lib.flush(self.descriptor)
|
||||
elseif self:is_closed() then error("stream is closed") end
|
||||
end
|
||||
end
|
||||
|
||||
return io_stream
|
||||
|
|
@ -3,5 +3,6 @@ name = "default"
|
|||
base_packs = ["base"]
|
||||
permissions = [
|
||||
"network",
|
||||
"record-audio"
|
||||
"record-audio",
|
||||
"sub-instances",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ local Socket = {__index={
|
|||
end
|
||||
return self:recv(length, usetable)
|
||||
end,
|
||||
as_stream=network.__as_stream,
|
||||
close=function(self) return network.__close(self.id) end,
|
||||
available=function(self) return network.__available(self.id) or 0 end,
|
||||
is_alive=function(self) return network.__is_alive(self.id) end,
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ console.add_command(
|
|||
for y=y1,y2 do
|
||||
for z=z1,z2 do
|
||||
for x=x1,x2 do
|
||||
block.set(x, y, z, id)
|
||||
block.set(x, y, z, id, 0, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ require "core:internal/extensions/inventory"
|
|||
asserts = require "core:internal/asserts"
|
||||
events = require "core:internal/events"
|
||||
|
||||
if test then
|
||||
require "core:internal/test"
|
||||
end
|
||||
|
||||
function pack.unload(prefix)
|
||||
events.remove_by_prefix(prefix)
|
||||
end
|
||||
|
|
@ -136,7 +140,20 @@ local __vc_named_coroutines = {}
|
|||
local __vc_next_coroutine = 1
|
||||
|
||||
function __vc_start_coroutine(chunk)
|
||||
local co = coroutine.create(chunk)
|
||||
local co = coroutine.create(function()
|
||||
local _, err = xpcall(chunk, function(msg)
|
||||
local traceback = debug.get_traceback(0)
|
||||
local s = string.format("%s:", msg)
|
||||
for i=1,#traceback - 2 do
|
||||
local frame = traceback[i]
|
||||
s = s .. "\n\t"..tb_frame_tostring(frame)
|
||||
end
|
||||
return s
|
||||
end)
|
||||
if err then
|
||||
error(err)
|
||||
end
|
||||
end)
|
||||
local id = __vc_next_coroutine
|
||||
__vc_next_coroutine = __vc_next_coroutine + 1
|
||||
__vc_coroutines[id] = co
|
||||
|
|
@ -334,6 +351,10 @@ else
|
|||
os.pid = ffi.C.getpid()
|
||||
end
|
||||
|
||||
require("core:io_stream").wrap_bytearray = require "core:internal/stream_providers/bytearray"
|
||||
|
||||
network.__as_stream = require "core:internal/stream_providers/socket"
|
||||
|
||||
math.randomseed(time.uptime() * 1536227939)
|
||||
|
||||
rules = require "core:internal/rules"
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ vc = {
|
|||
is_client = function()
|
||||
return not _vc_headless
|
||||
end,
|
||||
get_version = __vc_app.get_version,
|
||||
get_setting = __vc_app.get_setting,
|
||||
get_setting_str = __vc_app.get_setting_str,
|
||||
str_setting = __vc_app.str_setting,
|
||||
get_setting_info = __vc_app.get_setting_info,
|
||||
}
|
||||
|
||||
|
|
@ -310,12 +311,19 @@ local bytearray = require "core:internal/bytearray"
|
|||
Bytearray = bytearray.FFIBytearray
|
||||
Bytearray_as_string = bytearray.FFIBytearray_as_string
|
||||
Bytearray_as_ptr = bytearray.FFIBytearray_as_ptr
|
||||
I8view = bytearray.FFII8view
|
||||
U16view = bytearray.FFIU16view
|
||||
I16view = bytearray.FFII16view
|
||||
U32view = bytearray.FFIU32view
|
||||
I32view = bytearray.FFII32view
|
||||
U64view = bytearray.FFIU64view
|
||||
I64view = bytearray.FFII64view
|
||||
FLTview = bytearray.FFIFLTview
|
||||
DBLview = bytearray.FFIDBLview
|
||||
Bytearray_construct = function(...) return Bytearray(...) end
|
||||
|
||||
ctypes = require "core:internal/ctypes"
|
||||
|
||||
bit.compile = require "core:bitwise/compiler"
|
||||
bit.execute = require "core:bitwise/executor"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#include <shadows>
|
||||
#include <fog>
|
||||
|
||||
#define CLOUDS_FOG_FACTOR_MUL 0.3f
|
||||
#define CLOUDS_FOG_CURVE_MUL 0.4f
|
||||
|
||||
vec4 effect() {
|
||||
vec4 pos = texture(u_position, v_uv);
|
||||
float light = 1.0;
|
||||
|
|
@ -32,6 +35,10 @@ vec4 effect() {
|
|||
light = pow(light, u_gamma);
|
||||
|
||||
vec3 fogColor = texture(u_skybox, dir).rgb;
|
||||
float fog = calc_fog(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0);
|
||||
float fog = calc_fog(
|
||||
length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0,
|
||||
mix(1.0, CLOUDS_FOG_FACTOR_MUL, emission),
|
||||
mix(1.0, CLOUDS_FOG_CURVE_MUL, emission)
|
||||
);
|
||||
return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,33 @@ uniform float u_fogCurve;
|
|||
uniform bool u_alphaClip;
|
||||
uniform vec3 u_sunDir;
|
||||
|
||||
const float BAYER_MATRIX[64] = float[](
|
||||
0.0, 32.0, 8.0, 40.0, 2.0, 34.0, 10.0, 42.0,
|
||||
48.0, 16.0, 56.0, 24.0, 50.0, 18.0, 58.0, 26.0,
|
||||
12.0, 44.0, 4.0, 36.0, 14.0, 46.0, 6.0, 38.0,
|
||||
60.0, 28.0, 52.0, 20.0, 62.0, 30.0, 54.0, 22.0,
|
||||
3.0, 35.0, 11.0, 43.0, 1.0, 33.0, 9.0, 41.0,
|
||||
51.0, 19.0, 59.0, 27.0, 49.0, 17.0, 57.0, 25.0,
|
||||
15.0, 47.0, 7.0, 39.0, 13.0, 45.0, 5.0, 37.0,
|
||||
63.0, 31.0, 55.0, 23.0, 61.0, 29.0, 53.0, 21.0
|
||||
);
|
||||
|
||||
float calc_bayer(vec2 pos){
|
||||
ivec2 p = ivec2(mod(pos, 8.0));
|
||||
return BAYER_MATRIX[p.y * 8 + p.x] / 64.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture0, a_texCoord);
|
||||
float alpha = a_color.a * texColor.a;
|
||||
// anyway it's any alpha-test alternative required
|
||||
if (alpha < (u_alphaClip ? 0.5f : 0.15f)) {
|
||||
if (u_alphaClip) {
|
||||
float bayer = calc_bayer(gl_FragCoord.xy);
|
||||
if (alpha - bayer < 0.01f) {
|
||||
discard;
|
||||
}
|
||||
alpha = 1.0;
|
||||
} else if (alpha < 0.05f) {
|
||||
discard;
|
||||
}
|
||||
f_color = a_color * texColor;
|
||||
|
|
|
|||
|
|
@ -7,13 +7,17 @@ uniform float u_weatherFogOpacity;
|
|||
uniform float u_weatherFogDencity;
|
||||
uniform float u_weatherFogCurve;
|
||||
|
||||
float calc_fog(float depth) {
|
||||
float calc_fog(float depth, float fogFactor, float fogCurve) {
|
||||
return min(
|
||||
1.0,
|
||||
max(pow(depth * u_fogFactor, u_fogCurve),
|
||||
max(pow(depth * u_fogFactor * fogFactor, u_fogCurve * fogCurve),
|
||||
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve),
|
||||
u_weatherFogOpacity))
|
||||
);
|
||||
}
|
||||
|
||||
float calc_fog(float depth) {
|
||||
return calc_fog(depth, 1.0, 1.0);
|
||||
}
|
||||
|
||||
#endif // FOG_GLSL_
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue