From f942405184c2d6067fb5303b58a225edf7e452b1 Mon Sep 17 00:00:00 2001 From: LaG1924 <12997935+LaG1924@users.noreply.github.com> Date: Sat, 29 Jul 2017 19:55:16 +0500 Subject: 2017-07-29 --- CMakeLists.txt | 4 +- include/AssetManager.hpp | 75 ++++++ include/Core.hpp | 79 ++++++ include/GameState.hpp | 71 +++++ include/Nbt.hpp | 516 ++++++++++++++++++++++++++++++++++++ include/Utility.hpp | 55 ++++ include/Vector.hpp | 116 +++++++++ include/graphics/Gui.hpp | 7 + include/graphics/RenderSection.hpp | 42 +++ include/graphics/Shader.hpp | 24 ++ include/graphics/Texture.hpp | 14 + include/graphics/Widget.hpp | 8 + include/network/Network.hpp | 26 ++ include/network/NetworkClient.hpp | 26 ++ include/network/Packet.hpp | 521 +++++++++++++++++++++++++++++++++++++ include/network/Socket.hpp | 46 ++++ include/network/Stream.hpp | 107 ++++++++ include/world/Block.hpp | 15 ++ include/world/Collision.hpp | 8 + include/world/Section.hpp | 48 ++++ include/world/World.hpp | 38 +++ old/Nbt.hpp | 516 ++++++++++++++++++++++++++++++++++++ old/Utility.hpp | 59 +++++ old/Vector.hpp | 116 +++++++++ old/core/AssetManager.cpp | 181 +++++++++++++ old/core/AssetManager.hpp | 75 ++++++ old/core/Core.cpp | 457 ++++++++++++++++++++++++++++++++ old/core/Core.hpp | 95 +++++++ old/core/Event.cpp | 76 ++++++ old/core/Event.hpp | 96 +++++++ old/graphics/Gui.cpp | 1 + old/graphics/Gui.hpp | 8 + old/graphics/RenderSection.cpp | 356 +++++++++++++++++++++++++ old/graphics/RenderSection.hpp | 52 ++++ old/graphics/Shader.cpp | 115 ++++++++ old/graphics/Shader.hpp | 24 ++ old/graphics/Texture.cpp | 37 +++ old/graphics/Texture.hpp | 14 + old/graphics/Widget.cpp | 1 + old/graphics/Widget.hpp | 8 + old/main.cpp | 35 +++ old/network/Network.cpp | 224 ++++++++++++++++ old/network/Network.hpp | 26 ++ old/network/NetworkClient.cpp | 93 +++++++ old/network/NetworkClient.hpp | 26 ++ old/network/Packet.hpp | 521 +++++++++++++++++++++++++++++++++++++ old/network/Socket.cpp | 29 +++ old/network/Socket.hpp | 46 ++++ old/network/Stream.cpp | 348 +++++++++++++++++++++++++ old/network/Stream.hpp | 107 ++++++++ old/world/Block.cpp | 17 ++ old/world/Block.hpp | 15 ++ old/world/Collision.cpp | 28 ++ old/world/Collision.hpp | 8 + old/world/GameState.cpp | 383 +++++++++++++++++++++++++++ old/world/GameState.hpp | 71 +++++ old/world/Section.cpp | 147 +++++++++++ old/world/Section.hpp | 51 ++++ old/world/World.cpp | 129 +++++++++ old/world/World.hpp | 38 +++ src/AssetManager.cpp | 185 +++++++++++++ src/AssetManager.hpp | 77 ++++++ src/Block.cpp | 17 ++ src/Block.hpp | 15 ++ src/Collision.cpp | 28 ++ src/Collision.hpp | 8 + src/Core.cpp | 59 +++++ src/Core.hpp | 23 ++ src/Event.cpp | 100 +++++++ src/Event.hpp | 116 +++++++++ src/GameState.cpp | 383 +++++++++++++++++++++++++++ src/GameState.hpp | 71 +++++ src/Nbt.hpp | 516 ------------------------------------ src/Network.cpp | 224 ++++++++++++++++ src/Network.hpp | 26 ++ src/NetworkClient.cpp | 93 +++++++ src/NetworkClient.hpp | 26 ++ src/Packet.hpp | 521 +++++++++++++++++++++++++++++++++++++ src/Render.cpp | 137 ++++++++++ src/Render.hpp | 32 +++ src/Renderer.cpp | 15 ++ src/Renderer.hpp | 18 ++ src/RendererSection.cpp | 348 +++++++++++++++++++++++++ src/RendererSection.hpp | 49 ++++ src/RendererWorld.cpp | 1 + src/RendererWorld.hpp | 7 + src/Section.cpp | 147 +++++++++++ src/Section.hpp | 51 ++++ src/Shader.cpp | 115 ++++++++ src/Shader.hpp | 24 ++ src/Socket.cpp | 29 +++ src/Socket.hpp | 46 ++++ src/Stream.cpp | 348 +++++++++++++++++++++++++ src/Stream.hpp | 107 ++++++++ src/Texture.cpp | 37 +++ src/Texture.hpp | 14 + src/Utility.cpp | 73 ++++++ src/Utility.hpp | 65 +++-- src/World.cpp | 129 +++++++++ src/World.hpp | 39 +++ src/core/AssetManager.cpp | 2 +- src/core/AssetManager.hpp | 75 ------ src/core/Core.cpp | 166 +----------- src/core/Core.hpp | 95 ------- src/core/Event.cpp | 76 ------ src/core/Event.hpp | 96 ------- src/graphics/Gui.hpp | 8 - src/graphics/RenderSection.cpp | 54 ++-- src/graphics/RenderSection.hpp | 52 ---- src/graphics/Shader.hpp | 24 -- src/graphics/Texture.hpp | 14 - src/graphics/Widget.hpp | 8 - src/main.cpp | 7 +- src/network/Network.cpp | 8 +- src/network/Network.hpp | 26 -- src/network/NetworkClient.hpp | 26 -- src/network/Packet.hpp | 521 ------------------------------------- src/network/Socket.hpp | 46 ---- src/network/Stream.cpp | 9 +- src/network/Stream.hpp | 107 -------- src/world/Block.hpp | 15 -- src/world/Collision.hpp | 8 - src/world/GameState.cpp | 2 +- src/world/GameState.hpp | 71 ----- src/world/Section.cpp | 50 ++-- src/world/Section.hpp | 51 ---- src/world/World.hpp | 38 --- 127 files changed, 10299 insertions(+), 2150 deletions(-) create mode 100644 include/AssetManager.hpp create mode 100644 include/Core.hpp create mode 100644 include/GameState.hpp create mode 100644 include/Nbt.hpp create mode 100644 include/Utility.hpp create mode 100644 include/Vector.hpp create mode 100644 include/graphics/Gui.hpp create mode 100644 include/graphics/RenderSection.hpp create mode 100644 include/graphics/Shader.hpp create mode 100644 include/graphics/Texture.hpp create mode 100644 include/graphics/Widget.hpp create mode 100644 include/network/Network.hpp create mode 100644 include/network/NetworkClient.hpp create mode 100644 include/network/Packet.hpp create mode 100644 include/network/Socket.hpp create mode 100644 include/network/Stream.hpp create mode 100644 include/world/Block.hpp create mode 100644 include/world/Collision.hpp create mode 100644 include/world/Section.hpp create mode 100644 include/world/World.hpp create mode 100644 old/Nbt.hpp create mode 100644 old/Utility.hpp create mode 100644 old/Vector.hpp create mode 100644 old/core/AssetManager.cpp create mode 100644 old/core/AssetManager.hpp create mode 100644 old/core/Core.cpp create mode 100644 old/core/Core.hpp create mode 100644 old/core/Event.cpp create mode 100644 old/core/Event.hpp create mode 100644 old/graphics/Gui.cpp create mode 100644 old/graphics/Gui.hpp create mode 100644 old/graphics/RenderSection.cpp create mode 100644 old/graphics/RenderSection.hpp create mode 100644 old/graphics/Shader.cpp create mode 100644 old/graphics/Shader.hpp create mode 100644 old/graphics/Texture.cpp create mode 100644 old/graphics/Texture.hpp create mode 100644 old/graphics/Widget.cpp create mode 100644 old/graphics/Widget.hpp create mode 100644 old/main.cpp create mode 100644 old/network/Network.cpp create mode 100644 old/network/Network.hpp create mode 100644 old/network/NetworkClient.cpp create mode 100644 old/network/NetworkClient.hpp create mode 100644 old/network/Packet.hpp create mode 100644 old/network/Socket.cpp create mode 100644 old/network/Socket.hpp create mode 100644 old/network/Stream.cpp create mode 100644 old/network/Stream.hpp create mode 100644 old/world/Block.cpp create mode 100644 old/world/Block.hpp create mode 100644 old/world/Collision.cpp create mode 100644 old/world/Collision.hpp create mode 100644 old/world/GameState.cpp create mode 100644 old/world/GameState.hpp create mode 100644 old/world/Section.cpp create mode 100644 old/world/Section.hpp create mode 100644 old/world/World.cpp create mode 100644 old/world/World.hpp create mode 100644 src/AssetManager.cpp create mode 100644 src/AssetManager.hpp create mode 100644 src/Block.cpp create mode 100644 src/Block.hpp create mode 100644 src/Collision.cpp create mode 100644 src/Collision.hpp create mode 100644 src/Core.cpp create mode 100644 src/Core.hpp create mode 100644 src/Event.cpp create mode 100644 src/Event.hpp create mode 100644 src/GameState.cpp create mode 100644 src/GameState.hpp delete mode 100644 src/Nbt.hpp create mode 100644 src/Network.cpp create mode 100644 src/Network.hpp create mode 100644 src/NetworkClient.cpp create mode 100644 src/NetworkClient.hpp create mode 100644 src/Packet.hpp create mode 100644 src/Render.cpp create mode 100644 src/Render.hpp create mode 100644 src/Renderer.cpp create mode 100644 src/Renderer.hpp create mode 100644 src/RendererSection.cpp create mode 100644 src/RendererSection.hpp create mode 100644 src/RendererWorld.cpp create mode 100644 src/RendererWorld.hpp create mode 100644 src/Section.cpp create mode 100644 src/Section.hpp create mode 100644 src/Shader.cpp create mode 100644 src/Shader.hpp create mode 100644 src/Socket.cpp create mode 100644 src/Socket.hpp create mode 100644 src/Stream.cpp create mode 100644 src/Stream.hpp create mode 100644 src/Texture.cpp create mode 100644 src/Texture.hpp create mode 100644 src/Utility.cpp create mode 100644 src/World.cpp create mode 100644 src/World.hpp delete mode 100644 src/core/AssetManager.hpp delete mode 100644 src/core/Core.hpp delete mode 100644 src/core/Event.cpp delete mode 100644 src/core/Event.hpp delete mode 100644 src/graphics/Gui.hpp delete mode 100644 src/graphics/RenderSection.hpp delete mode 100644 src/graphics/Shader.hpp delete mode 100644 src/graphics/Texture.hpp delete mode 100644 src/graphics/Widget.hpp delete mode 100644 src/network/Network.hpp delete mode 100644 src/network/NetworkClient.hpp delete mode 100644 src/network/Packet.hpp delete mode 100644 src/network/Socket.hpp delete mode 100644 src/network/Stream.hpp delete mode 100644 src/world/Block.hpp delete mode 100644 src/world/Collision.hpp delete mode 100644 src/world/GameState.hpp delete mode 100644 src/world/Section.hpp delete mode 100644 src/world/World.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f165a67..8483add 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,7 @@ include(externalProject) file(GLOB_RECURSE SOURCES "./src/*.cpp") file(GLOB_RECURSE HEADERS "./src/*.hpp") -add_executable(AltCraft ${HEADERS} ${SOURCES}) - -target_include_directories(AltCraft PUBLIC src/) +add_executable(AltCraft ${HEADERS} ${SOURCES} src/Core.cpp src/Core.hpp src/Utility.cpp src/Render.cpp src/Render.hpp src/RendererWorld.cpp src/RendererWorld.hpp src/Renderer.cpp src/Renderer.hpp) ################ # CONFIGURATION diff --git a/include/AssetManager.hpp b/include/AssetManager.hpp new file mode 100644 index 0000000..26c7eca --- /dev/null +++ b/include/AssetManager.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include + +struct TextureCoordinates { + TextureCoordinates(float x = -1, float y = -1, float w = -1, float h = -1) : x(x), y(y), w(w), h(h) {} + + bool operator==(const TextureCoordinates &rhs) const { + return x == rhs.x && + y == rhs.y && + w == rhs.w && + h == rhs.h; + } + + explicit operator bool() const { + return !(*this == TextureCoordinates(-1, -1, -1, -1)); + } + + float x, y, w, h; +}; + +struct BlockTextureId { + //Block sides: 0 - bottom, 1 - top, 2 - north, 3 - south, 4 - west, 5 - east 6 - every side + BlockTextureId(int id = 0, int state = 0, int side = 6) : id(id), state(state), side(side) {} + + int id:9; + int state:4; + int side:3; + + + bool operator<(const BlockTextureId &rhs) const { + if (id < rhs.id) + return true; + if (rhs.id < id) + return false; + if (state < rhs.state) + return true; + if (rhs.state < state) + return false; + return side < rhs.side; + } +}; + +class AssetManager { + Texture *textureAtlas; + std::map assetIds; + std::map assetTextures; + std::map textureAtlasIndexes; +public: + AssetManager(); + + ~AssetManager(); + + void LoadTextureResources(); + + TextureCoordinates GetTextureByAssetName(std::string AssetName); + + std::string GetTextureAssetNameByBlockId(BlockTextureId block); + + GLuint GetTextureAtlas(); + + const std::map &GetTextureAtlasIndexes(); + + void LoadIds(); + + TextureCoordinates GetTextureByBlock(BlockTextureId block); +}; diff --git a/include/Core.hpp b/include/Core.hpp new file mode 100644 index 0000000..3cad094 --- /dev/null +++ b/include/Core.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class Core { + GameState *gameState; + NetworkClient *client; + sf::Window *window; + AssetManager *assetManager; + bool isMouseCaptured = false; + bool isRunning = true; + enum { + MainMenu, + Loading, + Playing, + PauseMenu, + } currentState = Playing; + float mouseXDelta, mouseYDelta; + float deltaTime; + float absTime; + + void RenderWorld(); + + void HandleMouseCapture(); + + void HandleEvents(); + + void InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle); + + void InitGlew(); + + void SetMouseCapture(bool IsCaptured); + + void PrepareToRendering(); + + void RenderFrame(); + + unsigned int width(); + + unsigned int height(); + + void UpdateChunksToRender(); + + void UpdateGameState(); + + std::thread gameStateLoopThread; + + Shader *shader; + //Cube verticies, Cube VAO, Cube UVs, TextureIndexes UboTextureIndexes, TextureData UboTextureIndexes, TextureData2 UboTextureIndexes, Blocks VBO, Models VBO, Line VAO, Lines VBO + GLuint UboTextureIndexes, UboTextureData; + std::vector toRender; + std::map availableChunks; + + int ChunkDistance = 1; + + RenderState renderState; + + double tickRate = 0; + +public: + Core(); + + ~Core(); + + void Exec(); +}; diff --git a/include/GameState.hpp b/include/GameState.hpp new file mode 100644 index 0000000..6741882 --- /dev/null +++ b/include/GameState.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +class GameState { + NetworkClient *nc; +public: + GameState(NetworkClient *NetClient, bool &quit); + + void Update(float deltaTime); + + //Navigation + enum Direction { + FORWARD, BACKWARD, LEFT, RIGHT, JUMP + }; + void HandleMovement(GameState::Direction direction, float deltaTime); + void HandleRotation(double yaw, double pitch); + glm::mat4 GetViewMatrix(); + void updateCameraVectors(); + + float Yaw(); + float Pitch(); + void SetYaw(float yaw); + void SetPitch(float pitch); + + glm::vec3 Position(); + void SetPosition(glm::vec3 Position); + glm::vec3 Front; + glm::vec3 Up; + glm::vec3 Right; + glm::vec3 WorldUp; + + //Everything other + World world; + bool &isRunning; + + std::string g_PlayerUuid; + std::string g_PlayerName; + bool g_IsGameStarted; + int g_PlayerEid; + int g_Gamemode; + int g_Dimension; + byte g_Difficulty; + byte g_MaxPlayers; + std::string g_LevelType; + bool g_ReducedDebugInfo; + Vector g_SpawnPosition; + bool g_PlayerInvulnerable; + bool g_PlayerFlying; + bool g_PlayerAllowFlying; + bool g_PlayerCreativeMode; + float g_PlayerFlyingSpeed; + float g_PlayerFovModifier; + float g_PlayerPitch; + float g_PlayerYaw; + double g_PlayerX; + double g_PlayerY; + double g_PlayerZ; + float g_PlayerHealth; + + bool g_OnGround = true; + double g_PlayerVelocityX = 0; + double g_PlayerVelocityY = 0; + double g_PlayerVelocityZ = 0; +}; diff --git a/include/Nbt.hpp b/include/Nbt.hpp new file mode 100644 index 0000000..03f5af0 --- /dev/null +++ b/include/Nbt.hpp @@ -0,0 +1,516 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace nbt { + enum TagType { + End, //nullptr + Byte, //int8_t + Short, //int16_t + Int, //int32_t + Long, //int64_t + Float, //float + Double, //double + ByteArray, //std::vector + String, //std::string + List, //std::vector + Compound, //std::vector + IntArray, //std::vector + Unknown, //dummy value + }; + + class NbtTag; + + typedef std::vector compound_t; + + typedef std::string string_t; + + typedef std::vector byteArray_t; + + typedef std::vector intArray_t; + + class NbtTag { + TagType type = Unknown; + string_t name = ""; + unsigned char *data = nullptr; + public: + NbtTag(TagType type, string_t name) : type(type), name(name) { + switch (type) { + case End: + data = nullptr; + break; + case Compound: + data = (unsigned char *) new compound_t; + break; + case String: + data = (unsigned char *) new string_t; + break; + case Int: + data = (unsigned char *) new int32_t; + break; + case Long: + data = (unsigned char *) new int64_t; + break; + case Byte: + data = (unsigned char *) new int8_t; + break; + case Short: + data = (unsigned char *) new int16_t; + break; + case Float: + data = (unsigned char *) new float; + break; + case Double: + data = (unsigned char *) new double; + break; + case ByteArray: + data = (unsigned char *) new byteArray_t; + break; + case List: + data = (unsigned char *) new compound_t; + break; + case IntArray: + data = (unsigned char *) new intArray_t; + } + } + + NbtTag(const NbtTag &other) : type(other.type), name(other.name) { + switch (type) { + case Byte: + data = (unsigned char *) new int8_t; + *((int8_t *) data) = *((int8_t *) other.data); + break; + case Short: + data = (unsigned char *) new int16_t; + *((int16_t *) data) = *((int16_t *) other.data); + break; + case Int: + data = (unsigned char *) new int32_t; + *((int32_t *) data) = *((int32_t *) other.data); + break; + case Long: + data = (unsigned char *) new int64_t; + *((int64_t *) data) = *((int64_t *) other.data); + break; + case Float: + data = (unsigned char *) new float; + *((float *) data) = *((float *) other.data); + break; + case Double: + data = (unsigned char *) new double; + *((double *) data) = *((double *) other.data); + break; + case ByteArray: + data = (unsigned char *) new byteArray_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + case String: + data = (unsigned char *) new string_t; + *((std::string *) data) = *((std::string *) other.data); + break; + case List: + data = (unsigned char *) new compound_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + case Compound: + data = (unsigned char *) new compound_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + case IntArray: + data = (unsigned char *) new intArray_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + } + } + + ~NbtTag() { + switch (type) { + case Byte: + delete ((int8_t *) data); + break; + case Short: + delete ((int16_t *) data); + break; + case Int: + delete ((int32_t *) data); + break; + case Long: + delete ((int64_t *) data); + break; + case Float: + delete ((float *) data); + break; + case Double: + delete ((double *) data); + break; + case ByteArray: + delete ((std::vector *) data); + break; + case String: + delete ((std::string *) data); + break; + case List: + delete ((std::vector *) data); + break; + case Compound: + delete ((std::vector *) data); + break; + case IntArray: + delete ((std::vector *) data); + break; + } + }; + + void swap(NbtTag &other) { + std::swap(other.data, data); + std::swap(other.name, name); + std::swap(other.type, type); + } + + NbtTag &operator=(NbtTag other) { + other.swap(*this); + return *this; + } + + TagType GetType() const { + return type; + } + + string_t GetName() const { + return name; + } + + + string_t &GetString() { + string_t &val = *reinterpret_cast(data); + return val; + } + + compound_t &GetCompound() { + std::vector &val = *reinterpret_cast *>(data); + return val; + } + + compound_t &GetList() { + std::vector &val = *reinterpret_cast *>(data); + return val; + } + + int64_t &GetLong() { + int64_t &val = *reinterpret_cast(data); + return val; + } + + float &GetFloat() { + float &val = *reinterpret_cast(data); + return val; + } + + double &GetDouble() { + double &val = *reinterpret_cast(data); + return val; + } + + byteArray_t &GetByteArray() { + auto &val = *reinterpret_cast(data); + return val; + } + + intArray_t &GetIntArray() { + auto &val = *reinterpret_cast(data); + return val; + } + + int16_t &GetShort() { + auto &val = *reinterpret_cast(data); + return val; + } + + int32_t &GetInt() { + auto &val = *reinterpret_cast(data); + return val; + } + + int8_t &GetByte() { + auto &val = *reinterpret_cast(data); + return val; + } + }; + + NbtTag ParseTag(unsigned char *data, size_t &size, TagType listItemType = Unknown) { + size = 0; + TagType type; + if (listItemType == Unknown) { + type = (TagType) *data; + data += 1; + size += 1; + } else + type = listItemType; + string_t name; + if (listItemType == Unknown && type != End) { + short nameLen = *((short *) data); + data += 2; + size += 2; + endswap(&nameLen); + name = std::string((char *) data, nameLen); + data += nameLen; + size += nameLen; + } + NbtTag tag(type, name); + switch (type) { + case Compound: { + do { + size_t s; + tag.GetCompound().push_back(ParseTag(data, s)); + data += s; + size += s; + } while (tag.GetCompound().back().GetType() != End); + tag.GetCompound().pop_back(); + return tag; + } + case String: { + short len = *((short *) data); + data += 2; + size += 2; + endswap(&len); + string_t str((char *) data, len); + data += len; + size += len; + tag.GetString() = str; + return tag; + } + case End: + return tag; + case Long: + tag.GetLong() = *((int64_t *) data); + endswap(&tag.GetLong()); + data += 8; + size += 8; + return tag; + case Short: + tag.GetShort() = *((int16_t *) data); + endswap(&tag.GetShort()); + data += 2; + size += 2; + return tag; + case Float: + tag.GetFloat() = *((float *) data); + endswap(&tag.GetFloat()); + data += 4; + size += 4; + return tag; + case Double: + tag.GetDouble() = *((double *) data); + endswap(&tag.GetDouble()); + data += 8; + size += 8; + return tag; + case Byte: + tag.GetByte() = *((int8_t *) data); + endswap(&tag.GetByte()); + data += 1; + size += 1; + return tag; + case Int: + tag.GetInt() = *((int32_t *) data); + endswap(&tag.GetInt()); + data += 4; + size += 4; + return tag; + case List: { + TagType listType = *((TagType *) data); + data += 1; + size += 1; + int32_t listLength = *((int32_t *) data); + endswap(&listLength); + data += 4; + size += 4; + for (int i = 0; i < listLength; i++) { + size_t s = 0; + std::vector &vec = tag.GetCompound(); + vec.push_back(ParseTag(data, s, listType)); + data += s; + size += s; + } + return tag; + } + case ByteArray: { + int32_t arrLength = *((int32_t *) data); + endswap(&arrLength); + data += 4; + size += 4; + for (int i = 0; i < arrLength; i++) { + signed char val = (signed char) data[i]; + std::vector &vec = tag.GetByteArray(); + vec.push_back(val); + } + data += arrLength; + size += arrLength; + return tag; + } + default: + throw 13; + } + } + + NbtTag ParseTag(unsigned char *data, size_t *optionalSize = nullptr) { + size_t s = 0; + size_t &size = (optionalSize ? *optionalSize : s); + return ParseTag(data, size); + } + + std::vector Decompress(unsigned char *data, size_t dataLen) { + const size_t decompBuffSize = 1024 * 16; + unsigned char *decompBuff = new unsigned char[decompBuffSize]; + std::vector uncompressed; + for (int i = 0; i < decompBuffSize; i++) + decompBuff[i] = 0; + + + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = data; + stream.avail_in = dataLen; + stream.next_out = decompBuff; + stream.avail_out = decompBuffSize; + + if (inflateInit2(&stream, 15 + 32) != Z_OK) { + delete[] decompBuff; + throw 171; + } + + int res; + do { + stream.avail_out = decompBuffSize; + + switch ((res = inflate(&stream, Z_NO_FLUSH))) { + case Z_MEM_ERROR: + throw 172; + case Z_DATA_ERROR: + throw 173; + case Z_NEED_DICT: + throw 174; + } + + uncompressed.resize(uncompressed.size() + decompBuffSize); + std::copy(decompBuff, decompBuff + decompBuffSize, uncompressed.end() - decompBuffSize); + } while (stream.avail_out == 0); + if (res != Z_STREAM_END) + throw 175; + if (inflateEnd(&stream) != Z_OK) + throw 176; + delete[] decompBuff; + return uncompressed; + } + + NbtTag ParseCompressed(unsigned char *data, size_t dataLen) { + auto uncompressed = Decompress(data, dataLen); + NbtTag root = ParseTag(uncompressed.data()); + return root; + } + + NbtTag Parse(unsigned char *data, size_t dataLen) { + bool isCompressed = *data != 10; + if (isCompressed) + return ParseCompressed(data, dataLen); + else + return ParseTag(data); + } + + void PrintTree(NbtTag &tree, int deepness = 0, std::ostream &ostream = std::cout) { + ostream << std::string(deepness, '\t') << "Tag "; + switch (tree.GetType()) { + case Byte: + ostream << "byte"; + break; + case Short: + ostream << "short"; + break; + case Int: + ostream << "int"; + break; + case Long: + ostream << "long"; + break; + case Float: + ostream << "float"; + break; + case Double: + ostream << "double"; + break; + case ByteArray: + ostream << "byte array"; + break; + case String: + ostream << "string"; + break; + case List: + ostream << "list"; + break; + case Compound: + ostream << "compound"; + break; + case IntArray: + ostream << "int array"; + break; + case End: + ostream << "end"; + break; + } + if (tree.GetName().length() > 0) + ostream << " (" << tree.GetName() << ")"; + ostream << ": "; + + if (tree.GetType() == Compound || tree.GetType() == List) { + std::vector &vec = (tree.GetType() == Compound ? tree.GetCompound() : tree.GetList()); + ostream << vec.size() << " entries {" << std::endl; + for (auto it = vec.begin(); it != vec.end(); ++it) { + PrintTree(*it, deepness + 1, std::cout); + } + ostream << std::string(deepness, '\t') << "}" << std::endl; + return; + } else { + switch (tree.GetType()) { + case Int: + ostream << tree.GetInt(); + break; + case String: + ostream << "\"" << tree.GetString() << "\""; + break; + case Double: + ostream << tree.GetDouble(); + break; + case Float: + ostream << tree.GetFloat(); + break; + case Short: + ostream << tree.GetShort(); + break; + case Byte: + ostream << (int) tree.GetByte(); + break; + case Long: + ostream << tree.GetLong(); + break; + case ByteArray: + ostream << "[" << tree.GetByteArray().size() << " bytes]: "; + for (int i = 0; i < (tree.GetByteArray().size() > 10 ? 10 : tree.GetByteArray().size()); i++) { + ostream << std::hex << "0x" << (tree.GetByteArray()[i] > 15 ? "" : "0") + << (int) tree.GetByteArray()[i] + << std::dec << " "; + } + break; + case IntArray: + break; + } + ostream << std::endl; + } + } +} \ No newline at end of file diff --git a/include/Utility.hpp b/include/Utility.hpp new file mode 100644 index 0000000..92e924f --- /dev/null +++ b/include/Utility.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include + +template +void endswap(T *objp) { + unsigned char *memp = reinterpret_cast(objp); + std::reverse(memp, memp + sizeof(T)); +} + +template +void endswap(T &obj) { + unsigned char *raw = reinterpret_cast(&obj); + std::reverse(raw, raw + sizeof(T)); +} + +inline void endswap(unsigned char *arr, size_t arrLen) { + std::reverse(arr, arr + arrLen); +} + +inline GLenum glCheckError_(const char *file, int line) { + GLenum errorCode; + while ((errorCode = glGetError()) != GL_NO_ERROR) { + std::string error; + switch (errorCode) { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + error = "STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + error = "STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + LOG(ERROR) << "OpenGL error: " << error << " at " << file << ":" << line; + } + return errorCode; +} + +#define glCheckError() glCheckError_(__FILE__, __LINE__) \ No newline at end of file diff --git a/include/Vector.hpp b/include/Vector.hpp new file mode 100644 index 0000000..a2d5c6a --- /dev/null +++ b/include/Vector.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include + +#include + +template +class Vector3 { + T x, y, z; +public: + Vector3(T X = 0, T Y = 0, T Z = 0) : x(X), y(Y), z(Z) {} + + Vector3(const Vector3 &rhs) : x(rhs.x), y(rhs.y), z(rhs.z) {} + + ~Vector3() = default; + + void SetX(T X) { x = X; } + + void SetY(T Y) { y = Y; } + + void SetZ(T Z) { z = Z; } + + T GetX() const { return x; } + + T GetY() const { return y; } + + T GetZ() const { return z; } + + double GetMagnitude() const { return std::sqrt(std::pow(x, 2) + std::pow(y, 2) + std::pow(z, 2)); } + + operator glm::vec3() const { + return glm::vec3(x, y, z); + } + + void swap(Vector3 &rhs) { + std::swap(x, rhs.x); + std::swap(y, rhs.y); + std::swap(z, rhs.z); + } + + Vector3 &operator=(Vector3 rhs) { + rhs.swap(*this); + return *this; + } + + Vector3 operator*(T rhs) const { + return Vector3( + x * rhs, + y * rhs, + z * rhs + ); + } + + Vector3 operator/(T rhs) const { + return Vector3( + x / rhs, + y / rhs, + z / rhs + ); + } + + Vector3 operator+(const Vector3 &rhs) const { + return Vector3( + x + rhs.x, + y + rhs.y, + z + rhs.z + ); + } + + Vector3 operator-(const Vector3 &rhs) const { + return Vector3( + x - rhs.x, + y - rhs.y, + z - rhs.z + ); + } + + Vector3 operator*(const Vector3 &rhs) const { + return Vector3( + x * rhs.x, + y * rhs.y, + z * rhs.z + ); + } + + Vector3 operator/(const Vector3 &rhs) const { + return Vector3( + x / rhs.x, + y / rhs.y, + z / rhs.z + ); + } + + bool operator==(const Vector3 &rhs) const { + return (x == rhs.x && y == rhs.y && z == rhs.z); + } + + bool operator!=(const Vector3 &rhs) const { + return !(*this == rhs); + } + + bool operator<(const Vector3 &rhs) const { + return std::tie(x, y, z) < std::tie(rhs.x, rhs.y, rhs.z); + } + + + friend std::ostream &operator<<(std::ostream &os, const Vector3 &vector3) { + os << vector3.x << ", " << vector3.y << ", " << vector3.z; + return os; + } +}; + +typedef Vector3 VectorF; +typedef Vector3 Vector; \ No newline at end of file diff --git a/include/graphics/Gui.hpp b/include/graphics/Gui.hpp new file mode 100644 index 0000000..641b941 --- /dev/null +++ b/include/graphics/Gui.hpp @@ -0,0 +1,7 @@ +#pragma once + +class Gui { + +public: + int WHY=0; +}; diff --git a/include/graphics/RenderSection.hpp b/include/graphics/RenderSection.hpp new file mode 100644 index 0000000..953d7ea --- /dev/null +++ b/include/graphics/RenderSection.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class RenderState { + GLuint ActiveVao; + GLuint ActiveShader; +public: + void SetActiveVao(GLuint Vao); + void SetActiveShader(GLuint Shader); +}; + +class RenderSection { + Vector sectionPosition; + World *world; + GLuint Vao, VboTextures, VboModels, VboColors; + + static GLuint VboVertices, VboUvs; + static std::map refCounterVbo; + static std::map refCounterVao; + + size_t numOfFaces; + +public: + RenderSection(World *world, Vector position); + RenderSection(const RenderSection &other); + ~RenderSection(); + + void UpdateState(const std::map &textureAtlas); + void Render(RenderState &state); + + Section *GetSection(); +}; \ No newline at end of file diff --git a/include/graphics/Shader.hpp b/include/graphics/Shader.hpp new file mode 100644 index 0000000..17a434e --- /dev/null +++ b/include/graphics/Shader.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include +#include + +class Shader +{ +private: + const GLchar *vertex; + const GLchar *fragment; +public: + // Идентификатор программы + GLuint Program; + // Конструктор считывает и собирает шейдер + Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const GLchar* geometryPath = nullptr); + // Использование программы + void Use(); + + void Reload(); +}; \ No newline at end of file diff --git a/include/graphics/Texture.hpp b/include/graphics/Texture.hpp new file mode 100644 index 0000000..277806a --- /dev/null +++ b/include/graphics/Texture.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +class Texture { + Texture(Texture&); + Texture&operator=(Texture&); +public: + GLuint texture; + Texture(std::string filename, GLenum textureWrapping = GL_CLAMP_TO_BORDER, GLenum textureFiltering = GL_NEAREST); + ~Texture(); +}; \ No newline at end of file diff --git a/include/graphics/Widget.hpp b/include/graphics/Widget.hpp new file mode 100644 index 0000000..c4d5dc1 --- /dev/null +++ b/include/graphics/Widget.hpp @@ -0,0 +1,8 @@ +#pragma once + +class Widget { + unsigned int x,y,w,h; +public: + Widget(Widget *parent); + ~Widget(); +}; diff --git a/include/network/Network.hpp b/include/network/Network.hpp new file mode 100644 index 0000000..1281289 --- /dev/null +++ b/include/network/Network.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "Socket.hpp" +#include "Packet.hpp" + +enum ConnectionState { + Handshaking, + Login, + Play, + Status, +}; + +class Network { + Socket *socket; + StreamSocket *stream; + + std::shared_ptr ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream); +public: + Network(std::string address, unsigned short port); + ~Network(); + + std::shared_ptr ReceivePacket(ConnectionState state = Play); + void SendPacket(Packet &packet); + std::shared_ptr ParsePacketPlay(PacketNamePlayCB id); +}; \ No newline at end of file diff --git a/include/network/NetworkClient.hpp b/include/network/NetworkClient.hpp new file mode 100644 index 0000000..22b1b22 --- /dev/null +++ b/include/network/NetworkClient.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include + +class NetworkClient { + Network network; + std::thread networkThread; + std::mutex toSendMutex; + std::mutex toReceiveMutex; + std::queue > toSend; + std::queue > toReceive; + bool isActive=true; + bool &isRunning; + ConnectionState state; + void NetworkLoop(); +public: + NetworkClient(std::string address, unsigned short port, std::string username, bool &quit); + ~NetworkClient(); + + std::shared_ptr ReceivePacket(); + void SendPacket(std::shared_ptr packet); +}; \ No newline at end of file diff --git a/include/network/Packet.hpp b/include/network/Packet.hpp new file mode 100644 index 0000000..685e3da --- /dev/null +++ b/include/network/Packet.hpp @@ -0,0 +1,521 @@ +#pragma once + +#include + +#include + +enum PacketNameLoginSB { + LoginStart = 0x00, + EncryptionResponse = 0x01, +}; +enum PacketNamePlaySB { + TeleportConfirm, + PrepareCraftingGrid, + TabCompleteSB, + ChatMessageSB, + ClientStatus, + ClientSettings, + ConfirmTransactionSB, + EnchantItem, + ClickWindow, + CloseWindowSB, + PluginMessageSB, + UseEntity, + KeepAliveSB, + Player, + PlayerPosition, + PlayerPositionAndLookSB, + PlayerLook, + VehicleMoveSB, + SteerBoat, + PlayerAbilitiesSB, + PlayerDigging, + EntityAction, + SteerVehicle, + CraftingBookData, + ResourcePackStatus, + AdvancementTab, + HeldItemChangeSB, + CreativeInventoryAction, + UpdateSign, + AnimationSB, + Spectate, + PlayerBlockPlacement, + UseItem, +}; + +enum PacketNameHandshakingCB { + Handshake = 0x00, +}; +enum PacketNameLoginCB { + Disconnect = 0x00, + EncryptionRequest = 0x01, + LoginSuccess = 0x02, + SetCompression = 0x03, +}; +enum PacketNamePlayCB { + SpawnObject = 0x00, + SpawnExperienceOrb, + SpawnGlobalEntity, + SpawnMob, + SpawnPainting, + SpawnPlayer, + AnimationCB, + Statistics, + BlockBreakAnimation, + UpdateBlockEntity, + BlockAction, + BlockChange, + BossBar, + ServerDifficulty, + TabCompleteCB, + ChatMessageCB, + MultiBlockChange, + ConfirmTransactionCB, + CloseWindowCB, + OpenWindow, + WindowItems, + WindowProperty, + SetSlot, + SetCooldown, + PluginMessageCB, + NamedSoundEffect, + DisconnectPlay, + EntityStatus, + Explosion, + UnloadChunk, + ChangeGameState, + KeepAliveCB, + ChunkData, + Effect, + Particle, + JoinGame, + Map, + EntityRelativeMove, + EntityLookAndRelativeMove, + EntityLook, + Entity, + VehicleMove, + OpenSignEditor, + PlayerAbilitiesCB, + CombatEvent, + PlayerListItem, + PlayerPositionAndLookCB, + UseBed, + UnlockRecipes, + DestroyEntities, + RemoveEntityEffect, + ResourcePackSend, + Respawn, + EntityHeadLook, + SelectAdvancementTab, + WorldBorder, + Camera, + HeldItemChangeCB, + DisplayScoreboard, + EntityMetadata, + AttachEntity, + EntityVelocity, + EntityEquipment, + SetExperience, + UpdateHealth, + ScoreboardObjective, + SetPassengers, + Teams, + UpdateScore, + SpawnPosition, + TimeUpdate, + Title, + SoundEffect, + PlayerListHeaderAndFooter, + CollectItem, + EntityTeleport, + Advancements, + EntityProperties, + EntityEffect, +}; + +struct Packet { + virtual ~Packet() = default; + virtual void ToStream(StreamOutput *stream) = 0; + virtual void FromStream(StreamInput *stream) = 0; + virtual int GetPacketId() = 0; +}; + +struct PacketHandshake : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(protocolVersion); + stream->WriteString(serverAddress); + stream->WriteUShort(serverPort); + stream->WriteVarInt(nextState); + } + + void FromStream(StreamInput *stream) override { + protocolVersion = stream->ReadVarInt(); + serverAddress = stream->ReadString(); + serverPort = stream->ReadUShort(); + nextState = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNameHandshakingCB::Handshake; + } + + int protocolVersion; + std::string serverAddress; + unsigned short serverPort; + int nextState; +}; + +struct PacketLoginStart : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginSB::LoginStart; + } + + std::string Username; +}; + +struct PacketLoginSuccess : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Uuid); + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Uuid = stream->ReadString(); + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginCB::LoginSuccess; + } + + std::string Uuid; + std::string Username; +}; + +struct PacketJoinGame : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(EntityId); + stream->WriteUByte(Gamemode); + stream->WriteInt(Dimension); + stream->WriteUByte(Difficulty); + stream->WriteUByte(MaxPlayers); + stream->WriteString(LevelType); + stream->WriteBool(ReducedDebugInfo); + } + + void FromStream(StreamInput *stream) override { + EntityId = stream->ReadInt(); + Gamemode = stream->ReadUByte(); + Dimension = stream->ReadInt(); + Difficulty = stream->ReadUByte(); + MaxPlayers = stream->ReadUByte(); + LevelType = stream->ReadString(); + ReducedDebugInfo = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlayCB::JoinGame; + } + + int EntityId; + unsigned char Gamemode; + int Dimension; + unsigned char Difficulty; + unsigned char MaxPlayers; + std::string LevelType; + bool ReducedDebugInfo; +}; + +struct PacketDisconnectPlay : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Reason); //TODO: Implement chat-wrapper + } + + void FromStream(StreamInput *stream) override { + Reason = stream->ReadChat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::DisconnectPlay; + } + + std::string Reason; +}; + +struct PacketSpawnPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WritePosition(Location); + } + + void FromStream(StreamInput *stream) override { + Location = stream->ReadPosition(); + } + + int GetPacketId() override { + return PacketNamePlayCB::SpawnPosition; + } + + Vector Location; +}; + +struct PacketKeepAliveCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::KeepAliveCB; + } + + int KeepAliveId; +}; + +struct PacketKeepAliveSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::KeepAliveSB; + } + + int KeepAliveId; + + PacketKeepAliveSB(int KeepAliveId) : KeepAliveId(KeepAliveId) {} +}; + +struct PacketPlayerPositionAndLookCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(Y); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteUByte(Flags); + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + Y = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + Flags = stream->ReadUByte(); + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::PlayerPositionAndLookCB; + } + + double X; + double Y; + double Z; + float Yaw; + float Pitch; + unsigned char Flags; + int TeleportId; +}; + +struct PacketTeleportConfirm : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::TeleportConfirm; + } + + int TeleportId; + + PacketTeleportConfirm(int TeleportId) : TeleportId(TeleportId) {} +}; + +struct PacketClientStatus : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(ActionId); + } + + void FromStream(StreamInput *stream) override { + ActionId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::ClientStatus; + } + + int ActionId; + + PacketClientStatus(int ActionId) : ActionId(ActionId) {} +}; + +struct PacketPlayerPositionAndLookSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + FeetY = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPositionAndLookSB; + } + + + double X; + double FeetY; + double Z; + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerPositionAndLookSB(double X, double FeetY, double Z, + float Yaw, float Pitch, bool OnGround) : X(X), FeetY(FeetY), Z(Z), Yaw(Yaw), + Pitch(Pitch), OnGround(OnGround) {} +}; + +struct PacketChunkData : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(ChunkX); + stream->WriteInt(ChunkZ); + stream->WriteBool(GroundUpContinuous); + stream->WriteInt(PrimaryBitMask); + stream->WriteVarInt(Data.size()); + stream->WriteByteArray(Data); + stream->WriteVarInt(BlockEntities.size()); + LOG(FATAL) << "Serializing unimplemented packet"; + } + + void FromStream(StreamInput *stream) override { + ChunkX = stream->ReadInt(); + ChunkZ = stream->ReadInt(); + GroundUpContinuous = stream->ReadBool(); + PrimaryBitMask = stream->ReadVarInt(); + int Size = stream->ReadVarInt(); + Data = stream->ReadByteArray(Size); + int NumberOfBlockEntities = stream->ReadVarInt(); //TODO: Need NBT + for (int i = 0; i < NumberOfBlockEntities; i++) { + //BlockEntities[i] = stream->ReadNbt(); + } + } + + int GetPacketId() override { + return PacketNamePlayCB::ChunkData; + } + + int ChunkX; + int ChunkZ; + bool GroundUpContinuous; + int PrimaryBitMask; + //int Size; + std::vector Data; + //int NumberOfBlockEntities; + std::vector BlockEntities; //TODO: Replace int with NbtTag and implement NbtTree +}; + +struct PacketPlayerPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + FeetY = stream->ReadDouble(); + Z = stream->ReadDouble(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPosition; + } + + double X; + double FeetY; + double Z; + bool OnGround; + + PacketPlayerPosition(double X, double Y, double Z, bool ground) : X(X), FeetY(Y), Z(Z), OnGround(ground) {} +}; + +struct PacketPlayerLook : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerLook; + } + + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerLook(float Yaw, float Pitch, bool ground) : Yaw(Yaw), Pitch(Pitch), OnGround(ground) {} +}; + +struct PacketUpdateHealth : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Health); + stream->WriteVarInt(Food); + stream->WriteFloat(FoodSaturation); + } + + void FromStream(StreamInput *stream) override { + Health = stream->ReadFloat(); + Food = stream->ReadVarInt(); + FoodSaturation = stream->ReadFloat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::UpdateHealth; + } + + float Health; + int Food; + float FoodSaturation; +}; \ No newline at end of file diff --git a/include/network/Socket.hpp b/include/network/Socket.hpp new file mode 100644 index 0000000..48bcad9 --- /dev/null +++ b/include/network/Socket.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +/** + * Platform independent class for working with platform dependent hardware socket + * @brief Wrapper around raw sockets + * @warning Connection state is based on lifetime of Socket object instance, ie connected at ctor and disconnect at dtor + * @todo Replace SFML's socket with WinSock and POSIX's socket implementation + */ +class Socket { + sf::TcpSocket socket; +public: + /** + * Constructs Socket class instance from IP's string and Port number and connects to remote server + * @param[in] address IP address of remote server. String should be ANSI and contains 4 one-byte values separated by dots + * @param[in] port target port of remote server to connect + * @throw std::runtime_error if connection is failed + */ + Socket(std::string address, unsigned short port); + + /** + * Destruct Socket instance and disconnect from server + * @warning There is no way to force disconnect, except use delete for manually allocated objects and scope of visibility for variables on stack + */ + ~Socket(); + + /** + * Reads data from socket and write to buffer + * @warning This is blocking function, and execution flow will not be returned until all required data is sended + * @warning Reported buffer length must be <= actual size of buffer, or memory corruption will be caused + * @param[out] buffPtr Pointer to buffer, where data must be placed + * @param[in] buffLen Length of data, that must be readed from server and writed to buffer + */ + void Read(unsigned char *buffPtr, size_t buffLen); + + /** + * Writes data from buffer to socket + * @warning This is blocking function, and execution flow will not be returned until all required data is received + * @param[in] buffPtr Pointer to buffer that contain data to send + * @param[in] buffLen Length of buffer + */ + void Write(unsigned char *buffPtr, size_t buffLen); +}; \ No newline at end of file diff --git a/include/network/Stream.hpp b/include/network/Stream.hpp new file mode 100644 index 0000000..a24dfbe --- /dev/null +++ b/include/network/Stream.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +class Stream { +public: + virtual ~Stream() {}; +}; + +class StreamInput : Stream { + virtual void ReadData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamInput() = default; + bool ReadBool(); + signed char ReadByte(); + unsigned char ReadUByte(); + short ReadShort(); + unsigned short ReadUShort(); + int ReadInt(); + long long ReadLong(); + float ReadFloat(); + double ReadDouble(); + std::string ReadString(); + std::string ReadChat(); + int ReadVarInt(); + long long ReadVarLong(); + std::vector ReadEntityMetadata(); + std::vector ReadSlot(); + std::vector ReadNbtTag(); + Vector ReadPosition(); + unsigned char ReadAngle(); + std::vector ReadUuid(); + std::vector ReadByteArray(size_t arrLength); +}; + +class StreamOutput : Stream { + virtual void WriteData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamOutput() = default; + void WriteBool(bool value); + void WriteByte(signed char value); + void WriteUByte(unsigned char value); + void WriteShort(short value); + void WriteUShort(unsigned short value); + void WriteInt(int value); + void WriteLong(long long value); + void WriteFloat(float value); + void WriteDouble(double value); + void WriteString(std::string value); + void WriteChat(std::string value); + void WriteVarInt(int value); + void WriteVarLong(long long value); + void WriteEntityMetadata(std::vector value); + void WriteSlot(std::vector value); + void WriteNbtTag(std::vector value); + void WritePosition(Vector value); + void WriteAngle(unsigned char value); + void WriteUuid(std::vector value); + void WriteByteArray(std::vector value); +}; + +class StreamBuffer : public StreamInput, public StreamOutput { + unsigned char *buffer; + unsigned char *bufferPtr; + size_t bufferLength; + + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + +public: + StreamBuffer(unsigned char *data, size_t dataLen); + StreamBuffer(size_t bufferLen); + ~StreamBuffer(); + + std::vector GetBuffer(); +}; + +class StreamCounter : public StreamOutput { + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + + size_t size; +public: + StreamCounter(size_t initialSize = 0); + ~StreamCounter(); + + size_t GetCountedSize(); +}; + +class StreamSocket : public StreamInput, public StreamOutput { + Socket *socket; + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; +public: + StreamSocket(Socket *socketPtr); + ~StreamSocket() = default; +}; \ No newline at end of file diff --git a/include/world/Block.hpp b/include/world/Block.hpp new file mode 100644 index 0000000..2f823fe --- /dev/null +++ b/include/world/Block.hpp @@ -0,0 +1,15 @@ +#pragma once + +struct Block { + Block(); + + Block(unsigned short id, unsigned char state); + + ~Block(); + + unsigned short id : 13; + unsigned char state : 4; + //unsigned char light:4; +}; + +bool operator<(const Block &lhs, const Block &rhs); \ No newline at end of file diff --git a/include/world/Collision.hpp b/include/world/Collision.hpp new file mode 100644 index 0000000..b88fbf7 --- /dev/null +++ b/include/world/Collision.hpp @@ -0,0 +1,8 @@ +#pragma once + +struct AABB { + double x,y,z; + double w,l,h; +}; + +bool TestCollision(AABB first, AABB second); \ No newline at end of file diff --git a/include/world/Section.hpp b/include/world/Section.hpp new file mode 100644 index 0000000..139b5b5 --- /dev/null +++ b/include/world/Section.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + +const int SECTION_WIDTH = 16; +const int SECTION_LENGTH = 16; +const int SECTION_HEIGHT = 16; + +class Section { + std::vector m_palette; + byte *m_dataBlocks = nullptr; + size_t m_dataBlocksLen; + byte *m_dataLight = nullptr; + byte *m_dataSkyLight = nullptr; + byte m_bitsPerBlock = 0; + std::vector m_blocks; + std::condition_variable parseWaiter; + + Section(); + + Vector worldPosition; + +public: + void Parse(); + + Section(Vector position, byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, byte bitsPerBlock, + std::vector palette); + + ~Section(); + + Block &GetBlock(Vector pos); + + Section &operator=(Section other); + + friend void swap(Section &a, Section &b); + + Section(const Section &other); + + Vector GetPosition(); +}; \ No newline at end of file diff --git a/include/world/World.hpp b/include/world/World.hpp new file mode 100644 index 0000000..6b09f1f --- /dev/null +++ b/include/world/World.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +class World { + std::map sections; + std::map sectionMutexes; + int dimension = 0; + + Section ParseSection(StreamInput *data, Vector position); + + World(const World &other); + World &operator=(const World &other); +public: + World(); + + ~World(); + + void ParseChunkData(std::shared_ptr packet); + + bool isPlayerCollides(double X, double Y, double Z); + + Block &GetBlock(Vector pos); + + std::vector GetSectionsList(); + + Section &GetSection(Vector sectionPos); + + glm::vec3 Raycast(glm::vec3 position, glm::vec3 direction, float maxLength = 1000.0f, float minPrecision = 0.01f); +}; \ No newline at end of file diff --git a/old/Nbt.hpp b/old/Nbt.hpp new file mode 100644 index 0000000..a90464d --- /dev/null +++ b/old/Nbt.hpp @@ -0,0 +1,516 @@ +#pragma once + +#include +#include +#include +#include + +#include "Utility.hpp" + +namespace nbt { + enum TagType { + End, //nullptr + Byte, //int8_t + Short, //int16_t + Int, //int32_t + Long, //int64_t + Float, //float + Double, //double + ByteArray, //std::vector + String, //std::string + List, //std::vector + Compound, //std::vector + IntArray, //std::vector + Unknown, //dummy value + }; + + class NbtTag; + + typedef std::vector compound_t; + + typedef std::string string_t; + + typedef std::vector byteArray_t; + + typedef std::vector intArray_t; + + class NbtTag { + TagType type = Unknown; + string_t name = ""; + unsigned char *data = nullptr; + public: + NbtTag(TagType type, string_t name) : type(type), name(name) { + switch (type) { + case End: + data = nullptr; + break; + case Compound: + data = (unsigned char *) new compound_t; + break; + case String: + data = (unsigned char *) new string_t; + break; + case Int: + data = (unsigned char *) new int32_t; + break; + case Long: + data = (unsigned char *) new int64_t; + break; + case Byte: + data = (unsigned char *) new int8_t; + break; + case Short: + data = (unsigned char *) new int16_t; + break; + case Float: + data = (unsigned char *) new float; + break; + case Double: + data = (unsigned char *) new double; + break; + case ByteArray: + data = (unsigned char *) new byteArray_t; + break; + case List: + data = (unsigned char *) new compound_t; + break; + case IntArray: + data = (unsigned char *) new intArray_t; + } + } + + NbtTag(const NbtTag &other) : type(other.type), name(other.name) { + switch (type) { + case Byte: + data = (unsigned char *) new int8_t; + *((int8_t *) data) = *((int8_t *) other.data); + break; + case Short: + data = (unsigned char *) new int16_t; + *((int16_t *) data) = *((int16_t *) other.data); + break; + case Int: + data = (unsigned char *) new int32_t; + *((int32_t *) data) = *((int32_t *) other.data); + break; + case Long: + data = (unsigned char *) new int64_t; + *((int64_t *) data) = *((int64_t *) other.data); + break; + case Float: + data = (unsigned char *) new float; + *((float *) data) = *((float *) other.data); + break; + case Double: + data = (unsigned char *) new double; + *((double *) data) = *((double *) other.data); + break; + case ByteArray: + data = (unsigned char *) new byteArray_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + case String: + data = (unsigned char *) new string_t; + *((std::string *) data) = *((std::string *) other.data); + break; + case List: + data = (unsigned char *) new compound_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + case Compound: + data = (unsigned char *) new compound_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + case IntArray: + data = (unsigned char *) new intArray_t; + *((std::vector *) data) = *((std::vector *) other.data); + break; + } + } + + ~NbtTag() { + switch (type) { + case Byte: + delete ((int8_t *) data); + break; + case Short: + delete ((int16_t *) data); + break; + case Int: + delete ((int32_t *) data); + break; + case Long: + delete ((int64_t *) data); + break; + case Float: + delete ((float *) data); + break; + case Double: + delete ((double *) data); + break; + case ByteArray: + delete ((std::vector *) data); + break; + case String: + delete ((std::string *) data); + break; + case List: + delete ((std::vector *) data); + break; + case Compound: + delete ((std::vector *) data); + break; + case IntArray: + delete ((std::vector *) data); + break; + } + }; + + void swap(NbtTag &other) { + std::swap(other.data, data); + std::swap(other.name, name); + std::swap(other.type, type); + } + + NbtTag &operator=(NbtTag other) { + other.swap(*this); + return *this; + } + + TagType GetType() const { + return type; + } + + string_t GetName() const { + return name; + } + + + string_t &GetString() { + string_t &val = *reinterpret_cast(data); + return val; + } + + compound_t &GetCompound() { + std::vector &val = *reinterpret_cast *>(data); + return val; + } + + compound_t &GetList() { + std::vector &val = *reinterpret_cast *>(data); + return val; + } + + int64_t &GetLong() { + int64_t &val = *reinterpret_cast(data); + return val; + } + + float &GetFloat() { + float &val = *reinterpret_cast(data); + return val; + } + + double &GetDouble() { + double &val = *reinterpret_cast(data); + return val; + } + + byteArray_t &GetByteArray() { + auto &val = *reinterpret_cast(data); + return val; + } + + intArray_t &GetIntArray() { + auto &val = *reinterpret_cast(data); + return val; + } + + int16_t &GetShort() { + auto &val = *reinterpret_cast(data); + return val; + } + + int32_t &GetInt() { + auto &val = *reinterpret_cast(data); + return val; + } + + int8_t &GetByte() { + auto &val = *reinterpret_cast(data); + return val; + } + }; + + NbtTag ParseTag(unsigned char *data, size_t &size, TagType listItemType = Unknown) { + size = 0; + TagType type; + if (listItemType == Unknown) { + type = (TagType) *data; + data += 1; + size += 1; + } else + type = listItemType; + string_t name; + if (listItemType == Unknown && type != End) { + short nameLen = *((short *) data); + data += 2; + size += 2; + endswap(&nameLen); + name = std::string((char *) data, nameLen); + data += nameLen; + size += nameLen; + } + NbtTag tag(type, name); + switch (type) { + case Compound: { + do { + size_t s; + tag.GetCompound().push_back(ParseTag(data, s)); + data += s; + size += s; + } while (tag.GetCompound().back().GetType() != End); + tag.GetCompound().pop_back(); + return tag; + } + case String: { + short len = *((short *) data); + data += 2; + size += 2; + endswap(&len); + string_t str((char *) data, len); + data += len; + size += len; + tag.GetString() = str; + return tag; + } + case End: + return tag; + case Long: + tag.GetLong() = *((int64_t *) data); + endswap(&tag.GetLong()); + data += 8; + size += 8; + return tag; + case Short: + tag.GetShort() = *((int16_t *) data); + endswap(&tag.GetShort()); + data += 2; + size += 2; + return tag; + case Float: + tag.GetFloat() = *((float *) data); + endswap(&tag.GetFloat()); + data += 4; + size += 4; + return tag; + case Double: + tag.GetDouble() = *((double *) data); + endswap(&tag.GetDouble()); + data += 8; + size += 8; + return tag; + case Byte: + tag.GetByte() = *((int8_t *) data); + endswap(&tag.GetByte()); + data += 1; + size += 1; + return tag; + case Int: + tag.GetInt() = *((int32_t *) data); + endswap(&tag.GetInt()); + data += 4; + size += 4; + return tag; + case List: { + TagType listType = *((TagType *) data); + data += 1; + size += 1; + int32_t listLength = *((int32_t *) data); + endswap(&listLength); + data += 4; + size += 4; + for (int i = 0; i < listLength; i++) { + size_t s = 0; + std::vector &vec = tag.GetCompound(); + vec.push_back(ParseTag(data, s, listType)); + data += s; + size += s; + } + return tag; + } + case ByteArray: { + int32_t arrLength = *((int32_t *) data); + endswap(&arrLength); + data += 4; + size += 4; + for (int i = 0; i < arrLength; i++) { + signed char val = (signed char) data[i]; + std::vector &vec = tag.GetByteArray(); + vec.push_back(val); + } + data += arrLength; + size += arrLength; + return tag; + } + default: + throw 13; + } + } + + NbtTag ParseTag(unsigned char *data, size_t *optionalSize = nullptr) { + size_t s = 0; + size_t &size = (optionalSize ? *optionalSize : s); + return ParseTag(data, size); + } + + std::vector Decompress(unsigned char *data, size_t dataLen) { + const size_t decompBuffSize = 1024 * 16; + unsigned char *decompBuff = new unsigned char[decompBuffSize]; + std::vector uncompressed; + for (int i = 0; i < decompBuffSize; i++) + decompBuff[i] = 0; + + + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.next_in = data; + stream.avail_in = dataLen; + stream.next_out = decompBuff; + stream.avail_out = decompBuffSize; + + if (inflateInit2(&stream, 15 + 32) != Z_OK) { + delete[] decompBuff; + throw 171; + } + + int res; + do { + stream.avail_out = decompBuffSize; + + switch ((res = inflate(&stream, Z_NO_FLUSH))) { + case Z_MEM_ERROR: + throw 172; + case Z_DATA_ERROR: + throw 173; + case Z_NEED_DICT: + throw 174; + } + + uncompressed.resize(uncompressed.size() + decompBuffSize); + std::copy(decompBuff, decompBuff + decompBuffSize, uncompressed.end() - decompBuffSize); + } while (stream.avail_out == 0); + if (res != Z_STREAM_END) + throw 175; + if (inflateEnd(&stream) != Z_OK) + throw 176; + delete[] decompBuff; + return uncompressed; + } + + NbtTag ParseCompressed(unsigned char *data, size_t dataLen) { + auto uncompressed = Decompress(data, dataLen); + NbtTag root = ParseTag(uncompressed.data()); + return root; + } + + NbtTag Parse(unsigned char *data, size_t dataLen) { + bool isCompressed = *data != 10; + if (isCompressed) + return ParseCompressed(data, dataLen); + else + return ParseTag(data); + } + + void PrintTree(NbtTag &tree, int deepness = 0, std::ostream &ostream = std::cout) { + ostream << std::string(deepness, '\t') << "Tag "; + switch (tree.GetType()) { + case Byte: + ostream << "byte"; + break; + case Short: + ostream << "short"; + break; + case Int: + ostream << "int"; + break; + case Long: + ostream << "long"; + break; + case Float: + ostream << "float"; + break; + case Double: + ostream << "double"; + break; + case ByteArray: + ostream << "byte array"; + break; + case String: + ostream << "string"; + break; + case List: + ostream << "list"; + break; + case Compound: + ostream << "compound"; + break; + case IntArray: + ostream << "int array"; + break; + case End: + ostream << "end"; + break; + } + if (tree.GetName().length() > 0) + ostream << " (" << tree.GetName() << ")"; + ostream << ": "; + + if (tree.GetType() == Compound || tree.GetType() == List) { + std::vector &vec = (tree.GetType() == Compound ? tree.GetCompound() : tree.GetList()); + ostream << vec.size() << " entries {" << std::endl; + for (auto it = vec.begin(); it != vec.end(); ++it) { + PrintTree(*it, deepness + 1, std::cout); + } + ostream << std::string(deepness, '\t') << "}" << std::endl; + return; + } else { + switch (tree.GetType()) { + case Int: + ostream << tree.GetInt(); + break; + case String: + ostream << "\"" << tree.GetString() << "\""; + break; + case Double: + ostream << tree.GetDouble(); + break; + case Float: + ostream << tree.GetFloat(); + break; + case Short: + ostream << tree.GetShort(); + break; + case Byte: + ostream << (int) tree.GetByte(); + break; + case Long: + ostream << tree.GetLong(); + break; + case ByteArray: + ostream << "[" << tree.GetByteArray().size() << " bytes]: "; + for (int i = 0; i < (tree.GetByteArray().size() > 10 ? 10 : tree.GetByteArray().size()); i++) { + ostream << std::hex << "0x" << (tree.GetByteArray()[i] > 15 ? "" : "0") + << (int) tree.GetByteArray()[i] + << std::dec << " "; + } + break; + case IntArray: + break; + } + ostream << std::endl; + } + } +} \ No newline at end of file diff --git a/old/Utility.hpp b/old/Utility.hpp new file mode 100644 index 0000000..11b4ff7 --- /dev/null +++ b/old/Utility.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include + +template +void endswap(T *objp) { + unsigned char *memp = reinterpret_cast(objp); + std::reverse(memp, memp + sizeof(T)); +} + +template +void endswap(T &obj) { + unsigned char *raw = reinterpret_cast(&obj); + std::reverse(raw, raw + sizeof(T)); +} + +inline void endswap(unsigned char *arr, size_t arrLen) { + std::reverse(arr, arr + arrLen); +} + +inline GLenum glCheckError_(const char *file, int line) { + GLenum errorCode; + while ((errorCode = glGetError()) != GL_NO_ERROR) { + std::string error; + switch (errorCode) { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + error = "STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + error = "STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + static int t = 0; + t++; + if (t>10) + LOG(FATAL); + LOG(ERROR) << "OpenGL error: " << error << " at " << file << ":" << line; + } + return errorCode; +} + +#define glCheckError() glCheckError_(__FILE__, __LINE__) \ No newline at end of file diff --git a/old/Vector.hpp b/old/Vector.hpp new file mode 100644 index 0000000..a2d5c6a --- /dev/null +++ b/old/Vector.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include + +#include + +template +class Vector3 { + T x, y, z; +public: + Vector3(T X = 0, T Y = 0, T Z = 0) : x(X), y(Y), z(Z) {} + + Vector3(const Vector3 &rhs) : x(rhs.x), y(rhs.y), z(rhs.z) {} + + ~Vector3() = default; + + void SetX(T X) { x = X; } + + void SetY(T Y) { y = Y; } + + void SetZ(T Z) { z = Z; } + + T GetX() const { return x; } + + T GetY() const { return y; } + + T GetZ() const { return z; } + + double GetMagnitude() const { return std::sqrt(std::pow(x, 2) + std::pow(y, 2) + std::pow(z, 2)); } + + operator glm::vec3() const { + return glm::vec3(x, y, z); + } + + void swap(Vector3 &rhs) { + std::swap(x, rhs.x); + std::swap(y, rhs.y); + std::swap(z, rhs.z); + } + + Vector3 &operator=(Vector3 rhs) { + rhs.swap(*this); + return *this; + } + + Vector3 operator*(T rhs) const { + return Vector3( + x * rhs, + y * rhs, + z * rhs + ); + } + + Vector3 operator/(T rhs) const { + return Vector3( + x / rhs, + y / rhs, + z / rhs + ); + } + + Vector3 operator+(const Vector3 &rhs) const { + return Vector3( + x + rhs.x, + y + rhs.y, + z + rhs.z + ); + } + + Vector3 operator-(const Vector3 &rhs) const { + return Vector3( + x - rhs.x, + y - rhs.y, + z - rhs.z + ); + } + + Vector3 operator*(const Vector3 &rhs) const { + return Vector3( + x * rhs.x, + y * rhs.y, + z * rhs.z + ); + } + + Vector3 operator/(const Vector3 &rhs) const { + return Vector3( + x / rhs.x, + y / rhs.y, + z / rhs.z + ); + } + + bool operator==(const Vector3 &rhs) const { + return (x == rhs.x && y == rhs.y && z == rhs.z); + } + + bool operator!=(const Vector3 &rhs) const { + return !(*this == rhs); + } + + bool operator<(const Vector3 &rhs) const { + return std::tie(x, y, z) < std::tie(rhs.x, rhs.y, rhs.z); + } + + + friend std::ostream &operator<<(std::ostream &os, const Vector3 &vector3) { + os << vector3.x << ", " << vector3.y << ", " << vector3.z; + return os; + } +}; + +typedef Vector3 VectorF; +typedef Vector3 Vector; \ No newline at end of file diff --git a/old/core/AssetManager.cpp b/old/core/AssetManager.cpp new file mode 100644 index 0000000..258f5d9 --- /dev/null +++ b/old/core/AssetManager.cpp @@ -0,0 +1,181 @@ +#include "AssetManager.hpp" + +namespace fs = std::experimental::filesystem; + +//const fs::path pathToAssets = "./assets/"; +//const fs::path pathToAssetsList = "./items.json"; +//const fs::path pathToTextureIndex = "./textures.json"; +const std::string pathToAssetsList = "./items.json"; +const std::string pathToTextureIndex = "./textures.json"; + +AssetManager::AssetManager() { + LoadIds(); + LoadTextureResources(); +} + +void AssetManager::LoadIds() { + std::ifstream in(pathToAssetsList); + nlohmann::json index; + in >> index; + for (auto &it:index) { + int id = it["type"].get(); + int state = it["meta"].get(); + std::string blockName = it["text_type"].get(); + assetIds[blockName] = Block(id, state); + } + LOG(INFO) << "Loaded " << assetIds.size() << " ids"; +} + +AssetManager::~AssetManager() { + delete textureAtlas; +} + +//TODO: This function must be replaced with runtime texture atlas generating +void AssetManager::LoadTextureResources() { + std::ifstream in(pathToTextureIndex); + nlohmann::json index; + in >> index; + std::string filename = index["meta"]["image"].get(); + float textureWidth = index["meta"]["size"]["w"].get(); + float textureHeight = index["meta"]["size"]["h"].get(); + for (auto &it:index["frames"]) { + auto frame = it["frame"]; + TextureCoordinates coord; + coord.x = frame["x"].get() / textureWidth; + coord.y = frame["y"].get() / textureHeight; + coord.w = frame["w"].get() / textureWidth; + coord.h = frame["h"].get() / textureHeight; + std::string assetName = it["filename"].get(); + assetName.insert(0, "minecraft/textures/"); + assetName.erase(assetName.length() - 4); + assetTextures[assetName] = coord; + } + + textureAtlas = new Texture(filename); + LOG(INFO) << "Texture atlas id is " << textureAtlas->texture; +} + +TextureCoordinates AssetManager::GetTextureByAssetName(std::string AssetName) { + if (assetTextures.find(AssetName) != assetTextures.end()) + return assetTextures[AssetName]; + else + return TextureCoordinates{-1, -1, -1, -1}; +} + +std::string AssetManager::GetTextureAssetNameByBlockId(BlockTextureId block) { + //Block sides: 0 - bottom, 1 - top, 2 - north, 3 - south, 4 - west, 5 - east 6 - every side + const std::map lookupTable = { + {BlockTextureId(0, 0), "minecraft/textures/blocks/air"}, + {BlockTextureId(1, 0), "minecraft/textures/blocks/stone"}, + {BlockTextureId(1, 1), "minecraft/textures/blocks/stone_granite"}, + + {BlockTextureId(2, 0, 0), "minecraft/textures/blocks/dirt"}, + {BlockTextureId(2, 0, 1), "minecraft/textures/blocks/grass_top"}, + {BlockTextureId(2, 0, 2), "minecraft/textures/blocks/grass_side"}, + {BlockTextureId(2, 0, 3), "minecraft/textures/blocks/grass_side"}, + {BlockTextureId(2, 0, 4), "minecraft/textures/blocks/grass_side"}, + {BlockTextureId(2, 0, 5), "minecraft/textures/blocks/grass_side"}, + + {BlockTextureId(3, 0), "minecraft/textures/blocks/dirt"}, + {BlockTextureId(4, 0), "minecraft/textures/blocks/cobblestone"}, + {BlockTextureId(5, 0), "minecraft/textures/blocks/planks"}, + + {BlockTextureId(7, 0), "minecraft/textures/blocks/bedrock"}, + + {BlockTextureId(17, 0, 0), "minecraft/textures/blocks/log_oak_top"}, + {BlockTextureId(17, 0, 1), "minecraft/textures/blocks/log_oak_top"}, + {BlockTextureId(17, 0, 2), "minecraft/textures/blocks/log_oak"}, + {BlockTextureId(17, 0, 3), "minecraft/textures/blocks/log_oak"}, + {BlockTextureId(17, 0, 4), "minecraft/textures/blocks/log_oak"}, + {BlockTextureId(17, 0, 5), "minecraft/textures/blocks/log_oak"}, + + {BlockTextureId(17, 1, 0), "minecraft/textures/blocks/log_spruce_top"}, + {BlockTextureId(17, 1, 1), "minecraft/textures/blocks/log_spruce_top"}, + {BlockTextureId(17, 1, 2), "minecraft/textures/blocks/log_spruce"}, + {BlockTextureId(17, 1, 3), "minecraft/textures/blocks/log_spruce"}, + {BlockTextureId(17, 1, 4), "minecraft/textures/blocks/log_spruce"}, + {BlockTextureId(17, 1, 5), "minecraft/textures/blocks/log_spruce"}, + + {BlockTextureId(17, 2, 0), "minecraft/textures/blocks/log_birch_top"}, + {BlockTextureId(17, 2, 1), "minecraft/textures/blocks/log_birch_top"}, + {BlockTextureId(17, 2, 2), "minecraft/textures/blocks/log_birch"}, + {BlockTextureId(17, 2, 3), "minecraft/textures/blocks/log_birch"}, + {BlockTextureId(17, 2, 4), "minecraft/textures/blocks/log_birch"}, + {BlockTextureId(17, 2, 5), "minecraft/textures/blocks/log_birch"}, + + {BlockTextureId(17, 3, 0), "minecraft/textures/blocks/log_jungle_top"}, + {BlockTextureId(17, 3, 1), "minecraft/textures/blocks/log_jungle_top"}, + {BlockTextureId(17, 3, 2), "minecraft/textures/blocks/log_jungle"}, + {BlockTextureId(17, 3, 3), "minecraft/textures/blocks/log_jungle"}, + {BlockTextureId(17, 3, 4), "minecraft/textures/blocks/log_jungle"}, + {BlockTextureId(17, 3, 5), "minecraft/textures/blocks/log_jungle"}, + + {BlockTextureId(18, 0), "minecraft/textures/blocks/leaves_oak"}, + {BlockTextureId(18, 1), "minecraft/textures/blocks/leaves_spruce"}, + {BlockTextureId(18, 2), "minecraft/textures/blocks/leaves_birch"}, + {BlockTextureId(18, 3), "minecraft/textures/blocks/leaves_jungle"}, + + {BlockTextureId(61, 0, 0), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(61, 0, 1), "minecraft/textures/blocks/furnace_top"}, + {BlockTextureId(61, 0, 2), "minecraft/textures/blocks/furnace_front_off"}, + {BlockTextureId(61, 0, 3), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(61, 0, 4), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(61, 0, 5), "minecraft/textures/blocks/furnace_side"}, + + {BlockTextureId(62, 0, 0), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(62, 0, 1), "minecraft/textures/blocks/furnace_top"}, + {BlockTextureId(62, 0, 2), "minecraft/textures/blocks/furnace_front_on"}, + {BlockTextureId(62, 0, 3), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(62, 0, 4), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(62, 0, 5), "minecraft/textures/blocks/furnace_side"}, + + + {BlockTextureId(31, 0), "minecraft/textures/blocks/deadbush"}, + {BlockTextureId(31, 1), "minecraft/textures/blocks/tallgrass"}, + {BlockTextureId(31, 2), "minecraft/textures/blocks/fern"}, + }; + auto ret = lookupTable.find(block); + if (ret == lookupTable.end()) + return ""; + else + return ret->second; +} + +GLuint AssetManager::GetTextureAtlas() { + return textureAtlas->texture; +} + +TextureCoordinates AssetManager::GetTextureByBlock(BlockTextureId block) { + std::string assetName = this->GetTextureAssetNameByBlockId(block); + return this->GetTextureByAssetName(assetName); +} + +const std::map &AssetManager::GetTextureAtlasIndexes() { + if (!textureAtlasIndexes.empty()) + return textureAtlasIndexes; + + LOG(INFO) << "Initializing texture atlas..."; + for (int id = 1; id < 128; id++) { + for (int state = 0; state < 16; state++) { + BlockTextureId blockTextureId(id, state, 6); + if (!this->GetTextureByBlock(blockTextureId) && + !this->GetTextureByBlock(BlockTextureId(id, state, 0))) { + continue; + } + if (this->GetTextureByBlock(blockTextureId)) { + for (int i = 0; i < 6; i++) { + TextureCoordinates tc = this->GetTextureByBlock(BlockTextureId(id, state, 6)); + textureAtlasIndexes[BlockTextureId(id, state, i)] = glm::vec4(tc.x, tc.y, tc.w, tc.h); + } + } else { + for (int i = 0; i < 6; i++) { + TextureCoordinates tc = this->GetTextureByBlock(BlockTextureId(id, state, i)); + textureAtlasIndexes[BlockTextureId(id, state, i)] = glm::vec4(tc.x, tc.y, tc.w, tc.h); + } + } + } + } + LOG(INFO) << "Created " << textureAtlasIndexes.size() << " texture indexes"; + + return textureAtlasIndexes; +} diff --git a/old/core/AssetManager.hpp b/old/core/AssetManager.hpp new file mode 100644 index 0000000..667140a --- /dev/null +++ b/old/core/AssetManager.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "../world/Block.hpp" +#include "../graphics/Texture.hpp" + +struct TextureCoordinates { + TextureCoordinates(float x = -1, float y = -1, float w = -1, float h = -1) : x(x), y(y), w(w), h(h) {} + + bool operator==(const TextureCoordinates &rhs) const { + return x == rhs.x && + y == rhs.y && + w == rhs.w && + h == rhs.h; + } + + explicit operator bool() const { + return !(*this == TextureCoordinates(-1, -1, -1, -1)); + } + + float x, y, w, h; +}; + +struct BlockTextureId { + //Block sides: 0 - bottom, 1 - top, 2 - north, 3 - south, 4 - west, 5 - east 6 - every side + BlockTextureId(int id = 0, int state = 0, int side = 6) : id(id), state(state), side(side) {} + + int id:9; + int state:4; + int side:3; + + + bool operator<(const BlockTextureId &rhs) const { + if (id < rhs.id) + return true; + if (rhs.id < id) + return false; + if (state < rhs.state) + return true; + if (rhs.state < state) + return false; + return side < rhs.side; + } +}; + +class AssetManager { + Texture *textureAtlas; + std::map assetIds; + std::map assetTextures; + std::map textureAtlasIndexes; +public: + AssetManager(); + + ~AssetManager(); + + void LoadTextureResources(); + + TextureCoordinates GetTextureByAssetName(std::string AssetName); + + std::string GetTextureAssetNameByBlockId(BlockTextureId block); + + GLuint GetTextureAtlas(); + + const std::map &GetTextureAtlasIndexes(); + + void LoadIds(); + + TextureCoordinates GetTextureByBlock(BlockTextureId block); +}; diff --git a/old/core/Core.cpp b/old/core/Core.cpp new file mode 100644 index 0000000..e98d204 --- /dev/null +++ b/old/core/Core.cpp @@ -0,0 +1,457 @@ +#include "Core.hpp" + +//Core::Core():toRenderMutex("toRender"),availableChunksMutex("availableChunks") { +Core::Core() { + LOG(INFO) << "Core initializing..."; + InitSfml(900, 450, "AltCraft"); + glCheckError(); + InitGlew(); + glCheckError(); + client = new NetworkClient("127.0.0.1", 25565, "HelloOne", isRunning); + gameState = new GameState(client, isRunning); + gameStateLoopThread = std::thread(&Core::UpdateGameState, this); + sectionUpdateLoopThread = std::thread(&Core::UpdateSections, this); + assetManager = new AssetManager; + PrepareToRendering(); + LOG(INFO) << "Core is initialized"; + glCheckError(); +} + +Core::~Core() { + LOG(INFO) << "Core stopping..."; + gameStateLoopThread.join(); + sectionUpdateLoopThread.join(); + delete shader; + delete gameState; + delete client; + delete assetManager; + delete window; + LOG(INFO) << "Core is stopped"; +} + +void Core::Exec() { + LOG(INFO) << "Main loop is executing!"; + isRunning = true; + while (isRunning) { + static sf::Clock clock, clock1; + deltaTime = clock.getElapsedTime().asSeconds(); + absTime = clock1.getElapsedTime().asSeconds(); + clock.restart(); + + static bool alreadyDone = false; + if (gameState->g_IsGameStarted && !alreadyDone) { + alreadyDone = true; + UpdateChunksToRender(); + } + + std::ostringstream toWindow; + auto camPos = gameState->Position(); + auto velPos = glm::vec3(gameState->g_PlayerVelocityX, gameState->g_PlayerVelocityY, + gameState->g_PlayerVelocityZ); + toWindow << std::setprecision(2) << std::fixed; + toWindow << "Pos: " << camPos.x << ", " << camPos.y << ", " << camPos.z << "; "; + toWindow << "Health: " << gameState->g_PlayerHealth << "; "; + //toWindow << "OG: " << gameState->g_OnGround << "; "; + toWindow << "Vel: " << velPos.x << ", " << velPos.y << ", " << velPos.z << "; "; + toWindow << "FPS: " << (1.0f / deltaTime) << " "; + toWindow << " (" << deltaTime * 1000 << "ms); "; + toWindow << "Tickrate: " << tickRate << " (" << (1.0 / tickRate * 1000) << "ms); "; + toWindow << "Sections: " << sectionRate << " (" << (1.0 / sectionRate * 1000) << "ms); "; + window->setTitle(toWindow.str()); + + HandleEvents(); + if (isMouseCaptured) HandleMouseCapture(); + glCheckError(); + + RenderFrame(); + if (isRendersShouldBeCreated) { + availableChunksMutex.lock(); + for (auto &it:renders) { + auto pair = std::make_pair(it, RenderSection(&gameState->world, it)); + availableChunks.insert(pair); + } + renders.clear(); + availableChunksMutex.unlock(); + isRendersShouldBeCreated = false; + waitRendersCreated.notify_all(); + } + + } +} + +void Core::RenderFrame() { + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + switch (currentState) { + case MainMenu: + //RenderGui(MenuScreen); + break; + case Loading: + //RenderGui(LoadingScreen); + break; + case Playing: + RenderWorld(); + //RenderGui(HUD); + break; + case PauseMenu: + RenderWorld(); + //RenderGui(PauseGui); + break; + } + + window->display(); +} + +void Core::InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle) { + LOG(INFO) << "Creating window: " << WinWidth << "x" << WinHeight << " \"" << WinTitle << "\""; + sf::ContextSettings contextSetting; + contextSetting.majorVersion = 3; + contextSetting.minorVersion = 3; + contextSetting.attributeFlags = contextSetting.Core; + contextSetting.depthBits = 24; + window = new sf::Window(sf::VideoMode(WinWidth, WinHeight), WinTitle, sf::Style::Default, contextSetting); + glCheckError(); + //window->setVerticalSyncEnabled(true); + //window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2, sf::VideoMode::getDesktopMode().height / 2)); + window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2 - window->getSize().x / 2, + sf::VideoMode::getDesktopMode().height / 2 - window->getSize().y / 2)); + + SetMouseCapture(false); +} + +void Core::InitGlew() { + LOG(INFO) << "Initializing GLEW"; + glewExperimental = GL_TRUE; + GLenum glewStatus = glewInit(); + glCheckError(); + if (glewStatus != GLEW_OK) { + LOG(FATAL) << "Failed to initialize GLEW: " << glewGetErrorString(glewStatus); + } + glViewport(0, 0, width(), height()); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glCheckError(); +} + +unsigned int Core::width() { + return window->getSize().x; +} + +unsigned int Core::height() { + return window->getSize().y; +} + +void Core::HandleEvents() { + sf::Event event; + while (window->pollEvent(event)) { + switch (event.type) { + case sf::Event::Closed: + LOG(INFO) << "Received close event by window closing"; + isRunning = false; + break; + case sf::Event::Resized: + glViewport(0, 0, width(), height()); + break; + case sf::Event::KeyPressed: + if (!window->hasFocus()) break; + switch (event.key.code) { + case sf::Keyboard::Escape: + LOG(INFO) << "Received close event by esc"; + isRunning = false; + break; + case sf::Keyboard::T: + SetMouseCapture(!isMouseCaptured); + break; + case sf::Keyboard::L: + ChunkDistance++; + LOG(INFO) << "Increased render distance: " << ChunkDistance; + break; + case sf::Keyboard::K: + if (ChunkDistance > 1) { + ChunkDistance--; + LOG(INFO) << "Decreased render distance: " << ChunkDistance; + } + break; + default: + break; + } + default: + break; + } + } + if (window->hasFocus()) { + if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) gameState->HandleMovement(GameState::FORWARD, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) gameState->HandleMovement(GameState::BACKWARD, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) gameState->HandleMovement(GameState::LEFT, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) gameState->HandleMovement(GameState::RIGHT, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) gameState->HandleMovement(GameState::JUMP, deltaTime); + UpdateChunksToRender(); + } +} + +void Core::HandleMouseCapture() { + sf::Vector2i mousePos = sf::Mouse::getPosition(*window); + sf::Vector2i center = sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2); + sf::Mouse::setPosition(center, *window); + mouseXDelta = (mousePos - center).x, mouseYDelta = (center - mousePos).y; + const float Sensetivity = 0.7f; + gameState->HandleRotation(mouseXDelta * Sensetivity, mouseYDelta * Sensetivity); + //camera.ProcessMouseMovement(mouseXDelta, mouseYDelta); +} + +void Core::RenderWorld() { + shader->Use(); + glCheckError(); + + GLint projectionLoc = glGetUniformLocation(shader->Program, "projection"); + GLint viewLoc = glGetUniformLocation(shader->Program, "view"); + GLint timeLoc = glGetUniformLocation(shader->Program, "time"); + glm::mat4 projection = glm::perspective(45.0f, (float) width() / (float) height(), 0.1f, 10000000.0f); + glm::mat4 view = gameState->GetViewMatrix(); + glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); + glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); + glUniform1f(timeLoc, absTime); + glUniform2f(glGetUniformLocation(shader->Program, "windowSize"), width(), height()); + + glCheckError(); + + toRenderMutex.lock(); + for (auto &render : toRender) { + availableChunksMutex.lock(); + auto iterator = availableChunks.find(render); + if (iterator == availableChunks.end()) { + availableChunksMutex.unlock(); + continue; + } + /*Section §ion = *iterator->second.GetSection(); + //availableChunksMutex.unlock(); + + std::vector sectionCorners = { + Vector(0, 0, 0), + Vector(0, 0, 16), + Vector(0, 16, 0), + Vector(0, 16, 16), + Vector(16, 0, 0), + Vector(16, 0, 16), + Vector(16, 16, 0), + Vector(16, 16, 16), + }; + bool isBreak = true; + glm::mat4 vp = projection * view; + for (auto &it:sectionCorners) { + glm::vec3 point(section.GetPosition().GetX() * 16 + it.GetX(), + section.GetPosition().GetY() * 16 + it.GetY(), + section.GetPosition().GetZ() * 16 + it.GetZ()); + glm::vec4 p = vp * glm::vec4(point, 1); + glm::vec3 res = glm::vec3(p) / p.w; + if (res.x < 1 && res.x > -1 && res.y < 1 && res.y > -1 && res.z > 0) { + isBreak = false; + break; + } + } + if (isBreak && glm::length(gameState->Position() - + glm::vec3(section.GetPosition().GetX() * 16, + section.GetPosition().GetY() * 16, + section.GetPosition().GetZ() * 16)) > 30.0f) { + availableChunksMutex.unlock(); + continue; + } + //availableChunksMutex.lock();*/ + iterator->second.Render(renderState); + availableChunksMutex.unlock(); + } + toRenderMutex.unlock(); + glCheckError(); +} + +void Core::SetMouseCapture(bool IsCaptured) { + window->setMouseCursorVisible(!isMouseCaptured); + sf::Mouse::setPosition(sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2), *window); + isMouseCaptured = IsCaptured; + window->setMouseCursorVisible(!IsCaptured); +} + +void Core::PrepareToRendering() { + shader = new Shader("./shaders/face.vs", "./shaders/face.fs"); + shader->Use(); + + //TextureAtlas texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, assetManager->GetTextureAtlas()); + glUniform1i(glGetUniformLocation(shader->Program, "textureAtlas"), 0); +} + +void Core::UpdateChunksToRender() { + return; + Vector playerChunk = Vector(floor(gameState->g_PlayerX / 16.0f), 0, floor(gameState->g_PlayerZ / 16.0f)); + static Vector previousPlayerChunk = playerChunk; + static bool firstTime = true; + static int previousRenderDistance = ChunkDistance; + if (previousPlayerChunk == playerChunk && !firstTime && ChunkDistance == previousRenderDistance) { + return; + } + previousPlayerChunk = playerChunk; + previousRenderDistance = ChunkDistance; + toRender.clear(); + for (auto &it:gameState->world.GetSectionsList()) { + Vector chunkPosition = it; + chunkPosition.SetY(0); + Vector delta = chunkPosition - playerChunk; + if (delta.GetMagnitude() > ChunkDistance) continue; + toRender.push_back(it); + } + for (auto &it:toRender) { + if (availableChunks.find(it) == availableChunks.end()) { + auto pair = std::make_pair(it, RenderSection(&gameState->world, it)); + pair.second.UpdateState(assetManager->GetTextureAtlasIndexes()); + availableChunks.insert(pair); + } else { + //availableChunks.find(it)->second.UpdateState(); + } + } + if (firstTime) LOG(INFO) << "Chunks to render: " << toRender.size() << " of " << availableChunks.size(); + firstTime = false; +} + +void Core::UpdateGameState() { + el::Helpers::setThreadName("Game"); + LOG(INFO) << "GameState thread is started"; + sf::Clock delta; + while (isRunning) { + float deltaTime = delta.getElapsedTime().asSeconds(); + delta.restart(); + gameState->Update(deltaTime); + const double targetDelta = 1 / 60.0; + std::chrono::duration> timeToSleep(targetDelta - delta.getElapsedTime().asSeconds()); + std::this_thread::sleep_for(timeToSleep); + tickRate = 1 / delta.getElapsedTime().asSeconds(); + } + LOG(INFO) << "GameState thread is stopped"; +} + +void Core::UpdateSections() { + glm::vec3 playerPosition = gameState->Position(); + float playerPitch = gameState->Pitch(); + float playerYaw = gameState->Yaw(); + sf::Clock delta; + std::vector chunksToRender; + auto currentSectionIterator = chunksToRender.begin(); + while (isRunning) { + delta.restart(); + if (glm::length(glm::distance(gameState->Position(), playerPosition)) > 5.0f) { + chunksToRender.clear(); + playerPosition = gameState->Position(); + Vector playerChunk = Vector(floor(playerPosition.x / 16.0f), 0, floor(playerPosition.z / 16.0f)); + for (auto &it:gameState->world.GetSectionsList()) { + Vector chunkPosition = it; + chunkPosition.SetY(0); + Vector delta = chunkPosition - playerChunk; + if (delta.GetMagnitude() > ChunkDistance) continue; + chunksToRender.push_back(it); + } + std::sort(chunksToRender.begin(), chunksToRender.end(), [playerChunk](auto first, auto second) { + glm::vec3 fDistance = first - playerChunk; + glm::vec3 sDistance = second - playerChunk; + return glm::length(fDistance) < glm::length(sDistance); + }); + for (auto &it:chunksToRender) { + availableChunksMutex.lock(); + if (availableChunks.find(it) == availableChunks.end()) { + availableChunksMutex.unlock(); + renders.push_back(it); + } else + availableChunksMutex.unlock(); + } + if (!renders.empty()) { + std::mutex mutex; + std::unique_lock lock(mutex); + isRendersShouldBeCreated = true; + while (isRendersShouldBeCreated) + waitRendersCreated.wait(lock); + } + currentSectionIterator = chunksToRender.begin(); + toRenderMutex.lock(); + toRender = chunksToRender; + toRenderMutex.unlock(); + } + if (currentSectionIterator != chunksToRender.end()) { + availableChunksMutex.lock(); + auto iterator = availableChunks.find(*currentSectionIterator); + if (iterator != availableChunks.end() && iterator->second.IsNeedUpdate()) { + RenderSection rs = std::move(iterator->second); + availableChunks.erase(iterator); + auto pair = std::make_pair(*currentSectionIterator, rs); + availableChunksMutex.unlock(); + + pair.second.UpdateState(assetManager->GetTextureAtlasIndexes()); + + availableChunksMutex.lock(); + availableChunks.insert(pair); + } + availableChunksMutex.unlock(); + currentSectionIterator = std::next(currentSectionIterator); + } + if (gameState->Pitch() != playerPitch || gameState->Yaw() != playerYaw) { + playerPitch = gameState->Pitch(); + playerYaw = gameState->Yaw(); + const std::vector sectionCorners = { + Vector(0, 0, 0), + Vector(0, 0, 16), + Vector(0, 16, 0), + Vector(0, 16, 16), + Vector(16, 0, 0), + Vector(16, 0, 16), + Vector(16, 16, 0), + Vector(16, 16, 16), + }; + const glm::mat4 projection = glm::perspective(45.0f, (float)width() / (float)height(), 0.1f, 10000000.0f); + const glm::mat4 view = gameState->GetViewMatrix(); + const glm::mat4 vp = projection * view; + for (auto& section: toRender) { + bool isCulled = true; + for (auto &it : sectionCorners) { + glm::vec3 point(section.GetX() * 16 + it.GetX(), + section.GetY() * 16 + it.GetY(), + section.GetZ() * 16 + it.GetZ()); + glm::vec4 p = vp * glm::vec4(point, 1); + glm::vec3 res = glm::vec3(p) / p.w; + if (res.x < 1 && res.x > -1 && res.y < 1 && res.y > -1 && res.z > 0) { + isCulled = false; + break; + } + } + bool isVisible = !isCulled || glm::length(gameState->Position() - + glm::vec3(section.GetX() * 16, section.GetY() * 16, section.GetZ() * 16)) < 30.0f; + availableChunksMutex.lock(); + auto iter = availableChunks.find(section); + if (iter != availableChunks.end()) + iter->second.SetEnabled(isVisible); + availableChunksMutex.unlock(); + + } + } + using namespace std::chrono_literals; + std::this_thread::sleep_for(5ms); + sectionRate = delta.getElapsedTime().asSeconds(); + delta.restart(); + } +} + +MyMutex::MyMutex(std::string name) { + str = name; +} + +void MyMutex::lock() { + LOG(WARNING) << "Thread " << std::this_thread::get_id() << " locked mutex " << str; + mtx.lock(); +} + +void MyMutex::unlock() { + LOG(WARNING) << "Thread " << std::this_thread::get_id() << " unlocked mutex " << str; + mtx.unlock(); +} \ No newline at end of file diff --git a/old/core/Core.hpp b/old/core/Core.hpp new file mode 100644 index 0000000..aaa143f --- /dev/null +++ b/old/core/Core.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include "../world/GameState.hpp" +#include "AssetManager.hpp" +#include "../graphics/Shader.hpp" +#include "../graphics/Gui.hpp" +#include "../graphics/RenderSection.hpp" +#include "../network/NetworkClient.hpp" + +struct MyMutex { + std::mutex mtx; + std::string str; + MyMutex(std::string name); + void lock(); + void unlock(); +}; + +class Core { + GameState *gameState; + NetworkClient *client; + sf::Window *window; + AssetManager *assetManager; + bool isMouseCaptured = false; + bool isRunning = true; + enum { + MainMenu, + Loading, + Playing, + PauseMenu, + } currentState = Playing; + float mouseXDelta, mouseYDelta; + float deltaTime; + float absTime; + + void RenderWorld(); + + void HandleMouseCapture(); + + void HandleEvents(); + + void InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle); + + void InitGlew(); + + void SetMouseCapture(bool IsCaptured); + + void PrepareToRendering(); + + void RenderFrame(); + + unsigned int width(); + + unsigned int height(); + + void UpdateChunksToRender(); + + void UpdateGameState(); + + void UpdateSections(); + + std::thread gameStateLoopThread; + std::thread sectionUpdateLoopThread; + + Shader *shader; + //Cube verticies, Cube VAO, Cube UVs, TextureIndexes UboTextureIndexes, TextureData UboTextureIndexes, TextureData2 UboTextureIndexes, Blocks VBO, Models VBO, Line VAO, Lines VBO + bool isRendersShouldBeCreated=false; + std::condition_variable waitRendersCreated; + std::vector renders; + std::mutex toRenderMutex; + std::vector toRender; + std::map availableChunks; + std::mutex availableChunksMutex; + + int ChunkDistance = 3; + + RenderState renderState; + + double tickRate = 0; + double sectionRate = 0; + +public: + Core(); + + ~Core(); + + void Exec(); +}; diff --git a/old/core/Event.cpp b/old/core/Event.cpp new file mode 100644 index 0000000..3b2cc7f --- /dev/null +++ b/old/core/Event.cpp @@ -0,0 +1,76 @@ +#include "Event.hpp" +#include + +std::queue EventAgregator::eventsToHandle; +std::mutex EventAgregator::queueMutex; +bool EventAgregator::isStarted = false; +std::vector EventAgregator::listeners; +std::mutex EventAgregator::listenersMutex; + +void EventAgregator::EventHandlingLoop() { + while (true) { + queueMutex.lock(); + if (!eventsToHandle.empty()) { + auto queue = eventsToHandle; + while (!eventsToHandle.empty()) + eventsToHandle.pop(); + queueMutex.unlock(); + + while (!queue.empty()) { + auto event = queue.front(); + listenersMutex.lock(); + for (auto& listener : listeners) { + LOG(INFO)<<"Listener notified about event"; + listener->PushEvent(event); + } + listenersMutex.unlock(); + queue.pop(); + } + + queueMutex.lock(); + } + queueMutex.unlock(); + } +} + +void EventAgregator::RegisterListener(EventListener &listener) { + listenersMutex.lock(); + LOG(INFO)<<"Registered handler "<<&listener; + listeners.push_back(&listener); + listenersMutex.unlock(); +} + +void EventAgregator::UnregisterListener(EventListener &listener) { + listenersMutex.lock(); + LOG(INFO)<<"Unregistered handler "<<&listener; + listeners.erase(std::find(listeners.begin(), listeners.end(), &listener)); + listenersMutex.unlock(); +} + + + +EventListener::EventListener() { + EventAgregator::RegisterListener(*this); +} + +EventListener::~EventListener() { + EventAgregator::UnregisterListener(*this); +} + +void EventListener::PushEvent(Event event) { + eventsMutex.lock(); + LOG(INFO)<<"Pushed event to queue"; + events.push(event); + eventsMutex.unlock(); +} + +/*void EventListener::RegisterHandler(EventType type, std::function handler) { + handlers[type] = handler; +}*/ + +bool EventListener::IsEventsQueueIsNotEmpty() { + eventsMutex.lock(); + bool value = !events.empty(); + eventsMutex.unlock(); + return value; +} \ No newline at end of file diff --git a/old/core/Event.hpp b/old/core/Event.hpp new file mode 100644 index 0000000..a8de1f3 --- /dev/null +++ b/old/core/Event.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../Vector.hpp" + +enum class EventType { + Echo, + ChunkChanged, +}; + +struct EchoData { + std::chrono::time_point time; +}; + +struct ChunkChangedData { + Vector chunkPosition; +}; + +using EventData = std::variant; + +struct Event { + EventType type; + EventData data; +}; + +class EventListener { + friend class EventAgregator; + + using HandlerFunc = std::function; + + std::map handlers; //TODO: There must be more elegant solution than std::variant of all data + + std::mutex eventsMutex; + + std::queue events; + + void PushEvent(Event event); + +public: + EventListener(); + ~EventListener(); + bool IsEventsQueueIsNotEmpty(); + + void RegisterHandler(EventType type, HandlerFunc handler) { + handlers[type] = handler; + } + + void HandleEvent() { + eventsMutex.lock(); + if (events.empty()) { + eventsMutex.unlock(); + return; + } + Event event = events.front(); + events.pop(); + eventsMutex.unlock(); + auto function = handlers[event.type]; + function(event.data); + } +}; + +class EventAgregator { + friend EventListener; + + EventAgregator() = default; + static std::queue eventsToHandle; + static std::mutex queueMutex; + static bool isStarted; + static std::vector listeners; + static std::mutex listenersMutex; + + static void EventHandlingLoop(); + + static void RegisterListener(EventListener &listener); + static void UnregisterListener(EventListener &listener); + +public: + static void PushEvent(EventType type, EventData data) { + if (!isStarted) { + isStarted = true; + std::thread(&EventAgregator::EventHandlingLoop).detach(); + } + Event event; + event.type = type; + event.data = data; + eventsToHandle.push(event); + } +}; \ No newline at end of file diff --git a/old/graphics/Gui.cpp b/old/graphics/Gui.cpp new file mode 100644 index 0000000..9e18549 --- /dev/null +++ b/old/graphics/Gui.cpp @@ -0,0 +1 @@ +#include "Gui.hpp" \ No newline at end of file diff --git a/old/graphics/Gui.hpp b/old/graphics/Gui.hpp new file mode 100644 index 0000000..e22a0a7 --- /dev/null +++ b/old/graphics/Gui.hpp @@ -0,0 +1,8 @@ +#pragma once + +class Gui { + +public: + int WHY=0; + +}; diff --git a/old/graphics/RenderSection.cpp b/old/graphics/RenderSection.cpp new file mode 100644 index 0000000..8ebd2c0 --- /dev/null +++ b/old/graphics/RenderSection.cpp @@ -0,0 +1,356 @@ +#include "RenderSection.hpp" +#include + +const GLfloat vertices[] = { + 0, 0, 0, + 1, 0, 1, + 1, 0, 0, + + 0, 0, 0, + 0, 0, 1, + 1, 0, 1, +}; + +const GLfloat uv_coords[] = { + 0.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, +}; + +void RenderState::SetActiveVao(GLuint Vao) { + if (Vao != ActiveVao) { + glBindVertexArray(Vao); + ActiveVao = Vao; + } +} + +void RenderState::SetActiveShader(GLuint Shader) { + if (Shader != ActiveShader) { + glUseProgram(Shader); + ActiveShader = Shader; + } +} + +const GLuint magicUniqueConstant = 88375; +GLuint RenderSection::VboVertices = magicUniqueConstant; +GLuint RenderSection::VboUvs = magicUniqueConstant; +std::map RenderSection::refCounterVbo; +std::map RenderSection::refCounterVao; + + +RenderSection::RenderSection(World *world, Vector position) : sectionPosition(position), world(world) { + if (VboVertices == magicUniqueConstant) { + glGenBuffers(1, &VboVertices); + glGenBuffers(1, &VboUvs); + + //Cube vertices + glBindBuffer(GL_ARRAY_BUFFER, VboVertices); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + //Cube UVs + glBindBuffer(GL_ARRAY_BUFFER, VboUvs); + glBufferData(GL_ARRAY_BUFFER, sizeof(uv_coords), uv_coords, GL_STATIC_DRAW); + + LOG(INFO) << "Created VBOs with vertices (" << VboVertices << ") and UVs (" << VboUvs + << ") for ordinary blocks"; + } + + glGenBuffers(1, &VboTextures); + if (refCounterVbo.find(VboTextures) == refCounterVbo.end()) + refCounterVbo[VboTextures] = 0; + refCounterVbo[VboTextures]++; + + glGenBuffers(1, &VboModels); + if (refCounterVbo.find(VboModels) == refCounterVbo.end()) + refCounterVbo[VboModels] = 0; + refCounterVbo[VboModels]++; + + glGenBuffers(1, &VboColors); + if (refCounterVbo.find(VboColors) == refCounterVbo.end()) + refCounterVbo[VboColors] = 0; + refCounterVbo[VboColors]++; + + glGenVertexArrays(1, &Vao); + if (refCounterVao.find(Vao) == refCounterVao.end()) + refCounterVao[Vao] = 0; + refCounterVao[Vao]++; + + glBindVertexArray(Vao); + { + //Cube vertices + GLuint VertAttribPos = 0; + glBindBuffer(GL_ARRAY_BUFFER, VboVertices); + glVertexAttribPointer(VertAttribPos, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(VertAttribPos); + + //Cube UVs + GLuint UvAttribPos = 2; + glBindBuffer(GL_ARRAY_BUFFER, VboUvs); + glVertexAttribPointer(UvAttribPos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(UvAttribPos); + + //Textures + GLuint textureAttribPos = 7; + glBindBuffer(GL_ARRAY_BUFFER, VboTextures); + glVertexAttribPointer(textureAttribPos, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(textureAttribPos); + glVertexAttribDivisor(textureAttribPos, 1); + glCheckError(); + + //Blocks models + GLuint matAttribPos = 8; + size_t sizeOfMat4 = 4 * 4 * sizeof(GLfloat); + glBindBuffer(GL_ARRAY_BUFFER, VboModels); + glVertexAttribPointer(matAttribPos + 0, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, nullptr); + glVertexAttribPointer(matAttribPos + 1, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, (void *) (1 * 4 * sizeof(GLfloat))); + glVertexAttribPointer(matAttribPos + 2, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, (void *) (2 * 4 * sizeof(GLfloat))); + glVertexAttribPointer(matAttribPos + 3, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, (void *) (3 * 4 * sizeof(GLfloat))); + glEnableVertexAttribArray(matAttribPos + 0); + glEnableVertexAttribArray(matAttribPos + 1); + glEnableVertexAttribArray(matAttribPos + 2); + glEnableVertexAttribArray(matAttribPos + 3); + glVertexAttribDivisor(matAttribPos + 0, 1); + glVertexAttribDivisor(matAttribPos + 1, 1); + glVertexAttribDivisor(matAttribPos + 2, 1); + glVertexAttribDivisor(matAttribPos + 3, 1); + + //Color + GLuint colorAttribPos = 12; + glBindBuffer(GL_ARRAY_BUFFER, VboColors); + glVertexAttribPointer(colorAttribPos, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(colorAttribPos); + glVertexAttribDivisor(colorAttribPos, 1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + glBindVertexArray(0); + glCheckError(); +} + +RenderSection::~RenderSection() { + refCounterVbo[VboTextures]--; + refCounterVbo[VboModels]--; + refCounterVbo[VboColors]--; + refCounterVao[Vao]--; + if (refCounterVbo[VboTextures] <= 0) + glDeleteBuffers(1, &VboTextures); + + if (refCounterVbo[VboModels] <= 0) + glDeleteBuffers(1, &VboTextures); + if (refCounterVbo[VboColors] <= 0) + glDeleteBuffers(1, &VboColors); + + if (refCounterVao[Vao] <= 0) + glDeleteVertexArrays(1, &Vao); +} + +void RenderSection::UpdateState(const std::map &textureAtlas) { + Section §ion = world->GetSection(sectionPosition); + models.clear(); + textures.clear(); + colors.clear(); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + Vector blockPos = Vector(x, y, z) + (sectionPosition * 16); + Block &block = world->GetBlock(blockPos); + if (block.id == 0) + continue; + + auto checkBlockVisibility = [&](Vector block) -> bool { + return section.GetBlock(block).id == 0 || + section.GetBlock(block).id == 31 || + section.GetBlock(block).id == 18; + }; + + unsigned char isVisible = 0; + if (x == 0 || x == 15 || y == 0 || y == 15 || z == 0 || z == 15) { + isVisible = 0b1111'1111; //All faces is visible + } else { + isVisible |= checkBlockVisibility(Vector(x - 1, y, z)) << 0; + isVisible |= checkBlockVisibility(Vector(x + 1, y, z)) << 1; + isVisible |= checkBlockVisibility(Vector(x, y + 1, z)) << 2; + isVisible |= checkBlockVisibility(Vector(x, y - 1, z)) << 3; + isVisible |= checkBlockVisibility(Vector(x, y, z - 1)) << 4; + isVisible |= checkBlockVisibility(Vector(x, y, z + 1)) << 5; + } + + if (isVisible == 0x00) + continue; + + glm::mat4 transform; + transform = glm::translate(transform, glm::vec3(sectionPosition.GetX() * 16, + sectionPosition.GetY() * 16, + sectionPosition.GetZ() * 16)); + transform = glm::translate(transform, glm::vec3(x, y, z)); + glm::vec3 biomeColor(0.275, 0.63, 0.1); + glm::vec3 color(0.0f, 0.0f, 0.0f); + if (block.id == 31 || block.id == 18) + color = biomeColor; + + if (block.id == 31) { //X-cross like blocks rendering + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 2)); + for (int i = 0; i < 4; i++) { + textures.push_back(texture->second); + colors.push_back(color); + } + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0.15f, 0, 0.15f)); + faceTransform = glm::scale(faceTransform, glm::vec3(1.0f, 0.9f, 1.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0, 0.0f, 1.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(45.0f), glm::vec3(1.0f, 0.0f, 0)); + for (int i = 0; i < 4; i++) { + models.push_back(faceTransform); + faceTransform = glm::translate(faceTransform, glm::vec3(0.0f, 0.0f, 0.5f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0.0f, 0.0f, -0.5f)); + } + continue; + } + + if (isVisible >> 0 & 0x1) { //east side of block (X+) + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0, 0.0f, 1.0f)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 2)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 1 & 0x1) { //west side X- + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(1, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0, 0.0f, 1.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 3)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 2 & 0x1) { //Top side Y+ + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 1, 0)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 1)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + if (block.id != 2) + colors.push_back(color); + else + colors.push_back(biomeColor); + } + if (isVisible >> 3 & 0x1) { //Bottom side Y- + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(180.0f), glm::vec3(1.0f, 0, 0)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 0)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 4 & 0x1) { //south side Z+ + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(1, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0.0f, -1.0f, 0.0f)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 3)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 5 & 0x1) { //north side Z- + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 0, 1)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0.0f, -1.0f, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1)); + faceTransform = glm::rotate(faceTransform, glm::radians(180.0f), glm::vec3(1, 0, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1.0f)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 4)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + } + } + } + numOfFaces = textures.size(); + hash = section.GetHash(); +} + +void RenderSection::Render(RenderState &state) { + if (!isEnabled) return; + if (!models.empty()) { + glBindBuffer(GL_ARRAY_BUFFER, VboTextures); + glBufferData(GL_ARRAY_BUFFER, textures.size() * sizeof(glm::vec4), textures.data(), GL_DYNAMIC_DRAW); + textures.clear(); + + glBindBuffer(GL_ARRAY_BUFFER, VboModels); + glBufferData(GL_ARRAY_BUFFER, models.size() * sizeof(glm::mat4), models.data(), GL_DYNAMIC_DRAW); + models.clear(); + + glBindBuffer(GL_ARRAY_BUFFER, VboColors); + glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_DYNAMIC_DRAW); + colors.clear(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + state.SetActiveVao(Vao); + glDrawArraysInstanced(GL_TRIANGLES, 0, 6, numOfFaces); + glCheckError(); +} + +Section *RenderSection::GetSection() { + return &world->GetSection(sectionPosition); +} + +RenderSection::RenderSection(const RenderSection &other) { + this->world = other.world; + this->VboModels = other.VboModels; + this->VboTextures = other.VboTextures; + this->VboColors = other.VboColors; + this->sectionPosition = other.sectionPosition; + this->Vao = other.Vao; + this->numOfFaces = other.numOfFaces; + this->models = other.models; + this->textures = other.textures; + this->colors = other.colors; + this->hash = other.hash; + + refCounterVbo[VboTextures]++; + refCounterVbo[VboModels]++; + refCounterVbo[VboColors]++; + refCounterVao[Vao]++; +} + +void RenderSection::SetEnabled(bool isEnabled) { + this->isEnabled = isEnabled; +} + +bool RenderSection::IsNeedUpdate() { + size_t currentHash = world->GetSection(sectionPosition).GetHash(); + bool isNeedUpdate = currentHash != hash; + return isNeedUpdate; +} \ No newline at end of file diff --git a/old/graphics/RenderSection.hpp b/old/graphics/RenderSection.hpp new file mode 100644 index 0000000..7f97624 --- /dev/null +++ b/old/graphics/RenderSection.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "../core/AssetManager.hpp" +#include "../world/Section.hpp" +#include "../world/World.hpp" + +class RenderState { + GLuint ActiveVao; + GLuint ActiveShader; +public: + void SetActiveVao(GLuint Vao); + void SetActiveShader(GLuint Shader); +}; + +class RenderSection { + Vector sectionPosition; + World *world; + GLuint Vao, VboTextures, VboModels, VboColors; + std::vector models; + std::vector textures; + std::vector colors; + + static GLuint VboVertices, VboUvs; + static std::map refCounterVbo; + static std::map refCounterVao; + + size_t numOfFaces = 0; + + bool isEnabled = true; + + size_t hash = 0; +public: + RenderSection(World *world, Vector position); + RenderSection(const RenderSection &other); + ~RenderSection(); + + void UpdateState(const std::map &textureAtlas); + void Render(RenderState &state); + + void SetEnabled(bool isEnabled); + + Section *GetSection(); + + bool IsNeedUpdate(); +}; \ No newline at end of file diff --git a/old/graphics/Shader.cpp b/old/graphics/Shader.cpp new file mode 100644 index 0000000..cf43115 --- /dev/null +++ b/old/graphics/Shader.cpp @@ -0,0 +1,115 @@ +#include "Shader.hpp" + +Shader::Shader(const GLchar *vertexPath, const GLchar *fragmentPath, const GLchar *geometryPath) { + vertex = vertexPath; + fragment = fragmentPath; + // 1. Получаем исходный код шейдера из filePath + std::string vertexCode; + std::string fragmentCode; + std::string geometryCode; + std::ifstream vShaderFile; + std::ifstream fShaderFile; + std::ifstream gShaderFile; + // Удостоверимся, что ifstream объекты могут выкидывать исключения + vShaderFile.exceptions(std::ifstream::failbit); + fShaderFile.exceptions(std::ifstream::failbit); + gShaderFile.exceptions(std::ifstream::failbit); + try { + // Открываем файлы + vShaderFile.open(vertexPath); + fShaderFile.open(fragmentPath); + if (geometryPath != nullptr) + gShaderFile.open(geometryPath); + std::stringstream vShaderStream, fShaderStream, gShaderStream; + // Считываем данные в потоки + vShaderStream << vShaderFile.rdbuf(); + fShaderStream << fShaderFile.rdbuf(); + if (geometryPath != nullptr) + gShaderStream << gShaderFile.rdbuf(); + // Закрываем файлы + vShaderFile.close(); + fShaderFile.close(); + if (geometryPath != nullptr) + gShaderFile.close(); + // Преобразовываем потоки в массив GLchar + vertexCode = vShaderStream.str(); + fragmentCode = fShaderStream.str(); + if (geometryPath != nullptr) + geometryCode = gShaderStream.str(); + } + catch (std::ifstream::failure e) { + LOG(ERROR) << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ"; + } + const GLchar *vShaderCode = vertexCode.c_str(); + const GLchar *fShaderCode = fragmentCode.c_str(); + const GLchar *gShaderCode = geometryCode.c_str(); + + // 2. Сборка шейдеров + GLuint vertex, fragment, geometry; + GLint success; + GLchar infoLog[512]; + + // Вершинный шейдер + vertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex, 1, &vShaderCode, NULL); + glCompileShader(vertex); + // Если есть ошибки - вывести их + glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertex, 512, NULL, infoLog); + LOG(ERROR) << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog; + }; + + // Вершинный шейдер + fragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment, 1, &fShaderCode, NULL); + glCompileShader(fragment); + // Если есть ошибки - вывести их + glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragment, 512, NULL, infoLog); + LOG(ERROR) << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog; + }; + + if (geometryPath != nullptr) { + geometry = glCreateShader(GL_GEOMETRY_SHADER); + glShaderSource(geometry, 1, &gShaderCode, NULL); + glCompileShader(geometry); + // Если есть ошибки - вывести их + glGetShaderiv(geometry, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(geometry, 512, NULL, infoLog); + LOG(ERROR) << "ERROR::SHADER::GEOMETRY::COMPILATION_FAILED\n" << infoLog; + }; + } + + // Шейдерная программа + this->Program = glCreateProgram(); + glAttachShader(this->Program, vertex); + glAttachShader(this->Program, fragment); + if (geometryPath != nullptr) + glAttachShader(this->Program, geometry); + glLinkProgram(this->Program); + //Если есть ошибки - вывести их + glGetProgramiv(this->Program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(this->Program, 512, NULL, infoLog); + LOG(FATAL) << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog; + } + + // Удаляем шейдеры, поскольку они уже в программу и нам больше не нужны. + glDeleteShader(vertex); + glDeleteShader(fragment); +} + +void Shader::Use() { + glUseProgram(this->Program); +} + +void Shader::Reload() { + const GLchar *vertexPath = vertex; + const GLchar *fragmentPath = fragment; + this->~Shader(); + new(this) Shader(vertexPath, fragmentPath); + LOG(INFO) << "Shader is realoded!"; +} diff --git a/old/graphics/Shader.hpp b/old/graphics/Shader.hpp new file mode 100644 index 0000000..17a434e --- /dev/null +++ b/old/graphics/Shader.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include +#include + +class Shader +{ +private: + const GLchar *vertex; + const GLchar *fragment; +public: + // Идентификатор программы + GLuint Program; + // Конструктор считывает и собирает шейдер + Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const GLchar* geometryPath = nullptr); + // Использование программы + void Use(); + + void Reload(); +}; \ No newline at end of file diff --git a/old/graphics/Texture.cpp b/old/graphics/Texture.cpp new file mode 100644 index 0000000..5d183c3 --- /dev/null +++ b/old/graphics/Texture.cpp @@ -0,0 +1,37 @@ +#include "Texture.hpp" + +Texture::Texture(std::string filename, GLenum textureWrapping, GLenum textureFiltering) { + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + //Texture options + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, textureWrapping); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, textureWrapping); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, textureFiltering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + //Image load + sf::Image image; + if (!image.loadFromFile(filename)) { + LOG(ERROR) << "Can't open image " << filename; + throw 201; + } + if (image.getPixelsPtr() == nullptr) { + LOG(ERROR) << "Image data is corrupted!"; + throw 202; + } + image.flipVertically(); + + + //Creating texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getSize().x, image.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, + (GLvoid *) image.getPixelsPtr()); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + +} + +Texture::~Texture() { + glDeleteTextures(1, &texture); +} diff --git a/old/graphics/Texture.hpp b/old/graphics/Texture.hpp new file mode 100644 index 0000000..5b2afcf --- /dev/null +++ b/old/graphics/Texture.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +class Texture { + Texture(Texture&); + Texture&operator=(Texture&); +public: + GLuint texture; + Texture(std::string filename, GLenum textureWrapping = GL_CLAMP_TO_BORDER, GLenum textureFiltering = GL_NEAREST); + ~Texture(); +}; \ No newline at end of file diff --git a/old/graphics/Widget.cpp b/old/graphics/Widget.cpp new file mode 100644 index 0000000..278af55 --- /dev/null +++ b/old/graphics/Widget.cpp @@ -0,0 +1 @@ +#include "Widget.hpp" \ No newline at end of file diff --git a/old/graphics/Widget.hpp b/old/graphics/Widget.hpp new file mode 100644 index 0000000..c4d5dc1 --- /dev/null +++ b/old/graphics/Widget.hpp @@ -0,0 +1,8 @@ +#pragma once + +class Widget { + unsigned int x,y,w,h; +public: + Widget(Widget *parent); + ~Widget(); +}; diff --git a/old/main.cpp b/old/main.cpp new file mode 100644 index 0000000..0f93420 --- /dev/null +++ b/old/main.cpp @@ -0,0 +1,35 @@ +#include "core/Core.hpp" +#include "core/Event.hpp" + +const char *getTimeSinceProgramStart(void) { + static auto initialTime = std::chrono::steady_clock().now(); + auto now = std::chrono::steady_clock().now(); + std::chrono::duration seconds = now-initialTime; + static char buffer[30]; + sprintf(buffer, "%.2f", seconds.count()); + return buffer; +} + +INITIALIZE_EASYLOGGINGPP + +int main_old() { + el::Configurations loggerConfiguration; + el::Helpers::installCustomFormatSpecifier( + el::CustomFormatSpecifier("%startTime", std::bind(getTimeSinceProgramStart))); + std::string format = "[%startTime][%level][%thread][%fbase]: %msg"; + loggerConfiguration.set(el::Level::Info, el::ConfigurationType::Format, format); + loggerConfiguration.set(el::Level::Error, el::ConfigurationType::Format, format); + loggerConfiguration.set(el::Level::Fatal, el::ConfigurationType::Format, format); + loggerConfiguration.set(el::Level::Warning, el::ConfigurationType::Format, format); + el::Helpers::setThreadName("Render"); + el::Loggers::reconfigureAllLoggers(loggerConfiguration); + el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); + LOG(INFO) << "Logger is configured"; + + LOG(WARNING)<<"Sizeof EventData is "< Network::ReceivePacket(ConnectionState state) { + int packetSize = stream->ReadVarInt(); + auto packetData = stream->ReadByteArray(packetSize); + StreamBuffer streamBuffer(packetData.data(), packetData.size()); + int packetId = streamBuffer.ReadVarInt(); + auto packet = ReceivePacketByPacketId(packetId, state, streamBuffer); + return packet; +} + +void Network::SendPacket(Packet &packet) { + StreamCounter packetSize; + packetSize.WriteVarInt(packet.GetPacketId()); + packet.ToStream(&packetSize); + stream->WriteVarInt(packetSize.GetCountedSize()); + stream->WriteVarInt(packet.GetPacketId()); + packet.ToStream(stream); +} + +std::shared_ptr Network::ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream) { + std::shared_ptr packet(nullptr); + switch (state) { + case Handshaking: + switch (packetId) { + case PacketNameHandshakingCB::Handshake: + packet = std::make_shared(); + break; + } + case Login: + switch (packetId) { + case PacketNameLoginCB::LoginSuccess: + packet = std::make_shared(); + break; + } + break; + case Play: + packet = ParsePacketPlay((PacketNamePlayCB) packetId); + break; + case Status: + break; + } + if (packet.get() != nullptr) + packet->FromStream(&stream); + return packet; +} + +std::shared_ptr Network::ParsePacketPlay(PacketNamePlayCB id) { + switch (id) { + case SpawnObject: + break; + case SpawnExperienceOrb: + break; + case SpawnGlobalEntity: + break; + case SpawnMob: + break; + case SpawnPainting: + break; + case SpawnPlayer: + break; + case AnimationCB: + break; + case Statistics: + break; + case BlockBreakAnimation: + break; + case UpdateBlockEntity: + break; + case BlockAction: + break; + case BlockChange: + break; + case BossBar: + break; + case ServerDifficulty: + break; + case TabCompleteCB: + break; + case ChatMessageCB: + break; + case MultiBlockChange: + break; + case ConfirmTransactionCB: + break; + case CloseWindowCB: + break; + case OpenWindow: + break; + case WindowItems: + break; + case WindowProperty: + break; + case SetSlot: + break; + case SetCooldown: + break; + case PluginMessageCB: + break; + case NamedSoundEffect: + break; + case DisconnectPlay: + return std::make_shared(); + case EntityStatus: + break; + case Explosion: + break; + case UnloadChunk: + break; + case ChangeGameState: + break; + case KeepAliveCB: + return std::make_shared(); + case ChunkData: + return std::make_shared(); + case Effect: + break; + case Particle: + break; + case JoinGame: + return std::make_shared(); + case Map: + break; + case EntityRelativeMove: + break; + case EntityLookAndRelativeMove: + break; + case EntityLook: + break; + case Entity: + break; + case VehicleMove: + break; + case OpenSignEditor: + break; + case PlayerAbilitiesCB: + break; + case CombatEvent: + break; + case PlayerListItem: + break; + case PlayerPositionAndLookCB: + return std::make_shared(); + case UseBed: + break; + case DestroyEntities: + break; + case RemoveEntityEffect: + break; + case ResourcePackSend: + break; + case Respawn: + break; + case EntityHeadLook: + break; + case WorldBorder: + break; + case Camera: + break; + case HeldItemChangeCB: + break; + case DisplayScoreboard: + break; + case EntityMetadata: + break; + case AttachEntity: + break; + case EntityVelocity: + break; + case EntityEquipment: + break; + case SetExperience: + break; + case UpdateHealth: + return std::make_shared(); + case ScoreboardObjective: + break; + case SetPassengers: + break; + case Teams: + break; + case UpdateScore: + break; + case SpawnPosition: + return std::make_shared(); + case TimeUpdate: + break; + case Title: + break; + case SoundEffect: + break; + case PlayerListHeaderAndFooter: + break; + case CollectItem: + break; + case EntityTeleport: + break; + case EntityProperties: + break; + case EntityEffect: + break; + case UnlockRecipes: + break; + case SelectAdvancementTab: + break; + case Advancements: + break; + } + return nullptr; +} diff --git a/old/network/Network.hpp b/old/network/Network.hpp new file mode 100644 index 0000000..1281289 --- /dev/null +++ b/old/network/Network.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "Socket.hpp" +#include "Packet.hpp" + +enum ConnectionState { + Handshaking, + Login, + Play, + Status, +}; + +class Network { + Socket *socket; + StreamSocket *stream; + + std::shared_ptr ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream); +public: + Network(std::string address, unsigned short port); + ~Network(); + + std::shared_ptr ReceivePacket(ConnectionState state = Play); + void SendPacket(Packet &packet); + std::shared_ptr ParsePacketPlay(PacketNamePlayCB id); +}; \ No newline at end of file diff --git a/old/network/NetworkClient.cpp b/old/network/NetworkClient.cpp new file mode 100644 index 0000000..0b759e6 --- /dev/null +++ b/old/network/NetworkClient.cpp @@ -0,0 +1,93 @@ +#include "NetworkClient.hpp" + +NetworkClient::NetworkClient(std::string address, unsigned short port, std::string username, bool &quit) + : network(address, port), isRunning(quit) { + state = Handshaking; + + PacketHandshake handshake; + handshake.protocolVersion = 335; + handshake.serverAddress = "127.0.0.1"; + handshake.serverPort = 25565; + handshake.nextState = 2; + network.SendPacket(handshake); + state = Login; + + PacketLoginStart loginStart; + loginStart.Username = "HelloOne"; + network.SendPacket(loginStart); + + auto response = std::static_pointer_cast(network.ReceivePacket(Login)); + if (response->Username != username) { + throw std::logic_error("Received username is not sended username"); + } + + state = Play; + + isActive = true; + std::thread thread(&NetworkClient::NetworkLoop, this); + std::swap(networkThread, thread); +} + +NetworkClient::~NetworkClient() { + isActive = false; + networkThread.join(); +} + +std::shared_ptr NetworkClient::ReceivePacket() { + if (toReceive.empty()) + return std::shared_ptr(nullptr); + toReceiveMutex.lock(); + auto ret = toReceive.front(); + toReceive.pop(); + toReceiveMutex.unlock(); + return ret; +} + +void NetworkClient::SendPacket(std::shared_ptr packet) { + toSendMutex.lock(); + toSend.push(packet); + toSendMutex.unlock(); +} + +void NetworkClient::NetworkLoop() { + auto timeOfLastKeepAlivePacket = std::chrono::steady_clock::now(); + el::Helpers::setThreadName("Network"); + LOG(INFO) << "Network thread is started"; + try { + while (isActive) { + toSendMutex.lock(); + while (!toSend.empty()) { + if (toSend.front()!=nullptr) + network.SendPacket(*toSend.front()); + toSend.pop(); + } + toSendMutex.unlock(); + auto packet = network.ReceivePacket(state); + if (packet.get() != nullptr) { + if (packet->GetPacketId() != PacketNamePlayCB::KeepAliveCB) { + toReceiveMutex.lock(); + toReceive.push(packet); + toReceiveMutex.unlock(); + } else { + timeOfLastKeepAlivePacket = std::chrono::steady_clock::now(); + auto packetKeepAlive = std::static_pointer_cast(packet); + auto packetKeepAliveSB = std::make_shared(packetKeepAlive->KeepAliveId); + network.SendPacket(*packetKeepAliveSB); + } + } + using namespace std::chrono_literals; + if (std::chrono::steady_clock::now() - timeOfLastKeepAlivePacket > 20s) { + auto disconnectPacket = std::make_shared(); + disconnectPacket->Reason = "Timeout"; + toReceiveMutex.lock(); + toReceive.push(disconnectPacket); + toReceiveMutex.unlock(); + break; + } + } + } catch (std::exception &e) { + LOG(ERROR) << "Exception catched in NetworkLoop: " << e.what(); + isRunning = false; + } + LOG(INFO) << "Network thread is stopped"; +} diff --git a/old/network/NetworkClient.hpp b/old/network/NetworkClient.hpp new file mode 100644 index 0000000..cf41f91 --- /dev/null +++ b/old/network/NetworkClient.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "Network.hpp" + +class NetworkClient { + Network network; + std::thread networkThread; + std::mutex toSendMutex; + std::mutex toReceiveMutex; + std::queue > toSend; + std::queue > toReceive; + bool isActive=true; + bool &isRunning; + ConnectionState state; + void NetworkLoop(); +public: + NetworkClient(std::string address, unsigned short port, std::string username, bool &quit); + ~NetworkClient(); + + std::shared_ptr ReceivePacket(); + void SendPacket(std::shared_ptr packet); +}; \ No newline at end of file diff --git a/old/network/Packet.hpp b/old/network/Packet.hpp new file mode 100644 index 0000000..d615332 --- /dev/null +++ b/old/network/Packet.hpp @@ -0,0 +1,521 @@ +#pragma once + +#include + +#include "Stream.hpp" + +enum PacketNameLoginSB { + LoginStart = 0x00, + EncryptionResponse = 0x01, +}; +enum PacketNamePlaySB { + TeleportConfirm, + PrepareCraftingGrid, + TabCompleteSB, + ChatMessageSB, + ClientStatus, + ClientSettings, + ConfirmTransactionSB, + EnchantItem, + ClickWindow, + CloseWindowSB, + PluginMessageSB, + UseEntity, + KeepAliveSB, + Player, + PlayerPosition, + PlayerPositionAndLookSB, + PlayerLook, + VehicleMoveSB, + SteerBoat, + PlayerAbilitiesSB, + PlayerDigging, + EntityAction, + SteerVehicle, + CraftingBookData, + ResourcePackStatus, + AdvancementTab, + HeldItemChangeSB, + CreativeInventoryAction, + UpdateSign, + AnimationSB, + Spectate, + PlayerBlockPlacement, + UseItem, +}; + +enum PacketNameHandshakingCB { + Handshake = 0x00, +}; +enum PacketNameLoginCB { + Disconnect = 0x00, + EncryptionRequest = 0x01, + LoginSuccess = 0x02, + SetCompression = 0x03, +}; +enum PacketNamePlayCB { + SpawnObject = 0x00, + SpawnExperienceOrb, + SpawnGlobalEntity, + SpawnMob, + SpawnPainting, + SpawnPlayer, + AnimationCB, + Statistics, + BlockBreakAnimation, + UpdateBlockEntity, + BlockAction, + BlockChange, + BossBar, + ServerDifficulty, + TabCompleteCB, + ChatMessageCB, + MultiBlockChange, + ConfirmTransactionCB, + CloseWindowCB, + OpenWindow, + WindowItems, + WindowProperty, + SetSlot, + SetCooldown, + PluginMessageCB, + NamedSoundEffect, + DisconnectPlay, + EntityStatus, + Explosion, + UnloadChunk, + ChangeGameState, + KeepAliveCB, + ChunkData, + Effect, + Particle, + JoinGame, + Map, + EntityRelativeMove, + EntityLookAndRelativeMove, + EntityLook, + Entity, + VehicleMove, + OpenSignEditor, + PlayerAbilitiesCB, + CombatEvent, + PlayerListItem, + PlayerPositionAndLookCB, + UseBed, + UnlockRecipes, + DestroyEntities, + RemoveEntityEffect, + ResourcePackSend, + Respawn, + EntityHeadLook, + SelectAdvancementTab, + WorldBorder, + Camera, + HeldItemChangeCB, + DisplayScoreboard, + EntityMetadata, + AttachEntity, + EntityVelocity, + EntityEquipment, + SetExperience, + UpdateHealth, + ScoreboardObjective, + SetPassengers, + Teams, + UpdateScore, + SpawnPosition, + TimeUpdate, + Title, + SoundEffect, + PlayerListHeaderAndFooter, + CollectItem, + EntityTeleport, + Advancements, + EntityProperties, + EntityEffect, +}; + +struct Packet { + virtual ~Packet() = default; + virtual void ToStream(StreamOutput *stream) = 0; + virtual void FromStream(StreamInput *stream) = 0; + virtual int GetPacketId() = 0; +}; + +struct PacketHandshake : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(protocolVersion); + stream->WriteString(serverAddress); + stream->WriteUShort(serverPort); + stream->WriteVarInt(nextState); + } + + void FromStream(StreamInput *stream) override { + protocolVersion = stream->ReadVarInt(); + serverAddress = stream->ReadString(); + serverPort = stream->ReadUShort(); + nextState = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNameHandshakingCB::Handshake; + } + + int protocolVersion; + std::string serverAddress; + unsigned short serverPort; + int nextState; +}; + +struct PacketLoginStart : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginSB::LoginStart; + } + + std::string Username; +}; + +struct PacketLoginSuccess : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Uuid); + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Uuid = stream->ReadString(); + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginCB::LoginSuccess; + } + + std::string Uuid; + std::string Username; +}; + +struct PacketJoinGame : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(EntityId); + stream->WriteUByte(Gamemode); + stream->WriteInt(Dimension); + stream->WriteUByte(Difficulty); + stream->WriteUByte(MaxPlayers); + stream->WriteString(LevelType); + stream->WriteBool(ReducedDebugInfo); + } + + void FromStream(StreamInput *stream) override { + EntityId = stream->ReadInt(); + Gamemode = stream->ReadUByte(); + Dimension = stream->ReadInt(); + Difficulty = stream->ReadUByte(); + MaxPlayers = stream->ReadUByte(); + LevelType = stream->ReadString(); + ReducedDebugInfo = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlayCB::JoinGame; + } + + int EntityId; + unsigned char Gamemode; + int Dimension; + unsigned char Difficulty; + unsigned char MaxPlayers; + std::string LevelType; + bool ReducedDebugInfo; +}; + +struct PacketDisconnectPlay : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Reason); //TODO: Implement chat-wrapper + } + + void FromStream(StreamInput *stream) override { + Reason = stream->ReadChat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::DisconnectPlay; + } + + std::string Reason; +}; + +struct PacketSpawnPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WritePosition(Location); + } + + void FromStream(StreamInput *stream) override { + Location = stream->ReadPosition(); + } + + int GetPacketId() override { + return PacketNamePlayCB::SpawnPosition; + } + + Vector Location; +}; + +struct PacketKeepAliveCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::KeepAliveCB; + } + + int KeepAliveId; +}; + +struct PacketKeepAliveSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::KeepAliveSB; + } + + int KeepAliveId; + + PacketKeepAliveSB(int KeepAliveId) : KeepAliveId(KeepAliveId) {} +}; + +struct PacketPlayerPositionAndLookCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(Y); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteUByte(Flags); + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + Y = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + Flags = stream->ReadUByte(); + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::PlayerPositionAndLookCB; + } + + double X; + double Y; + double Z; + float Yaw; + float Pitch; + unsigned char Flags; + int TeleportId; +}; + +struct PacketTeleportConfirm : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::TeleportConfirm; + } + + int TeleportId; + + PacketTeleportConfirm(int TeleportId) : TeleportId(TeleportId) {} +}; + +struct PacketClientStatus : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(ActionId); + } + + void FromStream(StreamInput *stream) override { + ActionId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::ClientStatus; + } + + int ActionId; + + PacketClientStatus(int ActionId) : ActionId(ActionId) {} +}; + +struct PacketPlayerPositionAndLookSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + FeetY = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPositionAndLookSB; + } + + + double X; + double FeetY; + double Z; + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerPositionAndLookSB(double X, double FeetY, double Z, + float Yaw, float Pitch, bool OnGround) : X(X), FeetY(FeetY), Z(Z), Yaw(Yaw), + Pitch(Pitch), OnGround(OnGround) {} +}; + +struct PacketChunkData : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(ChunkX); + stream->WriteInt(ChunkZ); + stream->WriteBool(GroundUpContinuous); + stream->WriteInt(PrimaryBitMask); + stream->WriteVarInt(Data.size()); + stream->WriteByteArray(Data); + stream->WriteVarInt(BlockEntities.size()); + LOG(FATAL) << "Serializing unimplemented packet"; + } + + void FromStream(StreamInput *stream) override { + ChunkX = stream->ReadInt(); + ChunkZ = stream->ReadInt(); + GroundUpContinuous = stream->ReadBool(); + PrimaryBitMask = stream->ReadVarInt(); + int Size = stream->ReadVarInt(); + Data = stream->ReadByteArray(Size); + int NumberOfBlockEntities = stream->ReadVarInt(); //TODO: Need NBT + for (int i = 0; i < NumberOfBlockEntities; i++) { + //BlockEntities[i] = stream->ReadNbt(); + } + } + + int GetPacketId() override { + return PacketNamePlayCB::ChunkData; + } + + int ChunkX; + int ChunkZ; + bool GroundUpContinuous; + int PrimaryBitMask; + //int Size; + std::vector Data; + //int NumberOfBlockEntities; + std::vector BlockEntities; //TODO: Replace int with NbtTag and implement NbtTree +}; + +struct PacketPlayerPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + FeetY = stream->ReadDouble(); + Z = stream->ReadDouble(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPosition; + } + + double X; + double FeetY; + double Z; + bool OnGround; + + PacketPlayerPosition(double X, double Y, double Z, bool ground) : X(X), FeetY(Y), Z(Z), OnGround(ground) {} +}; + +struct PacketPlayerLook : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerLook; + } + + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerLook(float Yaw, float Pitch, bool ground) : Yaw(Yaw), Pitch(Pitch), OnGround(ground) {} +}; + +struct PacketUpdateHealth : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Health); + stream->WriteVarInt(Food); + stream->WriteFloat(FoodSaturation); + } + + void FromStream(StreamInput *stream) override { + Health = stream->ReadFloat(); + Food = stream->ReadVarInt(); + FoodSaturation = stream->ReadFloat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::UpdateHealth; + } + + float Health; + int Food; + float FoodSaturation; +}; \ No newline at end of file diff --git a/old/network/Socket.cpp b/old/network/Socket.cpp new file mode 100644 index 0000000..2bbf49a --- /dev/null +++ b/old/network/Socket.cpp @@ -0,0 +1,29 @@ +#include "Socket.hpp" + +Socket::Socket(std::string address, unsigned short port) { + sf::Socket::Status connectionStatus = socket.connect(sf::IpAddress(address), port); + if (connectionStatus == sf::Socket::Status::Error) + throw std::runtime_error("Can't connect to remote server"); + else if (connectionStatus != sf::Socket::Status::Done) + throw std::runtime_error("Connection failed with unknown reason"); +} + +Socket::~Socket() { + socket.disconnect(); +} + +void Socket::Read(unsigned char *buffPtr, size_t buffLen) { + size_t received = 0; + socket.receive(buffPtr, buffLen, received); + size_t totalReceived = received; + while (totalReceived < buffLen) { + if (socket.receive(buffPtr + totalReceived, buffLen - totalReceived, received) != sf::Socket::Done) + throw std::runtime_error("Raw socket data receiving is failed"); + totalReceived += received; + } +} + +void Socket::Write(unsigned char *buffPtr, size_t buffLen) { + if (socket.send(buffPtr, buffLen) != sf::Socket::Done) + throw std::runtime_error("Raw socket data sending is failed"); +} diff --git a/old/network/Socket.hpp b/old/network/Socket.hpp new file mode 100644 index 0000000..48bcad9 --- /dev/null +++ b/old/network/Socket.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +/** + * Platform independent class for working with platform dependent hardware socket + * @brief Wrapper around raw sockets + * @warning Connection state is based on lifetime of Socket object instance, ie connected at ctor and disconnect at dtor + * @todo Replace SFML's socket with WinSock and POSIX's socket implementation + */ +class Socket { + sf::TcpSocket socket; +public: + /** + * Constructs Socket class instance from IP's string and Port number and connects to remote server + * @param[in] address IP address of remote server. String should be ANSI and contains 4 one-byte values separated by dots + * @param[in] port target port of remote server to connect + * @throw std::runtime_error if connection is failed + */ + Socket(std::string address, unsigned short port); + + /** + * Destruct Socket instance and disconnect from server + * @warning There is no way to force disconnect, except use delete for manually allocated objects and scope of visibility for variables on stack + */ + ~Socket(); + + /** + * Reads data from socket and write to buffer + * @warning This is blocking function, and execution flow will not be returned until all required data is sended + * @warning Reported buffer length must be <= actual size of buffer, or memory corruption will be caused + * @param[out] buffPtr Pointer to buffer, where data must be placed + * @param[in] buffLen Length of data, that must be readed from server and writed to buffer + */ + void Read(unsigned char *buffPtr, size_t buffLen); + + /** + * Writes data from buffer to socket + * @warning This is blocking function, and execution flow will not be returned until all required data is received + * @param[in] buffPtr Pointer to buffer that contain data to send + * @param[in] buffLen Length of buffer + */ + void Write(unsigned char *buffPtr, size_t buffLen); +}; \ No newline at end of file diff --git a/old/network/Stream.cpp b/old/network/Stream.cpp new file mode 100644 index 0000000..54b1e1b --- /dev/null +++ b/old/network/Stream.cpp @@ -0,0 +1,348 @@ +#include "Stream.hpp" + +const int MAX_VARINT_LENGTH = 5; + +bool StreamInput::ReadBool() { + unsigned char value; + ReadData(&value, 1); + return value != 0; +} + +signed char StreamInput::ReadByte() { + signed char value; + ReadData((unsigned char *) &value, 1); + endswap(value); + return value; +} + +unsigned char StreamInput::ReadUByte() { + unsigned char value; + ReadData(&value, 1); + endswap(value); + return value; +} + +short StreamInput::ReadShort() { + unsigned short value; + ReadData((unsigned char *) &value, 2); + endswap(value); + return value; +} + +unsigned short StreamInput::ReadUShort() { + unsigned short value; + ReadData((unsigned char *) &value, 2); + endswap(value); + return value; +} + +int StreamInput::ReadInt() { + int value; + ReadData((unsigned char *) &value, 4); + endswap(value); + return value; +} + +long long StreamInput::ReadLong() { + long long value; + ReadData((unsigned char *) &value, 8); + endswap(value); + return value; +} + +float StreamInput::ReadFloat() { + float value; + ReadData((unsigned char *) &value, 4); + endswap(value); + return value; +} + +double StreamInput::ReadDouble() { + double value; + ReadData((unsigned char *) &value, 8); + endswap(value); + return value; +} + +std::string StreamInput::ReadString() { + int strLength = ReadVarInt(); + unsigned char *buff = new unsigned char[strLength + 1]; + ReadData(buff, strLength); + buff[strLength] = 0; + std::string str((char *) buff); + delete buff; + return str; +} + +std::string StreamInput::ReadChat() { + std::string str, jsonStr = ReadString(); + nlohmann::json json; + try { + json = nlohmann::json::parse(jsonStr); + } catch (std::exception &e) { + LOG(WARNING) << "Chat json parsing failed: " << e.what(); + LOG(WARNING) << "Corrupted json: " << jsonStr; + return ""; + } + if (json.find("translate") != json.end()) + if (json["translate"].get() == "multiplayer.disconnect.kicked") + return "kicked by operator"; + for (auto &it:json["extra"]) { + str += it["text"].get(); + } + return str; +} + +int StreamInput::ReadVarInt() { + unsigned char data[MAX_VARINT_LENGTH] = {0}; + size_t dataLen = 0; + do { + ReadData(&data[dataLen], 1); + } while ((data[dataLen++] & 0x80) != 0); + + int readed = 0; + int result = 0; + char read; + do { + read = data[readed]; + int value = (read & 0b01111111); + result |= (value << (7 * readed)); + readed++; + } while ((read & 0b10000000) != 0); + + return result; +} + +long long StreamInput::ReadVarLong() { + return 0; +} + +std::vector StreamInput::ReadEntityMetadata() { + return std::vector(); +} + +std::vector StreamInput::ReadSlot() { + return std::vector(); +} + +std::vector StreamInput::ReadNbtTag() { + return std::vector(); +} + +Vector StreamInput::ReadPosition() { + unsigned long long t = ReadLong(); + int x = t >> 38; + int y = (t >> 26) & 0xFFF; + int z = t << 38 >> 38; + if (x >= pow(2, 25)) { + x -= pow(2, 26); + } + if (y >= pow(2, 11)) { + y -= pow(2, 12); + } + if (z >= pow(2, 25)) { + z -= pow(2, 26); + } + return Vector(x, y, z); +} + +unsigned char StreamInput::ReadAngle() { + return ReadUByte(); +} + +std::vector StreamInput::ReadUuid() { + unsigned char buff[16]; + ReadData(buff, 16); + endswap(buff, 16); + return std::vector(buff, buff + 16); +} + +std::vector StreamInput::ReadByteArray(size_t arrLength) { + unsigned char *buffer = new unsigned char[arrLength]; + ReadData(buffer, arrLength); + std::vector ret(buffer, buffer + arrLength); + delete buffer; + return ret; + +} + +void StreamOutput::WriteBool(bool value) { + unsigned char val = value ? 1 : 0; + endswap(val); + WriteData(&val, 1); +} + +void StreamOutput::WriteByte(signed char value) { + endswap(value); + WriteData((unsigned char *) &value, 1); +} + +void StreamOutput::WriteUByte(unsigned char value) { + endswap(value); + WriteData(&value, 1); +} + +void StreamOutput::WriteShort(short value) { + endswap(value); + WriteData((unsigned char *) &value, 2); +} + +void StreamOutput::WriteUShort(unsigned short value) { + endswap(value); + WriteData((unsigned char *) &value, 2); +} + +void StreamOutput::WriteInt(int value) { + endswap(value); + WriteData((unsigned char *) &value, 4); +} + +void StreamOutput::WriteLong(long long value) { + endswap(value); + WriteData((unsigned char *) &value, 8); +} + +void StreamOutput::WriteFloat(float value) { + endswap(value); + WriteData((unsigned char *) &value, 4); +} + +void StreamOutput::WriteDouble(double value) { + endswap(value); + WriteData((unsigned char *) &value, 8); +} + +void StreamOutput::WriteString(std::string value) { + WriteVarInt(value.size()); + WriteData((unsigned char *) value.data(), value.size()); +} + +void StreamOutput::WriteChat(std::string value) { + WriteString(value); +} + +void StreamOutput::WriteVarInt(int value) { + unsigned char buff[5]; + size_t len = 0; + do { + unsigned char temp = (unsigned char) (value & 0b01111111); + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buff[len] = temp; + len++; + } while (value != 0); + WriteData(buff, len); +} + +void StreamOutput::WriteVarLong(long long value) { + unsigned char buff[10]; + size_t len = 0; + do { + unsigned char temp = (unsigned char) (value & 0b01111111); + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buff[len] = temp; + len++; + } while (value != 0); + WriteData(buff, len); +} + +void StreamOutput::WriteEntityMetadata(std::vector value) { + LOG(FATAL) << "Used unimplemented WriteEntityMetadata: " << value.size(); +} + +void StreamOutput::WriteSlot(std::vector value) { + LOG(FATAL) << "Used unimplemented WriteSlot " << value.size(); +} + +void StreamOutput::WriteNbtTag(std::vector value) { + LOG(FATAL) << "Used unimplemented WriteNbtTag " << value.size(); +} + +void StreamOutput::WritePosition(Vector value) { + LOG(FATAL) << "Used unimplemented Position: " << value.GetX() << ", " << value.GetY() << " " << value.GetZ(); +} + +void StreamOutput::WriteAngle(unsigned char value) { + WriteUByte(value); +} + +void StreamOutput::WriteUuid(std::vector value) { + WriteByteArray(value); +} + +void StreamOutput::WriteByteArray(std::vector value) { + WriteData(value.data(), value.size()); +} + +void StreamBuffer::ReadData(unsigned char *buffPtr, size_t buffLen) { + size_t bufferLengthLeft = buffer + bufferLength - bufferPtr; + if (bufferLengthLeft < buffLen) + throw std::runtime_error("Required data is more, than in buffer available"); + std::memcpy(buffPtr, bufferPtr, buffLen); + bufferPtr += buffLen; +} + +void StreamBuffer::WriteData(unsigned char *buffPtr, size_t buffLen) { + size_t bufferLengthLeft = buffer + bufferLength - bufferPtr; + if (bufferLengthLeft < buffLen) + throw std::runtime_error("Required data is more, than in buffer available"); + std::memcpy(bufferPtr, buffPtr, buffLen); + bufferPtr += buffLen; +} + +StreamBuffer::StreamBuffer(unsigned char *data, size_t dataLen) { + buffer = new unsigned char[dataLen]; + bufferPtr = buffer; + bufferLength = dataLen; + std::memcpy(buffer, data, dataLen); +} + +StreamBuffer::StreamBuffer(size_t bufferLen) { + buffer = new unsigned char[bufferLen]; + bufferPtr = buffer; + bufferLength = bufferLen; + for (unsigned char *p = buffer; p != buffer + bufferLength; ++p) + *p = 0; +} + +StreamBuffer::~StreamBuffer() { + delete buffer; +} + +std::vector StreamBuffer::GetBuffer() { + return std::vector(buffer, buffer + bufferLength); +} + +void StreamCounter::WriteData(unsigned char *buffPtr, size_t buffLen) { + buffPtr++; + size += buffLen; +} + +StreamCounter::StreamCounter(size_t initialSize) : size(initialSize) { + +} + +StreamCounter::~StreamCounter() { + +} + +size_t StreamCounter::GetCountedSize() { + return size; +} + +void StreamSocket::ReadData(unsigned char *buffPtr, size_t buffLen) { + socket->Read(buffPtr, buffLen); +} + +void StreamSocket::WriteData(unsigned char *buffPtr, size_t buffLen) { + socket->Write(buffPtr, buffLen); +} + +StreamSocket::StreamSocket(Socket *socketPtr) : socket(socketPtr) { + +} \ No newline at end of file diff --git a/old/network/Stream.hpp b/old/network/Stream.hpp new file mode 100644 index 0000000..bf75bdb --- /dev/null +++ b/old/network/Stream.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "Socket.hpp" +#include "../Vector.hpp" +#include "../Utility.hpp" + +class Stream { +public: + virtual ~Stream() {}; +}; + +class StreamInput : Stream { + virtual void ReadData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamInput() = default; + bool ReadBool(); + signed char ReadByte(); + unsigned char ReadUByte(); + short ReadShort(); + unsigned short ReadUShort(); + int ReadInt(); + long long ReadLong(); + float ReadFloat(); + double ReadDouble(); + std::string ReadString(); + std::string ReadChat(); + int ReadVarInt(); + long long ReadVarLong(); + std::vector ReadEntityMetadata(); + std::vector ReadSlot(); + std::vector ReadNbtTag(); + Vector ReadPosition(); + unsigned char ReadAngle(); + std::vector ReadUuid(); + std::vector ReadByteArray(size_t arrLength); +}; + +class StreamOutput : Stream { + virtual void WriteData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamOutput() = default; + void WriteBool(bool value); + void WriteByte(signed char value); + void WriteUByte(unsigned char value); + void WriteShort(short value); + void WriteUShort(unsigned short value); + void WriteInt(int value); + void WriteLong(long long value); + void WriteFloat(float value); + void WriteDouble(double value); + void WriteString(std::string value); + void WriteChat(std::string value); + void WriteVarInt(int value); + void WriteVarLong(long long value); + void WriteEntityMetadata(std::vector value); + void WriteSlot(std::vector value); + void WriteNbtTag(std::vector value); + void WritePosition(Vector value); + void WriteAngle(unsigned char value); + void WriteUuid(std::vector value); + void WriteByteArray(std::vector value); +}; + +class StreamBuffer : public StreamInput, public StreamOutput { + unsigned char *buffer; + unsigned char *bufferPtr; + size_t bufferLength; + + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + +public: + StreamBuffer(unsigned char *data, size_t dataLen); + StreamBuffer(size_t bufferLen); + ~StreamBuffer(); + + std::vector GetBuffer(); +}; + +class StreamCounter : public StreamOutput { + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + + size_t size; +public: + StreamCounter(size_t initialSize = 0); + ~StreamCounter(); + + size_t GetCountedSize(); +}; + +class StreamSocket : public StreamInput, public StreamOutput { + Socket *socket; + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; +public: + StreamSocket(Socket *socketPtr); + ~StreamSocket() = default; +}; \ No newline at end of file diff --git a/old/world/Block.cpp b/old/world/Block.cpp new file mode 100644 index 0000000..e88068a --- /dev/null +++ b/old/world/Block.cpp @@ -0,0 +1,17 @@ +#include "Block.hpp" + +Block::~Block() {} + +Block::Block(unsigned short id, unsigned char state) : id(id), state(state) {} + +Block::Block() : id(0), state(0) {} + +bool operator<(const Block &lhs, const Block &rhs) { + if (lhs.id < rhs.id) + return true; + if (lhs.id == rhs.id) { + if (lhs.state != rhs.state) + return lhs.state < rhs.state; + } + return false; +} diff --git a/old/world/Block.hpp b/old/world/Block.hpp new file mode 100644 index 0000000..2f823fe --- /dev/null +++ b/old/world/Block.hpp @@ -0,0 +1,15 @@ +#pragma once + +struct Block { + Block(); + + Block(unsigned short id, unsigned char state); + + ~Block(); + + unsigned short id : 13; + unsigned char state : 4; + //unsigned char light:4; +}; + +bool operator<(const Block &lhs, const Block &rhs); \ No newline at end of file diff --git a/old/world/Collision.cpp b/old/world/Collision.cpp new file mode 100644 index 0000000..4f2c837 --- /dev/null +++ b/old/world/Collision.cpp @@ -0,0 +1,28 @@ +#include "Collision.hpp" + +bool TestCollision(AABB first, AABB second) { + double firstXl = first.x; + double firstXr = first.x + first.w; + + double firstYl = first.y; + double firstYr = first.y + first.h; + + double firstZl = first.z; + double firstZr = first.z + first.l; + + + double secondXl = second.x; + double secondXr = second.x + second.w; + + double secondYl = second.y; + double secondYr = second.y + second.h; + + double secondZl = second.z; + double secondZr = second.z + second.l; + + bool collidesOnX = firstXr >= secondXl && firstXl <= secondXr; + bool collidesOnY = firstYr >= secondYl && firstYl <= secondYr; + bool collidesOnZ = firstZr >= secondZl && firstZl <= secondZr; + + return collidesOnX && collidesOnY && collidesOnZ; +} diff --git a/old/world/Collision.hpp b/old/world/Collision.hpp new file mode 100644 index 0000000..b88fbf7 --- /dev/null +++ b/old/world/Collision.hpp @@ -0,0 +1,8 @@ +#pragma once + +struct AABB { + double x,y,z; + double w,l,h; +}; + +bool TestCollision(AABB first, AABB second); \ No newline at end of file diff --git a/old/world/GameState.cpp b/old/world/GameState.cpp new file mode 100644 index 0000000..3ccff37 --- /dev/null +++ b/old/world/GameState.cpp @@ -0,0 +1,383 @@ +#include "GameState.hpp" + +GameState::GameState(NetworkClient *Net, bool &quit) : nc(Net), isRunning(quit) { + Front = glm::vec3(0.0f, 0.0f, -1.0f); + this->SetPosition(glm::vec3(0.0f, 0.0f, 3.0f)); + this->WorldUp = glm::vec3(0.0f, 1.0f, 0.0f); + this->updateCameraVectors(); +} + +void GameState::Update(float deltaTime) { + if (g_IsGameStarted) { + std::chrono::steady_clock clock; + static auto timeOfPreviousSendedPacket(clock.now()); + auto delta = clock.now() - timeOfPreviousSendedPacket; + using namespace std::chrono_literals; + if (delta >= 50ms) { + nc->SendPacket(std::make_shared(g_PlayerX, g_PlayerY, g_PlayerZ, g_PlayerYaw, + g_PlayerPitch, g_OnGround)); + timeOfPreviousSendedPacket = clock.now(); + } + + const float gravity = -9.8f; + g_PlayerVelocityY += gravity * deltaTime; + + bool isCollides = world.isPlayerCollides(g_PlayerX, g_PlayerY + g_PlayerVelocityY * deltaTime, + g_PlayerZ); + if (!isCollides) { + g_PlayerY += g_PlayerVelocityY * deltaTime; + g_OnGround = false; + } else { + g_PlayerVelocityY = 0; + if (g_OnGround == false) { + auto updatePacket = std::make_shared(g_PlayerX, g_PlayerY, g_PlayerZ, true); + nc->SendPacket(updatePacket); + } + g_OnGround = true; + } + + isCollides = world.isPlayerCollides(g_PlayerX + g_PlayerVelocityX * deltaTime, g_PlayerY, + g_PlayerZ + g_PlayerVelocityZ * deltaTime); + if (!isCollides) { + g_PlayerX += g_PlayerVelocityX * deltaTime; + g_PlayerZ += g_PlayerVelocityZ * deltaTime; + } + + const float AirResistance = 10.0f; + glm::vec3 vel(g_PlayerVelocityX, 0, g_PlayerVelocityZ); + glm::vec3 resistForce = -vel * AirResistance * deltaTime; + vel += resistForce; + g_PlayerVelocityX = vel.x; + g_PlayerVelocityZ = vel.z; + } + + + //Packet handling + auto ptr = nc->ReceivePacket(); + while (ptr != nullptr) { + switch ((PacketNamePlayCB) ptr->GetPacketId()) { + case SpawnObject: + break; + case SpawnExperienceOrb: + break; + case SpawnGlobalEntity: + break; + case SpawnMob: + break; + case SpawnPainting: + break; + case SpawnPlayer: + break; + case AnimationCB: + break; + case Statistics: + break; + case BlockBreakAnimation: + break; + case UpdateBlockEntity: + break; + case BlockAction: + break; + case BlockChange: + break; + case BossBar: + break; + case ServerDifficulty: + break; + case TabCompleteCB: + break; + case ChatMessageCB: + break; + case MultiBlockChange: + break; + case ConfirmTransactionCB: + break; + case CloseWindowCB: + break; + case OpenWindow: + break; + case WindowItems: + break; + case WindowProperty: + break; + case SetSlot: + break; + case SetCooldown: + break; + case PluginMessageCB: + break; + case NamedSoundEffect: + break; + case DisconnectPlay: { + auto packet = std::static_pointer_cast(ptr); + LOG(INFO) << "Disconnect reason: " << packet->Reason; + isRunning = false; + break; + } + case EntityStatus: + break; + case Explosion: + break; + case UnloadChunk: + break; + case ChangeGameState: + break; + case KeepAliveCB: + LOG(WARNING) << "Receive KeepAlive packet in GameState handler"; + break; + case ChunkData: { + auto packet = std::static_pointer_cast(ptr); + world.ParseChunkData(packet); + break; + } + case Effect: + break; + case Particle: + break; + case JoinGame: { + auto packet = std::static_pointer_cast(ptr); + g_PlayerEid = packet->EntityId; + g_Gamemode = (packet->Gamemode & 0b11111011); + g_Dimension = packet->Dimension; + g_Difficulty = packet->Difficulty; + g_MaxPlayers = packet->MaxPlayers; + g_LevelType = packet->LevelType; + g_ReducedDebugInfo = packet->ReducedDebugInfo; + LOG(INFO) << "Gamemode is " << g_Gamemode << ", Difficulty is " << (int) g_Difficulty + << ", Level Type is " << g_LevelType; + break; + } + case Map: + break; + case EntityRelativeMove: + break; + case EntityLookAndRelativeMove: + break; + case EntityLook: + break; + case Entity: + break; + case VehicleMove: + break; + case OpenSignEditor: + break; + case PlayerAbilitiesCB: + break; + case CombatEvent: + break; + case PlayerListItem: + break; + case PlayerPositionAndLookCB: { + auto packet = std::static_pointer_cast(ptr); + if ((packet->Flags & 0x10) != 0) { + g_PlayerPitch += packet->Pitch; + } else { + g_PlayerPitch = packet->Pitch; + }; + + if ((packet->Flags & 0x08) != 0) { + g_PlayerYaw += packet->Yaw; + } else { + g_PlayerYaw = packet->Yaw; + } + + if ((packet->Flags & 0x01) != 0) { + g_PlayerX += packet->X; + } else { + g_PlayerX = packet->X; + } + + if ((packet->Flags & 0x02) != 0) { + g_PlayerY += packet->Y; + } else { + g_PlayerY = packet->Y; + } + + if ((packet->Flags & 0x04) != 0) { + g_PlayerZ += packet->Z; + } else { + g_PlayerZ = packet->Z; + } + + //if (!g_IsGameStarted) + LOG(INFO) << "PlayerPos is " << g_PlayerX << ", " << g_PlayerY << ", " << g_PlayerZ << "\t\tAngle: " + << g_PlayerYaw << "," << g_PlayerPitch; + + g_IsGameStarted = true; + + auto packetResponse = std::make_shared(packet->TeleportId); + auto packetPerformRespawn = std::make_shared(0); + + nc->SendPacket(packetResponse); + nc->SendPacket(packetPerformRespawn); + break; + } + case UseBed: + break; + case UnlockRecipes: + break; + case DestroyEntities: + break; + case RemoveEntityEffect: + break; + case ResourcePackSend: + break; + case Respawn: + break; + case EntityHeadLook: + break; + case SelectAdvancementTab: + break; + case WorldBorder: + break; + case Camera: + break; + case HeldItemChangeCB: + break; + case DisplayScoreboard: + break; + case EntityMetadata: + break; + case AttachEntity: + break; + case EntityVelocity: + break; + case EntityEquipment: + break; + case SetExperience: + break; + case UpdateHealth: { + auto packet = std::static_pointer_cast(ptr); + g_PlayerHealth = packet->Health; + if (g_PlayerHealth <= 0) { + LOG(INFO) << "Player is dead. Respawning..."; + auto packetPerformRespawn = std::make_shared(0); + nc->SendPacket(packetPerformRespawn); + } + break; + } + case ScoreboardObjective: + break; + case SetPassengers: + break; + case Teams: + break; + case UpdateScore: + break; + case SpawnPosition: { + auto packet = std::static_pointer_cast(ptr); + g_SpawnPosition = packet->Location; + LOG(INFO) << "Spawn position is " << g_SpawnPosition.GetX() << "," << g_SpawnPosition.GetY() << "," + << g_SpawnPosition.GetZ(); + break; + } + case TimeUpdate: + break; + case Title: + break; + case SoundEffect: + break; + case PlayerListHeaderAndFooter: + break; + case CollectItem: + break; + case EntityTeleport: + break; + case Advancements: + break; + case EntityProperties: + break; + case EntityEffect: + break; + } + ptr = nc->ReceivePacket(); + } +} + +void GameState::HandleMovement(GameState::Direction direction, float deltaTime) { + const float PlayerSpeed = 40.0; + float velocity = PlayerSpeed * deltaTime; + glm::vec3 vel(g_PlayerVelocityX, g_PlayerVelocityY, g_PlayerVelocityZ); + glm::vec3 front(cos(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch())), 0, + sin(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch()))); + front = glm::normalize(front); + glm::vec3 right = glm::normalize(glm::cross(front, this->WorldUp)); + switch (direction) { + case FORWARD: + vel += front * velocity; + break; + case BACKWARD: + vel -= front * velocity; + break; + case RIGHT: + vel += right * velocity; + break; + case LEFT: + vel -= right * velocity; + break; + case JUMP: + if (g_OnGround) { + vel.y += 5; + g_OnGround = false; + } + break; + } + g_PlayerVelocityX = vel.x; + g_PlayerVelocityY = vel.y; + g_PlayerVelocityZ = vel.z; +} + +void GameState::HandleRotation(double yaw, double pitch) { + this->SetYaw(Yaw() + yaw); + this->SetPitch(Pitch() + pitch); + if (this->Pitch() > 89.0f) + this->SetPitch(89.0f); + if (this->Pitch() < -89.0f) + this->SetPitch(-89.0f); + this->updateCameraVectors(); + + auto updatePacket = std::make_shared(g_PlayerYaw, g_PlayerPitch, g_OnGround); + nc->SendPacket(updatePacket); +} + +glm::mat4 GameState::GetViewMatrix() { + auto pos = this->Position(); + pos.y+=1.62; + return glm::lookAt(pos, pos + this->Front, this->Up); +} + +void GameState::updateCameraVectors() { + glm::vec3 front; + front.x = cos(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch())); + front.y = sin(glm::radians(this->Pitch())); + front.z = sin(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch())); + this->Front = glm::normalize(front); + this->Right = glm::normalize(glm::cross(this->Front, this->WorldUp)); + this->Up = glm::normalize(glm::cross(this->Right, this->Front)); +} + +float GameState::Yaw() { + return g_PlayerYaw + 90; +} + +float GameState::Pitch() { + return -g_PlayerPitch; +} + +void GameState::SetYaw(float yaw) { + g_PlayerYaw = yaw - 90; +} + +void GameState::SetPitch(float pitch) { + g_PlayerPitch = -pitch; +} + +glm::vec3 GameState::Position() { + return glm::vec3(g_PlayerX, g_PlayerY, g_PlayerZ); +} + +void GameState::SetPosition(glm::vec3 Position) { + g_PlayerX = Position.x; + g_PlayerY = Position.y; + g_PlayerZ = Position.z; +} diff --git a/old/world/GameState.hpp b/old/world/GameState.hpp new file mode 100644 index 0000000..2635054 --- /dev/null +++ b/old/world/GameState.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include "World.hpp" +#include "../network/NetworkClient.hpp" +#include "../Vector.hpp" + +class GameState { + NetworkClient *nc; +public: + GameState(NetworkClient *NetClient, bool &quit); + + void Update(float deltaTime); + + //Navigation + enum Direction { + FORWARD, BACKWARD, LEFT, RIGHT, JUMP + }; + void HandleMovement(GameState::Direction direction, float deltaTime); + void HandleRotation(double yaw, double pitch); + glm::mat4 GetViewMatrix(); + void updateCameraVectors(); + + float Yaw(); + float Pitch(); + void SetYaw(float yaw); + void SetPitch(float pitch); + + glm::vec3 Position(); + void SetPosition(glm::vec3 Position); + glm::vec3 Front; + glm::vec3 Up; + glm::vec3 Right; + glm::vec3 WorldUp; + + //Everything other + World world; + bool &isRunning; + + std::string g_PlayerUuid; + std::string g_PlayerName; + bool g_IsGameStarted; + int g_PlayerEid; + int g_Gamemode; + int g_Dimension; + byte g_Difficulty; + byte g_MaxPlayers; + std::string g_LevelType; + bool g_ReducedDebugInfo; + Vector g_SpawnPosition; + bool g_PlayerInvulnerable; + bool g_PlayerFlying; + bool g_PlayerAllowFlying; + bool g_PlayerCreativeMode; + float g_PlayerFlyingSpeed; + float g_PlayerFovModifier; + float g_PlayerPitch; + float g_PlayerYaw; + double g_PlayerX; + double g_PlayerY; + double g_PlayerZ; + float g_PlayerHealth; + + bool g_OnGround = true; + double g_PlayerVelocityX = 0; + double g_PlayerVelocityY = 0; + double g_PlayerVelocityZ = 0; +}; diff --git a/old/world/Section.cpp b/old/world/Section.cpp new file mode 100644 index 0000000..8b86afd --- /dev/null +++ b/old/world/Section.cpp @@ -0,0 +1,147 @@ +#include "Section.hpp" + + +Section::Section(Vector position, byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, + byte bitsPerBlock, + std::vector palette) { + worldPosition = position; + + m_dataBlocksLen = dataBlocksLength; + m_dataBlocks = new byte[m_dataBlocksLen]; + std::copy(dataBlocks, dataBlocks + m_dataBlocksLen, m_dataBlocks); + + m_dataLight = new byte[2048]; + std::copy(dataLight, dataLight + 2048, m_dataLight); + + if (dataSky) { + m_dataSkyLight = new byte[2048]; + std::copy(dataSky, dataSky + 2048, m_dataSkyLight); + } + + m_palette = palette; + m_bitsPerBlock = bitsPerBlock; +} + +Section::~Section() { + delete[] m_dataBlocks; + m_dataBlocksLen = 0; + m_dataBlocks = nullptr; + delete[] m_dataLight; + m_dataLight = nullptr; + delete[] m_dataSkyLight; + m_dataSkyLight = nullptr; +} + +Block &Section::GetBlock(Vector pos) { + return m_blocks[pos.GetY() * 256 + pos.GetZ() * 16 + pos.GetX()]; +} + +double totalParsingTime = 0; + +void Section::Parse() { + if (!m_blocks.empty()) + return; + + long long *longArray = reinterpret_cast(m_dataBlocks); + for (size_t i = 0; i < m_dataBlocksLen / 8; i++) + endswap(&longArray[i]); + std::vector blocks; + blocks.reserve(4096); + { + auto begin = std::chrono::steady_clock::now(); + int bitPos = 0; + unsigned short t = 0; + for (size_t i = 0; i < m_dataBlocksLen; i++) { + for (int j = 0; j < 8; j++) { + t |= (m_dataBlocks[i] & 0x01) ? 0x80 : 0x00; + t >>= 1; + m_dataBlocks[i] >>= 1; + bitPos++; + if (bitPos >= m_bitsPerBlock) { + bitPos = 0; + t >>= m_bitsPerBlock - 1; + blocks.push_back(t); + t = 0; + } + } + } + auto end = std::chrono::steady_clock::now(); + std::chrono::duration time = end - begin; + totalParsingTime += time.count(); + } + std::vector light; + light.reserve(4096); + for (int i = 0; i < 2048; i++) { + byte t = m_dataLight[i]; + byte first = t & 0b11110000; + byte second = t >> 4; + light.push_back(first); + light.push_back(second); + } + for (int i = 0; i < 4096; i++) { + unsigned short blockId = m_palette.size() > 0 ? m_palette[blocks[i]] : blocks[i]; + Block block(blockId >> 4, blockId & 0xF); + m_blocks.push_back(block); + } + delete[] m_dataBlocks; + m_dataBlocksLen = 0; + m_dataBlocks = nullptr; + delete[] m_dataLight; + m_dataLight = nullptr; + delete[] m_dataSkyLight; + m_dataSkyLight = nullptr; + + parseWaiter.notify_all(); +} + +Section &Section::operator=(Section other) { + std::swap(*this, other); + return *this; +} + +void swap(Section &a, Section &b) { + using std::swap; + swap(a.m_dataBlocksLen, b.m_dataBlocksLen); + swap(a.m_dataBlocks, b.m_dataBlocks); + swap(a.m_dataLight, b.m_dataLight); + swap(a.m_dataSkyLight, b.m_dataSkyLight); + swap(a.m_blocks, b.m_blocks); + swap(a.m_palette, b.m_palette); + swap(a.m_bitsPerBlock, b.m_bitsPerBlock); +} + +Section::Section(const Section &other) { + worldPosition = other.worldPosition; + m_dataBlocksLen = other.m_dataBlocksLen; + if (other.m_blocks.empty()) { + m_dataBlocks = new byte[m_dataBlocksLen]; + std::copy(other.m_dataBlocks, other.m_dataBlocks + m_dataBlocksLen, m_dataBlocks); + + m_dataLight = new byte[2048]; + std::copy(other.m_dataLight, other.m_dataLight + 2048, m_dataLight); + + if (other.m_dataSkyLight) { + m_dataSkyLight = new byte[2048]; + std::copy(other.m_dataSkyLight, other.m_dataSkyLight + 2048, m_dataSkyLight); + } + } else { + std::copy(other.m_blocks.begin(), other.m_blocks.end(), std::back_inserter(m_blocks)); + } + + m_palette = other.m_palette; + m_bitsPerBlock = other.m_bitsPerBlock; +} + +Vector Section::GetPosition() { + return worldPosition; +} + +size_t Section::GetHash() { + if (m_blocks.empty()) return 0; + + unsigned char *from = reinterpret_cast(m_blocks.data()); + size_t length = m_blocks.size() * sizeof(Block); + + std::string str(from, from + length); + return std::hash{}(str); +} \ No newline at end of file diff --git a/old/world/Section.hpp b/old/world/Section.hpp new file mode 100644 index 0000000..e98e231 --- /dev/null +++ b/old/world/Section.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "Block.hpp" +#include "../Vector.hpp" +#include "../Utility.hpp" + +const int SECTION_WIDTH = 16; +const int SECTION_LENGTH = 16; +const int SECTION_HEIGHT = 16; + +class Section { + std::vector m_palette; + byte *m_dataBlocks = nullptr; + size_t m_dataBlocksLen; + byte *m_dataLight = nullptr; + byte *m_dataSkyLight = nullptr; + byte m_bitsPerBlock = 0; + std::vector m_blocks; + std::condition_variable parseWaiter; + + Section(); + + Vector worldPosition; + +public: + void Parse(); + + Section(Vector position, byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, byte bitsPerBlock, + std::vector palette); + + ~Section(); + + Block &GetBlock(Vector pos); + + Section &operator=(Section other); + + friend void swap(Section &a, Section &b); + + Section(const Section &other); + + Vector GetPosition(); + + size_t GetHash(); +}; \ No newline at end of file diff --git a/old/world/World.cpp b/old/world/World.cpp new file mode 100644 index 0000000..487f4ba --- /dev/null +++ b/old/world/World.cpp @@ -0,0 +1,129 @@ +#include "World.hpp" + +void World::ParseChunkData(std::shared_ptr packet) { + StreamBuffer chunkData(packet->Data.data(), packet->Data.size()); + std::bitset<16> bitmask(packet->PrimaryBitMask); + for (int i = 0; i < 16; i++) { + if (bitmask[i]) { + Vector chunkPosition = Vector(packet->ChunkX, i, packet->ChunkZ); + Section section = ParseSection(&chunkData, chunkPosition); + section.Parse(); + sectionMutexes[chunkPosition].lock(); + auto it = sections.find(chunkPosition); + if (it == sections.end()) { + sections.insert(std::make_pair(chunkPosition, section)); + } else { + using std::swap; + swap(it->second, section); + } + sectionMutexes[chunkPosition].unlock(); + } + } +} + +Section World::ParseSection(StreamInput *data, Vector position) { + unsigned char bitsPerBlock = data->ReadUByte(); + int paletteLength = data->ReadVarInt(); + std::vector palette; + for (int i = 0; i < paletteLength; i++) { + palette.push_back(data->ReadVarInt()); + } + int dataArrayLength = data->ReadVarInt(); + auto dataArray = data->ReadByteArray(dataArrayLength * 8); + auto blockLight = data->ReadByteArray(4096 / 2); + std::vector skyLight; + if (dimension == 0) + skyLight = data->ReadByteArray(4096 / 2); + return Section(position, dataArray.data(), dataArray.size(), blockLight.data(), + (skyLight.size() > 0 ? skyLight.data() : nullptr), bitsPerBlock, palette); +} + +World::~World() { +} + +World::World() { + +} + +bool World::isPlayerCollides(double X, double Y, double Z) { + Vector PlayerChunk(floor(X / 16.0), floor(Y / 16.0), floor(Z / 16.0)); + std::vector closestSectionsCoordinates = { + Vector(PlayerChunk.GetX(), PlayerChunk.GetY(), PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX() + 1, PlayerChunk.GetY(), PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX() - 1, PlayerChunk.GetY(), PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY() + 1, PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY() - 1, PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY(), PlayerChunk.GetZ() + 1), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY(), PlayerChunk.GetZ() - 1), + }; + std::vector::iterator> closestSections; + for (auto &coord:closestSectionsCoordinates) { + auto it = sections.find(coord); + if (it != sections.end()) + closestSections.push_back(it); + } + if (closestSections.empty()) + return false; + + for (auto &it:closestSections) { + + const double PlayerWidth = 0.6; + const double PlayerHeight = 1.82; + const double PlayerLength = 0.6; + + AABB playerColl; + playerColl.x = X - PlayerWidth / 2.0; + playerColl.w = PlayerWidth; + playerColl.y = Y; + playerColl.h = PlayerHeight; + playerColl.z = Z - PlayerLength / 2.0; + playerColl.l = PlayerLength; + + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + Block block = it->second.GetBlock(Vector(x, y, z)); + if (block.id == 0 || block.id == 31) + continue; + AABB blockColl{(x + it->first.GetX() * 16.0), + (y + it->first.GetY() * 16.0), + (z + it->first.GetZ() * 16.0), 1, 1, 1}; + if (TestCollision(playerColl, blockColl)) + return true; + } + } + } + } + return false; +} + +Block &World::GetBlock(Vector pos) { + Vector sectionPos (floor(pos.GetX() / 16.0f),floor(pos.GetY() / 16.0f),floor(pos.GetZ()/16.0f)); + Vector inSectionPos = pos - (sectionPos * 16); + if (sections.find(sectionPos)==sections.end()){ + static Block block(0,0); + return block; + } + sectionMutexes[sectionPos].lock(); + Block& block = sections.find(sectionPos)->second.GetBlock(inSectionPos); + sectionMutexes[sectionPos].unlock(); + return block; +} + +std::vector World::GetSectionsList() { + std::vector sectionsList; + for (auto& it:sections) { + sectionsList.push_back(it.first); + } + return sectionsList; +} + +Section &World::GetSection(Vector sectionPos) { + sectionMutexes[sectionPos].lock(); + sectionMutexes[sectionPos].unlock(); + return sections.find(sectionPos)->second; +} + +glm::vec3 World::Raycast(glm::vec3 position, glm::vec3 direction, float maxLength, float minPrecision) { + return glm::vec3(position * direction / maxLength * minPrecision); +} diff --git a/old/world/World.hpp b/old/world/World.hpp new file mode 100644 index 0000000..05fe57d --- /dev/null +++ b/old/world/World.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + +class World { + std::map sections; + std::map sectionMutexes; + int dimension = 0; + + Section ParseSection(StreamInput *data, Vector position); + + World(const World &other); + World &operator=(const World &other); +public: + World(); + + ~World(); + + void ParseChunkData(std::shared_ptr packet); + + bool isPlayerCollides(double X, double Y, double Z); + + Block &GetBlock(Vector pos); + + std::vector GetSectionsList(); + + Section &GetSection(Vector sectionPos); + + glm::vec3 Raycast(glm::vec3 position, glm::vec3 direction, float maxLength = 1000.0f, float minPrecision = 0.01f); +}; \ No newline at end of file diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp new file mode 100644 index 0000000..351a118 --- /dev/null +++ b/src/AssetManager.cpp @@ -0,0 +1,185 @@ +#include +#include "AssetManager.hpp" + +//const fs::path pathToAssets = "./assets/"; +//const fs::path pathToAssetsList = "./items.json"; +//const fs::path pathToTextureIndex = "./textures.json"; +const std::string pathToAssetsList = "./items.json"; +const std::string pathToTextureIndex = "./textures.json"; + +AssetManager::AssetManager() { + LoadIds(); + LoadTextureResources(); +} + +void AssetManager::LoadIds() { + std::ifstream in(pathToAssetsList); + nlohmann::json index; + in >> index; + for (auto &it:index) { + int id = it["type"].get(); + int state = it["meta"].get(); + std::string blockName = it["text_type"].get(); + assetIds[blockName] = Block(id, state); + } + LOG(INFO) << "Loaded " << assetIds.size() << " ids"; +} + +AssetManager::~AssetManager() { + delete textureAtlas; +} + +//TODO: This function must be replaced with runtime texture atlas generating +void AssetManager::LoadTextureResources() { + std::ifstream in(pathToTextureIndex); + nlohmann::json index; + in >> index; + std::string filename = index["meta"]["image"].get(); + float textureWidth = index["meta"]["size"]["w"].get(); + float textureHeight = index["meta"]["size"]["h"].get(); + for (auto &it:index["frames"]) { + auto frame = it["frame"]; + TextureCoordinates coord; + coord.x = frame["x"].get() / textureWidth; + coord.y = frame["y"].get() / textureHeight; + coord.w = frame["w"].get() / textureWidth; + coord.h = frame["h"].get() / textureHeight; + std::string assetName = it["filename"].get(); + assetName.insert(0, "minecraft/textures/"); + assetName.erase(assetName.length() - 4); + assetTextures[assetName] = coord; + } + + textureAtlas = new Texture(filename); + LOG(INFO) << "Texture atlas id is " << textureAtlas->texture; +} + +TextureCoordinates AssetManager::GetTextureByAssetName(std::string AssetName) { + if (assetTextures.find(AssetName) != assetTextures.end()) + return assetTextures[AssetName]; + else + return TextureCoordinates{-1, -1, -1, -1}; +} + +std::string AssetManager::GetTextureAssetNameByBlockId(BlockTextureId block) { + //Block sides: 0 - bottom, 1 - top, 2 - north, 3 - south, 4 - west, 5 - east 6 - every side + const std::map lookupTable = { + {BlockTextureId(0, 0), "minecraft/textures/blocks/air"}, + {BlockTextureId(1, 0), "minecraft/textures/blocks/stone"}, + {BlockTextureId(1, 1), "minecraft/textures/blocks/stone_granite"}, + + {BlockTextureId(2, 0, 0), "minecraft/textures/blocks/dirt"}, + {BlockTextureId(2, 0, 1), "minecraft/textures/blocks/grass_top"}, + {BlockTextureId(2, 0, 2), "minecraft/textures/blocks/grass_side"}, + {BlockTextureId(2, 0, 3), "minecraft/textures/blocks/grass_side"}, + {BlockTextureId(2, 0, 4), "minecraft/textures/blocks/grass_side"}, + {BlockTextureId(2, 0, 5), "minecraft/textures/blocks/grass_side"}, + + {BlockTextureId(3, 0), "minecraft/textures/blocks/dirt"}, + {BlockTextureId(4, 0), "minecraft/textures/blocks/cobblestone"}, + {BlockTextureId(5, 0), "minecraft/textures/blocks/planks"}, + + {BlockTextureId(7, 0), "minecraft/textures/blocks/bedrock"}, + + {BlockTextureId(17, 0, 0), "minecraft/textures/blocks/log_oak_top"}, + {BlockTextureId(17, 0, 1), "minecraft/textures/blocks/log_oak_top"}, + {BlockTextureId(17, 0, 2), "minecraft/textures/blocks/log_oak"}, + {BlockTextureId(17, 0, 3), "minecraft/textures/blocks/log_oak"}, + {BlockTextureId(17, 0, 4), "minecraft/textures/blocks/log_oak"}, + {BlockTextureId(17, 0, 5), "minecraft/textures/blocks/log_oak"}, + + {BlockTextureId(17, 1, 0), "minecraft/textures/blocks/log_spruce_top"}, + {BlockTextureId(17, 1, 1), "minecraft/textures/blocks/log_spruce_top"}, + {BlockTextureId(17, 1, 2), "minecraft/textures/blocks/log_spruce"}, + {BlockTextureId(17, 1, 3), "minecraft/textures/blocks/log_spruce"}, + {BlockTextureId(17, 1, 4), "minecraft/textures/blocks/log_spruce"}, + {BlockTextureId(17, 1, 5), "minecraft/textures/blocks/log_spruce"}, + + {BlockTextureId(17, 2, 0), "minecraft/textures/blocks/log_birch_top"}, + {BlockTextureId(17, 2, 1), "minecraft/textures/blocks/log_birch_top"}, + {BlockTextureId(17, 2, 2), "minecraft/textures/blocks/log_birch"}, + {BlockTextureId(17, 2, 3), "minecraft/textures/blocks/log_birch"}, + {BlockTextureId(17, 2, 4), "minecraft/textures/blocks/log_birch"}, + {BlockTextureId(17, 2, 5), "minecraft/textures/blocks/log_birch"}, + + {BlockTextureId(17, 3, 0), "minecraft/textures/blocks/log_jungle_top"}, + {BlockTextureId(17, 3, 1), "minecraft/textures/blocks/log_jungle_top"}, + {BlockTextureId(17, 3, 2), "minecraft/textures/blocks/log_jungle"}, + {BlockTextureId(17, 3, 3), "minecraft/textures/blocks/log_jungle"}, + {BlockTextureId(17, 3, 4), "minecraft/textures/blocks/log_jungle"}, + {BlockTextureId(17, 3, 5), "minecraft/textures/blocks/log_jungle"}, + + {BlockTextureId(18, 0), "minecraft/textures/blocks/leaves_oak"}, + {BlockTextureId(18, 1), "minecraft/textures/blocks/leaves_spruce"}, + {BlockTextureId(18, 2), "minecraft/textures/blocks/leaves_birch"}, + {BlockTextureId(18, 3), "minecraft/textures/blocks/leaves_jungle"}, + + {BlockTextureId(61, 0, 0), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(61, 0, 1), "minecraft/textures/blocks/furnace_top"}, + {BlockTextureId(61, 0, 2), "minecraft/textures/blocks/furnace_front_off"}, + {BlockTextureId(61, 0, 3), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(61, 0, 4), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(61, 0, 5), "minecraft/textures/blocks/furnace_side"}, + + {BlockTextureId(62, 0, 0), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(62, 0, 1), "minecraft/textures/blocks/furnace_top"}, + {BlockTextureId(62, 0, 2), "minecraft/textures/blocks/furnace_front_on"}, + {BlockTextureId(62, 0, 3), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(62, 0, 4), "minecraft/textures/blocks/furnace_side"}, + {BlockTextureId(62, 0, 5), "minecraft/textures/blocks/furnace_side"}, + + + {BlockTextureId(31, 0), "minecraft/textures/blocks/deadbush"}, + {BlockTextureId(31, 1), "minecraft/textures/blocks/tallgrass"}, + {BlockTextureId(31, 2), "minecraft/textures/blocks/fern"}, + }; + auto ret = lookupTable.find(block); + if (ret == lookupTable.end()) + return ""; + else + return ret->second; +} + +GLuint AssetManager::GetTextureAtlas() { + return textureAtlas->texture; +} + +TextureCoordinates AssetManager::GetTextureByBlock(BlockTextureId block) { + std::string assetName = this->GetTextureAssetNameByBlockId(block); + return this->GetTextureByAssetName(assetName); +} + +const std::map &AssetManager::GetTextureAtlasIndexes() { + if (!textureAtlasIndexes.empty()) + return textureAtlasIndexes; + + LOG(INFO) << "Initializing texture atlas..."; + for (int id = 1; id < 128; id++) { + for (int state = 0; state < 16; state++) { + BlockTextureId blockTextureId(id, state, 6); + if (!this->GetTextureByBlock(blockTextureId) && + !this->GetTextureByBlock(BlockTextureId(id, state, 0))) { + continue; + } + if (this->GetTextureByBlock(blockTextureId)) { + for (int i = 0; i < 6; i++) { + TextureCoordinates tc = this->GetTextureByBlock(BlockTextureId(id, state, 6)); + textureAtlasIndexes[BlockTextureId(id, state, i)] = glm::vec4(tc.x, tc.y, tc.w, tc.h); + } + } else { + for (int i = 0; i < 6; i++) { + TextureCoordinates tc = this->GetTextureByBlock(BlockTextureId(id, state, i)); + textureAtlasIndexes[BlockTextureId(id, state, i)] = glm::vec4(tc.x, tc.y, tc.w, tc.h); + } + } + } + } + LOG(INFO) << "Created " << textureAtlasIndexes.size() << " texture indexes"; + + return textureAtlasIndexes; +} + +AssetManager &AssetManager::Instance() { + static AssetManager assetManager; + return assetManager; +} diff --git a/src/AssetManager.hpp b/src/AssetManager.hpp new file mode 100644 index 0000000..ba5546b --- /dev/null +++ b/src/AssetManager.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include "Block.hpp" +#include "Texture.hpp" + +struct TextureCoordinates { + TextureCoordinates(float x = -1, float y = -1, float w = -1, float h = -1) : x(x), y(y), w(w), h(h) {} + + bool operator==(const TextureCoordinates &rhs) const { + return x == rhs.x && + y == rhs.y && + w == rhs.w && + h == rhs.h; + } + + explicit operator bool() const { + return !(*this == TextureCoordinates(-1, -1, -1, -1)); + } + + float x, y, w, h; +}; + +struct BlockTextureId { + //Block sides: 0 - bottom, 1 - top, 2 - north, 3 - south, 4 - west, 5 - east 6 - every side + BlockTextureId(int id = 0, int state = 0, int side = 6) : id(id), state(state), side(side) {} + + int id:9; + int state:4; + int side:3; + + + bool operator<(const BlockTextureId &rhs) const { + if (id < rhs.id) + return true; + if (rhs.id < id) + return false; + if (state < rhs.state) + return true; + if (rhs.state < state) + return false; + return side < rhs.side; + } +}; + +class AssetManager { + Texture *textureAtlas; + std::map assetIds; + std::map assetTextures; + std::map textureAtlasIndexes; +public: + AssetManager(); + + ~AssetManager(); + + void LoadTextureResources(); + + TextureCoordinates GetTextureByAssetName(std::string AssetName); + + std::string GetTextureAssetNameByBlockId(BlockTextureId block); + + GLuint GetTextureAtlas(); + + const std::map &GetTextureAtlasIndexes(); + + void LoadIds(); + + TextureCoordinates GetTextureByBlock(BlockTextureId block); + + static AssetManager& Instance(); +}; diff --git a/src/Block.cpp b/src/Block.cpp new file mode 100644 index 0000000..e88068a --- /dev/null +++ b/src/Block.cpp @@ -0,0 +1,17 @@ +#include "Block.hpp" + +Block::~Block() {} + +Block::Block(unsigned short id, unsigned char state) : id(id), state(state) {} + +Block::Block() : id(0), state(0) {} + +bool operator<(const Block &lhs, const Block &rhs) { + if (lhs.id < rhs.id) + return true; + if (lhs.id == rhs.id) { + if (lhs.state != rhs.state) + return lhs.state < rhs.state; + } + return false; +} diff --git a/src/Block.hpp b/src/Block.hpp new file mode 100644 index 0000000..2f823fe --- /dev/null +++ b/src/Block.hpp @@ -0,0 +1,15 @@ +#pragma once + +struct Block { + Block(); + + Block(unsigned short id, unsigned char state); + + ~Block(); + + unsigned short id : 13; + unsigned char state : 4; + //unsigned char light:4; +}; + +bool operator<(const Block &lhs, const Block &rhs); \ No newline at end of file diff --git a/src/Collision.cpp b/src/Collision.cpp new file mode 100644 index 0000000..4f2c837 --- /dev/null +++ b/src/Collision.cpp @@ -0,0 +1,28 @@ +#include "Collision.hpp" + +bool TestCollision(AABB first, AABB second) { + double firstXl = first.x; + double firstXr = first.x + first.w; + + double firstYl = first.y; + double firstYr = first.y + first.h; + + double firstZl = first.z; + double firstZr = first.z + first.l; + + + double secondXl = second.x; + double secondXr = second.x + second.w; + + double secondYl = second.y; + double secondYr = second.y + second.h; + + double secondZl = second.z; + double secondZr = second.z + second.l; + + bool collidesOnX = firstXr >= secondXl && firstXl <= secondXr; + bool collidesOnY = firstYr >= secondYl && firstYl <= secondYr; + bool collidesOnZ = firstZr >= secondZl && firstZl <= secondZr; + + return collidesOnX && collidesOnY && collidesOnZ; +} diff --git a/src/Collision.hpp b/src/Collision.hpp new file mode 100644 index 0000000..b88fbf7 --- /dev/null +++ b/src/Collision.hpp @@ -0,0 +1,8 @@ +#pragma once + +struct AABB { + double x,y,z; + double w,l,h; +}; + +bool TestCollision(AABB first, AABB second); \ No newline at end of file diff --git a/src/Core.cpp b/src/Core.cpp new file mode 100644 index 0000000..3a89c07 --- /dev/null +++ b/src/Core.cpp @@ -0,0 +1,59 @@ +#include "Core.hpp" +#include "NetworkClient.hpp" + +Core::Core() { + coreLoop = std::thread(&Core::ExecuteCoreLoop, this); + render = new Render(900, 480, "AltCraft"); +} + +Core::~Core() { + delete render; + coreLoop.join(); +} + +void Core::ExecuteRenderLoop() { + render->ExecuteRenderLoop(); +} + +void Core::ExecuteCoreLoop() { + + EventListener events; + events.RegisterHandler(EventType::ConnectionSuccessfull, [](EventData eventData) { + GlobalAppStateData data = {GlobalState::Loading}; + EventAgregator::PushEvent(EventType::GlobalAppState, EventData{data}); + }); + events.RegisterHandler(EventType::GlobalAppState, [this](EventData eventData) { + auto data = std::get(eventData); + globalState = data.state; + }); + + while (globalState != GlobalState::Exiting) { + if (events.IsEventsQueueIsNotEmpty()) { + events.HandleEvent(); + } + } +} + +void Core::ExecuteNetworkLoop() { + NetworkClient *nc; + EventListener events; + bool isRunning = true; + events.RegisterHandler(EventType::ConnectToServer, [](EventData eventData) { + auto data = std::get(eventData); + try { + nc = new NetworkClient(data.address, data.port, "HelloOne", isRunning); + } catch (std::exception &e) { + GlobalAppStateData data{GlobalState::Exiting}; + EventAgregator::PushEvent(EventType::GlobalAppState, data); + } + }); + events.RegisterHandler(EventType::Disconnect, [nc](EventData eventData) { + delete nc; + nc = nullptr; + }); + + while (globalState != GlobalState::Exiting) { + + } + delete nc; +} diff --git a/src/Core.hpp b/src/Core.hpp new file mode 100644 index 0000000..3c2d8da --- /dev/null +++ b/src/Core.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +#include "Render.hpp" +#include "Event.hpp" + +class Core { + std::thread coreLoop; + std::thread networkLoop; + Render* render; + + GlobalState globalState = GlobalState::InitialLoading; + + void ExecuteCoreLoop(); + void ExecuteNetworkLoop(); +public: + Core(); + ~Core(); + + void ExecuteRenderLoop(); +}; \ No newline at end of file diff --git a/src/Event.cpp b/src/Event.cpp new file mode 100644 index 0000000..ff16178 --- /dev/null +++ b/src/Event.cpp @@ -0,0 +1,100 @@ +#include "Event.hpp" +#include + +std::queue EventAgregator::eventsToHandle; +std::mutex EventAgregator::queueMutex; +bool EventAgregator::isStarted = false; +std::vector EventAgregator::listeners; +std::mutex EventAgregator::listenersMutex; + +EventListener::EventListener() { + EventAgregator::RegisterListener(*this); +} + +EventListener::~EventListener() { + EventAgregator::UnregisterListener(*this); +} + +void EventListener::PushEvent(Event event) { + eventsMutex.lock(); + events.push(event); + eventsMutex.unlock(); +} + +bool EventListener::IsEventsQueueIsNotEmpty() { + eventsMutex.lock(); + bool value = !events.empty(); + eventsMutex.unlock(); + return value; +} + + +void EventListener::RegisterHandler(EventType type, EventListener::HandlerFunc handler) { + handlers[type] = handler; +} + +void EventListener::HandleEvent() { + eventsMutex.lock(); + if (events.empty()) { + eventsMutex.unlock(); + return; + } + Event event = events.front(); + events.pop(); + eventsMutex.unlock(); + auto function = handlers[event.type]; + function(event.data); +} + + + +void EventAgregator::RegisterListener(EventListener &listener) { + listenersMutex.lock(); + LOG(WARNING)<<"Registered handler "<<&listener; + listeners.push_back(&listener); + listenersMutex.unlock(); +} + +void EventAgregator::UnregisterListener(EventListener &listener) { + listenersMutex.lock(); + LOG(WARNING)<<"Unregistered handler "<<&listener; + listeners.erase(std::find(listeners.begin(), listeners.end(), &listener)); + listenersMutex.unlock(); +} + +void EventAgregator::PushEvent(EventType type, EventData data) { + Event event; + event.type = type; + event.data = data; + eventsToHandle.push(event); + if (!isStarted) { + isStarted = true; + std::thread(&EventAgregator::EventHandlingLoop).detach(); + } +} + +void EventAgregator::EventHandlingLoop() { + while (true) { + queueMutex.lock(); + if (!eventsToHandle.empty()) { + auto queue = eventsToHandle; + while (!eventsToHandle.empty()) + eventsToHandle.pop(); + queueMutex.unlock(); + + while (!queue.empty()) { + auto event = queue.front(); + listenersMutex.lock(); + for (auto& listener : listeners) { + LOG(ERROR)<<"Listener notified about event"; + listener->PushEvent(event); + } + listenersMutex.unlock(); + queue.pop(); + } + + queueMutex.lock(); + } + queueMutex.unlock(); + } +} diff --git a/src/Event.hpp b/src/Event.hpp new file mode 100644 index 0000000..2d830a4 --- /dev/null +++ b/src/Event.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Vector.hpp" +#include "Packet.hpp" + +enum class EventType { + Echo, + ChunkChanged, + ConnectToServer, + ConnectionSuccessfull, + GlobalAppState, + Disconnect, + SendPacket, + ReceivePacket, +}; + +struct EchoData { + std::chrono::time_point time; +}; + +struct ChunkChangedData { + Vector chunkPosition; +}; + +struct ConnectToServerData { + std::string address; + unsigned short port; +}; + +struct ConnectionSuccessfullData { + +}; + +enum class GlobalState { + InitialLoading, + MainMenu, + Loading, + InGame, + PauseMenu, + Exiting, +}; + +struct GlobalAppStateData { + GlobalState state; +}; + +struct DisconnectData { + +}; + +struct SendPacketData { + std::shared_ptr packet; +}; + +struct ReceivePacketData { + std::shared_ptr packet; +}; + +using EventData = std::variant; + +struct Event { + EventType type; + EventData data; +}; + +class EventListener { + friend class EventAgregator; + + using HandlerFunc = std::function; + + std::map handlers; //TODO: There must be more elegant solution than std::variant of all data + + std::mutex eventsMutex; + + std::queue events; + + void PushEvent(Event event); + +public: + EventListener(); + ~EventListener(); + bool IsEventsQueueIsNotEmpty(); + + void RegisterHandler(EventType type, HandlerFunc handler); + + void HandleEvent(); +}; + +class EventAgregator { + friend EventListener; + + EventAgregator() = default; + static std::queue eventsToHandle; + static std::mutex queueMutex; + static bool isStarted; + static std::vector listeners; + static std::mutex listenersMutex; + + static void EventHandlingLoop(); + + static void RegisterListener(EventListener &listener); + static void UnregisterListener(EventListener &listener); + +public: + static void PushEvent(EventType type, EventData data); +}; \ No newline at end of file diff --git a/src/GameState.cpp b/src/GameState.cpp new file mode 100644 index 0000000..3ccff37 --- /dev/null +++ b/src/GameState.cpp @@ -0,0 +1,383 @@ +#include "GameState.hpp" + +GameState::GameState(NetworkClient *Net, bool &quit) : nc(Net), isRunning(quit) { + Front = glm::vec3(0.0f, 0.0f, -1.0f); + this->SetPosition(glm::vec3(0.0f, 0.0f, 3.0f)); + this->WorldUp = glm::vec3(0.0f, 1.0f, 0.0f); + this->updateCameraVectors(); +} + +void GameState::Update(float deltaTime) { + if (g_IsGameStarted) { + std::chrono::steady_clock clock; + static auto timeOfPreviousSendedPacket(clock.now()); + auto delta = clock.now() - timeOfPreviousSendedPacket; + using namespace std::chrono_literals; + if (delta >= 50ms) { + nc->SendPacket(std::make_shared(g_PlayerX, g_PlayerY, g_PlayerZ, g_PlayerYaw, + g_PlayerPitch, g_OnGround)); + timeOfPreviousSendedPacket = clock.now(); + } + + const float gravity = -9.8f; + g_PlayerVelocityY += gravity * deltaTime; + + bool isCollides = world.isPlayerCollides(g_PlayerX, g_PlayerY + g_PlayerVelocityY * deltaTime, + g_PlayerZ); + if (!isCollides) { + g_PlayerY += g_PlayerVelocityY * deltaTime; + g_OnGround = false; + } else { + g_PlayerVelocityY = 0; + if (g_OnGround == false) { + auto updatePacket = std::make_shared(g_PlayerX, g_PlayerY, g_PlayerZ, true); + nc->SendPacket(updatePacket); + } + g_OnGround = true; + } + + isCollides = world.isPlayerCollides(g_PlayerX + g_PlayerVelocityX * deltaTime, g_PlayerY, + g_PlayerZ + g_PlayerVelocityZ * deltaTime); + if (!isCollides) { + g_PlayerX += g_PlayerVelocityX * deltaTime; + g_PlayerZ += g_PlayerVelocityZ * deltaTime; + } + + const float AirResistance = 10.0f; + glm::vec3 vel(g_PlayerVelocityX, 0, g_PlayerVelocityZ); + glm::vec3 resistForce = -vel * AirResistance * deltaTime; + vel += resistForce; + g_PlayerVelocityX = vel.x; + g_PlayerVelocityZ = vel.z; + } + + + //Packet handling + auto ptr = nc->ReceivePacket(); + while (ptr != nullptr) { + switch ((PacketNamePlayCB) ptr->GetPacketId()) { + case SpawnObject: + break; + case SpawnExperienceOrb: + break; + case SpawnGlobalEntity: + break; + case SpawnMob: + break; + case SpawnPainting: + break; + case SpawnPlayer: + break; + case AnimationCB: + break; + case Statistics: + break; + case BlockBreakAnimation: + break; + case UpdateBlockEntity: + break; + case BlockAction: + break; + case BlockChange: + break; + case BossBar: + break; + case ServerDifficulty: + break; + case TabCompleteCB: + break; + case ChatMessageCB: + break; + case MultiBlockChange: + break; + case ConfirmTransactionCB: + break; + case CloseWindowCB: + break; + case OpenWindow: + break; + case WindowItems: + break; + case WindowProperty: + break; + case SetSlot: + break; + case SetCooldown: + break; + case PluginMessageCB: + break; + case NamedSoundEffect: + break; + case DisconnectPlay: { + auto packet = std::static_pointer_cast(ptr); + LOG(INFO) << "Disconnect reason: " << packet->Reason; + isRunning = false; + break; + } + case EntityStatus: + break; + case Explosion: + break; + case UnloadChunk: + break; + case ChangeGameState: + break; + case KeepAliveCB: + LOG(WARNING) << "Receive KeepAlive packet in GameState handler"; + break; + case ChunkData: { + auto packet = std::static_pointer_cast(ptr); + world.ParseChunkData(packet); + break; + } + case Effect: + break; + case Particle: + break; + case JoinGame: { + auto packet = std::static_pointer_cast(ptr); + g_PlayerEid = packet->EntityId; + g_Gamemode = (packet->Gamemode & 0b11111011); + g_Dimension = packet->Dimension; + g_Difficulty = packet->Difficulty; + g_MaxPlayers = packet->MaxPlayers; + g_LevelType = packet->LevelType; + g_ReducedDebugInfo = packet->ReducedDebugInfo; + LOG(INFO) << "Gamemode is " << g_Gamemode << ", Difficulty is " << (int) g_Difficulty + << ", Level Type is " << g_LevelType; + break; + } + case Map: + break; + case EntityRelativeMove: + break; + case EntityLookAndRelativeMove: + break; + case EntityLook: + break; + case Entity: + break; + case VehicleMove: + break; + case OpenSignEditor: + break; + case PlayerAbilitiesCB: + break; + case CombatEvent: + break; + case PlayerListItem: + break; + case PlayerPositionAndLookCB: { + auto packet = std::static_pointer_cast(ptr); + if ((packet->Flags & 0x10) != 0) { + g_PlayerPitch += packet->Pitch; + } else { + g_PlayerPitch = packet->Pitch; + }; + + if ((packet->Flags & 0x08) != 0) { + g_PlayerYaw += packet->Yaw; + } else { + g_PlayerYaw = packet->Yaw; + } + + if ((packet->Flags & 0x01) != 0) { + g_PlayerX += packet->X; + } else { + g_PlayerX = packet->X; + } + + if ((packet->Flags & 0x02) != 0) { + g_PlayerY += packet->Y; + } else { + g_PlayerY = packet->Y; + } + + if ((packet->Flags & 0x04) != 0) { + g_PlayerZ += packet->Z; + } else { + g_PlayerZ = packet->Z; + } + + //if (!g_IsGameStarted) + LOG(INFO) << "PlayerPos is " << g_PlayerX << ", " << g_PlayerY << ", " << g_PlayerZ << "\t\tAngle: " + << g_PlayerYaw << "," << g_PlayerPitch; + + g_IsGameStarted = true; + + auto packetResponse = std::make_shared(packet->TeleportId); + auto packetPerformRespawn = std::make_shared(0); + + nc->SendPacket(packetResponse); + nc->SendPacket(packetPerformRespawn); + break; + } + case UseBed: + break; + case UnlockRecipes: + break; + case DestroyEntities: + break; + case RemoveEntityEffect: + break; + case ResourcePackSend: + break; + case Respawn: + break; + case EntityHeadLook: + break; + case SelectAdvancementTab: + break; + case WorldBorder: + break; + case Camera: + break; + case HeldItemChangeCB: + break; + case DisplayScoreboard: + break; + case EntityMetadata: + break; + case AttachEntity: + break; + case EntityVelocity: + break; + case EntityEquipment: + break; + case SetExperience: + break; + case UpdateHealth: { + auto packet = std::static_pointer_cast(ptr); + g_PlayerHealth = packet->Health; + if (g_PlayerHealth <= 0) { + LOG(INFO) << "Player is dead. Respawning..."; + auto packetPerformRespawn = std::make_shared(0); + nc->SendPacket(packetPerformRespawn); + } + break; + } + case ScoreboardObjective: + break; + case SetPassengers: + break; + case Teams: + break; + case UpdateScore: + break; + case SpawnPosition: { + auto packet = std::static_pointer_cast(ptr); + g_SpawnPosition = packet->Location; + LOG(INFO) << "Spawn position is " << g_SpawnPosition.GetX() << "," << g_SpawnPosition.GetY() << "," + << g_SpawnPosition.GetZ(); + break; + } + case TimeUpdate: + break; + case Title: + break; + case SoundEffect: + break; + case PlayerListHeaderAndFooter: + break; + case CollectItem: + break; + case EntityTeleport: + break; + case Advancements: + break; + case EntityProperties: + break; + case EntityEffect: + break; + } + ptr = nc->ReceivePacket(); + } +} + +void GameState::HandleMovement(GameState::Direction direction, float deltaTime) { + const float PlayerSpeed = 40.0; + float velocity = PlayerSpeed * deltaTime; + glm::vec3 vel(g_PlayerVelocityX, g_PlayerVelocityY, g_PlayerVelocityZ); + glm::vec3 front(cos(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch())), 0, + sin(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch()))); + front = glm::normalize(front); + glm::vec3 right = glm::normalize(glm::cross(front, this->WorldUp)); + switch (direction) { + case FORWARD: + vel += front * velocity; + break; + case BACKWARD: + vel -= front * velocity; + break; + case RIGHT: + vel += right * velocity; + break; + case LEFT: + vel -= right * velocity; + break; + case JUMP: + if (g_OnGround) { + vel.y += 5; + g_OnGround = false; + } + break; + } + g_PlayerVelocityX = vel.x; + g_PlayerVelocityY = vel.y; + g_PlayerVelocityZ = vel.z; +} + +void GameState::HandleRotation(double yaw, double pitch) { + this->SetYaw(Yaw() + yaw); + this->SetPitch(Pitch() + pitch); + if (this->Pitch() > 89.0f) + this->SetPitch(89.0f); + if (this->Pitch() < -89.0f) + this->SetPitch(-89.0f); + this->updateCameraVectors(); + + auto updatePacket = std::make_shared(g_PlayerYaw, g_PlayerPitch, g_OnGround); + nc->SendPacket(updatePacket); +} + +glm::mat4 GameState::GetViewMatrix() { + auto pos = this->Position(); + pos.y+=1.62; + return glm::lookAt(pos, pos + this->Front, this->Up); +} + +void GameState::updateCameraVectors() { + glm::vec3 front; + front.x = cos(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch())); + front.y = sin(glm::radians(this->Pitch())); + front.z = sin(glm::radians(this->Yaw())) * cos(glm::radians(this->Pitch())); + this->Front = glm::normalize(front); + this->Right = glm::normalize(glm::cross(this->Front, this->WorldUp)); + this->Up = glm::normalize(glm::cross(this->Right, this->Front)); +} + +float GameState::Yaw() { + return g_PlayerYaw + 90; +} + +float GameState::Pitch() { + return -g_PlayerPitch; +} + +void GameState::SetYaw(float yaw) { + g_PlayerYaw = yaw - 90; +} + +void GameState::SetPitch(float pitch) { + g_PlayerPitch = -pitch; +} + +glm::vec3 GameState::Position() { + return glm::vec3(g_PlayerX, g_PlayerY, g_PlayerZ); +} + +void GameState::SetPosition(glm::vec3 Position) { + g_PlayerX = Position.x; + g_PlayerY = Position.y; + g_PlayerZ = Position.z; +} diff --git a/src/GameState.hpp b/src/GameState.hpp new file mode 100644 index 0000000..2ac7bc1 --- /dev/null +++ b/src/GameState.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +#include "World.hpp" +#include "NetworkClient.hpp" +#include "Vector.hpp" + +class GameState { + NetworkClient *nc; +public: + GameState(NetworkClient *NetClient, bool &quit); + + void Update(float deltaTime); + + //Navigation + enum Direction { + FORWARD, BACKWARD, LEFT, RIGHT, JUMP + }; + void HandleMovement(GameState::Direction direction, float deltaTime); + void HandleRotation(double yaw, double pitch); + glm::mat4 GetViewMatrix(); + void updateCameraVectors(); + + float Yaw(); + float Pitch(); + void SetYaw(float yaw); + void SetPitch(float pitch); + + glm::vec3 Position(); + void SetPosition(glm::vec3 Position); + glm::vec3 Front; + glm::vec3 Up; + glm::vec3 Right; + glm::vec3 WorldUp; + + //Everything other + World world; + bool &isRunning; + + std::string g_PlayerUuid; + std::string g_PlayerName; + bool g_IsGameStarted; + int g_PlayerEid; + int g_Gamemode; + int g_Dimension; + byte g_Difficulty; + byte g_MaxPlayers; + std::string g_LevelType; + bool g_ReducedDebugInfo; + Vector g_SpawnPosition; + bool g_PlayerInvulnerable; + bool g_PlayerFlying; + bool g_PlayerAllowFlying; + bool g_PlayerCreativeMode; + float g_PlayerFlyingSpeed; + float g_PlayerFovModifier; + float g_PlayerPitch; + float g_PlayerYaw; + double g_PlayerX; + double g_PlayerY; + double g_PlayerZ; + float g_PlayerHealth; + + bool g_OnGround = true; + double g_PlayerVelocityX = 0; + double g_PlayerVelocityY = 0; + double g_PlayerVelocityZ = 0; +}; diff --git a/src/Nbt.hpp b/src/Nbt.hpp deleted file mode 100644 index 03f5af0..0000000 --- a/src/Nbt.hpp +++ /dev/null @@ -1,516 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -namespace nbt { - enum TagType { - End, //nullptr - Byte, //int8_t - Short, //int16_t - Int, //int32_t - Long, //int64_t - Float, //float - Double, //double - ByteArray, //std::vector - String, //std::string - List, //std::vector - Compound, //std::vector - IntArray, //std::vector - Unknown, //dummy value - }; - - class NbtTag; - - typedef std::vector compound_t; - - typedef std::string string_t; - - typedef std::vector byteArray_t; - - typedef std::vector intArray_t; - - class NbtTag { - TagType type = Unknown; - string_t name = ""; - unsigned char *data = nullptr; - public: - NbtTag(TagType type, string_t name) : type(type), name(name) { - switch (type) { - case End: - data = nullptr; - break; - case Compound: - data = (unsigned char *) new compound_t; - break; - case String: - data = (unsigned char *) new string_t; - break; - case Int: - data = (unsigned char *) new int32_t; - break; - case Long: - data = (unsigned char *) new int64_t; - break; - case Byte: - data = (unsigned char *) new int8_t; - break; - case Short: - data = (unsigned char *) new int16_t; - break; - case Float: - data = (unsigned char *) new float; - break; - case Double: - data = (unsigned char *) new double; - break; - case ByteArray: - data = (unsigned char *) new byteArray_t; - break; - case List: - data = (unsigned char *) new compound_t; - break; - case IntArray: - data = (unsigned char *) new intArray_t; - } - } - - NbtTag(const NbtTag &other) : type(other.type), name(other.name) { - switch (type) { - case Byte: - data = (unsigned char *) new int8_t; - *((int8_t *) data) = *((int8_t *) other.data); - break; - case Short: - data = (unsigned char *) new int16_t; - *((int16_t *) data) = *((int16_t *) other.data); - break; - case Int: - data = (unsigned char *) new int32_t; - *((int32_t *) data) = *((int32_t *) other.data); - break; - case Long: - data = (unsigned char *) new int64_t; - *((int64_t *) data) = *((int64_t *) other.data); - break; - case Float: - data = (unsigned char *) new float; - *((float *) data) = *((float *) other.data); - break; - case Double: - data = (unsigned char *) new double; - *((double *) data) = *((double *) other.data); - break; - case ByteArray: - data = (unsigned char *) new byteArray_t; - *((std::vector *) data) = *((std::vector *) other.data); - break; - case String: - data = (unsigned char *) new string_t; - *((std::string *) data) = *((std::string *) other.data); - break; - case List: - data = (unsigned char *) new compound_t; - *((std::vector *) data) = *((std::vector *) other.data); - break; - case Compound: - data = (unsigned char *) new compound_t; - *((std::vector *) data) = *((std::vector *) other.data); - break; - case IntArray: - data = (unsigned char *) new intArray_t; - *((std::vector *) data) = *((std::vector *) other.data); - break; - } - } - - ~NbtTag() { - switch (type) { - case Byte: - delete ((int8_t *) data); - break; - case Short: - delete ((int16_t *) data); - break; - case Int: - delete ((int32_t *) data); - break; - case Long: - delete ((int64_t *) data); - break; - case Float: - delete ((float *) data); - break; - case Double: - delete ((double *) data); - break; - case ByteArray: - delete ((std::vector *) data); - break; - case String: - delete ((std::string *) data); - break; - case List: - delete ((std::vector *) data); - break; - case Compound: - delete ((std::vector *) data); - break; - case IntArray: - delete ((std::vector *) data); - break; - } - }; - - void swap(NbtTag &other) { - std::swap(other.data, data); - std::swap(other.name, name); - std::swap(other.type, type); - } - - NbtTag &operator=(NbtTag other) { - other.swap(*this); - return *this; - } - - TagType GetType() const { - return type; - } - - string_t GetName() const { - return name; - } - - - string_t &GetString() { - string_t &val = *reinterpret_cast(data); - return val; - } - - compound_t &GetCompound() { - std::vector &val = *reinterpret_cast *>(data); - return val; - } - - compound_t &GetList() { - std::vector &val = *reinterpret_cast *>(data); - return val; - } - - int64_t &GetLong() { - int64_t &val = *reinterpret_cast(data); - return val; - } - - float &GetFloat() { - float &val = *reinterpret_cast(data); - return val; - } - - double &GetDouble() { - double &val = *reinterpret_cast(data); - return val; - } - - byteArray_t &GetByteArray() { - auto &val = *reinterpret_cast(data); - return val; - } - - intArray_t &GetIntArray() { - auto &val = *reinterpret_cast(data); - return val; - } - - int16_t &GetShort() { - auto &val = *reinterpret_cast(data); - return val; - } - - int32_t &GetInt() { - auto &val = *reinterpret_cast(data); - return val; - } - - int8_t &GetByte() { - auto &val = *reinterpret_cast(data); - return val; - } - }; - - NbtTag ParseTag(unsigned char *data, size_t &size, TagType listItemType = Unknown) { - size = 0; - TagType type; - if (listItemType == Unknown) { - type = (TagType) *data; - data += 1; - size += 1; - } else - type = listItemType; - string_t name; - if (listItemType == Unknown && type != End) { - short nameLen = *((short *) data); - data += 2; - size += 2; - endswap(&nameLen); - name = std::string((char *) data, nameLen); - data += nameLen; - size += nameLen; - } - NbtTag tag(type, name); - switch (type) { - case Compound: { - do { - size_t s; - tag.GetCompound().push_back(ParseTag(data, s)); - data += s; - size += s; - } while (tag.GetCompound().back().GetType() != End); - tag.GetCompound().pop_back(); - return tag; - } - case String: { - short len = *((short *) data); - data += 2; - size += 2; - endswap(&len); - string_t str((char *) data, len); - data += len; - size += len; - tag.GetString() = str; - return tag; - } - case End: - return tag; - case Long: - tag.GetLong() = *((int64_t *) data); - endswap(&tag.GetLong()); - data += 8; - size += 8; - return tag; - case Short: - tag.GetShort() = *((int16_t *) data); - endswap(&tag.GetShort()); - data += 2; - size += 2; - return tag; - case Float: - tag.GetFloat() = *((float *) data); - endswap(&tag.GetFloat()); - data += 4; - size += 4; - return tag; - case Double: - tag.GetDouble() = *((double *) data); - endswap(&tag.GetDouble()); - data += 8; - size += 8; - return tag; - case Byte: - tag.GetByte() = *((int8_t *) data); - endswap(&tag.GetByte()); - data += 1; - size += 1; - return tag; - case Int: - tag.GetInt() = *((int32_t *) data); - endswap(&tag.GetInt()); - data += 4; - size += 4; - return tag; - case List: { - TagType listType = *((TagType *) data); - data += 1; - size += 1; - int32_t listLength = *((int32_t *) data); - endswap(&listLength); - data += 4; - size += 4; - for (int i = 0; i < listLength; i++) { - size_t s = 0; - std::vector &vec = tag.GetCompound(); - vec.push_back(ParseTag(data, s, listType)); - data += s; - size += s; - } - return tag; - } - case ByteArray: { - int32_t arrLength = *((int32_t *) data); - endswap(&arrLength); - data += 4; - size += 4; - for (int i = 0; i < arrLength; i++) { - signed char val = (signed char) data[i]; - std::vector &vec = tag.GetByteArray(); - vec.push_back(val); - } - data += arrLength; - size += arrLength; - return tag; - } - default: - throw 13; - } - } - - NbtTag ParseTag(unsigned char *data, size_t *optionalSize = nullptr) { - size_t s = 0; - size_t &size = (optionalSize ? *optionalSize : s); - return ParseTag(data, size); - } - - std::vector Decompress(unsigned char *data, size_t dataLen) { - const size_t decompBuffSize = 1024 * 16; - unsigned char *decompBuff = new unsigned char[decompBuffSize]; - std::vector uncompressed; - for (int i = 0; i < decompBuffSize; i++) - decompBuff[i] = 0; - - - z_stream stream; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - stream.next_in = data; - stream.avail_in = dataLen; - stream.next_out = decompBuff; - stream.avail_out = decompBuffSize; - - if (inflateInit2(&stream, 15 + 32) != Z_OK) { - delete[] decompBuff; - throw 171; - } - - int res; - do { - stream.avail_out = decompBuffSize; - - switch ((res = inflate(&stream, Z_NO_FLUSH))) { - case Z_MEM_ERROR: - throw 172; - case Z_DATA_ERROR: - throw 173; - case Z_NEED_DICT: - throw 174; - } - - uncompressed.resize(uncompressed.size() + decompBuffSize); - std::copy(decompBuff, decompBuff + decompBuffSize, uncompressed.end() - decompBuffSize); - } while (stream.avail_out == 0); - if (res != Z_STREAM_END) - throw 175; - if (inflateEnd(&stream) != Z_OK) - throw 176; - delete[] decompBuff; - return uncompressed; - } - - NbtTag ParseCompressed(unsigned char *data, size_t dataLen) { - auto uncompressed = Decompress(data, dataLen); - NbtTag root = ParseTag(uncompressed.data()); - return root; - } - - NbtTag Parse(unsigned char *data, size_t dataLen) { - bool isCompressed = *data != 10; - if (isCompressed) - return ParseCompressed(data, dataLen); - else - return ParseTag(data); - } - - void PrintTree(NbtTag &tree, int deepness = 0, std::ostream &ostream = std::cout) { - ostream << std::string(deepness, '\t') << "Tag "; - switch (tree.GetType()) { - case Byte: - ostream << "byte"; - break; - case Short: - ostream << "short"; - break; - case Int: - ostream << "int"; - break; - case Long: - ostream << "long"; - break; - case Float: - ostream << "float"; - break; - case Double: - ostream << "double"; - break; - case ByteArray: - ostream << "byte array"; - break; - case String: - ostream << "string"; - break; - case List: - ostream << "list"; - break; - case Compound: - ostream << "compound"; - break; - case IntArray: - ostream << "int array"; - break; - case End: - ostream << "end"; - break; - } - if (tree.GetName().length() > 0) - ostream << " (" << tree.GetName() << ")"; - ostream << ": "; - - if (tree.GetType() == Compound || tree.GetType() == List) { - std::vector &vec = (tree.GetType() == Compound ? tree.GetCompound() : tree.GetList()); - ostream << vec.size() << " entries {" << std::endl; - for (auto it = vec.begin(); it != vec.end(); ++it) { - PrintTree(*it, deepness + 1, std::cout); - } - ostream << std::string(deepness, '\t') << "}" << std::endl; - return; - } else { - switch (tree.GetType()) { - case Int: - ostream << tree.GetInt(); - break; - case String: - ostream << "\"" << tree.GetString() << "\""; - break; - case Double: - ostream << tree.GetDouble(); - break; - case Float: - ostream << tree.GetFloat(); - break; - case Short: - ostream << tree.GetShort(); - break; - case Byte: - ostream << (int) tree.GetByte(); - break; - case Long: - ostream << tree.GetLong(); - break; - case ByteArray: - ostream << "[" << tree.GetByteArray().size() << " bytes]: "; - for (int i = 0; i < (tree.GetByteArray().size() > 10 ? 10 : tree.GetByteArray().size()); i++) { - ostream << std::hex << "0x" << (tree.GetByteArray()[i] > 15 ? "" : "0") - << (int) tree.GetByteArray()[i] - << std::dec << " "; - } - break; - case IntArray: - break; - } - ostream << std::endl; - } - } -} \ No newline at end of file diff --git a/src/Network.cpp b/src/Network.cpp new file mode 100644 index 0000000..dcdda10 --- /dev/null +++ b/src/Network.cpp @@ -0,0 +1,224 @@ +#include "Network.hpp" + +Network::Network(std::string address, unsigned short port) { + try { + socket = new Socket(address, port); + stream = new StreamSocket(socket); + } catch (std::exception &e) { + LOG(FATAL)< Network::ReceivePacket(ConnectionState state) { + int packetSize = stream->ReadVarInt(); + auto packetData = stream->ReadByteArray(packetSize); + StreamBuffer streamBuffer(packetData.data(), packetData.size()); + int packetId = streamBuffer.ReadVarInt(); + auto packet = ReceivePacketByPacketId(packetId, state, streamBuffer); + return packet; +} + +void Network::SendPacket(Packet &packet) { + StreamCounter packetSize; + packetSize.WriteVarInt(packet.GetPacketId()); + packet.ToStream(&packetSize); + stream->WriteVarInt(packetSize.GetCountedSize()); + stream->WriteVarInt(packet.GetPacketId()); + packet.ToStream(stream); +} + +std::shared_ptr Network::ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream) { + std::shared_ptr packet(nullptr); + switch (state) { + case Handshaking: + switch (packetId) { + case PacketNameHandshakingCB::Handshake: + packet = std::make_shared(); + break; + } + case Login: + switch (packetId) { + case PacketNameLoginCB::LoginSuccess: + packet = std::make_shared(); + break; + } + break; + case Play: + packet = ParsePacketPlay((PacketNamePlayCB) packetId); + break; + case Status: + break; + } + if (packet.get() != nullptr) + packet->FromStream(&stream); + return packet; +} + +std::shared_ptr Network::ParsePacketPlay(PacketNamePlayCB id) { + switch (id) { + case SpawnObject: + break; + case SpawnExperienceOrb: + break; + case SpawnGlobalEntity: + break; + case SpawnMob: + break; + case SpawnPainting: + break; + case SpawnPlayer: + break; + case AnimationCB: + break; + case Statistics: + break; + case BlockBreakAnimation: + break; + case UpdateBlockEntity: + break; + case BlockAction: + break; + case BlockChange: + break; + case BossBar: + break; + case ServerDifficulty: + break; + case TabCompleteCB: + break; + case ChatMessageCB: + break; + case MultiBlockChange: + break; + case ConfirmTransactionCB: + break; + case CloseWindowCB: + break; + case OpenWindow: + break; + case WindowItems: + break; + case WindowProperty: + break; + case SetSlot: + break; + case SetCooldown: + break; + case PluginMessageCB: + break; + case NamedSoundEffect: + break; + case DisconnectPlay: + return std::make_shared(); + case EntityStatus: + break; + case Explosion: + break; + case UnloadChunk: + break; + case ChangeGameState: + break; + case KeepAliveCB: + return std::make_shared(); + case ChunkData: + return std::make_shared(); + case Effect: + break; + case Particle: + break; + case JoinGame: + return std::make_shared(); + case Map: + break; + case EntityRelativeMove: + break; + case EntityLookAndRelativeMove: + break; + case EntityLook: + break; + case Entity: + break; + case VehicleMove: + break; + case OpenSignEditor: + break; + case PlayerAbilitiesCB: + break; + case CombatEvent: + break; + case PlayerListItem: + break; + case PlayerPositionAndLookCB: + return std::make_shared(); + case UseBed: + break; + case DestroyEntities: + break; + case RemoveEntityEffect: + break; + case ResourcePackSend: + break; + case Respawn: + break; + case EntityHeadLook: + break; + case WorldBorder: + break; + case Camera: + break; + case HeldItemChangeCB: + break; + case DisplayScoreboard: + break; + case EntityMetadata: + break; + case AttachEntity: + break; + case EntityVelocity: + break; + case EntityEquipment: + break; + case SetExperience: + break; + case UpdateHealth: + return std::make_shared(); + case ScoreboardObjective: + break; + case SetPassengers: + break; + case Teams: + break; + case UpdateScore: + break; + case SpawnPosition: + return std::make_shared(); + case TimeUpdate: + break; + case Title: + break; + case SoundEffect: + break; + case PlayerListHeaderAndFooter: + break; + case CollectItem: + break; + case EntityTeleport: + break; + case EntityProperties: + break; + case EntityEffect: + break; + case UnlockRecipes: + break; + case SelectAdvancementTab: + break; + case Advancements: + break; + } + return nullptr; +} diff --git a/src/Network.hpp b/src/Network.hpp new file mode 100644 index 0000000..1281289 --- /dev/null +++ b/src/Network.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "Socket.hpp" +#include "Packet.hpp" + +enum ConnectionState { + Handshaking, + Login, + Play, + Status, +}; + +class Network { + Socket *socket; + StreamSocket *stream; + + std::shared_ptr ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream); +public: + Network(std::string address, unsigned short port); + ~Network(); + + std::shared_ptr ReceivePacket(ConnectionState state = Play); + void SendPacket(Packet &packet); + std::shared_ptr ParsePacketPlay(PacketNamePlayCB id); +}; \ No newline at end of file diff --git a/src/NetworkClient.cpp b/src/NetworkClient.cpp new file mode 100644 index 0000000..0b759e6 --- /dev/null +++ b/src/NetworkClient.cpp @@ -0,0 +1,93 @@ +#include "NetworkClient.hpp" + +NetworkClient::NetworkClient(std::string address, unsigned short port, std::string username, bool &quit) + : network(address, port), isRunning(quit) { + state = Handshaking; + + PacketHandshake handshake; + handshake.protocolVersion = 335; + handshake.serverAddress = "127.0.0.1"; + handshake.serverPort = 25565; + handshake.nextState = 2; + network.SendPacket(handshake); + state = Login; + + PacketLoginStart loginStart; + loginStart.Username = "HelloOne"; + network.SendPacket(loginStart); + + auto response = std::static_pointer_cast(network.ReceivePacket(Login)); + if (response->Username != username) { + throw std::logic_error("Received username is not sended username"); + } + + state = Play; + + isActive = true; + std::thread thread(&NetworkClient::NetworkLoop, this); + std::swap(networkThread, thread); +} + +NetworkClient::~NetworkClient() { + isActive = false; + networkThread.join(); +} + +std::shared_ptr NetworkClient::ReceivePacket() { + if (toReceive.empty()) + return std::shared_ptr(nullptr); + toReceiveMutex.lock(); + auto ret = toReceive.front(); + toReceive.pop(); + toReceiveMutex.unlock(); + return ret; +} + +void NetworkClient::SendPacket(std::shared_ptr packet) { + toSendMutex.lock(); + toSend.push(packet); + toSendMutex.unlock(); +} + +void NetworkClient::NetworkLoop() { + auto timeOfLastKeepAlivePacket = std::chrono::steady_clock::now(); + el::Helpers::setThreadName("Network"); + LOG(INFO) << "Network thread is started"; + try { + while (isActive) { + toSendMutex.lock(); + while (!toSend.empty()) { + if (toSend.front()!=nullptr) + network.SendPacket(*toSend.front()); + toSend.pop(); + } + toSendMutex.unlock(); + auto packet = network.ReceivePacket(state); + if (packet.get() != nullptr) { + if (packet->GetPacketId() != PacketNamePlayCB::KeepAliveCB) { + toReceiveMutex.lock(); + toReceive.push(packet); + toReceiveMutex.unlock(); + } else { + timeOfLastKeepAlivePacket = std::chrono::steady_clock::now(); + auto packetKeepAlive = std::static_pointer_cast(packet); + auto packetKeepAliveSB = std::make_shared(packetKeepAlive->KeepAliveId); + network.SendPacket(*packetKeepAliveSB); + } + } + using namespace std::chrono_literals; + if (std::chrono::steady_clock::now() - timeOfLastKeepAlivePacket > 20s) { + auto disconnectPacket = std::make_shared(); + disconnectPacket->Reason = "Timeout"; + toReceiveMutex.lock(); + toReceive.push(disconnectPacket); + toReceiveMutex.unlock(); + break; + } + } + } catch (std::exception &e) { + LOG(ERROR) << "Exception catched in NetworkLoop: " << e.what(); + isRunning = false; + } + LOG(INFO) << "Network thread is stopped"; +} diff --git a/src/NetworkClient.hpp b/src/NetworkClient.hpp new file mode 100644 index 0000000..cf41f91 --- /dev/null +++ b/src/NetworkClient.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#include "Network.hpp" + +class NetworkClient { + Network network; + std::thread networkThread; + std::mutex toSendMutex; + std::mutex toReceiveMutex; + std::queue > toSend; + std::queue > toReceive; + bool isActive=true; + bool &isRunning; + ConnectionState state; + void NetworkLoop(); +public: + NetworkClient(std::string address, unsigned short port, std::string username, bool &quit); + ~NetworkClient(); + + std::shared_ptr ReceivePacket(); + void SendPacket(std::shared_ptr packet); +}; \ No newline at end of file diff --git a/src/Packet.hpp b/src/Packet.hpp new file mode 100644 index 0000000..d615332 --- /dev/null +++ b/src/Packet.hpp @@ -0,0 +1,521 @@ +#pragma once + +#include + +#include "Stream.hpp" + +enum PacketNameLoginSB { + LoginStart = 0x00, + EncryptionResponse = 0x01, +}; +enum PacketNamePlaySB { + TeleportConfirm, + PrepareCraftingGrid, + TabCompleteSB, + ChatMessageSB, + ClientStatus, + ClientSettings, + ConfirmTransactionSB, + EnchantItem, + ClickWindow, + CloseWindowSB, + PluginMessageSB, + UseEntity, + KeepAliveSB, + Player, + PlayerPosition, + PlayerPositionAndLookSB, + PlayerLook, + VehicleMoveSB, + SteerBoat, + PlayerAbilitiesSB, + PlayerDigging, + EntityAction, + SteerVehicle, + CraftingBookData, + ResourcePackStatus, + AdvancementTab, + HeldItemChangeSB, + CreativeInventoryAction, + UpdateSign, + AnimationSB, + Spectate, + PlayerBlockPlacement, + UseItem, +}; + +enum PacketNameHandshakingCB { + Handshake = 0x00, +}; +enum PacketNameLoginCB { + Disconnect = 0x00, + EncryptionRequest = 0x01, + LoginSuccess = 0x02, + SetCompression = 0x03, +}; +enum PacketNamePlayCB { + SpawnObject = 0x00, + SpawnExperienceOrb, + SpawnGlobalEntity, + SpawnMob, + SpawnPainting, + SpawnPlayer, + AnimationCB, + Statistics, + BlockBreakAnimation, + UpdateBlockEntity, + BlockAction, + BlockChange, + BossBar, + ServerDifficulty, + TabCompleteCB, + ChatMessageCB, + MultiBlockChange, + ConfirmTransactionCB, + CloseWindowCB, + OpenWindow, + WindowItems, + WindowProperty, + SetSlot, + SetCooldown, + PluginMessageCB, + NamedSoundEffect, + DisconnectPlay, + EntityStatus, + Explosion, + UnloadChunk, + ChangeGameState, + KeepAliveCB, + ChunkData, + Effect, + Particle, + JoinGame, + Map, + EntityRelativeMove, + EntityLookAndRelativeMove, + EntityLook, + Entity, + VehicleMove, + OpenSignEditor, + PlayerAbilitiesCB, + CombatEvent, + PlayerListItem, + PlayerPositionAndLookCB, + UseBed, + UnlockRecipes, + DestroyEntities, + RemoveEntityEffect, + ResourcePackSend, + Respawn, + EntityHeadLook, + SelectAdvancementTab, + WorldBorder, + Camera, + HeldItemChangeCB, + DisplayScoreboard, + EntityMetadata, + AttachEntity, + EntityVelocity, + EntityEquipment, + SetExperience, + UpdateHealth, + ScoreboardObjective, + SetPassengers, + Teams, + UpdateScore, + SpawnPosition, + TimeUpdate, + Title, + SoundEffect, + PlayerListHeaderAndFooter, + CollectItem, + EntityTeleport, + Advancements, + EntityProperties, + EntityEffect, +}; + +struct Packet { + virtual ~Packet() = default; + virtual void ToStream(StreamOutput *stream) = 0; + virtual void FromStream(StreamInput *stream) = 0; + virtual int GetPacketId() = 0; +}; + +struct PacketHandshake : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(protocolVersion); + stream->WriteString(serverAddress); + stream->WriteUShort(serverPort); + stream->WriteVarInt(nextState); + } + + void FromStream(StreamInput *stream) override { + protocolVersion = stream->ReadVarInt(); + serverAddress = stream->ReadString(); + serverPort = stream->ReadUShort(); + nextState = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNameHandshakingCB::Handshake; + } + + int protocolVersion; + std::string serverAddress; + unsigned short serverPort; + int nextState; +}; + +struct PacketLoginStart : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginSB::LoginStart; + } + + std::string Username; +}; + +struct PacketLoginSuccess : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Uuid); + stream->WriteString(Username); + } + + void FromStream(StreamInput *stream) override { + Uuid = stream->ReadString(); + Username = stream->ReadString(); + } + + int GetPacketId() override { + return PacketNameLoginCB::LoginSuccess; + } + + std::string Uuid; + std::string Username; +}; + +struct PacketJoinGame : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(EntityId); + stream->WriteUByte(Gamemode); + stream->WriteInt(Dimension); + stream->WriteUByte(Difficulty); + stream->WriteUByte(MaxPlayers); + stream->WriteString(LevelType); + stream->WriteBool(ReducedDebugInfo); + } + + void FromStream(StreamInput *stream) override { + EntityId = stream->ReadInt(); + Gamemode = stream->ReadUByte(); + Dimension = stream->ReadInt(); + Difficulty = stream->ReadUByte(); + MaxPlayers = stream->ReadUByte(); + LevelType = stream->ReadString(); + ReducedDebugInfo = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlayCB::JoinGame; + } + + int EntityId; + unsigned char Gamemode; + int Dimension; + unsigned char Difficulty; + unsigned char MaxPlayers; + std::string LevelType; + bool ReducedDebugInfo; +}; + +struct PacketDisconnectPlay : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteString(Reason); //TODO: Implement chat-wrapper + } + + void FromStream(StreamInput *stream) override { + Reason = stream->ReadChat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::DisconnectPlay; + } + + std::string Reason; +}; + +struct PacketSpawnPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WritePosition(Location); + } + + void FromStream(StreamInput *stream) override { + Location = stream->ReadPosition(); + } + + int GetPacketId() override { + return PacketNamePlayCB::SpawnPosition; + } + + Vector Location; +}; + +struct PacketKeepAliveCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::KeepAliveCB; + } + + int KeepAliveId; +}; + +struct PacketKeepAliveSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(KeepAliveId); + } + + void FromStream(StreamInput *stream) override { + KeepAliveId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::KeepAliveSB; + } + + int KeepAliveId; + + PacketKeepAliveSB(int KeepAliveId) : KeepAliveId(KeepAliveId) {} +}; + +struct PacketPlayerPositionAndLookCB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(Y); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteUByte(Flags); + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + Y = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + Flags = stream->ReadUByte(); + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlayCB::PlayerPositionAndLookCB; + } + + double X; + double Y; + double Z; + float Yaw; + float Pitch; + unsigned char Flags; + int TeleportId; +}; + +struct PacketTeleportConfirm : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(TeleportId); + } + + void FromStream(StreamInput *stream) override { + TeleportId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::TeleportConfirm; + } + + int TeleportId; + + PacketTeleportConfirm(int TeleportId) : TeleportId(TeleportId) {} +}; + +struct PacketClientStatus : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteVarInt(ActionId); + } + + void FromStream(StreamInput *stream) override { + ActionId = stream->ReadVarInt(); + } + + int GetPacketId() override { + return PacketNamePlaySB::ClientStatus; + } + + int ActionId; + + PacketClientStatus(int ActionId) : ActionId(ActionId) {} +}; + +struct PacketPlayerPositionAndLookSB : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + FeetY = stream->ReadDouble(); + Z = stream->ReadDouble(); + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPositionAndLookSB; + } + + + double X; + double FeetY; + double Z; + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerPositionAndLookSB(double X, double FeetY, double Z, + float Yaw, float Pitch, bool OnGround) : X(X), FeetY(FeetY), Z(Z), Yaw(Yaw), + Pitch(Pitch), OnGround(OnGround) {} +}; + +struct PacketChunkData : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteInt(ChunkX); + stream->WriteInt(ChunkZ); + stream->WriteBool(GroundUpContinuous); + stream->WriteInt(PrimaryBitMask); + stream->WriteVarInt(Data.size()); + stream->WriteByteArray(Data); + stream->WriteVarInt(BlockEntities.size()); + LOG(FATAL) << "Serializing unimplemented packet"; + } + + void FromStream(StreamInput *stream) override { + ChunkX = stream->ReadInt(); + ChunkZ = stream->ReadInt(); + GroundUpContinuous = stream->ReadBool(); + PrimaryBitMask = stream->ReadVarInt(); + int Size = stream->ReadVarInt(); + Data = stream->ReadByteArray(Size); + int NumberOfBlockEntities = stream->ReadVarInt(); //TODO: Need NBT + for (int i = 0; i < NumberOfBlockEntities; i++) { + //BlockEntities[i] = stream->ReadNbt(); + } + } + + int GetPacketId() override { + return PacketNamePlayCB::ChunkData; + } + + int ChunkX; + int ChunkZ; + bool GroundUpContinuous; + int PrimaryBitMask; + //int Size; + std::vector Data; + //int NumberOfBlockEntities; + std::vector BlockEntities; //TODO: Replace int with NbtTag and implement NbtTree +}; + +struct PacketPlayerPosition : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteDouble(X); + stream->WriteDouble(FeetY); + stream->WriteDouble(Z); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + X = stream->ReadDouble(); + FeetY = stream->ReadDouble(); + Z = stream->ReadDouble(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerPosition; + } + + double X; + double FeetY; + double Z; + bool OnGround; + + PacketPlayerPosition(double X, double Y, double Z, bool ground) : X(X), FeetY(Y), Z(Z), OnGround(ground) {} +}; + +struct PacketPlayerLook : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Yaw); + stream->WriteFloat(Pitch); + stream->WriteBool(OnGround); + } + + void FromStream(StreamInput *stream) override { + Yaw = stream->ReadFloat(); + Pitch = stream->ReadFloat(); + OnGround = stream->ReadBool(); + } + + int GetPacketId() override { + return PacketNamePlaySB::PlayerLook; + } + + float Yaw; + float Pitch; + bool OnGround; + + PacketPlayerLook(float Yaw, float Pitch, bool ground) : Yaw(Yaw), Pitch(Pitch), OnGround(ground) {} +}; + +struct PacketUpdateHealth : Packet { + void ToStream(StreamOutput *stream) override { + stream->WriteFloat(Health); + stream->WriteVarInt(Food); + stream->WriteFloat(FoodSaturation); + } + + void FromStream(StreamInput *stream) override { + Health = stream->ReadFloat(); + Food = stream->ReadVarInt(); + FoodSaturation = stream->ReadFloat(); + } + + int GetPacketId() override { + return PacketNamePlayCB::UpdateHealth; + } + + float Health; + int Food; + float FoodSaturation; +}; \ No newline at end of file diff --git a/src/Render.cpp b/src/Render.cpp new file mode 100644 index 0000000..ebfbc20 --- /dev/null +++ b/src/Render.cpp @@ -0,0 +1,137 @@ +#include "Render.hpp" + +#include "Utility.hpp" +#include "Shader.hpp" +#include "AssetManager.hpp" +#include "Event.hpp" + +Render::Render(unsigned int windowWidth, unsigned int windowHeight, std::string windowTitle) { + InitSfml(windowWidth, windowHeight, windowTitle); + glCheckError(); + InitGlew(); + glCheckError(); + PrepareToRendering(); + glCheckError(); +} + +Render::~Render() { + delete window; +} + +void Render::InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle) { + LOG(INFO) << "Creating window: " << WinWidth << "x" << WinHeight << " \"" << WinTitle << "\""; + sf::ContextSettings contextSetting; + contextSetting.majorVersion = 3; + contextSetting.minorVersion = 3; + contextSetting.attributeFlags = contextSetting.Core; + contextSetting.depthBits = 24; + window = new sf::Window(sf::VideoMode(WinWidth, WinHeight), WinTitle, sf::Style::Default, contextSetting); + glCheckError(); + window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2 - window->getSize().x / 2, + sf::VideoMode::getDesktopMode().height / 2 - window->getSize().y / 2)); + SetMouseCapture(false); +} + +void Render::InitGlew() { + LOG(INFO) << "Initializing GLEW"; + glewExperimental = GL_TRUE; + GLenum glewStatus = glewInit(); + glCheckError(); + if (glewStatus != GLEW_OK) { + LOG(FATAL) << "Failed to initialize GLEW: " << glewGetErrorString(glewStatus); + } + glViewport(0, 0, window->getSize().x, window->getSize().y); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + //glEnable(GL_BLEND); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glCheckError(); +} + +void Render::PrepareToRendering() { + shader = new Shader("./shaders/face.vs", "./shaders/face.fs"); + shader->Use(); + + //TextureAtlas texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, AssetManager::Instance().GetTextureAtlas()); + glUniform1i(glGetUniformLocation(shader->Program, "textureAtlas"), 0); +} + +void Render::RenderFrame() { + glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + + window->display(); +} + +void Render::HandleEvents() { + sf::Event event; + while (window->pollEvent(event)) { + switch (event.type) { + case sf::Event::Closed: + LOG(INFO) << "Received close event by window closing"; + isRunning = false; + break; + case sf::Event::Resized: + glViewport(0, 0, window->getSize().x, window->getSize().y); + break; + case sf::Event::KeyPressed: + if (!window->hasFocus()) break; + switch (event.key.code) { + case sf::Keyboard::Escape: + LOG(INFO) << "Received close event by esc"; + isRunning = false; + break; + case sf::Keyboard::T: + SetMouseCapture(!isMouseCaptured); + break; + default: + break; + } + default: + break; + } + } + if (window->hasFocus()) { + /*if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) gameState->HandleMovement(GameState::FORWARD, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) gameState->HandleMovement(GameState::BACKWARD, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) gameState->HandleMovement(GameState::LEFT, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) gameState->HandleMovement(GameState::RIGHT, deltaTime); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)) gameState->HandleMovement(GameState::JUMP, deltaTime);*/ + } +} + +void Render::HandleMouseCapture() { + sf::Vector2i mousePos = sf::Mouse::getPosition(*window); + sf::Vector2i center = sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2); + sf::Mouse::setPosition(center, *window); + mouseXDelta = (mousePos - center).x, mouseYDelta = (center - mousePos).y; + const float Sensetivity = 0.7f; + //gameState->HandleRotation(mouseXDelta * Sensetivity, mouseYDelta * Sensetivity); +} + +void Render::SetMouseCapture(bool IsCaptured) { + window->setMouseCursorVisible(!isMouseCaptured); + sf::Mouse::setPosition(sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2), *window); + isMouseCaptured = IsCaptured; + window->setMouseCursorVisible(!IsCaptured); +} + +void Render::ExecuteRenderLoop() { + using namespace std::chrono_literals; + LoopExecutionTimeController timer(16ms); + while (isRunning) { + HandleEvents(); + if (isMouseCaptured) HandleMouseCapture(); + glCheckError(); + + RenderFrame(); + timer.Update(); + } + EventData data = GlobalAppStateData{GlobalState::Exiting}; + EventAgregator::PushEvent(EventType::GlobalAppState,data); +} diff --git a/src/Render.hpp b/src/Render.hpp new file mode 100644 index 0000000..636c4c7 --- /dev/null +++ b/src/Render.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "Shader.hpp" + +class Render { + sf::Window *window; + bool isRunning=true; + bool isMouseCaptured = false; + float mouseXDelta, mouseYDelta; + Shader *shader; + + void SetMouseCapture(bool IsCaptured); + + void HandleMouseCapture(); + + void HandleEvents(); + + void InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle); + + void InitGlew(); + + void RenderFrame(); + + void PrepareToRendering(); +public: + Render(unsigned int windowWidth, unsigned int windowHeight, std::string windowTitle); + ~Render(); + + void ExecuteRenderLoop(); +}; \ No newline at end of file diff --git a/src/Renderer.cpp b/src/Renderer.cpp new file mode 100644 index 0000000..5fc8c2e --- /dev/null +++ b/src/Renderer.cpp @@ -0,0 +1,15 @@ +#include "Renderer.hpp" + +void RenderState::SetActiveVao(GLuint Vao) { + if (Vao != ActiveVao) { + glBindVertexArray(Vao); + ActiveVao = Vao; + } +} + +void RenderState::SetActiveShader(GLuint Shader) { + if (Shader != ActiveShader) { + glUseProgram(Shader); + ActiveShader = Shader; + } +} \ No newline at end of file diff --git a/src/Renderer.hpp b/src/Renderer.hpp new file mode 100644 index 0000000..81c5c50 --- /dev/null +++ b/src/Renderer.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +class RenderState { + GLuint ActiveVao; + GLuint ActiveShader; +public: + void SetActiveVao(GLuint Vao); + void SetActiveShader(GLuint Shader); +}; + +struct Renderer { + virtual ~Renderer() = default; + virtual void Render(RenderState& renderState) = 0; + virtual void PrepareResources() = 0; + virtual void PrepareRender() = 0; +}; \ No newline at end of file diff --git a/src/RendererSection.cpp b/src/RendererSection.cpp new file mode 100644 index 0000000..62a0dc7 --- /dev/null +++ b/src/RendererSection.cpp @@ -0,0 +1,348 @@ +#include "RendererSection.hpp" + +#include + +const GLfloat vertices[] = { + 0, 0, 0, + 1, 0, 1, + 1, 0, 0, + + 0, 0, 0, + 0, 0, 1, + 1, 0, 1, +}; + +const GLfloat uv_coords[] = { + 0.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f, + + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, +}; + +const GLuint magicUniqueConstant = 88375; +GLuint RendererSection::VboVertices = magicUniqueConstant; +GLuint RendererSection::VboUvs = magicUniqueConstant; +std::map RendererSection::refCounterVbo; +std::map RendererSection::refCounterVao; + + +RendererSection::RendererSection(World *world, Vector position) : sectionPosition(position), world(world) { + if (VboVertices == magicUniqueConstant) { + glGenBuffers(1, &VboVertices); + glGenBuffers(1, &VboUvs); + + //Cube vertices + glBindBuffer(GL_ARRAY_BUFFER, VboVertices); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + //Cube UVs + glBindBuffer(GL_ARRAY_BUFFER, VboUvs); + glBufferData(GL_ARRAY_BUFFER, sizeof(uv_coords), uv_coords, GL_STATIC_DRAW); + + LOG(INFO) << "Created VBOs with vertices (" << VboVertices << ") and UVs (" << VboUvs + << ") for ordinary blocks"; + } + + glGenBuffers(1, &VboTextures); + if (refCounterVbo.find(VboTextures) == refCounterVbo.end()) + refCounterVbo[VboTextures] = 0; + refCounterVbo[VboTextures]++; + + glGenBuffers(1, &VboModels); + if (refCounterVbo.find(VboModels) == refCounterVbo.end()) + refCounterVbo[VboModels] = 0; + refCounterVbo[VboModels]++; + + glGenBuffers(1, &VboColors); + if (refCounterVbo.find(VboColors) == refCounterVbo.end()) + refCounterVbo[VboColors] = 0; + refCounterVbo[VboColors]++; + + glGenVertexArrays(1, &Vao); + if (refCounterVao.find(Vao) == refCounterVao.end()) + refCounterVao[Vao] = 0; + refCounterVao[Vao]++; + + glBindVertexArray(Vao); + { + //Cube vertices + GLuint VertAttribPos = 0; + glBindBuffer(GL_ARRAY_BUFFER, VboVertices); + glVertexAttribPointer(VertAttribPos, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(VertAttribPos); + + //Cube UVs + GLuint UvAttribPos = 2; + glBindBuffer(GL_ARRAY_BUFFER, VboUvs); + glVertexAttribPointer(UvAttribPos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(UvAttribPos); + + //Textures + GLuint textureAttribPos = 7; + glBindBuffer(GL_ARRAY_BUFFER, VboTextures); + glVertexAttribPointer(textureAttribPos, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(textureAttribPos); + glVertexAttribDivisor(textureAttribPos, 1); + glCheckError(); + + //Blocks models + GLuint matAttribPos = 8; + size_t sizeOfMat4 = 4 * 4 * sizeof(GLfloat); + glBindBuffer(GL_ARRAY_BUFFER, VboModels); + glVertexAttribPointer(matAttribPos + 0, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, nullptr); + glVertexAttribPointer(matAttribPos + 1, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, (void *) (1 * 4 * sizeof(GLfloat))); + glVertexAttribPointer(matAttribPos + 2, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, (void *) (2 * 4 * sizeof(GLfloat))); + glVertexAttribPointer(matAttribPos + 3, 4, GL_FLOAT, GL_FALSE, sizeOfMat4, (void *) (3 * 4 * sizeof(GLfloat))); + glEnableVertexAttribArray(matAttribPos + 0); + glEnableVertexAttribArray(matAttribPos + 1); + glEnableVertexAttribArray(matAttribPos + 2); + glEnableVertexAttribArray(matAttribPos + 3); + glVertexAttribDivisor(matAttribPos + 0, 1); + glVertexAttribDivisor(matAttribPos + 1, 1); + glVertexAttribDivisor(matAttribPos + 2, 1); + glVertexAttribDivisor(matAttribPos + 3, 1); + + //Color + GLuint colorAttribPos = 12; + glBindBuffer(GL_ARRAY_BUFFER, VboColors); + glVertexAttribPointer(colorAttribPos, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), nullptr); + glEnableVertexAttribArray(colorAttribPos); + glVertexAttribDivisor(colorAttribPos, 1); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + glBindVertexArray(0); + glCheckError(); +} + +RendererSection::~RendererSection() { + refCounterVbo[VboTextures]--; + refCounterVbo[VboModels]--; + refCounterVbo[VboColors]--; + refCounterVao[Vao]--; + if (refCounterVbo[VboTextures] <= 0) + glDeleteBuffers(1, &VboTextures); + + if (refCounterVbo[VboModels] <= 0) + glDeleteBuffers(1, &VboTextures); + if (refCounterVbo[VboColors] <= 0) + glDeleteBuffers(1, &VboColors); + + if (refCounterVao[Vao] <= 0) + glDeleteVertexArrays(1, &Vao); +} + +void RendererSection::Render(RenderState &renderState) { + if (!isEnabled) return; + if (!models.empty()) { + PrepareRender(); + } + renderState.SetActiveVao(Vao); + glDrawArraysInstanced(GL_TRIANGLES, 0, 6, numOfFaces); + glCheckError(); +} + +Section *RendererSection::GetSection() { + return &world->GetSection(sectionPosition); +} + +RendererSection::RendererSection(const RendererSection &other) { + this->world = other.world; + this->VboModels = other.VboModels; + this->VboTextures = other.VboTextures; + this->VboColors = other.VboColors; + this->sectionPosition = other.sectionPosition; + this->Vao = other.Vao; + this->numOfFaces = other.numOfFaces; + this->models = other.models; + this->textures = other.textures; + this->colors = other.colors; + this->hash = other.hash; + + refCounterVbo[VboTextures]++; + refCounterVbo[VboModels]++; + refCounterVbo[VboColors]++; + refCounterVao[Vao]++; +} + +void RendererSection::SetEnabled(bool isEnabled) { + this->isEnabled = isEnabled; +} + +bool RendererSection::IsNeedUpdate() { + size_t currentHash = world->GetSection(sectionPosition).GetHash(); + bool isNeedUpdate = currentHash != hash; + return isNeedUpdate; +} + +void RendererSection::PrepareResources() { + const std::map &textureAtlas = AssetManager::Instance().GetTextureAtlasIndexes(); + Section §ion = world->GetSection(sectionPosition); + models.clear(); + textures.clear(); + colors.clear(); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + Vector blockPos = Vector(x, y, z) + (sectionPosition * 16); + Block &block = world->GetBlock(blockPos); + if (block.id == 0) + continue; + + auto checkBlockVisibility = [&](Vector block) -> bool { + return section.GetBlock(block).id == 0 || + section.GetBlock(block).id == 31 || + section.GetBlock(block).id == 18; + }; + + unsigned char isVisible = 0; + if (x == 0 || x == 15 || y == 0 || y == 15 || z == 0 || z == 15) { + isVisible = 0b1111'1111; //All faces is visible + } else { + isVisible |= checkBlockVisibility(Vector(x - 1, y, z)) << 0; + isVisible |= checkBlockVisibility(Vector(x + 1, y, z)) << 1; + isVisible |= checkBlockVisibility(Vector(x, y + 1, z)) << 2; + isVisible |= checkBlockVisibility(Vector(x, y - 1, z)) << 3; + isVisible |= checkBlockVisibility(Vector(x, y, z - 1)) << 4; + isVisible |= checkBlockVisibility(Vector(x, y, z + 1)) << 5; + } + + if (isVisible == 0x00) + continue; + + glm::mat4 transform; + transform = glm::translate(transform, glm::vec3(sectionPosition.GetX() * 16, + sectionPosition.GetY() * 16, + sectionPosition.GetZ() * 16)); + transform = glm::translate(transform, glm::vec3(x, y, z)); + glm::vec3 biomeColor(0.275, 0.63, 0.1); + glm::vec3 color(0.0f, 0.0f, 0.0f); + if (block.id == 31 || block.id == 18) + color = biomeColor; + + if (block.id == 31) { //X-cross like blocks rendering + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 2)); + for (int i = 0; i < 4; i++) { + textures.push_back(texture->second); + colors.push_back(color); + } + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0.15f, 0, 0.15f)); + faceTransform = glm::scale(faceTransform, glm::vec3(1.0f, 0.9f, 1.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0, 0.0f, 1.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(45.0f), glm::vec3(1.0f, 0.0f, 0)); + for (int i = 0; i < 4; i++) { + models.push_back(faceTransform); + faceTransform = glm::translate(faceTransform, glm::vec3(0.0f, 0.0f, 0.5f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0.0f, 0.0f, -0.5f)); + } + continue; + } + + if (isVisible >> 0 & 0x1) { //east side of block (X+) + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0, 0.0f, 1.0f)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 2)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 1 & 0x1) { //west side X- + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(1, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0, 0.0f, 1.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 3)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 2 & 0x1) { //Top side Y+ + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 1, 0)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 1)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + if (block.id != 2) + colors.push_back(color); + else + colors.push_back(biomeColor); + } + if (isVisible >> 3 & 0x1) { //Bottom side Y- + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(180.0f), glm::vec3(1.0f, 0, 0)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 0)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 4 & 0x1) { //south side Z+ + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(1, 0, 0)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0.0f, -1.0f, 0.0f)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 3)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + if (isVisible >> 5 & 0x1) { //north side Z- + glm::mat4 faceTransform = glm::translate(transform, glm::vec3(0, 0, 1)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f)); + faceTransform = glm::rotate(faceTransform, glm::radians(90.0f), glm::vec3(0.0f, -1.0f, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1)); + faceTransform = glm::rotate(faceTransform, glm::radians(180.0f), glm::vec3(1, 0, 0.0f)); + faceTransform = glm::translate(faceTransform, glm::vec3(0, 0, -1.0f)); + models.push_back(faceTransform); + auto texture = textureAtlas.find(BlockTextureId(block.id, block.state, 4)); + if (texture != textureAtlas.end()) + textures.push_back(texture->second); + else + textures.push_back(glm::vec4(0.0546875, 0.00442477876106194690, + 0.0078125, 0.00442477876106194690)); //Fallback TNT texture + colors.push_back(color); + } + } + } + } + numOfFaces = textures.size(); + hash = section.GetHash(); +} + +void RendererSection::PrepareRender() { + glBindBuffer(GL_ARRAY_BUFFER, VboTextures); + glBufferData(GL_ARRAY_BUFFER, textures.size() * sizeof(glm::vec4), textures.data(), GL_DYNAMIC_DRAW); + textures.clear(); + + glBindBuffer(GL_ARRAY_BUFFER, VboModels); + glBufferData(GL_ARRAY_BUFFER, models.size() * sizeof(glm::mat4), models.data(), GL_DYNAMIC_DRAW); + models.clear(); + + glBindBuffer(GL_ARRAY_BUFFER, VboColors); + glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_DYNAMIC_DRAW); + colors.clear(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} diff --git a/src/RendererSection.hpp b/src/RendererSection.hpp new file mode 100644 index 0000000..093a977 --- /dev/null +++ b/src/RendererSection.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "AssetManager.hpp" +#include "Section.hpp" +#include "World.hpp" +#include "Vector.hpp" +#include "Renderer.hpp" + +class RendererSection : Renderer { + Vector sectionPosition; + World *world; + GLuint Vao, VboTextures, VboModels, VboColors; + std::vector models; + std::vector textures; + std::vector colors; + + static GLuint VboVertices, VboUvs; + static std::map refCounterVbo; + static std::map refCounterVao; + + size_t numOfFaces = 0; + + bool isEnabled = true; + + size_t hash = 0; +public: + RendererSection(World *world, Vector position); + RendererSection(const RendererSection &other); + ~RendererSection() override; + + void Render(RenderState &renderState) override; + + void PrepareResources() override; + + void PrepareRender() override; + + void SetEnabled(bool isEnabled); + + Section *GetSection(); + + bool IsNeedUpdate(); +}; \ No newline at end of file diff --git a/src/RendererWorld.cpp b/src/RendererWorld.cpp new file mode 100644 index 0000000..c80250c --- /dev/null +++ b/src/RendererWorld.cpp @@ -0,0 +1 @@ +#include "RendererWorld.hpp" diff --git a/src/RendererWorld.hpp b/src/RendererWorld.hpp new file mode 100644 index 0000000..761bbde --- /dev/null +++ b/src/RendererWorld.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "RendererSection.hpp" + +class RendererWorld { + +}; \ No newline at end of file diff --git a/src/Section.cpp b/src/Section.cpp new file mode 100644 index 0000000..8b86afd --- /dev/null +++ b/src/Section.cpp @@ -0,0 +1,147 @@ +#include "Section.hpp" + + +Section::Section(Vector position, byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, + byte bitsPerBlock, + std::vector palette) { + worldPosition = position; + + m_dataBlocksLen = dataBlocksLength; + m_dataBlocks = new byte[m_dataBlocksLen]; + std::copy(dataBlocks, dataBlocks + m_dataBlocksLen, m_dataBlocks); + + m_dataLight = new byte[2048]; + std::copy(dataLight, dataLight + 2048, m_dataLight); + + if (dataSky) { + m_dataSkyLight = new byte[2048]; + std::copy(dataSky, dataSky + 2048, m_dataSkyLight); + } + + m_palette = palette; + m_bitsPerBlock = bitsPerBlock; +} + +Section::~Section() { + delete[] m_dataBlocks; + m_dataBlocksLen = 0; + m_dataBlocks = nullptr; + delete[] m_dataLight; + m_dataLight = nullptr; + delete[] m_dataSkyLight; + m_dataSkyLight = nullptr; +} + +Block &Section::GetBlock(Vector pos) { + return m_blocks[pos.GetY() * 256 + pos.GetZ() * 16 + pos.GetX()]; +} + +double totalParsingTime = 0; + +void Section::Parse() { + if (!m_blocks.empty()) + return; + + long long *longArray = reinterpret_cast(m_dataBlocks); + for (size_t i = 0; i < m_dataBlocksLen / 8; i++) + endswap(&longArray[i]); + std::vector blocks; + blocks.reserve(4096); + { + auto begin = std::chrono::steady_clock::now(); + int bitPos = 0; + unsigned short t = 0; + for (size_t i = 0; i < m_dataBlocksLen; i++) { + for (int j = 0; j < 8; j++) { + t |= (m_dataBlocks[i] & 0x01) ? 0x80 : 0x00; + t >>= 1; + m_dataBlocks[i] >>= 1; + bitPos++; + if (bitPos >= m_bitsPerBlock) { + bitPos = 0; + t >>= m_bitsPerBlock - 1; + blocks.push_back(t); + t = 0; + } + } + } + auto end = std::chrono::steady_clock::now(); + std::chrono::duration time = end - begin; + totalParsingTime += time.count(); + } + std::vector light; + light.reserve(4096); + for (int i = 0; i < 2048; i++) { + byte t = m_dataLight[i]; + byte first = t & 0b11110000; + byte second = t >> 4; + light.push_back(first); + light.push_back(second); + } + for (int i = 0; i < 4096; i++) { + unsigned short blockId = m_palette.size() > 0 ? m_palette[blocks[i]] : blocks[i]; + Block block(blockId >> 4, blockId & 0xF); + m_blocks.push_back(block); + } + delete[] m_dataBlocks; + m_dataBlocksLen = 0; + m_dataBlocks = nullptr; + delete[] m_dataLight; + m_dataLight = nullptr; + delete[] m_dataSkyLight; + m_dataSkyLight = nullptr; + + parseWaiter.notify_all(); +} + +Section &Section::operator=(Section other) { + std::swap(*this, other); + return *this; +} + +void swap(Section &a, Section &b) { + using std::swap; + swap(a.m_dataBlocksLen, b.m_dataBlocksLen); + swap(a.m_dataBlocks, b.m_dataBlocks); + swap(a.m_dataLight, b.m_dataLight); + swap(a.m_dataSkyLight, b.m_dataSkyLight); + swap(a.m_blocks, b.m_blocks); + swap(a.m_palette, b.m_palette); + swap(a.m_bitsPerBlock, b.m_bitsPerBlock); +} + +Section::Section(const Section &other) { + worldPosition = other.worldPosition; + m_dataBlocksLen = other.m_dataBlocksLen; + if (other.m_blocks.empty()) { + m_dataBlocks = new byte[m_dataBlocksLen]; + std::copy(other.m_dataBlocks, other.m_dataBlocks + m_dataBlocksLen, m_dataBlocks); + + m_dataLight = new byte[2048]; + std::copy(other.m_dataLight, other.m_dataLight + 2048, m_dataLight); + + if (other.m_dataSkyLight) { + m_dataSkyLight = new byte[2048]; + std::copy(other.m_dataSkyLight, other.m_dataSkyLight + 2048, m_dataSkyLight); + } + } else { + std::copy(other.m_blocks.begin(), other.m_blocks.end(), std::back_inserter(m_blocks)); + } + + m_palette = other.m_palette; + m_bitsPerBlock = other.m_bitsPerBlock; +} + +Vector Section::GetPosition() { + return worldPosition; +} + +size_t Section::GetHash() { + if (m_blocks.empty()) return 0; + + unsigned char *from = reinterpret_cast(m_blocks.data()); + size_t length = m_blocks.size() * sizeof(Block); + + std::string str(from, from + length); + return std::hash{}(str); +} \ No newline at end of file diff --git a/src/Section.hpp b/src/Section.hpp new file mode 100644 index 0000000..f40fdf9 --- /dev/null +++ b/src/Section.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include "Block.hpp" +#include "Vector.hpp" +#include "Utility.hpp" + +const int SECTION_WIDTH = 16; +const int SECTION_LENGTH = 16; +const int SECTION_HEIGHT = 16; + +class Section { + std::vector m_palette; + byte *m_dataBlocks = nullptr; + size_t m_dataBlocksLen; + byte *m_dataLight = nullptr; + byte *m_dataSkyLight = nullptr; + byte m_bitsPerBlock = 0; + std::vector m_blocks; + std::condition_variable parseWaiter; + + Section(); + + Vector worldPosition; + +public: + void Parse(); + + Section(Vector position, byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, byte bitsPerBlock, + std::vector palette); + + ~Section(); + + Block &GetBlock(Vector pos); + + Section &operator=(Section other); + + friend void swap(Section &a, Section &b); + + Section(const Section &other); + + Vector GetPosition(); + + size_t GetHash(); +}; \ No newline at end of file diff --git a/src/Shader.cpp b/src/Shader.cpp new file mode 100644 index 0000000..cf43115 --- /dev/null +++ b/src/Shader.cpp @@ -0,0 +1,115 @@ +#include "Shader.hpp" + +Shader::Shader(const GLchar *vertexPath, const GLchar *fragmentPath, const GLchar *geometryPath) { + vertex = vertexPath; + fragment = fragmentPath; + // 1. Получаем исходный код шейдера из filePath + std::string vertexCode; + std::string fragmentCode; + std::string geometryCode; + std::ifstream vShaderFile; + std::ifstream fShaderFile; + std::ifstream gShaderFile; + // Удостоверимся, что ifstream объекты могут выкидывать исключения + vShaderFile.exceptions(std::ifstream::failbit); + fShaderFile.exceptions(std::ifstream::failbit); + gShaderFile.exceptions(std::ifstream::failbit); + try { + // Открываем файлы + vShaderFile.open(vertexPath); + fShaderFile.open(fragmentPath); + if (geometryPath != nullptr) + gShaderFile.open(geometryPath); + std::stringstream vShaderStream, fShaderStream, gShaderStream; + // Считываем данные в потоки + vShaderStream << vShaderFile.rdbuf(); + fShaderStream << fShaderFile.rdbuf(); + if (geometryPath != nullptr) + gShaderStream << gShaderFile.rdbuf(); + // Закрываем файлы + vShaderFile.close(); + fShaderFile.close(); + if (geometryPath != nullptr) + gShaderFile.close(); + // Преобразовываем потоки в массив GLchar + vertexCode = vShaderStream.str(); + fragmentCode = fShaderStream.str(); + if (geometryPath != nullptr) + geometryCode = gShaderStream.str(); + } + catch (std::ifstream::failure e) { + LOG(ERROR) << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ"; + } + const GLchar *vShaderCode = vertexCode.c_str(); + const GLchar *fShaderCode = fragmentCode.c_str(); + const GLchar *gShaderCode = geometryCode.c_str(); + + // 2. Сборка шейдеров + GLuint vertex, fragment, geometry; + GLint success; + GLchar infoLog[512]; + + // Вершинный шейдер + vertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex, 1, &vShaderCode, NULL); + glCompileShader(vertex); + // Если есть ошибки - вывести их + glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertex, 512, NULL, infoLog); + LOG(ERROR) << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog; + }; + + // Вершинный шейдер + fragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment, 1, &fShaderCode, NULL); + glCompileShader(fragment); + // Если есть ошибки - вывести их + glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragment, 512, NULL, infoLog); + LOG(ERROR) << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog; + }; + + if (geometryPath != nullptr) { + geometry = glCreateShader(GL_GEOMETRY_SHADER); + glShaderSource(geometry, 1, &gShaderCode, NULL); + glCompileShader(geometry); + // Если есть ошибки - вывести их + glGetShaderiv(geometry, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(geometry, 512, NULL, infoLog); + LOG(ERROR) << "ERROR::SHADER::GEOMETRY::COMPILATION_FAILED\n" << infoLog; + }; + } + + // Шейдерная программа + this->Program = glCreateProgram(); + glAttachShader(this->Program, vertex); + glAttachShader(this->Program, fragment); + if (geometryPath != nullptr) + glAttachShader(this->Program, geometry); + glLinkProgram(this->Program); + //Если есть ошибки - вывести их + glGetProgramiv(this->Program, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(this->Program, 512, NULL, infoLog); + LOG(FATAL) << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog; + } + + // Удаляем шейдеры, поскольку они уже в программу и нам больше не нужны. + glDeleteShader(vertex); + glDeleteShader(fragment); +} + +void Shader::Use() { + glUseProgram(this->Program); +} + +void Shader::Reload() { + const GLchar *vertexPath = vertex; + const GLchar *fragmentPath = fragment; + this->~Shader(); + new(this) Shader(vertexPath, fragmentPath); + LOG(INFO) << "Shader is realoded!"; +} diff --git a/src/Shader.hpp b/src/Shader.hpp new file mode 100644 index 0000000..17a434e --- /dev/null +++ b/src/Shader.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include +#include + +class Shader +{ +private: + const GLchar *vertex; + const GLchar *fragment; +public: + // Идентификатор программы + GLuint Program; + // Конструктор считывает и собирает шейдер + Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const GLchar* geometryPath = nullptr); + // Использование программы + void Use(); + + void Reload(); +}; \ No newline at end of file diff --git a/src/Socket.cpp b/src/Socket.cpp new file mode 100644 index 0000000..2bbf49a --- /dev/null +++ b/src/Socket.cpp @@ -0,0 +1,29 @@ +#include "Socket.hpp" + +Socket::Socket(std::string address, unsigned short port) { + sf::Socket::Status connectionStatus = socket.connect(sf::IpAddress(address), port); + if (connectionStatus == sf::Socket::Status::Error) + throw std::runtime_error("Can't connect to remote server"); + else if (connectionStatus != sf::Socket::Status::Done) + throw std::runtime_error("Connection failed with unknown reason"); +} + +Socket::~Socket() { + socket.disconnect(); +} + +void Socket::Read(unsigned char *buffPtr, size_t buffLen) { + size_t received = 0; + socket.receive(buffPtr, buffLen, received); + size_t totalReceived = received; + while (totalReceived < buffLen) { + if (socket.receive(buffPtr + totalReceived, buffLen - totalReceived, received) != sf::Socket::Done) + throw std::runtime_error("Raw socket data receiving is failed"); + totalReceived += received; + } +} + +void Socket::Write(unsigned char *buffPtr, size_t buffLen) { + if (socket.send(buffPtr, buffLen) != sf::Socket::Done) + throw std::runtime_error("Raw socket data sending is failed"); +} diff --git a/src/Socket.hpp b/src/Socket.hpp new file mode 100644 index 0000000..48bcad9 --- /dev/null +++ b/src/Socket.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +/** + * Platform independent class for working with platform dependent hardware socket + * @brief Wrapper around raw sockets + * @warning Connection state is based on lifetime of Socket object instance, ie connected at ctor and disconnect at dtor + * @todo Replace SFML's socket with WinSock and POSIX's socket implementation + */ +class Socket { + sf::TcpSocket socket; +public: + /** + * Constructs Socket class instance from IP's string and Port number and connects to remote server + * @param[in] address IP address of remote server. String should be ANSI and contains 4 one-byte values separated by dots + * @param[in] port target port of remote server to connect + * @throw std::runtime_error if connection is failed + */ + Socket(std::string address, unsigned short port); + + /** + * Destruct Socket instance and disconnect from server + * @warning There is no way to force disconnect, except use delete for manually allocated objects and scope of visibility for variables on stack + */ + ~Socket(); + + /** + * Reads data from socket and write to buffer + * @warning This is blocking function, and execution flow will not be returned until all required data is sended + * @warning Reported buffer length must be <= actual size of buffer, or memory corruption will be caused + * @param[out] buffPtr Pointer to buffer, where data must be placed + * @param[in] buffLen Length of data, that must be readed from server and writed to buffer + */ + void Read(unsigned char *buffPtr, size_t buffLen); + + /** + * Writes data from buffer to socket + * @warning This is blocking function, and execution flow will not be returned until all required data is received + * @param[in] buffPtr Pointer to buffer that contain data to send + * @param[in] buffLen Length of buffer + */ + void Write(unsigned char *buffPtr, size_t buffLen); +}; \ No newline at end of file diff --git a/src/Stream.cpp b/src/Stream.cpp new file mode 100644 index 0000000..54b1e1b --- /dev/null +++ b/src/Stream.cpp @@ -0,0 +1,348 @@ +#include "Stream.hpp" + +const int MAX_VARINT_LENGTH = 5; + +bool StreamInput::ReadBool() { + unsigned char value; + ReadData(&value, 1); + return value != 0; +} + +signed char StreamInput::ReadByte() { + signed char value; + ReadData((unsigned char *) &value, 1); + endswap(value); + return value; +} + +unsigned char StreamInput::ReadUByte() { + unsigned char value; + ReadData(&value, 1); + endswap(value); + return value; +} + +short StreamInput::ReadShort() { + unsigned short value; + ReadData((unsigned char *) &value, 2); + endswap(value); + return value; +} + +unsigned short StreamInput::ReadUShort() { + unsigned short value; + ReadData((unsigned char *) &value, 2); + endswap(value); + return value; +} + +int StreamInput::ReadInt() { + int value; + ReadData((unsigned char *) &value, 4); + endswap(value); + return value; +} + +long long StreamInput::ReadLong() { + long long value; + ReadData((unsigned char *) &value, 8); + endswap(value); + return value; +} + +float StreamInput::ReadFloat() { + float value; + ReadData((unsigned char *) &value, 4); + endswap(value); + return value; +} + +double StreamInput::ReadDouble() { + double value; + ReadData((unsigned char *) &value, 8); + endswap(value); + return value; +} + +std::string StreamInput::ReadString() { + int strLength = ReadVarInt(); + unsigned char *buff = new unsigned char[strLength + 1]; + ReadData(buff, strLength); + buff[strLength] = 0; + std::string str((char *) buff); + delete buff; + return str; +} + +std::string StreamInput::ReadChat() { + std::string str, jsonStr = ReadString(); + nlohmann::json json; + try { + json = nlohmann::json::parse(jsonStr); + } catch (std::exception &e) { + LOG(WARNING) << "Chat json parsing failed: " << e.what(); + LOG(WARNING) << "Corrupted json: " << jsonStr; + return ""; + } + if (json.find("translate") != json.end()) + if (json["translate"].get() == "multiplayer.disconnect.kicked") + return "kicked by operator"; + for (auto &it:json["extra"]) { + str += it["text"].get(); + } + return str; +} + +int StreamInput::ReadVarInt() { + unsigned char data[MAX_VARINT_LENGTH] = {0}; + size_t dataLen = 0; + do { + ReadData(&data[dataLen], 1); + } while ((data[dataLen++] & 0x80) != 0); + + int readed = 0; + int result = 0; + char read; + do { + read = data[readed]; + int value = (read & 0b01111111); + result |= (value << (7 * readed)); + readed++; + } while ((read & 0b10000000) != 0); + + return result; +} + +long long StreamInput::ReadVarLong() { + return 0; +} + +std::vector StreamInput::ReadEntityMetadata() { + return std::vector(); +} + +std::vector StreamInput::ReadSlot() { + return std::vector(); +} + +std::vector StreamInput::ReadNbtTag() { + return std::vector(); +} + +Vector StreamInput::ReadPosition() { + unsigned long long t = ReadLong(); + int x = t >> 38; + int y = (t >> 26) & 0xFFF; + int z = t << 38 >> 38; + if (x >= pow(2, 25)) { + x -= pow(2, 26); + } + if (y >= pow(2, 11)) { + y -= pow(2, 12); + } + if (z >= pow(2, 25)) { + z -= pow(2, 26); + } + return Vector(x, y, z); +} + +unsigned char StreamInput::ReadAngle() { + return ReadUByte(); +} + +std::vector StreamInput::ReadUuid() { + unsigned char buff[16]; + ReadData(buff, 16); + endswap(buff, 16); + return std::vector(buff, buff + 16); +} + +std::vector StreamInput::ReadByteArray(size_t arrLength) { + unsigned char *buffer = new unsigned char[arrLength]; + ReadData(buffer, arrLength); + std::vector ret(buffer, buffer + arrLength); + delete buffer; + return ret; + +} + +void StreamOutput::WriteBool(bool value) { + unsigned char val = value ? 1 : 0; + endswap(val); + WriteData(&val, 1); +} + +void StreamOutput::WriteByte(signed char value) { + endswap(value); + WriteData((unsigned char *) &value, 1); +} + +void StreamOutput::WriteUByte(unsigned char value) { + endswap(value); + WriteData(&value, 1); +} + +void StreamOutput::WriteShort(short value) { + endswap(value); + WriteData((unsigned char *) &value, 2); +} + +void StreamOutput::WriteUShort(unsigned short value) { + endswap(value); + WriteData((unsigned char *) &value, 2); +} + +void StreamOutput::WriteInt(int value) { + endswap(value); + WriteData((unsigned char *) &value, 4); +} + +void StreamOutput::WriteLong(long long value) { + endswap(value); + WriteData((unsigned char *) &value, 8); +} + +void StreamOutput::WriteFloat(float value) { + endswap(value); + WriteData((unsigned char *) &value, 4); +} + +void StreamOutput::WriteDouble(double value) { + endswap(value); + WriteData((unsigned char *) &value, 8); +} + +void StreamOutput::WriteString(std::string value) { + WriteVarInt(value.size()); + WriteData((unsigned char *) value.data(), value.size()); +} + +void StreamOutput::WriteChat(std::string value) { + WriteString(value); +} + +void StreamOutput::WriteVarInt(int value) { + unsigned char buff[5]; + size_t len = 0; + do { + unsigned char temp = (unsigned char) (value & 0b01111111); + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buff[len] = temp; + len++; + } while (value != 0); + WriteData(buff, len); +} + +void StreamOutput::WriteVarLong(long long value) { + unsigned char buff[10]; + size_t len = 0; + do { + unsigned char temp = (unsigned char) (value & 0b01111111); + value >>= 7; + if (value != 0) { + temp |= 0b10000000; + } + buff[len] = temp; + len++; + } while (value != 0); + WriteData(buff, len); +} + +void StreamOutput::WriteEntityMetadata(std::vector value) { + LOG(FATAL) << "Used unimplemented WriteEntityMetadata: " << value.size(); +} + +void StreamOutput::WriteSlot(std::vector value) { + LOG(FATAL) << "Used unimplemented WriteSlot " << value.size(); +} + +void StreamOutput::WriteNbtTag(std::vector value) { + LOG(FATAL) << "Used unimplemented WriteNbtTag " << value.size(); +} + +void StreamOutput::WritePosition(Vector value) { + LOG(FATAL) << "Used unimplemented Position: " << value.GetX() << ", " << value.GetY() << " " << value.GetZ(); +} + +void StreamOutput::WriteAngle(unsigned char value) { + WriteUByte(value); +} + +void StreamOutput::WriteUuid(std::vector value) { + WriteByteArray(value); +} + +void StreamOutput::WriteByteArray(std::vector value) { + WriteData(value.data(), value.size()); +} + +void StreamBuffer::ReadData(unsigned char *buffPtr, size_t buffLen) { + size_t bufferLengthLeft = buffer + bufferLength - bufferPtr; + if (bufferLengthLeft < buffLen) + throw std::runtime_error("Required data is more, than in buffer available"); + std::memcpy(buffPtr, bufferPtr, buffLen); + bufferPtr += buffLen; +} + +void StreamBuffer::WriteData(unsigned char *buffPtr, size_t buffLen) { + size_t bufferLengthLeft = buffer + bufferLength - bufferPtr; + if (bufferLengthLeft < buffLen) + throw std::runtime_error("Required data is more, than in buffer available"); + std::memcpy(bufferPtr, buffPtr, buffLen); + bufferPtr += buffLen; +} + +StreamBuffer::StreamBuffer(unsigned char *data, size_t dataLen) { + buffer = new unsigned char[dataLen]; + bufferPtr = buffer; + bufferLength = dataLen; + std::memcpy(buffer, data, dataLen); +} + +StreamBuffer::StreamBuffer(size_t bufferLen) { + buffer = new unsigned char[bufferLen]; + bufferPtr = buffer; + bufferLength = bufferLen; + for (unsigned char *p = buffer; p != buffer + bufferLength; ++p) + *p = 0; +} + +StreamBuffer::~StreamBuffer() { + delete buffer; +} + +std::vector StreamBuffer::GetBuffer() { + return std::vector(buffer, buffer + bufferLength); +} + +void StreamCounter::WriteData(unsigned char *buffPtr, size_t buffLen) { + buffPtr++; + size += buffLen; +} + +StreamCounter::StreamCounter(size_t initialSize) : size(initialSize) { + +} + +StreamCounter::~StreamCounter() { + +} + +size_t StreamCounter::GetCountedSize() { + return size; +} + +void StreamSocket::ReadData(unsigned char *buffPtr, size_t buffLen) { + socket->Read(buffPtr, buffLen); +} + +void StreamSocket::WriteData(unsigned char *buffPtr, size_t buffLen) { + socket->Write(buffPtr, buffLen); +} + +StreamSocket::StreamSocket(Socket *socketPtr) : socket(socketPtr) { + +} \ No newline at end of file diff --git a/src/Stream.hpp b/src/Stream.hpp new file mode 100644 index 0000000..b856d5f --- /dev/null +++ b/src/Stream.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "Socket.hpp" +#include "Vector.hpp" +#include "Utility.hpp" + +class Stream { +public: + virtual ~Stream() {}; +}; + +class StreamInput : Stream { + virtual void ReadData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamInput() = default; + bool ReadBool(); + signed char ReadByte(); + unsigned char ReadUByte(); + short ReadShort(); + unsigned short ReadUShort(); + int ReadInt(); + long long ReadLong(); + float ReadFloat(); + double ReadDouble(); + std::string ReadString(); + std::string ReadChat(); + int ReadVarInt(); + long long ReadVarLong(); + std::vector ReadEntityMetadata(); + std::vector ReadSlot(); + std::vector ReadNbtTag(); + Vector ReadPosition(); + unsigned char ReadAngle(); + std::vector ReadUuid(); + std::vector ReadByteArray(size_t arrLength); +}; + +class StreamOutput : Stream { + virtual void WriteData(unsigned char *buffPtr, size_t buffLen) = 0; +public: + virtual ~StreamOutput() = default; + void WriteBool(bool value); + void WriteByte(signed char value); + void WriteUByte(unsigned char value); + void WriteShort(short value); + void WriteUShort(unsigned short value); + void WriteInt(int value); + void WriteLong(long long value); + void WriteFloat(float value); + void WriteDouble(double value); + void WriteString(std::string value); + void WriteChat(std::string value); + void WriteVarInt(int value); + void WriteVarLong(long long value); + void WriteEntityMetadata(std::vector value); + void WriteSlot(std::vector value); + void WriteNbtTag(std::vector value); + void WritePosition(Vector value); + void WriteAngle(unsigned char value); + void WriteUuid(std::vector value); + void WriteByteArray(std::vector value); +}; + +class StreamBuffer : public StreamInput, public StreamOutput { + unsigned char *buffer; + unsigned char *bufferPtr; + size_t bufferLength; + + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + +public: + StreamBuffer(unsigned char *data, size_t dataLen); + StreamBuffer(size_t bufferLen); + ~StreamBuffer(); + + std::vector GetBuffer(); +}; + +class StreamCounter : public StreamOutput { + void WriteData(unsigned char *buffPtr, size_t buffLen) override; + + size_t size; +public: + StreamCounter(size_t initialSize = 0); + ~StreamCounter(); + + size_t GetCountedSize(); +}; + +class StreamSocket : public StreamInput, public StreamOutput { + Socket *socket; + void ReadData(unsigned char *buffPtr, size_t buffLen) override; + void WriteData(unsigned char *buffPtr, size_t buffLen) override; +public: + StreamSocket(Socket *socketPtr); + ~StreamSocket() = default; +}; \ No newline at end of file diff --git a/src/Texture.cpp b/src/Texture.cpp new file mode 100644 index 0000000..5d183c3 --- /dev/null +++ b/src/Texture.cpp @@ -0,0 +1,37 @@ +#include "Texture.hpp" + +Texture::Texture(std::string filename, GLenum textureWrapping, GLenum textureFiltering) { + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + //Texture options + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, textureWrapping); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, textureWrapping); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, textureFiltering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + //Image load + sf::Image image; + if (!image.loadFromFile(filename)) { + LOG(ERROR) << "Can't open image " << filename; + throw 201; + } + if (image.getPixelsPtr() == nullptr) { + LOG(ERROR) << "Image data is corrupted!"; + throw 202; + } + image.flipVertically(); + + + //Creating texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getSize().x, image.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, + (GLvoid *) image.getPixelsPtr()); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + +} + +Texture::~Texture() { + glDeleteTextures(1, &texture); +} diff --git a/src/Texture.hpp b/src/Texture.hpp new file mode 100644 index 0000000..5b2afcf --- /dev/null +++ b/src/Texture.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +class Texture { + Texture(Texture&); + Texture&operator=(Texture&); +public: + GLuint texture; + Texture(std::string filename, GLenum textureWrapping = GL_CLAMP_TO_BORDER, GLenum textureFiltering = GL_NEAREST); + ~Texture(); +}; \ No newline at end of file diff --git a/src/Utility.cpp b/src/Utility.cpp new file mode 100644 index 0000000..9fecc08 --- /dev/null +++ b/src/Utility.cpp @@ -0,0 +1,73 @@ +#include +#include "Utility.hpp" + +GLenum glCheckError_(const char *file, int line) { + GLenum errorCode; + while ((errorCode = glGetError()) != GL_NO_ERROR) { + std::string error; + switch (errorCode) { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + error = "STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + error = "STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + static int t = 0; + t++; + if (t > 10) + LOG(FATAL); + LOG(ERROR) << "OpenGL error: " << error << " at " << file << ":" << line; + } + return errorCode; +} + +LoopExecutionTimeController::LoopExecutionTimeController(duration delayLength) : delayLength(delayLength) { + previousUpdate = clock::now(); +} + +LoopExecutionTimeController::~LoopExecutionTimeController() { + +} + +void LoopExecutionTimeController::SetDelayLength(duration length) { + delayLength = length; +} + +unsigned long long LoopExecutionTimeController::GetIterations() { + return iterations; +} + +void LoopExecutionTimeController::Update() { + iterations++; + auto timeToSleep = delayLength - GetDelta(); + if (timeToSleep.count()>0) + std::this_thread::sleep_for(timeToSleep); + previousUpdate = clock::now(); +} + +double LoopExecutionTimeController::GetDeltaMs() { + auto now = clock::now(); + return duration(now-previousUpdate).count(); +} + +LoopExecutionTimeController::duration LoopExecutionTimeController::GetDelta() { + auto now = clock::now(); + //std::cerr< +#include +#include +#include #include template @@ -20,40 +23,30 @@ inline void endswap(unsigned char *arr, size_t arrLen) { std::reverse(arr, arr + arrLen); } -inline GLenum glCheckError_(const char *file, int line) { - GLenum errorCode; - while ((errorCode = glGetError()) != GL_NO_ERROR) { - std::string error; - switch (errorCode) { - case GL_INVALID_ENUM: - error = "INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - error = "INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - error = "INVALID_OPERATION"; - break; - case GL_STACK_OVERFLOW: - error = "STACK_OVERFLOW"; - break; - case GL_STACK_UNDERFLOW: - error = "STACK_UNDERFLOW"; - break; - case GL_OUT_OF_MEMORY: - error = "OUT_OF_MEMORY"; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - error = "INVALID_FRAMEBUFFER_OPERATION"; - break; - } - static int t = 0; - t++; - if (t>10) - LOG(FATAL); - LOG(ERROR) << "OpenGL error: " << error << " at " << file << ":" << line; - } - return errorCode; -} +GLenum glCheckError_(const char *file, int line); +#define glCheckError() glCheckError_(__FILE__, __LINE__) + + + +class LoopExecutionTimeController { + using clock = std::chrono::steady_clock ; + using timePoint = std::chrono::time_point; + using duration = std::chrono::duration; + timePoint previousUpdate; + duration delayLength; + unsigned long long iterations=0; +public: + LoopExecutionTimeController(duration delayLength); + + ~LoopExecutionTimeController(); + + void SetDelayLength(duration length); + + unsigned long long GetIterations(); + + void Update(); + + double GetDeltaMs(); -#define glCheckError() glCheckError_(__FILE__, __LINE__) \ No newline at end of file + duration GetDelta(); +}; diff --git a/src/World.cpp b/src/World.cpp new file mode 100644 index 0000000..487f4ba --- /dev/null +++ b/src/World.cpp @@ -0,0 +1,129 @@ +#include "World.hpp" + +void World::ParseChunkData(std::shared_ptr packet) { + StreamBuffer chunkData(packet->Data.data(), packet->Data.size()); + std::bitset<16> bitmask(packet->PrimaryBitMask); + for (int i = 0; i < 16; i++) { + if (bitmask[i]) { + Vector chunkPosition = Vector(packet->ChunkX, i, packet->ChunkZ); + Section section = ParseSection(&chunkData, chunkPosition); + section.Parse(); + sectionMutexes[chunkPosition].lock(); + auto it = sections.find(chunkPosition); + if (it == sections.end()) { + sections.insert(std::make_pair(chunkPosition, section)); + } else { + using std::swap; + swap(it->second, section); + } + sectionMutexes[chunkPosition].unlock(); + } + } +} + +Section World::ParseSection(StreamInput *data, Vector position) { + unsigned char bitsPerBlock = data->ReadUByte(); + int paletteLength = data->ReadVarInt(); + std::vector palette; + for (int i = 0; i < paletteLength; i++) { + palette.push_back(data->ReadVarInt()); + } + int dataArrayLength = data->ReadVarInt(); + auto dataArray = data->ReadByteArray(dataArrayLength * 8); + auto blockLight = data->ReadByteArray(4096 / 2); + std::vector skyLight; + if (dimension == 0) + skyLight = data->ReadByteArray(4096 / 2); + return Section(position, dataArray.data(), dataArray.size(), blockLight.data(), + (skyLight.size() > 0 ? skyLight.data() : nullptr), bitsPerBlock, palette); +} + +World::~World() { +} + +World::World() { + +} + +bool World::isPlayerCollides(double X, double Y, double Z) { + Vector PlayerChunk(floor(X / 16.0), floor(Y / 16.0), floor(Z / 16.0)); + std::vector closestSectionsCoordinates = { + Vector(PlayerChunk.GetX(), PlayerChunk.GetY(), PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX() + 1, PlayerChunk.GetY(), PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX() - 1, PlayerChunk.GetY(), PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY() + 1, PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY() - 1, PlayerChunk.GetZ()), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY(), PlayerChunk.GetZ() + 1), + Vector(PlayerChunk.GetX(), PlayerChunk.GetY(), PlayerChunk.GetZ() - 1), + }; + std::vector::iterator> closestSections; + for (auto &coord:closestSectionsCoordinates) { + auto it = sections.find(coord); + if (it != sections.end()) + closestSections.push_back(it); + } + if (closestSections.empty()) + return false; + + for (auto &it:closestSections) { + + const double PlayerWidth = 0.6; + const double PlayerHeight = 1.82; + const double PlayerLength = 0.6; + + AABB playerColl; + playerColl.x = X - PlayerWidth / 2.0; + playerColl.w = PlayerWidth; + playerColl.y = Y; + playerColl.h = PlayerHeight; + playerColl.z = Z - PlayerLength / 2.0; + playerColl.l = PlayerLength; + + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + Block block = it->second.GetBlock(Vector(x, y, z)); + if (block.id == 0 || block.id == 31) + continue; + AABB blockColl{(x + it->first.GetX() * 16.0), + (y + it->first.GetY() * 16.0), + (z + it->first.GetZ() * 16.0), 1, 1, 1}; + if (TestCollision(playerColl, blockColl)) + return true; + } + } + } + } + return false; +} + +Block &World::GetBlock(Vector pos) { + Vector sectionPos (floor(pos.GetX() / 16.0f),floor(pos.GetY() / 16.0f),floor(pos.GetZ()/16.0f)); + Vector inSectionPos = pos - (sectionPos * 16); + if (sections.find(sectionPos)==sections.end()){ + static Block block(0,0); + return block; + } + sectionMutexes[sectionPos].lock(); + Block& block = sections.find(sectionPos)->second.GetBlock(inSectionPos); + sectionMutexes[sectionPos].unlock(); + return block; +} + +std::vector World::GetSectionsList() { + std::vector sectionsList; + for (auto& it:sections) { + sectionsList.push_back(it.first); + } + return sectionsList; +} + +Section &World::GetSection(Vector sectionPos) { + sectionMutexes[sectionPos].lock(); + sectionMutexes[sectionPos].unlock(); + return sections.find(sectionPos)->second; +} + +glm::vec3 World::Raycast(glm::vec3 position, glm::vec3 direction, float maxLength, float minPrecision) { + return glm::vec3(position * direction / maxLength * minPrecision); +} diff --git a/src/World.hpp b/src/World.hpp new file mode 100644 index 0000000..52493a7 --- /dev/null +++ b/src/World.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include + +#include "Block.hpp" +#include "Section.hpp" +#include "Packet.hpp" +#include "Collision.hpp" +#include "Vector.hpp" + +class World { + std::map sections; + std::map sectionMutexes; + int dimension = 0; + + Section ParseSection(StreamInput *data, Vector position); + + World(const World &other); + World &operator=(const World &other); +public: + World(); + + ~World(); + + void ParseChunkData(std::shared_ptr packet); + + bool isPlayerCollides(double X, double Y, double Z); + + Block &GetBlock(Vector pos); + + std::vector GetSectionsList(); + + Section &GetSection(Vector sectionPos); + + glm::vec3 Raycast(glm::vec3 position, glm::vec3 direction, float maxLength = 1000.0f, float minPrecision = 0.01f); +}; \ No newline at end of file diff --git a/src/core/AssetManager.cpp b/src/core/AssetManager.cpp index 14ea677..d263c4a 100644 --- a/src/core/AssetManager.cpp +++ b/src/core/AssetManager.cpp @@ -1,4 +1,4 @@ -#include +#include namespace fs = std::experimental::filesystem; diff --git a/src/core/AssetManager.hpp b/src/core/AssetManager.hpp deleted file mode 100644 index 26c7eca..0000000 --- a/src/core/AssetManager.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -#include -#include - -struct TextureCoordinates { - TextureCoordinates(float x = -1, float y = -1, float w = -1, float h = -1) : x(x), y(y), w(w), h(h) {} - - bool operator==(const TextureCoordinates &rhs) const { - return x == rhs.x && - y == rhs.y && - w == rhs.w && - h == rhs.h; - } - - explicit operator bool() const { - return !(*this == TextureCoordinates(-1, -1, -1, -1)); - } - - float x, y, w, h; -}; - -struct BlockTextureId { - //Block sides: 0 - bottom, 1 - top, 2 - north, 3 - south, 4 - west, 5 - east 6 - every side - BlockTextureId(int id = 0, int state = 0, int side = 6) : id(id), state(state), side(side) {} - - int id:9; - int state:4; - int side:3; - - - bool operator<(const BlockTextureId &rhs) const { - if (id < rhs.id) - return true; - if (rhs.id < id) - return false; - if (state < rhs.state) - return true; - if (rhs.state < state) - return false; - return side < rhs.side; - } -}; - -class AssetManager { - Texture *textureAtlas; - std::map assetIds; - std::map assetTextures; - std::map textureAtlasIndexes; -public: - AssetManager(); - - ~AssetManager(); - - void LoadTextureResources(); - - TextureCoordinates GetTextureByAssetName(std::string AssetName); - - std::string GetTextureAssetNameByBlockId(BlockTextureId block); - - GLuint GetTextureAtlas(); - - const std::map &GetTextureAtlasIndexes(); - - void LoadIds(); - - TextureCoordinates GetTextureByBlock(BlockTextureId block); -}; diff --git a/src/core/Core.cpp b/src/core/Core.cpp index e98d204..dcfa890 100644 --- a/src/core/Core.cpp +++ b/src/core/Core.cpp @@ -1,6 +1,5 @@ #include "Core.hpp" -//Core::Core():toRenderMutex("toRender"),availableChunksMutex("availableChunks") { Core::Core() { LOG(INFO) << "Core initializing..."; InitSfml(900, 450, "AltCraft"); @@ -9,8 +8,8 @@ Core::Core() { glCheckError(); client = new NetworkClient("127.0.0.1", 25565, "HelloOne", isRunning); gameState = new GameState(client, isRunning); - gameStateLoopThread = std::thread(&Core::UpdateGameState, this); - sectionUpdateLoopThread = std::thread(&Core::UpdateSections, this); + std::thread loop = std::thread(&Core::UpdateGameState, this); + std::swap(loop, gameStateLoopThread); assetManager = new AssetManager; PrepareToRendering(); LOG(INFO) << "Core is initialized"; @@ -20,7 +19,6 @@ Core::Core() { Core::~Core() { LOG(INFO) << "Core stopping..."; gameStateLoopThread.join(); - sectionUpdateLoopThread.join(); delete shader; delete gameState; delete client; @@ -56,7 +54,6 @@ void Core::Exec() { toWindow << "FPS: " << (1.0f / deltaTime) << " "; toWindow << " (" << deltaTime * 1000 << "ms); "; toWindow << "Tickrate: " << tickRate << " (" << (1.0 / tickRate * 1000) << "ms); "; - toWindow << "Sections: " << sectionRate << " (" << (1.0 / sectionRate * 1000) << "ms); "; window->setTitle(toWindow.str()); HandleEvents(); @@ -64,17 +61,6 @@ void Core::Exec() { glCheckError(); RenderFrame(); - if (isRendersShouldBeCreated) { - availableChunksMutex.lock(); - for (auto &it:renders) { - auto pair = std::make_pair(it, RenderSection(&gameState->world, it)); - availableChunks.insert(pair); - } - renders.clear(); - availableChunksMutex.unlock(); - isRendersShouldBeCreated = false; - waitRendersCreated.notify_all(); - } } } @@ -112,7 +98,7 @@ void Core::InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string W contextSetting.depthBits = 24; window = new sf::Window(sf::VideoMode(WinWidth, WinHeight), WinTitle, sf::Style::Default, contextSetting); glCheckError(); - //window->setVerticalSyncEnabled(true); + window->setVerticalSyncEnabled(true); //window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2, sf::VideoMode::getDesktopMode().height / 2)); window->setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().width / 2 - window->getSize().x / 2, sf::VideoMode::getDesktopMode().height / 2 - window->getSize().y / 2)); @@ -180,6 +166,11 @@ void Core::HandleEvents() { default: break; } + /*case sf::Event::MouseWheelScrolled: + if (!window->hasFocus()) + break; + camera.ProcessMouseScroll(event.mouseWheelScroll.delta); + break;*/ default: break; } @@ -220,16 +211,8 @@ void Core::RenderWorld() { glCheckError(); - toRenderMutex.lock(); for (auto &render : toRender) { - availableChunksMutex.lock(); - auto iterator = availableChunks.find(render); - if (iterator == availableChunks.end()) { - availableChunksMutex.unlock(); - continue; - } - /*Section §ion = *iterator->second.GetSection(); - //availableChunksMutex.unlock(); + Section §ion = *availableChunks.find(render)->second.GetSection(); std::vector sectionCorners = { Vector(0, 0, 0), @@ -242,8 +225,8 @@ void Core::RenderWorld() { Vector(16, 16, 16), }; bool isBreak = true; - glm::mat4 vp = projection * view; for (auto &it:sectionCorners) { + glm::mat4 vp = projection * view; glm::vec3 point(section.GetPosition().GetX() * 16 + it.GetX(), section.GetPosition().GetY() * 16 + it.GetY(), section.GetPosition().GetZ() * 16 + it.GetZ()); @@ -258,14 +241,10 @@ void Core::RenderWorld() { glm::vec3(section.GetPosition().GetX() * 16, section.GetPosition().GetY() * 16, section.GetPosition().GetZ() * 16)) > 30.0f) { - availableChunksMutex.unlock(); continue; } - //availableChunksMutex.lock();*/ - iterator->second.Render(renderState); - availableChunksMutex.unlock(); + availableChunks.find(render)->second.Render(renderState); } - toRenderMutex.unlock(); glCheckError(); } @@ -287,7 +266,6 @@ void Core::PrepareToRendering() { } void Core::UpdateChunksToRender() { - return; Vector playerChunk = Vector(floor(gameState->g_PlayerX / 16.0f), 0, floor(gameState->g_PlayerZ / 16.0f)); static Vector previousPlayerChunk = playerChunk; static bool firstTime = true; @@ -332,126 +310,4 @@ void Core::UpdateGameState() { tickRate = 1 / delta.getElapsedTime().asSeconds(); } LOG(INFO) << "GameState thread is stopped"; -} - -void Core::UpdateSections() { - glm::vec3 playerPosition = gameState->Position(); - float playerPitch = gameState->Pitch(); - float playerYaw = gameState->Yaw(); - sf::Clock delta; - std::vector chunksToRender; - auto currentSectionIterator = chunksToRender.begin(); - while (isRunning) { - delta.restart(); - if (glm::length(glm::distance(gameState->Position(), playerPosition)) > 5.0f) { - chunksToRender.clear(); - playerPosition = gameState->Position(); - Vector playerChunk = Vector(floor(playerPosition.x / 16.0f), 0, floor(playerPosition.z / 16.0f)); - for (auto &it:gameState->world.GetSectionsList()) { - Vector chunkPosition = it; - chunkPosition.SetY(0); - Vector delta = chunkPosition - playerChunk; - if (delta.GetMagnitude() > ChunkDistance) continue; - chunksToRender.push_back(it); - } - std::sort(chunksToRender.begin(), chunksToRender.end(), [playerChunk](auto first, auto second) { - glm::vec3 fDistance = first - playerChunk; - glm::vec3 sDistance = second - playerChunk; - return glm::length(fDistance) < glm::length(sDistance); - }); - for (auto &it:chunksToRender) { - availableChunksMutex.lock(); - if (availableChunks.find(it) == availableChunks.end()) { - availableChunksMutex.unlock(); - renders.push_back(it); - } else - availableChunksMutex.unlock(); - } - if (!renders.empty()) { - std::mutex mutex; - std::unique_lock lock(mutex); - isRendersShouldBeCreated = true; - while (isRendersShouldBeCreated) - waitRendersCreated.wait(lock); - } - currentSectionIterator = chunksToRender.begin(); - toRenderMutex.lock(); - toRender = chunksToRender; - toRenderMutex.unlock(); - } - if (currentSectionIterator != chunksToRender.end()) { - availableChunksMutex.lock(); - auto iterator = availableChunks.find(*currentSectionIterator); - if (iterator != availableChunks.end() && iterator->second.IsNeedUpdate()) { - RenderSection rs = std::move(iterator->second); - availableChunks.erase(iterator); - auto pair = std::make_pair(*currentSectionIterator, rs); - availableChunksMutex.unlock(); - - pair.second.UpdateState(assetManager->GetTextureAtlasIndexes()); - - availableChunksMutex.lock(); - availableChunks.insert(pair); - } - availableChunksMutex.unlock(); - currentSectionIterator = std::next(currentSectionIterator); - } - if (gameState->Pitch() != playerPitch || gameState->Yaw() != playerYaw) { - playerPitch = gameState->Pitch(); - playerYaw = gameState->Yaw(); - const std::vector sectionCorners = { - Vector(0, 0, 0), - Vector(0, 0, 16), - Vector(0, 16, 0), - Vector(0, 16, 16), - Vector(16, 0, 0), - Vector(16, 0, 16), - Vector(16, 16, 0), - Vector(16, 16, 16), - }; - const glm::mat4 projection = glm::perspective(45.0f, (float)width() / (float)height(), 0.1f, 10000000.0f); - const glm::mat4 view = gameState->GetViewMatrix(); - const glm::mat4 vp = projection * view; - for (auto& section: toRender) { - bool isCulled = true; - for (auto &it : sectionCorners) { - glm::vec3 point(section.GetX() * 16 + it.GetX(), - section.GetY() * 16 + it.GetY(), - section.GetZ() * 16 + it.GetZ()); - glm::vec4 p = vp * glm::vec4(point, 1); - glm::vec3 res = glm::vec3(p) / p.w; - if (res.x < 1 && res.x > -1 && res.y < 1 && res.y > -1 && res.z > 0) { - isCulled = false; - break; - } - } - bool isVisible = !isCulled || glm::length(gameState->Position() - - glm::vec3(section.GetX() * 16, section.GetY() * 16, section.GetZ() * 16)) < 30.0f; - availableChunksMutex.lock(); - auto iter = availableChunks.find(section); - if (iter != availableChunks.end()) - iter->second.SetEnabled(isVisible); - availableChunksMutex.unlock(); - - } - } - using namespace std::chrono_literals; - std::this_thread::sleep_for(5ms); - sectionRate = delta.getElapsedTime().asSeconds(); - delta.restart(); - } -} - -MyMutex::MyMutex(std::string name) { - str = name; -} - -void MyMutex::lock() { - LOG(WARNING) << "Thread " << std::this_thread::get_id() << " locked mutex " << str; - mtx.lock(); -} - -void MyMutex::unlock() { - LOG(WARNING) << "Thread " << std::this_thread::get_id() << " unlocked mutex " << str; - mtx.unlock(); } \ No newline at end of file diff --git a/src/core/Core.hpp b/src/core/Core.hpp deleted file mode 100644 index 039b3a3..0000000 --- a/src/core/Core.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct MyMutex { - std::mutex mtx; - std::string str; - MyMutex(std::string name); - void lock(); - void unlock(); -}; - -class Core { - GameState *gameState; - NetworkClient *client; - sf::Window *window; - AssetManager *assetManager; - bool isMouseCaptured = false; - bool isRunning = true; - enum { - MainMenu, - Loading, - Playing, - PauseMenu, - } currentState = Playing; - float mouseXDelta, mouseYDelta; - float deltaTime; - float absTime; - - void RenderWorld(); - - void HandleMouseCapture(); - - void HandleEvents(); - - void InitSfml(unsigned int WinWidth, unsigned int WinHeight, std::string WinTitle); - - void InitGlew(); - - void SetMouseCapture(bool IsCaptured); - - void PrepareToRendering(); - - void RenderFrame(); - - unsigned int width(); - - unsigned int height(); - - void UpdateChunksToRender(); - - void UpdateGameState(); - - void UpdateSections(); - - std::thread gameStateLoopThread; - std::thread sectionUpdateLoopThread; - - Shader *shader; - //Cube verticies, Cube VAO, Cube UVs, TextureIndexes UboTextureIndexes, TextureData UboTextureIndexes, TextureData2 UboTextureIndexes, Blocks VBO, Models VBO, Line VAO, Lines VBO - bool isRendersShouldBeCreated=false; - std::condition_variable waitRendersCreated; - std::vector renders; - std::mutex toRenderMutex; - std::vector toRender; - std::map availableChunks; - std::mutex availableChunksMutex; - - int ChunkDistance = 3; - - RenderState renderState; - - double tickRate = 0; - double sectionRate = 0; - -public: - Core(); - - ~Core(); - - void Exec(); -}; diff --git a/src/core/Event.cpp b/src/core/Event.cpp deleted file mode 100644 index 10b2eaa..0000000 --- a/src/core/Event.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include - -std::queue EventAgregator::eventsToHandle; -std::mutex EventAgregator::queueMutex; -bool EventAgregator::isStarted = false; -std::vector EventAgregator::listeners; -std::mutex EventAgregator::listenersMutex; - -void EventAgregator::EventHandlingLoop() { - while (true) { - queueMutex.lock(); - if (!eventsToHandle.empty()) { - auto queue = eventsToHandle; - while (!eventsToHandle.empty()) - eventsToHandle.pop(); - queueMutex.unlock(); - - while (!queue.empty()) { - auto event = queue.front(); - listenersMutex.lock(); - for (auto& listener : listeners) { - LOG(INFO)<<"Listener notified about event"; - listener->PushEvent(event); - } - listenersMutex.unlock(); - queue.pop(); - } - - queueMutex.lock(); - } - queueMutex.unlock(); - } -} - -void EventAgregator::RegisterListener(EventListener &listener) { - listenersMutex.lock(); - LOG(INFO)<<"Registered handler "<<&listener; - listeners.push_back(&listener); - listenersMutex.unlock(); -} - -void EventAgregator::UnregisterListener(EventListener &listener) { - listenersMutex.lock(); - LOG(INFO)<<"Unregistered handler "<<&listener; - listeners.erase(std::find(listeners.begin(), listeners.end(), &listener)); - listenersMutex.unlock(); -} - - - -EventListener::EventListener() { - EventAgregator::RegisterListener(*this); -} - -EventListener::~EventListener() { - EventAgregator::UnregisterListener(*this); -} - -void EventListener::PushEvent(Event event) { - eventsMutex.lock(); - LOG(INFO)<<"Pushed event to queue"; - events.push(event); - eventsMutex.unlock(); -} - -/*void EventListener::RegisterHandler(EventType type, std::function handler) { - handlers[type] = handler; -}*/ - -bool EventListener::IsEventsQueueIsNotEmpty() { - eventsMutex.lock(); - bool value = !events.empty(); - eventsMutex.unlock(); - return value; -} \ No newline at end of file diff --git a/src/core/Event.hpp b/src/core/Event.hpp deleted file mode 100644 index cfa990a..0000000 --- a/src/core/Event.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -enum class EventType { - Echo, - ChunkChanged, -}; - -struct EchoData { - std::chrono::time_point time; -}; - -struct ChunkChangedData { - Vector chunkPosition; -}; - -using EventData = std::variant; - -struct Event { - EventType type; - EventData data; -}; - -class EventListener { - friend class EventAgregator; - - using HandlerFunc = std::function; - - std::map handlers; //TODO: There must be more elegant solution than std::variant of all data - - std::mutex eventsMutex; - - std::queue events; - - void PushEvent(Event event); - -public: - EventListener(); - ~EventListener(); - bool IsEventsQueueIsNotEmpty(); - - void RegisterHandler(EventType type, HandlerFunc handler) { - handlers[type] = handler; - } - - void HandleEvent() { - eventsMutex.lock(); - if (events.empty()) { - eventsMutex.unlock(); - return; - } - Event event = events.front(); - events.pop(); - eventsMutex.unlock(); - auto function = handlers[event.type]; - function(event.data); - } -}; - -class EventAgregator { - friend EventListener; - - EventAgregator() = default; - static std::queue eventsToHandle; - static std::mutex queueMutex; - static bool isStarted; - static std::vector listeners; - static std::mutex listenersMutex; - - static void EventHandlingLoop(); - - static void RegisterListener(EventListener &listener); - static void UnregisterListener(EventListener &listener); - -public: - static void PushEvent(EventType type, EventData data) { - if (!isStarted) { - isStarted = true; - std::thread(&EventAgregator::EventHandlingLoop).detach(); - } - Event event; - event.type = type; - event.data = data; - eventsToHandle.push(event); - } -}; \ No newline at end of file diff --git a/src/graphics/Gui.hpp b/src/graphics/Gui.hpp deleted file mode 100644 index e22a0a7..0000000 --- a/src/graphics/Gui.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -class Gui { - -public: - int WHY=0; - -}; diff --git a/src/graphics/RenderSection.cpp b/src/graphics/RenderSection.cpp index ae072d6..b07759a 100644 --- a/src/graphics/RenderSection.cpp +++ b/src/graphics/RenderSection.cpp @@ -1,5 +1,4 @@ #include -#include const GLfloat vertices[] = { 0, 0, 0, @@ -43,6 +42,7 @@ std::map RenderSection::refCounterVao; RenderSection::RenderSection(World *world, Vector position) : sectionPosition(position), world(world) { + if (VboVertices == magicUniqueConstant) { glGenBuffers(1, &VboVertices); glGenBuffers(1, &VboUvs); @@ -138,7 +138,6 @@ RenderSection::~RenderSection() { refCounterVao[Vao]--; if (refCounterVbo[VboTextures] <= 0) glDeleteBuffers(1, &VboTextures); - if (refCounterVbo[VboModels] <= 0) glDeleteBuffers(1, &VboTextures); if (refCounterVbo[VboColors] <= 0) @@ -150,9 +149,9 @@ RenderSection::~RenderSection() { void RenderSection::UpdateState(const std::map &textureAtlas) { Section §ion = world->GetSection(sectionPosition); - models.clear(); - textures.clear(); - colors.clear(); + std::vector models; + std::vector textures; + std::vector colors; for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { @@ -296,27 +295,24 @@ void RenderSection::UpdateState(const std::map &textu } } } - numOfFaces = textures.size(); - hash = section.GetHash(); -} -void RenderSection::Render(RenderState &state) { - if (!isEnabled) return; - if (!models.empty()) { - glBindBuffer(GL_ARRAY_BUFFER, VboTextures); - glBufferData(GL_ARRAY_BUFFER, textures.size() * sizeof(glm::vec4), textures.data(), GL_DYNAMIC_DRAW); - textures.clear(); + glBindBuffer(GL_ARRAY_BUFFER, VboTextures); + glBufferData(GL_ARRAY_BUFFER, textures.size() * sizeof(glm::vec4), textures.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, VboModels); - glBufferData(GL_ARRAY_BUFFER, models.size() * sizeof(glm::mat4), models.data(), GL_DYNAMIC_DRAW); - models.clear(); + glBindBuffer(GL_ARRAY_BUFFER, VboModels); + glBufferData(GL_ARRAY_BUFFER, models.size() * sizeof(glm::mat4), models.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, VboColors); - glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_DYNAMIC_DRAW); - colors.clear(); + glBindBuffer(GL_ARRAY_BUFFER, VboColors); + glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glCheckError(); + + numOfFaces = textures.size(); +} + +void RenderSection::Render(RenderState &state) { state.SetActiveVao(Vao); glDrawArraysInstanced(GL_TRIANGLES, 0, 6, numOfFaces); glCheckError(); @@ -334,23 +330,9 @@ RenderSection::RenderSection(const RenderSection &other) { this->sectionPosition = other.sectionPosition; this->Vao = other.Vao; this->numOfFaces = other.numOfFaces; - this->models = other.models; - this->textures = other.textures; - this->colors = other.colors; - this->hash = other.hash; refCounterVbo[VboTextures]++; refCounterVbo[VboModels]++; refCounterVbo[VboColors]++; refCounterVao[Vao]++; } - -void RenderSection::SetEnabled(bool isEnabled) { - this->isEnabled = isEnabled; -} - -bool RenderSection::IsNeedUpdate() { - size_t currentHash = world->GetSection(sectionPosition).GetHash(); - bool isNeedUpdate = currentHash != hash; - return isNeedUpdate; -} \ No newline at end of file diff --git a/src/graphics/RenderSection.hpp b/src/graphics/RenderSection.hpp deleted file mode 100644 index 5973909..0000000 --- a/src/graphics/RenderSection.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -class RenderState { - GLuint ActiveVao; - GLuint ActiveShader; -public: - void SetActiveVao(GLuint Vao); - void SetActiveShader(GLuint Shader); -}; - -class RenderSection { - Vector sectionPosition; - World *world; - GLuint Vao, VboTextures, VboModels, VboColors; - std::vector models; - std::vector textures; - std::vector colors; - - static GLuint VboVertices, VboUvs; - static std::map refCounterVbo; - static std::map refCounterVao; - - size_t numOfFaces = 0; - - bool isEnabled = true; - - size_t hash = 0; -public: - RenderSection(World *world, Vector position); - RenderSection(const RenderSection &other); - ~RenderSection(); - - void UpdateState(const std::map &textureAtlas); - void Render(RenderState &state); - - void SetEnabled(bool isEnabled); - - Section *GetSection(); - - bool IsNeedUpdate(); -}; \ No newline at end of file diff --git a/src/graphics/Shader.hpp b/src/graphics/Shader.hpp deleted file mode 100644 index 17a434e..0000000 --- a/src/graphics/Shader.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -class Shader -{ -private: - const GLchar *vertex; - const GLchar *fragment; -public: - // Идентификатор программы - GLuint Program; - // Конструктор считывает и собирает шейдер - Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const GLchar* geometryPath = nullptr); - // Использование программы - void Use(); - - void Reload(); -}; \ No newline at end of file diff --git a/src/graphics/Texture.hpp b/src/graphics/Texture.hpp deleted file mode 100644 index 5b2afcf..0000000 --- a/src/graphics/Texture.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include - -class Texture { - Texture(Texture&); - Texture&operator=(Texture&); -public: - GLuint texture; - Texture(std::string filename, GLenum textureWrapping = GL_CLAMP_TO_BORDER, GLenum textureFiltering = GL_NEAREST); - ~Texture(); -}; \ No newline at end of file diff --git a/src/graphics/Widget.hpp b/src/graphics/Widget.hpp deleted file mode 100644 index c4d5dc1..0000000 --- a/src/graphics/Widget.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -class Widget { - unsigned int x,y,w,h; -public: - Widget(Widget *parent); - ~Widget(); -}; diff --git a/src/main.cpp b/src/main.cpp index 85fe70d..7bc0fb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ -#include "core/Core.hpp" -#include +#include "Core.hpp" +#include "Event.hpp" +#include "Utility.hpp" const char *getTimeSinceProgramStart(void) { static auto initialTime = std::chrono::steady_clock().now(); @@ -29,7 +30,7 @@ int main() { LOG(WARNING)<<"Sizeof EventData is "< Network::Network(std::string address, unsigned short port) { - try { - socket = new Socket(address, port); - stream = new StreamSocket(socket); - } catch (std::exception &e) { - LOG(FATAL)< -#include "Socket.hpp" -#include "Packet.hpp" - -enum ConnectionState { - Handshaking, - Login, - Play, - Status, -}; - -class Network { - Socket *socket; - StreamSocket *stream; - - std::shared_ptr ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream); -public: - Network(std::string address, unsigned short port); - ~Network(); - - std::shared_ptr ReceivePacket(ConnectionState state = Play); - void SendPacket(Packet &packet); - std::shared_ptr ParsePacketPlay(PacketNamePlayCB id); -}; \ No newline at end of file diff --git a/src/network/NetworkClient.hpp b/src/network/NetworkClient.hpp deleted file mode 100644 index 22b1b22..0000000 --- a/src/network/NetworkClient.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -class NetworkClient { - Network network; - std::thread networkThread; - std::mutex toSendMutex; - std::mutex toReceiveMutex; - std::queue > toSend; - std::queue > toReceive; - bool isActive=true; - bool &isRunning; - ConnectionState state; - void NetworkLoop(); -public: - NetworkClient(std::string address, unsigned short port, std::string username, bool &quit); - ~NetworkClient(); - - std::shared_ptr ReceivePacket(); - void SendPacket(std::shared_ptr packet); -}; \ No newline at end of file diff --git a/src/network/Packet.hpp b/src/network/Packet.hpp deleted file mode 100644 index 685e3da..0000000 --- a/src/network/Packet.hpp +++ /dev/null @@ -1,521 +0,0 @@ -#pragma once - -#include - -#include - -enum PacketNameLoginSB { - LoginStart = 0x00, - EncryptionResponse = 0x01, -}; -enum PacketNamePlaySB { - TeleportConfirm, - PrepareCraftingGrid, - TabCompleteSB, - ChatMessageSB, - ClientStatus, - ClientSettings, - ConfirmTransactionSB, - EnchantItem, - ClickWindow, - CloseWindowSB, - PluginMessageSB, - UseEntity, - KeepAliveSB, - Player, - PlayerPosition, - PlayerPositionAndLookSB, - PlayerLook, - VehicleMoveSB, - SteerBoat, - PlayerAbilitiesSB, - PlayerDigging, - EntityAction, - SteerVehicle, - CraftingBookData, - ResourcePackStatus, - AdvancementTab, - HeldItemChangeSB, - CreativeInventoryAction, - UpdateSign, - AnimationSB, - Spectate, - PlayerBlockPlacement, - UseItem, -}; - -enum PacketNameHandshakingCB { - Handshake = 0x00, -}; -enum PacketNameLoginCB { - Disconnect = 0x00, - EncryptionRequest = 0x01, - LoginSuccess = 0x02, - SetCompression = 0x03, -}; -enum PacketNamePlayCB { - SpawnObject = 0x00, - SpawnExperienceOrb, - SpawnGlobalEntity, - SpawnMob, - SpawnPainting, - SpawnPlayer, - AnimationCB, - Statistics, - BlockBreakAnimation, - UpdateBlockEntity, - BlockAction, - BlockChange, - BossBar, - ServerDifficulty, - TabCompleteCB, - ChatMessageCB, - MultiBlockChange, - ConfirmTransactionCB, - CloseWindowCB, - OpenWindow, - WindowItems, - WindowProperty, - SetSlot, - SetCooldown, - PluginMessageCB, - NamedSoundEffect, - DisconnectPlay, - EntityStatus, - Explosion, - UnloadChunk, - ChangeGameState, - KeepAliveCB, - ChunkData, - Effect, - Particle, - JoinGame, - Map, - EntityRelativeMove, - EntityLookAndRelativeMove, - EntityLook, - Entity, - VehicleMove, - OpenSignEditor, - PlayerAbilitiesCB, - CombatEvent, - PlayerListItem, - PlayerPositionAndLookCB, - UseBed, - UnlockRecipes, - DestroyEntities, - RemoveEntityEffect, - ResourcePackSend, - Respawn, - EntityHeadLook, - SelectAdvancementTab, - WorldBorder, - Camera, - HeldItemChangeCB, - DisplayScoreboard, - EntityMetadata, - AttachEntity, - EntityVelocity, - EntityEquipment, - SetExperience, - UpdateHealth, - ScoreboardObjective, - SetPassengers, - Teams, - UpdateScore, - SpawnPosition, - TimeUpdate, - Title, - SoundEffect, - PlayerListHeaderAndFooter, - CollectItem, - EntityTeleport, - Advancements, - EntityProperties, - EntityEffect, -}; - -struct Packet { - virtual ~Packet() = default; - virtual void ToStream(StreamOutput *stream) = 0; - virtual void FromStream(StreamInput *stream) = 0; - virtual int GetPacketId() = 0; -}; - -struct PacketHandshake : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteVarInt(protocolVersion); - stream->WriteString(serverAddress); - stream->WriteUShort(serverPort); - stream->WriteVarInt(nextState); - } - - void FromStream(StreamInput *stream) override { - protocolVersion = stream->ReadVarInt(); - serverAddress = stream->ReadString(); - serverPort = stream->ReadUShort(); - nextState = stream->ReadVarInt(); - } - - int GetPacketId() override { - return PacketNameHandshakingCB::Handshake; - } - - int protocolVersion; - std::string serverAddress; - unsigned short serverPort; - int nextState; -}; - -struct PacketLoginStart : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteString(Username); - } - - void FromStream(StreamInput *stream) override { - Username = stream->ReadString(); - } - - int GetPacketId() override { - return PacketNameLoginSB::LoginStart; - } - - std::string Username; -}; - -struct PacketLoginSuccess : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteString(Uuid); - stream->WriteString(Username); - } - - void FromStream(StreamInput *stream) override { - Uuid = stream->ReadString(); - Username = stream->ReadString(); - } - - int GetPacketId() override { - return PacketNameLoginCB::LoginSuccess; - } - - std::string Uuid; - std::string Username; -}; - -struct PacketJoinGame : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteInt(EntityId); - stream->WriteUByte(Gamemode); - stream->WriteInt(Dimension); - stream->WriteUByte(Difficulty); - stream->WriteUByte(MaxPlayers); - stream->WriteString(LevelType); - stream->WriteBool(ReducedDebugInfo); - } - - void FromStream(StreamInput *stream) override { - EntityId = stream->ReadInt(); - Gamemode = stream->ReadUByte(); - Dimension = stream->ReadInt(); - Difficulty = stream->ReadUByte(); - MaxPlayers = stream->ReadUByte(); - LevelType = stream->ReadString(); - ReducedDebugInfo = stream->ReadBool(); - } - - int GetPacketId() override { - return PacketNamePlayCB::JoinGame; - } - - int EntityId; - unsigned char Gamemode; - int Dimension; - unsigned char Difficulty; - unsigned char MaxPlayers; - std::string LevelType; - bool ReducedDebugInfo; -}; - -struct PacketDisconnectPlay : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteString(Reason); //TODO: Implement chat-wrapper - } - - void FromStream(StreamInput *stream) override { - Reason = stream->ReadChat(); - } - - int GetPacketId() override { - return PacketNamePlayCB::DisconnectPlay; - } - - std::string Reason; -}; - -struct PacketSpawnPosition : Packet { - void ToStream(StreamOutput *stream) override { - stream->WritePosition(Location); - } - - void FromStream(StreamInput *stream) override { - Location = stream->ReadPosition(); - } - - int GetPacketId() override { - return PacketNamePlayCB::SpawnPosition; - } - - Vector Location; -}; - -struct PacketKeepAliveCB : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteVarInt(KeepAliveId); - } - - void FromStream(StreamInput *stream) override { - KeepAliveId = stream->ReadVarInt(); - } - - int GetPacketId() override { - return PacketNamePlayCB::KeepAliveCB; - } - - int KeepAliveId; -}; - -struct PacketKeepAliveSB : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteVarInt(KeepAliveId); - } - - void FromStream(StreamInput *stream) override { - KeepAliveId = stream->ReadVarInt(); - } - - int GetPacketId() override { - return PacketNamePlaySB::KeepAliveSB; - } - - int KeepAliveId; - - PacketKeepAliveSB(int KeepAliveId) : KeepAliveId(KeepAliveId) {} -}; - -struct PacketPlayerPositionAndLookCB : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteDouble(X); - stream->WriteDouble(Y); - stream->WriteDouble(Z); - stream->WriteFloat(Yaw); - stream->WriteFloat(Pitch); - stream->WriteUByte(Flags); - stream->WriteVarInt(TeleportId); - } - - void FromStream(StreamInput *stream) override { - X = stream->ReadDouble(); - Y = stream->ReadDouble(); - Z = stream->ReadDouble(); - Yaw = stream->ReadFloat(); - Pitch = stream->ReadFloat(); - Flags = stream->ReadUByte(); - TeleportId = stream->ReadVarInt(); - } - - int GetPacketId() override { - return PacketNamePlayCB::PlayerPositionAndLookCB; - } - - double X; - double Y; - double Z; - float Yaw; - float Pitch; - unsigned char Flags; - int TeleportId; -}; - -struct PacketTeleportConfirm : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteVarInt(TeleportId); - } - - void FromStream(StreamInput *stream) override { - TeleportId = stream->ReadVarInt(); - } - - int GetPacketId() override { - return PacketNamePlaySB::TeleportConfirm; - } - - int TeleportId; - - PacketTeleportConfirm(int TeleportId) : TeleportId(TeleportId) {} -}; - -struct PacketClientStatus : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteVarInt(ActionId); - } - - void FromStream(StreamInput *stream) override { - ActionId = stream->ReadVarInt(); - } - - int GetPacketId() override { - return PacketNamePlaySB::ClientStatus; - } - - int ActionId; - - PacketClientStatus(int ActionId) : ActionId(ActionId) {} -}; - -struct PacketPlayerPositionAndLookSB : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteDouble(X); - stream->WriteDouble(FeetY); - stream->WriteDouble(Z); - stream->WriteFloat(Yaw); - stream->WriteFloat(Pitch); - stream->WriteBool(OnGround); - } - - void FromStream(StreamInput *stream) override { - X = stream->ReadDouble(); - FeetY = stream->ReadDouble(); - Z = stream->ReadDouble(); - Yaw = stream->ReadFloat(); - Pitch = stream->ReadFloat(); - OnGround = stream->ReadBool(); - } - - int GetPacketId() override { - return PacketNamePlaySB::PlayerPositionAndLookSB; - } - - - double X; - double FeetY; - double Z; - float Yaw; - float Pitch; - bool OnGround; - - PacketPlayerPositionAndLookSB(double X, double FeetY, double Z, - float Yaw, float Pitch, bool OnGround) : X(X), FeetY(FeetY), Z(Z), Yaw(Yaw), - Pitch(Pitch), OnGround(OnGround) {} -}; - -struct PacketChunkData : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteInt(ChunkX); - stream->WriteInt(ChunkZ); - stream->WriteBool(GroundUpContinuous); - stream->WriteInt(PrimaryBitMask); - stream->WriteVarInt(Data.size()); - stream->WriteByteArray(Data); - stream->WriteVarInt(BlockEntities.size()); - LOG(FATAL) << "Serializing unimplemented packet"; - } - - void FromStream(StreamInput *stream) override { - ChunkX = stream->ReadInt(); - ChunkZ = stream->ReadInt(); - GroundUpContinuous = stream->ReadBool(); - PrimaryBitMask = stream->ReadVarInt(); - int Size = stream->ReadVarInt(); - Data = stream->ReadByteArray(Size); - int NumberOfBlockEntities = stream->ReadVarInt(); //TODO: Need NBT - for (int i = 0; i < NumberOfBlockEntities; i++) { - //BlockEntities[i] = stream->ReadNbt(); - } - } - - int GetPacketId() override { - return PacketNamePlayCB::ChunkData; - } - - int ChunkX; - int ChunkZ; - bool GroundUpContinuous; - int PrimaryBitMask; - //int Size; - std::vector Data; - //int NumberOfBlockEntities; - std::vector BlockEntities; //TODO: Replace int with NbtTag and implement NbtTree -}; - -struct PacketPlayerPosition : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteDouble(X); - stream->WriteDouble(FeetY); - stream->WriteDouble(Z); - stream->WriteBool(OnGround); - } - - void FromStream(StreamInput *stream) override { - X = stream->ReadDouble(); - FeetY = stream->ReadDouble(); - Z = stream->ReadDouble(); - OnGround = stream->ReadBool(); - } - - int GetPacketId() override { - return PacketNamePlaySB::PlayerPosition; - } - - double X; - double FeetY; - double Z; - bool OnGround; - - PacketPlayerPosition(double X, double Y, double Z, bool ground) : X(X), FeetY(Y), Z(Z), OnGround(ground) {} -}; - -struct PacketPlayerLook : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteFloat(Yaw); - stream->WriteFloat(Pitch); - stream->WriteBool(OnGround); - } - - void FromStream(StreamInput *stream) override { - Yaw = stream->ReadFloat(); - Pitch = stream->ReadFloat(); - OnGround = stream->ReadBool(); - } - - int GetPacketId() override { - return PacketNamePlaySB::PlayerLook; - } - - float Yaw; - float Pitch; - bool OnGround; - - PacketPlayerLook(float Yaw, float Pitch, bool ground) : Yaw(Yaw), Pitch(Pitch), OnGround(ground) {} -}; - -struct PacketUpdateHealth : Packet { - void ToStream(StreamOutput *stream) override { - stream->WriteFloat(Health); - stream->WriteVarInt(Food); - stream->WriteFloat(FoodSaturation); - } - - void FromStream(StreamInput *stream) override { - Health = stream->ReadFloat(); - Food = stream->ReadVarInt(); - FoodSaturation = stream->ReadFloat(); - } - - int GetPacketId() override { - return PacketNamePlayCB::UpdateHealth; - } - - float Health; - int Food; - float FoodSaturation; -}; \ No newline at end of file diff --git a/src/network/Socket.hpp b/src/network/Socket.hpp deleted file mode 100644 index 48bcad9..0000000 --- a/src/network/Socket.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include - -#include - -/** - * Platform independent class for working with platform dependent hardware socket - * @brief Wrapper around raw sockets - * @warning Connection state is based on lifetime of Socket object instance, ie connected at ctor and disconnect at dtor - * @todo Replace SFML's socket with WinSock and POSIX's socket implementation - */ -class Socket { - sf::TcpSocket socket; -public: - /** - * Constructs Socket class instance from IP's string and Port number and connects to remote server - * @param[in] address IP address of remote server. String should be ANSI and contains 4 one-byte values separated by dots - * @param[in] port target port of remote server to connect - * @throw std::runtime_error if connection is failed - */ - Socket(std::string address, unsigned short port); - - /** - * Destruct Socket instance and disconnect from server - * @warning There is no way to force disconnect, except use delete for manually allocated objects and scope of visibility for variables on stack - */ - ~Socket(); - - /** - * Reads data from socket and write to buffer - * @warning This is blocking function, and execution flow will not be returned until all required data is sended - * @warning Reported buffer length must be <= actual size of buffer, or memory corruption will be caused - * @param[out] buffPtr Pointer to buffer, where data must be placed - * @param[in] buffLen Length of data, that must be readed from server and writed to buffer - */ - void Read(unsigned char *buffPtr, size_t buffLen); - - /** - * Writes data from buffer to socket - * @warning This is blocking function, and execution flow will not be returned until all required data is received - * @param[in] buffPtr Pointer to buffer that contain data to send - * @param[in] buffLen Length of buffer - */ - void Write(unsigned char *buffPtr, size_t buffLen); -}; \ No newline at end of file diff --git a/src/network/Stream.cpp b/src/network/Stream.cpp index 2545b48..447b13f 100644 --- a/src/network/Stream.cpp +++ b/src/network/Stream.cpp @@ -30,10 +30,11 @@ short StreamInput::ReadShort() { } unsigned short StreamInput::ReadUShort() { - unsigned short value; - ReadData((unsigned char *) &value, 2); - endswap(value); - return value; + unsigned char buff[2]; + ReadData(buff, 2); + unsigned short val = *(reinterpret_cast(buff)); + endswap(val); + return val; } int StreamInput::ReadInt() { diff --git a/src/network/Stream.hpp b/src/network/Stream.hpp deleted file mode 100644 index a24dfbe..0000000 --- a/src/network/Stream.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -class Stream { -public: - virtual ~Stream() {}; -}; - -class StreamInput : Stream { - virtual void ReadData(unsigned char *buffPtr, size_t buffLen) = 0; -public: - virtual ~StreamInput() = default; - bool ReadBool(); - signed char ReadByte(); - unsigned char ReadUByte(); - short ReadShort(); - unsigned short ReadUShort(); - int ReadInt(); - long long ReadLong(); - float ReadFloat(); - double ReadDouble(); - std::string ReadString(); - std::string ReadChat(); - int ReadVarInt(); - long long ReadVarLong(); - std::vector ReadEntityMetadata(); - std::vector ReadSlot(); - std::vector ReadNbtTag(); - Vector ReadPosition(); - unsigned char ReadAngle(); - std::vector ReadUuid(); - std::vector ReadByteArray(size_t arrLength); -}; - -class StreamOutput : Stream { - virtual void WriteData(unsigned char *buffPtr, size_t buffLen) = 0; -public: - virtual ~StreamOutput() = default; - void WriteBool(bool value); - void WriteByte(signed char value); - void WriteUByte(unsigned char value); - void WriteShort(short value); - void WriteUShort(unsigned short value); - void WriteInt(int value); - void WriteLong(long long value); - void WriteFloat(float value); - void WriteDouble(double value); - void WriteString(std::string value); - void WriteChat(std::string value); - void WriteVarInt(int value); - void WriteVarLong(long long value); - void WriteEntityMetadata(std::vector value); - void WriteSlot(std::vector value); - void WriteNbtTag(std::vector value); - void WritePosition(Vector value); - void WriteAngle(unsigned char value); - void WriteUuid(std::vector value); - void WriteByteArray(std::vector value); -}; - -class StreamBuffer : public StreamInput, public StreamOutput { - unsigned char *buffer; - unsigned char *bufferPtr; - size_t bufferLength; - - void ReadData(unsigned char *buffPtr, size_t buffLen) override; - void WriteData(unsigned char *buffPtr, size_t buffLen) override; - -public: - StreamBuffer(unsigned char *data, size_t dataLen); - StreamBuffer(size_t bufferLen); - ~StreamBuffer(); - - std::vector GetBuffer(); -}; - -class StreamCounter : public StreamOutput { - void WriteData(unsigned char *buffPtr, size_t buffLen) override; - - size_t size; -public: - StreamCounter(size_t initialSize = 0); - ~StreamCounter(); - - size_t GetCountedSize(); -}; - -class StreamSocket : public StreamInput, public StreamOutput { - Socket *socket; - void ReadData(unsigned char *buffPtr, size_t buffLen) override; - void WriteData(unsigned char *buffPtr, size_t buffLen) override; -public: - StreamSocket(Socket *socketPtr); - ~StreamSocket() = default; -}; \ No newline at end of file diff --git a/src/world/Block.hpp b/src/world/Block.hpp deleted file mode 100644 index 2f823fe..0000000 --- a/src/world/Block.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -struct Block { - Block(); - - Block(unsigned short id, unsigned char state); - - ~Block(); - - unsigned short id : 13; - unsigned char state : 4; - //unsigned char light:4; -}; - -bool operator<(const Block &lhs, const Block &rhs); \ No newline at end of file diff --git a/src/world/Collision.hpp b/src/world/Collision.hpp deleted file mode 100644 index b88fbf7..0000000 --- a/src/world/Collision.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -struct AABB { - double x,y,z; - double w,l,h; -}; - -bool TestCollision(AABB first, AABB second); \ No newline at end of file diff --git a/src/world/GameState.cpp b/src/world/GameState.cpp index d3a6bd3..79e2f1b 100644 --- a/src/world/GameState.cpp +++ b/src/world/GameState.cpp @@ -1,4 +1,4 @@ -#include +#include GameState::GameState(NetworkClient *Net, bool &quit) : nc(Net), isRunning(quit) { Front = glm::vec3(0.0f, 0.0f, -1.0f); diff --git a/src/world/GameState.hpp b/src/world/GameState.hpp deleted file mode 100644 index 6741882..0000000 --- a/src/world/GameState.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include - -class GameState { - NetworkClient *nc; -public: - GameState(NetworkClient *NetClient, bool &quit); - - void Update(float deltaTime); - - //Navigation - enum Direction { - FORWARD, BACKWARD, LEFT, RIGHT, JUMP - }; - void HandleMovement(GameState::Direction direction, float deltaTime); - void HandleRotation(double yaw, double pitch); - glm::mat4 GetViewMatrix(); - void updateCameraVectors(); - - float Yaw(); - float Pitch(); - void SetYaw(float yaw); - void SetPitch(float pitch); - - glm::vec3 Position(); - void SetPosition(glm::vec3 Position); - glm::vec3 Front; - glm::vec3 Up; - glm::vec3 Right; - glm::vec3 WorldUp; - - //Everything other - World world; - bool &isRunning; - - std::string g_PlayerUuid; - std::string g_PlayerName; - bool g_IsGameStarted; - int g_PlayerEid; - int g_Gamemode; - int g_Dimension; - byte g_Difficulty; - byte g_MaxPlayers; - std::string g_LevelType; - bool g_ReducedDebugInfo; - Vector g_SpawnPosition; - bool g_PlayerInvulnerable; - bool g_PlayerFlying; - bool g_PlayerAllowFlying; - bool g_PlayerCreativeMode; - float g_PlayerFlyingSpeed; - float g_PlayerFovModifier; - float g_PlayerPitch; - float g_PlayerYaw; - double g_PlayerX; - double g_PlayerY; - double g_PlayerZ; - float g_PlayerHealth; - - bool g_OnGround = true; - double g_PlayerVelocityX = 0; - double g_PlayerVelocityY = 0; - double g_PlayerVelocityZ = 0; -}; diff --git a/src/world/Section.cpp b/src/world/Section.cpp index ff2a4fb..d97d163 100644 --- a/src/world/Section.cpp +++ b/src/world/Section.cpp @@ -36,10 +36,8 @@ Block &Section::GetBlock(Vector pos) { return m_blocks[pos.GetY() * 256 + pos.GetZ() * 16 + pos.GetX()]; } -double totalParsingTime = 0; - void Section::Parse() { - if (!m_blocks.empty()) + if (m_dataBlocks == nullptr) return; long long *longArray = reinterpret_cast(m_dataBlocks); @@ -47,28 +45,23 @@ void Section::Parse() { endswap(&longArray[i]); std::vector blocks; blocks.reserve(4096); - { - auto begin = std::chrono::steady_clock::now(); - int bitPos = 0; - unsigned short t = 0; - for (size_t i = 0; i < m_dataBlocksLen; i++) { - for (int j = 0; j < 8; j++) { - t |= (m_dataBlocks[i] & 0x01) ? 0x80 : 0x00; - t >>= 1; - m_dataBlocks[i] >>= 1; - bitPos++; - if (bitPos >= m_bitsPerBlock) { - bitPos = 0; - t >>= m_bitsPerBlock - 1; - blocks.push_back(t); - t = 0; - } + int bitPos = 0; + unsigned short t = 0; + for (size_t i = 0; i < m_dataBlocksLen; i++) { + for (int j = 0; j < 8; j++) { + t |= (m_dataBlocks[i] & 0x01) ? 0x80 : 0x00; + t >>= 1; + m_dataBlocks[i] >>= 1; + bitPos++; + if (bitPos >= m_bitsPerBlock) { + bitPos = 0; + t >>= m_bitsPerBlock - 1; + blocks.push_back(t); + t = 0; } } - auto end = std::chrono::steady_clock::now(); - std::chrono::duration time = end - begin; - totalParsingTime += time.count(); } + std::vector light; light.reserve(4096); for (int i = 0; i < 2048; i++) { @@ -83,6 +76,9 @@ void Section::Parse() { Block block(blockId >> 4, blockId & 0xF); m_blocks.push_back(block); } + if ((light.size() + blocks.size()) / 2 != 4096) { + throw 118; + } delete[] m_dataBlocks; m_dataBlocksLen = 0; m_dataBlocks = nullptr; @@ -135,13 +131,3 @@ Section::Section(const Section &other) { Vector Section::GetPosition() { return worldPosition; } - -size_t Section::GetHash() { - if (m_blocks.empty()) return 0; - - unsigned char *from = reinterpret_cast(m_blocks.data()); - size_t length = m_blocks.size() * sizeof(Block); - - std::string str(from, from + length); - return std::hash{}(str); -} \ No newline at end of file diff --git a/src/world/Section.hpp b/src/world/Section.hpp deleted file mode 100644 index 2df0cfe..0000000 --- a/src/world/Section.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#include -#include -#include - -const int SECTION_WIDTH = 16; -const int SECTION_LENGTH = 16; -const int SECTION_HEIGHT = 16; - -class Section { - std::vector m_palette; - byte *m_dataBlocks = nullptr; - size_t m_dataBlocksLen; - byte *m_dataLight = nullptr; - byte *m_dataSkyLight = nullptr; - byte m_bitsPerBlock = 0; - std::vector m_blocks; - std::condition_variable parseWaiter; - - Section(); - - Vector worldPosition; - -public: - void Parse(); - - Section(Vector position, byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, byte bitsPerBlock, - std::vector palette); - - ~Section(); - - Block &GetBlock(Vector pos); - - Section &operator=(Section other); - - friend void swap(Section &a, Section &b); - - Section(const Section &other); - - Vector GetPosition(); - - size_t GetHash(); -}; \ No newline at end of file diff --git a/src/world/World.hpp b/src/world/World.hpp deleted file mode 100644 index 6b09f1f..0000000 --- a/src/world/World.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include -#include -#include -#include - -class World { - std::map sections; - std::map sectionMutexes; - int dimension = 0; - - Section ParseSection(StreamInput *data, Vector position); - - World(const World &other); - World &operator=(const World &other); -public: - World(); - - ~World(); - - void ParseChunkData(std::shared_ptr packet); - - bool isPlayerCollides(double X, double Y, double Z); - - Block &GetBlock(Vector pos); - - std::vector GetSectionsList(); - - Section &GetSection(Vector sectionPos); - - glm::vec3 Raycast(glm::vec3 position, glm::vec3 direction, float maxLength = 1000.0f, float minPrecision = 0.01f); -}; \ No newline at end of file -- cgit v1.2.3