From 493e4317e19725e2de2d51753e5c1906bf9c64ba Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 7 Dec 2014 23:27:31 +0000 Subject: Implemented messageboxes and the ability for the game module to send them to remote clients, send a messagebox if the player's ship is destroyed, this fixes having to press the respawn button twice. added messageboxes on network connection failures. --- src/client/Makefile.am | 2 + src/client/client.cc | 13 ++++ src/client/client.h | 5 ++ src/client/mainwindow.cc | 2 +- src/client/messagebox.cc | 150 ++++++++++++++++++++++++++++++++++++++++++++++ src/client/messagebox.h | 50 ++++++++++++++++ src/core/application.cc | 22 ++++++- src/core/application.h | 6 ++ src/core/gameserver.cc | 19 ++++++ src/core/gameserver.h | 3 +- src/core/netconnection.cc | 54 +++++++++++++++-- src/core/netserver.cc | 42 +++++++++++-- src/core/netserver.h | 7 ++- src/core/player.cc | 13 ++++ src/core/player.h | 5 +- src/game/base/ship.cc | 1 + 16 files changed, 380 insertions(+), 14 deletions(-) create mode 100644 src/client/messagebox.cc create mode 100644 src/client/messagebox.h (limited to 'src') diff --git a/src/client/Makefile.am b/src/client/Makefile.am index f6d447d..5577879 100644 --- a/src/client/Makefile.am +++ b/src/client/Makefile.am @@ -34,6 +34,7 @@ noinst_HEADERS = \ mainwindow.h \ mapwidget.h \ mapwindow.h \ + messagebox.h \ notifications.h \ reputationwindow.h \ savegamemenu.h \ @@ -70,6 +71,7 @@ libclient_la_SOURCES = \ mainwindow.cc \ mapwidget.cc \ mapwindow.cc \ + messagebox.cc \ notifications.cc \ reputationwindow.cc \ savegamemenu.cc \ diff --git a/src/client/client.cc b/src/client/client.cc index 128ac7f..336baee 100644 --- a/src/client/client.cc +++ b/src/client/client.cc @@ -105,12 +105,18 @@ void Client::init(int count, char **arguments) // initialize user interface ui::init(); + // main application window client_mainwindow = new MainWindow(ui::root()); + // messagebox window + client_messagebox = new Messagebox(ui::root()); + client_messagebox->hide(); + // FIXME needs to be a mainwindow child client_testmodelwindow = new TestModelWindow(ui::root()); client_testmodelwindow->hide(); + // Initialize the video subsystem if (!video::init()) { quit(1); @@ -391,6 +397,13 @@ void Client::notify_message(const core::Message::Channel channel, const std::str con_print << message << std::endl; } +void Client::notify_messagebox(const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2) +{ + client_messagebox->set_text(text); + client_messagebox->set_buttons(label1, command1, label2, command2); + client_messagebox->show(); +} + void Client::notify_loader(const std::string &message) { video::set_loader_message(message.c_str()); diff --git a/src/client/client.h b/src/client/client.h index f4bfcf3..2b0d187 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -14,6 +14,7 @@ #include "client/soundext.h" #include "client/testmodelwindow.h" #include "client/mainwindow.h" +#include "client/messagebox.h" #include "render/renderext.h" /// client part of the engine @@ -41,6 +42,9 @@ public: /// text notifications from the core virtual void notify_message(const core::Message::Channel channel, const std::string &message); + + /// messagebox notifications + virtual void notify_messagebox(const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2); /// loading message notification virtual void notify_loader(const std::string &message); @@ -111,6 +115,7 @@ private: MainWindow *client_mainwindow; TestModelWindow *client_testmodelwindow; + Messagebox *client_messagebox; unsigned long previous_timestamp; }; diff --git a/src/client/mainwindow.cc b/src/client/mainwindow.cc index 1d9f4ff..aa113a7 100644 --- a/src/client/mainwindow.cc +++ b/src/client/mainwindow.cc @@ -126,7 +126,7 @@ void MainWindow::draw() } else if (core::localcontrol()->state() == core::Entity::Destroyed) { mainwindow_gamewindow->hide(); - mainwindow_mainmenu->show_menu("respawn"); + // game module sends a messagebox to take approriate action } else if (mainwindow_gamewindow->hidden()) { mainwindow_gamewindow->show(); diff --git a/src/client/messagebox.cc b/src/client/messagebox.cc new file mode 100644 index 0000000..f0e449a --- /dev/null +++ b/src/client/messagebox.cc @@ -0,0 +1,150 @@ +/* + client/messagebox.cc + This file is part of the Osirion project and is distributed under + the terms and conditions of the GNU General Public License version 2 +*/ + +#include "audio/audio.h" +#include "client/messagebox.h" +#include "core/commandbuffer.h" +#include "ui/ui.h" + +namespace client +{ + +Messagebox::Messagebox(ui::Widget * parent) : + ui::Window(parent) +{ + set_border(false); + set_background(false); + set_label("messagebox"); + + messagebox_frame = new ui::Window(this); + messagebox_frame->set_border(true); + messagebox_frame->set_background(true); + + messagebox_label = new ui::Label(messagebox_frame); + messagebox_label->set_border(false); + messagebox_label->set_background(false); + messagebox_label->set_alignment(ui::AlignTop | ui::AlignHCenter); + + messagebox_button1 = new ui::Button(messagebox_frame); + messagebox_button2 = new ui::Button(messagebox_frame); +} + +Messagebox::~Messagebox() +{ +} + +void Messagebox::set_text(const std::string &text) +{ + messagebox_label->set_text(text); +} + +void Messagebox::set_buttons(const std::string &text1, const std::string &command1, const std::string &text2, const std::string &command2) +{ + if (text1.size()) { + messagebox_button1->set_text(text1); + } else { + messagebox_button1->set_text("Close"); + } + if (command1.size()) { + messagebox_button1->set_command("remote " + command1); + } else { + messagebox_button1->set_command(""); + } + + messagebox_button2->set_text(text2); + if (command2.size()) { + messagebox_button2->set_command("remote " + command2); + } else { + messagebox_button2->set_command(""); + } + + if (text2.size()) { + messagebox_button2->show(); + } else { + messagebox_button2->hide(); + } +} + +void Messagebox::resize() +{ + const float padding = ui::root()->font_large()->height(); + + set_size(parent()->size()); + + messagebox_frame->set_size( + ui::UI::elementsize.width() * 3.0f, + ui::UI::elementsize.width() * 1.5f + ); + messagebox_frame->set_location( + (width() - messagebox_frame->width()) * 0.5f, + (height() - messagebox_frame->height()) * 0.5f + ); + + messagebox_label->set_size(messagebox_frame->width() - padding * 2.0f, messagebox_frame->height() - padding * 2.0f); + messagebox_label->set_location(padding, padding); + + messagebox_button1->set_size(ui::UI::elementsize); + messagebox_button2->set_size(ui::UI::elementsize); + + if (messagebox_button2->visible()) { + const float l = (messagebox_frame->width() - messagebox_button1->width() - messagebox_button2->width() - padding * 2.0f) * 0.5f; + messagebox_button1->set_location(l, messagebox_frame->height() - messagebox_button1->height() - padding); + messagebox_button2->set_location(messagebox_button1->right() + padding, messagebox_frame->height() - messagebox_button2->height() - padding); + } else { + messagebox_button1->set_location((messagebox_frame->width() - messagebox_button1->width()) * 0.5f, messagebox_frame->height() - messagebox_button1->height() - padding); + } +} + +bool Messagebox::on_emit(Widget *sender, const Event event, void *data) +{ + if ((sender == messagebox_button1) || (sender == messagebox_button2)) { + if (event == Widget::EventButtonClicked) { + hide(); + } + return true; + } + + return false; +} + +bool Messagebox::on_keypress(const int key, const unsigned int modifier) +{ + std::string command; + + switch (key) { + case SDLK_RETURN: + case SDLK_KP_ENTER: + command.assign(messagebox_button1->command()); + if (command.size()) { + core::cmd() << command << std::endl; + } + audio::play("ui/clicked"); + hide(); + return true; + break; + + case SDLK_ESCAPE: + if (messagebox_button2->visible()) { + command.assign(messagebox_button2->command()); + } else { + command.assign(messagebox_button1->command()); + } + if (command.size()) { + core::cmd() << command << std::endl; + } + audio::play("ui/clicked"); + hide(); + return true; + break; + + default: + break; + } + + return Window::on_keypress(key, modifier); +} + +} // namespace client diff --git a/src/client/messagebox.h b/src/client/messagebox.h new file mode 100644 index 0000000..ac1d46b --- /dev/null +++ b/src/client/messagebox.h @@ -0,0 +1,50 @@ +/* + client/messagebox.h + This file is part of the Osirion project and is distributed under + the terms and conditions of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_CLIENT_MESSAGEBOX_H__ +#define __INCLUDED_CLIENT_MESSAGEBOX_H__ + +#include "ui/window.h" +#include "ui/button.h" +#include "ui/label.h" + +namespace client +{ + +/** + * @brief a generic messagebox window class + * */ +class Messagebox : public ui::Window +{ +public: + Messagebox(ui::Widget *parent = 0); + virtual ~Messagebox(); + + void set_text(const std::string &text); + + void set_buttons(const std::string &text1, const std::string &command1, const std::string &text2, const std::string &command2); + +protected: + virtual void resize(); + + virtual bool on_keypress(const int key, const unsigned int modifier); + + virtual bool on_emit(Widget *sender, const Event event, void *data); + +private: + /// the actual dialog widget + ui::Window *messagebox_frame; + + ui::Label *messagebox_label; + ui::Button *messagebox_button1; + ui::Button *messagebox_button2; +}; + +} + +#endif // __INCLUDED_CLIENT_MESSAGEBOX_H__ + + diff --git a/src/core/application.cc b/src/core/application.cc index 14bb9f3..b181178 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -469,10 +469,30 @@ void Application::notify_message(const core::Message::Channel channel, const std } } +void Application::messagebox(const char *text, const char *label1, const char *command1, const char *label2, const char *command2) +{ + std::string str_text(text ? text : "" ); + + std::string str_label1(label1 ? label1 : "" ); + std::string str_command1(command1 ? command1 : "" ); + + std::string str_label2(label2 ? label2 : "" ); + std::string str_command2(command2 ? command2 : "" ); + + notify_messagebox(str_text, str_label1, str_command1, str_label2, str_command2); +} + + +void Application::notify_messagebox(const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2) +{ + // the default implementation does nothing + // used by the client to show messageboxes +} + void Application::notify_loader(const std::string &message) { // the default implementation does nothing. - // used by the client to udpate the loader screen + // used by the client to update the loader screen } void Application::notify_zonechange() diff --git a/src/core/application.h b/src/core/application.h index 3f99187..da722db 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -82,6 +82,12 @@ public: /// loading message notification virtual void notify_loader(const std::string &message); + + /// messagebox notifications + virtual void notify_messagebox(const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2); + + /// messagebox notifications + void messagebox(const char *text, const char *label1 = 0, const char *command1 = 0, const char *label2 = 0, const char *command2 = 0); /// connect notification virtual void notify_connect(); diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index 544f660..37c486c 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -472,6 +472,25 @@ void GameServer::broadcast_sound(const std::string name, Player *ignore_player) } } +// server sends a messagebox to a single player +void GameServer::messagebox(Player *player, const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2) +{ + if (!text.size()) { + return; + } + + NetClient *client = player->client(); + + if (client) { + // this player is a network client, send message over network + server_network->send_messagebox(client, text, label1, command1, label2, command2); + + } else if (player == localplayer()) { + // local player, send message to the local application + application()->notify_messagebox(text, label1, command1, label2, command2); + } +} + // execute a command for a remote player void GameServer::exec(Player *player, std::string const & cmdline) { diff --git a/src/core/gameserver.h b/src/core/gameserver.h index 4f25a9b..3e9c50a 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -80,7 +80,8 @@ public: /// broadcast a sound to all players void broadcast_sound(std::string const sound, Player *ignore_player = 0); - + /// send a messagebox to a single player + void messagebox(Player *player, const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2); /// a player sends a command to the game server void exec(Player *player, std::string const &cmdline); diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index 4892792..ee3e754 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -56,8 +56,13 @@ void NetConnection::connect(std::string const &to_host, int to_port) struct hostent *serverhostent; serverhostent = gethostbyname(to_host.c_str()); if (!serverhostent) { - con_warn << "Could not resolve '" << to_host.c_str() << "'" << std::endl; + std::ostringstream str; + str << "Could not resolve hostname'" << to_host.c_str() << "'"; + con_error << str.str() << std::endl; + abort(); + + application()->messagebox(str.str().c_str()); return; } @@ -76,8 +81,13 @@ void NetConnection::connect(std::string const &to_host, int to_port) server_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*((struct in_addr *)serverhostent->h_addr))); memset(server_addr.sin_zero, '\0', sizeof server_addr.sin_zero); if (server_addr.sin_addr.s_addr == INADDR_NONE) { - con_error << "Network invalid address " << to_host << "!" << std::endl; + std::ostringstream str; + str << "Invalid address for '" << to_host << "'"; + con_error << str.str() << std::endl; + abort(); + + application()->messagebox(str.str().c_str()); return; } @@ -156,12 +166,14 @@ void NetConnection::receive() if (bytes_received == 0) { con_print << "^BDisconnected." << std::endl; - abort(); + abort(); + application()->messagebox("Disconnected from server."); return; } else if (bytes_received < 0) { con_error << "Network receive() error!" << std::endl; //perror("recv"); abort(); + application()->messagebox("Disconnected from server."); return; } @@ -249,8 +261,9 @@ void NetConnection::frame() int nb = select(fd() + 1, &readset, NULL, NULL, &timeout); if (nb == 0) { if (connection_timeout + NETTIMEOUT < core::application()->time()) { - con_error << "Connection timeout!\n"; + con_error << "Connection timeout exceeded!\n"; abort(); + application()->messagebox("Connection timeout exceeded!"); } return; } @@ -649,6 +662,39 @@ void NetConnection::parse_incoming_message(const std::string & message) } } + } else if (command.compare("box") == 0) { + + // box "text" "label1" "cmd1" "label2" "cmd2" + char c; + + // read text + std::string text; + while ((msgstream.get(c)) && (c != '"')); + while ((msgstream.get(c)) && (c != '"')) + text += c; + + std::string label1; + while ((msgstream.get(c)) && (c != '"')); + while ((msgstream.get(c)) && (c != '"')) + label1 += c; + + std::string command1; + while ((msgstream.get(c)) && (c != '"')); + while ((msgstream.get(c)) && (c != '"')) + command1 += c; + + std::string label2; + while ((msgstream.get(c)) && (c != '"')); + while ((msgstream.get(c)) && (c != '"')) + label2 += c; + + std::string command2; + while ((msgstream.get(c)) && (c != '"')); + while ((msgstream.get(c)) && (c != '"')) + command2 += c; + + application()->notify_messagebox(text, label1, command1, label2, command2); + } else if (command.compare("connect") == 0) { if (connection_state == Pending) { diff --git a/src/core/netserver.cc b/src/core/netserver.cc index cbb2dc7..1b49aee 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -487,20 +487,23 @@ void NetServer::frame(unsigned long timestamp) * The following messages can be send to a client * * frame - * ent + * ent * die * inf * inv * ping - * sup + * rep + * sup * * msg * supported message channels are "info" "public" "rcon" and "snd" * "snd" is a special channel to transmit sound events + * box + * send a messagebox * zone */ -// send a message on a specified channel to a single client +// send a text message on a specified channel to a single client void NetServer::send_message(NetClient *client, const Message::Channel channel, const std::string & message) { if (!message.size()) @@ -538,15 +541,44 @@ void NetServer::send_message(NetClient *client, const Message::Channel channel, break; } + std::string str_message(message); + aux::strip_quotes(str_message); + std::string msg("msg "); msg.append(msg_channel); msg += ' '; - msg.append(message); + msg.append(str_message); msg += '\n'; client->send_raw(msg); } +// send a messagebox to a single client +void NetServer::send_messagebox(NetClient *client, const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2) +{ + std::string str_text(text); + aux::strip_quotes(str_text); + + std::string str_label1(label1); + aux::strip_quotes(str_label1); + std::string str_command1(command1); + aux::strip_quotes(str_command1); + + std::string str_label2(label2); + aux::strip_quotes(str_label2); + std::string str_command2(command2); + aux::strip_quotes(str_command2); + + std::ostringstream msg(""); + msg << "box "; + msg << "\"" << str_text << '\"' << ' '; + msg << "\"" << str_label1 << '\"' << ' '; + msg << "\"" << str_command1 << '\"' << ' '; + msg << "\"" << str_label2 << '\"' << ' '; + msg << "\"" << str_command2 << '\"' << '\n'; + + client->send_raw(msg.str()); +} // disconnect a client void NetServer::send_disconnect(NetClient *client) @@ -745,7 +777,6 @@ void NetServer::send_inventory_update(NetClient *client, Entity *entity, const u * info * req * inv - * */ void NetServer::parse_incoming_message(NetClient *client, const std::string & message) { @@ -779,6 +810,7 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me con_print << client->host() << ":" << client->port() << " " << netmsgstream.str() << std::endl; send_message(client, Message::Info, netmsgstream.str()); + send_messagebox(client, netmsgstream.str(), "", "", "", ""); send_disconnect(client); } else { diff --git a/src/core/netserver.h b/src/core/netserver.h index 7277866..95ada0d 100644 --- a/src/core/netserver.h +++ b/src/core/netserver.h @@ -18,7 +18,9 @@ namespace core { -/// network server +/** + * Handles server-side network messages + * */ class NetServer { public: @@ -72,6 +74,9 @@ public: /// send a message on a specified channel void send_message(NetClient *client, const Message::Channel channel, const std::string & message); + + /// send a messagebox to a single client + void send_messagebox(NetClient *client, const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2); protected: diff --git a/src/core/player.cc b/src/core/player.cc index 14767bd..7511e06 100644 --- a/src/core/player.cc +++ b/src/core/player.cc @@ -95,6 +95,19 @@ void Player::print() const << std::setfill('0') << std::setw(2) << time_wasted_seconds << std::endl; } +void Player::messagebox(const char *text, const char *label1, const char *command1, const char *label2, const char *command2) +{ + std::string str_text(text ? text : "" ); + + std::string str_label1(label1 ? label1 : "" ); + std::string str_command1(command1 ? command1 : "" ); + + std::string str_label2(label2 ? label2 : "" ); + std::string str_command2(command2 ? command2 : "" ); + + server()->messagebox(this, str_text, str_label1, str_command1, str_label2, str_command2); +} + void Player::message(Message::Channel channel, const std::string text) { server()->message(this, channel, text); diff --git a/src/core/player.h b/src/core/player.h index a9eb4b0..e68a219 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -200,7 +200,10 @@ public: } - /*----- server-side mesage functions ------------------------------ */ + /*----- server-side message functions ----------------------------- */ + + /// send a messagebox to the player + void messagebox(const char *text, const char *label1 = 0, const char *command1 = 0, const char *label2 = 0, const char *command2 = 0); /// send a message to the player on one of the message channels void message(core::Message::Channel channel, const std::string text); diff --git a/src/game/base/ship.cc b/src/game/base/ship.cc index 9a4218f..0dd5415 100644 --- a/src/game/base/ship.cc +++ b/src/game/base/ship.cc @@ -434,6 +434,7 @@ void Ship::explode() // make the player watch his death if (owner()->control() == this) { owner()->set_view(this); + owner()->messagebox("Your ship has been destroyed.", "Respawn", "respawn"); } // cargo loss % is set by the g_cargoloss CVar -- cgit v1.2.3