Added possibility to use per fight EV statistics proposed by maboul

Fixed possible cause for client disconnects when having bad connection

Cleaned up game config reading code

minor code cleanups
This commit is contained in:
Pauli Nieminen 2008-05-11 19:34:32 +00:00
parent 9adac709e8
commit 859958b006
8 changed files with 168 additions and 91 deletions

View file

@ -57,6 +57,7 @@ opts.AddOptions(
BoolOption('profile', 'Set to build for profiling', False),
('program_suffix', 'suffix to append to names of installed programs',""),
BoolOption('python', 'Enable in-game python extensions.', True),
BoolOption('maboul_stats', 'Enable alternative excpeted damage calculations', False),
BoolOption('raw_sockets', 'Set to use raw receiving sockets in the multiplayer network layer rather than the SDL_net facilities', False),
('server_gid', 'group id of the user who runs wesnothd', ""),
('server_uid', 'user id of the user who runs wesnothd', ""),
@ -239,6 +240,9 @@ if env['internal_data']:
if env['python']:
env.Append(CPPDEFINES = "HAVE_PYTHON")
if env['maboul_stats']:
env.Append(CPPDEFINES = "MABOUL_STATS")
if sys.platform != "win32":
if env['prefsdir']:
env.Append(CPPDEFINES = "PREFERENCES_DIR='\"%s\"'" % env['prefsdir'] )

View file

@ -41,8 +41,9 @@ Version 1.5.0+svn:
* fixed multiplayer_connect to handle leave_game command from server
* improved reloading of game configs after installing or removing addons
* fixed threading bug in upload_logs
* dissallow_observers is forced on if side has controller=null
* dissallow_observers is on as default if side has controller=null
* Fixed null-pointer reference in network code
* Added possibility to use per fight EV statistics proposed by maboul
* Removed bug introduced in 1.5.0 that allowed use of :debug commands during
network play
* added some includes to fix compilation problems with Sun Studio 12

View file

@ -917,6 +917,19 @@ attack::attack(game_display& gui, const gamemap& map,
DBG_NG << "getting attack statistics\n";
statistics::attack_context attack_stats(a_->second, d_->second, a_stats_->chance_to_hit, d_stats_->chance_to_hit);
#ifdef MABOUL_STATS
{
// Calculate stats for battle
combatant attacker(bc_->get_attacker_stats());
combatant defender(bc_->get_defender_stats());
attacker.fight(defender);
const double attacker_inflict = static_cast<double>(d_->second.hitpoints()) - defender.average_hp();
const double defender_inflict = static_cast<double>(a_->second.hitpoints()) - attacker.average_hp();
attack_stats.attack_excepted_damage(attacker_inflict,defender_inflict);
}
#endif
orig_attacks_ = a_stats_->num_blows;
orig_defends_ = d_stats_->num_blows;
n_attacks_ = orig_attacks_;
@ -1010,10 +1023,13 @@ attack::attack(game_display& gui, const gamemap& map,
*a_stats_->weapon,d_stats_->weapon,
abs_n_attack_,float_text,a_stats_->drains,"");
}
const int drains_damage = a_stats_->drains ? damage_defender_takes / 2 : 0;
const int damage_done = minimum<int>(d_->second.hitpoints(), damage_defender_takes);
bool dies = d_->second.take_hit(damage_defender_takes);
LOG_NG << "defender took " << damage_defender_takes << (dies ? " and died" : "") << "\n";
attack_stats.attack_result(hits ? (dies ? statistics::attack_context::KILLS : statistics::attack_context::HITS)
: statistics::attack_context::MISSES, attacker_damage_);
: statistics::attack_context::MISSES, damage_done,drains_damage);
if(ran_results == NULL) {
log_scope2(engine, "setting random results");
@ -1261,6 +1277,8 @@ attack::attack(game_display& gui, const gamemap& map,
*d_stats_->weapon,a_stats_->weapon,
abs_n_defend_,float_text,d_stats_->drains,"");
}
const int drains_damage = d_stats_->drains ? damage_attacker_takes / 2 : 0;
const int damage_done = minimum<int>(a_->second.hitpoints(), damage_attacker_takes);
bool dies = a_->second.take_hit(damage_attacker_takes);
LOG_NG << "attacker took " << damage_attacker_takes << (dies ? " and died" : "") << "\n";
if(dies) {
@ -1305,7 +1323,7 @@ attack::attack(game_display& gui, const gamemap& map,
refresh_bc();
}
attack_stats.defend_result(hits ? (dies ? statistics::attack_context::KILLS : statistics::attack_context::HITS)
: statistics::attack_context::MISSES, defender_damage_);
: statistics::attack_context::MISSES, damage_done, drains_damage);
if(hits || dies){
int amount_drained = d_stats_->drains ? defender_damage_ / 2 : 0;

View file

@ -117,6 +117,7 @@ public:
void reset_game_cfg();
void reset_defines_map();
void reload_changed_game_config();
void read_configs(std::string&);
bool is_loading() const;
bool load_game();
@ -1751,6 +1752,91 @@ void game_controller::show_upload_begging()
disp().redraw_everything();
}
void game_controller::read_configs(std::string& error_log)
{
preproc_map defines_map(defines_map_);
std::string user_error_log;
//read the file and then write to the cache
scoped_istream stream = preprocess_file("data/", &defines_map, &error_log);
//reset the parse counter before reading the game files
if (loadscreen::global_loadscreen) {
loadscreen::global_loadscreen->parser_counter = 0;
}
read(game_config_, *stream, &error_log);
//load usermade add-ons
const std::string user_campaign_dir = get_user_data_dir() + "/data/campaigns/";
std::vector<std::string> user_campaigns, error_campaigns;
get_files_in_dir(user_campaign_dir,NULL,&user_campaigns,ENTIRE_FILE_PATH);
for(std::vector<std::string>::const_iterator uc = user_campaigns.begin(); uc != user_campaigns.end(); ++uc) {
std::string oldstyle_cfg = *uc + ".cfg";
std::string main_cfg = *uc + "/_main.cfg";
std::string toplevel;
if (file_exists(oldstyle_cfg))
toplevel = oldstyle_cfg;
else if (file_exists(main_cfg))
toplevel = main_cfg;
else
continue;
try {
preproc_map user_defines_map(defines_map);
scoped_istream stream = preprocess_file(toplevel, &user_defines_map);
std::string campaign_error_log;
config user_campaign_cfg;
read(user_campaign_cfg,*stream,&campaign_error_log);
if(campaign_error_log.empty()) {
game_config_.append(user_campaign_cfg);
} else {
user_error_log += campaign_error_log;
error_campaigns.push_back(*uc);
}
} catch(config::error& err) {
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
error_campaigns.push_back(*uc);
user_error_log += err.message + "\n";
} catch(preproc_config::error&) {
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
error_campaigns.push_back(*uc);
//no need to modify the error log here, already done by the preprocessor
} catch(io_exception&) {
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
error_campaigns.push_back(*uc);
}
}
if(error_campaigns.empty() == false) {
std::stringstream msg;
msg << _n("The following add-on had errors and could not be loaded:",
"The following add-ons had errors and could not be loaded:",
error_campaigns.size());
for(std::vector<std::string>::const_iterator i = error_campaigns.begin(); i != error_campaigns.end(); ++i) {
msg << "\n" << *i;
}
msg << "\n" << _("ERROR DETAILS:") << "\n" << font::nullify_markup(user_error_log);
gui::show_error_message(disp(),msg.str());
}
game_config_.merge_children("units");
config& hashes = game_config_.add_child("multiplayer_hashes");
for(config::child_list::const_iterator ch = game_config_.get_children("multiplayer").begin(); ch != game_config_.get_children("multiplayer").end(); ++ch) {
hashes[(**ch)["id"]] = (*ch)->hash();
}
}
//this function reads the game configuration, searching for valid cached copies first
void game_controller::read_game_cfg(bool use_cache)
{
@ -1816,86 +1902,10 @@ void game_controller::read_game_cfg(bool use_cache)
LOG_CONFIG << "no valid cache found. Writing cache to '" << fname << " with defines_map "<< str.str() << "'\n";
DBG_CONFIG << ((use_cache && file_exists(fname)) ? "yes":"no ") << " " << dir_checksum.modified << "==" << data_tree_checksum().modified << " " << dir_checksum.nfiles << "==" << data_tree_checksum().nfiles << " " << dir_checksum.sum_size << "==" << data_tree_checksum().sum_size << "\n";
preproc_map defines_map(defines_map_);
std::string error_log, user_error_log;
std::string error_log;
//read the file and then write to the cache
scoped_istream stream = preprocess_file("data/", &defines_map, &error_log);
//reset the parse counter before reading the game files
if (loadscreen::global_loadscreen) {
loadscreen::global_loadscreen->parser_counter = 0;
}
read(game_config_, *stream, &error_log);
//load usermade add-ons
const std::string user_campaign_dir = get_user_data_dir() + "/data/campaigns/";
std::vector<std::string> user_campaigns, error_campaigns;
get_files_in_dir(user_campaign_dir,NULL,&user_campaigns,ENTIRE_FILE_PATH);
for(std::vector<std::string>::const_iterator uc = user_campaigns.begin(); uc != user_campaigns.end(); ++uc) {
std::string oldstyle_cfg = *uc + ".cfg";
std::string main_cfg = *uc + "/_main.cfg";
std::string toplevel;
if (file_exists(oldstyle_cfg))
toplevel = oldstyle_cfg;
else if (file_exists(main_cfg))
toplevel = main_cfg;
else
continue;
try {
preproc_map user_defines_map(defines_map);
scoped_istream stream = preprocess_file(toplevel, &user_defines_map);
std::string campaign_error_log;
config user_campaign_cfg;
read(user_campaign_cfg,*stream,&campaign_error_log);
if(campaign_error_log.empty()) {
game_config_.append(user_campaign_cfg);
} else {
user_error_log += campaign_error_log;
error_campaigns.push_back(*uc);
}
} catch(config::error& err) {
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
error_campaigns.push_back(*uc);
user_error_log += err.message + "\n";
} catch(preproc_config::error&) {
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
error_campaigns.push_back(*uc);
//no need to modify the error log here, already done by the preprocessor
} catch(io_exception&) {
ERR_CONFIG << "error reading usermade add-on '" << *uc << "'\n";
error_campaigns.push_back(*uc);
}
}
if(error_campaigns.empty() == false) {
std::stringstream msg;
msg << _n("The following add-on had errors and could not be loaded:",
"The following add-ons had errors and could not be loaded:",
error_campaigns.size());
for(std::vector<std::string>::const_iterator i = error_campaigns.begin(); i != error_campaigns.end(); ++i) {
msg << "\n" << *i;
}
msg << "\n" << _("ERROR DETAILS:") << "\n" << font::nullify_markup(user_error_log);
gui::show_error_message(disp(),msg.str());
}
game_config_.merge_children("units");
config& hashes = game_config_.add_child("multiplayer_hashes");
for(config::child_list::const_iterator ch = game_config_.get_children("multiplayer").begin(); ch != game_config_.get_children("multiplayer").end(); ++ch) {
hashes[(**ch)["id"]] = (*ch)->hash();
}
read_configs(error_log);
if(!error_log.empty()) {
gui::show_error_message(disp(),
@ -1915,15 +1925,22 @@ void game_controller::read_game_cfg(bool use_cache)
}
}
set_unit_data();
set_unit_data();
return;
}
}
ERR_CONFIG << "caching cannot be done. Reading file\n";
preproc_map defines_map(defines_map_);
scoped_istream stream = preprocess_file("data/", &defines_map);
read(game_config_, *stream);
std::string error_log;
read_configs(error_log);
if(!error_log.empty()) {
gui::show_error_message(disp(),
_("Warning: Errors occurred while loading game configuration files: '") +
font::nullify_markup(error_log));
}
set_unit_data();
}

