summaryrefslogtreecommitdiffstats
path: root/game/client/session.cc
diff options
context:
space:
mode:
authoruntodesu <kirill@untode.su>2025-03-15 16:22:09 +0500
committeruntodesu <kirill@untode.su>2025-03-15 16:22:09 +0500
commit3bf42c6ff3805a0d42bbc661794a95ff31bedc26 (patch)
tree05049955847504808d6bed2bb7b155f8b03807bb /game/client/session.cc
parent02294547dcde0d4ad76e229106702261e9f10a51 (diff)
downloadvoxelius-3bf42c6ff3805a0d42bbc661794a95ff31bedc26.tar.bz2
voxelius-3bf42c6ff3805a0d42bbc661794a95ff31bedc26.zip
Add whatever I was working on for the last month
Diffstat (limited to 'game/client/session.cc')
-rw-r--r--game/client/session.cc303
1 files changed, 303 insertions, 0 deletions
diff --git a/game/client/session.cc b/game/client/session.cc
new file mode 100644
index 0000000..33e27b5
--- /dev/null
+++ b/game/client/session.cc
@@ -0,0 +1,303 @@
+#include "client/pch.hh"
+#include "client/session.hh"
+
+#include "core/config.hh"
+#include "core/crc64.hh"
+
+#include "shared/coord.hh"
+#include "shared/dimension.hh"
+#include "shared/head.hh"
+#include "shared/player.hh"
+#include "shared/protocol.hh"
+#include "shared/transform.hh"
+#include "shared/velocity.hh"
+#include "shared/voxel_registry.hh"
+
+#include "client/camera.hh"
+#include "client/chat.hh"
+#include "client/chunk_visibility.hh"
+#include "client/game.hh"
+#include "client/globals.hh"
+#include "client/gui_screen.hh"
+#include "client/message_box.hh"
+#include "client/progress_bar.hh"
+#include "client/window_title.hh"
+
+ENetPeer *session::peer = nullptr;
+std::uint16_t session::client_index = UINT16_MAX;
+std::uint64_t session::client_identity = UINT64_MAX;
+
+static std::uint64_t server_password_hash = UINT64_MAX;
+
+static void set_fixed_tickrate(std::uint16_t tickrate)
+{
+ globals::fixed_frametime_us = 1000000U / cxpr::clamp<std::uint64_t>(tickrate, 10U, 300U);
+ globals::fixed_frametime = static_cast<float>(globals::fixed_frametime_us) / 1000000.0f;
+ globals::fixed_accumulator = 0;
+}
+
+static void on_login_response_packet(const protocol::LoginResponse &packet)
+{
+ spdlog::info("session: assigned client_index={}", packet.client_index);
+ spdlog::info("session: assigned client_identity={}", packet.client_identity);
+ spdlog::info("session: server ticks at {} TPS", packet.server_tickrate);
+
+ session::client_index = packet.client_index;
+ session::client_identity = packet.client_identity;
+
+ set_fixed_tickrate(packet.server_tickrate);
+
+ progress_bar::set_title("connecting.loading_world");
+}
+
+static void on_disconnect_packet(const protocol::Disconnect &packet)
+{
+ enet_peer_disconnect(session::peer, 0);
+
+ spdlog::info("session: disconnected: {}", packet.reason);
+
+ client_chat::clear();
+
+ session::peer = nullptr;
+ session::client_index = UINT16_MAX;
+ session::client_identity = UINT64_MAX;
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+
+ server_password_hash = UINT64_MAX;
+
+ delete globals::dimension;
+ globals::player = entt::null;
+ globals::dimension = nullptr;
+
+ chunk_visibility::cleanup();
+
+ message_box::reset();
+ message_box::set_title("disconnected.disconnected");
+ message_box::set_subtitle(packet.reason.c_str());
+ message_box::add_button("disconnected.back", [](void) {
+ globals::gui_screen = GUI_PLAY_MENU;
+ window_title::update();
+ });
+
+ globals::gui_screen = GUI_MESSAGE_BOX;
+}
+
+static void on_set_voxel_packet(const protocol::SetVoxel &packet)
+{
+ auto cpos = coord::to_chunk(packet.vpos);
+ auto lpos = coord::to_local(packet.vpos);
+ auto index = coord::to_index(lpos);
+
+ if(auto chunk = globals::dimension->find_chunk(cpos)) {
+ if(chunk->get_voxel(index) != packet.voxel) {
+ chunk->set_voxel(packet.voxel, index);
+
+ ChunkUpdateEvent event;
+ event.dimension = globals::dimension;
+ event.chunk = chunk;
+ event.cpos = cpos;
+
+ // Send a generic ChunkUpdate event to shake
+ // up the mesher; directly calling world::set_voxel
+ // here would result in a networked feedback loop
+ // caused by event handler below tripping
+ globals::dispatcher.trigger(event);
+ }
+ }
+}
+
+// NOTE: [session] is a good place for this since [receive]
+// handles entity data sent by the server and [session] handles
+// everything else network related that is not player movement
+static void on_voxel_set(const VoxelSetEvent &event)
+{
+ if(session::peer) {
+ // Propagate changes to the server
+ // FIXME: should we also validate things here or wait for the server to do so
+ protocol::SetVoxel packet;
+ packet.vpos = coord::to_voxel(event.cpos, event.lpos);
+ packet.voxel = event.voxel;
+
+ protocol::send(session::peer, protocol::encode(packet));
+ }
+}
+
+void session::init(void)
+{
+ session::peer = nullptr;
+ session::client_index = UINT16_MAX;
+ session::client_identity = UINT64_MAX;
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+
+ server_password_hash = UINT64_MAX;
+
+ globals::dispatcher.sink<protocol::LoginResponse>().connect<&on_login_response_packet>();
+ globals::dispatcher.sink<protocol::Disconnect>().connect<&on_disconnect_packet>();
+ globals::dispatcher.sink<protocol::SetVoxel>().connect<&on_set_voxel_packet>();
+
+ globals::dispatcher.sink<VoxelSetEvent>().connect<&on_voxel_set>();
+}
+
+void session::deinit(void)
+{
+ session::disconnect("protocol.client_shutdown");
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+}
+
+void session::invalidate(void)
+{
+ if(session::peer) {
+ enet_peer_reset(session::peer);
+
+ message_box::reset();
+ message_box::set_title("disconnected.disconnected");
+ message_box::set_subtitle("enet.peer_connection_timeout");
+ message_box::add_button("disconnected.back", [](void) {
+ globals::gui_screen = GUI_PLAY_MENU;
+ window_title::update();
+ });
+
+ globals::gui_screen = GUI_MESSAGE_BOX;
+ }
+
+ client_chat::clear();
+
+ session::peer = nullptr;
+ session::client_index = UINT16_MAX;
+ session::client_identity = UINT64_MAX;
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+
+ server_password_hash = UINT64_MAX;
+
+ delete globals::dimension;
+ globals::player = entt::null;
+ globals::dimension = nullptr;
+
+ chunk_visibility::cleanup();
+}
+
+void session::connect(const char *host, std::uint16_t port, const char *password)
+{
+ ENetAddress address;
+ enet_address_set_host(&address, host);
+ address.port = port;
+
+ session::peer = enet_host_connect(globals::client_host, &address, 1, 0);
+ session::client_index = UINT16_MAX;
+ session::client_identity = UINT64_MAX;
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+
+ server_password_hash = crc64::get(password);
+
+ if(!session::peer) {
+ server_password_hash = UINT64_MAX;
+
+ message_box::reset();
+ message_box::set_title("disconnected.disconnected");
+ message_box::set_subtitle("enet.peer_connection_failed");
+ message_box::add_button("disconnected.back", [](void) {
+ globals::gui_screen = GUI_PLAY_MENU;
+ window_title::update();
+ });
+
+ globals::gui_screen = GUI_MESSAGE_BOX;
+
+ return;
+ }
+
+ progress_bar::reset();
+ progress_bar::set_title("connecting.connecting");
+ progress_bar::set_button("connecting.cancel_button", [](void) {
+ enet_peer_disconnect(session::peer, 0);
+
+ session::peer = nullptr;
+ session::client_index = UINT16_MAX;
+ session::client_identity = UINT64_MAX;
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+
+ server_password_hash = UINT64_MAX;
+
+ delete globals::dimension;
+ globals::player = entt::null;
+ globals::dimension = nullptr;
+
+ chunk_visibility::cleanup();
+
+ globals::gui_screen = GUI_PLAY_MENU;
+ });
+
+ globals::gui_screen = GUI_PROGRESS;
+}
+
+void session::disconnect(const char *reason)
+{
+ if(session::peer) {
+ protocol::Disconnect packet;
+ packet.reason = std::string(reason);
+
+ protocol::send(session::peer, protocol::encode(packet));
+
+ enet_host_flush(globals::client_host);
+ enet_host_service(globals::client_host, nullptr, 50);
+ enet_peer_reset(session::peer);
+
+ session::peer = nullptr;
+ session::client_index = UINT16_MAX;
+ session::client_identity = UINT64_MAX;
+
+ globals::fixed_frametime_us = UINT64_MAX;
+ globals::fixed_frametime = 0.0f;
+ globals::fixed_accumulator = 0;
+
+ server_password_hash = UINT64_MAX;
+
+ delete globals::dimension;
+ globals::player = entt::null;
+ globals::dimension = nullptr;
+
+ chunk_visibility::cleanup();
+
+ client_chat::clear();
+ }
+}
+
+void session::send_login_request(void)
+{
+ protocol::LoginRequest packet;
+ packet.version = protocol::VERSION;
+ packet.voxel_def_checksum = voxel_registry::checksum();
+ packet.password_hash = server_password_hash;
+ packet.username = client_game::username.get();
+
+ protocol::send(session::peer, protocol::encode(packet));
+
+ server_password_hash = UINT64_MAX;
+
+ progress_bar::set_title("connecting.logging_in");
+ globals::gui_screen = GUI_PROGRESS;
+}
+
+bool session::is_ingame(void)
+{
+ if(globals::dimension)
+ return globals::dimension->entities.valid(globals::player);
+ return false;
+}