diff --git a/.gitignore b/.gitignore index 32c07e5..4ab21d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ .kdev4 assets-orig build +build-* *.fossil *.project *.kdev4 +*.user TODO.txt diggler.cfg -.kdev_include_paths \ No newline at end of file +.kdev_include_paths +_* diff --git a/.gitmodules b/.gitmodules index d01fe81..e272b55 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "ext/StackTracePlus"] path = ext/StackTracePlus url = https://github.com/ignacio/StackTracePlus.git +[submodule "ext/Optional"] + path = ext/Optional + url = https://github.com/akrzemi1/Optional.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ce53f90..67dfd35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,22 @@ cmake_minimum_required(VERSION 2.8) project(Diggler C CXX) +set(CMAKE_LEGACY_CYGWIN_WIN32 0) + +if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}") + # Forbid in-tree builds. With CMake, doing an in-tree build breaks out-of-tree ones + # when generating from .in files into the source directory, overriding the path of out-of-tree + # generated ones. + # TL;DR: in-tree build = BAD IDEA. + file(REMOVE_RECURSE CMakeFiles CMakeCache.txt) + message(FATAL_ERROR + "You are doing an in-tree build (i.e. the source and binary directories are the same).\ +In-tree builds aren't supported and will *never* be.\ +Make a \"build\" directory and run cmake here. +\ +Don't forget to remove CMakeFiles and CMakeCache.txt.") +endif() + ### Find path to build dir, relative if possible, to symlink resources directories file(RELATIVE_PATH REL_BUILD_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) string(FIND "${REL_BUILD_PATH}" ".." RBP_2DOTPOS) @@ -43,13 +59,13 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) find_package(CXX14) if (NOT CXX14_FOUND) - message(FATAL_ERROR "C++14 feature support unavailable. -Please use the latest version of your compiler. - -Here are the minimum requirements: -- GCC version 5 (on Debian Jessie, enable Stretch/testing repos and get GCC from there) -- clang version 3.4 -- MSVC: wait until Microsoft goes bankrupt, and use another compiler in the meantime + message(FATAL_ERROR "C++14 feature support unavailable.\ +Please use the latest version of your compiler.\ +\ +Here are the minimum requirements:\ +- GCC version 5 (on Debian Jessie, enable Stretch/testing repos and get GCC from there)\ +- clang version 3.4\ +- MSVC: wait until Microsoft goes bankrupt, and use another compiler in the meantime\ - ICC: ICC 17.0 Beta doesn't yet support all required C++14 features") endif() set(CMAKE_CXX_FLAGS "${CXX14_FLAGS} ${CMAKE_CXX_FLAGS}") @@ -71,6 +87,8 @@ endif() pkg_search_module(EPOXY REQUIRED epoxy) pkg_search_module(GLFW REQUIRED glfw3) +pkg_search_module(SQLITE3 REQUIRED sqlite3) +pkg_search_module(LIBSODIUM REQUIRED libsodium) include_directories( ${ETC_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} @@ -78,6 +96,8 @@ include_directories( ${ENET_INCLUDE_DIRS} ${EPOXY_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS} + ${LIBSODIUM_INCLUDE_DIRS} + ${SQLITE3_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} ${MSGPACK_INCLUDE_DIR} ) @@ -94,6 +114,8 @@ target_link_libraries(diggler ${ENET_LIBRARIES} ${EPOXY_LIBRARIES} ${GLFW_LIBRARIES} + ${LIBSODIUM_LIBRARIES} + ${SQLITE3_LIBRARIES} ${OPENAL_LIBRARY} pthread) diff --git a/README.md b/README.md index f917054..502a67b 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,5 @@ You should already have OpenGL available thanks to Mesa or proprietary drivers. Arch: `pacman -S glm openal libepoxy glfw luajit` Debian (Jessie **w/ testing**, or up) & derivatives: `apt-get install libglm-dev libopenal-dev libepoxy-dev libglfw3-dev libx{i,randr}-dev libluajit-5.1-dev` + +Fedora (tested on F24): `dnf install glm-devel openal-soft-devel libepoxy-devel glfw-devel luajit-devel` diff --git a/cmake/FindCXX14.cmake b/cmake/FindCXX14.cmake index a81e43e..b92d823 100644 --- a/cmake/FindCXX14.cmake +++ b/cmake/FindCXX14.cmake @@ -12,11 +12,11 @@ include(CheckCXXSourceCompiles) include(FindPackageHandleStandardArgs) set(CXX14_FLAG_CANDIDATES - #Gnu and Intel Linux - "-std=c++14" - #Microsoft Visual Studio, and everything that automatically accepts C++14 + # GCC 6+ and everything that automatically accepts C++14 " " - #Intel windows + # GCC < 6 and Intel Linux + "-std=c++14" + # Intel Windows "/Qstd=c++14" ) @@ -24,6 +24,9 @@ set(CXX14_TEST_SOURCE " [[deprecated]] void unused() {} +template +constexpr T pi = T(3.1415926535897932385); + constexpr int numberwang(int n) { if (n < 0) { return 0b1'1001'0101; @@ -31,7 +34,7 @@ constexpr int numberwang(int n) { return n - 1; } -auto lambda = [](auto a, auto b) { return a * b; }; +auto lambda = [v = 0](auto a, auto b) { return v + a * b; }; auto square(int n) { return lambda(n, n) * numberwang(2); @@ -39,6 +42,7 @@ auto square(int n) { int main() { int s = square(3); + double pie = pi; return 0; } ") @@ -47,7 +51,7 @@ foreach(FLAG ${CXX14_FLAG_CANDIDATES}) set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") set(CMAKE_REQUIRED_FLAGS "${FLAG}") unset(CXX14_FLAG_DETECTED CACHE) - message(STATUS "Try C++14 flag = [${FLAG}]") + message(STATUS "Trying C++14 flag '${FLAG}'") check_cxx_source_compiles("${CXX14_TEST_SOURCE}" CXX14_FLAG_DETECTED) set(CMAKE_REQUIRED_FLAGS "${SAFE_CMAKE_REQUIRED_FLAGS}") if(CXX14_FLAG_DETECTED) diff --git a/ext/Optional b/ext/Optional new file mode 160000 index 0000000..3922965 --- /dev/null +++ b/ext/Optional @@ -0,0 +1 @@ +Subproject commit 3922965396fc455c6b1770374b9b4111799588a9 diff --git a/mods/TestMod/blocks.lua b/mods/TestMod/blocks.lua index 6e20d5d..881160d 100644 --- a/mods/TestMod/blocks.lua +++ b/mods/TestMod/blocks.lua @@ -5,7 +5,7 @@ local grassBlock = { variabilty = {'static'}, textures = { grass = { - path = 'tex/grass.png', + path = 'self:/tex/grass.png', ['repeat'] = { xdiv = 8, ydiv = 8 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 00e03fb..51a833d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ set(SRCS ${CSD}/Chunk.cpp ${CSD}/Clouds.cpp ${CSD}/Config.cpp + ${CSD}/content/AssetManager.cpp + ${CSD}/content/ModManager.cpp ${CSD}/content/Registry.cpp ${CSD}/EscMenu.cpp ${CSD}/Frustum.cpp @@ -34,6 +36,8 @@ set(SRCS ${CSD}/network/msgtypes/BlockUpdate.cpp ${CSD}/network/msgtypes/Chat.cpp ${CSD}/network/msgtypes/ChunkTransfer.cpp + ${CSD}/network/msgtypes/ConnectionParam.cpp + ${CSD}/network/msgtypes/PlayerUpdate.cpp ${CSD}/network/msgtypes/ServerInfo.cpp ${CSD}/network/ClientMessageHandler.cpp ${CSD}/network/NetHelper.cpp diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 9622b05..8dee2c2 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -92,7 +92,7 @@ Chunk::Chunk(Game *G, WorldRef W, int X, int Y, int Z) : calcMemUsage(); if (GlobalProperties::IsClient) { - G->R->WR->registerChunk(this); + G->R->renderers.world->registerChunk(this); } } @@ -155,7 +155,7 @@ Chunk::~Chunk() { std::free(imcData); #endif if (GlobalProperties::IsClient) { - G->R->WR->unregisterChunk(this); + G->R->renderers.world->unregisterChunk(this); } // getDebugStream() << W->id << '.' << wcx << ',' << wcy << ',' << wcz << " destruct" << std::endl; } @@ -442,7 +442,7 @@ void Chunk::updateClient() { } } - G->R->WR->updateChunk(this, vertex, v, idxOpaque, io, idxTransp, it); + G->R->renderers.world->updateChunk(this, vertex, v, idxOpaque, io, idxTransp, it); dirty = false; mut.unlock(); } diff --git a/src/Game.cpp b/src/Game.cpp index 6d1937e..60a2cf4 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,6 +1,8 @@ #include "Game.hpp" #include "Audio.hpp" +#include "content/AssetManager.hpp" +#include "content/ModManager.hpp" #include "content/Registry.hpp" #include "GlobalProperties.hpp" #include "KeyBinds.hpp" @@ -20,42 +22,67 @@ Game::Game() : void Game::init() { CR = new Content::Registry; + AM = std::make_unique(this); + MM = std::make_unique(this); LS = new Scripting::Lua::State(this); if (GlobalProperties::IsClient) { - PM = new ProgramManager(*this); - LP = new LocalPlayer(this); - RP = new RenderProperties; { // TODO move somewhere else? - RP->bloom = true; - RP->wavingLiquids = !true; - RP->fogStart = 16; - RP->fogEnd = 24; - } - R = new Render::gl::GLRenderer(this); - A = new Audio(*this); - KB = new KeyBinds; - PlayerPosUpdateFreq = 4; + initClient(); } if (GlobalProperties::IsServer) { + initServer(); } } -Game::~Game() { - delete CR; +void Game::initClient() { + PM = new ProgramManager(*this); + LP = new LocalPlayer(this); + RP = new RenderProperties; { // TODO move somewhere else? + RP->bloom = true; + RP->wavingLiquids = !true; + RP->fogStart = 16; + RP->fogEnd = 24; + } + R = new Render::gl::GLRenderer(this); + A = new Audio(*this); + KB = new KeyBinds; + PlayerPosUpdateFreq = 4; +} + +void Game::initServer() { +} + +void Game::finalize() { if (GlobalProperties::IsClient) { - delete KB; - delete A; - delete R; - delete RP; - delete LP; - delete PM; + finalizeClient(); } if (GlobalProperties::IsServer) { + finalizeServer(); } + delete LS; LS = nullptr; + MM.reset(); + AM.reset(); + delete CR; CR = nullptr; +} + +void Game::finalizeClient() { + delete KB; KB = nullptr; + delete A; A = nullptr; + delete R; R = nullptr; + delete RP; RP = nullptr; + delete LP; LP = nullptr; + delete PM; PM = nullptr; +} + +void Game::finalizeServer() { +} + +Game::~Game() { + finalize(); } void Game::updateTime(double time) { Time = time; - TimeMs = (int64)(time * 1000); + TimeMs = static_cast(time * 1000); } } diff --git a/src/Game.hpp b/src/Game.hpp index bb51aaa..080ebdf 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -1,6 +1,8 @@ -#ifndef GAME_HPP -#define GAME_HPP +#ifndef DIGGLER_GAME_HPP +#define DIGGLER_GAME_HPP + #include + #include "ui/FontManager.hpp" #include "Universe.hpp" #include "PlayerList.hpp" @@ -12,6 +14,8 @@ using std::shared_ptr; namespace Diggler { namespace Content { +class AssetManager; +class ModManager; class Registry; } @@ -35,7 +39,11 @@ class GameWindow; class KeyBinds; class Server; -class Game { +class Game final { +private: + template + using ptr = std::unique_ptr; + public: // Shared double Time; uint64 TimeMs; @@ -43,6 +51,8 @@ public: Universe *U; PlayerList players; Content::Registry *CR; + ptr AM; + ptr MM; Scripting::Lua::State *LS; // Server @@ -61,16 +71,22 @@ public: float fogStart, fogEnd; } *RP; Audio *A; - Net::Peer NS; + Net::Peer *NS; KeyBinds *KB; int PlayerPosUpdateFreq; Game(); void init(); + void initClient(); + void initServer(); + void finalize(); + void finalizeClient(); + void finalizeServer(); + void updateTime(double time); ~Game(); }; } -#endif +#endif /* DIGGLER_GAME_HPP */ diff --git a/src/GameState.cpp b/src/GameState.cpp index fe1f332..2a3cec9 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -26,6 +26,7 @@ #include "network/NetHelper.hpp" #include "Particles.hpp" +#include "network/msgtypes/PlayerUpdate.hpp" #include "network/msgtypes/BlockUpdate.hpp" #include "content/Registry.hpp" @@ -95,6 +96,10 @@ GameState::GameState(GameWindow *GW, const std::string &servHost, int servPort) m_highlightBox.att_coord = m_highlightBox.program->att("coord"); m_highlightBox.uni_unicolor = m_highlightBox.program->uni("unicolor"); m_highlightBox.uni_mvp = m_highlightBox.program->uni("mvp"); + { Render::gl::VAO::Config cfg = m_highlightBox.vao.configure(); + cfg.vertexAttrib(m_highlightBox.vbo, m_highlightBox.att_coord, 3, GL_FLOAT, 0); + cfg.commit(); + } m_3dFbo = new Render::gl::FBO(w, h, Texture::PixelFormat::RGB, true); m_3dRenderVBO = new Render::gl::VBO(); @@ -388,7 +393,7 @@ void GameState::updateViewport() { } void GameState::sendMsg(Net::OutMessage &msg, Net::Tfer mode, Net::Channels chan) { - G->H.send(G->NS, msg, mode, chan); + G->H.send(*G->NS, msg, mode, chan); } void GameState::run() { @@ -402,13 +407,15 @@ bool GameState::connectLoop() { std::string &serverHost = m_serverHost; int serverPort = m_serverPort; bool finished = false, success = false; Game *G = this->G; - m_networkThread = std::thread([G, &success, &finished, &serverHost, serverPort]() { + std::string failureStr; + m_networkThread = std::thread([G, &success, &finished, &serverHost, serverPort, &failureStr]() { try { G->H.create(); - G->NS = G->H.connect(serverHost, serverPort, 5000); + G->NS = &G->H.connect(serverHost, serverPort, 5000); success = true; } catch (const Net::Exception &e) { success = false; + failureStr = e.what(); } finished = true; }); @@ -447,9 +454,7 @@ bool GameState::connectLoop() { if (GW->shouldClose()) return true; if (!success) { - std::ostringstream oss; - oss << serverHost << ':' << serverPort << " did not respond"; - GW->showMessage("Could not connect to server", oss.str()); + GW->showMessage("Could not connect to server", failureStr); return true; } @@ -469,7 +474,7 @@ bool GameState::connectLoop() { switch (m_msg.getType()) { case Net::MessageType::PlayerJoin: { G->U = new Universe(G, true); - LP.id = m_msg.readU32(); + LP.sessId = m_msg.readU32(); LP.W = G->U->createWorld(m_msg.readI16()); } break; case Net::MessageType::PlayerQuit: { @@ -488,7 +493,7 @@ bool GameState::connectLoop() { G->LS->setGameLuaRuntimePath(gameLuaRuntimePath); G->LS->dofile(gameLuaRuntimePath + "/Diggler.lua"); - getDebugStream() << "Joined as " << LP.name << '/' << LP.id << std::endl; + getDebugStream() << "Joined as " << LP.name << '/' << LP.sessId << std::endl; return false; } @@ -520,11 +525,14 @@ void GameState::gameLoop() { if (G->LP->isAlive) { if (T > nextNetUpdate) { - Net::OutMessage msg(Net::MessageType::PlayerUpdate, Net::PlayerUpdateType::Move); - msg.writeVec3(LP->position); - msg.writeVec3(LP->velocity); - msg.writeVec3(LP->accel); - msg.writeFloat(LP->angle); + Net::MsgTypes::PlayerUpdateMove pum; + pum.position = LP->position; + if (LP->velocity != glm::vec3()) { + pum.velocity = LP->velocity; + pum.accel = LP->accel; + } + pum.angle = LP->angle; + Net::OutMessage msg; pum.writeToMsg(msg); sendMsg(msg, Net::Tfer::Unrel, Net::Channels::Movement); nextNetUpdate = T+1.0/G->PlayerPosUpdateFreq; } @@ -560,7 +568,7 @@ void GameState::gameLoop() { rp.world = WR.get(); rp.transform = m_transform; rp.frustum = G->LP->camera.frustum; - G->R->WR->render(rp); + G->R->renderers.world->render(rp); for (Player &p : G->players) { p.update(deltaT); if (G->LP->camera.frustum.sphereInFrustum(p.position, 2)) @@ -591,16 +599,14 @@ void GameState::gameLoop() { // TODO: replace harcoded 32 viewdistance if (G->LP->raytracePointed(32, &m_pointedBlock, &m_pointedFacing)) { m_highlightBox.program->bind(); - glEnableVertexAttribArray(m_highlightBox.att_coord); - m_highlightBox.vbo.bind(); - glVertexAttribPointer(m_highlightBox.att_coord, 3, GL_FLOAT, GL_FALSE, 0, 0); + m_highlightBox.vao.bind(); glUniform4f(m_highlightBox.uni_unicolor, 1.f, 1.f, 1.f, .1f); glUniformMatrix4fv(m_highlightBox.uni_mvp, 1, GL_FALSE, glm::value_ptr( glm::scale(glm::translate(m_transform, glm::vec3(m_pointedBlock)+glm::vec3(.5f)), glm::vec3(0.5f*1.03f)))); glDrawArrays(GL_TRIANGLES, 0, 6*2*3); - glDisableVertexAttribArray(m_highlightBox.att_coord); + m_highlightBox.vao.unbind(); } glDisable(GL_CULL_FACE); @@ -622,7 +628,7 @@ void GameState::gameLoop() { glEnableVertexAttribArray(bloom.extractor.att_texcoord); m_3dRenderVBO->bind(); glUniformMatrix4fv(bloom.extractor.uni_mvp, 1, GL_FALSE, glm::value_ptr(*G->UIM->PM1)); - glVertexAttribPointer(bloom.extractor.att_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0); + glVertexAttribPointer(bloom.extractor.att_coord, 2, GL_SHORT, GL_FALSE, sizeof(Coord2DTex), 0); glVertexAttribPointer(bloom.extractor.att_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u)); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableVertexAttribArray(bloom.extractor.att_texcoord); @@ -639,7 +645,7 @@ void GameState::gameLoop() { m_3dRenderVBO->bind(); glUniformMatrix4fv(bloom.renderer.uni_mvp, 1, GL_FALSE, glm::value_ptr(*G->UIM->PM1)); glUniform2f(bloom.renderer.uni_pixshift, 1.f/(GW->getW()/bloom.scale), 1.f/(GW->getH()/bloom.scale)); - glVertexAttribPointer(bloom.renderer.att_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0); + glVertexAttribPointer(bloom.renderer.att_coord, 2, GL_SHORT, GL_FALSE, sizeof(Coord2DTex), 0); glVertexAttribPointer(bloom.renderer.att_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u)); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableVertexAttribArray(bloom.renderer.att_texcoord); @@ -656,7 +662,7 @@ void GameState::gameLoop() { glEnableVertexAttribArray(bloom.renderer.att_texcoord); m_3dRenderVBO->bind(); glUniformMatrix4fv(bloom.renderer.uni_mvp, 1, GL_FALSE, glm::value_ptr(*G->UIM->PM1)); - glVertexAttribPointer(bloom.renderer.att_coord, 2, GL_INT, GL_FALSE, sizeof(Coord2DTex), 0); + glVertexAttribPointer(bloom.renderer.att_coord, 2, GL_SHORT, GL_FALSE, sizeof(Coord2DTex), 0); glVertexAttribPointer(bloom.renderer.att_texcoord, 2, GL_BYTE, GL_FALSE, sizeof(Coord2DTex), (GLvoid*)offsetof(Coord2DTex, u)); glDrawArrays(GL_TRIANGLES, 0, 6); glDisableVertexAttribArray(bloom.renderer.att_texcoord); @@ -675,8 +681,9 @@ void GameState::gameLoop() { } if (!G->LP->deathSent) { G->LP->deathSent = true; - Net::OutMessage out(Net::MessageType::PlayerUpdate, Net::PlayerUpdateType::Die); - out.writeU8((uint8)G->LP->deathReason); + Net::MsgTypes::PlayerUpdateDie pud; + pud.reason = G->LP->deathReason; + Net::OutMessage out; pud.writeToMsg(out); sendMsg(out, Net::Tfer::Rel, Net::Channels::Life); } renderDeathScreen(); diff --git a/src/GameState.hpp b/src/GameState.hpp index 6b509f3..98f5722 100644 --- a/src/GameState.hpp +++ b/src/GameState.hpp @@ -13,6 +13,7 @@ #include "GameWindow.hpp" #include "render/gl/FBO.hpp" #include "render/gl/VBO.hpp" +#include "render/gl/VAO.hpp" #include "content/Content.hpp" #include "network/Network.hpp" #include "network/ClientMessageHandler.hpp" @@ -69,7 +70,7 @@ private: } bloom; Render::gl::VBO *m_3dRenderVBO; - struct Coord2DTex { int x, y; uint8 u, v; }; + struct Coord2DTex { int16 x, y; uint8 u, v; }; Clouds *m_clouds; Skybox *m_sky; @@ -80,6 +81,7 @@ private: struct { Render::gl::VBO vbo; + Render::gl::VAO vao; const Program *program; GLuint att_coord, uni_unicolor, uni_mvp; } m_highlightBox; diff --git a/src/GameWindow.cpp b/src/GameWindow.cpp index 8f0ab39..e6e79be 100644 --- a/src/GameWindow.cpp +++ b/src/GameWindow.cpp @@ -116,7 +116,7 @@ GameWindow::GameWindow(Game *G) : G(G) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); glfwWindowHint(GLFW_SAMPLES, 0); // Gimme aliasing everywhere //glfwWindowHint(GLFW_STENCIL_BITS, 8); @@ -190,6 +190,8 @@ GameWindow::GameWindow(Game *G) : G(G) { } GameWindow::~GameWindow() { + m_currentState.reset(); + m_nextState.reset(); delete UIM; glfwDestroyWindow(m_window); @@ -243,13 +245,13 @@ void GameWindow::cbResize(int w, int h) { m_currentState->onResize(w, h); } -void GameWindow::setNextState(const shared_ptr next) { - m_nextState = next; +void GameWindow::setNextState(std::unique_ptr &&next) { + m_nextState = std::move(next); } void GameWindow::run() { while (m_nextState != nullptr && !glfwWindowShouldClose(m_window)) { - m_currentState = m_nextState; + m_currentState = std::move(m_nextState); m_nextState = nullptr; UIM->clear(); m_currentState->run(); @@ -257,7 +259,7 @@ void GameWindow::run() { } void GameWindow::showMessage(const std::string &msg, const std::string &submsg) { - setNextState(std::make_shared(this, msg, submsg)); + setNextState(std::move(std::make_unique(this, msg, submsg))); } } diff --git a/src/GameWindow.hpp b/src/GameWindow.hpp index 28cd21a..780f6b7 100644 --- a/src/GameWindow.hpp +++ b/src/GameWindow.hpp @@ -8,8 +8,6 @@ #include "Platform.hpp" #include "ui/Manager.hpp" -using std::shared_ptr; - namespace Diggler { class Game; @@ -22,7 +20,7 @@ private: GLFWwindow *m_window; int m_w, m_h; - shared_ptr m_currentState, m_nextState; + std::unique_ptr m_currentState, m_nextState; public: UI::Manager *UIM; @@ -52,7 +50,7 @@ public: void updateViewport(); - void setNextState(const shared_ptr next); + void setNextState(std::unique_ptr &&next); void run(); void showMessage(const std::string &msg, const std::string &submsg = ""); diff --git a/src/LocalPlayer.cpp b/src/LocalPlayer.cpp index 079aeaf..3bec661 100644 --- a/src/LocalPlayer.cpp +++ b/src/LocalPlayer.cpp @@ -12,6 +12,7 @@ #include "Audio.hpp" #include "Game.hpp" #include "network/NetHelper.hpp" +#include "render/gl/VAO.hpp" namespace Diggler { @@ -174,14 +175,11 @@ void LocalPlayer::update(float delta) { } void LocalPlayer::render(const glm::mat4 &transform) const { - const Program *P = G->PM->getProgram(PM_3D | PM_COLORED); - P->bind(); - glEnableVertexAttribArray(P->att("coord")); - glEnableVertexAttribArray(P->att("color")); - glUniformMatrix4fv(P->uni("mvp"), 1, GL_FALSE, glm::value_ptr(transform)); static Render::gl::VBO vbo; - const glm::ivec3 &min = aabbmin, &max = aabbmax; - struct Coord { int x, y, z; uint8 r, g, b; } pts[] = { + static Render::gl::VAO vao; + static bool vaoConfigured = false; + const glm::f32vec3 min = aabbmin, max = aabbmax; + struct Coord { float x, y, z; uint8 r, g, b; } pts[] = { { min.x, min.y, min.z, 0, 1, 0 }, { max.x, min.y, min.z, 0, 1, 0 }, { min.x, min.y, min.z, 0, 1, 0 }, @@ -209,12 +207,19 @@ void LocalPlayer::render(const glm::mat4 &transform) const { { min.x, min.y, max.z, 0, 1, 0 }, }; vbo.setDataKeepSize(pts, sizeof(pts)/sizeof(Coord), GL_STREAM_DRAW); - vbo.bind(); - glVertexAttribPointer(P->att("coord"), 3, GL_INT, GL_FALSE, sizeof(Coord), 0); - glVertexAttribPointer(P->att("color"), 3, GL_UNSIGNED_BYTE, GL_FALSE, sizeof(Coord), (GLvoid*)(offsetof(Coord, r))); + const Program &P = *G->PM->getProgram(PM_3D | PM_COLORED); + if (!vaoConfigured) { + vaoConfigured = true; + Render::gl::VAO::Config cfg = vao.configure(); + cfg.vertexAttrib(vbo, P.att("coord"), 3, GL_FLOAT, sizeof(Coord), 0); + cfg.vertexAttrib(vbo, P.att("color"), 3, GL_UNSIGNED_BYTE, sizeof(Coord), offsetof(Coord, r)); + cfg.commit(); + } + P.bind(); + glUniformMatrix4fv(P.uni("mvp"), 1, GL_FALSE, glm::value_ptr(transform)); + vao.bind(); glDrawArrays(GL_LINES, 0, sizeof(pts)/sizeof(Coord)); - glDisableVertexAttribArray(P->att("color")); - glDisableVertexAttribArray(P->att("coord")); + vao.unbind(); } void LocalPlayer::forceCameraUpdate() { diff --git a/src/Particles.cpp b/src/Particles.cpp index 182e22d..23788a2 100644 --- a/src/Particles.cpp +++ b/src/Particles.cpp @@ -8,11 +8,11 @@ namespace Diggler { ParticleEmitter::ParticleEmitter(Game *G) : G(G) { - G->R->PR->registerEmitter(this); + G->R->renderers.particles->registerEmitter(this); } ParticleEmitter::~ParticleEmitter() { - G->R->PR->unregisterEmitter(this); + G->R->renderers.particles->unregisterEmitter(this); } void ParticleEmitter::setMaxCount(uint count) { @@ -35,7 +35,7 @@ void ParticleEmitter::emit(Particle &p) { void ParticleEmitter::update(double delta) { ParticleRenderData *data = new ParticleRenderData[maxCount]; float deltaF = delta; - for (int i=0; i < maxCount; ++i) { + for (uint i = 0; i < maxCount; ++i) { Particle &p = particles[i]; p.vel += p.accel * deltaF; p.pos += p.vel * deltaF; @@ -44,7 +44,7 @@ void ParticleEmitter::update(double delta) { emit(p); data[i] = { p.pos.x, p.pos.y, p.pos.z, p.color.r, p.color.g, p.color.b, p.color.a, p.size }; } - G->R->PR->updateParticleData(this, data, maxCount); + G->R->renderers.particles->updateParticleData(this, data, maxCount); delete[] data; } diff --git a/src/Player.cpp b/src/Player.cpp index cbc1dbd..d4862a2 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -8,11 +8,14 @@ namespace Diggler { -Player::Renderer Player::R = {0}; +Player::Renderer Player::R = {}; Player::Player(Game *G) : - G(G), position(0), velocity(0), accel(0), angle(0), toolUseTime(0), - isAlive(true) { + G(G), + angle(0), + toolUseTime(0), + isAlive(true), + peer(nullptr) { if (GlobalProperties::IsClient) { if (R.prog == nullptr) { R.prog = G->PM->getProgram(PM_3D | PM_FOG); @@ -31,34 +34,12 @@ Player::Player(Game *G) : -sz, .0f, 0.0f, sz, szH, 0.0f, }; - R.vbo = new Render::gl::VBO; + R.vbo = std::make_unique(); R.vbo->setData(coords, 6*3); } } } -using std::swap; -Player::Player(Player &&p) { - *this = std::move(p); -} - -Player& Player::operator=(Player &&p) { - swap(direction, p.direction); - swap(G, p.G); - swap(position, p.position); - swap(velocity, p.velocity); - swap(accel, p.accel); - swap(name, p.name); - swap(id, p.id); - swap(P, p.P); - swap(isAlive, p.isAlive); - return *this; -} - -Player::~Player() { - delete R.vbo; -} - void Player::setPosVel(const glm::vec3 &pos, const glm::vec3 &vel, const glm::vec3 &acc) { lastPosition = m_predictPos; position = m_predictPos = pos; diff --git a/src/Player.hpp b/src/Player.hpp index 01f3ce8..159954a 100644 --- a/src/Player.hpp +++ b/src/Player.hpp @@ -2,11 +2,14 @@ #define PLAYER_HPP #include "Platform.hpp" +#include #include +#include #include #include +#include "platform/PreprocUtils.hpp" #include "network/Network.hpp" #include "World.hpp" @@ -14,6 +17,7 @@ namespace Diggler { class Program; namespace Render { +class PlayerRenderer; namespace gl { class VBO; } @@ -25,6 +29,9 @@ using PlayerGameID = uint32; class Player { protected: + friend class Render::PlayerRenderer; + uintptr_t rendererData; + static struct Renderer { const Program *prog; GLint att_coord, @@ -32,13 +39,10 @@ protected: uni_unicolor, uni_fogStart, uni_fogEnd; - Render::gl::VBO *vbo; + std::unique_ptr vbo; } R; double m_lastPosTime; glm::vec3 m_predictPos; - - Player(const Player&) = delete; - Player& operator=(const Player&) = delete; public: enum class Direction : uint8 { @@ -60,15 +64,15 @@ public: glm::vec3 position, lastPosition, velocity, accel; float angle; double toolUseTime; std::string name; - uint32 id; + using SessionID = uint32; + SessionID sessId; bool isAlive; - Net::Peer P; + Net::Peer *peer; std::list pendingChunks; Player(Game *G = nullptr); - Player(Player&&); - Player& operator=(Player&&); - ~Player(); + nocopy(Player); + defaultmove(Player); void setPosVel(const glm::vec3 &pos, const glm::vec3 &vel, const glm::vec3 &acc = glm::vec3()); void update(const float &delta); diff --git a/src/PlayerList.cpp b/src/PlayerList.cpp index 21317b7..cde15c8 100644 --- a/src/PlayerList.cpp +++ b/src/PlayerList.cpp @@ -31,12 +31,12 @@ void PlayerList::remove(const Player &plr) { throw std::out_of_range("Can't remove player: not in list"); } -Player* PlayerList::getByGameId(uint32 id) { - if (G->LP && id == G->LP->id) +Player* PlayerList::getBySessId(Player::SessionID sid) { + if (G->LP && sid == G->LP->sessId) return G->LP; for (auto it = begin(); it != end(); ++it) { - if (it->id == id) { + if (it->sessId == sid) { return &*it; } } @@ -58,7 +58,7 @@ Player* PlayerList::getByName(const std::string &name) { Player* PlayerList::getByPeer(const Net::Peer &peer) { for (auto it = begin(); it != end(); ++it) { - if (it->P == peer) { + if (*it->peer == peer) { return &*it; } } diff --git a/src/PlayerList.hpp b/src/PlayerList.hpp index 609bf29..fc94738 100644 --- a/src/PlayerList.hpp +++ b/src/PlayerList.hpp @@ -30,7 +30,7 @@ public: * @brief Gets a Player using its game session ID * @return Pointer to Player, may be nullptr if not found */ - Player* getByGameId(uint32); + Player* getBySessId(Player::SessionID); /** * @brief Gets a Player using its name diff --git a/src/Server.cpp b/src/Server.cpp index 88baef9..a46708b 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -11,6 +11,7 @@ #include "network/msgtypes/BlockUpdate.hpp" #include "network/msgtypes/Chat.hpp" #include "network/msgtypes/ChunkTransfer.hpp" +#include "network/msgtypes/PlayerUpdate.hpp" #include "network/Network.hpp" #include "network/NetHelper.hpp" #include "scripting/lua/State.hpp" @@ -27,8 +28,8 @@ namespace Diggler { inline Player* Server::getPlayerByPeer(const Peer &peer) { return G.players.getByPeer(peer); } -inline Player* Server::getPlayerById(uint32 id) { - return G.players.getByGameId(id); +inline Player *Server::getPlayerBySessId(uint32 id) { + return G.players.getBySessId(id); } inline Player* Server::getPlayerByName(const std::string &name) { return G.players.getByName(name); @@ -36,7 +37,7 @@ inline Player* Server::getPlayerByName(const std::string &name) { void Server::handlePlayerJoin(InMessage &msg, Peer &peer) { std::string name = msg.readString(); - getOutputStream() << peer.getHost() << " is joining as " << name << std::endl; + getOutputStream() << peer.peerHost() << " is joining as " << name << std::endl; // TODO: ban list Player *possible = getPlayerByName(name); @@ -45,42 +46,42 @@ void Server::handlePlayerJoin(InMessage &msg, Peer &peer) { OutMessage kick(MessageType::PlayerQuit, QuitReason::UsernameAlreadyUsed); kick.writeString("You are \faalready\f0 playing on this server"); H.send(peer, kick, Tfer::Rel); - getOutputStream() << peer.getHost() << " tried to connect as " << name << ": name is taken" << endl; + getOutputStream() << peer.peerHost() << " tried to connect as " << name << ": name is taken" << endl; return; } Player &plr = G.players.add(); plr.name = name; - plr.id = FastRand(); - plr.P = peer; + plr.sessId = FastRand(); + plr.peer = &peer; plr.W = G.U->getLoadWorld(0); // Confirm successful join OutMessage join(MessageType::PlayerJoin); - join.writeU32(plr.id); + join.writeU32(plr.sessId); join.writeI16(plr.W->id); H.send(peer, join, Tfer::Rel); // Send the player list for (Player &p : G.players) { - if (p.id == plr.id) + if (p.sessId == plr.sessId) continue; // ok, he knows he's here OutMessage playerMsg(MessageType::PlayerJoin); - playerMsg.writeU32(p.id); + playerMsg.writeU32(p.sessId); playerMsg.writeString(p.name); H.send(peer, playerMsg, Tfer::Rel); } // Broadcast player's join OutMessage broadcast(MessageType::PlayerJoin); - broadcast.writeU32(plr.id); + broadcast.writeU32(plr.sessId); broadcast.writeString(plr.name); for (Player &p : G.players) { - if (p.id == plr.id) + if (p.sessId == plr.sessId) continue; // don't send broadcast to the player - H.send(p.P, broadcast, Tfer::Rel); + H.send(*p.peer, broadcast, Tfer::Rel); } - getOutputStream() << plr.name << " joined from " << peer.getHost() << endl; + getOutputStream() << plr.name << " joined from " << peer.peerHost() << endl; for (int x = -2; x < 2; ++x) for (int y = -2; y < 2; ++y) for (int z = -2; z < 2; ++z) @@ -93,16 +94,16 @@ void Server::handlePlayerQuit(Peer &peer, QuitReason reason) { Player &plr = *plrPtr; // Broadcast disconnection OutMessage broadcast(MessageType::PlayerQuit, reason); - broadcast.writeU32(plr.id); + broadcast.writeU32(plr.sessId); for (Player &p : G.players) { - if (p.id == plr.id) + if (p.sessId == plr.sessId) continue; // dont send broadcast to the player - H.send(p.P, broadcast, Tfer::Rel); + H.send(*p.peer, broadcast, Tfer::Rel); } getOutputStream() << plr.name << " disconnected" << endl; G.players.remove(plr); } else { - getOutputStream() << peer.getHost() << " disconnected" << endl; + getOutputStream() << peer.peerHost() << " disconnected" << endl; } } @@ -119,7 +120,7 @@ void Server::handleChat(InMessage &msg, Player *plr) { cs.readFromMsg(msg); ChatPlayerTalk cpt; - cpt.player.id = plr->id; + cpt.player.id = plr->sessId; cpt.msg = cs.msg; OutMessage omsg; cpt.writeToMsg(omsg); @@ -137,28 +138,23 @@ void Server::handleCommand(Player *plr, const std::string &command, const std::v } void Server::handlePlayerUpdate(InMessage &msg, Player &plr) { - switch (msg.getSubtype()) { - case PlayerUpdateType::Move: { + using namespace Net::MsgTypes; + using S = PlayerUpdateSubtype; + switch (static_cast(msg.getSubtype())) { + case S::Move: { + PlayerUpdateMove pum; + pum.readFromMsg(msg); + pum.plrSessId = plr.sessId; // Broadcast movement - OutMessage bcast(MessageType::PlayerUpdate, PlayerUpdateType::Move); - bcast.writeU32(plr.id); - glm::vec3 pos = msg.readVec3(), - vel = msg.readVec3(), - acc = msg.readVec3(); - plr.setPosVel(pos, vel, acc); - plr.angle = msg.readFloat(); - bcast.writeVec3(pos); - bcast.writeVec3(vel); - bcast.writeVec3(acc); - bcast.writeFloat(plr.angle); + OutMessage bcast; pum.writeToMsg(bcast); for (Player &p : G.players) { - if (p.id == plr.id) + if (p.sessId == plr.sessId) continue; // dont send broadcast to the player // TODO: confirm position to player - H.send(p.P, bcast, Tfer::Unrel); + H.send(*p.peer, bcast, Tfer::Unrel); } } break; - case PlayerUpdateType::Die: + case S::Die: handlePlayerDeath(msg, plr); break; default: @@ -188,7 +184,7 @@ void Server::sendChunks(const std::list &cs, Player &P) { } OutMessage msg; ctr.writeToMsg(msg); - H.send(P.P, msg, Tfer::Rel, Channels::MapTransfer); + H.send(*P.peer, msg, Tfer::Rel, Channels::MapTransfer); } void Server::handlePlayerChunkRequest(InMessage &msg, Player &plr) { @@ -263,26 +259,27 @@ void Server::handlePlayerMapUpdate(InMessage &msg, Player &plr) { } void Server::handlePlayerDeath(InMessage &msg, Player &plr) { - uint8 drb = msg.readU8(); - Player::DeathReason dr = (Player::DeathReason)drb; - plr.setDead(false, dr); - OutMessage out(MessageType::PlayerUpdate, PlayerUpdateType::Die); - out.writeU32(plr.id); - out.writeU8(drb); + using namespace Net::MsgTypes; + PlayerUpdateDie pud; + pud.readFromMsg(msg); + pud.plrSessId = plr.sessId; + plr.setDead(false, pud.reason); + OutMessage out; pud.writeToMsg(out); for (Player &p : G.players) { - if (p.id != plr.id) - H.send(p.P, out, Tfer::Rel, Channels::Life); + if (p.sessId != plr.sessId) + H.send(*p.peer, out, Tfer::Rel, Channels::Life); } // Respawn player later - Game *G = &this->G; uint32 id = plr.id; - std::thread respawn([G, id] { + Game *G = &this->G; Player::SessionID sid = plr.sessId; + std::thread respawn([G, sid] { std::this_thread::sleep_for(std::chrono::seconds(2)); - Player *plr = G->S->getPlayerById(id); + Player *plr = G->S->getPlayerBySessId(sid); if (plr) { plr->setDead(false); - OutMessage out(MessageType::PlayerUpdate, PlayerUpdateType::Respawn); - out.writeU32(id); + PlayerUpdateRespawn pur; + pur.plrSessId = sid; + OutMessage out; pur.writeToMsg(out); NetHelper::Broadcast(G, out, Tfer::Rel, Channels::Life); } }); @@ -380,12 +377,13 @@ void Server::chunkUpdater(WorldRef WR, bool &continueUpdate) { void Server::run() { InMessage msg; - Peer peer; + Peer *peerPtr; bool continueUpdate = true; std::thread upd(&Server::chunkUpdater, this, G.U->getWorld(0), std::ref(continueUpdate)); Player *plr; while (true) { - if (H.recv(msg, peer, 100)) { + if (H.recv(msg, &peerPtr, 100)) { + Peer &peer = *peerPtr; plr = getPlayerByPeer(peer); if (plr != nullptr) { switch (msg.getType()) { @@ -408,7 +406,7 @@ void Server::run() { } switch (msg.getType()) { case MessageType::NetConnect: - getOutputStream() << peer.getHost() << " NEWCONN" << std::endl; + getOutputStream() << peer.peerHost() << " NEWCONN" << std::endl; break; case MessageType::NetDisconnect: handleDisconnect(peer); @@ -441,10 +439,10 @@ bool Server::isPlayerOnline(const std::string &playername) const { void Server::kick(Player &p, Net::QuitReason r, const std::string &message) { OutMessage msg(MessageType::PlayerQuit, r); - msg.writeU32(p.id); + msg.writeU32(p.sessId); msg.writeString(message); - H.send(p.P, msg, Tfer::Rel); - p.P.disconnect(); + H.send(*p.peer, msg, Tfer::Rel); + p.peer->disconnect(); } Server::~Server() { diff --git a/src/Server.hpp b/src/Server.hpp index fbffe93..4709922 100644 --- a/src/Server.hpp +++ b/src/Server.hpp @@ -50,7 +50,7 @@ public: bool isPlayerOnline(const std::string &playername) const; bool isIPOnline(const std::string &ip) const; - Player* getPlayerById(uint32 id); + Player* getPlayerBySessId(uint32 id); Player* getPlayerByPeer(const Net::Peer &peer); Player* getPlayerByName(const std::string &name); void kick(Player &p, Net::QuitReason r = Net::QuitReason::Kicked, const std::string& message = ""); diff --git a/src/Texture.cpp b/src/Texture.cpp index ad26cb2..95627e7 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -102,10 +102,14 @@ void Texture::setFiltering(Filter min, Filter mag) { static GLenum getWrapGlConstant(Texture::Wrapping wrap) { switch (wrap) { - case Texture::Wrapping::Clamp: - return GL_CLAMP; + case Texture::Wrapping::ClampEdge: + return GL_CLAMP_TO_EDGE; + case Texture::Wrapping::ClampBorder: + return GL_CLAMP_TO_BORDER; case Texture::Wrapping::Repeat: return GL_REPEAT; + case Texture::Wrapping::MirroredRepeat: + return GL_MIRRORED_REPEAT; } return 0; } diff --git a/src/Texture.hpp b/src/Texture.hpp index c8f2f19..b9a22ab 100644 --- a/src/Texture.hpp +++ b/src/Texture.hpp @@ -16,8 +16,10 @@ public: Linear }; enum class Wrapping { + ClampEdge, + ClampBorder, Repeat, - Clamp + MirroredRepeat }; private: GLuint m_id; @@ -54,7 +56,9 @@ public: void setSubTexture(int x, int y, uint w, uint h, const uint8_t *data, PixelFormat format = PixelFormat::RGB); void setFiltering(Filter min, Filter mag); void setWrapping(Wrapping s, Wrapping t); - + inline void setWrapping(Wrapping w) { + setWrapping(w, w); + } void bind() const { glBindTexture(GL_TEXTURE_2D, m_id); diff --git a/src/VersionInfo.hpp b/src/VersionInfo.hpp index 3e6f853..e45f887 100644 --- a/src/VersionInfo.hpp +++ b/src/VersionInfo.hpp @@ -1,5 +1,5 @@ -#ifndef VERSION_INFO_HPP -#define VERSION_INFO_HPP +#ifndef DIGGLER_VERSION_INFO_HPP +#define DIGGLER_VERSION_INFO_HPP namespace Diggler { const char* VersionString = "0.1.0"; @@ -8,4 +8,4 @@ namespace Diggler { int VersionRevision = 0; } -#endif \ No newline at end of file +#endif /* DIGGLER_VERSION_INFO_HPP */ diff --git a/src/WorldGenerator.hpp b/src/WorldGenerator.hpp index bbb0cc0..467bf6a 100644 --- a/src/WorldGenerator.hpp +++ b/src/WorldGenerator.hpp @@ -1,5 +1,6 @@ -#ifndef WORLD_GENERATOR_HPP -#define WORLD_GENERATOR_HPP +#ifndef DIGGLER_WORLD_GENERATOR_HPP +#define DIGGLER_WORLD_GENERATOR_HPP + #include namespace Diggler { @@ -13,4 +14,4 @@ public: } -#endif \ No newline at end of file +#endif /* DIGGLER_WORLD_GENERATOR_HPP */ diff --git a/src/content/Asset.hpp b/src/content/Asset.hpp new file mode 100644 index 0000000..5e50758 --- /dev/null +++ b/src/content/Asset.hpp @@ -0,0 +1,44 @@ +#ifndef DIGGLER_CONTENT_ASSET_HPP +#define DIGGLER_CONTENT_ASSET_HPP + +#include + +#include "../crypto/SHA256.hpp" + +namespace Diggler { +namespace Content { + +class Asset { +public: + enum class Type { + Texture, + Sound, + Music, + Schematic, + Model, + VoxelModel + }; + +protected: + Crypto::SHA256::Digest m_hash; + Type m_type; + msgpack::object m_info; + +public: + const Crypto::SHA256::Digest& hash() const { + return m_hash; + } + + Type type() const { + return m_type; + } + + const msgpack::object& info() const { + return m_info; + } +}; + +} +} + +#endif /* DIGGLER_CONTENT_ASSET_HPP */ diff --git a/src/content/AssetManager.cpp b/src/content/AssetManager.cpp new file mode 100644 index 0000000..e798e9a --- /dev/null +++ b/src/content/AssetManager.cpp @@ -0,0 +1,13 @@ +#include "AssetManager.hpp" + +#include "../Game.hpp" + +namespace Diggler { +namespace Content { + +AssetManager::AssetManager(Game *G) : + G(G) { +} + +} +} diff --git a/src/content/AssetManager.hpp b/src/content/AssetManager.hpp new file mode 100644 index 0000000..743f273 --- /dev/null +++ b/src/content/AssetManager.hpp @@ -0,0 +1,26 @@ +#ifndef DIGGLER_CONTENT_ASSET_MANAGER_HPP +#define DIGGLER_CONTENT_ASSET_MANAGER_HPP + +#include "Asset.hpp" +#include "Mod.hpp" + +namespace Diggler { + +class Game; + +namespace Content { + +class AssetManager final { +private: + Game *G; + +public: + AssetManager(Game*); + + +}; + +} +} + +#endif /* DIGGLER_CONTENT_ASSET_MANAGER_HPP */ diff --git a/src/content/Mod.hpp b/src/content/Mod.hpp new file mode 100644 index 0000000..0082f89 --- /dev/null +++ b/src/content/Mod.hpp @@ -0,0 +1,22 @@ +#ifndef DIGGLER_CONTENT_MOD_HPP +#define DIGGLER_CONTENT_MOD_HPP + +#include + +#include "../Platform.hpp" + +namespace Diggler { +namespace Content { + +using ModId = byte[32]; + +class Mod { +public: + ModId id; + std::string name, fsPath; +}; + +} +} + +#endif /* DIGGLER_CONTENT_MOD_HPP */ diff --git a/src/content/ModManager.cpp b/src/content/ModManager.cpp new file mode 100644 index 0000000..93ae27f --- /dev/null +++ b/src/content/ModManager.cpp @@ -0,0 +1,13 @@ +#include "ModManager.hpp" + +#include "../Game.hpp" + +namespace Diggler { +namespace Content { + +ModManager::ModManager(Game *G) : + G(G) { +} + +} +} diff --git a/src/content/ModManager.hpp b/src/content/ModManager.hpp new file mode 100644 index 0000000..14fb60c --- /dev/null +++ b/src/content/ModManager.hpp @@ -0,0 +1,57 @@ +#ifndef DIGGLER_CONTENT_MOD_MANAGER_HPP +#define DIGGLER_CONTENT_MOD_MANAGER_HPP + +#include "Mod.hpp" + +namespace Diggler { + +class Game; + +namespace Content { + +class ModManager final { +private: + Game *G; + +public: + ModManager(Game*); + + /** + * @brief Resolves a mod-relative or absolute path into a filesystem path + * Accepted schemes: builtin, mod:*, self + * @param mod Mod context + * @param path Mod-relative or absolute path + * @return Resolved filesystem path + */ + std::string filesystemPath(const Mod &mod, const std::string &path); + + /** + * @brief Resolves an absolute path into a filesystem path + * Accepted schemes: builtin, mod:* + * @param path Absolute path + * @return Resolved filesystem path + */ + std::string filesystemPath(const std::string &path); + + + /** + * @brief Determines if the path is absolute or mod-relative (i.e. requires a mod context to be + * resolved) + * @param path Path to check + * @return true if path is absolute, false otherwise + */ + static bool isAbsolutePath(const std::string &path); + + /** + * @brief Resolves a mod-relative path into an absolute path + * Accepted schemes: builtin, mod:*, self + * @param path Mod-relative path + * @return Resolved filesystem path + */ + std::string absolutePath(const Mod &mod, const std::string &path); +}; + +} +} + +#endif /* DIGGLER_CONTENT_MOD_MANAGER_HPP */ diff --git a/src/crypto/CryptoData.hpp b/src/crypto/CryptoData.hpp new file mode 100644 index 0000000..389f472 --- /dev/null +++ b/src/crypto/CryptoData.hpp @@ -0,0 +1,70 @@ +#ifndef DIGGLER_CRYPTO_CRYPTODATA_HPP +#define DIGGLER_CRYPTO_CRYPTODATA_HPP + +#include +#include + +#include + +namespace Diggler { +namespace Crypto { + +template +struct CryptoData { + constexpr static size_t Length = B; + + union { + unsigned char data[Length], bytes[Length]; + }; + + bool operator==(const CryptoData &o) const { + return sodium_memcmp(data, o.data, Length); + } + + bool operator!=(const CryptoData &o) const { + return !operator==(o); + } + + unsigned char& operator[](size_t idx) { + return data[idx]; + } + + unsigned char operator[](size_t idx) const { + return data[idx]; + } + + void zerofill() { + memset(data, 0, Length); + } + + std::string hex(bool caps = false) const { + std::string hex(Length * 2, '\0'); + const char *hextab = caps ? "0123456789ABCDEF" : "0123456789abcdef"; + for (size_t i = 0; i < Length; ++i) { + hex[i * 2 + 0] = hextab[data[i] >> 4]; + hex[i * 2 + 1] = hextab[data[i] & 0xF]; + } + return hex; + } +}; + +template +struct MlockedCryptoData : CryptoData { + MlockedCryptoData() { + sodium_mlock(this->data, this->Length); + } + + MlockedCryptoData(MlockedCryptoData &o) { + sodium_mlock(this->data, this->Length); + memcpy(this->data, o.data, this->Length); + } + + ~MlockedCryptoData() { + sodium_munlock(this->data, this->Length); + } +}; + +} +} + +#endif /* DIGGLER_CRYPTO_CRYPTODATA_HPP */ diff --git a/src/crypto/DiffieHellman.hpp b/src/crypto/DiffieHellman.hpp new file mode 100644 index 0000000..aa62bad --- /dev/null +++ b/src/crypto/DiffieHellman.hpp @@ -0,0 +1,37 @@ +#ifndef DIGGLER_CRYPTO_DIFFIE_HELLMAN_HPP +#define DIGGLER_CRYPTO_DIFFIE_HELLMAN_HPP + +#include + +#include "CryptoData.hpp" + +namespace Diggler { +namespace Crypto { +namespace DiffieHellman { + +constexpr static size_t ScalarBytes = crypto_scalarmult_SCALARBYTES; +constexpr static size_t Bytes = crypto_scalarmult_BYTES; + +struct SecretKey : MlockedCryptoData { +}; + +struct PublicKey : CryptoData { +}; + +struct SharedSecret : MlockedCryptoData { +}; + +inline void scalarmultBase(const SecretKey &sk, PublicKey &pk) { + crypto_scalarmult_base(pk.bytes, sk.bytes); +} + +[[gnu::warn_unused_result]] +inline bool scalarmult(const SecretKey &sk, const PublicKey &pk, SharedSecret &ss) { + return crypto_scalarmult(ss.bytes, sk.bytes, pk.bytes); +} + +} +} +} + +#endif /* DIGGLER_CRYPTO_DIFFIE_HELLMAN_HPP */ diff --git a/src/crypto/Random.hpp b/src/crypto/Random.hpp new file mode 100644 index 0000000..6d18382 --- /dev/null +++ b/src/crypto/Random.hpp @@ -0,0 +1,38 @@ +#ifndef DIGGLER_CRYPTO_RANDOM_HPP +#define DIGGLER_CRYPTO_RANDOM_HPP + +#include + +#include "CryptoData.hpp" +#include "../platform/Types.hpp" + +namespace Diggler { +namespace Crypto { +namespace Random { + +uint32 random() { + return randombytes_random(); +} + +uint32 uniform(const uint32 upperBound) { + return randombytes_uniform(upperBound); +} + +void buf(void *const buf, const size_t size) { + randombytes_buf(buf, size); +} + +template +void randomData(CryptoData &cd) { + randombytes_buf(cd.bytes, cd.Length); +} + +void stir() { + randombytes_stir(); +} + +} +} +} + +#endif /* DIGGLER_CRYPTO_RANDOM_HPP */ diff --git a/src/crypto/SHA256.hpp b/src/crypto/SHA256.hpp new file mode 100644 index 0000000..712f6c4 --- /dev/null +++ b/src/crypto/SHA256.hpp @@ -0,0 +1,42 @@ +#ifndef DIGGLER_CRYPTO_SHA256_HPP +#define DIGGLER_CRYPTO_SHA256_HPP + +#include + +#include "CryptoData.hpp" + +namespace Diggler { +namespace Crypto { + +struct SHA256 { + constexpr static size_t DigestBytes = crypto_hash_sha256_BYTES; + + struct Digest : CryptoData { + }; + + crypto_hash_sha256_state state; + + SHA256() { + crypto_hash_sha256_init(&state); + } + + template + void update(const T *data, size_t len) { + crypto_hash_sha256_update(&state, data, len); + } + + void finalize(unsigned char *digest) { + crypto_hash_sha256_final(&state, digest); + } + + Digest finalize() { + Digest digest; + crypto_hash_sha256_final(&state, digest.bytes); + return digest; + } +}; + +} +} + +#endif /* DIGGLER_CRYPTO_SHA256_HPP */ diff --git a/src/crypto/Sign.hpp b/src/crypto/Sign.hpp new file mode 100644 index 0000000..389847a --- /dev/null +++ b/src/crypto/Sign.hpp @@ -0,0 +1,41 @@ +#ifndef DIGGLER_CRYPTO_SIGN_HPP +#define DIGGLER_CRYPTO_SIGN_HPP + +#include + +#include "CryptoData.hpp" + +namespace Diggler { +namespace Crypto { +namespace Sign { + +constexpr size_t PublicKeyBytes = crypto_sign_PUBLICKEYBYTES; +constexpr size_t SecretKeyBytes = crypto_sign_SECRETKEYBYTES; +constexpr size_t SignatureBytes = crypto_sign_BYTES; + +struct PublicKey : CryptoData { +}; + +struct SecretKey : MlockedCryptoData { +}; + +struct Signature : CryptoData { +}; + +void keypair(PublicKey &pk, SecretKey &sk) { + crypto_sign_keypair(pk.bytes, sk.bytes); +} + +void sign(const unsigned char *m, unsigned long long mlen, const SecretKey &sk, Signature &sig) { + crypto_sign_detached(sig.bytes, nullptr, m, mlen, sk.bytes); +} + +bool verify(const unsigned char *m, unsigned long long mlen, Signature &sig, const PublicKey &pk) { + return crypto_sign_verify_detached(sig.bytes, m, mlen, pk.bytes); +} + +} +} +} + +#endif /* DIGGLER_CRYPTO_SIGN_HPP */ diff --git a/src/crypto/Sodium.hpp b/src/crypto/Sodium.hpp new file mode 100644 index 0000000..3e976e0 --- /dev/null +++ b/src/crypto/Sodium.hpp @@ -0,0 +1,15 @@ +#ifndef DIGGLER_CRYPTO_SODIUM_HPP +#define DIGGLER_CRYPTO_SODIUM_HPP + +namespace Diggler { +namespace Crypto { + +class Sodium { +public: + +}; + +} +} + +#endif /* DIGGLER_CRYPTO_SODIUM_HPP */ diff --git a/src/main.cpp b/src/main.cpp index 72be8be..dbf7d63 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -134,12 +134,13 @@ int main(int argc, char **argv) { /*/GW.setNextState(std::make_shared(&GW));/*/ if (networkSuccess) - GW.setNextState(std::make_shared(&GW, host, port)); + GW.setNextState(std::move(std::make_unique(&GW, host, port))); else GW.showMessage("Network init failed!"); /**/ GW.run(); + G->finalizeClient(); } G.reset(); } diff --git a/src/network/ClientMessageHandler.cpp b/src/network/ClientMessageHandler.cpp index 344e53b..a79639c 100644 --- a/src/network/ClientMessageHandler.cpp +++ b/src/network/ClientMessageHandler.cpp @@ -6,6 +6,7 @@ #include "msgtypes/BlockUpdate.hpp" #include "msgtypes/Chat.hpp" #include "msgtypes/ChunkTransfer.hpp" +#include "msgtypes/PlayerUpdate.hpp" namespace Diggler { namespace Net { @@ -65,7 +66,7 @@ bool ClientMessageHandler::handleMessage(InMessage &msg) { if (cpt.msg.type == msgpack::type::STR) { std::string playerName; if (cpt.player.display.type == msgpack::type::NIL) { - const Player *blabbermouth = GS.G->players.getByGameId(cpt.player.id); + const Player *blabbermouth = GS.G->players.getBySessId(cpt.player.id); if (blabbermouth != nullptr) { playerName = blabbermouth->name + "> "; } else { @@ -81,14 +82,14 @@ bool ClientMessageHandler::handleMessage(InMessage &msg) { } break; case MessageType::PlayerJoin: { Player &plr = GS.G->players.add(); - plr.id = msg.readU32(); + plr.sessId = msg.readU32(); plr.name = msg.readString(); - getDebugStream() << "Player " << plr.name << '(' << plr.id << ") joined the party!" << std::endl; + getDebugStream() << "Player " << plr.name << '(' << plr.sessId << ") joined the party!" << std::endl; } break; case MessageType::PlayerQuit: { uint32 id = msg.readU32(); try { - Player *plr = GS.G->players.getByGameId(id); + Player *plr = GS.G->players.getBySessId(id); if (plr != nullptr) { getOutputStream() << plr->name << " is gone :(" << std::endl; GS.G->players.remove(*plr); @@ -98,30 +99,51 @@ bool ClientMessageHandler::handleMessage(InMessage &msg) { } } break; case MessageType::PlayerUpdate: { - uint32 id = msg.readU32(); - try { - Player *plr = GS.G->players.getByGameId(id); - if (plr != nullptr) { - switch (msg.getSubtype()) { - case PlayerUpdateType::Move: { - glm::vec3 pos = msg.readVec3(), - vel = msg.readVec3(), - acc = msg.readVec3(); - plr->setPosVel(pos, vel, acc); - plr->angle = msg.readFloat(); - } break; - case PlayerUpdateType::Die: - plr->setDead(false, (Player::DeathReason)msg.readU8()); - break; - case PlayerUpdateType::Respawn: - plr->setDead(false); - break; - default: - break; + using S = PlayerUpdateSubtype; + switch (static_cast(msg.getSubtype())) { + case S::Move: { + PlayerUpdateMove pum; + pum.readFromMsg(msg); + if (!pum.plrSessId) { + getOutputStream() << "PlayerUpdateMove without player session ID" << std::endl; + return true; } - } - } catch (const std::out_of_range &e) { - getOutputStream() << "Invalid player update: #" << id << " is not on server" << std::endl; + Player *plr = GS.G->players.getBySessId(*pum.plrSessId); + if (!plr) { + getOutputStream() << "PlayerUpdateMove: sess#" << *pum.plrSessId << + " is not on server" << std::endl; + return true; + } + glm::vec3 pos = pum.position ? *pum.position : plr->position, + vel = pum.velocity ? *pum.velocity : plr->velocity, + acc = pum.accel ? *pum.accel : plr->accel; + plr->setPosVel(pos, vel, acc); + plr->angle = pum.angle; + } break; + case S::Die: { + PlayerUpdateDie pud; + pud.readFromMsg(msg); + Player *plr = GS.G->players.getBySessId(pud.plrSessId); + if (!plr) { + getOutputStream() << "PlayerUpdateDie: sess#" << pud.plrSessId << + " is not on server" << std::endl; + return true; + } + plr->setDead(false, pud.reason); + } break; + case S::Respawn: { + PlayerUpdateRespawn pur; + pur.readFromMsg(msg); + Player *plr = GS.G->players.getBySessId(pur.plrSessId); + if (!plr) { + getOutputStream() << "PlayerUpdateRespawn: sess#" << pur.plrSessId << + " is not on server" << std::endl; + return true; + } + plr->setDead(false); + } break; + default: + break; } } break; case MessageType::BlockUpdate: { diff --git a/src/network/NetHelper.cpp b/src/network/NetHelper.cpp index 74492ec..d9c1aff 100644 --- a/src/network/NetHelper.cpp +++ b/src/network/NetHelper.cpp @@ -17,7 +17,7 @@ void Broadcast(Game *G, const OutMessage &msg, Tfer tfer, Channels chan) { void Broadcast(Game &G, const OutMessage &msg, Tfer tfer, Channels chan) { for (Player &p : G.players) { - G.S->H.send(p.P, msg, tfer, chan); + G.S->H.send(*p.peer, msg, tfer, chan); } } @@ -27,20 +27,7 @@ void SendChat(Game *G, const std::string &str) { OutMessage msg; cs.writeToMsg(msg); - G->H.send(G->NS, msg, Tfer::Unseq); -} - -void SendToolUse(Game *G, int x, int y, int z) { - OutMessage msg(MessageType::PlayerUpdate, PlayerUpdateType::ToolUse); - msg.writeBool(true); - msg.writeIVec3(x, y, z); - G->H.send(G->NS, msg, Tfer::Rel, Channels::PlayerInteract); -} - -void SendToolUse(Game *G) { - OutMessage msg(MessageType::PlayerUpdate, PlayerUpdateType::ToolUse); - msg.writeBool(false); - G->H.send(G->NS, msg, Tfer::Rel, Channels::PlayerInteract); + G->H.send(*G->NS, msg, Tfer::Unseq); } diff --git a/src/network/NetHelper.hpp b/src/network/NetHelper.hpp index 55d5bac..8119f4e 100644 --- a/src/network/NetHelper.hpp +++ b/src/network/NetHelper.hpp @@ -20,10 +20,8 @@ void MakeEvent(Net::OutMessage&, Net::EventType, const Player&); void SendEvent(Game*, Net::EventType); void SendChat(Game*, const std::string&); -void SendToolUse(Game*); -void SendToolUse(Game*, int x, int y, int z); } } -#endif \ No newline at end of file +#endif diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 9eeddad..8e90a1f 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -1,10 +1,16 @@ #include "Network.hpp" -#include + +#include #include #include #include #include +#include + +#include "../crypto/Random.hpp" +#include "msgtypes/ConnectionParam.hpp" + #include namespace Diggler { @@ -53,8 +59,7 @@ Message::Message(MessageType t, uint8 s) : InMessage::InMessage() : Message(MessageType::Null, 0), - m_chan(Channels::Base), - m_packet(nullptr) { + m_chan(Channels::Base) { } InMessage::~InMessage() { @@ -79,36 +84,17 @@ void InMessage::fromData(const void *data, SizeT len, Channels chan) { m_length = len; m_type = static_cast(bytes[0]); m_subtype = bytes[1]; - m_data = static_cast(std::malloc(len-HeaderSize)); - std::memcpy(m_data, &(bytes[HeaderSize]), len-HeaderSize); -} - -void InMessage::fromPacket(void *packet, Channels chan) { - ENetPacket *enpkt = static_cast(packet); - SizeT len = enpkt->dataLength; - if (len < HeaderSize) { - throw std::invalid_argument("Message length is smaller than message header"); - } - uint8 *const bytes = static_cast(enpkt->data); - free(); - m_packet = packet; - m_chan = chan; - m_cursor = 0; - m_length = len; - m_type = static_cast(bytes[0]); - m_subtype = bytes[1]; - m_data = &(bytes[HeaderSize]); + // m_data/bytes is guaranteed never to be written to, so we can const_cast it + m_data = const_cast(bytes) + HeaderSize; } void InMessage::free() { - if (m_packet != nullptr) { - enet_packet_destroy(static_cast(m_packet)); - } else { - std::free(m_data); + if (m_data != nullptr) { + delete[] (m_data - HeaderSize); } m_type = MessageType::Null; m_subtype = m_length = m_cursor = 0; - m_packet = m_data = nullptr; + m_data = nullptr; } @@ -160,6 +146,14 @@ Channels InMessage::getChannel() const { return m_chan; } +Peer::Peer(Host &host, void *peer) : + host(host), + peer(peer) { + reinterpret_cast(this->peer)->data = this; + Crypto::Random::randomData(connectionPk); + Crypto::DiffieHellman::scalarmultBase(connectionSk, connectionPk); +} + bool Peer::operator==(const Peer &other) const { return peer == other.peer; } @@ -168,13 +162,13 @@ bool Peer::operator!=(const Peer &other) const { return !(*this == other); } -void Peer::disconnect() { - ENetPeer *const peer = static_cast(this->peer); - enet_peer_disconnect(peer, 0); +void Peer::disconnect(uint32 data) { + ENetPeer *const peer = reinterpret_cast(this->peer); + enet_peer_disconnect(peer, data); } -std::string Peer::getHost() { - const ENetPeer *const peer = static_cast(this->peer); +std::string Peer::peerHost() { + const ENetPeer *const peer = reinterpret_cast(this->peer); std::ostringstream oss; char *chars = new char[512]; enet_address_get_host_ip(&peer->host->address, chars, 512); @@ -184,8 +178,8 @@ std::string Peer::getHost() { return oss.str(); } -std::string Peer::getIp() { - const ENetPeer *const peer = static_cast(this->peer); +std::string Peer::peerIP() { + const ENetPeer *const peer = reinterpret_cast(this->peer); char *chars = new char[512]; enet_address_get_host_ip(&peer->host->address, chars, 512); std::string str(chars); @@ -193,8 +187,8 @@ std::string Peer::getIp() { return str; } -Port Peer::getPort() { - const ENetPeer *const peer = static_cast(this->peer); +Port Peer::peerPort() { + const ENetPeer *const peer = reinterpret_cast(this->peer); return peer->host->address.port; } @@ -206,6 +200,11 @@ Host::Host() : txBytes(0) { } +Host::~Host() { + ENetHost *const host = reinterpret_cast(this->host); + enet_host_destroy(host); +} + void Host::create(Port port, uint maxconn) { if (port == 0) { // Client host = enet_host_create(nullptr, 1, static_cast(Channels::MAX), 0, 0); @@ -220,8 +219,8 @@ void Host::create(Port port, uint maxconn) { } } -Peer Host::connect(const std::string &hostAddr, Port port, Timeout timeout) { - ENetHost *const host = static_cast(this->host); +Peer& Host::connect(const std::string &hostAddr, Port port, Timeout timeout) { + ENetHost *const host = reinterpret_cast(this->host); ENetAddress address; ENetEvent event; ENetPeer *peer; @@ -236,102 +235,140 @@ Peer Host::connect(const std::string &hostAddr, Port port, Timeout timeout) { if (enet_host_service(host, &event, timeout) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { - Peer p; p.peer = peer; - return p; + Peer *p = new Peer(*this, peer); + sendKeyExchange(*p); + return *p; } enet_peer_reset(peer); throw Exception(); } -Host::~Host() { - ENetHost *const host = static_cast(this->host); - enet_host_destroy(host); +void Host::processPeersToDelete() { + for (Peer *peer : m_peersToDelete) { + delete peer; + } + m_peersToDelete.clear(); } -/*static void hexDump(char in, uint8 *buf, int len) { +void Host::sendKeyExchange(Peer &p) { + MsgTypes::ConnectionParamDHKeyExchange dhke; + dhke.pk = p.connectionPk; + OutMessage keMsg; + dhke.writeToMsg(keMsg); + send(p, keMsg); +} + +static void hexDump(char in, uint8 *buf, int len) { std::cout << in << ": " << std::setiosflags(std::ios::internal); for (int i=0; i < len; ++i) { std::cout << std::setfill('0') << std::setw(2) << std::hex << (int)buf[i] << ' '; } std::cout << std::dec << std::endl; -}*/ - -bool Host::recv(InMessage &msg, Peer &peer, Timeout timeout) { - ENetHost *const host = static_cast(this->host); - ENetEvent event; - if (enet_host_service(host, &event, timeout) >= 0){ - switch (event.type) { - case ENET_EVENT_TYPE_NONE: - return false; - case ENET_EVENT_TYPE_CONNECT: - peer.peer = event.peer; - msg.setType(MessageType::NetConnect); - break; - case ENET_EVENT_TYPE_RECEIVE: - peer.peer = event.peer; - //hexDump('R', event.packet->data, event.packet->dataLength); - // Packet "ownership" is transferred to msg - msg.fromPacket(event.packet, static_cast(event.channelID)); - rxBytes += event.packet->dataLength; - break; - case ENET_EVENT_TYPE_DISCONNECT: - peer.peer = event.peer; - msg.setType(MessageType::NetDisconnect); - enet_peer_reset(event.peer); - } - return true; - } - throw Exception(); } -bool Host::recv(InMessage &msg, Timeout timeout) { - ENetHost *const host = static_cast(this->host); +bool Host::recv(InMessage &msg, Peer **peer, Timeout timeout) { + ENetHost *const host = reinterpret_cast(this->host); + processPeersToDelete(); + auto start = std::chrono::steady_clock::now(); + ENetEvent event; - if (enet_host_service(host, &event, timeout) >= 0){ - switch (event.type) { - case ENET_EVENT_TYPE_NONE: + while (true) { + auto now = std::chrono::steady_clock::now(); + enet_uint32 elapsed = static_cast( + std::chrono::duration_cast(now - start).count()); + if (enet_host_service(host, &event, timeout - elapsed) > 0) { + Peer *peerPtr = event.peer == nullptr ? nullptr : + reinterpret_cast(event.peer->data); + switch (event.type) { + case ENET_EVENT_TYPE_NONE: + break; + case ENET_EVENT_TYPE_CONNECT: + peerPtr = new Peer(*this, event.peer); + *peer = peerPtr; + sendKeyExchange(*peerPtr); + msg.setType(MessageType::NetConnect); + return true; + case ENET_EVENT_TYPE_RECEIVE: { + if (peer) { + *peer = peerPtr; + } + + const Message::SizeT pktLen = event.packet->dataLength; + const Channels pktChannel = static_cast(event.channelID); + const bool decrypt = (pktChannel == Channels::ConnectionMetaPlain); + byte *rcvData = new uint8[pktLen]; + if (decrypt) { + // TODO: decryption + std::memcpy(rcvData, event.packet->data, pktLen); + } else { + std::memcpy(rcvData, event.packet->data, pktLen); + } + // pktData's ownership is transferred to msg + msg.fromData(rcvData, pktLen, pktChannel); + rxBytes += event.packet->dataLength; + + if (msg.getType() == MessageType::ConnectionParam && + msg.getSubtype() == static_cast(MsgTypes::ConnectionParamSubtype::DHKeyExchange)) { + MsgTypes::ConnectionParamDHKeyExchange dhke; dhke.readFromMsg(msg); + peerPtr->remotePk = dhke.pk; + if (Crypto::DiffieHellman::scalarmult(peerPtr->connectionSk, peerPtr->remotePk, + peerPtr->sharedSecret) != 0) { + // TODO: properly handle key exchange failure + throw std::runtime_error("DH key exchange failed"); + } + getDebugStream() << "hello DH! " << peerPtr->sharedSecret.hex() << std::endl; + } else { + return true; + } + } break; + case ENET_EVENT_TYPE_DISCONNECT: + if (peer) { + *peer = peerPtr; + } + msg.setType(MessageType::NetDisconnect); + m_peersToDelete.emplace_back(peerPtr); + return true; + } + } else { return false; - case ENET_EVENT_TYPE_CONNECT: - msg.setType(MessageType::NetConnect); - break; - case ENET_EVENT_TYPE_RECEIVE: - //hexDump('R', event.packet->data, event.packet->dataLength); - // Packet "ownership" is transferred to msg - msg.fromPacket(event.packet, static_cast(event.channelID)); - rxBytes += event.packet->dataLength; - break; - case ENET_EVENT_TYPE_DISCONNECT: - msg.setType(MessageType::NetDisconnect); - enet_peer_reset(event.peer); } - return true; } throw Exception(); } void Host::send(Peer &peer, const OutMessage &msg, Tfer mode, Channels chan) { - ENetHost *const host = static_cast(this->host); + ENetHost *const host = reinterpret_cast(this->host); + const bool encrypt = (chan == Channels::ConnectionMetaPlain); - const uint8 header[Message::HeaderSize] = { - static_cast(msg.m_type), + const byte header[Message::HeaderSize] = { + static_cast(msg.m_type), msg.m_subtype }; - ENetPacket *packet; + size_t pktLen = Message::HeaderSize + (msg.m_actualData == nullptr ? 0 : msg.m_length); + ENetPacket *packet = enet_packet_create(nullptr, pktLen, TferToFlags(mode)); + byte *pktData = packet->data; + txBytes += pktLen; if (msg.m_actualData != nullptr) { std::memcpy(msg.m_actualData, header, Message::HeaderSize); - packet = enet_packet_create(msg.m_actualData, - Message::HeaderSize + msg.m_length, TferToFlags(mode)); - txBytes += Message::HeaderSize + msg.m_length; + if (encrypt) { + // TODO: don't memcpy, encrypt! + std::memcpy(pktData, msg.m_actualData, pktLen); + } else { + std::memcpy(pktData, msg.m_actualData, pktLen); + } } else { - packet = enet_packet_create(header, - Message::HeaderSize, TferToFlags(mode)); - txBytes += Message::HeaderSize; + if (encrypt) { + // TODO: don't memcpy, encrypt! + std::memcpy(pktData, header, pktLen); + } else { + std::memcpy(pktData, header, pktLen); + } } - //hexDump('S', packet->data, 2+msg.m_length); - enet_peer_send(static_cast(peer.peer), static_cast(chan), packet); + hexDump('S', pktData, pktLen); + enet_peer_send(reinterpret_cast(peer.peer), static_cast(chan), packet); enet_host_flush(host); } diff --git a/src/network/Network.hpp b/src/network/Network.hpp index d790b9c..3537cdc 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -1,10 +1,15 @@ #ifndef NETWORK_HPP #define NETWORK_HPP -#include "../Platform.hpp" -#include "../io/MemoryStream.hpp" -#include + #include +#include + +#include "../Platform.hpp" +#include "../platform/PreprocUtils.hpp" +#include "../crypto/DiffieHellman.hpp" +#include "../io/MemoryStream.hpp" + namespace Diggler { namespace Net { @@ -20,6 +25,8 @@ enum class Tfer { enum class Channels : uint8 { Base = 0, + ConnectionMeta, + ConnectionMetaPlain, Chat, Life, Movement, @@ -35,6 +42,8 @@ enum class MessageType : uint8 { ServerInfo = 220, + ConnectionParam = 200, + PlayerJoin = 1, PlayerUpdate, PlayerQuit, @@ -46,16 +55,6 @@ enum class MessageType : uint8 { NetDisconnect }; -enum PlayerUpdateType : uint8 { - Move, - ChangeTool, - ChangeClass, - ChangeTeam, - Die, - Respawn, - ToolUse -}; - enum QuitReason : uint8 { Quit, Timeout, @@ -82,6 +81,8 @@ protected: public: static constexpr uint HeaderSize = 2; + virtual ~Message() {} + inline MessageType getType() const { return m_type; } inline uint8 getSubtype() const { return m_subtype; } @@ -94,10 +95,8 @@ class InMessage : public Message, public InMemoryStream { protected: friend class Host; Channels m_chan; - void *m_packet; void setType(MessageType type); - void fromData(const void *data, SizeT len, Channels chan = Channels::Base); - void fromPacket(void *packet, Channels chan = Channels::Base); + void fromData(const void *data, SizeT, Channels); void free(); public: @@ -166,20 +165,43 @@ class Exception : public std::exception { using Port = uint16; +class Host; + struct Peer { - void *peer; + Crypto::DiffieHellman::SecretKey connectionSk; + Crypto::DiffieHellman::PublicKey connectionPk; + + Crypto::DiffieHellman::PublicKey remotePk; + Crypto::DiffieHellman::SharedSecret sharedSecret; + + Host &host; + void *const peer; + + Peer(Host&, void*); + nocopy(Peer); + nomove(Peer); bool operator==(const Peer&) const; bool operator!=(const Peer&) const; - void disconnect(); - std::string getHost(); - std::string getIp(); - Port getPort(); + /** + * @brief Disconnects the peer. + * @param data + * Adds the peer for pending disconnection. A NetDisconnect event will be generated once the + * peer has successfully disconnected, and the current Peer object will be deallocated as per + * Host::recv's rules. + */ + void disconnect(uint32 data = 0); + std::string peerHost(); + std::string peerIP(); + Port peerPort(); }; class Host { private: + std::vector m_peersToDelete; + void processPeersToDelete(); + void *host; uint64 rxBytes, txBytes; @@ -187,17 +209,27 @@ private: Host& operator=(Host&) = delete; Host& operator=(const Host&) = delete; + void sendKeyExchange(Peer&); + public: using Timeout = uint32; +public: Host(); ~Host(); void create(Port port = 0, uint maxconn = 64); - Peer connect(const std::string &hostAddr, Port port, Timeout timeout); + Peer& connect(const std::string &hostAddr, Port port, Timeout timeout); void send(Peer &peer, const OutMessage &msg, Tfer mode = Tfer::Rel, Channels chan = Channels::Base); - bool recv(InMessage &msg, Peer &peer, Timeout timeout); - bool recv(InMessage &msg, Timeout timeout = 0); + + // Returns true if a message is available, and put it in msg. + // msg may be modified even if recv returns false. + // If the msg is a NetDisconnect, returned peer object is put on a deletion list and will be + // freed upon the next call to recv. + bool recv(InMessage &msg, Peer **peer, Timeout timeout); + inline bool recv(InMessage &msg, Timeout timeout) { + return recv(msg, nullptr, timeout); + } inline uint64 getRxBytes() const { return rxBytes; diff --git a/src/network/msgtypes/ConnectionParam.cpp b/src/network/msgtypes/ConnectionParam.cpp new file mode 100644 index 0000000..a93f3d1 --- /dev/null +++ b/src/network/msgtypes/ConnectionParam.cpp @@ -0,0 +1,19 @@ +#include "ConnectionParam.hpp" + +namespace Diggler { +namespace Net { +namespace MsgTypes { + +void ConnectionParamDHKeyExchange::writeToMsg(OutMessage &msg) const { + msg.setType(MessageType::ConnectionParam, ConnectionParamSubtype::DHKeyExchange); + + msg.writeData(reinterpret_cast(&pk.bytes), pk.Length); +} + +void ConnectionParamDHKeyExchange::readFromMsg(InMessage &msg) { + msg.readData(&pk.bytes, pk.Length); +} + +} +} +} diff --git a/src/network/msgtypes/ConnectionParam.hpp b/src/network/msgtypes/ConnectionParam.hpp new file mode 100644 index 0000000..a25b863 --- /dev/null +++ b/src/network/msgtypes/ConnectionParam.hpp @@ -0,0 +1,27 @@ +#ifndef DIGGLER_NET_MSGTYPES_CONNECTIONPARAM_HPP +#define DIGGLER_NET_MSGTYPES_CONNECTIONPARAM_HPP + +#include "MsgType.hpp" + +#include "../../crypto/DiffieHellman.hpp" + +namespace Diggler { +namespace Net { +namespace MsgTypes { + +enum class ConnectionParamSubtype : uint8 { + DHKeyExchange +}; + +struct ConnectionParamDHKeyExchange : public MsgType { + Crypto::DiffieHellman::PublicKey pk; + + void writeToMsg(OutMessage&) const override; + void readFromMsg(InMessage&) override; +}; + +} +} +} + +#endif /* DIGGLER_NET_MSGTYPES_CONNECTIONPARAM_HPP */ diff --git a/src/platform/PreprocUtils.hpp b/src/platform/PreprocUtils.hpp new file mode 100644 index 0000000..9210adb --- /dev/null +++ b/src/platform/PreprocUtils.hpp @@ -0,0 +1,12 @@ +#ifndef DIGGLER_PLATFORM_PREPROC_UTILS_HPP +#define DIGGLER_PLATFORM_PREPROC_UTILS_HPP + +#define nocopy(c) c(const c&)=delete;c& operator=(const c&)=delete +#define nomove(c) c(c&&)=delete;c& operator=(c&&)=delete +#define nocopymove(c) nocopy(c);nomove(c) + +#define defaultcopy(c) c(const c&)=default;c& operator=(const c&)=default +#define defaultmove(c) c(c&&)=default;c& operator=(c&&)=default +#define defaultcopymove(c) defaultcopy(c);defaultmove(c) + +#endif /* DIGGLER_PLATFORM_PREPROC_UTILS_HPP */ diff --git a/src/platform/Types.hpp b/src/platform/Types.hpp index 7f58bd5..28be7da 100644 --- a/src/platform/Types.hpp +++ b/src/platform/Types.hpp @@ -1,18 +1,21 @@ #ifndef DIGGLER_PLATFORM_TYPES_HPP #define DIGGLER_PLATFORM_TYPES_HPP +#include +#include + namespace Diggler { -using int32 = int32_t; -using uint = uint32_t; -using uint32 = uint32_t; -using uint64 = uint64_t; -using uint16 = uint16_t; -using uint8 = uint8_t; -using int64 = int64_t; -using int16 = int16_t; -using int8 = int8_t; -using byte = uint8_t; +using uint = std::uint32_t; +using uint32 = std::uint32_t; +using uint64 = std::uint64_t; +using uint16 = std::uint16_t; +using uint8 = std::uint8_t; +using int64 = std::int64_t; +using int32 = std::int32_t; +using int16 = std::int16_t; +using int8 = std::int8_t; +using byte = std::uint8_t; using char32 = char32_t; using char16 = char16_t; diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index a59716c..e6fa382 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -21,8 +21,10 @@ protected: public: virtual ~Renderer() = 0; - ParticlesRenderer *PR; - WorldRenderer *WR; + struct Renderers { + ParticlesRenderer *particles; + WorldRenderer *world; + } renderers; }; inline Renderer::~Renderer() {} diff --git a/src/render/gl/FBO.cpp b/src/render/gl/FBO.cpp index de2a606..213e5af 100644 --- a/src/render/gl/FBO.cpp +++ b/src/render/gl/FBO.cpp @@ -1,5 +1,6 @@ #include "FBO.hpp" +#include "FeatureSupport.hpp" #include "Util.hpp" #ifdef IN_IDE_PARSER @@ -32,13 +33,25 @@ FBO::FBO(int w, int h, Texture::PixelFormat format, bool stencil) : m_hasStencil // Set up renderbuffer to which depth/stencil will be written to glGenRenderbuffers(1, &rboId); glBindRenderbuffer(GL_RENDERBUFFER, rboId); - if (stencil) - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); - else + if (stencil) { + glRenderbufferStorage(GL_RENDERBUFFER, FeatureSupport::FBO_ARB ? GL_DEPTH24_STENCIL8 : + GL_DEPTH24_STENCIL8_OES, w, h); + } else { glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h); + } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *tex, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, stencil ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId); + if (stencil) { + if (FeatureSupport::FBO_ARB) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, + rboId); + } else { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rboId); + } + } else { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId); + } glCheck(); } diff --git a/src/render/gl/FeatureSupport.cpp b/src/render/gl/FeatureSupport.cpp index 3cd9ad3..3c31343 100644 --- a/src/render/gl/FeatureSupport.cpp +++ b/src/render/gl/FeatureSupport.cpp @@ -1,5 +1,7 @@ #include "FeatureSupport.hpp" +#include + namespace Diggler { namespace Render { namespace gl { @@ -11,17 +13,32 @@ bool F::DSA_ARB, F::DSA_EXT, F::DSA, - F::shader_image_load_store; + F::shader_image_load_store, + F::FBO_ARB; void F::probe() { VAO = epoxy_has_gl_extension("GL_ARB_vertex_array_object") or epoxy_has_gl_extension("GL_OES_vertex_array_object"); - DSA_ARB = epoxy_has_gl_extension("ARB_direct_state_access"); - DSA_EXT = epoxy_has_gl_extension("EXT_direct_state_access"); + DSA_ARB = epoxy_has_gl_extension("GL_ARB_direct_state_access"); + DSA_EXT = epoxy_has_gl_extension("GL_EXT_direct_state_access"); DSA = DSA_ARB or DSA_EXT; - shader_image_load_store = epoxy_has_gl_extension("ARB_shader_image_load_store"); + shader_image_load_store = epoxy_has_gl_extension("GL_ARB_shader_image_load_store"); + FBO_ARB = epoxy_has_gl_extension("GL_ARB_framebuffer_object"); } +#define feature(x) if(x){oss<<#x<bind(); glEnableVertexAttribArray(R.att_coord); glEnableVertexAttribArray(R.att_texcoord); glEnableVertexAttribArray(R.att_color); - R.prog->bind(); m_texture->bind(); vbo.bind(); glUniformMatrix4fv(R.uni_mvp, 1, GL_FALSE, glm::value_ptr(matrix)); - glVertexAttribPointer(R.att_coord, 2, GL_INT, GL_FALSE, sizeof(Vertex), 0); + glVertexAttribPointer(R.att_coord, 2, GL_SHORT, GL_FALSE, sizeof(Vertex), 0); glVertexAttribPointer(R.att_texcoord, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, tx)); glVertexAttribPointer(R.att_color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)offsetof(Vertex, r)); glDrawArrays(GL_TRIANGLES, 0, count);