Beispiel #1
0
bool synced_context::can_undo()
{
	//this method should only works in a synced context.
	assert(is_synced());
	//if we called the rng or if we sended data of this action over the network already, undoing is impossible.
	return (!is_simultaneously_) && (random_new::generator->get_random_calls() == 0);
}
Beispiel #2
0
void calculate_conversion(void)
{
	for(i=0; (i < MAX_ENTRIES) && (table[i].state != ENTRY_FULL); ++i);
	
	if (i >= MAX_ENTRIES) return;	// table is empty
	
	local_average = table[i].local_time;
	offset_average = table[i].time_offset;
	
	local_sum = 0;
	offset_sum = 0;
	
	while (++i < MAX_ENTRIES)
	{
		if (table[i].state == ENTRY_FULL)
		{
			local_sum +=
				(long)(table[i].local_time - local_average) / table_entries;
			offset_sum +=
				(long)(table[i].time_offset - offset_average) / table_entries;
		}
	}
	
	local_average += local_sum;
	offset_average += offset_sum;
	
	local_sum = offset_sum = 0;
	for (i=0; i < MAX_ENTRIES; ++i)
	{
		if (table[i].state == ENTRY_FULL)
		{
			a = table[i].local_time - local_average;
			b = table[i].time_offset - offset_average;
			
			local_sum += (long long)a * a;
			offset_sum += (long long)a * b;
		}
	}
	
	if (local_sum != 0)
		skew = (float)offset_sum / (float)local_sum;

	num_entries = table_entries;
	
	PRINTF("FTSP conversion calculated: num_entries=%u, is_synced=%u\n",
	       num_entries, is_synced());
}	
Beispiel #3
0
int synced_context::get_unit_id_diff()
{
	//this method only works in a synced context.
	assert(is_synced());
	return resources::gameboard->unit_id_manager().get_save_id() - last_unit_id_;
}
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());
		}
	}
}
Beispiel #5
0
void add_new_entry(void)
{
	free_item = -1;
	oldest_item = 0;
	age = 0;
	oldest_time = 0;
	
	table_entries = 0;
	
	time_error = (long)(local_to_global(msg_rcv.local_time)
			- msg_rcv.global_time);
	if (is_synced() == FTSP_OK)
	{
		PRINTF("FTSP synced, error %ld\n", time_error);
		if ((time_error > ENTRY_THROWOUT_LIMIT) ||
				(-time_error > ENTRY_THROWOUT_LIMIT))
		{
			PRINTF("(big)\n");
			if (++num_errors > 3)
			{
				PRINTF("FTSP: num_errors > 3 => clear_table()\n");
				clear_table();
			}
		}
		else
		{
			PRINTF("(small)\n");
			num_errors = 0;
		}
	}
	else
	{
		PRINTF("FTSP not synced\n");
	}
	
	for (i=0; i < MAX_ENTRIES; ++i)
	{
		age = msg_rcv.local_time - table[i].local_time;
		
		if (age >= 0x7FFFFFFFL)
			table[i].state = ENTRY_EMPTY;
		
		if (table[i].state == ENTRY_EMPTY)
			free_item = i;
		else
			++table_entries;
		
		if (age >= oldest_time)
		{
			oldest_time = age;
			oldest_item = i;
		}
	}
	
	if (free_item < 0)
		free_item = oldest_item;
	else
		++table_entries;
	
	table[free_item].state = ENTRY_FULL;
	table[free_item].local_time = msg_rcv.local_time;
	table[free_item].time_offset = msg_rcv.global_time - msg_rcv.local_time;
}