From e9d7fd0ee916fa1824decd49a0ae48743e899a90 Mon Sep 17 00:00:00 2001 From: Auri Date: Tue, 3 Aug 2021 16:02:34 -0700 Subject: [PATCH] Performance Improvements, better MeshChunk. --- src/CMakeLists.txt | 15 +- src/client/conn/ClientNetworkInterpreter.cpp | 2 +- src/client/conn/ServerConnection.cpp | 4 +- src/client/graph/Drawable.h | 4 +- src/client/graph/mesh/ChunkMesh.cpp | 29 -- src/client/graph/mesh/ChunkMesh.h | 23 -- src/client/graph/mesh/ChunkMeshGenerator.cpp | 4 +- src/client/graph/mesh/ChunkMeshGenerator.h | 6 +- src/client/graph/mesh/ChunkRenderElem.h | 41 ++- src/client/graph/mesh/ChunkVertex.h | 22 -- src/client/graph/mesh/Mesh.cpp | 59 ++- src/client/graph/mesh/Mesh.h | 31 +- src/client/graph/mesh/MeshChunk.cpp | 41 ++- src/client/graph/mesh/MeshChunk.h | 61 ++-- src/client/gui/DebugGui.cpp | 140 +++---- src/client/gui/DebugGui.h | 25 +- src/client/gui/GameGui.h | 44 +-- src/client/scene/ConnectScene.cpp | 4 +- src/client/scene/GameScene.cpp | 4 +- src/client/stream/ChunkMeshDetails.h | 17 - src/client/stream/MeshChunkDetails.h | 15 + src/client/stream/MeshGenStream.cpp | 8 +- src/client/stream/MeshGenStream.h | 10 +- .../stream/WorldInterpolationStream.cpp | 1 - src/client/stream/WorldInterpolationStream.h | 30 +- src/lua/NoiseFromLua.cpp | 15 + src/lua/modules/Structure.cpp | 4 +- src/lua/modules/create_structure.h | 12 +- src/server/Server.cpp | 2 +- src/server/Server.h | 7 +- src/server/ServerClients.cpp | 2 +- src/server/stream/ServerPacketStream.cpp | 6 +- src/util/Types.h | 21 +- src/util/Util.h | 56 ++- src/util/net/NetHandler.cpp | 4 +- src/world/LocalWorld.cpp | 45 ++- src/world/LocalWorld.h | 77 ++-- src/world/ServerWorld.cpp | 135 ++++--- src/world/ServerWorld.h | 76 ++-- src/world/World.cpp | 30 +- src/world/World.h | 48 ++- src/world/dim/Dimension.cpp | 4 +- src/world/dim/LocalDimension.cpp | 138 +++---- src/world/dim/LocalDimension.h | 15 +- src/world/dim/ServerDimension.cpp | 53 ++- src/world/dim/ServerDimension.h | 2 +- src/world/dim/chunk/Chunk.cpp | 5 +- src/world/gen/MapGen.cpp | 94 ++--- src/world/gen/MapGen.h | 84 ++--- src/world/player/Player.h | 2 +- src/world/player/ServerPlayer.h | 2 +- .../mods/zeus_inventory/script/menu.lua | 2 +- .../mods/zeus_world/script/biomes/forest.lua | 2 +- .../zeus_world/script/biomes/highlands.lua | 2 +- .../mods/zeus_world/script/biomes/plains.lua | 6 +- .../menu/script/{faafafafa.lua => init.lua} | 0 .../zeus_default/models/wip/workbench.blend | Bin 0 -> 668460 bytes .../mods/zeus_default/textures/podzol.png | Bin 0 -> 2796 bytes .../zeus_default/textures/podzol_side.png | Bin 0 -> 2909 bytes .../mods/zeus_default/textures/workbench.png | Bin 0 -> 7193 bytes .../mods/zeus_default/textures/workbench.xcf | Bin 0 -> 22938 bytes .../zeus/mods/zeus_inventory/script/menu.lua | 2 +- .../mods/zeus_world/script/biomes/desert.lua | 98 ++--- .../mods/zeus_world/script/biomes/forest.lua | 344 +++++++++--------- .../zeus_world/script/biomes/highlands.lua | 114 +++--- .../mods/zeus_world/script/biomes/plains.lua | 159 ++++++-- subgames/zeus/mods/zeus_world/script/init.lua | 6 +- 67 files changed, 1254 insertions(+), 1060 deletions(-) delete mode 100644 src/client/graph/mesh/ChunkMesh.cpp delete mode 100644 src/client/graph/mesh/ChunkMesh.h delete mode 100644 src/client/graph/mesh/ChunkVertex.h delete mode 100644 src/client/stream/ChunkMeshDetails.h create mode 100644 src/client/stream/MeshChunkDetails.h rename subgames/parentheses/menu/script/{faafafafa.lua => init.lua} (100%) create mode 100644 subgames/zeus/mods/zeus_default/models/wip/workbench.blend create mode 100755 subgames/zeus/mods/zeus_default/textures/podzol.png create mode 100755 subgames/zeus/mods/zeus_default/textures/podzol_side.png create mode 100644 subgames/zeus/mods/zeus_default/textures/workbench.png create mode 100644 subgames/zeus/mods/zeus_default/textures/workbench.xcf diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1551aa32..4b0fa059 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,12 +17,9 @@ add_library(Zepha_Core client/graph/DrawableGroup.h client/graph/Font.cpp client/graph/Font.h - client/graph/mesh/ChunkMesh.cpp - client/graph/mesh/ChunkMesh.h client/graph/mesh/ChunkMeshGenerator.cpp client/graph/mesh/ChunkMeshGenerator.h client/graph/mesh/ChunkRenderElem.h - client/graph/mesh/ChunkVertex.h client/graph/mesh/EntityMesh.cpp client/graph/mesh/EntityMesh.h client/graph/mesh/EntityVertex.h @@ -103,7 +100,7 @@ add_library(Zepha_Core client/scene/Scene.h client/scene/SceneManager.cpp client/scene/SceneManager.h - client/stream/ChunkMeshDetails.h + client/stream/MeshChunkDetails.h client/stream/MeshGenStream.cpp client/stream/MeshGenStream.h client/stream/WorldInterpolationStream.cpp @@ -326,6 +323,14 @@ add_library(Zepha_Core lua/modules/Message.h lua/NoiseFromLua.cpp lua/NoiseFromLua.h - util/Types.h util/PerfTimer.cpp util/PerfTimer.h client/gui/compound/GuiPerfGraph.cpp client/gui/compound/GuiPerfGraph.h client/gui/compound/GuiCellGraph.cpp client/gui/compound/GuiCellGraph.h client/gui/basic/GuiCells.cpp client/gui/basic/GuiCells.h) + util/Types.h + util/PerfTimer.cpp + util/PerfTimer.h + client/gui/compound/GuiPerfGraph.cpp + client/gui/compound/GuiPerfGraph.h + client/gui/compound/GuiCellGraph.cpp + client/gui/compound/GuiCellGraph.h + client/gui/basic/GuiCells.cpp + client/gui/basic/GuiCells.h) target_include_directories(Zepha_Core PUBLIC .) \ No newline at end of file diff --git a/src/client/conn/ClientNetworkInterpreter.cpp b/src/client/conn/ClientNetworkInterpreter.cpp index 3c1be64f..16a4c7b8 100644 --- a/src/client/conn/ClientNetworkInterpreter.cpp +++ b/src/client/conn/ClientNetworkInterpreter.cpp @@ -103,7 +103,7 @@ void ClientNetworkInterpreter::receivedPacket(uptr p) { } case Packet::Type::ENTITY_REMOVED: { - world.getActiveDimension().l()->serverEntitiesRemoved(p->d); + world.getActiveDimension().l()->removeServerEntities(p->d); break; } diff --git a/src/client/conn/ServerConnection.cpp b/src/client/conn/ServerConnection.cpp index af9be53a..e2305b17 100644 --- a/src/client/conn/ServerConnection.cpp +++ b/src/client/conn/ServerConnection.cpp @@ -65,12 +65,12 @@ void ServerConnection::processConnecting() { else { enet_peer_reset(peer); if (attempt < attempts) { - std::cout << Log::info << "Failed to connect to server, retrying." << Log::endl; + std::cout << Log::info << "Failed to init to server, retrying." << Log::endl; connectionTime = std::chrono::high_resolution_clock::now(); attempt++; } else { - std::cout << Log::err << "Failed to connect to server." << Log::endl; + std::cout << Log::err << "Failed to init to server." << Log::endl; state = State::FAILED_CONNECT; } } diff --git a/src/client/graph/Drawable.h b/src/client/graph/Drawable.h index 3eb1caa0..c1f86042 100644 --- a/src/client/graph/Drawable.h +++ b/src/client/graph/Drawable.h @@ -7,7 +7,7 @@ class Renderer; class Drawable { - public: +public: virtual void update(double delta) {}; virtual void draw(Renderer& renderer) {}; @@ -18,7 +18,7 @@ class Drawable { virtual ~Drawable() = default; - protected: +protected: bool visible = true; }; diff --git a/src/client/graph/mesh/ChunkMesh.cpp b/src/client/graph/mesh/ChunkMesh.cpp deleted file mode 100644 index d80b55ec..00000000 --- a/src/client/graph/mesh/ChunkMesh.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// Created by aurailus on 25/11/18. -// - -#include "ChunkMesh.h" - -#include "ChunkVertex.h" - -void ChunkMesh::create(const std::vector& vertices, const std::vector& indices) { - indCount = static_cast(indices.size()); - - genArrays(static_cast(vertices.size() * sizeof(ChunkVertex)), - static_cast(indices.size() * sizeof(unsigned int)), - &vertices.front(), &indices.front()); - - unsigned int idx = 0; - createVertexAttrib(idx++, 3, GL_FLOAT, STRIDE_OFFSET_CHUNK(position)); - createVertexAttrib(idx++, 2, GL_FLOAT, STRIDE_OFFSET_CHUNK(texCoords)); - createVertexAttrib(idx++, 3, GL_FLOAT, STRIDE_OFFSET_CHUNK(blendColor)); - createVertexAttrib(idx++, 2, GL_FLOAT, STRIDE_OFFSET_CHUNK(blendMaskCoords)); - createVertexAttrib(idx++, 1, GL_FLOAT, STRIDE_OFFSET_CHUNK(normal)); - createVertexAttrib(idx++, 4, GL_FLOAT, STRIDE_OFFSET_CHUNK(light)); - createVertexAttrib(idx++, 1, GL_FLOAT, STRIDE_OFFSET_CHUNK(shaderMod)); - createVertexAttrib(idx, 3, GL_FLOAT, STRIDE_OFFSET_CHUNK(modValues)); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} \ No newline at end of file diff --git a/src/client/graph/mesh/ChunkMesh.h b/src/client/graph/mesh/ChunkMesh.h deleted file mode 100644 index 98d7de7a..00000000 --- a/src/client/graph/mesh/ChunkMesh.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by aurailus on 25/11/18. -// - -#pragma once - -#include -#include - -#include "Mesh.h" - -class ChunkVertex; - -class ChunkMesh : public Mesh { - public: - ChunkMesh() = default; - - ChunkMesh(const ChunkMesh& o) { throw std::runtime_error("No copy constructor for ChunkMeshes"); }; - - void create(const std::vector& vertices, const std::vector& indices); - - ~ChunkMesh() = default; -}; diff --git a/src/client/graph/mesh/ChunkMeshGenerator.cpp b/src/client/graph/mesh/ChunkMeshGenerator.cpp index 89ad9133..450304ff 100644 --- a/src/client/graph/mesh/ChunkMeshGenerator.cpp +++ b/src/client/graph/mesh/ChunkMeshGenerator.cpp @@ -15,10 +15,10 @@ #include "world/dim/chunk/Chunk.h" #include "game/def/mesh/BlockModel.h" #include "game/atlas/LocalBiomeAtlas.h" -#include "client/stream/ChunkMeshDetails.h" +#include "client/stream/MeshChunkDetails.h" #include "game/atlas/LocalDefinitionAtlas.h" -ChunkMeshGenerator::ChunkMeshGenerator(ChunkMeshDetails* meshDetails, LocalDefinitionAtlas& defs, +ChunkMeshGenerator::ChunkMeshGenerator(MeshChunkDetails* meshDetails, LocalDefinitionAtlas& defs, LocalBiomeAtlas& biomes, uptr chk, array, 6> adj, array& blockOffsets) : defs(defs), biomes(biomes), diff --git a/src/client/graph/mesh/ChunkMeshGenerator.h b/src/client/graph/mesh/ChunkMeshGenerator.h index e5d6ff03..a745c240 100644 --- a/src/client/graph/mesh/ChunkMeshGenerator.h +++ b/src/client/graph/mesh/ChunkMeshGenerator.h @@ -9,12 +9,12 @@ class MeshPart; class BlockDef; class NoiseSample; class LocalBiomeAtlas; -class ChunkMeshDetails; +class MeshChunkDetails; class LocalDefinitionAtlas; class ChunkMeshGenerator { public: - ChunkMeshGenerator(ChunkMeshDetails* meshDetails, LocalDefinitionAtlas& defs, LocalBiomeAtlas& biomes, + ChunkMeshGenerator(MeshChunkDetails* meshDetails, LocalDefinitionAtlas& defs, LocalBiomeAtlas& biomes, uptr chunk, array, 6> adjacent, array& blockOffsets); private: @@ -27,7 +27,7 @@ private: LocalBiomeAtlas& biomes; usize indOffset = 0; - ChunkMeshDetails* meshDetails; + MeshChunkDetails* meshDetails; uptr chunk; array, 6> adjacent; diff --git a/src/client/graph/mesh/ChunkRenderElem.h b/src/client/graph/mesh/ChunkRenderElem.h index 3bc4ac20..59e3af48 100644 --- a/src/client/graph/mesh/ChunkRenderElem.h +++ b/src/client/graph/mesh/ChunkRenderElem.h @@ -1,20 +1,41 @@ -// -// Created by aurailus on 28/09/19. -// - #pragma once -#include +#include "util/Types.h" class Renderer; +/** + * An abstract class for a visual representation of one or more chunks. + * Currently used by MeshChunk, but in the future Mesh MapBlocks will use it as well. + * Keeps track of the chunk positions that are using this element, and if it should be kept alive. + */ + struct ChunkRenderElem { + ChunkRenderElem() = default; + ChunkRenderElem(vec3 pos): pos(pos) {}; + + /** Sets the element's visual position. */ + virtual void setPos(vec3 pos) { + this->pos = pos; + }; + + /** Gets the element's visual position. */ + virtual vec3 getPos() { + return pos; + }; + + /** Draws the element to the screen. */ virtual void draw(Renderer& renderer) = 0; - virtual glm::vec3 getPos() = 0; + /** + * Specifies if a chunk is using this render element. + * Returns a boolean indicating if there are any chunks using the element. + */ + + virtual bool updateChunkUse(ivec3 chunk, bool used) = 0; - // Used to determine if the RenderElem should be deleted. - // Bool is if the RenderElem should be kept alive. - // True = keep, False = remove - virtual bool updateChunkUse(glm::vec3 chunk, bool used) = 0; +protected: + + /** The element's visual position. */ + vec3 pos {}; }; diff --git a/src/client/graph/mesh/ChunkVertex.h b/src/client/graph/mesh/ChunkVertex.h deleted file mode 100644 index 97561be2..00000000 --- a/src/client/graph/mesh/ChunkVertex.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by aurailus on 24/08/19. -// - -#pragma once - -#include -#include -#include - -struct ChunkVertex { - glm::vec3 position; - glm::vec2 texCoords; - glm::vec3 blendColor; - glm::vec2 blendMaskCoords; - float normal; - glm::vec4 light; - float shaderMod; - glm::vec3 modValues; -}; - -#define STRIDE_OFFSET_CHUNK(m) sizeof(struct ChunkVertex), (void *)offsetof(struct ChunkVertex, m) diff --git a/src/client/graph/mesh/Mesh.cpp b/src/client/graph/mesh/Mesh.cpp index ea83abf1..e6fc3f1c 100644 --- a/src/client/graph/mesh/Mesh.cpp +++ b/src/client/graph/mesh/Mesh.cpp @@ -1,41 +1,5 @@ -// -// Created by aurailus on 24/08/19. -// - #include "Mesh.h" -void Mesh::cleanup() { - if (VAO != 0) glDeleteVertexArrays(1, &VAO); - if (VBO != 0) glDeleteBuffers(1, &VBO); - if (IBO != 0) glDeleteBuffers(1, &IBO); - - IBO = 0; - VBO = 0; - VAO = 0; - indCount = 0; -} - -void Mesh::genArrays(GLuint vboLength, GLuint iboLength, const void* verticesPtr, const void* indicesPtr) { - glGenVertexArrays(1, &VAO); - glBindVertexArray(VAO); - - glGenBuffers(1, &IBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, iboLength, indicesPtr, GL_STATIC_DRAW); - - glGenBuffers(1, &VBO); - glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, vboLength, verticesPtr, GL_STATIC_DRAW); -} - -void Mesh::createVertexAttrib(GLuint offset, GLuint size, GLenum type, GLsizei stride, const void* pointer) { - glEnableVertexAttribArray(offset); - if (type == GL_INT) - glVertexAttribIPointer(offset, size, type, stride, pointer); - else - glVertexAttribPointer(offset, size, type, GL_FALSE, stride, pointer); -} - void Mesh::draw() const { glBindVertexArray(VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); @@ -44,5 +8,26 @@ void Mesh::draw() const { } Mesh::~Mesh() { - cleanup(); + if (VAO != 0) glDeleteVertexArrays(1, &VAO); + if (VBO != 0) glDeleteBuffers(1, &VBO); + if (IBO != 0) glDeleteBuffers(1, &IBO); +} + +void Mesh::genArrays(usize vboLength, usize iboLength, const void* vertices, const void* indices) { + glGenVertexArrays(1, &VAO); + glBindVertexArray(VAO); + + glGenBuffers(1, &IBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, iboLength, indices, GL_STATIC_DRAW); + + glGenBuffers(1, &VBO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, vboLength, vertices, GL_STATIC_DRAW); +} + +void Mesh::createVertexAttrib(u32 offset, u32 size, GLenum type, u32 stride, const void* pointer) { + glEnableVertexAttribArray(offset); + if (type == GL_INT) glVertexAttribIPointer(offset, size, type, stride, pointer); + else glVertexAttribPointer(offset, size, type, GL_FALSE, stride, pointer); } \ No newline at end of file diff --git a/src/client/graph/mesh/Mesh.h b/src/client/graph/mesh/Mesh.h index 701fb316..8e568a73 100644 --- a/src/client/graph/mesh/Mesh.h +++ b/src/client/graph/mesh/Mesh.h @@ -1,28 +1,27 @@ -// -// Created by aurailus on 24/08/19. -// - #pragma once #include +#include "util/Types.h" + +/** Returns the stride and the offset of the member in the struct. */ +#define STRIDE_OFFSET(Struct, Member) sizeof(struct Struct), (void*)offsetof(struct Struct, Member) + +/** A renderable mesh. Inherited by other mesh types. */ class Mesh { - public: - Mesh() = default; - - void cleanup(); - +public: + /** Draws the mesh to the screen. */ virtual void draw() const; ~Mesh(); - protected: - void genArrays(GLuint vboLength, GLuint iboLength, const void* verticesPtr, const void* indicesPtr); +protected: + /** Generates the vertex and index arrays on the GPU. */ + void genArrays(usize vboLength, usize iboLength, const void* vertices, const void* indices); - void createVertexAttrib(GLuint offset, GLuint size, GLenum type, GLsizei stride, const void* pointer); + /** Creates a vertex attribute on the VBO. */ + void createVertexAttrib(u32 offset, u32 size, GLenum type, u32 stride, const void* pointer); - GLuint VAO = 0; - GLuint VBO = 0; - GLuint IBO = 0; - GLsizei indCount = 0; + usize indCount = 0; + u32 VAO = 0, VBO = 0, IBO = 0; }; diff --git a/src/client/graph/mesh/MeshChunk.cpp b/src/client/graph/mesh/MeshChunk.cpp index 833703ad..690cd1af 100644 --- a/src/client/graph/mesh/MeshChunk.cpp +++ b/src/client/graph/mesh/MeshChunk.cpp @@ -1,35 +1,36 @@ -// -// Created by aurailus on 15/12/18. -// - -#include +#include #include #include "MeshChunk.h" #include "client/graph/Renderer.h" -#include "client/graph/mesh/ChunkMesh.h" -void MeshChunk::create(std::vector& vertices, std::vector& indices) { - this->mesh = std::make_shared(); - mesh->create(vertices, indices); -} +MeshChunk::MeshChunk(const vec3 pos, const vec& vertices, const vec& indices) : + ChunkRenderElem(pos), mesh(make_unique(vertices, indices)) {} void MeshChunk::draw(Renderer& renderer) { glm::mat4 model = glm::mat4(1.0); - model = glm::translate(model, pos * static_cast(16)); + model = glm::translate(model, pos * 16.f); renderer.setModelMatrix(model); mesh->draw(); } -void MeshChunk::setPos(glm::vec3 pos) { - this->pos = pos; -} - -glm::vec3 MeshChunk::getPos() { - return pos; -} - -bool MeshChunk::updateChunkUse(glm::vec3 pos, bool used) { +bool MeshChunk::updateChunkUse(ivec3 pos, bool used) { return used; +} + +MeshChunk::Mesh::Mesh(const vec& vertices, const vec& indices) { + this->indCount = indices.size(); + + genArrays(vertices.size() * sizeof(Vertex), indices.size() * sizeof(u32), &vertices.front(), &indices.front()); + + u32 idx = 0; + createVertexAttrib(idx++, 3, GL_FLOAT, STRIDE_OFFSET(Vertex, position)); + createVertexAttrib(idx++, 2, GL_FLOAT, STRIDE_OFFSET(Vertex, texCoords)); + createVertexAttrib(idx++, 3, GL_FLOAT, STRIDE_OFFSET(Vertex, blendColor)); + createVertexAttrib(idx++, 2, GL_FLOAT, STRIDE_OFFSET(Vertex, blendMaskCoords)); + createVertexAttrib(idx++, 1, GL_FLOAT, STRIDE_OFFSET(Vertex, normal)); + createVertexAttrib(idx++, 4, GL_FLOAT, STRIDE_OFFSET(Vertex, light)); + createVertexAttrib(idx++, 1, GL_FLOAT, STRIDE_OFFSET(Vertex, shaderMod)); + createVertexAttrib(idx, 3, GL_FLOAT, STRIDE_OFFSET(Vertex, modValues)); } \ No newline at end of file diff --git a/src/client/graph/mesh/MeshChunk.h b/src/client/graph/mesh/MeshChunk.h index 704f5b11..0a2a2934 100644 --- a/src/client/graph/mesh/MeshChunk.h +++ b/src/client/graph/mesh/MeshChunk.h @@ -1,34 +1,51 @@ -// -// Created by aurailus on 15/12/18. -// - #pragma once +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" -#include -#include - +#include "Mesh.h" +#include "util/Types.h" #include "ChunkRenderElem.h" #include "client/graph/Drawable.h" -class ChunkMesh; - -class ChunkVertex; +/** + * A drawable mesh of a single chunk. + */ class MeshChunk : public ChunkRenderElem, Drawable { - public: - MeshChunk() = default; +public: - void create(std::vector& vertices, std::vector& indices); + /** A single vertex of a ChunkMesh. */ + struct Vertex { + vec3 position; + + vec2 texCoords; + vec3 blendColor; + vec2 blendMaskCoords; + + f32 normal; + vec4 light; + + f32 shaderMod; + vec3 modValues; + }; + + /** Represents a MeshChunk's underlying mesh. */ + struct Mesh : public ::Mesh { + Mesh(const Mesh&) = delete; + Mesh(const vec& vertices, const vec& indices); + }; + + /** Creates a new MeshChunk with the data provided. */ + MeshChunk(const vec3 pos, const vec& vertices, const vec& indices); void draw(Renderer& renderer) override; - bool updateChunkUse(glm::vec3 chunk, bool used) override; + bool updateChunkUse(ivec3 chunk, bool used) override; + +private: - void setPos(glm::vec3 pos); - - glm::vec3 getPos() override; - - private: - std::shared_ptr mesh = nullptr; - glm::vec3 pos{}; -}; \ No newline at end of file + /** The underlying mesh used by this chunk. */ + uptr mesh = nullptr; +}; + +#pragma clang diagnostic pop \ No newline at end of file diff --git a/src/client/gui/DebugGui.cpp b/src/client/gui/DebugGui.cpp index e24bf704..70b0ac6c 100644 --- a/src/client/gui/DebugGui.cpp +++ b/src/client/gui/DebugGui.cpp @@ -13,142 +13,142 @@ DebugGui::DebugGui(u16vec2 buffer, SubgamePtr game, LocalWorld& world, vec& perfSections) : game(game), world(world) { - + auto fontRef = game.l()->textures["font"]; auto fpsHistogramRef = game.l()->textures["histogram"]; auto genericHistogramRef = game.l()->textures["histogram_white"]; - + Font f(game.l()->textures, fontRef); - + auto crosshairText = make_shared("crosshairText"); crosshairText->create({ 2, 2 }, {}, { 0.2, 0.2, 0.2, 0.5 }, { 1, 1, 1, 1 }, f); add(crosshairText); - + auto dataText = make_shared("dataText"); dataText->create({ 2, 2 }, {}, { 0.2, 0.2, 0.2, 0.5 }, { 1, 1, 1, 1 }, f); add(dataText); - + auto interpGraph = make_shared("interpGraph"); interpGraph->create({ 244, 64 }, {}, "Interp", 120, 256, genericHistogramRef, f); add(interpGraph); - + auto meshGraph = make_shared("meshGraph"); meshGraph->create({ 244, 64 }, {}, "Mesh", 120, 32, genericHistogramRef, f); add(meshGraph); - + auto genGraph = make_shared("genGraph"); genGraph->create({ 244, 64 }, {}, "Gen", 120, 16, genericHistogramRef, f); add(genGraph); - + auto packetGraph = make_shared("packetGraph"); packetGraph->create({ 244, 64 }, {}, "Packets", 120, 32, genericHistogramRef, f); add(packetGraph); - + auto fpsGraph = make_shared("fpsGraph"); fpsGraph->create({ 244, 64 }, {}, "FPS", 120, 60, fpsHistogramRef, f); add(fpsGraph); - + auto drawsGraph = make_shared("drawsGraph"); drawsGraph->create({ 244, 64 }, {}, "Draw Calls", 120, 0, genericHistogramRef, f); add(drawsGraph); - + auto gpuGraph = make_shared("gpuGraph"); gpuGraph->create({ 244, 64 }, {}, "GPU", 120, 1, genericHistogramRef, f); add(gpuGraph); - + auto perfGraph = make_shared("perfGraph"); perfGraph->create(344, {}, perfSections, "Performance", f); add(perfGraph); - + auto chunkStates = make_shared("chunkStates"); chunkStates->create(6, vec4(4), CHUNK_RANGE, "Chunk Compression", f); chunkStates->refresh(); add(chunkStates); - + positionElements(buffer); } void DebugGui::positionElements(u16vec2 buffer) { get("crosshairText")->setPos({ buffer.x / 2 + 22, buffer.y / 2 - 7 }); get("dataText")->setPos({ 10, 10 }); - + get("genGraph")->setPos({ buffer.x - 254, buffer.y - 70 - 160 }); get("packetGraph")->setPos({ buffer.x - 254, buffer.y - 70 - 240 }); get("meshGraph")->setPos({ buffer.x - 254, buffer.y - 70 - 80 }); get("interpGraph")->setPos({ buffer.x - 254, buffer.y - 70 }); - + get("fpsGraph")->setPos({ buffer.x - 254, 10 }); get("drawsGraph")->setPos({ buffer.x - 254, 90 }); get("gpuGraph")->setPos({ buffer.x - 254, 90 + 80 }); get("perfGraph")->setPos({ buffer.x - 354 - 254, 10 }); - + get("chunkStates")->setPos({ buffer.x - 264 - 300, buffer.y - 334 }); } void DebugGui::update(sptr player, f64 delta, u32 interpolatedChunks, u32 generatedChunks, u32 recievedPackets, vec& perfTimings, u32 drawnMeshChunks, u32 generatedMeshChunks) { - + Target target = player->getTarget(); - + auto& onBiomeDef = game->getBiomes().biomeFromId( world.getActiveDimension()->getBiome(glm::floor(player->getPos()))); - + // FPS and Draw calls graphs - + get("perfGraph")->updateTimings(perfTimings); get("fpsGraph")->pushValue(1 / delta); get("drawsGraph")->pushValue(drawnMeshChunks); - + int videoMemAvail, videoMemTotal; - + glGetIntegerv(0x9048, &videoMemTotal); glGetIntegerv(0x9049, &videoMemAvail); - + get("gpuGraph")->pushValue(std::round( (videoMemTotal - videoMemAvail) / static_cast(videoMemTotal) * 100.0) / 100.0f); - - + + // Thread information graphs - + get("meshGraph")->pushValue(generatedMeshChunks); get("interpGraph")->pushValue(interpolatedChunks); get("genGraph")->pushValue(generatedChunks); get("packetGraph")->pushValue(recievedPackets); - + // Textual information - + vec3 playerPos = glm::floor(player->getPos()); vec3 chunkPos = Space::Chunk::world::fromBlock(playerPos); vec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunkPos); vec3 regionPos = Space::Region::world::fromChunk(chunkPos); - + vec3 posOffsetFromChunk = Space::Block::relative::toChunk(playerPos); vec3 posOffsetFromBlock = Space::Block::relative::toMapBlock(playerPos); vec3 posOffsetFromRegion = Space::Block::relative::toRegion(playerPos); - + std::ostringstream str; - + str << "Dimension: " << world.getActiveDimension()->getIdentifier() << " [" << world.getActiveDimension()->getInd() << "]" << std::endl << std::endl - + << "Pos: " << playerPos << " (" << player->getPos() << ")" << std::endl << "Vel: " << player->getVel() << std::endl - + << "Yaw: " << player->getYaw() << ", " << "Pitch: " << player->getPitch() << std::endl << std::endl - + << "C: " << posOffsetFromChunk << " [" << chunkPos << "]" << std::endl << "M: " << posOffsetFromBlock << " [" << mapBlockPos << "]" << std::endl << "R: " << posOffsetFromRegion << " [" << regionPos << "]" << std::endl << std::endl - + << "Texture Slots: " << game.l()->textures.textureSlotsUsed << " / " << game.l()->textures.maxTextureSlots << " (" << round(game.l()->textures.textureSlotsUsed / static_cast(game.l()->textures.maxTextureSlots) * 100) << "%)" << std::endl << std::endl - + << "Biome: " << onBiomeDef.identifier << " [" << onBiomeDef.index << "]" << std::endl << std::endl; - + if (target.type == Target::Type::BLOCK) { string face = target.data.block.face == EVec::TOP ? "Top" : @@ -158,16 +158,16 @@ void DebugGui::update(sptr player, f64 delta, u32 interpolatedChunk 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 << "Pointed Position: " << target.data.block.pos << std::endl << "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 << "Pointed Position: " << entity.getPos() << std::endl << std::endl; @@ -175,44 +175,48 @@ void DebugGui::update(sptr player, f64 delta, u32 interpolatedChunk else { str << "No Target" << std::endl << std::endl; } - + // for (usize i = 0; i < perfTimings.size(); i++) { // str << perfSections[i] << ": " << perfTimings[i] << " ns." << std::endl; // } - + get("dataText")->setText(str.str()); - + // Chunk States - - auto chunkStates = get("chunkStates"); - ivec3 off = { 0, 0, 0 }; - for (off.x = 0; off.x < CHUNK_RANGE; off.x++) { - for (off.z = 0; off.z < CHUNK_RANGE; off.z++) { - f32 existAmount = 0; - f32 compressedAmount = 0; - ivec3 check = ivec3(chunkPos) + off - - glm::ivec3(floor(CHUNK_RANGE / 2), 0, floor(CHUNK_RANGE / 2)); - - for (off.y = 0; off.y < CHUNK_VERT; off.y++) { - check.y = static_cast(chunkPos.y) + off.y - CHUNK_VERT / 2; - const auto chunk = world.getActiveDimension()->getChunk(check); - if (chunk) { - existAmount++; - if (chunk->isCompressed()) compressedAmount++; + + if (chunkTimer == 0) { + auto chunkStates = get("chunkStates"); + ivec3 off = { 0, 0, 0 }; + for (off.x = 0; off.x < CHUNK_RANGE; off.x++) { + for (off.z = 0; off.z < CHUNK_RANGE; off.z++) { + f32 existAmount = 0; + f32 compressedAmount = 0; + ivec3 check = ivec3(chunkPos) + off - + glm::ivec3(floor(CHUNK_RANGE / 2), 0, floor(CHUNK_RANGE / 2)); + + for (off.y = 0; off.y < CHUNK_VERT; off.y++) { + check.y = static_cast(chunkPos.y) + off.y - CHUNK_VERT / 2; + const auto chunk = world.getActiveDimension()->getChunk(check); + if (chunk) { + existAmount++; + if (chunk->isCompressed()) compressedAmount++; + } } + + const auto color = glm::mix(CHUNK_UNLOADED, + glm::mix(CHUNK_UNCOMPRESSED, CHUNK_COMPRESSED, + compressedAmount / CHUNK_VERT),existAmount / CHUNK_VERT); + + chunkStates->setCellColor(u16vec2(off.x, off.z), color); } - - const auto color = glm::mix(CHUNK_UNLOADED, - glm::mix(CHUNK_UNCOMPRESSED, CHUNK_COMPRESSED, - compressedAmount / CHUNK_VERT),existAmount / CHUNK_VERT); - - chunkStates->setCellColor(u16vec2(off.x, off.z), color); } + chunkStates->refresh(); } - chunkStates->refresh(); + chunkTimer = (chunkTimer + 1) % CHUNK_INTERVAL; + // Crosshair information - + if (target.type == Target::Type::BLOCK) { const auto& def = game->getDefs().blockFromId(world.getActiveDimension()->getBlock(target.data.block.pos)); get("crosshairText")->setText(def.name + " (" + def.identifier + ") [" + std::to_string(def.index) + "]"); diff --git a/src/client/gui/DebugGui.h b/src/client/gui/DebugGui.h index 8589b10f..9a0c0c9e 100644 --- a/src/client/gui/DebugGui.h +++ b/src/client/gui/DebugGui.h @@ -17,35 +17,38 @@ class LocalSubgame; class DebugGui : public GuiContainer { public: - + enum class Visibility { OFF, FPS_ONLY, ON }; - + DebugGui(u16vec2 buffer, SubgamePtr game, LocalWorld& world, vec& perfSections); - + /** Resizes elements when the screen buffer is resized. */ void bufferResized(u16vec2 bufferSize); - + /** Sets which elements are visible based on the state provided. */ void changeVisibility(Visibility state); - + /** Positions all elements based on the buffer size. */ void positionElements(u16vec2 buffer); - + /** Updates the debug screen with the latest data. */ void update(sptr player, f64 delta, u32 interpolatedChunks, u32 generatedChunks, u32 recievedPackets, vec& perfTimings, u32 drawnMeshChunks, u32 generatedMeshChunks); - + private: - + constexpr static vec4 CHUNK_UNLOADED = { 1, 1, 1, 0.15 }; constexpr static vec4 CHUNK_COMPRESSED = { 1, 1, 1, 0.75 }; constexpr static vec4 CHUNK_UNCOMPRESSED = { 1, 0, 0, 0.75 }; - + constexpr static i32 CHUNK_VERT = 3; constexpr static i32 CHUNK_RANGE = 48; - + SubgamePtr game; LocalWorld& world; - + + u16 chunkTimer = 0; + constexpr static u16 CHUNK_INTERVAL = 5; + Visibility state = Visibility::ON; }; diff --git a/src/client/gui/GameGui.h b/src/client/gui/GameGui.h index daa46a16..e9e9918b 100644 --- a/src/client/gui/GameGui.h +++ b/src/client/gui/GameGui.h @@ -1,56 +1,52 @@ -// -// Created by aurailus on 05/02/19. -// - #pragma once #include "client/gui/GameGuiBuilder.h" #include "client/gui/compound/GuiInventoryList.h" class GameGui { - public: +public: explicit GameGui(InventoryRefsPtr refs, glm::vec2 bufferSize, SubgamePtr defs, Renderer& renderer); - + void winResized(glm::ivec2 win); - + void update(double delta); - + void setVisible(bool visible); - + bool isVisible(); - + void showMenu(std::shared_ptr root); - + void closeMenu(); - + const bool isInMenu() const; - + void setHud(std::shared_ptr hud); - + std::shared_ptr getHud(); - + void drawHud(Renderer& renderer); - + void drawMenu(Renderer& renderer); - - private: + +private: SubgamePtr defs; Renderer& renderer; - - glm::ivec2 win{}; + + ivec2 win {}; bool inMenu = false; - + std::shared_ptr hudRootElem = nullptr; - + std::shared_ptr menuRoot = std::make_shared("menuRoot"); std::shared_ptr menuLuaRoot = std::make_shared("menuLuaRoot"); GameGuiBuilder menuBuilder; std::shared_ptr hudRoot = std::make_shared("hudRoot"); std::shared_ptr hudLuaRoot = std::make_shared("hudLuaRoot"); GameGuiBuilder hudBuilder; - + std::shared_ptr handList = std::make_shared("hand"); - + InventoryRefsPtr refs; }; diff --git a/src/client/scene/ConnectScene.cpp b/src/client/scene/ConnectScene.cpp index 8c11e16a..c8f7db06 100644 --- a/src/client/scene/ConnectScene.cpp +++ b/src/client/scene/ConnectScene.cpp @@ -22,7 +22,7 @@ * Initializes a connection to the remote address, * sets up the GUI, and attempts to download subgame assets. * - * @param addr - The server address to connect to. + * @param addr - The server address to init to. */ ConnectScene::ConnectScene(Client& client, Address addr) : Scene(client), @@ -210,7 +210,7 @@ void ConnectScene::handleConnecting() { case ServerConnection::State::FAILED_CONNECT: connectState = State::FAILED_CONNECT; - statusText->setText(statusText->getText() + "\nFailed to connect :(\n"); + statusText->setText(statusText->getText() + "\nFailed to init :(\n"); break; case ServerConnection::State::ATTEMPTING_CONNECT: diff --git a/src/client/scene/GameScene.cpp b/src/client/scene/GameScene.cpp index e698cde4..bd6fc943 100644 --- a/src/client/scene/GameScene.cpp +++ b/src/client/scene/GameScene.cpp @@ -12,7 +12,7 @@ GameScene::GameScene(Client& client) : Scene(client), Packet r(Packet::Type::CONNECT_DATA_RECVD); r.sendTo(client.connection.getPeer(), Packet::Channel::CONNECT); - world.l()->connect(); + world.l()->init(); client.game->init(world, world.l()->getPlayer(), client); world.l()->updatePlayerDimension(); @@ -41,7 +41,7 @@ void GameScene::draw() { perf.start("draw:world"); renderer.beginChunkDeferredCalls(); renderer.enableTexture(&client.game->textures.atlasTexture); - world.l()->drawWorld(); + world.l()->drawChunks(); perf.start("draw:entities"); renderer.beginEntityDeferredCalls(); diff --git a/src/client/stream/ChunkMeshDetails.h b/src/client/stream/ChunkMeshDetails.h deleted file mode 100644 index 2570adee..00000000 --- a/src/client/stream/ChunkMeshDetails.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// Created by aurailus on 23/07/19. -// - -#pragma once - -#include -#include - -#include "client/graph/mesh/ChunkVertex.h" - -struct ChunkMeshDetails { - std::vector vertices; - std::vector indices; - - glm::ivec3 pos {}; -}; diff --git a/src/client/stream/MeshChunkDetails.h b/src/client/stream/MeshChunkDetails.h new file mode 100644 index 00000000..a7604392 --- /dev/null +++ b/src/client/stream/MeshChunkDetails.h @@ -0,0 +1,15 @@ +// +// Created by aurailus on 23/07/19. +// + +#pragma once + +#include "util/Types.h" +#include "client/graph/mesh/MeshChunk.h" + + +struct MeshChunkDetails { + ivec3 pos {}; + vec indices; + vec vertices; +}; diff --git a/src/client/stream/MeshGenStream.cpp b/src/client/stream/MeshGenStream.cpp index 3651077c..b96a7de2 100644 --- a/src/client/stream/MeshGenStream.cpp +++ b/src/client/stream/MeshGenStream.cpp @@ -6,7 +6,7 @@ #include "MeshGenStream.h" -#include "ChunkMeshDetails.h" +#include "MeshChunkDetails.h" #include "client/graph/mesh/ChunkMeshGenerator.h" #include "world/dim/chunk/Chunk.h" #include "world/dim/LocalDimension.h" @@ -33,8 +33,8 @@ MeshGenStream::MeshGenStream(SubgamePtr game, LocalDimension& dimension) : for (int i = 0; i < THREADS; i++) threads.emplace_back(*game.l(), noiseSampler); } -std::vector MeshGenStream::update() { - std::vector finishedChunks; +std::vector MeshGenStream::update() { + std::vector finishedChunks; for (u16 i = 0; i < THREAD_QUEUE_SIZE; i++) { for (Thread& t : threads) { @@ -44,7 +44,7 @@ std::vector MeshGenStream::update() { if (j.meshDetails->vertices.size()) { j.thisChunk = nullptr; finishedChunks.push_back(j.meshDetails); - j.meshDetails = new ChunkMeshDetails(); + j.meshDetails = new MeshChunkDetails(); } if (!queuedTasks.empty()) { diff --git a/src/client/stream/MeshGenStream.h b/src/client/stream/MeshGenStream.h index 3b508259..4f6b941e 100644 --- a/src/client/stream/MeshGenStream.h +++ b/src/client/stream/MeshGenStream.h @@ -11,7 +11,7 @@ #include "util/Vec.h" #include "util/CovariantPtr.h" -#include "ChunkMeshDetails.h" +#include "MeshChunkDetails.h" #include "world/gen/NoiseSample.h" class Chunk; @@ -22,8 +22,8 @@ class LocalDimension; class MeshGenStream { public: - static const u16 THREADS = 4; - static const u16 THREAD_QUEUE_SIZE = 16; + static const u16 THREADS = 6; + static const u16 THREAD_QUEUE_SIZE = 12; explicit MeshGenStream(SubgamePtr game, LocalDimension& dimension); @@ -33,13 +33,13 @@ public: //Will return a vector of MeshDetails pointers containing finished meshes. //Frees up the threads and starts new tasks. - std::vector update(); + std::vector update(); struct Job { std::unique_ptr thisChunk = nullptr; std::array, 6> adjacentChunks {}; - ChunkMeshDetails* meshDetails = new ChunkMeshDetails(); + MeshChunkDetails* meshDetails = new MeshChunkDetails(); bool busy = false; }; diff --git a/src/client/stream/WorldInterpolationStream.cpp b/src/client/stream/WorldInterpolationStream.cpp index da42641c..cbd96a5a 100644 --- a/src/client/stream/WorldInterpolationStream.cpp +++ b/src/client/stream/WorldInterpolationStream.cpp @@ -91,7 +91,6 @@ void WorldInterpolationStream::Thread::run() { u.chunks.reserve(64); while (!u.packet->d.atEnd()) { string data = u.packet->d.read(); -// std::cout << Util::toString(Deserializer(data).read()) << std::endl; u.chunks.emplace_back(make_shared()); u.chunks.back()->decompressFromString(data); } diff --git a/src/client/stream/WorldInterpolationStream.h b/src/client/stream/WorldInterpolationStream.h index fb4d7f90..96366dc6 100644 --- a/src/client/stream/WorldInterpolationStream.h +++ b/src/client/stream/WorldInterpolationStream.h @@ -22,52 +22,52 @@ class PacketView; class WorldInterpolationStream { public: - static const int THREADS = 4; - static const int THREAD_QUEUE_SIZE = 16; - + static const int THREADS = 1; + static const int THREAD_QUEUE_SIZE = 1; + WorldInterpolationStream(LocalSubgame& game, LocalWorld& world, unsigned int seed); - + // Queue parsing of packet `p`. void queuePacket(std::unique_ptr p); - + // Queue interpolation of Mapblock at `pos`. // bool queuePosition(glm::vec3 pos); // Returns a vector of BlockChunks that have finished processing, // and gives the threads new data to work with. std::unique_ptr>> update(); - + ~WorldInterpolationStream(); - + private: // enum class JobType { // EMPTY, // PACKET, // FARMAP // }; - + struct Job { bool locked = false; // JobType job = JobType::EMPTY; - + std::shared_ptr packet = nullptr; std::vector> chunks = {}; // std::shared_ptr mapblock = nullptr; // glm::vec3 mapBlockPos = {0, 0, 0}; }; - + struct Thread { explicit Thread(LocalSubgame& game, LocalWorld& world, unsigned int seed); - + void run(); - + bool kill = false; - + std::vector jobs = std::vector(THREAD_QUEUE_SIZE); - + std::thread thread; }; - + std::vector threads; std::queue> queuedPacketTasks; // std::unordered_set queuedInterpMap; diff --git a/src/lua/NoiseFromLua.cpp b/src/lua/NoiseFromLua.cpp index 905c94fe..7bbdcdd0 100644 --- a/src/lua/NoiseFromLua.cpp +++ b/src/lua/NoiseFromLua.cpp @@ -184,6 +184,7 @@ noise::module::Module* NoiseFromLua::parseNoise(std::vectorSetSeed(noise.get_or("seed", 0)); +// module->EnableDistance(noise.get_or("distance", false)); module->SetDisplacement(noise.get_or("displacement", 0)); module->SetFrequency(noise.get_or("frequency", 0)); @@ -233,5 +234,19 @@ noise::module::Module* NoiseFromLua::parseNoise(std::vectorSetSourceModule(0, *mod0); + module->SetXScale(noise.get_or("x_scale", 1)); + module->SetYScale(noise.get_or("y_scale", 1)); + module->SetZScale(noise.get_or("z_scale", 1)); + + modules.push_back(module); + return module; + } throw std::runtime_error("Invalid noise module specified."); } \ No newline at end of file diff --git a/src/lua/modules/Structure.cpp b/src/lua/modules/Structure.cpp index 784d3e2f..2e22561a 100644 --- a/src/lua/modules/Structure.cpp +++ b/src/lua/modules/Structure.cpp @@ -12,7 +12,7 @@ void Api::Module::Structure::bind() { sol::object Api::Module::Structure::create_structure(sol::table data) { auto origin = data.get>("origin"); -// auto probability = data.get("probability"); + auto probability = data.get("probability"); auto layout = data.get("layout"); unsigned int yWid = layout.size(); @@ -24,7 +24,7 @@ sol::object Api::Module::Structure::create_structure(sol::table data) { s->dimensions = { xWid, yWid, zWid }; s->origin = origin ? glm::ivec3 { *origin } : glm::ivec3 {}; s->layout.reserve(xWid * yWid * zWid); -// s->probability = probability; + s->probability = probability; for (unsigned int x = 1; x <= xWid; x++) for (unsigned int y = 1; y <= yWid; y++) diff --git a/src/lua/modules/create_structure.h b/src/lua/modules/create_structure.h index 92017de6..d003b209 100644 --- a/src/lua/modules/create_structure.h +++ b/src/lua/modules/create_structure.h @@ -12,16 +12,16 @@ namespace Api { if (!data) throw "expected a table as the first argument."; auto origin = data->get>("origin"); - auto schematic = data->get>("schematic"); + auto layout = data->get < sol::optional < sol::table >> ("layout"); if (!origin) throw std::runtime_error("expected a table as the first argument."); - if (!schematic) throw std::runtime_error("expected a table as the first argument."); + if (!layout) throw std::runtime_error("expected a table as the first argument."); auto s = std::make_shared(); - unsigned int yWid = schematic->size(); - unsigned int zWid = (*schematic).get(1).size(); - unsigned int xWid = (*schematic).get(1).get(1).size(); + unsigned int yWid = layout->size(); + unsigned int zWid = (*layout).get(1).size(); + unsigned int xWid = (*layout).get(1).get(1).size(); s->dimensions = { xWid, yWid, zWid }; s->stringData.resize(xWid * yWid * zWid); @@ -32,7 +32,7 @@ namespace Api { for (unsigned int z = 1; z <= zWid; z++) { for (unsigned int x = 1; x <= xWid; x++) { s->stringData[s->index({ x - 1, y - 1, z - 1 })] = - schematic->get(y).get(z).get_or(x, ""); + layout->get(y).get(z).get_or(x, ""); } } } diff --git a/src/server/Server.cpp b/src/server/Server.cpp index d5f7eb31..9c3549c5 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -33,7 +33,7 @@ void Server::update() { const static i64 interval_ns = static_cast((1000 / 60.f) * 1000000L); Timer loop(""); - world->update(delta); + world.s()->update(delta); game.s()->update(delta); // Read incoming events. diff --git a/src/server/Server.h b/src/server/Server.h index ca7cbc96..e9bcc75f 100644 --- a/src/server/Server.h +++ b/src/server/Server.h @@ -1,7 +1,3 @@ -// -// Created by aurailus on 09/01/19. -// - #pragma once #include "util/Types.h" @@ -12,9 +8,8 @@ #include "server/ServerClients.h" #include "world/inv/ServerInventoryRefs.h" -class ServerPlayer; - class Packet; +class ServerPlayer; class Server { public: diff --git a/src/server/ServerClients.cpp b/src/server/ServerClients.cpp index e08ad931..d426cde1 100644 --- a/src/server/ServerClients.cpp +++ b/src/server/ServerClients.cpp @@ -36,7 +36,7 @@ void ServerClients::createPlayer(sptr client, DimensionPtr dimensi client->player = make_shared(*client, dimension->getWorld(), game, dimension); game.s()->getParser().playerConnected(client->player); - client->player->setPos({ 256, -20, 256 }, true); +// client->player->setPos({ 256, -20, 256 }, true); Serializer() .append(NetField::ID).append(static_cast(client->player->getId())) diff --git a/src/server/stream/ServerPacketStream.cpp b/src/server/stream/ServerPacketStream.cpp index 6d6a4707..7c1f4575 100644 --- a/src/server/stream/ServerPacketStream.cpp +++ b/src/server/stream/ServerPacketStream.cpp @@ -44,8 +44,11 @@ std::unique_ptr>> S inProgressMap.emplace(pos); queuedTasks.pop(); +// std::cout << "going going" << std::endl; auto mapBlock = world.getDimension(pos.w)->getMapBlock(ivec3(pos)); - if (!mapBlock) continue; +// std::cout << Util::toString(pos) << ": gone, " << mapBlock << std::endl; + if (mapBlock == nullptr) continue; +// std::cout << "mappi: " << Util::toString(j.mapBlock->pos) << std::endl; j.mapBlock = make_unique(*mapBlock); j.dim = pos.w; j.locked = true; @@ -62,6 +65,7 @@ void ServerPacketStream::Thread::run() { for (Job& j : jobs) { if (j.locked) { empty = false; + std::cout << "run: " << Util::toString(j.mapBlock->pos) << std::endl; Serializer s {}; for (u16 i = 0; i < 64; i++) { diff --git a/src/util/Types.h b/src/util/Types.h index 420b13fc..0ac13b6f 100644 --- a/src/util/Types.h +++ b/src/util/Types.h @@ -40,17 +40,25 @@ using vec2 = glm::f32vec2; using vec3 = glm::f32vec3; using vec4 = glm::f32vec4; +using glm::i8vec2; +using glm::i8vec3; +using glm::i8vec4; +using glm::i16vec2; +using glm::i16vec3; +using glm::i16vec4; using ivec2 = glm::i32vec2; using ivec3 = glm::i32vec3; using ivec4 = glm::i32vec4; -using glm::i16vec2, glm::i16vec3, glm::i16vec4; -using glm::i8vec2, glm::i8vec3, glm::i8vec4; +using glm::u8vec2; +using glm::u8vec3; +using glm::u8vec4; +using glm::u16vec2; +using glm::u16vec3; +using glm::u16vec4; using uvec2 = glm::u32vec2; using uvec3 = glm::u32vec3; using uvec4 = glm::u32vec4; -using glm::u16vec2, glm::u16vec3, glm::u16vec4; -using glm::u8vec2, glm::u8vec3, glm::u8vec4; using std::array; using std::string; @@ -63,6 +71,7 @@ using sptr = std::shared_ptr; template using uptr = std::unique_ptr; -using std::make_shared, std::make_unique; +using std::make_shared; +using std::make_unique; -#pragma clang diagnostic pop \ No newline at end of file +#pragma clang diagnostic pop diff --git a/src/util/Util.h b/src/util/Util.h index 7bd43943..8d071f28 100644 --- a/src/util/Util.h +++ b/src/util/Util.h @@ -4,14 +4,10 @@ #pragma once -#include #include #include #include -#include -#include -#include -#include +#include #include "util/Log.h" #include "util/Types.h" @@ -23,7 +19,7 @@ namespace Util { return static_cast(t); } }; - + template , bool> = true> inline static string toFixed(T val, u8 precision = 2) { std::ostringstream out; @@ -31,17 +27,17 @@ namespace Util { out << std::fixed << val; return out.str(); } - + template , bool> = true> static string toString(T val) { return std::to_string(val); } - + template , bool> = true> static string toString(T val) { return toFixed(val); } - + template && std::is_same_v, V>, bool> = true> static string toString(V vec) { @@ -51,7 +47,7 @@ namespace Util { out << " ]"; return out.str(); } - + template && std::is_same_v, A>, bool> = true> static string toString(A arr) { @@ -59,7 +55,7 @@ namespace Util { for (usize i = 0; i < arr.size(); i++) out << (i == 0 ? "" : ", ") << arr[i]; return out.str(); } - + template && std::is_integral_v, bool> = true> static string toString(T vec) { @@ -67,7 +63,7 @@ namespace Util { for (usize i = 0; i < T::length(); i++) out << (i == 0 ? "" : ", ") << vec[i]; return out.str(); } - + template && std::is_integral_v, bool> = true> static string toString(T vec) { @@ -75,16 +71,16 @@ namespace Util { for (usize i = 0; i < T::length(); i++) out << (i == 0 ? "" : ", ") << toString(vec[i]); return out.str(); } - + inline static f32 packFloat(const vec3& vec) { auto charX = static_cast((vec.x + 1.0f) * 0.5f * 255.f); auto charY = static_cast((vec.y + 1.0f) * 0.5f * 255.f); auto charZ = static_cast((vec.z + 1.0f) * 0.5f * 255.f); - + u32 packedInt = (charX << 16) | (charY << 8) | charZ; return static_cast(static_cast(packedInt) / static_cast(1 << 24)); } - + inline static u32 intFromHexSegment(const string& t) { u32 x; std::stringstream ss; @@ -92,15 +88,15 @@ namespace Util { ss >> x; return x; } - + static vec4 hexToColorVec(string hex) { - vec4 color{}; - + vec4 color {}; + if (hex[0] == '#') hex.erase(0, 1); else std::cout << Log::err << "Color string does not begin with hash!" << Log::endl; - + string r, g, b, a; - + if (hex.length() == 3 || hex.length() == 4) { r = hex.substr(0, 1); r += r; @@ -121,19 +117,19 @@ namespace Util { std::cout << Log::err << "Color string \"" + hex + "\" is of incorrect length!" << Log::endl; return color; } - + color.r = intFromHexSegment(r) / 255.f; color.g = intFromHexSegment(g) / 255.f; color.b = intFromHexSegment(b) / 255.f; color.a = intFromHexSegment(a) / 255.f; - + return color; } - + static string getKeyStr(u16 key) { switch (key) { default: return ""; - + case 0: return "mouse0"; case 1: return "mouse1"; case 2: return "mouse2"; @@ -146,7 +142,7 @@ namespace Util { case 9: return "scrolldown"; case 10: return "scrollleft"; case 11: return "scrollright"; - + case 32: return "space"; case 39: return "'"; case 44: return ","; @@ -267,22 +263,22 @@ namespace Util { case 348: return "menu"; } } - + namespace { constexpr static u64 mix(char m, u64 s) { return ((s << 7) + ~(s >> 3)) + ~m; } } - + constexpr static u64 hash(const char* m) { return (*m) ? mix(*m, hash(m + 1)) : 0; } - + template std::function bind_this(C* c, Ret (C::*m)(Ts...)) { return [=](auto&& ... args) { return (c->*m)(std::forward(args)...); }; } - + template std::function bind_this(const C* c, Ret (C::*m)(Ts...) const) { return [=](auto&& ... args) { return (c->*m)(std::forward(args)...); }; @@ -293,4 +289,4 @@ template || (std::is_trivial_v && std::is_integral_v), bool> = true> std::ostream& operator<<(std::ostream& out, const T& t) { return out << Util::toString(t); -} \ No newline at end of file +} diff --git a/src/util/net/NetHandler.cpp b/src/util/net/NetHandler.cpp index 04f2c809..58cb446c 100644 --- a/src/util/net/NetHandler.cpp +++ b/src/util/net/NetHandler.cpp @@ -87,13 +87,13 @@ void NetHandler::initClient(Address hostAddress, int attempts, int timeout) { else { enet_peer_reset(peer); if (attempt < attempts) { - std::cout << Log::info << "Failed to connect to peer, retrying." << Log::endl; + std::cout << Log::info << "Failed to init to peer, retrying." << Log::endl; } } } if (state == NetState::FAILED_CONNECT) { - std::cout << Log::err << "Failed to connect to peer." << Log::endl; + std::cout << Log::err << "Failed to init to peer." << Log::endl; return; } } diff --git a/src/world/LocalWorld.cpp b/src/world/LocalWorld.cpp index 085196d2..ccad1036 100644 --- a/src/world/LocalWorld.cpp +++ b/src/world/LocalWorld.cpp @@ -1,9 +1,6 @@ -// -// Created by aurailus on 14/12/18. -// - #include "LocalWorld.h" +#include "util/PerfTimer.h" #include "util/net/PacketView.h" #include "client/graph/Renderer.h" #include "world/player/LocalPlayer.h" @@ -13,12 +10,12 @@ LocalWorld::LocalWorld(SubgamePtr game, ServerConnection& conn, Renderer& render World(game), renderer(renderer), net(conn, *this), + refs(make_shared(game, net)), debugGui(renderer.window.getSize(), game, *this, perfSections), - refs(std::make_shared(game, net)), - worldGenStream(std::make_shared(*game.l(), *this, 55)), - player(std::make_shared(game, *this, DimensionPtr(nullptr), renderer)) {} + worldGenStream(make_shared(*game.l(), *this, 55)), + player(make_shared(game, *this, DimensionPtr(nullptr), renderer)) {} -void LocalWorld::connect() { +void LocalWorld::init() { net.init(Util::bind_this(&(*refs), &LocalInventoryRefs::packetReceived)); refs->init(); } @@ -30,27 +27,28 @@ bool LocalWorld::updatePlayerDimension() { return true; } -void LocalWorld::update(double delta, vec& perfTimings, PerfTimer& perf) { +void LocalWorld::update(f64 delta, vec& perfTimings, PerfTimer& perf) { + + // Updates the dimensions. perf.start("update:world"); - World::update(delta); + for (auto& dimension : dimensions) dimension->update(delta); // Update children - perf.start("update:player"); if (*player) player.l()->update(renderer.window.input, delta, renderer.window.input.mouseDelta()); refs->update(delta, net); + + // Update the network perf.start("update:net"); net.update(); // Commit interpolated mapblocks - perf.start("update:chunks"); auto finishedChunks = worldGenStream->update(); lastInterpolations = finishedChunks->size() / 64; for (const auto& chunk : *finishedChunks) commitChunk(chunk); // Update debug interface - perf.start("update:debug"); debugGui.update( player.l(), delta, @@ -60,7 +58,6 @@ void LocalWorld::update(double delta, vec& perfTimings, PerfTimer& perf) activeDimension->getMeshChunksCommitted()); // Toggle regular interface - if (renderer.window.input.keyPressed(GLFW_KEY_F1)) { hudVisible = !hudVisible; debugGui.changeVisibility(hudVisible ? debugVisible ? DebugGui::Visibility::OFF : @@ -69,7 +66,6 @@ void LocalWorld::update(double delta, vec& perfTimings, PerfTimer& perf) } // Toggle debug interface - if (renderer.window.input.keyPressed(GLFW_KEY_F3)) { debugVisible = !debugVisible; debugGui.changeVisibility(hudVisible ? debugVisible ? DebugGui::Visibility::OFF : @@ -77,11 +73,11 @@ void LocalWorld::update(double delta, vec& perfTimings, PerfTimer& perf) } } -void LocalWorld::handleWorldPacket(std::unique_ptr p) { +void LocalWorld::handleWorldPacket(uptr p) { worldGenStream->queuePacket(std::move(p)); } -void LocalWorld::handlePlayerEntPacket(std::unique_ptr p) { +void LocalWorld::handlePlayerEntPacket(uptr p) { if (!player) throw std::runtime_error("Received playerEnt info *before* the player was created."); u32 id = p->d.read(); @@ -107,25 +103,26 @@ void LocalWorld::handlePlayerEntPacket(std::unique_ptr p) { // getActiveDimension().playerEntities.emplace_back(p->d.read(), id, playerModel); } -void LocalWorld::handleModMessage(const std::string& channel, const std::string& message) { +void LocalWorld::handleModMessage(const string& channel, const string& message) { game->getParser().safe_function(game->getParser().core["trigger"], "message", channel, game->getParser().safe_function(game->getParser().core["deserialize"], message)); } -void LocalWorld::commitChunk(std::shared_ptr c) { +void LocalWorld::commitChunk(sptr c) { activeDimension->setChunk(std::move(c)); } -DimensionPtr LocalWorld::createDimension(const std::string& identifier, std::unordered_set& biomes) { +DimensionPtr LocalWorld::createDimension(const string& identifier, std::unordered_set& biomes) { auto mapGen = std::make_shared(**game, *this, 0 /* TODO: Get the seed here */, biomes); dimensions.emplace_back(std::make_shared( game, *this, identifier, this->dimensions.size(), std::move(mapGen))); + dimensionIndexes[identifier] = dimensions.size() - 1; DimensionPtr d = dimensions.back(); return d; } -void LocalWorld::sendMessage(const std::string& channel, const std::string& message) { +void LocalWorld::sendMessage(const string& channel, const string& message) { net.sendPacket(Serializer().append(channel).append(message) .packet(Packet::Type::MOD_MESSAGE), Packet::Channel::INTERACT); } @@ -135,8 +132,8 @@ DimensionPtr LocalWorld::getActiveDimension() { } void LocalWorld::setActiveDimension(DimensionPtr dim) { - this->activeDimension->deactivate(); - this->activeDimension = dim.l(); + activeDimension->deactivate(); + activeDimension = dim.l(); } ClientNetworkInterpreter& LocalWorld::getNet() { @@ -151,7 +148,7 @@ InventoryRefsPtr LocalWorld::getRefs() { return refs; } -void LocalWorld::drawWorld() { +void LocalWorld::drawChunks() { activeDimension->renderChunks(renderer); } diff --git a/src/world/LocalWorld.h b/src/world/LocalWorld.h index 100f967e..4a7f149b 100644 --- a/src/world/LocalWorld.h +++ b/src/world/LocalWorld.h @@ -1,76 +1,109 @@ -// -// Created by aurailus on 14/12/18. -// - #pragma once #include "World.h" -#include "util/PerfTimer.h" #include "client/gui/DebugGui.h" #include "world/dim/LocalDimension.h" #include "client/conn/ClientNetworkInterpreter.h" class Window; class Renderer; +class PerfTimer; class LocalPlayer; class LocalSubgame; class LocalInventoryRefs; class WorldInterpolationStream; +/** + * Manages the local active dimension, + * and communication between the client and the server. + */ + class LocalWorld : public World { public: + LocalWorld(SubgamePtr game, ServerConnection& conn, Renderer& window, vec& perfSections); - void connect(); + /** Initializes the world, binding callbacks to the network handler. */ + void init(); + /** Sets the player's dimension to the default one, if it exists. TODO: Could this be done in init()? */ bool updatePlayerDimension(); - void update(double delta, vec& perfTimings, PerfTimer& perf); + /** Updates the dimension and the player, reads incoming packets, and commits decoded chunks. TODO: Decoding all the chunks from the getgo seems unnecessary... */ + void update(f64 delta, vec& perfTimings, PerfTimer& perf); - void handleWorldPacket(std::unique_ptr p); + /** Queues a packet to be read. */ + void handleWorldPacket(uptr p); - void handlePlayerEntPacket(std::unique_ptr p); + /** Reads a player entity packet, and updates the required entity. */ + void handlePlayerEntPacket(uptr p); - void handleModMessage(const std::string& channel, const std::string& message); + /** Triggers a callback in lua for the provided mod message. */ + void handleModMessage(const string& channel, const string& message); - void commitChunk(std::shared_ptr chunk); + /** Sets a chunk in the current dimension. */ + void commitChunk(sptr chunk); - virtual void sendMessage(const std::string& channel, const std::string& message) override; + /** Sends a mod message to the server on the channel specified. */ + virtual void sendMessage(const string& channel, const string& message) override; - virtual DimensionPtr - createDimension(const std::string& identifier, std::unordered_set& biomes) override; + /** Creates a new dimension with the identifier and biomes provided. */ + virtual DimensionPtr createDimension(const string& identifier, std::unordered_set& biomes) override; + /** Gets the active dimension. */ DimensionPtr getActiveDimension(); + /** Sets the active dimension. */ void setActiveDimension(DimensionPtr); + /** Gets the local player. */ PlayerPtr getPlayer(); + /** Returns a reference to the local inventory refs. */ virtual InventoryRefsPtr getRefs() override; + /** Returns a reference to the network handler. */ ClientNetworkInterpreter& getNet(); - /** Renders the visible block chunks to the screen. */ - void drawWorld(); - /** Renders the visible entities to the screen. */ + /** Renders the visible chunks to the screen. */ + void drawChunks(); + + /** Renders the entities to the screen. */ void drawEntities(); - /** Renders non-diagetic (UI) elements to the screen using an orthographic projection. */ + + /** Renders the interface to the screen using an orthographic projection. */ void drawInterface(); private: + + /** A reference to the renderer. */ Renderer& renderer; + /** The network handler. */ ClientNetworkInterpreter net; - std::shared_ptr refs; + + /** The local inventories. */ + sptr refs; + + /** The local player. */ PlayerPtr player; + /** The debug interface. */ DebugGui debugGui; - uint32_t lastInterpolations = 0; + /** The number of chunks that were interpolated last frome. */ + u32 lastInterpolations = 0; + + /** Whether or not the hud should be visible. */ bool hudVisible = true; + + /** Whether or not the debug interface should be visible. */ bool debugVisible = true; - std::shared_ptr activeDimension = nullptr; - std::shared_ptr worldGenStream = nullptr; + /** A pointer to the active dimension. */ + sptr activeDimension = nullptr; + + /** A reference to the world gen stream. */ + sptr worldGenStream; }; diff --git a/src/world/ServerWorld.cpp b/src/world/ServerWorld.cpp index f7bc3a79..f285bcd0 100644 --- a/src/world/ServerWorld.cpp +++ b/src/world/ServerWorld.cpp @@ -1,10 +1,7 @@ -// -// Created by aurailus on 05/03/19. -// - #include #include #include +#include #include "ServerWorld.h" @@ -22,33 +19,28 @@ #include "server/stream/ServerGenStream.h" #include "server/stream/ServerPacketStream.h" -ServerWorld::ServerWorld(unsigned int seed, SubgamePtr game, ServerClients& clients) : +ServerWorld::ServerWorld(u32 seed, SubgamePtr game, ServerClients& clients) : World(game), seed(seed), clients(clients), - refs(std::make_shared(game, clients)) { + refs(make_shared(game, clients)) { clients.init(this); generateOrder.reserve(mapBlockGenRange.x * 2 + 1 * mapBlockGenRange.x * 2 + 1 * mapBlockGenRange.y * 2 + 1); - std::unordered_set found {}; - std::queue queue {}; + std::unordered_set found {}; + std::queue queue {}; queue.emplace(0, 0, 0); found.emplace(0, 0, 0); - const std::vector dirs{ - ivec3 { 1, 0, 0 }, ivec3{ -1, 0, 0 }, - ivec3 { 0, 1, 0 }, ivec3{ 0, -1, 0 }, - ivec3 { 0, 0, 1 }, ivec3{ 0, 0, -1 }}; - while (!queue.empty()) { - glm::ivec3 pos = queue.front(); + ivec3 pos = queue.front(); queue.pop(); generateOrder.push_back(pos); - for (auto dir : dirs) { - glm::ivec3 offset = pos + dir; + for (auto dir : Vec::TO_VEC) { + ivec3 offset = pos + dir; if (offset.x < -mapBlockGenRange.x || offset.x > mapBlockGenRange.x || offset.y < -mapBlockGenRange.y || offset.y > mapBlockGenRange.y || offset.z < -mapBlockGenRange.x || offset.z > mapBlockGenRange.x || @@ -62,43 +54,67 @@ ServerWorld::ServerWorld(unsigned int seed, SubgamePtr game, ServerClients& clie } } -void ServerWorld::init(const std::string& worldDir) { - genStream = std::make_unique(*game.s(), *this); - packetStream = std::make_unique(*this); +void ServerWorld::init(const string& worldDir) { + genStream = make_unique(*game.s(), *this); +// packetStream = make_unique(*this); // fileManip = std::make_shared("worlds/" + worldDir + "/"); } -void ServerWorld::update(double delta) { - World::update(delta); +void ServerWorld::update(f64 delta) { + for (auto& dimension : dimensions) dimension->update(delta); + refs->update(); u32 genCount = 0; std::unordered_set updatedChunks {}; auto finishedGen = genStream->update(); -// if (finishedGen->size()) std::cout << finishedGen->size() << " finished gens" << std::endl; + + Timer t("Finishing Generation"); for (auto& data : *finishedGen) { + let dim = getDimension(data.dim); for (const auto& chunkPair : *data.created) { updatedChunks.insert(ivec4(chunkPair.first, data.dim)); - getDimension(data.dim)->setChunk(sptr(chunkPair.second)); + dim->setChunk(sptr(chunkPair.second)); } - // Mapblock might have been pruned in between generation assignment and now. - auto mb = getDimension(data.dim)->getMapBlock(glm::ivec3(data.pos)); - if (mb) mb->generated = true; + auto mapBlock = dim->getMapBlock(ivec3(data.pos)); - packetStream->queue(data.dim, data.pos); - genCount++; + if (!mapBlock->generated) { + mapBlock->generated = true; + assert(mapBlock); + + Serializer s {}; + for (u16 i = 0; i < 64; i++) { + auto chunk = mapBlock->get(i); + assert(chunk); + if (chunk) s.append(chunk->compressToString()); + } + + let packet = s.packet(Packet::Type::MAPBLOCK); + + for (auto& client : clients.getClients()) { + if (!client.second->player) continue; + packet.sendTo(client.second->peer, Packet::Channel::WORLD); + } + + genCount++; + totalGens++; + } + } + if (!finishedGen->empty()) { + t.printElapsedMs(); + std::cout << totalGens << std::endl; } - auto finishedPackets = packetStream->update(); +// auto finishedPackets = packetStream->update(); // if (finishedPackets->size()) std::cout << finishedPackets->size() << " finished packets" << std::endl; - for (auto& data : *finishedPackets) { - for (auto& client : clients.getClients()) { - if (!client.second->player) continue; - data->packet->sendTo(client.second->peer, Packet::Channel::WORLD); - } - } +// for (auto& data : *finishedPackets) { +// for (auto& client : clients.getClients()) { +// if (!client.second->player) continue; +// data->packet->sendTo(client.second->peer, Packet::Channel::WORLD); +// } +// } generatedMapBlocks = genCount; @@ -178,25 +194,16 @@ void ServerWorld::update(double delta) { } } -DimensionPtr ServerWorld::createDimension(const std::string& identifier, std::unordered_set& biomes) { - auto mapGen = std::make_shared(**game, *this, seed, biomes); - dimensions.emplace_back(std::make_shared( - game, *this, identifier, this->dimensions.size(), std::move(mapGen))); +DimensionPtr ServerWorld::createDimension(const string& identifier, std::unordered_set& biomes) { + dimensions.emplace_back(make_shared( + game, *this, identifier, this->dimensions.size(), + make_shared(**game, *this, seed, biomes))); + dimensionIndexes[identifier] = dimensions.size() - 1; DimensionPtr d = dimensions.back(); return d; } -DimensionPtr ServerWorld::getDimension(unsigned int index) { - return dimensions[index]; -} - -DimensionPtr ServerWorld::getDimension(const std::string& identifier) { - for (auto& dimension : dimensions) - if (dimension->getIdentifier() == identifier) return dimension; - throw std::runtime_error("No dimension named " + identifier + " found."); -} - InventoryRefsPtr ServerWorld::getRefs() { return InventoryRefsPtr(refs); } @@ -212,18 +219,18 @@ void ServerWorld::changedMapBlocks(ServerPlayer& player) { } void ServerWorld::generateMapBlocks(ServerPlayer& player) { - unsigned int generating = 0; - glm::ivec3 playerMapBlock = Space::MapBlock::world::fromBlock(player.getPos()); + u32 generating = 0; + ivec3 playerMapBlock = Space::MapBlock::world::fromBlock(player.getPos()); for (const auto& c : generateOrder) { - glm::ivec3 mapBlockPos = playerMapBlock + c; + ivec3 mapBlockPos = playerMapBlock + c; generating += generateMapBlock(player.getDim()->getInd(), mapBlockPos); } - std::cout << "Player moved, generating " << generating << " MapBlocks." << std::endl; +// std::cout << "Player moved, generating " << generating << " MapBlocks." << std::endl; } -bool ServerWorld::generateMapBlock(unsigned int dim, glm::ivec3 pos) { +bool ServerWorld::generateMapBlock(u16 dim, ivec3 pos) { auto dimension = getDimension(dim); if (!dimension->getMapBlock(pos) || !dimension->getMapBlock(pos)->generated) return genStream->queue(dim, pos); return false; @@ -240,7 +247,25 @@ void ServerWorld::sendChunksToPlayer(ServerPlayer& client) { for (auto& pos : generateOrder) { if (oldBounds.intersects(playerPos + pos) || !newBounds.intersects(playerPos + pos)) continue; - packetStream->queue(client.getDim()->getInd(), pos + playerPos); +// packetStream->queue(client.getDim()->getInd(), pos + playerPos); + +// auto dim = client.getDim(); +// std::cout << dim->getInd() << std::endl; +// auto mb = dim->getMapBlock(pos); +// std::cout << mb << std::endl; +// if (!mb) return; +// Serializer s {}; +// for (u16 i = 0; i < 64; i++) { +// auto chunk = mb->get(i); +// if (chunk) s.append(chunk->compressToString()); +// } +// +// let packet = make_unique(Packet::Type::MAPBLOCK); +// +// for (auto& client : clients.getClients()) { +// if (!client.second->player) continue; +// packet->sendTo(client.second->peer, Packet::Channel::WORLD); +// } } } diff --git a/src/world/ServerWorld.h b/src/world/ServerWorld.h index 30f0ffb5..70a3f83d 100644 --- a/src/world/ServerWorld.h +++ b/src/world/ServerWorld.h @@ -1,9 +1,3 @@ -// -// World subclass for the server. -// Handles blocks, entities, and clients. -// Created by aurailus on 05/03/19. -// - #pragma once #include "world/World.h" @@ -19,51 +13,77 @@ class ServerGenStream; class ServerInventoryRefs; class ServerPacketStream; +/** + * Manages server dimensions and players. + * Handles sending chunk and entity data to clients. + */ + class ServerWorld : public World { public: - explicit ServerWorld(unsigned int seed, SubgamePtr game, ServerClients& clients); - void init(const std::string& worldDir); + explicit ServerWorld(u32 seed, SubgamePtr game, ServerClients& clients); - void update(double delta) override; + /** Initializes the map and packet thread pools. */ + void init(const string& worldDir); - virtual void sendMessage(const std::string& channel, const std::string& message) override; + /** Updates dimensions, and sends new or dirty chunks to clients. */ + void update(double delta); + + /** Sends a mod message to the channel provided. */ + virtual void sendMessage(const string& channel, const string& message) override; - virtual DimensionPtr - createDimension(const std::string& identifier, std::unordered_set& biomes) override; - - virtual DimensionPtr getDimension(unsigned int index) override; - - virtual DimensionPtr getDimension(const std::string& identifier) override; + /** Creates a new dimension with the identifier and biomes provided. */ + virtual DimensionPtr createDimension(const string& identifier, std::unordered_set& biomes) override; + /** Returns a reference to the world's inventory refs. */ virtual InventoryRefsPtr getRefs() override; + /** Gets the list of connected clients. */ virtual ServerClients& getClients(); private: + + /** Called when a player changes mapblocks, to generate and send new chunks. */ void changedMapBlocks(ServerPlayer& player); - bool generateMapBlock(unsigned int dim, glm::ivec3 pos); + /** Generates a single mapblock, if it doesn't exist already. */ + bool generateMapBlock(u16 dim, ivec3 pos); + /** Generates mapblocks around the player specified. */ void generateMapBlocks(ServerPlayer& player); + /** Sends all of the surrounding chunks to the specified player. */ void sendChunksToPlayer(ServerPlayer& client); - std::shared_ptr genStream = nullptr; - std::shared_ptr packetStream = nullptr; - + /** Generates new chunks. */ + sptr genStream = nullptr; + + /** The seed for generating the world. */ u32 seed; + + /** A reference to the client list. */ ServerClients& clients; - std::shared_ptr refs; + + /** The server inventories. */ + sptr refs; // std::string worldDir; // std::shared_ptr fileManip; - u32 generatedMapBlocks = 0; - std::vector generateOrder; - - const ivec2 mapBlockGenRange = { 4, 4 }; - const ivec2 sendRange = { 4, 4 }; - const ivec2 activeChunkRange = { 16, 16 }; -}; + usize totalGens = 0; + /** The amount of mapblocks that were generated last frame. */ + u32 generatedMapBlocks = 0; + + /** A vector of positions for the order to generate mapblocks in. */ + vec generateOrder; + + /** The range in mapblocks to generate around clients. */ + const ivec2 mapBlockGenRange = { 4, 4 }; + + /** The range in mapblocks to send to clients. */ + const ivec2 sendRange = { 4, 4 }; + + /** The range around clients that chunks should be updated. */ + const ivec2 activeChunkRange = { 4, 4 }; +}; diff --git a/src/world/World.cpp b/src/world/World.cpp index 1b7a3c45..699c67be 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -1,33 +1,29 @@ -// -// Created by aurailus on 2020-01-09. -// - -#include - #include "World.h" +#include "util/Util.h" #include "world/dim/Dimension.h" -World::World(SubgamePtr game) : game(game) {} - -void World::update(double delta) { - for (auto& dimension : dimensions) dimension->update(delta); -} +//void World::update(f64 delta) { +// for (auto& dimension : dimensions) dimension->update(delta); +//} DimensionPtr World::getDefaultDimension() { if (defaultDimension.empty()) throw std::runtime_error("No default dimension was set."); return getDimension(defaultDimension); } -void World::setDefaultDimension(const std::string& identifier) { +void World::setDefaultDimension(const string& identifier) { defaultDimension = identifier; } -DimensionPtr World::getDimension(unsigned int index) { +DimensionPtr World::getDimension(u16 index) { + if (dimensions.size() <= index) throw std::runtime_error( + "Dimension #" + Util::toString(index) + " does not exist."); return dimensions[index]; } -DimensionPtr World::getDimension(const std::string& identifier) { - for (auto& dimension : dimensions) if (dimension->getIdentifier() == identifier) return dimension; - throw std::runtime_error("No dimension named " + identifier + " found."); -} +DimensionPtr World::getDimension(const string& identifier) { + if (dimensionIndexes.find(identifier) == dimensionIndexes.end() || dimensions.size() <= dimensionIndexes[identifier]) + throw std::runtime_error("Dimension '" + identifier + "'does not exist."); + return dimensions[dimensionIndexes[identifier]]; +} \ No newline at end of file diff --git a/src/world/World.h b/src/world/World.h index b63adcb1..6681375d 100644 --- a/src/world/World.h +++ b/src/world/World.h @@ -1,45 +1,59 @@ -// -// Created by aurailus on 2020-01-09. -// - #pragma once -#include -#include #include #include "util/Vec.h" +#include "util/Types.h" #include "util/CovariantPtr.h" class Subgame; - class Dimension; +/** + * Manages all loaded dimensions. + * Also handles all inventories, and sending mod messages. + * LocalWorld and ServerWorld are children of this class. + */ + class World { public: + World(const World& o) = delete; - explicit World(SubgamePtr game); + explicit World(SubgamePtr game) : game(game) {}; - virtual void update(double delta); - - virtual DimensionPtr createDimension(const std::string& identifier, std::unordered_set& biomes) = 0; + /** Creates a new dimension with the identifier and biomes provided. */ + virtual DimensionPtr createDimension(const string& identifier, std::unordered_set& biomes) = 0; + /** Gets the default dimension, throws if no default is set. */ virtual DimensionPtr getDefaultDimension(); - virtual void setDefaultDimension(const std::string& defaultDimension); + /** Sets the identifier of the default dimension. */ + virtual void setDefaultDimension(const string& defaultDimension); - virtual DimensionPtr getDimension(unsigned int index); + /** Gets a dimension by its index, throws if there is none. */ + virtual DimensionPtr getDimension(u16 index); - virtual DimensionPtr getDimension(const std::string& identifier); + /** Gets a dimension by its identifier, throws if there is none. */ + virtual DimensionPtr getDimension(const string& identifier); - virtual void sendMessage(const std::string& channel, const std::string& message) = 0; + /** Sends a mod message on the channel specified. */ + virtual void sendMessage(const string& channel, const string& message) = 0; + /** Returns a reference to the world's inventory refs. */ virtual InventoryRefsPtr getRefs() = 0; protected: - std::string defaultDimension{}; - std::vector> dimensions; + /** The identifier of the default dimension. */ + string defaultDimension {}; + + /** A vector of dimensions in the world. */ + vec> dimensions; + + /** A map of dimension identifiers to indexes. */ + std::unordered_map dimensionIndexes; + + /** A reference to the subgame. */ SubgamePtr game; }; diff --git a/src/world/dim/Dimension.cpp b/src/world/dim/Dimension.cpp index 7abeae5e..3c86745e 100644 --- a/src/world/dim/Dimension.cpp +++ b/src/world/dim/Dimension.cpp @@ -18,8 +18,8 @@ bool Dimension::setBlock(ivec3 pos, u16 block) { auto& def = game->getDefs().blockFromId(block); - glm::ivec4 oldLight = chunk->getLight(Space::Block::index(pos)); - glm::ivec3 newLight = def.lightSource; + ivec4 oldLight = chunk->getLight(Space::Block::index(pos)); + ivec3 newLight = def.lightSource; if (oldLight.x + oldLight.y + oldLight.z != 0) removeBlockLight(pos); if (newLight.x + newLight.y + newLight.z != 0) addBlockLight(pos, newLight); diff --git a/src/world/dim/LocalDimension.cpp b/src/world/dim/LocalDimension.cpp index 47ec5d3e..3c64d45e 100644 --- a/src/world/dim/LocalDimension.cpp +++ b/src/world/dim/LocalDimension.cpp @@ -35,75 +35,79 @@ void LocalDimension::deactivate() { } } -void LocalDimension::update(double delta) { +void LocalDimension::update(f64 delta) { finishMeshes(); + /* Update local entities and player entities. */ for (auto& entity : entities) entity.entity.l()->update(delta); for (auto& entity : playerEntities) entity.update(delta); - auto clientMapBlock = Space::MapBlock::world::fromBlock(static_cast(world).getPlayer()->getPos()); + /* + * Delete mapblocks and regions that are outside of the retain range, + * and compress chunks if they are idle. + */ - for (auto it = regions.cbegin(); it != regions.cend();) { - bool remove = false; - for (unsigned short m = 0; m < 64; m++) { - auto mapBlock = it->second->get(m); + let clientMapBlock = Space::MapBlock::world::fromBlock(static_cast(world).getPlayer()->getPos()); + + for (let it = regions.cbegin(); it != regions.cend();) { + for (u16 m = 0; m < 64; m++) { + let mapBlock = it->second->get(m); if (!mapBlock) continue; - if (abs(clientMapBlock.x - mapBlock->pos.x) > LocalDimension::MB_STORE_H + 1 - || abs(clientMapBlock.y - mapBlock->pos.y) > LocalDimension::MB_STORE_V + 1 - || abs(clientMapBlock.z - mapBlock->pos.z) > LocalDimension::MB_STORE_H + 1) { + if (abs(clientMapBlock.x - mapBlock->pos.x) > retainMapBlockRange.x || + abs(clientMapBlock.y - mapBlock->pos.y) > retainMapBlockRange.y || + abs(clientMapBlock.z - mapBlock->pos.z) > retainMapBlockRange.x) { - for (unsigned short c = 0; c < 64; c++) { - auto chunk = mapBlock->get(c); - if (!chunk) continue; - removeMeshChunk(chunk->getPos()); + for (u16 c = 0; c < 64; c++) { + let chunk = mapBlock->get(c); + if (chunk) removeMeshChunk(chunk->getPos()); } it->second->remove(m); - if (it->second->count <= 0) { - remove = true; - it = regions.erase(it); - break; - } + if (it->second->count <= 0) goto erase_region_and_continue; } else { - for (unsigned short c = 0; c < 64; c++) { - auto chunk = mapBlock->get(c); - if (!chunk) continue; - chunk->compressIfIdle(); + for (u16 c = 0; c < 64; c++) { + let chunk = mapBlock->get(c); + if (chunk) chunk->compressIfIdle(); } } } - if (!remove) it++; + + it++; + continue; + + erase_region_and_continue: + it = regions.erase(it); } } -void LocalDimension::setChunk(std::shared_ptr chunk) { +void LocalDimension::setChunk(sptr chunk) { Dimension::setChunk(chunk); - attemptMeshChunk(chunk); + meshChunk(chunk); } bool LocalDimension::setBlock(ivec3 pos, u16 block) { - bool exists = Dimension::setBlock(pos, block); - if (!exists) return false; + bool modified = Dimension::setBlock(pos, block); + if (!modified) return false; - auto chunkPos = Space::Chunk::world::fromBlock(pos); - auto chunk = getChunk(chunkPos); + let chunkPos = Space::Chunk::world::fromBlock(pos); + let chunk = getChunk(chunkPos); chunk->setDirty(true); - auto lp = Space::Block::relative::toChunk(pos); - auto cp = Space::Chunk::world::fromBlock(pos); + let lp = Space::Block::relative::toChunk(pos); + let cp = Space::Chunk::world::fromBlock(pos); - std::shared_ptr tempChunk; - if (lp.x == 15 && (tempChunk = getChunk(cp + ivec3{ 1, 0, 0 }))) tempChunk->setDirty(true); - else if (lp.x == 0 && (tempChunk = getChunk(cp + ivec3{ -1, 0, 0 }))) tempChunk->setDirty(true); - if (lp.y == 15 && (tempChunk = getChunk(cp + ivec3{ 0, 1, 0 }))) tempChunk->setDirty(true); - else if (lp.y == 0 && (tempChunk = getChunk(cp + ivec3{ 0, -1, 0 }))) tempChunk->setDirty(true); - if (lp.z == 15 && (tempChunk = getChunk(cp + ivec3{ 0, 0, 1 }))) tempChunk->setDirty(true); - else if (lp.z == 0 && (tempChunk = getChunk(cp + ivec3{ 0, 0, -1 }))) tempChunk->setDirty(true); + sptr adjacent; + if (lp.x == 15 && (adjacent = getChunk(cp + ivec3 { 1, 0, 0 }))) adjacent->setDirty(true); + else if (lp.x == 0 && (adjacent = getChunk(cp + ivec3 { -1, 0, 0 }))) adjacent->setDirty(true); + if (lp.y == 15 && (adjacent = getChunk(cp + ivec3 { 0, 1, 0 }))) adjacent->setDirty(true); + else if (lp.y == 0 && (adjacent = getChunk(cp + ivec3 { 0, -1, 0 }))) adjacent->setDirty(true); + if (lp.z == 15 && (adjacent = getChunk(cp + ivec3 { 0, 0, 1 }))) adjacent->setDirty(true); + else if (lp.z == 0 && (adjacent = getChunk(cp + ivec3 { 0, 0, -1 }))) adjacent->setDirty(true); - attemptMeshChunk(chunk, true); + meshChunk(chunk, true); return true; } @@ -129,7 +133,7 @@ void LocalDimension::blockInteract(const Target& target, PlayerPtr player) { } void LocalDimension::blockPlaceOrInteract(const Target& target, PlayerPtr player) { - std::tuple, sol::optional> res = game->getParser().safe_function( + std::tuple, sol::optional> res = game->getParser().safe_function( game->getParser().core["block_interact_or_place"], Api::Usertype::LocalPlayer(player.l()), Api::Usertype::Target(target)); @@ -144,7 +148,7 @@ void LocalDimension::blockPlaceOrInteract(const Target& target, PlayerPtr player } double LocalDimension::blockHit(const Target& target, PlayerPtr player) { - double timeout = 0, damage = 0; + f64 timeout = 0, damage = 0; sol::tie(damage, timeout) = game->getParser().safe_function(game->getParser().core["block_hit"], Api::Usertype::LocalPlayer(player.l()), Api::Usertype::Target(target)); @@ -164,13 +168,13 @@ void LocalDimension::wieldItemUse(const Target& target, PlayerPtr player) { inv->getList(player->getWieldList())->setStack(player->getWieldIndex(), ItemStack(*stack, game)); } -void LocalDimension::setMeshChunk(std::shared_ptr meshChunk) { +void LocalDimension::setMeshChunk(sptr meshChunk) { if (renderRefs.count(meshChunk->getPos())) removeMeshChunk(meshChunk->getPos()); renderElems.push_back(std::static_pointer_cast(meshChunk)); renderRefs.emplace(meshChunk->getPos(), --renderElems.end()); } -void LocalDimension::removeMeshChunk(const glm::ivec3& pos) { +void LocalDimension::removeMeshChunk(const ivec3& pos) { if (!renderRefs.count(pos)) return; auto refIter = renderRefs.at(pos); @@ -185,13 +189,13 @@ i64 LocalDimension::nextEntityInd() { } void LocalDimension::addLocalEntity(Api::Usertype::Entity entity) { - unsigned int id = entity.get_id(); + i64 id = entity.get_id(); entities.push_back(entity); entityRefs.emplace(id, --entities.end()); } void LocalDimension::removeLocalEntity(Api::Usertype::Entity entity) { - unsigned int id = entity.get_id(); + i64 id = entity.get_id(); if (!entityRefs.count(id)) return; auto refIter = entityRefs.at(id); @@ -303,7 +307,7 @@ void LocalDimension::serverEntitiesInfo(Deserializer& e) { } } -void LocalDimension::serverEntitiesRemoved(Deserializer& d) { +void LocalDimension::removeServerEntities(Deserializer& d) { d.read(); while (!d.atEnd()) { i64 id = d.read(); @@ -350,13 +354,13 @@ u32 LocalDimension::getMeshChunksCommitted() { std::unordered_set LocalDimension::propogateAddNodes() { auto updated = Dimension::propogateAddNodes(); - for (auto& update : updated) attemptMeshChunk(getChunk(update)); + for (auto& update : updated) meshChunk(getChunk(update)); return {}; } std::unordered_set LocalDimension::propogateRemoveNodes() { auto updated = Dimension::propogateRemoveNodes(); - for (auto& update : updated) attemptMeshChunk(getChunk(update)); + for (auto& update : updated) meshChunk(getChunk(update)); return {}; } @@ -364,36 +368,38 @@ void LocalDimension::finishMeshes() { lastMeshesCommitted = 0; auto finishedMeshes = meshGenStream->update(); - for (ChunkMeshDetails* meshDetails : finishedMeshes) { - if (!meshDetails->vertices.empty()) { - auto meshChunk = std::make_shared(); - meshChunk->create(meshDetails->vertices, meshDetails->indices); - meshChunk->setPos(meshDetails->pos); - - setMeshChunk(meshChunk); + for (MeshChunkDetails* details : finishedMeshes) { + if (!details->vertices.empty()) { + setMeshChunk(make_shared(details->pos, details->vertices, details->indices)); lastMeshesCommitted++; } - else removeMeshChunk(meshDetails->pos); + else removeMeshChunk(details->pos); - delete meshDetails; + delete details; } } -void LocalDimension::attemptMeshChunk(const sptr& chunk, bool priority, bool updateAdjacents) { - bool renderable = true; - for (auto dir : Vec::TO_VEC) if (!getAdjacentExists(chunk->getPos() + dir, updateAdjacents)) renderable = false; - if (!renderable) return; +void LocalDimension::meshChunk(const sptr& chunk, bool priority, bool updateAdjacents) { + // Run this loop first, because even if this chunk shouldn't render, the adjacents maybe should. + bool render = true; + for (let dir : Vec::TO_VEC) + if (!adjacentExists(chunk->getPos() + dir, updateAdjacents, priority)) + render = false; + if (!render) return; if (!chunk->isDirty()) return; - if (!chunk->chunkShouldRender()) removeMeshChunk(chunk->getPos()); + if (!chunk->chunkShouldRender()) { + removeMeshChunk(chunk->getPos()); + return; + } meshGenStream->queue(chunk->getPos(), priority); chunk->setDirty(false); } -bool LocalDimension::getAdjacentExists(vec3 pos, bool updateAdjacents) { - auto chunk = getChunk(pos); - if (chunk == nullptr) return false; - if (updateAdjacents) attemptMeshChunk(chunk, false, false); +bool LocalDimension::adjacentExists(ivec3 pos, bool update, bool priority) { + let chunk = getChunk(pos); + if (!chunk) return false; + if (update) meshChunk(chunk, priority, false); return true; } \ No newline at end of file diff --git a/src/world/dim/LocalDimension.h b/src/world/dim/LocalDimension.h index c08af9ba..e3e05edb 100644 --- a/src/world/dim/LocalDimension.h +++ b/src/world/dim/LocalDimension.h @@ -21,17 +21,17 @@ class ChunkRenderElem; class LocalDimension : public Dimension { public: - const static u8 MB_STORE_H = 4; - const static u8 MB_STORE_V = 4; - LocalDimension(SubgamePtr game, LocalWorld& world, const string& identifier, u16 ind, sptr mapGen); void deactivate(); + /** Updates chunks and entities. */ void update(f64 delta) override; + /** Sets the chunk, and then queues it to be meshed. */ void setChunk(sptr chunk) override; + /** Sets the block, and queues the relevant chunks for remeshing. */ bool setBlock(ivec3 pos, u16 block) override; virtual void blockPlace(const Target& target, PlayerPtr player) override; @@ -56,7 +56,7 @@ public: void serverEntitiesInfo(Deserializer& d); - void serverEntitiesRemoved(Deserializer& d); + void removeServerEntities(Deserializer& d); std::vector getEntitiesInRadius(vec3 pos, f32 radius); @@ -84,9 +84,11 @@ private: void finishMeshes(); - void attemptMeshChunk(const sptr& chunk, bool priority = false, bool updateAdjacents = true); + /** Queues a chunk to be meshed if its dirty and the adjacent chunks exist. */ + void meshChunk(const sptr& chunk, bool priority = false, bool updateAdjacents = true); - bool getAdjacentExists(vec3 pos, bool updateAdjacents); + /** Checks if a chunk exists and optionally meshes it if dirty. */ + bool adjacentExists(ivec3 pos, bool update, bool priority = false); sptr meshGenStream; @@ -96,6 +98,7 @@ private: std::unordered_map renderRefs{}; std::list> renderElems{}; + const ivec2 retainMapBlockRange = { 4, 4 }; i64 entityInd = -1; }; diff --git a/src/world/dim/ServerDimension.cpp b/src/world/dim/ServerDimension.cpp index 04985e76..17290c2c 100644 --- a/src/world/dim/ServerDimension.cpp +++ b/src/world/dim/ServerDimension.cpp @@ -22,36 +22,49 @@ ServerDimension::ServerDimension(SubgamePtr game, ServerWorld& world, const std: Dimension(game, static_cast(world), identifier, ind, std::move(mapGen)) {} void ServerDimension::update(double delta) { + + /* Update server entities. */ for (auto& entity : luaEntities) entity.entity.s()->update(delta); - for (const auto& region : regions) { - for (unsigned short i = 0; i < 64; i++) { - auto mapBlock = region.second->get(i); + /* + * Delete mapblocks and regions that are outside of the retain range, + * and compress chunks if they are idle. + */ + + for (let it = regions.cbegin(); it != regions.cend();) { + for (u16 m = 0; m < 64; m++) { + let mapBlock = it->second->get(m); if (!mapBlock) continue; bool clientNearby = false; for (auto& client : static_cast(world).getClients().getClients()) { - if (!client.second->player) continue; - if (client.second->player->getDim()->getInd() == ind) { - auto clientPos = Space::MapBlock::world::fromBlock(client.second->player->getPos()); - if (abs(clientPos.x - mapBlock->pos.x) <= discardRange.x + 1 - && abs(clientPos.y - mapBlock->pos.y) <= discardRange.y + 1 - && abs(clientPos.z - mapBlock->pos.z) <= discardRange.x + 1) { - clientNearby = true; - break; - } - } + if (!client.second->player || client.second->player->getDim()->getInd() != ind) continue; + auto clientMapBlock = Space::MapBlock::world::fromBlock(client.second->player->getPos()); + if (abs(clientMapBlock.x - mapBlock->pos.x) <= retainMapBlockRange.x && + abs(clientMapBlock.y - mapBlock->pos.y) <= retainMapBlockRange.y && + abs(clientMapBlock.z - mapBlock->pos.z) <= retainMapBlockRange.x) { + clientNearby = true; + break; + } } - if (!clientNearby) region.second->remove(i); + if (!clientNearby) { + it->second->remove(m); + if (it->second->count <= 0) goto erase_region_and_continue; + } else { - for (unsigned short c = 0; c < 64; c++) { - auto chunk = mapBlock->get(c); - if (!chunk) continue; - chunk->compressIfIdle(); + for (u16 c = 0; c < 64; c++) { + let chunk = mapBlock->get(c); + if (chunk) chunk->compressIfIdle(); } } } + + it++; + continue; + + erase_region_and_continue: + it = regions.erase(it); } } @@ -103,8 +116,8 @@ void ServerDimension::wieldItemUse(const Target& target, PlayerPtr player) { } void ServerDimension::setChunk(std::shared_ptr chunk) { -// std::shared_ptr existing = getChunk(chunk->getPos()); -// if (existing) chunk = combineChunks(chunk, existing); + std::shared_ptr existing = getChunk(chunk->getPos()); + if (existing) chunk = combineChunks(chunk, existing); Dimension::setChunk(chunk); } diff --git a/src/world/dim/ServerDimension.h b/src/world/dim/ServerDimension.h index a4b2573b..9dd7af0e 100644 --- a/src/world/dim/ServerDimension.h +++ b/src/world/dim/ServerDimension.h @@ -53,7 +53,7 @@ private: std::list luaEntities{}; std::list removedEntities{}; - const ivec2 discardRange = { 20, 20 }; + const ivec2 retainMapBlockRange = { 4, 4 }; i64 entityInd = 1; }; diff --git a/src/world/dim/chunk/Chunk.cpp b/src/world/dim/chunk/Chunk.cpp index 1cf6b5a2..82afa895 100644 --- a/src/world/dim/chunk/Chunk.cpp +++ b/src/world/dim/chunk/Chunk.cpp @@ -52,8 +52,11 @@ const array& Chunk::getBiomesArray() { } void Chunk::combineWith(sptr o) { + useDecompressed(); + o->useDecompressed(); + for (u16 i = 0; i < 4096; i++) - if (o->getBlock(i) > DefinitionAtlas::INVALID) setBlock(i, o->getBlock(i)); + if (o->getBlock(i) > DefinitionAtlas::INVALID) d->blocks[i] = o->d->blocks[i]; if (generationState == GenerationState::GENERATED || o->isGenerated()) { generationState = GenerationState::GENERATED; diff --git a/src/world/gen/MapGen.cpp b/src/world/gen/MapGen.cpp index 5baa58d8..c90d9e0f 100644 --- a/src/world/gen/MapGen.cpp +++ b/src/world/gen/MapGen.cpp @@ -1,7 +1,9 @@ #include +#include #include "MapGen.h" +#include "util/Types.h" #include "world/World.h" #include "game/Subgame.h" #include "util/Structure.h" @@ -14,7 +16,7 @@ MapGen::MapGen(Subgame& game, World& world, u32 seed, std::unordered_set biomes) : game(game), world(world), props(seed) { - + std::unordered_set biomeIndices {}; for (const auto& str : biomes) { if (str[0] == '#') @@ -22,7 +24,7 @@ MapGen::MapGen(Subgame& game, World& world, u32 seed, std::unordered_set biomeIndices.insert(biome->index); else biomeIndices.insert(game.getBiomes().biomeFromStr(str).index); } - + generateVoronoi(biomeIndices); } @@ -95,12 +97,16 @@ std::unique_ptr MapGen::generateArea(u16 dim, ivec3 origin, u1 uptr depth = populateChunkDepth(density, std::move(densityAbove)); generateChunkBlocks(job, pos, biomeMap, *depth); -// generateChunkDecorAndLight(job, pos, biomeMap, *depth); + generateChunkDecorAndLight(job, pos, biomeMap, *depth); densityAbove = std::move(density); } } } + + for (let& chunk : *job.chunks) { + chunk.second->compress(); + } return std::move(job.chunks); } @@ -109,7 +115,7 @@ void MapGen::generateVoronoi(const std::unordered_set& biomes) { vec> points {}; for (auto biomeInd : biomes) { auto& biome = game.getBiomes().biomeFromId(biomeInd); - + points.emplace_back(vec3 { static_cast(std::fmin(voronoiSize - 1, std::fmax(0, (biome.temperature + 1) / 2 * voronoiSize))), @@ -117,7 +123,7 @@ void MapGen::generateVoronoi(const std::unordered_set& biomes) { static_cast(std::fmin(voronoiSize - 1, std::fmax(0, biome.roughness * voronoiSize))) }, biomeInd); } - + voronoi.setPoints(points); } @@ -130,20 +136,20 @@ u16 MapGen::getBiomeAt(f32 temperature, f32 humidity, f32 roughness) { uptr MapGen::populateChunkDensity(MapGen::Job& job, ivec3 localPos) { auto data = make_unique(); - + for (u16 i = 0; i < 4096; i++) { ivec3 indPos = Space::Block::fromIndex(i); vec3 queryPos = (vec3(localPos) + vec3(indPos) / 16.f) / static_cast(job.size); (*data)[i] = (job.volume.get(queryPos) + job.heightmap.get({ queryPos.x, 0, queryPos.z })) - ((job.pos.y + localPos.y) * 16 + indPos.y); } - + return data; } uptr MapGen::populateChunkDepth(uptr& chunkDensity, uptr chunkDensityAbove) { auto data = make_unique(); - + for (u16 i = 0; i < 256; i++) { ivec2 pos = { i / 16, i % 16 }; short depth = 16; @@ -166,27 +172,27 @@ uptr MapGen::populateChunkDepth(uptr& chunkDensity (*data)[ind] = depth; } } - + return data; } void MapGen::generateChunkBlocks(Job& job, ivec3 localPos, vec biomeMap, ChunkData& depthMap) { ivec3 chunkPos = job.pos + localPos; - + auto partial = (job.chunks->count(chunkPos) ? job.chunks->at(chunkPos) : nullptr); if (partial) job.chunks->erase(chunkPos); - + auto& chunk = *(*job.chunks->emplace(chunkPos, new Chunk(chunkPos)).first).second; - + u16 partialBlock = DefinitionAtlas::INVALID; - + for (u16 i = 0; i < 4096; i++) { ivec3 indPos = Space::Block::fromIndex(i); - + u16 biomeId = biomeMap[(localPos.x * 16 + indPos.x) * (job.size * 16 + 1) + (localPos.z * 16 + indPos.z)]; auto& biome = game.getBiomes().biomeFromId(biomeId); chunk.d->biomes[i] = biomeId; - + f32 depth = depthMap[i]; u16 blockId = partialBlock > DefinitionAtlas::INVALID ? partialBlock @@ -194,49 +200,49 @@ void MapGen::generateChunkBlocks(Job& job, ivec3 localPos, vec biomeMap, Ch : depth <= 2 ? biome.topBlock : depth <= 4 ? biome.soilBlock : biome.rockBlock; - + if (chunk.d == nullptr) std::cout << "THE DATA ISNT LOADED." << std::endl; chunk.d->blocks[i] = blockId; - + } - + chunk.countRenderableBlocks(); } void MapGen::generateChunkDecorAndLight(Job& job, ivec3 localPos, vec biomeMap, ChunkData& depthMap) { - + vec3 posFloat = job.pos + localPos; std::default_random_engine generator(posFloat.x + posFloat.y * M_PI + posFloat.z * (M_PI * 2)); std::uniform_real_distribution distribution(0, 1); - + auto& chunk = job.chunks->at(job.pos + localPos); - + ivec3 abovePos = job.pos + localPos + ivec3 { 0, 1, 0 }; Chunk* above = (localPos.y != job.size - 1) ? job.chunks->count(abovePos) ? job.chunks->at(abovePos) : nullptr : nullptr; - + for (u16 i = 0; i < 256; i++) { ivec3 indPos = { i / 16, 15, i % 16 }; - + u16 biomeID = biomeMap[(localPos.x * 16 + indPos.x) * (job.size * 16 + 1) + (localPos.z * 16 + indPos.z)]; auto& biome = game.getBiomes().biomeFromId(biomeID); - - u16 schemID = -1; + + i16 schemID = -1; for (u16 j = 0; j < biome.schematics.size(); j++) { if (distribution(generator) > 1 - biome.schematics[j]->probability) { schemID = j; break; } } - + i8 light = -1; - - for (; indPos.y >= 0 && (light || schemID > -1); indPos.y--) { + + for (; indPos.y >= 0 && (light > -1 || schemID > -1); indPos.y--) { u16 ind = Space::Block::index(indPos); - - if (schemID > UINT16_MAX && depthMap[ind] > 1 && depthMap[ind] <= 2) { + + if (schemID > -1 && depthMap[ind] > 1 && depthMap[ind] <= 2) { ivec3 pos = (job.pos + localPos) * 16 + indPos; pos.y++; // Compensate for the fact that we're finding solid positions. auto& schematic = biome.schematics[schemID]; @@ -246,28 +252,28 @@ void MapGen::generateChunkDecorAndLight(Job& job, ivec3 localPos, vec biome } break; } - - if (light == -1) light = above ? above->getLight(Space::Block::index(indPos), 3) : - game.getDefs().blockFromId(chunk->getBlock(indPos)).lightPropagates ? 15 : 0; - - if (!light) continue; - - auto& blockDef = game.getDefs().blockFromId(chunk->getBlock(indPos)); - if (!blockDef.lightPropagates) light = 0; - else { - chunk->setLight(ind, 3, light); - job.sunlightQueue.emplace(ind, chunk); - } + + // if (light == -1) light = above ? above->getLight(Space::Block::index(indPos), 3) : + // game.getDefs().blockFromId(chunk->getBlock(indPos)).lightPropagates ? 15 : 0; + + // if (!light) continue; + + // auto& blockDef = game.getDefs().blockFromId(chunk->getBlock(indPos)); + // if (!blockDef.lightPropagates) light = 0; + // else { + // chunk->setLight(ind, 3, light); + // job.sunlightQueue.emplace(ind, chunk); + // } } } - + chunk->generationState = Chunk::GenerationState::GENERATED; } void MapGen::setBlock(MapGen::Job& job, ivec3 worldPos, u16 block, Chunk* hint) { if (block == DefinitionAtlas::INVALID) return; u16 ind = Space::Block::index(worldPos); - + if (hint && Space::Chunk::world::fromBlock(worldPos) == hint->getPos()) { if (hint->getBlock(ind) <= DefinitionAtlas::AIR) hint->setBlock(ind, block); } diff --git a/src/world/gen/MapGen.h b/src/world/gen/MapGen.h index 2581d586..54a6fc3a 100644 --- a/src/world/gen/MapGen.h +++ b/src/world/gen/MapGen.h @@ -27,56 +27,56 @@ class MapGen { public: /** The precision of the Biome map, as a divisor of the chunk size. */ constexpr static u8 BIOP = 4; - + /** The precision of the Terrain maps, as a divisor of the chunk size. */ constexpr static u8 TERP = 4; - + /** A type alias for the type the map of Chunks stored in the Job. */ typedef std::unordered_map ChunkMap; - + /** * A struct representing a single position in a chunk at which sunlight should be updated at. */ - + struct SunlightNode { SunlightNode(u16 index, Chunk* chunk) : index(index), chunk(chunk) {}; - + u16 index; Chunk* chunk; }; - + /** * A struct containing all the information for a generation job. * Contains a list of chunks, Noise samples, and the world position of the the job's root. */ - + struct Job { - + /** * Creates a new job with the root position and size specified, and initializes the NoiseSample params. * @param pos - The root position of the job. * @param size - The size in chunks of the job. */ - + Job(ivec3 pos, u16 size) : pos(pos), size(size), volume {{ size * TERP, (size + 1) * TERP }, { 1, 1.25 }}, heightmap {{ size * TERP, 0 }}, temperature {{ size * BIOP, 0 }}, roughness {{ size * BIOP, 0 }}, humidity {{ size * BIOP, 0 }} {} - + ivec3 pos {}; u16 size {}; - + uptr chunks = make_unique(); std::queue sunlightQueue {}; - + NoiseSample volume, heightmap; NoiseSample temperature, humidity, roughness; }; - + typedef array ChunkData; - + MapGen(const MapGen& o) = delete; - + /** * Create a MapGen object with the seed and biomes provided. * @@ -85,9 +85,9 @@ public: * @param seed - A seed to base the generation off of. * @param biomes - A list of biome identifiers or tags to include in generation. */ - + MapGen(Subgame& game, World& world, u32 seed, std::unordered_set biomes); - + /** * Generate a single chunk at the dimension and position provided. * As with all generate* functions, this may result in extraneous chunk partials being created. @@ -97,9 +97,9 @@ public: * @param pos - The position in the dimension to generate the chunk. * @returns a set of positions that were generated by this function call. */ - + [[maybe_unused]] uptr generateChunk(u16 dim, ivec3 pos); - + /** * Generate a mapblock at the dimension and position provided. * As with all generate* functions, this may result in extraneous chunk partials being created. @@ -109,9 +109,9 @@ public: * @param pos - The position in the dimension to generate the chunk. * @returns a set of positions that were generated by this function call. */ - + uptr generateMapBlock(u16 dim, ivec3 pos); - + /** * The underlying generate function called by both generateMapBlock and generateChunk. * Can also be called on it's own to generate an arbitrary region of chunks. @@ -121,11 +121,11 @@ public: * @param pos - The position in the dimension to generate the chunk. * @return - A set of positions that were generated by this function call. */ - + uptr generateArea(u16 dim, ivec3 origin, u16 size = 1); private: - + /** * Get the closest biome to the provided environmental values from the Vonoroi map. * Returns the index of the matched biome. @@ -135,18 +135,18 @@ private: * @param roughness - The roughness value of the position to check. * @returns the biome index of the environmentally closest biome. */ - + u16 getBiomeAt(f32 temperature, f32 humidity, f32 roughness); - + /** * Generate the Vonoroi biome map, using the biomes listed, * according to their definition parameters. * * @param biomes - The biomes to add to the map. */ - + void generateVoronoi(const std::unordered_set& biomes); - + /** * Create a density array for a chunk using a generation Job and an offset within it. * Returns a flattened array of block densities for every point in the chunk. @@ -155,9 +155,9 @@ private: * @param localPos - The offset of the chunk's data within the job. * @returns a ChunkData array containing the chunk's density. */ - + static uptr populateChunkDensity(Job& job, ivec3 localPos); - + /** * Create a depth array for a chunk using a generation Job and the chunk densities around it. * Returns a flattened array of block depths for every point in the chunk. @@ -166,9 +166,9 @@ private: * @param chunkDensityAbove - A density array of the chunk above it. * @returns a ChunkData array containing the chunk's depth. */ - + static uptr populateChunkDepth(uptr& chunkDensity, uptr chunkDensityAbove); - + /** * Generates a chunk's blocks from a generation Job and an offset within it, and inserts it into the Job. * Combines with any partials that have been previously created at that position within job. @@ -181,9 +181,9 @@ private: * @param biomeMap - The two-dimensional biome array of the entire generation area. * @param depthMap - The depth map of the chunk being generated. */ - + void generateChunkBlocks(Job& job, ivec3 localPos, vec biomeMap, ChunkData& depthMap); - + /** * Generates structures for a Chunk based on data within the generation job and an offset within it. * Also generates initial light cascade, which will later be refined by propogateSunlightNodes. @@ -194,9 +194,9 @@ private: * @param biomeMap - The two-dimensional biome array of the entire generation area. * @param depthMap - The depth map of the chunk being generated. */ - + void generateChunkDecorAndLight(Job& job, ivec3 localPos, vec biomeMap, ChunkData& depthMap); - + /** * Sets a block at the position specified into the Job, if the block at said position is not filled by * a material greater than air. If a chunk does not exist at the specified position, a partial is generated @@ -207,23 +207,23 @@ private: * @param block - The block to set. * @param hint - An optional parameter that may speed up the function if set to the chunk to set to. */ - + static void setBlock(Job& job, ivec3 worldPos, u16 block, Chunk* hint); - + /** * Calculates and smooths sunlight for an entire Job's chunks. * * @param job - The job to act upon. */ - + void propogateSunlightNodes(Job& job); - + u32 seed = 0; MapGenProps props; - + constexpr const static u16 voronoiSize = 64; Voronoi3D voronoi { voronoiSize }; - + Subgame& game; World& world; -}; \ No newline at end of file +}; diff --git a/src/world/player/Player.h b/src/world/player/Player.h index 51209eef..dadcf041 100644 --- a/src/world/player/Player.h +++ b/src/world/player/Player.h @@ -70,7 +70,7 @@ protected: vec3 lookOffset{}; - bool flying = false; + bool flying = true; string handList = ""; string wieldList = ""; diff --git a/src/world/player/ServerPlayer.h b/src/world/player/ServerPlayer.h index 98487951..126081e1 100644 --- a/src/world/player/ServerPlayer.h +++ b/src/world/player/ServerPlayer.h @@ -35,7 +35,7 @@ public: ENetPeer* getPeer(); - bool changedMapBlocks = false; + bool changedMapBlocks = true; vec3 lastPos = vec3(INFINITY); private: diff --git a/subgames/minimal/mods/zeus_inventory/script/menu.lua b/subgames/minimal/mods/zeus_inventory/script/menu.lua index 5233167f..ba6e38a9 100644 --- a/subgames/minimal/mods/zeus_inventory/script/menu.lua +++ b/subgames/minimal/mods/zeus_inventory/script/menu.lua @@ -121,6 +121,6 @@ end zepha.register_keybind("zeus:inventory:open_inventory", { description = "Open Inventory", - default = zepha.keys.e, + default = zepha.keys['.'], on_press = inventory.open_inventory }) \ No newline at end of file diff --git a/subgames/minimal/mods/zeus_world/script/biomes/forest.lua b/subgames/minimal/mods/zeus_world/script/biomes/forest.lua index 77a6e3a7..1433501f 100644 --- a/subgames/minimal/mods/zeus_world/script/biomes/forest.lua +++ b/subgames/minimal/mods/zeus_world/script/biomes/forest.lua @@ -81,7 +81,7 @@ local leaf_layer_3 = { local tree = zepha.create_structure({ origin = V(2, 2, 2), - schematic = { + layout = { trunk_layer_0, trunk_layer_0, trunk_layer_0, diff --git a/subgames/minimal/mods/zeus_world/script/biomes/highlands.lua b/subgames/minimal/mods/zeus_world/script/biomes/highlands.lua index c044f014..062df8ef 100644 --- a/subgames/minimal/mods/zeus_world/script/biomes/highlands.lua +++ b/subgames/minimal/mods/zeus_world/script/biomes/highlands.lua @@ -2,7 +2,7 @@ local identifier = "zeus:world:highlands" local grass = zepha.create_structure({ origin = V{1, 2, 3}, - schematic = { + layout = { {{"zeus:default:tall_grass_4"}} } }) diff --git a/subgames/minimal/mods/zeus_world/script/biomes/plains.lua b/subgames/minimal/mods/zeus_world/script/biomes/plains.lua index b95f176b..08f9d5e9 100644 --- a/subgames/minimal/mods/zeus_world/script/biomes/plains.lua +++ b/subgames/minimal/mods/zeus_world/script/biomes/plains.lua @@ -23,9 +23,9 @@ local shrub_layer_2 = { } local shrub = zepha.create_structure({ - origin = V{1, 1, 1}, + origin = V {1, 1, 1}, probability = 0.01, - schematic = { + layout = { shrub_layer_0, shrub_layer_1, shrub_layer_2, @@ -38,7 +38,7 @@ for i = 1, 5 do table.insert(structures, zepha.create_structure({ origin = V {1, 1, 1}, probability = 0.04, - schematic = {{{ "zeus:default:tall_grass_" .. tostring(i) }}} + layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}} })) end diff --git a/subgames/parentheses/menu/script/faafafafa.lua b/subgames/parentheses/menu/script/init.lua similarity index 100% rename from subgames/parentheses/menu/script/faafafafa.lua rename to subgames/parentheses/menu/script/init.lua diff --git a/subgames/zeus/mods/zeus_default/models/wip/workbench.blend b/subgames/zeus/mods/zeus_default/models/wip/workbench.blend new file mode 100644 index 0000000000000000000000000000000000000000..6144b58114f48579bf6044c491f3a485c2c4238f GIT binary patch literal 668460 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�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

>g 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 literal 0 HcmV?d00001 diff --git a/subgames/zeus/mods/zeus_default/textures/podzol.png b/subgames/zeus/mods/zeus_default/textures/podzol.png new file mode 100755 index 0000000000000000000000000000000000000000..fd62d0ba65b5dcbd60bdeb131d5fef2e71168267 GIT binary patch literal 2796 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 btor{yHc||NSt(Eg00000NkvXXu0mjfv!n(^ literal 0 HcmV?d00001 diff --git a/subgames/zeus/mods/zeus_default/textures/workbench.xcf b/subgames/zeus/mods/zeus_default/textures/workbench.xcf new file mode 100644 index 0000000000000000000000000000000000000000..8e5cb463a44b316e883298cca23fd4f4ceefd28f GIT binary patch literal 22938 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 A{Qv*} literal 0 HcmV?d00001 diff --git a/subgames/zeus/mods/zeus_inventory/script/menu.lua b/subgames/zeus/mods/zeus_inventory/script/menu.lua index a5918703..84b3d64d 100644 --- a/subgames/zeus/mods/zeus_inventory/script/menu.lua +++ b/subgames/zeus/mods/zeus_inventory/script/menu.lua @@ -131,6 +131,6 @@ end zepha.register_keybind("zeus:inventory:open_inventory", { description = "Open Inventory", - default = zepha.keys.p, + default = zepha.keys['.'], on_press = inventory.open_inventory }) \ No newline at end of file diff --git a/subgames/zeus/mods/zeus_world/script/biomes/desert.lua b/subgames/zeus/mods/zeus_world/script/biomes/desert.lua index 00157c79..77e86540 100644 --- a/subgames/zeus/mods/zeus_world/script/biomes/desert.lua +++ b/subgames/zeus/mods/zeus_world/script/biomes/desert.lua @@ -1,49 +1,49 @@ -local identifier = "zeus:world:desert" - -local noise = { - heightmap = { - module = "add", - sources = {{ - module = "const", - value = -80 - }, { - -- Elevation - module = "scale_bias", - source = { - module = "perlin", - frequency = 0.02, - octaves = 8 - }, - scale = 20, - bias = 32 - }, { - -- Features - module = "scale_bias", - source = { - module = "perlin", - frequency = 0.8, - octaves = 3, - }, - scale = 5, - bias = 6 - }} - } -} - -zepha.register_biome(identifier, { - environment = { - temperature = 40/100, - humidity = 20/100, - roughness = 10/100 - }, - blocks = { - top = "zeus:default:sand", - soil = "zeus:default:sand", - rock = "zeus:default:sandstone" - }, - tags = { natural = 1, default = 1 }, - biome_tint = "#e6fa61", - noise = noise -}) - -return identifier; \ No newline at end of file +-- local identifier = "zeus:world:desert" +-- +-- local noise = { +-- heightmap = { +-- module = "add", +-- sources = {{ +-- module = "const", +-- value = -80 +-- }, { +-- -- Elevation +-- module = "scale_bias", +-- source = { +-- module = "perlin", +-- frequency = 0.02, +-- octaves = 8 +-- }, +-- scale = 20, +-- bias = 32 +-- }, { +-- -- Features +-- module = "scale_bias", +-- source = { +-- module = "perlin", +-- frequency = 0.8, +-- octaves = 3, +-- }, +-- scale = 5, +-- bias = 6 +-- }} +-- } +-- } +-- +-- zepha.register_biome(identifier, { +-- environment = { +-- temperature = 40/100, +-- humidity = 20/100, +-- roughness = 10/100 +-- }, +-- blocks = { +-- top = "zeus:default:sand", +-- soil = "zeus:default:sand", +-- rock = "zeus:default:sandstone" +-- }, +-- tags = { natural = 1, default = 1 }, +-- biome_tint = "#e6fa61", +-- noise = noise +-- }) +-- +-- return identifier; \ No newline at end of file diff --git a/subgames/zeus/mods/zeus_world/script/biomes/forest.lua b/subgames/zeus/mods/zeus_world/script/biomes/forest.lua index 0050f944..c8df11bf 100644 --- a/subgames/zeus/mods/zeus_world/script/biomes/forest.lua +++ b/subgames/zeus/mods/zeus_world/script/biomes/forest.lua @@ -1,178 +1,178 @@ -local identifier = "zeus:world:forest" - -local noise = { - heightmap = { - module = "add", - sources = { - runfile(_PATH .. 'world_noise'), { - -- Features - module = "scale_bias", - source = { - module = "perlin", - frequency = .5, - octaves = 3, - }, - scale = 3, - bias = 0 - } } - } -} - ---local woo = "zeus:default:wood" ---local lea = "zeus:default:leaves" ---local inv = "invalid" +-- local identifier = "zeus:world:forest" -- ---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 noise = { +-- heightmap = { +-- module = "add", +-- sources = { +-- runfile(_PATH .. 'world_noise'), { +-- -- Features +-- module = "scale_bias", +-- source = { +-- module = "perlin", +-- frequency = .5, +-- octaves = 3, +-- }, +-- scale = 3, +-- bias = 0 +-- } } -- } ---}) +-- } -- ---local structures = { tree, shrub } +-- --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" }}} +-- --})) -- ---for i = 1, 5 do --- table.insert(structures, zepha.create_structure({ --- origin = V(), --- probability = 0.01, --- schematic = {{{ "zeus:default:tall_grass_" .. tostring(i) }}} --- })) ---end +-- local structures = {} -- ---table.insert(structures, zepha.create_structure({ --- origin = V(), --- probability = 0.0025, --- schematic = {{{ "zeus:flowers:flower_red_mushroom" }}} ---})) +-- zepha.register_biome(identifier, { +-- environment = { +-- temperature = 15/100, +-- humidity = 80/100, +-- roughness = 20/100, +-- }, +-- blocks = { +-- top = "zeus:default:podzol", +-- soil = "zeus:default:dirt", +-- rock = "zeus:default:stone" +-- }, +-- tags = { natural = 1, default = 1 }, +-- structures = structures, +-- biome_tint = "#7beb26", +-- noise = noise +-- }) -- ---table.insert(structures, zepha.create_structure({ --- origin = V(), --- probability = 0.0025, --- schematic = {{{ "zeus:flowers:flower_brown_mushroom" }}} ---})) - -local structures = {} - -zepha.register_biome(identifier, { - environment = { - temperature = 15/100, - humidity = 80/100, - roughness = 20/100, - }, - blocks = { - top = "zeus:default:podzol", - soil = "zeus:default:dirt", - rock = "zeus:default:stone" - }, - tags = { natural = 1, default = 1 }, - structures = structures, - biome_tint = "#7beb26", - noise = noise -}) - -return identifier \ No newline at end of file +-- return identifier \ No newline at end of file diff --git a/subgames/zeus/mods/zeus_world/script/biomes/highlands.lua b/subgames/zeus/mods/zeus_world/script/biomes/highlands.lua index 3d78569d..53a56456 100644 --- a/subgames/zeus/mods/zeus_world/script/biomes/highlands.lua +++ b/subgames/zeus/mods/zeus_world/script/biomes/highlands.lua @@ -1,57 +1,57 @@ -local identifier = "zeus:world:highlands" - -local noise = { - volume = { - module = "add", - sources = {{ - module = "add", - sources = {{ - -- Voronoi - module = "scale_bias", - source = { - module = "voronoi", - frequency = 0.2, - displacement = 5 - }, - scale = 8, - bias = -32 - }, { - -- Features - module = "scale_bias", - source = { - module = "perlin", - frequency = 0.6, - octaves = 3, - }, - scale = 3 - }} - }, { - -- Variation - module = "scale_bias", - source = { - module = "perlin", - frequency = 0.05, - octaves = 6 - }, - scale = 15 - }} - } -} - -zepha.register_biome(identifier, { - environment = { - temperature = 0/100, - humidity = 0/100, - roughness = 50/100 - }, - blocks = { - top = "zeus:default:grass", - soil = "zeus:default:dirt", - rock = "zeus:default:stone" - }, - tags = { natural = 1, default = 1 }, - biome_tint = "#c2fa61", - noise = noise -}) - -return identifier \ No newline at end of file +-- local identifier = "zeus:world:highlands" +-- +-- local noise = { +-- volume = { +-- module = "add", +-- sources = {{ +-- module = "add", +-- sources = {{ +-- -- Voronoi +-- module = "scale_bias", +-- source = { +-- module = "voronoi", +-- frequency = 0.2, +-- displacement = 5 +-- }, +-- scale = 8, +-- bias = -32 +-- }, { +-- -- Features +-- module = "scale_bias", +-- source = { +-- module = "perlin", +-- frequency = 0.6, +-- octaves = 3, +-- }, +-- scale = 3 +-- }} +-- }, { +-- -- Variation +-- module = "scale_bias", +-- source = { +-- module = "perlin", +-- frequency = 0.05, +-- octaves = 6 +-- }, +-- scale = 15 +-- }} +-- } +-- } +-- +-- zepha.register_biome(identifier, { +-- environment = { +-- temperature = 0/100, +-- humidity = 0/100, +-- roughness = 50/100 +-- }, +-- blocks = { +-- top = "zeus:default:grass", +-- soil = "zeus:default:dirt", +-- rock = "zeus:default:stone" +-- }, +-- tags = { natural = 1, default = 1 }, +-- biome_tint = "#c2fa61", +-- noise = noise +-- }) +-- +-- return identifier \ No newline at end of file diff --git a/subgames/zeus/mods/zeus_world/script/biomes/plains.lua b/subgames/zeus/mods/zeus_world/script/biomes/plains.lua index adcd383b..9408f696 100644 --- a/subgames/zeus/mods/zeus_world/script/biomes/plains.lua +++ b/subgames/zeus/mods/zeus_world/script/biomes/plains.lua @@ -5,15 +5,43 @@ local leaf = "zeus:default:leaves" local none = "invalid" local structures = {} +-- +-- table.insert(structures, zepha.create_structure({ +-- -- noise = { +-- -- module = "perlin", +-- -- frequency = 0.002, +-- -- octaves = 8 +-- -- }, +-- -- region_size = 4, +-- probability = 0.1, +-- -- origin = V{1, 1, 1}, +-- origin = V(), +-- layout = {{{ "zeus:flowers:flower_geranium" }}} +-- })) + +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({ - noise = { - module = "perlin", - frequency = 0.002, - octaves = 8 - }, - region_size = 4, - origin = V{1, 1, 1}, + origin = V(), + probability = 0.025, + layout = {{{ "zeus:flowers:flower_white_dandelion" }}} +})) + +table.insert(structures, zepha.create_structure({ + origin = V(), + probability = 0.025, layout = {{ { none, none, none }, { none, wood, none }, @@ -29,28 +57,105 @@ table.insert(structures, zepha.create_structure({ }} })) ---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 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 } +} + +table.insert(structures, zepha.create_structure({ + origin = V(2, 2, 2), + probability = 0.0005, + layout = { + 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 noise = { - heightmap = runfile(_PATH .. 'world_noise') +-- heightmap = runfile(_PATH .. 'world_noise'), + volume = { + module = "scale_bias", + scale = 3000, + bias = -3500, + source = { + module = "scale_point", + y_scale = 2, + source = { + module = "perlin", + frequency = 0.1 + } + } + } } zepha.register_biome(identifier, { diff --git a/subgames/zeus/mods/zeus_world/script/init.lua b/subgames/zeus/mods/zeus_world/script/init.lua index 831756cd..49e73745 100644 --- a/subgames/zeus/mods/zeus_world/script/init.lua +++ b/subgames/zeus/mods/zeus_world/script/init.lua @@ -5,8 +5,8 @@ zepha.create_dimension('zeus:world:default', { biomes = { '#natural', '#default' } }) -zepha.create_dimension('zeus:world:endless_desert', { - biomes = { 'zeus:world:desert' } -}) +-- zepha.create_dimension('zeus:world:endless_desert', { +-- biomes = { 'zeus:world:desert' } +-- }) zepha.set_default_dimension('zeus:world:default') \ No newline at end of file