Example #1
0
void user_choice_manager::pull()
{
	// there might be speak or similar commands in the replay before the user input.
	do_replay_handle();
	synced_context::pull_remote_user_input();
	do_replay_handle();
	update_local_choice();
	search_in_replay();
}
Example #2
0
user_choice_manager::user_choice_manager(const std::string &name, const mp_sync::user_choice &uch, std::set<int> sides)
	: required_(sides)
	, res_()
	, local_choice_(0)
	, wait_message_()
	, oos_(false)
	, uch_(uch)
	, tagname_(name)
	, current_side_(resources::controller->current_side())
	, changed_event_("user_choice_update")
{
	update_local_choice();
	const int max_side  = static_cast<int>(resources::gameboard->teams().size());

	for(int side : required_)
	{
		assert(1 <= side && side <= max_side);
		const team& t = resources::gameboard->teams()[side-1];
		assert(!t.is_empty());
		if(side != current_side_)
		{
			synced_context::set_is_simultaneously();
		}
	}

	do_replay_handle();
	search_in_replay();

}
Example #3
0
void user_choice_manager::search_in_replay()
{
	while(!finished() && !oos_)
	{
		do_replay_handle();
		if(resources::recorder->at_end()) {
			return;
		}

		DBG_REPLAY << "MP synchronization: extracting choice from replay with has_local_side=" << has_local_choice() << "\n";

		const config *action = resources::recorder->get_next_action();
		assert(action); //action cannot be null because resources::recorder->at_end() returned false.
		if( !action->has_child(tagname_) || !(*action)["dependent"].to_bool())
		{
			replay::process_error("[" + tagname_ + "] expected but none found\n. found instead:\n" + action->debug());
			//We save this action for later
			resources::recorder->revert_action();
			// execute this local choice locally
			oos_ = true;
			changed_event_.notify_observers();
			return;
		}
		int from_side = (*action)["from_side"].to_int(0);
		if((*action)["side_invalid"].to_bool(false) == true)
		{
			//since this 'cheat' can have a quite heavy effect especialy in umc content we give an oos error .
			replay::process_error("MP synchronization: side_invalid in replay data, this could mean someone wants to cheat.\n");
		}
		if(required_.find(from_side) == required_.end())
		{
			replay::process_error("MP synchronization: we got an answer from side " + std::to_string(from_side) + "for [" + tagname_ + "] which is not was we expected\n");
		}
		if(res_.find(from_side) != res_.end())
		{
			replay::process_error("MP synchronization: we got already our answer from side " + std::to_string(from_side) + "for [" + tagname_ + "] now we have it twice.\n");
		}
		res_[from_side] = action->child(tagname_);
		changed_event_.notify_observers();
	}
}
config synced_context::ask_server_choice(const server_choice& sch)
{
	if (!is_synced()) {
		ERR_REPLAY << "Trying to ask the server for a '" << sch.name() << "' choice in a unsynced context, doing the choice locally. This can cause OOS.\n";
		return sch.local_choice();
	}
	set_is_simultaneously();
	resources::controller->increase_server_request_number();
	const bool is_mp_game = resources::controller->is_networked_mp();
	bool did_require = false;

	DBG_REPLAY << "ask_server for random_seed\n";
	/*
		as soon as random or similar is involved, undoing is impossible.
	*/
	resources::undo_stack->clear();
	/*
		there might be speak or similar commands in the replay before the user input.
	*/
	while(true) {

		do_replay_handle();
		bool is_replay_end = resources::recorder->at_end();

		if (is_replay_end && !is_mp_game)
		{
			/* The decision is ours, and it will be inserted
			into the replay. */
			DBG_REPLAY << "MP synchronization: local server choice\n";
			leave_synced_context sync;
			config cfg = sch.local_choice();
			//-1 for "server" todo: change that.
			resources::recorder->user_input(sch.name(), cfg, -1);
			return cfg;

		}
		else if(is_replay_end && is_mp_game)
		{
			DBG_REPLAY << "MP synchronization: remote server choice\n";

			//here we can get into the situation that the decision has already been made but not received yet.
			synced_context::pull_remote_user_input();
			//FIXME: we should call play_controller::play_silce or the application will freeze while waiting for a remote choice.
			resources::controller->play_slice();
			/*
				we don't want to send multiple "require_random" to the server.
			*/
			if(!did_require)
			{
				sch.send_request();
				did_require = true;
			}

			SDL_Delay(10);
			continue;

		}
		else if (!is_replay_end)
		{
			/* The decision has already been made, and must
			be extracted from the replay. */
			DBG_REPLAY << "MP synchronization: replay server choice\n";
			do_replay_handle();
			const config *action = resources::recorder->get_next_action();
			if (!action)
			{
				replay::process_error("[" + std::string(sch.name()) + "] expected but none found\n");
				resources::recorder->revert_action();
				return sch.local_choice();
			}
			if (!action->has_child(sch.name()))
			{
				replay::process_error("[" + std::string(sch.name()) + "] expected but none found, found instead:\n " + action->debug() + "\n");

				resources::recorder->revert_action();
				return sch.local_choice();
			}
			if((*action)["from_side"].str() != "server" || (*action)["side_invalid"].to_bool(false) )
			{
				//we can proceed without getting OOS in this case, but allowing this would allow a "player chan choose their attack results in mp" cheat
				replay::process_error("wrong from_side or side_invalid this could mean someone wants to cheat\n");
			}
			return action->child(sch.name());
		}
	}
}