diff options
-rw-r--r-- | doc/STORYLINE | 6 | ||||
-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 |
27 files changed, 460 insertions, 143 deletions
diff --git a/doc/STORYLINE b/doc/STORYLINE index 4bd5c3b..c5c5514 100644 --- a/doc/STORYLINE +++ b/doc/STORYLINE @@ -220,14 +220,14 @@ The Alliance He was performing another scan of the sector when suddenly his instruments went off the scale. To his amazement he suddenly found himself on the edge on area where the - First Fleet had decided to regroup and refuel before they sneaked into Alliance Territory. + alien fleet had decided to regroup and refuel before they sneaked into Alliance Territory. The small vessel, barely powered and only slightly larger than an emergency life support pod, used the high levels of radiation in the nebula to elude the Tsu-Khan sensors and send a warning message the Alliance Fleet. With the help of the detailed scientific information about the nebula, the Alliance managed to jump right on top of the unsuspecting Tsu-Khan fleet. Within minutes, - the Emperial Flagship, primary target of the assault, had taken massive damage and was + the Imperial Flagship, primary target of the assault, had taken massive damage and was burning in the flames of explosive decompression. It was at the heart of the fleet when it exploded, and most of the heavy fighters and cruisers were hit by the shockwave and the massive rain of high-speed metallic debris following it. Some managed to retreat @@ -238,7 +238,7 @@ The Alliance through the Emperial forces like a knight with his spear, gave birth to the name "Battle of The Lance". -The Year + ------------------------------------------------------------------ PROJECT::OSIRION 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; } } |