1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2006-2015 Joerg Henrichs
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 3
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "race/history.hpp"
20
21 #include <stdio.h>
22
23 #include "io/file_manager.hpp"
24 #include "modes/world.hpp"
25 #include "karts/abstract_kart.hpp"
26 #include "karts/controller/controller.hpp"
27 #include "network/network_config.hpp"
28 #include "network/rewind_manager.hpp"
29 #include "physics/physics.hpp"
30 #include "race/race_manager.hpp"
31 #include "tracks/track.hpp"
32 #include "utils/constants.hpp"
33 #include "utils/file_utils.hpp"
34
35 History* history = 0;
36 bool History::m_online_history_replay = false;
37 //-----------------------------------------------------------------------------
38 /** Initialises the history object and sets the mode to none.
39 */
History()40 History::History()
41 {
42 m_replay_history = false;
43 } // History
44
45 //-----------------------------------------------------------------------------
46 /** Initialise the history for a new recording. It especially allocates memory
47 * to store the history.
48 */
initRecording()49 void History::initRecording()
50 {
51 allocateMemory();
52 m_event_index = 0;
53 m_all_input_events.clear();
54 } // initRecording
55
56 //-----------------------------------------------------------------------------
57 /** Allocates memory for the history. This is used when recording as well
58 * as when replaying (since in replay the data is read into memory first).
59 * \param number_of_frames Maximum number of frames to store.
60 */
allocateMemory(int size)61 void History::allocateMemory(int size)
62 {
63 m_all_input_events.clear();
64 if(size<0)
65 m_all_input_events.reserve(1024);
66 else
67 m_all_input_events.resize(size);
68 } // allocateMemory
69
70 //-----------------------------------------------------------------------------
71 /** Stores an input event (e.g. acceleration or steering event) into the
72 * history data for physics replay.
73 * \param kart_id The kart index which triggered the event.
74 * \param pa The action.
75 * \param value Value of the action (0=release, 32768 = pressed), in
76 * between in case of analog devices.
77 */
addEvent(int kart_id,PlayerAction pa,int value)78 void History::addEvent(int kart_id, PlayerAction pa, int value)
79 {
80 InputEvent ie;
81 // The event is added before m_current is increased. So in order to
82 // save the right index for this event, we need to use m_current+1.
83 ie.m_world_ticks = World::getWorld()->getTicksSinceStart();
84 ie.m_action = pa;
85 ie.m_value = value;
86 ie.m_kart_index = kart_id;
87 m_all_input_events.emplace_back(ie);
88 } // addEvent
89
90 //-----------------------------------------------------------------------------
91 /** Sets the kart position and controls to the recorded history value.
92 * \param world_ticks WOrld time in ticks.
93 * \param ticks Number of time steps.
94 */
updateReplay(int world_ticks)95 void History::updateReplay(int world_ticks)
96 {
97 World *world = World::getWorld();
98
99 while (m_event_index < m_all_input_events.size() &&
100 m_all_input_events[m_event_index].m_world_ticks <= world_ticks)
101 {
102 const InputEvent &ie = m_all_input_events[m_event_index];
103 AbstractKart *kart = world->getKart(ie.m_kart_index);
104 Log::verbose("history", "time %d event-time %d action %d %d",
105 world->getTicksSinceStart(), ie.m_world_ticks, ie.m_action,
106 ie.m_value);
107 kart->getController()->action(ie.m_action, ie.m_value);
108 m_event_index++;
109 } // while we have events for current time step.
110
111 // Check if we have reached the end of the buffer
112 if(m_event_index >= m_all_input_events.size())
113 {
114 Log::info("History", "Replay finished");
115 m_event_index= 0;
116 // This is useful to use a reproducable rewind problem:
117 // replay it with history, for debugging only
118 #undef DO_REWIND_AT_END_OF_HISTORY
119 #ifdef DO_REWIND_AT_END_OF_HISTORY
120 RewindManager::get()->rewindTo(5.0f);
121 exit(-1);
122 #else
123 world->reset();
124 #endif
125 } // if m_event_index >= m_all_input_events.size()
126
127 } // updateReplay
128
129 //-----------------------------------------------------------------------------
130 /** Saves the history stored in the internal data structures into a file called
131 * history.dat.
132 */
Save()133 void History::Save()
134 {
135 FILE *fd = fopen("history.dat","w");
136 if(fd)
137 Log::info("History", "Saved in ./history.dat.");
138 else
139 {
140 std::string fn = file_manager->getUserConfigFile("history.dat");
141 fd = FileUtils::fopenU8Path(fn, "w");
142 if(fd)
143 Log::info("History", "Saved in '%s'.", fn.c_str());
144 }
145 if(!fd)
146 {
147 Log::info("History", "Can't open history.dat file for writing - can't save history.");
148 Log::info("History", "Make sure history.dat in the current directory "
149 "or the config directory is writable.");
150 return;
151 }
152
153 World *world = World::getWorld();
154 const int num_karts = world->getNumKarts();
155 fprintf(fd, "STK-version: %s\n", STK_VERSION);
156 fprintf(fd, "History-version: %d\n", 1);
157 fprintf(fd, "numkarts: %d\n", num_karts);
158 fprintf(fd, "numplayers: %d\n", RaceManager::get()->getNumPlayers());
159 fprintf(fd, "difficulty: %d\n", RaceManager::get()->getDifficulty());
160 fprintf(fd, "reverse: %c\n", RaceManager::get()->getReverseTrack() ? 'y' : 'n');
161
162 fprintf(fd, "track: %s\n", Track::getCurrentTrack()->getIdent().c_str());
163
164 assert(num_karts > 0);
165
166 int k;
167 for(k=0; k<num_karts; k++)
168 {
169 fprintf(fd, "model %d: %s\n",k, world->getKart(k)->getIdent().c_str());
170 }
171 fprintf(fd, "count: %zu\n", m_all_input_events.size());
172
173 for (unsigned int i = 0; i < m_all_input_events.size(); i++)
174 {
175 fprintf(fd, "%d %d %d %d\n",
176 m_all_input_events[i].m_world_ticks,
177 m_all_input_events[i].m_kart_index,
178 m_all_input_events[i].m_action,
179 m_all_input_events[i].m_value );
180 } // for i
181
182 fprintf(fd, "History file end.\n");
183 fclose(fd);
184 } // Save
185
186 //-----------------------------------------------------------------------------
187 /** Loads a history from history.dat in the current directory.
188 */
Load()189 void History::Load()
190 {
191 char s[1024], s1[1024];
192 int n;
193
194 FILE *fd = fopen("history.dat","r");
195 if(fd)
196 Log::info("History", "Reading ./history.dat");
197 else
198 {
199 std::string fn = file_manager->getUserConfigFile("history.dat");
200 fd = FileUtils::fopenU8Path(fn, "r");
201 if(fd)
202 Log::info("History", "Reading '%s'.", fn.c_str());
203 }
204 if(!fd)
205 Log::fatal("History", "Could not open history.dat");
206
207 if (fgets(s, 1023, fd) == NULL)
208 Log::fatal("History", "Could not read history.dat.");
209
210 // Check for unsupported hsitory file formats:
211 if (sscanf(s, "Version-2: %1023s", s1) == 1 ||
212 sscanf(s, "Version-1: %1023s", s1) == 1)
213 {
214 Log::fatal("History",
215 "Old history file format is not supported anymore.");
216 }
217
218 if (sscanf(s,"STK-version: %1023s",s1)!=1)
219 Log::fatal("History", "No Version information found in history "
220 "file (bogus history file).");
221 if (strcmp(s1,STK_VERSION))
222 Log::warn("History", "History is version '%s', STK version is '%s'.",
223 s1, STK_VERSION);
224
225 if (fgets(s, 1023, fd) == NULL)
226 Log::fatal("History", "Could not read history.dat.");
227
228 int version;
229 if (sscanf(s, "History-version: %1023d", &version) != 1)
230 Log::fatal("Invalid version number found: '%s'", s);
231
232 if (version != 1)
233 Log::fatal("History",
234 "Old-style history files are not supported anymore.");
235
236 if (fgets(s, 1023, fd) == NULL)
237 Log::fatal("History", "Could not read history.dat.");
238
239 unsigned int num_karts;
240 if(sscanf(s, "numkarts: %u", &num_karts)!=1)
241 Log::fatal("History", "No number of karts found in history file.");
242 RaceManager::get()->setNumKarts(num_karts);
243
244 fgets(s, 1023, fd);
245 if(sscanf(s, "numplayers: %d",&n)!=1)
246 Log::fatal("History", "No number of players found in history file.");
247 RaceManager::get()->setNumPlayers(n);
248
249 fgets(s, 1023, fd);
250 if(sscanf(s, "difficulty: %d",&n)!=1)
251 Log::fatal("History", "No difficulty found in history file.");
252 RaceManager::get()->setDifficulty((RaceManager::Difficulty)n);
253
254
255 fgets(s, 1023, fd);
256 char r;
257 if (sscanf(s, "reverse: %c", &r) != 1)
258 Log::fatal("History", "Could not read reverse information: '%s'", s);
259 RaceManager::get()->setReverseTrack(r == 'y');
260
261 fgets(s, 1023, fd);
262 if(sscanf(s, "track: %1023s",s1)!=1)
263 Log::warn("History", "Track not found in history file.");
264 RaceManager::get()->setTrack(s1);
265 // This value doesn't really matter, but should be defined, otherwise
266 // the racing phase can switch to 'ending'
267 RaceManager::get()->setNumLaps(100);
268
269 for(unsigned int i=0; i<num_karts; i++)
270 {
271 fgets(s, 1023, fd);
272 if(sscanf(s, "model %d: %1023s",&n, s1) != 2)
273 Log::fatal("History", "No model information for kart %d found.", i);
274 m_kart_ident.push_back(s1);
275 if(i<RaceManager::get()->getNumPlayers() && !m_online_history_replay)
276 {
277 RaceManager::get()->setPlayerKart(i, s1);
278 }
279 } // for i<nKarts
280 // FIXME: The model information is currently ignored
281
282 fgets(s, 1023, fd);
283 int count;
284 if(sscanf(s,"count: %d",&count)!=1)
285 Log::fatal("History", "Number of records not found in history file.");
286
287 allocateMemory(count);
288 m_event_index = 0;
289
290 // We need to disable the rewind manager here (otherwise setting the
291 // KartControl data would access the rewind manager).
292 bool rewind_manager_was_enabled = RewindManager::isEnabled();
293 RewindManager::setEnable(false);
294
295 for (int i=0; i<count; i++)
296 {
297 fgets(s, 1023, fd);
298 InputEvent &ie = m_all_input_events[i];
299 int action = 0;
300 if (sscanf(s, "%d %d %d %d\n", &ie.m_world_ticks, &ie.m_kart_index,
301 &action, &ie.m_value) != 4)
302 {
303 Log::warn("History", "Problems reading event: '%s'", s);
304 }
305 ie.m_action = (PlayerAction)action;
306 } // for i
307 RewindManager::setEnable(rewind_manager_was_enabled);
308
309 fclose(fd);
310 } // Load
311
312