widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #02340
[Merge] lp:~widelands-dev/widelands/00_notification_framework into lp:widelands
SirVer has proposed merging lp:~widelands-dev/widelands/00_notification_framework into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/00_notification_framework/+merge/226975
Suggested commit message:
----
- Adds a new notifications library into src/notifications. This is a weakly coupled general purpose notification library that will help to reduce dependencies between the various Widelands libraries. Sending a note is cheap and defining one only requires a unique ID. I think this should be used for example to play sounds in the UI (Clicking a button will emit a 'Clicked' note, and library will play a sound for that). It also has tests.
- Removes the inheritance based logic/notifications.[h|cc] and ported all users to the new system. This removes a bunch of dicy multiple inheritance cases.
- Turns 'chat.[cc|h]' into a library. Also adds a wl_chat_ui library that can format chat messages. This needed some refactoring around how chat is handled.
----
--
https://code.launchpad.net/~widelands-dev/widelands/00_notification_framework/+merge/226975
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/00_notification_framework into lp:widelands.
=== modified file 'cmake/codecheck/rules/missing_padding'
--- cmake/codecheck/rules/missing_padding 2012-12-15 18:40:59 +0000
+++ cmake/codecheck/rules/missing_padding 2014-07-16 08:32:14 +0000
@@ -369,7 +369,6 @@
"b > a",
"AreaWatcher(const Player_Area<>);",
"void func (Player_Area<Area>);",
- "friend class NoteReceiver<T>;",
'a %= a;',
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2014-07-16 05:54:49 +0000
+++ src/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -65,6 +65,7 @@
add_subdirectory(ai)
add_subdirectory(base)
+add_subdirectory(chat)
add_subdirectory(economy)
add_subdirectory(editor)
add_subdirectory(game_io)
@@ -73,6 +74,7 @@
add_subdirectory(logic)
add_subdirectory(map_io)
add_subdirectory(network)
+add_subdirectory(notifications)
add_subdirectory(profile)
add_subdirectory(random)
add_subdirectory(scripting)
@@ -86,8 +88,6 @@
# library.
wl_library(widelands_ball_of_mud
SRCS
- chat.cc
- chat.h
interval.h
machdep.h
wlapplication.cc
@@ -109,7 +109,6 @@
logic
logic_game_controller
logic_game_settings
- logic_notification
map_io_map_loader
network
profile
=== modified file 'src/ai/CMakeLists.txt'
--- src/ai/CMakeLists.txt 2014-07-03 19:26:30 +0000
+++ src/ai/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -15,5 +15,4 @@
profile
base_i18n
logic
- logic_notification
)
=== modified file 'src/ai/computer_player.cc'
--- src/ai/computer_player.cc 2014-07-03 19:08:41 +0000
+++ src/ai/computer_player.cc 2014-07-16 08:32:14 +0000
@@ -27,6 +27,7 @@
{
}
+Computer_Player::~Computer_Player() {}
struct EmptyAI : Computer_Player {
EmptyAI(Widelands::Game & g, const Widelands::Player_Number pid)
=== modified file 'src/ai/computer_player.h'
--- src/ai/computer_player.h 2014-07-14 19:48:07 +0000
+++ src/ai/computer_player.h 2014-07-16 08:32:14 +0000
@@ -20,9 +20,15 @@
#ifndef WL_AI_COMPUTER_PLAYER_H
#define WL_AI_COMPUTER_PLAYER_H
+#include <string>
+#include <vector>
+
#include "base/macros.h"
-#include "logic/game.h"
-#include "logic/notification.h"
+#include "logic/widelands.h"
+
+namespace Widelands {
+class Game;
+} // namespace Widelands
/**
* The generic interface to AI instances, or "computer players".
@@ -30,17 +36,12 @@
* Instances of actual AI implementation can be created via the
* \ref Implementation interface.
*/
-struct Computer_Player :
- Widelands::NoteReceiver<Widelands::NoteImmovable>,
- Widelands::NoteReceiver<Widelands::NoteFieldPossession>
-{
+struct Computer_Player {
Computer_Player(Widelands::Game &, const Widelands::Player_Number);
+ virtual ~Computer_Player();
virtual void think () = 0;
- virtual void receive(const Widelands::NoteImmovable &) override {}
- virtual void receive(const Widelands::NoteFieldPossession &) override {}
-
Widelands::Game & game() const {return m_game;}
Widelands::Player_Number player_number() {return m_player_number;}
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2014-07-15 10:02:22 +0000
+++ src/ai/defaultai.cc 2014-07-16 08:32:14 +0000
@@ -87,6 +87,24 @@
military_under_constr_(0),
military_last_dismantle_(0),
military_last_build_(0) {
+
+ // Subscribe to NoteFieldPossession.
+ field_possession_subscriber_ =
+ Notifications::subscribe<NoteFieldPossession>([this](const NoteFieldPossession& note) {
+ if (note.ownership == NoteFieldPossession::Ownership::GAINED) {
+ unusable_fields.push_back(note.fc);
+ }
+ });
+
+ // Subscribe to NoteImmovables.
+ immovable_subscriber_ =
+ Notifications::subscribe<NoteImmovable>([this](const NoteImmovable& note) {
+ if (note.ownership == NoteImmovable::Ownership::GAINED) {
+ gain_immovable(*note.pi);
+ } else {
+ lose_immovable(*note.pi);
+ }
+ });
}
DefaultAI::~DefaultAI() {
@@ -198,21 +216,6 @@
}
}
-/// called by Widelands game engine when an immovable changed
-void DefaultAI::receive(const NoteImmovable& note) {
- if (note.lg == LOSE) {
-
- lose_immovable(*note.pi);
- } else
- gain_immovable(*note.pi);
-}
-
-/// called by Widelands game engine when a field changed
-void DefaultAI::receive(const NoteFieldPossession& note) {
- if (note.lg == GAIN)
- unusable_fields.push_back(note.fc);
-}
-
/**
* Cares for all variables not initialised during construction
*
@@ -221,8 +224,6 @@
*/
void DefaultAI::late_initialization() {
player = game().get_player(player_number());
- NoteReceiver<NoteImmovable>::connect(*player);
- NoteReceiver<NoteFieldPossession>::connect(*player);
tribe = &player->tribe();
log("ComputerPlayer(%d): initializing (%u)\n", player_number(), type);
Ware_Index const nr_wares = tribe->get_nrwares();
=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h 2014-07-05 16:41:51 +0000
+++ src/ai/defaultai.h 2014-07-16 08:32:14 +0000
@@ -21,10 +21,12 @@
#define WL_AI_DEFAULTAI_H
#include <map>
+#include <memory>
#include "ai/ai_help_structs.h"
#include "ai/computer_player.h"
#include "base/i18n.h"
+#include "logic/immovable.h"
namespace Widelands {
struct Road;
@@ -70,9 +72,6 @@
~DefaultAI();
virtual void think() override;
- virtual void receive(const Widelands::NoteImmovable&) override;
- virtual void receive(const Widelands::NoteFieldPossession&) override;
-
enum {
AGGRESSIVE = 2,
NORMAL = 1,
@@ -209,6 +208,9 @@
uint16_t military_last_dismantle_;
int32_t military_last_build_; // sometimes expansions just stops, this is time of last military
// building build
+
+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteFieldPossession>> field_possession_subscriber_;
+ std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_;
};
#endif // end of include guard: WL_AI_DEFAULTAI_H
=== added directory 'src/chat'
=== added file 'src/chat/CMakeLists.txt'
--- src/chat/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/chat/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -0,0 +1,7 @@
+wl_library(chat
+ SRCS
+ chat.h
+ chat.cc
+ DEPENDS
+ notifications
+)
=== renamed file 'src/chat.cc' => 'src/chat/chat.cc'
--- src/chat.cc 2014-07-05 14:22:44 +0000
+++ src/chat/chat.cc 2014-07-16 08:32:14 +0000
@@ -17,221 +17,6 @@
*
*/
-#include "chat.h"
-
-#include "logic/constants.h"
-#include "logic/player.h"
-
-using namespace Widelands;
-
-std::string ChatMessage::toOldRichText() const
-{
- std::string message = "<p font-color=#33ff33 font-size=9>";
-
- // Escape richtext characters
- // The goal of this code is two-fold:
- // 1. Assuming an honest game host, we want to prevent the ability of
- // clients to use richtext.
- // 2. Assuming a malicious host or meta server, we want to reduce the
- // likelihood that a bug in the richtext renderer can be exploited,
- // by restricting the set of allowed richtext commands.
- // Most notably, images are not allowed in richtext at all.
- //
- // Note that we do want host and meta server to send some richtext code,
- // as the ability to send formatted commands is nice for the usability
- // of meta server and dedicated servers, so we're treading a bit of a
- // fine line here.
- std::string sanitized;
- for (std::string::size_type pos = 0; pos < msg.size(); ++pos) {
- if (msg[pos] == '<') {
- if (playern < 0) {
- static const std::string good1 = "</p><p";
- static const std::string good2 = "<br>";
- if (!msg.compare(pos, good1.size(), good1)) {
- std::string::size_type nextclose = msg.find('>', pos + good1.size());
- if
- (nextclose != std::string::npos &&
- (nextclose == pos + good1.size() || msg[pos + good1.size()] == ' '))
- {
- sanitized += good1;
- pos += good1.size() - 1;
- continue;
- }
- } else if (!msg.compare(pos, good2.size(), good2)) {
- sanitized += good2;
- pos += good2.size() - 1;
- continue;
- }
- }
-
- sanitized += "<";
- } else {
- sanitized += msg[pos];
- }
- }
-
- // time calculation
- char ts[13];
- strftime(ts, sizeof(ts), "[%H:%M] </p>", localtime(&time));
- message += ts;
-
- message += "<p font-size=14 font-face=DejaVuSerif font-color=#";
- message += color();
-
- if (recipient.size() && sender.size()) {
- // Personal message handling
- if (sanitized.compare(0, 3, "/me")) {
- message += " font-decoration=underline>";
- message += sender;
- message += " @ ";
- message += recipient;
- message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
- message += sanitized;
- } else {
- message += ">@";
- message += recipient;
- message += " >> </p><p font-size=14";
- message += " font-face=DejaVuSerif font-color=#";
- message += color();
- message += " font-style=italic> ";
- message += sender;
- message += sanitized.substr(3);
- }
- } else {
- // Normal messages handling
- if (not sanitized.compare(0, 3, "/me")) {
- message += " font-style=italic>-> ";
- if (sender.size())
- message += sender;
- else
- message += "***";
- message += sanitized.substr(3);
- } else if (sender.size()) {
- message += " font-decoration=underline>";
- message += sender;
- message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
- message += sanitized;
- } else {
- message += " font-weight=bold>*** ";
- message += sanitized;
- }
- }
-
- // return the formated message
- return message + "<br></p>";
-}
-
-std::string ChatMessage::toPrintable() const
-{
- std::string message = "<p><font color=33ff33 size=9>";
-
- // Escape richtext characters
- // The goal of this code is two-fold:
- // 1. Assuming an honest game host, we want to prevent the ability of
- // clients to use richtext.
- // 2. Assuming a malicious host or meta server, we want to reduce the
- // likelihood that a bug in the richtext renderer can be exploited,
- // by restricting the set of allowed richtext commands.
- // Most notably, images are not allowed in richtext at all.
- //
- // Note that we do want host and meta server to send some richtext code,
- // as the ability to send formatted commands is nice for the usability
- // of meta server and dedicated servers, so we're treading a bit of a
- // fine line here.
- std::string sanitized;
- for (std::string::size_type pos = 0; pos < msg.size(); ++pos) {
- if (msg[pos] == '<') {
- if (playern < 0) {
- static const std::string good1 = "</p><p";
- static const std::string good2 = "<br>";
- if (!msg.compare(pos, good1.size(), good1)) {
- std::string::size_type nextclose = msg.find('>', pos + good1.size());
- if
- (nextclose != std::string::npos &&
- (nextclose == pos + good1.size() || msg[pos + good1.size()] == ' '))
- {
- sanitized += good1;
- pos += good1.size() - 1;
- continue;
- }
- } else if (!msg.compare(pos, good2.size(), good2)) {
- sanitized += good2;
- pos += good2.size() - 1;
- continue;
- }
- }
-
- sanitized += "\\<";
- } else {
- sanitized += msg[pos];
- }
- }
-
- // time calculation
- char ts[13];
- strftime(ts, sizeof(ts), "[%H:%M] ", localtime(&time));
- message += ts;
-
- message += "</font><font size=14 face=DejaVuSerif color=";
- message += color();
-
- if (recipient.size() && sender.size()) {
- // Personal message handling
- if (sanitized.compare(0, 3, "/me")) {
- message += " bold=1>";
- message += sender;
- message += " @ ";
- message += recipient;
- message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
- message += sanitized;
- } else {
- message += ">@";
- message += recipient;
- message += " \\> </font><font size=14";
- message += " face=DejaVuSerif color=";
- message += color();
- message += " italic=1 shadow=1> ";
- message += sender;
- message += sanitized.substr(3);
- }
- } else {
- // Normal messages handling
- if (not sanitized.compare(0, 3, "/me")) {
- message += " italic=1>-\\> ";
- if (sender.size())
- message += sender;
- else
- message += "***";
- message += sanitized.substr(3);
- } else if (sender.size()) {
- message += " bold=1>";
- message += sender;
- message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
- message += sanitized;
- } else {
- message += " bold=1>*** ";
- message += sanitized;
- }
- }
-
- // return the formated message
- return message + "</font><br></p>";
-}
-
-
-
-std::string ChatMessage::toPlainString() const
-{
- return sender + ": " + msg;
-}
-
-std::string ChatMessage::color() const
-{
- if ((playern >= 0) && playern < MAX_PLAYERS) {
- const RGBColor & clr = Player::Colors[playern];
- char buf[sizeof("ffffff")];
- snprintf(buf, sizeof(buf), "%.2x%.2x%.2x", clr.r, clr.g, clr.b);
- return buf;
- }
- return "999999";
-}
+#include "chat/chat.h"
+
+ChatProvider::~ChatProvider() {}
=== renamed file 'src/chat.h' => 'src/chat/chat.h'
--- src/chat.h 2014-07-05 16:41:51 +0000
+++ src/chat/chat.h 2014-07-16 08:32:14 +0000
@@ -17,110 +17,64 @@
*
*/
-#ifndef WL_CHAT_H
-#define WL_CHAT_H
+#ifndef WL_CHAT_CHAT_H
+#define WL_CHAT_CHAT_H
#include <ctime>
#include <string>
-
-#include "logic/notification.h"
-
-/**
- * Represents one chat message.
- */
+#include <vector>
+
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
+
+// A chat message as received in game.
struct ChatMessage {
- /**
- * The (real-)time at which the message was received.
- */
+ CAN_BE_SEND_AS_NOTE(NoteId::ChatMessage)
+
+ // The (real-)time at which the message was received.
time_t time;
- /**
- * The playercolor. Used to colorize the senders name;
- * negative numbers indicate system messages for which richtext is
- * allowed.
- */
+ // The playercolor. Used to colorize the senders name; negative numbers
+ // indicate system messages for which richtext is allowed.
int16_t playern;
- /**
- * A string identifying the sender of the message.
- *
- * This string is empty for system-generated messages.
- *
- * \note This is a string instead of an ID because the backlog of
- * chat messages might contain chat from a player who has since left
- * the game.
- */
+ // A string identifying the sender of the message.
+ // This string is empty for system-generated messages.
+ // This is a string instead of an ID because the backlog of
+ // chat messages might contain chat from a player who has since left
+ // the game.
std::string sender;
- /**
- * A string identifying the recipient of the message.
- *
- * This string should only be filled for personal messages.
- *
- * \note This is a string instead of an ID because the backlog of
- * chat messages might contain chat from a player who has since left
- * the game.
- */
+ // A string identifying the recipient of the message. This string should
+ // only be filled for personal messages. This is a string instead of an ID
+ // because the backlog of chat messages might contain chat from a player who
+ // has since left the game.
std::string recipient;
- /**
- * The actual chat message
- */
+ // The actual chat message
std::string msg;
-
-
- /**
- * \return a richtext string that can be displayed to the user.
- */
- std::string toPrintable() const;
- std::string toOldRichText() const;
-
- /**
- * \return a plain string containing the sender + message.
- */
- std::string toPlainString() const;
-
-
- /**
- * \returns the color of the sender
- */
- std::string color() const;
};
-
-/**
- * Provides the chatting interface during a game.
- *
- * Use this interface to send chat messages and to access the list of
- * received chat messages. Note that this class is a
- * Widelands::NoteSender<ChatMessage> and sends a notification every
- * time a new message is received.
- */
-struct ChatProvider : public Widelands::NoteSender<ChatMessage> {
- virtual ~ChatProvider() {}
-
- /**
- * Send the given chat message.
- *
- * The message may or may not appear in subsequent calls to \ref getMessages.
- */
+// Sends chat messages and owns the list of received chat messages.
+// Base classes must broadcast a ChatMessage as notification when a
+// new message is received.
+struct ChatProvider {
+ virtual ~ChatProvider();
+
+ // Send the given chat message. The message may or may not
+ // appear in subsequent calls to \ref getMessages.
virtual void send(const std::string &) = 0;
- /**
- * \return a (chronological) list of received chat messages.
- * This list need not be stable or monotonic. In other words,
- * subsequent calls to this functions may return a smaller or
- * greater number of chat messages.
- */
- virtual const std::vector<ChatMessage> & getMessages() const = 0;
+ // \return a (chronological) list of received chat messages.
+ // This list need not be stable or monotonic. In other words,
+ // subsequent calls to this functions may return a smaller or
+ // greater number of chat messages.
+ virtual const std::vector<ChatMessage>& getMessages() const = 0;
// reimplemented e.g. in internet_gaming to silence the chat if in game.
+ // TODO(sirver): this does not belong here. The receiver of the
+ // notifications should deal with this.
virtual bool sound_off() {return false;}
-
-protected:
- void send(const ChatMessage & c) {
- Widelands::NoteSender<ChatMessage>::send(c);
- }
};
-#endif // end of include guard: WL_CHAT_H
+#endif // end of include guard:
=== modified file 'src/economy/router.h'
--- src/economy/router.h 2014-07-05 16:41:51 +0000
+++ src/economy/router.h 2014-07-16 08:32:14 +0000
@@ -27,8 +27,8 @@
#include "logic/wareworker.h"
namespace Widelands {
+class ITransportCostCalculator;
struct IRoute;
-struct ITransportCostCalculator;
struct RoutingNode;
/**
=== modified file 'src/editor/map_generator.cc'
--- src/editor/map_generator.cc 2014-07-03 19:08:41 +0000
+++ src/editor/map_generator.cc 2014-07-16 08:32:14 +0000
@@ -414,8 +414,7 @@
delete[] values;
throw;
}
-
- return nullptr; // Should not be reached
+ // Never here.
}
=== modified file 'src/io/CMakeLists.txt'
--- src/io/CMakeLists.txt 2014-07-14 10:45:44 +0000
+++ src/io/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -54,7 +54,7 @@
DEPENDS
base_i18n
base_log
+ chat
io_fileread
io_filesystem
- widelands_ball_of_mud
)
=== modified file 'src/io/dedicated_log.h'
--- src/io/dedicated_log.h 2014-07-05 16:41:51 +0000
+++ src/io/dedicated_log.h 2014-07-16 08:32:14 +0000
@@ -21,7 +21,7 @@
#define WL_IO_DEDICATED_LOG_H
#include "base/log.h"
-#include "chat.h"
+#include "chat/chat.h"
#include "io/filesystem/disk_filesystem.h"
#include "io/filewrite.h"
=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt 2014-07-14 10:45:44 +0000
+++ src/logic/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -1,12 +1,3 @@
-wl_library(logic_notification
- SRCS
- notification.h
- notification.cc
- DEPENDS
- logic_widelands_geometry
-)
-
-
wl_library(logic_widelands_geometry
SRCS
widelands_geometry.cc
@@ -229,11 +220,11 @@
io_stream
logic_game_controller
logic_game_settings
- logic_notification
logic_widelands_geometry
map_io
map_io_map_loader
network
+ notifications
profile
random
scripting
=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc 2014-07-05 14:22:44 +0000
+++ src/logic/editor_game_base.cc 2014-07-16 08:32:14 +0000
@@ -122,25 +122,6 @@
return world_.get();
}
-void Editor_Game_Base::receive(const NoteImmovable & note)
-{
- note.pi->owner().receive(note);
-}
-
-void Editor_Game_Base::receive(const NoteFieldPossession & note)
-{
- get_player(note.fc.field->get_owned_by())->receive(note);
-}
-
-void Editor_Game_Base::receive(const NoteFieldTransformed & note)
-{
- Widelands::Map_Index const i = note.fc.field - &(*map_)[0];
-
- iterate_players_existing(p, map_->get_nrplayers(), *this, plr)
- if (plr->vision(i) > 1) // player currently sees field?
- plr->rediscover_node(*map_, (*map_)[0], note.fc);
-}
-
Interactive_GameBase* Editor_Game_Base::get_igbase()
{
return dynamic_cast<Interactive_GameBase *>(get_ibase());
@@ -237,8 +218,6 @@
delete map_;
map_ = new_map;
-
- NoteReceiver<NoteFieldTransformed>::connect(*map_);
}
@@ -269,7 +248,7 @@
if
(pid <= MAX_PLAYERS
- or
+ ||
not dynamic_cast<const Game *>(this))
{ // if this is editor, load the tribe anyways
// the tribe is used, postload it
@@ -533,12 +512,12 @@
assert(&first_field <= f.field);
assert (f.field < &first_field + m.max_index());
assert
- (direction == Road_SouthWest or
- direction == Road_SouthEast or
+ (direction == Road_SouthWest ||
+ direction == Road_SouthEast ||
direction == Road_East);
assert
- (roadtype == Road_None or roadtype == Road_Normal or
- roadtype == Road_Busy or roadtype == Road_Water);
+ (roadtype == Road_None || roadtype == Road_Normal ||
+ roadtype == Road_Busy || roadtype == Road_Water);
if (f.field->get_road(direction) == roadtype)
return;
@@ -571,7 +550,7 @@
Player::Field & player_field = (&first_player_field)[i];
if
(1 < player_field .vision
- or
+ ||
1 < (&first_player_field)[neighbour_i].vision)
{
player_field.roads &= ~mask;
@@ -641,6 +620,31 @@
cleanup_playerimmovables_area(player_area);
}
+void Editor_Game_Base::change_field_owner(const FCoords& fc, Player_Number const new_owner) {
+ const Field & first_field = map()[0];
+
+ Player_Number const old_owner = fc.field->get_owned_by();
+ if (old_owner == new_owner) {
+ return;
+ }
+
+ if (old_owner) {
+ Notifications::publish(
+ NoteFieldPossession(fc, NoteFieldPossession::Ownership::LOST, get_player(old_owner)));
+ }
+
+ fc.field->set_owned_by(new_owner);
+
+ // TODO: the player should do this when it gets the NoteFieldPossession.
+ // This means also sending a note when new_player = 0, i.e. the field is no
+ // longer owned.
+ inform_players_about_ownership(fc.field - &first_field, new_owner);
+
+ if (new_owner) {
+ Notifications::publish(
+ NoteFieldPossession(fc, NoteFieldPossession::Ownership::GAINED, get_player(new_owner)));
+ }
+}
void Editor_Game_Base::conquer_area_no_building
(Player_Area<Area<FCoords> > player_area)
@@ -656,15 +660,7 @@
assert (player_area.player_number <= map().get_nrplayers());
MapRegion<Area<FCoords> > mr(map(), player_area);
do {
- Player_Number const owner = mr.location().field->get_owned_by();
- if (owner != player_area.player_number) {
- if (owner)
- receive(NoteFieldPossession(mr.location(), LOSE));
- mr.location().field->set_owned_by(player_area.player_number);
- inform_players_about_ownership
- (mr.location().field - &first_field, player_area.player_number);
- receive (NoteFieldPossession(mr.location(), GAIN));
- }
+ change_field_owner(mr.location(), player_area.player_number);
} while (mr.advance(map()));
// This must reach one step beyond the conquered area to adjust the borders
@@ -699,7 +695,7 @@
assert (player_area.player_number <= map().get_nrplayers());
assert (preferred_player <= map().get_nrplayers());
assert(preferred_player != player_area.player_number);
- assert(not conquer or not preferred_player);
+ assert(not conquer || not preferred_player);
Player & conquering_player = player(player_area.player_number);
MapRegion<Area<FCoords> > mr(map(), player_area);
do {
@@ -711,33 +707,22 @@
Player_Number const owner = mr.location().field->get_owned_by();
if (conquer) {
// adds the influence
- Military_Influence new_influence_modified =
- conquering_player.military_influence(index) += influence;
- if (owner and not conquer_guarded_location_by_superior_influence)
+ Military_Influence new_influence_modified = conquering_player.military_influence(index) +=
+ influence;
+ if (owner && not conquer_guarded_location_by_superior_influence)
new_influence_modified = 1;
- if
- (not owner
- or
- player(owner).military_influence(index) < new_influence_modified)
- {
- if (owner)
- receive(NoteFieldPossession(mr.location(), LOSE));
- mr.location().field->set_owned_by(player_area.player_number);
- inform_players_about_ownership(index, player_area.player_number);
- receive (NoteFieldPossession(mr.location(), GAIN));
+ if (!owner || player(owner).military_influence(index) < new_influence_modified) {
+ change_field_owner(mr.location(), player_area.player_number);
}
- } else if
- (not (conquering_player.military_influence(index) -= influence)
- and
- owner == player_area.player_number)
- {
+ } else if (not(conquering_player.military_influence(index) -= influence) &&
+ owner == player_area.player_number) {
// The player completely lost influence over the location, which he
// owned. Now we must see if some other player has influence and if
// so, transfer the ownership to that player.
Player_Number best_player;
if
(preferred_player
- and
+ &&
player(preferred_player).military_influence(index))
best_player = preferred_player;
else {
@@ -761,17 +746,13 @@
}
}
if (best_player != player_area.player_number) {
- receive (NoteFieldPossession(mr.location(), LOSE));
- mr.location().field->set_owned_by (best_player);
- inform_players_about_ownership(index, best_player);
- if (best_player)
- receive (NoteFieldPossession(mr.location(), GAIN));
+ change_field_owner(mr.location(), best_player);
}
}
} while (mr.advance(map()));
- // This must reach one step beyond the conquered area to adjust the borders
- // of neighbour players.
+ // This must reach one step beyond the conquered area to adjust the borders
+ // of neighbour players.
++player_area.radius;
map().recalc_for_field_area(world(), player_area);
}
=== modified file 'src/logic/editor_game_base.h'
--- src/logic/editor_game_base.h 2014-07-15 05:12:37 +0000
+++ src/logic/editor_game_base.h 2014-07-16 08:32:14 +0000
@@ -29,8 +29,8 @@
#include "logic/bob.h"
#include "logic/building.h"
#include "logic/map.h"
-#include "logic/notification.h"
#include "logic/player_area.h"
+#include "notifications/notifications.h"
namespace UI {struct ProgressWindow;}
struct Fullscreen_Menu_LaunchGame;
@@ -53,98 +53,128 @@
struct Flag;
struct AttackController;
-class Editor_Game_Base :
- NoteReceiver<NoteImmovable>,
- NoteReceiver<NoteFieldPossession>,
- NoteReceiver<NoteFieldTransformed>
-{
+struct NoteFieldPossession {
+ CAN_BE_SEND_AS_NOTE(NoteId::FieldPossession)
+
+ // Has this been lost or gained?
+ enum class Ownership {LOST, GAINED};
+ Ownership ownership;
+
+ // The field that has been lost/gained.
+ FCoords fc;
+
+ // The player that has lost or gained this field.
+ Player * player;
+
+ NoteFieldPossession(const FCoords& init_fc, Ownership const init_ownership, Player* init_player)
+ : ownership(init_ownership), fc(init_fc), player(init_player) {
+ }
+};
+
+class Editor_Game_Base {
public:
friend class Interactive_Base;
friend struct Fullscreen_Menu_LaunchGame;
friend struct Game_Game_Class_Data_Packet;
- Editor_Game_Base(LuaInterface * lua);
+ Editor_Game_Base(LuaInterface* lua);
virtual ~Editor_Game_Base();
- void set_map(Map *);
- Map & map() const {return *map_;}
- Map * get_map() {return map_;}
- Map & get_map() const {return *map_;}
- const Object_Manager & objects() const {return objects_;}
- Object_Manager & objects() {return objects_;}
+ void set_map(Map*);
+ // TODO(sirver): this should just be const Map& map() and Map* mutable_map().
+ Map& map() const {
+ return *map_;
+ }
+ Map* get_map() {
+ return map_;
+ }
+ Map& get_map() const {
+ return *map_;
+ }
+ const Object_Manager& objects() const {
+ return objects_;
+ }
+ Object_Manager& objects() {
+ return objects_;
+ }
// logic handler func
virtual void think();
// Player commands
void remove_player(Player_Number);
- Player * add_player
- (Player_Number,
- uint8_t initialization_index,
- const std::string & tribe,
- const std::string & name,
- TeamNumber team = 0);
- Player * get_player(int32_t n) const;
- Player & player(int32_t n) const;
- virtual Player * get_safe_player(Player_Number);
+ Player* add_player(Player_Number,
+ uint8_t initialization_index,
+ const std::string& tribe,
+ const std::string& name,
+ TeamNumber team = 0);
+ Player* get_player(int32_t n) const;
+ Player& player(int32_t n) const;
+ virtual Player* get_safe_player(Player_Number);
// loading stuff
void allocate_player_maps();
virtual void postload();
- void load_graphics(UI::ProgressWindow & loader_ui);
+ void load_graphics(UI::ProgressWindow& loader_ui);
virtual void cleanup_for_load();
void set_road(FCoords, uint8_t direction, uint8_t roadtype);
// warping stuff. instantly creating map_objects
- Building & warp_building
- (Coords, Player_Number, Building_Index,
- Building::FormerBuildings former_buildings = Building::FormerBuildings());
- Building & warp_constructionsite
- (Coords, Player_Number, Building_Index, bool loading = false,
- Building::FormerBuildings former_buildings = Building::FormerBuildings());
- Building & warp_dismantlesite
- (Coords, Player_Number, bool loading = false,
- Building::FormerBuildings former_buildings = Building::FormerBuildings());
- Bob & create_bob(Coords, const BobDescr &, Player * owner = nullptr);
- Bob & create_bob
- (Coords, int, Tribe_Descr const * const = nullptr, Player * owner = nullptr);
- Bob & create_bob
- (Coords, const std::string & name, Tribe_Descr const * const = nullptr, Player * owner = nullptr);
- Immovable & create_immovable(Coords, uint32_t idx, Tribe_Descr const *);
- Immovable & create_immovable
- (Coords, const std::string & name, Tribe_Descr const *);
+ Building&
+ warp_building(Coords,
+ Player_Number,
+ Building_Index,
+ Building::FormerBuildings former_buildings = Building::FormerBuildings());
+ Building&
+ warp_constructionsite(Coords,
+ Player_Number,
+ Building_Index,
+ bool loading = false,
+ Building::FormerBuildings former_buildings = Building::FormerBuildings());
+ Building&
+ warp_dismantlesite(Coords,
+ Player_Number,
+ bool loading = false,
+ Building::FormerBuildings former_buildings = Building::FormerBuildings());
+ Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr);
+ Bob& create_bob(Coords, int, Tribe_Descr const* const = nullptr, Player* owner = nullptr);
+ Bob& create_bob(Coords,
+ const std::string& name,
+ Tribe_Descr const* const = nullptr,
+ Player* owner = nullptr);
+ Immovable& create_immovable(Coords, uint32_t idx, Tribe_Descr const*);
+ Immovable& create_immovable(Coords, const std::string& name, Tribe_Descr const*);
- int32_t get_gametime() const {return gametime_;}
- Interactive_Base * get_ibase() const {return ibase_;}
+ int32_t get_gametime() const {
+ return gametime_;
+ }
+ Interactive_Base* get_ibase() const {
+ return ibase_;
+ }
// safe system for storing pointers to non-Map_Object C++ objects
// unlike objects in the Object_Manager, these pointers need not be
// synchronized across the network, and they are not saved in savegames
- uint32_t add_trackpointer(void *);
- void * get_trackpointer(uint32_t serial);
+ uint32_t add_trackpointer(void*);
+ void* get_trackpointer(uint32_t serial);
void remove_trackpointer(uint32_t serial);
// Manually load a tribe into memory. Used by the editor
- const Tribe_Descr & manually_load_tribe(const std::string & tribe);
- const Tribe_Descr & manually_load_tribe(Player_Number const p) {
+ const Tribe_Descr& manually_load_tribe(const std::string& tribe);
+ const Tribe_Descr& manually_load_tribe(Player_Number const p) {
return manually_load_tribe(map().get_scenario_player_tribe(p));
}
// Get a tribe from the loaded list, when known or nullptr.
- Tribe_Descr const * get_tribe(const std::string & name) const;
+ Tribe_Descr const* get_tribe(const std::string& name) const;
void inform_players_about_ownership(Map_Index, Player_Number);
- void inform_players_about_immovable(Map_Index, Map_Object_Descr const *);
- void inform_players_about_road (FCoords, Map_Object_Descr const *);
-
- void unconquer_area
- (Player_Area<Area<FCoords> >, Player_Number destroying_player = 0);
- void conquer_area (Player_Area<Area<FCoords> >);
- void conquer_area_no_building(Player_Area<Area<FCoords> > const);
-
- void receive(const NoteImmovable &) override;
- void receive(const NoteFieldPossession &) override;
- void receive(const NoteFieldTransformed &) override;
+ void inform_players_about_immovable(Map_Index, Map_Object_Descr const*);
+ void inform_players_about_road(FCoords, Map_Object_Descr const*);
+
+ void unconquer_area(Player_Area<Area<FCoords>>, Player_Number destroying_player = 0);
+ void conquer_area(Player_Area<Area<FCoords>>);
+ void conquer_area_no_building(Player_Area<Area<FCoords>> const);
void cleanup_objects() {
objects().cleanup(*this);
@@ -152,15 +182,23 @@
// next function is used to update the current gametime,
// for queue runs e.g.
- int32_t & get_game_time_pointer() {return gametime_;}
- void set_ibase(Interactive_Base * const b) {ibase_ = b;}
+ int32_t& get_game_time_pointer() {
+ return gametime_;
+ }
+ void set_ibase(Interactive_Base* const b) {
+ ibase_ = b;
+ }
/// Lua frontend, used to run Lua scripts
- LuaInterface & lua() {return *lua_;}
-
- Players_Manager* player_manager() {return player_manager_.get();}
-
- Interactive_GameBase * get_igbase();
+ LuaInterface& lua() {
+ return *lua_;
+ }
+
+ Players_Manager* player_manager() {
+ return player_manager_.get();
+ }
+
+ Interactive_GameBase* get_igbase();
// Returns the world.
const World& world() const;
@@ -168,21 +206,6 @@
// Returns the world that can be modified. Prefer world() whenever possible.
World* mutable_world();
-private:
- // FIXME -- SDL returns time as uint32. Why do I have int32 ? Please comment or change this to uint32.
- int32_t gametime_;
- Object_Manager objects_;
-
- std::unique_ptr<LuaInterface> lua_;
- std::unique_ptr<Players_Manager> player_manager_;
-
- std::unique_ptr<World> world_;
- Interactive_Base* ibase_;
- Map* map_;
-
- uint32_t lasttrackserial_;
- std::map<uint32_t, void*> trackpointers_;
-
protected:
typedef std::vector<Tribe_Descr*> Tribe_Vector;
Tribe_Vector tribes_;
@@ -212,30 +235,47 @@
/// attacking) conquer a location even if another player already owns and
/// covers the location with a militarysite, if the conquering player's
/// influence becomes greater than the owner's influence.
- virtual void do_conquer_area
- (Player_Area<Area<FCoords> > player_area,
- bool conquer,
- Player_Number preferred_player = 0,
- bool neutral_when_no_influence = false,
- bool neutral_when_competing_influence = false,
- bool conquer_guarded_location_by_superior_influence = false);
- void cleanup_playerimmovables_area(Player_Area<Area<FCoords> >);
-
- DISALLOW_COPY_AND_ASSIGN(Editor_Game_Base);
-};
-
-#define iterate_players_existing(p, nr_players, egbase, player) \
- iterate_player_numbers(p, nr_players) \
- if (Widelands::Player * const player = (egbase).get_player(p)) \
-
-#define iterate_players_existing_novar(p, nr_players, egbase) \
- iterate_player_numbers(p, nr_players) \
- if ((egbase).get_player(p)) \
-
-#define iterate_players_existing_const(p, nr_players, egbase, player) \
- iterate_player_numbers(p, nr_players) \
- if (Widelands::Player const * const player = (egbase).get_player(p)) \
-
+ virtual void do_conquer_area(Player_Area<Area<FCoords>> player_area,
+ bool conquer,
+ Player_Number preferred_player = 0,
+ bool neutral_when_no_influence = false,
+ bool neutral_when_competing_influence = false,
+ bool conquer_guarded_location_by_superior_influence = false);
+ void cleanup_playerimmovables_area(Player_Area<Area<FCoords>>);
+
+ // Changes the owner of 'fc' from the current player to the new player and
+ // sends notifications about this.
+ void change_field_owner(const FCoords& fc, Player_Number new_owner);
+
+ // FIXME -- SDL returns time as uint32. Why do I have int32 ? Please comment or change this to
+ // uint32.
+ int32_t gametime_;
+ Object_Manager objects_;
+
+ std::unique_ptr<LuaInterface> lua_;
+ std::unique_ptr<Players_Manager> player_manager_;
+
+ std::unique_ptr<World> world_;
+ Interactive_Base* ibase_;
+ Map* map_;
+
+ uint32_t lasttrackserial_;
+ std::map<uint32_t, void*> trackpointers_;
+
+
+ DISALLOW_COPY_AND_ASSIGN(Editor_Game_Base);
+ };
+
+#define iterate_players_existing(p, nr_players, egbase, player) \
+ iterate_player_numbers( \
+ p, nr_players) if (Widelands::Player* const player = (egbase).get_player(p))
+
+#define iterate_players_existing_novar(p, nr_players, egbase) \
+ iterate_player_numbers(p, nr_players) if ((egbase).get_player(p))
+
+#define iterate_players_existing_const(p, nr_players, egbase, player) \
+ iterate_player_numbers( \
+ p, nr_players) if (Widelands::Player const* const player = (egbase).get_player(p))
}
#endif // end of include guard: WL_LOGIC_EDITOR_GAME_BASE_H
=== modified file 'src/logic/immovable.cc'
--- src/logic/immovable.cc 2014-07-15 10:02:22 +0000
+++ src/logic/immovable.cc 2014-07-16 08:32:14 +0000
@@ -50,6 +50,7 @@
#include "logic/worker.h"
#include "logic/world/world.h"
#include "map_io/one_world_legacy_lookup_table.h"
+#include "notifications/notifications.h"
#include "profile/profile.h"
#include "scripting/lua_table.h"
#include "sound/sound_handler.h"
@@ -1388,9 +1389,11 @@
* Set the immovable's owner. Currently, it can only be set once.
*/
void PlayerImmovable::set_owner(Player * const new_owner) {
+ assert(m_owner == nullptr);
+
m_owner = new_owner;
- m_owner->egbase().receive(NoteImmovable(this, GAIN));
+ Notifications::publish(NoteImmovable(this, NoteImmovable::Ownership::GAINED));
}
/**
@@ -1409,8 +1412,7 @@
while (!m_workers.empty())
m_workers[0]->set_location(nullptr);
- if (m_owner)
- m_owner->egbase().receive(NoteImmovable(this, LOSE));
+ Notifications::publish(NoteImmovable(this, NoteImmovable::Ownership::LOST));
BaseImmovable::cleanup(egbase);
}
=== modified file 'src/logic/immovable.h'
--- src/logic/immovable.h 2014-07-15 10:02:22 +0000
+++ src/logic/immovable.h 2014-07-16 08:32:14 +0000
@@ -26,6 +26,8 @@
#include "logic/buildcost.h"
#include "logic/instances.h"
#include "logic/widelands_geometry.h"
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
class LuaTable;
class OneWorldLegacyLookupTable;
@@ -40,8 +42,22 @@
class Worker;
class World;
struct Flag;
+struct PlayerImmovable;
struct Tribe_Descr;
+struct NoteImmovable {
+ CAN_BE_SEND_AS_NOTE(NoteId::Immovable)
+
+ PlayerImmovable* pi;
+
+ enum class Ownership {LOST, GAINED};
+ Ownership ownership;
+
+ NoteImmovable(PlayerImmovable* const init_pi, Ownership const init_ownership)
+ : pi(init_pi), ownership(init_ownership) {
+ }
+};
+
/**
* BaseImmovable is the base for all non-moving objects (immovables such as
* trees, buildings, flags, roads).
=== modified file 'src/logic/map.cc'
--- src/logic/map.cc 2014-07-03 19:26:30 +0000
+++ src/logic/map.cc 2014-07-16 08:32:14 +0000
@@ -46,6 +46,7 @@
#include "logic/world/world.h"
#include "map_io/s2map.h"
#include "map_io/widelands_map_loader.h"
+#include "notifications/notifications.h"
#include "wui/overlay_manager.h"
namespace Widelands {
@@ -1846,7 +1847,7 @@
{
c.field->set_terrain(c.t, terrain);
- NoteSender<NoteFieldTransformed>::send(NoteFieldTransformed(c));
+ Notifications::publish(NoteFieldTransformed(c, c.field - &m_fields[0]));
recalc_for_field_area(world, Area<FCoords>(c, 2));
=== modified file 'src/logic/map.h'
--- src/logic/map.h 2014-07-05 16:41:51 +0000
+++ src/logic/map.h 2014-07-16 08:32:14 +0000
@@ -32,10 +32,11 @@
#include "interval.h"
#include "logic/field.h"
#include "logic/map_revision.h"
-#include "logic/notification.h"
#include "logic/objective.h"
#include "logic/walkingdir.h"
#include "logic/widelands_geometry.h"
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
#include "random/random.h"
class FileSystem;
@@ -69,6 +70,16 @@
struct Path;
class Immovable;
+struct NoteFieldTransformed {
+ CAN_BE_SEND_AS_NOTE(NoteId::FieldTransformed)
+
+ FCoords fc;
+ Map_Index map_index;
+
+ NoteFieldTransformed(const FCoords& init_fc, const Map_Index init_map_index)
+ : fc(init_fc), map_index(init_map_index) {
+ }
+};
struct ImmovableFound {
BaseImmovable * object;
@@ -117,10 +128,7 @@
*
* Warning: width and height must be even
*/
-class Map :
- public ITransportCostCalculator,
- public NoteSender<NoteFieldTransformed>
-{
+class Map : public ITransportCostCalculator {
public:
friend class Editor;
friend class Editor_Game_Base;
=== removed file 'src/logic/notification.cc'
--- src/logic/notification.cc 2014-07-05 12:17:03 +0000
+++ src/logic/notification.cc 1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2006-2014 by the Widelands Development Team
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-// Dummy file as cmake cannot handle header only libraries :(.
=== removed file 'src/logic/notification.h'
--- src/logic/notification.h 2014-07-05 16:41:51 +0000
+++ src/logic/notification.h 1970-01-01 00:00:00 +0000
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2008-2010 by the Widelands Development Team
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef WL_LOGIC_NOTIFICATION_H
-#define WL_LOGIC_NOTIFICATION_H
-
-#include <algorithm>
-#include <vector>
-
-#include "logic/widelands_geometry.h"
-
-namespace Widelands {
-
-struct PlayerImmovable;
-
-/*
-
-Sender/Receiver infrastructure
-
-Used by computer players to be notified of events, but could/should be
-extended to a nofication system that sends player information.
-
-*/
-
-template<typename T>
-class NoteReceiver;
-
-template<typename T>
-class NoteSender {
- friend class NoteReceiver<T>;
-
- typedef std::vector<NoteReceiver<T>*> Links;
-
-public:
- ~NoteSender() {
- while (!m_links.empty())
- (*m_links.rbegin())->disconnect(*this);
- }
-
- void send(const T & note) const {
- for (auto& link : m_links) {
- link->receive(note);
- }
- }
-
-private:
- Links m_links;
-};
-
-template<typename T>
-class NoteReceiver {
- friend class NoteSender<T>;
-
- typedef std::vector<NoteSender<T>*> Links;
-
-public:
- virtual ~NoteReceiver() {
- while (!m_links.empty())
- disconnect(**m_links.rbegin());
- }
-
- void connect(NoteSender<T> & sender) {
- if (std::find(m_links.begin(), m_links.end(), &sender) != m_links.end())
- return;
- m_links.push_back(&sender);
- sender.m_links.push_back(this);
- }
-
- void disconnect(NoteSender<T> & sender) {
- typename NoteSender<T>::Links::iterator oit = std::find
- (sender.m_links.begin(), sender.m_links.end(), this);
- if (oit != sender.m_links.end())
- sender.m_links.erase(oit);
- typename Links::iterator it = std::find
- (m_links.begin(), m_links.end(), &sender);
- if (it != m_links.end())
- m_links.erase(it);
- }
-
- virtual void receive(const T & note) = 0;
-
-private:
- Links m_links;
-};
-
-
-enum losegain_t {LOSE = 0, GAIN};
-
-struct NoteImmovable {
- PlayerImmovable * pi;
- losegain_t lg;
-
- NoteImmovable(PlayerImmovable * const _pi, losegain_t const _lg)
- : pi(_pi), lg(_lg) {}
-};
-
-struct NoteFieldPossession {
- FCoords fc;
- losegain_t lg;
-
- NoteFieldPossession(const FCoords & _fc, losegain_t const _lg)
- : fc(_fc), lg(_lg) {}
-};
-
-struct NoteFieldTransformed {
- FCoords fc;
-
- NoteFieldTransformed(const FCoords & _fc)
- : fc(_fc) {}
-};
-
-}
-
-#endif // end of include guard: WL_LOGIC_NOTIFICATION_H
=== modified file 'src/logic/player.cc'
--- src/logic/player.cc 2014-07-15 10:02:22 +0000
+++ src/logic/player.cc 2014-07-16 08:32:14 +0000
@@ -172,6 +172,23 @@
m_ware_stocks (tribe_descr.get_nrwares ())
{
set_name(name);
+
+ // Subscribe to NoteImmovables.
+ immovable_subscriber_ =
+ Notifications::subscribe<NoteImmovable>([this](const NoteImmovable& note) {
+ if (note.pi->owner().player_number() == player_number()) {
+ if (upcast(Building, building, note.pi))
+ update_building_statistics(*building, note.ownership);
+ }
+ });
+
+ // Subscribe to NoteFieldTransformed.
+ field_transformed_subscriber_ =
+ Notifications::subscribe<NoteFieldTransformed>([this](const NoteFieldTransformed& note) {
+ if (vision(note.map_index) > 1) {
+ rediscover_node(egbase().map(), egbase().map()[0], note.fc);
+ }
+ });
}
@@ -1230,7 +1247,7 @@
* Only to be called by \ref receive
*/
void Player::update_building_statistics
- (Building & building, losegain_t const lg)
+ (Building & building, NoteImmovable::Ownership ownership)
{
upcast(ConstructionSite const, constructionsite, &building);
const std::string & building_name =
@@ -1246,7 +1263,7 @@
std::vector<Building_Stats> & stat =
m_building_stats[tribe().building_index(building_name.c_str())];
- if (lg == GAIN) {
+ if (ownership == NoteImmovable::Ownership::GAINED) {
Building_Stats new_building;
new_building.is_constructionsite = constructionsite;
new_building.pos = building.get_position();
@@ -1267,21 +1284,6 @@
}
}
-
-void Player::receive(const NoteImmovable & note)
-{
- if (upcast(Building, building, note.pi))
- update_building_statistics(*building, note.lg);
-
- NoteSender<NoteImmovable>::send(note);
-}
-
-
-void Player::receive(const NoteFieldPossession & note)
-{
- NoteSender<NoteFieldPossession>::send(note);
-}
-
void Player::setAI(const std::string & ai)
{
m_ai = ai;
=== modified file 'src/logic/player.h'
--- src/logic/player.h 2014-07-14 19:48:07 +0000
+++ src/logic/player.h 2014-07-16 08:32:14 +0000
@@ -20,6 +20,8 @@
#ifndef WL_LOGIC_PLAYER_H
#define WL_LOGIC_PLAYER_H
+#include <memory>
+
#include "base/macros.h"
#include "graphic/color.h"
#include "logic/building.h"
@@ -27,7 +29,6 @@
#include "logic/editor_game_base.h"
#include "logic/mapregion.h"
#include "logic/message_queue.h"
-#include "logic/notification.h"
#include "logic/tribe.h"
#include "logic/warehouse.h"
#include "logic/widelands.h"
@@ -54,10 +55,7 @@
* \ref GameController and friends, so that replays and network games function
* properly.
*/
-class Player :
- public NoteReceiver<NoteImmovable>, public NoteReceiver<NoteFieldPossession>,
- public NoteSender <NoteImmovable>, public NoteSender <NoteFieldPossession>
-{
+class Player {
public:
// hard-coded playercolors
static const RGBColor Colors[MAX_PLAYERS];
@@ -364,13 +362,6 @@
// Node visible if > 1
return (m_see_all ? 2 : 0) + m_fields[i].vision;
}
- /**
- * Called when a node becomes seen or has changed. Discovers the node and
- * those of the 6 surrounding edges/triangles that are not seen from another
- * node.
- */
- void rediscover_node(const Map &, const Widelands::Field &, FCoords)
- ;
bool has_view_changed() {
bool t = m_view_changed;
@@ -521,10 +512,6 @@
void ware_consumed(Ware_Index, uint8_t);
void next_ware_production_period();
- void receive(const NoteImmovable &) override;
- void receive(const NoteFieldPossession &) override;
- void receive(const NoteFieldTransformed &);
-
void setAI(const std::string &);
const std::string & getAI() const;
@@ -535,13 +522,20 @@
}
private:
- void update_building_statistics(Building &, losegain_t);
+ void update_building_statistics(Building &, NoteImmovable::Ownership ownership);
void update_team_players();
void play_message_sound(const std::string & sender);
void _enhance_or_dismantle
(Building *, Building_Index const index_of_new_building);
-private:
+ // Called when a node becomes seen or has changed. Discovers the node and
+ // those of the 6 surrounding edges/triangles that are not seen from another
+ // node.
+ void rediscover_node(const Map&, const Widelands::Field&, FCoords);
+
+ std::unique_ptr<Notifications::Subscriber<NoteImmovable>> immovable_subscriber_;
+ std::unique_ptr<Notifications::Subscriber<NoteFieldTransformed>> field_transformed_subscriber_;
+
MessageQueue m_messages;
Editor_Game_Base & m_egbase;
=== modified file 'src/logic/production_program.cc'
--- src/logic/production_program.cc 2014-07-15 18:30:46 +0000
+++ src/logic/production_program.cc 2014-07-16 08:32:14 +0000
@@ -361,8 +361,6 @@
} catch (const _wexception & e) {
throw game_data_error("economy: %s", e.what());
}
-
- return nullptr; // will never be reached
}
@@ -379,8 +377,6 @@
} catch (const _wexception & e) {
throw game_data_error("site: %s", e.what());
}
-
- return nullptr; // will never be reached
}
@@ -397,8 +393,6 @@
} catch (const _wexception & e) {
throw game_data_error("workers: %s", e.what());
}
-
- return nullptr; // will never be reached
}
@@ -422,8 +416,6 @@
} catch (const _wexception & e) {
throw game_data_error("invalid condition: %s", e.what());
}
-
- return nullptr; // will never be reached
}
=== modified file 'src/logic/ship.cc'
--- src/logic/ship.cc 2014-07-15 05:12:37 +0000
+++ src/logic/ship.cc 2014-07-16 08:32:14 +0000
@@ -630,7 +630,6 @@
send_message(game, "exp_coast", msg_head, msg_body, "ship_explore_island_cw.png");
return;
}
- break;
}
case EXP_COLONIZING: {
assert(m_expedition->seen_port_buildspaces && !m_expedition->seen_port_buildspaces->empty());
=== modified file 'src/logic/worker.cc'
--- src/logic/worker.cc 2014-07-16 05:33:19 +0000
+++ src/logic/worker.cc 2014-07-16 08:32:14 +0000
@@ -3089,8 +3089,6 @@
} catch (const std::exception & e) {
throw wexception("loading worker: %s", e.what());
}
-
- return nullptr; // Should not be reached
}
/**
=== modified file 'src/logic/world/map_gen.cc'
--- src/logic/world/map_gen.cc 2014-07-03 20:11:14 +0000
+++ src/logic/world/map_gen.cc 2014-07-16 08:32:14 +0000
@@ -51,7 +51,6 @@
default:
return nullptr;
};
- return nullptr;
}
MapGenLandResource::MapGenLandResource(const LuaTable& table, MapGenInfo& mapGenInfo) {
=== modified file 'src/network/CMakeLists.txt'
--- src/network/CMakeLists.txt 2014-07-14 10:45:44 +0000
+++ src/network/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -30,6 +30,7 @@
base_macros
base_md5
build_info
+ chat
game_io
helper
io_dedicated_log
=== modified file 'src/network/internet_gaming.h'
--- src/network/internet_gaming.h 2014-07-05 16:41:51 +0000
+++ src/network/internet_gaming.h 2014-07-16 08:32:14 +0000
@@ -29,7 +29,7 @@
#endif
#include "build_info.h"
-#include "chat.h"
+#include "chat/chat.h"
#include "network/internet_gaming_protocol.h"
#include "network/network.h"
#include "network/network_lan_promotion.h"
@@ -114,7 +114,10 @@
void send(const std::string &) override;
/// ChatProvider: adds the message to the message list and calls parent.
- void receive(const ChatMessage & msg) {messages.push_back(msg); ChatProvider::send(msg);}
+ void receive(const ChatMessage & msg) {
+ messages.push_back(msg);
+ Notifications::publish(msg);
+ }
/// ChatProvider: returns the list of chatmessages.
const std::vector<ChatMessage> & getMessages() const override {return messages;}
=== modified file 'src/network/netclient.cc'
--- src/network/netclient.cc 2014-07-14 10:45:44 +0000
+++ src/network/netclient.cc 2014-07-16 08:32:14 +0000
@@ -997,7 +997,7 @@
if (packet.Unsigned8())
c.recipient = packet.String();
d->chatmessages.push_back(c);
- ChatProvider::send(c); // NoteSender<ChatMessage>
+ Notifications::publish(c);
break;
}
case NETCMD_SYSTEM_MESSAGE_CODE: {
@@ -1011,7 +1011,7 @@
c.playern = UserSettings::none(); // == System message
// c.sender remains empty to indicate a system message
d->chatmessages.push_back(c);
- ChatProvider::send(c);
+ Notifications::publish(c);
break;
}
case NETCMD_DEDICATED_ACCESS: {
=== modified file 'src/network/netclient.h'
--- src/network/netclient.h 2014-07-05 16:41:51 +0000
+++ src/network/netclient.h 2014-07-16 08:32:14 +0000
@@ -20,7 +20,7 @@
#ifndef WL_NETWORK_NETCLIENT_H
#define WL_NETWORK_NETCLIENT_H
-#include "chat.h"
+#include "chat/chat.h"
#include "logic/game_controller.h"
#include "logic/game_settings.h"
#include "network/network.h"
=== modified file 'src/network/nethost.cc'
--- src/network/nethost.cc 2014-07-14 10:45:44 +0000
+++ src/network/nethost.cc 2014-07-16 08:32:14 +0000
@@ -34,7 +34,7 @@
#include "base/md5.h"
#include "base/wexception.h"
#include "build_info.h"
-#include "chat.h"
+#include "chat/chat.h"
#include "game_io/game_loader.h"
#include "game_io/game_preload_data_packet.h"
#include "helper.h"
@@ -514,7 +514,7 @@
void receive(const ChatMessage & msg) {
messages.push_back(msg);
- ChatProvider::send(msg);
+ Notifications::publish(msg);
}
private:
@@ -1022,8 +1022,6 @@
broadcast(s);
d->chat.receive(msg);
-
- dedicatedlog("[Host]: chat: %s\n", msg.toPlainString().c_str());
} else { // personal messages
SendPacket s;
s.Unsigned8(NETCMD_CHAT);
=== added directory 'src/notifications'
=== added file 'src/notifications/CMakeLists.txt'
--- src/notifications/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/notifications/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -0,0 +1,12 @@
+add_subdirectory(test)
+
+wl_library(notifications
+ SRCS
+ notifications.cc
+ notifications.h
+ notifications_impl.h
+ note_ids.h
+ DEPENDS
+ base_log
+ base_macros
+)
=== added file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h 1970-01-01 00:00:00 +0000
+++ src/notifications/note_ids.h 2014-07-16 08:32:14 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_NOTIFICATIONS_NOTE_IDS_H
+#define WL_NOTIFICATIONS_NOTE_IDS_H
+
+#include <stdint.h>
+
+// List all note ids here. They must be unique in the running
+// system, this is easier to guarantee when they are all listed in
+// one place.
+enum class NoteId : uint32_t {
+ ChatMessage,
+ LogMessage,
+ Immovable,
+ FieldPossession,
+ FieldTransformed,
+};
+
+#endif // end of include guard: WL_NOTIFICATIONS_NOTE_IDS_H
=== added file 'src/notifications/notifications.cc'
--- src/notifications/notifications.cc 1970-01-01 00:00:00 +0000
+++ src/notifications/notifications.cc 2014-07-16 08:32:14 +0000
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "notifications/notifications.h"
+
+#include "base/log.h"
+
+namespace Notifications {
+
+NotificationsManager* NotificationsManager::get() {
+ static NotificationsManager instance;
+ return &instance;
+}
+
+NotificationsManager::NotificationsManager() : next_subscriber_id_(1), num_subscribers_(0) {
+}
+
+NotificationsManager::~NotificationsManager() {
+ if (num_subscribers_ != 0) {
+ log("ERROR: NotificationsManager is destroyed, but there are still subscribers.");
+ }
+}
+
+} // namespace Notifications
=== added file 'src/notifications/notifications.h'
--- src/notifications/notifications.h 1970-01-01 00:00:00 +0000
+++ src/notifications/notifications.h 2014-07-16 08:32:14 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_NOTIFICATIONS_NOTIFICATIONS_H
+#define WL_NOTIFICATIONS_NOTIFICATIONS_H
+
+#include <functional>
+#include <memory>
+
+#include "notifications/notifications_impl.h"
+
+namespace Notifications {
+
+// The Notification framework is build around a singleton that dispatches
+// 'Note'. A Note is any class that has a uint32_t note_id() member that must
+// return something unique throughout the whole system. Use the macro
+// CAN_BE_SEND_AS_NOTE to define that method easily.
+//
+// The only public interface for the framework are the two functions below.
+
+#define CAN_BE_SEND_AS_NOTE(id) \
+ static uint32_t note_id() { \
+ return static_cast<uint32_t>(id); \
+ }
+
+// Subscribes to a Note of type 'T' with the given callback function. The
+// returned object is opaque, but will unsubscribe on destruction.
+template <typename T>
+std::unique_ptr<Subscriber<T>> subscribe(std::function<void(const T&)> callback) {
+ return NotificationsManager::get()->subscribe<T>(callback);
+}
+
+// Publishes 'message' to all existing subscribers.
+template <typename T> void publish(const T& message) {
+ return NotificationsManager::get()->publish<T>(message);
+}
+
+} // namespace Notifications
+
+#endif // end of include guard: WL_NOTIFICATIONS_NOTIFICATIONS_H
=== added file 'src/notifications/notifications_impl.h'
--- src/notifications/notifications_impl.h 1970-01-01 00:00:00 +0000
+++ src/notifications/notifications_impl.h 2014-07-16 08:32:14 +0000
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_NOTIFICATIONS_NOTIFICATIONS_IMPL_H
+#define WL_NOTIFICATIONS_NOTIFICATIONS_IMPL_H
+
+#include "base/macros.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <cassert>
+#include <list>
+#include <memory>
+#include <unordered_map>
+
+namespace Notifications {
+
+// Subscribes to a notification type and unsubscribes on destruction.
+template <typename T> class Subscriber {
+public:
+ Subscriber(uint32_t id, std::function<void(const T&)> callback) : id_(id), callback_(callback) {
+ }
+
+ ~Subscriber();
+
+private:
+ friend class NotificationsManager;
+
+ uint32_t id_;
+ std::function<void(const T&)> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Subscriber);
+};
+
+// Singleton that dispatches notifications and keeps track of all subscribers.
+// Implementation detail. Instead use the functions from the public header.
+class NotificationsManager {
+public:
+ // Returns the Singleton. Will create it if it does not yet exist.
+ static NotificationsManager* get();
+
+ // Creates a subscriber for 'T' with the given 'callback' and returns it.
+ template <typename T>
+ std::unique_ptr<Subscriber<T>> subscribe(std::function<void(const T&)> callback) {
+ std::list<void*>& subscribers = note_id_to_subscribers_[T::note_id()];
+ auto new_subscriber =
+ std::unique_ptr<Subscriber<T>>(new Subscriber<T>(next_subscriber_id_, callback));
+ subscribers.push_back(new_subscriber.get());
+ ++next_subscriber_id_;
+ ++num_subscribers_;
+ return new_subscriber;
+ }
+
+ // Publishes 'message' to all subscribers.
+ template <typename T> void publish(const T& message) {
+ for (void* p_subscriber : note_id_to_subscribers_[T::note_id()]) {
+ Subscriber<T>* subscriber = static_cast<Subscriber<T>*>(p_subscriber);
+ subscriber->callback_(message);
+ }
+ }
+
+ // Unsubscribes 'subscriber'.
+ template <typename T>
+ void unsubscribe(Subscriber<T>* subscriber) {
+ std::list<void*>& subscribers = note_id_to_subscribers_.at(T::note_id());
+ auto subscribers_it =
+ std::find_if(subscribers.begin(), subscribers.end(), [&subscriber](const void* p_subscriber) {
+ return static_cast<const Subscriber<T>*>(p_subscriber)->id_ == subscriber->id_;
+ });
+
+ assert(subscribers_it != subscribers.end());
+ subscribers.erase(subscribers_it);
+ --num_subscribers_;
+ }
+
+private:
+ // Private constructor for Singleton.
+ NotificationsManager();
+
+ // Checks that there are no more subscribers.
+ ~NotificationsManager();
+
+ uint32_t next_subscriber_id_;
+ uint32_t num_subscribers_;
+
+ // Ideally we would like to keep a list<Subscriber<T>*> instead of void* to
+ // be typesafe. Unfortunately, C++ does not allow for an easy way. I could
+ // introduce a base class and dispatch via a virtual function call, but
+ // since this framework should be as efficient as possible, I opted for
+ // using void* and casting instead.
+ std::unordered_map<uint32_t, std::list<void*>> note_id_to_subscribers_;
+
+ DISALLOW_COPY_AND_ASSIGN(NotificationsManager);
+};
+
+template <typename T> Subscriber<T>::~Subscriber() {
+ NotificationsManager::get()->unsubscribe<T>(this);
+}
+
+} // namespace Notifications
+
+#endif // end of include guard: WL_NOTIFICATIONS_NOTIFICATIONS_IMPL_H
=== added directory 'src/notifications/test'
=== added file 'src/notifications/test/CMakeLists.txt'
--- src/notifications/test/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/notifications/test/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -0,0 +1,6 @@
+wl_test(notifications_test
+ SRCS
+ notifications_test.cc
+ DEPENDS
+ notifications
+)
=== added file 'src/notifications/test/notifications_test.cc'
--- src/notifications/test/notifications_test.cc 1970-01-01 00:00:00 +0000
+++ src/notifications/test/notifications_test.cc 2014-07-16 08:32:14 +0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <memory>
+#include <string>
+
+#define BOOST_TEST_MODULE Notifications
+#include <boost/test/unit_test.hpp>
+
+#include "notifications/notifications.h"
+
+struct SimpleNote {
+ CAN_BE_SEND_AS_NOTE(100)
+
+ SimpleNote(const std::string& init_text) : text(init_text) {}
+
+ std::string text;
+};
+
+BOOST_AUTO_TEST_SUITE(NotificationsTestSuite)
+
+
+BOOST_AUTO_TEST_CASE(SimpleTest) {
+ std::vector<SimpleNote> received1;
+ auto subscriber1 = Notifications::subscribe<SimpleNote>(
+ [&received1](const SimpleNote& got) {received1.push_back(got);});
+
+ Notifications::publish(SimpleNote("Hello"));
+
+ std::vector<SimpleNote> received2;
+ auto subscriber2 = Notifications::subscribe<SimpleNote>(
+ [&received2](const SimpleNote& got) {received2.push_back(got);});
+
+ Notifications::publish(SimpleNote("World"));
+
+ BOOST_CHECK_EQUAL(received1.size(), 2);
+ BOOST_CHECK_EQUAL("Hello", received1[0].text);
+ BOOST_CHECK_EQUAL("World", received1[1].text);
+
+ BOOST_CHECK_EQUAL(received2.size(), 1);
+ BOOST_CHECK_EQUAL("World", received2[0].text);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
=== modified file 'src/scripting/lua_globals.cc'
--- src/scripting/lua_globals.cc 2014-06-21 15:17:04 +0000
+++ src/scripting/lua_globals.cc 2014-07-16 08:32:14 +0000
@@ -105,7 +105,6 @@
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
report_error(L, "Cannot format the given type %s at index %i", lua_typename(L, i), i);
- break;
}
}
=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc 2014-07-16 06:05:23 +0000
+++ src/scripting/lua_map.cc 2014-07-16 08:32:14 +0000
@@ -2121,7 +2121,6 @@
case BaseImmovable::BIG: lua_pushstring(L, "big"); break;
default:
report_error(L, "Unknown size in L_BaseImmovable::get_size: %i", o->get_size());
- break;
}
return 1;
}
=== modified file 'src/scripting/lua_map.h'
--- src/scripting/lua_map.h 2014-07-15 10:02:22 +0000
+++ src/scripting/lua_map.h 2014-07-16 08:32:14 +0000
@@ -185,7 +185,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(Building_Descr);
+ CASTED_GET_DESCRIPTION(Building_Descr)
};
@@ -218,7 +218,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(ProductionSite_Descr);
+ CASTED_GET_DESCRIPTION(ProductionSite_Descr)
};
@@ -250,7 +250,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(MilitarySite_Descr);
+ CASTED_GET_DESCRIPTION(MilitarySite_Descr)
};
@@ -289,7 +289,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(TrainingSite_Descr);
+ CASTED_GET_DESCRIPTION(TrainingSite_Descr)
};
@@ -321,7 +321,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(Warehouse_Descr);
+ CASTED_GET_DESCRIPTION(Warehouse_Descr)
};
@@ -356,7 +356,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(WareDescr);
+ CASTED_GET_DESCRIPTION(WareDescr)
};
@@ -393,7 +393,7 @@
*/
private:
- CASTED_GET_DESCRIPTION(Worker_Descr);
+ CASTED_GET_DESCRIPTION(Worker_Descr)
};
#undef CASTED_GET_DESCRIPTION
=== modified file 'src/ui_fsmenu/CMakeLists.txt'
--- src/ui_fsmenu/CMakeLists.txt 2014-07-14 10:45:44 +0000
+++ src/ui_fsmenu/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -61,5 +61,6 @@
ui_basic
widelands_ball_of_mud
wui
+ wui_chat_ui
wui_text_layout
)
=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt 2014-07-14 10:45:44 +0000
+++ src/wui/CMakeLists.txt 2014-07-16 08:32:14 +0000
@@ -7,6 +7,28 @@
graphic_color
)
+wl_library(wui_chat_ui
+ SRCS
+ chatoverlay.cc
+ chatoverlay.h
+ game_chat_menu.cc
+ game_chat_menu.h
+ gamechatpanel.cc
+ gamechatpanel.h
+ chat_msg_layout.h
+ chat_msg_layout.cc
+ DEPENDS
+ base_i18n
+ chat
+ graphic
+ graphic_color
+ graphic_text
+ logic
+ profile
+ ui_basic
+ wui
+)
+
wl_library(wui
SRCS
actionconfirm.cc
@@ -18,8 +40,6 @@
building_ui.cc
buildingwindow.cc
buildingwindow.h
- chatoverlay.cc
- chatoverlay.h
constructionsitewindow.cc
debugconsole.cc
debugconsole.h
@@ -28,8 +48,6 @@
encyclopedia_window.h
fieldaction.cc
fieldaction.h
- game_chat_menu.cc
- game_chat_menu.h
game_debug_ui.cc
game_debug_ui.h
game_main_menu.cc
@@ -48,8 +66,6 @@
game_summary.h
game_tips.cc
game_tips.h
- gamechatpanel.cc
- gamechatpanel.h
general_statistics_menu.cc
general_statistics_menu.h
interactive_base.cc
@@ -120,26 +136,27 @@
base_log
base_macros
base_time_string
+ chat
economy
game_io
graphic
graphic_color
graphic_image
graphic_surface
- graphic_text
io_fileread
io_filesystem
logic
logic_game_controller
logic_game_settings
- logic_notification
logic_widelands_geometry
network
+ notifications
profile
scripting
sound
ui_basic
ui_fsmenu
widelands_ball_of_mud
+ wui_chat_ui
wui_text_layout
)
=== added file 'src/wui/chat_msg_layout.cc'
--- src/wui/chat_msg_layout.cc 1970-01-01 00:00:00 +0000
+++ src/wui/chat_msg_layout.cc 2014-07-16 08:32:14 +0000
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "wui/chat_msg_layout.h"
+
+#include "chat/chat.h"
+#include "graphic/color.h"
+#include "logic/constants.h"
+#include "logic/player.h"
+
+namespace {
+
+// Returns the hexcolor for the 'player'.
+std::string color(const int16_t playern)
+{
+ if ((playern >= 0) && playern < MAX_PLAYERS) {
+ const RGBColor & clr = Widelands::Player::Colors[playern];
+ char buf[sizeof("ffffff")];
+ snprintf(buf, sizeof(buf), "%.2x%.2x%.2x", clr.r, clr.g, clr.b);
+ return buf;
+ }
+ return "999999";
+}
+
+} // namespace
+
+// TODO(sirver): remove as soon as old text renderer is gone.
+std::string format_as_old_richtext(const ChatMessage& chat_message) {
+ std::string message = "<p font-color=#33ff33 font-size=9>";
+
+ // Escape richtext characters
+ // The goal of this code is two-fold:
+ // 1. Assuming an honest game host, we want to prevent the ability of
+ // clients to use richtext.
+ // 2. Assuming a malicious host or meta server, we want to reduce the
+ // likelihood that a bug in the richtext renderer can be exploited,
+ // by restricting the set of allowed richtext commands.
+ // Most notably, images are not allowed in richtext at all.
+ //
+ // Note that we do want host and meta server to send some richtext code,
+ // as the ability to send formatted commands is nice for the usability
+ // of meta server and dedicated servers, so we're treading a bit of a
+ // fine line here.
+ std::string sanitized;
+ for (std::string::size_type pos = 0; pos < chat_message.msg.size(); ++pos) {
+ if (chat_message.msg[pos] == '<') {
+ if (chat_message.playern < 0) {
+ static const std::string good1 = "</p><p";
+ static const std::string good2 = "<br>";
+ if (!chat_message.msg.compare(pos, good1.size(), good1)) {
+ std::string::size_type nextclose = chat_message.msg.find('>', pos + good1.size());
+ if (nextclose != std::string::npos &&
+ (nextclose == pos + good1.size() || chat_message.msg[pos + good1.size()] == ' ')) {
+ sanitized += good1;
+ pos += good1.size() - 1;
+ continue;
+ }
+ } else if (!chat_message.msg.compare(pos, good2.size(), good2)) {
+ sanitized += good2;
+ pos += good2.size() - 1;
+ continue;
+ }
+ }
+
+ sanitized += "<";
+ } else {
+ sanitized += chat_message.msg[pos];
+ }
+ }
+
+ // time calculation
+ char ts[13];
+ strftime(ts, sizeof(ts), "[%H:%M] </p>", localtime(&chat_message.time));
+ message += ts;
+
+ message += "<p font-size=14 font-face=DejaVuSerif font-color=#";
+ message += color(chat_message.playern);
+
+ if (chat_message.recipient.size() && chat_message.sender.size()) {
+ // Personal message handling
+ if (sanitized.compare(0, 3, "/me")) {
+ message += " font-decoration=underline>";
+ message += chat_message.sender;
+ message += " @ ";
+ message += chat_message.recipient;
+ message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
+ message += sanitized;
+ } else {
+ message += ">@";
+ message += chat_message.recipient;
+ message += " >> </p><p font-size=14";
+ message += " font-face=DejaVuSerif font-color=#";
+ message += color(chat_message.playern);
+ message += " font-style=italic> ";
+ message += chat_message.sender;
+ message += sanitized.substr(3);
+ }
+ } else {
+ // Normal messages handling
+ if (not sanitized.compare(0, 3, "/me")) {
+ message += " font-style=italic>-> ";
+ if (chat_message.sender.size())
+ message += chat_message.sender;
+ else
+ message += "***";
+ message += sanitized.substr(3);
+ } else if (chat_message.sender.size()) {
+ message += " font-decoration=underline>";
+ message += chat_message.sender;
+ message += ":</p><p font-size=14 font-face=DejaVuSerif> ";
+ message += sanitized;
+ } else {
+ message += " font-weight=bold>*** ";
+ message += sanitized;
+ }
+ }
+
+ // return the formated message
+ return message + "<br></p>";
+}
+
+// Returns a richtext string that can be displayed to the user.
+std::string format_as_richtext(const ChatMessage& chat_message) {
+ std::string message = "<p><font color=33ff33 size=9>";
+
+ // Escape richtext characters
+ // The goal of this code is two-fold:
+ // 1. Assuming an honest game host, we want to prevent the ability of
+ // clients to use richtext.
+ // 2. Assuming a malicious host or meta server, we want to reduce the
+ // likelihood that a bug in the richtext renderer can be exploited,
+ // by restricting the set of allowed richtext commands.
+ // Most notably, images are not allowed in richtext at all.
+ //
+ // Note that we do want host and meta server to send some richtext code,
+ // as the ability to send formatted commands is nice for the usability
+ // of meta server and dedicated servers, so we're treading a bit of a
+ // fine line here.
+ std::string sanitized;
+ for (std::string::size_type pos = 0; pos < chat_message.msg.size(); ++pos) {
+ if (chat_message.msg[pos] == '<') {
+ if (chat_message.playern < 0) {
+ static const std::string good1 = "</p><p";
+ static const std::string good2 = "<br>";
+ if (!chat_message.msg.compare(pos, good1.size(), good1)) {
+ std::string::size_type nextclose = chat_message.msg.find('>', pos + good1.size());
+ if
+ (nextclose != std::string::npos &&
+ (nextclose == pos + good1.size() || chat_message.msg[pos + good1.size()] == ' '))
+ {
+ sanitized += good1;
+ pos += good1.size() - 1;
+ continue;
+ }
+ } else if (!chat_message.msg.compare(pos, good2.size(), good2)) {
+ sanitized += good2;
+ pos += good2.size() - 1;
+ continue;
+ }
+ }
+
+ sanitized += "\\<";
+ } else {
+ sanitized += chat_message.msg[pos];
+ }
+ }
+
+ // time calculation
+ char ts[13];
+ strftime(ts, sizeof(ts), "[%H:%M] ", localtime(&chat_message.time));
+ message += ts;
+
+ message += "</font><font size=14 face=DejaVuSerif color=";
+ message += color(chat_message.playern);
+
+ if (chat_message.recipient.size() && chat_message.sender.size()) {
+ // Personal message handling
+ if (sanitized.compare(0, 3, "/me")) {
+ message += " bold=1>";
+ message += chat_message.sender;
+ message += " @ ";
+ message += chat_message.recipient;
+ message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
+ message += sanitized;
+ } else {
+ message += ">@";
+ message += chat_message.recipient;
+ message += " \\> </font><font size=14";
+ message += " face=DejaVuSerif color=";
+ message += color(chat_message.playern);
+ message += " italic=1 shadow=1> ";
+ message += chat_message.sender;
+ message += sanitized.substr(3);
+ }
+ } else {
+ // Normal messages handling
+ if (not sanitized.compare(0, 3, "/me")) {
+ message += " italic=1>-\\> ";
+ if (chat_message.sender.size())
+ message += chat_message.sender;
+ else
+ message += "***";
+ message += sanitized.substr(3);
+ } else if (chat_message.sender.size()) {
+ message += " bold=1>";
+ message += chat_message.sender;
+ message += ":</font><font size=14 face=DejaVuSerif shadow=1 color=eeeeee> ";
+ message += sanitized;
+ } else {
+ message += " bold=1>*** ";
+ message += sanitized;
+ }
+ }
+
+ // return the formated message
+ return message + "</font><br></p>";
+}
=== added file 'src/wui/chat_msg_layout.h'
--- src/wui/chat_msg_layout.h 1970-01-01 00:00:00 +0000
+++ src/wui/chat_msg_layout.h 2014-07-16 08:32:14 +0000
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006-2014 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_WUI_CHAT_MSG_LAYOUT_H
+#define WL_WUI_CHAT_MSG_LAYOUT_H
+
+#include <string>
+
+struct ChatMessage;
+
+// Formats 'chat_message' as old richtext.
+std::string format_as_old_richtext(const ChatMessage& chat_message);
+
+// Formats 'chat_message' as richtext.
+std::string format_as_richtext(const ChatMessage& chat_message);
+
+#endif // end of include guard: WL_WUI_CHAT_MSG_LAYOUT_H
=== modified file 'src/wui/chatoverlay.cc'
--- src/wui/chatoverlay.cc 2014-07-14 10:45:44 +0000
+++ src/wui/chatoverlay.cc 2014-07-16 08:32:14 +0000
@@ -19,11 +19,14 @@
#include "wui/chatoverlay.h"
-#include "chat.h"
+#include <memory>
+
+#include "chat/chat.h"
#include "graphic/font_handler1.h"
#include "graphic/rendertarget.h"
#include "graphic/text/rt_errors.h"
#include "profile/profile.h"
+#include "wui/chat_msg_layout.h"
#include "wui/logmessage.h"
/**
@@ -32,8 +35,7 @@
static const int32_t CHAT_DISPLAY_TIME = 10;
static const uint32_t MARGIN = 2;
-struct ChatOverlay::Impl : Widelands::NoteReceiver<ChatMessage>,
- Widelands::NoteReceiver<LogMessage> {
+struct ChatOverlay::Impl {
bool transparent_;
ChatProvider * chat_;
bool havemessages_;
@@ -47,11 +49,24 @@
/// Log messages
std::vector<LogMessage> log_messages_;
- Impl() : transparent_(false), chat_(nullptr), havemessages_(false), oldest_(0) {}
+ std::unique_ptr<Notifications::Subscriber<ChatMessage>> chat_message_subscriber_;
+ std::unique_ptr<Notifications::Subscriber<LogMessage>> log_message_subscriber_;
+
+ Impl()
+ : transparent_(false),
+ chat_(nullptr),
+ havemessages_(false),
+ oldest_(0),
+ chat_message_subscriber_(
+ Notifications::subscribe<ChatMessage>([this](const ChatMessage&) {recompute();})),
+ log_message_subscriber_(
+ Notifications::subscribe<LogMessage>([this](const LogMessage& note) {
+ log_messages_.push_back(note);
+ recompute();
+ })) {
+ }
void recompute();
- virtual void receive(const ChatMessage & note) override;
- virtual void receive(const LogMessage & note) override;
};
ChatOverlay::ChatOverlay
@@ -72,20 +87,9 @@
void ChatOverlay::setChatProvider(ChatProvider & chat)
{
m->chat_ = &chat;
- Widelands::NoteReceiver<ChatMessage>* cmr
- = dynamic_cast<Widelands::NoteReceiver<ChatMessage>*>(m.get());
- cmr->connect(chat);
m->recompute();
}
-void ChatOverlay::setLogProvider(Widelands::NoteSender<LogMessage>& log_sender)
-{
- Widelands::NoteReceiver<LogMessage>* lmr
- = dynamic_cast<Widelands::NoteReceiver<LogMessage>*>(m.get());
- lmr->connect(log_sender);
-}
-
-
/**
* Check for message expiry.
*/
@@ -98,21 +102,6 @@
}
/**
- * Callback that is run when a new chat message comes in.
- */
-void ChatOverlay::Impl::receive(const ChatMessage & /* note */)
-{
- recompute();
-}
-
-void ChatOverlay::Impl::receive(const LogMessage& note)
-{
- log_messages_.push_back(note);
- recompute();
-}
-
-
-/**
* Recompute the chat message display.
*/
void ChatOverlay::Impl::recompute()
@@ -147,7 +136,7 @@
// Chat message is more recent
oldest_ = chat_->getMessages()[chat_idx].time;
if (now - oldest_ < CHAT_DISPLAY_TIME) {
- richtext = chat_->getMessages()[chat_idx].toPrintable()
+ richtext = format_as_richtext(chat_->getMessages()[chat_idx])
+ richtext;
}
chat_idx--;
=== modified file 'src/wui/chatoverlay.h'
--- src/wui/chatoverlay.h 2014-07-05 16:41:51 +0000
+++ src/wui/chatoverlay.h 2014-07-16 08:32:14 +0000
@@ -22,11 +22,9 @@
#include <memory>
-#include "logic/notification.h"
#include "ui_basic/panel.h"
struct ChatProvider;
-struct LogMessage;
/**
* The overlay that displays all new chat messages for some timeout on the main window.
@@ -38,7 +36,6 @@
~ChatOverlay();
void setChatProvider(ChatProvider &);
- void setLogProvider(Widelands::NoteSender<LogMessage> &);
virtual void draw(RenderTarget &) override;
virtual void think() override;
=== modified file 'src/wui/debugconsole.cc'
--- src/wui/debugconsole.cc 2014-07-05 12:48:58 +0000
+++ src/wui/debugconsole.cc 2014-07-16 08:32:14 +0000
@@ -24,7 +24,7 @@
#include <boost/bind.hpp>
#include "base/log.h"
-#include "chat.h"
+#include "chat/chat.h"
namespace DebugConsole {
@@ -105,7 +105,7 @@
if (messages.size() > 1000)
messages.erase(messages.begin(), messages.begin() + 100);
- ChatProvider::send(cm); // Notify listeners, i.e. the UI
+ Notifications::publish(cm); // Notify listeners, i.e. the UI
}
};
=== modified file 'src/wui/gamechatpanel.cc'
--- src/wui/gamechatpanel.cc 2014-04-16 10:49:34 +0000
+++ src/wui/gamechatpanel.cc 2014-07-16 08:32:14 +0000
@@ -20,6 +20,9 @@
#include "wui/gamechatpanel.h"
#include <limits>
+#include <string>
+
+#include "wui/chat_msg_layout.h"
/**
* Create a game chat panel
@@ -44,7 +47,8 @@
set_handle_mouse(true);
set_can_focus(true);
- connect(m_chat);
+ chat_message_subscriber_ =
+ Notifications::subscribe<ChatMessage>([this](const ChatMessage&) {recalculate();});
recalculate();
}
@@ -57,8 +61,7 @@
std::string str = "<rt>";
for (uint32_t i = 0; i < msgs.size(); ++i) {
- // FIXME use toPrintable() when old renderer is kicked out
- str += msgs[i].toOldRichText();
+ str += format_as_old_richtext(msgs[i]);
str += '\n';
}
str += "</rt>";
@@ -96,11 +99,6 @@
editbox.focus();
}
-void GameChatPanel::receive(const ChatMessage &)
-{
- recalculate();
-}
-
void GameChatPanel::keyEnter()
{
const std::string & str = editbox.text();
=== modified file 'src/wui/gamechatpanel.h'
--- src/wui/gamechatpanel.h 2014-07-05 16:41:51 +0000
+++ src/wui/gamechatpanel.h 2014-07-16 08:32:14 +0000
@@ -20,7 +20,9 @@
#ifndef WL_WUI_GAMECHATPANEL_H
#define WL_WUI_GAMECHATPANEL_H
-#include "chat.h"
+#include <memory>
+
+#include "chat/chat.h"
#include "ui_basic/editbox.h"
#include "ui_basic/multilinetextarea.h"
@@ -30,22 +32,16 @@
* Provides a panel that contains chat message scrollbar and a chat message
* entry field.
*/
-struct GameChatPanel :
- public UI::Panel, public Widelands::NoteReceiver<ChatMessage>
-{
+struct GameChatPanel : public UI::Panel {
GameChatPanel
(UI::Panel *,
int32_t x, int32_t y, uint32_t w, uint32_t h,
ChatProvider &);
- /**
- * Signal is called when a message has been sent by the user.
- */
+ // Signal is called when a message has been sent by the user.
boost::signals2::signal<void ()> sent;
- /**
- * Signal is called when the user has aborted entering a message.
- */
+ // Signal is called when the user has aborted entering a message.
boost::signals2::signal<void ()> aborted;
const std::string & get_edit_text() const {return editbox.text();}
@@ -53,8 +49,6 @@
void focusEdit();
- void receive(const ChatMessage &) override;
-
private:
void recalculate();
void keyEnter();
@@ -64,6 +58,7 @@
UI::Multiline_Textarea chatbox;
UI::EditBox editbox;
uint32_t chat_message_counter;
+ std::unique_ptr<Notifications::Subscriber<ChatMessage>> chat_message_subscriber_;
};
#endif // end of include guard: WL_WUI_GAMECHATPANEL_H
=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc 2014-07-14 10:45:44 +0000
+++ src/wui/interactive_base.cc 2014-07-16 08:32:14 +0000
@@ -45,6 +45,7 @@
#include "wui/game_chat_menu.h"
#include "wui/game_debug_ui.h"
#include "wui/interactive_player.h"
+#include "wui/logmessage.h"
#include "wui/mapviewpixelconstants.h"
#include "wui/mapviewpixelfunctions.h"
#include "wui/minimap.h"
@@ -103,13 +104,12 @@
m_road_build_player(0),
m_label_speed_shadow(this, get_w() - 1, 0, std::string(), UI::Align_TopRight),
m_label_speed(this, get_w(), 1, std::string(), UI::Align_TopRight),
- unique_window_handler_(new UniqueWindowHandler()),
- // Load workarea images.
// Start at idx 0 for 2 enhancements, idx 3 for 1, idx 5 if none
m_workarea_pics
{g_gr->images().get("pics/workarea123.png"), g_gr->images().get("pics/workarea23.png"),
g_gr->images().get("pics/workarea3.png"), g_gr->images().get("pics/workarea12.png"),
- g_gr->images().get("pics/workarea2.png"), g_gr->images().get("pics/workarea1.png")}
+ g_gr->images().get("pics/workarea2.png"), g_gr->images().get("pics/workarea1.png")},
+ unique_window_handler_(new UniqueWindowHandler())
{
m_toolbar.set_layout_toplevel(true);
m->quicknavigation->set_setview
@@ -127,8 +127,6 @@
set_dock_windows_to_edges
(global_s.get_bool("dock_windows_to_edges", false));
- m_chatOverlay->setLogProvider(m_log_sender);
-
// Switch to the new graphics system now, if necessary.
WLApplication::get()->refresh_graphics();
@@ -770,7 +768,7 @@
LogMessage lm;
lm.msg = message;
lm.time = time(nullptr);
- m_log_sender.send(lm);
+ Notifications::publish(lm);
}
=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h 2014-07-14 10:45:44 +0000
+++ src/wui/interactive_base.h 2014-07-16 08:32:14 +0000
@@ -26,10 +26,8 @@
#include "logic/editor_game_base.h"
#include "logic/map.h"
-#include "logic/notification.h"
#include "wui/chatoverlay.h"
#include "wui/debugconsole.h"
-#include "wui/logmessage.h"
#include "wui/mapview.h"
#include "wui/overlay_manager.h"
#include "ui_basic/box.h"
@@ -196,7 +194,6 @@
UI::Textarea m_label_speed;
UI::UniqueWindow::Registry m_debugconsole;
- Widelands::NoteSender<LogMessage> m_log_sender;
std::unique_ptr<UniqueWindowHandler> unique_window_handler_;
std::vector<const Image*> m_workarea_pics;
};
=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc 2014-07-05 12:48:58 +0000
+++ src/wui/interactive_spectator.cc 2014-07-16 08:32:14 +0000
@@ -21,7 +21,7 @@
#include "base/i18n.h"
#include "base/macros.h"
-#include "chat.h"
+#include "chat/chat.h"
#include "graphic/graphic.h"
#include "logic/game_controller.h"
#include "ui_basic/editbox.h"
=== modified file 'src/wui/logmessage.h'
--- src/wui/logmessage.h 2014-07-05 16:41:51 +0000
+++ src/wui/logmessage.h 2014-07-16 08:32:14 +0000
@@ -22,11 +22,15 @@
#include <string>
+#include "notifications/note_ids.h"
+#include "notifications/notifications.h"
/**
* Represents one log message.
*/
struct LogMessage {
+ CAN_BE_SEND_AS_NOTE(NoteId::LogMessage)
+
/**
* The (real-)time at which the message was received.
*/
Follow ups