]> git.lizzy.rs Git - dragonblocks3d-lua.git/commitdiff
Refactoring
authorElias Fleckenstein <eliasfleckenstein@web.de>
Tue, 11 Aug 2020 08:51:38 +0000 (10:51 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Tue, 11 Aug 2020 08:51:38 +0000 (10:51 +0200)
55 files changed:
.gitmodules
README
deps/moonassimp [new submodule]
deps/moonglmath [new submodule]
deps/moonimage [new submodule]
init.lua
modules/BlockSystem/src/init.lua [new file with mode: 0644]
modules/Client/dependencies.txt [new file with mode: 0644]
modules/Client/src/graphics.lua [new file with mode: 0644]
modules/Client/src/init.lua [new file with mode: 0644]
modules/Game/dependencies.txt [new file with mode: 0644]
modules/Game/ressources/dirt.png [new file with mode: 0644]
modules/Game/ressources/stone.png [new file with mode: 0644]
modules/Game/ressources/texture.xcf [new file with mode: 0644]
modules/Game/src/init.lua [new file with mode: 0644]
modules/MainMenu/dependencies.txt [deleted file]
modules/MainMenu/src/init.lua [deleted file]
modules/MapGen/dependencies.txt [new file with mode: 0644]
modules/MapGen/src/init.lua [new file with mode: 0644]
modules/PlayerSystem/src/init.lua [new file with mode: 0644]
modules/PlayerSystem/src/localplayer.lua [new file with mode: 0644]
modules/PlayerSystem/src/player.lua [new file with mode: 0644]
modules/PlayerSystem/src/remoteplayer.lua [new file with mode: 0644]
modules/RenderEngine/shaders/fragment.glsl [new file with mode: 0755]
modules/RenderEngine/shaders/vertex.glsl [new file with mode: 0755]
modules/RenderEngine/src/camera.lua [new file with mode: 0644]
modules/RenderEngine/src/cube.lua [new file with mode: 0644]
modules/RenderEngine/src/init.lua
modules/RenderEngine/src/mesh.lua [new file with mode: 0644]
modules/RenderEngine/src/shaders.lua [new file with mode: 0644]
modules/RenderEngine/src/textures.lua [new file with mode: 0644]
modules/RenderEngine/src/window.lua [new file with mode: 0644]
modules/WorldSystem/dependencies.txt [new file with mode: 0644]
modules/WorldSystem/src/block.lua [new file with mode: 0644]
modules/WorldSystem/src/chunk.lua [new file with mode: 0644]
modules/WorldSystem/src/init.lua [new file with mode: 0644]
modules/WorldSystem/src/map.lua [new file with mode: 0644]
modules/WorldSystem/src/world.lua [new file with mode: 0644]
src/class.lua
src/event_interface.lua [deleted file]
src/events.lua [new file with mode: 0644]
src/init.lua
src/module_manager.lua [deleted file]
src/modulemgr.lua [new file with mode: 0644]
src/serialisation.lua [new file with mode: 0644]
src/serializer.lua [deleted file]
src/task_manager.lua [deleted file]
src/taskmgr.lua [new file with mode: 0644]
util/hex2rgb.lua [new file with mode: 0644]
util/indexof.lua [deleted file]
util/objectmgr.lua [deleted file]
util/split.lua [deleted file]
util/string_split.lua [new file with mode: 0644]
util/table_assign.lua [new file with mode: 0644]
util/table_indexof.lua [new file with mode: 0644]

index 713f6c8c2946b11f34ecdeaf14244d6c2df44879..a6c4f677e56ce6f3e294e8c8721ddb2e965a11ef 100644 (file)
@@ -4,3 +4,12 @@
 [submodule "moonglfw"]
        path = deps/moonglfw
        url = https://github.com/stetre/moonglfw
+[submodule "moonglmath"]
+       path = deps/moonglmath
+       url = https://github.com/stetre/moonglmath
+[submodule "moonassimp"]
+       path = deps/moonassimp
+       url = https://github.com/stetre/moonassimp
+[submodule "moonimage"]
+       path = deps/moonimage
+       url = https://github.com/stetre/moonimage
diff --git a/README b/README
index 7792d15525301f7e0b67f36de583bd31348382f6..1115293d197354125b5dca8d6689d1d661aa76cc 100644 (file)
--- a/README
+++ b/README
@@ -4,4 +4,4 @@ $ luarocks install lsqlite3
 $ luarocks install luasocket
 $ luarocks install luafilesystem
 
-Build moongl and moonglfw: See build README.md in deps/moongl and deps/moonglfw
+Build moongl, moonglfw, moonglmath, moonimage and moonassimp: Only needed for running the client, see build instructions in deps/*/README.md
diff --git a/deps/moonassimp b/deps/moonassimp
new file mode 160000 (submodule)
index 0000000..9168e06
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 9168e065e17d164d2c7ddcb691fec23b47362fc1
diff --git a/deps/moonglmath b/deps/moonglmath
new file mode 160000 (submodule)
index 0000000..088ad95
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 088ad95710be4325dd368df19b5323e2837c4059
diff --git a/deps/moonimage b/deps/moonimage
new file mode 160000 (submodule)
index 0000000..1879986
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 18799868803a424bff4b95b79123f1d9c28c9483
index 5edef9f52bcc8f08c66010972c0bbbb48df3ab33..da40c8a8253d70b1c737763c8679ffcc85bc8573 100755 (executable)
--- a/init.lua
+++ b/init.lua
@@ -4,16 +4,12 @@ socket = require("socket")
 lsqlite3 = require("lsqlite3")
 gl = require("moongl")
 glfw = require("moonglfw")
-require("util/objectmgr")
-require("util/split")
-require("util/indexof")
-
-Dragonblocks = ObjectMgr.create()
+image = require("moonimage")
+glm = require("moonglmath")
+string.split = require("util/string_split")
+table.indexof = require("util/table_indexof")
+table.assign = require("util/table_assign")
+hex2rgb = require("util/hex2rgb")
 
 require("src/init")
 
-Dragonblocks:init()
-
-Dragonblocks:start_module(arg[1])
-
-Dragonblocks:start_tasks()
diff --git a/modules/BlockSystem/src/init.lua b/modules/BlockSystem/src/init.lua
new file mode 100644 (file)
index 0000000..0dcd48d
--- /dev/null
@@ -0,0 +1,18 @@
+BlockSystem.blocks = {}
+
+function BlockSystem:register_block(def)
+       local id = table.insert(self.blocks, def)
+       def.id = id
+       self.blocks[def.name] = def
+end
+
+function BlockSystem:get_def(key)
+       return self.blocks[key]
+end
+
+function BlockSystem:init_textures()
+       RenderEngine:init_texture_args()
+       for _, def in ipairs(self.blocks) do
+               def.texture = RenderEngine:create_texture(def.texture_path)
+       end
+end
diff --git a/modules/Client/dependencies.txt b/modules/Client/dependencies.txt
new file mode 100644 (file)
index 0000000..30cb2e1
--- /dev/null
@@ -0,0 +1,3 @@
+RenderEngine
+WorldSystem
+PlayerSystem
diff --git a/modules/Client/src/graphics.lua b/modules/Client/src/graphics.lua
new file mode 100644 (file)
index 0000000..1dc1e42
--- /dev/null
@@ -0,0 +1,44 @@
+local graphics = {}
+
+function graphics:init()
+       RenderEngine:init()
+       
+       RenderEngine.bininear_filter = false
+       RenderEngine.mipmap = false
+       RenderEngine.camera_pos = glm.vec3(-8, -18, -40)
+       RenderEngine.fov = 45
+       RenderEngine.mesh_effect_grow_time = 0.25
+       RenderEngine.mesh_effect_flyin_time = 1
+       RenderEngine.mesh_effect_flyin_offset = 20
+       --RenderEngine.mesh_effect_rotate_speed = 
+       
+       RenderEngine:set_wireframe(false)
+
+       RenderEngine:set_window_title("Dragonblocks 3D")
+       RenderEngine:set_window_size(1250, 750)
+       RenderEngine:set_window_pos(50, 50)
+       
+       RenderEngine:set_sky("#87CEEB")
+       
+       BlockSystem:init_textures()
+end
+
+function graphics:create_chunk_meshes(chunk)
+       for _, block in pairs(chunk.blocks) do
+               self:create_block_mesh(block, true)
+       end
+end
+
+function graphics:create_block_mesh(block, grow)
+       local mesh = RenderEngine.Mesh()
+       mesh:set_pos(block.pos)
+       mesh:set_size(glm.vec3(1, 1, 1))
+       mesh:set_texture(block.def.texture)
+       mesh:make_cube()
+       if grow then
+               mesh:set_effect(RenderEngine.Mesh.EFFECT_GROW)
+       end
+       mesh:add_to_scene()
+end
+
+return graphics
diff --git a/modules/Client/src/init.lua b/modules/Client/src/init.lua
new file mode 100644 (file)
index 0000000..0dbaa46
--- /dev/null
@@ -0,0 +1,7 @@
+Client.graphics = Client:run("graphics")
+
+Client.graphics:init()
+
+Client.map = WorldSystem.Map()
+
+RenderEngine:render_loop()
diff --git a/modules/Game/dependencies.txt b/modules/Game/dependencies.txt
new file mode 100644 (file)
index 0000000..bbea3c7
--- /dev/null
@@ -0,0 +1 @@
+BlockSystem
diff --git a/modules/Game/ressources/dirt.png b/modules/Game/ressources/dirt.png
new file mode 100644 (file)
index 0000000..5cc6e86
Binary files /dev/null and b/modules/Game/ressources/dirt.png differ
diff --git a/modules/Game/ressources/stone.png b/modules/Game/ressources/stone.png
new file mode 100644 (file)
index 0000000..a641ceb
Binary files /dev/null and b/modules/Game/ressources/stone.png differ
diff --git a/modules/Game/ressources/texture.xcf b/modules/Game/ressources/texture.xcf
new file mode 100644 (file)
index 0000000..0c09065
Binary files /dev/null and b/modules/Game/ressources/texture.xcf differ
diff --git a/modules/Game/src/init.lua b/modules/Game/src/init.lua
new file mode 100644 (file)
index 0000000..707ce0d
--- /dev/null
@@ -0,0 +1,11 @@
+local ressource_path = Game:get_path() .. "/ressources/"
+
+BlockSystem:register_block({
+       name = "game:stone",
+       texture_path = ressource_path .. "stone.png",
+})
+
+BlockSystem:register_block({
+       name = "game:dirt",
+       texture_path = ressource_path .. "dirt.png",
+})
diff --git a/modules/MainMenu/dependencies.txt b/modules/MainMenu/dependencies.txt
deleted file mode 100644 (file)
index 0055b8e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-RenderEngine
diff --git a/modules/MainMenu/src/init.lua b/modules/MainMenu/src/init.lua
deleted file mode 100644 (file)
index 503e73e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-RenderEngine:open_window()
-RenderEngine:set_window_title("Dragonblocks 3D - Main Menu")
-
-RenderEngine:add_render_task()
-
-MainMenu:init()
diff --git a/modules/MapGen/dependencies.txt b/modules/MapGen/dependencies.txt
new file mode 100644 (file)
index 0000000..93ba72e
--- /dev/null
@@ -0,0 +1 @@
+Game
diff --git a/modules/MapGen/src/init.lua b/modules/MapGen/src/init.lua
new file mode 100644 (file)
index 0000000..86f4087
--- /dev/null
@@ -0,0 +1,19 @@
+local stone = BlockSystem:get_def("game:stone")
+local dirt = BlockSystem:get_def("game:dirt")
+
+local air_probability = 2
+local random_blocks = {stone, dirt}
+local random_blocks_num = #random_blocks + air_probability
+
+function MapGen:generate(chunk)
+       for x = 0, 15 do
+               for y = 0, 15 do
+                       for z = 0, 15 do
+                               local block = random_blocks[math.random(random_blocks_num)]
+                               if block then
+                                       chunk:add_block(glm.vec3(x, y, z), block)
+                               end
+                       end
+               end
+       end
+end
diff --git a/modules/PlayerSystem/src/init.lua b/modules/PlayerSystem/src/init.lua
new file mode 100644 (file)
index 0000000..8c7cf17
--- /dev/null
@@ -0,0 +1,9 @@
+PlayerSystem.Player = PlayerSystem:run("player")
+
+function PlayerSystem:init(gametype)
+       if gametype == "client" then
+               PlayerSystem.LocalPlayer = PlayerSystem:run("localplayer")
+       elseif gametype == "server" then
+               PlayerSystem.RemotePlayer = PlayerSystem:run("remoteplayer")
+       end
+end
diff --git a/modules/PlayerSystem/src/localplayer.lua b/modules/PlayerSystem/src/localplayer.lua
new file mode 100644 (file)
index 0000000..18465e1
--- /dev/null
@@ -0,0 +1,23 @@
+local LocalPlayer = Dragonblocks.create_class()
+table.assign(LocalPlayer, PlayerSystem.Player)
+
+function LocalPlayer:constructor()
+       self:init()
+       self:set_fov(45)
+       self:add_event_listener("after_set_position", function(event) self:set_position_callback(event) end)
+end
+
+function LocalPlayer:set_position_callback(event)
+       -- Move Camera & Report to Server
+end
+
+function LocalPlayer:move(vec)
+       self:set_position(self.pos + vec)
+end
+
+function LocalPlayer:set_fov(fov)
+       self.fov = fov
+       RenderEngine.fov = fov
+end
+
+return LocalPlayer
diff --git a/modules/PlayerSystem/src/player.lua b/modules/PlayerSystem/src/player.lua
new file mode 100644 (file)
index 0000000..4bf03b9
--- /dev/null
@@ -0,0 +1,28 @@
+local Player = {}
+
+function Player:init()
+       Dragonblocks.create_event_interface(self)
+       self.pos = glm.vec3(0, 0, 0)
+end
+
+function Player:set_position(pos)
+       self:fire_event({
+               type = "on_set_position",
+               new_position = pos,
+               cancel = false
+       }, function(evt) self:raw_set_position(evt) end)
+end
+
+function Player:raw_set_position(event)
+       local self = event.origin
+       if not event.cancel then
+               self.pos = event.new_position
+       end
+       self:fire_event({
+               type = "after_set_position",
+       })
+end
+
+
+
+return Player
diff --git a/modules/PlayerSystem/src/remoteplayer.lua b/modules/PlayerSystem/src/remoteplayer.lua
new file mode 100644 (file)
index 0000000..96a81b0
--- /dev/null
@@ -0,0 +1,8 @@
+local RemotePlayer = Dragonblocks.create_class()
+table.assign(RemotePlayer, PlayerSystem.Player)
+
+function RemotePlayer:constructor()
+       self:init()
+end
+
+return RemotePlayer
diff --git a/modules/RenderEngine/shaders/fragment.glsl b/modules/RenderEngine/shaders/fragment.glsl
new file mode 100755 (executable)
index 0000000..8169c5d
--- /dev/null
@@ -0,0 +1,12 @@
+#version 330 core\r
+\r
+in vec2 ourTexCoord;\r
+\r
+out vec4 finalColor;\r
+\r
+uniform sampler2D ourTexture;\r
+\r
+void main()\r
+{\r
+       finalColor = texture(ourTexture, ourTexCoord);\r
+}\r
diff --git a/modules/RenderEngine/shaders/vertex.glsl b/modules/RenderEngine/shaders/vertex.glsl
new file mode 100755 (executable)
index 0000000..9c08313
--- /dev/null
@@ -0,0 +1,16 @@
+#version 330 core\r
+\r
+layout(location = 0) in vec3 aPos;\r
+layout(location = 1) in vec2 aTexCoord;\r
+\r
+out vec2 ourTexCoord;\r
+\r
+uniform mat4 model;\r
+uniform mat4 view;\r
+uniform mat4 projection;\r
+\r
+void main()\r
+{\r
+    gl_Position = projection * view  * model * vec4(aPos, 1.0);\r
+    ourTexCoord = aTexCoord;\r
+}\r
diff --git a/modules/RenderEngine/src/camera.lua b/modules/RenderEngine/src/camera.lua
new file mode 100644 (file)
index 0000000..8d1c8b6
--- /dev/null
@@ -0,0 +1 @@
diff --git a/modules/RenderEngine/src/cube.lua b/modules/RenderEngine/src/cube.lua
new file mode 100644 (file)
index 0000000..ba4e6c9
--- /dev/null
@@ -0,0 +1,43 @@
+return {
+  -0.5, -0.5, -0.5,  0.0, 0.0,
+   0.5, -0.5, -0.5,  1.0, 0.0,
+   0.5,  0.5, -0.5,  1.0, 1.0,
+   0.5,  0.5, -0.5,  1.0, 1.0,
+  -0.5,  0.5, -0.5,  0.0, 1.0,
+  -0.5, -0.5, -0.5,  0.0, 0.0,
+
+  -0.5, -0.5,  0.5,  0.0, 0.0,
+   0.5, -0.5,  0.5,  1.0, 0.0,
+   0.5,  0.5,  0.5,  1.0, 1.0,
+   0.5,  0.5,  0.5,  1.0, 1.0,
+  -0.5,  0.5,  0.5,  0.0, 1.0,
+  -0.5, -0.5,  0.5,  0.0, 0.0,
+
+  -0.5,  0.5,  0.5,  1.0, 0.0,
+  -0.5,  0.5, -0.5,  1.0, 1.0,
+  -0.5, -0.5, -0.5,  0.0, 1.0,
+  -0.5, -0.5, -0.5,  0.0, 1.0,
+  -0.5, -0.5,  0.5,  0.0, 0.0,
+  -0.5,  0.5,  0.5,  1.0, 0.0,
+
+   0.5,  0.5,  0.5,  1.0, 0.0,
+   0.5,  0.5, -0.5,  1.0, 1.0,
+   0.5, -0.5, -0.5,  0.0, 1.0,
+   0.5, -0.5, -0.5,  0.0, 1.0,
+   0.5, -0.5,  0.5,  0.0, 0.0,
+   0.5,  0.5,  0.5,  1.0, 0.0,
+
+  -0.5, -0.5, -0.5,  0.0, 1.0,
+   0.5, -0.5, -0.5,  1.0, 1.0,
+   0.5, -0.5,  0.5,  1.0, 0.0,
+   0.5, -0.5,  0.5,  1.0, 0.0,
+  -0.5, -0.5,  0.5,  0.0, 0.0,
+  -0.5, -0.5, -0.5,  0.0, 1.0,
+
+  -0.5,  0.5, -0.5,  0.0, 1.0,
+   0.5,  0.5, -0.5,  1.0, 1.0,
+   0.5,  0.5,  0.5,  1.0, 0.0,
+   0.5,  0.5,  0.5,  1.0, 0.0,
+  -0.5,  0.5,  0.5,  0.0, 0.0,
+  -0.5,  0.5, -0.5,  0.0, 1.0
+} 
index 0da1656d706cedfbd10ec393cc9643ab0b10192b..1d6d888b58f48bbad74ccdf540fa9d6f1b8f6ab4 100644 (file)
@@ -1,37 +1,80 @@
-glfw.window_hint("context version major", 3)
-glfw.window_hint("context version minor", 3)
-glfw.window_hint("opengl profile", "core")
+RenderEngine:run("camera")
+RenderEngine:run("shaders")
+RenderEngine:run("textures")
+RenderEngine:run("window")
 
-function RenderEngine.reshape(_, width, height)
-       gl.viewport(0, 0, width, height)
+RenderEngine.Mesh = RenderEngine:run("mesh")
+
+function RenderEngine:init()
+       self:init_glfw()
+       self:create_window()
+       self:init_glew()
+       self:load_shaders()
+       self:set_sky("#FFFFFF")
+       self:add_render_task()
 end
 
-function RenderEngine:open_window()
-       self.window = glfw.create_window(50, 50, "Unnamed Window")
-       glfw.make_context_current(self.window)
+function RenderEngine:init_glew()
        gl.init()
-       glfw.set_framebuffer_size_callback(self.window, RenderEngine.reshape)
 end
 
-function RenderEngine:set_window_title(title)
-       glfw.set_window_title(self.window, title)
+function RenderEngine:add_render_task()
+       Dragonblocks:add_task(function()
+
+       end)
+end
+
+function RenderEngine:render_loop()
+       self.last_time = glfw.get_time()
+       repeat
+               self:render()
+               --coroutine.yield()
+       until glfw.window_should_close(self.window)
 end
 
 function RenderEngine:render()
-       glfw.poll_events()
-       gl.clear_color(1.0, 0.5, 0.2, 1.0)
+       local dtime = glfw.get_time() - self.last_time
+       self.last_time = glfw.get_time()
+       
+       gl.clear_color(self.sky)
+       gl.enable("depth test")
        gl.clear("color", "depth")
+       
+       gl.use_program(self.shaders)
+       
+       local view_matrix = glm.translate(self.camera_pos)
+       local projection_matrix = glm.perspective(math.rad(self.fov), self.window_width / self.window_height, 0.1, 100) 
+       
+       gl.uniform_matrix4f(self.projection_matix_location, true, projection_matrix)
+       gl.uniform_matrix4f(self.view_matix_location, true, view_matrix)
+       
+       for _, mesh in ipairs(self.Mesh.list) do
+               mesh:render(dtime)
+       end
+       
        glfw.swap_buffers(self.window)
-       coroutine.yield()
+       glfw.poll_events()
 end
 
-function RenderEngine:render_loop()
-       repeat RenderEngine:render()
-       until glfw.window_should_close(self.window)
-end
 
-function RenderEngine:add_render_task()
-       Dragonblocks:add_task(function() RenderEngine:render_loop() end)
+--[[
+function RenderEngine:clear_removed_meshes()
+       local remove_indices = {} 
+       for index, mesh in pairs(self.meshes) do
+               if mesh.removed then
+                       table.insert(remove_indices, index)
+               end
+       end
+       for i, index in pairs(remove_indices)
+               table.remove(self.meshes, index - i + 1)
+       end
+end
+]]--
+function RenderEngine:set_sky(htmlcolor)
+       local r, g, b = hex2rgb(htmlcolor)
+       self.sky = {r, g, b, 1.0}
 end
 
-RenderEngine:init()
+function RenderEngine:set_wireframe(v)
+       gl.polygon_mode("front and back", (v and "line" or "fill"))
+end
diff --git a/modules/RenderEngine/src/mesh.lua b/modules/RenderEngine/src/mesh.lua
new file mode 100644 (file)
index 0000000..446e2b4
--- /dev/null
@@ -0,0 +1,110 @@
+local Mesh = Dragonblocks.create_class()
+
+Mesh.EFFECT_GROW = 1
+Mesh.EFFECT_FLYIN = 2
+Mesh.EFFECT_ROTATE = 3
+Mesh.CUBE = RenderEngine:run("cube")
+Mesh.list = {}
+
+function Mesh:set_size(size)
+       self.size = size
+end
+
+function Mesh:set_pos(pos)
+       self.pos = pos
+end
+
+function Mesh:set_texture(texture)
+       self.texture = texture
+end
+
+function Mesh:set_effect(effect, after)
+       self.effect = effect
+       self.effect_lasts =
+               (effect == Mesh.EFFECT_GROW) and RenderEngine.mesh_effect_grow_time
+               or
+               (effect == Mesh.EFFECT_FLYIN) and RenderEngine.mesh_effect_flyin_time
+       self.after_effect = after
+end
+
+function Mesh:add_to_scene()
+       if self.effect then
+               self.created = glfw.get_time()
+       end
+       table.insert(Mesh.list, self)
+end
+
+function Mesh:remove_from_scene()
+       local i = table.indexof(Mesh.list, self)
+       if i then
+               table.remove(Mesh.list, i)
+       end
+end
+
+function Mesh:make_cube()
+       self:apply_vertices(Mesh.CUBE)
+end
+
+function Mesh:apply_vertices(vertices)
+       self.vao = gl.gen_vertex_arrays(1)
+       self.vbo = gl.gen_buffers(1)
+       
+       gl.bind_vertex_array(self.vao)
+       
+       gl.bind_buffer("array", self.vbo)
+       gl.buffer_data("array", gl.pack("float", vertices), "static draw")
+       
+       local fsize = gl.sizeof("float")
+       
+       local stride = 5 * fsize
+       
+       gl.vertex_attrib_pointer(0, 3, "float", false, stride, 0)
+       gl.enable_vertex_attrib_array(0)
+       gl.vertex_attrib_pointer(1, 2, "float", false, stride, 3 * fsize)       
+       gl.enable_vertex_attrib_array(1)
+       
+       gl.unbind_buffer("array")
+       gl.unbind_vertex_array()
+       
+       return vao
+end
+
+function Mesh:render(dtime)
+       local pos, size = self.pos, self.size
+
+       if self.effect then
+               if self.effect_lasts then
+                       self.effect_lasts = self.effect_lasts - dtime
+                       if self.effect_lasts < 0 then
+                               if self.after_effect then
+                                       self:after_effect()
+                               end
+                               self.effect = nil
+                               self.effect_lasts = nil
+                               self.after_effect = nil
+                               goto draw
+                       end
+               end
+               if self.effect == Mesh.EFFECT_GROW then
+                       size = size * math.pow(1 - self.effect_lasts / RenderEngine.mesh_effect_grow_time, 1)
+               elseif self.effect == Mesh.EFFECT_FLYIN then
+                       pos = pos - glm.vec3(0, RenderEngine.mesh_effect_flyin_offset * self.effect_lasts / RenderEngine.mesh_effect_flyin_time, 0)
+               end
+       end
+       
+       ::draw::
+       
+       local model_matrix = 1
+               * glm.translate(pos)
+               * glm.scale(size)
+       
+       gl.uniform_matrix4f(gl.get_uniform_location(RenderEngine.shaders, "model"), true, model_matrix)
+       gl.active_texture(0)
+       gl.bind_texture("2d", self.texture)
+       gl.bind_vertex_array(self.vao)
+       gl.draw_arrays("triangles", 0, 36)
+       gl.unbind_vertex_array()
+       gl.unbind_texture("2d")
+end
+
+return Mesh
diff --git a/modules/RenderEngine/src/shaders.lua b/modules/RenderEngine/src/shaders.lua
new file mode 100644 (file)
index 0000000..f9cb691
--- /dev/null
@@ -0,0 +1,9 @@
+function RenderEngine:load_shaders()
+       local path = self:get_path() .. "/shaders/"
+       local program, vsh, fsh = gl.make_program({vertex = path .. "vertex.glsl", fragment = path .. "fragment.glsl"})
+       gl.delete_shaders(vsh, fs)
+       self.shaders = program
+       
+       self.view_matix_location = gl.get_uniform_location(self.shaders, "view")
+       self.projection_matix_location = gl.get_uniform_location(self.shaders, "projection")
+end
diff --git a/modules/RenderEngine/src/textures.lua b/modules/RenderEngine/src/textures.lua
new file mode 100644 (file)
index 0000000..847c9d2
--- /dev/null
@@ -0,0 +1,32 @@
+function RenderEngine:init_texture_args()
+       local base_filter = (self.bilinear_filter and "linear" or "nearest")
+       local mipmap = (self.mipmap and " mipmap nearest" or "")
+       self.texture_min_filter = base_filter .. mipmap
+       self.texture_mag_filter = base_filter
+end
+
+function RenderEngine:create_texture(path)
+       local texture = gl.gen_textures(1)
+
+       gl.bind_texture("2d", texture)
+       gl.texture_parameter("2d", "min filter", self.texture_min_filter)
+       gl.texture_parameter("2d", "mag filter", self.texture_mag_filter)
+       gl.texture_parameter("2d", "wrap s", "repeat")
+       gl.texture_parameter("2d", "wrap t", "repeat")
+       
+       local data, width, height, channels = image.load(path)
+       
+       if not data then
+               error("Failed to load texture '" .. path .. "'")
+       end
+       
+       gl.texture_image("2d", 0, "rgb", "rgba", "ubyte", data, width, height)
+       if self.mipmap then
+               gl.generate_mipmap("2d")
+       end
+       
+       gl.unbind_texture("2d")
+       data, width, height, channels = nil
+       
+       return texture
+end
diff --git a/modules/RenderEngine/src/window.lua b/modules/RenderEngine/src/window.lua
new file mode 100644 (file)
index 0000000..88bfc9c
--- /dev/null
@@ -0,0 +1,34 @@
+function RenderEngine:framebuffer_size_callback(_, width, height)
+       gl.viewport(0, 0, width, height)
+       self:update_window_size(width, height)
+end
+
+function RenderEngine:init_glfw()
+       glfw.window_hint("context version major", 3)
+       glfw.window_hint("context version minor", 3)
+       glfw.window_hint("opengl profile", "core")
+end
+
+function RenderEngine:create_window()
+       self.window = glfw.create_window(500, 500, "Unnamed Window")
+       glfw.make_context_current(self.window)
+       self:update_window_size()
+       glfw.set_framebuffer_size_callback(self.window, function (...) self:framebuffer_size_callback(...) end) 
+end
+
+function RenderEngine:set_window_title(title)
+       glfw.set_window_title(self.window, title)
+end
+
+function RenderEngine:set_window_pos(x, y)
+       glfw.set_window_pos(self.window, x, y)
+end
+
+function RenderEngine:set_window_size(width, height)
+       glfw.set_window_size(self.window, width, height)
+       self:update_window_size(width, height)
+end
+
+function RenderEngine:update_window_size(width, height)
+       self.window_width, self.window_height = width, height
+end
diff --git a/modules/WorldSystem/dependencies.txt b/modules/WorldSystem/dependencies.txt
new file mode 100644 (file)
index 0000000..06776e7
--- /dev/null
@@ -0,0 +1 @@
+MapGen
diff --git a/modules/WorldSystem/src/block.lua b/modules/WorldSystem/src/block.lua
new file mode 100644 (file)
index 0000000..2c67a27
--- /dev/null
@@ -0,0 +1,7 @@
+local Block = Dragonblocks.create_class()
+
+function Block:constructor(def, pos)
+       self.def, self.pos = def, pos
+end
+
+return Block
diff --git a/modules/WorldSystem/src/chunk.lua b/modules/WorldSystem/src/chunk.lua
new file mode 100644 (file)
index 0000000..1d68249
--- /dev/null
@@ -0,0 +1,31 @@
+local Chunk = Dragonblocks.create_class()
+
+local size = 16
+local size_squared = math.pow(size, 2)
+
+function Chunk:constructor()
+       self.blocks = {}
+       MapGen:generate(self)
+       if Client then
+               Client.graphics:create_chunk_meshes(self)
+       end
+end
+
+function Chunk:get_pos_hash(pos)
+       return pos.x + size * pos.y + size_squared * pos.z
+end
+
+function Chunk:add_block(pos, def)
+       local block = WorldSystem.Block(def, pos)
+       self.blocks[self:get_pos_hash(pos)] = block
+end
+
+function Chunk:remove_block(pos)
+       self.blocks[self:get_pos_hash(pos)] = nil
+end
+
+function Chunk:get_block(pos)
+       return self.blocks[self:get_pos_hash(pos)]
+end
+
+return Chunk
diff --git a/modules/WorldSystem/src/init.lua b/modules/WorldSystem/src/init.lua
new file mode 100644 (file)
index 0000000..d3ffdcc
--- /dev/null
@@ -0,0 +1,4 @@
+WorldSystem.World = WorldSystem:run("world")
+WorldSystem.Map = WorldSystem:run("map")
+WorldSystem.Chunk = WorldSystem:run("chunk")
+WorldSystem.Block = WorldSystem:run("block")
diff --git a/modules/WorldSystem/src/map.lua b/modules/WorldSystem/src/map.lua
new file mode 100644 (file)
index 0000000..44d7490
--- /dev/null
@@ -0,0 +1,7 @@
+local Map = Dragonblocks.create_class()
+
+function Map:constructor()
+       self.chunk = WorldSystem.Chunk()
+end
+
+return Map
diff --git a/modules/WorldSystem/src/world.lua b/modules/WorldSystem/src/world.lua
new file mode 100644 (file)
index 0000000..6bad913
--- /dev/null
@@ -0,0 +1,7 @@
+local World = Dragonblocks.create_class()
+
+function World:constructor()
+       self.map = WorldSystem.Map()
+end
+
+return World
index f424d733bca6aa9b422722e6a2f854ea18c6eec3..5b2c20fb7bf7be7859d9d685eabea9b4e0efa506 100644 (file)
@@ -1,21 +1,14 @@
-local class = {}
-
-local instance_metatable = {
-       __index = function(t, k)
-               if k == "_call" then return end
-               local f = rawget(t._class, k)
-               if type(f) == "function" then
-                       return f
+function Dragonblocks.create_class()
+       local class = self or {}
+       setmetatable(class, {
+               __call = function(_, ...)
+                       local o = {}
+                       setmetatable(o, {__index = class})
+                       if o.constructor then
+                               o:constructor(...)
+                       end
+                       return o
                end
-       end
-}
-
-function class:_call(...)
-       local o = {class = self}
-       setmetatable(o, instance_metatable)
-       if o.constructor then
-               o:constructor(table.unpack(...))
-       end
-end 
-
-return class
+       })
+       return class
+end
diff --git a/src/event_interface.lua b/src/event_interface.lua
deleted file mode 100644 (file)
index cb23a2d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-local event_interface = {}
-
-function event_interface:init()
-       assert(self._task_manager)
-       self:clear_event_listeners()
-end
-
-function event_interface:fire_event(event, callback)
-       event = event or {}
-       event.origin = self
-       local listeners = self._event_listeners[eventtype]
-       if listeners then
-               self._task_manager:add_task(function()
-                       for _, listener in ipairs(listeners) do
-                               listener(event)
-                               coroutine.yield()
-                       end
-                       callback(event)
-               end)
-       end
-end
-
-function event_interface:add_event_listener(eventtype, eventlistener)
-       self._event_listeners[eventtype] = self._event_listeners[eventtype] or {}
-       table.insert(self._event_listeners[eventtype], eventlistener)
-end
-       
-function event_interface:remove_event_listener(eventtype, eventlistener)
-       local listeners = self._event_listeners[eventtype]
-       if listeners then
-               for k, listener in ipairs(listeners) do
-                       if listener == eventlistener then
-                               table.remove(k)
-                               return self:removeEventListener(eventtype, eventlistener)
-                       end
-               end
-       end
-end
-
-function event_interface:clear_event_listeners()
-       self._event_listeners = {}
-end
-
-return event_interface
diff --git a/src/events.lua b/src/events.lua
new file mode 100644 (file)
index 0000000..312a9b3
--- /dev/null
@@ -0,0 +1,43 @@
+local events = {}
+
+function events:fire_event(event, callback)
+       event = event or {}
+       event.origin = self
+       local listeners = self._event_listeners[eventtype]
+       if listeners and #listeners > 0 then
+               for _, listener in ipairs(listeners) do
+                       listener(event)
+               end
+       end
+       if callback then
+               callback(event)
+       end
+end
+
+function events:add_event_listener(eventtype, eventlistener)
+       self._event_listeners[eventtype] = self._event_listeners[eventtype] or {}
+       table.insert(self._event_listeners[eventtype], eventlistener)
+end
+       
+function events:remove_event_listener(eventtype, eventlistener)
+       local listeners = self._event_listeners[eventtype]
+       if listeners then
+               for k, listener in ipairs(listeners) do
+                       if listener == eventlistener then
+                               table.remove(k)
+                               return self:removeEventListener(eventtype, eventlistener)
+                       end
+               end
+       end
+end
+
+function events:clear_event_listeners()
+       self._event_listeners = {}
+end
+
+function Dragonblocks:create_event_interface()
+       self = self or {}
+       table.assign(self, events)
+       self:clear_event_listeners()
+       return self
+end
index a036f98d083346ed66bd70068989d7e303b11318..b94e1516a47e064cccfddd9680ad8f93a5e6cc75 100644 (file)
@@ -1,13 +1,13 @@
-Dragonblocks.event_interface = require("src/event_interface")
-Dragonblocks.class = require("src/class")
-Dragonblocks.task_manager = require("src/task_manager")
-Dragonblocks.module_manager = require("src/module_manager")
-Dragonblocks.serializer = require("src/serializer")
+Dragonblocks = {}
 
-Dragonblocks:add_proto(Dragonblocks.module_manager)
-Dragonblocks:add_proto(Dragonblocks.task_manager)
-Dragonblocks:add_proto(Dragonblocks.serializer)
-
-Dragonblocks:register_event_interface(Dragonblocks)
+require("src/class")
+require("src/events")
+require("src/taskmgr")
+require("src/modulemgr")
+require("src/serialisation")
 
 print("Started Dragonblocks core")
+
+Dragonblocks:read_modules()
+Dragonblocks:start_module(arg[1])
+Dragonblocks:start_tasks()
diff --git a/src/module_manager.lua b/src/module_manager.lua
deleted file mode 100644 (file)
index dee4720..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-local module_ref = {}
-
-function module_ref:preinit()
-       self._dependencies = {}
-       self._started = false
-       local depfile = io.open(self._path .. "/dependencies.txt")
-       if depfile then
-               local data = depfile:read()
-               depfile:close()
-               self._dependencies = data:split("\n")
-       end
-       
-end
-
-function module_ref:init()
-       self._started = true
-end
-
-function module_ref:run_script(s)
-       return require(self._path .. "src/" .. s)
-end
-
-function module_ref:start()
-       _G[self._name] = self
-       self:run_script("init")
-       print("Started module " .. self._name)
-end
-
-function module_ref:get_path()
-       return self._path
-end
-
-function module_ref:get_data_path()
-       local p = self._data_path
-       if not lfs.attributes(p, "mode") then
-               lfs.mkdir(p)
-       end
-end
-
-local module_manager = {}
-
-module_manager.module_path = "modules/"
-module_manager.data_path = "data/"
-
-function module_manager:init()
-       if not lfs.attributes(self.data_path, "mode") then
-               lfs.mkdir(self.data_path)
-       end
-       self._modules = {}
-       for modulename in lfs.dir(self.module_path) do
-               if modulename:sub(1, 1) ~= "." then
-                       local m = ObjectMgr.create()
-                       m._name = modulename
-                       m._path = self.module_path .. modulename .. "/"
-                       m._data_path = self.data_path .. modulename .. "/"
-                       m:add_proto(module_ref)
-                       m:preinit()
-                       self._modules[modulename] = m
-               end
-       end
-end
-
-function module_manager:start_module(name)
-       local m = self._modules[name]
-       if not m then
-               error("Module '" .. name .. "' not found.")
-       elseif m._started then
-               return
-       end
-       for _, dep in ipairs(m._dependencies) do
-               self:start_module(dep)
-       end
-       m:start()
-end
-
-return module_manager
diff --git a/src/modulemgr.lua b/src/modulemgr.lua
new file mode 100644 (file)
index 0000000..7aaa1cf
--- /dev/null
@@ -0,0 +1,64 @@
+local Module = Dragonblocks.create_class()
+
+function Module:constructor(name)
+       self._name = name
+       self._dependencies = {}
+       self._started = false
+       local depfile = io.open(self:get_path() .. "/dependencies.txt")
+       if depfile then
+               local data = depfile:read("*a")
+               depfile:close()
+               self._dependencies = data:split("\n")
+       end
+end
+
+function Module:run(s)
+       return require(self:get_path() .. "/src/" .. s)
+end
+
+function Module:start()
+       _G[self._name] = self
+       self:run("init")
+       self._started = true
+       print("Started module " .. self._name)
+end
+
+function Module:get_path()
+       return "modules/" .. self._name 
+end
+
+function Module:get_data_path()
+       local p = "data/" .. self._name
+       if not lfs.attributes(p, "mode") then
+               lfs.mkdir(p)
+       end
+       return p
+end
+
+function Dragonblocks:read_modules()
+       if not lfs.attributes("data", "mode") then
+               lfs.mkdir(self.data_path)
+       end
+       self.modules = {}
+       for modulename in lfs.dir("modules") do
+               if modulename:sub(1, 1) ~= "." then
+                       local m = Module(modulename)
+                       self.modules[modulename] = m
+               end
+       end
+end
+
+function Dragonblocks:start_module(name)
+       local m = self.modules[name]
+       if not m then
+               error("Module '" .. name .. "' not found.")
+       elseif m._started then
+               return
+       end
+       for _, dep in ipairs(m._dependencies) do
+               self:start_module(dep)
+       end
+       m:start()
+end
+
+return module_manager
diff --git a/src/serialisation.lua b/src/serialisation.lua
new file mode 100644 (file)
index 0000000..a1877ed
--- /dev/null
@@ -0,0 +1,48 @@
+function Dragonblocks:serialize()
+       local data = "{"
+       for k, v in pairs(self) do
+               local kdata, vdata
+               local ktype, vtype = type(k), type(v)
+               local serialize_pair = true
+               if ktype == "number" then
+                       kdata = "[" .. k .. "]"
+               elseif ktype == "string" then
+                       if k:sub(1, 1) == "_" then
+                               serialize_pair = false
+                       else
+                               kdata = "[\"" .. k .. "\"]"
+                       end
+               else
+                       serialize_pair = false
+               end
+               if vtype == "table" then
+                       vdata = serializer.serialize(v)
+               elseif vtype == "string" then
+                       vdata = "\"" .. v .. "\""
+               elseif vtype == "number" then
+                       vdata = v
+               elseif vtype == "boolean" then
+                       vdata = v and "true" or "false"
+               else
+                       serialize_pair = false
+               end
+               if serialize_pair then
+                       data = data .. kdata .. "=" .. vdata .. ","
+               end
+       end
+       return data .. "}"
+end
+
+function Dragonblocks:deserialize(raw)
+       if not raw then
+               raw = self
+               self = {}
+       end
+       raw = "return" .. raw
+       local f = loadstring(raw)
+       local data = f and f()
+       if type(data) == "table" then
+               table.assign(self, raw)
+       end
+       return self
+end
diff --git a/src/serializer.lua b/src/serializer.lua
deleted file mode 100644 (file)
index 582e927..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-local serializer = {}
-
-function serializer:serialize()
-       local data = "{"
-       for k, v in pairs(self) do
-               local kdata, vdata
-               local ktype, vtype = type(k), type(v)
-               local serialize_pair = true
-               if ktype == "number" then
-                       kdata = "[" .. k .. "]"
-               elseif ktype == "string" then
-                       if k:sub(1, 1) == "_" then
-                               serialize_pair = false
-                       else
-                               kdata = "[\"" .. k .. "\"]"
-                       end
-               else
-                       serialize_pair = false
-               end
-               if vtype == "table" then
-                       vdata = serializer.serialize(v)
-               elseif vtype == "string" then
-                       vdata = "\"" .. v .. "\""
-               elseif vtype == "number" then
-                       vdata = v
-               elseif vtype == "boolean" then
-                       vdata = v and "true" or "false"
-               else
-                       serialize_pair = false
-               end
-               if serialize_pair then
-                       data = data .. kdata .. "=" .. vdata .. ","
-               end
-       end
-       return data .. "}"
-end
-
-function serializer:deserialize(raw)
-       raw = "return" .. (raw or "")
-       local f = loadstring(raw)
-       local data = f and f()
-       if type(data) == "table" then
-               for k, v in pairs(data) do
-                       self[k] = v
-               end
-       end
-end
-
-return serializer
diff --git a/src/task_manager.lua b/src/task_manager.lua
deleted file mode 100644 (file)
index 23a4ead..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-local task_manager = {}
-
-function task_manager:init()
-       self._tasks = {}
-end
-
-function task_manager:add_task(f)
-       local t = coroutine.create(f)
-       table.insert(self._tasks, t)
-       return t
-end
-
-function task_manager:step()
-       local t_start = socket.gettime()
-       local tasks = self._tasks
-       self._tasks = {}
-       for _, t in ipairs(tasks) do
-               if coroutine.resume(t) then
-                       table.insert(self._tasks, t)
-               end
-       end
-       self.tps = 1 / (socket.gettime() - t_start)
-end
-
-function task_manager:start_tasks()
-       repeat self:step()
-       until #self._tasks == 0
-end
-
-function task_manager:register_event_interface(e)
-       e._task_manager = self
-end
-
-return task_manager
diff --git a/src/taskmgr.lua b/src/taskmgr.lua
new file mode 100644 (file)
index 0000000..7792718
--- /dev/null
@@ -0,0 +1,24 @@
+Dragonblocks.tasks = {}
+
+function Dragonblocks:add_task(f)
+       local t = coroutine.create(f)
+       table.insert(self.tasks, t)
+       return t
+end
+
+function Dragonblocks:step()
+       local t_start = socket.gettime()
+       local tasks = self.tasks
+       self.tasks = {}
+       for _, t in ipairs(tasks) do
+               if coroutine.resume(t) then
+                       table.insert(self.tasks, t)
+               end
+       end
+       self.tps = 1 / (socket.gettime() - t_start)
+end
+
+function Dragonblocks:start_tasks()
+       repeat self:step()
+       until #self.tasks == 0
+end
diff --git a/util/hex2rgb.lua b/util/hex2rgb.lua
new file mode 100644 (file)
index 0000000..15072fb
--- /dev/null
@@ -0,0 +1,8 @@
+return function(hex)
+    local hex = hex:gsub("#","")
+    if hex:len() == 3 then
+      return (tonumber("0x"..hex:sub(1,1))*17)/255, (tonumber("0x"..hex:sub(2,2))*17)/255, (tonumber("0x"..hex:sub(3,3))*17)/255
+    else
+      return tonumber("0x"..hex:sub(1,2))/255, tonumber("0x"..hex:sub(3,4))/255, tonumber("0x"..hex:sub(5,6))/255
+    end
+end 
diff --git a/util/indexof.lua b/util/indexof.lua
deleted file mode 100644 (file)
index 24fbead..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-function table.indexof(list, val)
-       for i, v in ipairs(list) do
-               if v == val then
-                       return i
-               end
-       end
-       return -1
-end 
diff --git a/util/objectmgr.lua b/util/objectmgr.lua
deleted file mode 100644 (file)
index 3140915..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-local ObjectRef = {}
-
-function ObjectRef:init()
-       for _, p in ipairs(self._proto) do
-               if p ~= ObjectRef and p.init then
-                       p.init(self)
-               end
-       end
-end
-
-function ObjectRef:add_proto(p)
-       table.insert(self._proto, p)
-end
-
-ObjectMgr = {}
-
-ObjectMgr.metatable = {
-       __index = function(t, k)
-               for _, p in ipairs(t._proto) do
-                       local v = p[k]
-                       if v then
-                               return v
-                       end
-               end
-       end,
-       __call = function(t, ...)
-               return t:_call()
-       end,
-       __tostring = function(t)
-               return t.serialize and t:serialize() or "<not serializable>"
-       end,
-}
-
-function ObjectMgr.create()
-       local o = {}
-       o._proto = {ObjectRef}
-       setmetatable(o, ObjectMgr.metatable)
-       return o
-end
diff --git a/util/split.lua b/util/split.lua
deleted file mode 100644 (file)
index f0bc8af..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
-       delim = delim or ","
-       max_splits = max_splits or -2
-       local items = {}
-       local pos, len = 1, #str
-       local plain = not sep_is_pattern
-       max_splits = max_splits + 1
-       repeat
-               local np, npe = string.find(str, delim, pos, plain)
-               np, npe = (np or (len+1)), (npe or (len+1))
-               if (not np) or (max_splits == 1) then
-                       np = len + 1
-                       npe = np
-               end
-               local s = string.sub(str, pos, np - 1)
-               if include_empty or (s ~= "") then
-                       max_splits = max_splits - 1
-                       items[#items + 1] = s
-               end
-               pos = npe + 1
-       until (max_splits == 0) or (pos > (len + 1))
-       return items
-end 
diff --git a/util/string_split.lua b/util/string_split.lua
new file mode 100644 (file)
index 0000000..fb6dd7d
--- /dev/null
@@ -0,0 +1,23 @@
+return function(str, delim, include_empty, max_splits, sep_is_pattern)
+       delim = delim or ","
+       max_splits = max_splits or -2
+       local items = {}
+       local pos, len = 1, #str
+       local plain = not sep_is_pattern
+       max_splits = max_splits + 1
+       repeat
+               local np, npe = string.find(str, delim, pos, plain)
+               np, npe = (np or (len+1)), (npe or (len+1))
+               if (not np) or (max_splits == 1) then
+                       np = len + 1
+                       npe = np
+               end
+               local s = string.sub(str, pos, np - 1)
+               if include_empty or (s ~= "") then
+                       max_splits = max_splits - 1
+                       items[#items + 1] = s
+               end
+               pos = npe + 1
+       until (max_splits == 0) or (pos > (len + 1))
+       return items
+end 
diff --git a/util/table_assign.lua b/util/table_assign.lua
new file mode 100644 (file)
index 0000000..f14a8f5
--- /dev/null
@@ -0,0 +1,5 @@
+return function(t, o)
+       for k, v in pairs(o) do
+               t[k] = v
+       end
+end 
diff --git a/util/table_indexof.lua b/util/table_indexof.lua
new file mode 100644 (file)
index 0000000..ee9410a
--- /dev/null
@@ -0,0 +1,8 @@
+return function(list, val)
+       for i, v in ipairs(list) do
+               if v == val then
+                       return i
+               end
+       end
+       return -1
+end