mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-09 02:07:04 +08:00
Compare commits
9 Commits
fbb6fe2fa6
...
d31ec68f3c
| Author | SHA1 | Date | |
|---|---|---|---|
| d31ec68f3c | |||
| c7c6ee9a61 | |||
| db7b67f265 | |||
| 7e42f595c9 | |||
| 11c902d2bf | |||
| e5e84848a9 | |||
| 25e9b0e0d3 | |||
| 48300b5fe2 | |||
| 3c14a4ffe1 |
@@ -17,10 +17,11 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|||||||
if(MSVC)
|
if(MSVC)
|
||||||
add_compile_options(/utf-8)
|
add_compile_options(/utf-8)
|
||||||
endif()
|
endif()
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(Protobuf REQUIRED)
|
find_package(Protobuf REQUIRED)
|
||||||
find_package(absl REQUIRED)
|
find_package(absl REQUIRED)
|
||||||
|
find_package(zstd REQUIRED)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
find_package(Freetype REQUIRED)
|
find_package(Freetype REQUIRED)
|
||||||
@@ -207,6 +208,7 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
protobuf::libprotobuf
|
protobuf::libprotobuf
|
||||||
absl::log
|
absl::log
|
||||||
absl::check
|
absl::check
|
||||||
|
zstd::zstd
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
|||||||
8
assets/shaders/player_f_shader.glsl
Normal file
8
assets/shaders/player_f_shader.glsl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
in vec2 tc;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
color = vec4(0.0, 0.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
15
assets/shaders/player_v_shader.glsl
Normal file
15
assets/shaders/player_v_shader.glsl
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 pos;
|
||||||
|
layout (location = 1) in vec2 texCoord;
|
||||||
|
|
||||||
|
uniform mat4 mv_matrix;
|
||||||
|
uniform mat4 proj_matrix;
|
||||||
|
|
||||||
|
out vec2 tc;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 viewPos = mv_matrix * vec4(pos, 1.0);
|
||||||
|
tc = texCoord;
|
||||||
|
gl_Position = proj_matrix * viewPos;
|
||||||
|
}
|
||||||
22
cmake/modules/Findzstd.cmake
Normal file
22
cmake/modules/Findzstd.cmake
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
find_path(ZSTD_INCLUDE_DIRS
|
||||||
|
NAMES zstd.h
|
||||||
|
HINTS ${zstd_ROOT_DIR}/include)
|
||||||
|
|
||||||
|
find_library(ZSTD_LIBRARIES
|
||||||
|
NAMES zstd
|
||||||
|
HINTS ${zstd_ROOT_DIR}/lib)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
ZSTD_LIBRARIES
|
||||||
|
ZSTD_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
if(ZSTD_FOUND AND NOT (TARGET zstd::zstd))
|
||||||
|
add_library (zstd::zstd UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(zstd::zstd
|
||||||
|
PROPERTIES
|
||||||
|
IMPORTED_LOCATION ${ZSTD_LIBRARIES}
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
@@ -12,6 +12,12 @@ namespace Cubed {
|
|||||||
|
|
||||||
class App {
|
class App {
|
||||||
public:
|
public:
|
||||||
|
struct Argument {
|
||||||
|
bool is_client = false;
|
||||||
|
int port = 25530;
|
||||||
|
std::string ip{"127.0.0.1"};
|
||||||
|
};
|
||||||
|
|
||||||
App();
|
App();
|
||||||
~App();
|
~App();
|
||||||
static void cursor_position_callback(GLFWwindow* window, double xpos,
|
static void cursor_position_callback(GLFWwindow* window, double xpos,
|
||||||
@@ -60,9 +66,9 @@ private:
|
|||||||
inline static double fps_time_count = 0.0f;
|
inline static double fps_time_count = 0.0f;
|
||||||
inline static int frame_count = 0;
|
inline static int frame_count = 0;
|
||||||
inline static int fps = 0;
|
inline static int fps = 0;
|
||||||
|
Argument m_argument;
|
||||||
void init();
|
void init(int argc, char** argv);
|
||||||
|
void handle_argument(int argc, char** argv);
|
||||||
auto init_camera();
|
auto init_camera();
|
||||||
auto init_texture();
|
auto init_texture();
|
||||||
auto init_world();
|
auto init_world();
|
||||||
|
|||||||
@@ -5,11 +5,22 @@
|
|||||||
#include "Cubed/gameplay/client_player.hpp"
|
#include "Cubed/gameplay/client_player.hpp"
|
||||||
#include "Cubed/gameplay/game_time.hpp"
|
#include "Cubed/gameplay/game_time.hpp"
|
||||||
#include "Cubed/gameplay/network_client.hpp"
|
#include "Cubed/gameplay/network_client.hpp"
|
||||||
|
#include "Cubed/tools/thread_pool.hpp"
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <tbb/concurrent_unordered_map.h>
|
#include <tbb/concurrent_unordered_map.h>
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
|
|
||||||
|
struct OtherPlayerInfo {
|
||||||
|
std::string name;
|
||||||
|
glm::vec3 pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderPlayerData {
|
||||||
|
std::string name;
|
||||||
|
glm::vec3 pos;
|
||||||
|
};
|
||||||
|
|
||||||
class ClientWorld {
|
class ClientWorld {
|
||||||
public:
|
public:
|
||||||
ClientWorld();
|
ClientWorld();
|
||||||
@@ -34,17 +45,24 @@ public:
|
|||||||
void receive_block_change(const BlockChangeRsp& rsp);
|
void receive_block_change(const BlockChangeRsp& rsp);
|
||||||
void receive_time(const UpdateTime& rsp);
|
void receive_time(const UpdateTime& rsp);
|
||||||
|
|
||||||
|
void receive_other_player(const PlayerInfoRsp& rsp);
|
||||||
|
void receive_player_logout(const LogoutRsp& rsp);
|
||||||
int rendering_distance() const;
|
int rendering_distance() const;
|
||||||
void rendering_distance(int rendering_distance);
|
void rendering_distance(int rendering_distance);
|
||||||
void start_client_thread(std::string_view uuid);
|
void start_client_thread(std::string_view uuid);
|
||||||
void stop_client_thread();
|
void stop_client_thread();
|
||||||
|
|
||||||
|
void start_thread_pool();
|
||||||
|
void stop_thread_pool();
|
||||||
|
void change_pool_threads(int threads);
|
||||||
void hot_reload();
|
void hot_reload();
|
||||||
void request_chunk();
|
void request_chunk();
|
||||||
std::vector<glm::vec4>& planes();
|
std::vector<glm::vec4>& planes();
|
||||||
std::vector<ChunkRenderSnapshot>& render_snapshots();
|
const std::vector<ChunkRenderSnapshot>& render_snapshots() const;
|
||||||
|
const std::vector<RenderPlayerData>& render_player_data() const;
|
||||||
glm::vec3 sunlight_dir() const;
|
glm::vec3 sunlight_dir() const;
|
||||||
void receive_chunk(const ChunkDataRsp& data);
|
void receive_chunk(const ChunkDataRsp& data);
|
||||||
|
void exit();
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||||
m_timers.emplace(std::piecewise_construct,
|
m_timers.emplace(std::piecewise_construct,
|
||||||
@@ -58,7 +76,9 @@ private:
|
|||||||
tbb::concurrent_unordered_map<ChunkPos, ClientChunk, ChunkPos::Hash>;
|
tbb::concurrent_unordered_map<ChunkPos, ClientChunk, ChunkPos::Hash>;
|
||||||
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
|
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
|
||||||
using ChunkPosVector = std::vector<ChunkPos>;
|
using ChunkPosVector = std::vector<ChunkPos>;
|
||||||
|
using OtherPlayerHashMap = std::unordered_map<std::string, OtherPlayerInfo>;
|
||||||
ClientPlayer m_player;
|
ClientPlayer m_player;
|
||||||
|
OtherPlayerHashMap m_other_players;
|
||||||
ChunkHashMap m_chunks;
|
ChunkHashMap m_chunks;
|
||||||
std::vector<glm::vec4> m_planes;
|
std::vector<glm::vec4> m_planes;
|
||||||
std::jthread m_client_thread;
|
std::jthread m_client_thread;
|
||||||
@@ -67,13 +87,16 @@ private:
|
|||||||
std::mutex m_delete_vbo_mutex;
|
std::mutex m_delete_vbo_mutex;
|
||||||
std::mutex m_delete_vao_mutex;
|
std::mutex m_delete_vao_mutex;
|
||||||
std::mutex m_pending_upload_queue_mutex;
|
std::mutex m_pending_upload_queue_mutex;
|
||||||
std::mutex m_pending_chunk_data_queue_mutex;
|
std::mutex m_other_players_mutex;
|
||||||
|
|
||||||
std::deque<ClientChunk> m_pending_upload_queue;
|
std::deque<ClientChunk> m_pending_upload_queue;
|
||||||
std::deque<ChunkDataRsp> m_pending_chunk_data_queue;
|
|
||||||
std::vector<GLuint> m_pending_delete_vbo;
|
std::vector<GLuint> m_pending_delete_vbo;
|
||||||
std::vector<GLuint> m_pending_delete_vao;
|
std::vector<GLuint> m_pending_delete_vao;
|
||||||
|
|
||||||
std::deque<ChunkPos> m_dirty_queue;
|
std::deque<ChunkPos> m_dirty_queue;
|
||||||
std::vector<ChunkRenderSnapshot> m_render_snapshots;
|
std::vector<ChunkRenderSnapshot> m_render_snapshots;
|
||||||
|
std::vector<RenderPlayerData> m_render_player_data;
|
||||||
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
|
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
|
||||||
std::atomic<bool> m_game_running{false};
|
std::atomic<bool> m_game_running{false};
|
||||||
std::atomic<int> m_rendering_distance{24};
|
std::atomic<int> m_rendering_distance{24};
|
||||||
@@ -82,6 +105,9 @@ private:
|
|||||||
std::atomic<bool> m_requesting_chunk{false};
|
std::atomic<bool> m_requesting_chunk{false};
|
||||||
std::shared_ptr<NetworkClient> m_client;
|
std::shared_ptr<NetworkClient> m_client;
|
||||||
ChunkLoadStyle m_chunk_load_style{ChunkLoadStyle::CENTER};
|
ChunkLoadStyle m_chunk_load_style{ChunkLoadStyle::CENTER};
|
||||||
|
|
||||||
|
std::atomic<std::shared_ptr<ThreadPool>> m_thread_pool;
|
||||||
|
|
||||||
void client_run(std::stop_token token);
|
void client_run(std::stop_token token);
|
||||||
|
|
||||||
void set_player_pos();
|
void set_player_pos();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public:
|
|||||||
void send(Packet packet);
|
void send(Packet packet);
|
||||||
void start(std::string ip, int port = 25530);
|
void start(std::string ip, int port = 25530);
|
||||||
bool is_connected() const;
|
bool is_connected() const;
|
||||||
|
bool is_connect_error() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
asio::io_context m_io;
|
asio::io_context m_io;
|
||||||
@@ -32,6 +33,7 @@ private:
|
|||||||
|
|
||||||
std::atomic<bool> m_closed{false};
|
std::atomic<bool> m_closed{false};
|
||||||
std::atomic<bool> m_connected{false};
|
std::atomic<bool> m_connected{false};
|
||||||
|
std::atomic<bool> m_connect_error{false};
|
||||||
// ClientWorld is managed by App
|
// ClientWorld is managed by App
|
||||||
ClientWorld& m_world;
|
ClientWorld& m_world;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public:
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
// Run in another thread after initialization is complete
|
// Run in another thread after initialization is complete
|
||||||
void start_server();
|
void start_server(int port = 25530);
|
||||||
|
|
||||||
int port() const;
|
int port() const;
|
||||||
ServerWorld& server_world();
|
ServerWorld& server_world();
|
||||||
@@ -23,6 +23,7 @@ private:
|
|||||||
std::thread m_net_thread;
|
std::thread m_net_thread;
|
||||||
int m_port = 25530;
|
int m_port = 25530;
|
||||||
std::atomic<bool> m_stopped{false};
|
std::atomic<bool> m_stopped{false};
|
||||||
|
std::atomic<bool> m_started{false};
|
||||||
ServerWorld m_world;
|
ServerWorld m_world;
|
||||||
std::mutex m_session_mutex;
|
std::mutex m_session_mutex;
|
||||||
std::unordered_map<std::string, std::shared_ptr<Session>> m_session;
|
std::unordered_map<std::string, std::shared_ptr<Session>> m_session;
|
||||||
|
|||||||
@@ -1,18 +1,53 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "Cubed/tools/compression.hpp"
|
||||||
#include "packet.pb.h" // IWYU pragma: keep
|
#include "packet.pb.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
constexpr int HEADER_LEN = 8;
|
constexpr size_t HEADER_LEN =
|
||||||
|
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t);
|
||||||
|
constexpr size_t PACKET_COMPRESSION_THRESHOLD = 100;
|
||||||
using Packet = std::shared_ptr<std::vector<uint8_t>>;
|
using Packet = std::shared_ptr<std::vector<uint8_t>>;
|
||||||
|
enum class CompressType : uint16_t {
|
||||||
|
NONE = 0,
|
||||||
|
ZSTD = 1,
|
||||||
|
};
|
||||||
|
|
||||||
enum class PacketEnum {
|
inline CompressType get_compress_type(uint16_t id) {
|
||||||
|
using enum CompressType;
|
||||||
|
constexpr auto& to = std::to_underlying<CompressType>;
|
||||||
|
switch (id) {
|
||||||
|
case to(NONE):
|
||||||
|
return NONE;
|
||||||
|
case to(ZSTD):
|
||||||
|
return ZSTD;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(std::format("Unknown CompressType {}", id));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PacketHeader {
|
||||||
|
uint16_t cmd{};
|
||||||
|
CompressType compress_type{}; // 0=none 1=zlib
|
||||||
|
uint32_t uncompressed_size{};
|
||||||
|
uint32_t compressed_size{};
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PacketEnum : uint16_t {
|
||||||
LOGIN_REQ = 1001,
|
LOGIN_REQ = 1001,
|
||||||
LOGIN_RSP = 1002,
|
LOGIN_RSP = 1002,
|
||||||
|
LOGOUT_REQ = 1003,
|
||||||
|
LOGOUT_RSP = 1004,
|
||||||
PLAYER_INFO = 2001,
|
PLAYER_INFO = 2001,
|
||||||
PLAYER_POS = 2002,
|
PLAYER_POS = 2002,
|
||||||
|
PLAYER_INFO_RSP = 2003,
|
||||||
CHUNK_DATA_REQ = 3001,
|
CHUNK_DATA_REQ = 3001,
|
||||||
CHUNK_DATA_RSP = 3002,
|
CHUNK_DATA_RSP = 3002,
|
||||||
BLOCK_CHANGE_REQ = 3003,
|
BLOCK_CHANGE_REQ = 3003,
|
||||||
@@ -34,10 +69,16 @@ template <typename T> constexpr uint16_t get_packet_id() {
|
|||||||
return to_num(LOGIN_REQ);
|
return to_num(LOGIN_REQ);
|
||||||
} else if constexpr (is_same_v<U, LoginRsp>) {
|
} else if constexpr (is_same_v<U, LoginRsp>) {
|
||||||
return to_num(LOGIN_RSP);
|
return to_num(LOGIN_RSP);
|
||||||
|
} else if constexpr (is_same_v<U, LogoutReq>) {
|
||||||
|
return to_num(LOGOUT_REQ);
|
||||||
|
} else if constexpr (is_same_v<U, LogoutRsp>) {
|
||||||
|
return to_num(LOGOUT_RSP);
|
||||||
} else if constexpr (is_same_v<U, PlayerInfo>) {
|
} else if constexpr (is_same_v<U, PlayerInfo>) {
|
||||||
return to_num(PLAYER_INFO);
|
return to_num(PLAYER_INFO);
|
||||||
} else if constexpr (is_same_v<U, PlayerPos>) {
|
} else if constexpr (is_same_v<U, PlayerPos>) {
|
||||||
return to_num(PLAYER_POS);
|
return to_num(PLAYER_POS);
|
||||||
|
} else if constexpr (is_same_v<U, PlayerInfoRsp>) {
|
||||||
|
return to_num(PLAYER_INFO_RSP);
|
||||||
} else if constexpr (is_same_v<U, ChunkDataReq>) {
|
} else if constexpr (is_same_v<U, ChunkDataReq>) {
|
||||||
return to_num(CHUNK_DATA_REQ);
|
return to_num(CHUNK_DATA_REQ);
|
||||||
} else if constexpr (is_same_v<U, ChunkDataRsp>) {
|
} else if constexpr (is_same_v<U, ChunkDataRsp>) {
|
||||||
@@ -50,35 +91,106 @@ template <typename T> constexpr uint16_t get_packet_id() {
|
|||||||
return to_num(UPDATE_TIME);
|
return to_num(UPDATE_TIME);
|
||||||
} else if constexpr (is_same_v<U, Ping>) {
|
} else if constexpr (is_same_v<U, Ping>) {
|
||||||
return to_num(PING);
|
return to_num(PING);
|
||||||
} else if (is_same_v<U, Pong>) {
|
} else if constexpr (is_same_v<U, Pong>) {
|
||||||
return to_num(PONG);
|
return to_num(PONG);
|
||||||
} else {
|
} else {
|
||||||
static_assert(always_false<U>::value, "Unkonw Type");
|
static_assert(always_false<U>::value, "Unknown Type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> Packet make_packet(const T& msg) {
|
template <typename T>
|
||||||
uint16_t cmd = get_packet_id<T>();
|
requires std::derived_from<T, google::protobuf::Message>
|
||||||
|
Packet make_packet(const T& msg) {
|
||||||
|
PacketHeader header{};
|
||||||
|
header.cmd = get_packet_id<T>();
|
||||||
|
uint32_t raw_size = static_cast<uint32_t>(msg.ByteSizeLong());
|
||||||
|
std::vector<uint8_t> raw(raw_size);
|
||||||
|
|
||||||
uint32_t body_len = static_cast<uint32_t>(msg.ByteSizeLong());
|
if (!msg.SerializeToArray(raw.data(), raw_size)) {
|
||||||
|
|
||||||
uint32_t total_len = HEADER_LEN + body_len;
|
|
||||||
|
|
||||||
auto packet = std::make_shared<std::vector<uint8_t>>(total_len);
|
|
||||||
|
|
||||||
uint32_t total_len_net = htonl(total_len);
|
|
||||||
uint16_t cmd_net = htons(cmd);
|
|
||||||
|
|
||||||
std::memcpy(packet->data(), &total_len_net, sizeof(total_len_net));
|
|
||||||
|
|
||||||
std::memcpy(packet->data() + 4, &cmd_net, sizeof(cmd_net));
|
|
||||||
|
|
||||||
if (!msg.SerializeToArray(packet->data() + HEADER_LEN,
|
|
||||||
static_cast<int>(body_len))) {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
std::vector<uint8_t> payload;
|
||||||
|
if (raw_size >= PACKET_COMPRESSION_THRESHOLD) {
|
||||||
|
std::vector<uint8_t> compressed = compress_data(raw);
|
||||||
|
if (compressed.size() < raw.size()) {
|
||||||
|
payload = std::move(compressed);
|
||||||
|
header.compress_type = CompressType::ZSTD;
|
||||||
|
} else {
|
||||||
|
payload = std::move(raw);
|
||||||
|
header.compress_type = CompressType::NONE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payload = std::move(raw);
|
||||||
|
header.compress_type = CompressType::NONE;
|
||||||
|
}
|
||||||
|
header.uncompressed_size = raw_size;
|
||||||
|
header.compressed_size = static_cast<uint32_t>(payload.size());
|
||||||
|
|
||||||
|
auto packet =
|
||||||
|
std::make_shared<std::vector<uint8_t>>(HEADER_LEN + payload.size());
|
||||||
|
|
||||||
|
uint16_t cmd_net = htons(header.cmd);
|
||||||
|
uint16_t compress_type_net =
|
||||||
|
htons(std::to_underlying(header.compress_type));
|
||||||
|
uint32_t uncompressed_size_net = htonl(header.uncompressed_size);
|
||||||
|
uint32_t compressed_size_net = htonl(header.compressed_size);
|
||||||
|
|
||||||
|
std::memcpy(packet->data(), &cmd_net, sizeof(cmd_net));
|
||||||
|
|
||||||
|
std::memcpy(packet->data() + 2, &compress_type_net,
|
||||||
|
sizeof(compress_type_net));
|
||||||
|
std::memcpy(packet->data() + 4, &uncompressed_size_net,
|
||||||
|
sizeof(uncompressed_size_net));
|
||||||
|
std::memcpy(packet->data() + 8, &compressed_size_net,
|
||||||
|
sizeof(compressed_size_net));
|
||||||
|
std::memcpy(packet->data() + HEADER_LEN, payload.data(), payload.size());
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline PacketHeader decode_packet_header(std::span<const uint8_t> header) {
|
||||||
|
if (header.size() < HEADER_LEN)
|
||||||
|
throw std::runtime_error("Invalid header");
|
||||||
|
uint16_t cmd_net;
|
||||||
|
uint16_t compress_type_net;
|
||||||
|
uint32_t uncompressed_size_net;
|
||||||
|
uint32_t compressed_size_net;
|
||||||
|
std::memcpy(&cmd_net, header.data(), sizeof(cmd_net));
|
||||||
|
std::memcpy(&compress_type_net, header.data() + 2,
|
||||||
|
sizeof(compress_type_net));
|
||||||
|
std::memcpy(&uncompressed_size_net, header.data() + 4,
|
||||||
|
sizeof(uncompressed_size_net));
|
||||||
|
std::memcpy(&compressed_size_net, header.data() + 8,
|
||||||
|
sizeof(compressed_size_net));
|
||||||
|
|
||||||
|
return {ntohs(cmd_net), get_compress_type(ntohs(compress_type_net)),
|
||||||
|
ntohl(uncompressed_size_net), ntohl(compressed_size_net)};
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
requires std::derived_from<T, google::protobuf::Message>
|
||||||
|
bool decode_packet(T& message, std::span<const uint8_t> data,
|
||||||
|
const PacketHeader& header) {
|
||||||
|
if (data.size() != header.compressed_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.compress_type == CompressType::NONE &&
|
||||||
|
header.uncompressed_size != header.compressed_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (header.compress_type) {
|
||||||
|
case CompressType::NONE: {
|
||||||
|
return message.ParseFromArray(
|
||||||
|
data.data(), static_cast<int>(header.uncompressed_size));
|
||||||
|
}
|
||||||
|
case CompressType::ZSTD: {
|
||||||
|
auto raw = decompress_data(data, header.uncompressed_size);
|
||||||
|
return message.ParseFromArray(raw.data(), static_cast<int>(raw.size()));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||||
|
#include "Cubed/gameplay/game_time.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -11,19 +13,24 @@ class Session;
|
|||||||
class ServerPlayer {
|
class ServerPlayer {
|
||||||
public:
|
public:
|
||||||
ServerPlayer(std::string_view name, std::string_view uuid,
|
ServerPlayer(std::string_view name, std::string_view uuid,
|
||||||
ServerWorld& m_world, std::shared_ptr<Session> session);
|
ServerWorld& m_world, std::shared_ptr<Session> session,
|
||||||
|
TickType gametick);
|
||||||
const glm::vec3& get_pos() const;
|
const glm::vec3& get_pos() const;
|
||||||
const std::string& get_name() const;
|
const std::string& get_name() const;
|
||||||
const std::string& get_uuid() const;
|
const std::string& get_uuid() const;
|
||||||
std::shared_ptr<Session> get_session() const;
|
std::shared_ptr<Session> get_session() const;
|
||||||
void update_pos(float x, float y, float z);
|
void update_pos(float x, float y, float z);
|
||||||
|
void update_sync_gametick(TickType gametick);
|
||||||
|
bool is_disconnect(TickType current_gametick) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr TickType TIMEOUT = 200;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_uuid;
|
std::string m_uuid;
|
||||||
glm::vec3 m_pos{0.0f};
|
glm::vec3 m_pos{0.0f};
|
||||||
ServerWorld& m_world;
|
ServerWorld& m_world;
|
||||||
ChunkPos m_last_chunk_pos{0, 0};
|
ChunkPos m_last_chunk_pos{0, 0};
|
||||||
std::shared_ptr<Session> m_session;
|
std::shared_ptr<Session> m_session;
|
||||||
|
std::atomic<TickType> m_last_gametick{0};
|
||||||
};
|
};
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public:
|
|||||||
ServerWorld();
|
ServerWorld();
|
||||||
~ServerWorld();
|
~ServerWorld();
|
||||||
void player_join(std::string_view name, std::string_view uuid);
|
void player_join(std::string_view name, std::string_view uuid);
|
||||||
void handle_player_exit(const std::string& name);
|
void handle_player_exit(const std::string& uuid);
|
||||||
void init_world();
|
void init_world();
|
||||||
void need_gen(std::optional<std::string> uuid);
|
void need_gen(std::optional<std::string> uuid);
|
||||||
void update();
|
void update();
|
||||||
@@ -112,6 +112,7 @@ private:
|
|||||||
std::atomic<bool> m_gen_running{false};
|
std::atomic<bool> m_gen_running{false};
|
||||||
std::atomic<bool> m_need_gen_chunk{false};
|
std::atomic<bool> m_need_gen_chunk{false};
|
||||||
std::atomic<bool> m_is_rebuilding{false};
|
std::atomic<bool> m_is_rebuilding{false};
|
||||||
|
std::atomic<bool> m_init{false};
|
||||||
std::atomic<int> m_rendering_distance{24};
|
std::atomic<int> m_rendering_distance{24};
|
||||||
std::atomic<int> m_pool_threads{0};
|
std::atomic<int> m_pool_threads{0};
|
||||||
std::atomic<int> m_max_threads{1};
|
std::atomic<int> m_max_threads{1};
|
||||||
|
|||||||
@@ -272,6 +272,55 @@ constexpr float CROSS_TANGENTS[2][6][3] = {
|
|||||||
{-0.7071f, 0.0f, 0.7071f}}};
|
{-0.7071f, 0.0f, 0.7071f}}};
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Player
|
||||||
|
|
||||||
|
constexpr float VERTICES_PLAYER[6][6][3] = {
|
||||||
|
// ===== front (z = +1) =====
|
||||||
|
{{0.0f, 0.0f, 1.0f}, // bottom left
|
||||||
|
{0.0f, 2.0f, 1.0f}, // top left
|
||||||
|
{1.0f, 2.0f, 1.0f}, // top right
|
||||||
|
{1.0f, 2.0f, 1.0f}, // top right
|
||||||
|
{1.0f, 0.0f, 1.0f}, // bottom right
|
||||||
|
{0.0f, 0.0f, 1.0f}}, // bottom left
|
||||||
|
// ===== right (x = +1) =====
|
||||||
|
{{1.0f, 0.0f, 1.0f}, // bottom front
|
||||||
|
{1.0f, 0.0f, 0.0f}, // bottom back
|
||||||
|
{1.0f, 2.0f, 0.0f}, // top back
|
||||||
|
{1.0f, 2.0f, 0.0f}, // top back
|
||||||
|
{1.0f, 2.0f, 1.0f}, // top front
|
||||||
|
{1.0f, 0.0f, 1.0f}}, // bottom front
|
||||||
|
// ===== back (z = -1) =====
|
||||||
|
{{0.0f, 0.0f, 0.0f}, // bottom left
|
||||||
|
{1.0f, 0.0f, 0.0f}, // bottom right
|
||||||
|
{1.0f, 2.0f, 0.0f}, // top right
|
||||||
|
{1.0f, 2.0f, 0.0f}, // top right
|
||||||
|
{0.0f, 2.0f, 0.0f}, // top left
|
||||||
|
{0.0f, 0.0f, 0.0f}}, // bottom left
|
||||||
|
// ===== left (x = -1) =====
|
||||||
|
{{0.0f, 0.0f, 0.0f}, // bottom back
|
||||||
|
{0.0f, 0.0f, 1.0f}, // bottom front
|
||||||
|
{0.0f, 2.0f, 1.0f}, // top front
|
||||||
|
{0.0f, 2.0f, 1.0f}, // top front
|
||||||
|
{0.0f, 2.0f, 0.0f}, // top back
|
||||||
|
{0.0f, 0.0f, 0.0f}}, // bottom back
|
||||||
|
// ===== top (y = +2) =====
|
||||||
|
{{0.0f, 2.0f, 0.0f}, // back left
|
||||||
|
{1.0f, 2.0f, 0.0f}, // back right
|
||||||
|
{1.0f, 2.0f, 1.0f}, // front right
|
||||||
|
{1.0f, 2.0f, 1.0f}, // front right
|
||||||
|
{0.0f, 2.0f, 1.0f}, // front left
|
||||||
|
{0.0f, 2.0f, 0.0f}}, // back left
|
||||||
|
// ===== bottom (y = -1) =====
|
||||||
|
{{0.0f, 0.0f, 1.0f}, // front left
|
||||||
|
{1.0f, 0.0f, 1.0f}, // front right
|
||||||
|
{1.0f, 0.0f, 0.0f}, // back right
|
||||||
|
{1.0f, 0.0f, 0.0f}, // back right
|
||||||
|
{0.0f, 0.0f, 0.0f}, // back left
|
||||||
|
{0.0f, 0.0f, 1.0f}} // front left
|
||||||
|
};
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
// [-1, 1]
|
// [-1, 1]
|
||||||
constexpr float QUAD_VERTICES[] = {
|
constexpr float QUAD_VERTICES[] = {
|
||||||
// postion // texcoorlds
|
// postion // texcoorlds
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ private:
|
|||||||
bool m_water_depth_fade = true;
|
bool m_water_depth_fade = true;
|
||||||
bool m_pbr = true;
|
bool m_pbr = true;
|
||||||
bool m_flip_y = false;
|
bool m_flip_y = false;
|
||||||
|
|
||||||
|
bool m_init = false;
|
||||||
|
|
||||||
int m_shadow_mode = 0;
|
int m_shadow_mode = 0;
|
||||||
int m_light_cull_face = 0;
|
int m_light_cull_face = 0;
|
||||||
float m_aspect = 0.0f;
|
float m_aspect = 0.0f;
|
||||||
@@ -119,7 +122,7 @@ private:
|
|||||||
GLuint m_outline_indices_vbo = 0;
|
GLuint m_outline_indices_vbo = 0;
|
||||||
GLuint m_outline_vbo = 0;
|
GLuint m_outline_vbo = 0;
|
||||||
GLuint m_ui_vbo = 0;
|
GLuint m_ui_vbo = 0;
|
||||||
|
GLuint m_player_vbo = 0;
|
||||||
GLuint m_fbo = 0;
|
GLuint m_fbo = 0;
|
||||||
GLuint m_screen_texture = 0;
|
GLuint m_screen_texture = 0;
|
||||||
GLuint m_screen_depth_texture = 0;
|
GLuint m_screen_depth_texture = 0;
|
||||||
@@ -171,7 +174,7 @@ private:
|
|||||||
2 - outline vao
|
2 - outline vao
|
||||||
3 - ui vao
|
3 - ui vao
|
||||||
4 - text vao
|
4 - text vao
|
||||||
|
5 - player vao
|
||||||
*/
|
*/
|
||||||
std::vector<GLuint> m_vao;
|
std::vector<GLuint> m_vao;
|
||||||
std::vector<Vertex2D> m_ui;
|
std::vector<Vertex2D> m_ui;
|
||||||
@@ -186,6 +189,7 @@ private:
|
|||||||
void render_text();
|
void render_text();
|
||||||
void render_ui();
|
void render_ui();
|
||||||
void render_world();
|
void render_world();
|
||||||
|
void render_player();
|
||||||
void render_underwater();
|
void render_underwater();
|
||||||
void render_dev_panel();
|
void render_dev_panel();
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Cubed {
|
|||||||
class TextureManager {
|
class TextureManager {
|
||||||
private:
|
private:
|
||||||
bool m_need_reload = false;
|
bool m_need_reload = false;
|
||||||
|
bool m_init = false;
|
||||||
GLuint m_block_status_array = 0;
|
GLuint m_block_status_array = 0;
|
||||||
GLuint m_texture_array = 0;
|
GLuint m_texture_array = 0;
|
||||||
GLuint m_cross_plane_array = 0;
|
GLuint m_cross_plane_array = 0;
|
||||||
|
|||||||
35
include/Cubed/tools/arg_parser.hpp
Normal file
35
include/Cubed/tools/arg_parser.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace Cubed {
|
||||||
|
class ArgParser {
|
||||||
|
public:
|
||||||
|
ArgParser(int argc, char** argv) : m_args(argv, argc) {};
|
||||||
|
ArgParser(std::span<char*> args) : m_args(args) {}
|
||||||
|
|
||||||
|
bool has_next() const { return m_index < m_args.size(); }
|
||||||
|
|
||||||
|
std::string_view next() {
|
||||||
|
if (!has_next()) {
|
||||||
|
throw std::runtime_error("No more arguments");
|
||||||
|
}
|
||||||
|
return m_args[m_index++];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view require_next(std::string_view option) {
|
||||||
|
if (!has_next()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::format("{} requires an argument", option));
|
||||||
|
}
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::span<char*> m_args;
|
||||||
|
size_t m_index = 1;
|
||||||
|
};
|
||||||
|
} // namespace Cubed
|
||||||
40
include/Cubed/tools/compression.hpp
Normal file
40
include/Cubed/tools/compression.hpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include <zstd.h>
|
||||||
|
namespace Cubed {
|
||||||
|
constexpr int DEFAULT_ZSTD_LEVEL = 3;
|
||||||
|
inline std::vector<uint8_t> compress_data(std::span<const uint8_t> data) {
|
||||||
|
size_t max_size = ZSTD_compressBound(data.size());
|
||||||
|
std::vector<uint8_t> compressed_data(max_size);
|
||||||
|
size_t compressed_bytes =
|
||||||
|
ZSTD_compress(compressed_data.data(), max_size, data.data(),
|
||||||
|
data.size(), DEFAULT_ZSTD_LEVEL);
|
||||||
|
if (ZSTD_isError(compressed_bytes)) {
|
||||||
|
throw std::runtime_error(std::format(
|
||||||
|
"Compress Fail {}", ZSTD_getErrorName(compressed_bytes)));
|
||||||
|
}
|
||||||
|
compressed_data.resize(compressed_bytes);
|
||||||
|
return compressed_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::vector<uint8_t> decompress_data(std::span<const uint8_t> data,
|
||||||
|
uint32_t original_size) {
|
||||||
|
std::vector<uint8_t> decompressed_data(original_size);
|
||||||
|
size_t decompressed_bytes = ZSTD_decompress(
|
||||||
|
decompressed_data.data(), original_size, data.data(), data.size());
|
||||||
|
if (ZSTD_isError(decompressed_bytes)) {
|
||||||
|
throw std::runtime_error(std::format(
|
||||||
|
"Decompress Fail {}", ZSTD_getErrorName(decompressed_bytes)));
|
||||||
|
}
|
||||||
|
if (decompressed_bytes != original_size) {
|
||||||
|
throw std::runtime_error("Unexpected decompressed size");
|
||||||
|
}
|
||||||
|
|
||||||
|
return decompressed_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Cubed
|
||||||
@@ -23,6 +23,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_mouse_enable = false;
|
bool m_mouse_enable = false;
|
||||||
|
bool m_imgui_init = false;
|
||||||
float m_aspect;
|
float m_aspect;
|
||||||
GLFWwindow* m_window;
|
GLFWwindow* m_window;
|
||||||
int m_width;
|
int m_width;
|
||||||
|
|||||||
64
src/app.cpp
64
src/app.cpp
@@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
#include "Cubed/config.hpp"
|
#include "Cubed/config.hpp"
|
||||||
#include "Cubed/debug_collector.hpp"
|
#include "Cubed/debug_collector.hpp"
|
||||||
|
#include "Cubed/tools/arg_parser.hpp"
|
||||||
#include "Cubed/tools/cubed_assert.hpp"
|
#include "Cubed/tools/cubed_assert.hpp"
|
||||||
#include "Cubed/tools/log.hpp"
|
#include "Cubed/tools/log.hpp"
|
||||||
#include "Cubed/tools/system_info.hpp"
|
#include "Cubed/tools/system_info.hpp"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <imgui_impl_glfw.h>
|
#include <imgui_impl_glfw.h>
|
||||||
|
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
|
|
||||||
App::App() {}
|
App::App() {}
|
||||||
@@ -33,9 +33,11 @@ void App::cursor_position_callback(GLFWwindow* window, double xpos,
|
|||||||
app->m_camera.update_cursor_position_camera(xpos, ypos);
|
app->m_camera.update_cursor_position_camera(xpos, ypos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void App::init() {
|
void App::init(int argc, char** argv) {
|
||||||
|
handle_argument(argc, argv);
|
||||||
m_window.init();
|
m_window.init();
|
||||||
m_window.imgui_init();
|
m_window.imgui_init();
|
||||||
|
|
||||||
Logger::info("Window Init Success");
|
Logger::info("Window Init Success");
|
||||||
|
|
||||||
glfwSetWindowUserPointer(m_window.get_glfw_window(), this);
|
glfwSetWindowUserPointer(m_window.get_glfw_window(), this);
|
||||||
@@ -53,6 +55,7 @@ void App::init() {
|
|||||||
glfwSetCursorEnterCallback(m_window.get_glfw_window(),
|
glfwSetCursorEnterCallback(m_window.get_glfw_window(),
|
||||||
cursor_enter_callback);
|
cursor_enter_callback);
|
||||||
glfwSetCharCallback(m_window.get_glfw_window(), char_callback);
|
glfwSetCharCallback(m_window.get_glfw_window(), char_callback);
|
||||||
|
|
||||||
ChunkGenerator::init();
|
ChunkGenerator::init();
|
||||||
BlockManager::init();
|
BlockManager::init();
|
||||||
m_renderer.init();
|
m_renderer.init();
|
||||||
@@ -61,12 +64,13 @@ void App::init() {
|
|||||||
// MapTable::init_map();
|
// MapTable::init_map();
|
||||||
m_texture_manager.init_texture();
|
m_texture_manager.init_texture();
|
||||||
Logger::info("Texture Load Success");
|
Logger::info("Texture Load Success");
|
||||||
|
if (!m_argument.is_client) {
|
||||||
m_server.start_server();
|
m_server.start_server(m_argument.port);
|
||||||
|
}
|
||||||
|
|
||||||
m_client = std::make_shared<NetworkClient>(m_client_world);
|
m_client = std::make_shared<NetworkClient>(m_client_world);
|
||||||
|
|
||||||
m_client->start("127.0.0.1", 25530);
|
m_client->start(m_argument.ip, m_argument.port);
|
||||||
// init will send packet
|
// init will send packet
|
||||||
m_client_world.init("test", m_client);
|
m_client_world.init("test", m_client);
|
||||||
|
|
||||||
@@ -76,6 +80,53 @@ void App::init() {
|
|||||||
m_dev_panel.init();
|
m_dev_panel.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void App::handle_argument(int argc, char** argv) {
|
||||||
|
|
||||||
|
static const std::unordered_map<std::string_view,
|
||||||
|
std::function<void(ArgParser&)>>
|
||||||
|
HANDLERS{
|
||||||
|
|
||||||
|
{"--client", [&](ArgParser&) { m_argument.is_client = true; }},
|
||||||
|
|
||||||
|
{"--host", [&](ArgParser&) { m_argument.is_client = false; }},
|
||||||
|
|
||||||
|
{"-p",
|
||||||
|
[&](ArgParser& p) {
|
||||||
|
auto arg = p.require_next("-p");
|
||||||
|
|
||||||
|
auto r =
|
||||||
|
std::from_chars(arg.begin(), arg.end(), m_argument.port);
|
||||||
|
|
||||||
|
if (r.ec != std::errc{} || r.ptr != arg.end()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::format("Invalid port: {}", arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_argument.port > 65535) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::format("Port {} out of range", m_argument.port));
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
|
||||||
|
{"--ip",
|
||||||
|
[&](ArgParser& p) {
|
||||||
|
auto arg = p.require_next("--ip");
|
||||||
|
m_argument.ip = arg;
|
||||||
|
}}
|
||||||
|
|
||||||
|
};
|
||||||
|
ArgParser parser(argc, argv);
|
||||||
|
|
||||||
|
while (parser.has_next()) {
|
||||||
|
auto arg = parser.next();
|
||||||
|
if (auto it = HANDLERS.find(arg); it != HANDLERS.end()) {
|
||||||
|
it->second(parser);
|
||||||
|
} else {
|
||||||
|
Logger::warn("Unknown argument: {}", arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void App::key_callback(GLFWwindow* window, int key, int scancode, int action,
|
void App::key_callback(GLFWwindow* window, int key, int scancode, int action,
|
||||||
int mods) {
|
int mods) {
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
@@ -233,6 +284,7 @@ void App::run() {
|
|||||||
update();
|
update();
|
||||||
render();
|
render();
|
||||||
}
|
}
|
||||||
|
m_client_world.exit();
|
||||||
}
|
}
|
||||||
static Gait player_gait = Gait::WALK;
|
static Gait player_gait = Gait::WALK;
|
||||||
void App::update() {
|
void App::update() {
|
||||||
@@ -276,7 +328,7 @@ int App::start_cubed_application(int argc, char** argv) {
|
|||||||
App app;
|
App app;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
app.init();
|
app.init(argc, argv);
|
||||||
Logger::info("Game Init Finish Start Run...");
|
Logger::info("Game Init Finish Start Run...");
|
||||||
app.run();
|
app.run();
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ void DevPanel::show_about_table_bar() {
|
|||||||
ImGui::Text("Dear ImGui");
|
ImGui::Text("Dear ImGui");
|
||||||
ImGui::Text("Tbb");
|
ImGui::Text("Tbb");
|
||||||
ImGui::Text("Asio");
|
ImGui::Text("Asio");
|
||||||
|
ImGui::Text("protobuf");
|
||||||
|
ImGui::Text("zstd");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Special Thanks");
|
ImGui::Text("Special Thanks");
|
||||||
ImGui::Text("TANGERIME");
|
ImGui::Text("TANGERIME");
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ ClientWorld::ClientWorld() : m_player(*this) {}
|
|||||||
|
|
||||||
ClientWorld::~ClientWorld() {
|
ClientWorld::~ClientWorld() {
|
||||||
stop_client_thread();
|
stop_client_thread();
|
||||||
|
stop_thread_pool();
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_chunks_mutex);
|
std::lock_guard lock(m_chunks_mutex);
|
||||||
m_chunks.clear();
|
m_chunks.clear();
|
||||||
@@ -190,18 +190,44 @@ void ClientWorld::receive_time(const UpdateTime& rsp) {
|
|||||||
m_day_tick = rsp.day_tick();
|
m_day_tick = rsp.day_tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientWorld::receive_other_player(const PlayerInfoRsp& rsp) {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_other_players_mutex);
|
||||||
|
m_other_players[rsp.uuid()].name = rsp.name();
|
||||||
|
m_other_players[rsp.uuid()].pos =
|
||||||
|
glm::vec3{rsp.pos().x(), rsp.pos().y(), rsp.pos().z()};
|
||||||
|
// Logger::info("Player {} pos Update", rsp.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientWorld::receive_player_logout(const LogoutRsp& rsp) {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_other_players_mutex);
|
||||||
|
int sum = m_other_players.erase(rsp.uuid());
|
||||||
|
if (sum == 0) {
|
||||||
|
Logger::warn("Player {} not find", rsp.uuid());
|
||||||
|
} else {
|
||||||
|
Logger::info("Player {} erase", rsp.uuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClientWorld::init(std::string_view player_name,
|
void ClientWorld::init(std::string_view player_name,
|
||||||
std::shared_ptr<NetworkClient> client) {
|
std::shared_ptr<NetworkClient> client) {
|
||||||
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
||||||
m_player.init(player_name);
|
m_player.init(player_name);
|
||||||
m_client = client;
|
m_client = client;
|
||||||
// timer
|
// timer
|
||||||
register_timer("player_pos", 2, [this]() { report_player_pos(); });
|
register_timer("player_pos", 1, [this]() { report_player_pos(); });
|
||||||
LoginReq req;
|
LoginReq req;
|
||||||
req.set_name(m_player.get_name());
|
req.set_name(m_player.get_name());
|
||||||
while (!client->is_connected()) {
|
while (!client->is_connected()) {
|
||||||
|
if (client->is_connect_error()) {
|
||||||
|
throw std::runtime_error("Can't connect to the server");
|
||||||
|
}
|
||||||
std::this_thread::sleep_for(milliseconds(200));
|
std::this_thread::sleep_for(milliseconds(200));
|
||||||
}
|
}
|
||||||
|
start_thread_pool();
|
||||||
// request login
|
// request login
|
||||||
Logger::info("Send Login Request");
|
Logger::info("Send Login Request");
|
||||||
m_client->send(make_packet(req));
|
m_client->send(make_packet(req));
|
||||||
@@ -228,6 +254,26 @@ void ClientWorld::stop_client_thread() {
|
|||||||
}
|
}
|
||||||
m_game_running = false;
|
m_game_running = false;
|
||||||
}
|
}
|
||||||
|
void ClientWorld::start_thread_pool() { change_pool_threads(1); }
|
||||||
|
void ClientWorld::stop_thread_pool() {
|
||||||
|
auto pool_ptr = m_thread_pool.load();
|
||||||
|
if (pool_ptr) {
|
||||||
|
pool_ptr->stop();
|
||||||
|
}
|
||||||
|
m_thread_pool.store(nullptr);
|
||||||
|
Logger::info("Thread Pool Stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientWorld::change_pool_threads(int threads) {
|
||||||
|
int m_max_threads = std::thread::hardware_concurrency();
|
||||||
|
if (m_max_threads < 1) {
|
||||||
|
Logger::warn("Can't Get Max Support Threads, Set Max Threads to 4");
|
||||||
|
m_max_threads = 1;
|
||||||
|
}
|
||||||
|
int used_thread = std::clamp(threads, 1, m_max_threads);
|
||||||
|
Logger::info("Create New Thread Pool Use {} Threads", used_thread);
|
||||||
|
m_thread_pool.store(std::make_shared<ThreadPool>(used_thread));
|
||||||
|
}
|
||||||
|
|
||||||
void ClientWorld::hot_reload() {
|
void ClientWorld::hot_reload() {
|
||||||
auto& config = Config::get();
|
auto& config = Config::get();
|
||||||
@@ -238,28 +284,16 @@ void ClientWorld::hot_reload() {
|
|||||||
void ClientWorld::client_run(std::stop_token stoken) {
|
void ClientWorld::client_run(std::stop_token stoken) {
|
||||||
Logger::info("Client Thread Started");
|
Logger::info("Client Thread Started");
|
||||||
while (!stoken.stop_requested()) {
|
while (!stoken.stop_requested()) {
|
||||||
|
auto t1 = system_clock::now();
|
||||||
|
|
||||||
for (auto& x : m_timers) {
|
for (auto& x : m_timers) {
|
||||||
x.second.update();
|
x.second.update();
|
||||||
}
|
}
|
||||||
// vertex data will genrator in client thread instead of net thread;
|
|
||||||
std::vector<ChunkDataRsp> temp_data;
|
|
||||||
{
|
|
||||||
std::lock_guard lock(m_pending_chunk_data_queue_mutex);
|
|
||||||
for (auto& x : m_pending_chunk_data_queue) {
|
|
||||||
temp_data.emplace_back(std::move(x));
|
|
||||||
}
|
|
||||||
m_pending_chunk_data_queue.clear();
|
|
||||||
}
|
|
||||||
for (auto& x : temp_data) {
|
|
||||||
ClientChunk chunk{*this};
|
|
||||||
chunk.receive_chunk(x);
|
|
||||||
{
|
|
||||||
std::lock_guard lock(m_pending_upload_queue_mutex);
|
|
||||||
m_pending_upload_queue.emplace_back(std::move(chunk));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(milliseconds(DEFAULT_PER_TICK_TIME));
|
auto t2 = system_clock::now();
|
||||||
|
auto dt = duration_cast<microseconds>(t2 - t1);
|
||||||
|
auto st = std::max(dt, milliseconds(DEFAULT_PER_TICK_TIME) - dt);
|
||||||
|
std::this_thread::sleep_for(st);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,11 +401,26 @@ void ClientWorld::receive_chunk(const ChunkDataRsp& data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// vertex data will genrator in client thread pool instead of net thread;
|
||||||
{
|
auto pool = m_thread_pool.load();
|
||||||
std::lock_guard lock(m_pending_chunk_data_queue_mutex);
|
if (!pool) {
|
||||||
m_pending_chunk_data_queue.emplace_back(std::move(data));
|
Logger::error("Client Thread Pool is nullptr");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
pool->enqueue([this, data = std::move(data)]() {
|
||||||
|
ClientChunk chunk{*this};
|
||||||
|
chunk.receive_chunk(data);
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_pending_upload_queue_mutex);
|
||||||
|
m_pending_upload_queue.emplace_back(std::move(chunk));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientWorld::exit() {
|
||||||
|
LogoutReq req;
|
||||||
|
req.set_uuid(m_player.get_uuid());
|
||||||
|
m_client->send(make_packet(req));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWorld::update(float delta_time) {
|
void ClientWorld::update(float delta_time) {
|
||||||
@@ -447,6 +496,13 @@ void ClientWorld::update(float delta_time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_render_player_data.clear();
|
||||||
|
{
|
||||||
|
std::lock_guard lock(m_other_players_mutex);
|
||||||
|
for (auto& [uuid, player] : m_other_players) {
|
||||||
|
m_render_player_data.emplace_back(player.name, player.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 ClientWorld::sunlight_dir() const {
|
glm::vec3 ClientWorld::sunlight_dir() const {
|
||||||
@@ -473,8 +529,11 @@ int ClientWorld::rendering_distance() const {
|
|||||||
void ClientWorld::rendering_distance(int rendering_distance) {
|
void ClientWorld::rendering_distance(int rendering_distance) {
|
||||||
m_rendering_distance = rendering_distance;
|
m_rendering_distance = rendering_distance;
|
||||||
}
|
}
|
||||||
std::vector<ChunkRenderSnapshot>& ClientWorld::render_snapshots() {
|
const std::vector<ChunkRenderSnapshot>& ClientWorld::render_snapshots() const {
|
||||||
return m_render_snapshots;
|
return m_render_snapshots;
|
||||||
};
|
};
|
||||||
|
const std::vector<RenderPlayerData>& ClientWorld::render_player_data() const {
|
||||||
|
return m_render_player_data;
|
||||||
|
}
|
||||||
std::vector<glm::vec4>& ClientWorld::planes() { return m_planes; }
|
std::vector<glm::vec4>& ClientWorld::planes() { return m_planes; }
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -20,7 +20,7 @@ void NetworkClient::start(std::string ip, int port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkClient::is_connected() const { return m_connected.load(); }
|
bool NetworkClient::is_connected() const { return m_connected.load(); }
|
||||||
|
bool NetworkClient::is_connect_error() const { return m_connect_error.load(); }
|
||||||
asio::awaitable<void> NetworkClient::connect(std::string ip, int port) {
|
asio::awaitable<void> NetworkClient::connect(std::string ip, int port) {
|
||||||
Logger::info("Connect Begin");
|
Logger::info("Connect Begin");
|
||||||
try {
|
try {
|
||||||
@@ -38,39 +38,35 @@ asio::awaitable<void> NetworkClient::connect(std::string ip, int port) {
|
|||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
Logger::error("Client Error {}", e.what());
|
Logger::error("Client Error {}", e.what());
|
||||||
|
m_connect_error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
asio::awaitable<void> NetworkClient::read_loop() {
|
asio::awaitable<void> NetworkClient::read_loop() {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
std::array<char, HEADER_LEN> header;
|
std::array<uint8_t, HEADER_LEN> header_buffer;
|
||||||
co_await asio::async_read(m_socket, asio::buffer(header),
|
co_await asio::async_read(m_socket, asio::buffer(header_buffer),
|
||||||
asio::use_awaitable);
|
asio::use_awaitable);
|
||||||
uint32_t total_len_net;
|
auto header = decode_packet_header(header_buffer);
|
||||||
std::memcpy(&total_len_net, header.data(), sizeof(total_len_net));
|
uint32_t total_len = HEADER_LEN + header.compressed_size;
|
||||||
uint32_t total_len = ntohl(total_len_net);
|
|
||||||
|
|
||||||
uint16_t cmd_id_net;
|
|
||||||
std::memcpy(&cmd_id_net, header.data() + 4, sizeof(cmd_id_net));
|
|
||||||
uint16_t cmd_id = ntohs(cmd_id_net);
|
|
||||||
if (total_len < HEADER_LEN || total_len > MAX_PACKET_SIZE) {
|
if (total_len < HEADER_LEN || total_len > MAX_PACKET_SIZE) {
|
||||||
|
|
||||||
throw std::runtime_error("invalid packet length");
|
throw std::runtime_error("invalid packet length");
|
||||||
}
|
}
|
||||||
uint32_t body_len = total_len - HEADER_LEN;
|
std::vector<uint8_t> body_data(header.compressed_size);
|
||||||
std::vector<uint8_t> body_data(body_len);
|
if (header.compressed_size > 0) {
|
||||||
if (body_len > 0) {
|
|
||||||
co_await asio::async_read(m_socket, asio::buffer(body_data),
|
co_await asio::async_read(m_socket, asio::buffer(body_data),
|
||||||
asio::use_awaitable);
|
asio::use_awaitable);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto& to_num = std::to_underlying<PacketEnum>;
|
constexpr auto& to_num = std::to_underlying<PacketEnum>;
|
||||||
switch (cmd_id) {
|
switch (header.cmd) {
|
||||||
case to_num(PacketEnum::LOGIN_RSP): {
|
case to_num(PacketEnum::LOGIN_RSP): {
|
||||||
LoginRsp rsp;
|
LoginRsp rsp;
|
||||||
Logger::info("Client: Receive Login rsp");
|
Logger::info("Client: Receive Login rsp");
|
||||||
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(rsp, body_data, header)) {
|
||||||
if (rsp.success()) {
|
if (rsp.success()) {
|
||||||
m_world.start_client_thread(rsp.uuid());
|
m_world.start_client_thread(rsp.uuid());
|
||||||
} else {
|
} else {
|
||||||
@@ -82,23 +78,35 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
|||||||
ChunkDataRsp rsp;
|
ChunkDataRsp rsp;
|
||||||
Logger::info("Client: Receive Chunk Data rsp, size {}mb",
|
Logger::info("Client: Receive Chunk Data rsp, size {}mb",
|
||||||
body_data.size() / 1024.0f / 1024);
|
body_data.size() / 1024.0f / 1024);
|
||||||
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(rsp, body_data, header)) {
|
||||||
m_world.receive_chunk(rsp);
|
m_world.receive_chunk(rsp);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case to_num(PacketEnum::BLOCK_CHANGE_RSP): {
|
case to_num(PacketEnum::BLOCK_CHANGE_RSP): {
|
||||||
BlockChangeRsp rsp;
|
BlockChangeRsp rsp;
|
||||||
Logger::info("Client: Receive Block Change rsp");
|
Logger::info("Client: Receive Block Change rsp");
|
||||||
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(rsp, body_data, header)) {
|
||||||
m_world.receive_block_change(rsp);
|
m_world.receive_block_change(rsp);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case to_num(PacketEnum::UPDATE_TIME): {
|
case to_num(PacketEnum::UPDATE_TIME): {
|
||||||
UpdateTime rsp;
|
UpdateTime rsp;
|
||||||
if (rsp.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(rsp, body_data, header)) {
|
||||||
m_world.receive_time(rsp);
|
m_world.receive_time(rsp);
|
||||||
}
|
}
|
||||||
}
|
} break;
|
||||||
|
case to_num(PacketEnum::PLAYER_INFO_RSP): {
|
||||||
|
PlayerInfoRsp rsp;
|
||||||
|
if (decode_packet(rsp, body_data, header)) {
|
||||||
|
m_world.receive_other_player(rsp);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case to_num(PacketEnum::LOGOUT_RSP): {
|
||||||
|
LogoutRsp rsp;
|
||||||
|
if (decode_packet(rsp, body_data, header)) {
|
||||||
|
m_world.receive_player_logout(rsp);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const asio::system_error& e) {
|
} catch (const asio::system_error& e) {
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ NetworkServer::NetworkServer(int port) : m_port(port) {}
|
|||||||
NetworkServer::~NetworkServer() { stop(); }
|
NetworkServer::~NetworkServer() { stop(); }
|
||||||
|
|
||||||
void NetworkServer::stop() {
|
void NetworkServer::stop() {
|
||||||
|
if (!m_started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_stopped.exchange(true)) {
|
if (m_stopped.exchange(true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -79,9 +82,10 @@ void NetworkServer::net_run() {
|
|||||||
Logger::info("Server Started!");
|
Logger::info("Server Started!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkServer::start_server() {
|
void NetworkServer::start_server(int port) {
|
||||||
m_world.init_world();
|
m_world.init_world();
|
||||||
net_run();
|
net_run();
|
||||||
|
m_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NetworkServer::port() const { return m_port; }
|
int NetworkServer::port() const { return m_port; }
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
#include "Cubed/gameplay/server_world.hpp"
|
#include "Cubed/gameplay/server_world.hpp"
|
||||||
namespace Cubed {
|
namespace Cubed {
|
||||||
ServerPlayer::ServerPlayer(std::string_view name, std::string_view uuid,
|
ServerPlayer::ServerPlayer(std::string_view name, std::string_view uuid,
|
||||||
ServerWorld& world, std::shared_ptr<Session> session)
|
ServerWorld& world, std::shared_ptr<Session> session,
|
||||||
: m_name(name), m_uuid(uuid), m_world(world), m_session(session) {}
|
TickType gametick)
|
||||||
|
: m_name(name), m_uuid(uuid), m_world(world), m_session(session),
|
||||||
|
m_last_gametick(gametick) {}
|
||||||
const glm::vec3& ServerPlayer::get_pos() const { return m_pos; }
|
const glm::vec3& ServerPlayer::get_pos() const { return m_pos; }
|
||||||
const std::string& ServerPlayer::get_name() const { return m_name; }
|
const std::string& ServerPlayer::get_name() const { return m_name; }
|
||||||
const std::string& ServerPlayer::get_uuid() const { return m_uuid; }
|
const std::string& ServerPlayer::get_uuid() const { return m_uuid; }
|
||||||
@@ -18,4 +20,15 @@ void ServerPlayer::update_pos(float x, float y, float z) {
|
|||||||
m_last_chunk_pos = chunk_pos;
|
m_last_chunk_pos = chunk_pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerPlayer::update_sync_gametick(TickType gametick) {
|
||||||
|
m_last_gametick = gametick;
|
||||||
|
}
|
||||||
|
bool ServerPlayer::is_disconnect(TickType current_gametick) const {
|
||||||
|
if (current_gametick - m_last_gametick > TIMEOUT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
@@ -15,6 +15,9 @@ namespace Cubed {
|
|||||||
ServerWorld::ServerWorld() {}
|
ServerWorld::ServerWorld() {}
|
||||||
|
|
||||||
ServerWorld::~ServerWorld() {
|
ServerWorld::~ServerWorld() {
|
||||||
|
if (!m_init) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
stop_gen_thread();
|
stop_gen_thread();
|
||||||
stop_server_thread();
|
stop_server_thread();
|
||||||
wait_all_chunk_tasks();
|
wait_all_chunk_tasks();
|
||||||
@@ -43,6 +46,22 @@ void ServerWorld::send_time() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::init_world() {
|
void ServerWorld::init_world() {
|
||||||
|
|
||||||
|
register_timer("player disconnect", 5, [this]() {
|
||||||
|
std::vector<std::string> disconnect;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_player_mutex);
|
||||||
|
for (auto& [uuid, player] : m_players) {
|
||||||
|
if (player.is_disconnect(m_game_ticks)) {
|
||||||
|
disconnect.emplace_back(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& uuid : disconnect) {
|
||||||
|
handle_player_exit(uuid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
m_cave_carcer.init(ChunkGenerator::seed());
|
m_cave_carcer.init(ChunkGenerator::seed());
|
||||||
m_river_worm.init(ChunkGenerator::seed());
|
m_river_worm.init(ChunkGenerator::seed());
|
||||||
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
|
||||||
@@ -50,9 +69,6 @@ void ServerWorld::init_world() {
|
|||||||
|
|
||||||
auto t1 = std::chrono::system_clock::now();
|
auto t1 = std::chrono::system_clock::now();
|
||||||
|
|
||||||
// init players
|
|
||||||
// m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer"));
|
|
||||||
|
|
||||||
start_gen_thread();
|
start_gen_thread();
|
||||||
init_chunks();
|
init_chunks();
|
||||||
auto t2 = std::chrono::system_clock::now();
|
auto t2 = std::chrono::system_clock::now();
|
||||||
@@ -60,6 +76,7 @@ void ServerWorld::init_world() {
|
|||||||
Logger::info("Chunk Block Init Finish, Time Consuming: {}", d);
|
Logger::info("Chunk Block Init Finish, Time Consuming: {}", d);
|
||||||
|
|
||||||
start_server_thread();
|
start_server_thread();
|
||||||
|
m_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::init_chunks() { hot_reload(); }
|
void ServerWorld::init_chunks() { hot_reload(); }
|
||||||
@@ -396,13 +413,41 @@ void ServerWorld::update() {
|
|||||||
|
|
||||||
void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y,
|
void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y,
|
||||||
float z) {
|
float z) {
|
||||||
std::lock_guard lock(m_player_mutex);
|
std::string name;
|
||||||
auto it = m_players.find(uuid);
|
{
|
||||||
if (it == m_players.end()) {
|
std::lock_guard lock(m_player_mutex);
|
||||||
Logger::warn("Player {} is not in this Server", uuid);
|
auto it = m_players.find(uuid);
|
||||||
return;
|
if (it == m_players.end()) {
|
||||||
|
Logger::warn("Player {} is not in this Server", uuid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
it->second.update_pos(x, y, z);
|
||||||
|
it->second.update_sync_gametick(m_game_ticks);
|
||||||
|
name = it->second.get_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
// update other player pos;
|
||||||
|
std::vector<std::shared_ptr<Session>> other;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_player_mutex);
|
||||||
|
for (auto& [o_uuid, player] : m_players) {
|
||||||
|
if (o_uuid == uuid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
other.emplace_back(player.get_session());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& session : other) {
|
||||||
|
PlayerInfoRsp rsp;
|
||||||
|
rsp.set_uuid(uuid);
|
||||||
|
rsp.set_name(name);
|
||||||
|
auto* pos = rsp.mutable_pos();
|
||||||
|
pos->set_x(x);
|
||||||
|
pos->set_y(y);
|
||||||
|
pos->set_z(z);
|
||||||
|
session->send(make_packet(rsp));
|
||||||
}
|
}
|
||||||
it->second.update_pos(x, y, z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerWorld::handle_player_login(const std::string& name,
|
void ServerWorld::handle_player_login(const std::string& name,
|
||||||
@@ -411,9 +456,9 @@ void ServerWorld::handle_player_login(const std::string& name,
|
|||||||
Logger::info("Player {} (uuid {}) join the world", name, uuid);
|
Logger::info("Player {} (uuid {}) join the world", name, uuid);
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_player_mutex);
|
std::lock_guard lock(m_player_mutex);
|
||||||
m_players.emplace(std::piecewise_construct,
|
m_players.emplace(
|
||||||
std::forward_as_tuple(std::string(uuid)),
|
std::piecewise_construct, std::forward_as_tuple(std::string(uuid)),
|
||||||
std::forward_as_tuple(name, uuid, *this, session));
|
std::forward_as_tuple(name, uuid, *this, session, m_game_ticks));
|
||||||
}
|
}
|
||||||
m_uuid_to_name.emplace(uuid, name);
|
m_uuid_to_name.emplace(uuid, name);
|
||||||
LoginRsp rsp;
|
LoginRsp rsp;
|
||||||
@@ -427,13 +472,29 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
|
|||||||
std::lock_guard lock(m_player_mutex);
|
std::lock_guard lock(m_player_mutex);
|
||||||
auto it = m_players.find(uuid);
|
auto it = m_players.find(uuid);
|
||||||
if (it != m_players.end()) {
|
if (it != m_players.end()) {
|
||||||
|
Logger::info("Player {} Exit the Server", it->second.get_name());
|
||||||
m_players.erase(it);
|
m_players.erase(it);
|
||||||
} else {
|
} else {
|
||||||
Logger::error("Player {} isn't in Server", uuid);
|
Logger::error("Player {} isn't in Server", uuid);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_uuid_to_name.erase(uuid);
|
m_uuid_to_name.erase(uuid);
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Session>> sessions;
|
||||||
|
{
|
||||||
|
std::shared_lock lock(m_player_mutex);
|
||||||
|
for (auto& [uuid, player] : m_players) {
|
||||||
|
sessions.emplace_back(player.get_session());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& s : sessions) {
|
||||||
|
LogoutRsp rsp;
|
||||||
|
rsp.set_uuid(uuid);
|
||||||
|
s->send(make_packet(rsp));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 ServerWorld::get_player_pos(const std::string& uuid) const {
|
glm::vec3 ServerWorld::get_player_pos(const std::string& uuid) const {
|
||||||
@@ -488,6 +549,7 @@ void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) {
|
|||||||
auto it = m_players.find(uuid);
|
auto it = m_players.find(uuid);
|
||||||
if (it != m_players.end()) {
|
if (it != m_players.end()) {
|
||||||
s = it->second.get_session();
|
s = it->second.get_session();
|
||||||
|
it->second.update_sync_gametick(m_game_ticks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!s) {
|
if (!s) {
|
||||||
|
|||||||
@@ -37,39 +37,35 @@ const std::string& Session::uuid() const { return m_uuid; }
|
|||||||
asio::awaitable<void> Session::read_loop() {
|
asio::awaitable<void> Session::read_loop() {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
std::array<char, HEADER_LEN> header;
|
std::array<uint8_t, HEADER_LEN> header_buffer;
|
||||||
co_await asio::async_read(m_socket, asio::buffer(header),
|
co_await asio::async_read(m_socket, asio::buffer(header_buffer),
|
||||||
asio::use_awaitable);
|
asio::use_awaitable);
|
||||||
|
|
||||||
uint32_t total_len_net;
|
auto header = decode_packet_header(header_buffer);
|
||||||
std::memcpy(&total_len_net, header.data(), sizeof(total_len_net));
|
uint32_t total_len = HEADER_LEN + header.compressed_size;
|
||||||
uint32_t total_len = ntohl(total_len_net);
|
|
||||||
|
|
||||||
uint16_t cmd_id_net;
|
|
||||||
std::memcpy(&cmd_id_net, header.data() + 4, sizeof(cmd_id_net));
|
|
||||||
uint16_t cmd_id = ntohs(cmd_id_net);
|
|
||||||
if (total_len < HEADER_LEN || total_len > MAX_PACKET_SIZE) {
|
if (total_len < HEADER_LEN || total_len > MAX_PACKET_SIZE) {
|
||||||
|
|
||||||
throw std::runtime_error("invalid packet length");
|
throw std::runtime_error("invalid packet length");
|
||||||
}
|
}
|
||||||
uint32_t body_len = total_len - HEADER_LEN;
|
std::vector<uint8_t> body_data(header.compressed_size);
|
||||||
std::vector<uint8_t> body_data(body_len);
|
if (header.compressed_size > 0) {
|
||||||
if (body_len > 0) {
|
|
||||||
co_await asio::async_read(m_socket, asio::buffer(body_data),
|
co_await asio::async_read(m_socket, asio::buffer(body_data),
|
||||||
asio::use_awaitable);
|
asio::use_awaitable);
|
||||||
}
|
}
|
||||||
constexpr auto& to_num = std::to_underlying<PacketEnum>;
|
constexpr auto& to_num = std::to_underlying<PacketEnum>;
|
||||||
|
auto cmd_id = header.cmd;
|
||||||
if (cmd_id == to_num(PacketEnum::LOGIN_REQ)) {
|
if (cmd_id == to_num(PacketEnum::LOGIN_REQ)) {
|
||||||
LoginReq req;
|
LoginReq req;
|
||||||
Logger::info("Session: Receive Login req");
|
Logger::info("Session: Receive Login req");
|
||||||
if (req.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(req, body_data, header)) {
|
||||||
m_server_world.handle_player_login(req.name(),
|
m_server_world.handle_player_login(req.name(),
|
||||||
shared_from_this());
|
shared_from_this());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cmd_id == to_num(PacketEnum::PLAYER_POS)) {
|
if (cmd_id == to_num(PacketEnum::PLAYER_POS)) {
|
||||||
PlayerPos pos;
|
PlayerPos pos;
|
||||||
if (pos.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(pos, body_data, header)) {
|
||||||
m_server_world.sync_player_pos(pos.uuid(), pos.pos().x(),
|
m_server_world.sync_player_pos(pos.uuid(), pos.pos().x(),
|
||||||
pos.pos().y(),
|
pos.pos().y(),
|
||||||
pos.pos().z());
|
pos.pos().z());
|
||||||
@@ -77,8 +73,8 @@ asio::awaitable<void> Session::read_loop() {
|
|||||||
}
|
}
|
||||||
if (cmd_id == to_num(PacketEnum::CHUNK_DATA_REQ)) {
|
if (cmd_id == to_num(PacketEnum::CHUNK_DATA_REQ)) {
|
||||||
ChunkDataReq req;
|
ChunkDataReq req;
|
||||||
Logger::info("Session: Receive Chunk Data req");
|
// Logger::info("Session: Receive Chunk Data req");
|
||||||
if (req.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(req, body_data, header)) {
|
||||||
m_server_world.handle_chunk_req(
|
m_server_world.handle_chunk_req(
|
||||||
req.uuid(), ChunkPos(req.pos().x(), req.pos().z()));
|
req.uuid(), ChunkPos(req.pos().x(), req.pos().z()));
|
||||||
}
|
}
|
||||||
@@ -86,10 +82,16 @@ asio::awaitable<void> Session::read_loop() {
|
|||||||
if (cmd_id == to_num(PacketEnum::BLOCK_CHANGE_REQ)) {
|
if (cmd_id == to_num(PacketEnum::BLOCK_CHANGE_REQ)) {
|
||||||
BlockChangeReq req;
|
BlockChangeReq req;
|
||||||
Logger::info("Session: Receive Block Change req");
|
Logger::info("Session: Receive Block Change req");
|
||||||
if (req.ParseFromArray(body_data.data(), body_data.size())) {
|
if (decode_packet(req, body_data, header)) {
|
||||||
m_server_world.handle_block_change(req);
|
m_server_world.handle_block_change(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cmd_id == to_num(PacketEnum::LOGOUT_REQ)) {
|
||||||
|
LogoutReq req;
|
||||||
|
if (decode_packet(req, body_data, header)) {
|
||||||
|
m_server_world.handle_player_exit(req.uuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (const asio::system_error& e) {
|
} catch (const asio::system_error& e) {
|
||||||
auto ec = e.code();
|
auto ec = e.code();
|
||||||
|
|||||||
@@ -9,3 +9,11 @@ message LoginRsp {
|
|||||||
string uuid = 2;
|
string uuid = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message LogoutReq {
|
||||||
|
string uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message LogoutRsp {
|
||||||
|
string uuid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,10 @@ import "common/vector3.proto";
|
|||||||
message PlayerPos {
|
message PlayerPos {
|
||||||
string uuid = 1;
|
string uuid = 1;
|
||||||
Vec3 pos = 2;
|
Vec3 pos = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PlayerInfoRsp {
|
||||||
|
string uuid = 1;
|
||||||
|
string name = 2;
|
||||||
|
Vec3 pos = 3;
|
||||||
}
|
}
|
||||||
@@ -27,25 +27,28 @@ Renderer::Renderer(const Camera& camera, ClientWorld& world,
|
|||||||
m_texture_manager(texture_manager), m_world(world) {}
|
m_texture_manager(texture_manager), m_world(world) {}
|
||||||
|
|
||||||
Renderer::~Renderer() {
|
Renderer::~Renderer() {
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
if (m_init) {
|
||||||
glDeleteBuffers(1, &m_outline_vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
glDeleteBuffers(1, &m_outline_indices_vbo);
|
glDeleteBuffers(1, &m_outline_vbo);
|
||||||
glDeleteBuffers(1, &m_sky_vbo);
|
glDeleteBuffers(1, &m_outline_indices_vbo);
|
||||||
glDeleteBuffers(1, &m_ui_vbo);
|
glDeleteBuffers(1, &m_sky_vbo);
|
||||||
glDeleteBuffers(1, &m_text_vbo);
|
glDeleteBuffers(1, &m_ui_vbo);
|
||||||
glBindVertexArray(0);
|
glDeleteBuffers(1, &m_text_vbo);
|
||||||
glDeleteVertexArrays(NUM_VAO, m_vao.data());
|
glDeleteBuffers(1, &m_player_vbo);
|
||||||
glDeleteFramebuffers(1, &m_fbo);
|
glBindVertexArray(0);
|
||||||
glDeleteTextures(1, &m_screen_texture);
|
glDeleteVertexArrays(NUM_VAO, m_vao.data());
|
||||||
glDeleteTextures(1, &m_screen_depth_texture);
|
glDeleteFramebuffers(1, &m_fbo);
|
||||||
|
glDeleteTextures(1, &m_screen_texture);
|
||||||
|
glDeleteTextures(1, &m_screen_depth_texture);
|
||||||
|
|
||||||
glDeleteFramebuffers(1, &m_oit_fbo);
|
glDeleteFramebuffers(1, &m_oit_fbo);
|
||||||
glDeleteTextures(1, &m_accum_texture);
|
glDeleteTextures(1, &m_accum_texture);
|
||||||
glDeleteTextures(1, &m_reveal_texture);
|
glDeleteTextures(1, &m_reveal_texture);
|
||||||
glDeleteTextures(1, &m_oit_depth_texture);
|
glDeleteTextures(1, &m_oit_depth_texture);
|
||||||
|
|
||||||
glDeleteFramebuffers(1, &m_depth_map_fbo);
|
glDeleteFramebuffers(1, &m_depth_map_fbo);
|
||||||
glDeleteTextures(1, &m_depth_map_texture);
|
glDeleteTextures(1, &m_depth_map_texture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::hot_reload() {
|
void Renderer::hot_reload() {
|
||||||
@@ -86,7 +89,9 @@ void Renderer::init() {
|
|||||||
"shaders/billboard_f_shader.glsl"};
|
"shaders/billboard_f_shader.glsl"};
|
||||||
Shader water_shader{"water", "shaders/water_v_shader.glsl",
|
Shader water_shader{"water", "shaders/water_v_shader.glsl",
|
||||||
"shaders/water_f_shader.glsl"};
|
"shaders/water_f_shader.glsl"};
|
||||||
|
Shader player_shader{"player", "shaders/player_v_shader.glsl",
|
||||||
|
"shaders/player_f_shader.glsl"};
|
||||||
|
m_shaders.insert({player_shader.hash(), std::move(player_shader)});
|
||||||
m_shaders.insert({world_shader.hash(), std::move(world_shader)});
|
m_shaders.insert({world_shader.hash(), std::move(world_shader)});
|
||||||
m_shaders.insert({outline_shader.hash(), std::move(outline_shader)});
|
m_shaders.insert({outline_shader.hash(), std::move(outline_shader)});
|
||||||
m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)});
|
m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)});
|
||||||
@@ -100,6 +105,7 @@ void Renderer::init() {
|
|||||||
m_shaders.insert({depth_shader.hash(), std::move(depth_shader)});
|
m_shaders.insert({depth_shader.hash(), std::move(depth_shader)});
|
||||||
m_shaders.insert({billboard.hash(), std::move(billboard)});
|
m_shaders.insert({billboard.hash(), std::move(billboard)});
|
||||||
m_shaders.insert({water_shader.hash(), std::move(water_shader)});
|
m_shaders.insert({water_shader.hash(), std::move(water_shader)});
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glDepthFunc(GL_LEQUAL);
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
|
||||||
@@ -165,12 +171,22 @@ void Renderer::init() {
|
|||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
glEnableVertexAttribArray(2);
|
glEnableVertexAttribArray(2);
|
||||||
|
|
||||||
|
glBindVertexArray(m_vao[5]);
|
||||||
|
glGenBuffers(1, &m_player_vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, m_player_vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_PLAYER), VERTICES_PLAYER,
|
||||||
|
GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
init_quad();
|
init_quad();
|
||||||
init_text();
|
init_text();
|
||||||
hot_reload();
|
hot_reload();
|
||||||
|
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
m_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Shader& Renderer::get_shader(const std::string& name) const {
|
const Shader& Renderer::get_shader(const std::string& name) const {
|
||||||
@@ -214,7 +230,7 @@ void Renderer::render() {
|
|||||||
render_sky();
|
render_sky();
|
||||||
render_world();
|
render_world();
|
||||||
render_outline();
|
render_outline();
|
||||||
|
render_player();
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
@@ -924,6 +940,26 @@ void Renderer::render_world() {
|
|||||||
DebugCollector::get().report(
|
DebugCollector::get().report(
|
||||||
"rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum));
|
"rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::render_player() {
|
||||||
|
auto& shader = get_shader("player");
|
||||||
|
shader.use();
|
||||||
|
m_v_mat = m_camera.get_camera_lookat();
|
||||||
|
|
||||||
|
auto& players = m_world.render_player_data();
|
||||||
|
|
||||||
|
for (auto& player : players) {
|
||||||
|
m_m_mat = glm::translate(glm::mat4(1.0f),
|
||||||
|
player.pos + glm::vec3(-0.5f, 0.0f, -0.5f));
|
||||||
|
m_mv_mat = m_v_mat * m_m_mat;
|
||||||
|
shader.set_loc("mv_matrix", m_mv_mat);
|
||||||
|
shader.set_loc("proj_matrix", m_p_mat);
|
||||||
|
glBindVertexArray(m_vao[5]);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
void Renderer::render_dev_panel() {
|
void Renderer::render_dev_panel() {
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|||||||
@@ -36,14 +36,16 @@ TextureManager::TextureManager() {}
|
|||||||
TextureManager::~TextureManager() { delet_texture(); }
|
TextureManager::~TextureManager() { delet_texture(); }
|
||||||
|
|
||||||
void TextureManager::delet_texture() {
|
void TextureManager::delet_texture() {
|
||||||
glDeleteTextures(1, &m_texture_array);
|
if (m_init) {
|
||||||
glDeleteTextures(1, &m_block_status_array);
|
glDeleteTextures(1, &m_texture_array);
|
||||||
glDeleteTextures(1, &m_cross_plane_array);
|
glDeleteTextures(1, &m_block_status_array);
|
||||||
glDeleteTextures(1, &m_normal_texture_array);
|
glDeleteTextures(1, &m_cross_plane_array);
|
||||||
for (auto& id : m_item_textures) {
|
glDeleteTextures(1, &m_normal_texture_array);
|
||||||
glDeleteTextures(1, &id);
|
for (auto& id : m_item_textures) {
|
||||||
|
glDeleteTextures(1, &id);
|
||||||
|
}
|
||||||
|
Logger::info("Successfully delete all texture");
|
||||||
}
|
}
|
||||||
Logger::info("Successfully delete all texture");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint TextureManager::get_block_status_array() const {
|
GLuint TextureManager::get_block_status_array() const {
|
||||||
@@ -313,6 +315,7 @@ void TextureManager::init_texture() {
|
|||||||
init_block();
|
init_block();
|
||||||
init_block_status();
|
init_block_status();
|
||||||
init_ui();
|
init_ui();
|
||||||
|
m_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureManager::update() {
|
void TextureManager::update() {
|
||||||
|
|||||||
@@ -18,10 +18,14 @@ static int windowed_width = 800, windowed_height = 600;
|
|||||||
Window::Window(Renderer& renderer) : m_renderer(renderer) {}
|
Window::Window(Renderer& renderer) : m_renderer(renderer) {}
|
||||||
|
|
||||||
Window::~Window() {
|
Window::~Window() {
|
||||||
|
if (m_imgui_init) {
|
||||||
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
|
ImGui_ImplGlfw_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
if (ImGui::GetCurrentContext() != nullptr) {
|
||||||
ImGui_ImplGlfw_Shutdown();
|
ImGui::DestroyContext();
|
||||||
ImGui::DestroyContext();
|
}
|
||||||
|
|
||||||
if (m_window) {
|
if (m_window) {
|
||||||
glfwDestroyWindow(m_window);
|
glfwDestroyWindow(m_window);
|
||||||
@@ -212,6 +216,8 @@ void Window::imgui_init() {
|
|||||||
// Setup Platform/Renderer backends
|
// Setup Platform/Renderer backends
|
||||||
ImGui_ImplGlfw_InitForOpenGL(m_window, false);
|
ImGui_ImplGlfw_InitForOpenGL(m_window, false);
|
||||||
ImGui_ImplOpenGL3_Init();
|
ImGui_ImplOpenGL3_Init();
|
||||||
|
|
||||||
|
m_imgui_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Cubed
|
} // namespace Cubed
|
||||||
Reference in New Issue
Block a user