fix bug chat during action
when a player chats while multiple actions from another player are executed on the local machine we before got an assertion error becasue replay s add_command expects that there are no other commands yet to be processed on the replay. (this was broken atda4cdef146
) we now fix this problem by ensuring that there is never more data on the replay than needed. for this purpose i wrote that playturn_network_adapter.cpp whose main purpose it to split incoming [turn]s into smaller [turn]s we also don't need the backlog anymore because now with playturn_network_adapter we only feed process_network_data with small data pieces that can always be handled at once. I also removed the replay_ member of turn_info. Before, we read actions from replay_srouce and wrote actions to recorder. And after the actions in replay_source has been executed they had been pushed into recorder. With get_user_choice this doesn't work, because we might end up pushing the 'answer' (the user choice) of a get_user_choice in the recorder while the 'question' (the invoking user action) was in replay_source and then would be pushed onto the recorder after the 'answer', leading to a wrong order in the recorder. This could maybe also have been fixed by always pushing user choices into replay_source, and syncing the replay_source somehow after we enter a user choice, but the way it was done in this commit seemed easier. note that replay_ and backlog were already made unused inda4cdef146
(that was a rather uncomplete commit) We maybe (i didnt test, just a guess) could also fix this bug by using add_nonundoable_command instead of add_command for nonundoable commands in replay.cpp with add_nonundoable_command defined as: config& add_nonundoable_command() { auto c = cfg_.add_child_at("command",config(), pos_); pos_++; return c; } One reason why i decided against it is, that i feared add_child_at to be slow (vector::insert) if there are a lot of entries in cfg_ (for example when we join game that has already run may turns). With the playturn_network_adapter this shouldn't be an issue anymore, but it also isnt needed anymore.
This commit is contained in:
parent
d1cd2e0b17
commit
847067dac7
9 changed files with 324 additions and 117 deletions
|
@ -845,6 +845,7 @@ set(wesnoth-main_SRC
|
|||
playmp_controller.cpp
|
||||
playsingle_controller.cpp
|
||||
playturn.cpp
|
||||
playturn_network_adapter.cpp
|
||||
portrait.cpp
|
||||
random_new.cpp
|
||||
random_new_deterministic.cpp
|
||||
|
|
|
@ -478,6 +478,7 @@ wesnoth_sources = Split("""
|
|||
playmp_controller.cpp
|
||||
playsingle_controller.cpp
|
||||
playturn.cpp
|
||||
playturn_network_adapter.cpp
|
||||
portrait.cpp
|
||||
random_new.cpp
|
||||
random_new_deterministic.cpp
|
||||
|
|
|
@ -190,11 +190,9 @@ void playmp_controller::play_human_turn(){
|
|||
|
||||
try {
|
||||
config cfg;
|
||||
const network::connection res = network::receive_data(cfg);
|
||||
std::deque<config> backlog;
|
||||
|
||||
if(res != network::null_connection) {
|
||||
if (turn_data_->process_network_data(cfg, backlog, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
|
||||
if(network_reader_.read(cfg)) {
|
||||
if (turn_data_->process_network_data(cfg, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
|
||||
{
|
||||
// Clean undo stack if turn has to be restarted (losing control)
|
||||
if ( undo_stack_->can_undo() )
|
||||
|
@ -274,11 +272,8 @@ void playmp_controller::play_idle_loop()
|
|||
{
|
||||
try {
|
||||
config cfg;
|
||||
const network::connection res = network::receive_data(cfg);
|
||||
std::deque<config> backlog;
|
||||
|
||||
if(res != network::null_connection) {
|
||||
if (turn_data_->process_network_data(cfg, res, backlog, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
|
||||
if(network_reader_.read(cfg)) {
|
||||
if (turn_data_->process_network_data(cfg, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
|
||||
{
|
||||
throw end_turn_exception(gui_->playing_side());
|
||||
}
|
||||
|
@ -400,26 +395,26 @@ void playmp_controller::wait_for_upload()
|
|||
init_turn_data();
|
||||
}
|
||||
|
||||
config cfg;
|
||||
network_reader_.set_source(playturn_network_adapter::get_source_from_config(cfg));
|
||||
while(true) {
|
||||
try {
|
||||
config cfg;
|
||||
const network::connection res = dialogs::network_receive_dialog(
|
||||
*gui_, _("Waiting for next scenario..."), cfg);
|
||||
|
||||
std::deque<config> backlog;
|
||||
if(res != network::null_connection) {
|
||||
if (turn_data_->process_network_data(cfg, backlog, skip_replay_)
|
||||
== turn_info::PROCESS_END_LINGER) {
|
||||
if (turn_data_->process_network_data_from_reader(skip_replay_) == turn_info::PROCESS_END_LINGER) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch(const end_level_exception&) {
|
||||
network_reader_.set_source(playturn_network_adapter::read_network);
|
||||
turn_data_->send_data();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
network_reader_.set_source(playturn_network_adapter::read_network);
|
||||
if(set_turn_data) {
|
||||
delete turn_data_;
|
||||
turn_data_ = NULL;
|
||||
|
@ -469,30 +464,20 @@ void playmp_controller::play_network_turn(){
|
|||
LOG_NG << "is networked...\n";
|
||||
|
||||
end_turn_enable(false);
|
||||
turn_info turn_data(player_number_, replay_sender_);
|
||||
turn_info turn_data(player_number_, replay_sender_, network_reader_);
|
||||
turn_data.host_transfer().attach_handler(this);
|
||||
|
||||
for(;;) {
|
||||
|
||||
if (!network_processing_stopped_){
|
||||
bool have_data = false;
|
||||
config cfg;
|
||||
|
||||
if(data_backlog_.empty() == false) {
|
||||
have_data = true;
|
||||
cfg = data_backlog_.front();
|
||||
data_backlog_.pop_front();
|
||||
} else {
|
||||
have_data = network::receive_data(cfg) != network::null_connection;
|
||||
}
|
||||
|
||||
if(have_data) {
|
||||
if(network_reader_.read(cfg)) {
|
||||
if (replay_last_turn_ <= turn()){
|
||||
if (skip_replay_) {
|
||||
skip_replay_ = false;
|
||||
}
|
||||
}
|
||||
const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg, data_backlog_, skip_replay_);
|
||||
const turn_info::PROCESS_DATA_RESULT result = turn_data.process_network_data(cfg, skip_replay_);
|
||||
if(player_type_changed_ == true)
|
||||
{
|
||||
//we received a player change/quit during waiting in get_user_choice/synced_context::pull_remote_user_input
|
||||
|
@ -536,7 +521,7 @@ void playmp_controller::play_network_turn(){
|
|||
}
|
||||
|
||||
void playmp_controller::init_turn_data() {
|
||||
turn_data_ = new turn_info(player_number_, replay_sender_);
|
||||
turn_data_ = new turn_info(player_number_, replay_sender_,network_reader_);
|
||||
turn_data_->host_transfer().attach_handler(this);
|
||||
}
|
||||
|
||||
|
@ -565,7 +550,7 @@ void playmp_controller::process_oos(const std::string& err_msg) const {
|
|||
}
|
||||
|
||||
void playmp_controller::handle_generic_event(const std::string& name){
|
||||
turn_info turn_data(player_number_, replay_sender_);
|
||||
turn_info turn_data(player_number_, replay_sender_, network_reader_);
|
||||
|
||||
if (name == "ai_user_interact"){
|
||||
playsingle_controller::handle_generic_event(name);
|
||||
|
@ -573,9 +558,7 @@ void playmp_controller::handle_generic_event(const std::string& name){
|
|||
}
|
||||
else if ((name == "ai_gamestate_changed") || (name == "ai_sync_network")){
|
||||
turn_info::PROCESS_DATA_RESULT res = turn_data.sync_network();
|
||||
//we should have stopped before getting end turn.
|
||||
//for res == PROCESS_END_TURN a OOS error would be better i think.
|
||||
assert(res == turn_info::PROCESS_CONTINUE || res == turn_info::PROCESS_RESTART_TURN);
|
||||
assert(res == turn_info::PROCESS_CONTINUE || res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_FOUND_DEPENDENT);
|
||||
if(res == turn_info::PROCESS_RESTART_TURN)
|
||||
{
|
||||
player_type_changed_ = true;
|
||||
|
|
|
@ -63,9 +63,9 @@ playsingle_controller::playsingle_controller(const config& level,
|
|||
const config& game_config, CVideo& video, bool skip_replay) :
|
||||
play_controller(level, state_of_game, ticks, num_turns, game_config, video, skip_replay),
|
||||
cursor_setter(cursor::NORMAL),
|
||||
data_backlog_(),
|
||||
textbox_info_(),
|
||||
replay_sender_(recorder),
|
||||
network_reader_(),
|
||||
end_turn_(false),
|
||||
player_type_changed_(false),
|
||||
replaying_(false),
|
||||
|
@ -609,7 +609,7 @@ void playsingle_controller::play_turn(bool save)
|
|||
init_side(player_number_ - 1);
|
||||
} catch (end_turn_exception) {
|
||||
if (current_team().is_network() == false) {
|
||||
turn_info turn_data(player_number_, replay_sender_);
|
||||
turn_info turn_data(player_number_, replay_sender_,network_reader_);
|
||||
recorder.end_turn();
|
||||
turn_data.sync_network();
|
||||
}
|
||||
|
@ -626,7 +626,7 @@ void playsingle_controller::play_turn(bool save)
|
|||
if (current_team().is_human() && side_units(player_number_) == 0
|
||||
&& (resources::units->size() != 0 || player_number_ != 1))
|
||||
{
|
||||
turn_info turn_data(player_number_, replay_sender_);
|
||||
turn_info turn_data(player_number_, replay_sender_, network_reader_);
|
||||
recorder.end_turn();
|
||||
turn_data.sync_network();
|
||||
continue;
|
||||
|
@ -926,7 +926,7 @@ void playsingle_controller::play_ai_turn(){
|
|||
synced_context::run_in_synced_context("auto_shroud", replay_helper::get_auto_shroud(true));
|
||||
}
|
||||
|
||||
turn_info turn_data(player_number_, replay_sender_);
|
||||
turn_info turn_data(player_number_, replay_sender_, network_reader_);
|
||||
|
||||
try {
|
||||
ai::manager::play_turn(player_number_);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define PLAYSINGLE_CONTROLLER_H_INCLUDED
|
||||
|
||||
#include "play_controller.hpp"
|
||||
#include "playturn_network_adapter.hpp"
|
||||
#include "replay.hpp"
|
||||
|
||||
class playsingle_controller : public play_controller
|
||||
|
@ -92,10 +93,10 @@ protected:
|
|||
void store_gold(bool obs = false);
|
||||
|
||||
const cursor::setter cursor_setter;
|
||||
std::deque<config> data_backlog_;
|
||||
gui::floating_textbox textbox_info_;
|
||||
|
||||
replay_network_sender replay_sender_;
|
||||
playturn_network_adapter network_reader_;
|
||||
|
||||
bool end_turn_;
|
||||
bool player_type_changed_;
|
||||
|
|
153
src/playturn.cpp
153
src/playturn.cpp
|
@ -38,14 +38,17 @@
|
|||
static lg::log_domain log_network("network");
|
||||
#define ERR_NW LOG_STREAM(err, log_network)
|
||||
|
||||
turn_info::turn_info(unsigned team_num, replay_network_sender &replay_sender) :
|
||||
turn_info::turn_info(unsigned team_num, replay_network_sender &replay_sender,playturn_network_adapter &network_reader) :
|
||||
team_num_(team_num),
|
||||
replay_sender_(replay_sender),
|
||||
host_transfer_("host_transfer"),
|
||||
replay_()
|
||||
network_reader_(network_reader)
|
||||
{
|
||||
/**
|
||||
* We do network sync so [init_side] is transferred to network hosts
|
||||
* TODO: i think it is unintiutive that creating this object automatictly sends data over the network.
|
||||
* For example it means that playmp_controller::handle_generic_event("ai_user_interact") casues send_data,
|
||||
* Idk whether that is intended, but an explicit call would be better.
|
||||
*/
|
||||
if(network::nconnections() > 0)
|
||||
send_data();
|
||||
|
@ -57,19 +60,18 @@ turn_info::~turn_info()
|
|||
|
||||
turn_info::PROCESS_DATA_RESULT turn_info::sync_network()
|
||||
{
|
||||
turn_info::PROCESS_DATA_RESULT retv = turn_info::PROCESS_CONTINUE;
|
||||
//there should be nothing left on the replay and we should get turn_info::PROCESS_CONTINUE back.
|
||||
turn_info::PROCESS_DATA_RESULT retv = replay_to_process_data_result(do_replay(team_num_));
|
||||
if(network::nconnections() > 0) {
|
||||
|
||||
//receive data first, and then send data. When we sent the end of
|
||||
//the AI's turn, we don't want there to be any chance where we
|
||||
//could get data back pertaining to the next turn.
|
||||
config cfg;
|
||||
while( (retv == turn_info::PROCESS_CONTINUE) && (network::receive_data(cfg) != network::null_connection)) {
|
||||
std::deque<config> backlog;
|
||||
retv = process_network_data(cfg,backlog,false);
|
||||
while( (retv == turn_info::PROCESS_CONTINUE) && network_reader_.read(cfg)) {
|
||||
retv = process_network_data(cfg,false);
|
||||
cfg.clear();
|
||||
}
|
||||
|
||||
send_data();
|
||||
}
|
||||
return retv;
|
||||
|
@ -84,33 +86,21 @@ void turn_info::send_data()
|
|||
}
|
||||
}
|
||||
|
||||
void turn_info::handle_turn(
|
||||
bool& turn_end,
|
||||
turn_info::PROCESS_DATA_RESULT turn_info::handle_turn(
|
||||
const config& t,
|
||||
const bool skip_replay,
|
||||
std::deque<config>& backlog)
|
||||
const bool skip_replay)
|
||||
{
|
||||
if(turn_end == false) {
|
||||
/** @todo FIXME: Check what commands we execute when it's our turn! */
|
||||
//TODO: can we remove replay_ ? i think yes.
|
||||
replay_.append(t);
|
||||
replay_.set_skip(skip_replay);
|
||||
//t can contain a [command] or a [upload_log]
|
||||
assert(t.all_children_count() == 1);
|
||||
/** @todo FIXME: Check what commands we execute when it's our turn! */
|
||||
|
||||
bool was_skipping = recorder.is_skipping();
|
||||
recorder.set_skip(skip_replay);
|
||||
//turn_end = do_replay(team_num_, &replay_);
|
||||
//note, that this cunfion might call itself recursively: do_replay -> ... -> persist_var -> ... -> handle_generic_event -> sync_network -> handle_turn
|
||||
recorder.add_config(t, replay::MARK_AS_SENT);
|
||||
turn_end = do_replay(team_num_) == REPLAY_FOUND_END_TURN;
|
||||
recorder.set_skip(was_skipping);
|
||||
|
||||
} else {
|
||||
|
||||
//this turn has finished, so push the remaining moves
|
||||
//into the backlog
|
||||
backlog.push_back(config());
|
||||
backlog.back().add_child("turn", t);
|
||||
}
|
||||
bool was_skipping = recorder.is_skipping();
|
||||
recorder.set_skip(skip_replay);
|
||||
//note, that this function might call itself recursively: do_replay -> ... -> persist_var -> ... -> handle_generic_event -> sync_network -> handle_turn
|
||||
recorder.add_config(t, replay::MARK_AS_SENT);
|
||||
PROCESS_DATA_RESULT retv = replay_to_process_data_result(do_replay(team_num_));
|
||||
recorder.set_skip(was_skipping);
|
||||
return retv;
|
||||
}
|
||||
|
||||
void turn_info::do_save()
|
||||
|
@ -121,56 +111,69 @@ void turn_info::do_save()
|
|||
}
|
||||
}
|
||||
|
||||
turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg,
|
||||
std::deque<config>& backlog, bool skip_replay)
|
||||
turn_info::PROCESS_DATA_RESULT turn_info::process_network_data_from_reader(bool skip_replay)
|
||||
{
|
||||
config cfg;
|
||||
while(this->network_reader_.read(cfg))
|
||||
{
|
||||
PROCESS_DATA_RESULT res = process_network_data(cfg, skip_replay);
|
||||
if(res != PROCESS_CONTINUE)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return PROCESS_CONTINUE;
|
||||
}
|
||||
|
||||
turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg, bool skip_replay)
|
||||
{
|
||||
// we cannot be connected to multiple peers anymore because
|
||||
// the simple wesnothserver implementation in wesnoth was removed years ago.
|
||||
assert(network::nconnections() <= 1);
|
||||
|
||||
assert(cfg.all_children_count() == 1);
|
||||
assert(cfg.attribute_range().first == cfg.attribute_range().second);
|
||||
if(!recorder.at_end())
|
||||
{
|
||||
ERR_NW << "processing network data while still having data on the replay.\n";
|
||||
}
|
||||
|
||||
if (const config &msg = cfg.child("message"))
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), msg["sender"], msg["side"],
|
||||
msg["message"], events::chat_handler::MESSAGE_PUBLIC,
|
||||
preferences::message_bell());
|
||||
}
|
||||
|
||||
if (const config &msg = cfg.child("whisper") /*&& is_observer()*/)
|
||||
else if (const config &msg = cfg.child("whisper") /*&& is_observer()*/)
|
||||
{
|
||||
resources::screen->add_chat_message(time(NULL), "whisper: " + msg["sender"].str(), 0,
|
||||
msg["message"], events::chat_handler::MESSAGE_PRIVATE,
|
||||
preferences::message_bell());
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const config &ob, cfg.child_range("observer")) {
|
||||
else if (const config &ob = cfg.child("observer") )
|
||||
{
|
||||
resources::screen->add_observer(ob["name"]);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const config &ob, cfg.child_range("observer_quit")) {
|
||||
else if (const config &ob = cfg.child("observer_quit"))
|
||||
{
|
||||
resources::screen->remove_observer(ob["name"]);
|
||||
}
|
||||
|
||||
if (cfg.child("leave_game")) {
|
||||
else if (cfg.child("leave_game")) {
|
||||
throw network::error("");
|
||||
}
|
||||
|
||||
bool turn_end = false;
|
||||
|
||||
config::const_child_itors turns = cfg.child_range("turn");
|
||||
|
||||
const config& change = cfg.child_or_empty("change_controller");
|
||||
const std::string& side_drop = cfg["side_drop"].str();
|
||||
|
||||
BOOST_FOREACH(const config &t, turns)
|
||||
else if (const config &turn = cfg.child("turn"))
|
||||
{
|
||||
recorder.add_config(t, replay::MARK_AS_SENT);
|
||||
return handle_turn(turn, skip_replay);
|
||||
}
|
||||
handle_turn(turn_end, config(), skip_replay, backlog);
|
||||
|
||||
resources::whiteboard->process_network_data(cfg);
|
||||
|
||||
if (!change.empty())
|
||||
else if (cfg.has_child("whiteboard"))
|
||||
{
|
||||
resources::whiteboard->process_network_data(cfg);
|
||||
}
|
||||
else if (const config &change = cfg.child("change_controller"))
|
||||
{
|
||||
if(change.empty())
|
||||
{
|
||||
return PROCESS_CONTINUE;
|
||||
}
|
||||
//don't use lexical_cast_default it's "safer" to end on error
|
||||
const int side = lexical_cast<int>(change["side"]);
|
||||
const size_t index = static_cast<size_t>(side-1);
|
||||
|
@ -224,10 +227,12 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
return restart ? PROCESS_RESTART_TURN : PROCESS_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
//if a side has dropped out of the game.
|
||||
if(!side_drop.empty()) {
|
||||
const std::string controller = cfg["controller"];
|
||||
|
||||
else if (const config &side_drop_c = cfg.child("side_drop"))
|
||||
{
|
||||
const std::string& side_drop = side_drop_c["side_num"].str();
|
||||
const std::string controller = side_drop_c["controller"];
|
||||
//if a side has dropped out of the game.
|
||||
int side = atoi(side_drop.c_str());
|
||||
const size_t side_index = side-1;
|
||||
|
||||
|
@ -367,7 +372,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
|
||||
// The host has ended linger mode in a campaign -> enable the "End scenario" button
|
||||
// and tell we did get the notification.
|
||||
if (cfg.child("notify_next_scenario")) {
|
||||
else if (cfg.child("notify_next_scenario")) {
|
||||
gui::button* btn_end = resources::screen->find_action_button("button-endturn");
|
||||
if(btn_end) {
|
||||
btn_end->enable(true);
|
||||
|
@ -376,13 +381,17 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
|
|||
}
|
||||
|
||||
//If this client becomes the new host, notify the play_controller object about it
|
||||
if (const config &cfg_host_transfer = cfg.child("host_transfer")){
|
||||
else if (const config &cfg_host_transfer = cfg.child("host_transfer")){
|
||||
if (cfg_host_transfer["value"] == "1") {
|
||||
host_transfer_.notify_observers();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR_NW << "found unknown command:\n" << cfg.debug() << "\n";
|
||||
}
|
||||
|
||||
return turn_end ? PROCESS_END_TURN : PROCESS_CONTINUE;
|
||||
return PROCESS_CONTINUE;
|
||||
}
|
||||
|
||||
void turn_info::change_controller(const std::string& side, const std::string& controller)
|
||||
|
@ -405,6 +414,22 @@ void turn_info::change_side_controller(const std::string& side, const std::strin
|
|||
network::send_data(cfg, 0);
|
||||
}
|
||||
|
||||
turn_info::PROCESS_DATA_RESULT turn_info::replay_to_process_data_result(REPLAY_RETURN replayreturn)
|
||||
{
|
||||
switch(replayreturn)
|
||||
{
|
||||
case REPLAY_RETURN_AT_END:
|
||||
return PROCESS_CONTINUE;
|
||||
case REPLAY_FOUND_DEPENDENT:
|
||||
return PROCESS_FOUND_DEPENDENT;
|
||||
case REPLAY_FOUND_END_TURN:
|
||||
return PROCESS_END_TURN;
|
||||
default:
|
||||
assert(false);
|
||||
throw "found invalid REPLAY_RETURN";
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void turn_info::take_side(const std::string& side, const std::string& controller)
|
||||
{
|
||||
|
|
|
@ -20,46 +20,47 @@ class replay_network_sender;
|
|||
|
||||
#include "generic_event.hpp"
|
||||
#include "network.hpp"
|
||||
#include "playturn_network_adapter.hpp"
|
||||
#include "replay.hpp"
|
||||
|
||||
class turn_info
|
||||
{
|
||||
public:
|
||||
turn_info(unsigned team_num, replay_network_sender &network_sender);
|
||||
turn_info(unsigned team_num, replay_network_sender &network_sender, playturn_network_adapter &network_reader);
|
||||
|
||||
~turn_info();
|
||||
|
||||
|
||||
|
||||
enum PROCESS_DATA_RESULT {
|
||||
enum PROCESS_DATA_RESULT
|
||||
{
|
||||
PROCESS_CONTINUE,
|
||||
PROCESS_RESTART_TURN,
|
||||
PROCESS_END_TURN,
|
||||
/** When the host uploaded the next scenario this is returned. */
|
||||
PROCESS_END_LINGER
|
||||
};
|
||||
PROCESS_END_LINGER,
|
||||
/** When we couldn't process the network data because we found a dependent command, this should only happen if we were called playmp_controller::from handle_generic_event -> sync_network*/
|
||||
PROCESS_FOUND_DEPENDENT
|
||||
};
|
||||
|
||||
PROCESS_DATA_RESULT sync_network();
|
||||
|
||||
void send_data();
|
||||
|
||||
//function which will process incoming network data, and act on it. If there is
|
||||
//more data than a single turn's worth, excess data will be placed into 'backlog'.
|
||||
//No more than one turn's worth of data will be placed into a single backlog item,
|
||||
//so it is safe to assume that backlog won't be touched if cfg is a member of a previous
|
||||
//backlog.
|
||||
PROCESS_DATA_RESULT process_network_data(const config& cfg,std::deque<config>& backlog, bool skip_replay);
|
||||
//function which will process incoming network data received with playturn_network_adapter, and act on it.
|
||||
PROCESS_DATA_RESULT process_network_data(const config& cfg, bool skip_replay);
|
||||
|
||||
//reads as much data from network_reader_ as possible and processed it.
|
||||
PROCESS_DATA_RESULT process_network_data_from_reader(bool skip_replay);
|
||||
|
||||
events::generic_event& host_transfer() { return host_transfer_; }
|
||||
private:
|
||||
static void change_controller(const std::string& side, const std::string& controller);
|
||||
static void change_side_controller(const std::string& side, const std::string& player);
|
||||
|
||||
void handle_turn(
|
||||
bool& turn_end,
|
||||
static PROCESS_DATA_RESULT replay_to_process_data_result(REPLAY_RETURN replayreturn);
|
||||
PROCESS_DATA_RESULT handle_turn(
|
||||
const config& t,
|
||||
const bool skip_replay,
|
||||
std::deque<config>& backlog);
|
||||
const bool skip_replay);
|
||||
|
||||
void do_save();
|
||||
|
||||
|
@ -69,7 +70,7 @@ private:
|
|||
|
||||
events::generic_event host_transfer_;
|
||||
|
||||
replay replay_;
|
||||
playturn_network_adapter& network_reader_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
149
src/playturn_network_adapter.cpp
Normal file
149
src/playturn_network_adapter.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
#include "playturn_network_adapter.hpp"
|
||||
#include "network.hpp"
|
||||
#include "config_assign.hpp"
|
||||
#include "log.hpp"
|
||||
|
||||
#include <boost/assign.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
static lg::log_domain log_network("network");
|
||||
#define ERR_NW LOG_STREAM(err, log_network)
|
||||
|
||||
void playturn_network_adapter::read_from_network()
|
||||
{
|
||||
assert(data_.size() > 0);
|
||||
|
||||
this->data_.push_back(config());
|
||||
config& back = data_.back();
|
||||
bool has_data = this->network_reader_(back);
|
||||
//ping is handeled by network.cpp and we can ignore it.
|
||||
back.remove_attribute("ping");
|
||||
if((!has_data) || back.empty())
|
||||
{
|
||||
this->data_.pop_back();
|
||||
return;
|
||||
}
|
||||
assert(!data_.back().empty());
|
||||
|
||||
if(back.has_attribute("side_drop"))
|
||||
{
|
||||
config child;
|
||||
child["side_num"] = back["side_drop"];
|
||||
child["controller"] = back["controller"];
|
||||
this->data_.push_back(config_of("side_drop", child));
|
||||
back.remove_attribute("side_drop");
|
||||
back.remove_attribute("controller");
|
||||
}
|
||||
assert(!data_.back().empty());
|
||||
//there should be no attributes left.
|
||||
|
||||
if(back.attribute_range().first != back.attribute_range().second )
|
||||
{
|
||||
ERR_NW << "found unexpected attribute:" <<back.debug() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool playturn_network_adapter::is_at_end()
|
||||
{
|
||||
assert(data_.size() > 0);
|
||||
return this->next_ == data_.back().ordered_end();
|
||||
}
|
||||
|
||||
bool playturn_network_adapter::read(config& dst)
|
||||
{
|
||||
assert(dst.empty());
|
||||
if(is_at_end())
|
||||
{
|
||||
read_from_network();
|
||||
}
|
||||
if(is_at_end())
|
||||
{
|
||||
//that means we couldn't read anything from the network.
|
||||
return false;
|
||||
}
|
||||
//skip empty data.
|
||||
while(next_ == data_.begin()->ordered_end())
|
||||
{
|
||||
data_.pop_front();
|
||||
next_ = data_.front().ordered_begin();
|
||||
assert(!is_at_end());
|
||||
}
|
||||
config& child = dst.add_child(next_->key);
|
||||
//TODO: implement a non const version of ordered children
|
||||
config& child_old = const_cast<config&>(next_->cfg);
|
||||
if(next_->key == "turn")
|
||||
{
|
||||
//split [turn] indo different [turn] for each child.
|
||||
assert(next_->cfg.all_children_count() > next_command_num_);
|
||||
config::all_children_iterator itor = child_old.ordered_begin();
|
||||
//TODO: implement operator + (all_children_iterator, int ) properly
|
||||
for(unsigned int i = 0; i < next_command_num_; i++) {itor++;}
|
||||
//TODO: implement a non const version of ordered children
|
||||
config& childchild_old = const_cast<config&>(itor->cfg);
|
||||
config& childchild = child.add_child(itor->key);
|
||||
childchild.swap(childchild_old);
|
||||
|
||||
next_command_num_++;
|
||||
if(next_->cfg.all_children_count() == next_command_num_)
|
||||
{
|
||||
next_command_num_ = 0;
|
||||
next_++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
child.swap(child_old);
|
||||
next_++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
playturn_network_adapter::playturn_network_adapter(source_type source)
|
||||
: data_(boost::assign::list_of(config()).convert_to_container<std::list<config> >()),
|
||||
next_(data_.front().ordered_end()),
|
||||
next_command_num_(0),
|
||||
network_reader_(source)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
playturn_network_adapter::~playturn_network_adapter()
|
||||
{
|
||||
if(!is_at_end())
|
||||
{
|
||||
ERR_NW << "Destroing playturn_network_adapter with an non empty buffer, this means loss of network data\n";
|
||||
}
|
||||
}
|
||||
|
||||
void playturn_network_adapter::set_source(source_type source)
|
||||
{
|
||||
network_reader_ = source;
|
||||
}
|
||||
|
||||
static bool read_config(config& src, config& dst)
|
||||
{
|
||||
assert(dst.empty());
|
||||
if(!src.empty())
|
||||
{
|
||||
src.swap(dst);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
playturn_network_adapter::source_type playturn_network_adapter::get_source_from_config(config& cfg)
|
||||
{
|
||||
return boost::bind(read_config, cfg, _1);
|
||||
}
|
||||
|
||||
bool playturn_network_adapter::read_network(config& cfg)
|
||||
{
|
||||
return network::receive_data(cfg) != network::null_connection;
|
||||
}
|
46
src/playturn_network_adapter.hpp
Normal file
46
src/playturn_network_adapter.hpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
|
||||
#ifndef PLAYTURN_NETWORK_ADAPTER_HPP_INCLUDED
|
||||
#define PLAYTURN_NETWORK_ADAPTER_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
#include <list>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
/*
|
||||
The purpose if this class is to preprocess incoming network data, and provide a steam that always returns just one command/action at a time.
|
||||
Especialy we want each replay command in his own [turn].
|
||||
*/
|
||||
class playturn_network_adapter
|
||||
{
|
||||
public:
|
||||
typedef boost::function1<bool, config&> source_type;
|
||||
|
||||
playturn_network_adapter(source_type source = read_network);
|
||||
~playturn_network_adapter();
|
||||
|
||||
//returns true on success.
|
||||
//dst has to be empty befor the call.
|
||||
//after the call dst contains one child chen returned true otherise it's empty.
|
||||
bool read(config& dst);
|
||||
//returns false if there is still data in the internal buffer.
|
||||
bool is_at_end();
|
||||
void set_source(source_type source);
|
||||
//returns a function to be passed to set_source.
|
||||
static source_type get_source_from_config(config& src);
|
||||
//a function to be passed to set_source.
|
||||
static bool read_network(config& dst);
|
||||
private:
|
||||
//reads data from the network stream.
|
||||
void read_from_network();
|
||||
//this always contains one empty config becasue we want a vaid value for next_.
|
||||
std::list<config> data_;
|
||||
//the position of the next to be received element in data_->front().
|
||||
config::all_children_iterator next_;
|
||||
//if we are processing a [turn] with mutiple [command] we want to split them.
|
||||
//In this case next_command_num_ is the next to be processed turn into a command otherwise it's 0;
|
||||
unsigned int next_command_num_;
|
||||
//a function to receive data from the network.
|
||||
source_type network_reader_;
|
||||
};
|
||||
#endif
|
Loading…
Add table
Reference in a new issue