diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Nbt.hpp | 516 | ||||
-rw-r--r-- | src/Utility.hpp | 59 | ||||
-rw-r--r-- | src/Vector.hpp | 116 | ||||
-rw-r--r-- | src/core/AssetManager.cpp | 2 | ||||
-rw-r--r-- | src/core/AssetManager.hpp | 75 | ||||
-rw-r--r-- | src/core/Core.cpp | 4 | ||||
-rw-r--r-- | src/core/Core.hpp | 95 | ||||
-rw-r--r-- | src/core/Event.cpp | 76 | ||||
-rw-r--r-- | src/core/Event.hpp | 96 | ||||
-rw-r--r-- | src/graphics/Gui.hpp | 8 | ||||
-rw-r--r-- | src/graphics/RenderSection.cpp | 18 | ||||
-rw-r--r-- | src/graphics/RenderSection.hpp | 52 | ||||
-rw-r--r-- | src/graphics/Shader.hpp | 24 | ||||
-rw-r--r-- | src/graphics/Texture.hpp | 14 | ||||
-rw-r--r-- | src/graphics/Widget.hpp | 8 | ||||
-rw-r--r-- | src/main.cpp | 5 | ||||
-rw-r--r-- | src/network/Network.cpp | 8 | ||||
-rw-r--r-- | src/network/Network.hpp | 26 | ||||
-rw-r--r-- | src/network/NetworkClient.hpp | 26 | ||||
-rw-r--r-- | src/network/Packet.hpp | 521 | ||||
-rw-r--r-- | src/network/Socket.hpp | 46 | ||||
-rw-r--r-- | src/network/Stream.hpp | 107 | ||||
-rw-r--r-- | src/world/Block.hpp | 15 | ||||
-rw-r--r-- | src/world/Collision.hpp | 8 | ||||
-rw-r--r-- | src/world/GameState.cpp | 2 | ||||
-rw-r--r-- | src/world/GameState.hpp | 71 | ||||
-rw-r--r-- | src/world/Section.cpp | 55 | ||||
-rw-r--r-- | src/world/Section.hpp | 51 | ||||
-rw-r--r-- | src/world/World.hpp | 38 |
29 files changed, 2099 insertions, 43 deletions
diff --git a/src/Nbt.hpp b/src/Nbt.hpp new file mode 100644 index 0000000..03f5af0 --- /dev/null +++ b/src/Nbt.hpp @@ -0,0 +1,516 @@ +#pragma once + +#include <cstddef> +#include <vector> +#include <fstream> +#include <zlib.h> + +#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<signed char> + String, //std::string + List, //std::vector<NbtTag> + Compound, //std::vector<NbtTag> + IntArray, //std::vector<int32_t> + Unknown, //dummy value + }; + + class NbtTag; + + typedef std::vector<NbtTag> compound_t; + + typedef std::string string_t; + + typedef std::vector<signed char> byteArray_t; + + typedef std::vector<int> 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<signed char> *) data) = *((std::vector<signed char> *) 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<NbtTag> *) data) = *((std::vector<NbtTag> *) other.data); + break; + case Compound: + data = (unsigned char *) new compound_t; + *((std::vector<NbtTag> *) data) = *((std::vector<NbtTag> *) other.data); + break; + case IntArray: + data = (unsigned char *) new intArray_t; + *((std::vector<int> *) data) = *((std::vector<int> *) 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<signed char> *) data); + break; + case String: + delete ((std::string *) data); + break; + case List: + delete ((std::vector<NbtTag> *) data); + break; + case Compound: + delete ((std::vector<NbtTag> *) data); + break; + case IntArray: + delete ((std::vector<int> *) 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<std::string *>(data); + return val; + } + + compound_t &GetCompound() { + std::vector<NbtTag> &val = *reinterpret_cast<std::vector<NbtTag> *>(data); + return val; + } + + compound_t &GetList() { + std::vector<NbtTag> &val = *reinterpret_cast<std::vector<NbtTag> *>(data); + return val; + } + + int64_t &GetLong() { + int64_t &val = *reinterpret_cast<int64_t *>(data); + return val; + } + + float &GetFloat() { + float &val = *reinterpret_cast<float *>(data); + return val; + } + + double &GetDouble() { + double &val = *reinterpret_cast<double *>(data); + return val; + } + + byteArray_t &GetByteArray() { + auto &val = *reinterpret_cast<byteArray_t *>(data); + return val; + } + + intArray_t &GetIntArray() { + auto &val = *reinterpret_cast<intArray_t *>(data); + return val; + } + + int16_t &GetShort() { + auto &val = *reinterpret_cast<int16_t *>(data); + return val; + } + + int32_t &GetInt() { + auto &val = *reinterpret_cast<int32_t *>(data); + return val; + } + + int8_t &GetByte() { + auto &val = *reinterpret_cast<int8_t *>(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<NbtTag> &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<signed char> &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<unsigned char> Decompress(unsigned char *data, size_t dataLen) { + const size_t decompBuffSize = 1024 * 16; + unsigned char *decompBuff = new unsigned char[decompBuffSize]; + std::vector<unsigned char> 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<NbtTag> &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/Utility.hpp b/src/Utility.hpp new file mode 100644 index 0000000..11b4ff7 --- /dev/null +++ b/src/Utility.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include <algorithm> + +#include <GL/glew.h> + +template<class T> +void endswap(T *objp) { + unsigned char *memp = reinterpret_cast<unsigned char *>(objp); + std::reverse(memp, memp + sizeof(T)); +} + +template<class T> +void endswap(T &obj) { + unsigned char *raw = reinterpret_cast<unsigned char *>(&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/src/Vector.hpp b/src/Vector.hpp new file mode 100644 index 0000000..a2d5c6a --- /dev/null +++ b/src/Vector.hpp @@ -0,0 +1,116 @@ +#pragma once + +#include <ostream> +#include <cmath> +#include <tuple> + +#include <glm/vec3.hpp> + +template<class T> +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<T>( + x * rhs, + y * rhs, + z * rhs + ); + } + + Vector3 operator/(T rhs) const { + return Vector3<T>( + x / rhs, + y / rhs, + z / rhs + ); + } + + Vector3 operator+(const Vector3 &rhs) const { + return Vector3<T>( + x + rhs.x, + y + rhs.y, + z + rhs.z + ); + } + + Vector3 operator-(const Vector3 &rhs) const { + return Vector3<T>( + x - rhs.x, + y - rhs.y, + z - rhs.z + ); + } + + Vector3 operator*(const Vector3 &rhs) const { + return Vector3<T>( + x * rhs.x, + y * rhs.y, + z * rhs.z + ); + } + + Vector3 operator/(const Vector3 &rhs) const { + return Vector3<T>( + 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<double> VectorF; +typedef Vector3<signed long long> Vector;
\ No newline at end of file diff --git a/src/core/AssetManager.cpp b/src/core/AssetManager.cpp index d263c4a..14ea677 100644 --- a/src/core/AssetManager.cpp +++ b/src/core/AssetManager.cpp @@ -1,4 +1,4 @@ -#include <AssetManager.hpp> +#include <core/AssetManager.hpp> namespace fs = std::experimental::filesystem; diff --git a/src/core/AssetManager.hpp b/src/core/AssetManager.hpp new file mode 100644 index 0000000..26c7eca --- /dev/null +++ b/src/core/AssetManager.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include <experimental/filesystem> +#include <map> + +#include <GL/glew.h> +#include <glm/vec4.hpp> +#include <nlohmann/json.hpp> + +#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<std::string, Block> assetIds; + std::map<std::string, TextureCoordinates> assetTextures; + std::map<BlockTextureId,glm::vec4> textureAtlasIndexes; +public: + AssetManager(); + + ~AssetManager(); + + void LoadTextureResources(); + + TextureCoordinates GetTextureByAssetName(std::string AssetName); + + std::string GetTextureAssetNameByBlockId(BlockTextureId block); + + GLuint GetTextureAtlas(); + + const std::map<BlockTextureId,glm::vec4> &GetTextureAtlasIndexes(); + + void LoadIds(); + + TextureCoordinates GetTextureByBlock(BlockTextureId block); +}; diff --git a/src/core/Core.cpp b/src/core/Core.cpp index 44e2648..e98d204 100644 --- a/src/core/Core.cpp +++ b/src/core/Core.cpp @@ -44,7 +44,7 @@ void Core::Exec() { UpdateChunksToRender(); } - /*std::ostringstream toWindow; + std::ostringstream toWindow; auto camPos = gameState->Position(); auto velPos = glm::vec3(gameState->g_PlayerVelocityX, gameState->g_PlayerVelocityY, gameState->g_PlayerVelocityZ); @@ -57,7 +57,7 @@ void Core::Exec() { toWindow << " (" << deltaTime * 1000 << "ms); "; toWindow << "Tickrate: " << tickRate << " (" << (1.0 / tickRate * 1000) << "ms); "; toWindow << "Sections: " << sectionRate << " (" << (1.0 / sectionRate * 1000) << "ms); "; - window->setTitle(toWindow.str());*/ + window->setTitle(toWindow.str()); HandleEvents(); if (isMouseCaptured) HandleMouseCapture(); diff --git a/src/core/Core.hpp b/src/core/Core.hpp new file mode 100644 index 0000000..fdbb377 --- /dev/null +++ b/src/core/Core.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include <iomanip> +#include <tuple> + +#include <easylogging++.h> +#include <SFML/Window.hpp> +#include <GL/glew.h> +#include <glm/gtc/type_ptr.hpp> + +#include <world/GameState.hpp> +#include <core/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<Vector> renders; + std::mutex toRenderMutex; + std::vector<Vector> toRender; + std::map<Vector, RenderSection> 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 new file mode 100644 index 0000000..10b2eaa --- /dev/null +++ b/src/core/Event.cpp @@ -0,0 +1,76 @@ +#include <core/Event.hpp> +#include <easylogging++.h> + +std::queue <Event> EventAgregator::eventsToHandle; +std::mutex EventAgregator::queueMutex; +bool EventAgregator::isStarted = false; +std::vector<EventListener*> 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<void(void*)> 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 new file mode 100644 index 0000000..cfa990a --- /dev/null +++ b/src/core/Event.hpp @@ -0,0 +1,96 @@ +#pragma once + +#include <queue> +#include <map> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <variant> +#include <functional> + +#include <Vector.hpp> + +enum class EventType { + Echo, + ChunkChanged, +}; + +struct EchoData { + std::chrono::time_point<std::chrono::high_resolution_clock> time; +}; + +struct ChunkChangedData { + Vector chunkPosition; +}; + +using EventData = std::variant<EchoData, ChunkChangedData>; + +struct Event { + EventType type; + EventData data; +}; + +class EventListener { + friend class EventAgregator; + + using HandlerFunc = std::function<void(EventData)>; + + std::map<EventType, HandlerFunc> handlers; //TODO: There must be more elegant solution than std::variant of all data + + std::mutex eventsMutex; + + std::queue<Event> 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<Event> eventsToHandle; + static std::mutex queueMutex; + static bool isStarted; + static std::vector<EventListener *> 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 new file mode 100644 index 0000000..e22a0a7 --- /dev/null +++ b/src/graphics/Gui.hpp @@ -0,0 +1,8 @@ +#pragma once + +class Gui { + +public: + int WHY=0; + +}; diff --git a/src/graphics/RenderSection.cpp b/src/graphics/RenderSection.cpp index 90d4329..ae072d6 100644 --- a/src/graphics/RenderSection.cpp +++ b/src/graphics/RenderSection.cpp @@ -138,7 +138,7 @@ RenderSection::~RenderSection() { refCounterVao[Vao]--; if (refCounterVbo[VboTextures] <= 0) glDeleteBuffers(1, &VboTextures); - + if (refCounterVbo[VboModels] <= 0) glDeleteBuffers(1, &VboTextures); if (refCounterVbo[VboColors] <= 0) @@ -297,13 +297,11 @@ void RenderSection::UpdateState(const std::map<BlockTextureId, glm::vec4> &textu } } numOfFaces = textures.size(); - hash = section.GetHash(); + hash = section.GetHash(); } void RenderSection::Render(RenderState &state) { - if (!isEnabled) { - return; - } + 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); @@ -339,7 +337,7 @@ RenderSection::RenderSection(const RenderSection &other) { this->models = other.models; this->textures = other.textures; this->colors = other.colors; - this->hash = other.hash; + this->hash = other.hash; refCounterVbo[VboTextures]++; refCounterVbo[VboModels]++; @@ -348,11 +346,11 @@ RenderSection::RenderSection(const RenderSection &other) { } void RenderSection::SetEnabled(bool isEnabled) { - this->isEnabled = isEnabled; + this->isEnabled = isEnabled; } bool RenderSection::IsNeedUpdate() { - size_t currentHash = world->GetSection(sectionPosition).GetHash(); - bool isNeedUpdate = currentHash != hash; - return 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 new file mode 100644 index 0000000..5973909 --- /dev/null +++ b/src/graphics/RenderSection.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include <GL/glew.h> +#include <glm/detail/type_mat.hpp> +#include <glm/vec2.hpp> +#include <glm/detail/type_mat4x4.hpp> +#include <glm/gtx/transform.hpp> +#include <easylogging++.h> + +#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<glm::mat4> models; + std::vector<glm::vec4> textures; + std::vector<glm::vec3> colors; + + static GLuint VboVertices, VboUvs; + static std::map<GLuint, int> refCounterVbo; + static std::map<GLuint, int> 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<BlockTextureId, glm::vec4> &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 new file mode 100644 index 0000000..17a434e --- /dev/null +++ b/src/graphics/Shader.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <string> +#include <fstream> +#include <sstream> + +#include <easylogging++.h> +#include <GL/glew.h> + +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 new file mode 100644 index 0000000..277806a --- /dev/null +++ b/src/graphics/Texture.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <SFML/Graphics.hpp> +#include <easylogging++.h> +#include <GL/glew.h> + +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 new file mode 100644 index 0000000..c4d5dc1 --- /dev/null +++ b/src/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/src/main.cpp b/src/main.cpp index aa30ff8..bcf573b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ -#include "../include/Core.hpp" +#include "core/Core.hpp" +#include <core/Event.hpp> const char *getTimeSinceProgramStart(void) { static auto initialTime = std::chrono::steady_clock().now(); @@ -25,6 +26,8 @@ int main() { el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); LOG(INFO) << "Logger is configured"; + LOG(FATAL)<<"Sizeof data is "<<sizeof(EventData); + Core core; core.Exec(); diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 91cb481..798756d 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -1,8 +1,12 @@ #include <network/Network.hpp> Network::Network(std::string address, unsigned short port) { - socket = new Socket(address, port); - stream = new StreamSocket(socket); + try { + socket = new Socket(address, port); + stream = new StreamSocket(socket); + } catch (std::exception &e) { + LOG(FATAL)<<e.what(); + } } Network::~Network() { diff --git a/src/network/Network.hpp b/src/network/Network.hpp new file mode 100644 index 0000000..1281289 --- /dev/null +++ b/src/network/Network.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <memory> +#include "Socket.hpp" +#include "Packet.hpp" + +enum ConnectionState { + Handshaking, + Login, + Play, + Status, +}; + +class Network { + Socket *socket; + StreamSocket *stream; + + std::shared_ptr<Packet> ReceivePacketByPacketId(int packetId, ConnectionState state, StreamInput &stream); +public: + Network(std::string address, unsigned short port); + ~Network(); + + std::shared_ptr<Packet> ReceivePacket(ConnectionState state = Play); + void SendPacket(Packet &packet); + std::shared_ptr<Packet> ParsePacketPlay(PacketNamePlayCB id); +};
\ No newline at end of file diff --git a/src/network/NetworkClient.hpp b/src/network/NetworkClient.hpp new file mode 100644 index 0000000..22b1b22 --- /dev/null +++ b/src/network/NetworkClient.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <thread> +#include <queue> +#include <mutex> + +#include <network/Network.hpp> + +class NetworkClient { + Network network; + std::thread networkThread; + std::mutex toSendMutex; + std::mutex toReceiveMutex; + std::queue <std::shared_ptr<Packet>> toSend; + std::queue <std::shared_ptr<Packet>> 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 <Packet> ReceivePacket(); + void SendPacket(std::shared_ptr<Packet> packet); +};
\ No newline at end of file diff --git a/src/network/Packet.hpp b/src/network/Packet.hpp new file mode 100644 index 0000000..685e3da --- /dev/null +++ b/src/network/Packet.hpp @@ -0,0 +1,521 @@ +#pragma once + +#include <easylogging++.h> + +#include <network/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<unsigned char> Data; + //int NumberOfBlockEntities; + std::vector<int> 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 new file mode 100644 index 0000000..48bcad9 --- /dev/null +++ b/src/network/Socket.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include <string> + +#include <SFML/Network.hpp> + +/** + * 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.hpp b/src/network/Stream.hpp new file mode 100644 index 0000000..a24dfbe --- /dev/null +++ b/src/network/Stream.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include <algorithm> +#include <string> +#include <stdexcept> +#include <vector> +#include <cstring> + +#include <nlohmann/json.hpp> +#include <easylogging++.h> + +#include <network/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<unsigned char> ReadEntityMetadata(); + std::vector<unsigned char> ReadSlot(); + std::vector<unsigned char> ReadNbtTag(); + Vector ReadPosition(); + unsigned char ReadAngle(); + std::vector<unsigned char> ReadUuid(); + std::vector<unsigned char> 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<unsigned char> value); + void WriteSlot(std::vector<unsigned char> value); + void WriteNbtTag(std::vector<unsigned char> value); + void WritePosition(Vector value); + void WriteAngle(unsigned char value); + void WriteUuid(std::vector<unsigned char> value); + void WriteByteArray(std::vector<unsigned char> 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<unsigned char> 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 new file mode 100644 index 0000000..2f823fe --- /dev/null +++ b/src/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/src/world/Collision.hpp b/src/world/Collision.hpp new file mode 100644 index 0000000..b88fbf7 --- /dev/null +++ b/src/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/src/world/GameState.cpp b/src/world/GameState.cpp index 79e2f1b..d3a6bd3 100644 --- a/src/world/GameState.cpp +++ b/src/world/GameState.cpp @@ -1,4 +1,4 @@ -#include <GameState.hpp> +#include <world/GameState.hpp> 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 new file mode 100644 index 0000000..6741882 --- /dev/null +++ b/src/world/GameState.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include <nlohmann/json.hpp> +#include <glm/glm.hpp> +#include <glm/gtc/matrix_transform.hpp> + +#include <world/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/src/world/Section.cpp b/src/world/Section.cpp index 279d2b2..ff2a4fb 100644 --- a/src/world/Section.cpp +++ b/src/world/Section.cpp @@ -47,28 +47,28 @@ void Section::Parse() { endswap(&longArray[i]); std::vector<unsigned short> 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<double, std::milli> time = end - begin; - totalParsingTime += time.count(); - } + { + 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<double, std::milli> time = end - begin; + totalParsingTime += time.count(); + } std::vector<byte> light; light.reserve(4096); for (int i = 0; i < 2048; i++) { @@ -137,8 +137,11 @@ Vector Section::GetPosition() { } size_t Section::GetHash() { - if (m_blocks.empty()) - return 0; - std::string str((unsigned char*)m_blocks.data(), (unsigned char*)m_blocks.data() + m_blocks.size() * sizeof(Block)); - return std::hash<std::string>{}(str); + if (m_blocks.empty()) return 0; + + unsigned char *from = reinterpret_cast<unsigned char *>(m_blocks.data()); + size_t length = m_blocks.size() * sizeof(Block); + + std::string str(from, from + length); + return std::hash<std::string>{}(str); }
\ No newline at end of file diff --git a/src/world/Section.hpp b/src/world/Section.hpp new file mode 100644 index 0000000..2df0cfe --- /dev/null +++ b/src/world/Section.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <vector> +#include <map> +#include <condition_variable> +#include <functional> + +#include <easylogging++.h> + +#include <world/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<unsigned short> m_palette; + byte *m_dataBlocks = nullptr; + size_t m_dataBlocksLen; + byte *m_dataLight = nullptr; + byte *m_dataSkyLight = nullptr; + byte m_bitsPerBlock = 0; + std::vector<Block> 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<unsigned short> 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 new file mode 100644 index 0000000..6b09f1f --- /dev/null +++ b/src/world/World.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <map> +#include <bitset> + +#include <easylogging++.h> + +#include <world/Block.hpp> +#include <world/Section.hpp> +#include <network/Packet.hpp> +#include <world/Collision.hpp> + +class World { + std::map<Vector, Section> sections; + std::map<Vector, std::mutex> 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<PacketChunkData> packet); + + bool isPlayerCollides(double X, double Y, double Z); + + Block &GetBlock(Vector pos); + + std::vector<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 |