diff options
author | Stijn Buys <ingar@osirion.org> | 2010-09-22 21:32:34 +0000 |
---|---|---|
committer | Stijn Buys <ingar@osirion.org> | 2010-09-22 21:32:34 +0000 |
commit | a6f9773c358dd7d091ff64cbda504ab8d8066dd3 (patch) | |
tree | 226e23c4656957e908623ccda9d3d1c50240a0b4 /src | |
parent | bbb43d1c15f2858573f5abb595aa62f8224e4d76 (diff) |
full trading support for networked games
Diffstat (limited to 'src')
-rw-r--r-- | src/client/inventorylistview.cc | 81 | ||||
-rw-r--r-- | src/client/inventorylistview.h | 3 | ||||
-rw-r--r-- | src/client/map.cc | 2 | ||||
-rw-r--r-- | src/client/playerview.cc | 2 | ||||
-rw-r--r-- | src/client/trademenu.cc | 12 | ||||
-rw-r--r-- | src/client/video.cc | 4 | ||||
-rw-r--r-- | src/core/entity.cc | 36 | ||||
-rw-r--r-- | src/core/gameconnection.cc | 53 | ||||
-rw-r--r-- | src/core/gameconnection.h | 13 | ||||
-rw-r--r-- | src/core/gameinterface.h | 13 | ||||
-rw-r--r-- | src/core/gameserver.cc | 10 | ||||
-rw-r--r-- | src/core/gameserver.h | 10 | ||||
-rw-r--r-- | src/core/info.cc | 26 | ||||
-rw-r--r-- | src/core/info.h | 13 | ||||
-rw-r--r-- | src/core/inventory.cc | 20 | ||||
-rw-r--r-- | src/core/inventory.h | 24 | ||||
-rw-r--r-- | src/core/item.cc | 25 | ||||
-rw-r--r-- | src/core/item.h | 24 | ||||
-rw-r--r-- | src/core/net.h | 7 | ||||
-rw-r--r-- | src/core/netclient.h | 6 | ||||
-rw-r--r-- | src/core/netconnection.cc | 92 | ||||
-rw-r--r-- | src/core/netconnection.h | 7 | ||||
-rw-r--r-- | src/core/netplayer.cc | 1 | ||||
-rw-r--r-- | src/core/netserver.cc | 97 | ||||
-rw-r--r-- | src/core/netserver.h | 12 | ||||
-rw-r--r-- | src/ui/listview.cc | 4 |
26 files changed, 457 insertions, 140 deletions
diff --git a/src/client/inventorylistview.cc b/src/client/inventorylistview.cc index d87ed2b..e2b613b 100644 --- a/src/client/inventorylistview.cc +++ b/src/client/inventorylistview.cc @@ -4,14 +4,20 @@ the terms and conditions of the GNU General Public License version 2 */ +#include "core/application.h" #include "client/inventorylistview.h" #include "ui/listitem.h" +#include "ui/paint.h" namespace client { InventoryListView::InventoryListView(ui::Widget *parent) : ui::ListView (parent) { + listview_inventory = 0; + listview_infotype = 0; listview_timestamp = 0; + listview_infotimestamp = 0; + set_inventory(0, 0); } @@ -22,55 +28,82 @@ void InventoryListView::set_inventory(core::Inventory *inventory, core::InfoType { remove_children(); + deselect(); + + listview_timestamp = 0; + listview_infotimestamp = 0; listview_inventory = inventory; listview_infotype = infotype; - - const core::Item *selecteditem = (selected() ? selected()->item() : 0); - - if (!inventory || !infotype) { + + if (!listview_inventory || !listview_infotype) { return; } - // TODO scan the inventories and request updated infos - // update when necessary + const core::Item *selecteditem = (selected() ? selected()->item() : 0); + listview_timestamp = inventory->timestamp(); for (core::Inventory::Items::const_iterator it = inventory->items().begin(); it != inventory->items().end(); it++) { core::Item *item = (*it); - if (item->info() && (item->info()->type() == infotype) && (item->amount() != 0) ) { - ui::ListItem *listitem = 0; + if (item->info()) { - std::ostringstream str; - str << item->info()->name().c_str(); + // this makes sure inventory info gets requested from the server + core::game()->request_info(item->info()->id()); - if (item->amount() > 0) { - str << " (" << item->amount() << ")"; + if (item->info()->timestamp() > listview_infotimestamp) { + listview_infotimestamp = item->info()->timestamp(); } - listitem = new ui::ListItem(this, str.str().c_str()); - listitem->set_height(listitem->font()->height() * 2.0f); - listitem->set_item(item); - listitem->set_info(item->info()); - // preserve previous selection during update - if (item == selecteditem) { - ui::ListView::select(listitem); + + if ((item->info()->type() == infotype) && (item->amount() != 0)) { + ui::ListItem *listitem = 0; + + std::ostringstream str; + str << item->info()->name().c_str(); + + if (item->amount() > 0) { + str << " (" << item->amount() << ")"; + } + listitem = new ui::ListItem(this, str.str().c_str()); + listitem->set_height(listitem->font()->height() * 2.0f); + listitem->set_item(item); + listitem->set_info(item->info()); + // preserve previous selection during update + if (item == selecteditem) { + ui::ListView::select(listitem); + } } } } - listview_timestamp = inventory->timestamp(); - event_resize(); emit(EventListViewChanged); } +bool InventoryListView::verify_inventory() +{ + for (core::Inventory::Items::const_iterator it = listview_inventory->items().begin(); it != listview_inventory->items().end(); it++) { + core::Item *item = (*it); + if (item->info() && (item->info()->timestamp() > listview_infotimestamp)) { + return false; + } + } + return true; +} + void InventoryListView::draw() { - if (listview_inventory && (listview_timestamp != listview_inventory->timestamp())) { - set_inventory(listview_inventory, listview_infotype); + if (listview_inventory && listview_infotype) { + // inventory was updated + if (listview_timestamp != listview_inventory->timestamp()) { + set_inventory(listview_inventory, listview_infotype); + // inventory info was updated + } else if (!verify_inventory()) { + set_inventory(listview_inventory, listview_infotype); + } } ListView::draw(); } -} // namespace client
\ No newline at end of file +} // namespace client diff --git a/src/client/inventorylistview.h b/src/client/inventorylistview.h index ff10aa1..5d5a85f 100644 --- a/src/client/inventorylistview.h +++ b/src/client/inventorylistview.h @@ -30,7 +30,10 @@ protected: virtual void draw(); private: + bool verify_inventory(); + unsigned long listview_timestamp; + unsigned long listview_infotimestamp; core::Inventory *listview_inventory; core::InfoType *listview_infotype; }; diff --git a/src/client/map.cc b/src/client/map.cc index 46f96fd..54afc3f 100644 --- a/src/client/map.cc +++ b/src/client/map.cc @@ -266,7 +266,7 @@ void Map::set_target(const core::Entity *entity) { map_targetlabel->show(); if (map_target->info()) - map_inforecord = core::game()->info(map_target->info()->id()); + map_inforecord = core::game()->request_info(map_target->info()->id()); else map_inforecord = 0; diff --git a/src/client/playerview.cc b/src/client/playerview.cc index b5629f0..7791a2a 100644 --- a/src/client/playerview.cc +++ b/src/client/playerview.cc @@ -129,7 +129,7 @@ void PlayerView::show_menu(const std::string & args) view_trademenu->hide(); // requesting the info through game makes sure it is retreived from the server - view_buymenu->set_item( core::game()->info(id) ); + view_buymenu->set_item( core::game()->request_info(id)); // show buy menu view_buymenu->show(); } else { diff --git a/src/client/trademenu.cc b/src/client/trademenu.cc index 0b8fb64..91724f7 100644 --- a/src/client/trademenu.cc +++ b/src/client/trademenu.cc @@ -80,6 +80,7 @@ TradeMenu::TradeMenu(ui::Widget *parent, const char * label) : ui::Window(parent menu_listitem = 0; set_itemtype(0); + set_item(0); hide(); } @@ -97,17 +98,16 @@ void TradeMenu::set_itemtype(core::InfoType *item_type) core::Inventory *inventory_player = 0; core::Inventory *inventory_view = 0; - if (core::localcontrol()) - inventory_player = core::localcontrol()->inventory(); + inventory_player = core::game()->request_inventory(core::localcontrol()); if(core::localplayer()->view()) - inventory_view = core::localplayer()->view()->inventory(); + inventory_view = core::game()->request_inventory(core::localplayer()->view()); menu_inventorylistview->set_inventory(inventory_player, item_type); menu_traderlistview->set_inventory(inventory_view, item_type); - set_item(0); + //set_item(0); } void TradeMenu::set_item(ui::ListItem *item) @@ -302,10 +302,12 @@ bool TradeMenu::on_emit(Widget *sender, const Event event, void *data) set_item(menu_inventorylistview->selected()); } return true; + } else if (event == ui::Widget::EventListViewChanged) { // listview changed if (sender == menu_traderlistview) { set_item(menu_traderlistview->selected()); + } else if (sender == menu_inventorylistview) { set_item(menu_inventorylistview->selected()); } @@ -318,7 +320,7 @@ bool TradeMenu::on_emit(Widget *sender, const Event event, void *data) void TradeMenu::draw() { // sanity check - if ((menu_inventorylistview->inventory() != (core::localcontrol() ? core::localcontrol()->inventory() : 0 )) || + if ((menu_inventorylistview->inventory() != (core::localcontrol() ? core::localcontrol()->inventory() : 0 )) || (menu_traderlistview->inventory() != (core::localplayer()->view() ? core::localplayer()->view()->inventory() : 0 ))) { set_itemtype(menu_itemtype); } diff --git a/src/client/video.cc b/src/client/video.cc index 6eee36b..aa7ca9e 100644 --- a/src/client/video.cc +++ b/src/client/video.cc @@ -4,14 +4,14 @@ the terms and conditions of the GNU General Public License version 2 */ +#include "core/core.h" +#include "core/gameserver.h" #include "client/video.h" #include "client/input.h" #include "client/client.h" #include "client/targets.h" #include "render/render.h" #include "render/gl.h" -#include "core/core.h" -#include "core/gameserver.h" #include "filesystem/filesystem.h" #include "sys/sys.h" #include "ui/ui.h" diff --git a/src/core/entity.cc b/src/core/entity.cc index 0b3a402..1494438 100644 --- a/src/core/entity.cc +++ b/src/core/entity.cc @@ -183,6 +183,8 @@ Entity::Entity(std::istream & is) entity_inventory = 0; entity_info = 0; + entity_inventory = 0; + memset(entity_extension, 0, sizeof(entity_extension)); } @@ -219,6 +221,12 @@ void Entity::clear_updates() entity_created = false; entity_dirty = false; entity_oldzone = 0; + + if (entity_inventory && entity_inventory->dirty()) { + // inventory timestamp must be set for singleplayer + entity_inventory->set_timestamp(game()->timestamp()); + entity_inventory->set_dirty(false); + } } void Entity::set_info(Info *info) @@ -299,7 +307,11 @@ void Entity::serialize_server_create(std::ostream & os) const << "\"" <<label() << "\" " << "\"" << name() << "\" " << "\"" << (entity_model ? entity_model->name() : "") << "\" " - << (info() ? info()->id() : 0) << " "; + << (info() ? info()->id() : 0) << " " + << (inventory() ? 1 : 0) << " "; + + if (inventory()) + inventory()->serialize_server_update(os); } void Entity::receive_server_create(std::istream &is) @@ -371,6 +383,28 @@ void Entity::receive_server_create(std::istream &is) entity_info = 0; } + // has inventory + if (is >> o) { + if (!o) { + if (inventory()) { + con_warn << "Receive no inventory for entity " << id() << " " << label() << " with inventory!" << std::endl; + entity_inventory->clear(); + } + } else { + if (!inventory()) { + entity_inventory = new Inventory(); + } + } + } else { + if (inventory()) { + con_warn << "Receive no inventory for entity " << id() << " " << label() << " with inventory!" << std::endl; + entity_inventory->clear(); + } + } + + if (inventory()) { + inventory()->receive_server_update(is); + } entity_dirty = false; } diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index 3167790..f64c8fb 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -91,7 +91,7 @@ bool GameConnection::interactive() const return true; } -Info *GameConnection::info(unsigned int id) +Info *GameConnection::request_info(const unsigned int id) { if (!id) { con_warn << "Information requested for illegal id 0!" << std::endl; @@ -104,12 +104,11 @@ Info *GameConnection::info(unsigned int id) info = new Info(id); } - if ( !info->timestamp() || (connection_timestamp < info->timestamp() + INFOTIMEOUT) ) + if (info->type() || (connection_timestamp < info->timestamp() + INFOTIMEOUT) ) return info; // send an information request to the server if (connection_network) { - info->set_timestamp(connection_timestamp); connection_network->send_info_request(info); connection_network->transmit(); } else { @@ -119,51 +118,21 @@ Info *GameConnection::info(unsigned int id) return info; } -/* -Info *GameConnection::info(const std::string &type, const std::string &label) +Inventory *GameConnection::request_inventory(Entity *entity) { - if (!type.size()) { - con_warn << "Information requested with empty type label!" << std::endl; - return 0; - } - - if (!label.size()) { - con_warn << "Information requested with empty label!" << std::endl; + if (!entity) { + con_warn << "Inventory request for NULL entity" << std::endl; return 0; } - // find the info record type - InfoType *infotype = InfoType::find(type); - if (!infotype) { - // create a new info record type and set the label - infotype = new InfoType(type.c_str()); - } - - // find the info record - Info *info = Info::find(infotype, label); - if (info) { - if (!info->timestamp() || (connection_timestamp - info->timestamp()) < INFOTIMEOUT) - return info; - } else { - // create a new info record and set the label - info = new Info(infotype, label); - info->add_text("Requesting information..."); - } - - // send an information request to the server - if (connection_network) { - //con_debug << "Requesting info for " << info->type()->label() << ":" << info->label() << std::endl; - info->set_timestamp(connection_timestamp); - connection_network->send_info_request(info); + if (entity->inventory() && connection_network) { + connection_network->send_inventory_request(entity); connection_network->transmit(); - } else { - info->add_text("^RNot connected."); - info->set_timestamp(0); - } - return info; + } + + return (entity->inventory()); } -*/ - + void GameConnection::abort() { connection_running = false; diff --git a/src/core/gameconnection.h b/src/core/gameconnection.h index bb615c1..89c3aff 100644 --- a/src/core/gameconnection.h +++ b/src/core/gameconnection.h @@ -57,12 +57,13 @@ public: /// localplayer sends a private message to another player void private_message(std::string const &args); -/* - /// returns an info record - virtual Info *info(const std::string &type, const std::string &label); -*/ - /// returns an info record - virtual Info *info(unsigned int id); + + /// request info record with id + virtual Info *request_info(const unsigned int id); + + /// request inventory for entity with id + virtual Inventory *request_inventory(Entity *entity); + /*----- static ---------------------------------------------------- */ diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h index 11cc0de..bdce1d3 100644 --- a/src/core/gameinterface.h +++ b/src/core/gameinterface.h @@ -9,6 +9,7 @@ #include "core/player.h" #include "core/info.h" +#include "core/inventory.h" #include "model/vertexarray.h" namespace core @@ -61,12 +62,12 @@ public: /// return the current game time virtual unsigned long timestamp() const = 0; -/* - /// returns an info record - virtual Info *info(const std::string &type, const std::string &label) = 0; -*/ - /// returns an info record - virtual Info *info(unsigned int id) = 0; + + /// request info record with id + virtual Info *request_info(const unsigned int id) = 0; + + /// request inventory for entity with id + virtual Inventory *request_inventory(Entity *entity) = 0; /*----- mutators ------------------------------------------------- */ diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index 09a0fff..963db6d 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -243,19 +243,15 @@ float GameServer::time() const return ((float)(server_timestamp) / 1000.0f); } -Info *GameServer::info(unsigned int id) +Info *GameServer::request_info(unsigned int id) { return Info::find(id); } -Info *GameServer::info(const std::string &type, const std::string &label) +Inventory *GameServer::request_inventory(Entity *entity) { - // find the class - InfoType *infotype = InfoType::find(type); - if (!infotype) - return 0; + return (entity ? entity->inventory() : 0); - return Info::find(infotype, label); } void GameServer::abort() diff --git a/src/core/gameserver.h b/src/core/gameserver.h index 3f57a8b..775c41c 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -88,11 +88,11 @@ public: return server_startup; } - /// returns an info record - virtual Info *info(const std::string &type, const std::string &label); - - /// returns an info record - virtual Info *info(unsigned int id); + /// request info record with id + virtual Info *request_info(const unsigned int id); + + /// request inventory for entity with id + virtual Inventory *request_inventory(Entity *entity); /*----- static ---------------------------------------------------- */ diff --git a/src/core/info.cc b/src/core/info.cc index 16e68f5..cc1720e 100644 --- a/src/core/info.cc +++ b/src/core/info.cc @@ -53,6 +53,23 @@ InfoType *InfoType::find(const std::string & label) return 0; } +void InfoType::list() +{ + if (infotype_registry.size()) { + con_print << " "; + for (Registry::const_iterator it = infotype_registry.begin(); it != infotype_registry.end(); it++) { + InfoType *infotype = (*it); + + con_print << infotype->label() << " "; + } + con_print << std::endl; + } + con_print << "^B " << infotype_registry.size() << " info types" << std::endl; + + infotype_registry.clear(); +} + + /* ---- class Info ------------------------------------------------- */ unsigned int info_id_counter = 0; @@ -158,7 +175,10 @@ void Info::clear_text() void Info::serialize_server_update(std::ostream & os) const { - os << '"' << name() << "\" \"" << modelname() << "\" " << info_text.size() << " "; + os << '"' << name() << "\" \"" << modelname() << "\" " + << price() << " " + << volume() << " " + << info_text.size() << " "; for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) { if (it != info_text.begin()) @@ -186,6 +206,9 @@ void Info::receive_server_update(std::istream &is) while ((is.get(c)) && (c != '"')) n += c; set_modelname(n); + + is >> info_price; + is >> info_volume; // read info text size_t s; @@ -377,6 +400,7 @@ void Info::list(const Info *info) void Info::list() { + InfoType::list(); list_header(); for (Registry::iterator it = info_registry.begin(); it != info_registry.end(); it++) { Info *info = (*it); diff --git a/src/core/info.h b/src/core/info.h index b3fa58f..b8154d7 100644 --- a/src/core/info.h +++ b/src/core/info.h @@ -25,6 +25,9 @@ namespace core class InfoType : public Label { public: + /// info registry type definition + typedef std::vector<InfoType*> Registry; + /** * @brief create a new information card category * The constructor automaticly adds the instance to the registry @@ -40,11 +43,15 @@ public: /// search the infotype registry for a label static InfoType *find(const std::string & label); + + inline static const Registry ®istry() { + return infotype_registry; + } + /// list the infotypes + static void list(); + private: - /// info registry type definition - typedef std::vector<InfoType*> Registry; - static Registry infotype_registry; }; // class InfoType diff --git a/src/core/inventory.cc b/src/core/inventory.cc index 4b9a2e5..7957294 100644 --- a/src/core/inventory.cc +++ b/src/core/inventory.cc @@ -18,6 +18,7 @@ Inventory::Inventory(const float capacity) inventory_timestamp = 0; inventory_capacity = capacity; inventory_capacity_used = 0; + inventory_dirty = false; } Inventory::~Inventory() @@ -38,10 +39,11 @@ void Inventory::set_timestamp(const unsigned long timestamp) inventory_timestamp = timestamp; } -void Inventory::set_dirty() +void Inventory::set_dirty(const bool dirty) { - recalculate(); - inventory_timestamp = core::game()->timestamp(); + inventory_dirty = dirty; + if (dirty) + recalculate(); } void Inventory::add(Item *item) @@ -52,6 +54,7 @@ void Inventory::add(Item *item) return; } inventory_items.push_back(item); + inventory_dirty = true; } void Inventory::remove(Item *item) @@ -63,6 +66,7 @@ void Inventory::remove(Item *item) return; } } + // FIXME remove doesn't work over network } Item *Inventory::find(const Info *info) const @@ -97,6 +101,16 @@ void Inventory::recalculate() } } +void Inventory::serialize_server_update(std::ostream & os) const +{ + os << capacity() << " "; +} + +void Inventory::receive_server_update(std::istream &is) +{ + is >> inventory_capacity; +} + const long Inventory::max_amount(const long credits, const long price, const float volume) const { if ((price * volume) == 0) { diff --git a/src/core/inventory.h b/src/core/inventory.h index 7aaf299..80dbde1 100644 --- a/src/core/inventory.h +++ b/src/core/inventory.h @@ -45,7 +45,8 @@ public: }; /** - * @brief return the timestamp + * @brief return the timestamp of the last server update + * This is a client-side property and shoul not be used server-side */ inline const unsigned long timestamp() const { return inventory_timestamp; @@ -72,6 +73,10 @@ public: return inventory_capacity - inventory_capacity_used; } + inline const bool dirty() const { + return inventory_dirty; + } + /** * @brief returns the number of units of an item that can be stored in this inventory * @param credits number of player credits, limits the amount @@ -109,21 +114,24 @@ public: /** * @brief mark the inventory as dirty - * This method will set the timestamp to the current game time - * and will recalculate the available capacity - * @see recalculate() */ - void set_dirty(); + void set_dirty(const bool dirty = true); /** * @brief set the maximal inventory capacity, in cubic meters */ void set_capacity(const float capacity); -private: - // recalculate inventory capacity + /// recalculate inventory capacity void recalculate(); + + /// serialize a server-to-client update on a stream + void serialize_server_update(std::ostream & os) const; + /// receive a server-to-client update from a stream + void receive_server_update(std::istream &is); + +private: // items in the inventory Items inventory_items; @@ -135,6 +143,8 @@ private: // current capacity used, in cubic meters float inventory_capacity_used; + + bool inventory_dirty; }; } // namsepace core diff --git a/src/core/item.cc b/src/core/item.cc index 77b0f9c..e9d9f65 100644 --- a/src/core/item.cc +++ b/src/core/item.cc @@ -4,8 +4,10 @@ the terms of the GNU General Public License version 2 */ -#include "core/item.h" #include "sys/sys.h" +#include "core/application.h" +#include "core/item.h" + namespace core { @@ -17,6 +19,7 @@ Item::Item(const Info *info) item_info = info; item_amount = 0; item_price = info->price(); + set_timestamp(game() ? game()->timestamp() : 0); } Item::~Item() @@ -28,21 +31,41 @@ Item::~Item() void Item::set_amount(const long amount) { item_amount = amount; + set_timestamp(game() ? game()->timestamp() : 0); } void Item::inc_amount(const long amount) { item_amount += amount; + set_timestamp(game() ? game()->timestamp() : 0); } void Item::dec_amount(const long amount) { item_amount -= amount; + set_timestamp(game() ? game()->timestamp() : 0); } void Item::set_price(const long price) { item_price = price; + set_timestamp(game() ? game()->timestamp() : 0); +} + +void Item::set_timestamp(const unsigned long timestamp) +{ + item_timestamp = timestamp; +} + +void Item::serialize_server_update(std::ostream & os) const +{ + os << amount() << " " << price() << " "; +} + +void Item::receive_server_update(std::istream &is) +{ + is >> item_amount; + is >> item_price; } } // namespace core diff --git a/src/core/item.h b/src/core/item.h index 9c17dc9..e4e8507 100644 --- a/src/core/item.h +++ b/src/core/item.h @@ -39,24 +39,40 @@ public: inline const long price() const { return item_price; } + + inline const unsigned long timestamp() const { + return item_timestamp; + } /* ---- mutators ----------------------------------------------- */ /** * @brief set associated amount */ - void set_amount(const long amount); + void set_amount(const long amount); + + void inc_amount(const long amount); + + void dec_amount(const long amount); - void inc_amount(const long amount); + void set_price(const long price); - void dec_amount(const long amount); + void set_dirty(); + + /* ---- serializers -------------------------------------------- */ - void set_price(const long price); + void serialize_server_update(std::ostream & os) const; + + void receive_server_update(std::istream &is); private: + void set_timestamp(const unsigned long timestamp); + const Info *item_info; long item_price; long item_amount; + + unsigned long item_timestamp; }; } // namespace core diff --git a/src/core/net.h b/src/core/net.h index f6904b2..39dfdf6 100644 --- a/src/core/net.h +++ b/src/core/net.h @@ -28,6 +28,13 @@ const float NETTIMEOUT = 20; } +#ifndef _WIN32 +#include <sys/select.h> +#else +#include <winsock2.h> +#include <windows.h> +#endif + #include "core/netserver.h" #include "core/netclient.h" #include "core/netconnection.h" diff --git a/src/core/netclient.h b/src/core/netclient.h index 818422e..cca9529 100644 --- a/src/core/netclient.h +++ b/src/core/netclient.h @@ -7,6 +7,8 @@ #ifndef __INCLUDED_CORE_NETCLIENT_H__ #define __INCLUDED_CORE_NETCLIENT_H__ +#include "core/net.h" + #include <unistd.h> #include <errno.h> @@ -19,10 +21,6 @@ #include <netinet/in.h> #include <arpa/inet.h> -#else - -#include <windows.h> - #endif #include <string> diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index 39bed62..11e23c9 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -435,6 +435,17 @@ void NetConnection::send_info_request(Info *info) info->set_timestamp(timestamp()); } +// send an inventory update request +void NetConnection::send_inventory_request(Entity *entity) +{ + if (!entity || !entity->inventory()) + return; + + std::ostringstream msg; + msg << "inv " << entity->id() << " " << entity->inventory()->timestamp() << "\n"; + this->send_raw(msg.str()); +} + // parse incoming client messages /** * The following incoming messages are parsed; @@ -452,7 +463,8 @@ void NetConnection::send_info_request(Info *info) * sup <id> <typeid> * pif <id> * pid <id> - * inf <id> <typelabel> <label> + * inf <id> + * inv <id> <timestamp> * zone */ void NetConnection::parse_incoming_message(const std::string & message) @@ -787,6 +799,30 @@ void NetConnection::parse_incoming_message(const std::string & message) con_warn << "Received invalid info record message!" << std::endl; return; } + + if (id == 0) { + // special case: info type constructor + size_t s; + + if (!(msgstream >> s)) { + con_warn << "Received invalid info type message!" << std::endl; + return; + } + + for (size_t i = 0; i < s; i++) { + // read infotype label + n.clear(); + while ((msgstream.get(c)) && (c != '"')); + while ((msgstream.get(c)) && (c != '"')) + n +=c; + + if (n.size() && !InfoType::find(n)) { + // add new infotype + new InfoType(n.c_str()); + } + } + return; + } // read type label n.clear(); @@ -834,9 +870,61 @@ void NetConnection::parse_incoming_message(const std::string & message) info->set_id(id); info->receive_server_update(msgstream); - info->clear_timestamp(); + //info->clear_timestamp(); + info->set_timestamp(timestamp()); //con_debug << "Received info for " << info->id() << " " << info->type()->label() << ":" << info->label() << std::endl; + + } else if (command.compare("inv") == 0) { + + // received inventory update + unsigned int id = 0; + unsigned long server_timestamp; + + // read id + if (!(msgstream >> id >> server_timestamp)) { + con_warn << "Received invalid inventory update message!" << std::endl; + return; + } + + Entity *entity = Entity::find(id); + if (!entity) { + con_warn << "Received inventory update for non-existing entity " << id << "!" << std::endl; + return; + } + if (!entity->inventory()) { + con_warn << "Received inventory update for entity " << id << " without inventory!" << std::endl; + return; + } + + //con_debug << "CLIENT received inv message for entity " << id << " server timestamp " << server_timestamp << " client timestamp " << entity->inventory()->timestamp() << std::endl; + + + if (server_timestamp < entity->inventory()->timestamp()) + return; + + size_t nbitems = 0; + if (!(msgstream >> nbitems)) + nbitems = 0; + + //con_debug << "CLIENT number of items: " << nbitems << std::endl; + for (size_t i = 0; i < nbitems; i++) { + if (!(msgstream >> id)) { + con_warn << "Received inventory update without info id for existing entity " << id << "!" << std::endl; + return; + } + + Info *info = core::game()->request_info(id); + Item *item = entity->inventory()->find(info); + if (!item) { + item = new Item(info); + entity->inventory()->add(item); + } + item->receive_server_update(msgstream); + } + + entity->inventory()->set_timestamp(server_timestamp); + entity->inventory()->recalculate(); } } diff --git a/src/core/netconnection.h b/src/core/netconnection.h index 8aa0727..ffab093 100644 --- a/src/core/netconnection.h +++ b/src/core/netconnection.h @@ -7,6 +7,8 @@ #ifndef __INCLUDED_CORE_NETCONNECTION_H__ #define __INCLUDED_CORE_NETCONNECTION_H__ +#include "core/net.h" + #include <unistd.h> #include <errno.h> @@ -20,8 +22,6 @@ #include <sys/select.h> #include <netdb.h> -#else -#include <windows.h> #endif #include <string> @@ -81,6 +81,9 @@ public: /// send an info request void send_info_request(Info *info); + + /// send an inventory udpate request + void send_inventory_request(Entity *entity); /// transmit messages in the send queue to the remote server void transmit(); diff --git a/src/core/netplayer.cc b/src/core/netplayer.cc index 0935b4c..9d3e677 100644 --- a/src/core/netplayer.cc +++ b/src/core/netplayer.cc @@ -6,6 +6,7 @@ #include <string> +#include "core/net.h" #include "core/netplayer.h" #include "sys/sys.h" diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 2d83728..8d3e762 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -15,8 +15,6 @@ #include <netinet/in.h> #include <arpa/inet.h> -#else -#include <windows.h> #endif #include <iostream> @@ -295,6 +293,10 @@ void NetServer::client_initialize(NetClient *client) client->player()->send(welcome); client->transmit(); + // send info types + send_infotypes(client); + client->transmit(); + // send zones for (Zone::Registry::iterator it = Zone::registry().begin(); it != Zone::registry().end(); it++) { send_zone_update(client, (*it).second); @@ -377,6 +379,19 @@ void NetServer::client_frame(NetClient *client, unsigned long timestamp) } } + // send inventory update for control + // FIXME this should be done for all player assets + if (client->player()->control() && client->player()->control()->inventory() && client->player()->control()->inventory()->dirty()) { + //con_debug << "SERVER Sending inventory for entity " << client->player()->control()->id() << " server timestamp " << game()->timestamp() << std::endl; + send_inventory_update(client, client->player()->control(), client->player()->control()->inventory()->timestamp()); + } + + // send inventory updates for view + if (client->player()->view() && client->player()->view()->inventory() && client->player()->view()->inventory()->dirty()) { + //con_debug << "SERVER Sending inventory for entity " << client->player()->view()->id() << " server timestamp " << game()->timestamp() << std::endl; + send_inventory_update(client, client->player()->view(), client->player()->view()->inventory()->timestamp()); + } + // send updates for other players for (GameInterface::Players::iterator it = server()->players().begin(); it != server()->players().end(); it++) { if (((*it)->id() != client->player()->id()) && ((*it)->dirty())) { @@ -429,6 +444,8 @@ void NetServer::frame(unsigned long timestamp) * frame <timestamp> <previous timestamp> * ent <id> <entity create data> * die <id> <entity data> + * inf <id> + * inv <id> * ping <timestamp> * sup <entity update data> * @@ -571,6 +588,49 @@ void NetServer::send_info_update(NetClient *client, Info *info) client->send_raw(msg.str()); } +// send "inf" info types +void NetServer::send_infotypes(NetClient *client) +{ + std::ostringstream msg; + msg << "inf 0 " << InfoType::registry().size(); + + for (InfoType::Registry::const_iterator it = InfoType::registry().begin(); it != InfoType::registry().end(); it++) { + msg << " \"" << (*it)->label() << "\""; + } + msg << '\n'; + client->send_raw(msg.str()); +} + +// send a "inv" inventory update +void NetServer::send_inventory_update(NetClient *client, Entity *entity, const unsigned long timestamp) +{ + if (!entity || !entity->inventory()) + return; + + std::ostringstream msg; + msg << "inv " << entity->id() << " " << game()->timestamp() << " "; + + size_t nbitems = 0; + + std::ostringstream itemstr; + for (Inventory::Items::const_iterator it = entity->inventory()->items().begin(); it != entity->inventory()->items().end(); it++) { + const Item *item = (*it); + if (item->timestamp() >= timestamp) { + itemstr << item->info()->id() << " "; + item->serialize_server_update(itemstr); + nbitems++; + } + } + + msg << nbitems; + + if (nbitems) { + msg << " " << itemstr.str(); + } + msg << '\n'; + client->send_raw(msg.str()); +} + // parse incoming client messages /** @@ -584,8 +644,9 @@ void NetServer::send_info_update(NetClient *client, Info *info) * ping * say <text> * priv <player> <text> - * info <id> <typelabel> <label> + * info <id> * req <id> + * inv <id> * */ void NetServer::parse_incoming_message(NetClient *client, const std::string & message) @@ -641,8 +702,7 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me std::string oldname(client->player()->name()); client->player()->receive_client_update(msgstream); - if (client->state() == NetClient::Pending) { - + if (client->state() == NetClient::Pending) { client->client_state = NetClient::Connected; server()->player_connect(client->player()); @@ -740,6 +800,33 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me } } return; + + } else if (command.compare("inv") == 0) { + + // request information record + unsigned int id; + unsigned long client_timestamp; + + if (!(msgstream >> id >> client_timestamp)) { + con_warn << "^B" << client->player()->name() << "^W invalid inventory request" << std::endl; + return; + } + + Entity *entity = Entity::find (id); + if (!entity) { + con_warn << "^B" << client->player()->name() << "^W invalid request for non-existing entity " << id << std::endl; + return; + } + if (!entity->inventory()) { + con_warn << "^B" << client->player()->name() << "^W invalid request for entity " << id << " without inventory" << std::endl; + return; + } + + //con_debug << "SERVER Sending inventory for entity " << id << " client timestamp " << client_timestamp << std::endl; + + send_inventory_update(client, entity, client_timestamp); + + client->transmit(); } else if (command.compare("rcon") == 0) { if ((message.size() > command.size() + 1) && Cvar::sv_password->str().size()) { diff --git a/src/core/netserver.h b/src/core/netserver.h index afbb5a0..6eeb49b 100644 --- a/src/core/netserver.h +++ b/src/core/netserver.h @@ -7,12 +7,6 @@ #ifndef __INCLUDED_CORE_NETSERVER_H__ #define __INCLUDED_CORE_NETSERVER_H__ -#ifndef _WIN32 -#include <sys/select.h> -#else -#include <winsock2.h> -#endif - #include <list> #include <string> @@ -101,8 +95,14 @@ protected: /// send player disconnect information message void send_player_disconnect_info(NetClient *client, Player *player); + /// send info types + void send_infotypes(NetClient *client); + /// send player an info record void send_info_update(NetClient *client, Info *info); + + /// send player an inventory update + void send_inventory_update(NetClient *client, Entity *entity, const unsigned long timestamp); /// set the error state void abort(); diff --git a/src/ui/listview.cc b/src/ui/listview.cc index ced0dd6..fc75806 100644 --- a/src/ui/listview.cc +++ b/src/ui/listview.cc @@ -75,7 +75,7 @@ void ListView::deselect() void ListView::select(ListItem *item) { - if (is_child(item)) { + if (!item || is_child(item)) { listview_selecteditem = item; } } @@ -94,7 +94,7 @@ bool ListView::on_emit(Widget *sender, const Event event, void *data) return true; } - return ui::Widget::on_emit(sender, event, data); + return false; } } |