diff --git a/.gitignore b/.gitignore index 21aecb4b0..60c377b0a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ doc/html doc/latex # Binaries +build openminer openminer_server *.exe @@ -63,5 +64,6 @@ openminer_server *.zip test_atlas.png config.lua +server_config.lua TODO diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..faae46e10 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,11 @@ +[submodule "external/entt"] + path = external/entt + url = git://github.com/skypjack/entt.git + ignore = dirty +[submodule "external/SFML"] + path = external/SFML + url = git://github.com/SFML/SFML.git + ignore = dirty +[submodule "external/gamekit"] + path = external/gamekit + url = git://github.com/Unarelith/GameKit.git diff --git a/.travis.yml b/.travis.yml index c82373c18..396088dde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ os: linux -dist: trusty +dist: xenial language: cpp compiler: @@ -32,21 +32,11 @@ addons: before_install: - sudo cp /usr/include/lua5.2/* /usr/include/ - - git clone git://github.com/SFML/SFML.git - - cd SFML - - cmake . - - make -j8 - - sudo make install - - cd .. - - git clone git://github.com/Quent42340/GameKit.git - - cd GameKit - - cmake . - - make -j8 - - sudo make install - - cd .. script: - - cmake . + - mkdir build + - cd build + - cmake .. - make -j8 notifications: diff --git a/CMakeLists.txt b/CMakeLists.txt index 25af3a8be..718983b06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ endif () set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +include_directories(external) + #------------------------------------------------------------------------------ # Compiler flags #------------------------------------------------------------------------------ @@ -22,7 +24,7 @@ set(DEBUG_GCC_FLAGS -g -Og -Wall -Wextra -Wfatal-errors -Wno-variadic-macros) set(RELEASE_GCC_FLAGS -O3) set(RELWITHDEB_GCC_FLAGS -g -O3 -Wall -Wextra -Wfatal-errors -Wno-variadic-macros) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) #------------------------------------------------------------------------------ # Setting default build type @@ -55,21 +57,6 @@ endif() include_directories(${LUA_INCLUDE_DIR}) link_directories(${LUA_LIBRARY_DIRS}) -#------------------------------------------------------------------------------ -# - gamekit -#------------------------------------------------------------------------------ -if (WIN32) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WIN_LIBRARIES_PATH}/gamekit) -endif () - -find_package(GameKit REQUIRED) - -if(NOT GAMEKIT_FOUND) - message(FATAL_ERROR "gamekit is needed to build the project. Please install it correctly.") -endif() - -include_directories(${GAMEKIT_INCLUDE_DIR}) - #------------------------------------------------------------------------------ # - tinyxml2 #------------------------------------------------------------------------------ @@ -149,38 +136,66 @@ include_directories(${SDL2_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIRS}) #------------------------------------------------------------------------------ -# - SFML network +# - GLEW #------------------------------------------------------------------------------ if (WIN32) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WIN_LIBRARIES_PATH}/SFML) -endif () + set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WIN_LIBRARIES_PATH}/glew) + + find_package(GLEW REQUIRED) + + if(NOT GLEW_FOUND) + message(FATAL_ERROR "GLEW not found!") + endif(NOT GLEW_FOUND) -if (MINGW) - set(SFML_STATIC_LIBRARIES TRUE) + include_directories(${GLEW_INCLUDE_DIRS}) endif () -find_package(SFML COMPONENTS system network) +#------------------------------------------------------------------------------ +# Submodules +# from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html +#------------------------------------------------------------------------------ +find_package(Git QUIET) -if(NOT SFML_FOUND) - message(FATAL_ERROR "SFML is needed to build the project. Please install it correctly.") +if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") + option(GIT_SUBMODULE "Check submodules during build" ON) + if(GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() endif() -include_directories(${SFML_INCLUDE_DIRS}) +if(NOT EXISTS "${PROJECT_SOURCE_DIR}/external/gamekit/CMakeLists.txt") + message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") +endif() #------------------------------------------------------------------------------ -# - GLEW +# - EnTT #------------------------------------------------------------------------------ -if (WIN32) - set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WIN_LIBRARIES_PATH}/glew) +add_subdirectory(external/entt) +include_directories(external/entt/single_include) - find_package(GLEW REQUIRED) +#------------------------------------------------------------------------------ +# - gamekit +#------------------------------------------------------------------------------ +add_subdirectory(external/gamekit) +include_directories(external/gamekit/include) - if(NOT GLEW_FOUND) - message(FATAL_ERROR "GLEW not found!") - endif(NOT GLEW_FOUND) +#------------------------------------------------------------------------------ +# - SFML network +#------------------------------------------------------------------------------ +set(SFML_BUILD_AUDIO FALSE) +set(SFML_BUILD_GRAPHICS FALSE) +set(SFML_BUILD_WINDOW FALSE) +set(SFML_STATIC TRUE) - include_directories(${GLEW_INCLUDE_DIRS}) -endif () +set(BUILD_SHARED_LIBS OFF) + +add_subdirectory(external/SFML) #------------------------------------------------------------------------------ # Subdirectories diff --git a/README.md b/README.md index d58858787..9433341d0 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ - [Wiki](#wiki) - [Keys](#keys) - [How to compile](#how-to-compile) + - [Using Linux](#using-linux) + - [Using Windows](#using-windows) + - [Using OSX](#using-osx) - [Discussion](#discussion) - [Project status](#project-status) - [Screenshots](#screenshots) @@ -63,22 +66,25 @@ The long-term goal of this project is to provide a viable alternative to Minecra ### Using Linux - Dependencies: - - A compiler with C++14 support (GCC >= 5.0 or clang >= 3.4) - - [CMake](http://www.cmake.org/download/) - - [GameKit](http://github.com/Unarelith/GameKit) (requires `SDL2` + `tinyxml2`, will switch to `SFML` starting from 2.6) - - [SFML](https://www.sfml-dev.org/) (only used for network) + - A compiler with C++17 support (GCC >= 7.0 or clang >= 5.0) + - [CMake](http://www.cmake.org/download/) (>= 3.12.4) - [Lua](http://www.lua.org) + - [GameKit dependencies](https://github.com/Unarelith/GameKit#how-to-compile) - _Linux users: Check your distribution repositories for packages._ -- Run `cmake .` -- Run `make -j8` -- Run the client with `./openminer` -- If you want a multiplayer game, run the server with `./openminer_server` +- Run `cmake -B build . && cmake --build build -j8` +- Or `mkdir build && cd build && cmake .. && make -j8 && cmake ..` (for old CMake versions) +- Run the client with `./build/openminer` +- If you want a multiplayer game, run the server with `./build/openminer_server` ### Using Windows - [Wiki: Compiling on Windows using CMake and MinGW-w64](https://github.com/Unarelith/OpenMiner/wiki/Compiling-on-Windows-with-MinGW-w64) - [Wiki: Compiling on Windows using CMake and Visual Studio 2017](https://github.com/Unarelith/OpenMiner/wiki/Compiling-on-Windows-with-Visual-Studio-2017) +### Using OSX + +I don't have an OSX system at the moment. But you can help me create a guide for this OS if you have one! + ## Discussion - Discord: [join server](https://discord.gg/eN8k8wt) @@ -104,16 +110,16 @@ This list is non exhaustive. - Player model display (currently without rotation nor animation) - Dimensions (like the Nether or the Ender in Minecraft) ([#80](https://github.com/Unarelith/OpenMiner/pull/80)) - World loading/saving (using `/save ` and `/load ` commands, see [#26](https://github.com/Unarelith/OpenMiner/issues/26)) +- Texture pack system (partially implemented, see [#34](https://github.com/Unarelith/OpenMiner/issues/34)) +- Entities (block drops, mobs, etc...) ([#90](https://github.com/Unarelith/OpenMiner/pull/90)) ### Missing features -- Texture pack system ([#34](https://github.com/Unarelith/OpenMiner/issues/34)) - Fluid propagation ([#62](https://github.com/Unarelith/OpenMiner/issues/62)) - Day/night cycle with sun/moon display ([#73](https://github.com/Unarelith/OpenMiner/issues/73)) - Real worldgen (seed-based, cave tunnels) ([#79](https://github.com/Unarelith/OpenMiner/issues/79)) - Clouds ([#52](https://github.com/Unarelith/OpenMiner/pull/52)) - Particle system -- Entities (block drops, mobs, etc...) ## Screenshots diff --git a/cmake/FindGameKit.cmake b/cmake/FindGameKit.cmake deleted file mode 100644 index d0eff9317..000000000 --- a/cmake/FindGameKit.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2019 Quentin Bazin -# This file is licensed under LGPL 2.1 -# See LICENSE file - -# GAMEKIT_FOUND -# GAMEKIT_INCLUDE_DIR -# GAMEKIT_LIBARIES - -find_path(GAMEKIT_INCLUDE_DIR NAMES gk) -find_library(GAMEKIT_LIBRARIES NAMES gamekit) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(gamekit DEFAULT_MSG GAMEKIT_LIBRARIES GAMEKIT_INCLUDE_DIR) - -mark_as_advanced(GAMEKIT_INCLUDE_DIR GAMEKIT_LIBRARIES) - diff --git a/cmake/FindTinyXml2.cmake b/cmake/FindTinyXml2.cmake index 3f8850f38..e51aefef9 100644 --- a/cmake/FindTinyXml2.cmake +++ b/cmake/FindTinyXml2.cmake @@ -10,7 +10,7 @@ find_path(TINYXML2_INCLUDE_DIRS NAMES tinyxml2.h) find_library(TINYXML2_LIBRARIES NAMES tinyxml2) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(tinyxml2 DEFAULT_MSG TINYXML2_LIBRARIES TINYXML2_INCLUDE_DIRS) +find_package_handle_standard_args(TinyXml2 DEFAULT_MSG TINYXML2_LIBRARIES TINYXML2_INCLUDE_DIRS) mark_as_advanced(TINYXML2_INCLUDE_DIRS TINYXML2_LIBRARIES) diff --git a/cmake/sdl2/FindSDL2.cmake b/cmake/sdl2/FindSDL2.cmake index 8c73f58e7..f44dc406e 100644 --- a/cmake/sdl2/FindSDL2.cmake +++ b/cmake/sdl2/FindSDL2.cmake @@ -315,9 +315,11 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 VERSION_VAR SDL2_VERSION_STRING) if(SDL2MAIN_LIBRARY) + SET(FPHSA_NAME_MISMATCHED ON) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR VERSION_VAR SDL2_VERSION_STRING) + SET(FPHSA_NAME_MISMATCHED OFF) endif() diff --git a/config.example.lua b/config.example.lua index 1f2754455..e93461ee0 100644 --- a/config.example.lua +++ b/config.example.lua @@ -1,6 +1,6 @@ -- You can copy this file as 'config.lua' to load automatically those settings at client startup -- Here the default values are set, you can remove options or change them accordingly --- See client/source/Config.cpp for more details +-- See source/client/core/Config.cpp for more details -- Gameplay isFlyModeEnabled = false diff --git a/external/SFML b/external/SFML new file mode 160000 index 000000000..50e173e40 --- /dev/null +++ b/external/SFML @@ -0,0 +1 @@ +Subproject commit 50e173e403ef8912e3d8ac3c7ab3e27e32243339 diff --git a/external/entt b/external/entt new file mode 160000 index 000000000..89dc7f817 --- /dev/null +++ b/external/entt @@ -0,0 +1 @@ +Subproject commit 89dc7f817f224f368bb6e4fbfb3c5cdeb1daf433 diff --git a/external/gamekit b/external/gamekit new file mode 160000 index 000000000..d39510fa3 --- /dev/null +++ b/external/gamekit @@ -0,0 +1 @@ +Subproject commit d39510fa3920197ddff588aacf5a15175e67887a diff --git a/resources/shaders/color.f.glsl b/resources/shaders/color.f.glsl deleted file mode 100644 index 348318a72..000000000 --- a/resources/shaders/color.f.glsl +++ /dev/null @@ -1,14 +0,0 @@ -#version 120 - -varying vec4 v_color; -varying vec2 v_texCoord; - -uniform sampler2D u_tex; - -vec4 getColor() { - if (v_texCoord.x > -0.99 && v_texCoord.y > -0.99) { - return texture2D(u_tex, v_texCoord); - } - - return v_color; -} diff --git a/resources/shaders/game.f.glsl b/resources/shaders/game.f.glsl index 6a2c8a07c..18114e832 100644 --- a/resources/shaders/game.f.glsl +++ b/resources/shaders/game.f.glsl @@ -2,6 +2,7 @@ varying vec4 v_coord3d; varying vec4 v_color; +varying vec2 v_texCoord; varying vec2 v_lightValue; varying float v_ambientOcclusion; @@ -10,8 +11,7 @@ varying float v_dist; uniform int u_renderDistance; -// Get current pixel color -vec4 getColor(); +uniform sampler2D u_tex; // Get light color vec4 light(vec4 color, vec3 lightColor, vec4 lightPosition, float ambientIntensity, float diffuseIntensity); @@ -29,9 +29,12 @@ void main() { if(blockFace > -1. && v_dist > u_renderDistance) discard; // Get current pixel color and apply multiplier on grayscale textures - vec4 color = getColor(); - if (blockFace > -1 && color != v_color && color.r == color.g && color.g == color.b) { - color *= v_color; + vec4 color = v_color; + if (v_texCoord.x > -0.99 && v_texCoord.y > -0.99) { + color = texture2D(u_tex, v_texCoord); + if (blockFace > -1 && color.r == color.g && color.g == color.b) { + color *= v_color; + } } // Block breaking animation diff --git a/server_config.example.lua b/server_config.example.lua new file mode 100644 index 000000000..428e93514 --- /dev/null +++ b/server_config.example.lua @@ -0,0 +1,7 @@ +-- You can copy this file as 'server_config.lua' to load automatically those settings at server startup +-- Here the default values are set, you can remove options or change them accordingly +-- See source/server/core/ServerConfig.cpp for more details + +-- Gameplay +useItemDrops = false + diff --git a/source/client/CMakeLists.txt b/source/client/CMakeLists.txt index bc6d9889b..be66b4e7f 100644 --- a/source/client/CMakeLists.txt +++ b/source/client/CMakeLists.txt @@ -7,7 +7,7 @@ set(PROJECT_NAME ${CMAKE_PROJECT_NAME}) # Get source files #------------------------------------------------------------------------------ file(GLOB_RECURSE SOURCE_FILES *.cpp) -file(GLOB_RECURSE HEADER_FILES *.hpp ../server/*.hpp ../common/*.hpp ../../external/*.hpp) +file(GLOB_RECURSE HEADER_FILES *.hpp ../server/*.hpp ../common/*.hpp) foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) @@ -49,7 +49,8 @@ endif () target_link_libraries(${PROJECT_NAME} ${CMAKE_PROJECT_NAME}_server_lib - ${GAMEKIT_LIBRARIES} + ${CMAKE_PROJECT_NAME}_common + gamekit ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} @@ -60,6 +61,5 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network - ${UNIX_LIBS} - ${CMAKE_PROJECT_NAME}_common) + ${UNIX_LIBS}) diff --git a/source/client/core/ClientApplication.cpp b/source/client/core/ClientApplication.cpp index a2e421f16..01767d99c 100644 --- a/source/client/core/ClientApplication.cpp +++ b/source/client/core/ClientApplication.cpp @@ -58,8 +58,8 @@ void ClientApplication::init() { gk::CoreApplication::init(); m_window.addVertexAttribute(VertexAttribute::Coord3d, "coord3d", 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, coord3d))); - m_window.addVertexAttribute(VertexAttribute::Color, "color", 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color))); m_window.addVertexAttribute(VertexAttribute::TexCoord, "texCoord", 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, texCoord))); + m_window.addVertexAttribute(VertexAttribute::Color, "color", 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, color))); m_window.addVertexAttribute(VertexAttribute::Normal, "normal", 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, normal))); m_window.addVertexAttribute(VertexAttribute::LightValue, "lightValue", 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, lightValue))); m_window.addVertexAttribute(VertexAttribute::AmbientOcclusion, "ambientOcclusion", 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, ambientOcclusion))); diff --git a/source/client/core/Vertex.hpp b/source/client/core/Vertex.hpp index 59d430ebe..2dc52758e 100644 --- a/source/client/core/Vertex.hpp +++ b/source/client/core/Vertex.hpp @@ -48,7 +48,7 @@ namespace VertexAttribute { AmbientOcclusion = 256, Basic = Coord3d | TexCoord | Color, - All = 0xffff + All = Basic | Normal | LightValue | AmbientOcclusion }; } diff --git a/source/client/graphics/PlayerBox.cpp b/source/client/graphics/PlayerBox.cpp index d8dca989d..ec5dc5cbb 100644 --- a/source/client/graphics/PlayerBox.cpp +++ b/source/client/graphics/PlayerBox.cpp @@ -277,7 +277,7 @@ void PlayerBox::draw(gk::RenderTarget &target, gk::RenderStates states) const { states.transform *= getTransform(); states.texture = &m_texture; - states.vertexAttributes = VertexAttribute::Basic; + states.vertexAttributes = VertexAttribute::All; glCheck(glEnable(GL_CULL_FACE)); diff --git a/source/client/gui/InventoryCube.cpp b/source/client/gui/InventoryCube.cpp index 766b0b3db..ee0f88146 100644 --- a/source/client/gui/InventoryCube.cpp +++ b/source/client/gui/InventoryCube.cpp @@ -39,16 +39,20 @@ #include "TextureAtlas.hpp" #include "Vertex.hpp" -InventoryCube::InventoryCube(float size) : m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) { +InventoryCube::InventoryCube(float size, bool isEntity) + : m_textureAtlas(&gk::ResourceHandler::getInstance().get("atlas-blocks")) +{ m_size = size; - m_transform.setOrigin(size * 0.5, size * 0.5, size * 0.5); + if (!isEntity) { + m_transform.setOrigin(size * 0.5, size * 0.5, size * 0.5); - // NOTE: intrinsic rotations! The axis is the local axis of the object. - // Note also that we start looking at the bottom of the cube due to how - // glm::orto is used (see comment below). - m_transform.rotate(120.f, {1, 0, 0}); - m_transform.rotate(-45.f, {0, 0, 1}); + // NOTE: intrinsic rotations! The axis is the local axis of the object. + // Note also that we start looking at the bottom of the cube due to how + // glm::ortho is used (see comment below). + m_transform.rotate(120.f, {1, 0, 0}); + m_transform.rotate(-45.f, {0, 0, 1}); + } } using namespace BlockGeometry; @@ -72,7 +76,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) { const gk::FloatBox &boundingBox = block.boundingBox(); - constexpr s8f faceValue[nFaces]{2, 2, 4, 4, -1, 3}; + constexpr s8f faceValue[nFaces]{2, 2, 4, 4, 3, 3}; for (u8 f = 0; f < nFaces; ++f) { // Calculate UV's @@ -96,7 +100,7 @@ void InventoryCube::updateVertexBuffer(const Block &block) { V1 = (f <= 3) ? 1.f - boundingBox.z : (f == 4) ? boundingBox.y + boundingBox.sizeY : 1.f - boundingBox.y; } - const gk::FloatRect &blockTexCoords = m_textureAtlas.getTexCoords(block.tiles().getTextureForFace(f)); + const gk::FloatRect &blockTexCoords = m_textureAtlas->getTexCoords(block.tiles().getTextureForFace(f)); for (u8f v = 0; v < nVertsPerFace; ++v) { if (block.drawType() == BlockDrawType::Cactus) { @@ -144,8 +148,8 @@ void InventoryCube::draw(gk::RenderTarget &target, gk::RenderStates states) cons // at start. states.projectionMatrix = glm::ortho(0.0f, (float)Config::screenWidth, (float)Config::screenHeight, 0.0f, DIST_2D_FAR, DIST_2D_NEAR); - states.texture = &m_textureAtlas.texture(); - states.vertexAttributes = VertexAttribute::Basic; + states.texture = &m_textureAtlas->texture(); + states.vertexAttributes = VertexAttribute::All; glCheck(glEnable(GL_CULL_FACE)); glCheck(glEnable(GL_DEPTH_TEST)); diff --git a/source/client/gui/InventoryCube.hpp b/source/client/gui/InventoryCube.hpp index 1419bf2d1..c668279ee 100644 --- a/source/client/gui/InventoryCube.hpp +++ b/source/client/gui/InventoryCube.hpp @@ -36,16 +36,18 @@ class TextureAtlas; class InventoryCube : public gk::Drawable, public gk::Transformable { public: - InventoryCube(float size = 1.0f); + InventoryCube(float size = 1.0f, bool isEntity = false); void updateVertexBuffer(const Block &block); + float size() const { return m_size; } + private: void draw(gk::RenderTarget &target, gk::RenderStates states) const override; float m_size = 1.0f; - const TextureAtlas &m_textureAtlas; + const TextureAtlas *m_textureAtlas; gk::VertexBuffer m_vbo; bool m_isVboInitialized = false; diff --git a/source/client/hud/BlockCursor.cpp b/source/client/hud/BlockCursor.cpp index 3f74dd31a..d79552e8c 100644 --- a/source/client/hud/BlockCursor.cpp +++ b/source/client/hud/BlockCursor.cpp @@ -149,13 +149,10 @@ void BlockCursor::update(const Hotbar &hotbar) { timeToBreak = m_currentBlock->timeToBreak(currentStack.item().harvestCapability(), currentStack.item().miningSpeed()); if (ticks > m_animationStart + timeToBreak * 1000) { - ItemStack itemDrop = m_currentBlock->getItemDrop(); - m_player.inventory().addStack(itemDrop.item().stringID(), itemDrop.amount()); m_world.setBlock(m_selectedBlock.x, m_selectedBlock.y, m_selectedBlock.z, 0); m_animationStart = ticks; m_client.sendPlayerDigBlock(m_selectedBlock); - m_client.sendPlayerInvUpdate(); } } } @@ -260,6 +257,7 @@ void BlockCursor::draw(gk::RenderTarget &target, gk::RenderStates states) const glCheck(glDisable(GL_POLYGON_OFFSET_FILL)); glCheck(glDisable(GL_CULL_FACE)); + glCheck(glEnable(GL_DEPTH_TEST)); // Subtract the camera position - see comment in ClientWorld::draw() gk::Vector3d cameraPosition = m_player.camera().getDPosition(); diff --git a/source/client/hud/DebugOverlay.cpp b/source/client/hud/DebugOverlay.cpp index fbe58910f..609fa875a 100644 --- a/source/client/hud/DebugOverlay.cpp +++ b/source/client/hud/DebugOverlay.cpp @@ -27,6 +27,7 @@ #include #include "ClientPlayer.hpp" +#include "ClientScene.hpp" #include "ClientWorld.hpp" #include "Config.hpp" #include "DebugOverlay.hpp" @@ -70,6 +71,8 @@ void DebugOverlay::update() { stream << "dimension: " << m_player.dimension(); stream << '\n'; stream << "Loaded chunks: " << m_world.loadedChunkCount(); + stream << '\n'; + stream << "Alive entities: " << m_world.scene().registry().alive(); m_positionText.setText(stream.str()); } diff --git a/source/client/network/Client.hpp b/source/client/network/Client.hpp index 31e2a07e3..5ccb5825a 100644 --- a/source/client/network/Client.hpp +++ b/source/client/network/Client.hpp @@ -30,6 +30,7 @@ #include #include +#include #include #include diff --git a/source/client/network/ClientCommandHandler.cpp b/source/client/network/ClientCommandHandler.cpp index 2c242b830..1f4988972 100644 --- a/source/client/network/ClientCommandHandler.cpp +++ b/source/client/network/ClientCommandHandler.cpp @@ -27,11 +27,19 @@ #include #include +#include + +#include "AnimationComponent.hpp" #include "Client.hpp" #include "ClientPlayer.hpp" #include "ClientWorld.hpp" #include "ClientCommandHandler.hpp" +#include "DrawableComponent.hpp" +#include "DrawableDef.hpp" #include "LuaGUIState.hpp" +#include "NetworkComponent.hpp" +#include "PositionComponent.hpp" +#include "RotationComponent.hpp" #include "Registry.hpp" void ClientCommandHandler::sendPlayerInvUpdate() { @@ -201,6 +209,7 @@ void ClientCommandHandler::setupCallbacks() { m_player.setPosition(x, y, z); m_world.clear(); m_world.updateSky(dimension); + m_entityMap.clear(); } }); @@ -241,5 +250,88 @@ void ClientCommandHandler::setupCallbacks() { } } }); + + m_client.setCommandCallback(Network::Command::EntitySpawn, [this](sf::Packet &packet) { + entt::entity entityID; + packet >> entityID; + + auto ®istry = m_world.scene().registry(); + + auto it = m_entityMap.find(entityID); + if (it == m_entityMap.end()) { + entt::entity entity = registry.create(); + m_entityMap.emplace(entityID, entity); + registry.emplace(entity, entityID); + } + else if (registry.get(it->second).entityID != entityID) { + gkError() << "EntitySpawn: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; + } + }); + + m_client.setCommandCallback(Network::Command::EntityDespawn, [this](sf::Packet &packet) { + entt::entity entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + m_world.scene().registry().destroy(it->second); + } + else + gkError() << "EntityDespawn: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityPosition, [this](sf::Packet &packet) { + entt::entity entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + auto &position = m_world.scene().registry().get_or_emplace(it->second); + packet >> position.x >> position.y >> position.z; + } + else + gkError() << "EntityPosition: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityRotation, [this](sf::Packet &packet) { + entt::entity entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + float w, x, y, z; + packet >> w >> x >> y >> z; + + auto &rotation = m_world.scene().registry().get_or_emplace(it->second); + rotation.quat = glm::quat(w, x, y, z); + } + else + gkError() << "EntityRotation: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityAnimation, [this](sf::Packet &packet) { + entt::entity entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + auto &animation = m_world.scene().registry().get_or_emplace(it->second); + animation.deserialize(packet); + } + else + gkError() << "EntityAnimation: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; + }); + + m_client.setCommandCallback(Network::Command::EntityDrawableDef, [this](sf::Packet &packet) { + entt::entity entityID; + packet >> entityID; + + auto it = m_entityMap.find(entityID); + if (it != m_entityMap.end()) { + packet >> m_world.scene().registry().get_or_emplace(it->second); + } + else + gkError() << "EntityDrawableDef: Entity ID" << std::underlying_type_t(entityID) << "is invalid"; + }); } diff --git a/source/client/network/ClientCommandHandler.hpp b/source/client/network/ClientCommandHandler.hpp index 9b3b5e5ab..d8c59e4a1 100644 --- a/source/client/network/ClientCommandHandler.hpp +++ b/source/client/network/ClientCommandHandler.hpp @@ -29,6 +29,8 @@ #include +#include + #include "PlayerBox.hpp" namespace gk { @@ -70,6 +72,8 @@ class ClientCommandHandler { std::unordered_map &m_playerBoxes; + std::unordered_map m_entityMap; + bool m_isRegistryInitialized = false; bool m_isSingleplayer = false; diff --git a/source/client/scene/ClientScene.cpp b/source/client/scene/ClientScene.cpp new file mode 100644 index 000000000..dd08fff63 --- /dev/null +++ b/source/client/scene/ClientScene.cpp @@ -0,0 +1,46 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "AnimationController.hpp" +#include "ClientScene.hpp" +#include "RenderingController.hpp" + +ClientScene::ClientScene() { + m_controllers.emplace_back(new AnimationController); + m_controllers.emplace_back(new RenderingController); +} + +void ClientScene::draw(gk::RenderTarget &target, gk::RenderStates states) const { + if (!m_camera) return; + + // Subtract the camera position - see comment in ClientWorld::draw() + gk::Vector3d cameraPosition = m_camera->getDPosition(); + states.transform.translate(-cameraPosition.x, -cameraPosition.y, -cameraPosition.z); + + for (auto &controller : m_controllers) + controller->draw(m_registry, target, states); +} + diff --git a/source/client/scene/ClientScene.hpp b/source/client/scene/ClientScene.hpp new file mode 100644 index 000000000..2b4922905 --- /dev/null +++ b/source/client/scene/ClientScene.hpp @@ -0,0 +1,54 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef CLIENTSCENE_HPP_ +#define CLIENTSCENE_HPP_ + +#include +#include + +#include +#include + +#include + +#include "Scene.hpp" + +class ClientPlayer; + +class ClientScene : public Scene, public gk::Drawable { + public: + ClientScene(); + + void setCamera(gk::Camera &camera) { m_camera = &camera; } + + private: + void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + + gk::Camera *m_camera = nullptr; +}; + +#endif // CLIENTSCENE_HPP_ diff --git a/source/client/scene/controller/AnimationController.cpp b/source/client/scene/controller/AnimationController.cpp new file mode 100644 index 000000000..4deebc56d --- /dev/null +++ b/source/client/scene/controller/AnimationController.cpp @@ -0,0 +1,77 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include + +#include "AnimationComponent.hpp" +#include "AnimationController.hpp" +#include "PositionComponent.hpp" +#include "RotationComponent.hpp" + +void AnimationController::update(entt::registry ®istry) { + registry.view().each([](auto, auto &position, auto &animation) { + for (auto &it : animation.list) { + if (it.type == AnimationType::Translation) { + float dx = it.translation.dx; + float dy = it.translation.dy; + float dz = it.translation.dz; + + if (it.translation.cx + it.translation.dx > it.translation.max + || it.translation.cx + it.translation.dx < it.translation.min) + dx = (it.translation.loop) ? -dx : 0; + + if (it.translation.cy + it.translation.dy > it.translation.max + || it.translation.cy + it.translation.dy < it.translation.min) + dy = (it.translation.loop) ? -dy : 0; + + if (it.translation.cz + it.translation.dz > it.translation.max + || it.translation.cz + it.translation.dz < it.translation.min) + dz = (it.translation.loop) ? -dz : 0; + + position.x += dx; + position.y += dy; + position.z += dz; + + it.translation.cx += dx; + it.translation.cy += dy; + it.translation.cz += dz; + + it.translation.dx = dx; + it.translation.dy = dy; + it.translation.dz = dz; + } + } + }); + + registry.view().each([](auto, auto &rotation, auto &animation) { + for (auto &it : animation.list) { + if (it.type == AnimationType::Rotation) { + rotation.quat = glm::angleAxis(glm::radians(it.rotation.angle), glm::vec3{it.rotation.axisX, it.rotation.axisY, it.rotation.axisZ}) * rotation.quat; + } + } + }); +} + diff --git a/source/client/scene/controller/AnimationController.hpp b/source/client/scene/controller/AnimationController.hpp new file mode 100644 index 000000000..eae10be26 --- /dev/null +++ b/source/client/scene/controller/AnimationController.hpp @@ -0,0 +1,37 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef ANIMATIONCONTROLLER_HPP_ +#define ANIMATIONCONTROLLER_HPP_ + +#include "AbstractController.hpp" + +class AnimationController : public AbstractController { + public: + void update(entt::registry ®istry) override; +}; + +#endif // ANIMATIONCONTROLLER_HPP_ diff --git a/source/client/scene/controller/RenderingController.cpp b/source/client/scene/controller/RenderingController.cpp new file mode 100644 index 000000000..c17b25d25 --- /dev/null +++ b/source/client/scene/controller/RenderingController.cpp @@ -0,0 +1,63 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include + +#include "DrawableDef.hpp" +#include "DrawableComponent.hpp" +#include "InventoryCube.hpp" +#include "PositionComponent.hpp" +#include "RenderingController.hpp" +#include "RotationComponent.hpp" + +#include "Registry.hpp" + +void RenderingController::update(entt::registry ®istry) { + registry.view(entt::exclude).each([&](auto entity, auto &drawableDef) { + const InventoryCubeDef &cubeDef = drawableDef.getInventoryCubeDef(); + + DrawableComponent &drawable = registry.get_or_emplace(entity); + + InventoryCube &cube = drawable.setDrawable(cubeDef.size, true); + cube.setOrigin(cubeDef.origin); + cube.updateVertexBuffer(Registry::getInstance().getBlockFromStringID(cubeDef.blockID)); + + registry.remove(entity); + }); +} + +void RenderingController::draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) { + registry.view().each([&](auto, auto &drawable, auto &position, auto &rotation) { + gk::Transformable transformable; + transformable.setPosition(position.x, position.y, position.z); + transformable.getRotationTransform().getMatrix() = glm::toMat4(rotation.quat); + + gk::RenderStates drawStates = states; + drawStates.transform *= transformable.getTransform(); + drawable.draw(target, drawStates); + }); +} + diff --git a/source/client/scene/controller/RenderingController.hpp b/source/client/scene/controller/RenderingController.hpp new file mode 100644 index 000000000..21bbe4ceb --- /dev/null +++ b/source/client/scene/controller/RenderingController.hpp @@ -0,0 +1,39 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef RENDERINGCONTROLLER_HPP_ +#define RENDERINGCONTROLLER_HPP_ + +#include "AbstractController.hpp" + +class RenderingController : public AbstractController { + public: + void update(entt::registry ®istry) override; + + void draw(entt::registry ®istry, gk::RenderTarget &target, gk::RenderStates states) override; +}; + +#endif // RENDERINGCONTROLLER_HPP_ diff --git a/source/client/states/GameState.cpp b/source/client/states/GameState.cpp index 4bb674b86..a9146c703 100644 --- a/source/client/states/GameState.cpp +++ b/source/client/states/GameState.cpp @@ -156,7 +156,6 @@ void GameState::initShaders() { m_shader.createProgram(); m_shader.addShader(GL_VERTEX_SHADER, "resources/shaders/game.v.glsl"); - m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/color.f.glsl"); m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/light.f.glsl"); m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/fog.f.glsl"); m_shader.addShader(GL_FRAGMENT_SHADER, "resources/shaders/game.f.glsl"); diff --git a/source/client/world/ClientWorld.cpp b/source/client/world/ClientWorld.cpp index 33243e318..853288d10 100644 --- a/source/client/world/ClientWorld.cpp +++ b/source/client/world/ClientWorld.cpp @@ -34,13 +34,13 @@ #include "ClientCommandHandler.hpp" #include "ClientPlayer.hpp" #include "ClientWorld.hpp" +#include "ItemDropFactory.hpp" #include "Registry.hpp" #include "Sky.hpp" #include "TextureAtlas.hpp" #include "World.hpp" -ClientWorld::ClientWorld() : - m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) +ClientWorld::ClientWorld() : m_textureAtlas(gk::ResourceHandler::getInstance().get("atlas-blocks")) { } @@ -66,6 +66,8 @@ void ClientWorld::update() { World::isReloadRequested = false; sendChunkRequests(); + + m_scene.update(); } void ClientWorld::sendChunkRequests() { @@ -100,6 +102,7 @@ void ClientWorld::checkPlayerChunk(double playerX, double playerY, double player void ClientWorld::clear() { m_chunks.clear(); + m_scene.registry().clear(); } void ClientWorld::updateSky(u16 dimensionID) { @@ -315,5 +318,8 @@ void ClientWorld::draw(gk::RenderTarget &target, gk::RenderStates states) const } m_camera->setDPosition(cameraPos); // Restore the camera to its original position + + states.transform = gk::Transform::Identity; + target.draw(m_scene, states); } diff --git a/source/client/world/ClientWorld.hpp b/source/client/world/ClientWorld.hpp index fa70516e0..00ae9bd54 100644 --- a/source/client/world/ClientWorld.hpp +++ b/source/client/world/ClientWorld.hpp @@ -33,6 +33,7 @@ #include #include "ClientChunk.hpp" +#include "ClientScene.hpp" #include "Network.hpp" #include "World.hpp" @@ -59,8 +60,11 @@ class ClientWorld : public World, public gk::Drawable { Chunk *getChunk(int cx, int cy, int cz) const override; + const ClientScene &scene() const { return m_scene; } + ClientScene &scene() { return m_scene; } + void setClient(ClientCommandHandler &client) { m_client = &client; } - void setCamera(gk::Camera &camera) { m_camera = &camera; } + void setCamera(gk::Camera &camera) { m_camera = &camera; m_scene.setCamera(camera); } std::size_t loadedChunkCount() const { return m_chunks.size(); } @@ -69,6 +73,8 @@ class ClientWorld : public World, public gk::Drawable { void draw(gk::RenderTarget &target, gk::RenderStates states) const override; + ClientScene m_scene; + ChunkMap m_chunks; TextureAtlas &m_textureAtlas; diff --git a/source/common/CMakeLists.txt b/source/common/CMakeLists.txt index 35235c0f6..a86c3abea 100644 --- a/source/common/CMakeLists.txt +++ b/source/common/CMakeLists.txt @@ -7,7 +7,7 @@ set(PROJECT_NAME ${CMAKE_PROJECT_NAME}_common) # Get source files #------------------------------------------------------------------------------ file(GLOB_RECURSE SOURCE_FILES *.cpp) -file(GLOB_RECURSE HEADER_FILES *.hpp ../../external/*.hpp) +file(GLOB_RECURSE HEADER_FILES *.hpp) foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) @@ -18,6 +18,7 @@ endforeach(HEADER_FILE) # Add library #------------------------------------------------------------------------------ add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) +add_dependencies(${PROJECT_NAME} EnTT sfml-network gamekit) #------------------------------------------------------------------------------ # Compiler flags diff --git a/source/common/network/Network.cpp b/source/common/network/Network.cpp index 4358b4552..f623db0b1 100644 --- a/source/common/network/Network.cpp +++ b/source/common/network/Network.cpp @@ -59,8 +59,16 @@ std::string Network::commandToString(Network::Command command) { {Network::Command::RegistryData, "RegistryData"}, - {Network::Command::ChatMessage, "ChatMessage"} + {Network::Command::ChatMessage, "ChatMessage"}, + + {Network::Command::EntitySpawn, "EntitySpawn"}, + {Network::Command::EntityDespawn, "EntityDespawn"}, + {Network::Command::EntityPosition, "EntityPosition"}, + {Network::Command::EntityRotation, "EntityRotation"}, + {Network::Command::EntityAnimation, "EntityRotation"}, + {Network::Command::EntityDrawableDef, "EntityDrawableDef"}, }; + return commandNames[command]; } diff --git a/source/common/network/Network.hpp b/source/common/network/Network.hpp index d64fba087..3c07b4624 100644 --- a/source/common/network/Network.hpp +++ b/source/common/network/Network.hpp @@ -66,6 +66,14 @@ namespace Network { // Chat commands ChatMessage = 21, // [NetworkCommand][u16 client id][std::string message] (both) + + // Entity commands + EntitySpawn = 22, // [NetworkCommand][u32 entity id] (from Server only) + EntityDespawn = 23, // [NetworkCommand][u32 entity id] (from Server only) + EntityPosition = 24, // [NetworkCommand][u32 entity id][double x, double y, double z] (from Server only) + EntityRotation = 25, // [NetworkCommand][u32 entity id][float w, float x, float y, float z] (from Server only) + EntityAnimation = 26, // [NetworkCommand][u32 entity id][AnimationComponent anim] (from Server only) + EntityDrawableDef = 27, // [NetworkCommand][u32 entity id][DrawableDef def] (from Server only) }; std::string commandToString(Command command); diff --git a/source/common/network/NetworkUtils.cpp b/source/common/network/NetworkUtils.cpp index 0c9e9da4b..1d1ae7eea 100644 --- a/source/common/network/NetworkUtils.cpp +++ b/source/common/network/NetworkUtils.cpp @@ -36,3 +36,15 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Color &color) { return packet; } +sf::Packet &operator<<(sf::Packet &packet, const entt::entity &entity) { + packet << std::underlying_type_t(entity); + return packet; +} + +sf::Packet &operator>>(sf::Packet &packet, entt::entity &entity) { + std::underlying_type_t id; + packet >> id; + entity = static_cast(id); + return packet; +} + diff --git a/source/common/network/NetworkUtils.hpp b/source/common/network/NetworkUtils.hpp index 1e8333865..c79787a58 100644 --- a/source/common/network/NetworkUtils.hpp +++ b/source/common/network/NetworkUtils.hpp @@ -143,4 +143,12 @@ sf::Packet &operator>>(sf::Packet &packet, gk::Vector3 &vec) { sf::Packet &operator<<(sf::Packet &packet, const gk::Color &color); sf::Packet &operator>>(sf::Packet &packet, gk::Color &color); +//====================================================================================== +// entt::entity +//====================================================================================== +#include + +sf::Packet &operator<<(sf::Packet &packet, const entt::entity &entity); +sf::Packet &operator>>(sf::Packet &packet, entt::entity &entity); + #endif // NETWORKUTILS_HPP_ diff --git a/source/common/scene/Scene.hpp b/source/common/scene/Scene.hpp new file mode 100644 index 000000000..01ad40386 --- /dev/null +++ b/source/common/scene/Scene.hpp @@ -0,0 +1,48 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef SCENE_HPP_ +#define SCENE_HPP_ + +#include + +#include "AbstractController.hpp" +#include "ISerializable.hpp" + +class Scene { + public: + virtual void update() { for (auto &controller : m_controllers) controller->update(m_registry); } + + const entt::registry ®istry() const { return m_registry; } + entt::registry ®istry() { return m_registry; } + + protected: + mutable entt::registry m_registry; + + std::deque> m_controllers; +}; + +#endif // SCENE_HPP_ diff --git a/source/common/scene/component/AnimationComponent.hpp b/source/common/scene/component/AnimationComponent.hpp new file mode 100644 index 000000000..a730a5c04 --- /dev/null +++ b/source/common/scene/component/AnimationComponent.hpp @@ -0,0 +1,132 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef ANIMATIONCOMPONENT_HPP_ +#define ANIMATIONCOMPONENT_HPP_ + +#include + +#include + +#include "ISerializable.hpp" +#include "NetworkUtils.hpp" + +enum class AnimationType { + Rotation, + Translation, +}; + +struct AnimationData : public ISerializable { + AnimationType type; + + union { + struct { + float axisX; + float axisY; + float axisZ; + + float angle; + } rotation; + + struct { + float dx, dy, dz; + float cx, cy, cz; + float min, max; + bool loop; + } translation; + }; + + void serialize(sf::Packet &packet) const override { + packet << u8(type); + if (type == AnimationType::Rotation) { + packet << rotation.axisX << rotation.axisY << rotation.axisZ << rotation.angle; + } + else if (type == AnimationType::Translation) { + packet << translation.dx << translation.dy << translation.dz + << translation.cx << translation.cy << translation.cz + << translation.min << translation.max << translation.loop; + } + } + + void deserialize(sf::Packet &packet) override { + u8 type; + packet >> type; + if (type == u8(AnimationType::Rotation)) { + packet >> rotation.axisX >> rotation.axisY >> rotation.axisZ >> rotation.angle; + } + else if (type == u8(AnimationType::Translation)) { + packet >> translation.dx >> translation.dy >> translation.dz + >> translation.cx >> translation.cy >> translation.cz + >> translation.min >> translation.max >> translation.loop; + } + + this->type = AnimationType(type); + } +}; + +struct AnimationComponent : public ISerializable { + void addRotation(float axisX, float axisY, float axisZ, float angle) { + list.emplace_back(); + AnimationData &data = list.back(); + + data.type = AnimationType::Rotation; + + data.rotation.axisX = axisX; + data.rotation.axisY = axisY; + data.rotation.axisZ = axisZ; + + data.rotation.angle = angle; + } + + void addTranslation(float dx, float dy, float dz, float min, float max, bool loop) { + list.emplace_back(); + AnimationData &data = list.back(); + + data.type = AnimationType::Translation; + + data.translation.dx = dx; + data.translation.dy = dy; + data.translation.dz = dz; + + data.translation.cx = 0; + data.translation.cy = 0; + data.translation.cz = 0; + + data.translation.min = min; + data.translation.max = max; + + data.translation.loop = loop; + } + + void serialize(sf::Packet &packet) const override { packet << list; } + void deserialize(sf::Packet &packet) override { packet >> list; } + + std::vector list; + + bool isUpdated = true; +}; + +#endif // ANIMATIONCOMPONENT_HPP_ diff --git a/source/common/scene/component/DrawableComponent.hpp b/source/common/scene/component/DrawableComponent.hpp new file mode 100644 index 000000000..3d80c172b --- /dev/null +++ b/source/common/scene/component/DrawableComponent.hpp @@ -0,0 +1,51 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef DRAWABLECOMPONENT_HPP_ +#define DRAWABLECOMPONENT_HPP_ + +#include + +#include + +class DrawableComponent { + public: + template + auto setDrawable(Args &&...args) -> typename std::enable_if::value, T &>::type { + m_drawable = std::make_shared(std::forward(args)...); + return *std::static_pointer_cast(m_drawable); + } + + void draw(gk::RenderTarget &target, gk::RenderStates states) { + if (m_drawable) + target.draw(*m_drawable, states); + } + + private: + std::shared_ptr m_drawable; +}; + +#endif // DRAWABLECOMPONENT_HPP_ diff --git a/source/common/scene/component/DrawableDef.cpp b/source/common/scene/component/DrawableDef.cpp new file mode 100644 index 000000000..f96f05d2f --- /dev/null +++ b/source/common/scene/component/DrawableDef.cpp @@ -0,0 +1,48 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "DrawableDef.hpp" +#include "NetworkUtils.hpp" + +InventoryCubeDef &DrawableDef::addInventoryCube() { + m_cubes.emplace_back(); + return m_cubes.back(); +} + +void DrawableDef::serialize(sf::Packet &packet) const { + for (auto &it : m_cubes) + packet << u8(DrawableType::InventoryCube) << it; +} + +void DrawableDef::deserialize(sf::Packet &packet) { + u8 type; + packet >> type; + if (type == DrawableType::InventoryCube) { + m_cubes.emplace_back(); + packet >> m_cubes.back(); + } +} + diff --git a/source/common/scene/component/DrawableDef.hpp b/source/common/scene/component/DrawableDef.hpp new file mode 100644 index 000000000..bdafa21d8 --- /dev/null +++ b/source/common/scene/component/DrawableDef.hpp @@ -0,0 +1,58 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef DRAWABLEDEF_HPP_ +#define DRAWABLEDEF_HPP_ + +#include + +#include "ISerializable.hpp" +#include "InventoryCubeDef.hpp" + +namespace DrawableType { + enum : u8 { + Undefined = 0, + InventoryCube = 1, + }; +} + +class DrawableDef : public ISerializable { + public: + InventoryCubeDef &addInventoryCube(); + + // FIXME + const InventoryCubeDef &getInventoryCubeDef() const { return m_cubes.back(); } + + void serialize(sf::Packet &packet) const override; + void deserialize(sf::Packet &packet) override; + + bool isUpdated = true; + + protected: + std::vector m_cubes; +}; + +#endif // DRAWABLEDEF_HPP_ diff --git a/source/common/scene/component/InventoryCubeDef.hpp b/source/common/scene/component/InventoryCubeDef.hpp new file mode 100644 index 000000000..b286e45fc --- /dev/null +++ b/source/common/scene/component/InventoryCubeDef.hpp @@ -0,0 +1,46 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef INVENTORYCUBEDEF_HPP_ +#define INVENTORYCUBEDEF_HPP_ + +#include + +#include + +#include "ISerializable.hpp" +#include "NetworkUtils.hpp" + +struct InventoryCubeDef : public ISerializable { + float size = 1.f; + gk::Vector3f origin{0, 0, 0}; + std::string blockID{"_:air"}; + + void serialize(sf::Packet &packet) const override { packet << size << origin << blockID; } + void deserialize(sf::Packet &packet) override { packet >> size >> origin >> blockID; } +}; + +#endif // INVENTORYCUBEDEF_HPP_ diff --git a/source/common/scene/component/NetworkComponent.hpp b/source/common/scene/component/NetworkComponent.hpp new file mode 100644 index 000000000..61be9b3f8 --- /dev/null +++ b/source/common/scene/component/NetworkComponent.hpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef NETWORKCOMPONENT_HPP_ +#define NETWORKCOMPONENT_HPP_ + +#include + +struct NetworkComponent { + entt::entity entityID; + + bool hasSpawned = false; +}; + +#endif // NETWORKCOMPONENT_HPP_ diff --git a/source/common/scene/component/PositionComponent.hpp b/source/common/scene/component/PositionComponent.hpp new file mode 100644 index 000000000..e85e2c90d --- /dev/null +++ b/source/common/scene/component/PositionComponent.hpp @@ -0,0 +1,42 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef POSITIONCOMPONENT_HPP_ +#define POSITIONCOMPONENT_HPP_ + +#include + +struct PositionComponent { + double x = 0; + double y = 0; + double z = 0; + + u16 dimension = 0; + + bool isUpdated = true; +}; + +#endif // POSITIONCOMPONENT_HPP_ diff --git a/source/common/scene/component/RotationComponent.hpp b/source/common/scene/component/RotationComponent.hpp new file mode 100644 index 000000000..010fb2bf6 --- /dev/null +++ b/source/common/scene/component/RotationComponent.hpp @@ -0,0 +1,38 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef ROTATIONCOMPONENT_HPP_ +#define ROTATIONCOMPONENT_HPP_ + +#include + +struct RotationComponent { + glm::quat quat{1, 0, 0, 0}; + + bool isUpdated = true; +}; + +#endif // ROTATIONCOMPONENT_HPP_ diff --git a/source/common/scene/controller/AbstractController.hpp b/source/common/scene/controller/AbstractController.hpp new file mode 100644 index 000000000..908ef453d --- /dev/null +++ b/source/common/scene/controller/AbstractController.hpp @@ -0,0 +1,43 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef ABSTRACTCONTROLLER_HPP_ +#define ABSTRACTCONTROLLER_HPP_ + +#include +#include + +#include + +class AbstractController { + public: + virtual ~AbstractController() = default; + + virtual void update(entt::registry &) {} + virtual void draw(entt::registry &, gk::RenderTarget &, gk::RenderStates) {} +}; + +#endif // ABSTRACTCONTROLLER_HPP_ diff --git a/source/common/world/Chunk.cpp b/source/common/world/Chunk.cpp index ddd74ef0c..19cad394c 100644 --- a/source/common/world/Chunk.cpp +++ b/source/common/world/Chunk.cpp @@ -31,6 +31,7 @@ #include "Chunk.hpp" #include "EngineConfig.hpp" #include "Registry.hpp" +#include "World.hpp" Chunk::Chunk(s32 x, s32 y, s32 z, World &world) : m_world(world) { m_x = x; @@ -88,6 +89,7 @@ void Chunk::setBlock(int x, int y, int z, u16 type) { } onBlockPlaced(x, y, z, block); + m_world.onBlockPlaced(x + m_x * width, y + m_y * depth, z + m_z * height, block); if (m_data[z][y][x] != 0) { const Block &oldBlock = Registry::getInstance().getBlock(m_data[z][y][x]); diff --git a/source/common/world/World.hpp b/source/common/world/World.hpp index 2aebce377..e96e176ab 100644 --- a/source/common/world/World.hpp +++ b/source/common/world/World.hpp @@ -46,6 +46,8 @@ class World { u16 getData(int x, int y, int z) const; void setData(int x, int y, int z, u16 data) const; + virtual void onBlockPlaced(int, int, int, const Block &) {} + static bool isReloadRequested; }; diff --git a/source/server/CMakeLists.txt b/source/server/CMakeLists.txt index 42ea96b7f..3798b3f6a 100644 --- a/source/server/CMakeLists.txt +++ b/source/server/CMakeLists.txt @@ -6,8 +6,10 @@ set(PROJECT_NAME ${PROJECT_NAME}_server) #------------------------------------------------------------------------------ # Get source files #------------------------------------------------------------------------------ -file(GLOB_RECURSE SOURCE_FILES */*.cpp ../../external/*.cpp) -file(GLOB_RECURSE HEADER_FILES *.hpp ../common/*.hpp ../../external/*.hpp) +file(GLOB_RECURSE SOURCE_FILES */*.cpp ../../external/FastNoise.cpp) +file(GLOB_RECURSE HEADER_FILES *.hpp ../common/*.hpp) + +include_directories(../../external ../../external/entt/single_include) foreach(HEADER_FILE ${HEADER_FILES}) get_filename_component(HEADER_DIRECTORY ${HEADER_FILE} DIRECTORY) @@ -66,7 +68,8 @@ target_link_libraries(${PROJECT_NAME}_lib sfml-system sfml-network) target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_lib - ${GAMEKIT_LIBRARIES} + ${CMAKE_PROJECT_NAME}_common + gamekit ${OPENGL_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} @@ -77,6 +80,5 @@ target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} sfml-system sfml-network - ${UNIX_LIBS} - ${CMAKE_PROJECT_NAME}_common) + ${UNIX_LIBS}) diff --git a/source/server/core/PlayerList.cpp b/source/server/core/PlayerList.cpp new file mode 100644 index 000000000..d67bef2d5 --- /dev/null +++ b/source/server/core/PlayerList.cpp @@ -0,0 +1,55 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "PlayerList.hpp" + +ServerPlayer &PlayerList::addPlayer(ClientInfo &client) { + m_players.emplace(client.id, client); + return m_players.at(client.id); +} + +void PlayerList::removePlayer(u16 id) { + auto it = m_players.find(id); + if (it != m_players.end()) + m_players.erase(it); +} + +const ServerPlayer *PlayerList::getPlayer(u16 id) const { + auto it = m_players.find(id); + if (it == m_players.end()) + return nullptr; + + return &it->second; +} + +ServerPlayer *PlayerList::getPlayer(u16 id) { + auto it = m_players.find(id); + if (it == m_players.end()) + return nullptr; + + return &it->second; +} + diff --git a/source/server/core/PlayerList.hpp b/source/server/core/PlayerList.hpp new file mode 100644 index 000000000..b2c8ac851 --- /dev/null +++ b/source/server/core/PlayerList.hpp @@ -0,0 +1,56 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef PLAYERLIST_HPP_ +#define PLAYERLIST_HPP_ + +#include + +#include "ServerPlayer.hpp" + +class PlayerList { + using Container = std::unordered_map; + using Iterator = Container::iterator; + using ConstIterator = Container::const_iterator; + + public: + ServerPlayer &addPlayer(ClientInfo &client); + void removePlayer(u16 id); + + const ServerPlayer *getPlayer(u16 id) const; + ServerPlayer *getPlayer(u16 id); + + Iterator begin() { return m_players.begin(); } + Iterator end() { return m_players.end(); } + + ConstIterator begin() const { return m_players.begin(); } + ConstIterator end() const { return m_players.end(); } + + private: + Container m_players; +}; + +#endif // PLAYERLIST_HPP_ diff --git a/source/server/core/ServerApplication.cpp b/source/server/core/ServerApplication.cpp index 3655d704b..10df83824 100644 --- a/source/server/core/ServerApplication.cpp +++ b/source/server/core/ServerApplication.cpp @@ -29,6 +29,7 @@ #include "BlockGeometry.hpp" #include "ServerApplication.hpp" #include "ServerBlock.hpp" +#include "ServerConfig.hpp" namespace fs = ghc::filesystem; @@ -58,6 +59,8 @@ void ServerApplication::init() { if (m_argumentParser.getArgument("port").isFound) m_port = std::stoi(m_argumentParser.getArgument("port").parameter); + ServerConfig::loadConfigFromFile("server_config.lua"); + m_server.init(m_port); m_server.setRunning(true); @@ -72,7 +75,7 @@ void ServerApplication::init() { m_serverCommandHandler.setupCallbacks(); m_worldController.setServer(m_serverCommandHandler); - m_worldController.init(); + m_worldController.init(m_players); m_scriptEngine.luaCore().setRegistry(&m_registry); @@ -90,7 +93,7 @@ int ServerApplication::run(bool isProtected) { } catch(const gk::Exception &e) { if (m_eventHandler) - m_eventHandler->emplaceEvent(false); + m_eventHandler->emplaceEvent(false, 0); std::cerr << "Fatal error " << e.what() << std::endl; return 1; diff --git a/source/server/core/ServerApplication.hpp b/source/server/core/ServerApplication.hpp index 60bb12564..4be063efc 100644 --- a/source/server/core/ServerApplication.hpp +++ b/source/server/core/ServerApplication.hpp @@ -37,12 +37,12 @@ #include "Server.hpp" #include "ServerCommandHandler.hpp" #include "ServerModLoader.hpp" -#include "ServerPlayer.hpp" +#include "PlayerList.hpp" #include "WorldController.hpp" struct ServerOnlineEvent { bool isOnline; - u16 port; + int port; }; class ServerApplication { @@ -73,7 +73,7 @@ class ServerApplication { u16 m_port = 4242; WorldController m_worldController{m_registry, m_clock}; - std::unordered_map m_players; + PlayerList m_players; Server m_server; ServerCommandHandler m_serverCommandHandler{m_scriptEngine, m_server, m_worldController, m_players, m_registry}; diff --git a/source/server/core/ServerConfig.cpp b/source/server/core/ServerConfig.cpp new file mode 100644 index 000000000..e8fd637c0 --- /dev/null +++ b/source/server/core/ServerConfig.cpp @@ -0,0 +1,53 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "ServerConfig.hpp" + +// Gameplay +bool ServerConfig::useItemDrops = false; + +#include +#include + +#include + +void ServerConfig::loadConfigFromFile(const char *file) { + if (gk::Filesystem::fileExists(file)) { + sol::state lua; + + try { + lua.safe_script_file(file); + + useItemDrops = lua["useItemDrops"].get_or(useItemDrops); + + gkInfo() << "Config file loaded successfully"; + } + catch (sol::error &e) { + gkError() << e.what(); + } + } +} + diff --git a/source/server/core/ServerConfig.hpp b/source/server/core/ServerConfig.hpp new file mode 100644 index 000000000..4ee378fa7 --- /dev/null +++ b/source/server/core/ServerConfig.hpp @@ -0,0 +1,37 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef SERVERCONFIG_HPP_ +#define SERVERCONFIG_HPP_ + +namespace ServerConfig { + // Gameplay + extern bool useItemDrops; + + void loadConfigFromFile(const char *file); +} + +#endif // SERVERCONFIG_HPP_ diff --git a/source/server/network/Server.cpp b/source/server/network/Server.cpp index 723f800a9..5b10822ba 100644 --- a/source/server/network/Server.cpp +++ b/source/server/network/Server.cpp @@ -173,8 +173,8 @@ void Server::disconnectClient(ClientInfo &client) { } } -void Server::sendToAllClients(sf::Packet &packet) { - for (ClientInfo &client : m_info.clients()) { +void Server::sendToAllClients(sf::Packet &packet) const { + for (const ClientInfo &client : m_info.clients()) { client.tcpSocket->send(packet); } } diff --git a/source/server/network/Server.hpp b/source/server/network/Server.hpp index dffe32afa..080841b48 100644 --- a/source/server/network/Server.hpp +++ b/source/server/network/Server.hpp @@ -48,7 +48,7 @@ class Server { void handleGameEvents(); - void sendToAllClients(sf::Packet &packet); + void sendToAllClients(sf::Packet &packet) const; bool isRunning() const { return m_isRunning; } diff --git a/source/server/network/ServerCommandHandler.cpp b/source/server/network/ServerCommandHandler.cpp index 3804ce5f7..46ed261b3 100644 --- a/source/server/network/ServerCommandHandler.cpp +++ b/source/server/network/ServerCommandHandler.cpp @@ -24,12 +24,15 @@ * * ===================================================================================== */ +#include "AnimationComponent.hpp" #include "BlockData.hpp" +#include "DrawableDef.hpp" +#include "NetworkComponent.hpp" +#include "PlayerList.hpp" #include "Registry.hpp" #include "ScriptEngine.hpp" #include "Server.hpp" #include "ServerBlock.hpp" -#include "ServerPlayer.hpp" #include "ServerCommandHandler.hpp" #include "WorldController.hpp" @@ -55,18 +58,37 @@ void ServerCommandHandler::sendBlockInvUpdate(s32 x, s32 y, s32 z, const Invento } void ServerCommandHandler::sendPlayerPosUpdate(u16 clientID, bool isTeleportation, const ClientInfo *client) const { - const ServerPlayer &player = m_players.at(clientID); + const ServerPlayer *player = m_players.getPlayer(clientID); + if (player) { + sf::Packet packet; + packet << Network::Command::PlayerPosUpdate; + packet << clientID; + packet << player->x() << player->y() << player->z(); + packet << isTeleportation; - sf::Packet packet; - packet << Network::Command::PlayerPosUpdate; - packet << clientID; - packet << player.x() << player.y() << player.z(); - packet << isTeleportation; + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); + } + else + gkError() << ("Failed to send pos update for player " + std::to_string(clientID) + ": Player not found").c_str(); +} - if (!client) - m_server.sendToAllClients(packet); +void ServerCommandHandler::sendPlayerInvUpdate(u16 clientID, const ClientInfo *client) const { + ServerPlayer *player = m_players.getPlayer(clientID); + if (player) { + sf::Packet packet; + packet << Network::Command::PlayerInvUpdate; + packet << clientID << player->inventory(); + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); + } else - client->tcpSocket->send(packet); + gkError() << ("Failed to send inv update for player " + std::to_string(clientID) + ": Player not found").c_str(); } void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client) const { @@ -78,6 +100,12 @@ void ServerCommandHandler::sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, m_server.sendToAllClients(packet); else client->tcpSocket->send(packet); + + // FIXME: sendPlayerChangeDimension shouldn't be exposed to Lua + // Instead, there should be a world.changePlayerDimension function that sends + // the packet above + the entities (instead of doing that here) + if (client) + m_worldController.getWorld(dimension).scene().sendEntities(*client); } void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client) const { @@ -90,6 +118,66 @@ void ServerCommandHandler::sendChatMessage(u16 clientID, const std::string &mess client->tcpSocket->send(packet); } +void ServerCommandHandler::sendEntitySpawn(entt::entity entityID, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntitySpawn << entityID; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityDespawn(entt::entity entityID, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityDespawn << entityID; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityPosition(entt::entity entityID, double x, double y, double z, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityPosition << entityID << x << y << z; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityRotation(entt::entity entityID, float w, float x, float y, float z, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityRotation << entityID << w << x << y << z; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityAnimation(entt::entity entityID, const AnimationComponent &animation, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityAnimation << entityID << animation; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + +void ServerCommandHandler::sendEntityDrawableDef(entt::entity entityID, const DrawableDef &drawableDef, const ClientInfo *client) const { + sf::Packet packet; + packet << Network::Command::EntityDrawableDef << entityID << drawableDef; + + if (!client) + m_server.sendToAllClients(packet); + else + client->tcpSocket->send(packet); +} + void ServerCommandHandler::setupCallbacks() { m_server.setConnectionCallback([this](ClientInfo &client) { sf::Packet packet; @@ -105,9 +193,7 @@ void ServerCommandHandler::setupCallbacks() { client.tcpSocket->send(spawnPacket); } - m_players.emplace(client.id, client); - - auto &player = m_players.at(client.id); + auto &player = m_players.addPlayer(client); player.setPosition(m_spawnPosition.x, m_spawnPosition.y, m_spawnPosition.z); // FIXME: Find a better way to give starting items @@ -115,7 +201,7 @@ void ServerCommandHandler::setupCallbacks() { sf::Packet invPacket; invPacket << Network::Command::PlayerInvUpdate << client.id; - invPacket << m_players.at(client.id).inventory(); + invPacket << player.inventory(); client.tcpSocket->send(invPacket); // Send spawn packet to all clients for this player @@ -123,12 +209,13 @@ void ServerCommandHandler::setupCallbacks() { spawnPacket << Network::Command::PlayerSpawn << client.id; spawnPacket << m_spawnPosition.x << m_spawnPosition.y << m_spawnPosition.z; m_server.sendToAllClients(spawnPacket); + + // Send entities to the client + m_worldController.getWorld(player.dimension()).scene().sendEntities(client); }); m_server.setCommandCallback(Network::Command::ClientDisconnect, [this](ClientInfo &client, sf::Packet &) { - auto it = m_players.find(client.id); - if (it != m_players.end()) - m_players.erase(it); + m_players.removePlayer(client.id); }); m_server.setCommandCallback(Network::Command::ChunkRequest, [this](ClientInfo &client, sf::Packet &packet) { @@ -141,9 +228,15 @@ void ServerCommandHandler::setupCallbacks() { m_server.setCommandCallback(Network::Command::PlayerInvUpdate, [this](ClientInfo &client, sf::Packet &packet) { u16 clientId; packet >> clientId; - if (clientId == client.id) { - packet >> m_players.at(client.id).inventory(); + + ServerPlayer *player = m_players.getPlayer(clientId); + if (player) { + if (clientId == client.id) { + packet >> player->inventory(); + } } + else + gkError() << ("Failed to update inventory of player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerPosUpdate, [this](ClientInfo &client, sf::Packet &packet) { @@ -152,34 +245,52 @@ void ServerCommandHandler::setupCallbacks() { packet >> clientId; packet >> x >> y >> z; - if (clientId == client.id) - m_players.at(client.id).setPosition(x, y, z); + ServerPlayer *player = m_players.getPlayer(clientId); + if (player) { + if (clientId == client.id) + player->setPosition(x, y, z); + } + else + gkError() << ("Failed to update position of player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerPlaceBlock, [this](ClientInfo &client, sf::Packet &packet) { - s32 x, y, z; - u32 block; - packet >> x >> y >> z >> block; + ServerPlayer *player = m_players.getPlayer(client.id); + if (player) { + s32 x, y, z; + u32 block; + packet >> x >> y >> z >> block; - ServerWorld &world = getWorldForClient(client.id); - world.setData(x, y, z, block >> 16); - world.setBlock(x, y, z, block & 0xffff); + ServerWorld &world = getWorldForClient(client.id); + world.setData(x, y, z, block >> 16); + world.setBlock(x, y, z, block & 0xffff); - m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockPlaced, glm::ivec3{x, y, z}, m_players.at(client.id), world, client, *this); + m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockPlaced, glm::ivec3{x, y, z}, *player, world, client, *this); - sf::Packet answer; - answer << Network::Command::BlockUpdate << x << y << z << block; - m_server.sendToAllClients(answer); + sf::Packet answer; + answer << Network::Command::BlockUpdate << x << y << z << block; + m_server.sendToAllClients(answer); + } + else + gkError() << ("Failed to place block using player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerDigBlock, [this](ClientInfo &client, sf::Packet &packet) { - s32 x, y, z; - packet >> x >> y >> z; - getWorldForClient(client.id).setBlock(x, y, z, 0); - - sf::Packet answer; - answer << Network::Command::BlockUpdate << x << y << z << u32(0); - m_server.sendToAllClients(answer); + ServerPlayer *player = m_players.getPlayer(client.id); + if (player) { + s32 x, y, z; + packet >> x >> y >> z; + + ServerWorld &world = getWorldForClient(client.id); + world.onBlockDigged(x, y, z, Registry::getInstance().getBlock(world.getBlock(x, y, z)), *player); + world.setBlock(x, y, z, 0); + + sf::Packet answer; + answer << Network::Command::BlockUpdate << x << y << z << u32(0); + m_server.sendToAllClients(answer); + } + else + gkError() << ("Failed to dig block using player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::PlayerInventory, [this](ClientInfo &client, sf::Packet &packet) { @@ -213,19 +324,24 @@ void ServerCommandHandler::setupCallbacks() { }); m_server.setCommandCallback(Network::Command::BlockActivated, [this](ClientInfo &client, sf::Packet &packet) { - s32 x, y, z; - u16 screenWidth, screenHeight; - u8 guiScale; - packet >> x >> y >> z >> screenWidth >> screenHeight >> guiScale; + ServerPlayer *player = m_players.getPlayer(client.id); + if (player) { + s32 x, y, z; + u16 screenWidth, screenHeight; + u8 guiScale; + packet >> x >> y >> z >> screenWidth >> screenHeight >> guiScale; - ServerWorld &world = getWorldForClient(client.id); + ServerWorld &world = getWorldForClient(client.id); - u16 id = world.getBlock(x, y, z); - ServerBlock &block = (ServerBlock &)(m_registry.getBlock(id)); - bool hasBeenActivated = block.onBlockActivated({x, y, z}, m_players.at(client.id), world, client, *this, screenWidth, screenHeight, guiScale); + u16 id = world.getBlock(x, y, z); + ServerBlock &block = (ServerBlock &)(m_registry.getBlock(id)); + bool hasBeenActivated = block.onBlockActivated({x, y, z}, *player, world, client, *this, screenWidth, screenHeight, guiScale); - if (hasBeenActivated) - m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockActivated, glm::ivec3{x, y, z}, block, m_players.at(client.id), world, client, *this); + if (hasBeenActivated) + m_scriptEngine.luaCore().onEvent(LuaEventType::OnBlockActivated, glm::ivec3{x, y, z}, block, *player, world, client, *this); + } + else + gkError() << ("Failed to activate block using player " + std::to_string(client.id) + ": Player not found").c_str(); }); m_server.setCommandCallback(Network::Command::BlockInvUpdate, [this](ClientInfo &client, sf::Packet &packet) { @@ -267,14 +383,18 @@ void ServerCommandHandler::setupCallbacks() { } void ServerCommandHandler::setPlayerPosition(u16 clientID, s32 x, s32 y, s32 z) { - m_players.at(clientID).setPosition(x, y, z); + ServerPlayer *player = m_players.getPlayer(clientID); + if (player) + player->setPosition(x, y, z); + else + gkError() << ("Failed to set position for player " + std::to_string(clientID) + ": Player not found").c_str(); } inline ServerWorld &ServerCommandHandler::getWorldForClient(u16 clientID) { - auto it = m_players.find(clientID); - if (it == m_players.end()) + ServerPlayer *player = m_players.getPlayer(clientID); + if (!player) throw EXCEPTION("Player instance not found for client", clientID); - return m_worldController.getWorld(it->second.dimension()); + return m_worldController.getWorld(player->dimension()); } diff --git a/source/server/network/ServerCommandHandler.hpp b/source/server/network/ServerCommandHandler.hpp index aff2807e1..f0ef794fd 100644 --- a/source/server/network/ServerCommandHandler.hpp +++ b/source/server/network/ServerCommandHandler.hpp @@ -31,12 +31,17 @@ #include +#include + #include "ChatCommandHandler.hpp" struct BlockData; +struct AnimationComponent; class ClientInfo; +class DrawableDef; class Inventory; +class PlayerList; class Registry; class ScriptEngine; class Server; @@ -46,14 +51,21 @@ class WorldController; class ServerCommandHandler { public: - ServerCommandHandler(ScriptEngine &scriptEngine, Server &server, WorldController &worldController, std::unordered_map &players, Registry ®istry) + ServerCommandHandler(ScriptEngine &scriptEngine, Server &server, WorldController &worldController, PlayerList &players, Registry ®istry) : m_scriptEngine(scriptEngine), m_server(server), m_worldController(worldController), m_players(players), m_registry(registry) {} void sendBlockDataUpdate(s32 x, s32 y, s32 z, const BlockData *blockData, const ClientInfo *client = nullptr) const; void sendBlockInvUpdate(s32 x, s32 y, s32 z, const Inventory &inventory, const ClientInfo *client = nullptr) const; void sendPlayerPosUpdate(u16 clientID, bool isTeleportation = false, const ClientInfo *client = nullptr) const; + void sendPlayerInvUpdate(u16 clientID, const ClientInfo *client = nullptr) const; void sendPlayerChangeDimension(u16 clientID, s32 x, s32 y, s32 z, u16 dimension, const ClientInfo *client = nullptr) const; void sendChatMessage(u16 clientID, const std::string &message, const ClientInfo *client = nullptr) const; + void sendEntitySpawn(entt::entity entityID, const ClientInfo *client = nullptr) const; + void sendEntityDespawn(entt::entity entityID, const ClientInfo *client = nullptr) const; + void sendEntityPosition(entt::entity entityID, double x, double y, double z, const ClientInfo *client = nullptr) const; + void sendEntityRotation(entt::entity entityID, float w, float x, float y, float z, const ClientInfo *client = nullptr) const; + void sendEntityAnimation(entt::entity entityID, const AnimationComponent &animation, const ClientInfo *client = nullptr) const; + void sendEntityDrawableDef(entt::entity entityID, const DrawableDef &drawableDef, const ClientInfo *client = nullptr) const; void setupCallbacks(); @@ -68,7 +80,7 @@ class ServerCommandHandler { Server &m_server; WorldController &m_worldController; - std::unordered_map &m_players; + PlayerList &m_players; Registry &m_registry; diff --git a/source/server/scene/ServerScene.cpp b/source/server/scene/ServerScene.cpp new file mode 100644 index 000000000..2abe93d15 --- /dev/null +++ b/source/server/scene/ServerScene.cpp @@ -0,0 +1,44 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "CollisionController.hpp" +#include "NetworkController.hpp" +#include "ServerScene.hpp" + +ServerScene::ServerScene(PlayerList &players) { + m_collisionController = static_cast(m_controllers.emplace_back(new CollisionController(players)).get()); + m_networkController = static_cast(m_controllers.emplace_back(new NetworkController).get()); +} + +void ServerScene::sendEntities(const ClientInfo &client) { + m_networkController->sendEntities(m_registry, client); +} + +void ServerScene::setServer(ServerCommandHandler *server) { + m_collisionController->setServer(server); + m_networkController->setServer(server); +} + diff --git a/source/server/scene/ServerScene.hpp b/source/server/scene/ServerScene.hpp new file mode 100644 index 000000000..c911dbe01 --- /dev/null +++ b/source/server/scene/ServerScene.hpp @@ -0,0 +1,51 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef SERVERSCENE_HPP_ +#define SERVERSCENE_HPP_ + +#include "Scene.hpp" + +class ClientInfo; +class CollisionController; +class NetworkController; +class PlayerList; +class ServerCommandHandler; + +class ServerScene : public Scene { + public: + ServerScene(PlayerList &players); + + void sendEntities(const ClientInfo &client); + + void setServer(ServerCommandHandler *server); + + private: + CollisionController *m_collisionController = nullptr; + NetworkController *m_networkController = nullptr; +}; + +#endif // SERVERSCENE_HPP_ diff --git a/source/server/scene/controller/CollisionController.cpp b/source/server/scene/controller/CollisionController.cpp new file mode 100644 index 000000000..f5373f06d --- /dev/null +++ b/source/server/scene/controller/CollisionController.cpp @@ -0,0 +1,51 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "CollisionController.hpp" +#include "ItemStack.hpp" +#include "NetworkComponent.hpp" +#include "PlayerList.hpp" +#include "PositionComponent.hpp" +#include "ServerCommandHandler.hpp" + +void CollisionController::update(entt::registry ®istry) { + // FIXME: This should be a callback in a CollisionComponent + registry.view().each([&](auto entity, auto &position, auto &box, auto &itemStack, auto &network) { + for (auto &it : m_players) { + if (it.second.dimension() == position.dimension) { + gk::DoubleBox hitbox = box + gk::Vector3d{position.x, position.y, position.z}; + gk::DoubleBox playerHitbox = it.second.hitbox() + gk::Vector3d{it.second.x(), it.second.y(), it.second.z()}; + if (hitbox.intersects(playerHitbox)) { + it.second.inventory().addStack(itemStack.item().stringID(), itemStack.amount()); + m_server->sendPlayerInvUpdate(it.second.clientID(), &it.second.client()); + m_server->sendEntityDespawn(network.entityID); + registry.destroy(entity); + } + } + } + }); +} + diff --git a/source/server/scene/controller/CollisionController.hpp b/source/server/scene/controller/CollisionController.hpp new file mode 100644 index 000000000..af9620a53 --- /dev/null +++ b/source/server/scene/controller/CollisionController.hpp @@ -0,0 +1,49 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef COLLISIONCONTROLLER_HPP_ +#define COLLISIONCONTROLLER_HPP_ + +#include "AbstractController.hpp" + +class PlayerList; +class ServerCommandHandler; + +class CollisionController : public AbstractController { + public: + CollisionController(PlayerList &players) : m_players(players) {} + + void update(entt::registry ®istry); + + void setServer(ServerCommandHandler *server) { m_server = server; } + + private: + ServerCommandHandler *m_server = nullptr; + + PlayerList &m_players; +}; + +#endif // COLLISIONCONTROLLER_HPP_ diff --git a/source/server/scene/controller/NetworkController.cpp b/source/server/scene/controller/NetworkController.cpp new file mode 100644 index 000000000..930b1a193 --- /dev/null +++ b/source/server/scene/controller/NetworkController.cpp @@ -0,0 +1,93 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "AnimationComponent.hpp" +#include "DrawableDef.hpp" +#include "NetworkComponent.hpp" +#include "NetworkController.hpp" +#include "PositionComponent.hpp" +#include "RotationComponent.hpp" +#include "ServerCommandHandler.hpp" + +void NetworkController::update(entt::registry ®istry) { + registry.view().each([this] (auto, auto &network) { + if (!network.hasSpawned) { + m_server->sendEntitySpawn(network.entityID); + network.hasSpawned = true; + } + }); + + registry.view().each([this] (auto, auto &network, auto &position) { + if (position.isUpdated) { + m_server->sendEntityPosition(network.entityID, position.x, position.y, position.z); + position.isUpdated = false; + } + }); + + registry.view().each([this] (auto, auto &network, auto &rotation) { + if (rotation.isUpdated) { + m_server->sendEntityRotation(network.entityID, rotation.quat.w, rotation.quat.x, rotation.quat.y, rotation.quat.z); + rotation.isUpdated = false; + } + }); + + registry.view().each([this] (auto, auto &network, auto &animation) { + if (animation.isUpdated) { + m_server->sendEntityAnimation(network.entityID, animation); + animation.isUpdated = false; + } + }); + + registry.view().each([this] (auto, auto &network, auto &drawableDef) { + if (drawableDef.isUpdated) { + m_server->sendEntityDrawableDef(network.entityID, drawableDef); + drawableDef.isUpdated = false; + } + }); +} + +void NetworkController::sendEntities(entt::registry ®istry, const ClientInfo &client) { + registry.view().each([&] (auto entity, auto &network) { + m_server->sendEntitySpawn(network.entityID, &client); + + if (auto *position = registry.try_get(entity) ; position) { + m_server->sendEntityPosition(network.entityID, position->x, position->y, position->z, &client); + } + + if (auto *rotation = registry.try_get(entity) ; rotation) { + m_server->sendEntityRotation(network.entityID, rotation->quat.w, rotation->quat.x, rotation->quat.y, rotation->quat.z, &client); + } + + if (auto *animation = registry.try_get(entity) ; animation) { + m_server->sendEntityAnimation(network.entityID, *animation, &client); + } + + if (auto *drawableDef = registry.try_get(entity) ; drawableDef) { + m_server->sendEntityDrawableDef(network.entityID, *drawableDef, &client); + } + }); +} + diff --git a/source/server/scene/controller/NetworkController.hpp b/source/server/scene/controller/NetworkController.hpp new file mode 100644 index 000000000..51572038c --- /dev/null +++ b/source/server/scene/controller/NetworkController.hpp @@ -0,0 +1,47 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef NETWORKCONTROLLER_HPP_ +#define NETWORKCONTROLLER_HPP_ + +#include "AbstractController.hpp" + +class ClientInfo; +class ServerCommandHandler; + +class NetworkController : public AbstractController { + public: + void update(entt::registry ®istry) override; + + void sendEntities(entt::registry ®istry, const ClientInfo &client); + + void setServer(ServerCommandHandler *server) { m_server = server; } + + private: + ServerCommandHandler *m_server = nullptr; +}; + +#endif // NETWORKCONTROLLER_HPP_ diff --git a/source/server/scene/factory/ItemDropFactory.cpp b/source/server/scene/factory/ItemDropFactory.cpp new file mode 100644 index 000000000..a38d5c40b --- /dev/null +++ b/source/server/scene/factory/ItemDropFactory.cpp @@ -0,0 +1,56 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#include "AnimationComponent.hpp" +#include "DrawableComponent.hpp" +#include "DrawableDef.hpp" +#include "ItemDropFactory.hpp" +#include "ItemStack.hpp" +#include "NetworkComponent.hpp" +#include "PositionComponent.hpp" +#include "Registry.hpp" +#include "RotationComponent.hpp" + +void ItemDropFactory::create(entt::registry ®istry, double x, double y, double z, u16 dimension, const std::string &itemID, u16 amount) { + auto entity = registry.create(); + registry.emplace(entity, x, y, z, dimension); + registry.emplace(entity); + registry.emplace(entity, entity); + + auto &drawableDef = registry.emplace(entity); + auto &cube = drawableDef.addInventoryCube(); + cube.size = 0.25f; + cube.origin = gk::Vector3f{cube.size / 2.f, cube.size / 2.f, cube.size / 2.f}; + cube.blockID = itemID; + + auto &animationComponent = registry.emplace(entity); + animationComponent.addRotation(0.f, 0.f, 1.f, 0.5f); + animationComponent.addTranslation(0.f, 0.f, -0.0005f, -0.2f, 0.f, true); + + registry.emplace(entity, 0., 0., 0., cube.size, cube.size, cube.size); + registry.emplace(entity, itemID, amount); +} + diff --git a/source/server/scene/factory/ItemDropFactory.hpp b/source/server/scene/factory/ItemDropFactory.hpp new file mode 100644 index 000000000..8816f87b9 --- /dev/null +++ b/source/server/scene/factory/ItemDropFactory.hpp @@ -0,0 +1,39 @@ +/* + * ===================================================================================== + * + * OpenMiner + * + * Copyright (C) 2018-2020 Unarelith, Quentin Bazin + * Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md) + * + * This file is part of OpenMiner. + * + * OpenMiner is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * OpenMiner is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with OpenMiner; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * ===================================================================================== + */ +#ifndef ITEMDROPFACTORY_HPP_ +#define ITEMDROPFACTORY_HPP_ + +#include + +#include + +class ItemDropFactory { + public: + static void create(entt::registry ®istry, double x, double y, double z, u16 dimension, const std::string &itemID, u16 amount = 1); +}; + +#endif // ITEMDROPFACTORY_HPP_ diff --git a/source/server/world/ServerWorld.cpp b/source/server/world/ServerWorld.cpp index d65b39ed0..850f27bbc 100644 --- a/source/server/world/ServerWorld.cpp +++ b/source/server/world/ServerWorld.cpp @@ -26,10 +26,13 @@ */ #include +#include "Dimension.hpp" #include "EngineConfig.hpp" +#include "ItemDropFactory.hpp" #include "Network.hpp" #include "Server.hpp" #include "ServerCommandHandler.hpp" +#include "ServerConfig.hpp" #include "ServerPlayer.hpp" #include "ServerWorld.hpp" @@ -51,6 +54,8 @@ void ServerWorld::update() { } } } + + m_scene.update(); } void ServerWorld::createChunkNeighbours(ServerChunk &chunk) { @@ -146,6 +151,16 @@ void ServerWorld::sendRequestedData(ClientInfo &client, int cx, int cy, int cz) sendChunkData(client, chunk); } +void ServerWorld::onBlockDigged(int x, int y, int z, const Block &block, ServerPlayer &player) { + if (ServerConfig::useItemDrops) { + ItemDropFactory::create(m_scene.registry(), x + 0.5, y + 0.5, z + 0.5, m_dimension.id(), block.getItemDrop().item().stringID(), block.getItemDrop().amount()); + } + else { + player.inventory().addStack(block.getItemDrop().item().stringID(), block.getItemDrop().amount()); + m_server->sendPlayerInvUpdate(player.clientID(), &player.client()); + } +} + ServerChunk &ServerWorld::createChunk(s32 cx, s32 cy, s32 cz) { ServerChunk *chunk = (ServerChunk *)getChunk(cx, cy, cz); if (!chunk) { diff --git a/source/server/world/ServerWorld.hpp b/source/server/world/ServerWorld.hpp index a24adb2e1..6409aa685 100644 --- a/source/server/world/ServerWorld.hpp +++ b/source/server/world/ServerWorld.hpp @@ -30,6 +30,7 @@ #include #include "ServerChunk.hpp" +#include "ServerScene.hpp" #include "TerrainGenerator.hpp" #include "World.hpp" @@ -39,6 +40,7 @@ namespace gk { class ClientInfo; class Dimension; +class PlayerList; class ServerCommandHandler; class ServerPlayer; @@ -46,8 +48,8 @@ class ServerWorld : public World { using ChunkMap = std::unordered_map>; public: - ServerWorld(const Dimension &dimension, gk::GameClock &clock) - : m_dimension(dimension), m_terrainGenerator(dimension), m_clock(clock) {} + ServerWorld(PlayerList &players, const Dimension &dimension, gk::GameClock &clock) + : m_dimension(dimension), m_terrainGenerator(dimension), m_clock(clock), m_scene(players) {} void update(); @@ -55,6 +57,8 @@ class ServerWorld : public World { void sendChunkData(const ClientInfo &client, ServerChunk &chunk); void sendRequestedData(ClientInfo &client, s32 cx, s32 cy, s32 cz); + void onBlockDigged(int x, int y, int z, const Block &block, ServerPlayer &player); + ServerChunk &createChunk(s32 cx, s32 cy, s32 cz); Chunk *getChunk(int cx, int cy, int cz) const override; @@ -65,7 +69,9 @@ class ServerWorld : public World { TerrainGenerator &terrainGenerator() { return m_terrainGenerator; } - void setServer(ServerCommandHandler *server) { m_server = server; } + ServerScene &scene() { return m_scene; } + + void setServer(ServerCommandHandler *server) { m_server = server; m_scene.setServer(server); } private: const Dimension &m_dimension; @@ -79,6 +85,8 @@ class ServerWorld : public World { ServerCommandHandler *m_server = nullptr; gk::GameClock &m_clock; + + ServerScene m_scene; }; #endif // SERVERWORLD_HPP_ diff --git a/source/server/world/WorldController.cpp b/source/server/world/WorldController.cpp index 73170b2f5..ec5e8229d 100644 --- a/source/server/world/WorldController.cpp +++ b/source/server/world/WorldController.cpp @@ -29,9 +29,9 @@ #include "Registry.hpp" #include "WorldController.hpp" -void WorldController::init() { +void WorldController::init(PlayerList &players) { for (const Dimension &dimension : m_registry.dimensions()) { - m_worldList.emplace_back(dimension, m_clock); + m_worldList.emplace_back(players, dimension, m_clock); m_worldList.back().setServer(m_server); } } diff --git a/source/server/world/WorldController.hpp b/source/server/world/WorldController.hpp index e691b1f80..59538a911 100644 --- a/source/server/world/WorldController.hpp +++ b/source/server/world/WorldController.hpp @@ -41,7 +41,7 @@ class WorldController { public: WorldController(Registry ®istry, gk::GameClock &clock) : m_registry(registry), m_clock(clock) {} - void init(); + void init(PlayerList &players); void update(); @@ -53,7 +53,7 @@ class WorldController { void setServer(ServerCommandHandler &server) { m_server = &server; } private: - std::vector m_worldList; + std::deque m_worldList; Registry &m_registry;