View file

@ -180,7 +180,7 @@ int receive_bytes(TCPsocket s, char* buf, size_t nbytes)
}
bool receive_with_timeout(TCPsocket s, char* buf, size_t nbytes,
bool update_stats=false, int timeout_ms=10000)
bool update_stats=false, int timeout_ms=60000)
{
int startTicks = SDL_GetTicks();
int time_used = 0;

View file

@ -398,7 +398,24 @@ stats& attack_context::defender_stats()
return get_stats(defender_side);
}
void attack_context::attack_result(attack_context::ATTACK_RESULT res, int damage)
#ifdef MABOUL_STATS
void attack_context::attack_excepted_damage(double attacker_inflict, double defender_inflict)
{
attacker_inflict *= 100.0;
defender_inflict *= 100.0;
attacker_stats().expected_damage_inflicted += attacker_inflict;
attacker_stats().expected_damage_taken += defender_inflict;
defender_stats().expected_damage_inflicted += defender_inflict;
defender_stats().expected_damage_taken += attacker_inflict;
attacker_stats().turn_expected_damage_inflicted += attacker_inflict;
attacker_stats().turn_expected_damage_taken += defender_inflict;
defender_stats().turn_expected_damage_inflicted += defender_inflict;
defender_stats().turn_expected_damage_taken += attacker_inflict;
}
#endif
void attack_context::attack_result(attack_context::ATTACK_RESULT res, int damage, int drain)
{
if(stats_disabled > 0)
return;
@ -406,16 +423,24 @@ void attack_context::attack_result(attack_context::ATTACK_RESULT res, int damage
push_back(attacker_res,(res == MISSES ? '0' : '1'));
if(res != MISSES) {
#ifdef MABOUL_STATS
attacker_stats().damage_taken -= drain;
defender_stats().damage_inflicted -= drain;
attacker_stats().turn_damage_taken -= drain;
defender_stats().turn_damage_inflicted -= drain;
#endif
attacker_stats().damage_inflicted += damage;
defender_stats().damage_taken += damage;
attacker_stats().turn_damage_inflicted += damage;
defender_stats().turn_damage_taken += damage;
}
#ifndef MABOUL_STATS
const int exp_damage = damage * chance_to_hit_defender;
attacker_stats().expected_damage_inflicted += exp_damage;
defender_stats().expected_damage_taken += exp_damage;
attacker_stats().turn_expected_damage_inflicted += exp_damage;
defender_stats().turn_expected_damage_taken += exp_damage;
#endif
if(res == KILLS) {
attacker_stats().killed[defender_type]++;
@ -423,24 +448,33 @@ void attack_context::attack_result(attack_context::ATTACK_RESULT res, int damage
}
}
void attack_context::defend_result(attack_context::ATTACK_RESULT res, int damage)
void attack_context::defend_result(attack_context::ATTACK_RESULT res, int damage, int drain)
{
if(stats_disabled > 0)
return;
push_back(defender_res,(res == MISSES ? '0' : '1'));
if(res != MISSES) {
#ifdef MABOUL_STATS
defender_stats().damage_taken -= drain;
attacker_stats().damage_inflicted -= drain;
defender_stats().turn_damage_taken -= drain;
attacker_stats().turn_damage_inflicted -= drain;
#endif
attacker_stats().damage_taken += damage;
defender_stats().damage_inflicted += damage;
attacker_stats().turn_damage_taken += damage;
defender_stats().turn_damage_inflicted += damage;
}
#ifndef MABOUL_STATS
const int exp_damage = damage * chance_to_hit_attacker;
attacker_stats().expected_damage_taken += exp_damage;
defender_stats().expected_damage_inflicted += exp_damage;
attacker_stats().turn_expected_damage_taken += exp_damage;
defender_stats().turn_expected_damage_inflicted += exp_damage;
#endif
if(res == KILLS) {
attacker_stats().deaths[attacker_type]++;

View file

@ -85,8 +85,11 @@ namespace statistics
enum ATTACK_RESULT { MISSES, HITS, KILLS };
void attack_result(ATTACK_RESULT res, int damage);
void defend_result(ATTACK_RESULT res, int damage);
#ifdef MABOUL_STATS
void attack_excepted_damage(double attacker_inflict, double defender_inflict);
#endif
void attack_result(ATTACK_RESULT res, int damage, int drain);
void defend_result(ATTACK_RESULT res, int damage, int drain);
private:

View file

@ -160,7 +160,7 @@ team::team_info::team_info(const config& cfg) :
if(village_income.empty())
income_per_village = game_config::village_income;
else
income_per_village = atoi(village_income.c_str());
income_per_village = lexical_cast_default<int>(village_income, game_config::village_income);
const std::string& enemies_list = cfg["enemy"];
if(!enemies_list.empty()) {