Update file loading and mod config.

master
Auri 2021-09-06 18:33:01 -07:00
parent 3c0bc568fe
commit 791bdd7eb1
119 changed files with 1031 additions and 1040 deletions

View File

@ -43,7 +43,7 @@ add_executable(${MAIN_EXEC_NAME} ${SOURCE_FILES})
target_include_directories(${MAIN_EXEC_NAME} PUBLIC src)
target_link_libraries(${MAIN_EXEC_NAME} PUBLIC glfw glm enet glad assimp FastNoise nothings-stb gzip cute_headers nlohmann_json)
target_link_libraries(${MAIN_EXEC_NAME} PUBLIC glfw glm enet glad assimp FastNoise nothings-stb gzip cute_headers nlohmann_json stdc++fs)
target_link_libraries(${MAIN_EXEC_NAME} PUBLIC lua sol2)
if(WIN32)

View File

@ -1,6 +1,7 @@
{
"name": "base",
"name": "Base",
"identifier": "zepha:base",
"description": "Base definitions for the Zepha Engine.",
"version": "1.0.0",
"depends": []
"main": "main"
}

View File

@ -1,14 +1,14 @@
zepha.player:set_hud(zepha.gui(function()
return Gui.Box {
background = "base:viginette",
background = 'zepha:base:viginette',
Gui.Box {
-- id = "crosshair",
id = 'crosshair',
size = { "22px", "22px" },
pos = { "50cw - 50sw", "50ch - 50sh" },
size = { '22px', '22px' },
pos = { '50cw - 50sw', '50ch - 50sh' },
background = "base:crosshair"
background = 'zepha:base:crosshair'
}
}
end))

View File

@ -1,6 +0,0 @@
require(_PATH .. "models/init")
require(_PATH .. "player_interact")
require(_PATH .. "inventory")
require(_PATH .. "tools")
if zepha.client then require(_PATH .. "hud") end

View File

