Chunk Refactor

- compressed and decompressed chunks
- remove Lockable from chunk, chunks should be cloned across threads
- Benchmarks in Main, not ready for build
master
Auri 2021-06-20 17:50:45 -07:00
parent 9466d7692a
commit 4b28437b80
65 changed files with 1367 additions and 1363 deletions

View File

@ -1,9 +1,11 @@
-- Load Libraries
runfile(_PATH .. "modules/fenv")
runfile(_PATH .. "modules/math")
runfile(_PATH .. "modules/string")
runfile(_PATH .. "modules/gui")
runfile(_PATH .. "modules/dump")
runfile(_PATH .. "modules/math")
runfile(_PATH .. "modules/table")
runfile(_PATH .. "modules/after")
runfile(_PATH .. "modules/vector")

View File

@ -1,10 +1,29 @@
zepha.serialize = function(data)
--
-- Options table:
-- {
-- circular = true | false, default = true
-- }
--
zepha.serialize = function(data, opt, path, table_refs)
if not path then path = '"root"' end
if not opt then opt = {} end
local circular = opt.circular == nil and true or opt.circular
if not table_refs then table_refs = { [path] = data } end
local data_type = type(data)
if data_type == 'table' then
if table_refs and table_refs[data] then
return circular and ('#REF[' .. table_refs[data] .. ']') or 'nil' end
table_refs[data] = path
local values = {}
for k, v in pairs(data) do
table.insert(values, zepha.serialize(k) .. ' = ' .. zepha.serialize(v))
local key = zepha.serialize(k, opt, '', table_refs)
local value = zepha.serialize(v, opt, path .. '.' .. key, table_refs)
table.insert(values, key .. ' = ' .. value)
end
return '{ ' .. table.concat(values, ', ') .. ' }'
end
@ -17,18 +36,6 @@ zepha.serialize = function(data)
return 'nil'
end
local function trim(str)
return str:match('^%s*(.-)%s*$')
end
local function split(input, sep, op)
if sep == nil then sep = "%s" end
local t = {}
for str in string.gmatch(input, '([^'..sep..']+)') do
table.insert(t, op and op(str) or str) end
return t
end
local function find_match(str, a, b, off)
local pattern = '[' .. a .. b .. ']'
if not off then off = 1 end
@ -44,44 +51,50 @@ local function find_match(str, a, b, off)
end
end
zepha.deserialize = function(str)
str = trim(str)
zepha.deserialize = function(str, opt, path, table_refs)
if not table_refs then table_refs = {} end
if not path then path = '"root"' end
if not opt then opt = {} end
str = str:trim(str)
if str:sub(1, 1) == '{' then
local tbl = {}
str = trim(str:sub(2, #str - 1))
str = str:sub(2, #str - 1):trim()
-- print(path, tbl)
table_refs[path] = tbl
while #str > 0 do
local sep, key, val
if str:sub(1, 1) == ',' then
str = trim(str:sub(2))
end
if str:sub(1, 1) == ',' then str = str:sub(2):trim() end
if str:sub(1, 1) == '{' then
-- Handling a table key
local e = find_match(str, '{', '}')
local tbl_key = str:sub(1, e)
key = zepha.deserialize(tbl_key)
str = trim(str:sub(str:find('=', e + 1) + 1))
key = zepha.deserialize(tbl_key, opt, path, table_refs)
str = str:sub(str:find('=', e + 1) + 1):trim()
else
-- Handling a normal key
local end_ind = str:find('=')
key = zepha.deserialize(str:sub(1, end_ind - 1))
str = trim(str:sub(end_ind + 1))
key = zepha.deserialize(str:sub(1, end_ind - 1), opt, path, table_refs)
str = str:sub(end_ind + 1):trim()
end
if str:sub(1, 1) == '{' then
-- Handling a table value
local e = find_match(str, '{', '}')
local tbl_val = str:sub(1, e)
val = zepha.deserialize(tbl_val)
str = trim(str:sub(e + 1))
val = zepha.deserialize(tbl_val, opt, path .. '."' .. key .. '"', table_refs)
str = str:sub(e + 1):trim()
else
-- Handling a normal value
local end_ind = str:find(',')
val = zepha.deserialize(str:sub(1, (end_ind or #str + 1) - 1))
str = trim(str:sub(end_ind or #str + 1))
val = zepha.deserialize(str:sub(1, (end_ind or #str + 1) - 1),
opt, path .. '."' .. key .. '"', table_refs)
str = str:sub(end_ind or #str + 1):trim()
end
tbl[key] = val
@ -94,6 +107,10 @@ zepha.deserialize = function(str)
if str:find('^true') then return true end
if str:find('^false') then return false end
if str:find('^nil') then return nil end
if str:find('^#REF%[') then
local end_ind = (str:find(']') or 7) - 1
return table_refs[str:sub(6, end_ind)]
end
return tonumber(str)
end

View File

@ -0,0 +1,18 @@
-- Zepha String Library Extension
-- Version 1.0
-- string.trim
-- Returns a new string with whitespace removed.
string.trim = function(str)
return str:match('^%s*(.-)%s*$')
end
-- string.split
-- Splits a string by a delimiter, optionally applying an operation to it after separating it.
string.split = function(input, sep, op)
if sep == nil then sep = "%s" end
local t = {}
for str in string.gmatch(input, '([^'..sep..']+)') do
table.insert(t, op and op(str) or str) end
return t
end

View File

@ -235,8 +235,8 @@ add_library(Zepha_Core
util/Ray.cpp
util/Ray.h
util/RIE.h
util/Schematic.cpp
util/Schematic.h
util/Structure.cpp
util/Structure.h
util/Space.h
util/Target.cpp
util/Target.h
@ -322,6 +322,8 @@ add_library(Zepha_Core
lua/modules/Structure.cpp
lua/modules/Structure.h
lua/modules/Message.cpp
lua/modules/Message.h)
lua/modules/Message.h
lua/NoiseFromLua.cpp
lua/NoiseFromLua.h)
target_include_directories(Zepha_Core PUBLIC .)

View File

@ -14,10 +14,11 @@
#include <stb_image/stb_image.h>
#include <cute_files/cute_files.h>
#include <world/dim/chunk/Chunk.h>
#pragma clang diagnostic pop
#include "StartGame.h"
//#include "StartGame.h"
/**
* Main entrance point to the program. (Am I really describing what the main function is?)
@ -27,7 +28,40 @@
* @param argv - Argument array
* @returns - A numerical value indicating exit status.
*/
#include <GLFW/glfw3.h>
#include <iostream>
int main(int argc, char* argv[]) {
return StartGame(argc, argv);
// return StartGame(argc, argv);
Chunk a = Chunk();
for (int i = 0; i < 128; i++) {
const auto rnd = floor(rand() * 100);
for (int j = 0; j < 32; j++) {
a.setBlock(i * 32 + j, rnd);
a.setBiome(i * 32 + j, rnd * 2);
}
}
glfwInit();
auto start = glfwGetTime() * 1000;
for (int i = 0; i < 1000; i++) {
auto b = Chunk(a);
// auto l = a.compress();
// std::cout << b.isGenerated() << std::endl;
}
auto end = glfwGetTime() * 1000;
std::cout << (end - start) << std::endl;
// Chunk b = Chunk(a.compress());
// b.decompress();
//
//// std::cout << a.compress().length() << std::endl;
//
// for (int i = 0; i < 4096; i++) assert(a.getBlock(i) == b.getBlock(i));
}

View File

@ -52,10 +52,10 @@ void ClientNetworkInterpreter::update() {
auto player = world.getPlayer();
if (player)
Serializer()
.appendE(NetField::POS).append(player->getPos())
.appendE(NetField::VEL).append(player->getVel())
.appendE(NetField::LOOK_PITCH).append(player->getPitch())
.appendE(NetField::LOOK_YAW).append(player->getYaw())
.appendEnum(NetField::POS).append(player->getPos())
.appendEnum(NetField::VEL).append(player->getVel())
.appendEnum(NetField::LOOK_PITCH).append(player->getPitch())
.appendEnum(NetField::LOOK_YAW).append(player->getYaw())
.packet(Packet::Type::THIS_PLAYER_INFO, false).sendTo(connection.getPeer(), Packet::Channel::INTERACT);
}

View File

@ -24,12 +24,12 @@
ChunkMeshGenerator::ChunkMeshGenerator(ChunkMeshDetails* meshDetails, LocalDefinitionAtlas& defs,
LocalBiomeAtlas& biomes,
std::shared_ptr<Chunk> chunk, std::array<std::shared_ptr<Chunk>, 6> adjacent,
std::unique_ptr<Chunk> chunk, std::array<std::unique_ptr<Chunk>, 6> adjacent,
std::array<NoiseSample, 3>& blockOffsets) :
meshDetails(meshDetails),
adjacent(adjacent),
adjacent(std::move(adjacent)),
biomes(biomes),
chunk(chunk),
chunk(std::move(chunk)),
defs(defs) {
Timer t("Mesh generation");
@ -37,17 +37,14 @@ ChunkMeshGenerator::ChunkMeshGenerator(ChunkMeshDetails* meshDetails, LocalDefin
meshDetails->vertices.reserve(5000);
meshDetails->indices.reserve(7000);
auto l = chunk->getReadLock();
RIE::expand<unsigned int, 4096>(chunk->cGetBlocks(), eBlocks);
RIE::expand<unsigned short, 4096>(chunk->cGetBiomes(), eBiomes);
l.unlock();
BlockDef* block = nullptr;
BiomeDef* biome = nullptr;
for (unsigned short i = 0; i < 4096; i++) {
if (!block || block->index != eBlocks[i]) block = &defs.blockFromId(eBlocks[i]);
if (!biome || biome->index != eBiomes[i]) biome = &biomes.biomeFromId(eBiomes[i]);
if (!block || block->index != chunk->getBlocksArray()[i])
block = &defs.blockFromId(chunk->getBlocksArray()[i]);
if (!biome || biome->index != chunk->getBiomesArray()[i])
biome = &biomes.biomeFromId(chunk->getBiomesArray()[i]);
BlockModel& model = block->model;
glm::vec3 biomeTint = biome->tint;
@ -69,63 +66,43 @@ ChunkMeshGenerator::ChunkMeshGenerator(ChunkMeshDetails* meshDetails, LocalDefin
}
}
glm::ivec3 pos = { off.x - 1, off.y, off.z };
if (!getBlockAt(pos).culls)
addFaces(vis, model.parts[static_cast<int>(EVec::XNEG)], biomeTint, getLightAt(pos));
pos = { off.x + 1, off.y, off.z };
if (!getBlockAt(pos).culls)
addFaces(vis, model.parts[static_cast<int>(EVec::XPOS)], biomeTint, getLightAt(pos));
pos = { off.x, off.y - 1, off.z };
if (!getBlockAt(pos).culls)
addFaces(vis, model.parts[static_cast<int>(EVec::YNEG)], biomeTint, getLightAt(pos));
pos = { off.x, off.y + 1, off.z };
if (!getBlockAt(pos).culls)
addFaces(vis, model.parts[static_cast<int>(EVec::YPOS)], biomeTint, getLightAt(pos));
pos = { off.x, off.y, off.z - 1 };
if (!getBlockAt(pos).culls)
addFaces(vis, model.parts[static_cast<int>(EVec::ZNEG)], biomeTint, getLightAt(pos));
pos = { off.x, off.y, off.z + 1 };
if (!getBlockAt(pos).culls)
addFaces(vis, model.parts[static_cast<int>(EVec::ZPOS)], biomeTint, getLightAt(pos));
for (unsigned char j = 0; j < 6; j++) {
const auto pos = off + Vec::TO_VEC[j];
const auto blockInd = getBlockAt(pos);
const auto* blockDef = block->index == blockInd ? block : &defs.blockFromId(blockInd);
if (!blockDef->culls) addFaces(vis, model.parts[j], biomeTint, getLightAt(pos));
}
addFaces(vis, model.parts[static_cast<int>(EVec::NO_CULL)], biomeTint, getLightAt(off));
}
meshDetails->vertices.shrink_to_fit();
meshDetails->indices.shrink_to_fit();
// t.printElapsedMs();
}
BlockDef& ChunkMeshGenerator::getBlockAt(const glm::ivec3& pos) {
glm::ivec3 dir = { (pos.x < 0 ? -1 : pos.x > 15 ? 1 : 0),
(pos.y < 0 ? -1 : pos.y > 15 ? 1 : 0), (pos.z < 0 ? -1 : pos.z > 15 ? 1 : 0) };
if (dir != glm::ivec3{ 0, 0, 0 }) {
unsigned int ChunkMeshGenerator::getBlockAt(const glm::ivec3& pos) {
auto dir = glm::floor(glm::vec3(pos) / 16.f);
if (dir.x != 0 || dir.y != 0 || dir.z != 0) {
unsigned int ind = static_cast<unsigned int>(Vec::TO_ENUM.at(dir));
auto& chunk = adjacent[ind];
return defs.blockFromId(chunk->getBlock(Space::Block::index(pos - dir * 16)));
return adjacent[ind]->getBlock(Space::Block::index(pos));
}
return defs.blockFromId(eBlocks[Space::Block::index(pos)]);
return chunk->getBlocksArray()[Space::Block::index(pos)];
}
glm::vec4 ChunkMeshGenerator::getLightAt(const glm::ivec3& pos) {
glm::ivec3 dir = { (pos.x < 0 ? -1 : pos.x > 15 ? 1 : 0),
(pos.y < 0 ? -1 : pos.y > 15 ? 1 : 0), (pos.z < 0 ? -1 : pos.z > 15 ? 1 : 0) };
if (dir != glm::ivec3{ 0, 0, 0 }) {
auto dir = glm::floor(glm::vec3(pos) / 16.f);
if (dir.x != 0 || dir.y != 0 || dir.z != 0) {
unsigned int ind = static_cast<unsigned int>(Vec::TO_ENUM.at(dir));
auto& chunk = adjacent[ind];
return chunk->getLight(Space::Block::index(pos - dir * 16));
return adjacent[ind]->getLight(Space::Block::index(pos));
}
return chunk->getLight(Space::Block::index(pos));
}
void
ChunkMeshGenerator::addFaces(const glm::vec3& offset, const std::vector<MeshPart>& meshParts, const glm::vec3& tint,
glm::vec4 light) {
void ChunkMeshGenerator::addFaces(const glm::vec3& offset, const std::vector<MeshPart>& meshParts,
const glm::vec3& tint, glm::vec4 light) {
for (const MeshPart& mp : meshParts) {
glm::vec3 modData = {};
@ -136,7 +113,8 @@ ChunkMeshGenerator::addFaces(const glm::vec3& offset, const std::vector<MeshPart
case ShaderMod::ROTATE_Y:
case ShaderMod::ROTATE_Z:
case ShaderMod::SWAY_ATTACHED:
case ShaderMod::SWAY_FULL_BLOCK:modData = { Util::packFloat((offset - 8.f) / 8.f), mp.modValue, 0 };
case ShaderMod::SWAY_FULL_BLOCK:
modData = { Util::packFloat((offset - 8.f) / 8.f), mp.modValue, 0 };
break;
}
@ -144,8 +122,8 @@ ChunkMeshGenerator::addFaces(const glm::vec3& offset, const std::vector<MeshPart
meshDetails->vertices.push_back({
vertex.pos + offset,
vertex.tex,
mp.blendInd ? tint : glm::vec3{ 1, 1, 1 },
mp.blendInd ? vertex.blendMask : glm::vec2{ -1, -1 },
mp.blendInd ? tint : glm::vec3 { 1, 1, 1 },
mp.blendInd ? vertex.blendMask : glm::vec2 { -1, -1 },
Util::packFloat(vertex.nml),
glm::vec4(light),
static_cast<float>(mp.shaderMod),
@ -153,9 +131,8 @@ ChunkMeshGenerator::addFaces(const glm::vec3& offset, const std::vector<MeshPart
});
}
for (unsigned int index : mp.indices) {
for (unsigned int index : mp.indices)
meshDetails->indices.push_back(indOffset + index);
}
indOffset += mp.vertices.size();
}

View File

@ -9,28 +9,21 @@
#include <memory>
class Chunk;
class MeshPart;
class BlockDef;
class NoiseSample;
class LocalBiomeAtlas;
class ChunkMeshDetails;
class LocalDefinitionAtlas;
class ChunkMeshGenerator {
public:
public:
ChunkMeshGenerator(ChunkMeshDetails* meshDetails, LocalDefinitionAtlas& defs, LocalBiomeAtlas& biomes,
std::shared_ptr<Chunk> chunk, std::array<std::shared_ptr<Chunk>, 6> adjacent,
std::unique_ptr<Chunk> chunk, std::array<std::unique_ptr<Chunk>, 6> adjacent,
std::array<NoiseSample, 3>& blockOffsets);
private:
inline BlockDef& getBlockAt(const glm::ivec3& pos);
private:
inline unsigned int getBlockAt(const glm::ivec3& pos);
inline glm::vec4 getLightAt(const glm::ivec3& pos);
void
@ -42,9 +35,6 @@ class ChunkMeshGenerator {
unsigned int indOffset = 0;
ChunkMeshDetails* meshDetails;
std::shared_ptr<Chunk> chunk;
std::array<std::shared_ptr<Chunk>, 6> adjacent;
std::array<unsigned int, 4096> eBlocks;
std::array<unsigned short, 4096> eBiomes;
std::unique_ptr<Chunk> chunk;
std::array<std::unique_ptr<Chunk>, 6> adjacent;
};

View File

@ -12,9 +12,8 @@
#include "world/player/LocalPlayer.h"
#include "client/gui/compound/GuiLabelledGraph.h"
DebugGui::DebugGui(glm::vec2 bufferSize, SubgamePtr game, WorldPtr world) :
game(game),
world(world) {
DebugGui::DebugGui(glm::vec2 bufferSize, SubgamePtr game, LocalWorld& world) :
game(game), world(world) {
auto fontRef = game.l()->textures["font"];
auto fpsHistogramRef = game.l()->textures["histogram"];
@ -78,105 +77,106 @@ void DebugGui::positionElements(glm::vec2 bufferSize) {
get<GuiLabelledGraph>("gpuGraph")->setPos({ bufferWidth - 254, 90 + 80 });
}
void DebugGui::update(std::shared_ptr<LocalPlayer> player, double fps, int /*chunks*/, int drawCalls, int ssGen,
int ssPack) {
void
DebugGui::update(std::shared_ptr<LocalPlayer> player, double delta,
uint32_t interpolatedChunks, uint32_t generatedChunks, uint32_t recievedPackets,
uint32_t drawnMeshChunks, uint32_t generatedMeshChunks) {
Target target = player->getTarget();
auto& onBiomeDef = game->getBiomes().biomeFromId(
world.l()->getActiveDimension()->getBiome(glm::floor(player->getPos())));
world.getActiveDimension()->getBiome(glm::floor(player->getPos())));
/* Top-right Graphs */ {
get<GuiLabelledGraph>("fpsGraph")->pushValue(static_cast<float>(fps));
get<GuiLabelledGraph>("drawsGraph")->pushValue(drawCalls);
int videoMemAvail, videoMemTotal;
glGetIntegerv(0x9048, &videoMemTotal);
glGetIntegerv(0x9049, &videoMemAvail);
get<GuiLabelledGraph>("gpuGraph")->pushValue(static_cast<int>(std::round(
(videoMemTotal - videoMemAvail) / static_cast<float>(videoMemTotal) * 100.0)) / 100.0f);
}
// FPS and Draw calls graphs
/* Bottom-right Graphs */ {
get<GuiLabelledGraph>("meshGraph")->pushValue(world.l()->lastMeshUpdates);
get<GuiLabelledGraph>("interpGraph")->pushValue(world.l()->mapBlocksInterpolated);
get<GuiLabelledGraph>("genGraph")->pushValue(static_cast<float>(ssGen));
get<GuiLabelledGraph>("packetGraph")->pushValue(static_cast<float>(ssPack));
}
get<GuiLabelledGraph>("fpsGraph")->pushValue(static_cast<float>(1 / delta));
get<GuiLabelledGraph>("drawsGraph")->pushValue(drawnMeshChunks);
/* Top-left Data */ {
glm::vec3 playerPos = glm::floor(player->getPos());
glm::vec3 chunkPos = Space::Chunk::world::fromBlock(playerPos);
glm::vec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunkPos);
glm::vec3 regionPos = Space::Region::world::fromChunk(chunkPos);
glm::vec3 posOffsetFromChunk = Space::Block::relative::toChunk(playerPos);
glm::vec3 posOffsetFromBlock = Space::Block::relative::toMapBlock(playerPos);
glm::vec3 posOffsetFromRegion = Space::Block::relative::toRegion(playerPos);
std::ostringstream str;
using namespace Util;
str << "Dimension: " << world.l()->getActiveDimension()->getIdentifier()
<< " [" << world.l()->getActiveDimension()->getInd() << "]" << std::endl << std::endl;
str << "Pos: " << vecToString(playerPos) << " (" << floatVecToString(player->getPos()) << ")" << std::endl;
str << "Vel: " << floatVecToString(player->getVel()) << std::endl;
str << "Yaw: " << floatToString(player->getYaw()) << ", ";
str << "Pitch: " << floatToString(player->getPitch()) << std::endl << std::endl;
str << "C: " << vecToString(posOffsetFromChunk) << " [" << vecToString(chunkPos) << "]" << std::endl;
str << "M: " << vecToString(posOffsetFromBlock) << " [" << vecToString(mapBlockPos) << "]" << std::endl;
str << "R: " << vecToString(posOffsetFromRegion) << " [" << vecToString(regionPos) << "]" << std::endl
<< std::endl;
str << "Texture Slots: " << game.l()->textures.textureSlotsUsed << " / " << game.l()->textures.maxTextureSlots
<< " ("
<< round(game.l()->textures.textureSlotsUsed / static_cast<float>(game.l()->textures.maxTextureSlots) * 100)
<< "%)" << std::endl << std::endl;
str << "Biome: " << onBiomeDef.identifier << " [" << onBiomeDef.index << "]" << std::endl << std::endl;
if (target.type == Target::Type::BLOCK) {
std::string face =
target.data.block.face == EVec::TOP ? "TOP" :
target.data.block.face == EVec::BOTTOM ? "BOTTOM" :
target.data.block.face == EVec::LEFT ? "LEFT" :
target.data.block.face == EVec::RIGHT ? "RIGHT" :
target.data.block.face == EVec::FRONT ? "FRONT" :
target.data.block.face == EVec::BACK ? "BACK" :
"NONE";
const auto& def = game->getDefs().blockFromId(world.l()->getActiveDimension()->getBlock(target.data.block.pos));
str << "Pointing At: " << def.identifier << " [" << def.index << "]" << std::endl;
str << "Pointed Position: " << vecToString(target.data.block.pos) << std::endl;
str << "Pointed Face: " << face << std::endl;
}
else if (target.type == Target::Type::ENTITY) {
const auto& entity = **world.l()->getActiveDimension().l()->getEntityById(target.data.entity.id).entity;
str << "Pointing At: " << (target.data.entity.id < 0 ? "Local" : "Server")
<< " Entity #" << std::fabs(target.data.entity.id) << std::endl;
str << "Pointed Position: " << floatVecToString(entity.getPos()) << std::endl;
}
else {
str << "No Target";
}
get<GuiText>("dataText")->setText(str.str());
}
int videoMemAvail, videoMemTotal;
/* Crosshair Text */ {
if (target.type == Target::Type::BLOCK) {
const auto& def = game->getDefs().blockFromId(world.l()->getActiveDimension()->getBlock(target.data.block.pos));
get<GuiText>("crosshairText")->setText(
def.name + " (" + def.identifier + ") [" + std::to_string(def.index) + "]");
}
else get<GuiText>("crosshairText")->setText("");
glGetIntegerv(0x9048, &videoMemTotal);
glGetIntegerv(0x9049, &videoMemAvail);
get<GuiLabelledGraph>("gpuGraph")->pushValue(static_cast<int>(std::round(
(videoMemTotal - videoMemAvail) / static_cast<float>(videoMemTotal) * 100.0)) / 100.0f);
// Thread information graphs
get<GuiLabelledGraph>("meshGraph")->pushValue(generatedMeshChunks);
get<GuiLabelledGraph>("interpGraph")->pushValue(interpolatedChunks);
get<GuiLabelledGraph>("genGraph")->pushValue(generatedChunks);
get<GuiLabelledGraph>("packetGraph")->pushValue(recievedPackets);
// Textual information
glm::vec3 playerPos = glm::floor(player->getPos());
glm::vec3 chunkPos = Space::Chunk::world::fromBlock(playerPos);
glm::vec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunkPos);
glm::vec3 regionPos = Space::Region::world::fromChunk(chunkPos);
glm::vec3 posOffsetFromChunk = Space::Block::relative::toChunk(playerPos);
glm::vec3 posOffsetFromBlock = Space::Block::relative::toMapBlock(playerPos);
glm::vec3 posOffsetFromRegion = Space::Block::relative::toRegion(playerPos);
std::ostringstream str;
using namespace Util;
str << "Dimension: " << world.getActiveDimension()->getIdentifier()
<< " [" << world.getActiveDimension()->getInd() << "]" << std::endl << std::endl;
str << "Pos: " << vecToString(playerPos) << " (" << floatVecToString(player->getPos()) << ")" << std::endl;
str << "Vel: " << floatVecToString(player->getVel()) << std::endl;
str << "Yaw: " << floatToString(player->getYaw()) << ", ";
str << "Pitch: " << floatToString(player->getPitch()) << std::endl << std::endl;
str << "C: " << vecToString(posOffsetFromChunk) << " [" << vecToString(chunkPos) << "]" << std::endl;
str << "M: " << vecToString(posOffsetFromBlock) << " [" << vecToString(mapBlockPos) << "]" << std::endl;
str << "R: " << vecToString(posOffsetFromRegion) << " [" << vecToString(regionPos) << "]" << std::endl
<< std::endl;
str << "Texture Slots: " << game.l()->textures.textureSlotsUsed << " / " << game.l()->textures.maxTextureSlots
<< " ("
<< round(game.l()->textures.textureSlotsUsed / static_cast<float>(game.l()->textures.maxTextureSlots) * 100)
<< "%)" << std::endl << std::endl;
str << "Biome: " << onBiomeDef.identifier << " [" << onBiomeDef.index << "]" << std::endl << std::endl;
if (target.type == Target::Type::BLOCK) {
std::string face =
target.data.block.face == EVec::TOP ? "TOP" :
target.data.block.face == EVec::BOTTOM ? "BOTTOM" :
target.data.block.face == EVec::LEFT ? "LEFT" :
target.data.block.face == EVec::RIGHT ? "RIGHT" :
target.data.block.face == EVec::FRONT ? "FRONT" :
target.data.block.face == EVec::BACK ? "BACK" :
"NONE";
const auto& def = game->getDefs().blockFromId(world.getActiveDimension()->getBlock(target.data.block.pos));
str << "Pointing At: " << def.identifier << " [" << def.index << "]" << std::endl;
str << "Pointed Position: " << vecToString(target.data.block.pos) << std::endl;
str << "Pointed Face: " << face << std::endl;
}
else if (target.type == Target::Type::ENTITY) {
const auto& entity = **world.getActiveDimension().l()->getEntityById(target.data.entity.id).entity;
str << "Pointing At: " << (target.data.entity.id < 0 ? "Local" : "Server")
<< " Entity #" << std::fabs(target.data.entity.id) << std::endl;
str << "Pointed Position: " << floatVecToString(entity.getPos()) << std::endl;
}
else str << "No Target";
get<GuiText>("dataText")->setText(str.str());
// Crosshair information
if (target.type == Target::Type::BLOCK) {
const auto& def = game->getDefs().blockFromId(world.getActiveDimension()->getBlock(target.data.block.pos));
get<GuiText>("crosshairText")->setText(
def.name + " (" + def.identifier + ") [" + std::to_string(def.index) + "]");
}
else get<GuiText>("crosshairText")->setText("");
}
void DebugGui::bufferResized(glm::vec2 bufferSize) {

View File

@ -8,15 +8,13 @@
#include "util/CovariantPtr.h"
class LocalWorld;
class LocalPlayer;
class LocalSubgame;
class LocalWorld;
class DebugGui : public GuiContainer {
public:
DebugGui(glm::vec2 bufferSize, SubgamePtr game, WorldPtr world);
public:
DebugGui(glm::vec2 bufferSize, SubgamePtr game, LocalWorld& world);
void bufferResized(glm::vec2 bufferSize);
@ -24,11 +22,13 @@ class DebugGui : public GuiContainer {
void positionElements(glm::vec2 bufferSize);
void update(std::shared_ptr<LocalPlayer> player, double fps, int chunks, int drawCalls, int ssGen, int ssPack);
void update(std::shared_ptr<LocalPlayer> player, double delta,
uint32_t interpolatedChunks, uint32_t generatedChunks, uint32_t recievedPackets,
uint32_t drawnMeshChunks, uint32_t generatedMeshChunks);
private:
private:
int displayMode;
WorldPtr world;
LocalWorld& world;
SubgamePtr game;
};

View File

@ -9,8 +9,7 @@
#include "client/graph/Renderer.h"
GameScene::GameScene(Client& client) : Scene(client),
world(std::make_shared<LocalWorld>(client.game, client.connection, client.renderer)),
debugGui(client.renderer.window.getSize(), client.game, world) {
world(std::make_shared<LocalWorld>(client.game, client.connection, client.renderer)) {
Packet r(Packet::Type::CONNECT_DATA_RECVD);
r.sendTo(client.connection.getPeer(), Packet::Channel::CONNECT);
@ -19,7 +18,7 @@ GameScene::GameScene(Client& client) : Scene(client),
client.game->init(world, world.l()->getPlayer(), client);
world.l()->updatePlayerDimension();
client.renderer.window.addResizeCallback("gamescene", Util::bind_this(&debugGui, &DebugGui::bufferResized));
// client.renderer.window.addResizeCallback("gamescene", Util::bind_this(&debugGui, &DebugGui::bufferResized));
client.renderer.setClearColor(148, 194, 240);
client.renderer.window.input.lockMouse(true);
}
@ -30,25 +29,8 @@ void GameScene::update() {
client.game->update(client.getDelta());
world->update(client.getDelta());
for (auto entity : entities) entity->update(client.getDelta());
double lastFps = 1 / client.getDelta();
debugGui.update(world.l()->getPlayer().l(), lastFps, world.l()->getActiveDimension().l()->getMeshChunkCount(),
drawCalls, world.l()->getNet().serverSideChunkGens, world.l()->getNet().recvPackets);
world.l()->getNet().serverSideChunkGens = 0;
world.l()->getNet().recvPackets = 0;
if (window.input.keyPressed(GLFW_KEY_F1)) {
hudVisible = !hudVisible;
debugGui.changeVisibilityState(hudVisible ? debugVisible ? 0 : 2 : 1);
world.l()->getPlayer().l()->setHudVisible(hudVisible);
}
if (window.input.keyPressed(GLFW_KEY_F3)) {
debugVisible = !debugVisible;
debugGui.changeVisibilityState(hudVisible ? debugVisible ? 0 : 2 : 1);
}
}
void GameScene::draw() {
@ -57,25 +39,19 @@ void GameScene::draw() {
renderer.beginChunkDeferredCalls();
renderer.enableTexture(&client.game->textures.atlasTexture);
drawCalls = world.l()->renderChunks(renderer);
world.l()->drawWorld();
renderer.beginEntityDeferredCalls();
for (auto entity : entities) entity->draw(renderer);
world.l()->renderEntities(renderer);
renderer.enableTexture(&client.game->textures.atlasTexture);
world.l()->drawEntities();
renderer.endDeferredCalls();
renderer.beginGUIDrawCalls();
renderer.enableTexture(&client.game->textures.atlasTexture);
world.l()->getPlayer().l()->drawHud(renderer);
debugGui.draw(renderer);
world.l()->getPlayer().l()->drawMenu(renderer);
world.l()->drawInterface();
renderer.swapBuffers();
}
void GameScene::cleanup() {
client.renderer.window.removeResizeCallback("gamescene");
// client.renderer.window.removeResizeCallback("gamescene");
}

View File

@ -7,17 +7,12 @@
#include "Scene.h"
#include "world/LocalWorld.h"
#include "client/gui/DebugGui.h"
#include "world/player/LocalPlayer.h"
#include "world/inv/LocalInventoryRefs.h"
#include "client/conn/ClientNetworkInterpreter.h"
class LocalSubgame;
class Drawable;
class GameScene : public Scene {
public:
public:
GameScene(Client& client);
void update() override;
@ -26,14 +21,7 @@ class GameScene : public Scene {
void cleanup() override;
public:
public:
WorldPtr world;
DebugGui debugGui;
std::vector<Drawable*> entities;
int drawCalls = 0;
bool debugVisible = true;
bool hudVisible = true;
};

View File

@ -55,13 +55,16 @@ std::vector<ChunkMeshDetails*> MeshGenStream::update() {
std::shared_ptr<Chunk> chunk = dimension.getChunk(pos);
if (chunk == nullptr) goto breakAddTask;
j.meshDetails->pos = pos;
j.thisChunk = std::shared_ptr<Chunk>(chunk);
// TODO: Is it necessary to construct the new chunk in there?
j.thisChunk = std::make_unique<Chunk>(Chunk(*chunk));
int ind = 0;
for (const glm::ivec3& dir : Vec::TO_VEC) {
std::shared_ptr<Chunk> adjacent = dimension.getChunk(pos + dir);
j.adjacentChunks[ind++] = std::shared_ptr<Chunk>(adjacent);
// TODO: Here too
j.adjacentChunks[ind++] = std::make_unique<Chunk>(Chunk(*adjacent));
if (adjacent == nullptr) goto breakAddTask;
}
@ -87,8 +90,8 @@ void MeshGenStream::Thread::exec() {
auto& u = jobs[i];
if (!u.busy) continue;
ChunkMeshGenerator m(u.meshDetails, game.getDefs(), game.getBiomes(), u.thisChunk, u.adjacentChunks,
offsetSamplers);
ChunkMeshGenerator m(u.meshDetails, game.getDefs(), game.getBiomes(),
std::move(u.thisChunk), std::move(u.adjacentChunks), offsetSamplers);
empty = false;
u.busy = false;
}

View File

@ -36,8 +36,8 @@ class MeshGenStream {
std::vector<ChunkMeshDetails*> update();
struct Job {
std::shared_ptr<Chunk> thisChunk = nullptr;
std::array<std::shared_ptr<Chunk>, 6> adjacentChunks{};
std::unique_ptr<Chunk> thisChunk = nullptr;
std::array<std::unique_ptr<Chunk>, 6> adjacentChunks {};
ChunkMeshDetails* meshDetails = new ChunkMeshDetails();

View File

@ -85,7 +85,7 @@ void WorldInterpolationStream::Thread::run() {
if (u.packet->type == Packet::Type::CHUNK) {
u.chunks.reserve(1);
u.chunks.emplace_back(std::make_shared<Chunk>());
u.chunks.back()->deserialize(u.packet->d);
u.chunks.back()->decompress(u.packet->d.data);
}
else if (u.packet->type == Packet::Type::MAPBLOCK) {
u.chunks.reserve(64);
@ -93,7 +93,7 @@ void WorldInterpolationStream::Thread::run() {
std::string dat = u.packet->d.read<std::string>();
Deserializer d(dat);
u.chunks.emplace_back(std::make_shared<Chunk>());
u.chunks.back()->deserialize(d);
u.chunks.back()->decompress(d.data);
}
}

View File

@ -12,7 +12,7 @@
#include <glm/glm.hpp>
#include <libnoise/module/modulebase.h>
class Schematic;
class Structure;
struct BiomeDef {
std::string identifier = "";
@ -30,7 +30,7 @@ struct BiomeDef {
std::vector<noise::module::Module*> heightmap;
std::vector<noise::module::Module*> volume;
std::vector<std::shared_ptr<Schematic>> schematics;
std::vector<std::shared_ptr<Structure>> schematics;
glm::vec3 tint{};
};

View File

@ -10,9 +10,9 @@
#include "MeshPart.h"
struct BlockModel {
std::array<std::vector<MeshPart>, 7> parts{};
std::vector<std::pair<MeshMod, float>> meshMods{};
std::set<std::shared_ptr<AtlasRef>> textureRefs{};
std::array<std::vector<MeshPart>, 7> parts {};
std::vector<std::pair<MeshMod, float>> meshMods {};
std::set<std::shared_ptr<AtlasRef>> textureRefs {};
bool culls = false;
bool visible = false;

237
src/lua/NoiseFromLua.cpp Normal file
View File

@ -0,0 +1,237 @@
#include "lua/Lua.h"
#include "NoiseFromLua.h"
std::vector<noise::module::Module*> NoiseFromLua::build(sol::table noise) {
std::vector<noise::module::Module*> modules;
parseNoise(modules, noise);
return std::move(modules);
}
noise::module::Module* NoiseFromLua::parseNoise(std::vector<noise::module::Module*>& modules, sol::table noise) {
std::string type = noise["module"];
// Modifer Modules
if (type == "abs") {
auto module = new noise::module::Abs();
modules.push_back(module);
return module;
}
else if (type == "clamp") {
auto module = new noise::module::Clamp();
module->SetBounds(noise.get_or<float>("low", noise::module::DEFAULT_CLAMP_LOWER_BOUND),
noise.get_or<float>("high", noise::module::DEFAULT_CLAMP_UPPER_BOUND));
modules.push_back(module);
return module;
}
else if (type == "curve") {
auto module = new noise::module::Exponent();
module->SetExponent(noise.get_or<float>("exponent", noise::module::DEFAULT_EXPONENT));
modules.push_back(module);
return module;
}
else if (type == "invert") {
auto module = new noise::module::Invert();
modules.push_back(module);
return module;
}
else if (type == "scale_bias") {
auto module = new noise::module::ScaleBias();
sol::table source = noise["source"];
auto mod = parseNoise(modules, source);
module->SetSourceModule(0, *mod);
module->SetScale(noise.get_or<float>("scale", noise::module::DEFAULT_SCALE));
module->SetBias(noise.get_or<float>("bias", noise::module::DEFAULT_BIAS));
modules.push_back(module);
return module;
}
// Combiner Modules
else if (type == "add") {
auto module = new noise::module::Add();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "max") {
auto module = new noise::module::Max();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "min") {
auto module = new noise::module::Min();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "multiply") {
auto module = new noise::module::Multiply();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "power") {
auto module = new noise::module::Power();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
// Generator modules
else if (type == "billow") {
auto module = new noise::module::Billow();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_BILLOW_OCTAVE_COUNT));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_BILLOW_FREQUENCY));
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_BILLOW_LACUNARITY));
module->SetPersistence(noise.get_or<float>("persistence", noise::module::DEFAULT_BILLOW_PERSISTENCE));
modules.push_back(module);
return module;
}
else if (type == "checkerboard") {
auto module = new noise::module::Checkerboard();
modules.push_back(module);
return module;
}
else if (type == "const") {
auto module = new noise::module::Const();
module->SetConstValue(noise.get_or<float>("value", noise::module::DEFAULT_CONST_VALUE));
modules.push_back(module);
return module;
}
else if (type == "cylinders") {
auto module = new noise::module::Cylinders();
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_CYLINDERS_FREQUENCY));
modules.push_back(module);
return module;
}
else if (type == "ridged_multi") {
auto module = new noise::module::RidgedMulti();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_RIDGED_OCTAVE_COUNT));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_RIDGED_FREQUENCY));
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_RIDGED_LACUNARITY));
modules.push_back(module);
return module;
}
else if (type == "spheres") {
auto module = new noise::module::Spheres();
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_SPHERES_FREQUENCY));
modules.push_back(module);
return module;
}
else if (type == "perlin") {
auto module = new noise::module::Perlin();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_PERLIN_OCTAVE_COUNT));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_PERLIN_FREQUENCY));
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_PERLIN_LACUNARITY));
module->SetPersistence(noise.get_or<float>("persistence", noise::module::DEFAULT_PERLIN_PERSISTENCE));
modules.push_back(module);
return module;
}
else if (type == "voronoi") {
auto module = new noise::module::Voronoi();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetDisplacement(noise.get_or<float>("displacement", 0));
module->SetFrequency(noise.get_or<float>("frequency", 0));
modules.push_back(module);
return module;
}
// Selector Modules
else if (type == "blend") {
auto module = new noise::module::Blend();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
auto control = parseNoise(modules, noise["control"]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
module->SetSourceModule(1, *control);
modules.push_back(module);
return module;
}
else if (type == "select") {
auto module = new noise::module::Select();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
auto control = parseNoise(modules, noise["control"]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
module->SetSourceModule(1, *control);
modules.push_back(module);
return module;
}
// Transformer Modules
else if (type == "turbulence") {
auto module = new noise::module::Turbulence();
sol::table source = noise["source"];
auto mod0 = parseNoise(modules, source);
module->SetSourceModule(0, *mod0);
module->SetPower(noise.get_or<float>("power", noise::module::DEFAULT_TURBULENCE_POWER));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_TURBULENCE_FREQUENCY));
module->SetRoughness(noise.get_or<float>("roughness", noise::module::DEFAULT_TURBULENCE_ROUGHNESS));
modules.push_back(module);
return module;
}
throw std::runtime_error("Invalid noise module specified.");
}

35
src/lua/NoiseFromLua.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <vector>
#include <sol/forward.hpp>
#include <libnoise/module/add.h>
#include <libnoise/module/module.h>
#include <libnoise/module/modulebase.h>
namespace NoiseFromLua {
/**
* Builds a noise module vector from a Lua noise table.
* The top level module will always be the last element in the vector.
*
* @param noise - The lua noise definition to parse.
* @returns a vector containing all of the noise modules built.
*/
std::vector<noise::module::Module*> build(sol::table noise);
/**
* Will initialize Noise::Module instances from a lua noise module definition, and recursively initialize it's
* child modules as well. All modules will be added in reverse-discovered-order to the modules vector reference
* passed in. The top level module will always be the last element in the vector.
*
* @param modules - The vector reference to insert generated noise modules into.
* @param noise - The lua noise definition to parse.
* @returns the noise module that was parsed.
*/
noise::module::Module* parseNoise(std::vector<noise::module::Module*>& modules, sol::table noise);
};

View File

@ -2,7 +2,7 @@
#include "lua/Lua.h"
#include "game/Subgame.h"
#include "util/Schematic.h"
#include "util/Structure.h"
#include "game/def/BlockDef.h"
#include "game/atlas/DefinitionAtlas.h"
@ -12,25 +12,25 @@ void Api::Module::Structure::bind() {
sol::object Api::Module::Structure::create_structure(sol::table data) {
auto origin = data.get<sol::optional<glm::vec3>>("origin");
auto schematic = data.get<sol::table>("schematic");
auto probability = data.get<float>("probability");
// auto probability = data.get<float>("probability");
auto layout = data.get<sol::table>("layout");
unsigned int yWid = schematic.size();
unsigned int zWid = schematic.get<sol::table>(1).size();
unsigned int xWid = schematic.get<sol::table>(1).get<sol::table>(1).size();
unsigned int yWid = layout.size();
unsigned int zWid = layout.get<sol::table>(1).size();
unsigned int xWid = layout.get<sol::table>(1).get<sol::table>(1).size();
auto s = std::make_shared<::Schematic>();
auto s = std::make_shared<::Structure>();
s->dimensions = { xWid, yWid, zWid };
s->origin = origin ? glm::ivec3 { *origin } : glm::ivec3 {};
s->blocks.reserve(xWid * yWid * zWid);
s->probability = probability;
s->layout.reserve(xWid * yWid * zWid);
// s->probability = probability;
for (unsigned int x = 1; x <= yWid; x++)
for (unsigned int y = 1; y <= zWid; y++)
for (unsigned int z = 1; z <= xWid; z++)
s->blocks.push_back(game.getDefs().blockFromStr(schematic.
for (unsigned int x = 1; x <= xWid; x++)
for (unsigned int y = 1; y <= yWid; y++)
for (unsigned int z = 1; z <= zWid; z++)
s->layout.push_back(game.getDefs().blockFromStr(layout.
get<sol::table>(y).get<sol::table>(z).get_or<std::string>(x, "")).index);
return sol::make_object<std::shared_ptr<Schematic>>(lua, s);
return sol::make_object<std::shared_ptr<::Structure>>(lua, s);
}

View File

@ -1,11 +1,8 @@
#pragma once
#include <libnoise/module/add.h>
#include <libnoise/module/module.h>
#include <libnoise/module/modulebase.h>
#include "lua/Lua.h"
#include "util/Util.h"
#include "lua/NoiseFromLua.h"
#include "game/LocalSubgame.h"
#include "game/ServerSubgame.h"
#include "game/def/BiomeDef.h"
@ -16,245 +13,6 @@
namespace RegisterBiome {
namespace {
/**
* Will initialize Noise::Module instances from a lua noise module definition, and recursively initialize it's
* child modules as well. All modules will be added in reverse-discovered-order to the modules vector reference
* passed in. The top level module will always be the last element in the vector.
*
* @param modules - The vector reference to insert generated noise modules into.
* @param noise - The lua noise definition to parse.
* @returns the noise module that was parsed.
*/
static noise::module::Module* parseNoise(std::vector<noise::module::Module*>& modules, sol::table noise) {
std::string type = noise["module"];
// Modifer Modules
if (type == "abs") {
auto module = new noise::module::Abs();
modules.push_back(module);
return module;
}
else if (type == "clamp") {
auto module = new noise::module::Clamp();
module->SetBounds(noise.get_or<float>("low", noise::module::DEFAULT_CLAMP_LOWER_BOUND),
noise.get_or<float>("high", noise::module::DEFAULT_CLAMP_UPPER_BOUND));
modules.push_back(module);
return module;
}
else if (type == "curve") {
auto module = new noise::module::Exponent();
module->SetExponent(noise.get_or<float>("exponent", noise::module::DEFAULT_EXPONENT));
modules.push_back(module);
return module;
}
else if (type == "invert") {
auto module = new noise::module::Invert();
modules.push_back(module);
return module;
}
else if (type == "scale_bias") {
auto module = new noise::module::ScaleBias();
sol::table source = noise["source"];
auto mod = parseNoise(modules, source);
module->SetSourceModule(0, *mod);
module->SetScale(noise.get_or<float>("scale", noise::module::DEFAULT_SCALE));
module->SetBias(noise.get_or<float>("bias", noise::module::DEFAULT_BIAS));
modules.push_back(module);
return module;
}
// Combiner Modules
else if (type == "add") {
auto module = new noise::module::Add();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "max") {
auto module = new noise::module::Max();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "min") {
auto module = new noise::module::Min();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "multiply") {
auto module = new noise::module::Multiply();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
else if (type == "power") {
auto module = new noise::module::Power();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
modules.push_back(module);
return module;
}
// Generator modules
else if (type == "billow") {
auto module = new noise::module::Billow();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_BILLOW_OCTAVE_COUNT));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_BILLOW_FREQUENCY));
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_BILLOW_LACUNARITY));
module->SetPersistence(noise.get_or<float>("persistence", noise::module::DEFAULT_BILLOW_PERSISTENCE));
modules.push_back(module);
return module;
}
else if (type == "checkerboard") {
auto module = new noise::module::Checkerboard();
modules.push_back(module);
return module;
}
else if (type == "const") {
auto module = new noise::module::Const();
module->SetConstValue(noise.get_or<float>("value", noise::module::DEFAULT_CONST_VALUE));
modules.push_back(module);
return module;
}
else if (type == "cylinders") {
auto module = new noise::module::Cylinders();
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_CYLINDERS_FREQUENCY));
modules.push_back(module);
return module;
}
else if (type == "ridged_multi") {
auto module = new noise::module::RidgedMulti();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_RIDGED_OCTAVE_COUNT));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_RIDGED_FREQUENCY));
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_RIDGED_LACUNARITY));
modules.push_back(module);
return module;
}
else if (type == "spheres") {
auto module = new noise::module::Spheres();
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_SPHERES_FREQUENCY));
modules.push_back(module);
return module;
}
else if (type == "perlin") {
auto module = new noise::module::Perlin();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetOctaveCount(noise.get_or<float>("octaves", noise::module::DEFAULT_PERLIN_OCTAVE_COUNT));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_PERLIN_FREQUENCY));
module->SetLacunarity(noise.get_or<float>("lacunarity", noise::module::DEFAULT_PERLIN_LACUNARITY));
module->SetPersistence(noise.get_or<float>("persistence", noise::module::DEFAULT_PERLIN_PERSISTENCE));
modules.push_back(module);
return module;
}
else if (type == "voronoi") {
auto module = new noise::module::Voronoi();
module->SetSeed(noise.get_or<float>("seed", 0));
module->SetDisplacement(noise.get_or<float>("displacement", 0));
module->SetFrequency(noise.get_or<float>("frequency", 0));
modules.push_back(module);
return module;
}
// Selector Modules
else if (type == "blend") {
auto module = new noise::module::Blend();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
auto control = parseNoise(modules, noise["control"]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
module->SetSourceModule(1, *control);
modules.push_back(module);
return module;
}
else if (type == "select") {
auto module = new noise::module::Select();
sol::table sources = noise["sources"];
auto mod0 = parseNoise(modules, sources[1]);
auto mod1 = parseNoise(modules, sources[2]);
auto control = parseNoise(modules, noise["control"]);
module->SetSourceModule(0, *mod0);
module->SetSourceModule(1, *mod1);
module->SetSourceModule(1, *control);
modules.push_back(module);
return module;
}
// Transformer Modules
else if (type == "turbulence") {
auto module = new noise::module::Turbulence();
sol::table source = noise["source"];
auto mod0 = parseNoise(modules, source);
module->SetSourceModule(0, *mod0);
module->SetPower(noise.get_or<float>("power", noise::module::DEFAULT_TURBULENCE_POWER));
module->SetFrequency(noise.get_or<float>("frequency", noise::module::DEFAULT_TURBULENCE_FREQUENCY));
module->SetRoughness(noise.get_or<float>("roughness", noise::module::DEFAULT_TURBULENCE_ROUGHNESS));
modules.push_back(module);
return module;
}
throw std::runtime_error("Invalid noise module specified.");
}
/**
* Registers a biome from the biomes table to the Biome Atlas.
* Generic method that works on both the client and the server.
@ -313,11 +71,11 @@ namespace RegisterBiome {
if (noiseList) {
if (noiseList->get<sol::optional<sol::table>>("heightmap"))
parseNoise(heightmapModules, noiseList->get<sol::table>("heightmap"));
NoiseFromLua::parseNoise(heightmapModules, noiseList->get<sol::table>("heightmap"));
else heightmapModules.push_back(new noise::module::Const);
if (noiseList->get<sol::optional<sol::table>>("volume"))
parseNoise(volumeModules, noiseList->get<sol::table>("volume"));
NoiseFromLua::parseNoise(volumeModules, noiseList->get<sol::table>("volume"));
else volumeModules.push_back(new noise::module::Const);
}
else {
@ -325,10 +83,10 @@ namespace RegisterBiome {
heightmapModules.push_back(new noise::module::Const);
}
std::vector<std::shared_ptr<Schematic>> schematics {};
std::vector<std::shared_ptr<Structure>> schematics {};
if (biomeTable.get<sol::optional<sol::table>>("structures"))
for (auto s : biomeTable.get<sol::table>("structures"))
schematics.push_back(s.second.as<std::shared_ptr<Schematic>>());
schematics.push_back(s.second.as<std::shared_ptr<Structure>>());
// Create biome definition
BiomeDef* biomeDef = new BiomeDef();

View File

@ -15,8 +15,7 @@ std::shared_ptr<LuaGuiElement> LuaGuiElement::create(const std::string& type, so
for (const auto& pair : data) {
if (pair.first.is<float>()) {
if (!pair.second.is<std::shared_ptr<LuaGuiElement>>())
throw std::runtime_error("Child is not a GuiElement.");
if (!pair.second.is<std::shared_ptr<LuaGuiElement>>()) continue;
elem->children.push_back(pair.second.as<std::shared_ptr<LuaGuiElement>>());
elem->children.back()->parent = elem.get();
}
@ -107,8 +106,7 @@ void LuaGuiElement::remove(sol::this_state s, sol::object elem) {
}
else if (elem.is<std::string>()) {
auto child = this->get_child(s, sol::make_object<std::string>(s, elem.as<std::string>()));
if (!child) throw std::runtime_error("Can't find child of key " + elem.as<std::string>());
remove(s, child);
if (child) remove(s, child);
}
else if (elem.is<std::shared_ptr<LuaGuiElement>>()) {
auto parent = elem.as<std::shared_ptr<LuaGuiElement>>()->parent;

View File

@ -66,10 +66,10 @@ void Server::update() {
if (!player) continue;
Packet p = Serializer()
.appendE(NetField::ID).append(player->getId())
.appendE(NetField::POS).append(player->getPos())
.appendE(NetField::LOOK_YAW).append(player->getYaw())
.appendE(NetField::LOOK_PITCH).append(player->getPitch())
.appendEnum(NetField::ID).append(player->getId())
.appendEnum(NetField::POS).append(player->getPos())
.appendEnum(NetField::LOOK_YAW).append(player->getYaw())
.appendEnum(NetField::LOOK_PITCH).append(player->getPitch())
.packet(Packet::Type::PLAYER_ENT_INFO, false);
for (auto& iter : clients.players)
@ -116,23 +116,19 @@ void Server::playerPacketReceived(PacketView& p, PlayerPtr player) {
playersUpdated.emplace(player->getId());
break;
case Packet::Type::BLOCK_HIT:
p.d.read(pos).readE(face);
case Packet::Type::BLOCK_HIT: p.d.read(pos).readEnum(face);
player->getDim()->blockHit(Target(player->getDim(), pos, face), player);
break;
case Packet::Type::BLOCK_PLACE:
p.d.read(pos).readE(face);
case Packet::Type::BLOCK_PLACE: p.d.read(pos).readEnum(face);
player->getDim()->blockPlace(Target(player->getDim(), pos, face), player);
break;
case Packet::Type::BLOCK_INTERACT:
p.d.read(pos).readE(face);
case Packet::Type::BLOCK_INTERACT: p.d.read(pos).readEnum(face);
player->getDim()->blockInteract(Target(player->getDim(), pos, face), player);
break;
case Packet::Type::BLOCK_PLACE_OR_INTERACT:
p.d.read(pos).readE(face);
case Packet::Type::BLOCK_PLACE_OR_INTERACT: p.d.read(pos).readEnum(face);
player->getDim()->blockPlaceOrInteract(Target(player->getDim(), pos, face), player);
break;

View File

@ -59,13 +59,13 @@ void ServerClients::createPlayer(std::shared_ptr<ServerClient> client, Dimension
players.push_back(player);
game.s()->getParser().playerConnected(player);
player->setPos({ 0, -32, 0 }, true);
player->setPos({ 32, -20, 32 }, true);
Serializer()
.appendE(NetField::ID).append(player->getId())
.appendE(NetField::POS).append(player->getPos())
.appendE(NetField::LOOK_PITCH).append(player->getPitch())
.appendE(NetField::LOOK_YAW).append(player->getYaw())
.appendEnum(NetField::ID).append(player->getId())
.appendEnum(NetField::POS).append(player->getPos())
.appendEnum(NetField::LOOK_PITCH).append(player->getPitch())
.appendEnum(NetField::LOOK_YAW).append(player->getYaw())
.packet(Packet::Type::THIS_PLAYER_INFO).sendTo(player->getPeer(), Packet::Channel::INTERACT);
}

View File

@ -69,7 +69,7 @@ void ServerPacketStream::Thread::run() {
Serializer s{};
for (unsigned int i = 0; i < 64; i++) {
auto chunk = mapBlock->get(i);
if (chunk) s.append(chunk->serialize());
if (chunk) s.append(chunk->compress());
}
j.packet = std::make_unique<Packet>(Packet::Type::MAPBLOCK);

View File

@ -4,8 +4,9 @@
#pragma once
#include <iostream>
#include <array>
#include <vector>
#include <iostream>
#include <type_traits>
struct RIE {
@ -180,4 +181,23 @@ struct RIE {
out.push_back(len);
out.push_back(block);
}
template<typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
static void encode(const std::vector<T>& array, std::vector<T>& out) {
T len = 1;
T block = array[0];
for (unsigned int i = 1; i < array.size(); i++) {
T newBlock = array[i];
if (newBlock != block) {
out.push_back(len);
out.push_back(block);
block = newBlock;
len = 0;
}
len++;
}
out.push_back(len);
out.push_back(block);
}
};

View File

@ -2,12 +2,12 @@
// Created by aurailus on 2020-02-01.
//
#include "Schematic.h"
#include "Structure.h"
#include "game/def/BlockDef.h"
#include "game/atlas/DefinitionAtlas.h"
glm::ivec3 Schematic::getOffset(unsigned int ind) {
glm::ivec3 Structure::getOffset(unsigned int ind) {
glm::ivec3 vec{};
vec.z = ind / (dimensions.x * dimensions.y);
@ -18,6 +18,6 @@ glm::ivec3 Schematic::getOffset(unsigned int ind) {
return vec;
}
unsigned int Schematic::index(const glm::ivec3& vec) {
unsigned int Structure::index(const glm::ivec3& vec) {
return static_cast<unsigned int>(vec.x + dimensions.x * (vec.y + dimensions.y * vec.z));
}

View File

@ -10,16 +10,14 @@
class DefinitionAtlas;
struct Schematic {
struct Structure {
glm::ivec3 origin {};
glm::ivec3 dimensions {};
std::vector<unsigned int> blocks {};
std::vector<unsigned int> layout {};
float probability = 0;
inline unsigned int length() {
return blocks.size();
}
inline unsigned int length() { return layout.size(); }
glm::ivec3 getOffset(unsigned int ind);

View File

@ -12,8 +12,8 @@
#include "Space.h"
enum class EVec {
LEFT = 0, RIGHT = 1, BOTTOM = 2, TOP = 3, FRONT = 4, BACK = 5,
XNEG = 0, XPOS = 1, YNEG = 2, YPOS = 3, ZPOS = 4, ZNEG = 5,
LEFT = 0, RIGHT = 1, BOTTOM = 2, TOP = 3, BACK = 4, FRONT = 5,
XNEG = 0, XPOS = 1, YNEG = 2, YPOS = 3, ZNEG = 4, ZPOS = 5,
INVALID = -1, NONE = -1,
NO_CULL = 6,
@ -44,17 +44,29 @@ namespace Vec {
// Adjacent Arrays & Maps
const static std::array<glm::ivec3, 6> TO_VEC = {
glm::ivec3{ -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 }, { 0, 0, -1 }, { 0, 0, 1 }};
glm::ivec3 { -1, 0, 0 },
glm::ivec3 { 1, 0, 0 },
glm::ivec3 { 0, -1, 0 },
glm::ivec3 { 0, 1, 0 },
glm::ivec3 { 0, 0, -1 },
glm::ivec3 { 0, 0, 1 }
};
const static std::array<glm::ivec3, 6> TO_VEC_R = {
glm::ivec3{ 1, 0, 0 }, { -1, 0, 0 }, { 0, 1, 0 }, { 0, -1, 0 }, { 0, 0, 1 }, { 0, 0, -1 }};
glm::ivec3 { 1, 0, 0 },
glm::ivec3 { -1, 0, 0 },
glm::ivec3 { 0, 1, 0 },
glm::ivec3 { 0, -1, 0 },
glm::ivec3 { 0, 0, 1 },
glm::ivec3 { 0, 0, -1 }
};
const static std::unordered_map<glm::ivec3, EVec, Vec::ivec3> TO_ENUM = {
{ TO_VEC[0], EVec::LEFT }, { TO_VEC[1], EVec::RIGHT }, { TO_VEC[2], EVec::BOTTOM },
{ TO_VEC[3], EVec::TOP }, { TO_VEC[4], EVec::FRONT }, { TO_VEC[5], EVec::BACK }};
{ TO_VEC[0], EVec::XNEG }, { TO_VEC[1], EVec::XPOS }, { TO_VEC[2], EVec::YNEG },
{ TO_VEC[3], EVec::YPOS }, { TO_VEC[4], EVec::ZNEG }, { TO_VEC[5], EVec::ZPOS }};
const static std::unordered_map<glm::ivec3, EVec, Vec::ivec3> TO_ENUM_R = {
{ TO_VEC[0], EVec::RIGHT }, { TO_VEC[1], EVec::LEFT }, { TO_VEC[2], EVec::TOP },
{ TO_VEC[3], EVec::BOTTOM }, { TO_VEC[4], EVec::BACK }, { TO_VEC[5], EVec::FRONT }};
{ TO_VEC[0], EVec::XPOS }, { TO_VEC[1], EVec::XNEG }, { TO_VEC[2], EVec::YPOS },
{ TO_VEC[3], EVec::YNEG }, { TO_VEC[4], EVec::ZPOS }, { TO_VEC[5], EVec::ZNEG }};
};

View File

@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include <stdexcept>
@ -11,7 +12,7 @@
#include <glm/vec3.hpp>
class Deserializer {
public:
public:
Deserializer(const std::string& data) : data(&data[0]), len(data.length()) {};
Deserializer(const char* start, size_t len) : data(start), len(len) {};
@ -25,13 +26,45 @@ class Deserializer {
return *this;
};
template<typename T, int L>
inline std::array<T, L> readArr() {
auto oldInd = ind;
ind += L * sizeof(T);
std::array<T, L> res;
for (int i = 0; i < L; i++) res[i] = *reinterpret_cast<const T*>(&data[oldInd + i * sizeof(T)]);
return res;
}
template<typename T, int L>
inline Deserializer& readArr(std::array<T, L>& ref) {
ref = readArr<T, L>();
return *this;
}
template<typename T>
inline std::vector<T> readVec() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len * sizeof(T);
return std::vector<T>(
reinterpret_cast<const T*>(&data[oldInd]),
reinterpret_cast<const T*>(&data[ind]));
}
template<typename T>
inline Deserializer& readVec(std::vector<T>& ref) {
ref = readVec<T>();
return *this;
}
template<typename E, typename = typename std::enable_if<std::is_enum<E>::value>::type>
inline E readE() {
inline E readEnum() {
return static_cast<E>(read<unsigned short>());
};
template<typename E, typename = typename std::enable_if<std::is_enum<E>::value>::type>
inline Deserializer& readE(E& ref) {
inline Deserializer& readEnum(E& ref) {
ref = static_cast<E>(read<unsigned short>());
return *this;
};
@ -44,27 +77,32 @@ class Deserializer {
size_t len;
size_t ind = 0;
private:
private:
typedef union {
int ln;
char bytes[8];
} long_long_union;
typedef union {
int in;
char bytes[4];
} int_union;
typedef union {
unsigned int in;
char bytes[4];
} uint_union;
typedef union {
short sh;
char bytes[2];
} short_union;
typedef union {
unsigned short sh;
char bytes[2];
} ushort_union;
typedef union {
float fl;
char bytes[4];
@ -192,85 +230,4 @@ inline glm::ivec3 Deserializer::read<glm::ivec3>() {
read<int>(),
read<int>()
};
}
template<>
inline std::vector<int> Deserializer::read<std::vector<int>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len * 4;
return std::vector<int>(
reinterpret_cast<const int*>(&data[oldInd]),
reinterpret_cast<const int*>(&data[ind]));
}
template<>
inline std::vector<unsigned int> Deserializer::read<std::vector<unsigned int>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len * 4;
return std::vector<unsigned int>(
reinterpret_cast<const unsigned int*>(&data[oldInd]),
reinterpret_cast<const unsigned int*>(&data[ind]));
}
template<>
inline std::vector<short> Deserializer::read<std::vector<short>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len * 2;
return std::vector<short>(
reinterpret_cast<const short*>(&data[oldInd]),
reinterpret_cast<const short*>(&data[ind]));
}
template<>
inline std::vector<unsigned short> Deserializer::read<std::vector<unsigned short>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len * 2;
return std::vector<unsigned short>(
reinterpret_cast<const unsigned short*>(&data[oldInd]),
reinterpret_cast<const unsigned short*>(&data[ind]));
}
template<>
inline std::vector<char> Deserializer::read<std::vector<char>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len;
return std::vector<char>(
reinterpret_cast<const char*>(&data[oldInd]),
reinterpret_cast<const char*>(&data[ind]));
}
template<>
inline std::vector<unsigned char> Deserializer::read<std::vector<unsigned char>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len;
return std::vector<unsigned char>(
reinterpret_cast<const unsigned char*>(&data[oldInd]),
reinterpret_cast<const unsigned char*>(&data[ind]));
}
template<>
inline std::vector<float> Deserializer::read<std::vector<float>>() {
unsigned int len = read<unsigned int>();
auto oldInd = ind;
ind += len * 4;
return std::vector<float>(
reinterpret_cast<const float*>(&data[oldInd]),
reinterpret_cast<const float*>(&data[ind]));
}
template<>
inline std::vector<std::string> Deserializer::read<std::vector<std::string>>() {
unsigned int len = read<unsigned int>();
std::vector<std::string> v{};
v.reserve(len);
for (unsigned int i = 0; i < len; i++) {
v.push_back(read<std::string>());
}
return std::move(v);
}

View File

@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include <stdexcept>
@ -14,14 +15,29 @@
#include "Packet.h"
class Serializer {
public:
public:
std::string data{};
template<typename T>
inline Serializer& append(const T& elem) { throw std::runtime_error("Tried to append a non-serializable type"); };
template<typename T, size_t L>
inline Serializer& appendArr(const std::array<T, L>& elem) {
data.reserve(data.length() + elem.size() * sizeof(T));
data += std::string { reinterpret_cast<const char*>(&elem[0]), elem.size() * sizeof(T) };
return *this;
}
template<typename T>
inline Serializer& appendVec(const std::vector<T>& elem) {
data.reserve(data.length() + elem.size() * sizeof(T) + 4);
append<unsigned int>(elem.size());
data += std::string { reinterpret_cast<const char*>(&elem[0]), elem.size() * sizeof(T) };
return *this;
}
template<typename E, typename = typename std::enable_if<std::is_enum<E>::value>::type>
inline Serializer& appendE(const E& elem) {
inline Serializer& appendEnum(const E& elem) {
append<unsigned short>(static_cast<unsigned short>(elem));
return *this;
}
@ -32,27 +48,32 @@ class Serializer {
return std::move(packet);
};
private:
private:
typedef union {
long long ln;
char bytes[8];
} long_long_union;
typedef union {
int in;
char bytes[4];
} int_union;
typedef union {
unsigned int in;
char bytes[4];
} uint_union;
typedef union {
short sh;
char bytes[2];
} short_union;
typedef union {
unsigned short sh;
char bytes[2];
} ushort_union;
typedef union {
float fl;
char bytes[4];
@ -172,69 +193,4 @@ inline Serializer& Serializer::append<glm::ivec3>(const glm::ivec3& elem) {
append<int>(elem.y);
append<int>(elem.z);
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<int>>(const std::vector<int>& elem) {
data.reserve(data.length() + elem.size() * 4 + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() * 4 };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<unsigned int>>(const std::vector<unsigned int>& elem) {
data.reserve(data.length() + elem.size() * 4 + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() * 4 };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<short>>(const std::vector<short>& elem) {
data.reserve(data.length() + elem.size() * 2 + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() * 2 };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<unsigned short>>(const std::vector<unsigned short>& elem) {
data.reserve(data.length() + elem.size() * 2 + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() * 2 };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<char>>(const std::vector<char>& elem) {
data.reserve(data.length() + elem.size() + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<unsigned char>>(const std::vector<unsigned char>& elem) {
data.reserve(data.length() + elem.size() + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<float>>(const std::vector<float>& elem) {
data.reserve(data.length() + elem.size() * 4 + 4);
append<unsigned int>(elem.size());
data += std::string{ reinterpret_cast<const char*>(&elem[0]), elem.size() * 4 };
return *this;
}
template<>
inline Serializer& Serializer::append<std::vector<std::string>>(const std::vector<std::string>& elem) {
append<unsigned int>(elem.size());
for (unsigned int i = 0; i < elem.size(); i++) {
append<std::string>(elem[i]);
}
return *this;
}

View File

@ -11,8 +11,9 @@
LocalWorld::LocalWorld(SubgamePtr game, ServerConnection& conn, Renderer& renderer) :
World(game),
net(conn, *this),
renderer(renderer),
net(conn, *this),
debugGui(renderer.window.getSize(), game, *this),
refs(std::make_shared<LocalInventoryRefs>(game, net)),
worldGenStream(std::make_shared<WorldInterpolationStream>(*game.l(), *this, 55)),
player(std::make_shared<LocalPlayer>(game, *this, DimensionPtr(nullptr), renderer)) {}
@ -32,13 +33,41 @@ bool LocalWorld::updatePlayerDimension() {
void LocalWorld::update(double delta) {
World::update(delta);
// Update children
if (*player) player.l()->update(renderer.window.input, delta, renderer.window.input.mouseDelta());
refs->update(delta, net);
net.update();
// Commit interpolated mapblocks
auto finishedChunks = worldGenStream->update();
mapBlocksInterpolated = finishedChunks->size() / 64;
lastInterpolations = finishedChunks->size() / 64;
for (const auto& chunk : *finishedChunks) commitChunk(chunk);
// Update debug interface
debugGui.update(
player.l(), delta,
lastInterpolations,
net.serverSideChunkGens, net.recvPackets,
activeDimension->getMeshChunksDrawn(),
activeDimension->getMeshChunksCommitted());
// Toggle regular interface
if (renderer.window.input.keyPressed(GLFW_KEY_F1)) {
hudVisible = !hudVisible;
debugGui.changeVisibilityState(hudVisible ? debugVisible ? 0 : 2 : 1);
player.l()->setHudVisible(hudVisible);
}
// Toggle debug interface
if (renderer.window.input.keyPressed(GLFW_KEY_F3)) {
debugVisible = !debugVisible;
debugGui.changeVisibilityState(hudVisible ? debugVisible ? 0 : 2 : 1);
}
}
void LocalWorld::handleWorldPacket(std::unique_ptr<PacketView> p) {
@ -115,11 +144,17 @@ InventoryRefsPtr LocalWorld::getRefs() {
return refs;
}
int LocalWorld::renderChunks(Renderer& renderer) {
return activeDimension->renderChunks(renderer);
void LocalWorld::drawWorld() {
activeDimension->renderChunks(renderer);
}
void LocalWorld::renderEntities(Renderer& renderer) {
void LocalWorld::drawEntities() {
activeDimension->renderEntities(renderer);
player.l()->draw(renderer);
}
}
void LocalWorld::drawInterface() {
player.l()->drawHud(renderer);
debugGui.draw(renderer);
player.l()->drawMenu(renderer);
}

View File

@ -6,19 +6,15 @@
#include "World.h"
#include "client/gui/DebugGui.h"
#include "world/dim/LocalDimension.h"
#include "client/conn/ClientNetworkInterpreter.h"
class Window;
class Renderer;
class LocalPlayer;
class LocalSubgame;
class LocalInventoryRefs;
class WorldInterpolationStream;
class LocalWorld : public World {
@ -54,12 +50,12 @@ public:
ClientNetworkInterpreter& getNet();
int renderChunks(Renderer& render);
void renderEntities(Renderer& renderer);
int mapBlocksInterpolated = 0;
int lastMeshUpdates = 0;
/** Renders the visible block chunks to the screen. */
void drawWorld();
/** Renders the visible entities to the screen. */
void drawEntities();
/** Renders non-diagetic (UI) elements to the screen using an orthographic projection. */
void drawInterface();
private:
Renderer& renderer;
@ -68,7 +64,12 @@ private:
std::shared_ptr<LocalInventoryRefs> refs;
PlayerPtr player;
std::shared_ptr<LocalDimension> activeDimension = nullptr;
DebugGui debugGui;
uint32_t lastInterpolations = 0;
bool hudVisible = true;
bool debugVisible = true;
std::shared_ptr<LocalDimension> activeDimension = nullptr;
std::shared_ptr<WorldInterpolationStream> worldGenStream = nullptr;
};

