00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "global.hpp"
00021
00022 #define GETTEXT_DOMAIN "wesnoth"
00023
00024 #include "config.hpp"
00025 #include "construct_dialog.hpp"
00026 #include "display.hpp"
00027 #include "game_config.hpp"
00028 #include "game_preferences.hpp"
00029 #include "gamestatus.hpp"
00030 #include "gettext.hpp"
00031 #include "filesystem.hpp"
00032 #include "serialization/parser.hpp"
00033 #include "team.hpp"
00034 #include "tstring.hpp"
00035 #include "upload_log.hpp"
00036 #include "wesconfig.h"
00037 #include "wml_separators.hpp"
00038
00039 #include "SDL_net.h"
00040
00041 #include <vector>
00042 #include <string>
00043
00044 #define TARGET_HOST "stats.wesnoth.org"
00045 #define TARGET_URL "/upload.cgi"
00046 #define TARGET_PORT 80
00047
00048 struct upload_log::thread_info upload_log::thread_;
00049 upload_log::manager* upload_log::manager_ = 0;
00050
00051
00052 upload_log::manager::~manager()
00053 {
00054 upload_log::manager_ = 0;
00055 threading::thread *t = thread_.t;
00056 if (t)
00057 t->join();
00058 }
00059
00060 void upload_log::manager::manage()
00061 {
00062
00063
00064 if (thread_.shutdown)
00065 {
00066 thread_.t->join();
00067 thread_.shutdown = false;
00068 thread_.t = 0;
00069 }
00070 }
00071
00072 static void send_string(TCPsocket sock, const std::string &str)
00073 {
00074 if (SDLNet_TCP_Send(sock, const_cast<void*>(static_cast<const void*>(
00075 str.c_str())), str.length()) != static_cast<int>(str.length())) {
00076
00077 throw network::error("");
00078 }
00079 }
00080
00081
00082
00083
00084 static int upload_logs(void *_ti)
00085 {
00086 TCPsocket sock = NULL;
00087 upload_log::thread_info *ti = static_cast<upload_log::thread_info*>(_ti);
00088
00089 const char *header =
00090 "POST " TARGET_URL " HTTP/1.1\n"
00091 "Host: " TARGET_HOST "\n"
00092 "User-Agent: Wesnoth " VERSION "\n"
00093 "Content-Type: text/plain\n";
00094
00095 try {
00096 std::vector<std::string> files;
00097
00098
00099 get_files_in_dir(get_upload_dir(), &files, NULL, ENTIRE_FILE_PATH);
00100
00101 IPaddress ip;
00102 if (SDLNet_ResolveHost(&ip, TARGET_HOST, TARGET_PORT) == 0) {
00103 std::vector<std::string>::iterator i;
00104 for (i = files.begin(); i!=files.end() && *i!=ti->lastfile; i++) {
00105 std::string contents;
00106 char response[10];
00107
00108 contents = read_file(*i);
00109
00110 sock = SDLNet_TCP_Open(&ip);
00111 if (!sock)
00112 break;
00113 send_string(sock, header);
00114 send_string(sock, "Content-length: ");
00115 send_string(sock, lexical_cast<std::string>(contents.length()));
00116 send_string(sock, "\n\n");
00117 send_string(sock, contents.c_str());
00118
00119 if (SDLNet_TCP_Recv(sock, response, sizeof(response))
00120 != sizeof(response))
00121 break;
00122
00123 if (memcmp(response, "HTTP/1.", strlen("HTTP/1.")) != 0)
00124 break;
00125 if (memcmp(response+8, " 2", strlen(" 2")) != 0)
00126 break;
00127
00128 delete_directory(*i);
00129 SDLNet_TCP_Close(sock);
00130 sock = NULL;
00131 }
00132 }
00133 } catch(...) { }
00134
00135 if (sock)
00136 SDLNet_TCP_Close(sock);
00137 ti->shutdown = true;
00138 return 0;
00139 }
00140
00141
00142
00143 upload_log::upload_log(bool enable) : game_(NULL), enabled_(enable)
00144 {
00145 filename_ = next_filename(get_upload_dir(), 100);
00146 if (upload_log::manager_)
00147 upload_log::manager_->manage();
00148 if (preferences::upload_log() && !thread_.t) {
00149
00150
00151 thread_.lastfile = filename_;
00152 thread_.t = new threading::thread(upload_logs, &thread_);
00153 }
00154 }
00155
00156 upload_log::~upload_log()
00157 {
00158
00159 if (game_finished(game_))
00160 config_.add_child("game", *game_);
00161
00162 if (game_)
00163 delete game_;
00164
00165 if (enabled_ && !config_.empty() && !game_config::debug) {
00166 config_["version"] = VERSION;
00167 config_["format_version"] = "1";
00168 config_["id"] = preferences::upload_id();
00169 config_["serial"] = lexical_cast<std::string>(time(NULL)) + file_name(filename_);
00170 std::ostream *out = ostream_file(filename_);
00171 write(*out, config_);
00172 delete out;
00173
00174 if (upload_log::manager_)
00175 upload_log::manager_->manage();
00176
00177
00178 if (preferences::upload_log() && !thread_.t) {
00179 thread_.lastfile = next_filename(get_upload_dir(), 1000);
00180 thread_.t = new threading::thread(upload_logs, &thread_);
00181 }
00182 }
00183 }
00184
00185 bool upload_log::game_finished(config *game)
00186 {
00187 if (!game)
00188 return false;
00189
00190 return game->child("victory") || game->child("defeat") || game->child("quit");
00191 }
00192
00193 config &upload_log::add_game_result(const std::string &str, int turn)
00194 {
00195 config &child = game_->add_child(str);
00196 child["time"] = lexical_cast<std::string>(SDL_GetTicks() / 1000);
00197 child["end_turn"] = lexical_cast<std::string>(turn);
00198 return child;
00199 }
00200
00201
00202 void upload_log::start(game_state &state, const team &team,
00203 unsigned team_number,
00204 const unit_map &units,
00205 const t_string &turn,
00206 int num_turns)
00207 {
00208 std::vector<const unit*> all_units;
00209
00210
00211 if (game_finished(game_))
00212 config_.add_child("game", *game_);
00213
00214
00215
00216 delete game_;
00217 game_ = new config();
00218 (*game_)["time"] = lexical_cast<std::string>(SDL_GetTicks() / 1000);
00219 (*game_)["campaign"] = state.campaign_define;
00220 (*game_)["difficulty"] = state.difficulty;
00221 (*game_)["scenario"] = state.scenario;
00222 if (!state.version.empty())
00223 (*game_)["version"] = state.version;
00224 if (!turn.empty())
00225 (*game_)["start_turn"] = turn;
00226 (*game_)["gold"] = lexical_cast<std::string>(team.gold());
00227 (*game_)["num_turns"] = lexical_cast<std::string>(num_turns);
00228
00229
00230
00231 for (unit_map::const_iterator un = units.begin(); un != units.end(); ++un){
00232 if (un->second.side() == team_number) {
00233 all_units.push_back(&un->second);
00234 }
00235 }
00236
00237
00238 player_info &player = state.players.begin()->second;
00239 for (std::vector<unit>::iterator it = player.available_units.begin();
00240 it != player.available_units.end();
00241 ++it) {
00242 all_units.push_back(&*it);
00243 }
00244
00245
00246 std::vector<const unit*>::const_iterator i;
00247 for (i = all_units.begin(); i != all_units.end(); ++i) {
00248 if ((*i)->can_recruit()) {
00249 config &sp = game_->add_child("special-unit");
00250 sp["name"] = (*i)->id();
00251 sp["level"] = lexical_cast<std::string>((*i)->level());
00252 sp["experience"] = lexical_cast<std::string>((*i)->experience());
00253 }
00254 }
00255
00256
00257 config &summ = game_->add_child("units-by-level");
00258 bool higher_units = true;
00259 for (int level = 0; higher_units; level++) {
00260 std::map<std::string, int> tally;
00261
00262 higher_units = false;
00263 for (i = all_units.begin(); i != all_units.end(); ++i) {
00264 if ((*i)->level() > level)
00265 higher_units = true;
00266 else if ((*i)->level() == level) {
00267 if (tally.find((*i)->type_id()) == tally.end())
00268 tally[(*i)->type_id()] = 1;
00269 else
00270 tally[(*i)->type_id()]++;
00271 }
00272 }
00273 if (!tally.empty()) {
00274 config &tc = summ.add_child(lexical_cast<std::string>(level));
00275 for (std::map<std::string, int>::iterator t = tally.begin();
00276 t != tally.end();
00277 t++) {
00278 config &uc = tc.add_child(t->first);
00279 uc["count"] = lexical_cast<std::string>(t->second);
00280 }
00281 }
00282 }
00283 }
00284
00285
00286 void upload_log::defeat(int turn)
00287 {
00288
00289 if (game_) {
00290 add_game_result("defeat", turn);
00291 }
00292 }
00293
00294 void upload_log::victory(int turn, int gold)
00295 {
00296
00297 if (game_) {
00298 config &e = add_game_result("victory", turn);
00299 e["gold"] = lexical_cast<std::string>(gold);
00300 }
00301 }
00302
00303 void upload_log::quit(int turn)
00304 {
00305 std::string turnstr = lexical_cast<std::string>(turn);
00306
00307
00308 if (!game_ || game_->get_attribute("start_turn") == turnstr || turn == 1)
00309 return;
00310
00311 add_game_result("quit", turn);
00312 }
00313
00314
00315 void upload_log_dialog::show_beg_dialog(display& disp)
00316 {
00317 std::string msg = std::string(_("Wesnoth relies on volunteers like yourself for feedback, especially beginners and new players. Wesnoth keeps summaries of your games: you can help us improve game play by giving permission to send these summaries (anonymously) to wesnoth.org.\n"))
00318 + " \n`" + _("Summaries are stored here:")
00319 + " \n`~" + get_upload_dir() + "\n \n`"
00320 + _("You can view the results at:") + "\n`~"
00321 + "http://stats.wesnoth.org/?" + preferences::upload_id() + "\n \n";
00322 gui::dialog d(disp, _("Help us make Wesnoth better for you!"), msg, gui::OK_ONLY);
00323
00324 d.add_option(_("Enable summary uploads"),
00325 preferences::upload_log(), gui::dialog::BUTTON_CHECKBOX_LEFT);
00326 d.show();
00327 preferences::set_upload_log(d.option_checked());
00328 }