From ac9ae2acb2ddcc1bb1e2c0bf8cd4447fb4842348 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Tue, 1 Aug 2017 19:13:07 +0200 Subject: [PATCH 01/10] Move Konstructs class to konstructs.cpp/h. --- CMakeLists.txt | 3 +- include/konstructs.h | 111 +++++++ src/konstructs.cpp | 680 +++++++++++++++++++++++++++++++++++++++ src/main.cpp | 737 +------------------------------------------ 4 files changed, 795 insertions(+), 736 deletions(-) create mode 100644 include/konstructs.h create mode 100644 src/konstructs.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ee15f4..c7a93d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,8 @@ include_directories( dependencies/nanogui/ext/nanovg/src dependencies/optional-lite dependencies/tinyobjloader - lib/include) + lib/include + include) FILE( GLOB SOURCE_FILES diff --git a/include/konstructs.h b/include/konstructs.h new file mode 100644 index 0000000..c8ec7d7 --- /dev/null +++ b/include/konstructs.h @@ -0,0 +1,111 @@ +#ifndef KONSTRUCTS_KONSTRUCTS_H +#define KONSTRUCTS_KONSTRUCTS_H + +#include +#if defined(WIN32) +#define _WINSOCKAPI_ + #include + #include +#else +#include +#endif +#include +#include +#include +#include +#include "tiny_obj_loader.h" +#include "optional.hpp" +#include "matrix.h" +#include "shader.h" +#include "crosshair_shader.h" +#include "block.h" +#include "chunk.h" +#include "world.h" +#include "chunk_shader.h" +#include "sky_shader.h" +#include "selection_shader.h" +#include "hud.h" +#include "hud_shader.h" +#include "player_shader.h" +#include "textures.h" +#include "util.h" +#include "settings.h" + +#define KONSTRUCTS_APP_TITLE "Konstructs" +#define MOUSE_CLICK_DELAY_IN_FRAMES 15 + +namespace konstructs { + class Konstructs: public nanogui::Screen { + public: + + Konstructs(Settings settings); + ~Konstructs(); + virtual bool scrollEvent(const Vector2i &p, const Vector2f &rel); + virtual bool mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers); + virtual bool keyboardEvent(int key, int scancode, int action, int modifiers); + virtual void draw(NVGcontext *ctx); + virtual void drawContents(); + + private: + + /** This function uses nanovg to print text on top of the screen. This is + * used for both the debug screen and messages sent from the server. + */ + void print_top_text(); + int translate_button(int button); + bool update_view_distance(); + void update_radius(); + void handle_mouse(); + void handle_keys(); + void close_hud(); + void handle_network(); + void handle_packet(konstructs::Packet *packet); + void handle_player_packet(const string &str); + void handle_other_player_packet(const string &str); + void handle_delete_other_player_packet(const string &str); + void handle_block_type(const string &str); + void handle_texture(konstructs::Packet *packet); + void handle_belt(const string &str); + void handle_inventory(const string &str); + void handle_held_stack(const string &str); + void handle_time(const string &str); + float time_of_day(); + float daylight(); + void show_menu(int state, string message); + void setup_connection(); + + BlockTypeInfo blocks; + CrosshairShader crosshair_shader; + int radius; + float view_distance; + float near_distance; + int day_length; + World world; + SkyShader sky_shader; + ChunkShader chunk_shader; + SelectionShader selection_shader; + HudShader hud_shader; + PlayerShader *player_shader; + ChunkModelFactory model_factory; + Client client; + Player player; + Vector3i player_chunk; + optional> looking_at; + Hud hud; + double px; + double py; + FPS fps; + double last_frame; + bool menu_state; + bool debug_text_enabled; + nanogui::Window *window; + uint32_t frame; + uint32_t faces; + uint32_t max_faces; + double frame_time; + uint32_t click_delay; + Settings settings; + }; +}; + +#endif //KONSTRUCTS_KONSTRUCTS_H diff --git a/src/konstructs.cpp b/src/konstructs.cpp new file mode 100644 index 0000000..50032f7 --- /dev/null +++ b/src/konstructs.cpp @@ -0,0 +1,680 @@ +#include "konstructs.h" + +using std::cout; +using std::cerr; +using std::endl; +using namespace konstructs; +using nonstd::optional; +using nonstd::nullopt; +using std::pair; + + +Konstructs::Konstructs(Settings settings) : + nanogui::Screen(Eigen::Vector2i(settings.client.window_width, + settings.client.window_height), + KONSTRUCTS_APP_TITLE), + player(0, Vector3f(0.0f, 0.0f, 0.0f), 0.0f, 0.0f), + px(0), py(0), + model_factory(blocks), + radius(settings.client.radius_start), + client(settings.client.debug), + view_distance((float) settings.client.radius_start * CHUNK_SIZE), + near_distance(0.125f), + sky_shader(settings.client.field_of_view, SKY_TEXTURE, near_distance), + chunk_shader(settings.client.field_of_view, BLOCK_TEXTURES, DAMAGE_TEXTURE, SKY_TEXTURE, near_distance, + load_chunk_vertex_shader(), load_chunk_fragment_shader()), + hud_shader(17, 14, INVENTORY_TEXTURE, BLOCK_TEXTURES, FONT_TEXTURE, HEALTH_BAR_TEXTURE), + selection_shader(settings.client.field_of_view, near_distance, 0.52), + day_length(600), + last_frame(glfwGetTime()), + looking_at(nullopt), + hud(17, 14, 9), + menu_state(false), + debug_text_enabled(false), + frame(0), + click_delay(0), + settings(settings) { + + using namespace nanogui; + performLayout(mNVGContext); + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + Settings::Server server = settings.server; + if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { + setup_connection(); + } else { + show_menu(0, string("Connect to a server")); + } + blocks.is_plant[SOLID_TYPE] = 0; + blocks.is_obstacle[SOLID_TYPE] = 1; + blocks.is_transparent[SOLID_TYPE] = 0; + blocks.state[SOLID_TYPE] = STATE_SOLID; + memset(&fps, 0, sizeof(fps)); + + tinyobj::shape_t shape = load_player(); + player_shader = new PlayerShader(settings.client.field_of_view, PLAYER_TEXTURE, SKY_TEXTURE, + near_distance, shape); +} + +Konstructs::~Konstructs() { + delete player_shader; +} + +bool Konstructs::scrollEvent(const Vector2i &p, const Vector2f &rel) { + hud.scroll(rel[1]); + return true; +} + +bool Konstructs::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) { + if (hud.get_interactive()) { + if (down) { + double x, y; + glfwGetCursorPos(mGLFWWindow, &x, &y); + + auto clicked_at = hud_shader.clicked_at(x, y, mSize.x(), mSize.y()); + + if (clicked_at) { + Vector2i pos = *clicked_at; + if (hud.active(pos)) { + int index = pos[0] + pos[1] * 17; + client.click_inventory(index, translate_button(button)); + } + } + } + } else if (!menu_state) { + // Clicking at the window captures the mouse pointer + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } + return nanogui::Screen::mouseButtonEvent(p, button, down, modifiers); +} + +bool Konstructs::keyboardEvent(int key, int scancode, int action, int modifiers) { + if (nanogui::Screen::keyboardEvent(key, scancode, action, modifiers)) { + return true; + } + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + if (hud.get_interactive()) { + close_hud(); + } else { + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + } else if (key == GLFW_KEY_F1 && action == GLFW_PRESS) { + // TODO: implement this again when time has come ;) + /*if (!menu_state) { + show_menu("","",""); + } else { + hide_menu(); + }*/ + } else if (key == settings.keys.debug && action == GLFW_PRESS) { + debug_text_enabled = !debug_text_enabled; + } else if (key == settings.keys.fly + && action == GLFW_PRESS + && settings.client.debug) { + player.fly(); + } else if (key == settings.keys.tertiary && action == GLFW_PRESS) { + if (hud.get_interactive()) { + close_hud(); + } else if (client.is_connected()) { + if (looking_at) { + auto &l = *looking_at; + uint8_t direction = direction_from_vector(l.first.position, l.second.position); + uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); + client.click_at(1, l.second.position, 3, hud.get_selection(), direction, rotation); + } else { + client.click_at(0, Vector3i::Zero(), 3, hud.get_selection(), 0, 0); + } + } + } else if (key > 48 && key < 58 && action == GLFW_PRESS) { + hud.set_selected(key - 49); + } else { + return false; + } + return true; +} + +void Konstructs::draw(NVGcontext *ctx) { + Screen::draw(ctx); +} + +void Konstructs::drawContents() { + using namespace nanogui; + update_fps(&fps); + frame++; + if (client.is_connected()) { + handle_network(); + handle_keys(); + handle_mouse(); + looking_at = player.looking_at(world, blocks); + glClear(GL_DEPTH_BUFFER_BIT); + for (auto model : model_factory.fetch_models()) { + chunk_shader.add(model); + } + sky_shader.render(player, mSize.x(), mSize.y(), time_of_day(), view_distance); + glClear(GL_DEPTH_BUFFER_BIT); + faces = chunk_shader.render(player, mSize.x(), mSize.y(), + daylight(), time_of_day(), radius, + view_distance, player_chunk); + if (faces > max_faces) { + max_faces = faces; + } + player_shader->render(player, mSize.x(), mSize.y(), + daylight(), time_of_day(), view_distance); + if (looking_at && !hud.get_interactive() && !menu_state) { + selection_shader.render(player, mSize.x(), mSize.y(), + looking_at->second.position, view_distance); + } + glClear(GL_DEPTH_BUFFER_BIT); + if (!hud.get_interactive() && !menu_state) { + crosshair_shader.render(mSize.x(), mSize.y()); + } + double mx, my; + glfwGetCursorPos(mGLFWWindow, &mx, &my); + hud_shader.render(mSize.x(), mSize.y(), mx, my, hud, blocks); + update_radius(); + print_top_text(); + } else if (!menu_state) { + show_menu(2, client.get_error_message()); + } +} + +/** This function uses nanovg to print text on top of the screen. This is + * used for both the debug screen and messages sent from the server. + */ +void Konstructs::print_top_text() { + int width, height; + glfwGetFramebufferSize(mGLFWWindow, &width, &height); + + ostringstream os; + if (debug_text_enabled) { + double frame_fps = 1.15 / frame_time; + os << std::fixed << std::setprecision(2); + os << "Server: " << settings.server.address + << " user: " << settings.server.username + << " x: " << player.position(0) + << " y: " << player.position(1) + << " z: " << player.position(2) + << std::endl; + if (looking_at) { + auto l = *looking_at; + uint8_t direction = direction_from_vector(l.first.position, l.second.position); + uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); + os << "Pointing at x: " << l.second.position(0) << ", " + << "y: " << l.second.position(1) << ", " + << "z: " << l.second.position(2) << ", " + << "dir: " << direction_to_string[direction] << ", " + << "rot: " << rotation_to_string[rotation] + << std::endl; + } else { + os << "Pointing at nothing." << std::endl; + } + os << "View distance: " << view_distance << " (" << radius << "/" << client.get_loaded_radius() << ") " + << "faces: " << faces << "(" << max_faces << ") " + << "FPS: " << fps.fps << "(" << frame_fps << ")" << endl; + os << "Chunks: " << world.size() << " " + << "models: " << chunk_shader.size() << endl; + os << "Model factory, waiting: " << model_factory.waiting() << " " + << "created: " << model_factory.total_created() << " " + << "empty: " << model_factory.total_empty() << " " + << "total: " << model_factory.total() << endl; + + } + + glActiveTexture(GL_TEXTURE0); + nvgFontBlur(mNVGContext, 0.8f); + nvgFontSize(mNVGContext, 20.0f); + nvgTextBox(mNVGContext, 10, 20, width - 10, os.str().c_str(), NULL); +} + +int Konstructs::translate_button(int button) { + switch (button) { + case GLFW_MOUSE_BUTTON_1: + return 1; + case GLFW_MOUSE_BUTTON_2: + return 2; + case GLFW_MOUSE_BUTTON_3: + return 3; + } +} + +bool Konstructs::update_view_distance() { + double frame_fps = 1.15 / frame_time; + float fps = settings.client.frames_per_second; + + if (frame_fps > 0.0 && frame_fps < fps && radius > 1) { + view_distance = view_distance - (float) CHUNK_SIZE * 0.2f * ((fps - (float) frame_fps) / fps); + return true; + } else if (frame_fps >= fps + && radius < settings.client.radius_max + && model_factory.waiting() == 0 + && radius <= client.get_loaded_radius()) { + view_distance = view_distance + 0.05f; + return true; + } else { + return false; + } +} + +void Konstructs::update_radius() { + if (update_view_distance()) { + int new_radius = (int) (view_distance / (float) CHUNK_SIZE) + 1; + radius = new_radius; + client.set_radius(radius); + } +} + +void Konstructs::handle_mouse() { + int exclusive = + glfwGetInputMode(mGLFWWindow, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; + if (exclusive && (px || py)) { + double mx, my; + glfwGetCursorPos(mGLFWWindow, &mx, &my); + float m = 0.0025; + float drx = (mx - px) * m; + float dry = (my - py) * m; + + player.rotate_x(dry); + player.rotate_y(drx); + px = mx; + py = my; + + if (click_delay == 0) { + if (looking_at) { + auto &l = *looking_at; + uint8_t direction = direction_from_vector(l.first.position, l.second.position); + uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); + if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) { + click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; + client.click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_1), hud.get_selection(), + direction, rotation); + } else if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS && + player.can_place(l.first.position, world, blocks)) { + optional selected = hud.selected(); + if (selected) { + BlockData block = {selected->type, selected->health, + DIRECTION_UP, + ROTATION_IDENTITY + }; + if (blocks.is_orientable[block.type]) { + block.direction = direction; + block.rotation = rotation; + } + auto chunk_opt = + world.chunk_by_block(l.first.position); + if (chunk_opt) { + ChunkData updated_chunk = + chunk_opt->set(l.first.position, block); + world.insert(updated_chunk); + model_factory.create_models({updated_chunk.position}, world); + } + } + click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; + client.click_at(1, l.first.position, translate_button(GLFW_MOUSE_BUTTON_2), hud.get_selection(), + direction, rotation); + } else if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS) { + click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; + client.click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), + direction, rotation); + } + } else if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS) { + click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; + client.click_at(0, Vector3i::Zero(), translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), + 0, 0); + } + } else { + click_delay--; + } + } else { + glfwGetCursorPos(mGLFWWindow, &px, &py); + } +} + +void Konstructs::handle_keys() { + int sx = 0; + int sz = 0; + bool jump = false; + bool sneak = false; + double now = glfwGetTime(); + double dt = now - last_frame; + frame_time = now - last_frame; + dt = MIN(dt, 0.2); + dt = MAX(dt, 0.0); + last_frame = now; + if (glfwGetKey(mGLFWWindow, settings.keys.up)) { + sz--; + } + if (glfwGetKey(mGLFWWindow, settings.keys.down)) { + sz++; + } + if (glfwGetKey(mGLFWWindow, settings.keys.left)) { + sx--; + } + if (glfwGetKey(mGLFWWindow, settings.keys.right)) { + sx++; + } + if (glfwGetKey(mGLFWWindow, settings.keys.jump)) { + jump = true; + } + if (glfwGetKey(mGLFWWindow, settings.keys.sneak)) { + sneak = true; + } + client.position(player.update_position(sz, sx, (float) dt, world, + blocks, near_distance, jump, sneak), + player.rx(), player.ry()); + Vector3i new_chunk(chunked_vec(player.camera())); + if (new_chunk != player_chunk) { + player_chunk = new_chunk; + client.set_player_chunk(player_chunk); + } +} + +void Konstructs::close_hud() { + hud.set_interactive(false); + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + client.close_inventory(); + for (int i = 0; i < 17; i++) { + for (int j = 1; j < 14; j++) { + Vector2i pos(i, j); + hud.reset_background(pos); + hud.reset_stack(pos); + } + } +} + +void Konstructs::handle_network() { + for (auto packet : client.receive(100)) { + handle_packet(packet.get()); + } + Vector3f pos = player.position; + Vector3i player_chunk(chunked(pos[0]), chunked(pos[2]), chunked(pos[1])); + + auto prio = client.receive_prio_chunk(player_chunk); + + model_factory.update_player_chunk(player_chunk); + /* Insert prio chunk into world */ + if (prio) { + world.insert(*prio); + model_factory.create_models({(*prio).position}, world); + } + auto new_chunks = client.receive_chunks(1); + if (!new_chunks.empty()) { + std::vector positions; + positions.reserve(new_chunks.size()); + for (auto chunk : new_chunks) { + world.insert(chunk); + positions.push_back(chunk.position); + } + model_factory.create_models(positions, world); + } + if (frame % 7883 == 0) { + /* Book keeping */ + world.delete_unused_chunks(player_chunk, radius + KEEP_EXTRA_CHUNKS); + } + +} + +void Konstructs::handle_packet(konstructs::Packet *packet) { + switch (packet->type) { + case 'P': + handle_other_player_packet(packet->to_string()); + break; + case 'D': + handle_delete_other_player_packet(packet->to_string()); + break; + case 'U': + handle_player_packet(packet->to_string()); + break; + case 'W': + handle_block_type(packet->to_string()); + break; + case 'M': + handle_texture(packet); + break; + case 'G': + handle_belt(packet->to_string()); + break; + case 'I': + handle_inventory(packet->to_string()); + hud.set_interactive(true); + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + break; + case 'i': + handle_held_stack(packet->to_string()); + break; + case 'T': + handle_time(packet->to_string()); + break; + default: + cout << "UNKNOWN: " << packet->type << endl; + break; + } +} + +void Konstructs::handle_player_packet(const string &str) { + int pid; + float x, y, z, rx, ry; + + if (sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", + &pid, &x, &y, &z, &rx, &ry) != 6) { + throw std::runtime_error(str); + } + player = Player(pid, Vector3f(x, y, z), rx, ry); + player_chunk = chunked_vec(player.camera()); + client.set_player_chunk(player_chunk); + client.set_radius(radius); + client.set_logged_in(true); +} + +void Konstructs::handle_other_player_packet(const string &str) { + int pid; + float x, y, z, rx, ry; + if (sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", + &pid, &x, &y, &z, &rx, &ry) != 6) { + throw std::runtime_error(str); + } + player_shader->add(Player(pid, Vector3f(x, y, z), rx, ry)); +} + +void Konstructs::handle_delete_other_player_packet(const string &str) { + int pid; + + if (sscanf(str.c_str(), ",%d", + &pid) != 1) { + throw std::runtime_error(str); + } + player_shader->remove(pid); +} + +void Konstructs::handle_block_type(const string &str) { + int w, obstacle, transparent, left, right, top, bottom, front, back, orientable; + char shape[16]; + char state[16]; + if (sscanf(str.c_str(), ",%d,%15[^,],%15[^,],%d,%d,%d,%d,%d,%d,%d,%d,%d", + &w, shape, state, &obstacle, &transparent, &left, &right, + &top, &bottom, &front, &back, &orientable) != 12) { + throw std::runtime_error(str); + } + blocks.is_plant[w] = strncmp(shape, "plant", 16) == 0; + if (strncmp(state, "solid", 16) == 0) { + blocks.state[w] = STATE_SOLID; + } else if (strncmp(state, "liquid", 16) == 0) { + blocks.state[w] = STATE_LIQUID; + } else if (strncmp(state, "gas", 16) == 0) { + blocks.state[w] = STATE_GAS; + } else if (strncmp(state, "plasma", 16) == 0) { + blocks.state[w] = STATE_PLASMA; + } else { + throw std::invalid_argument("Invalid block type state received!"); + } + blocks.is_obstacle[w] = obstacle; + blocks.is_transparent[w] = transparent; + blocks.is_orientable[w] = orientable; + blocks.blocks[w][0] = left; + blocks.blocks[w][1] = right; + blocks.blocks[w][2] = top; + blocks.blocks[w][3] = bottom; + blocks.blocks[w][4] = front; + blocks.blocks[w][5] = back; +} + +void Konstructs::handle_texture(konstructs::Packet *packet) { + GLuint texture; + glGenTextures(1, &texture); + glActiveTexture(GL_TEXTURE0 + BLOCK_TEXTURES); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + load_png_texture_from_buffer(packet->buffer(), packet->size); +} + +void Konstructs::handle_belt(const string &str) { + uint32_t column, size, type, health; + if (sscanf(str.c_str(), ",%u,%u,%u,%u", + &column, &size, &type, &health) != 4) { + throw std::runtime_error(str); + } + + if (size < 1) { + hud.reset_belt(column); + } else { + hud.set_belt(column, {size, (uint16_t) type, (uint16_t) health}); + } +} + +void Konstructs::handle_inventory(const string &str) { + uint32_t index, size, type, health; + if (sscanf(str.c_str(), ",%u,%u,%u,%u", + &index, &size, &type, &health) != 4) { + throw std::runtime_error(str); + } + uint32_t row = index / 17; + uint32_t column = index % 17; + Vector2i pos(column, row); + + if (type == -1) { + hud.reset_background(pos); + hud.reset_stack(pos); + } else { + hud.set_background(pos, 2); + hud.set_stack(pos, {size, (uint16_t) type, (uint16_t) health}); + } +} + +void Konstructs::handle_held_stack(const string &str) { + uint32_t amount, type; + if (sscanf(str.c_str(), ",%u,%u", + &amount, &type) != 2) { + throw std::runtime_error(str); + } + if (type == -1) { + hud.reset_held(); + } else { + hud.set_held({amount, (uint16_t) type}); + } +} + +void Konstructs::handle_time(const string &str) { + long time_value; + if (sscanf(str.c_str(), ",%lu", &time_value) != 1) { + throw std::runtime_error(str); + } + glfwSetTime((double) time_value); +} + +float Konstructs::time_of_day() { + if (day_length <= 0) { + return 0.5; + } + float t; + t = glfwGetTime(); + t = t / day_length; + t = t - (int) t; + return t; +} + +float Konstructs::daylight() { + float timer = time_of_day(); + if (timer < 0.5) { + float t = (timer - 0.25) * 100; + return 1 / (1 + powf(2, -t)); + } else { + float t = (timer - 0.85) * 100; + return 1 - 1 / (1 + powf(2, -t)); + } +} + + +void Konstructs::show_menu(int state, string message) { + using namespace nanogui; + + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glActiveTexture(GL_TEXTURE0); + + FormHelper *gui = new FormHelper(this); + window = gui->addWindow({0, 0}, "Main Menu"); + gui->setFixedSize({125, 20}); + + if (state == 1) { + // Popup message + + auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Server connection", message); + } else if (state == 2) { + // Popup message with connect/cancel buttons. + + auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, + "Server connection", message, + "Reconnect", "Cancel", true); + dlg->setCallback([&](int result) { + if (result == 0) { + window->dispose(); + menu_state = false; + setup_connection(); + } + }); + } + +#if defined(KONSTRUCTS_SINGLE_PLAYER) + gui->addGroup("Singleplayer game"); + gui->addButton("Play", [&]() { + settings.server.username = "singleplayer"; + settings.server.password = "singleplayer"; + settings.server.address = "localhost"; + window->dispose(); + menu_state = false; + setup_connection(); + }); + gui->addGroup("Multiplayer"); +#endif + gui->addVariable("Server address", settings.server.address); + gui->addVariable("Username", settings.server.username); + gui->addVariable("Password", settings.server.password); + gui->addButton("Connect", [&]() { + if (settings.server.username != "" && + settings.server.password != "" && + settings.server.address != "") { + // Note: The mouse pointer is intentionally not locked here. + // See: setup_connection() + window->dispose(); + menu_state = false; + setup_connection(); + save_settings(settings); + } + }); + + window->center(); + performLayout(mNVGContext); + menu_state = true; +} + +void Konstructs::setup_connection() { + try { + client.open_connection(settings.server); + load_textures(); + client.set_connected(true); + + // Lock the mouse _after_ a successful connection. This prevents the + // player to get stuck if he or she connects to a server that drops + // the SYN. + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } catch (const std::exception &ex) { + show_menu(1, client.get_error_message()); + } +} diff --git a/src/main.cpp b/src/main.cpp index a2b6530..a960b8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,753 +4,21 @@ #define _WINSOCKAPI_ #include #include -#else - #include #endif #include #include #include #include -#include "tiny_obj_loader.h" -#include "optional.hpp" -#include "matrix.h" -#include "shader.h" -#include "crosshair_shader.h" -#include "block.h" -#include "chunk.h" -#include "world.h" -#include "chunk_shader.h" -#include "sky_shader.h" -#include "selection_shader.h" -#include "hud.h" -#include "hud_shader.h" -#include "player_shader.h" -#include "textures.h" -#include "util.h" +#include -#define KONSTRUCTS_APP_TITLE "Konstructs" #define MAX_PENDING_CHUNKS 64 -#define MOUSE_CLICK_DELAY_IN_FRAMES 15 -using std::cout; -using std::cerr; -using std::endl; using namespace konstructs; -using nonstd::optional; -using nonstd::nullopt; -using std::pair; void print_usage(); void glfw_error(int error_code, const char *error_string); -class Konstructs: public nanogui::Screen { -public: - Konstructs(Settings settings) : - nanogui::Screen(Eigen::Vector2i(settings.client.window_width, - settings.client.window_height), - KONSTRUCTS_APP_TITLE), - player(0, Vector3f(0.0f, 0.0f, 0.0f), 0.0f, 0.0f), - px(0), py(0), - model_factory(blocks), - radius(settings.client.radius_start), - client(settings.client.debug), - view_distance((float)settings.client.radius_start*CHUNK_SIZE), - near_distance(0.125f), - sky_shader(settings.client.field_of_view, SKY_TEXTURE, near_distance), - chunk_shader(settings.client.field_of_view, BLOCK_TEXTURES, DAMAGE_TEXTURE, SKY_TEXTURE, near_distance, - load_chunk_vertex_shader(), load_chunk_fragment_shader()), - hud_shader(17, 14, INVENTORY_TEXTURE, BLOCK_TEXTURES, FONT_TEXTURE, HEALTH_BAR_TEXTURE), - selection_shader(settings.client.field_of_view, near_distance, 0.52), - day_length(600), - last_frame(glfwGetTime()), - looking_at(nullopt), - hud(17, 14, 9), - menu_state(false), - debug_text_enabled(false), - frame(0), - click_delay(0), - settings(settings) { - - using namespace nanogui; - performLayout(mNVGContext); - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - Settings::Server server = settings.server; - if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { - setup_connection(); - } else { - show_menu(0, string("Connect to a server")); - } - blocks.is_plant[SOLID_TYPE] = 0; - blocks.is_obstacle[SOLID_TYPE] = 1; - blocks.is_transparent[SOLID_TYPE] = 0; - blocks.state[SOLID_TYPE] = STATE_SOLID; - memset(&fps, 0, sizeof(fps)); - - tinyobj::shape_t shape = load_player(); - player_shader = new PlayerShader(settings.client.field_of_view, PLAYER_TEXTURE, SKY_TEXTURE, - near_distance, shape); - } - - ~Konstructs() { - delete player_shader; - } - - virtual bool scrollEvent(const Vector2i &p, const Vector2f &rel) { - hud.scroll(rel[1]); - return true; - } - - virtual bool mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) { - if(hud.get_interactive()) { - if(down) { - double x, y; - glfwGetCursorPos(mGLFWWindow, &x, &y); - - auto clicked_at = hud_shader.clicked_at(x, y, mSize.x(), mSize.y()); - - if(clicked_at) { - Vector2i pos = *clicked_at; - if(hud.active(pos)) { - int index = pos[0] + pos[1] * 17; - client.click_inventory(index, translate_button(button)); - } - } - } - } else if(!menu_state) { - // Clicking at the window captures the mouse pointer - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - } - return Screen::mouseButtonEvent(p, button, down, modifiers); - } - - virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) { - if (Screen::keyboardEvent(key, scancode, action, modifiers)) { - return true; - } - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - if(hud.get_interactive()) { - close_hud(); - } else { - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } - } else if (key == GLFW_KEY_F1 && action == GLFW_PRESS) { - // TODO: implement this again when time has come ;) - /*if (!menu_state) { - show_menu("","",""); - } else { - hide_menu(); - }*/ - } else if (key == settings.keys.debug && action == GLFW_PRESS) { - debug_text_enabled = !debug_text_enabled; - } else if (key == settings.keys.fly - && action == GLFW_PRESS - && settings.client.debug) { - player.fly(); - } else if(key == settings.keys.tertiary && action == GLFW_PRESS) { - if(hud.get_interactive()) { - close_hud(); - } else if (client.is_connected()) { - if(looking_at) { - auto &l = *looking_at; - uint8_t direction = direction_from_vector(l.first.position, l.second.position); - uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); - client.click_at(1, l.second.position, 3, hud.get_selection(), direction, rotation); - } else { - client.click_at(0, Vector3i::Zero(), 3, hud.get_selection(), 0, 0); - } - } - } else if(key > 48 && key < 58 && action == GLFW_PRESS) { - hud.set_selected(key - 49); - } else { - return false; - } - return true; - } - - virtual void draw(NVGcontext *ctx) { - Screen::draw(ctx); - } - - virtual void drawContents() { - using namespace nanogui; - update_fps(&fps); - frame++; - if (client.is_connected()) { - handle_network(); - handle_keys(); - handle_mouse(); - looking_at = player.looking_at(world, blocks); - glClear(GL_DEPTH_BUFFER_BIT); - for(auto model : model_factory.fetch_models()) { - chunk_shader.add(model); - } - sky_shader.render(player, mSize.x(), mSize.y(), time_of_day(), view_distance); - glClear(GL_DEPTH_BUFFER_BIT); - faces = chunk_shader.render(player, mSize.x(), mSize.y(), - daylight(), time_of_day(), radius, - view_distance, player_chunk); - if(faces > max_faces) { - max_faces = faces; - } - player_shader->render(player, mSize.x(), mSize.y(), - daylight(), time_of_day(), view_distance); - if(looking_at && !hud.get_interactive() && !menu_state) { - selection_shader.render(player, mSize.x(), mSize.y(), - looking_at->second.position, view_distance); - } - glClear(GL_DEPTH_BUFFER_BIT); - if(!hud.get_interactive() && !menu_state) { - crosshair_shader.render(mSize.x(), mSize.y()); - } - double mx, my; - glfwGetCursorPos(mGLFWWindow, &mx, &my); - hud_shader.render(mSize.x(), mSize.y(), mx, my, hud, blocks); - update_radius(); - print_top_text(); - } else if(!menu_state) { - show_menu(2, client.get_error_message()); - } - } - -private: - - /** This function uses nanovg to print text on top of the screen. This is - * used for both the debug screen and messages sent from the server. - */ - void print_top_text() { - int width, height; - glfwGetFramebufferSize(mGLFWWindow, &width, &height); - - ostringstream os; - if (debug_text_enabled) { - double frame_fps = 1.15 / frame_time; - os << std::fixed << std::setprecision(2); - os << "Server: " << settings.server.address - << " user: " << settings.server.username - << " x: " << player.position(0) - << " y: " << player.position(1) - << " z: " << player.position(2) - << std::endl; - if(looking_at) { - auto l = *looking_at; - uint8_t direction = direction_from_vector(l.first.position, l.second.position); - uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); - os << "Pointing at x: " << l.second.position(0) << ", " - << "y: " << l.second.position(1) << ", " - << "z: " << l.second.position(2) << ", " - << "dir: " << direction_to_string[direction] << ", " - << "rot: " << rotation_to_string[rotation] - << std::endl; - } else { - os << "Pointing at nothing." << std::endl; - } - os << "View distance: " << view_distance << " (" << radius << "/" << client.get_loaded_radius() << ") " - << "faces: " << faces << "(" << max_faces << ") " - << "FPS: " << fps.fps << "(" << frame_fps << ")" << endl; - os << "Chunks: " << world.size() << " " - << "models: " << chunk_shader.size() << endl; - os << "Model factory, waiting: " << model_factory.waiting() << " " - << "created: " << model_factory.total_created() << " " - << "empty: " << model_factory.total_empty() << " " - << "total: " << model_factory.total() << endl; - - } - - glActiveTexture(GL_TEXTURE0); - nvgFontBlur(mNVGContext, 0.8f); - nvgFontSize(mNVGContext, 20.0f); - nvgTextBox(mNVGContext, 10, 20, width - 10, os.str().c_str(), NULL); - } - - int translate_button(int button) { - switch(button) { - case GLFW_MOUSE_BUTTON_1: - return 1; - case GLFW_MOUSE_BUTTON_2: - return 2; - case GLFW_MOUSE_BUTTON_3: - return 3; - } - } - - bool update_view_distance() { - double frame_fps = 1.15 / frame_time; - float fps = settings.client.frames_per_second; - - if(frame_fps > 0.0 && frame_fps < fps && radius > 1) { - view_distance = view_distance - (float)CHUNK_SIZE * 0.2f * ((fps - (float)frame_fps) / fps); - return true; - } else if(frame_fps >= fps - && radius < settings.client.radius_max - && model_factory.waiting() == 0 - && radius <= client.get_loaded_radius()) { - view_distance = view_distance + 0.05f; - return true; - } else { - return false; - } - } - - void update_radius() { - if (update_view_distance()) { - int new_radius = (int)(view_distance / (float)CHUNK_SIZE) + 1; - radius = new_radius; - client.set_radius(radius); - } - } - - void handle_mouse() { - int exclusive = - glfwGetInputMode(mGLFWWindow, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; - if (exclusive && (px || py)) { - double mx, my; - glfwGetCursorPos(mGLFWWindow, &mx, &my); - float m = 0.0025; - float drx = (mx - px) * m; - float dry = (my - py) * m; - - player.rotate_x(dry); - player.rotate_y(drx); - px = mx; - py = my; - - if(click_delay == 0) { - if(looking_at) { - auto &l = *looking_at; - uint8_t direction = direction_from_vector(l.first.position, l.second.position); - uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); - if(glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) { - click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_1), hud.get_selection(), - direction, rotation); - } else if(glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS && - player.can_place(l.first.position, world, blocks)) { - optional selected = hud.selected(); - if(selected) { - BlockData block = { selected->type, selected->health, - DIRECTION_UP, - ROTATION_IDENTITY - }; - if(blocks.is_orientable[block.type]) { - block.direction = direction; - block.rotation = rotation; - } - auto chunk_opt = - world.chunk_by_block(l.first.position); - if(chunk_opt) { - ChunkData updated_chunk = - chunk_opt->set(l.first.position, block); - world.insert(updated_chunk); - model_factory.create_models({updated_chunk.position}, world); - } - } - click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(1, l.first.position, translate_button(GLFW_MOUSE_BUTTON_2), hud.get_selection(), - direction, rotation); - } else if(glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS) { - click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), - direction, rotation); - } - } else if(glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS) { - click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(0, Vector3i::Zero(), translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), - 0, 0); - } - } else { - click_delay--; - } - } else { - glfwGetCursorPos(mGLFWWindow, &px, &py); - } - } - - void handle_keys() { - int sx = 0; - int sz = 0; - bool jump = false; - bool sneak = false; - double now = glfwGetTime(); - double dt = now - last_frame; - frame_time = now - last_frame; - dt = MIN(dt, 0.2); - dt = MAX(dt, 0.0); - last_frame = now; - if(glfwGetKey(mGLFWWindow, settings.keys.up)) { - sz--; - } - if(glfwGetKey(mGLFWWindow, settings.keys.down)) { - sz++; - } - if(glfwGetKey(mGLFWWindow, settings.keys.left)) { - sx--; - } - if(glfwGetKey(mGLFWWindow, settings.keys.right)) { - sx++; - } - if(glfwGetKey(mGLFWWindow, settings.keys.jump)) { - jump = true; - } - if(glfwGetKey(mGLFWWindow, settings.keys.sneak)) { - sneak = true; - } - client.position(player.update_position(sz, sx, (float)dt, world, - blocks, near_distance, jump, sneak), - player.rx(), player.ry()); - Vector3i new_chunk(chunked_vec(player.camera())); - if(new_chunk != player_chunk) { - player_chunk = new_chunk; - client.set_player_chunk(player_chunk); - } - } - - void close_hud() { - hud.set_interactive(false); - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - client.close_inventory(); - for(int i = 0; i < 17; i++) { - for(int j = 1; j < 14; j++) { - Vector2i pos(i, j); - hud.reset_background(pos); - hud.reset_stack(pos); - } - } - } - - void handle_network() { - for(auto packet : client.receive(100)) { - handle_packet(packet.get()); - } - Vector3f pos = player.position; - Vector3i player_chunk(chunked(pos[0]), chunked(pos[2]), chunked(pos[1])); - - auto prio = client.receive_prio_chunk(player_chunk); - - model_factory.update_player_chunk(player_chunk); - /* Insert prio chunk into world */ - if(prio) { - world.insert(*prio); - model_factory.create_models({(*prio).position}, world); - } - auto new_chunks = client.receive_chunks(1); - if(!new_chunks.empty()) { - std::vector positions; - positions.reserve(new_chunks.size()); - for(auto chunk : new_chunks) { - world.insert(chunk); - positions.push_back(chunk.position); - } - model_factory.create_models(positions, world); - } - if(frame % 7883 == 0) { - /* Book keeping */ - world.delete_unused_chunks(player_chunk, radius + KEEP_EXTRA_CHUNKS); - } - - } - - void handle_packet(konstructs::Packet *packet) { - switch(packet->type) { - case 'P': - handle_other_player_packet(packet->to_string()); - break; - case 'D': - handle_delete_other_player_packet(packet->to_string()); - break; - case 'U': - handle_player_packet(packet->to_string()); - break; - case 'W': - handle_block_type(packet->to_string()); - break; - case 'M': - handle_texture(packet); - break; - case 'G': - handle_belt(packet->to_string()); - break; - case 'I': - handle_inventory(packet->to_string()); - hud.set_interactive(true); - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - break; - case 'i': - handle_held_stack(packet->to_string()); - break; - case 'T': - handle_time(packet->to_string()); - break; - default: - cout << "UNKNOWN: " << packet->type << endl; - break; - } - } - - void handle_player_packet(const string &str) { - int pid; - float x, y, z, rx, ry; - - if(sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", - &pid, &x, &y, &z, &rx, &ry) != 6) { - throw std::runtime_error(str); - } - player = Player(pid, Vector3f(x, y, z), rx, ry); - player_chunk = chunked_vec(player.camera()); - client.set_player_chunk(player_chunk); - client.set_radius(radius); - client.set_logged_in(true); - } - - void handle_other_player_packet(const string &str) { - int pid; - float x, y, z, rx, ry; - if(sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", - &pid, &x, &y, &z, &rx, &ry) != 6) { - throw std::runtime_error(str); - } - player_shader->add(Player(pid, Vector3f(x, y, z), rx, ry)); - } - - void handle_delete_other_player_packet(const string &str) { - int pid; - - if(sscanf(str.c_str(), ",%d", - &pid) != 1) { - throw std::runtime_error(str); - } - player_shader->remove(pid); - } - - void handle_block_type(const string &str) { - int w, obstacle, transparent, left, right, top, bottom, front, back, orientable; - char shape[16]; - char state[16]; - if(sscanf(str.c_str(), ",%d,%15[^,],%15[^,],%d,%d,%d,%d,%d,%d,%d,%d,%d", - &w, shape, state, &obstacle, &transparent, &left, &right, - &top, &bottom, &front, &back, &orientable) != 12) { - throw std::runtime_error(str); - } - blocks.is_plant[w] = strncmp(shape, "plant", 16) == 0; - if(strncmp(state, "solid", 16) == 0) { - blocks.state[w] = STATE_SOLID; - } else if(strncmp(state, "liquid", 16) == 0) { - blocks.state[w] = STATE_LIQUID; - } else if(strncmp(state, "gas", 16) == 0) { - blocks.state[w] = STATE_GAS; - } else if(strncmp(state, "plasma", 16) == 0) { - blocks.state[w] = STATE_PLASMA; - } else { - throw std::invalid_argument("Invalid block type state received!"); - } - blocks.is_obstacle[w] = obstacle; - blocks.is_transparent[w] = transparent; - blocks.is_orientable[w] = orientable; - blocks.blocks[w][0] = left; - blocks.blocks[w][1] = right; - blocks.blocks[w][2] = top; - blocks.blocks[w][3] = bottom; - blocks.blocks[w][4] = front; - blocks.blocks[w][5] = back; - } - - void handle_texture(konstructs::Packet *packet) { - GLuint texture; - glGenTextures(1, &texture); - glActiveTexture(GL_TEXTURE0 + BLOCK_TEXTURES); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - load_png_texture_from_buffer(packet->buffer(), packet->size); - } - - void handle_belt(const string &str) { - uint32_t column, size, type, health; - if(sscanf(str.c_str(), ",%u,%u,%u,%u", - &column, &size, &type, &health) != 4) { - throw std::runtime_error(str); - } - - if(size < 1) { - hud.reset_belt(column); - } else { - hud.set_belt(column, {size, (uint16_t)type, (uint16_t)health}); - } - } - - void handle_inventory(const string &str) { - uint32_t index, size, type, health; - if(sscanf(str.c_str(), ",%u,%u,%u,%u", - &index, &size, &type, &health) != 4) { - throw std::runtime_error(str); - } - uint32_t row = index / 17; - uint32_t column = index % 17; - Vector2i pos(column, row); - - if(type == -1) { - hud.reset_background(pos); - hud.reset_stack(pos); - } else { - hud.set_background(pos, 2); - hud.set_stack(pos, {size, (uint16_t)type, (uint16_t)health}); - } - } - - void handle_held_stack(const string &str) { - uint32_t amount, type; - if(sscanf(str.c_str(), ",%u,%u", - &amount, &type) != 2) { - throw std::runtime_error(str); - } - if(type == -1) { - hud.reset_held(); - } else { - hud.set_held({amount, (uint16_t)type}); - } - } - - void handle_time(const string &str) { - long time_value; - if(sscanf(str.c_str(), ",%lu", &time_value) != 1) { - throw std::runtime_error(str); - } - glfwSetTime((double)time_value); - } - - float time_of_day() { - if (day_length <= 0) { - return 0.5; - } - float t; - t = glfwGetTime(); - t = t / day_length; - t = t - (int)t; - return t; - } - - float daylight() { - float timer = time_of_day(); - if (timer < 0.5) { - float t = (timer - 0.25) * 100; - return 1 / (1 + powf(2, -t)); - } else { - float t = (timer - 0.85) * 100; - return 1 - 1 / (1 + powf(2, -t)); - } - } - - - void show_menu(int state, string message) { - using namespace nanogui; - - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - glActiveTexture(GL_TEXTURE0); - - FormHelper *gui = new FormHelper(this); - window = gui->addWindow({0,0}, "Main Menu"); - gui->setFixedSize({125, 20}); - - if (state == 1) { - // Popup message - - auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Server connection", message); - } else if (state == 2) { - // Popup message with connect/cancel buttons. - - auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, - "Server connection", message, - "Reconnect", "Cancel", true); - dlg->setCallback([&](int result) { - if (result == 0) { - window->dispose(); - menu_state = false; - setup_connection(); - } - }); - } - - #if defined(KONSTRUCTS_SINGLE_PLAYER) - gui->addGroup("Singleplayer game"); - gui->addButton("Play", [&]() { - settings.server.username = "singleplayer"; - settings.server.password = "singleplayer"; - settings.server.address = "localhost"; - window->dispose(); - menu_state = false; - setup_connection(); - }); - gui->addGroup("Multiplayer"); - #endif - gui->addVariable("Server address", settings.server.address); - gui->addVariable("Username", settings.server.username); - gui->addVariable("Password", settings.server.password); - gui->addButton("Connect", [&]() { - if (settings.server.username != "" && - settings.server.password != "" && - settings.server.address != "") { - // Note: The mouse pointer is intentionally not locked here. - // See: setup_connection() - window->dispose(); - menu_state = false; - setup_connection(); - save_settings(settings); - } - }); - - window->center(); - performLayout(mNVGContext); - menu_state = true; - } - - void setup_connection() { - try { - client.open_connection(settings.server); - load_textures(); - client.set_connected(true); - - // Lock the mouse _after_ a successful connection. This prevents the - // player to get stuck if he or she connects to a server that drops - // the SYN. - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - } catch(const std::exception& ex) { - show_menu(1, client.get_error_message()); - } - } - - BlockTypeInfo blocks; - CrosshairShader crosshair_shader; - int radius; - float view_distance; - float near_distance; - int day_length; - World world; - SkyShader sky_shader; - ChunkShader chunk_shader; - SelectionShader selection_shader; - HudShader hud_shader; - PlayerShader *player_shader; - ChunkModelFactory model_factory; - Client client; - Player player; - Vector3i player_chunk; - optional> looking_at; - Hud hud; - double px; - double py; - FPS fps; - double last_frame; - bool menu_state; - bool debug_text_enabled; - nanogui::Window *window; - uint32_t frame; - uint32_t faces; - uint32_t max_faces; - double frame_time; - uint32_t click_delay; - Settings settings; -}; - #ifdef WIN32 int init_winsock() { WORD wVersionRequested; @@ -790,7 +58,6 @@ void glfw_error(int error_code, const char *error_string) { cout << "GLFW Error[" << error_code << "]: " << error_string << endl; } - int main(int argc, char ** argv) { Settings settings; @@ -855,7 +122,7 @@ int main(int argc, char ** argv) { #if defined(WIN32) MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK); #else - std::cerr << error_msg << endl; + std::cerr << error_msg << std::endl; #endif return -1; } From ee5594f34fb286326e3b64d7a5f17751d8f89333 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sat, 12 Aug 2017 20:17:27 +0200 Subject: [PATCH 02/10] More refactoring. Moved things around, this is not a beauty but a stepping stone. The client starts but it looks like something with the server connection fails or is messed up because no chunks, inventory and so on are rendered. Possible the problem is just the renderer. --- Doxyfile | 8 +- include/cli.h | 25 ++++ include/konstructs.h | 65 ++++++++-- include/network.h | 79 ++++++++++++ include/platform.h | 22 ++++ src/cli.cpp | 49 ++++++++ src/konstructs.cpp | 283 +++++-------------------------------------- src/main.cpp | 93 +------------- src/network.cpp | 252 ++++++++++++++++++++++++++++++++++++++ src/platform.cpp | 34 ++++++ 10 files changed, 556 insertions(+), 354 deletions(-) create mode 100644 include/cli.h create mode 100644 include/network.h create mode 100644 include/platform.h create mode 100644 src/cli.cpp create mode 100644 src/network.cpp create mode 100644 src/platform.cpp diff --git a/Doxyfile b/Doxyfile index c779a5c..2c6e820 100644 --- a/Doxyfile +++ b/Doxyfile @@ -415,19 +415,19 @@ EXTRACT_ALL = YES # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = NO +EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. -EXTRACT_PACKAGE = NO +EXTRACT_PACKAGE = YES # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = NO +EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, @@ -758,7 +758,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = src/ +INPUT = src/ include/ lib/src/ lib/include/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/include/cli.h b/include/cli.h new file mode 100644 index 0000000..4c382aa --- /dev/null +++ b/include/cli.h @@ -0,0 +1,25 @@ + +#ifndef KONSTRUCTS_CLI_H +#define KONSTRUCTS_CLI_H + +namespace konstructs { + + /** + * This class contains cli logic + */ + class Cli { + + public: + + /** + * Print the cli command line options to stdout + */ + static void print_usage(); + + static void argument_parser(int argc, char **argv, Settings settings); + }; + +} + + +#endif //KONSTRUCTS_CLI_H diff --git a/include/konstructs.h b/include/konstructs.h index c8ec7d7..52db0de 100644 --- a/include/konstructs.h +++ b/include/konstructs.h @@ -30,6 +30,7 @@ #include "textures.h" #include "util.h" #include "settings.h" +#include "network.h" #define KONSTRUCTS_APP_TITLE "Konstructs" #define MOUSE_CLICK_DELAY_IN_FRAMES 15 @@ -40,9 +41,22 @@ namespace konstructs { Konstructs(Settings settings); ~Konstructs(); + + /** + * Called by GLFW when the mouse scroll wheel is used. + */ virtual bool scrollEvent(const Vector2i &p, const Vector2f &rel); + + /** + * Called by GLFW when a mouse button is pressed + */ virtual bool mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers); + + /** + * Called by GLEW when a keyboard button is pressed + */ virtual bool keyboardEvent(int key, int scancode, int action, int modifiers); + virtual void draw(NVGcontext *ctx); virtual void drawContents(); @@ -52,27 +66,52 @@ namespace konstructs { * used for both the debug screen and messages sent from the server. */ void print_top_text(); + + /** + * Translate GLFW mouse button presses to 1 to 3 + * @param button The GLFW mouse button ID + */ int translate_button(int button); + + /** + * Update view distance variable dependent on your framerate. + */ bool update_view_distance(); + + /** + * If view distance has updated, set a new view radius. + */ void update_radius(); + + /** + * Manage the mouse pointer. + */ void handle_mouse(); + + /** + * Manage the keyboard. + */ void handle_keys(); + + /** + * Close the hud and send a message to the server. + */ void close_hud(); - void handle_network(); - void handle_packet(konstructs::Packet *packet); - void handle_player_packet(const string &str); - void handle_other_player_packet(const string &str); - void handle_delete_other_player_packet(const string &str); - void handle_block_type(const string &str); - void handle_texture(konstructs::Packet *packet); - void handle_belt(const string &str); - void handle_inventory(const string &str); - void handle_held_stack(const string &str); - void handle_time(const string &str); + + /** + * Returns the time of day + */ float time_of_day(); + + /** + * TODO: What do this? + */ float daylight(); + + /** + * Open and display the nanogui main menu + */ void show_menu(int state, string message); - void setup_connection(); BlockTypeInfo blocks; CrosshairShader crosshair_shader; @@ -87,7 +126,6 @@ namespace konstructs { HudShader hud_shader; PlayerShader *player_shader; ChunkModelFactory model_factory; - Client client; Player player; Vector3i player_chunk; optional> looking_at; @@ -105,6 +143,7 @@ namespace konstructs { double frame_time; uint32_t click_delay; Settings settings; + Network network; }; }; diff --git a/include/network.h b/include/network.h new file mode 100644 index 0000000..dbe7320 --- /dev/null +++ b/include/network.h @@ -0,0 +1,79 @@ +#ifndef KONSTRUCTS_NETWORK_H +#define KONSTRUCTS_NETWORK_H + +#if defined(WIN32) +#define _WINSOCKAPI_ + #include + #include +#else +#include +#endif +#include +#include +#include +#include +#include "tiny_obj_loader.h" +#include "optional.hpp" +#include "matrix.h" +#include "shader.h" +#include "crosshair_shader.h" +#include "block.h" +#include "chunk.h" +#include "world.h" +#include "client.h" +#include "chunk_shader.h" +#include "sky_shader.h" +#include "selection_shader.h" +#include "hud.h" +#include "hud_shader.h" +#include "player_shader.h" +#include "textures.h" +#include "util.h" +#include "settings.h" + +namespace konstructs { + class Network { + public: + Network(Settings settings); + void handle_network(Player player, + ChunkModelFactory *model_factory, + World world, + int radius, + uint32_t frame, + Hud hud, + GLFWwindow *mGLFWWindow, + PlayerShader *player_shader, + Vector3i player_chunk, + BlockTypeInfo blocks); + void setup_connection(Settings::Server server, GLFWwindow *mGLFWWindow); + Client* get_client(); + + private: + void handle_packet(konstructs::Packet *packet, + Hud hud, + GLFWwindow *mGLFWWindow, + PlayerShader *player_shader, + Player player, + Vector3i player_chunk, + int radius, + BlockTypeInfo blocks); + void handle_player_packet(const string &str, + Player player, + Vector3i player_chunk, + int radius); + void handle_other_player_packet(const string &str, + PlayerShader *player_shader); + void handle_delete_other_player_packet(const string &str, + PlayerShader *player_shader); + void handle_block_type(const string &str, BlockTypeInfo blocks); + void handle_texture(konstructs::Packet *packet); + void handle_belt(const string &str, Hud hud); + void handle_inventory(const string &str, Hud hud); + void handle_held_stack(const string &str, Hud hud); + void handle_time(const string &str); + + Client client; + }; +}; + +#endif //KONSTRUCTS_NETWORK_H diff --git a/include/platform.h b/include/platform.h new file mode 100644 index 0000000..d02058b --- /dev/null +++ b/include/platform.h @@ -0,0 +1,22 @@ + +#ifndef KONSTRUCTS_PLATFORM_H +#define KONSTRUCTS_PLATFORM_H + +namespace konstructs { + + /** + * This class contains platform specific code + */ + class Platform { + + public: + + /** + * Init the winsock ddl under WIN32, do nothing on other platforms. + */ + static int init_winsock(); + }; +} + + +#endif //KONSTRUCTS_PLATFORM_H diff --git a/src/cli.cpp b/src/cli.cpp new file mode 100644 index 0000000..d2f4702 --- /dev/null +++ b/src/cli.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include "cli.h" + +using namespace konstructs; + +void Cli::print_usage() { + std::cout << ("OPTIONS: -h/--help - Show this help") << std::endl; + std::cout << (" -s/--server
- Server to enter") << std::endl; + std::cout << (" -u/--username - Username to login") << std::endl; + std::cout << (" -p/--password - Passworld to login") << std::endl; + exit(0); +} + +void Cli::argument_parser(int argc, char **argv, Settings settings) { + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + Cli::print_usage(); + } + if (strcmp(argv[i], "--server") == 0 || strcmp(argv[i], "-s") == 0) { + if (!argv[i+1]) { + Cli::print_usage(); + } else { + settings.server.address = argv[i+1]; + ++i; + } + } + if (strcmp(argv[i], "--username") == 0 || strcmp(argv[i], "-u") == 0) { + if (!argv[i+1]) { + Cli::print_usage(); + } else { + settings.server.username = argv[i+1]; + ++i; + } + } + if (strcmp(argv[i], "--password") == 0 || strcmp(argv[i], "-p") == 0) { + if (!argv[i+1]) { + Cli::print_usage(); + } else { + settings.server.password = argv[i+1]; + ++i; + } + } + if (strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) { + settings.client.debug = true; + } + } +} \ No newline at end of file diff --git a/src/konstructs.cpp b/src/konstructs.cpp index 50032f7..7c0d10b 100644 --- a/src/konstructs.cpp +++ b/src/konstructs.cpp @@ -1,4 +1,5 @@ #include "konstructs.h" +#include "network.h" using std::cout; using std::cerr; @@ -17,7 +18,6 @@ Konstructs::Konstructs(Settings settings) : px(0), py(0), model_factory(blocks), radius(settings.client.radius_start), - client(settings.client.debug), view_distance((float) settings.client.radius_start * CHUNK_SIZE), near_distance(0.125f), sky_shader(settings.client.field_of_view, SKY_TEXTURE, near_distance), @@ -33,14 +33,15 @@ Konstructs::Konstructs(Settings settings) : debug_text_enabled(false), frame(0), click_delay(0), - settings(settings) { + settings(settings), + network(settings) { using namespace nanogui; performLayout(mNVGContext); glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); Settings::Server server = settings.server; if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { - setup_connection(); + network.setup_connection(server, mGLFWWindow); } else { show_menu(0, string("Connect to a server")); } @@ -76,7 +77,7 @@ bool Konstructs::mouseButtonEvent(const Vector2i &p, int button, bool down, int Vector2i pos = *clicked_at; if (hud.active(pos)) { int index = pos[0] + pos[1] * 17; - client.click_inventory(index, translate_button(button)); + network.get_client()->click_inventory(index, translate_button(button)); } } } @@ -113,14 +114,14 @@ bool Konstructs::keyboardEvent(int key, int scancode, int action, int modifiers) } else if (key == settings.keys.tertiary && action == GLFW_PRESS) { if (hud.get_interactive()) { close_hud(); - } else if (client.is_connected()) { + } else if (network.get_client()->is_connected()) { if (looking_at) { auto &l = *looking_at; uint8_t direction = direction_from_vector(l.first.position, l.second.position); uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); - client.click_at(1, l.second.position, 3, hud.get_selection(), direction, rotation); + network.get_client()->click_at(1, l.second.position, 3, hud.get_selection(), direction, rotation); } else { - client.click_at(0, Vector3i::Zero(), 3, hud.get_selection(), 0, 0); + network.get_client()->click_at(0, Vector3i::Zero(), 3, hud.get_selection(), 0, 0); } } } else if (key > 48 && key < 58 && action == GLFW_PRESS) { @@ -139,8 +140,17 @@ void Konstructs::drawContents() { using namespace nanogui; update_fps(&fps); frame++; - if (client.is_connected()) { - handle_network(); + if (network.get_client()->is_connected()) { + network.handle_network(player, + &model_factory, + world, + radius, + frame, + hud, + mGLFWWindow, + player_shader, + player_chunk, + blocks); handle_keys(); handle_mouse(); looking_at = player.looking_at(world, blocks); @@ -172,7 +182,7 @@ void Konstructs::drawContents() { update_radius(); print_top_text(); } else if (!menu_state) { - show_menu(2, client.get_error_message()); + show_menu(2, network.get_client()->get_error_message()); } } @@ -206,7 +216,7 @@ void Konstructs::print_top_text() { } else { os << "Pointing at nothing." << std::endl; } - os << "View distance: " << view_distance << " (" << radius << "/" << client.get_loaded_radius() << ") " + os << "View distance: " << view_distance << " (" << radius << "/" << network.get_client()->get_loaded_radius() << ") " << "faces: " << faces << "(" << max_faces << ") " << "FPS: " << fps.fps << "(" << frame_fps << ")" << endl; os << "Chunks: " << world.size() << " " @@ -245,7 +255,7 @@ bool Konstructs::update_view_distance() { } else if (frame_fps >= fps && radius < settings.client.radius_max && model_factory.waiting() == 0 - && radius <= client.get_loaded_radius()) { + && radius <= network.get_client()->get_loaded_radius()) { view_distance = view_distance + 0.05f; return true; } else { @@ -257,7 +267,7 @@ void Konstructs::update_radius() { if (update_view_distance()) { int new_radius = (int) (view_distance / (float) CHUNK_SIZE) + 1; radius = new_radius; - client.set_radius(radius); + network.get_client()->set_radius(radius); } } @@ -283,7 +293,7 @@ void Konstructs::handle_mouse() { uint8_t rotation = rotation_from_vector(direction, player.camera_direction()); if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS) { click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_1), hud.get_selection(), + network.get_client()->click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_1), hud.get_selection(), direction, rotation); } else if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS && player.can_place(l.first.position, world, blocks)) { @@ -307,16 +317,16 @@ void Konstructs::handle_mouse() { } } click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(1, l.first.position, translate_button(GLFW_MOUSE_BUTTON_2), hud.get_selection(), + network.get_client()->click_at(1, l.first.position, translate_button(GLFW_MOUSE_BUTTON_2), hud.get_selection(), direction, rotation); } else if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS) { click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), + network.get_client()->click_at(1, l.second.position, translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), direction, rotation); } } else if (glfwGetMouseButton(mGLFWWindow, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS) { click_delay = MOUSE_CLICK_DELAY_IN_FRAMES; - client.click_at(0, Vector3i::Zero(), translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), + network.get_client()->click_at(0, Vector3i::Zero(), translate_button(GLFW_MOUSE_BUTTON_3), hud.get_selection(), 0, 0); } } else { @@ -356,20 +366,20 @@ void Konstructs::handle_keys() { if (glfwGetKey(mGLFWWindow, settings.keys.sneak)) { sneak = true; } - client.position(player.update_position(sz, sx, (float) dt, world, + network.get_client()->position(player.update_position(sz, sx, (float) dt, world, blocks, near_distance, jump, sneak), player.rx(), player.ry()); Vector3i new_chunk(chunked_vec(player.camera())); if (new_chunk != player_chunk) { player_chunk = new_chunk; - client.set_player_chunk(player_chunk); + network.get_client()->set_player_chunk(player_chunk); } } void Konstructs::close_hud() { hud.set_interactive(false); glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - client.close_inventory(); + network.get_client()->close_inventory(); for (int i = 0; i < 17; i++) { for (int j = 1; j < 14; j++) { Vector2i pos(i, j); @@ -379,206 +389,6 @@ void Konstructs::close_hud() { } } -void Konstructs::handle_network() { - for (auto packet : client.receive(100)) { - handle_packet(packet.get()); - } - Vector3f pos = player.position; - Vector3i player_chunk(chunked(pos[0]), chunked(pos[2]), chunked(pos[1])); - - auto prio = client.receive_prio_chunk(player_chunk); - - model_factory.update_player_chunk(player_chunk); - /* Insert prio chunk into world */ - if (prio) { - world.insert(*prio); - model_factory.create_models({(*prio).position}, world); - } - auto new_chunks = client.receive_chunks(1); - if (!new_chunks.empty()) { - std::vector positions; - positions.reserve(new_chunks.size()); - for (auto chunk : new_chunks) { - world.insert(chunk); - positions.push_back(chunk.position); - } - model_factory.create_models(positions, world); - } - if (frame % 7883 == 0) { - /* Book keeping */ - world.delete_unused_chunks(player_chunk, radius + KEEP_EXTRA_CHUNKS); - } - -} - -void Konstructs::handle_packet(konstructs::Packet *packet) { - switch (packet->type) { - case 'P': - handle_other_player_packet(packet->to_string()); - break; - case 'D': - handle_delete_other_player_packet(packet->to_string()); - break; - case 'U': - handle_player_packet(packet->to_string()); - break; - case 'W': - handle_block_type(packet->to_string()); - break; - case 'M': - handle_texture(packet); - break; - case 'G': - handle_belt(packet->to_string()); - break; - case 'I': - handle_inventory(packet->to_string()); - hud.set_interactive(true); - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - break; - case 'i': - handle_held_stack(packet->to_string()); - break; - case 'T': - handle_time(packet->to_string()); - break; - default: - cout << "UNKNOWN: " << packet->type << endl; - break; - } -} - -void Konstructs::handle_player_packet(const string &str) { - int pid; - float x, y, z, rx, ry; - - if (sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", - &pid, &x, &y, &z, &rx, &ry) != 6) { - throw std::runtime_error(str); - } - player = Player(pid, Vector3f(x, y, z), rx, ry); - player_chunk = chunked_vec(player.camera()); - client.set_player_chunk(player_chunk); - client.set_radius(radius); - client.set_logged_in(true); -} - -void Konstructs::handle_other_player_packet(const string &str) { - int pid; - float x, y, z, rx, ry; - if (sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", - &pid, &x, &y, &z, &rx, &ry) != 6) { - throw std::runtime_error(str); - } - player_shader->add(Player(pid, Vector3f(x, y, z), rx, ry)); -} - -void Konstructs::handle_delete_other_player_packet(const string &str) { - int pid; - - if (sscanf(str.c_str(), ",%d", - &pid) != 1) { - throw std::runtime_error(str); - } - player_shader->remove(pid); -} - -void Konstructs::handle_block_type(const string &str) { - int w, obstacle, transparent, left, right, top, bottom, front, back, orientable; - char shape[16]; - char state[16]; - if (sscanf(str.c_str(), ",%d,%15[^,],%15[^,],%d,%d,%d,%d,%d,%d,%d,%d,%d", - &w, shape, state, &obstacle, &transparent, &left, &right, - &top, &bottom, &front, &back, &orientable) != 12) { - throw std::runtime_error(str); - } - blocks.is_plant[w] = strncmp(shape, "plant", 16) == 0; - if (strncmp(state, "solid", 16) == 0) { - blocks.state[w] = STATE_SOLID; - } else if (strncmp(state, "liquid", 16) == 0) { - blocks.state[w] = STATE_LIQUID; - } else if (strncmp(state, "gas", 16) == 0) { - blocks.state[w] = STATE_GAS; - } else if (strncmp(state, "plasma", 16) == 0) { - blocks.state[w] = STATE_PLASMA; - } else { - throw std::invalid_argument("Invalid block type state received!"); - } - blocks.is_obstacle[w] = obstacle; - blocks.is_transparent[w] = transparent; - blocks.is_orientable[w] = orientable; - blocks.blocks[w][0] = left; - blocks.blocks[w][1] = right; - blocks.blocks[w][2] = top; - blocks.blocks[w][3] = bottom; - blocks.blocks[w][4] = front; - blocks.blocks[w][5] = back; -} - -void Konstructs::handle_texture(konstructs::Packet *packet) { - GLuint texture; - glGenTextures(1, &texture); - glActiveTexture(GL_TEXTURE0 + BLOCK_TEXTURES); - glBindTexture(GL_TEXTURE_2D, texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - load_png_texture_from_buffer(packet->buffer(), packet->size); -} - -void Konstructs::handle_belt(const string &str) { - uint32_t column, size, type, health; - if (sscanf(str.c_str(), ",%u,%u,%u,%u", - &column, &size, &type, &health) != 4) { - throw std::runtime_error(str); - } - - if (size < 1) { - hud.reset_belt(column); - } else { - hud.set_belt(column, {size, (uint16_t) type, (uint16_t) health}); - } -} - -void Konstructs::handle_inventory(const string &str) { - uint32_t index, size, type, health; - if (sscanf(str.c_str(), ",%u,%u,%u,%u", - &index, &size, &type, &health) != 4) { - throw std::runtime_error(str); - } - uint32_t row = index / 17; - uint32_t column = index % 17; - Vector2i pos(column, row); - - if (type == -1) { - hud.reset_background(pos); - hud.reset_stack(pos); - } else { - hud.set_background(pos, 2); - hud.set_stack(pos, {size, (uint16_t) type, (uint16_t) health}); - } -} - -void Konstructs::handle_held_stack(const string &str) { - uint32_t amount, type; - if (sscanf(str.c_str(), ",%u,%u", - &amount, &type) != 2) { - throw std::runtime_error(str); - } - if (type == -1) { - hud.reset_held(); - } else { - hud.set_held({amount, (uint16_t) type}); - } -} - -void Konstructs::handle_time(const string &str) { - long time_value; - if (sscanf(str.c_str(), ",%lu", &time_value) != 1) { - throw std::runtime_error(str); - } - glfwSetTime((double) time_value); -} - float Konstructs::time_of_day() { if (day_length <= 0) { return 0.5; @@ -626,23 +436,11 @@ void Konstructs::show_menu(int state, string message) { if (result == 0) { window->dispose(); menu_state = false; - setup_connection(); + network.setup_connection(settings.server, mGLFWWindow); } }); } -#if defined(KONSTRUCTS_SINGLE_PLAYER) - gui->addGroup("Singleplayer game"); - gui->addButton("Play", [&]() { - settings.server.username = "singleplayer"; - settings.server.password = "singleplayer"; - settings.server.address = "localhost"; - window->dispose(); - menu_state = false; - setup_connection(); - }); - gui->addGroup("Multiplayer"); -#endif gui->addVariable("Server address", settings.server.address); gui->addVariable("Username", settings.server.username); gui->addVariable("Password", settings.server.password); @@ -654,7 +452,7 @@ void Konstructs::show_menu(int state, string message) { // See: setup_connection() window->dispose(); menu_state = false; - setup_connection(); + network.setup_connection(settings.server, mGLFWWindow); save_settings(settings); } }); @@ -662,19 +460,4 @@ void Konstructs::show_menu(int state, string message) { window->center(); performLayout(mNVGContext); menu_state = true; -} - -void Konstructs::setup_connection() { - try { - client.open_connection(settings.server); - load_textures(); - client.set_connected(true); - - // Lock the mouse _after_ a successful connection. This prevents the - // player to get stuck if he or she connects to a server that drops - // the SYN. - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - } catch (const std::exception &ex) { - show_menu(1, client.get_error_message()); - } -} +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a960b8d..81610e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,106 +1,25 @@ - #include -#if defined(WIN32) - #define _WINSOCKAPI_ - #include - #include -#endif #include #include -#include -#include #include - -#define MAX_PENDING_CHUNKS 64 +#include "platform.h" +#include "cli.h" using namespace konstructs; -void print_usage(); - -void glfw_error(int error_code, const char *error_string); - -#ifdef WIN32 -int init_winsock() { - WORD wVersionRequested; - WSADATA wsaData; - int err; - - wVersionRequested = MAKEWORD(2, 2); - err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) { - printf("WSAStartup failed with error: %d\n", err); - return 1; - } - - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { - printf("Could not find a usable version of Winsock.dll\n"); - WSACleanup(); - return 1; - } - - return 0; -} -#else -int init_winsock() { - return 0; -} -#endif - -void print_usage() { - printf("OPTIONS: -h/--help - Show this help\n"); - printf(" -s/--server
- Server to enter\n"); - printf(" -u/--username - Username to login\n"); - printf(" -p/--password - Passworld to login\n\n"); - exit(0); -} - void glfw_error(int error_code, const char *error_string) { cout << "GLFW Error[" << error_code << "]: " << error_string << endl; } -int main(int argc, char ** argv) { +int main(int argc, char **argv) { Settings settings; load_settings(settings); save_settings(settings); - if (argc > 1) { - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { - print_usage(); - } - if (strcmp(argv[i], "--server") == 0 || strcmp(argv[i], "-s") == 0) { - if (!argv[i+1]) { - print_usage(); - } else { - settings.server.address = argv[i+1]; - ++i; - } - } - if (strcmp(argv[i], "--username") == 0 || strcmp(argv[i], "-u") == 0) { - if (!argv[i+1]) { - print_usage(); - } else { - settings.server.username = argv[i+1]; - ++i; - } - } - if (strcmp(argv[i], "--password") == 0 || strcmp(argv[i], "-p") == 0) { - if (!argv[i+1]) { - print_usage(); - } else { - settings.server.password = argv[i+1]; - ++i; - } - } - if (strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) { - settings.client.debug = true; - } - } - - } + Cli::argument_parser(argc, argv, settings); - if (init_winsock()) { + if (Platform::init_winsock()) { printf("Failed to load winsock"); return 1; } @@ -119,7 +38,7 @@ int main(int argc, char ** argv) { nanogui::shutdown(); } catch (const std::runtime_error &e) { std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what()); - #if defined(WIN32) + #ifdef WIN32 MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK); #else std::cerr << error_msg << std::endl; diff --git a/src/network.cpp b/src/network.cpp new file mode 100644 index 0000000..1661516 --- /dev/null +++ b/src/network.cpp @@ -0,0 +1,252 @@ +#include "network.h" + +using std::cout; +using std::cerr; +using std::endl; +using namespace konstructs; +using nonstd::optional; +using nonstd::nullopt; +using std::pair; + +Network::Network(Settings settings) : client(settings.client.debug) {} + +void Network::handle_network(Player player, + ChunkModelFactory *model_factory, + World world, + int radius, + uint32_t frame, + Hud hud, + GLFWwindow *mGLFWWindow, + PlayerShader *player_shader, + Vector3i player_chunk, + BlockTypeInfo blocks) { + for (auto packet : client.receive(100)) { + handle_packet(packet.get(), hud, mGLFWWindow, player_shader, player, player_chunk, radius, blocks); + } + Vector3f pos = player.position; + Vector3i _player_chunk(chunked(pos[0]), chunked(pos[2]), chunked(pos[1])); + + auto prio = client.receive_prio_chunk(_player_chunk); + + model_factory->update_player_chunk(_player_chunk); + /* Insert prio chunk into world */ + if (prio) { + world.insert(*prio); + model_factory->create_models({(*prio).position}, world); + } + auto new_chunks = client.receive_chunks(1); + if (!new_chunks.empty()) { + std::vector positions; + positions.reserve(new_chunks.size()); + for (auto chunk : new_chunks) { + world.insert(chunk); + positions.push_back(chunk.position); + } + model_factory->create_models(positions, world); + } + if (frame % 7883 == 0) { + /* Book keeping */ + world.delete_unused_chunks(_player_chunk, radius + KEEP_EXTRA_CHUNKS); + } + +} + +void Network::handle_packet(konstructs::Packet *packet, + Hud hud, + GLFWwindow *mGLFWWindow, + PlayerShader *player_shader, + Player player, + Vector3i player_chunk, + int radius, + BlockTypeInfo blocks) { + switch (packet->type) { + case 'P': + handle_other_player_packet(packet->to_string(), player_shader); + break; + case 'D': + handle_delete_other_player_packet(packet->to_string(), player_shader); + break; + case 'U': + handle_player_packet(packet->to_string(), player, player_chunk, radius); + break; + case 'W': + handle_block_type(packet->to_string(), blocks); + break; + case 'M': + handle_texture(packet); + break; + case 'G': + handle_belt(packet->to_string(), hud); + break; + case 'I': + handle_inventory(packet->to_string(), hud); + hud.set_interactive(true); + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + break; + case 'i': + handle_held_stack(packet->to_string(), hud); + break; + case 'T': + handle_time(packet->to_string()); + break; + default: + cout << "UNKNOWN: " << packet->type << endl; + break; + } +} + +void Network::handle_player_packet(const string &str, + Player player, + Vector3i player_chunk, + int radius) { + int pid; + float x, y, z, rx, ry; + + if (sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", + &pid, &x, &y, &z, &rx, &ry) != 6) { + throw std::runtime_error(str); + } + player = Player(pid, Vector3f(x, y, z), rx, ry); + player_chunk = chunked_vec(player.camera()); + client.set_player_chunk(player_chunk); + client.set_radius(radius); + client.set_logged_in(true); +} + +void Network::handle_other_player_packet(const string &str, + PlayerShader *player_shader) { + int pid; + float x, y, z, rx, ry; + if (sscanf(str.c_str(), ",%d,%f,%f,%f,%f,%f", + &pid, &x, &y, &z, &rx, &ry) != 6) { + throw std::runtime_error(str); + } + player_shader->add(Player(pid, Vector3f(x, y, z), rx, ry)); +} + +void Network::handle_delete_other_player_packet(const string &str, + PlayerShader *player_shader) { + int pid; + + if (sscanf(str.c_str(), ",%d", + &pid) != 1) { + throw std::runtime_error(str); + } + player_shader->remove(pid); +} + +void Network::handle_block_type(const string &str, BlockTypeInfo blocks) { + int w, obstacle, transparent, left, right, top, bottom, front, back, orientable; + char shape[16]; + char state[16]; + if (sscanf(str.c_str(), ",%d,%15[^,],%15[^,],%d,%d,%d,%d,%d,%d,%d,%d,%d", + &w, shape, state, &obstacle, &transparent, &left, &right, + &top, &bottom, &front, &back, &orientable) != 12) { + throw std::runtime_error(str); + } + blocks.is_plant[w] = strncmp(shape, "plant", 16) == 0; + if (strncmp(state, "solid", 16) == 0) { + blocks.state[w] = STATE_SOLID; + } else if (strncmp(state, "liquid", 16) == 0) { + blocks.state[w] = STATE_LIQUID; + } else if (strncmp(state, "gas", 16) == 0) { + blocks.state[w] = STATE_GAS; + } else if (strncmp(state, "plasma", 16) == 0) { + blocks.state[w] = STATE_PLASMA; + } else { + throw std::invalid_argument("Invalid block type state received!"); + } + blocks.is_obstacle[w] = obstacle; + blocks.is_transparent[w] = transparent; + blocks.is_orientable[w] = orientable; + blocks.blocks[w][0] = left; + blocks.blocks[w][1] = right; + blocks.blocks[w][2] = top; + blocks.blocks[w][3] = bottom; + blocks.blocks[w][4] = front; + blocks.blocks[w][5] = back; +} + +void Network::handle_texture(konstructs::Packet *packet) { + GLuint texture; + glGenTextures(1, &texture); + glActiveTexture(GL_TEXTURE0 + BLOCK_TEXTURES); + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + load_png_texture_from_buffer(packet->buffer(), packet->size); +} + +void Network::handle_belt(const string &str, Hud hud) { + uint32_t column, size, type, health; + if (sscanf(str.c_str(), ",%u,%u,%u,%u", + &column, &size, &type, &health) != 4) { + throw std::runtime_error(str); + } + + if (size < 1) { + hud.reset_belt(column); + } else { + hud.set_belt(column, {size, (uint16_t) type, (uint16_t) health}); + } +} + +void Network::handle_inventory(const string &str, Hud hud) { + uint32_t index, size, type, health; + if (sscanf(str.c_str(), ",%u,%u,%u,%u", + &index, &size, &type, &health) != 4) { + throw std::runtime_error(str); + } + uint32_t row = index / 17; + uint32_t column = index % 17; + Vector2i pos(column, row); + + if (type == -1) { + hud.reset_background(pos); + hud.reset_stack(pos); + } else { + hud.set_background(pos, 2); + hud.set_stack(pos, {size, (uint16_t) type, (uint16_t) health}); + } +} + +void Network::handle_held_stack(const string &str, Hud hud) { + uint32_t amount, type; + if (sscanf(str.c_str(), ",%u,%u", + &amount, &type) != 2) { + throw std::runtime_error(str); + } + if (type == -1) { + hud.reset_held(); + } else { + hud.set_held({amount, (uint16_t) type}); + } +} + +void Network::handle_time(const string &str) { + long time_value; + if (sscanf(str.c_str(), ",%lu", &time_value) != 1) { + throw std::runtime_error(str); + } + glfwSetTime((double) time_value); +} + +void Network::setup_connection(Settings::Server server, GLFWwindow *mGLFWWindow) { + try { + client.open_connection(server); + load_textures(); + client.set_connected(true); + + // Lock the mouse _after_ a successful connection. This prevents the + // player to get stuck if he or she connects to a server that drops + // the SYN. + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } catch (const std::exception &ex) { + // show_menu(1, client.get_error_message()); TODO: fix this + std::cerr << client.get_error_message() << std::endl; + } +} + +Client* Network::get_client() { + return &client; +} \ No newline at end of file diff --git a/src/platform.cpp b/src/platform.cpp new file mode 100644 index 0000000..9eecac7 --- /dev/null +++ b/src/platform.cpp @@ -0,0 +1,34 @@ +#include "platform.h" +#include +#ifdef WIN32 +#define _WINSOCKAPI_ +#include +#include +#endif + +using namespace konstructs; + +int Platform::init_winsock() { +#ifdef WIN32 + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD(2, 2); + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + printf("WSAStartup failed with error: %d\n", err); + return 1; + } + + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { + printf("Could not find a usable version of Winsock.dll\n"); + WSACleanup(); + return 1; + } + + return 0; +#else + return 0; +#endif +} \ No newline at end of file From a199b22162d23b412d6a9f3e6f7f7637dc56ec84 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sat, 12 Aug 2017 21:42:46 +0200 Subject: [PATCH 03/10] Actually update the settings settings struct. --- include/cli.h | 8 +++++++- src/cli.cpp | 10 +++++----- src/main.cpp | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/cli.h b/include/cli.h index 4c382aa..d204e74 100644 --- a/include/cli.h +++ b/include/cli.h @@ -16,7 +16,13 @@ namespace konstructs { */ static void print_usage(); - static void argument_parser(int argc, char **argv, Settings settings); + /** + * Simple argument parser that updates the settings struct + * @param argc From main() + * @param argv From main() + * @param settings The settings object to update + */ + static void argument_parser(int argc, char **argv, Settings *settings); }; } diff --git a/src/cli.cpp b/src/cli.cpp index d2f4702..fcd1d53 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -13,7 +13,7 @@ void Cli::print_usage() { exit(0); } -void Cli::argument_parser(int argc, char **argv, Settings settings) { +void Cli::argument_parser(int argc, char **argv, Settings *settings) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { Cli::print_usage(); @@ -22,7 +22,7 @@ void Cli::argument_parser(int argc, char **argv, Settings settings) { if (!argv[i+1]) { Cli::print_usage(); } else { - settings.server.address = argv[i+1]; + settings->server.address = argv[i+1]; ++i; } } @@ -30,7 +30,7 @@ void Cli::argument_parser(int argc, char **argv, Settings settings) { if (!argv[i+1]) { Cli::print_usage(); } else { - settings.server.username = argv[i+1]; + settings->server.username = argv[i+1]; ++i; } } @@ -38,12 +38,12 @@ void Cli::argument_parser(int argc, char **argv, Settings settings) { if (!argv[i+1]) { Cli::print_usage(); } else { - settings.server.password = argv[i+1]; + settings->server.password = argv[i+1]; ++i; } } if (strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) { - settings.client.debug = true; + settings->client.debug = true; } } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 81610e0..97c282f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,7 @@ int main(int argc, char **argv) { load_settings(settings); save_settings(settings); - Cli::argument_parser(argc, argv, settings); + Cli::argument_parser(argc, argv, &settings); if (Platform::init_winsock()) { printf("Failed to load winsock"); From 2d27abd880303d3c7aaafeb260ccbf67c20697e8 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sat, 12 Aug 2017 22:11:02 +0200 Subject: [PATCH 04/10] Got tired of all warnings from nano/eigen. --- include/konstructs.h | 4 ++++ src/main.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/konstructs.h b/include/konstructs.h index 52db0de..624e807 100644 --- a/include/konstructs.h +++ b/include/konstructs.h @@ -1,7 +1,11 @@ #ifndef KONSTRUCTS_KONSTRUCTS_H #define KONSTRUCTS_KONSTRUCTS_H +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-attributes" #include +#pragma GCC diagnostic pop + #if defined(WIN32) #define _WINSOCKAPI_ #include diff --git a/src/main.cpp b/src/main.cpp index 97c282f..c5048f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,8 @@ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-attributes" #include +#pragma GCC diagnostic pop + #include #include #include From 71fa306b8edf8705a3e555f65bc3695ef8b70c86 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sat, 12 Aug 2017 22:48:48 +0200 Subject: [PATCH 05/10] Use new GUI class, grey screen. --- include/cli.h | 3 ++- include/gui.h | 43 +++++++++++++++++++++++++++++++++++++++++++ src/cli.cpp | 2 +- src/gui.cpp | 31 +++++++++++++++++++++++++++++++ src/main.cpp | 6 +++--- 5 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 include/gui.h create mode 100644 src/gui.cpp diff --git a/include/cli.h b/include/cli.h index d204e74..f607bbf 100644 --- a/include/cli.h +++ b/include/cli.h @@ -1,7 +1,8 @@ - #ifndef KONSTRUCTS_CLI_H #define KONSTRUCTS_CLI_H +#include "settings.h" + namespace konstructs { /** diff --git a/include/gui.h b/include/gui.h new file mode 100644 index 0000000..a58a418 --- /dev/null +++ b/include/gui.h @@ -0,0 +1,43 @@ +#ifndef KONSTRUCTS_GUI_H +#define KONSTRUCTS_GUI_H + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-attributes" +#include +#pragma GCC diagnostic pop + +#include "settings.h" + +using Eigen::Vector2i; +using Eigen::Vector2f; +using nanogui::Screen; + +namespace konstructs { + + class GUI: public nanogui::Screen { + public: + + GUI(Settings settings); + + /** + * Called by GLFW when the mouse scroll wheel is used. + */ + virtual bool scrollEvent(const Vector2i &p, const Vector2f &rel); + + /** + * Called by GLFW when a mouse button is pressed + */ + virtual bool mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers); + + /** + * Called by GLEW when a keyboard button is pressed + */ + virtual bool keyboardEvent(int key, int scancode, int action, int modifiers); + + virtual void draw(NVGcontext *ctx); + virtual void drawContents(); + }; +} + + +#endif //KONSTRUCTS_GUI_H diff --git a/src/cli.cpp b/src/cli.cpp index fcd1d53..f25be12 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -46,4 +46,4 @@ void Cli::argument_parser(int argc, char **argv, Settings *settings) { settings->client.debug = true; } } -} \ No newline at end of file +} diff --git a/src/gui.cpp b/src/gui.cpp new file mode 100644 index 0000000..1671f85 --- /dev/null +++ b/src/gui.cpp @@ -0,0 +1,31 @@ +#include "gui.h" + +using Eigen::Vector2i; +using Eigen::Vector2f; +using nanogui::Screen; +using namespace konstructs; + +GUI::GUI(Settings settings) : + nanogui::Screen(Eigen::Vector2i(settings.client.window_width, + settings.client.window_height), + "Konstructs") {} + +bool GUI::scrollEvent(const Vector2i &p, const Vector2f &rel) { + // TODO +} + +bool GUI::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) { + // TODO +} + +bool GUI::keyboardEvent(int key, int scancode, int action, int modifiers) { + // TODO +} + +void GUI::draw(NVGcontext *ctx) { + Screen::draw(ctx); +} + +void GUI::drawContents() { + // TODO +} diff --git a/src/main.cpp b/src/main.cpp index c5048f8..e8c5e0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,14 +5,14 @@ #include #include -#include #include "platform.h" #include "cli.h" +#include "gui.h" using namespace konstructs; void glfw_error(int error_code, const char *error_string) { - cout << "GLFW Error[" << error_code << "]: " << error_string << endl; + std::cout << "GLFW Error[" << error_code << "]: " << error_string << std::endl; } int main(int argc, char **argv) { @@ -33,7 +33,7 @@ int main(int argc, char **argv) { nanogui::init(); { - nanogui::ref app = new Konstructs(settings); + nanogui::ref app = new GUI(settings); app->drawAll(); app->setVisible(true); nanogui::mainloop(); From 088d199d30c4393e05b22e20094066919fb79aa3 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sat, 12 Aug 2017 23:03:14 +0200 Subject: [PATCH 06/10] Load more of the old program. --- src/konstructs.cpp | 7 ++----- src/main.cpp | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/konstructs.cpp b/src/konstructs.cpp index 7c0d10b..bd14b32 100644 --- a/src/konstructs.cpp +++ b/src/konstructs.cpp @@ -9,11 +9,7 @@ using nonstd::optional; using nonstd::nullopt; using std::pair; - Konstructs::Konstructs(Settings settings) : - nanogui::Screen(Eigen::Vector2i(settings.client.window_width, - settings.client.window_height), - KONSTRUCTS_APP_TITLE), player(0, Vector3f(0.0f, 0.0f, 0.0f), 0.0f, 0.0f), px(0), py(0), model_factory(blocks), @@ -35,7 +31,7 @@ Konstructs::Konstructs(Settings settings) : click_delay(0), settings(settings), network(settings) { - +/* using namespace nanogui; performLayout(mNVGContext); glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); @@ -54,6 +50,7 @@ Konstructs::Konstructs(Settings settings) : tinyobj::shape_t shape = load_player(); player_shader = new PlayerShader(settings.client.field_of_view, PLAYER_TEXTURE, SKY_TEXTURE, near_distance, shape); + */ } Konstructs::~Konstructs() { diff --git a/src/main.cpp b/src/main.cpp index e8c5e0e..aa87e2d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include "platform.h" #include "cli.h" #include "gui.h" +#include "konstructs.h" using namespace konstructs; @@ -34,6 +35,7 @@ int main(int argc, char **argv) { { nanogui::ref app = new GUI(settings); + Konstructs *app_data = new Konstructs(settings); // dead code, wip app->drawAll(); app->setVisible(true); nanogui::mainloop(); From a2666722c4090f56943ad27c16830cb3527b0b10 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sun, 13 Aug 2017 11:24:34 +0200 Subject: [PATCH 07/10] Main menu is back (no interactions) --- include/gui.h | 9 +++++- include/konstructs.h | 1 - src/gui.cpp | 70 ++++++++++++++++++++++++++++++++++++++-- src/konstructs.cpp | 77 +++----------------------------------------- src/main.cpp | 2 -- 5 files changed, 79 insertions(+), 80 deletions(-) diff --git a/include/gui.h b/include/gui.h index a58a418..5aefe73 100644 --- a/include/gui.h +++ b/include/gui.h @@ -7,6 +7,7 @@ #pragma GCC diagnostic pop #include "settings.h" +#include "konstructs.h" using Eigen::Vector2i; using Eigen::Vector2f; @@ -16,7 +17,6 @@ namespace konstructs { class GUI: public nanogui::Screen { public: - GUI(Settings settings); /** @@ -36,6 +36,13 @@ namespace konstructs { virtual void draw(NVGcontext *ctx); virtual void drawContents(); + + private: + void show_menu(int state, string message); + + Konstructs konstructs_data; + bool menu_state; + Settings settings; }; } diff --git a/include/konstructs.h b/include/konstructs.h index 624e807..7ed9ab4 100644 --- a/include/konstructs.h +++ b/include/konstructs.h @@ -138,7 +138,6 @@ namespace konstructs { double py; FPS fps; double last_frame; - bool menu_state; bool debug_text_enabled; nanogui::Window *window; uint32_t frame; diff --git a/src/gui.cpp b/src/gui.cpp index 1671f85..904ec78 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -6,9 +6,23 @@ using nanogui::Screen; using namespace konstructs; GUI::GUI(Settings settings) : - nanogui::Screen(Eigen::Vector2i(settings.client.window_width, - settings.client.window_height), - "Konstructs") {} + Screen(Eigen::Vector2i(settings.client.window_width, + settings.client.window_height), + "Konstructs"), + konstructs_data(settings), + menu_state(false), + settings(settings) { + + performLayout(mNVGContext); + //glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + Settings::Server server = settings.server; + if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { + //network.setup_connection(server, mGLFWWindow); + } else { + show_menu(0, string("Connect to a server")); + } + +} bool GUI::scrollEvent(const Vector2i &p, const Vector2f &rel) { // TODO @@ -29,3 +43,53 @@ void GUI::draw(NVGcontext *ctx) { void GUI::drawContents() { // TODO } + +void GUI::show_menu(int state, string message) { + using namespace nanogui; + + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glActiveTexture(GL_TEXTURE0); + + FormHelper *gui = new FormHelper(this); + Window *window = gui->addWindow({0, 0}, "Main Menu"); + gui->setFixedSize({125, 20}); + + if (state == 1) { + // Popup message + + auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Server connection", message); + } else if (state == 2) { + // Popup message with connect/cancel buttons. + + auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, + "Server connection", message, + "Reconnect", "Cancel", true); + dlg->setCallback([&](int result) { + if (result == 0) { + window->dispose(); + menu_state = false; + //network.setup_connection(settings.server, mGLFWWindow); + } + }); + } + + gui->addVariable("Server address", settings.server.address); + gui->addVariable("Username", settings.server.username); + gui->addVariable("Password", settings.server.password); + gui->addButton("Connect", [&]() { + if (settings.server.username != "" && + settings.server.password != "" && + settings.server.address != "") { + // Note: The mouse pointer is intentionally not locked here. + // See: setup_connection() + window->dispose(); + menu_state = false; + // network.setup_connection(settings.server, mGLFWWindow); + // save_settings(settings); + } + }); + + window->center(); + performLayout(mNVGContext); + menu_state = true; +} \ No newline at end of file diff --git a/src/konstructs.cpp b/src/konstructs.cpp index bd14b32..ce746b7 100644 --- a/src/konstructs.cpp +++ b/src/konstructs.cpp @@ -25,22 +25,12 @@ Konstructs::Konstructs(Settings settings) : last_frame(glfwGetTime()), looking_at(nullopt), hud(17, 14, 9), - menu_state(false), debug_text_enabled(false), frame(0), click_delay(0), settings(settings), network(settings) { -/* - using namespace nanogui; - performLayout(mNVGContext); - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - Settings::Server server = settings.server; - if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { - network.setup_connection(server, mGLFWWindow); - } else { - show_menu(0, string("Connect to a server")); - } + blocks.is_plant[SOLID_TYPE] = 0; blocks.is_obstacle[SOLID_TYPE] = 1; blocks.is_transparent[SOLID_TYPE] = 0; @@ -50,7 +40,6 @@ Konstructs::Konstructs(Settings settings) : tinyobj::shape_t shape = load_player(); player_shader = new PlayerShader(settings.client.field_of_view, PLAYER_TEXTURE, SKY_TEXTURE, near_distance, shape); - */ } Konstructs::~Konstructs() { @@ -78,7 +67,7 @@ bool Konstructs::mouseButtonEvent(const Vector2i &p, int button, bool down, int } } } - } else if (!menu_state) { + } else if (false) { // TODO !menu_state) { // Clicking at the window captures the mouse pointer glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } @@ -97,11 +86,6 @@ bool Konstructs::keyboardEvent(int key, int scancode, int action, int modifiers) } } else if (key == GLFW_KEY_F1 && action == GLFW_PRESS) { // TODO: implement this again when time has come ;) - /*if (!menu_state) { - show_menu("","",""); - } else { - hide_menu(); - }*/ } else if (key == settings.keys.debug && action == GLFW_PRESS) { debug_text_enabled = !debug_text_enabled; } else if (key == settings.keys.fly @@ -165,12 +149,12 @@ void Konstructs::drawContents() { } player_shader->render(player, mSize.x(), mSize.y(), daylight(), time_of_day(), view_distance); - if (looking_at && !hud.get_interactive() && !menu_state) { + if (looking_at && !hud.get_interactive() && false) { // TODO !menu_state) { selection_shader.render(player, mSize.x(), mSize.y(), looking_at->second.position, view_distance); } glClear(GL_DEPTH_BUFFER_BIT); - if (!hud.get_interactive() && !menu_state) { + if (!hud.get_interactive() && false ) { // TODO !menu_state) { crosshair_shader.render(mSize.x(), mSize.y()); } double mx, my; @@ -178,8 +162,6 @@ void Konstructs::drawContents() { hud_shader.render(mSize.x(), mSize.y(), mx, my, hud, blocks); update_radius(); print_top_text(); - } else if (!menu_state) { - show_menu(2, network.get_client()->get_error_message()); } } @@ -407,54 +389,3 @@ float Konstructs::daylight() { return 1 - 1 / (1 + powf(2, -t)); } } - - -void Konstructs::show_menu(int state, string message) { - using namespace nanogui; - - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - glActiveTexture(GL_TEXTURE0); - - FormHelper *gui = new FormHelper(this); - window = gui->addWindow({0, 0}, "Main Menu"); - gui->setFixedSize({125, 20}); - - if (state == 1) { - // Popup message - - auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Server connection", message); - } else if (state == 2) { - // Popup message with connect/cancel buttons. - - auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, - "Server connection", message, - "Reconnect", "Cancel", true); - dlg->setCallback([&](int result) { - if (result == 0) { - window->dispose(); - menu_state = false; - network.setup_connection(settings.server, mGLFWWindow); - } - }); - } - - gui->addVariable("Server address", settings.server.address); - gui->addVariable("Username", settings.server.username); - gui->addVariable("Password", settings.server.password); - gui->addButton("Connect", [&]() { - if (settings.server.username != "" && - settings.server.password != "" && - settings.server.address != "") { - // Note: The mouse pointer is intentionally not locked here. - // See: setup_connection() - window->dispose(); - menu_state = false; - network.setup_connection(settings.server, mGLFWWindow); - save_settings(settings); - } - }); - - window->center(); - performLayout(mNVGContext); - menu_state = true; -} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index aa87e2d..e8c5e0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,6 @@ #include "platform.h" #include "cli.h" #include "gui.h" -#include "konstructs.h" using namespace konstructs; @@ -35,7 +34,6 @@ int main(int argc, char **argv) { { nanogui::ref app = new GUI(settings); - Konstructs *app_data = new Konstructs(settings); // dead code, wip app->drawAll(); app->setVisible(true); nanogui::mainloop(); From 24a5072df4525f5f1a9fd428af93e363940444f1 Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sun, 13 Aug 2017 14:15:20 +0200 Subject: [PATCH 08/10] Menu interaction works. --- include/gui.h | 25 +++++++++++++++++++++++++ src/gui.cpp | 24 ++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/include/gui.h b/include/gui.h index 5aefe73..d09447b 100644 --- a/include/gui.h +++ b/include/gui.h @@ -13,6 +13,10 @@ using Eigen::Vector2i; using Eigen::Vector2f; using nanogui::Screen; +#define KONSTRUCTS_GUI_MENU_NORMAL 0 +#define KONSTRUCTS_GUI_MENU_POPUP 1 +#define KONSTRUCTS_GUI_MENU_RECONNECT 2 + namespace konstructs { class GUI: public nanogui::Screen { @@ -38,11 +42,32 @@ namespace konstructs { virtual void drawContents(); private: + + /** + * Display the main menu + * @param state KONSTRUCTS_GUI_MENU_NORMAL = No extra popups + * KONSTRUCTS_GUI_MENU_POPUP = A popup message + * KONSTRUCTS_GUI_MENU_RECONNECT = Server connection retry dialog + * @param message A message to be displayed + */ void show_menu(int state, string message); + /** + * Holds various konstructs data like for example shaders + * and the chunk processing facility. This is mainly here + * for legacy reasons and may be removed in the future. + */ Konstructs konstructs_data; + + /** + * Show or hide the mouse pointer. + * @param state Mouse pointer state + */ + void show_pointer(bool state); + bool menu_state; Settings settings; + nanogui::Window *window; }; } diff --git a/src/gui.cpp b/src/gui.cpp index 904ec78..6769e8b 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -14,12 +14,12 @@ GUI::GUI(Settings settings) : settings(settings) { performLayout(mNVGContext); - //glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + show_pointer(false); Settings::Server server = settings.server; if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { //network.setup_connection(server, mGLFWWindow); } else { - show_menu(0, string("Connect to a server")); + show_menu(KONSTRUCTS_GUI_MENU_NORMAL, string("Connect to a server")); } } @@ -29,7 +29,7 @@ bool GUI::scrollEvent(const Vector2i &p, const Vector2f &rel) { } bool GUI::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) { - // TODO + return nanogui::Screen::mouseButtonEvent(p, button, down, modifiers); } bool GUI::keyboardEvent(int key, int scancode, int action, int modifiers) { @@ -47,18 +47,18 @@ void GUI::drawContents() { void GUI::show_menu(int state, string message) { using namespace nanogui; - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + show_pointer(true); glActiveTexture(GL_TEXTURE0); FormHelper *gui = new FormHelper(this); - Window *window = gui->addWindow({0, 0}, "Main Menu"); + window = gui->addWindow({0, 0}, "Main Menu"); gui->setFixedSize({125, 20}); - if (state == 1) { + if (state == KONSTRUCTS_GUI_MENU_POPUP) { // Popup message auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, "Server connection", message); - } else if (state == 2) { + } else if (state == KONSTRUCTS_GUI_MENU_RECONNECT) { // Popup message with connect/cancel buttons. auto dlg = new MessageDialog(this, MessageDialog::Type::Warning, @@ -85,11 +85,19 @@ void GUI::show_menu(int state, string message) { window->dispose(); menu_state = false; // network.setup_connection(settings.server, mGLFWWindow); - // save_settings(settings); + save_settings(settings); } }); window->center(); performLayout(mNVGContext); menu_state = true; +} + +void GUI::show_pointer(bool state) { + if (state) { + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } else { + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } } \ No newline at end of file From 95de61298b3087255d15bbbb30cf5e05fd7c712d Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sun, 13 Aug 2017 15:46:33 +0200 Subject: [PATCH 09/10] Better manage connection and the login UI. --- include/gui.h | 7 +++++++ include/network.h | 2 +- src/gui.cpp | 24 +++++++++++++++++------- src/network.cpp | 13 ++++++------- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/include/gui.h b/include/gui.h index d09447b..7c8fd6a 100644 --- a/include/gui.h +++ b/include/gui.h @@ -65,8 +65,15 @@ namespace konstructs { */ void show_pointer(bool state); + /** + * Connect to the server, this is a convenience method wrapping + * network.setup_connection(..). + */ + bool connect(); + bool menu_state; Settings settings; + Network network; nanogui::Window *window; }; } diff --git a/include/network.h b/include/network.h index dbe7320..8947c64 100644 --- a/include/network.h +++ b/include/network.h @@ -45,7 +45,7 @@ namespace konstructs { PlayerShader *player_shader, Vector3i player_chunk, BlockTypeInfo blocks); - void setup_connection(Settings::Server server, GLFWwindow *mGLFWWindow); + bool setup_connection(Settings::Server server, GLFWwindow *mGLFWWindow); Client* get_client(); private: diff --git a/src/gui.cpp b/src/gui.cpp index 6769e8b..fef493e 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -11,13 +11,14 @@ GUI::GUI(Settings settings) : "Konstructs"), konstructs_data(settings), menu_state(false), - settings(settings) { + settings(settings), + network(settings) { performLayout(mNVGContext); show_pointer(false); Settings::Server server = settings.server; if (server.username.size() > 0 && server.password.size() > 0 && server.address.size() > 0) { - //network.setup_connection(server, mGLFWWindow); + connect(); } else { show_menu(KONSTRUCTS_GUI_MENU_NORMAL, string("Connect to a server")); } @@ -68,7 +69,7 @@ void GUI::show_menu(int state, string message) { if (result == 0) { window->dispose(); menu_state = false; - //network.setup_connection(settings.server, mGLFWWindow); + connect(); } }); } @@ -80,12 +81,11 @@ void GUI::show_menu(int state, string message) { if (settings.server.username != "" && settings.server.password != "" && settings.server.address != "") { - // Note: The mouse pointer is intentionally not locked here. - // See: setup_connection() window->dispose(); menu_state = false; - // network.setup_connection(settings.server, mGLFWWindow); - save_settings(settings); + if (connect()) { + save_settings(settings); + } } }); @@ -100,4 +100,14 @@ void GUI::show_pointer(bool state) { } else { glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } +} + +bool GUI::connect() { + if (network.setup_connection(settings.server, mGLFWWindow)) { + show_pointer(false); + return true; + } else { + show_menu(KONSTRUCTS_GUI_MENU_RECONNECT, "Error: Connection failed"); + return false; + } } \ No newline at end of file diff --git a/src/network.cpp b/src/network.cpp index 1661516..33e5f09 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -231,19 +231,18 @@ void Network::handle_time(const string &str) { glfwSetTime((double) time_value); } -void Network::setup_connection(Settings::Server server, GLFWwindow *mGLFWWindow) { +bool Network::setup_connection(Settings::Server server, GLFWwindow *mGLFWWindow) { try { client.open_connection(server); + /* TODO: We do not really know if the server accepted our protocol + version, or our username/password combo here. set_connected + will release a lock and start to receive data, and fail. */ load_textures(); client.set_connected(true); - - // Lock the mouse _after_ a successful connection. This prevents the - // player to get stuck if he or she connects to a server that drops - // the SYN. - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + return true; } catch (const std::exception &ex) { - // show_menu(1, client.get_error_message()); TODO: fix this std::cerr << client.get_error_message() << std::endl; + return false; } } From 30c6bdc4f8b6de802321e3a0a97ef77ae016aaef Mon Sep 17 00:00:00 2001 From: Stefan Berggren Date: Sun, 13 Aug 2017 22:18:35 +0200 Subject: [PATCH 10/10] Move over mouseButtonEvent to GUI. --- include/gui.h | 6 ++++++ include/konstructs.h | 12 ++++-------- src/gui.cpp | 34 +++++++++++++++++++++++++++++++-- src/konstructs.cpp | 45 ++++++-------------------------------------- 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/include/gui.h b/include/gui.h index 7c8fd6a..1c26994 100644 --- a/include/gui.h +++ b/include/gui.h @@ -71,6 +71,12 @@ namespace konstructs { */ bool connect(); + /** + * Translate GLFW mouse button presses to 1 to 3 + * @param button The GLFW mouse button ID + */ + int translate_button(int button); + bool menu_state; Settings settings; Network network; diff --git a/include/konstructs.h b/include/konstructs.h index 7ed9ab4..3d5a94e 100644 --- a/include/konstructs.h +++ b/include/konstructs.h @@ -64,6 +64,10 @@ namespace konstructs { virtual void draw(NVGcontext *ctx); virtual void drawContents(); + // Members that are called from gui.cpp + Hud hud; + HudShader hud_shader; + private: /** This function uses nanovg to print text on top of the screen. This is @@ -71,12 +75,6 @@ namespace konstructs { */ void print_top_text(); - /** - * Translate GLFW mouse button presses to 1 to 3 - * @param button The GLFW mouse button ID - */ - int translate_button(int button); - /** * Update view distance variable dependent on your framerate. */ @@ -127,13 +125,11 @@ namespace konstructs { SkyShader sky_shader; ChunkShader chunk_shader; SelectionShader selection_shader; - HudShader hud_shader; PlayerShader *player_shader; ChunkModelFactory model_factory; Player player; Vector3i player_chunk; optional> looking_at; - Hud hud; double px; double py; FPS fps; diff --git a/src/gui.cpp b/src/gui.cpp index fef493e..27dd0be 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -26,10 +26,29 @@ GUI::GUI(Settings settings) : } bool GUI::scrollEvent(const Vector2i &p, const Vector2f &rel) { - // TODO + konstructs_data.hud.scroll(rel[1]); } bool GUI::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) { + if (konstructs_data.hud.get_interactive()) { + if (down) { + double x, y; + glfwGetCursorPos(mGLFWWindow, &x, &y); + + auto clicked_at = konstructs_data.hud_shader.clicked_at(x, y, mSize.x(), mSize.y()); + + if (clicked_at) { + Vector2i pos = *clicked_at; + if (konstructs_data.hud.active(pos)) { + int index = pos[0] + pos[1] * 17; + network.get_client()->click_inventory(index, translate_button(button)); + } + } + } + } else if (!menu_state) { + // Clicking at the window captures the mouse pointer + glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } return nanogui::Screen::mouseButtonEvent(p, button, down, modifiers); } @@ -110,4 +129,15 @@ bool GUI::connect() { show_menu(KONSTRUCTS_GUI_MENU_RECONNECT, "Error: Connection failed"); return false; } -} \ No newline at end of file +} + +int GUI::translate_button(int button) { + switch (button) { + case GLFW_MOUSE_BUTTON_1: + return 1; + case GLFW_MOUSE_BUTTON_2: + return 2; + case GLFW_MOUSE_BUTTON_3: + return 3; + } +} diff --git a/src/konstructs.cpp b/src/konstructs.cpp index ce746b7..e6e01e3 100644 --- a/src/konstructs.cpp +++ b/src/konstructs.cpp @@ -46,34 +46,6 @@ Konstructs::~Konstructs() { delete player_shader; } -bool Konstructs::scrollEvent(const Vector2i &p, const Vector2f &rel) { - hud.scroll(rel[1]); - return true; -} - -bool Konstructs::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) { - if (hud.get_interactive()) { - if (down) { - double x, y; - glfwGetCursorPos(mGLFWWindow, &x, &y); - - auto clicked_at = hud_shader.clicked_at(x, y, mSize.x(), mSize.y()); - - if (clicked_at) { - Vector2i pos = *clicked_at; - if (hud.active(pos)) { - int index = pos[0] + pos[1] * 17; - network.get_client()->click_inventory(index, translate_button(button)); - } - } - } - } else if (false) { // TODO !menu_state) { - // Clicking at the window captures the mouse pointer - glfwSetInputMode(mGLFWWindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - } - return nanogui::Screen::mouseButtonEvent(p, button, down, modifiers); -} - bool Konstructs::keyboardEvent(int key, int scancode, int action, int modifiers) { if (nanogui::Screen::keyboardEvent(key, scancode, action, modifiers)) { return true; @@ -213,17 +185,6 @@ void Konstructs::print_top_text() { nvgTextBox(mNVGContext, 10, 20, width - 10, os.str().c_str(), NULL); } -int Konstructs::translate_button(int button) { - switch (button) { - case GLFW_MOUSE_BUTTON_1: - return 1; - case GLFW_MOUSE_BUTTON_2: - return 2; - case GLFW_MOUSE_BUTTON_3: - return 3; - } -} - bool Konstructs::update_view_distance() { double frame_fps = 1.15 / frame_time; float fps = settings.client.frames_per_second; @@ -250,7 +211,12 @@ void Konstructs::update_radius() { } } +// TODO: Remove +bool Konstructs::scrollEvent(const Vector2i &p, const Vector2f &rel) {} +bool Konstructs::mouseButtonEvent(const Vector2i &p, int button, bool down, int modifiers) {} + void Konstructs::handle_mouse() { + /* int exclusive = glfwGetInputMode(mGLFWWindow, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; if (exclusive && (px || py)) { @@ -314,6 +280,7 @@ void Konstructs::handle_mouse() { } else { glfwGetCursorPos(mGLFWWindow, &px, &py); } + */ } void Konstructs::handle_keys() {