diff --git a/CMakeLists.txt b/CMakeLists.txt index 78d2adb..b82dd48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,8 @@ set(CLIENT_ONLY_SOURCES "src/gui/context.cpp" "src/gui/font.hpp" "src/gui/font.cpp" + "src/gui/menu.hpp" + "src/gui/menu.cpp" "src/utils/files.cpp" ) diff --git a/src/client/app.cpp b/src/client/app.cpp index fea2985..6595d30 100644 --- a/src/client/app.cpp +++ b/src/client/app.cpp @@ -35,27 +35,6 @@ void App::Frame() delta_time_ = 0.1f; // Cap delta time to avoid large jumps } - // detect inputs originating in this frame - //game::PlayerInputFlags new_input = input_ & ~prev_input_; - - // detect input changes - for (size_t i = 0; i < game::IN__COUNT; ++i) - { - auto in_old = prev_input_ & (1 << i); - auto in_new = input_ & (1 << i); - - if (in_old > in_new) // released - { - SendInput(static_cast(i), false); - } - else if (in_new > in_old) // pressed - { - SendInput(static_cast(i), true); - } - } - - prev_input_ = input_; - if (session_) { game::view::UpdateInfo updinfo; @@ -76,18 +55,31 @@ void App::Frame() gui_.Begin(); - const game::view::WorldView* world; + // draw session if (session_) { session_->Draw(dlist_, params, gui_); } + // draw stats + UpdateStats(); + DrawStats(); + // draw chat UpdateChat(); DrawChat(); + // draw menu + if (menu_) + { + auto menu_size = menu_->MeasureSize(); + menu_->Draw(gui_, glm::vec2(viewport_size_) - menu_size - 10.0f); + } + gui_.Render(); renderer_.DrawList(dlist_, params); + + ++stat_frames_; } void App::Connected() @@ -97,11 +89,6 @@ void App::Connected() // init session session_ = std::make_unique(*this); - - // send login - auto msg = BeginMsg(net::MSG_ID); - net::PlayerName name; - msg.Write(name); } void App::ProcessMessage(net::InMessage& msg) @@ -118,6 +105,12 @@ void App::ProcessMessage(net::InMessage& msg) { std::cerr << "FAILED to process message!" << std::endl; } + + // record stats + ++stat_msgs_; + stat_msglen_total_ += s; + stat_msglen_min_ = std::min(stat_msglen_min_, s); + stat_msglen_max_ = std::max(stat_msglen_max_, s); } void App::Disconnected(const std::string& reason) @@ -130,6 +123,40 @@ void App::Disconnected(const std::string& reason) session_.reset(); } +static bool InputToMenuInput(game::PlayerInputType& in, gui::MenuInput& mi) +{ + switch (in) + { + case game::IN_FORWARD: mi = gui::MI_UP; return true; + case game::IN_BACKWARD: mi = gui::MI_DOWN; return true; + case game::IN_LEFT: mi = gui::MI_LEFT; return true; + case game::IN_RIGHT: mi = gui::MI_RIGHT; return true; + case game::IN_JUMP: mi = gui::MI_ENTER; return true; + case game::IN_CROUCH: mi = gui::MI_BACK; return true; + default: return false; + } +}; + +void App::Input(game::PlayerInputType in, bool pressed, bool repeated) +{ + if (in == game::IN_MENU && pressed) + { + OpenSettings(); + return; + } + + gui::MenuInput mi; + if (menu_ && pressed && InputToMenuInput(in, mi)) + { + menu_->Input(mi); + return; + } + + if (session_) + session_->Input(in, pressed, repeated); + +} + void App::MouseMove(const glm::vec2& delta) { float sensitivity = 0.002f; // Sensitivity factor for mouse movement @@ -156,15 +183,6 @@ void App::AddChatMessagePrefix(const std::string& prefix, const std::string& tex App::~App() {} -void App::SendInput(game::PlayerInputType type, bool enable) -{ - auto msg = BeginMsg(net::MSG_IN); - uint8_t val = type; - if (enable) - val |= 128; - msg.Write(val); -} - void App::UpdateChat() { // remove expired or over the limit messages @@ -191,3 +209,74 @@ void App::DrawChat() gui_.DrawText(chat_[i].text, pos, color); } } + +static void AddSlider(gui::Menu& menu, std::string text, int& value, int min, int max) +{ + auto& slider = menu.Add(std::move(text)); + auto on_switch = [&slider, &value, min, max] (int v) { + value += v; + + // clamp + if (value < min) + value = min; + else if (value > max) + value = max; + + slider.SetSelectionText(std::to_string(value)); + }; + + slider.SetSwitchCallback(on_switch); + on_switch(0); +} + +void App::OpenSettings() +{ + menu_ = std::make_unique(); + + AddSlider(*menu_, "jak moc to řve", volume_, 0, 100); + + auto& ok = menu_->Add("0k"); + ok.SetClickCallback([this] { menu_.reset(); }); +} + +#define COL_LABEL "^ccc" +#define COL_VALUE "^5ff" + +void App::UpdateStats() +{ + if (time_ < stats_time_ + 1.0f) + return; + + stats_time_ = time_; + + fps_text_.clear(); + fps_text_ += COL_VALUE; + fps_text_ += std::to_string(stat_frames_); + fps_text_ += COL_LABEL " fps"; + + if (stat_msgs_ > 0) + { + msglen_text_ = COL_LABEL "net: n=" COL_VALUE; + msglen_text_ += std::to_string(stat_msgs_); + msglen_text_ += COL_LABEL " min=" COL_VALUE; + msglen_text_ += std::to_string(stat_msglen_min_); + msglen_text_ += COL_LABEL " max=" COL_VALUE; + msglen_text_ += std::to_string(stat_msglen_max_); + msglen_text_ += COL_LABEL " total=" COL_VALUE; + msglen_text_ += std::to_string(stat_msglen_total_); + } + + stat_frames_ = 0; + stat_msgs_ = 0; + stat_msglen_total_ = 0; + stat_msglen_min_ = SIZE_MAX; + stat_msglen_max_ = 0; +} + +void App::DrawStats() +{ + glm::vec2 pos(viewport_size_.x - 5.0f, 5.0f); + gui_.DrawTextAligned(fps_text_, pos, glm::vec2(-1.0f, 0.0f)); + pos.y += 30.0f; + gui_.DrawTextAligned(msglen_text_, pos, glm::vec2(-1.0f, 0.0f)); +} diff --git a/src/client/app.hpp b/src/client/app.hpp index 4769cb1..c814f32 100644 --- a/src/client/app.hpp +++ b/src/client/app.hpp @@ -10,7 +10,7 @@ #include "audio/master.hpp" #include "net/msg_producer.hpp" #include "net/inmessage.hpp" - +#include "gui/menu.hpp" #include "gameview/client_session.hpp" struct ChatMessage @@ -20,7 +20,7 @@ struct ChatMessage glm::vec4 color = glm::vec4(1.0f); }; -class App : public net::MsgProducer +class App { public: App(); @@ -33,12 +33,15 @@ public: void SetTime(float time) { time_ = time; } void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; } - void SetInput(game::PlayerInputFlags input) { input_ = input; } + + void Input(game::PlayerInputType in, bool pressed, bool repeated); void MouseMove(const glm::vec2& delta); float GetTime() const { return time_; } float GetDeltaTime() const { return delta_time_; } + game::view::ClientSession* GetSession() { return session_.get(); } + audio::Master& GetAudioMaster() { return audiomaster_; } void AddChatMessage(const std::string& text); @@ -47,16 +50,17 @@ public: ~App(); private: - void SendInput(game::PlayerInputType type, bool enable); - void UpdateChat(); void DrawChat(); + void OpenSettings(); + + void UpdateStats(); + void DrawStats(); + private: float time_ = 0.0f; glm::ivec2 viewport_size_ = {800, 600}; - game::PlayerInputFlags input_ = 0; - game::PlayerInputFlags prev_input_ = 0; float prev_time_ = 0.0f; float delta_time_ = 0.0f; @@ -70,4 +74,19 @@ private: std::unique_ptr session_; std::deque chat_; + + std::unique_ptr menu_; + + // settings + int volume_ = 50; + + // stats + float stats_time_ = 0.0f; + size_t stat_frames_ = 0; + size_t stat_msgs_ = 0; + size_t stat_msglen_total_ = 0; + size_t stat_msglen_min_ = SIZE_MAX; + size_t stat_msglen_max_ = 0; + std::string fps_text_ = { 0 }; + std::string msglen_text_ = { 0 }; }; diff --git a/src/client/main.cpp b/src/client/main.cpp index 8ce0521..d6ffbaf 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef EMSCRIPTEN #include @@ -138,6 +139,20 @@ static void ShutdownSDL() SDL_Quit(); } +static const std::map s_inputmap = { + { SDL_SCANCODE_W, game::IN_FORWARD }, + { SDL_SCANCODE_S, game::IN_BACKWARD }, + { SDL_SCANCODE_A, game::IN_LEFT }, + { SDL_SCANCODE_D, game::IN_RIGHT }, + { SDL_SCANCODE_SPACE, game::IN_JUMP }, + { SDL_SCANCODE_LCTRL, game::IN_CROUCH }, + { SDL_SCANCODE_E, game::IN_USE }, + { SDL_SCANCODE_F3, game::IN_DEBUG1 }, + { SDL_SCANCODE_F4, game::IN_DEBUG2 }, + { SDL_SCANCODE_F5, game::IN_DEBUG3 }, + { SDL_SCANCODE_TAB, game::IN_MENU }, +}; + static void PollEvents() { SDL_Event event; @@ -150,13 +165,27 @@ static void PollEvents() return; case SDL_MOUSEMOTION: - int xrel = event.motion.xrel; - int yrel = event.motion.yrel; - if (xrel != 0 || yrel != 0) { - s_app->MouseMove(glm::vec2(static_cast(xrel), static_cast(yrel))); + int xrel = event.motion.xrel; + int yrel = event.motion.yrel; + if (xrel != 0 || yrel != 0) + { + s_app->MouseMove(glm::vec2(static_cast(xrel), static_cast(yrel))); + } } break; + + case SDL_KEYDOWN: + case SDL_KEYUP: + { + auto in_it = s_inputmap.find(event.key.keysym.scancode); + if (in_it != s_inputmap.end()) + { + s_app->Input(in_it->second, event.key.state == SDL_PRESSED, event.key.repeat != 0); + } + } + break; + } } @@ -326,56 +355,30 @@ static void Frame() game::PlayerInputFlags input = 0; const uint8_t* kbd_state = SDL_GetKeyboardState(nullptr); - if (kbd_state[SDL_GetScancodeFromKey(SDLK_w)]) - input |= (1 << game::IN_FORWARD); - if (kbd_state[SDL_GetScancodeFromKey(SDLK_s)]) - input |= (1 << game::IN_BACKWARD); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_a)]) - input |= (1 << game::IN_LEFT); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_d)]) - input |= (1 << game::IN_RIGHT); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_SPACE)]) - input |= (1 << game::IN_JUMP); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_LCTRL)]) - input |= (1 << game::IN_CROUCH); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_e)]) - input |= (1 << game::IN_USE); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_F3)]) - input |= (1 << game::IN_DEBUG1); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_F4)]) - input |= (1 << game::IN_DEBUG2); - - if (kbd_state[SDL_GetScancodeFromKey(SDLK_F5)]) - input |= (1 << game::IN_DEBUG3); int mouse_state = SDL_GetMouseState(nullptr, nullptr); if (mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT)) input |= (1 << game::IN_ATTACK); - s_app->SetInput(input); - s_app->Frame(); - if (s_ws_connected) + auto session = s_app->GetSession(); + if (session) { - auto msg = s_app->GetMsg(); - if (!msg.empty()) + if (s_ws_connected) { - WSSend(msg); + auto msg = session->GetMsg(); + if (!msg.empty()) + { + WSSend(msg); + } } + + session->ResetMsg(); } - s_app->ResetMsg(); - SDL_GL_SwapWindow(s_window); } diff --git a/src/game/player_input.hpp b/src/game/player_input.hpp index c6257d1..a7b65a2 100644 --- a/src/game/player_input.hpp +++ b/src/game/player_input.hpp @@ -21,6 +21,7 @@ namespace game IN_DEBUG3, IN_DEBUG4, IN_DEBUG5, + IN_MENU, IN__COUNT, }; diff --git a/src/gameview/client_session.cpp b/src/gameview/client_session.cpp index 4137d98..97d92e8 100644 --- a/src/gameview/client_session.cpp +++ b/src/gameview/client_session.cpp @@ -6,7 +6,13 @@ #include "vehicleview.hpp" -game::view::ClientSession::ClientSession(App& app) : app_(app) {} +game::view::ClientSession::ClientSession(App& app) : app_(app) +{ + // send login + auto msg = BeginMsg(net::MSG_ID); + net::PlayerName name; + msg.Write(name); +} bool game::view::ClientSession::ProcessMessage(net::InMessage& msg) { @@ -48,6 +54,17 @@ bool game::view::ClientSession::ProcessSingleMessage(net::MessageType type, net: } } +void game::view::ClientSession::Input(game::PlayerInputType in, bool pressed, bool repeated) +{ + + + if (repeated) + return; + + SendInput(in, pressed); + +} + void game::view::ClientSession::ProcessMouseMove(float delta_yaw, float delta_pitch) { yaw_ = glm::mod(yaw_ + delta_yaw, glm::two_pi()); @@ -171,6 +188,15 @@ void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListPar GetAudioMaster().SetListenerOrientation(camera_world); } +void game::view::ClientSession::SendInput(game::PlayerInputType type, bool enable) +{ + auto msg = BeginMsg(net::MSG_IN); + uint8_t val = type; + if (enable) + val |= 128; + msg.Write(val); +} + void game::view::ClientSession::SendViewAngles(float time) { if (time - last_send_time_ < 0.040f) @@ -184,7 +210,7 @@ void game::view::ClientSession::SendViewAngles(float time) if (yaw_q.value == view_yaw_q_.value && pitch_q.value == view_pitch_q_.value) return; - auto msg = app_.BeginMsg(net::MSG_VIEWANGLES); + auto msg = BeginMsg(net::MSG_VIEWANGLES); msg.Write(yaw_q.value); msg.Write(pitch_q.value); diff --git a/src/gameview/client_session.hpp b/src/gameview/client_session.hpp index 70ed182..3b861d5 100644 --- a/src/gameview/client_session.hpp +++ b/src/gameview/client_session.hpp @@ -8,13 +8,15 @@ #include "gfx/renderer.hpp" #include "net/defs.hpp" #include "net/inmessage.hpp" +#include "net/msg_producer.hpp" +#include "game/player_input.hpp" class App; namespace game::view { -class ClientSession +class ClientSession : public net::MsgProducer { public: ClientSession(App& app); @@ -22,15 +24,14 @@ public: bool ProcessMessage(net::InMessage& msg); bool ProcessSingleMessage(net::MessageType type, net::InMessage& msg); + void Input(game::PlayerInputType in, bool pressed, bool repeated); void ProcessMouseMove(float delta_yaw, float delta_pitch); void Update(const UpdateInfo& info); void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui); const WorldView* GetWorld() const { return world_.get(); } - void GetViewInfo(glm::vec3& eye, glm::mat4& view) const; - audio::Master& GetAudioMaster() const; private: @@ -40,6 +41,8 @@ private: bool ProcessChatMsg(net::InMessage& msg); void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params, gui::Context& gui); + + void SendInput(game::PlayerInputType type, bool enable); void SendViewAngles(float time); private: diff --git a/src/gui/context.cpp b/src/gui/context.cpp index d42f424..f63f612 100644 --- a/src/gui/context.cpp +++ b/src/gui/context.cpp @@ -131,7 +131,7 @@ void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_ BeginTexture(font_->GetTexture().get()); uint32_t cp = 0; - const float line_height = font_->GetLineHeight(); + const float line_height = font_->GetLineHeight() * scale; float space_size = line_height * 0.3f; glm::vec2 cursor = pos; @@ -191,7 +191,7 @@ void gui::Context::DrawText(std::string_view text, const glm::vec2& pos, uint32_ if (!glyph) continue; // Dont even have "missing" glyph, font is shit - glm::vec2 p0 = cursor + glyph->offset; + glm::vec2 p0 = cursor + glyph->offset * scale; glm::vec2 p1 = p0 + glyph->size * scale; PushRect(p0, glyph->uv0, p1, glyph->uv1, curr_color); diff --git a/src/gui/menu.cpp b/src/gui/menu.cpp index 0141f2b..aa2fed6 100644 --- a/src/gui/menu.cpp +++ b/src/gui/menu.cpp @@ -60,10 +60,10 @@ void gui::Menu::SwitchFocus(int dir) // ButtonMenuItem -gui::ButtonMenuItem::ButtonMenuItem(std::string text, std::function click_cb) - : text_(std::move(text)), click_cb_(std::move(click_cb)) +gui::ButtonMenuItem::ButtonMenuItem(std::string text) + : text_(std::move(text)) { - size_ = glm::vec2(500.0f, 30.0f); + size_ = glm::vec2(300.0f, 30.0f); } void gui::ButtonMenuItem::Draw(const DrawMenuItemArgs& args) const @@ -79,11 +79,15 @@ void gui::ButtonMenuItem::Input(MenuInput in) click_cb_(); } +void gui::ButtonMenuItem::SetClickCallback(std::function click_cb) +{ + click_cb_ = std::move(click_cb); +} + // SelectMenuItem -gui::SelectMenuItem::SelectMenuItem(std::string text, std::function click_cb, - std::function switch_cb) - : ButtonMenuItem(std::move(text), std::move(click_cb)), switch_cb_(std::move(switch_cb)) +gui::SelectMenuItem::SelectMenuItem(std::string text) + : ButtonMenuItem(std::move(text)) { } @@ -118,3 +122,8 @@ void gui::SelectMenuItem::Input(MenuInput in) break; } } + +void gui::SelectMenuItem::SetSwitchCallback(std::function switch_cb) +{ + switch_cb_ = std::move(switch_cb); +} diff --git a/src/gui/menu.hpp b/src/gui/menu.hpp index 0d58d9f..1daa289 100644 --- a/src/gui/menu.hpp +++ b/src/gui/menu.hpp @@ -82,11 +82,13 @@ class ButtonMenuItem : public MenuItem public: using Super = MenuItem; - ButtonMenuItem(std::string text, std::function click_cb); + ButtonMenuItem(std::string text); virtual void Draw(const DrawMenuItemArgs& args) const override; virtual void Input(MenuInput in) override; + void SetClickCallback(std::function click_cb); + virtual ~ButtonMenuItem() = default; private: @@ -99,11 +101,12 @@ class SelectMenuItem : public ButtonMenuItem public: using Super = ButtonMenuItem; - SelectMenuItem(std::string text, std::function click_cb, std::function switch_cb); + SelectMenuItem(std::string text); virtual void Draw(const DrawMenuItemArgs& args) const override; virtual void Input(MenuInput in) override; + void SetSwitchCallback(std::function switch_cb); void SetSelectionText(std::string select_text) { select_text_ = std::move(select_text); } virtual ~SelectMenuItem() = default;