From b4dd5d3bd7d2152fdf02e0e7422b1305caf151f2 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Sun, 22 Jan 2012 00:49:02 +0100 Subject: Client-side prediction of inventory changes, and some inventory menu fixes --- src/client.cpp | 40 +++++++++++++++ src/client.h | 2 + src/guiInventoryMenu.cpp | 30 ++++++++--- src/inventory.cpp | 43 ++++++++++++++++ src/inventory.h | 4 ++ src/inventorymanager.cpp | 129 ++++++++++++++++++++++++++++------------------- src/inventorymanager.h | 7 +++ src/server.cpp | 12 +++++ 8 files changed, 210 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/src/client.cpp b/src/client.cpp index 602f0cf84..0463aa81c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -225,6 +225,8 @@ Client::Client( m_server_ser_ver(SER_FMT_VER_INVALID), m_playeritem(0), m_inventory_updated(false), + m_inventory_from_server(NULL), + m_inventory_from_server_age(0.0), m_time_of_day(0), m_map_seed(0), m_password(password), @@ -269,6 +271,8 @@ Client::~Client() m_mesh_update_thread.setRun(false); while(m_mesh_update_thread.IsRunning()) sleep_ms(100); + + delete m_inventory_from_server; } void Client::connect(Address address) @@ -657,6 +661,30 @@ void Client::step(float dtime) } } } + + /* + If the server didn't update the inventory in a while, revert + the local inventory (so the player notices the lag problem + and knows something is wrong). + */ + if(m_inventory_from_server) + { + float interval = 10.0; + float count_before = floor(m_inventory_from_server_age / interval); + + m_inventory_from_server_age += dtime; + + float count_after = floor(m_inventory_from_server_age / interval); + + if(count_after != count_before) + { + // Do this every seconds after TOCLIENT_INVENTORY + // Reset the locally changed inventory to the authoritative inventory + Player *player = m_env.getLocalPlayer(); + player->inventory = *m_inventory_from_server; + m_inventory_updated = true; + } + } } // Virtual methods from con::PeerHandler @@ -975,6 +1003,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_inventory_updated = true; + delete m_inventory_from_server; + m_inventory_from_server = new Inventory(player->inventory); + m_inventory_from_server_age = 0.0; + //infostream<<"Client got player inventory:"<inventory.print(infostream); } @@ -1931,7 +1963,15 @@ Inventory* Client::getInventory(const InventoryLocation &loc) } void Client::inventoryAction(InventoryAction *a) { + /* + Send it to the server + */ sendInventoryAction(a); + + /* + Predict some local inventory changes + */ + a->clientApply(this, this); } ClientActiveObject * Client::getSelectedActiveObject( diff --git a/src/client.h b/src/client.h index 6d5e5c525..efdf315f7 100644 --- a/src/client.h +++ b/src/client.h @@ -367,6 +367,8 @@ private: u8 m_server_ser_ver; u16 m_playeritem; bool m_inventory_updated; + Inventory *m_inventory_from_server; + float m_inventory_from_server_age; core::map m_active_blocks; PacketCounter m_packetcounter; // Received from the server. 0-23999 diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index a4b16b251..cd371d062 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -537,6 +537,11 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) s.i = -1; // make it invalid again } + bool identical = (m_selected_item != NULL) && s.isValid() && + (inv_selected == inv_s) && + (m_selected_item->listname == s.listname) && + (m_selected_item->i == s.i); + // buttons: 0 = left, 1 = right, 2 = middle // up/down: 0 = down (press), 1 = up (release), 2 = unknown event int button = 0; @@ -602,13 +607,26 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(s.isValid()) { - // Clicked another slot: move + // Clicked a slot: move if(button == 1) // right move_amount = 1; else if(button == 2) // middle move_amount = MYMIN(m_selected_amount, 10); else // left move_amount = m_selected_amount; + dstream << "move_amount=" << move_amount<<"\n"; + dstream << "m_selected_amount=" << m_selected_amount<<"\n"; + + if(identical) + { + if(move_amount >= m_selected_amount) + m_selected_amount = 0; + else + m_selected_amount -= move_amount; + move_amount = 0; + } + dstream << "move_amount=" << move_amount<<"\n"; + dstream << "m_selected_amount=" << m_selected_amount<<"\n"; } else if(getAbsoluteClippingRect().isPointInside(m_pointer)) { @@ -636,9 +654,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(m_selected_item != NULL && m_selected_dragging && s.isValid()) { - if((inv_selected != inv_s) || - (m_selected_item->listname != s.listname) || - (m_selected_item->i != s.i)) + if(!identical) { // Dragged to different slot: move all selected move_amount = m_selected_amount; @@ -675,18 +691,19 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(leftover.count == stack_from.count) { // Swap the stacks + m_selected_amount -= stack_to.count; } else if(leftover.empty()) { // Item fits + m_selected_amount -= move_amount; } else { // Item only fits partially move_amount -= leftover.count; + m_selected_amount -= move_amount; } - assert(move_amount > 0 && move_amount <= m_selected_amount); - m_selected_amount -= move_amount; infostream<<"Handing IACTION_MOVE to manager"<addItem(dest_i, item1); + + // If something is returned, the item was not fully added + if(!item1.empty()) + { + // If olditem is returned, nothing was added. + bool nothing_added = (item1.count == oldcount); + + // If something else is returned, part of the item was left unadded. + // Add the other part back to the source item + addItem(i, item1); + + // If olditem is returned, nothing was added. + // Swap the items + if(nothing_added) + { + // Take item from source list + item1 = changeItem(i, ItemStack()); + // Adding was not possible, swap the items. + ItemStack item2 = dest->changeItem(dest_i, item1); + // Put item from destination list to the source list + changeItem(i, item2); + } + } +} + /* Inventory */ diff --git a/src/inventory.h b/src/inventory.h index 3f5d83589..fcac5f647 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -233,6 +233,10 @@ public: // Similar to takeItem, but keeps the slot intact. ItemStack peekItem(u32 i, u32 peekcount) const; + // Move an item to a different list (or a different stack in the same list) + // count is the maximum number of items to move (0 for everything) + void moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count = 0); + private: std::vector m_items; u32 m_size; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index b42a80673..b04a1c177 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -133,6 +133,10 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is) return a; } +/* + IMoveAction +*/ + IMoveAction::IMoveAction(std::istream &is) { std::string ts; @@ -193,59 +197,13 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<", to_list=\""<getItem(from_i).empty()) - { - infostream<<"IMoveAction::apply(): FAIL: source item not found: " - <<"from_inv=\""<