Introduce Lua scripting, basic block registration, tiled textures

master
Dorian Wouters 2016-08-06 15:44:57 +02:00
parent f4620436d9
commit 28d5b18448
No known key found for this signature in database
GPG Key ID: 6E9DA8063322434B
34 changed files with 1385 additions and 193 deletions

3
.gitmodules vendored
View File

@ -7,3 +7,6 @@
[submodule "assets/lua/lds"]
path = assets/lua/lds
url = https://github.com/neomantra/lds.git
[submodule "ext/StackTracePlus"]
path = ext/StackTracePlus
url = https://github.com/ignacio/StackTracePlus.git

View File

@ -97,7 +97,13 @@ target_link_libraries(diggler
${OPENAL_LIBRARY}
pthread)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${PATH_TO_TOPDIR}/assets" "${PROJECT_BINARY_DIR}/assets")
add_custom_command(TARGET diggler PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink "${PATH_TO_TOPDIR}/assets"
"${PROJECT_BINARY_DIR}/assets")
add_custom_command(TARGET diggler PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_SOURCE_DIR}/src/scripting/lua/api"
"${PROJECT_BINARY_DIR}/assets/lua/ffi")
#install(TARGETS digglerz RUNTIME DESTINATION bin)

View File

@ -1,19 +1,41 @@
require('io')
local rtpath = digglerNative.gameLuaRuntimePath
package.path = package.path .. ';' .. rtpath .. '/?.lua;' .. rtpath .. '/lds/?.lua'
local diggler = {
ffi = require('ffi')
io = require('io')
debug = require('debug')
local STP = require "StackTracePlus"
debug.traceback = STP.stacktrace
diggler = {
mods = {},
modOverlays = {},
modsById = {},
publicEnv = {},
exportedFuncs = {}
}
ffi.cdef[[
struct Diggler_Game;
union PtrConvert {
void *ptr;
char str[sizeof(void*)];
};
]]
do
local cvt = ffi.new('union PtrConvert', {str = digglerNative.gameInstancePtrStr})
diggler.gameInstance = ffi.cast('struct Diggler_Game*', cvt.ptr)
end
function diggler.export(name, func)
diggler.exportedFuncs[name] = func
end
package.loaded['diggler'] = diggler
package.loaded['lds'] = require('lds/lds')
package.loaded['lds'] = require('lds')
local function setoverlay(tab, orig)
local mt = getmetatable(tab) or {}
@ -42,22 +64,64 @@ local function trim(s)
return match(s,'^()%s*$') and '' or match(s,'^%s*(.*%S)')
end
diggler.modGlobalOverrides = {
collectgarbage = function(opt, ...)
if opt == 'count' or opt == 'collect' then
return collectgarbage(opt)
end
end
}
function diggler.loadModLua(path)
local digglerOverlay = {}
local packageOverlay = { ['path'] = path .. '/?.lua;' .. package.path }
setoverlay(packageOverlay, package)
setoverlay(digglerOverlay, diggler.publicEnv)
local env = {
['package'] = packageOverlay,
['print'] = function (...)
package = packageOverlay,
diggler = digglerOverlay,
print = function (...)
print("<init " .. path .. ">", ...)
end,
['require'] = function (module)
CurrentModPath = path
}
for k, v in pairs({
require = function (module)
if module == 'diggler' then
return digglerOverlay
end
return require(module)
end,
dofile = function (name)
local f, e = loadfile(name)
if not f then error(e, 2) end
setfenv(f, env)
return f()
end,
loadfile = function (name)
if name == nil then
return
end
local f, e = loadfile(name)
if f then
setfenv(f, env)
end
return f, e
end,
loadstring = function (string, chunkname)
local f = loadstring(string, chunkname)
if f then
setfenv(f, env)
end
return f
end
}
}) do
env[k] = v
end
for k, v in pairs(diggler.modGlobalOverrides) do
env[k] = v
end
env['_G'] = env
setoverlay(env, _G)
local file = io.open(path .. '/mod.lua', "r")
@ -177,12 +241,14 @@ function diggler.getModByUuid(mod, uuid)
end
diggler.export("getMod", diggler.getMod)
--[[
function diggler.registerBlock(mod, name, block)
print("Calling registerBlock from mod " .. (mod and mod.id or "<none>"))
end
diggler.export("registerBlock", diggler.registerBlock)
]]
dofile(rtpath .. '/api/io.lua')
dofile(rtpath .. '/api/registerBlock.lua')
local m = diggler.loadMod('TestMod')
local m = diggler.loadMod('/media/source/Diggler/mods/TestMod')
diggler.initMod(m)

View File