@ -1,8 +1,8 @@
-- Register hand item
zepha.register_item("base:hand", {
name = "Hand (you broke the game)",
zepha.register_item(':hand', {
name = 'Hand (you broke the game)',
textures = {"base:hand"},
textures = { 'zepha:base:hand' },
tool_props = {
interval = 0.25,
@ -12,14 +12,14 @@ zepha.register_item("base:hand", {
-- Register main and hand inventories
if zepha.server then
zepha.bind("new_player", function(p)
zepha.bind('new_player', function(p)
local inv = p:get_inventory()
local main = inv:add_list("main", 30, 10)
local main = inv:add_list('main', 30, 10)
inv:set_default_list(main)
local hand = inv:add_list("hand", 1, 1)
hand:add_stack({"base:hand", 1})
local hand = inv:add_list('hand', 1, 1)
hand:add_stack({ 'zepha:base:hand', 1 })
p:set_hand_list(hand)
end)
end

View File

@ -0,0 +1,8 @@
require './models'
require './player_interact'
require './inventory'
require './tools'
if zepha.client then
require './hud'
end

View File

@ -3,10 +3,10 @@
-- Texture order is: top, bottom, left, right, front, back.
--
zepha.register_blockmodel("base:block", {
zepha.register_blockmodel(':block', {
parts = {
{
face = "back",
face = 'back',
tex = 3,
points = {
0, 0, 0, 0, 1,
@ -15,7 +15,7 @@ zepha.register_blockmodel("base:block", {
0, 1, 0, 0, 0
}
}, {
face = "front",
face = 'front',
tex = 4,
points = {
1, 1, 1, 0, 0,
@ -24,7 +24,7 @@ zepha.register_blockmodel("base:block", {
1, 1, 0, 1, 0
}
}, {
face = "top",
face = 'top',
tex = 1,
points = {
0, 1, 0, 0, 0,
@ -33,7 +33,7 @@ zepha.register_blockmodel("base:block", {
1, 1, 0, 1, 0
}
}, {
face = "bottom",
face = 'bottom',
tex = 2,
points = {
0, 0, 0, 0, 0,
@ -42,7 +42,7 @@ zepha.register_blockmodel("base:block", {
0, 0, 1, 0, 1
}
}, {
face = "left",
face = 'left',
tex = 5,
points = {
0, 0, 1, 0, 1,
@ -51,7 +51,7 @@ zepha.register_blockmodel("base:block", {
0, 1, 1, 0, 0
}
}, {
face = "right",
face = 'right',
tex = 6,
points = {
0, 0, 0, 1, 1,

View File

@ -6,10 +6,10 @@
local amp = 0.1
zepha.register_blockmodel("base:block_foliage", {
zepha.register_blockmodel(':block_foliage', {
parts = {
{
face = "back",
face = 'back',
tex = 3,
points = {
0, 0, 0, 0, 1,
@ -18,7 +18,7 @@ zepha.register_blockmodel("base:block_foliage", {
0, 1, 0, 0, 0
}
}, {
face = "front",
face = 'front',
tex = 3,
points = {
1, 1, 1, 0, 0,
@ -27,7 +27,7 @@ zepha.register_blockmodel("base:block_foliage", {
1, 1, 0, 1, 0
}
}, {
face = "top",
face = 'top',
tex = 1,
points = {
0, 1, 0, 0, 0,
@ -36,7 +36,7 @@ zepha.register_blockmodel("base:block_foliage", {
1, 1, 0, 1, 0
}
}, {
face = "bottom",
face = 'bottom',
tex = 2,
points = {
0, 0, 0, 0, 0,
@ -45,7 +45,7 @@ zepha.register_blockmodel("base:block_foliage", {
0, 0, 1, 0, 1
}
}, {
face = "left",
face = 'left',
tex = 3,
points = {
0, 0, 1, 0, 1,
@ -54,7 +54,7 @@ zepha.register_blockmodel("base:block_foliage", {
0, 1, 1, 0, 0
}
}, {
face = "right",
face = 'right',
tex = 3,
points = {
0, 0, 0, 1, 1,
@ -64,7 +64,7 @@ zepha.register_blockmodel("base:block_foliage", {
}
}, {
--Floats begin here
face = "left",
face = 'left',
tex = 4,
points = {
0, 1, 1, 0, 0,
@ -73,11 +73,11 @@ zepha.register_blockmodel("base:block_foliage", {
1, 1, 1, 1, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = amp
}
}, {
face = "right",
face = 'right',
tex = 4,
points = {
1.005, 0.2, -0.2, 0, 1,
@ -86,11 +86,11 @@ zepha.register_blockmodel("base:block_foliage", {
1, 1, 0, 0, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = amp
}
}, {
face = "front",
face = 'front',
tex = 4,
points = {
1.2, 0.2, 1.005, 0, 1,
@ -99,11 +99,11 @@ zepha.register_blockmodel("base:block_foliage", {
1, 1, 1, 0, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = amp
}
}, {
face = "back",
face = 'back',
tex = 4,
points = {
0, 1, 0, 0, 0,
@ -112,7 +112,7 @@ zepha.register_blockmodel("base:block_foliage", {
0, 1, 1, 1, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = amp
}
}

View File

@ -3,10 +3,10 @@
-- Texture order is: top, bottom, left, right, front, back.
--
zepha.register_blockmodel("base:block_slab", {
zepha.register_blockmodel(':block_slab', {
parts = {
{
face = "back",
face = 'back',
tex = 3,
points = {
0, 0, 0, 0, 0.5,
@ -15,7 +15,7 @@ zepha.register_blockmodel("base:block_slab", {
0, 0.5, 0, 0, 0
}
}, {
face = "front",
face = 'front',
tex = 4,
points = {
1, 0.5, 1, 0, 0,
@ -24,7 +24,7 @@ zepha.register_blockmodel("base:block_slab", {
1, 0.5, 1, 0, 0
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0, 0.5, 0, 0, 0,
@ -33,7 +33,7 @@ zepha.register_blockmodel("base:block_slab", {
1, 0.5, 0, 1, 0
}
}, {
face = "bottom",
face = 'bottom',
tex = 2,
points = {
0, 0, 0, 0, 0,
@ -42,7 +42,7 @@ zepha.register_blockmodel("base:block_slab", {
0, 0, 1, 0, 1
}
}, {
face = "left",
face = 'left',
tex = 5,
points = {
0, 0, 1, 0, 0.5,
@ -51,7 +51,7 @@ zepha.register_blockmodel("base:block_slab", {
0, 0.5, 1, 0, 0
}
}, {
face = "right",
face = 'right',
tex = 6,
points = {
0, 0, 0, 1, 0.5,

View File

@ -3,10 +3,10 @@
-- Texture order is: top, bottom, left, right, front, back.
--
zepha.register_blockmodel("base:block_slab_foliage", {
zepha.register_blockmodel(':block_slab_foliage', {
parts = {
{
face = "back",
face = 'back',
tex = 3,
points = {
0, 0, 0, 0, 0.5,
@ -15,7 +15,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
0, 0.5, 0, 0, 0
}
}, {
face = "front",
face = 'front',
tex = 4,
points = {
1, 0.5, 1, 0, 0,
@ -24,7 +24,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
1, 0.5, 0, 1, 0
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0, 0.5, 0, 0, 0,
@ -33,7 +33,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
1, 0.5, 0, 1, 0
}
}, {
face = "bottom",
face = 'bottom',
tex = 2,
points = {
0, 0, 0, 0, 0,
@ -42,7 +42,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
0, 0, 1, 0, 1
}
}, {
face = "left",
face = 'left',
tex = 5,
points = {
0, 0, 1, 0, 0.5,
@ -51,7 +51,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
0, 0.5, 1, 0, 0
}
}, {
face = "right",
face = 'right',
tex = 6,
points = {
0, 0, 0, 1, 0.5,
@ -61,7 +61,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
}
}, {
--Floats begin here
face = "left",
face = 'left',
tex = 7,
points = {
0, 0.5, 1, 0, 0,
@ -70,7 +70,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
1, 0.5, 1, 1, 0
}
}, {
face = "right",
face = 'right',
tex = 7,
points = {
1, -0.3, -0.2, 0, 1,
@ -79,7 +79,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
1, 0.5, 0, 0, 0
}
}, {
face = "front",
face = 'front',
tex = 7,
points = {
1.2, -0.3, 1, 0, 1,
@ -88,7 +88,7 @@ zepha.register_blockmodel("base:block_slab_foliage", {
1, 0.5, 1, 0, 0
}
}, {
face = "left",
face = 'left',
tex = 7,
points = {
0, 0.5, 0, 0, 0,

View File

@ -4,10 +4,10 @@
-- Only takes one texture, which is displayed on all faces.
--
zepha.register_blockmodel("base:cross_large", {
zepha.register_blockmodel(':cross_large', {
parts = {
{
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0, 0, 0, 0, 1,
@ -16,7 +16,7 @@ zepha.register_blockmodel("base:cross_large", {
0, 1, 0, 0, 0
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
1, 1, 1, 1, 0,
@ -25,7 +25,7 @@ zepha.register_blockmodel("base:cross_large", {
0, 1, 0, 0, 0
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
1, 1, 0, 1, 0,
@ -34,7 +34,7 @@ zepha.register_blockmodel("base:cross_large", {
0, 1, 1, 0, 0
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0, 0, 1, 0, 1,

View File

@ -7,20 +7,20 @@
local wave_amplitude = 0.6
local offset_amplitude = 0.3
zepha.register_blockmodel("base:cross_plant", {
zepha.register_blockmodel(':cross_plant', {
mesh_mods = {
{
type = "offset_x",
type = 'offset_x',
amplitude = offset_amplitude,
},
{
type = "offset_z",
type = 'offset_z',
amplitude = offset_amplitude,
}
},
parts = {
{
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0.1, 0, 0.1, 0, 1,
@ -29,15 +29,15 @@ zepha.register_blockmodel("base:cross_plant", {
0.1, 0.9, 0.1, 0, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = wave_amplitude
},
mesh_mod = {
type = "offset",
type = 'offset',
scale = 0.2
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0.9, 0.9, 0.9, 1, 0,
@ -46,15 +46,15 @@ zepha.register_blockmodel("base:cross_plant", {
0.1, 0.9, 0.1, 0, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = wave_amplitude
},
mesh_mod = {
type = "offset",
type = 'offset',
scale = 0.2
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0.9, 0.9, 0.1, 1, 0,
@ -63,15 +63,15 @@ zepha.register_blockmodel("base:cross_plant", {
0.1, 0.9, 0.9, 0, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = wave_amplitude
},
mesh_mod = {
type = "offset",
type = 'offset',
scale = 0.2
}
}, {
face = "nocull",
face = 'nocull',
tex = 1,
points = {
0.1, 0, 0.9, 0, 1,
@ -80,11 +80,11 @@ zepha.register_blockmodel("base:cross_plant", {
0.1, 0.9, 0.9, 0, 0
},
shader_mod = {
type = "sway_attached",
type = 'sway_attached',
amplitude = wave_amplitude
},
mesh_mod = {
type = "offset",
type = 'offset',
scale = 0.2
}
}

View File

@ -1,9 +0,0 @@
require(_PATH .. "none")
require(_PATH .. "block")
require(_PATH .. "block_foliage")
require(_PATH .. "cross_large")
require(_PATH .. "block_slab")
require(_PATH .. "block_slab_foliage")
require(_PATH .. "leaf_like")
require(_PATH .. "cross_plant")
require(_PATH .. "pillar")

View File

@ -6,10 +6,10 @@
local amp = 0.2
zepha.register_blockmodel("base:leaf_like", {
zepha.register_blockmodel(':leaf_like', {
parts = {
{
face = "back",
face = 'back',
tex = 1,
points = {
0, 0, 0, 0, 1,
@ -18,11 +18,11 @@ zepha.register_blockmodel("base:leaf_like", {
0, 1, 0, 0, 0
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}, {
face = "front",
face = 'front',
tex = 1,
points = {
1, 1, 1, 0, 0,
@ -31,11 +31,11 @@ zepha.register_blockmodel("base:leaf_like", {
1, 1, 0, 1, 0
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}, {
face = "top",
face = 'top',
tex = 1,
points = {
0, 1, 0, 0, 0,
@ -44,11 +44,11 @@ zepha.register_blockmodel("base:leaf_like", {
1, 1, 0, 1, 0
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}, {
face = "bottom",
face = 'bottom',
tex = 1,
points = {
0, 0, 0, 0, 0,
@ -57,11 +57,11 @@ zepha.register_blockmodel("base:leaf_like", {
0, 0, 1, 0, 1
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}, {
face = "left",
face = 'left',
tex = 1,
points = {
0, 0, 1, 0, 1,
@ -70,11 +70,11 @@ zepha.register_blockmodel("base:leaf_like", {
0, 1, 1, 0, 0
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}, {
face = "right",
face = 'right',
tex = 1,
points = {
0, 0, 0, 1, 1,
@ -83,11 +83,11 @@ zepha.register_blockmodel("base:leaf_like", {
1, 0, 0, 0, 1
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}, {
face = "nocull",
face = 'nocull',
tex = 2,
points = {
-0.31, 1.30, -0.3, 0, 0,
@ -111,7 +111,7 @@ zepha.register_blockmodel("base:leaf_like", {
1.3, 1.30, -0.3, 1, 0
},
shader_mod = {
type = "sway_full_block",
type = 'sway_full_block',
amplitude = amp
}
}

View File

@ -0,0 +1,9 @@
require './none'
require './block'
require './block_foliage'
require './cross_large'
require './block_slab'
require './block_slab_foliage'
require './leaf_like'
require './cross_plant'
require './pillar'

View File

@ -2,4 +2,4 @@
-- Basic 'none' model renders nothing.
--
zepha.register_blockmodel("base:none", { parts = {} })
zepha.register_blockmodel(':none', { parts = {} })

View File

@ -3,10 +3,10 @@
-- Texture order is: top, bottom, side
--
zepha.register_blockmodel("base:pillar", {
zepha.register_blockmodel(':pillar', {
parts = {
{
face = "back",
face = 'back',
tex = 3,
points = {
0, 0, 4/16, 0, 1,
@ -15,7 +15,7 @@ zepha.register_blockmodel("base:pillar", {
0, 1, 4/16, 0, 0
}
}, {
face = "front",
face = 'front',
tex = 3,
points = {
1, 1, 12/16, 0, 0,
@ -24,7 +24,7 @@ zepha.register_blockmodel("base:pillar", {
1, 1, 4/16, 8/16, 0
}
}, {
face = "top",
face = 'top',
tex = 1,
points = {
4/16, 1, 0, 4/16, 0,
@ -43,7 +43,7 @@ zepha.register_blockmodel("base:pillar", {
1, 1, 4/16, 1, 4/16
}
}, {
face = "bottom",
face = 'bottom',
tex = 2,
points = {
4/16, 0, 0, 4/16, 0,
@ -62,7 +62,7 @@ zepha.register_blockmodel("base:pillar", {
12/16, 0, 1, 12/16, 1
}
}, {
face = "left",
face = 'left',
tex = 3,
points = {
4/16, 0, 1, 0, 1,
@ -71,7 +71,7 @@ zepha.register_blockmodel("base:pillar", {
4/16, 1, 1, 0, 0
}
}, {
face = "right",
face = 'right',
tex = 3,
points = {
4/16, 0, 0, 8/16, 1,
@ -81,7 +81,7 @@ zepha.register_blockmodel("base:pillar", {
}
},
{
face = "nocull",
face = 'nocull',
tex = 3,
points = {
0, 0, 4/16, 10/16, 0,

View File

@ -32,8 +32,8 @@ function zepha.block_interact(player, target)
local def = zepha.registered_blocks[block]
local args = { target.dim, target.pos, player }
local cb = zepha.server and "on_interact" or "on_interact_client"
if type(def[cb]) == "function" then def[cb](table.unpack(args)) end
local cb = zepha.server and 'on_interact' or 'on_interact_client'
if type(def[cb]) == 'function' then def[cb](table.unpack(args)) end
zepha.trigger(cb, table.unpack(args))
return def.on_interact or def.on_interact_client
@ -89,14 +89,14 @@ function zepha.block_break(player, target)
local args = { target.dim, target.pos, player }
local cb = zepha.server and "on_break" or "on_break_client"
if type(def[cb]) == "function" then def[cb](table.unpack(args)) end
local cb = zepha.server and 'on_break' or 'on_break_client'
if type(def[cb]) == 'function' then def[cb](table.unpack(args)) end
zepha.trigger(cb, table.unpack(args))
target.dim:set_block(target.pos, "air")
target.dim:set_block(target.pos, 'air')
local cb = zepha.server and "after_break" or "after_break_client"
if type(def[cb]) == "function" then def[cb](table.unpack(args)) end
local cb = zepha.server and 'after_break' or 'after_break_client'
if type(def[cb]) == 'function' then def[cb](table.unpack(args)) end
zepha.trigger(cb, table.unpack(args))
end
@ -112,8 +112,8 @@ function zepha.item_use(player, target, stack)
if stack == nil or def == nil then return stack end
local args = { stack, target, player }
local cb = zepha.server and "on_use" or "on_use_client"
local cb = zepha.server and 'on_use' or 'on_use_client'
if type(def[cb]) == "function" then return def[cb](table.unpack(args)) end
if type(def[cb]) == 'function' then return def[cb](table.unpack(args)) end
return nil
end

View File

@ -37,7 +37,7 @@ function zepha.get_hit_impact(player, block)
local damage = 0
for group, tool_damage in pairs(props.damage_groups) do
local mul = (def.multipliers or {})[group]
if mul == nil then mul = (def.multipliers or {})["_other"] end
if mul == nil then mul = (def.multipliers or {})._other end
if mul == nil then mul = 1 end
damage = math.max(damage, tool_damage * mul)

View File

@ -1,17 +0,0 @@
-- Load Libraries
require(_PATH .. "modules/fenv")
require(_PATH .. "modules/math")
require(_PATH .. "modules/string")
require(_PATH .. "modules/gui")
require(_PATH .. "modules/dump")
require(_PATH .. "modules/table")
require(_PATH .. "modules/after")
require(_PATH .. "modules/vector")
require(_PATH .. "modules/entity")
require(_PATH .. "modules/callbacks")
require(_PATH .. "modules/serialization")
-- Register base models (if not on main menu)
if zepha.client or zepha.server then require(_PATH .. "game/init") end

View File

@ -0,0 +1,18 @@
-- Load Libraries
require './modules/fenv'
require './modules/math'
require './modules/string'
require './modules/gui'
require './modules/dump'
require './modules/table'
require './modules/after'
require './modules/vector'
require './modules/entity'
require './modules/callbacks'
require './modules/serialization'
-- Register base models (if not on main menu)
if zepha.client or zepha.server then
require './game'
end

View File

@ -2,13 +2,14 @@ zepha.registered_callbacks = {}
function zepha.bind(event, fn)
if zepha.registered_callbacks[event] == nil then zepha.registered_callbacks[event] = {} end
if type(fn) ~= "function" then return end
table.insert(zepha.registered_callbacks[event], fn)
end
function zepha.trigger(event, ...)
if zepha.registered_callbacks[event] == nil then return nil end
for _, EVT_CALLBACK in pairs(zepha.registered_callbacks[event]) do
if (type(EVT_CALLBACK) == "function") then EVT_CALLBACK(...) end
for i, EVENT_CALLBACK in pairs(zepha.registered_callbacks[event]) do
EVENT_CALLBACK(...)
end
end

View File

@ -12,14 +12,14 @@
-- @param {number | nil} idn - Internal value, do not assign manually.
--
_G["format"] = function(val, nl, idn)
_G['format'] = function(val, nl, idn)
idn = idn or 0
if nl == nil then nl = 3 end
if type(val) == "function" or type(val) == "userdata" or type(val) == "thread" then return tostring(val) end
if type(val) == "string" then return '"' .. val .. '"' end
if type(val) == "nil" then return 'nil' end
if type(val) == "table" then
if type(val) == 'function' or type(val) == 'userdata' or type(val) == 'thread' then return tostring(val) end
if type(val) == 'string' then return '"' .. val .. '"' end
if type(val) == 'nil' then return 'nil' end
if type(val) == 'table' then
local strings = {}
local num_keys = 0
@ -42,6 +42,6 @@ _G["format"] = function(val, nl, idn)
return val
end
_G["dump"] = function(val, nl)
_G['dump'] = function(val, nl)
print(format(val, nl))
end

View File

@ -2,6 +2,6 @@ zepha.entities = {}
function zepha.__builtin.update_entities(delta)
for k, v in pairs(zepha.entities) do
if type(v.on_update) == "function" then v:on_update(delta) end
if type(v.on_update) == 'function' then v:on_update(delta) end
end
end
end

View File

@ -12,14 +12,14 @@ end
-- register_element
-- Add an element to the Gui namespace.
local function register_element(key)
if type(key) == "table" then
if type(key) == 'table' then
for _, v in pairs(key) do register_element(v) end
return
end
env.Gui[key] = function(data) return create_element(key, data) end
end
register_element({ "Box", "Text" })
register_element({ 'Box', 'Text' })
-- zepha.build_gui
-- Allows you to Build UI Elements with the GUI namespace outside of a callback.

View File

@ -10,7 +10,7 @@ end
-- string.split
-- Splits a string by a delimiter, optionally applying an operation to it after separating it.
string.split = function(input, sep, op)
if sep == nil then sep = "%s" end
if sep == nil then sep = '%s' end
local t = {}
for str in string.gmatch(input, '([^'..sep..']+)') do
table.insert(t, op and op(str) or str) end

View File

@ -1,4 +1,4 @@
-- Performs a shallow copy of a table.
function table.clone(tbl)
return {table.unpack(tbl)}
return { table.unpack(tbl) }
end

View File

@ -1,7 +1,7 @@
-- Zepha Vector Library
-- Version 1.0
_G["vector"] = {}
_G['vector'] = {}
-- create_vector
-- Create a new vector, and give it a metatable.
@ -33,7 +33,7 @@ end
function vector.add(v1, v2)
assert(vector.is_vector(v1))
if vector.is_vector(v2) then return create_vector(rawget(v1, 1) + rawget(v2, 1), rawget(v1, 2) + rawget(v2, 2), rawget(v1, 3) + rawget(v2, 3))
elseif type(v2) == "number" then return create_vector(rawget(v1, 1) + v2, rawget(v1, 2) + v2, rawget(v1, 3) + v2) end
elseif type(v2) == 'number' then return create_vector(rawget(v1, 1) + v2, rawget(v1, 2) + v2, rawget(v1, 3) + v2) end
end
vector.__add = vector.add
@ -60,7 +60,7 @@ vector.__sub = vector.subtract
function vector.multiply(v1, m)
assert(vector.is_vector(v1))
if vector.is_vector(m) then return create_vector(rawget(v1, 1) * rawget(m, 1), rawget(v1, 2) * rawget(m, 2), rawget(v1, 3) * rawget(m, 3))
elseif type(m) == "number" then return create_vector(rawget(v1, 1) * m, rawget(v1, 2) * m, rawget(v1, 3) * m) end
elseif type(m) == 'number' then return create_vector(rawget(v1, 1) * m, rawget(v1, 2) * m, rawget(v1, 3) * m) end
end
vector.__mul = vector.multiply
@ -71,7 +71,7 @@ function vector.divide(v1, m)
assert(vector.is_vector(v1))
if vector.is_vector(m) then
return create_vector(rawget(v1, 1) / rawget(m, 1), rawget(v1, 2) / rawget(m, 2), rawget(v1, 3) / rawget(m, 3))
elseif type(m) == "number" then
elseif type(m) == 'number' then
return create_vector(rawget(v1, 1) / m, rawget(v1, 2) / m, rawget(v1, 3) / m)
end
end
@ -159,24 +159,24 @@ vector.unit = vector.normalize
-- Alias x, y, z to 1, 2, 3
function vector:__index(key)
if key == "x" then return rawget(self, 1) end
if key == "y" then return rawget(self, 2) end
if key == "z" then return rawget(self, 3) end
if key == 'x' then return rawget(self, 1) end
if key == 'y' then return rawget(self, 2) end
if key == 'z' then return rawget(self, 3) end
return getmetatable(self)[key]
end
function vector:__newindex(key, val)
if key == "x" then rawset(self, 1, val)
elseif key == "y" then rawset(self, 2, val)
elseif key == "z" then rawset(self, 3, val) end
if key == 'x' then rawset(self, 1, val)
elseif key == 'y' then rawset(self, 2, val)
elseif key == 'z' then rawset(self, 3, val) end
end
function vector:__tostring()
return table.concat({
"{",
tostring(rawget(self, 1)), ", ",
tostring(rawget(self, 2)), ", ",
tostring(rawget(self, 3)), "}"
'{',
tostring(rawget(self, 1)), ', ',
tostring(rawget(self, 2)), ', ',
tostring(rawget(self, 3)), '}'
})
end
@ -195,18 +195,18 @@ end
-- Table constructor works with 1-3 values
vector.new = function(x, y, z)
-- Blank new constructor, return empty vector
if x == nil or (type(x) == "table" and x[1] == nil) then return create_vector(0, 0, 0)
if x == nil or (type(x) == 'table' and x[1] == nil) then return create_vector(0, 0, 0)
-- Invalid type passed to function, return nil
elseif type(x) ~= "number" and type(x) ~= "table" then return nil
elseif type(x) ~= 'number' and type(x) ~= 'table' then return nil
-- Passed a table as x with at least x and y parameters, z will be set to table value or 0
elseif type(x) == "table" and (vector.is_vector(x) or (type(x[1]) == "number" and type(x[2]) == "number")) then return create_vector(x[1], x[2], x[3] or 0)
elseif type(x) == 'table' and (vector.is_vector(x) or (type(x[1]) == 'number' and type(x[2]) == 'number')) then return create_vector(x[1], x[2], x[3] or 0)
-- Only one number was passed, give a vector with all three values set to it
elseif y == nil then return create_vector(x, x, x)
-- Invalid type passed to function, return nil
elseif (type(y) ~= "number" and type(y) ~= "table") or type(z) ~= "number" then return end
elseif (type(y) ~= 'number' and type(y) ~= 'table') or type(z) ~= 'number' then return end
-- Create a vector with the values x, y, and z
return create_vector(x, y, z)
end
-- vector.new shorthand
_G["V"] = vector.new
_G['V'] = vector.new

View File

@ -5,7 +5,7 @@
#include "MenuSandbox.h"
#include "lua/LuaMod.h"
#include "lua/Mod.h"
#include "client/Client.h"
#include "lua/ErrorFormatter.h"
#include "client/menu/SubgameDef.h"
@ -38,18 +38,15 @@ void MenuSandbox::loadApi() {
lua["zepha"] = core;
core["__builtin"] = lua.create_table();
modules.emplace_back(std::make_unique<Api::Module::Time>(Api::State::CLIENT, lua, core));
Api::Usertype::GuiElement::bind(lua, core, root);
// ClientApi::gui_element(lua);
MenuApi::set_gui(lua, core, sandboxRoot);
MenuApi::start_game(client, core);
bindModules();
lua.set_function("require", &MenuSandbox::runFileSandboxed, this);
lua.set_function("require", &MenuSandbox::require, this);
lua["dofile"] = lua["loadfile"] = lua["require"];
}
@ -58,8 +55,8 @@ void MenuSandbox::load(const SubgameDef& subgame) {
subgameName = subgame.config.name;
try {
loadAndRunMod(subgame.subgamePath + "/../../assets/base");
loadAndRunMod(subgame.subgamePath + "/menu");
loadMod(subgame.subgamePath + "/../../assets/base");
loadMod(subgame.subgamePath + "/menu");
}
catch (sol::error e) {
string err = static_cast<sol::error>(e).what();
@ -81,11 +78,11 @@ void MenuSandbox::load(const SubgameDef& subgame) {
fileName.erase(std::remove_if(fileName.begin(), fileName.end(), isspace), fileName.end());
for (const let& file : mod.files) {
if (file.path != fileName) continue;
if (file.first != fileName) continue;
let msg = ErrorFormatter::formatError(fileName,
std::stoi(line.substr(lineNumStart + 1, lineNumEnd - lineNumStart - 1)),
err, file.file);
err, file.first);
showError(msg);
return;
@ -105,83 +102,41 @@ void MenuSandbox::update(double delta) {
}
}
sol::protected_function_result MenuSandbox::runFileSandboxed(const string& file) {
for (LuaMod::File& f : mod.files) {
if (f.path != file) continue;
sol::environment env(lua, sol::create, lua.globals());
env["_PATH"] = f.path.substr(0, f.path.find_last_of('/') + 1);
env["_FILE"] = f.path;
env["_MODNAME"] = mod.config.name;
using Pfr = sol::protected_function_result;
return lua.safe_script(f.file, env,
[](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
"@" + f.path, sol::load_mode::text);
}
throw std::runtime_error("Error opening '" + file + "', file not found.");
sol::protected_function_result MenuSandbox::require(sol::this_environment thisEnv, const string& path) {
sol::environment env = static_cast<sol::environment>(thisEnv);
string currentPath = env.get<string>("__PATH");
std::cout << "CURRENT: " << currentPath << "\nPATH: " << path << std::endl;
return loadFile(path);
// auto modName = env.get<std::string>("_MODNAME");
// std::string iden = identifier[0] == ':' ? modName + identifier : identifier;
}
void MenuSandbox::loadAndRunMod(const string& modPath) {
if (!cf_file_exists(modPath.data())) throw std::runtime_error("Directory not found.");
sol::protected_function_result MenuSandbox::loadFile(const string& path) {
std::cout << "load " << path << std::endl;
let it = mod.files.find(path);
if (it == mod.files.end()) throw sol::error("Error opening '" + path + "', file not found.");
let& file = it->second;
LuaMod mod;
string root = modPath + "/script";
sol::environment env(lua, sol::create, lua.globals());
env["_PATH"] = path.substr(0, path.find_last_of('/') + 1);
env["_FILE"] = path;
env["_MODNAME"] = mod.name;
std::list<string> dirsToScan { root };
std::list<string> luaFiles {};
using Pfr = sol::protected_function_result;
return lua.safe_script(file, env,
[](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
"@" + path, sol::load_mode::text);
}
void MenuSandbox::loadMod(const std::filesystem::path& path) {
mod = { path, true };
cf_dir_t dir;
while (!dirsToScan.empty()) {
string dirStr = *dirsToScan.begin();
dirsToScan.erase(dirsToScan.begin());
if (!cf_file_exists(dirStr.data())) throw std::runtime_error("Missing 'script' directory.");
cf_dir_open(&dir, dirStr.data());
while (dir.has_next) {
// Read through files in the directory
cf_file_t scannedFile;
cf_read_file(&dir, &scannedFile);
if (strncmp(scannedFile.name, ".", 1) != 0) {
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
else {
auto path = std::string_view(scannedFile.path);
if (path.rfind('.') != std::string::npos && path.rfind('.') == path.rfind(".lua"))
luaFiles.emplace_back(scannedFile.path);
}
}
cf_dir_next(&dir);
}
cf_dir_close(&dir);
}
if (std::filesystem::exists(path / "textures"))
menuAssets = client.game->textures.loadDirectory((path / "textures").string(), false, true);
mod.modPath = modPath;
for (string& file : luaFiles) {
size_t rootPos = file.find(root);
string modPath = file;
if (rootPos == std::string::npos)
throw std::runtime_error("Attempted to access file \"" + file + "\", which is outside of the mod root.");
modPath.erase(rootPos, root.length() + 1);
modPath.resize(modPath.size() - 4);
std::ifstream t(file);
string fileStr((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
LuaMod::File f { modPath, fileStr };
mod.files.push_back(f);
}
string texPath = modPath + "/textures";
if (cf_file_exists(texPath.data()))
menuAssets = client.game->textures.loadDirectory(texPath, false, true);
this->mod = mod;
runFileSandboxed("init");
loadFile("/main");
}
void MenuSandbox::showError(const string& err) {

View File

@ -6,7 +6,7 @@
#include "lua/LuaParser.h"
#include "lua/LuaMod.h"
#include "lua/Mod.h"
#include "client/gui/Root.h"
#include "client/gui/Element.h"
@ -31,13 +31,17 @@ private:
void loadApi();
void loadAndRunMod(const string& modPath);
void loadMod(const std::filesystem::path& path);
void showError(const string& err);
sol::protected_function_result runFileSandboxed(const string& file);
/** Exposed to Lua, can provide a relative path, uses environment variables to resolve. */
sol::protected_function_result require(sol::this_environment thisEnv, const string& path);
LuaMod mod {};
/** Loads a file with the right enviroment, needs a canonical path. */
sol::protected_function_result loadFile(const string& path);
Mod mod {};
string subgameName;
Client& client;

View File

@ -49,7 +49,7 @@ void ConnectScene::update() {
if (p.type == Packet::Type::SERVER_INFO) {
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+ "Received server properties.\n");
+ "Received server properties.\n");
const u32 seed = p.d.read<u32>();
std::cout << seed << std::endl;
@ -75,8 +75,7 @@ void ConnectScene::update() {
}
else if (p.type == Packet::Type::BIOME_IDENTIFIER_LIST) {
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+
"Received block & biome index-identifier table.\nDownloading mods... ");
+ "Received block & biome index-identifier table.\nDownloading mods... ");
client.game->getBiomes().setIdentifiers(p.d.read<vec<string>>());
state = State::MODS;
@ -93,11 +92,11 @@ void ConnectScene::update() {
PacketView p(e.packet);
if (p.type == Packet::Type::MODS) {
auto mod = LuaMod(p);
auto mod = Mod(p);
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "") +
(modsFound == 0 ? "" : ", ") +
((modsFound) % 8 == 0 && modsFound != 0 ? "\n" : "") +
"`c0`u" + mod.config.name + "`r`c1");
(modsFound == 0 ? "" : ", ") +
((modsFound) % 8 == 0 && modsFound != 0 ? "\n" : "") +
"`c0`u" + mod.name + "`r`c1");
modsFound++;
client.game->getParser().addMod(std::move(mod));
}
@ -105,8 +104,7 @@ void ConnectScene::update() {
client.game->getParser().setModLoadOrder(p.d.read<vec<string>>());
status->setProp(Gui::Prop::CONTENT, status->getStyle<string>(Gui::Prop::CONTENT, "")
+
".\n`c7Done`c1 downloading mods. Received the mods order.\nReceiving media");
+ ".\n`c7Done`c1 downloading mods. Received the mods order.\nReceiving media");
state = State::MEDIA;
Packet resp(Packet::Type::MEDIA);

View File

@ -43,8 +43,7 @@ void LocalLuaParser::init(WorldPtr world, PlayerPtr player, Client& client) {
loadApi(world);
try {
runFileSandboxed("base/init");
for (string mod : modLoadOrder) if (mod != "base") runFileSandboxed(mod + "/init");
for (let& mod : modOrder) loadFile(mods[mod].clientMain);
}
catch (sol::error r) {
string err = r.what();
@ -72,18 +71,18 @@ void LocalLuaParser::init(WorldPtr world, PlayerPtr player, Client& client) {
string modName = fullPath.substr(0, fileNameStart);
if (modName == "base") continue;
let iter = mods.find(modName);
if (iter == mods.end()) continue;
let modIter = mods.find(modName);
if (modIter == mods.end()) continue;
let& mod = modIter->second;
for (const let& file : iter->second.files) {
if (file.path != fullPath) continue;
let msg = ErrorFormatter::formatError(fullPath,
std::stoi(line.substr(lineNumStart + 1, lineNumEnd - lineNumStart - 1)),
err, file.file);
throw ModException(msg);
}
let fileIter = mod.files.find(fullPath);
if (fileIter == mod.files.end()) continue;
let msg = ErrorFormatter::formatError(fullPath,
std::stoi(line.substr(lineNumStart + 1, lineNumEnd - lineNumStart - 1)),
err, fileIter->first);
throw ModException(msg);
}
throw ModException(err);
@ -99,12 +98,12 @@ void LocalLuaParser::update(double delta) {
}
}
void LocalLuaParser::addMod(const LuaMod& mod) {
mods.emplace(mod.config.name, mod);
void LocalLuaParser::addMod(const Mod& mod) {
mods.emplace(mod.identifier, mod);
}
void LocalLuaParser::setModLoadOrder(const vec<string> order) {
modLoadOrder = order;
modOrder = order;
}
void LocalLuaParser::addKBObserver(usize id) {
@ -173,32 +172,103 @@ void LocalLuaParser::loadApi(WorldPtr world) {
bindModules();
lua.set_function("require", &LocalLuaParser::runFileSandboxed, this);
lua.set_function("require", &LocalLuaParser::require, this);
lua["dofile"] = lua["loadfile"] = lua["require"];
}
sol::protected_function_result LocalLuaParser::runFileSandboxed(const std::string& file) {
size_t modname_length = file.find('/');
if (modname_length == std::string::npos)
throw std::runtime_error("Error opening \"" + file + "\", specified file is invalid.");
std::string modname = file.substr(0, modname_length);
//sol::protected_function_result LocalLuaParser::require(const string& path) {
// usize modnameEnd = path.find('/');
// if (modnameEnd == string::npos) throw std::runtime_error(
// "Error opening '" + path + "', specified file is invalid.");
// string modname = path.substr(0, modnameEnd);
//
// let modIt = mods.find(modname);
// if (modIt == mods.end()) throw sol::error("Error opening '" + path + "', mod not found.");
// let& mod = modIt->second;
//
// let fileIt = mod.files.find(path);
// if (fileIt == mod.files.end()) throw sol::error("Error opening '" + path + "', file not found.");
// let& file = fileIt->second;
//
// sol::environment env(lua, sol::create, lua.globals());
// env["_PATH"] = path.substr(0, path.find_last_of('/') + 1);
// env["_FILE"] = path;
// env["_MODNAME"] = mod.identifier;
//
// using Pfr = sol::protected_function_result;
// return lua.safe_script(file, env,
// [](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
// "@" + path, sol::load_mode::text);
//}
sol::protected_function_result LocalLuaParser::require(sol::this_environment env, string requirePath) {
string currentPath = static_cast<sol::environment>(env).get<string>("__PATH");
vec<string> pathSegments;
let iter = mods.find(modname);
if (iter == mods.end()) throw std::runtime_error("Error opening \"" + file + "\", mod not found.");
for (const LuaMod::File& f : iter->second.files) {
if (f.path != file) continue;
if (requirePath[0] == '.') {
usize lastPos = 0, pos;
while ((pos = currentPath.find('/', lastPos)) != string::npos) {
pathSegments.emplace_back(currentPath.substr(lastPos, pos - lastPos));
lastPos = pos + 1;
}
if (lastPos < currentPath.size())
pathSegments.emplace_back(currentPath.substr(lastPos, currentPath.size()));
sol::environment env(lua, sol::create, lua.globals());
env["_PATH"] = f.path.substr(0, f.path.find_last_of('/') + 1);
env["_FILE"] = f.path;
env["_MODNAME"] = modname;
lastPos = 0;
while ((pos = requirePath.find('/', lastPos)) != string::npos) {
pathSegments.emplace_back(requirePath.substr(lastPos, pos - lastPos));
lastPos = pos + 1;
}
if (lastPos < requirePath.size())
pathSegments.emplace_back(requirePath.substr(lastPos, requirePath.size()));
using Pfr = sol::protected_function_result;
let res = lua.safe_script(f.file, env,
[](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
"@" + f.path, sol::load_mode::text);
return res;
for (let it = pathSegments.begin(); it != pathSegments.end();) {
if (*it == ".") {
pathSegments.erase(it);
}
else if (*it == "..") {
if (it - pathSegments.begin() < 2) throw std::runtime_error("Path escapes sandbox.");
pathSegments.erase(it);
pathSegments.erase(--it);
}
else it++;
}
std::stringstream s;
for (usize i = 0; i < pathSegments.size(); i++) s << (i != 0 ? "/" : "") << pathSegments[i];
requirePath = s.str();
}
throw std::runtime_error("Error opening \"" + file + "\", file not found.");
return loadFile(requirePath);
}
sol::protected_function_result LocalLuaParser::loadFile(string path) {
usize modnameEnd = path.find('/');
if (modnameEnd == string::npos) throw std::runtime_error(
"Error opening '" + path + "', specified file is invalid.");
string modname = path.substr(0, modnameEnd);
let modIt = mods.find(modname);
if (modIt == mods.end()) throw sol::error("Error opening '" + path + "', mod not found.");
let& mod = modIt->second;
std::string_view file;
let it = mod.files.find(path);
if (it != mod.files.end()) file = it->second;
else {
it = mod.files.find(path + "/main");
if (it == mod.files.end()) throw sol::error("Error opening '" + path + "', file not found.");
file = it->second;
path += "/main";
}
sol::environment env(lua, sol::create, lua.globals());
env["__PATH"] = path.substr(0, path.find_last_of('/') + 1);
env["__FILE"] = path;
env["__MOD_NAME"] = mod.identifier;
using Pfr = sol::protected_function_result;
return lua.safe_script(file, env,
[](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
"@" + path, sol::load_mode::text);
}

View File

@ -9,7 +9,7 @@
#include "lua/LuaParser.h"
#include "LuaMod.h"
#include "Mod.h"
#include "client/Callback.h"
#include "util/CovariantPtr.h"
#include "lua/LuaKeybindHandler.h"
@ -27,7 +27,7 @@ public:
void update(double delta) override;
void addMod(const LuaMod& mod);
void addMod(const Mod& mod);
void setModLoadOrder(const vec<string> order);
@ -38,14 +38,18 @@ public:
private:
void loadApi(WorldPtr world);
sol::protected_function_result runFileSandboxed(const std::string& file);
/** Exposed to Lua, can provide a relative path, uses environment variables to resolve. */
sol::protected_function_result require(sol::this_environment env, string requirePath);
/** Loads a file with the right enviroment, needs a canonical path. */
sol::protected_function_result loadFile(string path);
Client* client;
PlayerPtr player;
double accumulatedDelta = 0;
vec<CallbackRef> refs;
vec<string> modLoadOrder {};
vec<string> modOrder {};
std::unordered_set<usize> kbObservers {};
std::unordered_map<string, LuaMod> mods {};
std::unordered_map<string, Mod> mods {};
};

View File

@ -1,23 +0,0 @@
#include <gzip/decompress.hpp>
#include "LuaMod.h"
#include "util/net/PacketView.h"
LuaMod::LuaMod(PacketView& p) {
auto serialized = p.d.read<string>();
string mod = gzip::decompress(serialized.c_str(), serialized.length());
vec<string> depends;
string name, description, version;
Deserializer d(mod);
d.read(name).read(description).read(version).read(depends);
config = { name, description, version, depends };
while (!d.atEnd()) {
string path, contents;
d.read(path).read(contents);
files.emplace_back(path, contents);
}
}

View File

@ -1,44 +0,0 @@
#pragma once
#include "util/Types.h"
class PacketView;
/**
* Holds the name, description, version and dependencies
* of a lua mod, as well as its scripts.
*/
class LuaMod {
public:
struct File {
File(string path, string file): path(path), file(file) {};
string path;
string file;
};
struct Config {
string name;
string description;
string version;
vec<string> depends;
};
LuaMod() = default;
/** Creates a new lua mod from a packet containing mod data. */
LuaMod(PacketView& p);
/** The mod's source files. */
vec<File> files {};
/** The mod's configuration data. */
Config config {};
/** The mod's filesystem path. */
string modPath;
/** The serialized mod, populated on the server. */
string serialized;
};

117
src/lua/Mod.cpp Normal file
View File

@ -0,0 +1,117 @@
#include <fstream>
#include <iostream>
#include <filesystem>
#include <nlohmann/json.hpp>
#include <gzip/compress.hpp>
#include <gzip/decompress.hpp>
#include "Mod.h"
#include "util/net/Serializer.h"
#include "util/net/PacketView.h"
Mod::Mod(const std::filesystem::path& path, bool noConfig) {
nlohmann::json json {};
std::ifstream(path / "conf.json") >> json;
rawPath = path;
if (!noConfig) {
if (!json.contains("name")) throw std::invalid_argument("Config is missing 'name'.");
if (!json.contains("identifier")) throw std::invalid_argument("Config is missing 'identifier'.");
if (!json.contains("description")) throw std::invalid_argument("Config is missing 'description'.");
if (!json.contains("version")) throw std::invalid_argument("Config is missing 'version'.");
if (!json.contains("main")) throw std::invalid_argument("Config is missing 'main'.");
name = json.at("name");
identifier = json.at("identifier");
description = json.at("description");
version = json.at("version");
}
if (json.contains("dependencies")) {
nlohmann::json::object_t deps = json.at("dependencies");
for (const let& it : deps) {
const string& identifier = it.first;
const Version& version = it.second;
dependencies.emplace(identifier, version);
}
}
std::filesystem::recursive_directory_iterator iter(path / "script");
for (let& file : iter) {
if (!file.is_regular_file() || file.path().extension() != ".lua") continue;
std::ifstream fileStream(file.path());
string fileStr = string((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
string rawPath = std::filesystem::relative(file.path(), path / "script").string();
string relativePath = identifier + "/" + rawPath.substr(0, rawPath.size() - 4);
files.emplace(relativePath, fileStr);
}
if (json.contains("main") && json.at("main").is_object()) {
nlohmann::json::object_t main = json.at("main");
for (const let& it : main) {
const string& type = it.first;
const string& path = identifier + "/" + static_cast<string>(it.second);
if (type != "client" && type != "server")
throw std::invalid_argument("Unrecognized main specifier '" + type + "' specified.");
if (files.find(path) == files.end())
throw std::invalid_argument("main file '" + path + "' not found.");
if (type == "client") clientMain = path;
else if (type == "server") serverMain = path;
}
}
else {
string main = identifier + "/" + (json.contains("main") && json.at("main").is_string()
? static_cast<string>(json.at("main")) : "main");
clientMain = main;
serverMain = main;
}
}
Mod::Mod(PacketView& p) {
string comp = p.d.read<string>();
string serialized = gzip::decompress(comp.data(), comp.length());
Deserializer d(serialized);
d.read(name).read(identifier).read(description).read(version).read(clientMain);
vec<string> deps = d.read<vec<string>>();
for (let& dep : deps) {
usize space = dep.find(' ');
dependencies.emplace(dep.substr(0, space - 1), dep.substr(space));
}
while (!d.atEnd()) {
string path, contents;
d.read(path).read(contents);
files.emplace(path, contents);
}
}
const string& Mod::serialize() const {
if (!serialized.empty()) return serialized;
vec<string> serializedDeps;
for (let& pair : dependencies) serializedDeps.emplace_back(pair.first + ' ' + pair.second);
Serializer s;
s.append(name)
.append(identifier)
.append(description)
.append(version)
.append(clientMain)
.append(serializedDeps);
for (let& pair : files) {
s.append(pair.first).append(pair.second);
}
serialized = gzip::compress(s.data.c_str(), s.data.length());
return serialized;
}

61
src/lua/Mod.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include <filesystem>
#include <unordered_map>
#include "util/Types.h"
class PacketView;
/**
* Holds the properties and source files for a Lua mod.
*/
class Mod {
public:
/** Stores a version, this will be a struct in the future. */
typedef string Version;
/** Default constructor. */
Mod() = default;
/** Creates a new lua mod from the mod path on the filesystem. */
Mod(const std::filesystem::path& path, bool noConfig = false);
/** Creates a new lua mod from a packet containing mod data. */
Mod(PacketView& p);
const string& serialize() const;
/** The mod's name. */
string name;
/** The mod's identifier. */
string identifier;
/** The mod's description. */
string description;
/** The version of the mod. */
Version version;
/** The mod's dependencies and versions. */
std::unordered_map<string, Version> dependencies {};
/** Main entrypoint for the server. On the client this will be blank. */
string serverMain {};
/** Main entrypoint for the client, may be a blank string indicating no client side code. */
string clientMain {};
/** The mod's source files. */
std::unordered_map<string, string> files {};
/** The mod's path on the filesystem. */
std::filesystem::path rawPath {};
private:
/** The serialized mod, only present on the server. */
mutable string serialized {};
};

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 11/06/19.
//
#include <gzip/compress.hpp>
#include "ServerLuaParser.h"
@ -40,11 +36,10 @@ void ServerLuaParser::init(WorldPtr world, const std::string& path) {
loadApi(world);
handler.loadMods(static_cast<ServerSubgame&>(game), path + "mods");
handler.executeMods(std::bind(&ServerLuaParser::runFileSandboxed, this, std::placeholders::_1));
handler.executeMods(std::bind(&ServerLuaParser::loadFile, this, std::placeholders::_1));
std::cout << Log::info << "Loaded " << handler.cGetMods().size() << " mods: [ ";
for (unsigned int i = 0; i < handler.cGetMods().size(); i++)
std::cout << handler.cGetMods()[i].config.name << (i < handler.cGetMods().size() - 1 ? ", " : " ]\n");
std::cout << Log::info << "Loaded " << handler.modOrder.size() <<
" mods: " << Util::toString(handler.modOrder) << std::endl;
}
void ServerLuaParser::update(double delta) {
@ -57,12 +52,10 @@ void ServerLuaParser::update(double delta) {
}
void ServerLuaParser::sendModsPacket(ENetPeer* peer) const {
for (const LuaMod& mod : handler.cGetMods())
Serializer().append(mod.serialized).packet(Packet::Type::MODS).sendTo(peer, Packet::Channel::CONNECT);
vec<string> order {};
for (const LuaMod& mod : handler.cGetMods()) order.push_back(mod.config.name);
Serializer().append(order).packet(Packet::Type::MOD_ORDER).sendTo(peer, Packet::Channel::CONNECT);
for (const let& pair : handler.mods)
Serializer().append(pair.second.serialize())
.packet(Packet::Type::MODS).sendTo(peer, Packet::Channel::CONNECT);
Serializer().append(handler.modOrder).packet(Packet::Type::MOD_ORDER).sendTo(peer, Packet::Channel::CONNECT);
}
void ServerLuaParser::playerConnected(std::shared_ptr<ServerPlayer> client) {
@ -117,11 +110,11 @@ void ServerLuaParser::loadApi(WorldPtr world) {
Api::Util::createRegister(lua, core, "mesh");
Api::Util::createRegister(lua, core, "item",
[&](const auto& iden) { RegisterItem::server(core, game, iden); });
[&](const let& iden) { RegisterItem::server(core, game, iden); });
Api::Util::createRegister(lua, core, "block",
[&](const auto& iden) { RegisterBlock::server(core, game, iden); });
[&](const let& iden) { RegisterBlock::server(core, game, iden); });
Api::Util::createRegister(lua, core, "biome",
[&](const auto& iden) { RegisterBiome::server(core, game, iden); });
[&](const let& iden) { RegisterBiome::server(core, game, iden); });
Api::Util::createRegister(lua, core, "keybind");
Api::Util::createRegister(lua, core, "blockmodel");
Api::Util::createRegister(lua, core, "entity", nullptr, "entities");
@ -140,32 +133,78 @@ void ServerLuaParser::loadApi(WorldPtr world) {
bindModules();
lua.set_function("require", &ServerLuaParser::runFileSandboxed, this);
lua.set_function("require", &ServerLuaParser::require, this);
lua["dofile"] = lua["loadfile"] = lua["require"];
}
sol::protected_function_result ServerLuaParser::runFileSandboxed(const std::string& file) {
size_t modname_length = file.find('/');
if (modname_length == std::string::npos)
throw std::runtime_error("Error opening \"" + file + "\", specified file is invalid.");
std::string modname = file.substr(0, modname_length);
sol::protected_function_result ServerLuaParser::require(sol::this_environment env, string requirePath) {
string currentPath = static_cast<sol::environment>(env).get<string>("__PATH");
vec<string> pathSegments;
for (const LuaMod& mod : handler.cGetMods()) {
if (modname != mod.config.name) continue;
for (const LuaMod::File& f : mod.files) {
if (f.path != file) continue;
sol::environment env(lua, sol::create, lua.globals());
env["_PATH"] = f.path.substr(0, f.path.find_last_of('/') + 1);
env["_FILE"] = f.path;
env["_MODNAME"] = mod.config.name;
using Pfr = sol::protected_function_result;
return lua.safe_script(f.file, env,
[](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
"@" + f.path, sol::load_mode::text);
if (requirePath[0] == '.') {
usize lastPos = 0, pos;
while ((pos = currentPath.find('/', lastPos)) != string::npos) {
pathSegments.emplace_back(currentPath.substr(lastPos, pos - lastPos));
lastPos = pos + 1;
}
throw std::runtime_error("Error opening \"" + file + "\", file not found.");
if (lastPos < currentPath.size())
pathSegments.emplace_back(currentPath.substr(lastPos, currentPath.size()));
lastPos = 0;
while ((pos = requirePath.find('/', lastPos)) != string::npos) {
pathSegments.emplace_back(requirePath.substr(lastPos, pos - lastPos));
lastPos = pos + 1;
}
if (lastPos < requirePath.size())
pathSegments.emplace_back(requirePath.substr(lastPos, requirePath.size()));
for (let it = pathSegments.begin(); it != pathSegments.end();) {
if (*it == ".") {
pathSegments.erase(it);
}
else if (*it == "..") {
if (it - pathSegments.begin() < 2) throw std::runtime_error("Path escapes sandbox.");
pathSegments.erase(it);
pathSegments.erase(--it);
}
else it++;
}
std::stringstream s;
for (usize i = 0; i < pathSegments.size(); i++) s << (i != 0 ? "/" : "") << pathSegments[i];
requirePath = s.str();
}
throw std::runtime_error("Error opening \"" + file + "\", mod not found.");
return loadFile(requirePath);
}
sol::protected_function_result ServerLuaParser::loadFile(string path) {
usize modnameEnd = path.find('/');
if (modnameEnd == string::npos) throw std::runtime_error(
"Error opening '" + path + "', specified file is invalid.");
string modname = path.substr(0, modnameEnd);
let modIt = handler.mods.find(modname);
if (modIt == handler.mods.end()) throw sol::error("Error opening '" + path + "', mod not found.");
let& mod = modIt->second;
std::string_view file;
let it = mod.files.find(path);
if (it != mod.files.end()) file = it->second;
else {
it = mod.files.find(path + "/main");
if (it == mod.files.end()) throw sol::error("Error opening '" + path + "', file not found.");
file = it->second;
path += "/main";
}
sol::environment env(lua, sol::create, lua.globals());
env["__PATH"] = path.substr(0, path.find_last_of('/') + 1);
env["__FILE"] = path;
env["__MOD_NAME"] = mod.identifier;
using Pfr = sol::protected_function_result;
return lua.safe_script(file, env,
[](lua_State*, Pfr pfr) -> Pfr { throw static_cast<sol::error>(pfr); },
"@" + path, sol::load_mode::text);
}

View File

@ -5,15 +5,11 @@
#pragma once
#include <enet.h>
#include "LuaParser.h"
#include "ServerModHandler.h"
class ServerWorld;
class ServerPlayer;
class ServerSubgame;
class ServerLuaParser : public LuaParser {
@ -33,7 +29,11 @@ public:
private:
void loadApi(WorldPtr world);
sol::protected_function_result runFileSandboxed(const std::string& file);
/** Exposed to Lua, can provide a relative path, uses environment variables to resolve. */
sol::protected_function_result require(sol::this_environment env, string requirePath);
/** Loads a file with the right enviroment, needs a canonical path. */
sol::protected_function_result loadFile(string path);
ServerModHandler handler;
double accumulatedDelta = 0;

View File

@ -1,298 +1,110 @@
//
// Created by aurailus on 2020-02-19.
//
#include <sstream>
#include <fstream>
#include <filesystem>
#include <stb_image.h>
#include <cute_files.h>
#include <unordered_map>
#include <gzip/compress.hpp>
#include <nlohmann/json.hpp>
#include "ServerModHandler.h"
#include "game/ServerSubgame.h"
#include "util/net/Serializer.h"
void ServerModHandler::loadMods(ServerSubgame& defs, const std::string& path) {
auto modDirs = findModDirectories(path);
mods = initializeLuaMods(modDirs);
void ServerModHandler::loadMods(ServerSubgame& defs, const string& path) {
let mod = Mod { std::filesystem::path(path) / "../../../assets/base" };
mods.emplace(mod.identifier, mod);
loadTextures(defs, mods);
loadModels(defs, mods);
organizeDependencies(mods);
serializeMods(mods);
}
void ServerModHandler::executeMods(std::function<void(std::string)> run) {
for (LuaMod& mod : mods) {
if (mod.config.name == "base") {
run(mod.config.name + "/init");
break;
std::filesystem::recursive_directory_iterator iter(path);
for (let& dir : iter) {
if (!dir.is_directory() || !std::filesystem::exists(dir.path() / "conf.json")) continue;
try {
let mod = Mod { dir.path() };
mods.emplace(mod.identifier, mod);
}
catch (std::invalid_argument e) {
std::cout << Log::err << "Failed to load mod '"
<< dir.path().string() << "\n" << e.what() << Log::endl;
continue;
}
}
for (LuaMod& mod : mods) {
if (mod.config.name != "base") run(mod.config.name + "/init");
}
loadAssets(defs);
sortMods();
}
const std::vector<LuaMod>& ServerModHandler::cGetMods() const {
return mods;
void ServerModHandler::executeMods(std::function<void(string)> run) {
for (let& mod : modOrder) run(mods[mod].serverMain);
}
std::list<std::string> ServerModHandler::findModDirectories(const std::string& path) {
std::list<std::string> modDirs{ path + "/../../../assets/base" };
std::list<std::string> dirsToScan{ path };
cf_dir_t dir;
while (!dirsToScan.empty()) {
std::string dirStr = *dirsToScan.begin();
dirsToScan.erase(dirsToScan.begin());
bool isModFolder = false;
cf_dir_open(&dir, dirStr.c_str());
std::list<std::string> subDirs;
while (dir.has_next) {
cf_file_t scannedFile;
cf_read_file(&dir, &scannedFile);
void ServerModHandler::loadAssets(ServerSubgame& defs) {
for (const let& mod : mods) {
let path = std::filesystem::path(mod.second.rawPath) / "textures";
if (std::filesystem::exists(path)) {
std::filesystem::recursive_directory_iterator iter(path);
if (strncmp(scannedFile.name, ".", 1) == 0) {
cf_dir_next(&dir);
continue;
}
if (scannedFile.is_dir) subDirs.emplace_back(scannedFile.path);
else if (std::string_view(scannedFile.name).size() == 9 &&
std::string_view(scannedFile.name).rfind("conf.json") == 0) {
for (let& file : iter) {
if (file.path().extension() != ".png") continue;
isModFolder = true;
break;
int width, height;
u8* data = stbi_load(file.path().string().data(), &width, &height, nullptr, 4);
string str(reinterpret_cast<char*>(data), static_cast<usize>(width * height * 4));
std::string comp = gzip::compress(str.data(), str.length());
free(data);
let name = file.path().filename().string();
name = name.substr(0, name.size() - 4);
defs.assets.textures.emplace_back(
mod.second.identifier + ":" + name,
comp, static_cast<u16>(width), static_cast<u16>(height));
}
cf_dir_next(&dir);
}
cf_dir_close(&dir);
if (isModFolder) modDirs.push_back(dirStr);
else for (const std::string& s : subDirs) dirsToScan.push_back(s);
path = std::filesystem::path(mod.second.rawPath) / "models";
if (std::filesystem::exists(path)) {
std::filesystem::recursive_directory_iterator iter(path);
for (let& file : iter) {
if (file.path().extension() != ".b3d") continue;
std::ifstream t(file.path());
std::stringstream buffer;
buffer << t.rdbuf();
defs.assets.models.emplace_back(SerializedModel {
mod.second.identifier + ":" + file.path().filename().string(),
buffer.str(), "b3d" });
}
}
}
}
void ServerModHandler::sortMods() {
modOrder.emplace_back("zepha:base");
return std::move(modDirs);
}
std::vector<LuaMod> ServerModHandler::initializeLuaMods(const std::list<std::string>& modDirs) {
using nlohmann::json;
std::function<void(Mod&, const string&, std::unordered_map<string, string>&)> insertMod =
[&](Mod& mod, const string& parent, std::unordered_map<string, string>& pending) {
for (let& o : modOrder) if (o == mod.identifier) return;
let circIt = pending.find(mod.identifier);
if (circIt != pending.end()) {
throw std::runtime_error("Circular dependency, '" + circIt->second + "' requires '" + mod.identifier +
"', but " + mod.identifier + " is required elsewhere.");
}
pending.emplace(mod.identifier, parent);
for (let& dep : mod.dependencies) {
if (dep.first == "zepha") continue;
let depIt = mods.find(dep.first);
if (depIt == mods.end()) throw std::runtime_error(
"Mod '" + mod.identifier + "' is missing dependency '" + dep.first + "'.");
insertMod(depIt->second, mod.identifier, pending);
}
modOrder.emplace_back(mod.identifier);
pending.erase(mod.identifier);
};
std::vector<LuaMod> mods{};
cf_dir_t dir;
for (const std::string& modDir : modDirs) {
std::string root = modDir + "/script";
std::list<std::string> dirsToScan{ root };
std::list<std::string> luaFiles{};
while (!dirsToScan.empty()) {
std::string dirStr = *dirsToScan.begin();
dirsToScan.erase(dirsToScan.begin());
cf_dir_open(&dir, dirStr.c_str());
while (dir.has_next) {
cf_file_t scannedFile;
cf_read_file(&dir, &scannedFile);
if (strncmp(scannedFile.name, ".", 1) != 0) {
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
else {
char* dot = strrchr(scannedFile.path, '.');
if (dot && (strncmp(dot, ".lua", 4) == 0)) luaFiles.emplace_back(scannedFile.path);
}
}
cf_dir_next(&dir);
}
cf_dir_close(&dir);
}
LuaMod mod{};
mod.modPath = modDir;
auto& conf = mod.config;
std::ifstream i(modDir + "/conf.json");
json j{};
i >> j;
auto depends = j["depends"];
if (strncmp(depends.type_name(), "array", 5) == 0) {
for (auto& it : depends) {
if (strncmp(it.type_name(), "string", 6) == 0) {
conf.depends.push_back(static_cast<std::string>(it));
}
}
}
conf.name = j["name"];
conf.description = j["description"];
conf.version = j["version"];
for (std::string& file : luaFiles) {
size_t rootPos = file.find(root);
std::string modPath = file;
assert(rootPos != std::string::npos);
std::ifstream t(file);
std::string fileStr = std::string((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
modPath.erase(rootPos, root.length());
modPath.insert(0, conf.name);
modPath.resize(modPath.size() - 4);
LuaMod::File f{ modPath, fileStr };
mod.files.push_back(f);
}
mods.push_back(mod);
for (let& mod : mods) {
std::unordered_map<string, string> pending {};
insertMod(mod.second, "TOP LEVEL", pending);
}
return mods;
}
void ServerModHandler::loadTextures(ServerSubgame& defs, const std::vector<LuaMod>& mods) {
cf_dir_t dir;
for (const LuaMod& mod : mods) {
std::string root = mod.modPath + "/textures";
std::list<std::string> dirsToScan{ root };
while (!dirsToScan.empty()) {
std::string dirStr = *dirsToScan.begin();
dirsToScan.erase(dirsToScan.begin());
if (!cf_file_exists(dirStr.c_str())) continue;
cf_dir_open(&dir, dirStr.c_str());
while (dir.has_next) {
cf_file_t scannedFile;
cf_read_file(&dir, &scannedFile);
if (strncmp(scannedFile.name, ".", 1) != 0) {
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
else {
char* dot = strrchr(scannedFile.path, '.');
if (dot && strncmp(dot, ".png", 4) == 0) {
std::string name = std::string(scannedFile.name).substr(0,
std::string(scannedFile.name).size() - 4);
name.insert(0, mod.config.name + ":");
int width, height;
unsigned char* data = stbi_load(scannedFile.path, &width, &height, nullptr, 4);
std::string str(reinterpret_cast<char*>(data),
static_cast<unsigned long>(width * height * 4));
std::string comp = gzip::compress(str.data(), str.length());
free(data);
defs.assets.textures.emplace_back(
std::move(name), comp, static_cast<u16>(width), static_cast<u16>(height));
}
}
}
cf_dir_next(&dir);
}
cf_dir_close(&dir);
}
}
}
void ServerModHandler::loadModels(ServerSubgame& defs, const std::vector<LuaMod>& mods) {
cf_dir_t dir;
for (const LuaMod& mod : mods) {
std::string root = mod.modPath + "/models";
std::list<std::string> dirsToScan{ root };
while (!dirsToScan.empty()) {
std::string dirStr = *dirsToScan.begin();
dirsToScan.erase(dirsToScan.begin());
if (!cf_file_exists(dirStr.c_str())) continue;
cf_dir_open(&dir, dirStr.c_str());
while (dir.has_next) {
cf_file_t scannedFile;
cf_read_file(&dir, &scannedFile);
if (strncmp(scannedFile.name, ".", 1) != 0) {
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
else {
char* dot = strrchr(scannedFile.path, '.');
if (dot && strncmp(dot, ".b3d", 4) == 0) {
std::string name = std::string(scannedFile.name).substr(0,
std::string(scannedFile.name).size() - 4);
name.insert(0, mod.config.name + ":");
std::ifstream t(scannedFile.path);
std::stringstream buffer;
buffer << t.rdbuf();
defs.assets.models.push_back({ std::move(name), buffer.str(), "b3d" });
}
}
}
cf_dir_next(&dir);
}
cf_dir_close(&dir);
}
}
}
void ServerModHandler::organizeDependencies(std::vector<LuaMod>& mods) {
for (int i = 0; i < mods.size(); i++) {
LuaMod& mod = mods[i];
auto& deps = mod.config.depends;
bool modifiedList = false;
for (std::string& dep : deps) {
for (int j = 0; j < mods.size(); j++) {
LuaMod& otherMod = mods[j];
if (otherMod.config.name == dep) {
if (j > i) {
LuaMod copy = otherMod;
mods.erase(mods.begin() + j);
mods.insert(mods.begin() + i, copy);
i++;
modifiedList = true;
break;
}
}
}
}
if (modifiedList) i = -1;
}
}
void ServerModHandler::serializeMods(std::vector<LuaMod>& mods) {
for (LuaMod& mod : mods) {
Serializer s;
s.append(mod.config.name)
.append(mod.config.description)
.append(mod.config.version)
.append(mod.config.depends);
for (LuaMod::File& file : mod.files) {
s.append(file.path).append(file.file);
}
string comp = gzip::compress(s.data.c_str(), s.data.length());
mod.serialized = comp;
}
}
}

View File

@ -1,36 +1,24 @@
//
// Created by aurailus on 2020-02-19.
//
#pragma once
#include <list>
#include <string>
#include <functional>
#include "LuaMod.h"
#include "Mod.h"
class ServerSubgame;
class ServerModHandler {
public:
void loadMods(ServerSubgame& defs, const std::string& path);
public:
void loadMods(ServerSubgame& defs, const string& path);
void executeMods(std::function<void(std::string)> run);
void executeMods(std::function<void(string)> run);
const std::vector<LuaMod>& cGetMods() const;
vec<string> modOrder {};
std::unordered_map<string, Mod> mods {};
private:
void loadAssets(ServerSubgame& defs);
private:
static std::list<std::string> findModDirectories(const std::string& path);
void sortMods();
static std::vector<LuaMod> initializeLuaMods(const std::list<std::string>& modDirs);
static void loadTextures(ServerSubgame& defs, const std::vector<LuaMod>& mods);
static void loadModels(ServerSubgame& defs, const std::vector<LuaMod>& mods);
static void organizeDependencies(std::vector<LuaMod>& mods);
static void serializeMods(std::vector<LuaMod>& mods);
std::vector<LuaMod> mods{};
// void serializeMods();
};

View File

@ -19,7 +19,7 @@ namespace {
void registerFn(sol::table& core, const std::string& table, sol::environment env,
std::function<void(std::string)> after, const std::string& identifier, const sol::table& data) {
auto modName = env.get<std::string>("_MODNAME");
auto modName = env.get<std::string>("__MOD_NAME");
std::string iden = identifier[0] == ':' ? modName + identifier : identifier;
if (iden[0] == '!') iden = iden.substr(1, iden.length() - 1);

View File

@ -88,7 +88,7 @@ namespace RegisterBlock {
static std::pair<BlockModel, BlockModel>
createBlockModel(sol::table blockTable, sol::table blockModels, TextureAtlas* atlas) {
// Get the specified block model
auto modelStr = blockTable.get_or<std::string>("model", "base:block");
auto modelStr = blockTable.get_or<std::string>("model", "zepha:base:block");
auto modelOpt = blockModels.get<sol::optional<sol::table>>(modelStr);
if (!modelOpt) throw std::runtime_error("Non-existent model \"" + modelStr + "\" specified");

View File

@ -1,31 +0,0 @@
zepha.set_gui(zepha.gui(function()
return Gui.Box {
background = "#334",
Gui.Text {
pos = { "4dp", "4dp" },
text_size = "2px",
content = "Minimalminimalmmnal"
},
Gui.Box {
pos = { "64dp", "64dp" },
size = { "128dp * (16/9)", "128dp" },
background = "zeus_background"
},
Gui.Box {
pos = { "64dp + 128dp", "128dp" },
size = { "128dp * (16/9)", "128dp" },
background = "zeus_background",
Gui.Text {
pos = "4dp",
text_size = "4px",
content = "What's the fuck it'd going on?"
}
}
}
end))

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1 @@
{}

View File

@ -1,6 +1,12 @@
{
"name": "@auri:basic_tools",
"name": "Basic Tools",
"identifier": "@auri:basic_tools",
"description": "Basic tools for Zepha.",
"version": "0.0.1",
"depends": ["zeus:materials", "@auri:crafting", "@auri:hot_wheel"]
}
"main": "main",
"dependencies": {
"zeus:materials": "^0",
"@auri:crafting": "^0",
"@auri:hot_wheel": "^0"
}
}

View File

@ -1,5 +1,13 @@
{
"name": "@auri:chat",
"name": "Chat",
"identifier": "@auri:chat",
"description": "A simple chat interface and API supporting messaging and commands.",
"version": "0.0.1"
"version": "0.0.1",
"main": {
"client": "client/main",
"server": "server/main"
},
"dependencies": {
"zepha": "^0"
}
}

View File

@ -1,8 +0,0 @@
-- The global chat object.
_G['chat'] = {
channels = {},
channel_order = {},
}
if zepha.server then require(_PATH .. 'api_server')
else require(_PATH .. 'api_client') end

View File

@ -1,3 +1,5 @@
require '../common/api'
chat.open = false
chat.current_channel = nil
chat._message_persist_time = 5

View File

@ -0,0 +1,2 @@
require './api'
require './gui'

View File

@ -0,0 +1,4 @@
_G['chat'] = {
channels = {},
channel_order = {},
}

View File

@ -1,8 +0,0 @@
require(_PATH .. 'api')
if zepha.client then require(_PATH .. 'gui') end
if zepha.server then
chat.create_channel('chat', { name = 'Chat', icon = '@auri:chat:chat_icon' })
end
require(_PATH .. 'test')

View File

@ -1,3 +1,5 @@
require '../common/api'
-- Creates a new chat channel with the name and options provided.
chat.create_channel = function(identifier, options)
chat.channels[identifier] = {

View File

@ -0,0 +1,5 @@
require './api'
chat.create_channel('chat', { name = 'Chat', icon = '@auri:chat:chat_icon' })
chat.create_channel('commands', { name = 'Commands', icon = '@auri:chat:commands_icon' })
chat.create_channel('debug', { name = 'Debug', icon = '@auri:chat:debug_icon' })

View File

@ -1,4 +0,0 @@
if zepha.server then
chat.create_channel('commands', { name = 'Commands', icon = '@auri:chat:commands_icon' })
chat.create_channel('debug', { name = 'Debug', icon = '@auri:chat:debug_icon' })
end

View File

@ -1,6 +1,7 @@
{
"name": "@auri:crafting",
"description": "Basic grid-crafting system for Zepha.",
"version": "1.0.0",
"depends": []
"name": "Grid Crafting",
"identifier": "@auri:crafting",
"description": "A robust grid-crafting system for Zepha.",
"version": "0.0.1",
"main": "main"
}

View File

@ -1,6 +1,12 @@
{
"name": "@auri:crazy_blocks",
"name": "Crazy Blocks",
"identifier": "@auri:crazy_blocks",
"description": "Various objects for testing block interactions.",
"version": "1.0.0",
"depends": ["zeus:default", "@auri:inventory", "@auri:hot_wheel"]
}
"version": "0.0.1",
"main": "main",
"dependencies": {
"zeus:default": "^0",
"zeus:inventory": "^0",
"@auri:hot_wheel": "^0"
}
}

View File

@ -4,7 +4,6 @@ zepha.register_blockmodel("@auri:crazy_blocks:box", {
zepha.register_block("@auri:crazy_blocks:box", {
name = "Box",
model = "base:block",
textures = {"zeus:default:oak_log_side", "zeus:default:oak_planks"},
on_interact = function(dim, pos, player)
local in_desert = player:get_dimension().identifier == 'zeus:world:endless_desert'

View File

@ -1,4 +1,4 @@
require(_PATH .. "chest")
require './chest'
local function stacker_action(dim, pos)
local v = V(0, 1, 0)
@ -12,7 +12,6 @@ end
zepha.register_block("@auri:crazy_blocks:stacker", {
name = "Stacker",
model = "base:block",
textures = {"zeus:default:oak_planks"},
on_interact = stacker_action,
on_interact_client = stacker_action
@ -20,7 +19,6 @@ zepha.register_block("@auri:crazy_blocks:stacker", {
zepha.register_block("@auri:crazy_blocks:inventory", {
name = "Open Inventory",
model = "base:block",
textures = {"zeus:default:cobblestone"},
on_interact_client = inventory_action
})

View File

@ -1,5 +1,7 @@
{
"name": "@auri:health",
"name": "Health",
"identifier": "@auri:health",
"description": "Player API expansion that adds health.",
"version": "0.0.1"
}
"version": "0.0.1",
"main": "main"
}

View File

@ -1,6 +0,0 @@
_G['health'] = {}
health.internal = {}
require(_PATH .. 'api')
require(_PATH .. 'interface')
require(_PATH .. 'hooks')

View File

@ -0,0 +1,6 @@
_G['health'] = {}
health.internal = {}
require './api'
require './interface'
require './hooks'

View File

@ -1,6 +1,13 @@
{
"name": "@auri:hot_wheel",
"name": "Hot Wheel",
"identifier": "@auri:hot_wheel",
"description": "A speedy way to select items from your inventory :3",
"version": "0.0.1",
"depends": [ "@auri:health" ]
}
"main": {
"client": "client/main",
"server": "server/main"
},
"dependencies": {
"@auri:health": "^0"
}
}

View File

@ -0,0 +1,4 @@
_G["hot_wheel"] = {}
require './interface'
require './keys'

View File

@ -1,10 +0,0 @@
_G["hot_wheel"] = {}
if zepha.server then
require(_PATH .. "register")
end
if zepha.client then
require(_PATH .. "interface")
require(_PATH .. "keys")
end

View File

@ -1,31 +0,0 @@
zepha.bind("new_player", function(p)
local inv = p:get_inventory()
inv:add_list("hot_wheel_1", 5, 5)
inv:add_list("hot_wheel_2", 5, 5)
inv:add_list("hot_wheel_3", 5, 5)
inv:add_list("hot_wheel_4", 5, 5)
inv:add_list("hot_wheel_5", 5, 5)
inv:add_list("hot_wheel_6", 5, 5)
-- -- Get hot wheel
-- local invs = {
-- inv:get_list("hot_wheel_1"),
-- inv:get_list("hot_wheel_2"),
-- inv:get_list("hot_wheel_3"),
-- inv:get_list("hot_wheel_4"),
-- inv:get_list("hot_wheel_5"),
-- inv:get_list("hot_wheel_6")
-- }
--
-- for _,inv in pairs(invs) do
-- inv.allow_take = function() return 0 end
--
-- inv.allow_put = function(slot, item)
-- zepha.after(function()
-- -- This delay is necessary to avoid the engine overwriting it with nothing
-- inv:set_stack(slot, item)
-- end, 0)
-- return 0
-- end
-- end
end)

View File

@ -0,0 +1,10 @@
zepha.bind('new_player', function(player)
print('added inventories :)')
local inv = player:get_inventory()
inv:add_list('hot_wheel_1', 5, 5)
inv:add_list('hot_wheel_2', 5, 5)
inv:add_list('hot_wheel_3', 5, 5)
inv:add_list('hot_wheel_4', 5, 5)
inv:add_list('hot_wheel_5', 5, 5)
inv:add_list('hot_wheel_6', 5, 5)
end)

View File

@ -1,5 +1,7 @@
{
"name": "@auri:tnt",
"name": "TNT",
"identifier": "@auri:tnt",
"description": "Bet you've never seen this one before.",
"version": "0.0.1"
}
"version": "0.0.1",
"main": "main"
}

View File

@ -1,6 +1,6 @@
zepha.register_block("@auri:tnt:tnt", {
name = "TNT",
model = "base:block",
model = "zepha:base:block",
textures = {
"@auri:tnt:tnt_top",
"@auri:tnt:tnt_bottom",

View File

@ -0,0 +1,10 @@
{
"name": "World Edit",
"identifier": "@auri:world_edit",
"description": "Copy, Paste, Save, and Load regions of the world.",
"version": "0.0.1",
"main": "main",
"dependencies": {
"@auri:hot_wheel": "^0"
}
}

View File

@ -1,5 +1,7 @@
{
"name": "@auri:yield",
"name": "Yield",
"identifier": "@auri:yield",
"description": "A mod to enable collecting items from blocks when mined. Can be configured to use dropped items or just directly add to inventory.",
"version": "0.0.1"
}
"version": "0.0.1",
"main": "main"
}

View File

@ -1,6 +0,0 @@
require("@auri:yield/dropped_item")
local DROP_ENTITY = true
if DROP_ENTITY then require("@auri:yield/mode/entity")
else require("@auri:yield/mode/direct") end

View File

@ -0,0 +1,9 @@
require './dropped_item'
local DROP_ENTITY = true
if DROP_ENTITY then
require './mode/entity'
else
require './mode/direct'
end

View File

@ -1,4 +1,4 @@
local get_yield = require("@auri:yield/get_yield")
local get_yield = require '../get_yield'
if zepha.server then
zepha.bind("on_break", function(pos, player)

View File

@ -1,4 +1,4 @@
local get_yield = require("@auri:yield/get_yield")
local get_yield = require '../get_yield'
if zepha.server then
zepha.bind("on_break", function(dim, pos)

View File

@ -1,6 +1,12 @@
{
"name": "zeus:default",
"name": "Zeus",
"identifier": "zeus:default",
"description": "Default mod for the Zeus subgame.",
"version": "0.0.1",
"depends": ["zeus:materials", "@aurailus:basictools", "@aurailus:crafting"]
}
"main": "main",
"dependencies": {
"zeus:materials": "^0",
"@auri:basic_tools": "^0",
"@auri:crafting": "^0"
}
}

View File

@ -2,7 +2,7 @@ zepha.register_block(":bush_stem", {
name = "Bush Stem",
culls = false,
model = "base:cross_large",
model = "zepha:base:cross_large",
textures = { "zeus:default:bush_stem" },
far_render = false,

View File

@ -1,7 +1,6 @@
zepha.register_block(":cobblestone", {
name = "Cobblestone",
model = "base:block",
textures = { "zeus:default:cobblestone" },
tool_props = {

View File

@ -1,7 +1,6 @@
zepha.register_block(":dirt", {
name = "Dirt",
model = "base:block",
textures = { "zeus:default:dirt" },
tool_props = {

View File

@ -1,7 +1,7 @@
zepha.register_block(":grass", {
name = "Grass",
model = "base:block_foliage",
model = "zepha:base:block_foliage",
textures = {
"tint(0, zeus:default:grass_top)",
"zeus:default:dirt",

View File

@ -1,13 +0,0 @@
require(_PATH .. "bush_stem")
require(_PATH .. "cobblestone")
require(_PATH .. "podzol")
require(_PATH .. "dirt")
require(_PATH .. "grass")
require(_PATH .. "leaves")
require(_PATH .. "cactus")
require(_PATH .. "sand")
require(_PATH .. "sandstone")
require(_PATH .. "stone")
require(_PATH .. "tallgrass")
require(_PATH .. "wood")
require(_PATH .. "light")

View File

@ -2,7 +2,7 @@ zepha.register_block(":leaves", {
name = "Leaves",
culls = false,
model = "base:leaf_like",
model = "zepha:base:leaf_like",
textures = {
"zeus:default:leaves",
"zeus:default:leaves_puff"

View File

@ -0,0 +1,13 @@
require './bush_stem'
require './cobblestone'
require './podzol'
require './dirt'
require './grass'
require './leaves'
require './cactus'
require './sand'
require './sandstone'
require './stone'
require './tallgrass'
require './wood'
require './light'

View File

@ -1,7 +1,6 @@
zepha.register_block(":podzol", {
name = "Podzol",
model = "base:block",
textures = {
"zeus:default:podzol",
"zeus:default:dirt",

View File

@ -1,7 +1,6 @@
zepha.register_block(":sand", {
name = "Sand",
model = "base:block",
textures = { "zeus:default:sand" },
tool_props = {

View File

@ -1,7 +1,6 @@
zepha.register_block(":sandstone", {
name = "Sandstone",
model = "base:block",
textures = {
"zeus:default:sandstone_top",
"zeus:default:sandstone_top",

View File

@ -1,7 +1,6 @@
zepha.register_block(":stone", {
name = "Stone",
model = "base:block",
textures = {"zeus:default:stone"},
tool_props = {

View File

@ -4,7 +4,7 @@ for i = 1, 5, 1 do
culls = false,
solid = false,
model = "base:cross_plant",
model = "zepha:base:cross_plant",
textures = { "tint(0, zeus:default:tallgrass_"..i..")" },
lowdef_render = false,

View File

@ -1,7 +1,7 @@
zepha.register_block(":wood", {
name = "Log",
model = "base:pillar",
model = "zepha:base:pillar",
textures = {
"zeus:default:oak_log_top_pillar",
"zeus:default:oak_log_top_pillar",

View File

@ -1,4 +0,0 @@
require(_PATH .. "rabbit")
require(_PATH .. "raven")
require(_PATH .. "bee")
require(_PATH .. "test")

View File

@ -0,0 +1,4 @@
require './rabbit'
require './raven'
require './bee'
require './test'

Some files were not shown because too many files have changed in this diff Show More