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