Implemented automatic saving of game replays.

This commit is contained in:
Gunter Labes 2009-04-01 07:26:04 +00:00
parent 27c5f6eb4f
commit 3c776a28cf
5 changed files with 67 additions and 5 deletions

View file

@ -35,6 +35,8 @@ Version 1.7.0-svn:
Czech, German, Finnish, Hebrew, Italian, Russian, Slovak, Turkish
* Updated CJK character range
* Fixed word wrapping in CJK languages (patch #1140 from sylecn)
* Multiplayer server:
* Implemented automatic saving of game replays.
* User interface:
* Fixed bug #13257: Attack dialog always uses the active name of a weapon
special

View file

@ -138,9 +138,15 @@ The message of the day.
.B passwd
The password used to gain admin privileges. Usually it starts with `admin '.
.TP
.B replay_save_path
The directory where the server stores game replays. (Don't forget the trailing /!) Defaults to `' which means the directory wesnothd was started in.
.TP
.B restart_command
The command that the server uses to start a new server process via the `restart' command. (Can only be issued via the fifo.)
.TP
.B save_replays
Defines whether the server will automatically save replays of games. (default: \fBfalse\fR)
.TP
.B versions_accepted
A comma separated list of version strings to be accepted by the server. `*' and `?' from wildcard patterns are supported.
(defaults to the corresponding wesnoth version)

View file

@ -14,6 +14,7 @@
#include "../global.hpp"
#include "../filesystem.hpp"
#include "../game_config.hpp" // game_config::observer_team_name
#include "../log.hpp"
#include "../map.hpp" // gamemap::MAX_PLAYERS
@ -53,7 +54,8 @@ namespace wesnothd {
int game::id_num = 1;
game::game(player_map& players, const network::connection host,
const std::string name) :
const std::string name, bool save_replays,
const std::string replay_save_path) :
player_info_(&players),
id_(id_num++),
name_(name),
@ -72,7 +74,9 @@ game::game(player_map& players, const network::connection host,
end_turn_(0),
all_observers_muted_(false),
bans_(),
termination_()
termination_(),
save_replays_(save_replays),
replay_save_path_(replay_save_path)
{
// Hack to handle the pseudo games lobby_ and not_logged_in_.
if (owner_ == 0) return;
@ -89,6 +93,8 @@ game::game(player_map& players, const network::connection host,
game::~game()
{
save_replay();
user_vector users = all_game_users();
for (user_vector::const_iterator u = users.begin(); u != users.end(); ++u) {
remove_player(*u, false, true);
@ -1193,6 +1199,43 @@ void game::send_history(const network::connection sock) const
history_.push_back(doc);
}
void game::save_replay() {
if (!save_replays_) return;
std::string replay_commands;
for(std::vector<simple_wml::document*>::iterator i = history_.begin();
i != history_.end(); ++i) {
const simple_wml::node::child_list& turn_list = (*i)->root().children("turn");
for (simple_wml::node::child_list::const_iterator turn = turn_list.begin();
turn != turn_list.end(); ++turn) {
replay_commands += (*turn)->output();
}
}
std::stringstream name;
name << level_["name"] << " Turn " << current_turn() << " (" << id_ << ").gz";
std::stringstream replay_data;
replay_data << "campaign_type=\"multiplayer\"\n"
<< "difficulty=\"NORMAL\"\n"
<< "label=\"" << name.str() << "\"\n"
<< "version=\"" << level_["version"] << "\"\n"
<< "[replay]\n" << replay_commands << "[/replay]\n"
<< "[replay_start]\n" << level_.output() << "[/replay_start]\n";
simple_wml::document replay(replay_data.str().c_str(), simple_wml::INIT_STATIC);
std::string filename(name.str());
std::replace(filename.begin(), filename.end(), ' ', '_');
//filename.erase(std::remove(filename.begin(), filename.end(), '\''), filename.end());
DBG_GAME << "saving replay: " << filename << std::endl;
scoped_ostream os(ostream_file(replay_save_path_ + filename));
(*os) << replay.output_compressed();
if (!os->good()) {
LOG_GAME << "Could not save replay! (" << filename << ")\n";
}
}
void game::record_data(simple_wml::document* data) {
data->compress();
history_.push_back(data);

View file

@ -34,7 +34,9 @@ typedef std::vector<network::connection> side_vector;
class game
{
public:
game(player_map& players, const network::connection host=0, const std::string name="");
game(player_map& players, const network::connection host=0,
const std::string name="", bool save_replays=false,
const std::string replay_save_path="");
~game();
int id() const { return id_; }
@ -218,6 +220,7 @@ private:
void send_observerjoins(const network::connection sock=0) const;
void send_observerquit(const player_map::const_iterator observer) const;
void send_history(const network::connection sock) const;
void save_replay();
/** In case of a host transfer, notify the new host about its status. */
void notify_new_host();
@ -306,6 +309,9 @@ private:
std::vector<std::string> bans_;
std::string termination_;
bool save_replays_;
std::string replay_save_path_;
};
struct game_is_member {

View file

@ -340,6 +340,8 @@ private:
size_t max_ip_log_size_;
std::string uh_name_;
bool deny_unregistered_login_;
bool save_replays_;
std::string replay_save_path_;
/** Parse the server config into local variables. */
void load_config();
@ -521,6 +523,9 @@ void server::load_config() {
input_.reset();
input_.reset(new input_stream(fifo_path));
save_replays_ = utils::string_bool(cfg_["save_replays"], false);
replay_save_path_ = cfg_["replay_save_path"];
admin_passwd_ = cfg_["passwd"];
motd_ = cfg_["motd"];
lan_server_ = lexical_cast_default<time_t>(cfg_["lan_server"], 0);
@ -1803,7 +1808,7 @@ void server::process_data_lobby(const network::connection sock,
<< "\tcreates a new game: \"" << game_name << "\".\n";
// Create the new game, remove the player from the lobby
// and set the player as the host/owner.
games_.push_back(new wesnothd::game(players_, sock, game_name));
games_.push_back(new wesnothd::game(players_, sock, game_name, save_replays_, replay_save_path_));
wesnothd::game& g = *games_.back();
if(game_password.empty() == false) {
g.set_password(game_password);
@ -2026,7 +2031,7 @@ void server::process_data_game(const network::connection sock,
//desc[""] = data["objectives"];
//desc[""] = data["random_start_time"];
//desc[""] = data["turns"];
//desc["client_version"] = data["version"];
//desc.set_attr_dup("client_version", data["version"]);
// Record the full scenario in g->level()