Project::OSiRiON - Git repositories
Project::OSiRiON
News . About . Screenshots . Downloads . Forum . Wiki . Tracker . Git
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStijn Buys <ingar@osirion.org>2010-09-22 21:32:34 +0000
committerStijn Buys <ingar@osirion.org>2010-09-22 21:32:34 +0000
commita6f9773c358dd7d091ff64cbda504ab8d8066dd3 (patch)
tree226e23c4656957e908623ccda9d3d1c50240a0b4 /src
parentbbb43d1c15f2858573f5abb595aa62f8224e4d76 (diff)
full trading support for networked games
Diffstat (limited to 'src')
-rw-r--r--src/client/inventorylistview.cc81
-rw-r--r--src/client/inventorylistview.h3
-rw-r--r--src/client/map.cc2
-rw-r--r--src/client/playerview.cc2
-rw-r--r--src/client/trademenu.cc12
-rw-r--r--src/client/video.cc4
-rw-r--r--src/core/entity.cc36
-rw-r--r--src/core/gameconnection.cc53
-rw-r--r--src/core/gameconnection.h13
-rw-r--r--src/core/gameinterface.h13
-rw-r--r--src/core/gameserver.cc10
-rw-r--r--src/core/gameserver.h10
-rw-r--r--src/core/info.cc26
-rw-r--r--src/core/info.h13
-rw-r--r--src/core/inventory.cc20
-rw-r--r--src/core/inventory.h24
-rw-r--r--src/core/item.cc25
-rw-r--r--src/core/item.h24
-rw-r--r--src/core/net.h7
-rw-r--r--src/core/netclient.h6
-rw-r--r--src/core/netconnection.cc92
-rw-r--r--src/core/netconnection.h7
-rw-r--r--src/core/netplayer.cc1
-rw-r--r--src/core/netserver.cc97
-rw-r--r--src/core/netserver.h12
-rw-r--r--src/ui/listview.cc4
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 &registry() {
+ 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;
}
}