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