Merge branch 'main' into content-loader-refactor

This commit is contained in:
MihailRis 2025-04-13 18:04:15 +03:00
commit 3bfe24c63c
75 changed files with 862 additions and 746 deletions

27
dev/tests/bytearray.lua Normal file
View File

@ -0,0 +1,27 @@
local arr = Bytearray()
assert(#arr == 0)
for i=1,10 do
arr[i] = 10 - i
assert(#arr == i)
assert(arr[i] == 10 - i)
end
for i, v in ipairs(arr) do
assert(v == 10 - i)
end
Bytearray.remove(arr, 2)
assert(#arr == 9)
Bytearray.insert(arr, {5, 3, 6})
assert(#arr == 12)
Bytearray.insert(arr, 2, 8)
assert(#arr == 13)
for i=1,10 do
assert(arr[i] == 10 - i)
end
print(#arr, arr:get_capacity())
arr:trim()
assert(#arr == arr:get_capacity())

View File

@ -1,8 +1,8 @@
local util = require "core:tests_util"
util.create_demo_world()
app.set_setting("chunks.load-distance", 3)
app.set_setting("chunks.load-speed", 1)
app.set_setting("chunks.load-distance", 15)
app.set_setting("chunks.load-speed", 15)
local pid1 = player.create("Xerxes")
assert(player.get_name(pid1) == "Xerxes")

View File

@ -0,0 +1,4 @@
local data_buffer = require "core:data_buffer"
local buffer = data_buffer({}, 'LE', true)
buffer:put_int16(2025)
debug.print(buffer)

View File

@ -29,6 +29,9 @@ entity:has_component(name: str) -> bool
-- Enables/disables the component
entity:set_enabled(name: str, enable: bool)
-- Returns id of player the entity is bound
entity:get_player() -> int or nil
```
## Built-in components

View File

@ -29,6 +29,9 @@ entity:has_component(name: str) -> bool
-- Включает/выключает компонент по имени
entity:set_enabled(name: str, enable: bool)
-- Возвращает id игрока, к которому привязана сущность
entity:get_player() -> int или nil
```
## Встроенные компоненты

View File

@ -0,0 +1,28 @@
language = "GLSL"
extensions = ["glsl", "glslv", "glslf"]
line-comment = "//"
multiline-comment-start = "/*"
multiline-comment-end = "*/"
keywords = [
"attribute", "break", "bvec2", "bvec3", "bvec4", "centroid", "continue",
"discard", "dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2",
"dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4", "dvec2",
"dvec3", "dvec4", "else", "flat", "float", "highp", "if", "in", "inout",
"int", "invariant", "isampler1D", "isampler1DArray", "isampler2D",
"isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect",
"isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray",
"ivec2", "ivec3", "ivec4", "layout", "lowp", "mat2", "mat2x2", "mat2x3",
"mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3",
"mat4x4", "mediump", "noperspective", "out", "patch", "precision", "return",
"sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow",
"sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow",
"sampler2DMS", "sampler2DMSArray", "sampler2DRect", "sampler2DRectShadow",
"sampler2DShadow", "sampler3D", "samplerBuffer", "samplerCube",
"samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "smooth",
"subroutine", "uniform", "usampler1D", "usampler1DArray", "usampler2D",
"usampler2DArray", "usampler2DMS", "usampler2DMSArray", "usampler2DRect",
"usampler3D", "usamplerBuffer", "usamplerCube", "usamplerCubeArray",
"uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void",
"while",
]

View File

@ -0,0 +1,12 @@
language = "Lua"
extensions = ["lua"]
line-comment = "--"
multiline-comment-start = "[==["
multiline-comment-end = "]==]"
multiline-string-start = "[["
multiline-string-end = "]]"
keywords = [
"and", "break", "do", "else", "elseif", "end", "false", "for", "function",
"if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true",
"until", "while"
]

View File

@ -74,7 +74,6 @@
multiline='true'
line-numbers='true'
oncontrolkey='on_control_combination'
syntax='lua'
size-func="-1,40"
text-wrap='false'
scroll-step='50'

View File

@ -197,6 +197,7 @@ function open_file_in_editor(filename, line, mutable)
editor.scroll = 0
editor.text = source
editor.focused = true
editor.syntax = file.ext(filename)
if line then
time.post_runnable(function()
editor.caret = editor:linePos(line)

View File

@ -0,0 +1,215 @@
local MIN_CAPACITY = 8
local _type = type
local FFI = ffi
FFI.cdef[[
void* malloc(size_t);
void free(void*);
typedef struct {
unsigned char* bytes;
int size;
int capacity;
} bytearray_t;
]]
local malloc = FFI.C.malloc
local free = FFI.C.free
local function grow_buffer(self, elems)
local new_capacity = math.ceil(self.capacity / 0.75 + elems)
local prev = self.bytes
self.bytes = malloc(new_capacity)
FFI.copy(self.bytes, prev, self.size)
self.capacity = new_capacity
free(prev)
end
local function trim_buffer(self)
if self.size == self.capacity then
return
end
local size = self.size
local prev = self.bytes
self.bytes = malloc(size)
FFI.copy(self.bytes, prev, self.size)
self.capacity = size
free(prev)
end
local function count_elements(b)
local elems = 1
if _type(b) ~= "number" then
elems = #b
end
return elems
end
local function append(self, b)
local elems = count_elements(b)
if self.size + elems > self.capacity then
grow_buffer(self, elems)
end
if _type(b) == "number" then
self.bytes[self.size] = b
else
for i=1, #b do
self.bytes[self.size + i - 1] = b[i]
end
end
self.size = self.size + elems
end
local function insert(self, index, b)
if b == nil then
b = index
index = self.size + 1
end
if index <= 0 or index > self.size + 1 then
return
end
local elems = count_elements(b)
if self.size + elems > self.capacity then
grow_buffer(self, elems)
end
for i=self.size, index - 1, -1 do
self.bytes[i + elems] = self.bytes[i]
end
if _type(b) == "number" then
self.bytes[index - 1] = b
else
for i=1, #b do
self.bytes[index + i - 2] = b[i]
end
end
self.size = self.size + elems
end
local function remove(self, index, elems)
if index <= 0 or index > self.size then
return
end
if elems == nil then
elems = 1
end
if index + elems > self.size then
elems = self.size - index + 1
end
for i=index - 1, self.size - elems - 1 do
self.bytes[i] = self.bytes[i + elems]
end
self.size = self.size - elems
end
local function clear(self)
self.size = 0
end
local function reserve(self, new_capacity)
if new_capacity <= self.capacity then
return
end
local prev = self.bytes
self.bytes = malloc(new_capacity)
FFI.copy(self.bytes, prev, self.size)
self.capacity = new_capacity
free(prev)
end
local function get_capacity(self)
return self.capacity
end
local bytearray_methods = {
append=append,
insert=insert,
remove=remove,
trim=trim_buffer,
clear=clear,
reserve=reserve,
get_capacity=get_capacity,
}
local bytearray_mt = {
__index = function(self, key)
if _type(key) == "string" then
return bytearray_methods[key]
end
if key <= 0 or key > self.size then
return
end
return self.bytes[key - 1]
end,
__newindex = function(self, key, value)
if key == self.size + 1 then
return append(self, value)
elseif key <= 0 or key > self.size then
return
end
self.bytes[key - 1] = value
end,
__tostring = function(self)
return string.format("FFIBytearray[%s]{...}", tonumber(self.size))
end,
__len = function(self)
return tonumber(self.size)
end,
__gc = function(self)
free(self.bytes)
end,
__ipairs = function(self)
local i = 0
return function()
i = i + 1
if i <= self.size then
return i, self.bytes[i - 1]
end
end
end
}
local bytearray_type = FFI.metatype("bytearray_t", bytearray_mt)
local FFIBytearray = {
__call = function (n)
local t = type(n)
if t == "string" then
local buffer = malloc(#n)
FFI.copy(buffer, n, #n)
return bytearray_type(buffer, #n, #n)
elseif t == "table" then
local capacity = math.max(#n, MIN_CAPACITY)
local buffer = malloc(capacity)
for i=1,#n do
buffer[i - 1] = n[i]
end
return bytearray_type(buffer, #n, capacity)
end
n = n or 0
if n < MIN_CAPACITY then
return bytearray_type(malloc(MIN_CAPACITY), n, MIN_CAPACITY)
else
return bytearray_type(malloc(n), n, n)
end
end,
}
table.merge(FFIBytearray, bytearray_methods)
local function FFIBytearray_as_string(bytes)
local t = type(bytes)
if t == "cdata" then
return FFI.string(bytes.bytes, bytes.size)
elseif t == "table" then
local buffer = FFI.new("unsigned char[?]", #bytes)
for i=1, #bytes do
buffer[i - 1] = bytes[i]
end
return FFI.string(buffer, #bytes)
else
error("Bytearray expected, got "..type(bytes))
end
end
return {
FFIBytearray = setmetatable(FFIBytearray, FFIBytearray),
FFIBytearray_as_string = FFIBytearray_as_string
}

View File

@ -263,6 +263,11 @@ entities.get_all = function(uids)
return stdcomp.get_all(uids)
end
end
local bytearray = require "core:internal/bytearray"
Bytearray = bytearray.FFIBytearray
Bytearray_as_string = bytearray.FFIBytearray_as_string
Bytearray_construct = Bytearray.__call
ffi = nil
math.randomseed(time.uptime() * 1536227939)

View File

@ -1,6 +1,3 @@
local _ffi = ffi
ffi = nil
-- Lua has no parallelizm, also _set_data does not call any lua functions so
-- may be reused one global ffi buffer per lua_State
local canvas_ffi_buffer
@ -8,14 +5,14 @@ local canvas_ffi_buffer_size = 0
function __vc_Canvas_set_data(self, data)
if type(data) == "cdata" then
self:_set_data(tostring(_ffi.cast("uintptr_t", data)))
self:_set_data(tostring(ffi.cast("uintptr_t", data)))
end
local width = self.width
local height = self.height
local size = width * height * 4
if size > canvas_ffi_buffer_size then
canvas_ffi_buffer = _ffi.new(
canvas_ffi_buffer = ffi.new(
string.format("unsigned char[%s]", size)
)
canvas_ffi_buffer_size = size
@ -23,7 +20,7 @@ function __vc_Canvas_set_data(self, data)
for i=0, size - 1 do
canvas_ffi_buffer[i] = data[i + 1]
end
self:_set_data(tostring(_ffi.cast("uintptr_t", canvas_ffi_buffer)))
self:_set_data(tostring(ffi.cast("uintptr_t", canvas_ffi_buffer)))
end
function crc32(bytes, chksum)
@ -31,14 +28,14 @@ function crc32(bytes, chksum)
local length = #bytes
if type(bytes) == "table" then
local buffer_len = _ffi.new('int[1]', length)
local buffer = _ffi.new(
local buffer_len = ffi.new('int[1]', length)
local buffer = ffi.new(
string.format("char[%s]", length)
)
for i=1, length do
buffer[i - 1] = bytes[i]
end
bytes = _ffi.string(buffer, buffer_len[0])
bytes = ffi.string(buffer, buffer_len[0])
end
return _crc32(bytes, chksum)
end

View File

@ -166,6 +166,9 @@ size_t BasicParser<CharT>::remain() const {
template<typename CharT>
bool BasicParser<CharT>::isNext(const std::basic_string<CharT>& substring) {
if (substring.empty()) {
return false;
}
if (source.length() - pos < substring.length()) {
return false;
}

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "GLSLExtension.hpp"
#include <sstream>
@ -95,22 +96,6 @@ inline void source_line(std::stringstream& ss, uint linenum) {
ss << "#line " << linenum << "\n";
}
static std::optional<Type> param_type_from(
const std::string& name
) {
static const std::unordered_map<std::string, Type> typeNames {
{"float", Type::FLOAT},
{"vec2", Type::VEC2},
{"vec3", Type::VEC3},
{"vec4", Type::VEC4},
};
const auto& found = typeNames.find(name);
if (found == typeNames.end()) {
return std::nullopt;
}
return found->second;
}
static Value default_value_for(Type type) {
switch (type) {
case Type::FLOAT:
@ -212,11 +197,13 @@ public:
}
bool processParamDirective() {
using Param = PostEffect::Param;
skipWhitespace(false);
// Parse type name
auto typeName = parseName();
auto type = param_type_from(typeName);
if (!type.has_value()) {
Param::Type type;
if (!Param::TypeMeta.getItem(typeName, type)) {
throw error("unsupported param type " + util::quote(typeName));
}
skipWhitespace(false);
@ -228,17 +215,17 @@ public:
skipWhitespace(false);
ss << "uniform " << typeName << " " << paramName << ";\n";
auto defValue = default_value_for(type.value());
auto defValue = default_value_for(type);
// Parse default value
if (peekNoJump() == '=') {
skip(1);
skipWhitespace(false);
defValue = parseDefaultValue(type.value(), typeName);
defValue = parseDefaultValue(type, typeName);
}
skipLine();
params[paramName] = PostEffect::Param(type.value(), std::move(defValue));
params[paramName] = PostEffect::Param(type, std::move(defValue));
return false;
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include "devtools/syntax.hpp"
namespace lua {
std::vector<devtools::Token> tokenize(
std::string_view file, std::wstring_view source
);
}

View File

@ -1,28 +1,74 @@
#include "lua_parsing.hpp"
#include "syntax_parser.hpp"
#include <set>
#include "data/dv.hpp"
#include "util/stringutil.hpp"
#include "BasicParser.hpp"
using namespace lua;
using namespace devtools;
static std::set<std::wstring_view> keywords {
L"and", L"break", L"do", L"else", L"elseif", L"end", L"false", L"for", L"function",
L"if", L"in", L"local", L"nil", L"not", L"or", L"repeat", L"return", L"then", L"true",
L"until", L"while"
};
dv::value Syntax::serialize() const {
auto map = dv::object();
map["language"] = language;
map["line-comment"] = util::wstr2str_utf8(lineComment);
map["multiline-comment-start"] = util::wstr2str_utf8(multilineCommentStart);
map["multiline-comment-end"] = util::wstr2str_utf8(multilineCommentEnd);
map["multiline-string-start"] = util::wstr2str_utf8(multilineStringStart);
map["multiline-string-end"] = util::wstr2str_utf8(multilineStringEnd);
static bool is_lua_keyword(std::wstring_view view) {
return keywords.find(view) != keywords.end();
auto& extsList = map.list("extensions");
for (const auto& ext : extensions) {
extsList.add(ext);
}
auto& keywordsList = map.list("keywords");
for (const auto& keyword : keywords) {
keywordsList.add(util::wstr2str_utf8(keyword));
}
return map;
}
inline bool is_lua_identifier_start(int c) {
void Syntax::deserialize(const dv::value& src) {
src.at("language").get(language);
std::string lineComment;
std::string multilineCommentStart;
std::string multilineCommentEnd;
std::string multilineStringStart;
std::string multilineStringEnd;
src.at("line-comment").get(lineComment);
src.at("multiline-comment-start").get(multilineCommentStart);
src.at("multiline-comment-end").get(multilineCommentEnd);
src.at("multiline-string-start").get(multilineStringStart);
src.at("multiline-string-end").get(multilineStringEnd);
this->lineComment = util::str2wstr_utf8(lineComment);
this->multilineCommentStart = util::str2wstr_utf8(multilineCommentStart);
this->multilineCommentEnd = util::str2wstr_utf8(multilineCommentEnd);
this->multilineStringStart = util::str2wstr_utf8(multilineStringStart);
this->multilineStringEnd = util::str2wstr_utf8(multilineStringEnd);
if (src.has("extensions")) {
const auto& extsList = src["extensions"];
for (const auto& ext : extsList) {
extensions.insert(ext.asString());
}
}
if (src.has("keywords")) {
const auto& keywordsList = src["keywords"];
for (const auto& keyword : keywordsList) {
keywords.insert(util::str2wstr_utf8(keyword.asString()));
}
}
}
inline bool is_common_identifier_start(int c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
}
inline bool is_lua_identifier_part(int c) {
return is_lua_identifier_start(c) || is_digit(c);
inline bool is_common_identifier_part(int c) {
return is_common_identifier_start(c) || is_digit(c);
}
inline bool is_lua_operator_start(int c) {
@ -32,10 +78,13 @@ inline bool is_lua_operator_start(int c) {
}
class Tokenizer : BasicParser<wchar_t> {
const Syntax& syntax;
std::vector<Token> tokens;
public:
Tokenizer(std::string_view file, std::wstring_view source)
: BasicParser(file, source) {
Tokenizer(
const Syntax& syntax, std::string_view file, std::wstring_view source
)
: BasicParser(file, source), syntax(syntax) {
}
std::wstring parseLuaName() {
@ -111,9 +160,12 @@ public:
}
wchar_t c = peek();
auto start = currentLocation();
if (is_lua_identifier_start(c)) {
if (is_common_identifier_start(c)) {
auto name = parseLuaName();
TokenTag tag = (is_lua_keyword(name) ? TokenTag::KEYWORD : TokenTag::NAME);
TokenTag tag =
(syntax.keywords.find(name) == syntax.keywords.end()
? TokenTag::NAME
: TokenTag::KEYWORD);
emitToken(
tag,
std::move(name),
@ -133,24 +185,29 @@ public:
emitToken(tag, std::wstring(literal), start);
continue;
}
const auto& mcommentStart = syntax.multilineCommentStart;
if (!mcommentStart.empty() && c == mcommentStart[0] &&
isNext(syntax.multilineCommentStart)) {
auto string = readUntil(syntax.multilineCommentEnd, true);
skip(syntax.multilineCommentEnd.length());
emitToken(
TokenTag::COMMENT,
std::wstring(string) + syntax.multilineCommentEnd,
start
);
continue;
}
const auto& mstringStart = syntax.multilineStringStart;
if (!mstringStart.empty() && c == mstringStart[0] &&
isNext(syntax.multilineStringStart)) {
skip(mstringStart.length());
auto string = readUntil(syntax.multilineStringEnd, true);
skip(syntax.multilineStringEnd.length());
emitToken(TokenTag::STRING, std::wstring(string), start);
continue;
}
switch (c) {
case '(': case '[': case '{':
if (isNext(L"[==[")) {
auto string = readUntil(L"]==]", true);
skip(4);
emitToken(
TokenTag::COMMENT,
std::wstring(string) + L"]==]",
start
);
continue;
} else if (isNext(L"[[")) {
skip(2);
auto string = readUntil(L"]]", true);
skip(2);
emitToken(TokenTag::STRING, std::wstring(string), start);
continue;
}
emitToken(TokenTag::OPEN_BRACKET, std::wstring({c}), start, true);
continue;
case ')': case ']': case '}':
@ -188,6 +245,8 @@ public:
}
};
std::vector<Token> lua::tokenize(std::string_view file, std::wstring_view source) {
return Tokenizer(file, source).tokenize();
std::vector<Token> devtools::tokenize(
const Syntax& syntax, std::string_view file, std::wstring_view source
) {
return Tokenizer(syntax, file, source).tokenize();
}

View File

@ -0,0 +1,28 @@
#pragma once
#include <set>
#include <string>
#include <vector>
#include "devtools/syntax.hpp"
#include "interfaces/Serializable.hpp"
namespace devtools {
struct Syntax : Serializable {
std::string language;
std::set<std::string> extensions;
std::set<std::wstring> keywords;
std::wstring lineComment;
std::wstring multilineCommentStart;
std::wstring multilineCommentEnd;
std::wstring multilineStringStart;
std::wstring multilineStringEnd;
dv::value serialize() const override;
void deserialize(const dv::value& src) override;
};
std::vector<Token> tokenize(
const Syntax& syntax, std::string_view file, std::wstring_view source
);
}

View File

@ -1,7 +1,6 @@
#pragma once
#include <memory>
#include <optional>
#include <set>
#include <stdexcept>
#include <string>
@ -25,23 +24,6 @@ namespace rigging {
class SkeletonConfig;
}
constexpr const char* ContentType_name(ContentType type) {
switch (type) {
case ContentType::NONE:
return "none";
case ContentType::BLOCK:
return "block";
case ContentType::ITEM:
return "item";
case ContentType::ENTITY:
return "entity";
case ContentType::GENERATOR:
return "generator";
default:
return "unknown";
}
}
class namereuse_error : public std::runtime_error {
ContentType type;
public:
@ -185,26 +167,6 @@ public:
}
};
constexpr const char* to_string(ResourceType type) {
switch (type) {
case ResourceType::CAMERA:
return "camera";
case ResourceType::POST_EFFECT_SLOT:
return "post-effect-slot";
default:
return "unknown";
}
}
inline std::optional<ResourceType> ResourceType_from(std::string_view str) {
if (str == "camera") {
return ResourceType::CAMERA;
} else if (str == "post-effect-slot") {
return ResourceType::POST_EFFECT_SLOT;
}
return std::nullopt;
}
using ResourceIndicesSet = ResourceIndices[RESOURCE_TYPES_COUNT];
/// @brief Content is a definitions repository

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "ContentLoader.hpp"
#include <algorithm>
@ -259,28 +260,25 @@ void ContentLoader::loadBlock(
}
// block model
std::string modelTypeName = to_string(def.model);
std::string modelTypeName = BlockModelMeta.getNameString(def.model);
root.at("model").get(modelTypeName);
root.at("model-name").get(def.modelName);
if (auto model = BlockModel_from(modelTypeName)) {
if (*model == BlockModel::custom && def.customModelRaw == nullptr) {
if (BlockModelMeta.getItem(modelTypeName, def.model)) {
if (def.model == BlockModel::custom && def.customModelRaw == nullptr) {
if (root.has("model-primitives")) {
def.customModelRaw = root["model-primitives"];
} else if (def.modelName.empty()) {
throw std::runtime_error(name + ": no 'model-primitives' or 'model-name' found");
}
}
def.model = *model;
} else if (!modelTypeName.empty()) {
logger.error() << "unknown model: " << modelTypeName;
def.model = BlockModel::none;
}
std::string cullingModeName = to_string(def.culling);
std::string cullingModeName = CullingModeMeta.getNameString(def.culling);
root.at("culling").get(cullingModeName);
if (auto mode = CullingMode_from(cullingModeName)) {
def.culling = *mode;
} else {
if (!CullingModeMeta.getItem(cullingModeName, def.culling)) {
logger.error() << "unknown culling mode: " << cullingModeName;
}
@ -518,9 +516,7 @@ void ContentLoader::loadEntity(
std::string bodyTypeName;
root.at("body-type").get(bodyTypeName);
if (auto bodyType = BodyType_from(bodyTypeName)) {
def.bodyType = *bodyType;
}
BodyTypeMeta.getItem(bodyTypeName, def.bodyType);
root.at("skeleton-name").get(def.skeletonName);
root.at("blocking").get(def.blocking);
@ -749,8 +745,9 @@ void ContentLoader::load() {
if (io::exists(resourcesFile)) {
auto resRoot = io::read_json(resourcesFile);
for (const auto& [key, arr] : resRoot.asObject()) {
if (auto resType = ResourceType_from(key)) {
loadResources(*resType, arr);
ResourceType type;
if (ResourceTypeMeta.getItem(key, type)) {
loadResources(type, arr);
} else {
// Ignore unknown resources
logger.warning() << "unknown resource type: " << key;
@ -763,8 +760,9 @@ void ContentLoader::load() {
if (io::exists(aliasesFile)) {
auto resRoot = io::read_json(aliasesFile);
for (const auto& [key, arr] : resRoot.asObject()) {
if (auto resType = ResourceType_from(key)) {
loadResourceAliases(*resType, arr);
ResourceType type;
if (ResourceTypeMeta.getItem(key, type)) {
loadResourceAliases(type, arr);
} else {
// Ignore unknown resources
logger.warning() << "unknown resource type: " << key;

View File

@ -1,12 +1,21 @@
#pragma once
#include "typedefs.hpp"
#include "util/EnumMetadata.hpp"
class Content;
class ContentPackRuntime;
enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR };
VC_ENUM_METADATA(ContentType)
{"none", ContentType::NONE},
{"block", ContentType::BLOCK},
{"item", ContentType::ITEM},
{"entity", ContentType::ENTITY},
{"generator", ContentType::GENERATOR},
VC_ENUM_END
enum class ResourceType : size_t {
CAMERA,
POST_EFFECT_SLOT,
@ -15,3 +24,8 @@ enum class ResourceType : size_t {
inline constexpr auto RESOURCE_TYPES_COUNT =
static_cast<size_t>(ResourceType::LAST) + 1;
VC_ENUM_METADATA(ResourceType)
{"camera", ResourceType::CAMERA},
{"post-effect-slot", ResourceType::POST_EFFECT_SLOT},
VC_ENUM_END

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "../ContentLoader.hpp"
#include <algorithm>
@ -210,13 +211,9 @@ void ContentLoader::loadGenerator(
map.at("heights-bpd").get(def.heightsBPD);
std::string interpName;
map.at("heights-interpolation").get(interpName);
if (auto interp = InterpolationType_from(interpName)) {
def.heightsInterpolation = *interp;
}
InterpolationTypeMeta.getItem(interpName, def.heightsInterpolation);
map.at("biomes-interpolation").get(interpName);
if (auto interp = InterpolationType_from(interpName)) {
def.biomesInterpolation = *interp;
}
InterpolationTypeMeta.getItem(interpName, def.biomesInterpolation);
map.at("sea-level").get(def.seaLevel);
map.at("wide-structs-chunks-radius").get(def.wideStructsChunksRadius);

View File

@ -4,7 +4,6 @@
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <optional>
#include "typedefs.hpp"
#include "interfaces/Serializable.hpp"

29
src/devtools/Editor.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "Editor.hpp"
#include "engine/Engine.hpp"
#include "io/engine_paths.hpp"
#include "coders/syntax_parser.hpp"
#include "SyntaxProcessor.hpp"
using namespace devtools;
Editor::Editor(Engine& engine)
: engine(engine), syntaxProcessor(std::make_unique<SyntaxProcessor>()) {
}
Editor::~Editor() = default;
void Editor::loadTools() {
const auto& paths = engine.getResPaths();
auto files = paths.listdir("devtools/syntax");
for (const auto& file : files) {
auto config = io::read_object(file);
auto syntax = std::make_unique<Syntax>();
syntax->deserialize(config);
syntaxProcessor->addSyntax(std::move(syntax));
}
}
SyntaxProcessor& Editor::getSyntaxProcessor() {
return *syntaxProcessor;
}

23
src/devtools/Editor.hpp Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <memory>
class Engine;
namespace devtools {
class SyntaxProcessor;
class Editor {
public:
Editor(Engine& engine);
~Editor();
void loadTools();
SyntaxProcessor& getSyntaxProcessor();
private:
Engine& engine;
std::unique_ptr<SyntaxProcessor> syntaxProcessor;
};
}

View File

@ -1,7 +1,7 @@
#include "syntax_highlighting.hpp"
#include "SyntaxProcessor.hpp"
#include "coders/commons.hpp"
#include "coders/lua_parsing.hpp"
#include "coders/syntax_parser.hpp"
#include "graphics/core/Font.hpp"
using namespace devtools;
@ -55,16 +55,28 @@ static std::unique_ptr<FontStylesScheme> build_styles(
return std::make_unique<FontStylesScheme>(std::move(styles));
}
std::unique_ptr<FontStylesScheme> devtools::syntax_highlight(
const std::string& lang, std::wstring_view source
void SyntaxProcessor::addSyntax(
std::unique_ptr<Syntax> syntax
) {
const auto ptr = syntax.get();
langs.emplace_back(std::move(syntax));
for (auto& ext : ptr->extensions) {
langsExtensions[ext] = ptr;
}
}
std::unique_ptr<FontStylesScheme> SyntaxProcessor::highlight(
const std::string& ext, std::wstring_view source
) const {
const auto& found = langsExtensions.find(ext);
if (found == langsExtensions.end()) {
return nullptr;
}
const auto& syntax = *found->second;
try {
if (lang == "lua") {
auto tokens = lua::tokenize("<string>", source);
return build_styles(tokens);
} else {
return nullptr;
}
auto tokens = tokenize(syntax, "<string>", source);
return build_styles(tokens);
} catch (const parsing_error& err) {
return nullptr;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <set>
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
struct FontStylesScheme;
namespace devtools {
struct Syntax;
enum SyntaxStyles {
DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR
};
class SyntaxProcessor {
public:
std::unique_ptr<FontStylesScheme> highlight(
const std::string& ext, std::wstring_view source
) const;
void addSyntax(std::unique_ptr<Syntax> syntax);
private:
std::vector<std::unique_ptr<Syntax>> langs;
std::unordered_map<std::string, const Syntax*> langsExtensions;
};
}

View File

@ -1,16 +0,0 @@
#pragma once
#include <string>
#include <memory>
struct FontStylesScheme;
namespace devtools {
enum SyntaxStyles {
DEFAULT, KEYWORD, LITERAL, COMMENT, ERROR
};
std::unique_ptr<FontStylesScheme> syntax_highlight(
const std::string& lang, std::wstring_view source
);
}

View File

@ -12,6 +12,7 @@
#include "coders/json.hpp"
#include "coders/toml.hpp"
#include "coders/commons.hpp"
#include "devtools/Editor.hpp"
#include "content/ContentControl.hpp"
#include "core_defs.hpp"
#include "io/io.hpp"
@ -73,6 +74,7 @@ Engine& Engine::getInstance() {
void Engine::initialize(CoreParameters coreParameters) {
params = std::move(coreParameters);
settingsHandler = std::make_unique<SettingsHandler>(settings);
editor = std::make_unique<devtools::Editor>(*this);
cmd = std::make_unique<cmd::CommandsInterpreter>();
network = network::Network::create(settings.network);
@ -134,6 +136,7 @@ void Engine::initialize(CoreParameters coreParameters) {
);
}
content = std::make_unique<ContentControl>(paths, *input, [this]() {
editor->loadTools();
langs::setup(langs::get_current(), paths.resPaths.collectRoots());
if (!isHeadless()) {
for (auto& pack : content->getAllContentPacks()) {

View File

@ -33,6 +33,10 @@ namespace network {
class Network;
}
namespace devtools {
class Editor;
}
class initialize_error : public std::runtime_error {
public:
initialize_error(const std::string& message) : std::runtime_error(message) {}
@ -63,6 +67,7 @@ class Engine : public util::ObjectsKeeper {
std::unique_ptr<Window> window;
std::unique_ptr<Input> input;
std::unique_ptr<gui::GUI> gui;
std::unique_ptr<devtools::Editor> editor;
PostRunnables postRunnables;
Time time;
OnWorldOpen levelConsumer;
@ -161,4 +166,8 @@ public:
cmd::CommandsInterpreter& getCmd() {
return *cmd;
}
devtools::Editor& getEditor() {
return *editor;
}
};

View File

@ -314,6 +314,7 @@ void Hud::updateWorldGenDebug() {
void Hud::update(bool visible) {
const auto& chunks = *player.chunks;
bool is_menu_open = menu.hasOpenPage();
debugPanel->setVisible(
debug && visible && !(inventoryOpen && inventoryView == nullptr)
@ -322,13 +323,13 @@ void Hud::update(bool visible) {
if (!visible && inventoryOpen) {
closeInventory();
}
if (pause && !menu.hasOpenPage()) {
if (pause && !is_menu_open) {
setPause(false);
}
if (!gui.isFocusCaught()) {
processInput(visible);
}
if ((menu.hasOpenPage() || inventoryOpen) == input.getCursor().locked) {
if ((is_menu_open || inventoryOpen) == input.getCursor().locked) {
input.toggleCursor();
}
@ -349,6 +350,8 @@ void Hud::update(bool visible) {
contentAccessPanel->setSize(glm::vec2(caSize.x, windowSize.y));
contentAccess->setMinSize(glm::vec2(1, windowSize.y));
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
darkOverlay->setVisible(is_menu_open);
menu.setVisible(is_menu_open);
if (visible) {
for (auto& element : elements) {
@ -360,7 +363,7 @@ void Hud::update(bool visible) {
}
cleanup();
debugMinimap->setVisible(debug && showGeneratorMinimap);
debugMinimap->setVisible(debug && showGeneratorMinimap && visible);
if (debug && showGeneratorMinimap) {
updateWorldGenDebug();
}
@ -593,10 +596,6 @@ void Hud::setDebug(bool flag) {
void Hud::draw(const DrawContext& ctx){
const auto& viewport = ctx.getViewport();
bool is_menu_open = menu.hasOpenPage();
darkOverlay->setVisible(is_menu_open);
menu.setVisible(is_menu_open);
updateElementsPosition(viewport);
uicamera->setFov(viewport.y);

View File

@ -7,6 +7,7 @@
#include <glm/glm.hpp>
#include "data/dv_fwd.hpp"
#include "util/EnumMetadata.hpp"
class Shader;
@ -14,6 +15,14 @@ class PostEffect {
public:
struct Param {
enum class Type { FLOAT, VEC2, VEC3, VEC4 };
VC_ENUM_METADATA(Type)
{"float", Type::FLOAT},
{"vec2", Type::VEC2},
{"vec3", Type::VEC3},
{"vec4", Type::VEC4},
VC_ENUM_END
using Value = std::variant<float, glm::vec2, glm::vec3, glm::vec4>;
Type type;

View File

@ -1,39 +0,0 @@
#include "commons.hpp"
#include <map>
std::optional<CursorShape> CursorShape_from(std::string_view name) {
static std::map<std::string_view, CursorShape> shapes = {
{"arrow", CursorShape::ARROW},
{"text", CursorShape::TEXT},
{"crosshair", CursorShape::CROSSHAIR},
{"pointer", CursorShape::POINTER},
{"ew-resize", CursorShape::EW_RESIZE},
{"ns-resize", CursorShape::NS_RESIZE},
{"nwse-resize", CursorShape::NWSE_RESIZE},
{"nesw-resize", CursorShape::NESW_RESIZE},
{"all-resize", CursorShape::ALL_RESIZE},
{"not-allowed", CursorShape::NOT_ALLOWED}
};
const auto& found = shapes.find(name);
if (found == shapes.end()) {
return std::nullopt;
}
return found->second;
}
std::string to_string(CursorShape shape) {
static std::string names[] = {
"arrow",
"text",
"crosshair",
"pointer",
"ew-resize",
"ns-resize",
"nwse-resize",
"nesw-resize",
"all-resize",
"not-allowed"
};
return names[static_cast<int>(shape)];
}

View File

@ -3,6 +3,8 @@
#include <string>
#include <optional>
#include "util/EnumMetadata.hpp"
enum class DrawPrimitive {
point = 0,
line,
@ -48,8 +50,18 @@ enum class CursorShape {
LAST=NOT_ALLOWED
};
std::optional<CursorShape> CursorShape_from(std::string_view name);
std::string to_string(CursorShape shape);
VC_ENUM_METADATA(CursorShape)
{"arrow", CursorShape::ARROW},
{"text", CursorShape::TEXT},
{"crosshair", CursorShape::CROSSHAIR},
{"pointer", CursorShape::POINTER},
{"ew-resize", CursorShape::EW_RESIZE},
{"ns-resize", CursorShape::NS_RESIZE},
{"nwse-resize", CursorShape::NWSE_RESIZE},
{"nesw-resize", CursorShape::NESW_RESIZE},
{"all-resize", CursorShape::ALL_RESIZE},
{"not-allowed", CursorShape::NOT_ALLOWED},
VC_ENUM_END
class Flushable {
public:

View File

@ -364,3 +364,7 @@ Input& GUI::getInput() {
Window& GUI::getWindow() {
return engine.getWindow();
}
devtools::Editor& GUI::getEditor() {
return engine.getEditor();
}

View File

@ -18,6 +18,10 @@ class Engine;
class Input;
class Window;
namespace devtools {
class Editor;
}
/*
Some info about padding and margin.
Padding is element inner space, margin is outer
@ -159,5 +163,6 @@ namespace gui {
const Input& getInput() const;
Input& getInput();
Window& getWindow();
devtools::Editor& getEditor();
};
}

View File

@ -8,7 +8,8 @@
#include "../markdown.hpp"
#include "Label.hpp"
#include "assets/Assets.hpp"
#include "devtools/syntax_highlighting.hpp"
#include "devtools/Editor.hpp"
#include "devtools/SyntaxProcessor.hpp"
#include "engine/Engine.hpp"
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
@ -811,7 +812,8 @@ void TextBox::stepDefaultUp(bool shiftPressed, bool breakSelection) {
void TextBox::refreshSyntax() {
if (!syntax.empty()) {
if (auto styles = devtools::syntax_highlight(syntax, input)) {
const auto& processor = gui.getEditor().getSyntaxProcessor();
if (auto styles = processor.highlight(syntax, input)) {
label->setStyles(std::move(styles));
}
}

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "gui_xml.hpp"
#include <stdexcept>
@ -168,8 +169,9 @@ static void read_uinode(
node.setTooltipDelay(element.attr("tooltip-delay").asFloat());
}
if (element.has("cursor")) {
if (auto cursor = CursorShape_from(element.attr("cursor").getText())) {
node.setCursor(*cursor);
CursorShape cursor;
if (CursorShapeMeta.getItem(element.attr("cursor").getText(), cursor)) {
node.setCursor(cursor);
}
}

View File

@ -35,12 +35,14 @@ void ChunksController::update(
int64_t maxDuration, int loadDistance, uint padding, Player& player
) const {
const auto& position = player.getPosition();
int centerX = floordiv<CHUNK_W>(position.x);
int centerY = floordiv<CHUNK_D>(position.z);
int centerX = floordiv<CHUNK_W>(glm::floor(position.x));
int centerY = floordiv<CHUNK_D>(glm::floor(position.z));
if (player.isLoadingChunks()) {
/// FIXME: one generator for multiple players
generator->update(centerX, centerY, loadDistance);
} else {
return;
}
int64_t mcstotal = 0;

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "EngineController.hpp"
#include <algorithm>
@ -164,7 +165,7 @@ static dv::value create_missing_content_report(
auto root = dv::object();
auto& contentEntries = root.list("content");
for (auto& entry : report->getMissingContent()) {
std::string contentName = ContentType_name(entry.type);
std::string contentName = ContentTypeMeta.getNameString(entry.type);
auto& contentEntry = contentEntries.object();
contentEntry["type"] = contentName;
contentEntry["name"] = entry.name;

View File

@ -77,8 +77,8 @@ void LevelController::update(float delta, bool pause) {
player->updateEntity();
glm::vec3 position = player->getPosition();
player->chunks->configure(
position.x,
position.z,
glm::floor(position.x),
glm::floor(position.z),
settings.chunks.loadDistance.get() + settings.chunks.padding.get()
);
chunks->update(

View File

@ -1,3 +1,5 @@
#define VC_ENABLE_REFLECTION
#include "util/stringutil.hpp"
#include "libentity.hpp"
@ -96,8 +98,8 @@ static int l_set_crouching(lua::State* L) {
static int l_get_body_type(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
return lua::pushstring(
L, to_string(entity->getRigidbody().hitbox.type)
return lua::pushlstring(
L, BodyTypeMeta.getName(entity->getRigidbody().hitbox.type)
);
}
return 0;
@ -105,9 +107,9 @@ static int l_get_body_type(lua::State* L) {
static int l_set_body_type(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
if (auto type = BodyType_from(lua::tostring(L, 2))) {
entity->getRigidbody().hitbox.type = *type;
} else {
if (!BodyTypeMeta.getItem(
lua::tostring(L, 2), entity->getRigidbody().hitbox.type
)) {
throw std::runtime_error(
"unknown body type " + util::quote(lua::tostring(L, 2))
);

View File

@ -16,14 +16,14 @@ static int l_encode(lua::State* L) {
return lua::pushstring(L, util::base64_encode(
reinterpret_cast<const ubyte*>(buffer.data()), buffer.size()
));
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
return lua::pushstring(
L,
util::base64_encode(
bytes->data().data(),
bytes->data().size()
)
} else {
auto string = lua::bytearray_as_string(L, 1);
auto out = util::base64_encode(
reinterpret_cast<const ubyte*>(string.data()),
string.size()
);
lua::pop(L);
return lua::pushstring(L, std::move(out));
}
throw std::runtime_error("array or ByteArray expected");
}
@ -36,13 +36,10 @@ static int l_decode(lua::State* L) {
lua::pushinteger(L, buffer[i] & 0xFF);
lua::rawseti(L, i+1);
}
return 1;
} else {
lua::newuserdata<lua::LuaBytearray>(L, buffer.size());
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
bytearray->data().reserve(buffer.size());
std::memcpy(bytearray->data().data(), buffer.data(), buffer.size());
return lua::create_bytearray(L, buffer.data(), buffer.size());
}
return 1;
}
const luaL_Reg base64lib[] = {

View File

@ -9,9 +9,7 @@ static int l_tobytes(lua::State* L) {
if (lua::gettop(L) >= 2) {
compress = lua::toboolean(L, 2);
}
return lua::newuserdata<lua::LuaBytearray>(
L, json::to_binary(value, compress)
);
return lua::create_bytearray(L, json::to_binary(value, compress));
}
static int l_frombytes(lua::State* L) {
@ -24,17 +22,18 @@ static int l_frombytes(lua::State* L) {
lua::pop(L);
}
return lua::pushvalue(L, json::from_binary(buffer.data(), len));
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, -1)) {
const auto& buffer = bytes->data();
return lua::pushvalue(
L, json::from_binary(buffer.data(), buffer.size())
);
} else {
throw std::runtime_error("table or Bytearray expected");
auto string = lua::bytearray_as_string(L, 1);
auto out = json::from_binary(
reinterpret_cast<const ubyte*>(string.data()), string.size()
);
lua::pop(L);
return lua::pushvalue(L, std::move(out));
}
}
const luaL_Reg bjsonlib[] = {
{"tobytes", lua::wrap<l_tobytes>},
{"frombytes", lua::wrap<l_frombytes>},
{NULL, NULL}};
{NULL, NULL}
};

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "content/Content.hpp"
#include "content/ContentLoader.hpp"
#include "content/ContentControl.hpp"
@ -311,7 +312,7 @@ static int l_get_textures(lua::State* L) {
static int l_get_model(lua::State* L) {
if (auto def = require_block(L)) {
return lua::pushstring(L, to_string(def->model));
return lua::pushlstring(L, BlockModelMeta.getName(def->model));
}
return 0;
}

View File

@ -99,7 +99,7 @@ static int pack(lua::State* L, const char* format, bool usetable) {
}
return 1;
} else {
return lua::newuserdata<lua::LuaBytearray>(L, builder.build());
return lua::create_bytearray(L, builder.build());
}
}
@ -130,8 +130,8 @@ static int count_elements(const char* format) {
static int l_unpack(lua::State* L) {
const char* format = lua::require_string(L, 1);
int count = count_elements(format);
auto bytes = lua::require_bytearray(L, 2);
ByteReader reader(bytes);
auto bytes = lua::bytearray_as_string(L, 2);
ByteReader reader(reinterpret_cast<const ubyte*>(bytes.data()), bytes.size());
bool bigEndian = false;
for (size_t i = 0; format[i]; i++) {

View File

@ -248,12 +248,14 @@ static int l_load_texture(lua::State* L) {
}
lua::pop(L);
load_texture(buffer.data(), buffer.size(), lua::require_string(L, 2));
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
} else {
auto string = lua::bytearray_as_string(L, 1);
load_texture(
bytes->data().data(),
bytes->data().size(),
reinterpret_cast<const ubyte*>(string.data()),
string.size(),
lua::require_string(L, 2)
);
lua::pop(L);
}
return 0;
}

View File

@ -128,7 +128,7 @@ static int l_read_bytes(lua::State* L) {
auto bytes = io::read_bytes(path);
if (lua::gettop(L) < 2 || !lua::toboolean(L, 2)) {
lua::newuserdata<lua::LuaBytearray>(L, std::move(bytes));
lua::create_bytearray(L, std::move(bytes));
} else {
lua::createtable(L, length, 0);
int newTable = lua::gettop(L);
@ -148,18 +148,12 @@ static int l_read_bytes(lua::State* L) {
static int l_write_bytes(lua::State* L) {
io::path path = get_writeable_path(L);
if (auto bytearray = lua::touserdata<lua::LuaBytearray>(L, 2)) {
auto& bytes = bytearray->data();
return lua::pushboolean(
L, io::write_bytes(path, bytes.data(), bytes.size())
);
}
std::vector<ubyte> bytes;
lua::read_bytes_from_table(L, 2, bytes);
return lua::pushboolean(
L, io::write_bytes(path, bytes.data(), bytes.size())
auto string = lua::bytearray_as_string(L, 2);
bool res = io::write_bytes(
path, reinterpret_cast<const ubyte*>(string.data()), string.size()
);
lua::pop(L);
return lua::pushboolean(L, res);
}
static int l_list_all_res(lua::State* L, const std::string& path) {

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "libgui.hpp"
#include "assets/Assets.hpp"
#include "engine/Engine.hpp"
@ -422,7 +423,7 @@ static int p_get_line_pos(UINode*, lua::State* L) {
}
static int p_get_cursor(UINode* node, lua::State* L) {
return lua::pushstring(L, to_string(node->getCursor()));
return lua::pushlstring(L, CursorShapeMeta.getName(node->getCursor()));
}
static int p_get_scroll(UINode* node, lua::State* L) {
@ -660,9 +661,9 @@ static void p_set_focused(
}
static void p_set_cursor(UINode* node, lua::State* L, int idx) {
if (auto cursor = CursorShape_from(lua::require_string(L, idx))) {
node->setCursor(*cursor);
}
auto cursor = CursorShape::ARROW; // reset to default
CursorShapeMeta.getItem(lua::require_string(L, idx), cursor);
node->setCursor(cursor);
}
static int p_set_scroll(UINode* node, lua::State* L, int idx) {

View File

@ -109,13 +109,13 @@ static int l_send(lua::State* L, network::Network& network) {
}
lua::pop(L);
connection->send(buffer.data(), size);
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 2)) {
connection->send(
reinterpret_cast<char*>(bytes->data().data()), bytes->data().size()
);
} else if (lua::isstring(L, 2)) {
auto string = lua::tolstring(L, 2);
connection->send(string.data(), string.length());
} else {
auto string = lua::bytearray_as_string(L, 2);
connection->send(string.data(), string.length());
lua::pop(L);
}
return 0;
}
@ -140,13 +140,10 @@ static int l_recv(lua::State* L, network::Network& network) {
lua::pushinteger(L, buffer[i] & 0xFF);
lua::rawseti(L, i+1);
}
return 1;
} else {
lua::newuserdata<lua::LuaBytearray>(L, size);
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
bytearray->data().reserve(size);
std::memcpy(bytearray->data().data(), buffer.data(), size);
return lua::create_bytearray(L, buffer.data(), size);
}
return 1;
}
static int l_available(lua::State* L, network::Network& network) {

View File

@ -14,13 +14,10 @@ static int l_tobytes(lua::State* L) {
lua::pushinteger(L, string[i] & 0xFF);
lua::rawseti(L, i+1);
}
return 1;
} else {
lua::newuserdata<lua::LuaBytearray>(L, string.length());
auto bytearray = lua::touserdata<lua::LuaBytearray>(L, -1);
bytearray->data().reserve(string.length());
std::memcpy(bytearray->data().data(), string.data(), string.length());
return lua::create_bytearray(L, string.data(), string.length());
}
return 1;
}
static int l_tostring(lua::State* L) {
@ -35,16 +32,10 @@ static int l_tostring(lua::State* L) {
}
lua::pop(L);
return lua::pushlstring(L, buffer.data(), size);
} else if (auto bytes = lua::touserdata<lua::LuaBytearray>(L, 1)) {
return lua::pushstring(
L,
std::string(
reinterpret_cast<char*>(bytes->data().data()),
bytes->data().size()
)
);
} else {
lua::bytearray_as_string(L, 1);
return 1;
}
return 1;
}
static int l_length(lua::State* L) {

View File

@ -144,7 +144,7 @@ static int l_get_chunk_data(lua::State* L) {
} else {
chunkData = compressed_chunks::encode(*chunk);
}
return lua::newuserdata<lua::LuaBytearray>(L, std::move(chunkData));
return lua::create_bytearray(L, std::move(chunkData));
}
static void integrate_chunk_client(Chunk& chunk) {
@ -175,14 +175,17 @@ static int l_set_chunk_data(lua::State* L) {
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::require_bytearray(L, 3);
auto buffer = lua::bytearray_as_string(L, 3);
auto chunk = level->chunks->getChunk(x, z);
if (chunk == nullptr) {
return lua::pushboolean(L, false);
}
compressed_chunks::decode(
*chunk, buffer.data(), buffer.size(), *content->getIndices()
*chunk,
reinterpret_cast<const ubyte*>(buffer.data()),
buffer.size(),
*content->getIndices()
);
if (controller->getChunksController()->lighting == nullptr) {
return lua::pushboolean(L, true);
@ -198,10 +201,16 @@ static int l_save_chunk_data(lua::State* L) {
int x = static_cast<int>(lua::tointeger(L, 1));
int z = static_cast<int>(lua::tointeger(L, 2));
auto buffer = lua::require_bytearray(L, 3);
auto buffer = lua::bytearray_as_string(L, 3);
compressed_chunks::save(
x, z, std::move(buffer), level->getWorld()->wfile->getRegions()
x,
z,
std::vector(
reinterpret_cast<const ubyte*>(buffer.data()),
reinterpret_cast<const ubyte*>(buffer.data()) + buffer.size()
),
level->getWorld()->wfile->getRegions()
);
return 0;
}

View File

@ -18,26 +18,6 @@ namespace lua {
virtual const std::string& getTypeName() const = 0;
};
class LuaBytearray : public Userdata {
std::vector<ubyte> buffer;
public:
LuaBytearray(size_t capacity);
LuaBytearray(std::vector<ubyte> buffer);
LuaBytearray(const ubyte* data, size_t size);
virtual ~LuaBytearray();
const std::string& getTypeName() const override {
return TYPENAME;
}
inline std::vector<ubyte>& data() {
return buffer;
}
static int createMetatable(lua::State*);
inline static std::string TYPENAME = "Bytearray";
};
static_assert(!std::is_abstract<LuaBytearray>());
class LuaHeightmap : public Userdata {
std::shared_ptr<Heightmap> map;
std::unique_ptr<fnl_state> noise;

View File

@ -117,7 +117,6 @@ void lua::init_state(State* L, StateType stateType) {
initialize_libs_extends(L);
newusertype<LuaBytearray>(L);
newusertype<LuaHeightmap>(L);
newusertype<LuaVoxelFragment>(L);
newusertype<LuaCanvas>(L);

View File

@ -39,14 +39,16 @@ int l_crc32(lua::State* L) {
string.length()
)
);
} else if (auto bytearray = lua::touserdata<lua::LuaBytearray>(L, 1)) {
auto& bytes = bytearray->data();
return lua::pushinteger(L, crc32(value, bytes.data(), bytes.size()));
} else if (lua::istable(L, 1)) {
std::vector<ubyte> bytes;
lua::read_bytes_from_table(L, 1, bytes);
return lua::pushinteger(L, crc32(value, bytes.data(), bytes.size()));
} else {
throw std::runtime_error("invalid argument 1");
auto string = lua::bytearray_as_string(L, 1);
auto res = crc32(
value, reinterpret_cast<const ubyte*>(string.data()), string.size()
);
lua::pop(L);
return lua::pushinteger(L, res);
}
}

View File

@ -61,7 +61,7 @@ int lua::pushvalue(State* L, const dv::value& value) {
break;
case value_type::bytes: {
const auto& bytes = value.asBytes();
newuserdata<LuaBytearray>(L, bytes.data(), bytes.size());
create_bytearray(L, bytes.data(), bytes.size());
break;
}
}
@ -128,18 +128,14 @@ dv::value lua::tovalue(State* L, int idx) {
return map;
}
}
case LUA_TUSERDATA: {
if (auto bytes = touserdata<LuaBytearray>(L, idx)) {
const auto& data = bytes->data();
return std::make_shared<dv::objects::Bytes>(data.data(), data.size());
}
[[fallthrough]];
}
default:
throw std::runtime_error(
"lua type " + std::string(lua_typename(L, type)) +
" is not supported"
default: {
auto data = bytearray_as_string(L, idx);
auto bytes = std::make_shared<dv::objects::Bytes>(
reinterpret_cast<const ubyte*>(data.data()), data.size()
);
pop(L);
return bytes;
}
}
}

View File

@ -746,14 +746,22 @@ namespace lua {
}
}
inline std::vector<ubyte> require_bytearray(lua::State* L, int idx) {
if (auto* bytearray = lua::touserdata<LuaBytearray>(L, idx)) {
return bytearray->data();
} else if (lua::istable(L, idx)) {
std::vector<ubyte> bytes;
read_bytes_from_table(L, idx, bytes);
return bytes;
}
throw std::runtime_error("bytearray expected");
inline int create_bytearray(lua::State* L, const void* bytes, size_t size) {
lua::requireglobal(L, "Bytearray_construct");
lua::pushlstring(
L, std::string_view(reinterpret_cast<const char*>(bytes), size)
);
return lua::call(L, 1, 1);
}
inline int create_bytearray(lua::State* L, const std::vector<ubyte>& bytes) {
return create_bytearray(L, bytes.data(), bytes.size());
}
inline std::string_view bytearray_as_string(lua::State* L, int idx) {
lua::requireglobal(L, "Bytearray_as_string");
lua::pushvalue(L, idx);
lua::call(L, 1, 1);
return lua::tolstring(L, -1);
}
}

View File

@ -1,206 +0,0 @@
#include "../lua_custom_types.hpp"
#include <sstream>
#include "util/listutil.hpp"
#include "../lua_util.hpp"
using namespace lua;
LuaBytearray::LuaBytearray(size_t capacity) : buffer(capacity) {
buffer.resize(capacity);
}
LuaBytearray::LuaBytearray(std::vector<ubyte> buffer) : buffer(std::move(buffer)) {
}
LuaBytearray::LuaBytearray(const ubyte* data, size_t size) : buffer(data, data + size) {
}
LuaBytearray::~LuaBytearray() {
}
static int l_append(lua::State* L) {
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
if (lua::isnumber(L, 2)) {
auto value = tointeger(L, 2);
buffer->data().push_back(static_cast<ubyte>(value));
} else if (lua::istable(L, 2)) {
lua::read_bytes_from_table(L, 2, buffer->data());
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 2)) {
util::concat(buffer->data(), extension->data());
} else {
throw std::runtime_error("integer/table/Bytearray expected");
}
}
return 0;
}
static int l_insert(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
auto index = tointeger(L, 2) - 1;
if (static_cast<size_t>(index) > data.size()) {
return 0;
}
if (lua::isnumber(L, 3)) {
auto value = tointeger(L, 3);
data.insert(data.begin() + index, static_cast<ubyte>(value));
} else if (lua::istable(L, 3)) {
std::vector<ubyte> temp;
lua::read_bytes_from_table(L, 3, temp);
data.insert(data.begin() + index, temp.begin(), temp.end());
} else if (auto extension = lua::touserdata<LuaBytearray>(L, 3)) {
const std::vector<ubyte>& src = extension->data();
data.insert(data.begin() + index, src.begin(), src.end());
} else {
throw std::runtime_error("integer/table/Bytearray expected");
}
return 0;
}
static int l_remove(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
auto index = tointeger(L, 2) - 1;
if (static_cast<size_t>(index) > data.size()) {
return 0;
}
data.erase(data.begin() + index);
return 0;
}
static std::unordered_map<std::string, lua_CFunction> methods {
{"append", lua::wrap<l_append>},
{"insert", lua::wrap<l_insert>},
{"remove", lua::wrap<l_remove>},
};
static int l_meta_meta_call(lua::State* L) {
if (lua_istable(L, 2)) {
size_t len = objlen(L, 2);
std::vector<ubyte> buffer(len);
buffer.resize(len);
for (size_t i = 0; i < len; i++) {
rawgeti(L, i + 1);
buffer[i] = static_cast<ubyte>(tointeger(L, -1));
pop(L);
}
return newuserdata<LuaBytearray>(L, std::move(buffer));
}
auto size = tointeger(L, 2);
if (size < 0) {
throw std::runtime_error("size can not be less than 0");
}
return newuserdata<LuaBytearray>(L, static_cast<size_t>(size));
}
static int l_meta_index(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
if (isstring(L, 2)) {
auto found = methods.find(tostring(L, 2));
if (found != methods.end()) {
return pushcfunction(L, found->second);
}
}
auto index = tointeger(L, 2) - 1;
if (static_cast<size_t>(index) > data.size()) {
return 0;
}
return pushinteger(L, data.at(index));
}
static int l_meta_newindex(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
auto index = static_cast<size_t>(tointeger(L, 2) - 1);
auto value = tointeger(L, 3);
if (index >= data.size()) {
if (index == data.size()) {
data.push_back(static_cast<ubyte>(value));
}
return 0;
}
data[index] = static_cast<ubyte>(value);
return 0;
}
static int l_meta_len(lua::State* L) {
if (auto buffer = touserdata<LuaBytearray>(L, 1)) {
return pushinteger(L, buffer->data().size());
}
return 0;
}
static int l_meta_tostring(lua::State* L) {
auto buffer = touserdata<LuaBytearray>(L, 1);
if (buffer == nullptr) {
return 0;
}
auto& data = buffer->data();
if (data.size() > 512) {
return pushstring(
L, "bytearray[" + std::to_string(data.size()) + "]{...}"
);
} else {
std::stringstream ss;
ss << "bytearray[" << std::to_string(data.size()) << "]{";
for (size_t i = 0; i < data.size(); i++) {
if (i > 0) {
ss << " ";
}
ss << static_cast<uint>(data[i]);
}
ss << "}";
return pushstring(L, ss.str());
}
}
static int l_meta_add(lua::State* L) {
auto bufferA = touserdata<LuaBytearray>(L, 1);
auto bufferB = touserdata<LuaBytearray>(L, 2);
if (bufferA == nullptr || bufferB == nullptr) {
return 0;
}
auto& dataA = bufferA->data();
auto& dataB = bufferB->data();
std::vector<ubyte> ab;
ab.reserve(dataA.size() + dataB.size());
ab.insert(ab.end(), dataA.begin(), dataA.end());
ab.insert(ab.end(), dataB.begin(), dataB.end());
return newuserdata<LuaBytearray>(L, std::move(ab));
}
int LuaBytearray::createMetatable(lua::State* L) {
createtable(L, 0, 6);
pushcfunction(L, lua::wrap<l_meta_index>);
setfield(L, "__index");
pushcfunction(L, lua::wrap<l_meta_newindex>);
setfield(L, "__newindex");
pushcfunction(L, lua::wrap<l_meta_len>);
setfield(L, "__len");
pushcfunction(L, lua::wrap<l_meta_tostring>);
setfield(L, "__tostring");
pushcfunction(L, lua::wrap<l_meta_add>);
setfield(L, "__add");
createtable(L, 0, 1);
pushcfunction(L, lua::wrap<l_meta_meta_call>);
setfield(L, "__call");
setmetatable(L);
return 1;
}

View File

@ -5,17 +5,6 @@
#include <stdexcept>
#include <glm/glm.hpp>
std::optional<InterpolationType> InterpolationType_from(std::string_view str) {
if (str == "nearest") {
return InterpolationType::NEAREST;
} else if (str == "linear") {
return InterpolationType::LINEAR;
} else if (str == "cubic") {
return InterpolationType::CUBIC;
}
return std::nullopt;
}
static inline float sample_at(
const float* buffer,
uint width,

View File

@ -6,6 +6,7 @@
#include "typedefs.hpp"
#include "maths/Heightmap.hpp"
#include "util/EnumMetadata.hpp"
enum class InterpolationType {
NEAREST,
@ -13,7 +14,11 @@ enum class InterpolationType {
CUBIC,
};
std::optional<InterpolationType> InterpolationType_from(std::string_view str);
VC_ENUM_METADATA(InterpolationType)
{"nearest", InterpolationType::NEAREST},
{"linear", InterpolationType::LINEAR},
{"cubic", InterpolationType::CUBIC},
VC_ENUM_END
class Heightmap {
uint width, height;

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "Entities.hpp"
#include <glm/ext/matrix_transform.hpp>
@ -216,9 +217,7 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
dv::get_vec(bodymap, "vel", body.hitbox.velocity);
std::string bodyTypeName;
map.at("type").get(bodyTypeName);
if (auto bodyType = BodyType_from(bodyTypeName)) {
body.hitbox.type = *bodyType;
}
BodyTypeMeta.getItem(bodyTypeName, body.hitbox.type);
bodymap["crouch"].asBoolean(body.hitbox.crouching);
bodymap["damping"].asNumber(body.hitbox.linearDamping);
}
@ -329,7 +328,7 @@ dv::value Entities::serialize(const Entity& entity) {
if (def.save.body.settings) {
bodymap["damping"] = rigidbody.hitbox.linearDamping;
if (hitbox.type != def.bodyType) {
bodymap["type"] = to_string(hitbox.type);
bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type);
}
if (hitbox.crouching) {
bodymap["crouch"] = hitbox.crouching;

View File

@ -2,7 +2,6 @@
#include <glm/glm.hpp>
#include <memory>
#include <optional>
#include "interfaces/Serializable.hpp"
#include "settings.hpp"

View File

@ -2,26 +2,6 @@
#include <stdexcept>
std::optional<BodyType> BodyType_from(std::string_view str) {
if (str == "kinematic") {
return BodyType::KINEMATIC;
} else if (str == "dynamic") {
return BodyType::DYNAMIC;
} else if (str == "static") {
return BodyType::STATIC;
}
return std::nullopt;
}
std::string to_string(BodyType type) {
switch (type) {
case BodyType::KINEMATIC: return "kinematic";
case BodyType::DYNAMIC: return "dynamic";
case BodyType::STATIC: return "static";
default: return "unknown";
}
}
Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize)
: type(type),
position(position),

View File

@ -2,10 +2,10 @@
#include "maths/aabb.hpp"
#include "typedefs.hpp"
#include "util/EnumMetadata.hpp"
#include <set>
#include <string>
#include <optional>
#include <functional>
#include <glm/glm.hpp>
@ -41,8 +41,11 @@ enum class BodyType {
STATIC, KINEMATIC, DYNAMIC
};
std::optional<BodyType> BodyType_from(std::string_view str);
std::string to_string(BodyType type);
VC_ENUM_METADATA(BodyType)
{"static", BodyType::STATIC},
{"kinematic", BodyType::KINEMATIC},
{"dynamic", BodyType::DYNAMIC},
VC_ENUM_END
struct Hitbox {
BodyType type;

View File

@ -1,37 +1,13 @@
#define VC_ENABLE_REFLECTION
#include "NotePreset.hpp"
#include <map>
#include <vector>
#include "data/dv_util.hpp"
std::string to_string(NoteDisplayMode mode) {
static std::vector<std::string> names = {
"static_billboard",
"y_free_billboard",
"xy_free_billboard",
"projected"
};
return names.at(static_cast<int>(mode));
}
std::optional<NoteDisplayMode> NoteDisplayMode_from(std::string_view s) {
static std::map<std::string_view, NoteDisplayMode, std::less<>> map {
{"static_billboard", NoteDisplayMode::STATIC_BILLBOARD},
{"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD},
{"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD},
{"projected", NoteDisplayMode::PROJECTED}
};
const auto& found = map.find(s);
if (found == map.end()) {
return std::nullopt;
}
return found->second;
}
#include <vector>
dv::value NotePreset::serialize() const {
return dv::object({
{"display", to_string(displayMode)},
{"display", NoteDisplayModeMeta.getNameString(displayMode)},
{"color", dv::to_value(color)},
{"scale", scale},
{"render_distance", renderDistance},
@ -42,7 +18,7 @@ dv::value NotePreset::serialize() const {
void NotePreset::deserialize(const dv::value& src) {
if (src.has("display")) {
displayMode = NoteDisplayMode_from(src["display"].asString()).value();
NoteDisplayModeMeta.getItem(src["display"].asString(), displayMode);
}
if (src.has("color")) {
dv::get_vec(src["color"], color);

View File

@ -2,10 +2,10 @@
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <optional>
#include <string>
#include "interfaces/Serializable.hpp"
#include "util/EnumMetadata.hpp"
enum class NoteDisplayMode {
STATIC_BILLBOARD,
@ -14,8 +14,12 @@ enum class NoteDisplayMode {
PROJECTED
};
std::string to_string(NoteDisplayMode mode);
std::optional<NoteDisplayMode> NoteDisplayMode_from(std::string_view s);
VC_ENUM_METADATA(NoteDisplayMode)
{"static_billboard", NoteDisplayMode::STATIC_BILLBOARD},
{"y_free_billboard", NoteDisplayMode::Y_FREE_BILLBOARD},
{"xy_free_billboard", NoteDisplayMode::XY_FREE_BILLBOARD},
{"projected", NoteDisplayMode::PROJECTED},
VC_ENUM_END
struct NotePreset : public Serializable {
NoteDisplayMode displayMode = NoteDisplayMode::STATIC_BILLBOARD;

View File

@ -1,26 +1,8 @@
#define VC_ENABLE_REFLECTION
#include "ParticlesPreset.hpp"
#include "data/dv_util.hpp"
std::string to_string(ParticleSpawnShape shape) {
static std::string names[] = {
"ball",
"sphere",
"box"
};
return names[static_cast<int>(shape)];
}
ParticleSpawnShape ParticleSpawnShape_from(std::string_view s) {
if (s == "ball") {
return ParticleSpawnShape::BALL;
} else if (s == "sphere") {
return ParticleSpawnShape::SPHERE;
} else {
return ParticleSpawnShape::BOX;
}
}
dv::value ParticlesPreset::serialize() const {
auto root = dv::object();
if (frames.empty()) {
@ -47,7 +29,7 @@ dv::value ParticlesPreset::serialize() const {
root["min_angular_vel"] = minAngularVelocity;
root["max_angular_vel"] = maxAngularVelocity;
root["spawn_spread"] = dv::to_value(size);
root["spawn_shape"] = to_string(spawnShape);
root["spawn_shape"] = ParticleSpawnShapeMeta.getName(spawnShape);
root["random_sub_uv"] = randomSubUV;
return root;
}
@ -82,7 +64,7 @@ void ParticlesPreset::deserialize(const dv::value& src) {
dv::get_vec(src["explosion"], explosion);
}
if (src.has("spawn_shape")) {
spawnShape = ParticleSpawnShape_from(src["spawn_shape"].asString());
ParticleSpawnShapeMeta.getItem(src["spawn_shape"].asString(), spawnShape);
}
if (src.has("frames")) {
for (const auto& frame : src["frames"]) {

View File

@ -5,8 +5,9 @@
#include <vector>
#include "interfaces/Serializable.hpp"
#include "util/EnumMetadata.hpp"
enum ParticleSpawnShape {
enum class ParticleSpawnShape {
/// @brief Coordinates are regulary distributed within
/// the volume of a ball.
BALL = 0,
@ -18,8 +19,11 @@ enum ParticleSpawnShape {
BOX
};
std::string to_string(ParticleSpawnShape shape);
ParticleSpawnShape ParticleSpawnShape_from(std::string_view s);
VC_ENUM_METADATA(ParticleSpawnShape)
{"ball", ParticleSpawnShape::BALL},
{"sphere", ParticleSpawnShape::SPHERE},
{"box", ParticleSpawnShape::BOX},
VC_ENUM_END
struct ParticlesPreset : public Serializable {
/// @brief Collision detection
@ -53,7 +57,7 @@ struct ParticlesPreset : public Serializable {
/// @brief Maximum angular velocity
float maxAngularVelocity = 0.0f;
/// @brief Spawn spread shape
ParticleSpawnShape spawnShape = BALL;
ParticleSpawnShape spawnShape = ParticleSpawnShape::BALL;
/// @brief Spawn spread
glm::vec3 spawnSpread {};
/// @brief Texture name

61
src/util/EnumMetadata.hpp Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#ifdef VC_ENABLE_REFLECTION
#define VC_ENUM_METADATA(NAME) static inline util::EnumMetadata<NAME> NAME##Meta {
#define VC_ENUM_END };
#include <map>
#include <string>
#include <utility>
namespace util {
template<typename EnumT>
class EnumMetadata {
public:
EnumMetadata(
std::initializer_list<std::pair<const std::string_view, EnumT>> items
)
: items(items) {
for (const auto& [name, item] : items) {
names[item] = name;
}
}
std::string_view getName(EnumT item) const {
const auto& found = names.find(item);
if (found == names.end()) {
return "";
}
return found->second;
}
std::string getNameString(EnumT item) const {
return std::string(getName(item));
}
bool getItem(std::string_view name, EnumT& dst) const {
const auto& found = items.find(name);
if (found == items.end()) {
return false;
}
dst = found->second;
return true;
}
size_t size() const {
return items.size();
}
private:
std::map<std::string_view, EnumT> items;
std::map<EnumT, std::string_view> names;
};
}
#else
#include <initializer_list>
#define VC_ENUM_METADATA(NAME) \
struct NAME##__PAIR {const char* n; NAME i;}; \
[[maybe_unused]] static inline std::initializer_list<NAME##__PAIR> NAME##_PAIRS {
#define VC_ENUM_END };
#endif // VC_ENABLE_REFLECTION

View File

@ -18,62 +18,6 @@ dv::value BlockMaterial::serialize() const {
});
}
std::string to_string(BlockModel model) {
switch (model) {
case BlockModel::none:
return "none";
case BlockModel::block:
return "block";
case BlockModel::xsprite:
return "X";
case BlockModel::aabb:
return "aabb";
case BlockModel::custom:
return "custom";
default:
return "unknown";
}
}
std::optional<BlockModel> BlockModel_from(std::string_view str) {
if (str == "none") {
return BlockModel::none;
} else if (str == "block") {
return BlockModel::block;
} else if (str == "X") {
return BlockModel::xsprite;
} else if (str == "aabb") {
return BlockModel::aabb;
} else if (str == "custom") {
return BlockModel::custom;
}
return std::nullopt;
}
std::string to_string(CullingMode mode) {
switch (mode) {
case CullingMode::DEFAULT:
return "default";
case CullingMode::OPTIONAL:
return "optional";
case CullingMode::DISABLED:
return "disabled";
default:
return "unknown";
}
}
std::optional<CullingMode> CullingMode_from(std::string_view str) {
if (str == "default") {
return CullingMode::DEFAULT;
} else if (str == "optional") {
return CullingMode::OPTIONAL;
} else if (str == "disabled") {
return CullingMode::DISABLED;
}
return std::nullopt;
}
CoordSystem::CoordSystem(glm::ivec3 axisX, glm::ivec3 axisY, glm::ivec3 axisZ)
: axes({axisX, axisY, axisZ}) {
fix = glm::ivec3(0);

View File

@ -1,7 +1,6 @@
#pragma once
#include <glm/glm.hpp>
#include <optional>
#include <string>
#include <vector>
#include <array>
@ -10,6 +9,7 @@
#include "maths/UVRegion.hpp"
#include "maths/aabb.hpp"
#include "typedefs.hpp"
#include "util/EnumMetadata.hpp"
struct ParticlesPreset;
@ -93,8 +93,13 @@ enum class BlockModel {
custom
};
std::string to_string(BlockModel model);
std::optional<BlockModel> BlockModel_from(std::string_view str);
VC_ENUM_METADATA(BlockModel)
{"none", BlockModel::none},
{"block", BlockModel::block},
{"X", BlockModel::xsprite},
{"aabb", BlockModel::aabb},
{"custom", BlockModel::custom},
VC_ENUM_END
enum class CullingMode {
DEFAULT,
@ -102,8 +107,11 @@ enum class CullingMode {
DISABLED,
};
std::string to_string(CullingMode mode);
std::optional<CullingMode> CullingMode_from(std::string_view str);
VC_ENUM_METADATA(CullingMode)
{"default", CullingMode::DEFAULT},
{"optional", CullingMode::OPTIONAL},
{"disabled", CullingMode::DISABLED},
VC_ENUM_END
using BoxModel = AABB;

View File

@ -32,18 +32,18 @@ Chunks::Chunks(
: events(events),
indices(indices),
areaMap(w, d) {
areaMap.setCenter(ox-w/2, oz-d/2);
areaMap.setCenter(ox - w / 2, oz - d / 2);
areaMap.setOutCallback([this](int, int, const auto& chunk) {
this->events->trigger(LevelEventType::CHUNK_HIDDEN, chunk.get());
});
}
void Chunks::configure(int32_t x, int32_t z, uint32_t radius) {
setCenter(x, z);
uint32_t diameter = radius * 2LL;
if (getWidth() != diameter) {
resize(diameter, diameter);
}
setCenter(x, z);
}
voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const {
@ -313,7 +313,7 @@ glm::vec3 Chunks::rayCastToObstacle(
}
void Chunks::setCenter(int32_t x, int32_t z) {
areaMap.setCenter(floordiv(x, CHUNK_W), floordiv(z, CHUNK_D));
areaMap.setCenter(floordiv<CHUNK_W>(x), floordiv<CHUNK_D>(z));
}
void Chunks::resize(uint32_t newW, uint32_t newD) {

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "World.hpp"
#include <glm/glm.hpp>
@ -49,7 +50,7 @@ void World::updateTimers(float delta) {
void World::writeResources(const Content& content) {
auto root = dv::object();
for (size_t typeIndex = 0; typeIndex < RESOURCE_TYPES_COUNT; typeIndex++) {
auto typeName = to_string(static_cast<ResourceType>(typeIndex));
auto typeName = ResourceTypeMeta.getNameString(static_cast<ResourceType>(typeIndex));
auto& list = root.list(typeName);
auto& indices = content.resourceIndices[typeIndex];
for (size_t i = 0; i < indices.size(); i++) {

View File

@ -1,3 +1,4 @@
#define VC_ENABLE_REFLECTION
#include "WorldFiles.hpp"
#include <cassert>
@ -182,8 +183,9 @@ bool WorldFiles::readResourcesData(const Content& content) {
}
auto root = io::read_json(file);
for (const auto& [key, arr] : root.asObject()) {
if (auto resType = ResourceType_from(key)) {
read_resources_data(content, arr, *resType);
ResourceType type;
if (ResourceTypeMeta.getItem(key, type)) {
read_resources_data(content, arr, type);
} else {
logger.warning() << "unknown resource type: " << key;
}

View File

@ -355,8 +355,7 @@ void WorldGenerator::generateHeightmap(
void WorldGenerator::update(int centerX, int centerY, int loadDistance) {
surroundMap.setCenter(centerX, centerY);
// 2 is safety padding preventing ChunksController rounding problem
surroundMap.resize(loadDistance + 2);
surroundMap.resize(loadDistance);
surroundMap.setCenter(centerX, centerY);
}

View File

@ -1,27 +0,0 @@
#include "coders/lua_parsing.hpp"
#include <gtest/gtest.h>
#include "coders/commons.hpp"
#include "io/io.hpp"
#include "io/devices/StdfsDevice.hpp"
#include "util/stringutil.hpp"
namespace fs = std::filesystem;
TEST(lua_parsing, Tokenizer) {
io::set_device("res", std::make_shared<io::StdfsDevice>(fs::u8path("../../res")));
auto filename = "res:scripts/stdlib.lua";
auto source = io::read_string(filename);
try {
auto tokens = lua::tokenize(filename, util::str2wstr_utf8(source));
for (const auto& token : tokens) {
std::cout << (int)token.tag << " "
<< util::quote(util::wstr2str_utf8(token.text))
<< std::endl;
}
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw err;
}
}