Update file loading and mod config.
parent
3c0bc568fe
commit
791bdd7eb1
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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))
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,8 @@
|
|||
require './models'
|
||||
require './player_interact'
|
||||
require './inventory'
|
||||
require './tools'
|
||||
|
||||
if zepha.client then
|
||||
require './hud'
|
||||
end
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'
|
|
@ -2,4 +2,4 @@
|
|||
-- Basic 'none' model renders nothing.
|
||||
--
|
||||
|
||||
zepha.register_blockmodel("base:none", { parts = {} })
|
||||
zepha.register_blockmodel(':none', { parts = {} })
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
-- Performs a shallow copy of a table.
|
||||
function table.clone(tbl)
|
||||
return {table.unpack(tbl)}
|
||||
return { table.unpack(tbl) }
|
||||
end
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {};
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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 {};
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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))
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -1,3 +1,5 @@
|
|||
require '../common/api'
|
||||
|
||||
chat.open = false
|
||||
chat.current_channel = nil
|
||||
chat._message_persist_time = 5
|
|
@ -0,0 +1,2 @@
|
|||
require './api'
|
||||
require './gui'
|
|
@ -0,0 +1,4 @@
|
|||
_G['chat'] = {
|
||||
channels = {},
|
||||
channel_order = {},
|
||||
}
|
|
@ -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')
|
|
@ -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] = {
|
|
@ -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' })
|
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
})
|
|
@ -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"
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
_G['health'] = {}
|
||||
health.internal = {}
|
||||
|
||||
require(_PATH .. 'api')
|
||||
require(_PATH .. 'interface')
|
||||
require(_PATH .. 'hooks')
|
|
@ -0,0 +1,6 @@
|
|||
_G['health'] = {}
|
||||
health.internal = {}
|
||||
|
||||
require './api'
|
||||
require './interface'
|
||||
require './hooks'
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
_G["hot_wheel"] = {}
|
||||
|
||||
require './interface'
|
||||
require './keys'
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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"
|
||||
}
|
|
@ -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",
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,9 @@
|
|||
require './dropped_item'
|
||||
|
||||
local DROP_ENTITY = true
|
||||
|
||||
if DROP_ENTITY then
|
||||
require './mode/entity'
|
||||
else
|
||||
require './mode/direct'
|
||||
end
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
zepha.register_block(":cobblestone", {
|
||||
name = "Cobblestone",
|
||||
|
||||
model = "base:block",
|
||||
textures = { "zeus:default:cobblestone" },
|
||||
|
||||
tool_props = {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
zepha.register_block(":dirt", {
|
||||
name = "Dirt",
|
||||
|
||||
model = "base:block",
|
||||
textures = { "zeus:default:dirt" },
|
||||
|
||||
tool_props = {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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")
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
|
@ -1,7 +1,6 @@
|
|||
zepha.register_block(":podzol", {
|
||||
name = "Podzol",
|
||||
|
||||
model = "base:block",
|
||||
textures = {
|
||||
"zeus:default:podzol",
|
||||
"zeus:default:dirt",
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
zepha.register_block(":sand", {
|
||||
name = "Sand",
|
||||
|
||||
model = "base:block",
|
||||
textures = { "zeus:default:sand" },
|
||||
|
||||
tool_props = {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
zepha.register_block(":sandstone", {
|
||||
name = "Sandstone",
|
||||
|
||||
model = "base:block",
|
||||
textures = {
|
||||
"zeus:default:sandstone_top",
|
||||
"zeus:default:sandstone_top",
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
zepha.register_block(":stone", {
|
||||
name = "Stone",
|
||||
|
||||
model = "base:block",
|
||||
textures = {"zeus:default:stone"},
|
||||
|
||||
tool_props = {
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
require(_PATH .. "rabbit")
|
||||
require(_PATH .. "raven")
|
||||
require(_PATH .. "bee")
|
||||
require(_PATH .. "test")
|
|
@ -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
Loading…
Reference in New Issue