Version check & login form init

This commit is contained in:
tovjemam 2026-03-14 20:31:48 +01:00
parent e1f9212d3c
commit c6d9a2cd77
16 changed files with 184 additions and 30 deletions

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>PortalGame</title> <title>Fekalni gtacko</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style> <style>
@ -14,6 +14,7 @@
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
background: black; background: black;
color: aqua;
} }
canvas { canvas {
@ -25,15 +26,30 @@
</head> </head>
<body> <body>
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas> <canvas id="canvas" oncontextmenu="event.preventDefault()" style="display:none"></canvas>
<div id="loginform">
<h1>fekální gtačko</h1>
<form>
kdo <input type="text" name="name" id="name"><br>
kam <input type="text" name="url" id="url"><br>
<input type="button" value="0k" onclick="ok()">
</form>
</div>
<script> <script>
var inited = false;
var Module = { var Module = {
preRun: [], preRun: [],
postRun: [], postRun: [],
canvas: (function() { canvas: (function() {
return document.getElementById('canvas'); return document.getElementById('canvas');
})() })(),
onRuntimeInitialized: function() {
inited = true;
}
}; };
// Resize canvas to fit the window and inform SDL (via Emscripten) // Resize canvas to fit the window and inform SDL (via Emscripten)
@ -51,6 +67,23 @@
window.addEventListener('resize', resizeCanvas); window.addEventListener('resize', resizeCanvas);
window.addEventListener('load', resizeCanvas); window.addEventListener('load', resizeCanvas);
function ok()
{
if (!inited)
return;
const name = document.getElementById("name").value;
const url = document.getElementById("url").value;
Module.ccall("SetName", name);
Module.ccall("SetUrl", url);
document.getElementById("loginform").style = "display:none";
document.getElementById("canvas").style = "";
Module.ccall("RunMain");
}
</script> </script>
{{{ SCRIPT }}} {{{ SCRIPT }}}

View File

@ -34,6 +34,9 @@ public:
void SetTime(float time) { time_ = time; } void SetTime(float time) { time_ = time; }
void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; } void SetViewportSize(int width, int height) { viewport_size_ = {width, height}; }
void SetUserName(const std::string& username) { username_ = username; }
const std::string& GetUserName() const { return username_; }
void Input(game::PlayerInputType in, bool pressed, bool repeated); void Input(game::PlayerInputType in, bool pressed, bool repeated);
void MouseMove(const glm::vec2& delta); void MouseMove(const glm::vec2& delta);
@ -71,6 +74,7 @@ private:
audio::Master audiomaster_; audio::Master audiomaster_;
std::string username_;
std::unique_ptr<game::view::ClientSession> session_; std::unique_ptr<game::view::ClientSession> session_;
std::deque<ChatMessage> chat_; std::deque<ChatMessage> chat_;

View File

@ -28,11 +28,20 @@
#include "app.hpp" #include "app.hpp"
#include "gl.hpp" #include "gl.hpp"
static std::string s_username;
static std::string s_url;
static SDL_Window *s_window = nullptr; static SDL_Window *s_window = nullptr;
static SDL_GLContext s_context = nullptr; static SDL_GLContext s_context = nullptr;
static bool s_quit = false; static bool s_quit = false;
static std::unique_ptr<App> s_app; static std::unique_ptr<App> s_app;
struct ClientConfig
{
std::string username;
std::string url;
};
static void ThrowSDLError(const std::string& message) static void ThrowSDLError(const std::string& message)
{ {
std::string error = SDL_GetError(); std::string error = SDL_GetError();
@ -66,7 +75,7 @@ static void InitSDL()
std::cout << "Creating SDL window..." << std::endl; std::cout << "Creating SDL window..." << std::endl;
s_window = s_window =
SDL_CreateWindow("PortalGame", 100, 100, 640, 480, SDL_CreateWindow("Fekalni gtacko", 100, 100, 640, 480,
SDL_WINDOW_SHOWN /* | SDL_WINDOW_MAXIMIZED */| SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); SDL_WINDOW_SHOWN /* | SDL_WINDOW_MAXIMIZED */| SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!s_window) if (!s_window)
{ {
@ -384,7 +393,10 @@ static void Frame()
} }
static void Main() { static void Main() {
if (!WSInit(WS_URL)) if (s_url.empty())
s_url = WS_URL;
if (!WSInit(s_url.c_str()))
return; return;
InitSDL(); InitSDL();
@ -401,8 +413,10 @@ static void Main() {
SDL_SetRelativeMouseMode(SDL_TRUE); SDL_SetRelativeMouseMode(SDL_TRUE);
s_app = std::make_unique<App>(); s_app = std::make_unique<App>();
s_app->AddChatMessagePrefix("WebSocket", "připojování na " + std::string(WS_URL)); s_app->SetUserName(s_username);
s_app->AddChatMessagePrefix("WebSocket", "připojování na " + s_url);
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
emscripten_set_main_loop(Frame, 0, true); emscripten_set_main_loop(Frame, 0, true);
@ -429,7 +443,7 @@ static void Main() {
std::this_thread::sleep_for(t_next - t_now); std::this_thread::sleep_for(t_next - t_now);
} }
} }
s_app.reset(); s_app.reset();
ShutdownGL(); ShutdownGL();
@ -439,7 +453,10 @@ static void Main() {
#endif // EMSCRIPTEN #endif // EMSCRIPTEN
} }
int main(int argc, char *argv[]) extern "C"
{
void RunMain()
{ {
try { try {
Main(); Main();
@ -447,8 +464,35 @@ int main(int argc, char *argv[])
catch (const std::exception& e) { catch (const std::exception& e) {
std::cerr << "[ERROR] " << e.what() << std::endl; std::cerr << "[ERROR] " << e.what() << std::endl;
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", e.what(), nullptr); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", e.what(), nullptr);
return EXIT_FAILURE;
} }
return EXIT_SUCCESS;
} }
void SetName(const char* name)
{
s_username = name;
}
void SetUrl(const char* url)
{
s_url = url;
}
}
#ifndef EMSCRIPTEN
int main(int argc, char *argv[])
{
SetName("random guvno");
RunMain();
return 0;
}
#else
int main(int argc, char *argv[])
{
}
#endif

View File

@ -173,7 +173,7 @@ void game::OpenWorld::CreatePlayerCharacter(Player& player)
RemovePlayerCharacter(player); RemovePlayerCharacter(player);
auto& character = SpawnRandomCharacter<PlayerCharacter>(*this, player); auto& character = SpawnRandomCharacter<PlayerCharacter>(*this, player);
character.SetNametag("player (" + std::to_string(character.GetEntNum()) + ")"); // character.SetNametag("player (" + std::to_string(character.GetEntNum()) + ")");
character.SetPosition({100.0f, 100.0f, 5.0f}); character.SetPosition({100.0f, 100.0f, 5.0f});
player_characters_[&player] = &character; player_characters_[&player] = &character;

View File

@ -65,7 +65,7 @@ void game::Player::SetCamera(net::EntNum entnum)
msg.Write(entnum); msg.Write(entnum);
} }
void game::Player::SendChat(const std::string text) void game::Player::SendChat(const std::string& text)
{ {
auto msg = BeginMsg(net::MSG_CHAT); auto msg = BeginMsg(net::MSG_CHAT);
net::ChatMessage chatm = text; net::ChatMessage chatm = text;

View File

@ -28,7 +28,9 @@ public:
void SetWorld(std::shared_ptr<World> world); void SetWorld(std::shared_ptr<World> world);
void SetCamera(net::EntNum entnum); void SetCamera(net::EntNum entnum);
void SendChat(const std::string text); void SendChat(const std::string& text);
const std::string& GetName() const { return name_; }
PlayerInputFlags GetInput() const { return in_; } PlayerInputFlags GetInput() const { return in_; }
float GetViewYaw() const { return view_yaw_; } float GetViewYaw() const { return view_yaw_; }

View File

@ -5,6 +5,8 @@ game::PlayerCharacter::PlayerCharacter(World& world, OpenWorld& openworld, Playe
{ {
EnablePhysics(true); EnablePhysics(true);
VehicleChanged(); VehicleChanged();
SetNametag(player.GetName());
} }
void game::PlayerCharacter::Update() void game::PlayerCharacter::Update()

View File

@ -5,13 +5,14 @@
// #include <glm/gtx/common.hpp> // #include <glm/gtx/common.hpp>
#include "vehicleview.hpp" #include "vehicleview.hpp"
#include "utils/version.hpp"
game::view::ClientSession::ClientSession(App& app) : app_(app) game::view::ClientSession::ClientSession(App& app) : app_(app)
{ {
// send login // send login
auto msg = BeginMsg(net::MSG_ID); auto msg = BeginMsg(net::MSG_ID);
net::PlayerName name; msg.Write<net::Version>(FEKAL_VERSION);
msg.Write(name); msg.Write(net::PlayerName(app.GetUserName()));
} }
bool game::view::ClientSession::ProcessMessage(net::InMessage& msg) bool game::view::ClientSession::ProcessMessage(net::InMessage& msg)

View File

@ -55,7 +55,7 @@ enum MessageType : uint8_t
MSG_COUNT, MSG_COUNT,
}; };
using PlayerName = FixedStr<24>; using PlayerName = FixedStr<64>;
using MapName = FixedStr<32>; using MapName = FixedStr<32>;
using ModelName = FixedStr<64>; using ModelName = FixedStr<64>;
using ChatMessage = FixedStr<1024>; using ChatMessage = FixedStr<1024>;
@ -131,4 +131,8 @@ using ObjCount = ObjNum;
using NumTexels = uint16_t; using NumTexels = uint16_t;
// version
using Version = uint32_t;
} // namespace net } // namespace net

View File

@ -1,7 +1,10 @@
#include "client.hpp" #include "client.hpp"
#include <format>
#include "server.hpp" #include "server.hpp"
#include "utils/validate.hpp" #include "utils/validate.hpp"
#include "utils/version.hpp"
sv::Client::Client(Server& server, WSConnId id) : server_(server), id_(id) {} sv::Client::Client(Server& server, WSConnId id) : server_(server), id_(id) {}
@ -31,13 +34,7 @@ bool sv::Client::ProcessSingleMessage(net::MessageType type, net::InMessage& msg
if (type != net::MSG_ID) if (type != net::MSG_ID)
return false; return false;
net::PlayerName name; return ProcessLoginMsg(msg);
if (!msg.Read(name) || !utils::IsAlphanumeric(name))
return false;
player_ = std::make_unique<game::Player>(server_.GetGame(), name);
state_ = CS_PLAYER;
return true;
} }
return false; return false;
@ -58,7 +55,45 @@ void sv::Client::Update()
} }
} }
void sv::Client::SendChat(const std::string& text)
{
// not buffering till update here (unlike in Player), sent instantly
static std::vector<char> msg_buf;
msg_buf.clear();
net::OutMessage msg(msg_buf);
msg.Write(net::MSG_CHAT);
msg.Write(net::ChatMessage(text));
Send(std::string(msg_buf.data(), msg_buf.size()));
}
void sv::Client::Disconnect()
{
server_.Disconnect(*this);
}
void sv::Client::Send(std::string msg) void sv::Client::Send(std::string msg)
{ {
server_.Send(*this, std::move(msg)); server_.Send(*this, std::move(msg));
} }
bool sv::Client::ProcessLoginMsg(net::InMessage& msg)
{
net::Version ver = 0;
msg.Read(ver);
// check ver
if (ver != FEKAL_VERSION)
{
SendChat(std::format("^f55špatná verze {}, server je na verzi {}", ver, FEKAL_VERSION));
return false;
}
net::PlayerName name;
if (!msg.Read(name) || !utils::IsAlphanumeric(name))
return false;
player_ = std::make_unique<game::Player>(server_.GetGame(), name);
state_ = CS_PLAYER;
return true;
}

