From 882b77d3f0c1339394dcd0120c2dbaacac6ebf2a Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 19 Feb 2023 13:09:59 +0100 Subject: [PATCH] Lobby: show own worlds --- assets/textures/pack_owner.png | Bin 723 -> 576 bytes src/client/client.h | 4 -- src/client/client_packethandler.cpp | 2 +- src/core/world.h | 9 ++++ src/gui/gameplay.cpp | 8 +++- src/gui/lobby.cpp | 61 +++++++++++++++++++----- src/gui/lobby.h | 6 ++- src/server/database_world.cpp | 71 +++++++++++++++++++--------- src/server/database_world.h | 10 +++- src/server/server_packethandler.cpp | 20 +++++--- 10 files changed, 140 insertions(+), 51 deletions(-) diff --git a/assets/textures/pack_owner.png b/assets/textures/pack_owner.png index a2317b1a3624cab761f96d5c76a5e69c57ffb90b..1136dfd99768171e566751d2af07a8ceb4ebe3b4 100644 GIT binary patch delta 320 zcmV-G0l)s!1;7N5Bn$RXOjJbx000000Fg1!kw68L0RbC-#dNH=00001bW%=J06^y0 zW&i*H_(?=TR7l6|mg^3JFbsux4@DGIblw#ZbO;EW7eL(mzl{FW9On;s zCtz>}G*AG4JrNuLvh5d89N2-OOdLrztr}7dTAn?dHceATD{^bjL;BSD&vsFVFI^d< zi-yDFqR+_f76@SMDyQe}7!`L6>VVVIRbGj5PzRNWd83Q8k;kG6Xp!rbv(O;AT2^&p zgpAWVbQfusSO~!x7yD>w7;W0F>ZCj96202~;!po07Xit=(J@9IeiM-C`o4CqOIIYH S6Uzkv0000o9V9Edh07!IFPE!B`2M!Vw9Udbk zD=suVJx^R_XKHI~admflgp81un3#s{d09UDS@g8o; ztC_t2R|y6v=z1afO81_If0ST=;>47bb@^Iyn_}6V6yX3tCq`ZP?k*(M4WbKh0BX9P zHK?u$Bvc2M=Qa`GZ_0KI;yFW2RT_$_4IPgd9Tv0?aJe;(UZRM2Bw9Vktuh90v^spZ zt!lk~PgC37JgwUl8E^jtk1eufNUkyJJ29T2y&IWihib00000NkvXX Hu0mjfq}|yW diff --git a/src/client/client.h b/src/client/client.h index 30cef9c..632a233 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -24,10 +24,6 @@ struct ClientStartData { std::string nickname; }; -struct LobbyWorld : public WorldMeta { - blockpos_t size; -}; - // Abstract for inheritance class Client : public Environment, public GameEventHandler { public: diff --git a/src/client/client_packethandler.cpp b/src/client/client_packethandler.cpp index 0244375..9cc1bf4 100644 --- a/src/client/client_packethandler.cpp +++ b/src/client/client_packethandler.cpp @@ -57,7 +57,7 @@ void Client::pkt_Lobby(Packet &pkt) std::string world_id(pkt.readStr16()); - LobbyWorld world; + LobbyWorld world(world_id); world.readCommon(pkt); world.size.X = pkt.read(); world.size.Y = pkt.read(); diff --git a/src/core/world.h b/src/core/world.h index 28dcb50..afb6f46 100644 --- a/src/core/world.h +++ b/src/core/world.h @@ -38,6 +38,9 @@ struct BlockUpdateHash { }; struct WorldMeta { + WorldMeta(const std::string &id) : + id(id) {} + // For networking void readCommon(Packet &pkt); void writeCommon(Packet &pkt) const; @@ -72,6 +75,12 @@ struct WorldMeta { } keys[3] = {}; }; +struct LobbyWorld : public WorldMeta { + LobbyWorld(const std::string &id) : + WorldMeta(id) {} + + blockpos_t size; +}; class World : public IReferenceCounted { public: diff --git a/src/gui/gameplay.cpp b/src/gui/gameplay.cpp index fc1ce56..c2c8dde 100644 --- a/src/gui/gameplay.cpp +++ b/src/gui/gameplay.cpp @@ -218,8 +218,11 @@ void SceneGameplay::step(float dtime) bool SceneGameplay::OnEvent(const SEvent &e) { - if (m_blockselector->OnEvent(e)) - return true; + auto element = m_gui->guienv->getFocus(); + if (!element || element->getID() != ID_BoxChat) { + if (m_blockselector->OnEvent(e)) + return true; + } if (e.EventType == EET_GUI_EVENT) { switch (e.GUIEvent.EventType) { @@ -263,6 +266,7 @@ bool SceneGameplay::OnEvent(const SEvent &e) m_ignore_keys = true; break; case gui::EGET_ELEMENT_FOCUS_LOST: + // !! This is not triggered when dragging & releasing the mouse m_ignore_keys = false; break; default: break; diff --git a/src/gui/lobby.cpp b/src/gui/lobby.cpp index 4b21f56..968684a 100644 --- a/src/gui/lobby.cpp +++ b/src/gui/lobby.cpp @@ -1,5 +1,6 @@ #include "lobby.h" #include "client/client.h" +#include "client/localplayer.h" #include #include #include @@ -9,7 +10,8 @@ enum ElementId : int { ID_LabelLobby = 100, ID_BtnRefresh, - ID_ListWorlds, + ID_ListPublic, + ID_ListMine, ID_BoxWorldID, ID_BtnJoin, }; @@ -33,7 +35,7 @@ void SceneLobby::draw() { auto rect = m_gui->getRect({10, 15}, {80, 30}); - m_worldlist = gui->addListBox(rect, nullptr, ID_ListWorlds, true); + m_publiclist = gui->addListBox(rect, nullptr, ID_ListPublic, true); core::recti rect_lab( rect.UpperLeftCorner + core::vector2di(0, -25), @@ -49,9 +51,21 @@ void SceneLobby::draw() m_refreshbtn = gui->addButton(rect_btn, nullptr, ID_BtnRefresh, L"Refresh"); } + { + auto rect = m_gui->getRect({10, 51}, {80, 25}); + m_mylist = gui->addListBox(rect, nullptr, ID_ListMine, true); + + core::recti rect_lab( + rect.UpperLeftCorner + core::vector2di(0, -25), + core::dimension2di(100, 25) + ); + auto e = gui->addStaticText(L"My worlds", rect_lab); + e->setOverrideColor(Gui::COLOR_ON_BG); + } + { // Custom world ID box - auto rect = m_gui->getRect({10, 75}, {20, -30}); + auto rect = m_gui->getRect({10, 83}, {20, -30}); gui->addEditBox(L"", rect, true, nullptr, ID_BoxWorldID); core::recti rect_lab( @@ -61,7 +75,7 @@ void SceneLobby::draw() auto e = gui->addStaticText(L"Custom world ID", rect_lab); e->setOverrideColor(Gui::COLOR_ON_BG); - auto rect_btn = m_gui->getRect({32, 75}, {-100, -30}); + auto rect_btn = m_gui->getRect({32, 83}, {-100, -30}); gui->addButton(rect_btn, nullptr, ID_BtnJoin, L"Join"); } @@ -97,11 +111,22 @@ bool SceneLobby::OnEvent(const SEvent &e) } break; case gui::EGET_LISTBOX_SELECTED_AGAIN: - if (e.GUIEvent.Caller->getID() == ID_ListWorlds) { + if (e.GUIEvent.Caller->getID() == ID_ListPublic) { gui::IGUIListBox *list = (gui::IGUIListBox *)e.GUIEvent.Caller; try { - world_id = m_index_to_worldid.at(list->getSelected()); + world_id = m_public_index_to_worldid.at(list->getSelected()); + } catch (std::exception &) { + break; + } + + m_gui->joinWorld(this); + } + if (e.GUIEvent.Caller->getID() == ID_ListMine) { + gui::IGUIListBox *list = (gui::IGUIListBox *)e.GUIEvent.Caller; + + try { + world_id = m_my_index_to_worldid.at(list->getSelected()); } catch (std::exception &) { break; } @@ -134,26 +159,38 @@ void SceneLobby::updateWorldList() return; m_dirty_worldlist = false; - m_worldlist->clear(); - m_index_to_worldid.clear(); + m_publiclist->clear(); + m_mylist->clear(); + m_public_index_to_worldid.clear(); + m_my_index_to_worldid.clear(); + auto player = m_gui->getClient()->getMyPlayer(); auto worlds = m_gui->getClient()->world_list; for (const auto &it : worlds) { + bool is_mine = player->name == it.second.owner; auto size = it.second.size; std::ostringstream os; os << "[" << it.second.online << " online]"; os << " id=" << it.first; os << " ( " << size.X << "x" << size.Y << " )"; - os << " by " << it.second.owner; + if (is_mine) + os << (it.second.is_public ? " - public" : " - private"); + else + os << " by " << it.second.owner; core::stringw textw; core::multibyteToWString(textw, os.str().c_str()); - auto i = m_worldlist->addItem(textw.c_str()); - m_worldlist->setItemOverrideColor(i, 0xFFFFFFFF); + auto dst = is_mine ? m_mylist : m_publiclist; + + auto i = dst->addItem(textw.c_str()); + dst->setItemOverrideColor(i, 0xFFFFFFFF); - m_index_to_worldid.push_back(it.first); + if (is_mine) + m_my_index_to_worldid.push_back(it.first); + else + m_public_index_to_worldid.push_back(it.first); } m_refreshbtn->setEnabled(true); diff --git a/src/gui/lobby.h b/src/gui/lobby.h index 6287446..db99301 100644 --- a/src/gui/lobby.h +++ b/src/gui/lobby.h @@ -25,7 +25,9 @@ class SceneLobby : public SceneHandler { private: void updateWorldList(); bool m_dirty_worldlist = false; - std::vector m_index_to_worldid; - gui::IGUIListBox *m_worldlist = nullptr; + std::vector m_public_index_to_worldid, + m_my_index_to_worldid; + gui::IGUIListBox *m_publiclist = nullptr; + gui::IGUIListBox *m_mylist = nullptr; gui::IGUIButton *m_refreshbtn = nullptr; }; diff --git a/src/server/database_world.cpp b/src/server/database_world.cpp index edcb9bd..525aae5 100644 --- a/src/server/database_world.cpp +++ b/src/server/database_world.cpp @@ -1,6 +1,5 @@ #include "database_world.h" #include "core/packet.h" -#include "core/world.h" #include DatabaseWorld::~DatabaseWorld() @@ -24,6 +23,7 @@ bool DatabaseWorld::tryOpen(const char *filepath) "`width` INTEGER," "`height` INTEGER," "`owner` TEXT," + "`title` TEXT," "`plays` INTEGER," "`visibility` INTEGER," "`player_flags` BLOB," @@ -43,9 +43,13 @@ bool DatabaseWorld::tryOpen(const char *filepath) -1, &m_stmt_read, nullptr)); good &= ok("write", sqlite3_prepare_v2(m_database, "REPLACE INTO `worlds` " - "(`id`, `width`, `height`, `owner`, `plays`, `visibility`, `player_flags`, `data`) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + "(`id`, `width`, `height`, `owner`, `title`, `plays`, `visibility`, `player_flags`, `data`) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", -1, &m_stmt_write, nullptr)); + good &= ok("by_player", sqlite3_prepare_v2(m_database, + "SELECT `id`, `width`, `height`, `title`, `plays`, `visibility` " + "FROM `worlds` WHERE `owner` = ?", + -1, &m_stmt_by_player, nullptr)); return good; } @@ -61,6 +65,7 @@ void DatabaseWorld::close() ok("~end", sqlite3_finalize(m_stmt_end)); ok("~read", sqlite3_finalize(m_stmt_read)); ok("~write", sqlite3_finalize(m_stmt_write)); + ok("~by_player", sqlite3_finalize(m_stmt_by_player)); ok("close", sqlite3_close_v2(m_database)); m_database = nullptr; @@ -72,14 +77,6 @@ static int custom_bind_string(sqlite3_stmt *s, int col, const std::string &text) return sqlite3_bind_text(s, col, text.c_str(), text.size(), nullptr); } -static uint32_t stupid_worldid_hash(const std::string &id) -{ - uint32_t v = 0; - for (char c : id) - v ^= (v << 3) + (uint8_t)c; - return v; -} - bool DatabaseWorld::load(World *world) { if (!m_database) @@ -107,21 +104,22 @@ bool DatabaseWorld::load(World *world) world->createDummy(size); meta.owner = (const char *)sqlite3_column_text(s, 3); - meta.plays = sqlite3_column_int(s, 4); - meta.is_public = sqlite3_column_int(s, 5) == 1; + meta.title = (const char *)sqlite3_column_text(s, 4); + meta.plays = sqlite3_column_int(s, 5); + meta.is_public = sqlite3_column_int(s, 6) == 1; { // Player flags - const void *blob = sqlite3_column_blob(s, 6); - const size_t len = sqlite3_column_bytes(s, 6); + const void *blob = sqlite3_column_blob(s, 7); + const size_t len = sqlite3_column_bytes(s, 7); Packet pkt((const char *)blob, len); meta.readPlayerFlags(pkt); } { // World data - const void *blob = sqlite3_column_blob(s, 7); - const size_t len = sqlite3_column_bytes(s, 7); + const void *blob = sqlite3_column_blob(s, 8); + const size_t len = sqlite3_column_bytes(s, 8); Packet pkt((const char *)blob, len); world->read(pkt); } @@ -151,18 +149,19 @@ bool DatabaseWorld::save(const World *world) sqlite3_bind_int(s, 2, world->getSize().X); sqlite3_bind_int(s, 3, world->getSize().Y); custom_bind_string(s, 4, meta.owner); - sqlite3_bind_int(s, 5, meta.plays); - sqlite3_bind_int(s, 6, meta.is_public ? 1 : 0); + custom_bind_string(s, 5, meta.title); + sqlite3_bind_int(s, 6, meta.plays); + sqlite3_bind_int(s, 7, meta.is_public ? 1 : 0); // IMPORTANT: slite3_bind_*(...) does NOT copy the data. // The packets must be alive until sqlite3_step(...) Packet p_flags; meta.writePlayerFlags(p_flags); - sqlite3_bind_blob(s, 7, p_flags.data(), p_flags.size(), nullptr); + sqlite3_bind_blob(s, 8, p_flags.data(), p_flags.size(), nullptr); Packet p_world; world->write(p_world, World::Method::Plain); - sqlite3_bind_blob(s, 8, p_world.data(), p_world.size(), nullptr); + sqlite3_bind_blob(s, 9, p_world.data(), p_world.size(), nullptr); bool good = ok("save_s", sqlite3_step(s)); ok("save_r", sqlite3_reset(s)); @@ -185,8 +184,36 @@ bool DatabaseWorld::runCustomQuery(const char *query) return good; } +std::vector DatabaseWorld::getByPlayer(const std::string &name) const +{ + std::vector out; + if (!m_database) + return out; + + auto s = m_stmt_by_player; + custom_bind_string(s, 1, name); + + while (sqlite3_step(s) == SQLITE_ROW) { + std::string world_id = (const char *)sqlite3_column_text(s, 0); + + LobbyWorld meta(world_id); + meta.size.X = sqlite3_column_int(s, 1); + meta.size.Y = sqlite3_column_int(s, 2); + meta.title = sqlite3_column_int(s, 3); + meta.owner = name; + meta.plays = sqlite3_column_int(s, 4); + meta.is_public = sqlite3_column_int(s, 5) == 1; + + out.emplace_back(meta); + } + + ok("byPlayer", sqlite3_errcode(m_database)); + sqlite3_reset(s); + return out; +} + -bool DatabaseWorld::ok(const char *where, int status) +bool DatabaseWorld::ok(const char *where, int status) const { if (status == SQLITE_OK || status == SQLITE_DONE) return true; diff --git a/src/server/database_world.h b/src/server/database_world.h index 81107b7..965b291 100644 --- a/src/server/database_world.h +++ b/src/server/database_world.h @@ -1,6 +1,8 @@ #pragma once -class World; +#include "core/world.h" // WorldMeta +#include + struct sqlite3; struct sqlite3_stmt; @@ -16,8 +18,10 @@ class DatabaseWorld { bool runCustomQuery(const char *query); + std::vector getByPlayer(const std::string &name) const; + private: - bool ok(const char *where, int status); + bool ok(const char *where, int status) const; sqlite3 *m_database = nullptr; @@ -26,4 +30,6 @@ class DatabaseWorld { sqlite3_stmt *m_stmt_read = nullptr; sqlite3_stmt *m_stmt_write = nullptr; + + sqlite3_stmt *m_stmt_by_player = nullptr; }; diff --git a/src/server/server_packethandler.cpp b/src/server/server_packethandler.cpp index e13539e..ec7e0e8 100644 --- a/src/server/server_packethandler.cpp +++ b/src/server/server_packethandler.cpp @@ -94,12 +94,6 @@ void Server::pkt_GetLobby(peer_t peer_id, Packet &) worlds.insert(world); } - - World demo("dummytest"); - demo.getMeta().owner = "foo mc bar"; - demo.createEmpty(blockpos_t(30, 20)); - worlds.insert(&demo); - for (auto world : worlds) { const auto &meta = world->getMeta(); if (!meta.is_public) @@ -115,6 +109,20 @@ void Server::pkt_GetLobby(peer_t peer_id, Packet &) out.write(size.Y); } + if (m_world_db) { + auto player = getPlayerNoLock(peer_id); + auto found = m_world_db->getByPlayer(player->name); + for (const auto &meta : found) { + out.write(true); // continue! + + out.writeStr16(meta.id); // world ID + meta.writeCommon(out); + // Additional Lobby fields + out.write(meta.size.X); + out.write(meta.size.Y); + } + } + out.write(false); // terminate m_con->send(peer_id, 0, out);