summaryrefslogtreecommitdiffstats
path: root/code
diff options
context:
space:
mode:
authorLaG1924 <12997935+LaG1924@users.noreply.github.com>2017-05-13 16:01:56 +0200
committerLaG1924 <12997935+LaG1924@users.noreply.github.com>2017-05-13 16:01:56 +0200
commit1563ae5be6bc130a9b3a23464f7e28fdb1e87da3 (patch)
treeb1f65a03827494fa78e320b134f4cc7df54754bb /code
parent2017-05-12 (diff)
downloadAltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.tar
AltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.tar.gz
AltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.tar.bz2
AltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.tar.lz
AltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.tar.xz
AltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.tar.zst
AltCraft-1563ae5be6bc130a9b3a23464f7e28fdb1e87da3.zip
Diffstat (limited to 'code')
-rw-r--r--code/Block.cpp10
-rw-r--r--code/Block.hpp15
-rw-r--r--code/CMakeLists.txt95
-rw-r--r--code/Field.cpp307
-rw-r--r--code/Field.hpp119
-rw-r--r--code/FieldParser.cpp106
-rw-r--r--code/FieldParser.hpp30
-rw-r--r--code/Game.cpp159
-rw-r--r--code/Game.hpp58
-rw-r--r--code/Nbt.hpp516
-rw-r--r--code/Network.cpp108
-rw-r--r--code/Network.hpp26
-rw-r--r--code/NetworkClient.cpp107
-rw-r--r--code/NetworkClient.hpp42
-rw-r--r--code/Packet.cpp100
-rw-r--r--code/Packet.hpp130
-rw-r--r--code/PacketBuilder.cpp65
-rw-r--r--code/PacketBuilder.hpp17
-rw-r--r--code/PacketParser.cpp147
-rw-r--r--code/PacketParser.hpp38
-rw-r--r--code/PositionF.cpp50
-rw-r--r--code/PositionF.hpp29
-rw-r--r--code/PositionI.cpp120
-rw-r--r--code/PositionI.hpp50
-rw-r--r--code/Section.cpp132
-rw-r--r--code/Section.hpp40
-rw-r--r--code/World.cpp107
-rw-r--r--code/World.hpp32
-rw-r--r--code/graphics/AssetManager.cpp143
-rw-r--r--code/graphics/AssetManager.hpp52
-rw-r--r--code/graphics/Camera3D.cpp79
-rw-r--r--code/graphics/Camera3D.hpp66
-rw-r--r--code/graphics/Display.cpp268
-rw-r--r--code/graphics/Display.hpp33
-rw-r--r--code/graphics/Shader.cpp90
-rw-r--r--code/graphics/Shader.hpp22
-rw-r--r--code/graphics/Texture.cpp39
-rw-r--r--code/graphics/Texture.hpp12
-rw-r--r--code/main.cpp10
-rw-r--r--code/shaders/simple.fs22
-rw-r--r--code/shaders/simple.vs15
-rw-r--r--code/utility.cpp66
-rw-r--r--code/utility.h16
43 files changed, 3688 insertions, 0 deletions
diff --git a/code/Block.cpp b/code/Block.cpp
new file mode 100644
index 0000000..64e5330
--- /dev/null
+++ b/code/Block.cpp
@@ -0,0 +1,10 @@
+#include "Block.hpp"
+
+Block::~Block() {}
+
+Block::Block(unsigned short idAndState, unsigned char light) : id(idAndState >> 4), state(idAndState & 0x0F),
+ light(light) {}
+
+Block::Block(unsigned short id, unsigned char state, unsigned char light) : id(id), state(state), light(light) {}
+
+Block::Block() : id(0), state(0), light(0) {}
diff --git a/code/Block.hpp b/code/Block.hpp
new file mode 100644
index 0000000..7c780c1
--- /dev/null
+++ b/code/Block.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+struct Block {
+ Block(unsigned short idAndState, unsigned char light);
+
+ Block(unsigned short id, unsigned char state, unsigned char light);
+
+ Block();
+
+ ~Block();
+
+ unsigned short id:13;
+ unsigned char state:4;
+ unsigned char light:4;
+}; \ No newline at end of file
diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt
new file mode 100644
index 0000000..c084e3e
--- /dev/null
+++ b/code/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.0)
+project(AltCraft)
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
+
+if (CMAKE_COMPILER_IS_GNUCXX)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -w -Werror")
+ #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
+ #set(CMAKE_CXX_FLASG "${CMAKE_CXX_FLAGS} -g -O0")
+ #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
+ #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -pg")
+ set(PLATFORM_LIBRARIES "pthread")
+endif ()
+
+set(SOURCE_FILES main.cpp Field.hpp utility.cpp Packet.hpp FieldParser.hpp Network.hpp Network.cpp NetworkClient.cpp
+ NetworkClient.hpp json.hpp PacketBuilder.cpp PacketBuilder.hpp Packet.cpp FieldParser.cpp Field.cpp
+ PacketParser.cpp PacketParser.hpp PositionF.cpp PositionF.hpp PositionI.cpp PositionI.hpp Game.cpp
+ Game.hpp World.cpp World.hpp Block.cpp Block.hpp Section.cpp Section.hpp Nbt.hpp graphics/AssetManager.cpp
+ graphics/AssetManager.hpp graphics/Display.cpp graphics/Display.hpp graphics/Camera3D.hpp graphics/Camera3D.cpp
+ graphics/Shader.hpp graphics/Shader.cpp graphics/Texture.hpp graphics/Texture.cpp)
+
+add_executable(AltCraft ${SOURCE_FILES})
+target_link_libraries(AltCraft ${PLATFORM_LIBRARIES})
+
+add_subdirectory(depedencies)
+target_include_directories(AltCraft PUBLIC ./depedencies/include)
+target_link_libraries(AltCraft deps)
+
+#Setup SFML
+find_package(SFML 2 COMPONENTS system window graphics network REQUIRED)
+if (SFML_FOUND)
+ target_link_libraries(AltCraft ${SFML_LIBRARIES})
+ target_include_directories(AltCraft PUBLIC ${SFML_INCLUDE_DIR})
+else ()
+ message(FATAL_ERROR "SFML not found!")
+endif ()
+
+#Setup OpenGL
+find_package(OpenGL REQUIRED)
+if (OPENGL_FOUND)
+ target_link_libraries(AltCraft ${OPENGL_LIBRARIES})
+ target_include_directories(AltCraft PUBLIC ${OPENGL_INCLUDE_DIRS})
+else ()
+ message(FATAL_ERROR "OpenGL not found!")
+endif ()
+
+#Setup Zlib
+find_package(ZLIB REQUIRED)
+if (ZLIB_FOUND)
+ target_link_libraries(AltCraft ${ZLIB_LIBRARIES})
+ target_include_directories(AltCraft PUBLIC ${ZLIB_INCLUDE_DIR})
+else ()
+ message(FATAL_ERROR "Zlib not found!")
+endif ()
+
+
+#[[
+#Setup SFML
+find_package(SFML 2 COMPONENTS system network graphics window REQUIRED)
+include_directories(${SFML_INCLUDE_DIR})
+if (NOT SFML_FOUND)
+ message(FATAL_ERROR "SFML not found!")
+endif ()
+
+#Setup Zlib
+find_package(ZLIB REQUIRED)
+include_directories(${ZLIB_INCLUDE_DIR})
+if (NOT ZLIB_FOUND)
+ message(FATAL_ERROR "Zlib not found!")
+endif ()
+
+#Setup GLEW
+find_package(GLEW REQUIRED)
+include_directories(${GLEW_INCLUDE_DIRS})
+if (NOT GLEW_FOUND)
+ message(FATAL_ERROR "GLEW not found!")
+endif ()
+
+#Setup OpenGL
+find_package(OpenGL REQUIRED)
+include_directories(${OPENGL_INCLUDE_DIRS})
+if (NOT OPENGL_FOUND)
+ message(FATAL_ERROR "OpenGL not found!")
+endif ()
+
+set(SOURCE_FILES main.cpp Field.hpp utility.cpp Packet.hpp FieldParser.hpp Network.hpp Network.cpp NetworkClient.cpp
+ NetworkClient.hpp json.hpp PacketBuilder.cpp PacketBuilder.hpp Packet.cpp FieldParser.cpp Field.cpp
+ PacketParser.cpp PacketParser.hpp PositionF.cpp PositionF.hpp PositionI.cpp PositionI.hpp Game.cpp
+ Game.hpp World.cpp World.hpp Block.cpp Block.hpp Section.cpp Section.hpp Nbt.hpp graphics/AssetManager.cpp
+ graphics/AssetManager.hpp)
+
+add_executable(AltCraft ${SOURCE_FILES})
+target_link_libraries(AltCraft ${PLATFORM_LIBRARIES} ${SFML_LIBRARIES} ${ZLIB_LIBRARIES} ${SOIL_LIBRARY}
+ ${GLFW_LIBRARIES} ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES})
+]] \ No newline at end of file
diff --git a/code/Field.cpp b/code/Field.cpp
new file mode 100644
index 0000000..c95c32d
--- /dev/null
+++ b/code/Field.cpp
@@ -0,0 +1,307 @@
+#include <cmath>
+#include "Field.hpp"
+
+Field::Field() {
+}
+
+Field::Field(const Field &other) : m_dataLength(other.m_dataLength), m_type(other.m_type) {
+
+ m_data = new byte[m_dataLength];
+ std::copy(other.m_data,other.m_data+m_dataLength,m_data);
+}
+
+void Field::swap(Field &other) {
+ std::swap(other.m_dataLength, m_dataLength);
+ std::swap(other.m_data, m_data);
+ std::swap(other.m_type, m_type);
+}
+
+Field &Field::operator=(Field other) {
+ other.swap(*this);
+ return *this;
+}
+
+Field::~Field() {
+ Clear();
+}
+
+size_t Field::GetLength() {
+ if (m_data != nullptr && m_dataLength == 0)
+ throw 102;
+ return m_dataLength;
+}
+
+void Field::Clear() {
+ m_dataLength = 0;
+ delete[] m_data;
+ m_data = nullptr;
+}
+
+void Field::CopyToBuff(byte *ptr) {
+ if (m_dataLength > 0)
+ std::copy(m_data,m_data+m_dataLength,ptr);
+}
+
+void Field::SetRaw(byte *ptr, size_t len, FieldType type) {
+ Clear();
+ m_dataLength = len;
+ m_type = type;
+ m_data = new byte[m_dataLength];
+ std::copy(ptr,ptr+m_dataLength,m_data);
+}
+
+int Field::GetVarInt() {
+
+ size_t readed;
+ return VarIntRead(m_data, readed);
+
+}
+
+void Field::SetVarInt(int value) {
+ Clear();
+ m_type = VarInt;
+ m_data = new byte[5];
+ m_dataLength = VarIntWrite(value, m_data);
+}
+
+int Field::GetInt() {
+ int value = *(reinterpret_cast<int *>(m_data));
+ endswap(&value);
+ return value;
+}
+
+void Field::SetInt(int value) {
+ Clear();
+ m_type = Int;
+ m_data = new byte[4];
+ m_dataLength = 4;
+ int *p = reinterpret_cast<int *>(m_data);
+ *p = value;
+ endswap(p);
+}
+
+bool Field::GetBool() {
+ return *m_data != 0x00;
+}
+
+void Field::SetBool(bool value) {
+ Clear();
+ m_type = Boolean;
+ m_data = new byte[1];
+ m_dataLength = 1;
+ *m_data = value ? 0x01 : 0x00;
+}
+
+unsigned short Field::GetUShort() {
+ unsigned short *p = reinterpret_cast<unsigned short *>(m_data);
+ unsigned short t = *p;
+ endswap(&t);
+ return t;
+}
+
+void Field::SetUShort(unsigned short value) {
+ Clear();
+ m_type = UnsignedShort;
+ m_dataLength = 2;
+ m_data = new byte[2];
+ unsigned short *p = reinterpret_cast<unsigned short *>(m_data);
+ *p = value;
+ endswap(p);
+}
+
+std::string Field::GetString() {
+ Field fLen;
+ byte *ptr = m_data;
+ size_t l;
+ int val = VarIntRead(ptr, l);
+ ptr += l;
+ std::string s((char *) ptr, val);
+ return s;
+}
+
+void Field::SetString(std::string value) {
+ Clear();
+ m_type = String;
+ Field fLen;
+ fLen.SetVarInt(value.size());
+ m_dataLength = value.size() + fLen.GetLength();
+ m_data = new byte[m_dataLength];
+ byte *p = m_data;
+ fLen.CopyToBuff(p);
+ p += fLen.GetLength();
+ std::copy(value.begin(),value.end(),p);
+}
+
+long long Field::GetLong() {
+ long long t = *reinterpret_cast<long long *>(m_data);
+ endswap(&t);
+ return t;
+}
+
+void Field::SetLong(long long value) {
+ Clear();
+ m_type = Long;
+ m_dataLength = 8;
+ m_data = new byte[m_dataLength];
+ long long *p = reinterpret_cast<long long *>(m_data);
+ *p = value;
+ endswap(p);
+}
+
+FieldType Field::GetType() {
+ return m_type;
+}
+
+byte Field::GetUByte() {
+ byte t = *reinterpret_cast<byte *>(m_data);
+ endswap(&t);
+ return t;
+}
+
+void Field::SetUByte(byte value) {
+ Clear();
+ m_type = UnsignedByte;
+ endswap(&value);
+ m_dataLength = 1;
+ m_data = new byte[m_dataLength];
+ byte *p = reinterpret_cast<byte *>(m_data);
+ *p = value;
+}
+
+sbyte Field::GetByte() {
+ sbyte t = *reinterpret_cast<sbyte *>(m_data);
+ endswap(&t);
+ return t;
+}
+
+void Field::SetByte(sbyte value) {
+ Clear();
+ m_type = Byte8_t;
+ endswap(&value);
+ m_dataLength = 1;
+ m_data = new byte[m_dataLength];
+ sbyte *p = reinterpret_cast<sbyte *>(m_data);
+ *p = value;
+}
+
+float Field::GetFloat() {
+ float t = *reinterpret_cast<float *>(m_data);
+ endswap(&t);
+ return t;
+}
+
+void Field::SetFloat(float value) {
+ Clear();
+ m_type = Float;
+ endswap(&value);
+ m_dataLength = 4;
+ m_data = new byte[m_dataLength];
+ float *p = reinterpret_cast<float *>(m_data);
+ *p = value;
+}
+
+PositionI Field::GetPosition() {
+ unsigned long long t = *reinterpret_cast<unsigned long long *>(m_data);
+ endswap(&t);
+ 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);
+ }
+ PositionI val;
+ val.SetX(x);
+ val.setZ(z);
+ val.SetY(y);
+ return val;
+}
+
+void Field::SetPosition(PositionI value) {
+ Clear();
+ m_type = Position;
+ m_dataLength = 8;
+ m_data = new byte[m_dataLength];
+ unsigned long long *t = reinterpret_cast<unsigned long long *>(m_data);
+ unsigned long long x = ((unsigned long long) value.GetX()) << 38;
+ unsigned long long y = ((unsigned long long) value.GetY()) << 26;
+ unsigned long long z = value.GetZ();
+ endswap(&x);
+ endswap(&z);
+ endswap(&y);
+ *t = x | y | z;
+}
+
+double Field::GetDouble() {
+ double t = *reinterpret_cast<double *>(m_data);
+ endswap(&t);
+ return t;
+}
+
+void Field::SetDouble(double value) {
+ Clear();
+ m_type = Double;
+ endswap(&value);
+ m_dataLength = 8;
+ m_data = new byte[m_dataLength];
+ double *p = reinterpret_cast<double *>(m_data);
+ *p = value;
+}
+
+size_t Field::GetFieldLength(FieldType type) {
+ switch (type) {
+ case Unknown:
+ return 0;
+ case Boolean:
+ return 1;
+ case Byte8_t:
+ return 1;
+ case UnsignedByte:
+ return 1;
+ case Short:
+ return 2;
+ case UnsignedShort:
+ return 2;
+ case Int:
+ return 4;
+ case Long:
+ return 8;
+ case Float:
+ return 4;
+ case Double:
+ return 8;
+ case Position:
+ return 8;
+ case Angle:
+ return 4;
+ case Uuid:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+std::vector<Field> Field::GetArray() {
+ /*std::vector<Field> vec;
+ if (m_type<20){
+ size_t fieldLen=GetFieldLength(m_type);
+ byte* ptr = m_data;
+ for (int i=0;i<m_dataLength/fieldLen;i++){
+ Field f;
+ f.SetRaw(ptr,fieldLen,m_type);
+ vec.push_back(f);
+ ptr+=fieldLen;
+ }
+ return vec;
+ }*/
+ return m_childs;
+}
+
+void Field::Attach(Field field) {
+ m_childs.push_back(field);
+}
diff --git a/code/Field.hpp b/code/Field.hpp
new file mode 100644
index 0000000..43769dc
--- /dev/null
+++ b/code/Field.hpp
@@ -0,0 +1,119 @@
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include "utility.h"
+#include "PositionI.hpp"
+
+typedef unsigned char byte;
+typedef signed char sbyte;
+
+enum FieldType {
+ Unknown = 0,
+ Boolean, //Bool
+ Byte8_t, //int8_t
+ UnsignedByte, //uint8_t
+ Short, //int16_t
+ UnsignedShort, //uint16_t
+ Int, //int32_t
+ Long, //int64_t
+ Float, //float
+ Double, //double
+ Position, //PositionI
+ Angle, //uint8_t
+ Uuid, //byte* (2 bytes)
+ //Unknown-length data
+
+ String = 100, //std::string
+ Chat, //std::string
+ VarInt, //int32_t
+ VarLong, //int64_t
+ ChunkSection, //byte*
+ EntityMetadata, //byte*
+ Slot, //byte*
+ NbtTag, //byte*
+ ByteArray, //byte*
+};
+
+class Field {
+public:
+ Field();
+
+ Field(const Field &other);
+
+ void swap(Field &other);
+
+ Field &operator=(Field other);
+
+ ~Field();
+
+ size_t GetLength();
+
+ void Clear();
+
+ void CopyToBuff(byte *ptr);
+
+ void SetRaw(byte *ptr, size_t len = 0, FieldType type = Unknown);
+
+ FieldType GetType();
+
+ void Attach(Field field);
+
+ static size_t GetFieldLength(FieldType type);
+
+ //Cpp-types setters/getters for binary content of MC's data types
+
+ int GetVarInt();
+
+ void SetVarInt(int value);
+
+ int GetInt();
+
+ void SetInt(int value);
+
+ bool GetBool();
+
+ void SetBool(bool value);
+
+ unsigned short GetUShort();
+
+ void SetUShort(unsigned short value);
+
+ std::string GetString();
+
+ void SetString(std::string value);
+
+ long long GetLong();
+
+ void SetLong(long long value);
+
+ byte GetUByte();
+
+ void SetUByte(byte value);
+
+ sbyte GetByte();
+
+ void SetByte(sbyte value);
+
+ float GetFloat();
+
+ void SetFloat(float value);
+
+ PositionI GetPosition();
+
+ void SetPosition(PositionI value);
+
+ double GetDouble();
+
+ void SetDouble(double value);
+
+ std::vector<Field> GetArray();
+
+private:
+ size_t m_dataLength = 0;
+ byte *m_data = nullptr;
+ FieldType m_type = Unknown;
+ std::vector<Field> m_childs;
+};
diff --git a/code/FieldParser.cpp b/code/FieldParser.cpp
new file mode 100644
index 0000000..500a973
--- /dev/null
+++ b/code/FieldParser.cpp
@@ -0,0 +1,106 @@
+#include "FieldParser.hpp"
+
+Field FieldParser::Parse(FieldType type, byte *data, size_t len) {
+ switch (type) {
+ case VarInt:
+ return ParseVarInt(data, len);
+ case Boolean:
+ return ParseBool(data, len);
+ case String:
+ return ParseString(data, len);
+ case Long:
+ return ParseLong(data, len);
+ case Int:
+ return ParseInt(data, len);
+ case UnsignedByte:
+ return ParseUByte(data, len);
+ case Byte8_t:
+ return ParseByte(data, len);
+ case Float:
+ return ParseFloat(data, len);
+ case Position:
+ return ParsePosition(data, len);
+ case Double:
+ return ParseDouble(data, len);
+ case ByteArray:
+ return ParseByteArray(data, len);
+ default:
+ throw 105;
+ }
+}
+
+Field FieldParser::ParseString(byte *data, size_t len) {
+ Field fLen = ParseVarInt(data, 0);
+ Field f;
+ f.SetRaw(data, fLen.GetLength() + fLen.GetVarInt(), String);
+ return f;
+}
+
+Field FieldParser::ParseBool(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data,1,Boolean);
+ return f;
+}
+
+Field FieldParser::ParseVarInt(byte *data, size_t len) {
+ if (len != 0) {
+ Field f;
+ f.SetRaw(data, len, VarInt);
+ return f;
+ }
+ int val = VarIntRead(data, len);
+ Field f;
+ f.SetVarInt(val);
+ return f;
+}
+
+Field FieldParser::ParseLong(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 8, Long);
+ return f;
+}
+
+Field FieldParser::ParseInt(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 4, Int);
+ return f;
+}
+
+Field FieldParser::ParseUByte(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 1, UnsignedByte);
+ return f;
+}
+
+Field FieldParser::ParseByte(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 1, Byte8_t);
+ return f;
+}
+
+Field FieldParser::ParseFloat(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 4, Float);
+ return f;
+}
+
+Field FieldParser::ParsePosition(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 8, Position);
+ return f;
+}
+
+Field FieldParser::ParseDouble(byte *data, size_t len) {
+ Field f;
+ f.SetRaw(data, 8, Double);
+ return f;
+}
+
+Field FieldParser::ParseByteArray(byte *data, size_t len) {
+ if (len == 0)
+ throw 119;
+ Field f;
+ f.SetRaw(data, len, Byte8_t);
+ //f.SetRaw(data, len, ByteArray);
+ return f;
+}
diff --git a/code/FieldParser.hpp b/code/FieldParser.hpp
new file mode 100644
index 0000000..274ab9e
--- /dev/null
+++ b/code/FieldParser.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "Field.hpp"
+
+class FieldParser {
+public:
+ static Field ParseVarInt(byte *data, size_t len);
+
+ static Field ParseBool(byte *data, size_t len);
+
+ static Field ParseString(byte *data, size_t len);
+
+ static Field Parse(FieldType type, byte* data, size_t len=0);
+
+ static Field ParseLong(byte *data, size_t len);
+
+ static Field ParseInt(byte *data, size_t len);
+
+ static Field ParseUByte(byte *data, size_t len);
+
+ static Field ParseByte(byte *data, size_t len);
+
+ static Field ParseFloat(byte *data, size_t len);
+
+ static Field ParsePosition(byte *data, size_t len);
+
+ static Field ParseDouble(byte *data, size_t len);
+
+ static Field ParseByteArray(byte *data, size_t len);
+}; \ No newline at end of file
diff --git a/code/Game.cpp b/code/Game.cpp
new file mode 100644
index 0000000..db38977
--- /dev/null
+++ b/code/Game.cpp
@@ -0,0 +1,159 @@
+#include "Game.hpp"
+#include "PacketParser.hpp"
+#include "PacketBuilder.hpp"
+#include "json.hpp"
+
+Game::Game() {
+ m_display = new Display(1280, 720, "AltCraft", &m_world);
+ m_nc = new NetworkClient("127.0.0.1", 25565, "HelloOne");
+ Packet &response = *m_nc->GetPacket();
+ if (response.GetId() != 0x02) {
+ std::cout << response.GetId() << std::endl;
+ throw 127;
+ }
+ PacketParser::Parse(response, Login);
+ g_PlayerUuid = response.GetField(0).GetString();
+ g_PlayerName = response.GetField(1).GetString();
+ delete &response;
+ m_networkState = ConnectionState::Play;
+ std::cout << g_PlayerName << "'s UUID is " << g_PlayerUuid << std::endl;
+}
+
+Game::~Game() {
+ std::cout << "Stopping game thread..." << std::endl;
+ m_exit=true;
+ m_gameThread.join();
+ std::cout << "Stopping graphics..." << std::endl;
+ delete m_display;
+ std::cout << "Stopping network..." << std::endl;
+ delete m_nc;
+}
+
+void Game::MainLoop() {
+ while (!m_exit) {
+ ParsePackets();
+ if (m_display->IsClosed())
+ m_exit = true;
+ }
+}
+
+void Game::ParsePackets() {
+ Packet *packetPtr = m_nc->GetPacket();
+ if (!packetPtr) {
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(16ms);
+ return;
+ }
+ Packet packet = *packetPtr;
+ delete packetPtr;
+ PacketParser::Parse(packet);
+ nlohmann::json json;
+
+ switch (packet.GetId()) {
+ case 0x23:
+ g_PlayerEid = packet.GetField(0).GetInt();
+ g_Gamemode = (packet.GetField(1).GetUByte() & 0b11111011);
+ g_Dimension = packet.GetField(2).GetInt();
+ g_Difficulty = packet.GetField(3).GetUByte();
+ g_MaxPlayers = packet.GetField(4).GetUByte();
+ g_LevelType = packet.GetField(5).GetString();
+ g_ReducedDebugInfo = packet.GetField(6).GetBool();
+ std::cout << "Gamemode is " << (int) g_Gamemode << ", Difficulty is " << (int) g_Difficulty
+ << ", Level Type is " << g_LevelType << std::endl;
+ break;
+ case 0x0D:
+ g_Difficulty = packet.GetField(0).GetUByte();
+ std::cout << "Difficulty now is " << (int) g_Difficulty << std::endl;
+ break;
+ case 0x43:
+ g_SpawnPosition = packet.GetField(0).GetPosition();
+ std::cout << "Spawn position is " << g_SpawnPosition.GetX() << "," << g_SpawnPosition.GetY() << ","
+ << g_SpawnPosition.GetZ() << std::endl;
+ break;
+ case 0x2B:
+ g_PlayerInvulnerable = (packet.GetField(0).GetByte() & 0x01) != 0;
+ g_PlayerFlying = (packet.GetField(0).GetByte() & 0x02) != 0;
+ g_PlayerAllowFlying = (packet.GetField(0).GetByte() & 0x04) != 0;
+ g_PlayerCreativeMode = (packet.GetField(0).GetByte() & 0x08) != 0;
+ g_PlayerFlyingSpeed = packet.GetField(1).GetFloat();
+ g_PlayerFovModifier = packet.GetField(2).GetFloat();
+ std::cout << "Fov modifier is " << g_PlayerFovModifier << std::endl;
+ break;
+ case 0x2E:
+ if ((packet.GetField(5).GetByte() & 0x10) != 0) {
+ g_PlayerPitch += packet.GetField(4).GetFloat();
+ } else {
+ g_PlayerPitch = packet.GetField(4).GetFloat();
+ };
+
+ if ((packet.GetField(5).GetByte() & 0x08) != 0) {
+ g_PlayerYaw += packet.GetField(3).GetFloat();
+ } else {
+ g_PlayerYaw = packet.GetField(3).GetFloat();
+ }
+
+ if ((packet.GetField(5).GetByte() & 0x01) != 0) {
+ g_PlayerX += packet.GetField(0).GetDouble();
+ } else {
+ g_PlayerX = packet.GetField(0).GetDouble();
+ }
+
+ if ((packet.GetField(5).GetByte() & 0x02) != 0) {
+ g_PlayerY += packet.GetField(1).GetDouble();
+ } else {
+ g_PlayerY = packet.GetField(1).GetDouble();
+ }
+
+ if ((packet.GetField(5).GetByte() & 0x04) != 0) {
+ g_PlayerZ += packet.GetField(2).GetDouble();
+ } else {
+ g_PlayerZ = packet.GetField(2).GetDouble();
+ }
+
+ g_IsGameStarted = true;
+ m_nc->AddPacketToQueue(PacketBuilder::CPlay0x03(0));
+ m_nc->AddPacketToQueue(PacketBuilder::CPlay0x00(packet.GetField(6).GetVarInt()));
+ std::cout << "Game is started! " << std::endl;
+ std::cout << "PlayerPos is " << g_PlayerX << "," << g_PlayerY << "," << g_PlayerZ << "\tAngle: " << g_PlayerYaw
+ << "," << g_PlayerPitch << std::endl;
+ m_display->SetPlayerPos(g_PlayerX, g_PlayerY,g_PlayerZ);
+ gameStartWaiter.notify_all();
+ break;
+ case 0x1A:
+ json = nlohmann::json::parse(packet.GetField(0).GetString());
+ std::cout << "Disconnect reason: " << json["text"].get<std::string>() << std::endl;
+ throw 119;
+ break;
+ case 0x20:
+ m_world.ParseChunkData(packet);
+ break;
+ case 0x07:
+ std::cout << "Statistics:" << std::endl;
+ //int items = packet.GetField(0).GetVarInt();
+ for (int i = 0; i < packet.GetField(0).GetVarInt(); i++) {
+ std::cout << "\t" << packet.GetField(1).GetArray()[0].GetString() << ": "
+ << packet.GetField(1).GetArray()[1].GetVarInt() << std::endl;
+ }
+ break;
+ default:
+ //std::cout << std::hex << packet.GetId() << std::dec << std::endl;
+ break;
+ }
+ if (g_IsGameStarted) {
+ std::chrono::steady_clock clock;
+ static auto timeOfPreviousSendedPpalPacket(clock.now());
+ std::chrono::duration<double, std::milli> delta = clock.now() - timeOfPreviousSendedPpalPacket;
+ if (delta.count() >= 50) {
+ m_nc->AddPacketToQueue(
+ PacketBuilder::CPlay0x0D(g_PlayerX, g_PlayerY, g_PlayerZ, g_PlayerYaw, g_PlayerPitch, true));
+ timeOfPreviousSendedPpalPacket = clock.now();
+ /*std::cout << "PlayerPos is " << g_PlayerX << "," << g_PlayerY << "," << g_PlayerZ << " " << g_PlayerYaw
+ << "," << g_PlayerPitch << std::endl;*/
+ }
+ }
+}
+
+void Game::Exec() {
+ m_gameThread = std::thread(&Game::MainLoop, this);
+ m_display->MainLoop();
+}
diff --git a/code/Game.hpp b/code/Game.hpp
new file mode 100644
index 0000000..b9096a5
--- /dev/null
+++ b/code/Game.hpp
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "PositionI.hpp"
+#include "NetworkClient.hpp"
+#include "World.hpp"
+#include "graphics/Display.hpp"
+
+class Game {
+public:
+ Game();
+
+ ~Game();
+
+ void Exec();
+
+private:
+ //utility variables
+ NetworkClient *m_nc;
+ std::thread m_ncThread;
+ bool m_exit = false;
+ ConnectionState m_networkState = ConnectionState::Handshaking;
+ Display *m_display;
+ std::thread m_gameThread;
+
+ //utility methods
+ void ParsePackets();
+ void MainLoop();
+
+ //GameState update - condVars
+ std::condition_variable gameStartWaiter;
+
+ //game state variables
+ World m_world;
+
+ std::string g_PlayerUuid;
+ std::string g_PlayerName;
+ int g_PlayerEid;
+ byte g_Gamemode;
+ byte g_Difficulty;
+ int g_Dimension;
+ byte g_MaxPlayers;
+ std::string g_LevelType;
+ bool g_ReducedDebugInfo;
+ PositionI g_SpawnPosition;
+ bool g_PlayerInvulnerable;
+ bool g_PlayerFlying;
+ bool g_PlayerAllowFlying;
+ bool g_PlayerCreativeMode;
+ int g_PlayerFlyingSpeed;
+ int g_PlayerFovModifier;
+ bool g_IsGameStarted = false;
+ float g_PlayerPitch;
+ float g_PlayerYaw;
+ double g_PlayerX;
+ double g_PlayerY;
+ double g_PlayerZ;
+};
+
diff --git a/code/Nbt.hpp b/code/Nbt.hpp
new file mode 100644
index 0000000..3e43db7
--- /dev/null
+++ b/code/Nbt.hpp
@@ -0,0 +1,516 @@
+#pragma once
+
+#include <cstddef>
+#include <vector>
+#include <iostream>
+#include <zlib.h>
+#include <fstream>
+#include "utility.h"
+
+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/code/Network.cpp b/code/Network.cpp
new file mode 100644
index 0000000..d979037
--- /dev/null
+++ b/code/Network.cpp
@@ -0,0 +1,108 @@
+#include <iostream>
+#include "Network.hpp"
+#include "PacketBuilder.hpp"
+
+Network::Network(std::string address, unsigned short port) : m_address(address), m_port(port) {
+ std::cout << "Connecting to server " << m_address << ":" << m_port << std::endl;
+ sf::Socket::Status status = m_socket.connect(sf::IpAddress(m_address), m_port);
+ m_socket.setBlocking(true);
+ if (status != sf::Socket::Done) {
+ if (status == sf::Socket::Error) {
+ std::cerr << "Can't connect to remote server" << std::endl;
+ throw 14;
+ } else {
+ std::cerr << "Connection failed with unknown reason" << std::endl;
+ throw 13;
+ }
+ }
+ std::cout << "Connected." << std::endl;
+}
+
+Network::~Network() {
+ std::cout << "Disconnecting..." << std::endl;
+ m_socket.disconnect();
+}
+
+void Network::SendHandshake(std::string username) {
+ //Handshake packet
+ Packet handshakePacket = PacketBuilder::CHandshaking0x00(316, m_address, m_port, 2);
+ SendPacket(handshakePacket);
+
+ //LoginStart packet
+ Field fName;
+ fName.SetString(username);
+ Packet loginPacket(0);
+ loginPacket.AddField(fName);
+ SendPacket(loginPacket);
+}
+
+void Network::SendPacket(Packet &packet) {
+ m_socket.setBlocking(true);
+ byte *packetData = new byte[packet.GetLength()];
+ packet.CopyToBuff(packetData);
+ m_socket.send(packetData, packet.GetLength());
+ delete[] packetData;
+}
+
+Packet Network::ReceivePacket() {
+ byte bufLen[5] = {0};
+ size_t rec = 0;
+ for (int i = 0; i < 5; i++) {
+ byte buff = 0;
+ size_t r = 0;
+ m_socket.receive(&buff, 1, r);
+ rec += r;
+ bufLen[i] = buff;
+ if ((buff & 0b10000000) == 0) {
+ break;
+ }
+ }
+ Field fLen = FieldParser::Parse(VarInt, bufLen);
+ size_t packetLen = fLen.GetVarInt() + fLen.GetLength();
+ if (packetLen > 1024 * 1024 * 30)
+ std::cout << "OMG! SIZEOF PACKET IS " << packetLen << std::endl;
+ if (packetLen < rec) {
+ return Packet(bufLen);
+ }
+ byte *bufPack = new byte[packetLen];
+ std::copy(bufLen, bufLen + rec, bufPack);
+ size_t dataLen = rec;
+ while (m_socket.receive(bufPack + dataLen, packetLen - dataLen, rec) == sf::Socket::Done && dataLen < packetLen) {
+ dataLen += rec;
+ }
+ if (dataLen < packetLen)
+ throw 93;
+ else {
+ Packet p(bufPack);
+ delete[] bufPack;
+ return p;
+ }
+
+ /*if (m_socket.receive(bufPack + rec, packetLen - rec, rec) != sf::Socket::Done) {
+ delete[] bufPack;
+ throw 93;
+ }
+ rec++;
+ //Check for losted data
+ int losted = 0;
+ for (int i = packetLen - 2; i > 0; i--)
+ if (bufPack[i] == 'N')
+ losted++;
+ if (losted > 100) {
+ if (m_socket.receive(bufPack + rec, packetLen - rec, rec) != sf::Socket::Done) {
+ throw 93;
+ }
+ std::cout << "Keep receiving!" << std::endl;
+ }
+ //One more time
+ losted = 0;
+ for (int i = packetLen - 2; i > 0; i--)
+ if (bufPack[i] == 'N')
+ losted++;
+ if (losted > 100) {
+ std::cout << "\x1b[31m" << "Losted " << losted << " bytes of " << packetLen << "\x1b[0m" << std::endl;
+ delete[] bufPack;
+ throw 93;
+ }*/
+ throw 94;
+}
diff --git a/code/Network.hpp b/code/Network.hpp
new file mode 100644
index 0000000..74df92c
--- /dev/null
+++ b/code/Network.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <string>
+#include <SFML/Network.hpp>
+#include "Packet.hpp"
+
+
+class Network {
+public:
+ Network(std::string address, unsigned short port);
+
+ ~Network();
+
+ void SendHandshake(std::string username);
+
+ void SendPacket(Packet &packet);
+
+ Packet ReceivePacket();
+
+private:
+ std::string m_address;
+ unsigned short m_port;
+ sf::TcpSocket m_socket;
+ bool m_isCommpress=false;
+};
+
diff --git a/code/NetworkClient.cpp b/code/NetworkClient.cpp
new file mode 100644
index 0000000..d6835e0
--- /dev/null
+++ b/code/NetworkClient.cpp
@@ -0,0 +1,107 @@
+#include "NetworkClient.hpp"
+#include "PacketParser.hpp"
+#include "PacketBuilder.hpp"
+#include "json.hpp"
+
+ServerInfo NetworkClient::ServerPing(std::string address, unsigned short port) {
+ ServerInfo info;
+ Network network(address, port);
+ Packet packet_handshake = PacketBuilder::CHandshaking0x00(316, address, port, 1);
+ network.SendPacket(packet_handshake);
+ Packet packet_request(0);
+ network.SendPacket(packet_request);
+ Packet packet_response = network.ReceivePacket();
+ PacketParser::Parse(packet_response, Login);
+ //std::string json = static_cast<FieldString *>(packet_response_parsed.GetFieldById(0))->GetValue();
+ std::string json = packet_response.GetField(0).GetString();
+ try {
+ nlohmann::json j = nlohmann::json::parse(json);
+ info.protocol = j["version"]["protocol"].get<int>();
+ info.version = j["version"]["name"].get<std::string>();
+ info.players_max = j["players"]["max"].get<int>();
+ info.players_online = j["players"]["online"].get<int>();
+ info.description = j["description"]["text"].get<std::string>();
+ for (auto t:j["description"]["extra"]) {
+ info.description += t["text"].get<std::string>();
+ }
+ if (!j["favicon"].is_null())
+ info.favicon = j["favicon"].get<std::string>();
+ info.json = json;
+ for (auto t:j["players"]["sample"]) {
+ std::pair<std::string, std::string> player;
+ player.first = t["id"].get<std::string>();
+ player.second = t["name"].get<std::string>();
+ info.players.push_back(player);
+ }
+ } catch (const nlohmann::detail::exception e) {
+ std::cerr << "Parsed json is not valid (" << e.id << "): " << e.what() << std::endl;
+ }
+ //Ping
+ Packet packet_ping(0x01);
+ Field payload;
+ payload.SetLong(771235);
+ packet_ping.AddField(payload);
+ std::chrono::high_resolution_clock clock;
+ auto t1 = clock.now();
+ network.SendPacket(packet_ping);
+ Packet pong = network.ReceivePacket();
+ auto t2 = clock.now();
+ pong.ParseField(Long);
+ if (pong.GetField(0).GetLong() == 771235) {
+ std::chrono::duration<double, std::milli> pingTime = t2 - t1;
+ info.ping = pingTime.count();
+ }
+ return info;
+}
+
+NetworkClient::NetworkClient(std::string address, unsigned short port, std::string username) : m_network(address,
+ port) {
+ m_network.SendHandshake(username);
+ Update();
+ m_networkThread = std::thread(&NetworkClient::MainLoop, this);
+}
+
+NetworkClient::~NetworkClient() {
+ isContinue=false;
+ m_networkThread.join();
+}
+
+Packet * NetworkClient::GetPacket() {
+ if (m_received.size() < 1)
+ return nullptr;
+ Packet packet = m_received.front();
+ m_received.pop();
+ return new Packet(packet);
+}
+
+void NetworkClient::AddPacketToQueue(Packet packet) {
+ m_toSend.push(packet);
+}
+
+void NetworkClient::Update() {
+ if (m_toSend.size() > 0) {
+ m_network.SendPacket(m_toSend.front());
+ m_toSend.pop();
+ }
+ Packet received = m_network.ReceivePacket();
+ if (received.GetId() == 0x1F) {
+ PacketParser::Parse(received);
+ Packet response = PacketBuilder::CPlay0x0B(received.GetField(0).GetVarInt());
+ m_network.SendPacket(response);
+ return;
+ }
+ m_updateMutex.lock();
+ m_received.push(received);
+ m_updateMutex.unlock();
+}
+
+void NetworkClient::MainLoop() {
+ try {
+ while (isContinue) {
+ Update();
+ }
+ } catch (int e){
+ std::cerr<<"NetworkClient exception: "<<e<<std::endl;
+ }
+
+}
diff --git a/code/NetworkClient.hpp b/code/NetworkClient.hpp
new file mode 100644
index 0000000..a41b5f4
--- /dev/null
+++ b/code/NetworkClient.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <queue>
+#include <thread>
+#include <mutex>
+#include "Network.hpp"
+
+struct ServerInfo{
+ std::string version;
+ int protocol = 0;
+ int players_max = 0;
+ int players_online = 0;
+ std::vector<std::pair<std::string, std::string>> players;
+ std::string description;
+ double ping = 0;
+ std::string favicon;
+ std::string json;
+};
+class NetworkClient {
+public:
+ NetworkClient(std::string address, unsigned short port, std::string username);
+ ~NetworkClient();
+
+ void Update();
+
+ void MainLoop();
+
+ Packet * GetPacket();
+ void AddPacketToQueue(Packet packet);
+
+ static ServerInfo ServerPing(std::string address,unsigned short port);
+private:
+ std::mutex m_updateMutex;
+ std::thread m_networkThread;
+ bool isContinue=true;
+ NetworkClient (const NetworkClient&);
+ NetworkClient&operator=(const NetworkClient&);
+ Network m_network;
+ std::queue <Packet> m_received;
+ std::queue <Packet> m_toSend;
+};
+
diff --git a/code/Packet.cpp b/code/Packet.cpp
new file mode 100644
index 0000000..695e371
--- /dev/null
+++ b/code/Packet.cpp
@@ -0,0 +1,100 @@
+#include "Packet.hpp"
+
+Packet::Packet(int id) {
+ Field fLen;
+ fLen.SetVarInt(0);
+ m_fields.push_back(fLen);
+ Field fId;
+ fId.SetVarInt(id);
+ m_fields.push_back(fId);
+}
+
+Packet Packet::operator=(Packet other) {
+ other.swap(*this);
+ return *this;
+}
+
+void Packet::swap(Packet &other) {
+ std::swap(m_fields, other.m_fields);
+ std::swap(m_data, other.m_data);
+ std::swap(m_parsePtr, other.m_parsePtr);
+ std::swap(m_dataLength, other.m_dataLength);
+}
+
+void Packet::CopyToBuff(byte *ptr) {
+ m_fields[0].SetVarInt(GetLength() - m_fields[0].GetLength());
+ for (int i = 0; i < m_fields.size(); i++) {
+ m_fields[i].CopyToBuff(ptr);
+ ptr += m_fields[i].GetLength();
+ }
+}
+
+void Packet::ParseField(FieldType type, size_t len) {
+ if (type == ByteArray && len == 0)
+ throw 118;
+ Field f = FieldParser::Parse(type, m_parsePtr, len);
+ m_fields.push_back(f);
+ m_parsePtr += f.GetLength();
+ if (m_parsePtr == m_data + m_dataLength) {
+ delete[] m_data;
+ m_data = nullptr;
+ m_dataLength = 0;
+ m_parsePtr = nullptr;
+ }
+}
+
+Packet::Packet(byte *data) {
+ Field fLen = FieldParser::Parse(VarInt, data);
+ data += fLen.GetLength();
+ Field fId = FieldParser::Parse(VarInt, data);
+ data += fId.GetLength();
+ m_dataLength = fLen.GetVarInt() - fId.GetLength();
+ m_data = new byte[m_dataLength];
+ std::copy(data,data+m_dataLength,m_data);
+ m_parsePtr = m_data;
+ m_fields.push_back(fLen);
+ m_fields.push_back(fId);
+}
+
+Field &Packet::GetField(int id) {
+ if (id < -2 || id >= m_fields.size() - 2)
+ throw 111;
+ return m_fields[id + 2];
+}
+
+size_t Packet::GetLength() {
+ size_t len = 0;
+ for (int i = 0; i < m_fields.size(); i++)
+ len += m_fields[i].GetLength();
+ return len + m_dataLength;
+}
+
+void Packet::AddField(Field field) {
+ m_fields.push_back(field);
+}
+
+int Packet::GetId() {
+ return m_fields[1].GetVarInt();
+}
+
+Packet::Packet(const Packet &other) {
+ if (other.m_dataLength > 0) {
+ m_dataLength = other.m_dataLength;
+ m_data = new byte[m_dataLength];
+ m_parsePtr = m_data + (other.m_data - other.m_parsePtr);
+ std::copy(other.m_data, other.m_data + m_dataLength, m_data);
+ }
+ m_fields = other.m_fields;
+}
+
+void Packet::ParseFieldArray(Field &field, FieldType type, size_t len) {
+ Field f = FieldParser::Parse(type, m_parsePtr, len);
+ field.Attach(f);
+ m_parsePtr += f.GetLength();
+ if (m_parsePtr == m_data + m_dataLength) {
+ delete[] m_data;
+ m_data = nullptr;
+ m_dataLength = 0;
+ m_parsePtr = nullptr;
+ }
+}
diff --git a/code/Packet.hpp b/code/Packet.hpp
new file mode 100644
index 0000000..67e95e5
--- /dev/null
+++ b/code/Packet.hpp
@@ -0,0 +1,130 @@
+#pragma once
+
+#include <vector>
+#include "Field.hpp"
+#include "FieldParser.hpp"
+
+enum ConnectionState {
+ Login,
+ Handshaking,
+ Play,
+ Status,
+};
+
+enum PacketsClientBound{
+ SpawnObject=0x00,
+ SpawnExperienceOrb,
+ SpawnGlobalEntity,
+ SpawnMob,
+ SpawnPainting,
+ SpawnPlayer,
+ Animation,
+ Statistics,
+ BlockBreakAnimation,
+ UpdateBlockEntity,
+ BlockAction,
+ BlockChange,
+ BossBar,
+ ServerDifficulty,
+ Tab,
+ ChatMessage,
+ MultiBlockChange,
+ ConfirmTransaction,
+ CloseWindow,
+ OpenWindow,
+ WindowItems,
+ WindowProperty,
+ SetSlot,
+ SetCooldown,
+ PluginMessage,
+ NamedSoundEffect,
+ Disconnect,
+ EntityStatus,
+ Explosion,
+ UnloadChunk,
+ ChangeGameState,
+ KeepAlive,
+ ChunkData,
+ Effect,
+ Particle,
+ JoinGame,
+ Map,
+ EntityRelativeMove,
+ EntityLookAndRelativeMove,
+ EntityLook,
+ Entity,
+ VehicleMove,
+ OpenSignEditor,
+ PlayerAbilities,
+ CombatEvent,
+ PlayerListItem,
+ PlayerPositionAndLook,
+ UseBed,
+ DestroyEntities,
+ RemoveEntityEffect,
+ ResourcePackSend,
+ Respawn,
+ EntityHeadLook,
+ WorldBorder,
+ Camera,
+ HeldItemChange,
+ DisplayScoreboard,
+ EntityMetadata_,
+ AttachEntity,
+ EntityVelocity,
+ EntityEquipment,
+ SetExperience,
+ UpdateHealth,
+ ScoreboardObjective,
+ SetPassengers,
+ Teams,
+ UpdateScore,
+ SpawnPosition,
+ TimeUpdate,
+ Title,
+ SoundEffect,
+ PlayerListHeaderAndFooter,
+ CollectItem,
+ EntityTeleport,
+ EntityProperties,
+ EntityEffect,
+};
+
+class Packet {
+public:
+ Packet(int id);
+
+ Packet(byte *data);
+
+ Packet(const Packet &other);
+
+ ~Packet() {
+ delete[] m_data;
+ }
+
+ int GetId();
+
+ void AddField(Field field);
+
+ void ParseField(FieldType type, size_t len = 0);
+
+ void ParseFieldArray(Field &field, FieldType type, size_t len);
+
+ Field & GetField(int id);
+
+ size_t GetLength();
+
+ void CopyToBuff(byte *ptr);
+
+ void swap(Packet &other);
+
+ Packet operator=(Packet other);
+
+private:
+ Packet();
+
+ std::vector<Field> m_fields;
+ byte *m_data = nullptr;
+ byte *m_parsePtr = nullptr;
+ size_t m_dataLength = 0;
+}; \ No newline at end of file
diff --git a/code/PacketBuilder.cpp b/code/PacketBuilder.cpp
new file mode 100644
index 0000000..4083ea3
--- /dev/null
+++ b/code/PacketBuilder.cpp
@@ -0,0 +1,65 @@
+#include "PacketBuilder.hpp"
+
+Packet PacketBuilder::CHandshaking0x00(int protocolVerison, std::string address, unsigned short port, int nextState) {
+ Packet handshakePacket(0);
+ Field fProtocol;
+ fProtocol.SetVarInt(protocolVerison);
+ Field fAddress;
+ fAddress.SetString(address);
+ Field fPort;
+ fPort.SetUShort(port);
+ Field fNextState;
+ fNextState.SetVarInt(nextState);
+ handshakePacket.AddField(fProtocol);
+ handshakePacket.AddField(fAddress);
+ handshakePacket.AddField(fPort);
+ handshakePacket.AddField(fNextState);
+ return handshakePacket;
+}
+
+Packet PacketBuilder::CPlay0x0B(int keepAliveId) {
+ Packet keepAlivePacket(0x0B);
+ Field fKeepAlive;
+ fKeepAlive.SetVarInt(keepAliveId);
+ keepAlivePacket.AddField(fKeepAlive);
+ return keepAlivePacket;
+}
+
+Packet PacketBuilder::CPlay0x03(int actionId) {
+ Packet clientStatusPacket(0x03);
+ Field fActionId;
+ fActionId.SetVarInt(actionId);
+ clientStatusPacket.AddField(fActionId);
+ return clientStatusPacket;
+}
+
+Packet PacketBuilder::CPlay0x00(int teleportId) {
+ Packet teleportConfirmPacket(0x00);
+ Field fTeleportId;
+ fTeleportId.SetVarInt(teleportId);
+ teleportConfirmPacket.AddField(fTeleportId);
+ return teleportConfirmPacket;
+}
+
+Packet PacketBuilder::CPlay0x0D(double x, double y, double z, float yaw, float pitch, bool onGround) {
+ Packet playerPositionAndLookPacket(0x0D);
+ Field fX;
+ Field fY;
+ Field fZ;
+ Field fYaw;
+ Field fPitch;
+ Field fOnGround;
+ fX.SetDouble(x);
+ fY.SetDouble(y);
+ fZ.SetDouble(z);
+ fYaw.SetFloat(yaw);
+ fPitch.SetFloat(pitch);
+ fOnGround.SetBool(onGround);
+ playerPositionAndLookPacket.AddField(fX);
+ playerPositionAndLookPacket.AddField(fY);
+ playerPositionAndLookPacket.AddField(fZ);
+ playerPositionAndLookPacket.AddField(fYaw);
+ playerPositionAndLookPacket.AddField(fPitch);
+ playerPositionAndLookPacket.AddField(fOnGround);
+ return playerPositionAndLookPacket;
+}
diff --git a/code/PacketBuilder.hpp b/code/PacketBuilder.hpp
new file mode 100644
index 0000000..2fcb737
--- /dev/null
+++ b/code/PacketBuilder.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+
+#include "Packet.hpp"
+
+class PacketBuilder {
+public:
+ static Packet CHandshaking0x00(int protocolVerison, std::string address, unsigned short port, int nextState);
+ static Packet CPlay0x0B(int keepAliveId);
+
+ static Packet CPlay0x03(int actionId);
+
+ static Packet CPlay0x00(int teleportId);
+
+ static Packet CPlay0x0D(double x, double y, double z, float yaw, float pitch, bool onGround);
+};
+
diff --git a/code/PacketParser.cpp b/code/PacketParser.cpp
new file mode 100644
index 0000000..488c812
--- /dev/null
+++ b/code/PacketParser.cpp
@@ -0,0 +1,147 @@
+#include "PacketParser.hpp"
+
+void PacketParser::Parse(Packet &packet, ConnectionState state, bool ClientBound) {
+ if (ClientBound) {
+ switch (state) {
+ case Login:
+ ParseLogin(packet);
+ break;
+ case Handshaking:
+ break;
+ case Play:
+ ParsePlay(packet);
+ break;
+ case Status:
+
+ break;
+ }
+ } else {
+ ParseServerBound(packet, state);
+ }
+}
+
+void PacketParser::ParseServerBound(Packet &packet, ConnectionState state) {
+ throw 107;
+}
+
+void PacketParser::ParseLogin(Packet &packet) {
+ switch (packet.GetId()) {
+ case 0x00:
+ ParseLogin0x00(packet);
+ break;
+ case 0x02:
+ ParseLogin0x02(packet);
+ break;
+ default:
+ {
+ int i = packet.GetId();
+ //throw 112;
+ }
+ }
+}
+
+void PacketParser::ParsePlay(Packet &packet) {
+ switch (packet.GetId()) {
+ case 0x23:
+ ParsePlay0x23(packet);
+ break;
+ case 0x1F:
+ ParsePlay0x1F(packet);
+ break;
+ case 0x0D:
+ ParsePlay0x0D(packet);
+ break;
+ case 0x2B:
+ ParsePlay0x2B(packet);
+ break;
+ case 0x43:
+ ParsePlay0x43(packet);
+ break;
+ case 0x2E:
+ ParsePlay0x2E(packet);
+ break;
+ case 0x1A:
+ ParsePlay0x1A(packet);
+ break;
+ case 0x20:
+ ParsePlay0x20(packet);
+ break;
+ case 0x07:
+ ParsePlay0x07(packet);
+ default:
+ //throw 113;
+ break;
+ }
+}
+
+void PacketParser::ParseLogin0x00(Packet &packet) {
+ packet.ParseField(String);
+}
+
+void PacketParser::ParseLogin0x02(Packet &packet) {
+ packet.ParseField(String);
+ packet.ParseField(String);
+}
+
+void PacketParser::ParsePlay0x23(Packet &packet) {
+ packet.ParseField(Int);
+ packet.ParseField(UnsignedByte);
+ packet.ParseField(Int);
+ packet.ParseField(UnsignedByte);
+ packet.ParseField(UnsignedByte);
+ packet.ParseField(String);
+ packet.ParseField(Boolean);
+}
+
+void PacketParser::ParsePlay0x1F(Packet &packet) {
+ packet.ParseField(VarInt);
+}
+
+void PacketParser::ParsePlay0x0D(Packet &packet) {
+ packet.ParseField(UnsignedByte);
+}
+
+void PacketParser::ParsePlay0x2B(Packet &packet) {
+ packet.ParseField(Byte8_t);
+ packet.ParseField(Float);
+ packet.ParseField(Float);
+}
+
+void PacketParser::ParsePlay0x43(Packet &packet) {
+ packet.ParseField(Position);
+}
+
+void PacketParser::ParsePlay0x2E(Packet &packet) {
+ packet.ParseField(Double);
+ packet.ParseField(Double);
+ packet.ParseField(Double);
+ packet.ParseField(Float);
+ packet.ParseField(Float);
+ packet.ParseField(Byte8_t);
+ packet.ParseField(VarInt);
+}
+
+void PacketParser::ParsePlay0x1A(Packet &packet) {
+ packet.ParseField(String);
+}
+
+void PacketParser::ParsePlay0x20(Packet &packet) {
+ packet.ParseField(Int);
+ packet.ParseField(Int);
+ packet.ParseField(Boolean);
+ packet.ParseField(VarInt);
+ packet.ParseField(VarInt);
+ packet.ParseField(ByteArray, packet.GetField(4).GetVarInt());
+ packet.ParseField(VarInt);
+ //packet.ParseField(NbtTag);
+ //packet.GetField(7).SetArray(packet.GetField(6).GetVarInt());
+}
+
+void PacketParser::ParsePlay0x07(Packet &packet) {
+ packet.ParseField(VarInt);
+ packet.AddField(Field());
+ for (int i=0;i<packet.GetField(0).GetVarInt();i++){
+ packet.ParseFieldArray(packet.GetField(1), String, 0);
+ packet.ParseFieldArray(packet.GetField(1), VarInt, 0);
+ }
+}
diff --git a/code/PacketParser.hpp b/code/PacketParser.hpp
new file mode 100644
index 0000000..8ca6195
--- /dev/null
+++ b/code/PacketParser.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+
+#include "Packet.hpp"
+
+class PacketParser {
+public:
+ static void Parse(Packet &packet, ConnectionState state = Play, bool ClientBound = true);
+
+ static void ParseServerBound(Packet &packet, ConnectionState state);
+
+ static void ParseLogin(Packet &packet);
+
+ static void ParsePlay(Packet &packet);
+
+ static void ParseLogin0x00(Packet &packet);
+
+ static void ParseLogin0x02(Packet &packet);
+
+ static void ParsePlay0x23(Packet &packet);
+
+ static void ParsePlay0x1F(Packet &packet);
+
+ static void ParsePlay0x0D(Packet &packet);
+
+ static void ParsePlay0x2B(Packet &packet);
+
+ static void ParsePlay0x43(Packet &packet);
+
+ static void ParsePlay0x2E(Packet &packet);
+
+ static void ParsePlay0x1A(Packet &packet);
+
+ static void ParsePlay0x20(Packet &packet);
+
+ static void ParsePlay0x07(Packet &packet);
+};
+
diff --git a/code/PositionF.cpp b/code/PositionF.cpp
new file mode 100644
index 0000000..8502b91
--- /dev/null
+++ b/code/PositionF.cpp
@@ -0,0 +1,50 @@
+#include "PositionF.hpp"
+
+PositionF::PositionF(double x, double z, double y) : m_x(x), m_y(y), m_z(z) {
+
+}
+
+PositionF::~PositionF() {
+
+}
+
+double PositionF::GetX() const {
+ return m_x;
+}
+
+double PositionF::GetY() const {
+ return m_y;
+}
+
+double PositionF::GetZ() const {
+ return m_z;
+}
+
+void PositionF::SetX(double x) {
+ m_x = x;
+}
+
+void PositionF::SetY(double y) {
+ m_y = y;
+}
+
+void PositionF::setZ(double z) {
+ m_z = z;
+}
+
+bool PositionF::operator==(const PositionF &other) const {
+ return other.m_x == m_x && other.m_z == m_z && other.m_y == other.m_y;
+}
+
+PositionF &PositionF::operator=(const PositionF &other) {
+ m_y = other.m_y;
+ m_z = other.m_z;
+ m_x = other.m_x;
+ return *this;
+}
+
+PositionF::PositionF(const PositionF &other) {
+ m_y = other.m_y;
+ m_z = other.m_z;
+ m_x = other.m_x;
+}
diff --git a/code/PositionF.hpp b/code/PositionF.hpp
new file mode 100644
index 0000000..b90a2a1
--- /dev/null
+++ b/code/PositionF.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+class PositionF {
+public:
+ PositionF(double x, double z, double y);
+
+ ~PositionF();
+
+ double GetX() const;
+
+ double GetY() const;
+
+ double GetZ() const;
+
+ void SetX(double x);
+
+ void SetY(double y);
+
+ void setZ(double z);
+
+ bool operator==(const PositionF &other) const;
+
+ PositionF &operator=(const PositionF &other);
+
+ PositionF(const PositionF &other);
+
+private:
+ double m_x, m_y, m_z;
+}; \ No newline at end of file
diff --git a/code/PositionI.cpp b/code/PositionI.cpp
new file mode 100644
index 0000000..7de5dc0
--- /dev/null
+++ b/code/PositionI.cpp
@@ -0,0 +1,120 @@
+#include <cmath>
+#include "PositionI.hpp"
+
+PositionI::PositionI(int x, int z, int y) : m_x(x), m_y(y), m_z(z) {
+
+}
+
+PositionI::~PositionI() {
+
+}
+
+int PositionI::GetX() const {
+ return m_x;
+}
+
+int PositionI::GetY() const {
+ return m_y;
+}
+
+int PositionI::GetZ() const {
+ return m_z;
+}
+
+void PositionI::SetX(int x) {
+ m_x = x;
+}
+
+void PositionI::SetY(int y) {
+ m_y = y;
+}
+
+void PositionI::setZ(int z) {
+ m_z = z;
+}
+
+bool PositionI::operator==(const PositionI &other) const {
+ return other.m_x == m_x && other.m_z == m_z && other.m_y == other.m_y;
+}
+
+PositionI &PositionI::operator=(const PositionI &other) {
+ m_y = other.m_y;
+ m_z = other.m_z;
+ m_x = other.m_x;
+ return *this;
+}
+
+PositionI::PositionI(const PositionI &other) {
+ m_y = other.m_y;
+ m_z = other.m_z;
+ m_x = other.m_x;
+}
+
+PositionI::PositionI() : m_x(0), m_y(0), m_z(0) {
+
+}
+
+bool PositionI::operator<(const PositionI &rhs) const {
+ if (m_x < rhs.m_x)
+ return true;
+ if (rhs.m_x < m_x)
+ return false;
+ if (m_y < rhs.m_y)
+ return true;
+ if (rhs.m_y < m_y)
+ return false;
+ return m_z < rhs.m_z;
+}
+
+bool PositionI::operator>(const PositionI &rhs) const {
+ return rhs < *this;
+}
+
+bool PositionI::operator<=(const PositionI &rhs) const {
+ return !(rhs < *this);
+}
+
+bool PositionI::operator>=(const PositionI &rhs) const {
+ return !(*this < rhs);
+}
+
+PositionI PositionI::operator-(const PositionI &other) const {
+ return PositionI(
+ m_x - other.m_x,
+ m_z - other.m_z,
+ m_y - other.m_y
+ );
+}
+
+double PositionI::GetDistance() {
+ return (std::sqrt(std::pow(m_x, 2) + std::pow(m_y, 2) + std::pow(m_z, 2)));
+}
+
+PositionI PositionI::operator*(int other) const {
+ return PositionI(
+ m_x * other,
+ m_z * other,
+ m_y * other
+ );
+}
+
+PositionI PositionI::operator*(const PositionI &other) const {
+ return PositionI(
+ m_x * other.m_x,
+ m_z * other.m_z,
+ m_y * other.m_y
+ );
+}
+
+PositionI PositionI::operator/(int other) const {
+ return PositionI(
+ m_x / other,
+ m_z / other,
+ m_y / other
+ );
+}
+
+std::ostream &operator<<(std::ostream &os, const PositionI &i) {
+ os << "(" << i.m_x << ", " << i.m_y << ", " << i.m_z << ")";
+ return os;
+}
diff --git a/code/PositionI.hpp b/code/PositionI.hpp
new file mode 100644
index 0000000..25455ef
--- /dev/null
+++ b/code/PositionI.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <ostream>
+
+class PositionI {
+public:
+ PositionI(int x, int z, int y);
+
+ PositionI();
+
+ ~PositionI();
+
+ int GetX() const;
+
+ int GetY() const;
+
+ int GetZ() const;
+
+ void SetX(int x);
+
+ void SetY(int y);
+
+ void setZ(int z);
+
+ double GetDistance();
+
+ bool operator==(const PositionI &other) const;
+
+ PositionI &operator=(const PositionI &other);
+
+ PositionI(const PositionI &other);
+
+ bool operator<(const PositionI &rhs) const;
+
+ bool operator>(const PositionI &rhs) const;
+
+ bool operator<=(const PositionI &rhs) const;
+
+ bool operator>=(const PositionI &rhs) const;
+
+ PositionI operator-(const PositionI &other)const;
+ PositionI operator*(int other)const;
+ PositionI operator*(const PositionI &other)const;
+ PositionI operator/(int other)const;
+
+ friend std::ostream &operator<<(std::ostream &os, const PositionI &i);
+
+private:
+ int m_x, m_y, m_z;
+}; \ No newline at end of file
diff --git a/code/Section.cpp b/code/Section.cpp
new file mode 100644
index 0000000..8df5953
--- /dev/null
+++ b/code/Section.cpp
@@ -0,0 +1,132 @@
+#include <iostream>
+#include "Section.hpp"
+
+Section::Section(byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, byte bitsPerBlock,
+ std::vector<unsigned short> palette) {
+ 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() {
+}
+
+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(PositionI pos) {
+ if (m_dataBlocks != nullptr) {
+ std::mutex parseMutex;
+ std::unique_lock<std::mutex> parseLocker(parseMutex);
+ parseWaiter.wait(parseLocker);
+ while (m_dataBlocks != nullptr) {
+ parseWaiter.wait(parseLocker);
+ }
+ }
+ return m_blocks[pos.GetY() * 256 + pos.GetZ() * 16 + pos.GetX()];
+}
+
+void Section::Parse() {
+ if (m_dataBlocks == nullptr)
+ return;
+
+ long long *longArray = reinterpret_cast<long long *>(m_dataBlocks);
+ for (int i = 0; i < m_dataBlocksLen / 8; i++)
+ endswap(&longArray[i]);
+ std::vector<unsigned short> blocks;
+ blocks.reserve(4096);
+ int bitPos = 0;
+ unsigned short t = 0;
+ for (int 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;
+ }
+ }
+ }
+
+ std::vector<byte> 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, light[i]);
+ m_blocks.push_back(block);
+ }
+ if ((light.size() + blocks.size()) / 2 != 4096) {
+ throw 118;
+ }
+ 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) {
+ other.swap(*this);
+ return *this;
+}
+
+void Section::swap(Section &other) {
+ std::swap(other.m_dataBlocksLen, m_dataBlocksLen);
+ std::swap(other.m_dataBlocks, m_dataBlocks);
+ std::swap(other.m_dataLight, m_dataLight);
+ std::swap(other.m_dataSkyLight, m_dataSkyLight);
+ std::swap(other.m_blocks, m_blocks);
+ std::swap(other.m_palette, m_palette);
+ std::swap(other.m_bitsPerBlock, m_bitsPerBlock);
+}
+
+Section::Section(const Section &other) {
+ m_dataBlocksLen = other.m_dataBlocksLen;
+ 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);
+ }
+
+ m_palette = other.m_palette;
+ m_bitsPerBlock = other.m_bitsPerBlock;
+}
+
+
diff --git a/code/Section.hpp b/code/Section.hpp
new file mode 100644
index 0000000..8e1a0d1
--- /dev/null
+++ b/code/Section.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <vector>
+#include <map>
+#include <condition_variable>
+#include "Block.hpp"
+#include "Field.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;
+public:
+ void Parse();
+
+ Section(byte *dataBlocks, size_t dataBlocksLength, byte *dataLight, byte *dataSky, byte bitsPerBlock,
+ std::vector<unsigned short> palette);
+
+ Section();
+
+ ~Section();
+
+ Block &GetBlock(PositionI pos);
+
+ Section &operator=(Section other);
+
+ void swap(Section &other);
+
+ Section(const Section &other);
+
+}; \ No newline at end of file
diff --git a/code/World.cpp b/code/World.cpp
new file mode 100644
index 0000000..c19f88c
--- /dev/null
+++ b/code/World.cpp
@@ -0,0 +1,107 @@
+#include <iostream>
+#include <bitset>
+#include "World.hpp"
+
+void World::ParseChunkData(Packet packet) {
+ int chunkX = packet.GetField(0).GetInt();
+ int chunkZ = packet.GetField(1).GetInt();
+ bool isGroundContinuous = packet.GetField(2).GetBool();
+ std::bitset<16> bitmask(packet.GetField(3).GetVarInt());
+ int entities = packet.GetField(5).GetVarInt();
+
+ size_t dataLen = packet.GetField(5).GetLength();
+ byte *content = new byte[dataLen];
+ byte *contentOrigPtr = content;
+ packet.GetField(5).CopyToBuff(content);
+
+ if (isGroundContinuous)
+ dataLen -= 256;
+
+ byte *biomes = content + packet.GetField(5).GetLength() - 256;
+ for (int i = 0; i < 16; i++) {
+ if (bitmask[i]) {
+ size_t len = 0;
+ m_sections[PositionI(chunkX, chunkZ, i)] = ParseSection(content, len);
+ m_sectionToParse.push(m_sections.find(PositionI(chunkX, chunkZ, i)));
+ m_parseSectionWaiter.notify_one();
+ content += len;
+ }
+ }
+ delete[] contentOrigPtr;
+}
+
+Section World::ParseSection(byte *data, size_t &dataLen) {
+ dataLen = 0;
+
+ Field fBitsPerBlock = FieldParser::Parse(UnsignedByte, data);
+ byte bitsPerBlock = fBitsPerBlock.GetUByte();
+ data += fBitsPerBlock.GetLength();
+ dataLen += fBitsPerBlock.GetLength();
+
+ Field fPaletteLength = FieldParser::Parse(VarInt, data);
+ int paletteLength = fPaletteLength.GetVarInt();
+ data += fPaletteLength.GetLength();
+ dataLen += fPaletteLength.GetLength();
+
+ std::vector<unsigned short> palette;
+ if (paletteLength > 0) {
+ for (unsigned char i = 0; i < paletteLength; i++) {
+ endswap(&i);
+ Field f = FieldParser::Parse(VarInt, data);
+ data += f.GetLength();
+ dataLen += f.GetLength();
+ palette.push_back(f.GetVarInt());
+ endswap(&i);
+ }
+ }
+
+ Field fDataLength = FieldParser::Parse(VarInt, data);
+ data += fDataLength.GetLength();
+ dataLen += fDataLength.GetLength();
+
+ int dataLength = fDataLength.GetVarInt();
+ size_t dataSize = dataLength * 8;
+ dataLen += dataSize;
+ byte *dataBlocks = data;
+
+ data += 2048;
+ dataLen += 2048;
+ byte *dataLight = data;
+
+ byte *dataSky = nullptr;
+ if (m_dimension == 0) {
+ data += 2048;
+ dataLen += 2048;
+ dataSky = data;
+ }
+
+ return Section(dataBlocks, dataSize, dataLight, dataSky, bitsPerBlock, palette);
+}
+
+World::~World() {
+ isContinue = false;
+ m_parseSectionWaiter.notify_all();
+ m_sectionParseThread.join();
+}
+
+void World::SectionParsingThread() {
+ while (isContinue) {
+ std::unique_lock<std::mutex> sectionParseLocker(m_parseSectionMutex);
+ m_parseSectionWaiter.wait(sectionParseLocker);
+ while (m_sectionToParse.size() == 0 && isContinue) {
+ m_parseSectionWaiter.wait(sectionParseLocker);
+ }
+ while (m_sectionToParse.size() > 0) {
+ auto it = m_sectionToParse.front();
+ m_sectionToParse.pop();
+ it->second.Parse();
+ /*std::cout << "Parsed chunk" << it->first.GetX() << "x" << it->first.GetY() << "x" << it->first.GetZ()
+ << std::endl;*/
+ }
+ }
+}
+
+World::World() {
+ m_sectionParseThread = std::thread(&World::SectionParsingThread, this);
+}
+
diff --git a/code/World.hpp b/code/World.hpp
new file mode 100644
index 0000000..1f35585
--- /dev/null
+++ b/code/World.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <map>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <queue>
+#include "Block.hpp"
+#include "Packet.hpp"
+#include "Section.hpp"
+
+class World {
+ //utility vars
+ World(const World& other);
+ World&operator=(const World &other);
+ bool isContinue=true;
+ std::mutex m_parseSectionMutex;
+ std::condition_variable m_parseSectionWaiter;
+ std::thread m_sectionParseThread;
+ std::queue<std::map<PositionI,Section>::iterator> m_sectionToParse;
+ //utility methods
+ void SectionParsingThread();
+ //game vars
+ int m_dimension = 0;
+ //game methods
+ Section ParseSection(byte *data, size_t &dataLen);
+public:
+ World();
+ ~World();
+ void ParseChunkData(Packet packet);
+ std::map<PositionI, Section> m_sections;
+}; \ No newline at end of file
diff --git a/code/graphics/AssetManager.cpp b/code/graphics/AssetManager.cpp
new file mode 100644
index 0000000..1840c63
--- /dev/null
+++ b/code/graphics/AssetManager.cpp
@@ -0,0 +1,143 @@
+#include "AssetManager.hpp"
+
+const std::string pathToAssets = "./assets/";
+const std::string pathToObjects = pathToAssets + "objects/";
+const std::string pathToIndexFile = pathToAssets + "indexes/1.11.json";
+const std::string pathToAssetsMc = "./assetsMc/";
+
+const std::map<Asset::AssetType, std::string> assetTypeFileExtensions{
+ std::make_pair(Asset::AssetType::Texture, ".png"),
+ std::make_pair(Asset::AssetType::Lang, ".lang"),
+ std::make_pair(Asset::AssetType::Sound, ".ogg"),
+};
+
+AssetManager::AssetManager() {
+ std::ifstream indexFile(pathToIndexFile);
+ if (!indexFile) {
+ std::cerr << "Can't open file " << pathToIndexFile << std::endl;
+ }
+ nlohmann::json json = nlohmann::json::parse(indexFile)["objects"];
+ for (auto it = json.begin(); it != json.end(); ++it) {
+ size_t fileNameExtensionPos = -1;
+ std::string name = it.key();
+ Asset::AssetType type = Asset::Unknown;
+ for (auto &it:assetTypeFileExtensions) {
+ if ((fileNameExtensionPos = name.find(it.second)) != std::string::npos) {
+ type = it.first;
+ name = name.substr(0, fileNameExtensionPos);
+ break;
+ }
+ }
+ std::string hash = it.value()["hash"].get<std::string>();
+ size_t size = it.value()["size"].get<int>();
+ Asset asset{name, hash, Asset::AssetData(), size, type};
+ this->assets[name] = asset;
+ }
+}
+
+AssetManager::~AssetManager() {
+
+}
+
+Asset &AssetManager::GetAsset(std::string AssetName) {
+ if (instance().assets.find(AssetName) == instance().assets.end() || !instance().assets[AssetName].isParsed())
+ LoadAsset(AssetName);
+ return instance().assets[AssetName];
+}
+
+void AssetManager::LoadAsset(std::string AssetName) {
+ if (instance().assets.find(AssetName) != instance().assets.end() && instance().assets[AssetName].isParsed())
+ return;
+ std::string AssetFileName = GetPathToAsset(AssetName);
+ Asset &asset = instance().assets[AssetName];
+
+
+ if (asset.type == Asset::Texture) {
+ asset.data.texture = new Texture(AssetFileName,GL_CLAMP_TO_BORDER,GL_NEAREST);
+ //asset.data.texture.loadFromFile((asset.name + assetTypeFileExtensions.at(asset.type)));
+ }
+}
+
+std::string AssetManager::GetPathToAsset(std::string AssetName) {
+ if (instance().assets.find(AssetName) != instance().assets.end()){
+ auto it = instance().assets.find(AssetName);
+ return pathToObjects + std::string(instance().assets[AssetName].hash.c_str(), 2) + "/" +
+ instance().assets[AssetName].hash;
+ }
+
+ instance().assets[AssetName].hash="";
+ instance().assets[AssetName].type=Asset::AssetType::Texture;
+ instance().assets[AssetName].name=AssetName;
+ instance().assets[AssetName].size=0;
+ return pathToAssetsMc + "" + instance().assets[AssetName].name +
+ assetTypeFileExtensions.at(instance().assets[AssetName].type);
+}
+
+std::string AssetManager::GetAssetNameByBlockId(unsigned short id) {
+ std::string assetBase = "minecraft/textures/blocks/";
+ std::string textureName;
+ switch (id){
+ case 0:
+ textureName="air";
+ break;
+ case 1:
+ textureName="stone";
+ break;
+ case 2:
+ textureName="grass";
+ break;
+ case 3:
+ textureName="dirt";
+ break;
+ case 16:
+ textureName="coal_ore";
+ break;
+ case 17:
+ textureName="log_oak";
+ break;
+ case 31:
+ textureName="air";
+ break;
+ default:
+ //std::cout<<id<<std::endl;
+ textureName="beacon";
+ break;
+ }
+ return assetBase+textureName;
+}
+
+bool Asset::isParsed() {
+ switch (type) {
+ case Unknown:
+ return false;
+ break;
+ case Texture:
+ return this->data.texture != nullptr;
+ break;
+ case Sound:
+ return false;
+ break;
+ case Model:
+ return false;
+ break;
+ case Lang:
+ return false;
+ break;
+ }
+}
+
+Asset::~Asset() {
+ switch (type) {
+ case Unknown:
+ break;
+ case Texture:
+ delete this->data.texture;
+ break;
+ case Sound:
+ break;
+ case Model:
+ break;
+ case Lang:
+ break;
+ }
+}
diff --git a/code/graphics/AssetManager.hpp b/code/graphics/AssetManager.hpp
new file mode 100644
index 0000000..c7ef81a
--- /dev/null
+++ b/code/graphics/AssetManager.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <fstream>
+#include <string>
+#include <map>
+#include "../json.hpp"
+#include "Texture.hpp"
+
+struct Asset {
+ std::string name = "";
+ std::string hash = "";
+ union AssetData{
+ Texture *texture;
+ } data;
+ size_t size = 0;
+ enum AssetType {
+ Unknown,
+ Texture,
+ Sound,
+ Model,
+ Lang,
+ } type = Unknown;
+ bool isParsed();
+ ~Asset();
+};
+
+class AssetManager {
+ AssetManager();
+
+ ~AssetManager();
+
+ AssetManager(const AssetManager &);
+
+ AssetManager &operator=(const AssetManager &);
+
+ std::map<std::string, Asset> assets;
+
+ static AssetManager &instance() {
+ static AssetManager assetManager;
+ return assetManager;
+ }
+
+ static std::string GetPathToAsset(std::string AssetName);
+public:
+
+ static Asset &GetAsset(std::string AssetName);
+
+ static void LoadAsset(std::string AssetName);
+
+ static std::string GetAssetNameByBlockId(unsigned short id);
+};
+
diff --git a/code/graphics/Camera3D.cpp b/code/graphics/Camera3D.cpp
new file mode 100644
index 0000000..eb740e4
--- /dev/null
+++ b/code/graphics/Camera3D.cpp
@@ -0,0 +1,79 @@
+#include "Camera3D.hpp"
+
+Camera3D::Camera3D(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)),
+ MovementSpeed(SPEED),
+ MouseSensitivity(SENSITIVTY),
+ Zoom(ZOOM) {
+ this->Position = position;
+ this->WorldUp = up;
+ this->Yaw = yaw;
+ this->Pitch = pitch;
+ this->updateCameraVectors();
+}
+
+Camera3D::Camera3D(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw,
+ GLfloat pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVTY),
+ Zoom(ZOOM) {
+ this->Position = glm::vec3(posX, posY, posZ);
+ this->WorldUp = glm::vec3(upX, upY, upZ);
+ this->Yaw = yaw;
+ this->Pitch = pitch;
+ this->updateCameraVectors();
+}
+
+glm::mat4 Camera3D::GetViewMatrix() {
+ return glm::lookAt(this->Position, this->Position + this->Front, this->Up);
+}
+
+void Camera3D::ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime) {
+ GLfloat velocity = this->MovementSpeed * deltaTime;
+ if (direction == FORWARD)
+ this->Position += this->Front * velocity;
+ if (direction == BACKWARD)
+ this->Position -= this->Front * velocity;
+ if (direction == LEFT)
+ this->Position -= this->Right * velocity;
+ if (direction == RIGHT)
+ this->Position += this->Right * velocity;
+}
+
+void Camera3D::ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch) {
+ xoffset *= this->MouseSensitivity;
+ yoffset *= this->MouseSensitivity;
+
+ this->Yaw += xoffset;
+ this->Pitch += yoffset;
+
+ // Make sure that when pitch is out of bounds, screen doesn't get flipped
+ if (constrainPitch) {
+ if (this->Pitch > 89.0f)
+ this->Pitch = 89.0f;
+ if (this->Pitch < -89.0f)
+ this->Pitch = -89.0f;
+ }
+
+ // Update Front, Right and Up Vectors using the updated Eular angles
+ this->updateCameraVectors();
+}
+
+void Camera3D::ProcessMouseScroll(GLfloat yoffset) {
+ if (this->Zoom >= 1.0f && this->Zoom <= 45.0f)
+ this->Zoom -= yoffset/5.0f;
+ if (this->Zoom <= 1.0f)
+ this->Zoom = 1.0f;
+ if (this->Zoom >= 45.0f)
+ this->Zoom = 45.0f;
+}
+
+void Camera3D::updateCameraVectors() {
+ // Calculate the new Front vector
+ 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);
+ // Also re-calculate the Right and Up vector
+ this->Right = glm::normalize(glm::cross(this->Front,
+ this->WorldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
+ this->Up = glm::normalize(glm::cross(this->Right, this->Front));
+}
diff --git a/code/graphics/Camera3D.hpp b/code/graphics/Camera3D.hpp
new file mode 100644
index 0000000..eac1f47
--- /dev/null
+++ b/code/graphics/Camera3D.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+// Std. Includes
+#include <vector>
+
+// GL Includes
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+
+// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
+enum Camera_Movement {
+ FORWARD,
+ BACKWARD,
+ LEFT,
+ RIGHT
+};
+
+// Default camera values
+const GLfloat YAW = -90.0f;
+const GLfloat PITCH = 0.0f;
+const GLfloat SPEED = 30.0f;
+const GLfloat SENSITIVTY = 0.2f;
+const GLfloat ZOOM = 45.0f;
+
+// An abstract camera class that processes input and calculates the corresponding Eular Angles, Vectors and Matrices for use in OpenGL
+class Camera3D {
+public:
+ // Camera3D Attributes
+ glm::vec3 Position;
+ glm::vec3 Front;
+ glm::vec3 Up;
+ glm::vec3 Right;
+ glm::vec3 WorldUp;
+ // Eular Angles
+ GLfloat Yaw;
+ GLfloat Pitch;
+ // Camera3D options
+ GLfloat MovementSpeed;
+ GLfloat MouseSensitivity;
+ GLfloat Zoom;
+
+ // Constructor with vectors
+ explicit Camera3D(glm::vec3 position = glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f),
+ GLfloat yaw = YAW, GLfloat pitch = PITCH);
+
+ // Constructor with scalar values
+ Camera3D(GLfloat posX, GLfloat posY, GLfloat posZ, GLfloat upX, GLfloat upY, GLfloat upZ, GLfloat yaw, GLfloat pitch);
+
+ // Returns the view matrix calculated using Eular Angles and the LookAt Matrix
+ glm::mat4 GetViewMatrix();
+
+ // Processes input received from any keyboard-like input system. Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems)
+ void ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime);
+
+ // Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
+ void ProcessMouseMovement(GLfloat xoffset, GLfloat yoffset, GLboolean constrainPitch = true);
+
+ // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
+ void ProcessMouseScroll(GLfloat yoffset);
+
+private:
+ // Calculates the front vector from the Camera3D's (updated) Eular Angles
+ void updateCameraVectors();
+}; \ No newline at end of file
diff --git a/code/graphics/Display.cpp b/code/graphics/Display.cpp
new file mode 100644
index 0000000..a68c35b
--- /dev/null
+++ b/code/graphics/Display.cpp
@@ -0,0 +1,268 @@
+#include <iomanip>
+#include "Display.hpp"
+#include "AssetManager.hpp"
+
+Display::Display(unsigned int winWidth, unsigned int winHeight, const char *winTitle, World *worldPtr) : world(
+ worldPtr) {
+ 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);
+ window->setVerticalSyncEnabled(true);
+ window->setMouseCursorVisible(false);
+ sf::Mouse::setPosition(sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2), *window);
+
+ //Glew
+ glewExperimental = GL_TRUE;
+ if (glewInit() != GLEW_OK) {
+ std::cout << "Failed to initialize GLEW" << std::endl;
+ throw 3;
+ }
+ glViewport(0, 0, width(), height());
+ glEnable(GL_DEPTH_TEST);
+}
+
+bool Display::IsClosed() {
+ return !window->isOpen();
+}
+
+void Display::SetPlayerPos(double playerX, double playerY, double playerZ) {
+ camera.Position = glm::vec3(playerX, playerY, playerZ);
+ const int ChunkDistance = 1;
+ PositionI playerChunk = PositionI((int) playerX / 16, (int) playerZ / 16, (int) playerY / 16);
+ /*std::cout << "Player chunk position: " << playerChunk.GetX() << " "
+ << playerChunk.GetZ() << " " << playerChunk.GetY() << std::endl;*/
+ for (auto &it:world->m_sections) {
+ PositionI chunkPosition = it.first;
+ PositionI delta = chunkPosition - playerChunk;
+ if (delta.GetDistance() > ChunkDistance)
+ continue;
+ /*std::cout << "Rendering " << delta.GetDistance() << " Detailed: " << delta.GetX() << " " << delta.GetZ() << " "
+ << delta.GetY() << std::endl <<
+ "\t" << chunkPosition.GetX() << " " << chunkPosition.GetZ() << " "
+ << chunkPosition.GetY() << std::endl;*/
+ toRender.push_back(it.first);
+ }
+ std::cout << "Chunks to render: " << toRender.size() << std::endl;
+}
+
+void Display::MainLoop() {
+ Shader shader("./shaders/simple.vs", "./shaders/simple.fs");
+
+ GLfloat vertices[] = {
+ -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
+ 0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
+ 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
+ -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
+ -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
+
+ -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
+ 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
+ 0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
+ 0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
+ -0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
+ -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
+
+ -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
+ -0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
+ -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
+ -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
+ -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
+ -0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
+
+ 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
+ 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
+ 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
+
+ -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
+ 0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
+ -0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
+ -0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
+
+ -0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
+ 0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
+ 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
+ 0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
+ -0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
+ -0.5f, 0.5f, -0.5f, 0.0f, 1.0f
+ };
+ GLuint indices[] = {
+ 0, 1, 2,
+ 0, 2, 3
+ };
+ GLuint VBO, VAO, EBO;
+ glGenBuffers(1, &VBO);
+ glGenBuffers(1, &EBO);
+ glGenVertexArrays(1, &VAO);
+
+ glBindVertexArray(VAO);
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, VBO);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid *) (3 * sizeof(GLfloat)));
+ glEnableVertexAttribArray(2);
+ }
+ glBindVertexArray(0);
+
+ shader.Use();
+
+ bool captureMouse = true;
+
+ bool isRunning = true;
+ while (isRunning) {
+ static sf::Clock clock, clock1;
+ float deltaTime = clock.getElapsedTime().asSeconds();
+ float absTime = clock1.getElapsedTime().asSeconds();
+ clock.restart();
+ sf::Event event;
+ while (window->pollEvent(event)) {
+ switch (event.type) {
+ case sf::Event::Closed:
+ window->close();
+ isRunning = false;
+ break;
+ case sf::Event::Resized:
+ glViewport(0, 0, width(), height());
+ break;
+ case sf::Event::KeyPressed:
+ switch (event.key.code) {
+ case sf::Keyboard::Escape:
+ isRunning = false;
+ break;
+ case sf::Keyboard::T:
+ captureMouse = !captureMouse;
+ window->setMouseCursorVisible(!captureMouse);
+ sf::Mouse::setPosition(sf::Vector2i(window->getSize().x / 2, window->getSize().y / 2),
+ *window);
+ break;
+ case sf::Keyboard::R:
+ shader.Reload();
+ break;
+ default:
+ break;
+ }
+ case sf::Event::MouseWheelScrolled:
+ camera.ProcessMouseScroll(event.mouseWheelScroll.delta);
+ break;
+ default:
+ break;
+ }
+ }
+ std::ostringstream toWindow;
+ glm::highp_vec3 cameraPosition(camera.Position);
+ toWindow << std::setprecision(2) << std::fixed << "Pos: " << cameraPosition.x << ", " << cameraPosition.y
+ << ", " << cameraPosition.z << "; ";
+ toWindow << "FPS: " << (1.0f / deltaTime) << " ";
+ window->setTitle(toWindow.str());
+ if (captureMouse) {
+ 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);
+ int deltaX = (mousePos - center).x, deltaY = (center - mousePos).y;
+ camera.ProcessMouseMovement(deltaX, deltaY);
+ }
+ if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
+ camera.ProcessKeyboard(Camera_Movement::FORWARD, deltaTime);
+ if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
+ camera.ProcessKeyboard(Camera_Movement::BACKWARD, deltaTime);
+ if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
+ camera.ProcessKeyboard(Camera_Movement::LEFT, deltaTime);
+ if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
+ camera.ProcessKeyboard(Camera_Movement::RIGHT, deltaTime);
+
+
+ //Render code
+ glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ shader.Use();
+
+ GLint modelLoc = glGetUniformLocation(shader.Program, "model");
+ GLint projectionLoc = glGetUniformLocation(shader.Program, "projection");
+ GLint viewLoc = glGetUniformLocation(shader.Program, "view");
+ GLint blockLoc = glGetUniformLocation(shader.Program, "block");
+ GLint timeLoc = glGetUniformLocation(shader.Program, "time");
+ glm::mat4 projection = glm::perspective(camera.Zoom, (float) width() / (float) height(), 0.1f, 1000.0f);
+ glm::mat4 view = camera.GetViewMatrix();
+ glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection));
+ glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
+ glUniform1f(timeLoc, absTime);
+
+ glBindVertexArray(VAO);
+ /*for (GLuint i = 0; i < 10; i++) {
+ glm::mat4 model;
+ glm::vec3 cubePositions[] = {
+ glm::vec3(0, 0, 0),
+ glm::vec3(0, 0, 1),
+ glm::vec3(0, 0, 2),
+ glm::vec3(1, 0, 0),
+ glm::vec3(1, 0, 1),
+ glm::vec3(1, 0, 2),
+ glm::vec3(2, 0, 0),
+ glm::vec3(2, 0, 1),
+ glm::vec3(2, 0, 2),
+ glm::vec3(3, 0, 3),
+ };
+ if (toRender.size()<1)
+ continue;
+ model = glm::translate(model,
+ glm::vec3(toRender[0].GetX() * 16, toRender[0].GetZ() * 16,
+ toRender[0].GetY() * 16));
+ model = glm::translate(model, cubePositions[i]);
+
+ GLfloat angle = 20.0f * (i);
+ //model = glm::rotate(model, glm::radians(angle * absTime), glm::vec3(1.0f, 0.3f, 0.5f));
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
+
+ glDrawArrays(GL_TRIANGLES, 0, 36);
+ }*/
+
+ for (auto &sectionPos:toRender) {
+ Section &section = world->m_sections[sectionPos];
+ for (int y = 0; y < 16; y++) {
+ for (int z = 0; z < 16; z++) {
+ for (int x = 0; x < 16; x++) {
+ glm::mat4 model;
+ model = glm::translate(model,
+ glm::vec3(sectionPos.GetX() * 16, sectionPos.GetY() * 16,
+ sectionPos.GetZ() * 16));
+ model = glm::translate(model, glm::vec3(x, y, z));
+
+ Block block = section.GetBlock(PositionI(x, z, y));
+ glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
+ glUniform1i(blockLoc, block.id);
+
+ std::string textureName = AssetManager::GetAssetNameByBlockId(block.id);
+ if (textureName.find("air") != std::string::npos)
+ continue;
+ Texture &texture1 = *(AssetManager::GetAsset(textureName).data.texture);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture1.texture);
+ glUniform1i(glGetUniformLocation(shader.Program, "blockTexture"), 0);
+
+ glDrawArrays(GL_TRIANGLES, 0, 36);
+ }
+ }
+ }
+ }
+ glBindVertexArray(0);
+
+ //End of render code
+
+ window->display();
+ }
+
+}
diff --git a/code/graphics/Display.hpp b/code/graphics/Display.hpp
new file mode 100644
index 0000000..314ef5d
--- /dev/null
+++ b/code/graphics/Display.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <SFML/Window.hpp>
+#include "../World.hpp"
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include "Shader.hpp"
+#include "Texture.hpp"
+#include "Camera3D.hpp"
+
+class Display {
+ sf::Window *window;
+ World* world;
+ std::vector<PositionI> toRender;
+ Camera3D camera;
+public:
+ Display(unsigned int winWidth, unsigned int winHeight, const char winTitle[9], World *worldPtr);
+
+ bool IsClosed();
+
+ void SetPlayerPos(double playerX, double playerY, double playerZ);
+
+ void MainLoop();
+
+ unsigned int width() {
+ return window->getSize().x;
+ }
+
+ unsigned int height() {
+ return window->getSize().y;
+ }
+}; \ No newline at end of file
diff --git a/code/graphics/Shader.cpp b/code/graphics/Shader.cpp
new file mode 100644
index 0000000..c84e169
--- /dev/null
+++ b/code/graphics/Shader.cpp
@@ -0,0 +1,90 @@
+#include "Shader.hpp"
+
+Shader::Shader(const GLchar *vertexPath, const GLchar *fragmentPath) {
+ vertex = vertexPath;
+ fragment = fragmentPath;
+ // 1. Получаем исходный код шейдера из filePath
+ std::string vertexCode;
+ std::string fragmentCode;
+ std::ifstream vShaderFile;
+ std::ifstream fShaderFile;
+ // Удостоверимся, что ifstream объекты могут выкидывать исключения
+ vShaderFile.exceptions(std::ifstream::failbit);
+ fShaderFile.exceptions(std::ifstream::failbit);
+ try {
+ // Открываем файлы
+ vShaderFile.open(vertexPath);
+ fShaderFile.open(fragmentPath);
+ std::stringstream vShaderStream, fShaderStream;
+ // Считываем данные в потоки
+ vShaderStream << vShaderFile.rdbuf();
+ fShaderStream << fShaderFile.rdbuf();
+ // Закрываем файлы
+ vShaderFile.close();
+ fShaderFile.close();
+ // Преобразовываем потоки в массив GLchar
+ vertexCode = vShaderStream.str();
+ fragmentCode = fShaderStream.str();
+ }
+ catch (std::ifstream::failure e) {
+ std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
+ }
+ const GLchar *vShaderCode = vertexCode.c_str();
+ const GLchar *fShaderCode = fragmentCode.c_str();
+
+
+ // 2. Сборка шейдеров
+ GLuint vertex, fragment;
+ 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);
+ std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
+ };
+
+ // Вершинный шейдер
+ 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);
+ std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
+ };
+
+ // Шейдерная программа
+ this->Program = glCreateProgram();
+ glAttachShader(this->Program, vertex);
+ glAttachShader(this->Program, fragment);
+ glLinkProgram(this->Program);
+ //Если есть ошибки - вывести их
+ glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
+ std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
+ }
+
+ // Удаляем шейдеры, поскольку они уже в программу и нам больше не нужны.
+ 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);
+ std::cout<<"Shader is realoded!"<<std::endl;
+}
diff --git a/code/graphics/Shader.hpp b/code/graphics/Shader.hpp
new file mode 100644
index 0000000..8178d2a
--- /dev/null
+++ b/code/graphics/Shader.hpp
@@ -0,0 +1,22 @@
+#include <string>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <GL/glew.h>
+
+class Shader
+{
+private:
+ const GLchar *vertex;
+ const GLchar *fragment;
+public:
+ // Идентификатор программы
+ GLuint Program;
+ // Конструктор считывает и собирает шейдер
+ Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
+ // Использование программы
+ void Use();
+
+ void Reload();
+}; \ No newline at end of file
diff --git a/code/graphics/Texture.cpp b/code/graphics/Texture.cpp
new file mode 100644
index 0000000..0104530
--- /dev/null
+++ b/code/graphics/Texture.cpp
@@ -0,0 +1,39 @@
+#include <iostream>
+#include <SFML/Graphics.hpp>
+#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)) {
+ std::cout << "Can't open image " << filename << std::endl;
+ throw 201;
+ }
+ if (image.getPixelsPtr()==nullptr){
+ std::cout<<"Image data is corrupted!"<<std::endl;
+ 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/code/graphics/Texture.hpp b/code/graphics/Texture.hpp
new file mode 100644
index 0000000..8e3f1af
--- /dev/null
+++ b/code/graphics/Texture.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#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/code/main.cpp b/code/main.cpp
new file mode 100644
index 0000000..a88baf9
--- /dev/null
+++ b/code/main.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+#include "Game.hpp"
+#include "graphics/AssetManager.hpp"
+
+int main() {
+
+ Game game;
+ game.Exec();
+ return 0;
+} \ No newline at end of file
diff --git a/code/shaders/simple.fs b/code/shaders/simple.fs
new file mode 100644
index 0000000..2d2a582
--- /dev/null
+++ b/code/shaders/simple.fs
@@ -0,0 +1,22 @@
+#version 330 core
+in vec2 TexCoord;
+
+out vec4 color;
+
+uniform sampler2D blockTexture;
+uniform int block;
+uniform float time;
+
+void main()
+{
+ //color = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.0);
+ /*if (block==1)
+ color = vec4(0.2,0.2,0.2,1);
+ else if (block==0)
+ color = vec4(0,0,0,1);
+ else
+ color = vec4(1,1,1,1);*/
+ color = texture(blockTexture,TexCoord);
+ //color = vec4(TexCoord.x,TexCoord.y,0,1);
+}
+
diff --git a/code/shaders/simple.vs b/code/shaders/simple.vs
new file mode 100644
index 0000000..7233efc
--- /dev/null
+++ b/code/shaders/simple.vs
@@ -0,0 +1,15 @@
+#version 330 core
+layout (location = 0) in vec3 position;
+layout (location = 2) in vec2 texCoord;
+
+out vec2 TexCoord;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * view * model * vec4(position, 1.0f);
+ TexCoord = vec2(texCoord.x, texCoord.y);
+} \ No newline at end of file
diff --git a/code/utility.cpp b/code/utility.cpp
new file mode 100644
index 0000000..aa50e9f
--- /dev/null
+++ b/code/utility.cpp
@@ -0,0 +1,66 @@
+#include "utility.h"
+
+int VarIntRead(unsigned char *data, size_t &readed) {
+ readed = 0;
+ int result = 0;
+ char read;
+ do {
+ read = data[readed];
+ int value = (read & 0b01111111);
+ result |= (value << (7 * readed));
+
+ readed++;
+ if (readed > 5) {
+ throw "VarInt is too big";
+ }
+ } while ((read & 0b10000000) != 0);
+
+ return result;
+}
+
+size_t VarIntWrite(unsigned int value, unsigned char *data) {
+ size_t len = 0;
+ do {
+ unsigned char temp = (unsigned char) (value & 0b01111111);
+ value >>= 7;
+ if (value != 0) {
+ temp |= 0b10000000;
+ }
+ data[len] = temp;
+ len++;
+ } while (value != 0);
+ return len;
+}
+
+long long int ReadVarLong(unsigned char *data, int &readed) {
+ readed = 0;
+ long long result = 0;
+ unsigned char read;
+ do {
+ read = data[readed];
+ long long value = (read & 0b01111111);
+ result |= (value << (7 * readed));
+
+ readed++;
+ if (readed > 10) {
+ throw "VarLong is too big";
+ }
+ } while ((read & 0b10000000) != 0);
+ return result;
+}
+
+unsigned char *WriteVarLong(unsigned long long int value, int &len) {
+ unsigned char *data = new unsigned char[10];
+ len = 0;
+ do {
+ unsigned char temp = (unsigned char) (value & 0b01111111);
+ value >>= 7;
+ if (value != 0) {
+ temp |= 0b10000000;
+ }
+ data[len] = temp;
+ len++;
+ } while (value != 0);
+
+ return data;
+} \ No newline at end of file
diff --git a/code/utility.h b/code/utility.h
new file mode 100644
index 0000000..32120cb
--- /dev/null
+++ b/code/utility.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <algorithm>
+
+int VarIntRead(unsigned char *data, size_t &readed);
+
+size_t VarIntWrite(unsigned int value, unsigned char *data);
+
+long long int ReadVarLong(unsigned char *data, int &readed);
+
+unsigned char *WriteVarLong(unsigned long long int value, int &len);
+
+template<class T>
+void endswap(T *objp) {
+ unsigned char *memp = reinterpret_cast<unsigned char *>(objp);
+ std::reverse(memp, memp + sizeof(T));
+} \ No newline at end of file