View File

@ -28,7 +28,9 @@ public:
void Update(); void Update();
// void Disconnect(const std::string& reason); void SendChat(const std::string& text);
void Disconnect();
WSConnId GetConnId() const { return id_; } WSConnId GetConnId() const { return id_; }
ClientState GetState() const { return state_; } ClientState GetState() const { return state_; }
@ -39,6 +41,8 @@ public:
private: private:
void Send(std::string msg); void Send(std::string msg);
bool ProcessLoginMsg(net::InMessage& msg);
private: private:
Server& server_; Server& server_;
WSConnId id_ = 0; WSConnId id_ = 0;

View File

@ -61,6 +61,11 @@ void sv::Server::Send(Client& client, std::string msg)
ws_.Send(client.GetConnId(), std::move(msg)); ws_.Send(client.GetConnId(), std::move(msg));
} }
void sv::Server::Disconnect(Client& client)
{
ws_.Close(client.GetConnId());
}
void sv::Server::PollWSEvents() void sv::Server::PollWSEvents()
{ {
WSEvent event; WSEvent event;
@ -98,9 +103,10 @@ void sv::Server::HandleWSConnect(WSConnId conn)
void sv::Server::HandleWSMessage(WSConnId conn, const std::string& data) void sv::Server::HandleWSMessage(WSConnId conn, const std::string& data)
{ {
net::InMessage msg(data.data(), data.size()); net::InMessage msg(data.data(), data.size());
if (!clients_.at(conn)->ProcessMessage(msg)) auto& client = clients_.at(conn);
if (!client->ProcessMessage(msg))
{ {
// TODO: disconnect client->Disconnect();
} }
} }

View File

@ -21,6 +21,8 @@ public:
void Send(Client& client, std::string msg); void Send(Client& client, std::string msg);
void Disconnect(Client& client);
game::Game& GetGame() { return game_; } game::Game& GetGame() { return game_; }
int64_t GetTime() const { return time_; } int64_t GetTime() const { return time_; }

View File

@ -86,17 +86,31 @@ bool sv::WSServer::PollEvent(WSEvent& out_event)
void sv::WSServer::Send(WSConnId conn_id, std::string data) void sv::WSServer::Send(WSConnId conn_id, std::string data)
{ {
std::lock_guard<std::mutex> lock(mtx_); std::lock_guard<std::mutex> lock(mtx_);
auto it = id2conn_.find(conn_id); auto it = id2conn_.find(conn_id);
if (it == id2conn_.end()) if (it == id2conn_.end())
{ {
std::cerr << "attempted to send message to unknown conn ID " << conn_id << std::endl; std::cerr << "attempted to send message to unknown conn ID " << conn_id << std::endl;
return; return;
} }
(*it->second).send_binary(std::move(data)); (*it->second).send_binary(std::move(data));
} }
void sv::WSServer::Close(WSConnId conn_id)
{
std::lock_guard<std::mutex> lock(mtx_);
auto it = id2conn_.find(conn_id);
if (it == id2conn_.end())
{
std::cerr << "attempted to close unknown conn ID " << conn_id << std::endl;
return;
}
(*it->second).close();
}
void sv::WSServer::Exit() void sv::WSServer::Exit()
{ {
std::lock_guard<std::mutex> lock(mtx_); std::lock_guard<std::mutex> lock(mtx_);

View File

@ -50,7 +50,7 @@ public:
bool PollEvent(WSEvent& out_event); bool PollEvent(WSEvent& out_event);
void Send(WSConnId conn_id, std::string data); void Send(WSConnId conn_id, std::string data);
// void Close(WSConnId conn_id); void Close(WSConnId conn_id);
void Exit(); void Exit();

3
src/utils/version.hpp Normal file
View File

@ -0,0 +1,3 @@
#pragma once
#define FEKAL_VERSION 2026031401