Project::OSiRiON - Git repositories
Project::OSiRiON
News . About . Screenshots . Downloads . Forum . Wiki . Tracker . Git
summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorStijn Buys <ingar@osirion.org>2009-04-15 17:08:51 +0000
committerStijn Buys <ingar@osirion.org>2009-04-15 17:08:51 +0000
commita95028547981614e06ea7a6d22b853b85418cea3 (patch)
treed4d6998a4118a4d8690ce138d586abfba4893179 /src/core
parent4f33f59571f10019c1e7a0e3640b2f69c159a8cf (diff)
added info registry, list_info
added network info transfer added info based buy menu and related game changes
Diffstat (limited to 'src/core')
-rw-r--r--src/core/Makefile.am3
-rw-r--r--src/core/commandbuffer.cc33
-rw-r--r--src/core/descriptions.cc27
-rw-r--r--src/core/descriptions.h9
-rw-r--r--src/core/gameconnection.cc22
-rw-r--r--src/core/gameconnection.h3
-rw-r--r--src/core/gameinterface.cc4
-rw-r--r--src/core/gameinterface.h8
-rw-r--r--src/core/gameserver.cc5
-rw-r--r--src/core/gameserver.h3
-rw-r--r--src/core/info.cc183
-rw-r--r--src/core/info.h108
-rw-r--r--src/core/net.h2
-rw-r--r--src/core/netconnection.cc33
-rw-r--r--src/core/netconnection.h4
-rw-r--r--src/core/netserver.cc30
-rw-r--r--src/core/netserver.h4
17 files changed, 467 insertions, 14 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 317a99e..af59d00 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -3,7 +3,7 @@ INCLUDES = -I$(top_srcdir)/src
libcore_la_SOURCES = application.cc commandbuffer.cc core.cc cvar.cc \
descriptions.cc entity.cc extension.cc func.cc gameconnection.cc gameinterface.cc \
- gameserver.cc loader.cc module.cc netclient.cc netconnection.cc netplayer.cc \
+ gameserver.cc info.cc loader.cc module.cc netclient.cc netconnection.cc netplayer.cc \
netserver.cc parser.cc player.cc stats.cc timer.cc zone.cc
libcore_la_LDFLAGS = -avoid-version -no-undefined
libcore_la_LIBADD = $(top_builddir)/src/model/libmodel.la \
@@ -15,3 +15,4 @@ noinst_HEADERS = application.h commandbuffer.h core.h cvar.h entity.h func.h \
gameconnection.h gameinterface.h gameserver.h message.h module.h net.h netclient.h \
netconnection.h netserver.h player.h range.h stats.h timer.h parser.h descriptions.h \
extension.h loader.h
+_SOURCES = info.h
diff --git a/src/core/commandbuffer.cc b/src/core/commandbuffer.cc
index 21002bd..84b63be 100644
--- a/src/core/commandbuffer.cc
+++ b/src/core/commandbuffer.cc
@@ -16,6 +16,7 @@
#include "core/commandbuffer.h"
#include "core/gameconnection.h"
#include "core/func.h"
+#include "core/info.h"
#include "core/cvar.h"
#include "core/loader.h"
#include "core/zone.h"
@@ -23,6 +24,8 @@
namespace core
{
+std::stringstream CommandBuffer::cmdbuf(std::stringstream::in | std::stringstream::out);
+
void func_print(std::string const &args)
{
con_print << args << "\n";
@@ -38,6 +41,15 @@ void func_print_file(std::string const &args)
CommandBuffer::print_file(filename);
}
+void func_list_info(std::string const &args)
+{
+ Info *info = Info::find(args);
+ if (info)
+ info->print();
+ else
+ Info::list();
+}
+
void func_list_func(std::string const &args)
{
Func::list();
@@ -151,7 +163,19 @@ void func_exec(std::string const &args)
CommandBuffer::exec_file(filename);
}
-std::stringstream CommandBuffer::cmdbuf(std::stringstream::in | std::stringstream::out);
+void func_remote(std::string const &args)
+{
+ if (connection()) {
+ if ((args[0] == '\\') || (args[0] == '/')) {
+ connection()->forward(args.substr(1, args.size()-1));
+ } else {
+ connection()->forward(args);
+ }
+ } else {
+ cmd() << args;
+ CommandBuffer::exec();
+ }
+}
void CommandBuffer::init()
{
@@ -164,6 +188,9 @@ void CommandBuffer::init()
func = Func::add("list_func", (FuncPtr)func_list_func);
func->set_info("list functions");
+ func = Func::add("list_info", (FuncPtr)func_list_info);
+ func->set_info("list infos");
+
func = Func::add("list_var", (FuncPtr)func_list_var);
func->set_info("list variables");
@@ -190,6 +217,9 @@ void CommandBuffer::init()
func = Func::add("exec", (FuncPtr)func_exec);
func->set_info("[filename] execute commands from file");
+
+ func = Func::add("remote", (FuncPtr)func_remote);
+ func->set_info("[command] send a command to the game server");
}
void CommandBuffer::shutdown()
@@ -200,6 +230,7 @@ void CommandBuffer::shutdown()
Func::remove("toggle");
Func::remove("list_var");
Func::remove("list_func");
+ Func::remove("list_info");
Func::remove("list_ent");
Func::remove("list_model");
Func::remove("list_module");
diff --git a/src/core/descriptions.cc b/src/core/descriptions.cc
index fe37d2c..8c0f4ee 100644
--- a/src/core/descriptions.cc
+++ b/src/core/descriptions.cc
@@ -16,6 +16,7 @@ ButtonDescription::ButtonDescription()
{
button_model = 0;
button_align = Center;
+ button_commandtype = CommandNone;
}
ButtonDescription::~ButtonDescription()
@@ -27,8 +28,9 @@ void ButtonDescription::set_text(const std::string &text)
button_text.assign(text);
}
-void ButtonDescription::set_command(const std::string &command)
+void ButtonDescription::set_command(const std::string &command, const CommandType command_type)
{
+ button_commandtype = command_type;
button_command.assign(command);
}
@@ -85,9 +87,10 @@ void Descriptions::serialize(MenuDescription *menu, std::ostream & os)
for (MenuDescription::Buttons::iterator it = menu-> buttons().begin(); it != menu->buttons().end(); it++) {
ButtonDescription *button = (*it);
- os << "\"" << button->text() << "\" "
+ os << "\"" << button->text() << "\" "
<< button->alignment() << " "
- << "\"" << button->command() << "\" "
+ << button->command_type() << " "
+ << "\"" << button->command() << "\" "
<< "\"" << button->modelname() << "\" ";
}
}
@@ -97,6 +100,7 @@ MenuDescription * Descriptions::receive(std::istream &is)
MenuDescription *menu = new MenuDescription();
int a;
+ int t;
std::string n;
size_t nb;
char c;
@@ -135,12 +139,22 @@ MenuDescription * Descriptions::receive(std::istream &is)
else
button->set_alignment(ButtonDescription::Center);
+ // button command type
+ is >> t;
+
// button command
n.clear();
while ( (is.get(c)) && (c != '"'));
while ( (is.get(c)) && (c != '"'))
n += c;
- if (n.size()) button->set_command(n);
+
+ if (n.size()) {
+ if (t == ButtonDescription::CommandGame) {
+ button->set_command(n, ButtonDescription::CommandGame);
+ } else if (t == ButtonDescription::CommandMenu) {
+ button->set_command(n, ButtonDescription::CommandMenu);
+ }
+ }
// button modelname
n.clear();
@@ -213,7 +227,10 @@ bool Descriptions::load_entity_menus(core::Entity *entity, const std::string &me
if (strval[i] == ',') strval[i] = ';';
}
aux::strip_quotes(strval);
- button->set_command(strval);
+
+ // default command is a game command
+ button->set_command(strval, ButtonDescription::CommandGame);
+
} else if (inifile.got_key_string("model", strval)) {
button->set_modelname(strval);
} else if (inifile.got_key_string("align", strval)) {
diff --git a/src/core/descriptions.h b/src/core/descriptions.h
index 345d354..dec7896 100644
--- a/src/core/descriptions.h
+++ b/src/core/descriptions.h
@@ -28,6 +28,8 @@ class ButtonDescription
public:
enum Align {Center=0, Left=1, Right=2};
+ enum CommandType {CommandNone=0, CommandGame=1, CommandMenu=2};
+
ButtonDescription();
~ButtonDescription();
@@ -36,6 +38,9 @@ public:
/// button text
inline const std::string & text() const { return button_text; }
+ /// button command type
+ inline const CommandType command_type() const {return button_commandtype; }
+
/// button command
inline const std::string & command() const { return button_command; }
@@ -54,7 +59,7 @@ public:
void set_text(const std::string &text);
/// set button command
- void set_command(const std::string &command);
+ void set_command(const std::string &command, const CommandType command_type);
/// set button name
void set_modelname(const std::string &modelname);
@@ -64,10 +69,10 @@ public:
private:
std::string button_text;
+ CommandType button_commandtype;
std::string button_command;
std::string button_modelname;
Align button_align;
-
model::Model *button_model;
};
diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc
index c2763ee..2dc0e32 100644
--- a/src/core/gameconnection.cc
+++ b/src/core/gameconnection.cc
@@ -74,6 +74,28 @@ GameConnection::~GameConnection()
connection_instance = 0;
}
+Info *GameConnection::info(const std::string &label)
+{
+ // check if we already have the info record
+ Info *info = Info::find(label);
+ if (info)
+ return info;
+
+ // create a new information record and set the label
+ info = new Info(label);
+ Info::add(info);
+
+ info->text().push_back("Requesting information...");
+
+ // send an information request to the server
+ if (connection_network) {
+ connection_network->send_info_request(info);
+ } else {
+ info->text().push_back("^RNot connected.");
+ }
+ return info;
+}
+
void GameConnection::abort()
{
connection_running = false;
diff --git a/src/core/gameconnection.h b/src/core/gameconnection.h
index e1a7008..5880f9b 100644
--- a/src/core/gameconnection.h
+++ b/src/core/gameconnection.h
@@ -51,6 +51,9 @@ public:
/// localplayer sends a private message to another player
void private_message(std::string const &args);
+ /// returns an info record
+ Info *info(const std::string &label);
+
/*----- static ---------------------------------------------------- */
/// return the current game connection
diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc
index f658619..0ade34b 100644
--- a/src/core/gameinterface.cc
+++ b/src/core/gameinterface.cc
@@ -12,6 +12,7 @@
#include "core/application.h"
#include "core/cvar.h"
#include "core/func.h"
+#include "core/info.h"
#include "core/gameinterface.h"
#include "core/player.h"
#include "core/zone.h"
@@ -120,6 +121,9 @@ void GameInterface::clear()
// remove all models
model::Model::clear();
+ // remove infos
+ Info::clear();
+
// clear player list
for (Players::iterator it = game_players.begin(); it != game_players.end(); it++) {
Player *player = (*it);
diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h
index 22b85d5..8dc401b 100644
--- a/src/core/gameinterface.h
+++ b/src/core/gameinterface.h
@@ -8,6 +8,7 @@
#define __INCLUDED_CORE_GAMEINTERFACE_H__
#include "core/player.h"
+#include "core/info.h"
namespace core
{
@@ -38,6 +39,9 @@ public:
/// show a list of connected players
void list_players();
+ /// return the current game time, in seconds
+ float time() const { return ((float)(timestamp()) / 1000.0f); }
+
/*----- virtual inspectors --------------------------------------- */
/// returns true if the game server can run a time frime
@@ -49,8 +53,8 @@ public:
/// return the current game time
virtual unsigned long timestamp() const = 0;
- /// return the current game time, in seconds
- float time() const { return ((float)(timestamp()) / 1000.0f); }
+ /// returns an info record
+ virtual Info *info(const std::string &label) = 0;
/*----- mutators ------------------------------------------------- */
diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc
index fc7a814..b82946b 100644
--- a/src/core/gameserver.cc
+++ b/src/core/gameserver.cc
@@ -226,6 +226,11 @@ GameServer::~GameServer()
server_instance = 0;
}
+Info *GameServer::info(const std::string &label)
+{
+ return Info::find(label);
+}
+
void GameServer::abort()
{
server_running = false;
diff --git a/src/core/gameserver.h b/src/core/gameserver.h
index 31cfca6..ac5f19e 100644
--- a/src/core/gameserver.h
+++ b/src/core/gameserver.h
@@ -77,6 +77,9 @@ public:
/// time the server was started
inline const unsigned long startup() const { return server_startup; }
+ /// returns an info record
+ Info *info(const std::string &label);
+
/*----- static ---------------------------------------------------- */
/// return the current game server
diff --git a/src/core/info.cc b/src/core/info.cc
new file mode 100644
index 0000000..fc10fe5
--- /dev/null
+++ b/src/core/info.cc
@@ -0,0 +1,183 @@
+/*
+ core/info.xx
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
+*/
+
+#include "auxiliary/functions.h"
+#include "core/info.h"
+#include "sys/sys.h"
+
+namespace core
+{
+
+Info::Registry Info::registry;
+
+Info::Info(const std::string & label)
+{
+ info_label.assign(label);
+ aux::to_lowercase(info_label);
+ aux::strip_quotes(info_label);
+ info_timestamp = 0;
+}
+
+void Info::set_name(const std::string & name)
+{
+ info_name.assign(name);
+}
+
+void Info::set_name(const char *name)
+{
+ info_name.assign(name);
+}
+
+void Info::set_modelname(const std::string & modelname)
+{
+ info_modelname.assign(modelname);
+}
+
+void Info::set_modelname(const char *modelname)
+{
+ info_modelname.assign(modelname);
+}
+
+void Info::set_timestamp(const unsigned long timestamp)
+{
+ info_timestamp = timestamp;
+}
+
+void Info::clear_timestamp()
+{
+ info_timestamp = 0;
+}
+
+void Info::add_text(const char *text)
+{
+ std::string str(text);
+ aux::strip_quotes(str);
+ info_text.push_back(str);
+}
+
+void Info::add_text(const std::string & text)
+{
+ add_text(text.c_str());
+}
+
+void Info::clear_text()
+{
+ info_text.clear();
+}
+
+void Info::serialize_server_update(std::ostream & os) const
+{
+ os << '"' << name() << "\" \"" << modelname() << "\" " << info_text.size() << " ";
+
+ for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) {
+ if (it != info_text.begin())
+ os << ' ';
+
+ os << '"' << (*it) << '"';
+ }
+}
+
+void Info::receive_server_update(std::istream &is)
+{
+ std::string n;
+ char c;
+
+ // read name
+ while ( (is.get(c)) && (c != '"'));
+ while ( (is.get(c)) && (c != '"'))
+ n += c;
+ info_name.assign(n);
+
+ // read model name
+ n.clear();
+ while ( (is.get(c)) && (c != '"'));
+ while ( (is.get(c)) && (c != '"'))
+ n += c;
+
+ info_modelname.assign(n);
+
+ // read info text
+ size_t s;
+ info_text.clear();
+ is >> s;
+ for (size_t i = 0; (i < s) && is.good(); i++) {
+ n.clear();
+ while ( (is.get(c)) && (c != '"'));
+ while ( (is.get(c)) && (c != '"'))
+ n += c;
+
+ info_text.push_back(n);
+ }
+}
+
+Info::~Info()
+{
+ info_text.clear();
+ info_modelname.clear();
+ info_text.clear();
+}
+
+void Info::print() const
+{
+ con_print << "label: ^B" << label() << " ^Nname: ^B" << name() << "^N" << std::endl;
+
+ for (Text::const_iterator it = info_text.begin(); it != info_text.end(); it++) {
+ con_print << " " << (*it) << std::endl;
+ }
+}
+
+/* ---- static info registry --------------------------------------- */
+
+void Info::add(Info *info)
+{
+ if (find(info->label()))
+ return;
+
+ registry[info->label()] = info;
+}
+
+Info *Info::find(const char *label)
+{
+ for (Registry::iterator it = registry.begin(); it != registry.end(); it++) {
+ Info *info = (*it).second;
+ if (info->label().compare(label) == 0) {
+ return info;
+ }
+ }
+ return 0;
+}
+
+Info *Info::find(const std::string & label)
+{
+ for (Registry::iterator it = registry.begin(); it != registry.end(); it++) {
+ Info *info = (*it).second;
+ if (info->label().compare(label) == 0) {
+ return info;
+ }
+ }
+ return 0;
+}
+
+void Info::clear()
+{
+ for (Registry::iterator it = registry.begin(); it != registry.end(); it++) {
+ Info *info = (*it).second;;
+ delete info;
+ }
+ registry.clear();
+}
+
+void Info::list()
+{
+ for (Registry::iterator it = registry.begin(); it != registry.end(); it++) {
+ Info *info = (*it).second;;
+ con_print << info->label() << std::endl;
+ }
+ con_print << registry.size() << " registered infos" << std::endl;
+}
+
+}
+
diff --git a/src/core/info.h b/src/core/info.h
new file mode 100644
index 0000000..cc480bf
--- /dev/null
+++ b/src/core/info.h
@@ -0,0 +1,108 @@
+/*
+ core/info.h
+ This file is part of the Osirion project and is distributed under
+ the terms of the GNU General Public License version 2
+*/
+
+#ifndef __INCLUDED_CORE_INFO_H__
+#define __INCLUDED_CORE_INFO_H__
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <deque>
+
+#include "model/model.h"
+
+namespace core
+{
+
+/**
+ * an information record
+ */
+class Info
+{
+public:
+ /// create a new labeled information record
+ Info(const std::string & label);
+ /// delete the information record
+ ~Info();
+
+ typedef std::deque<std::string> Text;
+
+ inline const std::string & label() const { return info_label; }
+
+ inline const std::string & name() const { return info_name; }
+
+ inline const std::string & modelname() const { return info_modelname; }
+
+ inline const unsigned long &timestamp() const { return info_timestamp; }
+
+ inline Text & text() { return info_text; }
+
+ void set_name(const std::string & name);
+
+ void set_name(const char *name);
+
+ void set_modelname(const std::string & modelname);
+
+ void set_modelname(const char *modelname);
+
+ /// set the timestamp
+ void set_timestamp(const unsigned long timestamp);
+
+ /// clear the timestamp
+ void clear_timestamp();
+
+ /// add a line of info text
+ void add_text(const char *text);
+
+ /// add a line of info text
+ void add_text(const std::string & text);
+
+ /// clear the info text
+ void clear_text();
+
+ /// print info to sys::con_out
+ void print() const;
+
+ /// 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);
+
+/* ---- static info registry --------------------------------------- */
+
+ typedef std::map<std::string, Info*> Registry;
+
+ /// add an item to the info regsitry
+ static void add(Info *info);
+
+ /// search the info registry for a labeled item
+ static Info *find(const char * label);
+
+ /// search the info registry for a labeled item
+ static Info *find(const std::string & label);
+
+ /// clear the info registry
+ static void clear();
+
+ /// list the info registry
+ static void list();
+
+private:
+ std::string info_label;
+ std::string info_name;
+ std::string info_modelname;
+ Text info_text;
+
+ long info_credits;
+ static Registry registry;
+
+ unsigned long info_timestamp;
+};
+
+}
+#endif // __INCLUDED_CORE_INFO_H__
+
diff --git a/src/core/net.h b/src/core/net.h
index 887bf1b..01c15be 100644
--- a/src/core/net.h
+++ b/src/core/net.h
@@ -11,7 +11,7 @@ namespace core
{
/// network protocol version
-const unsigned int PROTOCOLVERSION = 15;
+const unsigned int PROTOCOLVERSION = 16;
/// maximum lenght of a (compressed) network message block
const unsigned int FRAMESIZE = 1152;
diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc
index fc2b6bf..dfb86f7 100644
--- a/src/core/netconnection.cc
+++ b/src/core/netconnection.cc
@@ -410,6 +410,16 @@ void NetConnection::send_ping_reply(unsigned long timestamp)
this->send_raw(msg.str());
}
+// send an info record request
+void NetConnection::send_info_request(Info *info)
+{
+ std::ostringstream msg;
+ msg << "inf " << '"' << info->label() << '"' << '\n';
+ this->send_raw(msg.str());
+
+ info->set_timestamp(application()->timestamp());
+}
+
// parse incoming client messages
/**
* The following incoming messages are parsed;
@@ -674,7 +684,28 @@ void NetConnection::parse_incoming_message(const std::string & message)
return;
}
}
-
+ } else if (command == "inf") {
+
+ // incoming info record
+ std::string label;
+ char c;
+
+ while ( (msgstream.get(c)) && (c != '"'));
+ while ( (msgstream.get(c)) && (c != '"'))
+ label += c;
+
+ if (label.size()) {
+ Info *info = Info::find(label);
+ if (!info) {
+ info = new Info(label);
+ Info::add(info);
+ }
+
+ info->receive_server_update(msgstream);
+ info->clear_timestamp();
+ } else {
+ con_warn << "Received empty information record!" << std::endl;
+ }
} else if (command == "sup") {
if (connection_state == Connected)
{
diff --git a/src/core/netconnection.h b/src/core/netconnection.h
index a17b35d..7c0b23d 100644
--- a/src/core/netconnection.h
+++ b/src/core/netconnection.h
@@ -30,6 +30,7 @@
#include "core/entity.h"
#include "core/net.h"
+#include "core/info.h"
namespace core
{
@@ -71,6 +72,9 @@ public:
/// send a console command to the remote server
void send_rcon(std::string const &cmdline);
+ /// send an info request
+ void send_info_request(Info *info);
+
/// transmit messages in the send queue to the remote server
void transmit();
diff --git a/src/core/netserver.cc b/src/core/netserver.cc
index 62d9b74..5b3a991 100644
--- a/src/core/netserver.cc
+++ b/src/core/netserver.cc
@@ -525,7 +525,7 @@ void NetServer::send_player_update(NetClient *client)
void NetServer::send_player_update(NetClient *client, Player *player)
{
std::ostringstream msg;
- msg << "pif " << player->id() << " ";
+ msg << "pif " << player->id() << ' ';
client->player()->serialize_server_update(msg);
msg << '\n';
client->send_raw(msg.str());
@@ -539,6 +539,16 @@ void NetServer::send_player_disconnect_info(NetClient *client, Player *player)
client->send_raw(msg.str());
}
+// send a "inf" info record
+void NetServer::send_info_update(NetClient *client, Info *info)
+{
+ std::ostringstream msg;
+ msg << "inf " << '"' << info->label() << '"' << ' ';
+ info->serialize_server_update(msg);
+ msg << '\n';
+ client->send_raw(msg.str());
+}
+
// parse incoming client messages
/**
@@ -547,6 +557,7 @@ void NetServer::send_player_disconnect_info(NetClient *client, Player *player)
* disconnect
* cmd <game command>
* cup
+ * inf
* pif
* ping
* say <text>
@@ -641,6 +652,23 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me
return;
}
+ if (command == "inf") {
+ std::string n;
+ char c;
+
+ while ( (msgstream.get(c)) && (c != '"'));
+ while ( (msgstream.get(c)) && (c != '"'))
+ n += c;
+
+ if (n.size()) {
+ Info *info = Info::find(n);
+ if (info) {
+ send_info_update(client, info);
+ client->transmit();
+ }
+ }
+ }
+
if (command == "rcon") {
if ((message.size() > command.size()+1) && Cvar::sv_password->str().size()) {
if ((Cvar::sv_password->str().compare(client->player()->rconpassword()) == 0)) {
diff --git a/src/core/netserver.h b/src/core/netserver.h
index 9ef0f0d..e54ea2e 100644
--- a/src/core/netserver.h
+++ b/src/core/netserver.h
@@ -19,6 +19,7 @@
#include "core/net.h"
#include "core/netclient.h"
#include "core/player.h"
+#include "core/info.h"
namespace core
{
@@ -92,6 +93,9 @@ protected:
/// send player disconnect information message
void send_player_disconnect_info(NetClient *client, Player *player);
+ /// send player an info record
+ void send_info_update(NetClient *client, Info *info);
+
/// set the error state
void abort();