--- /dev/null
+/client/
+/server/
--- /dev/null
+[submodule "moongl"]
+ path = deps/moongl
+ url = https://github.com/stetre/moongl
+[submodule "moonglfw"]
+ path = deps/moonglfw
+ url = https://github.com/stetre/moonglfw
--- /dev/null
+depends:
+$ sudo apt install lua5.3 libsqlite3
+$ luarocks install lsqlite3
+$ luarocks install luasocket
+$ luarocks install luafilesystem
+
+Build moongl and moonglfw: See build README.md in deps/moongl and deps/moonglfw
--- /dev/null
+#! /usr/bin/env lua
+require("lfs")
+require("socket")
+require("lsqlite3")
+require("moongl")
+require("moonglfw")
+require("util/objectmgr")
+require("util/split")
+require("util/indexof")
+
+Dragonblocks = ObjectMgr.create()
+
+require("src/init")
+
+Dragonblocks:init()
+
+Dragonblocks:start_module(arg[1] or "Menu")
--- /dev/null
+print("Success!")
+Menu:add_proto(Dragonblocks.serializer)
+Menu:init()
+
--- /dev/null
+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
+ 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
--- /dev/null
+local event_interface = {}
+
+function event_interface:init()
+ self:clear_event_listeners()
+end
+
+function event_interface:fire_event(eventtype, event)
+ event = event or {}
+ event.type = eventtype
+ event.origin = self
+ local listeners = self._event_listeners[eventtype]
+ if listeners then
+ for _, listener in ipairs(listeners) do
+ listener(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
--- /dev/null
+Dragonblocks.event_interface = require("src/event_interface")
+Dragonblocks.class = require("src/class")
+Dragonblocks.module_manager = require("src/module_manager")
+Dragonblocks.serializer = require("src/serializer")
+
+Dragonblocks:add_proto(Dragonblocks.module_manager)
+Dragonblocks:add_proto(Dragonblocks.serializer)
--- /dev/null
+local module_ref = {}
+
+function module_ref:preinit()
+ self._dependencies = {}
+ self._started = false
+ local depfile = io.open(self._path .. "/.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:start()
+ _G[self._name] = self
+ require(self._path .. "src/init")
+end
+
+local module_manager = {}
+
+module_manager.module_path = "modules/"
+
+function module_manager:init()
+ 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: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("Failed to start module '" .. name .. "'.")
+ elseif m._started then
+ return
+ end
+ for _, dep in ipairs(m._dependencies) do
+ self:start_module(dep)
+ end
+ m:start()
+end
+
+return module_manager
--- /dev/null
+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
--- /dev/null
+function table.indexof(list, val)
+ for i, v in ipairs(list) do
+ if v == val then
+ return i
+ end
+ end
+ return -1
+end
--- /dev/null
+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
--- /dev/null
+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