diff options
Diffstat (limited to 'code')
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 §ionPos:toRender) { + Section §ion = 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 |