@ -0,0 +1,415 @@
-- tables
local _G = _G
local string, io, debug, coroutine = string, io, debug, coroutine
-- functions
local tostring, print, require = tostring, print, require
local next, assert = next, assert
local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs
local error = error
assert(debug, "debug table must be available at this point")
local io_open = io.open
local string_gmatch = string.gmatch
local string_sub = string.sub
local table_concat = table.concat
local _M = {
max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)'
}
-- this tables should be weak so the elements in them won't become uncollectable
local m_known_tables = { [_G] = "_G (global table)" }
local function add_known_module(name, desc)
local ok, mod = pcall(require, name)
if ok then
m_known_tables[mod] = desc
end
end
add_known_module("string", "string module")
add_known_module("io", "io module")
add_known_module("os", "os module")
add_known_module("table", "table module")
add_known_module("math", "math module")
add_known_module("package", "package module")
add_known_module("debug", "debug module")
add_known_module("coroutine", "coroutine module")
-- lua5.2
add_known_module("bit32", "bit32 module")
-- luajit
add_known_module("bit", "bit module")
add_known_module("jit", "jit module")
-- lua5.3
if _VERSION >= "Lua 5.3" then
add_known_module("utf8", "utf8 module")
end
local m_user_known_tables = {}
local m_known_functions = {}
for _, name in ipairs{
-- Lua 5.2, 5.1
"assert",
"collectgarbage",
"dofile",
"error",
"getmetatable",
"ipairs",
"load",
"loadfile",
"next",
"pairs",
"pcall",
"print",
"rawequal",
"rawget",
"rawlen",
"rawset",
"require",
"select",
"setmetatable",
"tonumber",
"tostring",
"type",
"xpcall",
-- Lua 5.1
"gcinfo",
"getfenv",
"loadstring",
"module",
"newproxy",
"setfenv",
"unpack",
-- TODO: add table.* etc functions
} do
if _G[name] then
m_known_functions[_G[name]] = name
end
end
local m_user_known_functions = {}
local function safe_tostring (value)
local ok, err = pcall(tostring, value)
if ok then return err else return ("<failed to get printable value>: '%s'"):format(err) end
end
-- Private:
-- Parses a line, looking for possible function definitions (in a very naïve way)
-- Returns '(anonymous)' if no function name was found in the line
local function ParseLine(line)
assert(type(line) == "string")
--print(line)
local match = line:match("^%s*function%s+(%w+)")
if match then
--print("+++++++++++++function", match)
return match
end
match = line:match("^%s*local%s+function%s+(%w+)")
if match then
--print("++++++++++++local", match)
return match
end
match = line:match("^%s*local%s+(%w+)%s+=%s+function")
if match then
--print("++++++++++++local func", match)
return match
end
match = line:match("%s*function%s*%(") -- this is an anonymous function
if match then
--print("+++++++++++++function2", match)
return "(anonymous)"
end
return "(anonymous)"
end
-- Private:
-- Tries to guess a function's name when the debug info structure does not have it.
-- It parses either the file or the string where the function is defined.
-- Returns '?' if the line where the function is defined is not found
local function GuessFunctionName(info)
--print("guessing function name")
if type(info.source) == "string" and info.source:sub(1,1) == "@" then
local file, err = io_open(info.source:sub(2), "r")
if not file then
print("file not found: "..tostring(err)) -- whoops!
return "?"
end
local line
for _ = 1, info.linedefined do
line = file:read("*l")
end
if not line then
print("line not found") -- whoops!
return "?"
end
return ParseLine(line)
else
local line
local lineNumber = 0
for l in string_gmatch(info.source, "([^\n]+)\n-") do
lineNumber = lineNumber + 1
if lineNumber == info.linedefined then
line = l
break
end
end
if not line then
print("line not found") -- whoops!
return "?"
end
return ParseLine(line)
end
end
---
-- Dumper instances are used to analyze stacks and collect its information.
--
local Dumper = {}
Dumper.new = function(thread)
local t = { lines = {} }
for k,v in pairs(Dumper) do t[k] = v end
t.dumping_same_thread = (thread == coroutine.running())
-- if a thread was supplied, bind it to debug.info and debug.get
-- we also need to skip this additional level we are introducing in the callstack (only if we are running
-- in the same thread we're inspecting)
if type(thread) == "thread" then
t.getinfo = function(level, what)
if t.dumping_same_thread and type(level) == "number" then
level = level + 1
end
return debug.getinfo(thread, level, what)
end
t.getlocal = function(level, loc)
if t.dumping_same_thread then
level = level + 1
end
return debug.getlocal(thread, level, loc)
end
else
t.getinfo = debug.getinfo
t.getlocal = debug.getlocal
end
return t
end
-- helpers for collecting strings to be used when assembling the final trace
function Dumper:add (text)
self.lines[#self.lines + 1] = text
end
function Dumper:add_f (fmt, ...)
self:add(fmt:format(...))
end
function Dumper:concat_lines ()
return table_concat(self.lines)
end
---
-- Private:
-- Iterates over the local variables of a given function.
--
-- @param level The stack level where the function is.
--
function Dumper:DumpLocals (level)
local prefix = "\t "
local i = 1
if self.dumping_same_thread then
level = level + 1
end
local name, value = self.getlocal(level, i)
if not name then
return
end
self:add("\tLocal variables:\r\n")
while name do
if type(value) == "number" then
self:add_f("%s%s = number: %g\r\n", prefix, name, value)
elseif type(value) == "boolean" then
self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value))
elseif type(value) == "string" then
self:add_f("%s%s = string: %q\r\n", prefix, name, value)
elseif type(value) == "userdata" then
self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value))
elseif type(value) == "nil" then
self:add_f("%s%s = nil\r\n", prefix, name)
elseif type(value) == "table" then
if m_known_tables[value] then
self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value])
elseif m_user_known_tables[value] then
self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value])
else
local txt = "{"
for k,v in pairs(value) do
txt = txt..safe_tostring(k)..":"..safe_tostring(v)
if #txt > _M.max_tb_output_len then
txt = txt.." (more...)"
break
end
if next(value, k) then txt = txt..", " end
end
self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}")
end
elseif type(value) == "function" then
local info = self.getinfo(value, "nS")
local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value]
if info.what == "C" then
self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value)))
else
local source = info.short_src
if source:sub(2,7) == "string" then
source = source:sub(9) -- uno más, por el espacio que viene (string "Baragent.Main", por ejemplo)
end
--for k,v in pairs(info) do print(k,v) end
fun_name = fun_name or GuessFunctionName(info)
self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source)
end
elseif type(value) == "thread" then
self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value))
end
i = i + 1
name, value = self.getlocal(level, i)
end
end
---
-- Public:
-- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc.
-- This function is suitable to be used as an error handler with pcall or xpcall
--
-- @param thread An optional thread whose stack is to be inspected (defaul is the current thread)
-- @param message An optional error string or object.
-- @param level An optional number telling at which level to start the traceback (default is 1)
--
-- Returns a string with the stack trace and a string with the original error.
--
function _M.stacktrace(thread, message, level)
if type(thread) ~= "thread" then
-- shift parameters left
thread, message, level = nil, thread, message
end
thread = thread or coroutine.running()
level = level or 1
local dumper = Dumper.new(thread)
local original_error
if type(message) == "table" then
dumper:add("an error object {\r\n")
local first = true
for k,v in pairs(message) do
if first then
dumper:add(" ")
first = false
else
dumper:add(",\r\n ")
end
dumper:add(safe_tostring(k))
dumper:add(": ")
dumper:add(safe_tostring(v))
end
dumper:add("\r\n}")
original_error = dumper:concat_lines()
elseif type(message) == "string" then
dumper:add(message)
original_error = message
end
dumper:add("\r\n")
dumper:add[[
Stack Traceback
===============
]]
--print(error_message)
local level_to_show = level
if dumper.dumping_same_thread then level = level + 1 end
local info = dumper.getinfo(level, "nSlf")
while info do
if info.what == "main" then
if string_sub(info.source, 1, 1) == "@" then
dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, string_sub(info.source, 2), info.currentline)
else
dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.short_src, info.currentline)
end
elseif info.what == "C" then
--print(info.namewhat, info.name)
--for k,v in pairs(info) do print(k,v, type(v)) end
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func)
dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name)
--dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value)))
elseif info.what == "tail" then
--print("tail")
--for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name)
dumper:add_f("(%d) tail call\r\n", level_to_show)
dumper:DumpLocals(level)
elseif info.what == "Lua" then
local source = info.short_src
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name
if source:sub(2, 7) == "string" then
source = source:sub(9)
end
local was_guessed = false
if not function_name or function_name == "?" then
--for k,v in pairs(info) do print(k,v, type(v)) end
function_name = GuessFunctionName(info)
was_guessed = true
end
-- test if we have a file name
local function_type = (info.namewhat == "") and "function" or info.namewhat
if info.source and info.source:sub(1, 1) == "@" then
dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
elseif info.source and info.source:sub(1,1) == '#' then
dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
else
dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, function_name, info.currentline, source)
end
dumper:DumpLocals(level)
else
dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what)
end
level = level + 1
level_to_show = level_to_show + 1
info = dumper.getinfo(level, "nSlf")
end
return dumper:concat_lines(), original_error
end
--
-- Adds a table to the list of known tables
function _M.add_known_table(tab, description)
if m_known_tables[tab] then
error("Cannot override an already known table")
end
m_user_known_tables[tab] = description
end
--
-- Adds a function to the list of known functions
function _M.add_known_function(fun, description)
if m_known_functions[fun] then
error("Cannot override an already known function")
end
m_user_known_functions[fun] = description
end
return _M

9
assets/lua/api/io.lua Normal file
View File

@ -0,0 +1,9 @@
diggler.io = {}
diggler.io.readFile = function(mod, path)
local file = io.open(path, 'rb')
if not file then return nil end
local content = file:read('*a')
file:close()
return content
end

View File

@ -0,0 +1,62 @@
ffi.cdef(diggler.io.readFile(nil, digglerNative.gameLuaRuntimePath .. '/ffi/content/BlockDef.code.h'))
ffi.cdef(diggler.io.readFile(nil, digglerNative.gameLuaRuntimePath .. '/ffi/content/Registry.code.h'))
local registerBlock = function(mod, name, block)
local app = block.appearance
local texturesCount = 0
for _, _ in pairs(app.textures) do
texturesCount = texturesCount + 1
end
local textures = ffi.new('struct Diggler_Content_BlockDef_appearance_texture[?]', texturesCount)
local texids = {}
do
local i = 0
for k, v in pairs(app.textures) do
local tex = textures[i]
local rpt = v['repeat']
tex.name = k
tex.path = v.path
tex.repeatXdiv = (rpt and rpt.xdiv) or 1
tex.repeatYdiv = (rpt and rpt.ydiv) or 1
texids[k] = i
i = i + 1
end
end
local bdef = ffi.new('struct Diggler_Content_BlockDef', {
appearance = {
variabilty = 0,
texturesCount = texturesCount,
textures = textures,
look = {
type = 1
}
}
})
local cbdata = bdef.appearance.look.data.cube
local sides = app.look.sides
for k, v in pairs(sides) do
if k == 'all' then
for f = 0, 5 do
cbdata.sides[f].texture = texids[v]
end
else
local faceids = {
xi = 0, ['x+'] = 0,
xd = 1, ['x-'] = 1,
yi = 2, ['y+'] = 2,
yd = 3, ['y-'] = 3,
zi = 4, ['z+'] = 4,
zs = 5, ['z-'] = 5
}
if faceids[k] then
cbdata.sides[faceids[k]].texture = texids[v]
end
end
end
ffi.C.Diggler_Content_Registry_registerBlock(diggler.gameInstance, name, bdef)
end
diggler.export("registerBlock", registerBlock)

24
mods/TestMod/blocks.lua Normal file
View File

@ -0,0 +1,24 @@
print("Yo, " .. CurrentMod.id .. " blocks.lua here")
local grassBlock = {
appearance = {
variabilty = {'static'},
textures = {
grass = {
path = 'tex/grass.png',
['repeat'] = {
xdiv = 8,
ydiv = 8
}
}
},
look = {
type = 'cube',
sides = {
all = 'grass'
}
}
}
}
diggler.registerBlock('grass', grassBlock)

View File

