1 #include "MultiplayerCommon.h"
2 
3 #include "Directories.h"
4 #include "GameRules.h"
5 #include "i18n.h"
6 #include "LoggerWithOptionsDB.h"
7 #include "OptionsDB.h"
8 #include "Random.h"
9 #include "AppInterface.h"
10 #include "../universe/Enums.h"
11 
12 
13 #if defined(_MSC_VER) && defined(int64_t)
14 #undef int64_t
15 #endif
16 
17 
18 const std::string MP_SAVE_FILE_EXTENSION = ".mps";
19 const std::string SP_SAVE_FILE_EXTENSION = ".sav";
20 
21 
22 namespace {
23     // command-line options
AddOptions(OptionsDB & db)24     void AddOptions(OptionsDB& db) {
25         db.Add<std::string>("resource.path",                UserStringNop("OPTIONS_DB_RESOURCE_DIR"),           PathToString(GetRootDataDir() / "default"));
26         db.Add<std::string>('S', "save.path",               UserStringNop("OPTIONS_DB_SAVE_DIR"),               PathToString(GetUserDataDir() / "save"));
27         db.Add<std::string>("save.server.path",             UserStringNop("OPTIONS_DB_SERVER_SAVE_DIR"),        PathToString(GetUserDataDir() / "save"));
28         db.Add<std::string>("log-level",                    UserStringNop("OPTIONS_DB_LOG_LEVEL"),              "",
29                             OrValidator<std::string>(LogLevelValidator(), DiscreteValidator<std::string>("")),  false);
30         db.Add<std::string>("log-file",                     UserStringNop("OPTIONS_DB_LOG_FILE"),               "",
31                             Validator<std::string>(),                                                           false);
32         // Default stringtable filename is deferred to i18n.cpp::InitStringtableFileName
33         db.Add<std::string>("resource.stringtable.path",    UserStringNop("OPTIONS_DB_STRINGTABLE_FILENAME"),   "");
34         db.Add("save.format.binary.enabled",                UserStringNop("OPTIONS_DB_BINARY_SERIALIZATION"),   false);
35         db.Add("save.format.xml.zlib.enabled",              UserStringNop("OPTIONS_DB_XML_ZLIB_SERIALIZATION"), true);
36         db.Add("save.auto.hostless.enabled",                UserStringNop("OPTIONS_DB_AUTOSAVE_HOSTLESS"),      true);
37         db.Add<int>("save.auto.interval",                   UserStringNop("OPTIONS_DB_AUTOSAVE_INTERVAL"),      0);
38         db.Add<std::string>("load",                         UserStringNop("OPTIONS_DB_LOAD"),                   "",                     Validator<std::string>(), false);
39         db.Add("save.auto.exit.enabled",                    UserStringNop("OPTIONS_DB_AUTOSAVE_GAME_CLOSE"),    true);
40         db.AddFlag('q', "quickstart",                       UserStringNop("OPTIONS_DB_QUICKSTART"),             false);
41 
42         // Common galaxy settings
43         db.Add("setup.seed",                UserStringNop("OPTIONS_DB_GAMESETUP_SEED"),                         std::string("0"),       Validator<std::string>());
44         db.Add("setup.star.count",          UserStringNop("OPTIONS_DB_GAMESETUP_STARS"),                        150,                    RangedValidator<int>(10, 5000));
45         db.Add("setup.galaxy.shape",        UserStringNop("OPTIONS_DB_GAMESETUP_GALAXY_SHAPE"),                 DISC,                   RangedValidator<Shape>(SPIRAL_2, RANDOM));
46         db.Add("setup.galaxy.age",          UserStringNop("OPTIONS_DB_GAMESETUP_GALAXY_AGE"),                   GALAXY_SETUP_MEDIUM,    RangedValidator<GalaxySetupOption>(GALAXY_SETUP_LOW, GALAXY_SETUP_RANDOM));
47         db.Add("setup.planet.density",      UserStringNop("OPTIONS_DB_GAMESETUP_PLANET_DENSITY"),               GALAXY_SETUP_MEDIUM,    RangedValidator<GalaxySetupOption>(GALAXY_SETUP_LOW, GALAXY_SETUP_RANDOM));
48         db.Add("setup.starlane.frequency",  UserStringNop("OPTIONS_DB_GAMESETUP_STARLANE_FREQUENCY"),           GALAXY_SETUP_MEDIUM,    RangedValidator<GalaxySetupOption>(GALAXY_SETUP_LOW, GALAXY_SETUP_RANDOM));
49         db.Add("setup.specials.frequency",  UserStringNop("OPTIONS_DB_GAMESETUP_SPECIALS_FREQUENCY"),           GALAXY_SETUP_MEDIUM,    RangedValidator<GalaxySetupOption>(GALAXY_SETUP_NONE, GALAXY_SETUP_RANDOM));
50         db.Add("setup.monster.frequency",   UserStringNop("OPTIONS_DB_GAMESETUP_MONSTER_FREQUENCY"),            GALAXY_SETUP_MEDIUM,    RangedValidator<GalaxySetupOption>(GALAXY_SETUP_NONE, GALAXY_SETUP_RANDOM));
51         db.Add("setup.native.frequency",    UserStringNop("OPTIONS_DB_GAMESETUP_NATIVE_FREQUENCY"),             GALAXY_SETUP_MEDIUM,    RangedValidator<GalaxySetupOption>(GALAXY_SETUP_NONE, GALAXY_SETUP_RANDOM));
52         db.Add("setup.ai.player.count",     UserStringNop("OPTIONS_DB_GAMESETUP_NUM_AI_PLAYERS"),               6,                      RangedValidator<int>(0, IApp::MAX_AI_PLAYERS()));
53         db.Add("setup.ai.aggression",       UserStringNop("OPTIONS_DB_GAMESETUP_AI_MAX_AGGRESSION"),            MANIACAL,               RangedValidator<Aggression>(BEGINNER, MANIACAL));
54 
55 
56         // AI Testing options-- the following options are to facilitate AI testing and do not currently have an options page widget;
57         // they are intended to be changed via the command line and are not currently storable in the configuration file.
58         db.Add<std::string>("ai-path",                      UserStringNop("OPTIONS_DB_AI_FOLDER_PATH"),         "python/AI",
59                             Validator<std::string>(),                                                           false);
60         db.Add<std::string>("ai-config",                    UserStringNop("OPTIONS_DB_AI_CONFIG"),              "",
61                             Validator<std::string>(),                                                           false);
62     }
63     bool temp_bool = RegisterOptions(&AddOptions);
64 
AddRules(GameRules & rules)65     void AddRules(GameRules& rules) {
66         rules.Add<int>("RULE_THRESHOLD_HUMAN_PLAYER_WIN", "RULE_THRESHOLD_HUMAN_PLAYER_WIN_DESC",
67                        "MULTIPLAYER", 0, true,  RangedValidator<int>(0, 999));
68 
69         rules.Add<bool>("RULE_ONLY_ALLIANCE_WIN", "RULE_ONLY_ALLIANCE_WIN_DESC",
70                        "MULTIPLAYER", true, true);
71 
72         rules.Add<bool>("RULE_ALLOW_CONCEDE", "RULE_ALLOW_CONCEDE_DESC",
73                        "MULTIPLAYER", false, true);
74 
75         rules.Add<int>("RULE_CONCEDE_COLONIES_THRESHOLD", "RULE_CONCEDE_COLONIES_THRESHOLD_DESC",
76                        "MULTIPLAYER", 1, true,  RangedValidator<int>(0, 9999));
77 
78         rules.Add<bool>("RULE_SHOW_DETAILED_EMPIRES_DATA", "RULE_SHOW_DETAILED_EMPIRES_DATA_DESC",
79                        "MULTIPLAYER", true, true);
80     }
81     bool temp_bool2 = RegisterGameRules(&AddRules);
82 }
83 
84 /////////////////////////////////////////////////////
85 // GalaxySetupData
86 /////////////////////////////////////////////////////
87 namespace {
88     // returns number in range 0 to one less than the interger representation of
89     // enum_vals_count, determined by the random seed
90     template <typename T1>
GetIdx(const T1 & enum_vals_count,const std::string & seed)91     int GetIdx(const T1& enum_vals_count, const std::string& seed) {
92         DebugLogger() << "hashing seed: " << seed;
93         // use probably-bad but adequate for this purpose hash function to
94         // convert seed into a hash value
95         int hash_value = 223;
96         for (size_t i = 0; i < seed.length(); ++i) {
97             //DebugLogger() << "hash value: " << hash_value << " char: " << static_cast<int>(seed[i]);
98             hash_value += (seed[i] * 61);
99             hash_value %= 191;
100         }
101         DebugLogger() << "final hash value: " << hash_value
102                       << " and returning: " << hash_value % static_cast<int>(enum_vals_count)
103                       << " from 0 to " << static_cast<int>(enum_vals_count) - 1;
104         return hash_value % static_cast<int>(enum_vals_count);
105     }
106 
107     static char alphanum[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
108 }
109 
GalaxySetupData()110 GalaxySetupData::GalaxySetupData() :
111     m_size(100),
112     m_shape(SPIRAL_2),
113     m_age(GALAXY_SETUP_MEDIUM),
114     m_starlane_freq(GALAXY_SETUP_MEDIUM),
115     m_planet_density(GALAXY_SETUP_MEDIUM),
116     m_specials_freq(GALAXY_SETUP_MEDIUM),
117     m_monster_freq(GALAXY_SETUP_MEDIUM),
118     m_native_freq(GALAXY_SETUP_MEDIUM),
119     m_ai_aggr(MANIACAL),
120     m_encoding_empire(ALL_EMPIRES)
121 {}
122 
GalaxySetupData(GalaxySetupData && base)123 GalaxySetupData::GalaxySetupData(GalaxySetupData&& base) :
124     m_seed(std::move(base.m_seed)),
125     m_size(base.m_size),
126     m_shape(base.m_shape),
127     m_age(base.m_age),
128     m_starlane_freq(base.m_starlane_freq),
129     m_planet_density(base.m_planet_density),
130     m_specials_freq(base.m_specials_freq),
131     m_monster_freq(base.m_monster_freq),
132     m_native_freq(base.m_native_freq),
133     m_ai_aggr(base.m_ai_aggr),
134     m_game_rules(std::move(base.m_game_rules)),
135     m_game_uid(std::move(base.m_game_uid)),
136     m_encoding_empire(base.m_encoding_empire)
137 { SetSeed(m_seed); }
138 
GetSeed() const139 const std::string& GalaxySetupData::GetSeed() const
140 { return m_seed; }
141 
GetSize() const142 int GalaxySetupData::GetSize() const
143 { return m_size; }
144 
GetShape() const145 Shape GalaxySetupData::GetShape() const {
146     if (m_shape != RANDOM)
147         return m_shape;
148     size_t num_shapes = static_cast<size_t>(GALAXY_SHAPES) - 1; // -1 so that RANDOM isn't counted
149     return static_cast<Shape>(GetIdx(num_shapes, m_seed + "shape"));
150 }
151 
GetAge() const152 GalaxySetupOption GalaxySetupData::GetAge() const {
153     if (m_age != GALAXY_SETUP_RANDOM)
154         return m_age;
155     return static_cast<GalaxySetupOption>(GetIdx(3, m_seed + "age") + 1);       // need range 1-3 for age
156 }
157 
GetStarlaneFreq() const158 GalaxySetupOption GalaxySetupData::GetStarlaneFreq() const {
159     if (m_starlane_freq != GALAXY_SETUP_RANDOM)
160         return m_starlane_freq;
161     return static_cast<GalaxySetupOption>(GetIdx(3, m_seed + "lanes") + 1);     // need range 1-3 for starlane freq
162 }
163 
GetPlanetDensity() const164 GalaxySetupOption GalaxySetupData::GetPlanetDensity() const {
165     if (m_planet_density != GALAXY_SETUP_RANDOM)
166         return m_planet_density;
167     return static_cast<GalaxySetupOption>(GetIdx(3, m_seed + "planets") + 1);   // need range 1-3 for planet density
168 }
169 
GetSpecialsFreq() const170 GalaxySetupOption GalaxySetupData::GetSpecialsFreq() const {
171     if (m_specials_freq != GALAXY_SETUP_RANDOM)
172         return m_specials_freq;
173     return static_cast<GalaxySetupOption>(GetIdx(4, m_seed + "specials"));      // need range 0-3 for planet density
174 }
175 
GetMonsterFreq() const176 GalaxySetupOption GalaxySetupData::GetMonsterFreq() const {
177     if (m_monster_freq != GALAXY_SETUP_RANDOM)
178         return m_monster_freq;
179     return static_cast<GalaxySetupOption>(GetIdx(4, m_seed + "monsters"));      // need range 0-3 for monster frequency
180 }
181 
GetNativeFreq() const182 GalaxySetupOption GalaxySetupData::GetNativeFreq() const {
183     if (m_native_freq != GALAXY_SETUP_RANDOM)
184         return m_native_freq;
185     return static_cast<GalaxySetupOption>(GetIdx(4, m_seed + "natives"));       // need range 0-3 for native frequency
186 }
187 
GetAggression() const188 Aggression GalaxySetupData::GetAggression() const
189 { return m_ai_aggr; }
190 
GetGameRules() const191 const std::map<std::string, std::string>& GalaxySetupData::GetGameRules() const
192 { return m_game_rules; }
193 
GetGameUID() const194 const std::string& GalaxySetupData::GetGameUID() const
195 { return m_game_uid; }
196 
SetSeed(const std::string & seed)197 void GalaxySetupData::SetSeed(const std::string& seed) {
198     std::string new_seed = seed;
199     if (new_seed.empty() || new_seed == "RANDOM") {
200         ClockSeed();
201         new_seed.clear();
202         for (int i = 0; i < 8; ++i)
203             new_seed += alphanum[RandSmallInt(0, (sizeof(alphanum) - 2))];
204         DebugLogger() << "Set empty or requested random seed to " << new_seed;
205     }
206     m_seed = std::move(new_seed);
207 }
208 
SetGameUID(const std::string & game_uid)209 void GalaxySetupData::SetGameUID(const std::string& game_uid)
210 { m_game_uid = game_uid; }
211 
212 /////////////////////////////////////////////////////
213 // PlayerSetupData
214 /////////////////////////////////////////////////////
operator ==(const PlayerSetupData & lhs,const PlayerSetupData & rhs)215 bool operator==(const PlayerSetupData& lhs, const PlayerSetupData& rhs) {
216     return  lhs.m_client_type == rhs.m_client_type &&
217             lhs.m_empire_color == rhs.m_empire_color &&
218             lhs.m_empire_name == rhs.m_empire_name &&
219             lhs.m_player_name == rhs.m_player_name &&
220             lhs.m_save_game_empire_id == rhs.m_save_game_empire_id &&
221             lhs.m_starting_species_name == rhs.m_starting_species_name &&
222             lhs.m_player_ready == rhs.m_player_ready &&
223             lhs.m_starting_team == rhs.m_starting_team;
224 }
225 
operator !=(const PlayerSetupData & lhs,const PlayerSetupData & rhs)226 bool operator!=(const PlayerSetupData& lhs, const PlayerSetupData& rhs)
227 { return !(lhs == rhs); }
228 
229 
230 ////////////////////////////////////////////////////
231 // MultiplayerLobbyData
232 /////////////////////////////////////////////////////
Dump() const233 std::string MultiplayerLobbyData::Dump() const {
234     std::stringstream stream;
235     for (const std::pair<int, PlayerSetupData>& psd : m_players) {
236         stream << psd.first << ": " << (psd.second.m_player_name.empty() ? "NO NAME" : psd.second.m_player_name) << "  ";
237         if (psd.second.m_client_type == Networking::CLIENT_TYPE_AI_PLAYER)
238             stream << "AI PLAYER";
239         else if (psd.second.m_client_type == Networking::CLIENT_TYPE_HUMAN_PLAYER)
240             stream << "HUMAN PLAYER";
241         else if (psd.second.m_client_type == Networking::CLIENT_TYPE_HUMAN_OBSERVER)
242             stream << "HUMAN OBSERVER";
243         else if (psd.second.m_client_type == Networking::CLIENT_TYPE_HUMAN_MODERATOR)
244             stream << "HUMAN MODERATOR";
245         else
246             stream << "UNKNOWN CLIENT TPYE";
247         stream << "  " << (psd.second.m_empire_name.empty() ? "NO EMPIRE NAME" : psd.second.m_empire_name) << std::endl;
248     }
249     return stream.str();
250 }
251 
252