1// SuperTux
2// Copyright (C) 2017 Tobias Markus <tobbi.bugs@googlemail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "supertux/game_session_recorder.hpp"
18
19#include <fstream>
20
21#include "control/input_manager.hpp"
22#include "math/random.hpp"
23#include "object/player.hpp"
24#include "supertux/game_session.hpp"
25#include "supertux/gameconfig.hpp"
26#include "supertux/globals.hpp"
27#include "supertux/sector.hpp"
28#include "util/log.hpp"
29
30GameSessionRecorder::GameSessionRecorder() :
31 m_capture_file(),
32 m_capture_demo_stream(),
33 m_playback_demo_stream(),
34 m_demo_controller(),
35 m_playing(false)
36{
37}
38
39GameSessionRecorder::~GameSessionRecorder()
40{
41}
42
43void
44GameSessionRecorder::start_recording()
45{
46 if (!m_capture_file.empty()) {
47 int newSeed = 0; // next run uses a new seed
48 while (newSeed == 0) // which is the next non-zero random num.
49 newSeed = gameRandom.rand();
50 g_config->random_seed = newSeed;
51 gameRandom.seed(g_config->random_seed);
52 log_info << "Next run uses random seed " << g_config->random_seed <<std::endl;
53 record_demo(m_capture_file);
54 }
55}
56
57void
58GameSessionRecorder::record_demo(const std::string& filename)
59{
60 m_capture_demo_stream.reset(new std::ofstream(filename.c_str()));
61 if (!m_capture_demo_stream->good()) {
62 std::stringstream msg;
63 msg << "Couldn't open demo file '" << filename << "' for writing.";
64 throw std::runtime_error(msg.str());
65 }
66 m_capture_file = filename;
67
68 char buf[30]; // save the seed in the demo file
69 snprintf(buf, sizeof(buf), "random_seed=%10d", g_config->random_seed);
70 for (int i = 0; i == 0 || buf[i-1]; i++)
71 m_capture_demo_stream->put(buf[i]);
72}
73
74int
75GameSessionRecorder::get_demo_random_seed(const std::string& filename) const
76{
77 std::unique_ptr<std::istream> test_stream(new std::ifstream(filename.c_str()));
78 if (test_stream->good())
79 {
80 char buf[30]; // recall the seed from the demo file
81 int seed;
82
83 for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
84 test_stream->get(buf[i]);
85
86 if (sscanf(buf, "random_seed=%10d", &seed) == 1)
87 {
88 log_info << "Random seed " << seed << " from demo file" << std::endl;
89 return seed;
90 }
91 else
92 {
93 log_info << "Demo file contains no random number" << std::endl;
94 }
95 }
96 return 0;
97}
98
99void
100GameSessionRecorder::play_demo(const std::string& filename)
101{
102 m_playing = true;
103
104 m_playback_demo_stream.reset();
105 m_demo_controller.reset();
106
107 m_playback_demo_stream.reset(new std::ifstream(filename.c_str()));
108 if (!m_playback_demo_stream->good()) {
109 std::stringstream msg;
110 msg << "Couldn't open demo file '" << filename << "' for reading.";
111 throw std::runtime_error(msg.str());
112 }
113
114 reset_demo_controller();
115
116 // skip over random seed, if it exists in the file
117 char buf[30]; // ascii decimal seed
118 int seed;
119 for (int i=0; i<30 && (i==0 || buf[i-1]); i++)
120 m_playback_demo_stream->get(buf[i]);
121 if (sscanf(buf, "random_seed=%010d", &seed) != 1)
122 m_playback_demo_stream->seekg(0); // old style w/o seed, restart at beg
123
124 m_playing = false;
125}
126
127void
128GameSessionRecorder::reset_demo_controller()
129{
130 if (!m_demo_controller) {
131 m_demo_controller.reset(new CodeController());
132 }
133
134 auto game_session = GameSession::current();
135 Player& player = game_session->get_current_sector().get_player();
136 player.set_controller(m_demo_controller.get());
137}
138
139void
140GameSessionRecorder::process_events()
141{
142 // playback a demo?
143 if (m_playback_demo_stream != nullptr)
144 {
145 m_demo_controller->update();
146
147 char left, right, up, down, jump, action;
148
149 m_playback_demo_stream->get(left);
150 m_playback_demo_stream->get(right);
151 m_playback_demo_stream->get(up);
152 m_playback_demo_stream->get(down);
153 m_playback_demo_stream->get(jump);
154 m_playback_demo_stream->get(action);
155
156 m_demo_controller->press(Control::LEFT, left != 0);
157 m_demo_controller->press(Control::RIGHT, right != 0);
158 m_demo_controller->press(Control::UP, up != 0);
159 m_demo_controller->press(Control::DOWN, down != 0);
160 m_demo_controller->press(Control::JUMP, jump != 0);
161 m_demo_controller->press(Control::ACTION, action != 0);
162 }
163
164 // save input for demo?
165 if (m_capture_demo_stream != nullptr)
166 {
167 Controller& controller = InputManager::current()->get_controller();
168
169 m_capture_demo_stream->put(controller.hold(Control::LEFT));
170 m_capture_demo_stream->put(controller.hold(Control::RIGHT));
171 m_capture_demo_stream->put(controller.hold(Control::UP));
172 m_capture_demo_stream->put(controller.hold(Control::DOWN));
173 m_capture_demo_stream->put(controller.hold(Control::JUMP));
174 m_capture_demo_stream->put(controller.hold(Control::ACTION));
175 }
176}
177
178/* EOF */
179