@ -2,14 +2,18 @@ local D = require('diggler')
local TestMod = {
uuid = "BWtAgKUjcW+H+CUQcOW3/Q",
id = "TestMod:",
id = "TestMod",
name = "Test Mod",
version = 1,
versionStr = "1.0.0",
description = "A mod to test Lua scripting ability",
tags = {"test"},
authors = {"gravgun"},
license = "GPLv3",
authors = {"ElementW"},
license = {
name = "GPLv3",
descUrl = "https://www.gnu.org/licenses/gpl-3.0.html",
fulltextUrl = "https://www.gnu.org/licenses/gpl-3.0.txt"
},
deps = {},
optdeps = {},
@ -28,13 +32,7 @@ local TestMod = {
function TestMod.init()
print("Hey i'm " .. CurrentMod.id)
D.registerBlock('test', {
dispname = 'block.test.name',
sandboxTab = 'blocks',
harvest = { pickaxe = 0, shovel = 10000 },
maxStackSize = 32,
tex = "test.png"
})
dofile(CurrentModPath .. '/blocks.lua')
end
function TestMod.deinit()

BIN
mods/TestMod/tex/grass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

View File

@ -3,6 +3,7 @@ if(NOT DEFINED SRCS)
endif()
add_subdirectory("render")
add_subdirectory("scripting")
set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
set(SRCS

View File

@ -294,6 +294,7 @@ void Chunk::updateClient() {
for(int8 x = 0; x < CX; x++) {
for(int8 y = 0; y < CY; y++) {
for(int8 z = 0; z < CZ; z++) {
const glm::ivec3 blockPos(x + wcx * CX, y + wcy * CY, z + wcz * CZ);
bt = data->id[I(x,y,z)];
// Empty block?
@ -344,7 +345,7 @@ void Chunk::updateClient() {
mayDisp = false;
}
GLushort *index; uint i; bool transp = ContentRegistry::isTransparent(bt);
GLushort *index; uint i; bool transp = CR.isTransparent(bt);
if (transp) {
index = idxTransp;
i = it;
@ -356,10 +357,10 @@ void Chunk::updateClient() {
// View from negative x
bn = getBlockId(x - 1, y, z);
if ((mayDisp && bn == BlockTypeLava && getBlockId(x-1, y+1, z) != BlockTypeLava)
|| ContentRegistry::isFaceVisible(bt, bn)) {
|| CR.isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = CR.gTC(bt, FaceDirection::XDec);
tc = CR.blockTexCoord(bt, FaceDirection::XDec, blockPos);
vertex[v++] = {x, y, z, 0, tc->x, tc->v, .6f, .6f, .6f};
vertex[v++] = {x, y, z + 1, 0, tc->u, tc->v, .6f, .6f, .6f};
vertex[v++] = {x, y + 1, z, w, tc->x, tc->y, .6f, .6f, .6f};
@ -369,10 +370,10 @@ void Chunk::updateClient() {
// View from positive x
bn = getBlockId(x + 1, y, z);
if ((mayDisp && bn == BlockTypeLava && getBlockId(x+1, y+1, z) != BlockTypeLava)
|| ContentRegistry::isFaceVisible(bt, bn)) {
|| CR.isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = CR.gTC(bt, FaceDirection::XInc);
tc = CR.blockTexCoord(bt, FaceDirection::XInc, blockPos);
vertex[v++] = {x + 1, y, z, 0, tc->u, tc->v, .6f, .6f, .6f};
vertex[v++] = {x + 1, y + 1, z, w, tc->u, tc->y, .6f, .6f, .6f};
vertex[v++] = {x + 1, y, z + 1, 0, tc->x, tc->v, .6f, .6f, .6f};
@ -382,11 +383,11 @@ void Chunk::updateClient() {
// Negative Y
bn = getBlockId(x, y - 1, z);
if ((hasWaves && bn == BlockTypeLava)
|| ContentRegistry::isFaceVisible(bt, bn)) {
|| CR.isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
float shade = (data->id[I(x,y,z)] == BlockTypeShock) ? 1.5f : .2f;;
tc = CR.gTC(bt, FaceDirection::YDec);
tc = CR.blockTexCoord(bt, FaceDirection::YDec, blockPos);
vertex[v++] = {x, y, z, 0, tc->u, tc->v, shade, shade, shade};
vertex[v++] = {x + 1, y, z, 0, tc->u, tc->y, shade, shade, shade};
vertex[v++] = {x, y, z + 1, 0, tc->x, tc->v, shade, shade, shade};
@ -396,10 +397,10 @@ void Chunk::updateClient() {
// Positive Y
bn = getBlockId(x, y + 1, z);
if ((hasWaves && bt == BlockTypeLava && bu != BlockTypeLava)
|| ContentRegistry::isFaceVisible(bt, bn)) {
|| CR.isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = CR.gTC(bt, FaceDirection::YInc);
tc = CR.blockTexCoord(bt, FaceDirection::YInc, blockPos);
vertex[v++] = {x, y + 1, z, w, tc->x, tc->v, .8f, .8f, .8f};
vertex[v++] = {x, y + 1, z + 1, w, tc->u, tc->v, .8f, .8f, .8f};
vertex[v++] = {x + 1, y + 1, z, w, tc->x, tc->y, .8f, .8f, .8f};
@ -409,10 +410,10 @@ void Chunk::updateClient() {
// Negative Z
bn = getBlockId(x, y, z - 1);
if ((mayDisp && bn == BlockTypeLava && getBlockId(x, y+1, z-1) != BlockTypeLava)
|| ContentRegistry::isFaceVisible(bt, bn)) {
|| CR.isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = CR.gTC(bt, FaceDirection::ZDec);
tc = CR.blockTexCoord(bt, FaceDirection::ZDec, blockPos);
vertex[v++] = {x, y, z, 0, tc->u, tc->v, .4f, .4f, .4f};
vertex[v++] = {x, y + 1, z, w, tc->u, tc->y, .4f, .4f, .4f};
vertex[v++] = {x + 1, y, z, 0, tc->x, tc->v, .4f, .4f, .4f};
@ -422,10 +423,10 @@ void Chunk::updateClient() {
// Positive Z
bn = getBlockId(x, y, z + 1);
if ((mayDisp && bn == BlockTypeLava && getBlockId(x, y+1, z+1) != BlockTypeLava)
|| ContentRegistry::isFaceVisible(bt, bn)) {
|| CR.isFaceVisible(bt, bn)) {
index[i++] = v; index[i++] = v+1; index[i++] = v+2;
index[i++] = v+2; index[i++] = v+1; index[i++] = v+3;
tc = CR.gTC(bt, FaceDirection::ZInc);
tc = CR.blockTexCoord(bt, FaceDirection::ZInc, blockPos);
vertex[v++] = {x, y, z + 1, 0, tc->x, tc->v, .4f, .4f, .4f};
vertex[v++] = {x + 1, y, z + 1, 0, tc->u, tc->v, .4f, .4f, .4f};
vertex[v++] = {x, y + 1, z + 1, w, tc->x, tc->y, .4f, .4f, .4f};

View File

@ -1,9 +1,11 @@
#include "Game.hpp"
#include "Audio.hpp"
#include "content/Registry.hpp"
#include "GlobalProperties.hpp"
#include "KeyBinds.hpp"
#include "render/gl/Renderer.hpp"
#include "scripting/lua/State.hpp"
namespace Diggler {
@ -18,6 +20,7 @@ Game::Game() :
void Game::init() {
CR = new ContentRegistry;
LS = new Scripting::Lua::State(this);
if (GlobalProperties::IsClient) {
PM = new ProgramManager(*this);
LP = new LocalPlayer(this);

View File

@ -15,6 +15,12 @@ namespace Render {
class Renderer;
}
namespace Scripting {
namespace Lua {
class State;
}
}
namespace UI {
class Manager;
}
@ -34,10 +40,11 @@ public:
Universe *U;
PlayerList players;
ContentRegistry *CR;
Scripting::Lua::State *LS;
// Server
Server *S;
// Client
Config *C;
GameWindow *GW;
@ -54,7 +61,7 @@ public:
Net::Peer NS;
KeyBinds *KB;
int PlayerPosUpdateFreq;
Game();
void init();
void updateTime(double time);

View File

@ -1,6 +1,5 @@
#include "GameState.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <algorithm>
#include <chrono>
#include <cstdio>
@ -8,11 +7,16 @@
#include <memory>
#include <sstream>
#include <thread>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "KeyBinds.hpp"
#include "GlobalProperties.hpp"
#include "Game.hpp"
#include "render/gl/FBO.hpp"
#include "render/Renderer.hpp"
#include "scripting/lua/State.hpp"
#include "Clouds.hpp"
#include "Chatbox.hpp"
#include "CaveGenerator.hpp"
@ -220,7 +224,7 @@ void GameState::onKey(int key, int scancode, int action, int mods) {
break;
}
if (m_chatBox->isChatting()) {
if (m_chatBox && m_chatBox->isChatting()) {
switch (key) {
case GLFW_KEY_ENTER:
if (action == GLFW_PRESS) {
@ -311,7 +315,7 @@ void GameState::onMouseButton(int key, int action, int mods) {
Net::MsgTypes::BlockUpdatePlace bup;
bup.worldId = G->LP->W->id;
bup.pos = face;
bup.id = Content::BlockUnknownId;
bup.id = 2; //Content::BlockUnknownId;
bup.data = 0;
bup.writeToMsg(msg);
}
@ -475,10 +479,15 @@ bool GameState::connectLoop() {
default: {
std::ostringstream sstm;
sstm << "Type: " << (int)m_msg.getType() << " Subtype: " << (int)m_msg.getSubtype();
GW->showMessage("Received weird packet", sstm.str());
GW->showMessage("Received unexpected packet", sstm.str());
} return true;
}
G->LS->initialize();
const std::string gameLuaRuntimePath(getAssetsDirectory() + "/lua");
G->LS->setGameLuaRuntimePath(gameLuaRuntimePath);
G->LS->dofile(gameLuaRuntimePath + "/Diggler.lua");
getDebugStream() << "Joined as " << LP.name << '/' << LP.id << std::endl;
return false;
}
@ -687,6 +696,8 @@ void GameState::gameLoop() {
}
Net::OutMessage quit(Net::MessageType::PlayerQuit);
sendMsg(quit, Net::Tfer::Rel);
G->LS->finalize();
}
void GameState::renderDeathScreen() {

View File

@ -1,16 +1,21 @@
#include "Server.hpp"
#include <algorithm>
#include <iterator>
#include <thread>
#include <sstream>
#include <lua.h>
#include "Game.hpp"
#include "network/msgtypes/BlockUpdate.hpp"
#include "network/msgtypes/Chat.hpp"
#include "network/msgtypes/ChunkTransfer.hpp"
#include "network/Network.hpp"
#include "network/NetHelper.hpp"
#include "scripting/lua/State.hpp"
#include "VersionInfo.hpp"
#include "CaveGenerator.hpp"
#include <iterator>
#include <algorithm>
#include <thread>
#include <sstream>
using std::cout;
using std::endl;
@ -301,6 +306,10 @@ Server::Server(Game &G, uint16 port) : G(G) {
} catch (Net::Exception &e) {
getErrorStream() << "Couldn't open port " << port << " for listening" << endl <<
"Make sure no other server instance is running" << endl;
if (port <= 1024) {
getErrorStream() << "The selected port is in range 1-1024, which typically require root "
"privileges. Make sure you have permission to bind to this port." << endl;
}
throw "Server init failed";
}
@ -309,10 +318,7 @@ Server::Server(Game &G, uint16 port) : G(G) {
}
void Server::startInternals() {
L = luaL_newstate();
luaL_openlibs(L);
luaL_loadfile(L, "");
lua_pcall(L, 0, 0, 0);
G.LS->initialize();
}
// FIXME ugly ugly hack to keep it in mem
@ -336,7 +342,7 @@ void Server::stop() {
}
void Server::stopInternals() {
lua_close(L);
G.LS->finalize();
}
void Server::chunkUpdater(WorldRef WR, bool &continueUpdate) {

View File

@ -1,7 +1,8 @@
#ifndef SERVER_HPP
#define SERVER_HPP
#include <memory>
#include <lua.hpp>
#include "network/Network.hpp"
#include "Player.hpp"
@ -14,7 +15,6 @@ class Game;
class Server {
private:
Game &G;
lua_State *L;
// TODO: REMOVEME!!!
std::list<ChunkRef> holdChunksInMem;

View File

@ -1,18 +1,56 @@
#ifndef BLOCK_DEF_HPP
#define BLOCK_DEF_HPP
#ifndef DIGGLER_BLOCK_DEF_HPP
#define DIGGLER_BLOCK_DEF_HPP
#include <unordered_map>
#include "ObjectDef.hpp"
#include "../Texture.hpp"
#include "../util/TexturePacker.hpp"
//#include "../AABB.hpp"
namespace Diggler {
class BlockDef : public ObjectDef {
public:
bool solid, fullBlock;
//AABBVector boxes;
struct Appearance {
Variability variability;
struct Texture {
Diggler::Texture *tex;
TexturePacker::Coord coord;
std::vector<TexturePacker::Coord> divCoords;
struct Repeat {
uint8 xdiv, ydiv;
} repeat;
};
std::unordered_map<std::string, Texture> textures;
struct Look {
enum class Type : uint8 {
Hidden = 0,
Cube = 1
} type;
union Data {
struct Cube {
struct {
std::unordered_map<std::string, Texture>::iterator texture;
} sides[6];
} cube;
Data() {}
} data;
} look;
} appearance;
struct PhysicalProperties {
bool hasCollision;
bool fullBlock;
PhysicalProperties() :
hasCollision(true),
fullBlock(true) {
}
} phys;
};
}
#endif
#endif /* DIGGLER_BLOCK_DEF_HPP */

View File

@ -22,16 +22,16 @@ struct LightData {
LightData& operator=(uint16 i) { l = i; return *this; }
constexpr operator uint16() const { return l; }
constexpr uint8 getS() const { return (l >> 24) & 0x0F; }
constexpr uint8 getS() const { return (l >> 12) & 0x0F; }
void setS(uint8 s) { l = (l & 0x0FFF) & (s << 24); }
constexpr uint8 getR() const { return (l >> 16) & 0x0F; }
constexpr uint8 getR() const { return (l >> 8) & 0x0F; }
void setR(uint8 r) { l = (l & 0xF0FF) & (r << 16); }
constexpr uint8 getG() const { return (l >> 8) & 0x0F; }
constexpr uint8 getG() const { return (l >> 4) & 0x0F; }
void setG(uint8 g) { l = (l & 0xFF0F) & (g << 8); }
constexpr uint8 getB() const { return l & 0x0F; }
void setB(uint8 b) { l = (l & 0xFFF0) & b; }
void setSRGB(uint8 s, uint8 r, uint8 g, uint8 b) { l = (s << 24) & (r << 16) & (g << 8) & b; }
void setRGB(uint8 r, uint8 g, uint8 b) { l = (l & 0xF000) & (r << 16) & (g << 8) & b; }
void setSRGB(uint8 s, uint8 r, uint8 g, uint8 b) { l = (s << 12) & (r << 8) & (g << 4) & b; }
void setRGB(uint8 r, uint8 g, uint8 b) { l = (l & 0xF000) & (r << 8) & (g << 4) & b; }
};
static_assert(sizeof(LightData) == 2, "LightData has extra padding");
static_assert(std::is_pod<LightData>::value, "LightData is not POD");

View File

@ -6,20 +6,13 @@ namespace Diggler {
class ObjectDef {
public:
/**
* Energy-Matter Value
* Stone has an EMV of 10
*/
int emv;
enum class Variability : uint8 {
Static,
Dynamic,
Stream
} visualVariability;
};
};
}
#endif
#endif

View File

@ -2,8 +2,43 @@
#include "Content.hpp"
#include "../GlobalProperties.hpp"
#define PRINT_BLOCK_REGISTRATIONS 1
namespace Diggler {
using CR = ContentRegistry;
CR::BlockRegistration::BlockRegistration(ContentRegistry &registry,
const ContentRegistry::BlockNameMap::iterator &it) :
registry(registry),
it(it),
state(Uncommitted),
def(it->second->second) {
}
CR::BlockRegistration::~BlockRegistration() {
if (state == Uncommitted) {
registry.m_blocks.erase(it->second);
}
}
CR::BlockRegistration::BlockRegistration(CR::BlockRegistration &&o) :
registry(o.registry),
it(o.it),
state(o.state),
def(o.def) {
o.state = Moved;
}
BlockId CR::BlockRegistration::commit() {
state = Committed;
#if PRINT_BLOCK_REGISTRATIONS
getDebugStream() << "Registered block " << it->first << " with id " <<
it->second->first << std::endl;
#endif
return it->second->first;
}
#define NON 0x0
#define RED 0x1
#define BLU 0x2
@ -41,14 +76,14 @@ static const DefBlocksInfo DefBlocksInfos[] = {
{"diggler:transp_blue", "Force Field", 0, 0, 25, ANY, "translucent_blue.png"}
};
bool ContentRegistry::isTransparent(BlockId id) {
bool ContentRegistry::isTransparent(BlockId id) const {
if (id == Content::BlockAirId)
return true;
return false;
// TODO return getBlockDef(id).isTransparent;
}
bool ContentRegistry::isFaceVisible(BlockId id1, BlockId id2) {
bool ContentRegistry::isFaceVisible(BlockId id1, BlockId id2) const {
// TODO: node mesh/boxes -> not fullblock, faces may not be hidden
if (isTransparent(id1)) {
return (id1 != id2);
@ -57,7 +92,7 @@ bool ContentRegistry::isFaceVisible(BlockId id1, BlockId id2) {
}
}
bool ContentRegistry::canEntityGoThrough(BlockId id/* , Entity& ent*/) {
bool ContentRegistry::canEntityGoThrough(BlockId id/* , Entity& ent*/) const {
if (id == Content::BlockAirId)
return true;
return false;
@ -66,87 +101,34 @@ bool ContentRegistry::canEntityGoThrough(BlockId id/* , Entity& ent*/) {
}
using Coord = TexturePacker::Coord;
static Coord unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8;
#define AddTex(b, t) Coord b = m_texturePacker->add(getAssetPath("blocks", t));
ContentRegistry::ContentRegistry() : m_atlas(nullptr) {
ContentRegistry::ContentRegistry() :
m_atlas(nullptr),
m_nextMaxBlockId(Content::BlockUnknownId + 1) {
{ ContentRegistry::BlockRegistration br(registerBlock(Content::BlockAirId, "air"));
br.def.appearance.look.type = BlockDef::Appearance::Look::Type::Hidden;
br.def.phys.hasCollision = false;
br.commit();
}
{ ContentRegistry::BlockRegistration br(registerBlock(Content::BlockUnknownId, "unknown"));
br.def.appearance.look.type = BlockDef::Appearance::Look::Type::Hidden;
br.def.phys.hasCollision = true;
br.commit();
}
m_texturePacker = new TexturePacker(64*8, 64*8);
m_texturePacker->freezeTexUpdate(true);
// Valve checkerboard! :)
m_unknownBlockTex = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\xFF\x00\xFF\xFF\x00\xFF\x00\x00\x00");
#if 1
AddTex(texDirt, "tex_block_dirt.png");
AddTex(texDirtSign, "tex_block_dirt_sign.png");
AddTex(texRock, "tex_block_rock.png");
AddTex(texOre, "tex_block_ore.png");
AddTex(texGold, "tex_block_silver.png");
AddTex(texDiamond, "tex_block_diamond.png");
AddTex(texHomeRed, "tex_block_home_red.png");
AddTex(texHomeBlue, "tex_block_home_blue.png");
AddTex(texSolidRed, "tex_block_red.png");
AddTex(texSolidBlue, "tex_block_blue.png");
AddTex(texLadder, "tex_block_ladder.png");
AddTex(texLadderTop, "tex_block_ladder_top.png");
AddTex(texSpikes, "tex_block_spikes.png");
AddTex(texJump, "tex_block_jump.png");
AddTex(texJumpTop, "tex_block_jump_top.png");
AddTex(texExplosive, "tex_block_explosive.png");
AddTex(texMetal, "tex_block_metal.png");
AddTex(texBankTopRed, "tex_block_bank_top_red.png");
AddTex(texBankLeftRed, "tex_block_bank_left_red.png");
AddTex(texBankFrontRed, "tex_block_bank_front_red.png");
AddTex(texBankRightRed, "tex_block_bank_right_red.png");
AddTex(texBankBackRed, "tex_block_bank_back_red.png");
AddTex(texBankTopBlue, "tex_block_bank_top_blue.png");
AddTex(texBankLeftBlue, "tex_block_bank_left_blue.png");
AddTex(texBankFrontBlue, "tex_block_bank_front_blue.png");
AddTex(texBankRightBlue, "tex_block_bank_right_blue.png");
AddTex(texBankBackBlue, "tex_block_bank_back_blue.png");
AddTex(texTeleSideA, "tex_block_teleporter_a.png");
AddTex(texTeleSideB, "tex_block_teleporter_b.png");
AddTex(texTeleTop, "tex_block_teleporter_top.png");
AddTex(texTeleBottom, "tex_block_teleporter_bottom.png");
AddTex(texLava, "tex_block_lava.png");
AddTex(texRoad, "tex_block_road_orig.png");
AddTex(texRoadTop, "tex_block_road_top.png");
AddTex(texRoadBottom, "tex_block_road_bottom.png");
AddTex(texBeaconRed, "tex_block_beacon_top_red.png");
AddTex(texBeaconBlue, "tex_block_beacon_top_blue.png");
AddTex(texTransRed, "tex_block_trans_red.png");
AddTex(texTransBlue, "tex_block_trans_blue.png");
Coord texNil {0, 0, 0, 0};
const Coord sideTextures[22][6] = {
/* Air */ {texNil, texNil, texNil, texNil, texNil, texNil},
/* Dirt */ {texDirt, texDirt, texDirt, texDirt, texDirt, texDirt},
/* Ore */ {texOre, texOre, texOre, texOre, texOre, texOre},
/* Gold */ {texGold, texGold, texGold, texGold, texGold, texGold},
/*Diamond*/ {texDiamond, texDiamond, texDiamond, texDiamond, texDiamond, texDiamond},
/* Rock */ {texRock, texRock, texRock, texRock, texRock, texRock},
/* Ladder */{texLadder, texLadder, texLadderTop, texLadderTop, texLadder, texLadder},
/* TNT*/ {texExplosive, texExplosive, texExplosive, texExplosive, texExplosive, texExplosive},
/* Jump */ {texJump, texJump, texJumpTop, texTeleBottom, texJump, texJump},
/* Shock */ {texTeleSideA, texTeleSideA, texTeleBottom, texSpikes, texTeleSideB, texTeleSideB},
/*BankRed*/ {texBankFrontRed, texBankBackRed, texBankTopRed, texBankTopRed, texBankLeftRed, texBankRightRed},
/*BankBlue*/{texBankFrontBlue, texBankBackBlue, texBankTopBlue, texBankTopBlue, texBankLeftBlue, texBankRightBlue},
/*BeaconR*/ {texTeleSideA, texTeleSideA, texBeaconRed, texLadderTop, texTeleSideB, texTeleSideB},
/*BeaconB*/ {texTeleSideA, texTeleSideA, texBeaconBlue, texLadderTop, texTeleSideB, texTeleSideB},
/* Road */ {texRoad, texRoad, texRoad, texRoad, texRoad, texRoad},
/* SolidR */{texSolidRed, texSolidRed, texSolidRed, texSolidRed, texSolidRed, texSolidRed},
/* SolidB */{texSolidBlue, texSolidBlue, texSolidBlue, texSolidBlue, texSolidBlue, texSolidBlue},
/* Metal */ {texMetal, texMetal, texMetal, texMetal, texMetal, texMetal},
/*DirtSign*/{texDirtSign, texDirtSign, texDirtSign, texDirtSign, texDirtSign, texDirtSign},
/* Lava */ {texLava, texLava, texLava, texLava, texLava, texLava},
/* TransR */{texTransRed, texTransRed, texTransRed, texTransRed, texTransRed, texTransRed},
/* TransB */{texTransBlue, texTransBlue, texTransBlue, texTransBlue, texTransBlue, texTransBlue},
};
for (int i=0; i < 22; ++i) {
BlockFaceTexCoords c;
for (int j=0; j < 6; ++j)
c.coords[j] = sideTextures[i][j];
m_coords.push_back(c);
}
#endif
unk1 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\xFF\x00\xFF\xFF\x00\xFF\x00\x00\x00");
unk2 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\x00\x00\xFF\x00\x00\xFF\x00\x00\x00");
unk3 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\x00\xFF\x00\x00\xFF\x00\x00\x00\x00");
unk4 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\xFF\x00\x00\xFF\x00\x00\x00\x00\x00");
unk5 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\x00\xFF\xFF\x00\xFF\xFF\x00\x00\x00");
unk6 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\xFF\xFF\x00\xFF\xFF\x00\x00\x00\x00");
unk7 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00");
unk8 = m_texturePacker->add(2, 2, 3, (uint8*)"\x00\x00\x00\x00\x0F\x00\x00\x0F\x00\x00\x00\x00");
m_texturePacker->freezeTexUpdate(false);
m_atlas = m_texturePacker->getAtlas();
@ -156,15 +138,92 @@ ContentRegistry::~ContentRegistry() {
delete m_texturePacker;
}
const TexturePacker::Coord* ContentRegistry::gTC(BlockId t, FaceDirection d) const {
TexturePacker::Coord ContentRegistry::addTexture(const std::string &texName,
const std::string &path) {
const TexturePacker::Coord coord = m_texturePacker->add(path);
m_textureCoords.emplace(std::piecewise_construct,
std::forward_as_tuple(texName),
std::forward_as_tuple(coord));
return coord;
}
const TexturePacker::Coord* ContentRegistry::blockTexCoord(BlockId t, FaceDirection d,
const glm::ivec3 &pos) const {
if (t == Content::BlockUnknownId) {
return &m_unknownBlockTex;
const Coord *unk[] = {
&unk1, &unk2, &unk3, &unk4, &unk5, &unk6, &unk7, &unk8
};
return unk[rmod(pos.x, 2) + 2*(rmod(pos.y, 2)) + 4*(rmod(pos.z, 2))]; //&m_unknownBlockTex;
}
return &(m_coords[(int)t].coords[(int)d]);
const BlockDef &bdef = m_blocks.at(t);
using Type = BlockDef::Appearance::Look::Type;
switch (bdef.appearance.look.type) {
case Type::Cube: {
const BlockDef::Appearance::Texture &tex =
bdef.appearance.look.data.cube.sides[static_cast<uint>(d)].texture->second;
if (tex.repeat.xdiv == 1 && tex.repeat.ydiv == 1) {
return &tex.coord;
}
size_t idx = 0;
switch (d) {
case FaceDirection::XInc:
idx = rmod(pos.z, tex.repeat.xdiv) +
tex.repeat.xdiv*(rmod(pos.y, tex.repeat.ydiv));
break;
case FaceDirection::XDec:
idx = rmod(-pos.z - 1, tex.repeat.xdiv) +
tex.repeat.xdiv*(rmod(pos.y, tex.repeat.ydiv));
break;
case FaceDirection::YInc:
idx = rmod(-pos.z - 1, tex.repeat.xdiv) +
tex.repeat.xdiv*(rmod(pos.x, tex.repeat.ydiv));
break;
case FaceDirection::YDec:
idx = rmod(pos.z, tex.repeat.xdiv) +
tex.repeat.xdiv*(rmod(pos.x, tex.repeat.ydiv));
break;
case FaceDirection::ZInc:
idx = rmod(-pos.x - 1, tex.repeat.xdiv) +
tex.repeat.xdiv*(rmod(pos.y, tex.repeat.ydiv));
break;
case FaceDirection::ZDec:
idx = rmod(pos.x, tex.repeat.xdiv) +
tex.repeat.xdiv*(rmod(pos.y, tex.repeat.ydiv));
break;
}
return &tex.divCoords[idx];
} break;
case Type::Hidden:
;
}
return nullptr;
}
const Texture* ContentRegistry::getAtlas() const {
return m_atlas;
}
ContentRegistry::BlockRegistration ContentRegistry::registerBlock(BlockId id, const char *name) {
BlockIdMap::iterator bit = m_blocks.emplace(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple())
.first;
return BlockRegistration(*this, m_blockNames.emplace(std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(bit))
.first);
}
ContentRegistry::BlockRegistration ContentRegistry::registerBlock(const char *name) {
BlockId id = Content::BlockUnknownId;
if (m_freedBlockIds.empty()) {
id = m_nextMaxBlockId;
++m_nextMaxBlockId;
} else {
id = m_freedBlockIds.back();
m_freedBlockIds.pop_back();
}
return registerBlock(id, name);
}
}

View File

@ -1,13 +1,18 @@
#ifndef CONTENT_REGISTRY_HPP
#define CONTENT_REGISTRY_HPP
#include <unordered_map>
#include <vector>
#include <glm/glm.hpp>
#include "BlockDef.hpp"
#include "Content.hpp"
#include "../Texture.hpp"
#include "../util/TexturePacker.hpp"
namespace Diggler {
class BlockDef;
enum class FaceDirection : uint8_t {
XInc = 0,
XDec = 1,
@ -18,40 +23,67 @@ enum class FaceDirection : uint8_t {
};
class ContentRegistry {
public:
using BlockIdMap = std::unordered_map<BlockId, BlockDef>;
using BlockNameMap = std::unordered_map<std::string, BlockIdMap::iterator>;
class BlockRegistration {
protected:
ContentRegistry &registry;
const BlockNameMap::iterator it;
enum {
Uncommitted,
Committed,
Moved
} state;
public:
BlockDef &def;
BlockRegistration(ContentRegistry &registry, const BlockNameMap::iterator &it);
~BlockRegistration();
BlockRegistration(const BlockRegistration&) = delete;
BlockRegistration& operator=(const BlockRegistration&) = delete;
BlockRegistration(BlockRegistration&&);
BlockRegistration& operator=(BlockRegistration&&) = delete;
BlockId commit();
};
private:
friend class Registration;
// Client
TexturePacker *m_texturePacker;
const Texture *m_atlas;
// TODO remove?
union BlockFaceTexCoords {
struct {
TexturePacker::Coord front, back, top, bottom, left, right;
};
TexturePacker::Coord coords[6];
};
std::vector<BlockFaceTexCoords> m_coords;
TexturePacker::Coord m_unknownBlockTex;
std::unordered_map<std::string, TexturePacker::Coord> m_textureCoords;
// Shared
;
BlockIdMap m_blocks;
BlockId m_nextMaxBlockId;
BlockNameMap m_blockNames;
std::vector<BlockId> m_freedBlockIds;
// No copy
ContentRegistry(const ContentRegistry&) = delete;
ContentRegistry& operator=(const ContentRegistry&) = delete;
BlockRegistration registerBlock(BlockId id, const char *name);
public:
ContentRegistry();
~ContentRegistry();
static bool isTransparent(BlockId id);
static bool isFaceVisible(BlockId id1, BlockId id2);
static bool canEntityGoThrough(BlockId id/* , Entity& ent*/);
const TexturePacker::Coord* gTC(BlockId, FaceDirection) const;
bool isTransparent(BlockId id) const;
bool isFaceVisible(BlockId id1, BlockId id2) const;
bool canEntityGoThrough(BlockId id/* , Entity& ent*/) const;
TexturePacker::Coord addTexture(const std::string &texName, const std::string &path);
const TexturePacker::Coord* blockTexCoord(BlockId, FaceDirection, const glm::ivec3&) const;
const Texture* getAtlas() const;
BlockId registerBlock(/* TODO */);
BlockId registerItem(/* TODO */);
BlockId registerFluid(/* TODO */);
BlockRegistration registerBlock(const char *name);
void registerMapgen(/* TODO */);
const BlockDef& getBlockDef(BlockId);

View File

@ -62,7 +62,7 @@ void GLWorldRenderer::updateChunk(Chunk *c, Chunk::Vertex *vertices, uint vertCo
uint16 *indicesOpq, uint idxOpqCount, uint16 *indicesTpt, uint idxTptCount) {
ChunkEntry &ce = *reinterpret_cast<ChunkEntry*>(getRendererData(c));
ce.vbo.setDataKeepSize(vertices, vertCount, GL_DYNAMIC_DRAW);
ce.ibo.resizeGrow(sizeof(uint16) * (idxOpqCount + idxTptCount));
ce.ibo.resizeGrow(sizeof(uint16) * (idxOpqCount + idxTptCount), GL_DYNAMIC_DRAW);
ce.ibo.setSubData(indicesOpq, 0, idxOpqCount);
ce.ibo.setSubData(indicesTpt, idxOpqCount, idxTptCount);
ce.indicesOpq = idxOpqCount;

View File

@ -0,0 +1,6 @@
add_subdirectory("lua")
set(SRCS
${SRCS}
PARENT_SCOPE
)

View File

@ -0,0 +1,7 @@
set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
set(SRCS
${SRCS}
${CSD}/api/content/Registry.cpp
${CSD}/State.cpp
PARENT_SCOPE
)

197
src/scripting/lua/State.cpp Normal file
View File

@ -0,0 +1,197 @@
#include "State.hpp"
#include <sstream>
#include <stdexcept>
extern "C" {
#include <lauxlib.h>
#include <lualib.h>
#include <luajit.h>
}
namespace Diggler {
namespace Scripting {
namespace Lua {
static int wrap_exceptions(lua_State *L, lua_CFunction f) {
try {
return f(L);
} catch (const char *s) {
lua_pushstring(L, s);
} catch (const std::exception &e) {
lua_pushstring(L, e.what());
} catch (...) {
lua_pushliteral(L, "caught (...)");
}
return lua_error(L);
}
static std::string intpad(int i, int digits) {
std::string ret(digits, ' ');
bool neg = i < 0;
if (neg) {
i = -i;
}
for (int d = digits - 1; d > 0; --d) {
if (i > 0) {
ret[d] = '0' + (i % 10);
i /= 10;
} else if (neg) {
ret[d] = '-';
break;
}
}
return ret;
}
static std::string stackdump(lua_State* L) {
std::ostringstream oss;
int i, top = lua_gettop(L);
oss << "Lua: " << top << " stack entries" << std::endl;
for (i = 1; i <= top; i++) {
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING:
oss << intpad(i, 3) << " string: '" << lua_tostring(L, i) << "'" << std::endl;
break;
case LUA_TBOOLEAN:
oss << intpad(i, 3) << " bool: " << (lua_toboolean(L, i) ? "true" : "false") << std::endl;
break;
case LUA_TNUMBER:
oss << intpad(i, 3) << " number: " << lua_tonumber(L, i) << std::endl;
break;
default:
oss << intpad(i, 3) << ' ' << lua_typename(L, t) << std::endl;
break;
}
}
return oss.str();
}
State::State(Game *G) :
G(G),
state(nullptr) {
}
State::~State() {
if (state != nullptr) {
finalize();
}
}
void State::initialize() {
state = luaL_newstate();
lua_pushlightuserdata(state, (void*) wrap_exceptions);
luaJIT_setmode(state, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
lua_pop(state, 1);
luaL_openlibs(state);
lua_pushstring(state, "DigglerGameInstance");
lua_pushlightuserdata(state, G);
lua_settable(state, LUA_REGISTRYINDEX);
lua_pushstring(state, "DigglerErrorHandler");
lua_pushcfunction(state, handleLuaError);
lua_settable(state, LUA_REGISTRYINDEX);
lua_newtable(state);
lua_pushlstring(state, reinterpret_cast<const char*>(&G), sizeof(G));
lua_setfield(state, -2, "gameInstancePtrStr");
lua_setglobal(state, "digglerNative");
}
void State::finalize() {
lua_close(state);
state = nullptr;
}
std::string State::traceback() {
std::cout << stackdump(state);
if (!lua_isstring(state, 1))
return "";
lua_getglobal(state, "debug");
if (!lua_istable(state, -1)) {
lua_pop(state, 1);
return "";
}
lua_getfield(state, -1, "traceback");
if (!lua_isfunction(state, -1)) {
lua_pop(state, 2);
return "";
}
lua_pushvalue(state, 1);
lua_pushinteger(state, 2);
lua_call(state, 2, 1);
const char *err = lua_tostring(state, -1);
return err ? err : "<no traceback>";
}
int State::handleLuaError(lua_State *state) {
lua_getfield(state, LUA_REGISTRYINDEX, "DigglerGameInstance");
Game *G = static_cast<Game*>(lua_touserdata(state, -1));
G->LS->error = G->LS->traceback();
return 0;
}
void State::setGameLuaRuntimePath(const std::string &path) {
lua_getglobal(state, "digglerNative");
lua_pushstring(state, path.c_str());
lua_setfield(state, -2, "gameLuaRuntimePath");
lua_pop(state, 1);
}
void State::dofile(const char *path) {
int luaRet;
lua_getfield(state, LUA_REGISTRYINDEX, "DigglerErrorHandler");
if ((luaRet = luaL_loadfile(state, path))) {
lua_remove(state, -2); // pop error handler
if (hasError()) {
std::string err(std::move(error));
throw std::runtime_error(err);
} else {
throw std::runtime_error(traceback());
}
}
if ((luaRet = lua_pcall(state, 0, 0, -2))) {
lua_remove(state, -2); // pop error handler
if (hasError()) {
std::string err(std::move(error));
throw std::runtime_error(err);
} else {
throw std::runtime_error(traceback());
}
}
lua_pop(state, 1);
}
void State::dostring(const char *code) {
int luaRet;
lua_getfield(state, LUA_REGISTRYINDEX, "DigglerErrorHandler");
if ((luaRet = luaL_loadstring(state, code))) {
lua_remove(state, -2); // pop error handler
if (hasError()) {
std::string err(std::move(error));
throw std::runtime_error(err);
} else {
throw std::runtime_error(traceback());
}
}
if ((luaRet = lua_pcall(state, 0, 0, -2))) {
lua_remove(state, -2); // pop error handler
if (hasError()) {
std::string err(std::move(error));
throw std::runtime_error(err);
} else {
throw std::runtime_error(traceback());
}
}
lua_pop(state, 1);
}
}
}
}

View File

@ -0,0 +1,55 @@
#ifndef DIGGLER_SCRIPTING_LUA_STATE_HPP
#define DIGGLER_SCRIPTING_LUA_STATE_HPP
extern "C" {
#include <lua.h>
}
#include "../../Game.hpp"
namespace Diggler {
namespace Scripting {
namespace Lua {
class State {
private:
Game *G;
static int handleLuaError(lua_State*);
std::string error;
inline bool hasError() const {
return error.size() > 0;
}
inline void clearError() {
error.clear();
}
public:
lua_State *state;
State(Game*);
~State();
void initialize();
void finalize();
std::string traceback();
void setGameLuaRuntimePath(const std::string &path);
void dofile(const char *path);
inline void dofile(const std::string &path) {
dofile(path.c_str());
}
void dostring(const char *code);
inline void dostring(const std::string &code) {
dostring(code.c_str());
}
};
}
}
}
#endif /* DIGGLER_SCRIPTING_LUA_STATE_HPP */

View File

@ -0,0 +1,23 @@
struct Diggler_Content_BlockDef {
struct {
int variabilty;
int texturesCount;
struct Diggler_Content_BlockDef_appearance_texture {
const char *name, *path;
uint8_t repeatXdiv, repeatYdiv;
} *textures;
struct {
enum {
LookType_Hidden = 0,
LookType_Cube = 1
} type;
union {
struct {
struct {
int texture;
} sides[6];
} cube;
} data;
} look;
} appearance;
};

View File

@ -0,0 +1,10 @@
#ifndef DIGGLER_SCRIPTING_LUA_API_CONTENT_BLOCK_DEF_H
#define DIGGLER_SCRIPTING_LUA_API_CONTENT_BLOCK_DEF_H
#include <cstdint>
extern "C" {
#include "BlockDef.code.h"
}
#endif /* DIGGLER_SCRIPTING_LUA_API_CONTENT_BLOCK_DEF_H */

View File

@ -0,0 +1,2 @@
void Diggler_Content_Registry_registerBlock(struct Diggler_Game*,
const char *name, struct Diggler_Content_BlockDef*);

View File

@ -0,0 +1,67 @@
#include "Registry.h"
#include "BlockDef.h"
#include "../../../../content/Registry.hpp"
#include "../../../../Game.hpp"
using namespace Diggler;
void Diggler_Content_Registry_registerBlock(struct Diggler_Game *cG,
const char *name, struct Diggler_Content_BlockDef *cBdef) {
Game &G = *reinterpret_cast<Game*>(cG);
ContentRegistry::BlockRegistration br(G.CR->registerBlock(name));
{ decltype(cBdef->appearance) &cApp = cBdef->appearance;
decltype(br.def.appearance) &app = br.def.appearance;
std::vector<decltype(app.textures)::iterator> textureIts;
textureIts.reserve(cApp.texturesCount);
for (int i = 0; i < cApp.texturesCount; ++i) {
decltype(*cApp.textures) &cTex = cApp.textures[i];
std::pair<decltype(app.textures)::iterator, bool> itPair =
app.textures.emplace(std::piecewise_construct,
std::forward_as_tuple(cTex.name),
std::forward_as_tuple());
decltype(app.textures)::iterator &it = itPair.first;
decltype(app.textures)::value_type::second_type &tex = it->second;
tex.coord = G.CR->addTexture(cTex.name, cTex.path);
cTex.repeatXdiv = cTex.repeatYdiv = 4;
tex.repeat.xdiv = cTex.repeatXdiv;
tex.repeat.ydiv = cTex.repeatYdiv;
if (cTex.repeatXdiv > 1 || cTex.repeatYdiv > 1) {
uint16 width = (tex.coord.u - tex.coord.x) / cTex.repeatXdiv,
height = (tex.coord.v - tex.coord.y) / cTex.repeatYdiv;
getDebugStream() << "Split " << tex.coord.x << ' ' << tex.coord.y << ' ' << tex.coord.u <<
' ' << tex.coord.v << " into " << static_cast<int>(tex.repeat.xdiv) << 'x' <<
static_cast<int>(tex.repeat.ydiv) << std::endl;
for (int16 y = cTex.repeatYdiv - 1; y >= 0; --y) {
for (int16 x = cTex.repeatXdiv - 1; x >= 0; --x) {
tex.divCoords.emplace_back(TexturePacker::Coord {
static_cast<uint16>(tex.coord.x + width * x),
static_cast<uint16>(tex.coord.y + height * y),
static_cast<uint16>(tex.coord.x + width * (x + 1)),
static_cast<uint16>(tex.coord.y + height * (y + 1))
});
const TexturePacker::Coord &coord = tex.divCoords.back();
getDebugStream() << "> " << coord.x << ' ' << coord.y << ' ' << coord.u <<
' ' << coord.v << std::endl;
}
}
}
textureIts.emplace_back(it);
}
{ decltype(cApp.look) &cLook = cApp.look;
decltype(app.look) &look = app.look;
using Type = BlockDef::Appearance::Look::Type;
look.type = static_cast<Type>(cLook.type);
switch (look.type) {
case Type::Cube:
for (int i = 0; i < 6 /* Math::Geometry::Cube::FaceCount */; ++i) {
look.data.cube.sides[i].texture = textureIts.at(cLook.data.cube.sides[i].texture);
}
break;
case Type::Hidden:
break;
}
}
}
br.commit();
}

View File

@ -0,0 +1,8 @@
#ifndef DIGGLER_SCRIPTING_LUA_API_CONTENT_REGISTRY_H
#define DIGGLER_SCRIPTING_LUA_API_CONTENT_REGISTRY_H
extern "C" {
#include "Registry.code.h"
}
#endif /* DIGGLER_SCRIPTING_LUA_API_CONTENT_REGISTRY_H */

View File

@ -4,14 +4,14 @@
namespace Diggler {
bool BitmapDumper::dumpAsPpm(int w, int h, const void *data, const char *path) {
const char *bytes = (const char*) data;
const char *bytes = static_cast<const char*>(data);
FILE *fpr = fopen(path, "wb");
if (fpr == nullptr) {
return false;
}
fprintf(fpr, "P6\n%d %d\n255\n", w, h);
for (int n = 0; n < w * h; n += 4) {
fwrite((const void*)&bytes[n], 1, 3, fpr);
for (int n = 0; n < w * h * 4; n += 4) {
fwrite(static_cast<const void*>(&bytes[n]), 1, 3, fpr);
}
fclose(fpr);
return true;

View File

@ -1,12 +1,14 @@
#include "TexturePacker.hpp"
#include <stb_image.h>
#include <cmath>
#include <stdexcept>
#include <cstring>
#include <stdexcept>
#include <thread>
#include <stb_image.h>
#include "BitmapDumper.hpp"
#include "../Texture.hpp"
#include <cstdio>
using namespace std;
#define ENABLE_TIMING 0
@ -16,6 +18,48 @@ using namespace std;
namespace Diggler {
// https://gist.github.com/fairlight1337/4935ae72bcbcc1ba5c72
static void HSVtoRGB(float h, float s, float v, float &r, float &g, float &b) {
const float c = v * s; // Chroma
const float prime = fmod(h / 60.f, 6.f);
const float x = c * (1 - fabs(fmod(prime, 2.f) - 1));
const float m = v - c;
if (0 <= prime && prime < 1) {
r = c;
g = x;
b = 0;
} else if (1 <= prime && prime < 2) {
r = x;
g = c;
b = 0;
} else if (2 <= prime && prime < 3) {
r = 0;
g = c;
b = x;
} else if (3 <= prime && prime < 4) {
r = 0;
g = x;
b = c;
} else if (4 <= prime && prime < 5) {
r = x;
g = 0;
b = c;
} else if (5 <= prime && prime < 6) {
r = c;
g = 0;
b = x;
} else {
r = 0;
g = 0;
b = 0;
}
r += m;
g += m;
b += m;
}
TexturePacker::TexturePacker(uint w, uint h) :
atlasWidth(w), atlasHeight(h), m_freezeTexUpdate(false) {
if (w <= 2 || h <= 2)
@ -37,7 +81,42 @@ TexturePacker::Coord TexturePacker::add(const std::string& path) {
int width, height, channels;
unsigned char *ptr = stbi_load(path.c_str(), &width, &height, &channels, STBI_rgb_alpha);
if (!ptr || !width || !height) {
return add(2, 2, 4, (const uint8*)"\xFF\x00\x00\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF\xFF\x00\x00\xFF");
if (!m_defaultTexture) {
m_defaultTexture.reset(new uint8[8*8*4]);
uint i = 0;
for (uint8 y = 0; y < 8; ++y) {
for (uint8 x = 0; x < 8; ++x) {
m_defaultTexture[i] = m_defaultTexture[i + 1] = m_defaultTexture[i + 2] = (x ^ y) * 32;
m_defaultTexture[i + 3] = 255;
i += 4;
}
}
int x = 0, y = 0, xi = 1, yi = 0; uint c = 0;
while (true) {
float r, g, b;
i = (y * 8 + x) * 4;
HSVtoRGB(c * (360.f / (8+8+6+6)), 1.f, 1.f, r, g, b);
m_defaultTexture[i ] = static_cast<uint8>(r * 255);
m_defaultTexture[i + 1] = static_cast<uint8>(g * 255);
m_defaultTexture[i + 2] = static_cast<uint8>(b * 255);
m_defaultTexture[i + 3] = 255;
if (x == 7) {
if (y == 0) {
xi = 0; yi = 1;
} else if (y == 7) {
xi = -1; yi = 0;
}
} else if (x == 0) {
if (y == 7) {
xi = 0; yi = -1;
} else if (y == 1) {
break;
}
}
x += xi; y += yi; ++c;
}
}
return add(8, 8, 4, m_defaultTexture.get());
}
if (width % 4 != 0 || height % 4 != 0) {
getDebugStream() << path << " is bad: " << width << 'x' << height << std::endl;
@ -55,8 +134,8 @@ TexturePacker::Coord TexturePacker::add(const std::string& path) {
using Coord = TexturePacker::Coord;
static bool coordCollides(const Coord &c, const std::vector<Coord> &cs) {
for (const Coord &tc : cs) {
bool xOverlap = (c.x >= tc.x && c.x <= tc.u) || (c.u >= tc.x && c.u <= tc.u);
bool yOverlap = (c.y >= tc.y && c.y <= tc.v) || (c.v >= tc.y && c.v <= tc.v);
bool xOverlap = (c.x >= tc.x && c.x < tc.u) || (c.u > tc.x && c.u <= tc.u);
bool yOverlap = (c.y >= tc.y && c.y < tc.v) || (c.v > tc.y && c.v <= tc.v);
/*getDebugStream() << "Collide? {" << c.x << ", " << c.y << ", " << c.u << ", " << c.v
<< "} {" << tc.x << ", " << tc.y << ", " << tc.u << ", " << tc.v << "} = "
<< xOverlap << '&' << yOverlap << std::endl;*/
@ -69,18 +148,18 @@ static bool coordCollides(const Coord &c, const std::vector<Coord> &cs) {
static Coord findCoordinates(const std::vector<Coord> &cs, int w, int h, int mx, int my) {
// Almost-naïve find algorithm
// Actually not *that* dumb
Coord c {0, 0, w-1, h-1};
Coord c {0, 0, w, h};
if (!coordCollides(c, cs))
return c;
for (const Coord &tc : cs) {
// Right
c.x = tc.u+1; c.y = tc.y;
c.u = c.x + w-1; c.v = c.y + h-1;
c.x = tc.u; c.y = tc.y;
c.u = c.x + w; c.v = c.y + h;
if (c.u < mx && c.v < my && !coordCollides(c, cs))
return c;
// Bottom
c.x = tc.x; c.y = tc.v+1;
c.u = c.x + w-1; c.v = c.y + h-1;
c.x = tc.x; c.y = tc.v;
c.u = c.x + w; c.v = c.y + h;
if (c.u < mx && c.v < my && !coordCollides(c, cs))
return c;
}
@ -121,15 +200,15 @@ TexturePacker::Coord TexturePacker::add(int width, int height, int channels, con
#endif
uint glScaleX = (1 << (sizeof(Coord::x)*8))/atlasWidth,
glScaleY = (1 << (sizeof(Coord::y)*8))/atlasHeight;
glScaleY = (1 << (sizeof(Coord::y)*8))/atlasHeight;
updateTex();
return Coord {
targetX*glScaleX,
targetY*glScaleY,
(targetX + width)*glScaleX-1,
(targetY + height)*glScaleY-1,
(targetX + width)*glScaleX,
(targetY + height)*glScaleY,
};
}
@ -137,7 +216,7 @@ void TexturePacker::updateTex() {
if (m_freezeTexUpdate)
return;
atlasTex->setTexture(atlasData, Texture::PixelFormat::RGBA);
// BitmapDumper::dumpAsPpm(atlasWidth, atlasHeight, atlasData, "/tmp/diggler_atlas.ppm");
BitmapDumper::dumpAsPpm(atlasWidth, atlasHeight, atlasData, "/tmp/diggler_atlas.ppm");
}
void TexturePacker::freezeTexUpdate(bool f) {

View File

@ -3,6 +3,8 @@
#include "../Platform.hpp"
#include <memory>
namespace Diggler {
class Texture;
@ -16,6 +18,8 @@ public:
int atlasWidth, atlasHeight;
private:
std::unique_ptr<uint8[]> m_defaultTexture;
uint8 *atlasData;
Texture *atlasTex;