1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2006-2015 SuperTuxKart-Team
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 "tracks/track_manager.hpp"
20
21 #include "config/stk_config.hpp"
22 #include "graphics/irr_driver.hpp"
23 #include "io/file_manager.hpp"
24 #include "tracks/track.hpp"
25
26 #include <algorithm>
27 #include <iostream>
28 #include <sstream>
29 #include <stdexcept>
30 #include <stdio.h>
31
32 TrackManager* track_manager = 0;
33 std::vector<std::string> TrackManager::m_track_search_path;
34
35 /** Constructor (currently empty). The real work happens in loadTrackList.
36 */
TrackManager()37 TrackManager::TrackManager()
38 {} // TrackManager
39
40 //-----------------------------------------------------------------------------
41 /** Delete all tracks.
42 */
~TrackManager()43 TrackManager::~TrackManager()
44 {
45 for(Tracks::iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
46 delete *i;
47 } // ~TrackManager
48
49 //-----------------------------------------------------------------------------
removeTrackSearchDirs()50 void TrackManager::removeTrackSearchDirs()
51 {
52 m_track_search_path.clear();
53 } // removeTrackSearchDirs
54
55 //-----------------------------------------------------------------------------
56 /** Adds a directory from which tracks are loaded. The track manager checks if
57 * either this directory itself contains a track, and if any subdirectory
58 * contains a track.
59 * \param dir The directory to add.
60 */
addTrackSearchDir(const std::string & dir)61 void TrackManager::addTrackSearchDir(const std::string &dir)
62 {
63 m_track_search_path.push_back(dir);
64 } // addTrackDir
65
66 //-----------------------------------------------------------------------------
67 /** Returns the number of racing tracks. Those are tracks that are not
68 * internal (like cut scenes), arenas, or soccer fields.
69 */
getNumberOfRaceTracks() const70 int TrackManager::getNumberOfRaceTracks() const
71 {
72 int n=0;
73 for(unsigned int i=0; i<m_tracks.size(); i++)
74 if(m_tracks[i]->isRaceTrack())
75 n++;
76 return n;
77 } // getNumberOfRaceTracks
78
79 //-----------------------------------------------------------------------------
80 /** Get TrackData by the track identifier.
81 * \param ident Identifier = basename of the directory the track is in.
82 * \return The corresponding track object, or NULL if not found
83 */
getTrack(const std::string & ident) const84 Track* TrackManager::getTrack(const std::string& ident) const
85 {
86 for(Tracks::const_iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
87 {
88 if ((*i)->getIdent() == ident)
89 return *i;
90 }
91
92 return NULL;
93
94 } // getTrack
95
96 //-----------------------------------------------------------------------------
97 /** Removes all cached data from all tracks. This is called when the screen
98 * resolution is changed and all textures need to be bound again.
99 */
removeAllCachedData()100 void TrackManager::removeAllCachedData()
101 {
102 for(Tracks::const_iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
103 (*i)->removeCachedData();
104 } // removeAllCachedData
105 //-----------------------------------------------------------------------------
106 /** Sets all tracks that are not in the list a to be unavailable. This is used
107 * by the network manager upon receiving the list of available tracks from
108 * a client.
109 * \param tracks List of all track identifiere (available on a client).
110 */
setUnavailableTracks(const std::vector<std::string> & tracks)111 void TrackManager::setUnavailableTracks(const std::vector<std::string> &tracks)
112 {
113 for(Tracks::const_iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
114 {
115 if(!m_track_avail[i-m_tracks.begin()]) continue;
116 const std::string id=(*i)->getIdent();
117 if (std::find(tracks.begin(), tracks.end(), id)==tracks.end())
118 {
119 m_track_avail[i-m_tracks.begin()] = false;
120 Log::warn("TrackManager", "Track '%s' not available on all clients, disabled.",
121 id.c_str());
122 } // if id not in tracks
123 } // for all available tracks in track manager
124
125 } // setUnavailableTracks
126
127 //-----------------------------------------------------------------------------
128 /** Returns a list with all track identifiert.
129 */
getAllTrackIdentifiers()130 std::vector<std::string> TrackManager::getAllTrackIdentifiers()
131 {
132 std::vector<std::string> all;
133 for(Tracks::const_iterator i = m_tracks.begin(); i != m_tracks.end(); ++i)
134 {
135 all.push_back((*i)->getIdent());
136 }
137 return all;
138 } // getAllTrackNames
139
140 //-----------------------------------------------------------------------------
141 /** Loads all tracks from the track directory (data/track).
142 */
loadTrackList()143 void TrackManager::loadTrackList()
144 {
145 m_all_track_dirs.clear();
146 m_track_group_names.clear();
147 m_track_groups.clear();
148 m_arena_group_names.clear();
149 m_soccer_arena_group_names.clear();
150 m_arena_groups.clear();
151 m_soccer_arena_groups.clear();
152 m_track_avail.clear();
153 // This function is called when install a new addons, delete previous
154 // tracks
155 for (Track* track : m_tracks)
156 delete track;
157 m_tracks.clear();
158
159 for(unsigned int i=0; i<m_track_search_path.size(); i++)
160 {
161 const std::string &dir = m_track_search_path[i];
162
163 // First test if the directory itself contains a track:
164 // ----------------------------------------------------
165 if(loadTrack(dir)) continue; // track found, no more tests
166
167 // Then see if a subdir of this dir contains tracks
168 // ------------------------------------------------
169 std::set<std::string> dirs;
170 file_manager->listFiles(dirs, dir);
171 for(std::set<std::string>::iterator subdir = dirs.begin();
172 subdir != dirs.end(); subdir++)
173 {
174 if(*subdir=="." || *subdir=="..") continue;
175 loadTrack(dir+*subdir+"/");
176 } // for dir in dirs
177 } // for i <m_track_search_path.size()
178 } // loadTrackList
179
180 // ----------------------------------------------------------------------------
181 /** Tries to load a track from a single directory. Returns true if a track was
182 * successfully loaded.
183 * \param dirname Name of the directory to load the track from.
184 */
loadTrack(const std::string & dirname)185 bool TrackManager::loadTrack(const std::string& dirname)
186 {
187 std::string config_file = dirname+"track.xml";
188 if(!file_manager->fileExists(config_file))
189 return false;
190
191 Track *track;
192
193 try
194 {
195 track = new Track(config_file);
196 }
197 catch (std::exception& e)
198 {
199 Log::error("TrackManager", "Cannot load track <%s> : %s\n",
200 dirname.c_str(), e.what());
201 return false;
202 }
203
204 if (track->getVersion()<stk_config->m_min_track_version ||
205 track->getVersion()>stk_config->m_max_track_version)
206 {
207 Log::warn("TrackManager", "Track '%s' is not supported "
208 "by this binary, ignored. (Track is version %i, this "
209 "executable supports from %i to %i).",
210 track->getIdent().c_str(), track->getVersion(),
211 stk_config->m_min_track_version,
212 stk_config->m_max_track_version);
213 delete track;
214 return false;
215 }
216 m_all_track_dirs.push_back(dirname);
217 m_tracks.push_back(track);
218 m_track_avail.push_back(true);
219 updateGroups(track);
220
221 // Populate the texture cache with track screenshots
222 // (internal tracks like end cutscene don't have screenshots)
223 if (!track->isInternal())
224 irr_driver->getTexture(track->getScreenshotFile());
225
226 return true;
227 } // loadTrack
228
229 // ----------------------------------------------------------------------------
230 /** Removes a track.
231 * \param ident Identifier of the track (i.e. the name of the directory).
232 */
removeTrack(const std::string & ident)233 void TrackManager::removeTrack(const std::string &ident)
234 {
235 Track *track = getTrack(ident);
236 if (track == NULL)
237 Log::fatal("TrackManager", "There is no track named '%s'!!", ident.c_str());
238
239 if (track->isInternal()) return;
240
241 std::vector<Track*>::iterator it = std::find(m_tracks.begin(),
242 m_tracks.end(), track);
243 if (it == m_tracks.end())
244 Log::fatal("TrackManager", "Cannot find track '%s' in map!!", ident.c_str());
245
246 int index = int(it - m_tracks.begin());
247
248 // Remove the track from all groups it belongs to
249 Group2Indices &group_2_indices =
250 (track->isArena() ? m_arena_groups :
251 (track->isSoccer() ? m_soccer_arena_groups :
252 m_track_groups));
253
254 std::vector<std::string> &group_names =
255 (track->isArena() ? m_arena_group_names :
256 (track->isSoccer() ? m_soccer_arena_group_names :
257 m_track_group_names));
258
259 const std::vector<std::string>& groups=track->getGroups();
260 for(unsigned int i=0; i<groups.size(); i++)
261 {
262 std::vector<int> &indices = group_2_indices[groups[i]];
263 std::vector<int>::iterator j;
264 j = std::find(indices.begin(), indices.end(), index);
265 assert(j!=indices.end());
266 indices.erase(j);
267
268 // If the track was the last member of a group,
269 // completely remove the group
270 if(indices.size()==0)
271 {
272 group_2_indices.erase(groups[i]);
273 std::vector<std::string>::iterator it_g;
274 it_g = std::find(group_names.begin(), group_names.end(),
275 groups[i]);
276 assert(it_g!=group_names.end());
277 group_names.erase(it_g);
278 } // if complete group must be removed
279 } // for i in groups
280
281 // Adjust all indices of tracks with an index number higher than
282 // the removed track, since they have been moved down. This must
283 // be done for all tracks and all arenas
284 for(unsigned int i=0; i<3; i++) // i=0: soccer arenas, i=1: arenas, i=2: tracks
285 {
286 Group2Indices &g2i = (i==0 ? m_soccer_arena_groups :
287 (i==1 ? m_arena_groups :
288 m_track_groups));
289 Group2Indices::iterator j;
290 for(j=g2i.begin(); j!=g2i.end(); j++)
291 {
292 for(unsigned int i=0; i<(*j).second.size(); i++)
293 if((*j).second[i]>index) (*j).second[i]--;
294 } // for j in group_2_indices
295 } // for i in arenas, tracks
296
297 m_tracks.erase(it);
298 m_all_track_dirs.erase(m_all_track_dirs.begin()+index);
299 m_track_avail.erase(m_track_avail.begin()+index);
300 delete track;
301 } // removeTrack
302
303 // ----------------------------------------------------------------------------
304 /** \brief Updates the groups after a track was read in.
305 * \param track Pointer to the new track, whose groups are now analysed.
306 */
updateGroups(const Track * track)307 void TrackManager::updateGroups(const Track* track)
308 {
309 if (track->isInternal()) return;
310
311 const std::vector<std::string>& new_groups = track->getGroups();
312
313 Group2Indices &group_2_indices =
314 (track->isArena() ? m_arena_groups :
315 (track->isSoccer() ? m_soccer_arena_groups :
316 m_track_groups));
317
318 std::vector<std::string> &group_names =
319 (track->isArena() ? m_arena_group_names :
320 (track->isSoccer() ? m_soccer_arena_group_names :
321 m_track_group_names));
322
323 const unsigned int groups_amount = (unsigned int)new_groups.size();
324 for(unsigned int i=0; i<groups_amount; i++)
325 {
326 bool group_exists = group_2_indices.find(new_groups[i])
327 != group_2_indices.end();
328 if(!group_exists)
329 group_names.push_back(new_groups[i]);
330 group_2_indices[new_groups[i]].push_back((int)m_tracks.size()-1);
331 }
332 } // updateGroups
333
334 // ----------------------------------------------------------------------------
getTrackIndexByIdent(const std::string & ident) const335 int TrackManager::getTrackIndexByIdent(const std::string& ident) const
336 {
337 for (unsigned i = 0; i < m_tracks.size(); i++)
338 {
339 if (m_tracks[i]->getIdent() == ident)
340 return i;
341 }
342 return -1;
343 } // getTrackIndexByIdent
344