View File

@ -219,7 +219,7 @@ void LocalDimension::serverEntitiesInfo(Deserializer& e) {
}
while (!d.atEnd()) {
const auto field = d.readE<NetField>();
const auto field = d.readEnum<NetField>();
switch (field) {
default:
std::cout << Log::err << "Entity received unhandled NetField, Type "
@ -311,16 +311,15 @@ Api::Usertype::Entity& LocalDimension::getEntityById(long long id) {
return *entityRefs.at(id);
}
int LocalDimension::renderChunks(Renderer& renderer) {
int count = 0;
void LocalDimension::renderChunks(Renderer& renderer) {
lastMeshesDrawn = 0;
for (auto& renderElement : renderElems) {
FrustumAABB bbox(renderElement->getPos() * glm::vec3(16), glm::vec3(16));
if (renderer.camera.inFrustum(bbox) != Frustum::OUTSIDE) {
renderElement->draw(renderer);
count++;
lastMeshesDrawn++;
}
}
return count;
}
void LocalDimension::renderEntities(Renderer& renderer) {
@ -328,8 +327,12 @@ void LocalDimension::renderEntities(Renderer& renderer) {
for (auto& entity : playerEntities) entity.draw(renderer);
}
int LocalDimension::getMeshChunkCount() {
return static_cast<int>(renderElems.size());
uint32_t LocalDimension::getMeshChunksDrawn() {
return lastMeshesDrawn;
}
uint32_t LocalDimension::getMeshChunksCommitted() {
return lastMeshesCommitted;
}
std::unordered_set<glm::ivec3, Vec::ivec3> LocalDimension::propogateAddNodes() {
@ -345,7 +348,7 @@ std::unordered_set<glm::ivec3, Vec::ivec3> LocalDimension::propogateRemoveNodes(
}
void LocalDimension::finishMeshes() {
lastMeshUpdates = 0;
lastMeshesCommitted = 0;
auto finishedMeshes = meshGenStream->update();
for (ChunkMeshDetails* meshDetails : finishedMeshes) {
@ -355,7 +358,7 @@ void LocalDimension::finishMeshes() {
meshChunk->setPos(meshDetails->pos);
setMeshChunk(meshChunk);
lastMeshUpdates++;
lastMeshesCommitted++;
}
else removeMeshChunk(meshDetails->pos);

View File

@ -66,13 +66,15 @@ public:
Api::Usertype::Entity& getEntityById(long long id);
int renderChunks(Renderer& renderer);
void renderChunks(Renderer& renderer);
void renderEntities(Renderer& renderer);
int getMeshChunkCount();
uint32_t getMeshChunksDrawn();
uint32_t getMeshChunksCommitted();
int lastMeshUpdates = 0;
int lastMeshesDrawn = 0;
int lastMeshesCommitted = 0;
std::vector<PlayerEntity> playerEntities;
protected:

View File

@ -5,6 +5,7 @@
#include <gzip/compress.hpp>
#include <gzip/decompress.hpp>
#include <gzip/utils.hpp>
#include <util/RIE.h>
#include "Chunk.h"
@ -14,122 +15,100 @@
#include "game/atlas/DefinitionAtlas.h"
Chunk::Chunk(const Chunk& o) :
pos(o.pos), state(o.state),
blocks(o.blocks), biomes(o.biomes),
sunLight(o.sunLight), blockLight(o.blockLight),
dirty(o.dirty), shouldRender(o.shouldRender),
renderableBlocks(o.renderableBlocks) {}
pos(o.pos),
dirty(true),
renderableBlocks(o.renderableBlocks),
generationState(o.generationState),
compressionState(o.compressionState),
c(o.c) {
if (d != nullptr) *d = *o.d;
}
Chunk::Chunk(glm::ivec3 pos, bool partial) : pos(pos), state(partial ? State::PARTIAL : State::EMPTY) {}
Chunk::Chunk(glm::ivec3 pos, bool partial) :
pos(pos),
d(new ChunkData()),
compressionState(CompressionState::DECOMPRESSED),
generationState(partial ? GenerationState::PARTIAL : GenerationState::EMPTY) {}
Chunk::Chunk(glm::ivec3 pos, const std::vector<unsigned int>& blocks, const std::vector<unsigned short>& biomes) :
blocks(std::move(blocks)), biomes(std::move(biomes)),
state(State::GENERATED), pos(pos) {
countRenderableBlocks();
Chunk::Chunk(const std::string& data) : c(data) {}
Chunk::~Chunk() {
if (compressionState == CompressionState::DECOMPRESSED) delete d;
}
bool Chunk::setBlock(unsigned int ind, unsigned int blk) {
if (!RIE::write(ind, blk, blocks, 4096)) return false;
assertDecompressed();
if (ind > 4096) throw ChunkException(pos, "Index out of range.");
if (blk == DefinitionAtlas::AIR) {
renderableBlocks = std::max(renderableBlocks - 1, 0);
if (renderableBlocks == 0) shouldRender = false;
}
else {
shouldRender = true;
renderableBlocks++;
}
if (d->blocks[ind] == blk) return false;
d->blocks[ind] = blk;
if (blk == DefinitionAtlas::AIR) renderableBlocks = std::max(renderableBlocks - 1, 0);
else renderableBlocks++;
return true;
}
const std::vector<unsigned int>& Chunk::cGetBlocks() const {
return blocks;
const std::array<unsigned int, 4096>& Chunk::getBlocksArray() const {
assertDecompressed();
return d->blocks;
}
const std::vector<unsigned short>& Chunk::cGetBiomes() const {
return biomes;
const std::array<unsigned short, 4096>& Chunk::getBiomesArray() const {
assertDecompressed();
return d->biomes;
}
void Chunk::combineWith(std::shared_ptr<Chunk> o) {
// TODO: Leverage the RIE streams to make this more efficient.
for (unsigned int i = 0; i < 4096; i++) {
for (unsigned int i = 0; i < 4096; i++)
if (o->getBlock(i) > DefinitionAtlas::INVALID) setBlock(i, o->getBlock(i));
}
if (state == State::GENERATED || o->isGenerated()) {
state = State::GENERATED;
if (generationState == GenerationState::GENERATED || o->isGenerated()) {
generationState = GenerationState::GENERATED;
countRenderableBlocks();
}
else state = State::PARTIAL;
else generationState = GenerationState::PARTIAL;
}
std::string Chunk::serialize() {
std::vector<unsigned short> blArray = std::vector<unsigned short>(4096);
std::vector<unsigned char> slArray = std::vector<unsigned char>(2048);
for (unsigned short i = 0; i < 4096; i++) {
blocklight_union bl;
bl.b = blockLight[i];
blArray[i] = bl.sh;
}
for (unsigned short i = 0; i < 2048; i++) {
sunlight_union sl;
sl.s = sunLight[i];
slArray[i] = sl.ch;
}
std::string Chunk::compress() {
Serializer s;
std::string temp = Serializer().append(pos).append(blocks).append(biomes).append(blArray).append(slArray).data;
std::vector<unsigned int> blocksRIE = {};
std::vector<unsigned short> biomesRIE = {};
RIE::encode<unsigned int, 4096>(d->blocks, blocksRIE);
RIE::encode<unsigned short, 4096>(d->biomes, biomesRIE);
std::string temp = Serializer().append(pos).appendVec(blocksRIE).appendVec(biomesRIE)
.appendArr(d->blockLight).appendArr(d->sunLight).data;
s.append<std::string>(gzip::compress(temp.data(), temp.size()));
return s.data;
}
void Chunk::deserialize(Deserializer& d) {
std::string gzipped = d.read<std::string>();
void Chunk::decompress(const std::string& data) {
const auto& toDecompress = (data.length() ? data : c);
std::string gzipped = Deserializer(toDecompress).read<std::string>();
if (!gzip::is_compressed(gzipped.data(), gzipped.length()))
throw std::runtime_error("Chunk contains invalid gzipped data.");
std::vector<unsigned char> slArray {};
std::vector<unsigned short> blArray {};
c = "";
d = new ChunkData {};
compressionState = CompressionState::DECOMPRESSED;
Deserializer(gzip::decompress(gzipped.data(), gzipped.length()))
.read<glm::ivec3>(pos)
.read<std::vector<unsigned int>>(blocks)
.read<std::vector<unsigned short>>(biomes)
.read<std::vector<unsigned short>>(blArray)
.read<std::vector<unsigned char>>(slArray);
for (unsigned short i = 0; i < 4096; i++) {
blocklight_union bl;
bl.sh = blArray[i];
blockLight[i] = bl.b;
}
for (unsigned short i = 0; i < 2048; i++) {
sunlight_union sl;
sl.ch = slArray[i];
sunLight[i] = sl.s;
}
.readArr<unsigned int, 4096>(d->blocks)
.readArr<unsigned short, 4096>(d->biomes)
.readArr<BlockLight, 4096>(d->blockLight)
.readArr<SunLight, 2048>(d->sunLight);
countRenderableBlocks();
}
void Chunk::countRenderableBlocks() {
shouldRender = false;
renderableBlocks = 0;
for (unsigned int i = 0; i < blocks.size(); i += 2) {
unsigned int nInd = (i == blocks.size() - 2 ? 4095 : blocks[i + 2]);
unsigned int cInd = blocks[i];
if (blocks[i + 1] > DefinitionAtlas::AIR) {
renderableBlocks += nInd - cInd;
shouldRender = true;
}
}
for (int i = 0; i < d->blocks.size(); i++)
if (d->blocks[i] != DefinitionAtlas::AIR) renderableBlocks++;
}

View File

@ -3,354 +3,215 @@
#include <mutex>
#include <array>
#include <vector>
#include <memory>
#include <glm/vec3.hpp>
#include "util/Lockable.h"
#include "util/RIE.h"
#include "util/Space.h"
class Deserializer;
class ChunkException : public std::exception {
private:
glm::ivec3 pos;
std::string errorMessage;
public:
ChunkException(glm::ivec3 pos, std::string errorMessage):
pos(pos), errorMessage(errorMessage) {}
const char* what() const throw() {
return errorMessage.c_str();
}
};
/**
* A single chunk.
* Represents a 16^3 region of blocks in the world.
* Implements Lockable. Must be manually locked when being used across threads.
* A block chunk that stores a 16^3 region of world data.
* Can be compressed, any attempts to access data in the chunk will decompress it,
* and the dimension will automatically compress inactive chunks.
*/
class Chunk : public Lockable {
class Chunk {
public:
friend class MapGen;
/**
* An enum for indicating the state of a Chunk.
*/
/** An enum for the compression state of the chunk. */
enum class CompressionState { COMPRESSED, DECOMPRESSED };
enum class State {
EMPTY, PARTIAL, GENERATED
};
/** An enum for indicating the generation state of a Chunk. */
enum class GenerationState { EMPTY, PARTIAL, GENERATED };
/**
* A struct for storing Block lighting at a position.
* Uses a bitfield, where each light channel is allocated 5 bits.
* Each channel can have an intensity from 0 (off), to 31 (full).
* Each channel can have an intensity from 0 (none), to 31 (full).
*/
struct BlockLight {
/** The red channel */
unsigned char r: 5;
/** The green channel */
unsigned char g: 5;
/** The blue channel */
unsigned char b: 5,
/** A leftover bit, declared to make the struct use an even 16. */
: 1;
/** RGB values, and an extra bit, to make the struct an even 2 bytes. */
unsigned char r: 5, g: 5, b: 5, : 1;
};
/**
* A union for accessing a short as a BlockLight struct, or vice versa.
*/
/** A union for accessing a short as a BlockLight struct, or vice versa. */
typedef union {
short sh;
BlockLight b;
} blocklight_union;
/**
* A struct for storing Sunlight at two positions.
* Sunlight intensity ranges from 0 (none) to 15 (full), which can fit in 4 bits,
* so each SunLight struct can store two positions of sunlight.
* Sunlight with a maximum light value of 15 will cascade downwards infinitely, without losing intensity.
* A struct for storing Sunlight at two positions. Ranges from 0 (none) to 15 (full),
* so two values can be stored in one byte. Sunlight with a value of 15 will
* cascade downwards infinitely without losing intensity.
*/
struct SunLight {
/** The odd positioned light value */
unsigned char a: 4;
/** The even positioned light value */
unsigned char b: 4;
/** The two sunlight values stored in a struct. */
unsigned char a: 4, b: 4;
};
/**
* A union for accessing a char as a Sunlight struct, or vice versa.
*/
/** A union for accessing a char as a Sunlight struct, or vice versa. */
typedef union {
char ch;
SunLight s;
} sunlight_union;
/**
* Initialize an empty, ungenerated chunk containing only INVALID.
* Used in Map Generation.
*/
Chunk() = default;
/**
* A simple copy constructor.
*
* @param o - The chunk to copy.
*/
/** Copy constructor, clones the chunk. */
Chunk(const Chunk& o);
/**
* Basic chunk pos constructor, initializes an empty, ungenerated chunk
* that can optionally be identified as a partial.
*
* @param pos - The position of the Chunk in its dimension.
* @param partial - True if the chunk is a MapGen partial.
*/
/** Basic chunk constructor, initializes an empty, ungenerated chunk. */
Chunk(glm::ivec3 pos = glm::ivec3(0), bool partial = false);
Chunk(glm::ivec3 pos, bool partial = false);
/** Creates a chunk with the compressed data specified. */
Chunk(const std::string& data);
/**
* Initializes a generated chunk with the blocks and biomes RIE arrays specified.
* Used in Chunk deserialization from the server.
*
* @param pos - The position of the Chunk in its dimension.
* @param blocks - An RIE array of block positions.
* @param biomes - An RIE array of biome positions.
*/
Chunk(glm::ivec3 pos, const std::vector<unsigned int>& blocks, const std::vector<unsigned short>& biomes);
/**
* Get the position of the chunk.
*
* @returns the position of the chunk.
*/
/** Destroys chunk data pointer. */
~Chunk();
/** Returns the position of the chunk. */
inline glm::ivec3 getPos() const;
/**
* Set the position of the chunk.
*
* @param newPos - The new position of the chunk.
*/
/** Sets the position of the chunk. */
inline void setPos(glm::ivec3 newPos);
/**
* Get the chunk's dirty state, which is whether it needs to be remeshed or not.
* Returns the chunk's dirty state, which is whether it needs to be remeshed or not.
* This value is only set through other classes using setDirty.
*
* @returns if the chunk is dirty.
*/
inline bool isDirty() const;
/**
* Indicate that a chunk needs to be remeshed.
*
* @param isDirty - If the chunk is dirty.
*/
/** Set a chunks dirty state, which indicates that it needs to be remeshed. */
inline void setDirty(bool isDirty);
/**
* Indicates whether or not the chunk should render,
* which will be true if it contains renderable blocks, or false otherwise.
*
* @returns if the chunk should render.
*/
/** Returns a boolean indicating if the chunk needs a visual representation in the world. */
inline bool chunkShouldRender() const;
/**
* Returns whether or not the chunk is a partial.
* A partial is a chunk that has not been fully generated, often only containing structure data.
* Ungenerated material is filled with INVALID.
*
* @returns if the chunk is a partial.
*/
/** Returns a boolean indicating if the chunk is a partial. */
// TODO: Partials should not be stored as chunks and should go away.
[[maybe_unused]] inline bool isPartial() const;
/**
* Returns whether or not the chunk has been fully generated.
* A fully generated chunk is full of its own materials and structures,
* but it may still be manipulated later by structures generated nearby.
*
* @returns if the chunk has been generated.
*/
inline bool isGenerated() const;
/**
* Gets the block ID at the index specified.
*
* @param ind - The index to get the block at.
* @returns the block ID at the requested index.
*/
/** Returns the block ID at the index specified. */
inline unsigned int getBlock(unsigned int ind) const;
/**
* Sets the block ID at the index specified.
*
* @param ind - The index to set the block at.
* @param blk - The block ID to set the block to.
* @returns a boolean indicating if the block replaced a *different* block.
* @returns a boolean indicating if the newly placed block is different than the old one.
*/
bool setBlock(unsigned int ind, unsigned int blk);
/**
* Gets the block ID at the requested local position.
*
* @param pos - The position to get the block at.
* @returns the block ID at the requested position.
*/
/** Returns the block ID at the position specified, wrapping to local coordinates. */
inline unsigned int getBlock(const glm::ivec3& pos) const;
/**
* Sets the block ID at the requested local position.
*
* @param pos - The position to set the block at.
* @param blk - The block ID to set the block to.
* @returns a boolean indicating if the block replaced a *different* block.
* Sets the block ID at the position specified, wrapping to local coordinates.
* @returns a boolean indicating if the newly placed block is different than the old one.
*/
inline bool setBlock(const glm::ivec3& pos, unsigned int blk);
/**
* Gets the biome ID at the index specified.
*
* @param ind - The index to get the block at.
* @returns the biome ID of the biome at the requested index.
*/
/** Gets the biome ID at the index specified. */
inline unsigned short getBiome(unsigned int ind) const;
/**
* Sets the biome ID at the index specified.
*
* @param ind - The index to set the biome at.
* @param blk - The biome ID to set the biome to.
* @returns a boolean indicating if the biome replaced a *different* biome.
* @returns a boolean indicating if the newly placed biome is different than the old one.
*/
inline bool setBiome(unsigned int ind, unsigned short bio);
/**
* Gets the biome ID at the local position specified.
*
* @param pos - The position to get the block at.
* @returns the biome ID of the biome at the requested index.
*/
/** Returns the biome ID at the position specified, wrapping to local coordinates. */
inline unsigned short getBiome(const glm::ivec3& pos) const;
/**
* Sets the biome ID at the local position specified.
*
* @param pos - The position to set the biome at.
* @param blk - The biome ID to set the biome to.
* @returns a boolean indicating if the biome replaced a *different* biome.
* Sets the biome ID at the position specified, wrapping to local coordinates.
* @returns a boolean indicating if the newly placed biome is different than the old one.
*/
inline bool setBiome(const glm::ivec3& pos, unsigned short bio);
/**
* Returns a reference to the chunk's raw blocks array.
*
* @returns a const reference to the chunk's internal block RIE array.
*/
/** Returns a constant reference to the chunk's raw blocks array. */
const std::array<unsigned int, 4096>& getBlocksArray() const;
const std::vector<unsigned int>& cGetBlocks() const;
/**
* Returns a reference to the chunk's raw biomes array.
*
* @returns a const reference to the chunk's internal biome RIE array.
*/
const std::vector<unsigned short>& cGetBiomes() const;
/**
* Gets the light value at the specified index.
*
* @param ind - The index to get the light values at.
* @returns a four dimensional vector in the format R, G, B, S, with the light values at the specified index.
*/
/** Returns a constant reference to the chunk's raw biomes array. */
const std::array<unsigned short, 4096>& getBiomesArray() const;
/** Returns the light value at the specified index as a vector in R, G, B, Sunlight format. */
inline glm::ivec4 getLight(unsigned int ind);
/**
* Sets the light value at the specified index to the vector specified.
*
* @param ind - The index to set the light values at.
* @param light - a four dimensional vector in the format R, G, B, S, with the desired light values.
*/
/** Sets the light value at the specified index to the vector specified in R, G, B, Sunlight format. */
inline void setLight(unsigned int ind, glm::ivec4 light);
/**
* Gets a single channel's light value at the specified index.
*
* @param ind - The index to get the light value at.
* @param channel - The channel as a char, where 0 = red, 1 = green, 2 = blue, 3 = sunlight.
* @returns the light value of the specified channel and index.
*/
/** Returns the specified channel of the light value at the specified index. */
inline unsigned char getLight(unsigned int ind, unsigned char channel);
/**
* Sets a single channel's light value at the specified index.
*
* @param ind - The index to set the light value at.
* @param channel - The channel as a char, where 0 = red, 1 = green, 2 = blue, 3 = sunlight.
* @returns the light value to set.
*/
/** Sets the specified channel of the light value at the specified index. */
inline void setLight(unsigned int ind, unsigned char channel, unsigned char light);
/**
* Combines a chunk's blocks with another's, which may be a partial.
* The other chunk's blocks will take priority, but INVALID will be ignored.
* Will update the chunk's state to generated one of the two was already generated.
*
* @param o - The chunk to combine this one with.
*/
void combineWith(std::shared_ptr<Chunk> o);
/**
* Serializes the chunk for sending over the network.
*
* @returns a packet string containing the chunk's data.
*/
/** Compresses the chunk, returning a string representing it. */
std::string compress();
std::string serialize();
/**
* Deserialize chunk data into this chunk.
*
* @param d - A deserializer, whose current index is the start of a serialized chunk string.
*/
void deserialize(Deserializer& d);
/** Decompresses a compressed chunk string, or itself. */
void decompress(const std::string& data = "");
private:
/** Internal data of a decompressed chunk. */
struct ChunkData {
/** Internal block data. */
std::array<unsigned int, 4096> blocks {};
/** Internal biome data. */
std::array<unsigned short, 4096> biomes {};
/** Internal sunlight data. */
std::array<SunLight, 2048> sunLight {};
/** Internal blocklight data. */
std::array<BlockLight, 4096> blockLight {};
};
/**
* Gets the sunlight intensity at the specified index.
*
* @param ind - The index to get the sunlight at.
* @returns the sunlight intensity as a char at the specified index.
*/
/** Throws an exception if the chunk is compressed. */
inline void assertDecompressed() const;
/** Gets the sunlight intensity at the specified index. */
inline unsigned char getSunlight(unsigned int ind);
/**
* Sets the sunlight intensity at the specified index.
*
* @param ind - The index to set the sunlight at.
* @param val - The value to set the sunlight to, which must range from 0 - 15.
*/
/** Sets the sunlight intensity at the specified index. */
inline void setSunlight(unsigned int ind, unsigned char val);
/**
@ -360,18 +221,26 @@ private:
void countRenderableBlocks();
State state = State::EMPTY;
/** Whether or not the chunk is compressed. */
CompressionState compressionState = CompressionState::COMPRESSED;
/** Whether or not the chunk is generated. */
GenerationState generationState = GenerationState::EMPTY;
/** The position of the chunk in its dimension. */
glm::ivec3 pos {};
/** Whether or not the chunk needs to be remeshed. */
bool dirty = true;
bool shouldRender = true;
/** The number of non-transparent blocks in the chunk. */
unsigned short renderableBlocks = 0;
/** Internal decompressed chunk data. */
ChunkData* d = nullptr;
std::vector<unsigned int> blocks { 0, 0 };
std::vector<unsigned short> biomes { 0, 0 };
std::array<SunLight, 2048> sunLight {};
std::array<BlockLight, 4096> blockLight {};
/** Internal compressed chunk data. */
std::string c = "";
};
#include "Chunk.inl"

View File

@ -18,82 +18,104 @@ void Chunk::setDirty(bool isDirty) {
}
bool Chunk::chunkShouldRender() const {
return shouldRender;
return renderableBlocks > 0;
}
[[maybe_unused]] bool Chunk::isPartial() const {
return state == State::PARTIAL;
return generationState == GenerationState::PARTIAL;
}
bool Chunk::isGenerated() const {
return state == State::GENERATED;
return generationState == GenerationState::GENERATED;
}
inline unsigned int Chunk::getBlock(unsigned int ind) const {
if (ind >= 4096) return 0; // Invalid
return RIE::read<unsigned int>(ind, blocks, 4096);
assertDecompressed();
if (ind >= 4096) throw ChunkException(pos, "Index out of range.");
return d->blocks[ind];
}
inline unsigned int Chunk::getBlock(const glm::ivec3& reqPos) const {
if (reqPos.x > 15 || reqPos.x < 0 || reqPos.y > 15 || reqPos.y < 0 || reqPos.z > 15 || reqPos.z < 0) return 0;
return getBlock(Space::Block::index(reqPos));
}
inline bool Chunk::setBlock(const glm::ivec3& newPos, unsigned int blk) {
if (newPos.x > 15 || newPos.x < 0 || newPos.y > 15 || newPos.y < 0 || newPos.z > 15 || newPos.z < 0) return false;
return setBlock(Space::Block::index(newPos), blk);
}
inline unsigned short Chunk::getBiome(unsigned int ind) const {
if (ind >= 4096) return 0; // Invalid
return RIE::read<unsigned short>(ind, biomes, 4096);
assertDecompressed();
if (ind >= 4096) throw ChunkException(pos, "Index out of range.");
return d->biomes[ind];
}
inline unsigned short Chunk::getBiome(const glm::ivec3& reqPos) const {
if (reqPos.x > 15 || reqPos.x < 0 || reqPos.y > 15 || reqPos.y < 0 || reqPos.z > 15 || reqPos.z < 0) return 0;
return getBiome(Space::Block::index(reqPos));
}
inline bool Chunk::setBiome(unsigned int ind, unsigned short bio) {
return RIE::write(ind, bio, biomes, 4096);
assertDecompressed();
if (ind > 4096) throw ChunkException(pos, "Index out of range.");
if (d->biomes[ind] == bio) return false;
d->biomes[ind] = bio;
return true;
}
inline bool Chunk::setBiome(const glm::ivec3& newPos, unsigned short bio) {
if (newPos.x > 15 || newPos.x < 0 || newPos.y > 15 || newPos.y < 0 || newPos.z > 15 || newPos.z < 0) return false;
return setBiome(Space::Block::index(newPos), bio);
}
inline glm::ivec4 Chunk::getLight(unsigned int ind) {
return { blockLight[ind].r, blockLight[ind].g, blockLight[ind].b, getSunlight(ind) };
assertDecompressed();
return { d->blockLight[ind].r, d->blockLight[ind].g, d->blockLight[ind].b, getSunlight(ind) };
}
inline void Chunk::setLight(unsigned int ind, glm::ivec4 light) {
blockLight[ind].r = static_cast<unsigned char>(light.x);
blockLight[ind].g = static_cast<unsigned char>(light.y);
blockLight[ind].b = static_cast<unsigned char>(light.z);
assertDecompressed();
if (ind > 4096) throw ChunkException(pos, "Index out of range.");
if (light.x > 31 || light.y > 31 || light.z > 31 || light.w > 15)
throw ChunkException(pos, "Light value out of range.");
d->blockLight[ind].r = static_cast<unsigned char>(light.x);
d->blockLight[ind].g = static_cast<unsigned char>(light.y);
d->blockLight[ind].b = static_cast<unsigned char>(light.z);
setSunlight(ind, static_cast<unsigned char>(light.w));
}
inline unsigned char Chunk::getLight(unsigned int ind, unsigned char channel) {
return channel == 0 ? blockLight[ind].r :
channel == 1 ? blockLight[ind].g :
channel == 2 ? blockLight[ind].b :
getSunlight(ind);
assertDecompressed();
if (ind > 4096) throw ChunkException(pos, "Index out of range.");
return channel == 0 ? d->blockLight[ind].r :
channel == 1 ? d->blockLight[ind].g :
channel == 2 ? d->blockLight[ind].b :
getSunlight(ind);
}
inline void Chunk::setLight(unsigned int ind, unsigned char channel, unsigned char light) {
channel == 0 ? blockLight[ind].r = light :
channel == 1 ? blockLight[ind].g = light :
channel == 2 ? blockLight[ind].b = light :
(setSunlight(ind, light), 0);
assertDecompressed();
if (ind > 4096) throw ChunkException(pos, "Index out of range.");
if ((channel < 4 && light > 31) || (channel == 4 && light > 15))
throw ChunkException(pos, "Light value out of range.");
if (channel == 0) d->blockLight[ind].r = light;
else if (channel == 1) d->blockLight[ind].g = light;
else if (channel == 2) d->blockLight[ind].b = light;
else setSunlight(ind, light);
}
void Chunk::assertDecompressed() const {
if (compressionState == CompressionState::COMPRESSED)
throw ChunkException(pos, "Chunk is compressed.");
}
inline unsigned char Chunk::getSunlight(unsigned int ind) {
if (ind % 2 == 0) return sunLight[ind / 2].a;
else return sunLight[ind / 2].b;
if (ind % 2 == 0) return d->sunLight[ind / 2].a;
else return d->sunLight[ind / 2].b;
}
inline void Chunk::setSunlight(unsigned int ind, unsigned char val) {
if (ind % 2 == 0) sunLight[ind / 2].a = val;
else sunLight[ind / 2].b = val;
if (ind % 2 == 0) d->sunLight[ind / 2].a = val;
else d->sunLight[ind / 2].b = val;
}

View File

@ -95,54 +95,43 @@ std::string ServerLuaEntity::serialize() {
case NetField::ALL:
case NetField::POS:
s.appendE(NetField::POS).append(pos);
case NetField::POS: s.appendEnum(NetField::POS).append(pos);
if (field != NetField::ALL) break;
case NetField::VEL:
s.appendE(NetField::VEL).append(vel);
case NetField::VEL: s.appendEnum(NetField::VEL).append(vel);
if (field != NetField::ALL) break;
case NetField::ROT:
s.appendE(NetField::ROT).append(rot);
case NetField::ROT: s.appendEnum(NetField::ROT).append(rot);
if (field != NetField::ALL) break;
case NetField::SCALE:
s.appendE(NetField::SCALE).append(scale);
case NetField::SCALE: s.appendEnum(NetField::SCALE).append(scale);
if (field != NetField::ALL) break;
case NetField::VISUAL_OFF:
s.appendE(NetField::VISUAL_OFF).append(visualOff);
case NetField::VISUAL_OFF: s.appendEnum(NetField::VISUAL_OFF).append(visualOff);
if (field != NetField::ALL) break;
case NetField::DISPLAY:
s.appendE(NetField::DISPLAY).append(dMode).append(dArgA).append(dArgB);
case NetField::DISPLAY: s.appendEnum(NetField::DISPLAY).append(dMode).append(dArgA).append(dArgB);
if (field != NetField::ALL) break;
case NetField::ANIM_STATE:
s.appendE(NetField::ANIM_STATE).append<bool>(animation.isPlaying());
case NetField::ANIM_STATE: s.appendEnum(NetField::ANIM_STATE).append<bool>(animation.isPlaying());
if (field != NetField::ALL) break;
case NetField::ANIM_RANGE:
s.appendE(NetField::ANIM_RANGE).append<unsigned int>(animation.getBounds().x)
s.appendEnum(NetField::ANIM_RANGE).append<unsigned int>(animation.getBounds().x)
.append<unsigned int>(animation.getBounds().y).append<bool>(animation.isLooping());
if (field != NetField::ALL) break;
case NetField::DIM:
s.appendE(NetField::DIM).append(dim->getInd());
case NetField::DIM: s.appendEnum(NetField::DIM).append(dim->getInd());
if (field != NetField::ALL) break;
case NetField::COLLISION_BOX:
s.appendE(NetField::COLLISION_BOX).append<bool>(collisionBox.has_value());
case NetField::COLLISION_BOX: s.appendEnum(NetField::COLLISION_BOX).append<bool>(collisionBox.has_value());
if (collisionBox) s.append(collisionBox->a).append(collisionBox->b);
if (field != NetField::ALL) break;
case NetField::COLLIDES:
s.appendE(NetField::COLLIDES).append(collides);
case NetField::COLLIDES: s.appendEnum(NetField::COLLIDES).append(collides);
if (field != NetField::ALL) break;
case NetField::GRAVITY:
s.appendE(NetField::GRAVITY).append(gravity);
case NetField::GRAVITY: s.appendEnum(NetField::GRAVITY).append(gravity);
if (field != NetField::ALL) break;
}
}

View File

@ -32,7 +32,7 @@ void FileManipulator::commitChunk(Chunk& chunk) {
std::string filePath = path + "/" + fileName;
createRegionFileIfNotExists(reg);
std::string chunkData = chunk.serialize();
std::string chunkData = chunk.compress();
unsigned int dataBlockSize = floor(chunkData.length() / BLOCK_SIZE);
std::fstream file(filePath, std::ios::in | std::ios::out | std::ios::binary);

View File

@ -8,7 +8,7 @@
#include "world/World.h"
#include "game/Subgame.h"
#include "util/Schematic.h"
#include "util/Structure.h"
#include "game/def/BiomeDef.h"
#include "game/def/BlockDef.h"
#include "world/dim/Dimension.h"
@ -210,8 +210,8 @@ void MapGen::generateChunkBlocks(Job& job, glm::ivec3 localPos,
if (partial && i >= partialNextAt) {
partialInd++;
partialBlock = partial->blocks[partialInd * 2 + 1];
partialNextAt = (partialInd * 2 + 2 >= partial->blocks.size()) ? 4096 : partial->blocks[partialInd * 2 + 2];
partialBlock = partial->d->blocks[partialInd * 2 + 1];
partialNextAt = (partialInd * 2 + 2 >= partial->d->blocks.size()) ? 4096 : partial->d->blocks[partialInd * 2 + 2];
}
float depth = depthMap[i];
@ -223,14 +223,15 @@ void MapGen::generateChunkBlocks(Job& job, glm::ivec3 localPos,
: biome.rockBlock;
if (biomeID != cBiomeID) {
chunk.biomes.emplace_back(i);
chunk.biomes.emplace_back(biomeID);
//TODO: Fix MapGen
// chunk.d->biomes.emplace_back(i);
// chunk.d->biomes.emplace_back(biomeID);
cBiomeID = biomeID;
}
if (blockID != cBlockID) {
chunk.blocks.emplace_back(i);
chunk.blocks.emplace_back(blockID);
// chunk.d->blocks.emplace_back(i);
// chunk.d->blocks.emplace_back(blockID);
cBlockID = blockID;
}
}
@ -277,7 +278,7 @@ void MapGen::generateChunkDecorAndLight(Job& job, glm::ivec3 localPos, std::vect
auto& schematic = biome.schematics[schemID];
for (unsigned int j = 0; j < schematic->length(); j++) {
glm::ivec3 off = schematic->getOffset(j);
setBlock(job, pos + off - schematic->origin, schematic->blocks[j], chunk);
setBlock(job, pos + off - schematic->origin, schematic->layout[j], chunk);
}
break;
}
@ -296,7 +297,7 @@ void MapGen::generateChunkDecorAndLight(Job& job, glm::ivec3 localPos, std::vect
}
}
chunk->state = Chunk::State::GENERATED;
chunk->generationState = Chunk::GenerationState::GENERATED;
}
void MapGen::setBlock(MapGen::Job& job, glm::ivec3 worldPos, unsigned int block, std::shared_ptr<Chunk> hint) {

View File

@ -137,7 +137,7 @@ void LocalPlayer::assertField(Packet packet) {
void LocalPlayer::handleAssertion(Deserializer& d) {
while (!d.atEnd()) {
const auto field = d.readE<NetField>();
const auto field = d.readEnum<NetField>();
switch (field) {
default:
std::cout << Log::err << "Player received unhandled NetField, Type "
@ -190,10 +190,10 @@ void LocalPlayer::handleAssertion(Deserializer& d) {
bool LocalPlayer::getKey(Input& input, LocalPlayer::PlayerControl control) {
if (gameGui.isInMenu()) return false;
return input.keyDown(
control == PlayerControl::FORWARD ? GLFW_KEY_W :
control == PlayerControl::BACKWARD ? GLFW_KEY_S :
control == PlayerControl::FORWARD ? GLFW_KEY_COMMA :
control == PlayerControl::BACKWARD ? GLFW_KEY_O :
control == PlayerControl::LEFT ? GLFW_KEY_A :
control == PlayerControl::RIGHT ? GLFW_KEY_D :
control == PlayerControl::RIGHT ? GLFW_KEY_E :
control == PlayerControl::JUMP ? GLFW_KEY_SPACE :
control == PlayerControl::MOD1 ? GLFW_KEY_LEFT_SHIFT :
GLFW_KEY_LEFT_CONTROL);

View File

@ -12,17 +12,17 @@
void Player::setDim(DimensionPtr dim, bool assert) {
Entity::setDim(dim);
if (assert) assertField(Serializer().appendE(NetField::DIM).append(dim->getInd()).packet());
if (assert) assertField(Serializer().appendEnum(NetField::DIM).append(dim->getInd()).packet());
}
void Player::setPos(glm::vec3 pos, bool assert) {
Entity::setPos(pos);
if (assert) assertField(Serializer().appendE(NetField::POS).append(pos).packet());
if (assert) assertField(Serializer().appendEnum(NetField::POS).append(pos).packet());
}
void Player::setVel(glm::vec3 vel, bool assert) {
Entity::setVel(vel);
if (assert) assertField(Serializer().appendE(NetField::VEL).append(vel).packet());
if (assert) assertField(Serializer().appendEnum(NetField::VEL).append(vel).packet());
}
float Player::getYaw() {
@ -31,7 +31,7 @@ float Player::getYaw() {
void Player::setYaw(float yaw, bool assert) {
this->yaw = yaw;
if (assert) assertField(Serializer().appendE(NetField::LOOK_YAW).append(yaw).packet());
if (assert) assertField(Serializer().appendEnum(NetField::LOOK_YAW).append(yaw).packet());
}
float Player::getPitch() {
@ -40,7 +40,7 @@ float Player::getPitch() {
void Player::setPitch(float pitch, bool assert) {
this->pitch = pitch;
if (assert) assertField(Serializer().appendE(NetField::LOOK_PITCH).append(pitch).packet());
if (assert) assertField(Serializer().appendEnum(NetField::LOOK_PITCH).append(pitch).packet());
}
glm::vec3 Player::getLookOffset() {
@ -49,7 +49,7 @@ glm::vec3 Player::getLookOffset() {
void Player::setLookOffset(glm::vec3 lookOffset, bool assert) {
this->lookOffset = lookOffset;
if (assert) assertField(Serializer().appendE(NetField::LOOK_OFF).append(lookOffset).packet());
if (assert) assertField(Serializer().appendEnum(NetField::LOOK_OFF).append(lookOffset).packet());
}
@ -59,7 +59,7 @@ bool Player::isFlying() {
void Player::setFlying(bool flying, bool assert) {
this->flying = flying;
if (assert) assertField(Serializer().appendE(NetField::FLYING).append(flying).packet());
if (assert) assertField(Serializer().appendEnum(NetField::FLYING).append(flying).packet());
}
std::string Player::getHandList() {
@ -68,7 +68,7 @@ std::string Player::getHandList() {
void Player::setHandList(const std::string& list, bool assert) {
handList = list;
if (assert) assertField(Serializer().appendE(NetField::HAND_INV).append(handList).packet());
if (assert) assertField(Serializer().appendEnum(NetField::HAND_INV).append(handList).packet());
}
std::string Player::getWieldList() {
@ -77,7 +77,7 @@ std::string Player::getWieldList() {
void Player::setWieldList(const std::string& list, bool assert) {
wieldList = list;
if (assert) assertField(Serializer().appendE(NetField::WIELD_INV).append(wieldList).packet());
if (assert) assertField(Serializer().appendEnum(NetField::WIELD_INV).append(wieldList).packet());
}
unsigned short Player::getWieldIndex() {
@ -86,5 +86,5 @@ unsigned short Player::getWieldIndex() {
void Player::setWieldIndex(unsigned short index, bool assert) {
wieldIndex = index;
if (assert) assertField(Serializer().appendE(NetField::WIELD_INDEX).append(index).packet());
if (assert) assertField(Serializer().appendEnum(NetField::WIELD_INDEX).append(index).packet());
}

View File

@ -22,7 +22,7 @@ void ServerPlayer::assertField(Packet packet) {
void ServerPlayer::handleAssertion(Deserializer& d) {
while (!d.atEnd()) {
const auto field = d.readE<NetField>();
const auto field = d.readEnum<NetField>();
switch (field) {
default:
std::cout << Log::err << "Player received unhandled NetField, Type "

View File

@ -87,10 +87,8 @@ zepha.register_item("@auri:basic_tools:wooden_shovel", {
if zepha.server then
zepha.bind("new_player", function(player)
local inv = player:get_inventory():get_list("hot_wheel_1");
inv:add_stack({"@auri:basic_tools:flint_pickaxe", 1})
-- inv:add_stack({"@auri:basic_tools:wooden_hatchet", 1})
-- inv:add_stack({"@auri:basic_tools:wooden_shovel", 1})
inv:add_stack({"@auri:basic_tools:flint_shovel", 1})
local hw = player:get_inventory():get_list("hot_wheel_1");
hw:add_stack({"@auri:basic_tools:flint_pickaxe", 1})
hw:add_stack({"@auri:basic_tools:flint_shovel", 1})
end)
end

View File

@ -1,7 +1,23 @@
health.damage_player = function(player, damage)
health.player_damage = function(player, damage)
local health = health.health_values[player.id]
health.health = health.health - damage
health.buffer = damage
zepha.send_message("@auri:health:damage", health)
end
health.player_set = function(player, health, buffer)
local health = health.health_values[player.id]
health.health = health
health.buffer = buffer or 0
zepha.send_message("@auri:health:damage", health)
end
health.player_heal = function(player, add)
local health = health.health_values[player.id]
health.health = math.min(health.health + health.buffer + add, health.max)
health.buffer = 0
zepha.send_message("@auri:health:damage", health)
end

View File

@ -1,11 +0,0 @@
health.health_values = {}
if zepha.server then
zepha.bind('player_join', function(player)
health.health_values[player.id] = { health = 20, buffer = 0 }
end)
else
zepha.bind('message', function(channel, message)
dump(message)
end)
end

View File

@ -0,0 +1,23 @@
health.health_values = {}
if zepha.server then
zepha.bind('player_join', function(player)
health.health_values[player.id] = { health = 10, buffer = 0, max = 10 }
zepha.send_message("@auri:health:damage", health.health_values[player.id])
zepha.after(function()
health.player_heal(player, 0.5)
return true
end, 5)
end)
else
health.my_health = { health = 10, buffer = 0, max = 10 }
health.internal.update(true)
health.render_default(true)
zepha.bind('message', function(channel, message)
if channel ~= "@auri:health:damage" then return end
health.my_health = message
health.internal.update(true)
end)
end

View File

@ -1,10 +1,6 @@
local api = {}
_G['health'] = api
_G['health'] = {}
health.internal = {}
runfile(_PATH .. 'api')
runfile(_PATH .. 'core')
if zepha.client then
runfile(_PATH .. 'interface')
api.default_render(true)
end
runfile(_PATH .. 'interface')
runfile(_PATH .. 'hooks')

View File

@ -1,39 +1,76 @@
if not zepha.client then return end
local hud = zepha.player:get_hud()
health.get_component = function()
return zepha.build_gui(function()
health.internal._wrapper = zepha.build_gui(function()
return Gui.Rect {
key = 'health_wrapper'
}
end)
function health.internal.update()
local hp = health.my_health
health.internal._width = hp.max * 8 + 1
health.internal._wrapper:remove('@health:component')
health.internal._wrapper:append(function()
local elem = Gui.Rect {
key = "health_root",
size = { 10 * 8 + 1, 9 },
key = '@health:component',
size = { health.internal._width, 9 },
}
for i = 1, 10 do
local crop_x = (i < 5 and 0 or i < 8 and 1 or 2) * 9
-- Background
for i = 1, hp.max do
elem:append(Gui.Rect {
size = { 9, 9 },
position = { 8 * (i - 1), 0 },
background = "crop(" .. tostring(crop_x) .. ", 0, 9, 9, @auri:health:hearts)"
background = 'crop(0, 0, 9, 9, @auri:health:hearts)'
})
end
local red_start, red_end = 0, math.ceil(hp.health * 2)
local blue_start, blue_end = red_end, red_end + math.ceil(hp.buffer * 2)
for i = 1, math.ceil(hp.max * 2) do
local start = i % 2
if i > red_start and i <= red_end then
local segment = start ~= 0 and 9 or 18
elem:append(Gui.Rect {
size = { 9, 9 },
position = { 8 * math.floor((i - 1) / 2), 0 },
background = 'crop(9, ' .. segment .. ', 9, 9, @auri:health:hearts)'
})
elseif i > blue_start and i <= blue_end then
local segment = start ~= 0 and 9 or 18
elem:append(Gui.Rect {
size = { 9, 9 },
position = { 8 * math.floor((i - 1) / 2), 0 },
background = 'crop(18, ' .. segment .. ', 9, 9, @auri:health:hearts)'
})
end
end
return elem
end)
end
health.default_render = function(render)
if health._default_elem then hud:remove(health._default_elem) end
health._default_elem = nil
function health.render_default(render)
hud:remove('@health:default')
if render then
health._default_elem = zepha.build_gui(function()
hud:append(function()
return Gui.Rect {
size = { 10 * 8 + 1, 9 },
position = { "50%", "100%" },
position_anchor = { "50%", "200%" },
key = '@health:default',
health.get_component()
size = { health.internal._width, 9 },
position = { '50%', '100%' },
position_anchor = { '50%', '200%' },
health.internal._wrapper
}
end)
hud:append(health._default_elem)
end
end
function health.get_element()
return health.internal._wrapper
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -3,8 +3,8 @@ local hud = zepha.player:get_hud()
local health_elem = nil
if health then
health.default_render(false)
health_elem = health.get_component()
health.render_default(false)
health_elem = health.get_element()
end
hud:append(function()

View File

@ -1,5 +1,6 @@
runfile(_PATH .. "bush_stem")
runfile(_PATH .. "cobblestone")
runfile(_PATH .. "podzol")
runfile(_PATH .. "dirt")
runfile(_PATH .. "grass")
runfile(_PATH .. "leaves")

View File

@ -0,0 +1,23 @@
zepha.register_block(":podzol", {
name = "Podzol",
model = "base:block",
textures = {
"zeus:default:podzol",
"zeus:default:dirt",
"zeus:default:podzol_side"
},
tool_props = {
health = 25,
multipliers = {
scoop = 2.0,
smash = 0,
}
},
yields = function()
if math.random() >= 0.5 then return "zeus:default:dirt"
else return "zeus:materials:stick" end
end
})

View File

@ -66,7 +66,7 @@ zepha.register_entity("zeus:default:rabbit", {
else
self.object.vel = V {}
if self.attack_timer == 0 then
health.damage_player(closest_player, 1)
health.player_damage(closest_player, 0.5)
self.attack_timer = ATTACK_INTERVAL
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

View File

@ -9,10 +9,10 @@ local flowers = {
"yellow_dandelion"
}
local tchelper = function(first, rest) return first:upper()..rest:lower() end
local titlecase = function(first, rest) return first:upper()..rest:lower() end
for _,flower in pairs(flowers) do
local name = flower:gsub("_", " "):gsub("(%a)([%w_']*)", tchelper)
local name = flower:gsub("_", " "):gsub("(%a)([%w_']*)", titlecase)
zepha.register_block("zeus:flowers:flower_" .. flower, {
culls = false,

View File

@ -131,6 +131,6 @@ end
zepha.register_keybind("zeus:inventory:open_inventory", {
description = "Open Inventory",
default = zepha.keys.e,
default = zepha.keys.p,
on_press = inventory.open_inventory
})

View File

@ -4,3 +4,12 @@ runfile("zeus:materials/items/flint")
runfile("zeus:materials/items/flint_heads")
runfile("zeus:materials/items/plant_fibre")
runfile("zeus:materials/items/plant_twine")
if zepha.server then
zepha.bind("new_player", function(player)
local hw = player:get_inventory():get_list("main");
hw:add_stack({"zeus:materials:flint", 16})
hw:add_stack({"zeus:materials:rock", 8})
hw:add_stack({"zeus:materials:plant_twine", 4})
end)
end

View File

@ -3,113 +3,160 @@ local identifier = "zeus:world:forest"
local noise = {
heightmap = {
module = "add",
sources = {{
-- Elevation
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
scale = 250,
bias = -32
}, {
sources = {
runfile(_PATH .. 'world_noise'), {
-- Features
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.2,
frequency = .5,
octaves = 3,
},
scale = 6,
bias = 6
}}
scale = 3,
bias = 0
} }
}
}
local woo = "zeus:default:wood"
local lea = "zeus:default:leaves"
local inv = "invalid"
--local woo = "zeus:default:wood"
--local lea = "zeus:default:leaves"
--local inv = "invalid"
--
--local trunk_layer_0 = {
-- { inv, inv, inv, inv, inv },
-- { inv, woo, woo, woo, inv },
-- { inv, woo, woo, woo, inv },
-- { inv, woo, woo, woo, inv },
-- { inv, inv, inv, inv, inv }
--}
--
--local trunk_layer_1 = {
-- { inv, inv, inv, inv, inv },
-- { inv, inv, woo, inv, inv },
-- { inv, woo, woo, woo, inv },
-- { inv, inv, woo, inv, inv },
-- { inv, inv, inv, inv, inv }
--}
--
--local trunk_layer_2 = {
-- { inv, inv, inv, inv, inv },
-- { inv, inv, inv, inv, inv },
-- { inv, inv, woo, inv, inv },
-- { inv, inv, inv, inv, inv },
-- { inv, inv, inv, inv, inv }
--}
--
--local leaf_layer_1 = {
-- { inv, lea, lea, lea, inv },
-- { lea, lea, lea, lea, lea },
-- { lea, lea, woo, lea, lea },
-- { lea, lea, lea, lea, lea },
-- { inv, lea, lea, lea, inv }
--}
--
--local leaf_layer_2 = {
-- { inv, inv, inv, inv, inv },
-- { inv, lea, lea, lea, inv },
-- { inv, lea, woo, lea, inv },
-- { inv, lea, lea, lea, inv },
-- { inv, inv, inv, inv, inv }
--}
--
--local leaf_layer_3 = {
-- { inv, inv, inv, inv, inv },
-- { inv, lea, lea, inv, inv },
-- { inv, lea, lea, lea, inv },
-- { inv, inv, lea, lea, inv },
-- { inv, inv, inv, inv, inv }
--}
--
--local tree = zepha.create_structure({
-- origin = V(2, 2, 2),
-- probability = 0.01,
-- schematic = {
-- trunk_layer_0,
-- trunk_layer_0,
-- trunk_layer_0,
-- trunk_layer_0,
-- trunk_layer_1,
-- trunk_layer_1,
-- trunk_layer_1,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- trunk_layer_2,
-- leaf_layer_2,
-- leaf_layer_1,
-- leaf_layer_1,
-- leaf_layer_1,
-- leaf_layer_1,
-- leaf_layer_2,
-- leaf_layer_3
-- }
--})
--
--local woo = "zeus:default:wood"
--local lea = "zeus:default:leaves"
--local inv = "invalid"
--
--local shrub_layer_0 = {
-- { inv, inv, inv },
-- { inv, woo, inv },
-- { inv, inv, inv }
--}
--
--local shrub_layer_1 = {
-- { inv, lea, inv },
-- { lea, woo, lea },
-- { inv, lea, inv }
--}
--
--local shrub_layer_2 = {
-- { inv, inv, inv },
-- { inv, lea, inv },
-- { inv, inv, inv }
--}
--
--local shrub = zepha.create_structure({
-- origin = V{1, 1, 1},
-- probability = 0.005,
-- schematic = {
-- shrub_layer_0,
-- shrub_layer_1,
-- shrub_layer_2,
-- }
--})
--
--local structures = { tree, shrub }
--
--for i = 1, 5 do
-- table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.01,
-- schematic = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
-- }))
--end
--
--table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.0025,
-- schematic = {{{ "zeus:flowers:flower_red_mushroom" }}}
--}))
--
--table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.0025,
-- schematic = {{{ "zeus:flowers:flower_brown_mushroom" }}}
--}))
local trunk_layer_0 = {
{ inv, inv, inv, inv, inv },
{ inv, woo, woo, woo, inv },
{ inv, woo, woo, woo, inv },
{ inv, woo, woo, woo, inv },
{ inv, inv, inv, inv, inv }
}
local trunk_layer_1 = {
{ inv, inv, inv, inv, inv },
{ inv, inv, woo, inv, inv },
{ inv, woo, woo, woo, inv },
{ inv, inv, woo, inv, inv },
{ inv, inv, inv, inv, inv }
}
local trunk_layer_2 = {
{ inv, inv, inv, inv, inv },
{ inv, inv, inv, inv, inv },
{ inv, inv, woo, inv, inv },
{ inv, inv, inv, inv, inv },
{ inv, inv, inv, inv, inv }
}
local leaf_layer_1 = {
{ inv, lea, lea, lea, inv },
{ lea, lea, lea, lea, lea },
{ lea, lea, woo, lea, lea },
{ lea, lea, lea, lea, lea },
{ inv, lea, lea, lea, inv }
}
local leaf_layer_2 = {
{ inv, inv, inv, inv, inv },
{ inv, lea, lea, lea, inv },
{ inv, lea, woo, lea, inv },
{ inv, lea, lea, lea, inv },
{ inv, inv, inv, inv, inv }
}
local leaf_layer_3 = {
{ inv, inv, inv, inv, inv },
{ inv, lea, lea, inv, inv },
{ inv, lea, lea, lea, inv },
{ inv, inv, lea, lea, inv },
{ inv, inv, inv, inv, inv }
}
local tree = zepha.create_structure({
origin = V(2, 2, 2),
probability = 0.01,
schematic = {
trunk_layer_0,
trunk_layer_0,
trunk_layer_0,
trunk_layer_0,
trunk_layer_1,
trunk_layer_1,
trunk_layer_1,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
trunk_layer_2,
leaf_layer_2,
leaf_layer_1,
leaf_layer_1,
leaf_layer_1,
leaf_layer_1,
leaf_layer_2,
leaf_layer_3
}
})
local structures = {}
zepha.register_biome(identifier, {
environment = {
@ -118,12 +165,12 @@ zepha.register_biome(identifier, {
roughness = 20/100,
},
blocks = {
top = "zeus:default:grass",
top = "zeus:default:podzol",
soil = "zeus:default:dirt",
rock = "zeus:default:stone"
},
tags = { natural = 1, default = 1 },
structures = { tree },
structures = structures,
biome_tint = "#7beb26",
noise = noise
})

View File

@ -1,90 +1,56 @@
local identifier = "zeus:world:plains"
local woo = "zeus:default:wood"
local lea = "zeus:default:leaves"
local inv = "invalid"
local wood = "zeus:default:wood"
local leaf = "zeus:default:leaves"
local none = "invalid"
local shrub_layer_0 = {
{ inv, inv, inv },
{ inv, woo, inv },
{ inv, inv, inv }
}
local structures = {}
local shrub_layer_1 = {
{ inv, lea, inv },
{ lea, woo, lea },
{ inv, lea, inv }
}
local shrub_layer_2 = {
{ inv, inv, inv },
{ inv, lea, inv },
{ inv, inv, inv }
}
local shrub = zepha.create_structure({
table.insert(structures, zepha.create_structure({
noise = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
region_size = 4,
origin = V{1, 1, 1},
probability = 0.01,
schematic = {
shrub_layer_0,
shrub_layer_1,
shrub_layer_2,
}
})
local structures = { shrub }
for i = 1, 5 do
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.1,
schematic = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
}))
end
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
schematic = {{{ "zeus:flowers:flower_geranium" }}}
layout = {{
{ none, none, none },
{ none, wood, none },
{ none, none, none }
}, {
{ none, leaf, none },
{ leaf, wood, leaf },
{ none, leaf, none }
}, {
{ none, none, none },
{ none, leaf, none },
{ none, none, none }
}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
schematic = {{{ "zeus:flowers:flower_white_dandelion" }}}
}))
--for i = 1, 5 do
-- table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.1,
-- layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
-- }))
--end
--
--table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.025,
-- layout = {{{ "zeus:flowers:flower_geranium" }}}
--}))
--
--table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.025,
-- layout = {{{ "zeus:flowers:flower_white_dandelion" }}}
--}))
local noise = {
heightmap = {
module = "add",
sources = {{
module = "const",
value = -12
}, {
module = "add",
sources = {{
-- Elevation
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
scale = 250,
bias = -32
}, {
-- Features
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.2,
octaves = 3,
},
scale = 6,
bias = 6
}}
}}
}
heightmap = runfile(_PATH .. 'world_noise')
}
zepha.register_biome(identifier, {

View File

@ -0,0 +1,24 @@
return {
module = "add",
sources = {{
-- Elevation
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
scale = 250,
bias = -32
}, {
-- Features
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.2,
octaves = 3,
},
scale = 6,
bias = 6
}}
};