Remake input, add test menu and debug info

This commit is contained in:
zbyv 2026-03-11 22:06:54 +01:00
parent b6f6b28c98
commit e4aded6099
10 changed files with 253 additions and 98 deletions

View File

@ -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"
)

View File

@ -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<game::PlayerInputType>(i), false);
}
else if (in_new > in_old) // pressed
{
SendInput(static_cast<game::PlayerInputType>(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<game::view::ClientSession>(*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<gui::SelectMenuItem>(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<gui::Menu>();
AddSlider(*menu_, "jak moc to řve", volume_, 0, 100);
auto& ok = menu_->Add<gui::ButtonMenuItem>("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));
}

View File

@ -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<game::view::ClientSession> session_;
std::deque<ChatMessage> chat_;
std::unique_ptr<gui::Menu> 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 };
};

View File

@ -4,6 +4,7 @@
#include <vector>
#include <chrono>
#include <thread>
#include <map>
#ifdef EMSCRIPTEN
#include <emscripten.h>
@ -138,6 +139,20 @@ static void ShutdownSDL()
SDL_Quit();
}
static const std::map<SDL_Scancode, game::PlayerInputType> 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<float>(xrel), static_cast<float>(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,55 +355,29 @@ 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();
auto session = s_app->GetSession();
if (session)
{
if (s_ws_connected)
{
auto msg = s_app->GetMsg();
auto msg = session->GetMsg();
if (!msg.empty())
{
WSSend(msg);
}
}
s_app->ResetMsg();
session->ResetMsg();
}
SDL_GL_SwapWindow(s_window);
}

View File

@ -21,6 +21,7 @@ namespace game
IN_DEBUG3,
IN_DEBUG4,
IN_DEBUG5,
IN_MENU,
IN__COUNT,
};

View File

@ -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<float>());
@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -60,10 +60,10 @@ void gui::Menu::SwitchFocus(int dir)
// ButtonMenuItem
gui::ButtonMenuItem::ButtonMenuItem(std::string text, std::function<void()> 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<void()> click_cb)
{
click_cb_ = std::move(click_cb);
}
// SelectMenuItem
gui::SelectMenuItem::SelectMenuItem(std::string text, std::function<void()> click_cb,
std::function<void(int)> 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<void(int)> switch_cb)
{
switch_cb_ = std::move(switch_cb);
}

View File

@ -82,11 +82,13 @@ class ButtonMenuItem : public MenuItem
public:
using Super = MenuItem;
ButtonMenuItem(std::string text, std::function<void()> click_cb);
ButtonMenuItem(std::string text);
virtual void Draw(const DrawMenuItemArgs& args) const override;
virtual void Input(MenuInput in) override;
void SetClickCallback(std::function<void()> click_cb);
virtual ~ButtonMenuItem() = default;
private:
@ -99,11 +101,12 @@ class SelectMenuItem : public ButtonMenuItem
public:
using Super = ButtonMenuItem;
SelectMenuItem(std::string text, std::function<void()> click_cb, std::function<void(int)> switch_cb);
SelectMenuItem(std::string text);
virtual void Draw(const DrawMenuItemArgs& args) const override;
virtual void Input(MenuInput in) override;
void SetSwitchCallback(std::function<void(int)> switch_cb);
void SetSelectionText(std::string select_text) { select_text_ = std::move(select_text); }
virtual ~SelectMenuItem() = default;