1 // terrasync.cxx -- scenery fetcher
3 // Started by Curtis Olson, November 2002.
5 // Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
6 // Copyright (C) 2008 Alexander R. Perry <alex.perry@ieee.org>
7 // Copyright (C) 2011 Thorsten Brehm <brehmt@gmail.com>
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 # include <simgear_config.h>
29 #include <simgear/compiler.h>
38 #elif defined(_MSC_VER)
44 #include <stdlib.h> // atoi() atof() abs() system()
45 #include <signal.h> // signal()
53 #include <simgear/compiler.h>
55 #include "terrasync.hxx"
57 #include <simgear/bucket/newbucket.hxx>
58 #include <simgear/misc/sg_path.hxx>
59 #include <simgear/misc/strutils.hxx>
60 #include <simgear/threads/SGQueue.hxx>
61 #include <simgear/misc/sg_dir.hxx>
62 #include <simgear/debug/BufferedLogCallback.hxx>
63 #include <simgear/props/props_io.hxx>
64 #include <simgear/io/HTTPClient.hxx>
65 #include <simgear/io/SVNRepository.hxx>
66 #include <simgear/structure/exception.hxx>
68 static const bool svn_built_in_available = true;
70 using namespace simgear;
73 const char* rsync_cmd =
74 "rsync --verbose --archive --delete --perms --owner --group";
76 const char* svn_options =
79 namespace UpdateInterval
81 // interval in seconds to allow an update to repeat after a successful update (=daily)
82 static const double SuccessfulAttempt = 24*60*60;
83 // interval in seconds to allow another update after a failed attempt (10 minutes)
84 static const double FailedAttempt = 10*60;
87 typedef map<string,time_t> CompletedTiles;
89 ///////////////////////////////////////////////////////////////////////////////
90 // helper functions ///////////////////////////////////////////////////////////
91 ///////////////////////////////////////////////////////////////////////////////
92 string stripPath(string path)
94 // svn doesn't like trailing white-spaces or path separators - strip them!
95 path = simgear::strutils::strip(path);
96 size_t slen = path.length();
98 ((path[slen-1]=='/')||(path[slen-1]=='\\')))
102 return path.substr(0,slen);
105 bool hasWhitespace(string path)
107 return path.find(' ')!=string::npos;
110 ///////////////////////////////////////////////////////////////////////////////
111 // SyncItem ////////////////////////////////////////////////////////////
112 ///////////////////////////////////////////////////////////////////////////////
118 Stop = 0, ///< special item indicating to stop the SVNThread
129 Cached, ///< using already cached result
142 SyncItem(string dir, Type ty) :
153 ///////////////////////////////////////////////////////////////////////////////
156 * @brief SyncSlot encapsulates a queue of sync items we will fetch
157 * serially. Multiple slots exist to sync different types of item in
164 isNewDirectory(false),
168 SyncItem currentItem;
170 std::queue<SyncItem> queue;
171 std::auto_ptr<SVNRepository> repository;
173 bool busy; ///< is the slot working or idle
175 unsigned int nextWarnTimeout;
178 static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
179 static const int SYNC_SLOT_SHARED_DATA = 1; /// shared Models and Airport data
180 static const int SYNC_SLOT_AI_DATA = 2; /// AI traffic and models
181 static const unsigned int NUM_SYNC_SLOTS = 3;
184 * @brief translate a sync item type into one of the available slots.
185 * This provides the scheduling / balancing / prioritising between slots.
187 static unsigned int syncSlotForType(SyncItem::Type ty)
190 case SyncItem::Tile: return SYNC_SLOT_TILES;
191 case SyncItem::SharedModels:
192 case SyncItem::AirportData:
193 return SYNC_SLOT_SHARED_DATA;
194 case SyncItem::AIData:
195 return SYNC_SLOT_AI_DATA;
198 return SYNC_SLOT_SHARED_DATA;
203 ///////////////////////////////////////////////////////////////////////////////
204 // SGTerraSync::SvnThread /////////////////////////////////////////////////////
205 ///////////////////////////////////////////////////////////////////////////////
206 class SGTerraSync::SvnThread : public SGThread
210 virtual ~SvnThread( ) { stop(); }
215 bool isIdle() {return !_busy; }
216 void request(const SyncItem& dir) {waitingTiles.push_front(dir);}
217 bool isDirty() { bool r = _is_dirty;_is_dirty = false;return r;}
218 bool hasNewTiles() { return !_freshTiles.empty();}
219 SyncItem getNewTile() { return _freshTiles.pop_front();}
221 void setSvnServer(string server) { _svn_server = stripPath(server);}
222 void setSvnDataServer(string server) { _svn_data_server = stripPath(server);}
224 void setExtSvnUtility(string svn_util) { _svn_command = simgear::strutils::strip(svn_util);}
225 void setRsyncServer(string server) { _rsync_server = simgear::strutils::strip(server);}
226 void setLocalDir(string dir) { _local_dir = stripPath(dir);}
227 string getLocalDir() { return _local_dir;}
228 void setUseSvn(bool use_svn) { _use_svn = use_svn;}
229 void setAllowedErrorCount(int errors) {_allowed_errors = errors;}
231 void setCachePath(const SGPath& p) {_persistentCachePath = p;}
232 void setCacheHits(unsigned int hits) {_cache_hits = hits;}
233 void setUseBuiltin(bool built_in) { _use_built_in = built_in;}
235 volatile bool _active;
236 volatile bool _running;
238 volatile bool _stalled;
239 volatile int _fail_count;
240 volatile int _updated_tile_count;
241 volatile int _success_count;
242 volatile int _consecutive_errors;
243 volatile int _allowed_errors;
244 volatile int _cache_hits;
245 volatile int _transfer_rate;
246 // kbytes, not bytes, because bytes might overflow 2^31
247 volatile int _total_kb_downloaded;
252 // external model run and helpers
254 void syncPathExternal(const SyncItem& next);
255 bool runExternalSyncCommand(const char* dir);
257 // internal mode run and helpers
259 void updateSyncSlot(SyncSlot& slot);
261 // commond helpers between both internal and external models
263 bool isPathCached(const SyncItem& next) const;
264 void initCompletedTilesPersistentCache();
265 void writeCompletedTilesPersistentCache() const;
266 void updated(SyncItem item, bool isNewDirectory);
267 void fail(SyncItem failedItem);
271 SyncSlot _syncSlots[NUM_SYNC_SLOTS];
273 volatile bool _is_dirty;
275 SGBlockingDeque <SyncItem> waitingTiles;
276 CompletedTiles _completedTiles;
277 SGBlockingDeque <SyncItem> _freshTiles;
280 string _svn_data_server;
282 string _rsync_server;
284 SGPath _persistentCachePath;
287 SGTerraSync::SvnThread::SvnThread() :
293 _updated_tile_count(0),
295 _consecutive_errors(0),
299 _total_kb_downloaded(0),
305 _http.setUserAgent("terrascenery-" SG_STRINGIZE(SG_VERSION));
308 void SGTerraSync::SvnThread::stop()
310 // drop any pending requests
311 waitingTiles.clear();
316 // set stop flag and wake up the thread with an empty request
318 SyncItem w(string(), SyncItem::Stop);
324 bool SGTerraSync::SvnThread::start()
331 SG_LOG(SG_TERRAIN,SG_ALERT,
332 "Cannot start scenery download. Local cache directory is undefined.");
338 SGPath path(_local_dir);
341 SG_LOG(SG_TERRAIN,SG_ALERT,
342 "Cannot start scenery download. Directory '" << _local_dir <<
343 "' does not exist. Set correct directory path or create directory folder.");
349 path.append("version");
352 SG_LOG(SG_TERRAIN,SG_ALERT,
353 "Cannot start scenery download. Directory '" << _local_dir <<
354 "' contains the base package. Use a separate directory.");
360 _use_svn |= _use_built_in;
362 if ((_use_svn)&&(_svn_server==""))
364 SG_LOG(SG_TERRAIN,SG_ALERT,
365 "Cannot start scenery download. Subversion scenery server is undefined.");
370 if ((!_use_svn)&&(_rsync_server==""))
372 SG_LOG(SG_TERRAIN,SG_ALERT,
373 "Cannot start scenery download. Rsync scenery server is undefined.");
380 _updated_tile_count = 0;
382 _consecutive_errors = 0;
389 if (_use_svn && _use_built_in)
390 status = "Using built-in SVN support. ";
393 status = "Using external SVN utility '";
394 status += _svn_command;
399 status = "Using RSYNC. ";
402 // not really an alert - but we want to (always) see this message, so user is
403 // aware we're downloading scenery (and using bandwidth).
404 SG_LOG(SG_TERRAIN,SG_ALERT,
405 "Starting automatic scenery download/synchronization. "
407 << "Directory: '" << _local_dir << "'.");
413 bool SGTerraSync::SvnThread::runExternalSyncCommand(const char* dir)
416 SGPath localPath( _local_dir );
417 localPath.append( dir );
421 buf << "\"" << _svn_command << "\" "
422 << svn_options << " "
423 << "\"" << _svn_server << "/" << dir << "\" "
424 << "\"" << localPath.str_native() << "\"";
426 buf << rsync_cmd << " "
427 << "\"" << _rsync_server << "/" << dir << "/\" "
428 << "\"" << localPath.str_native() << "/\"";
433 // windows command line parsing is just lovely...
434 // to allow white spaces, the system call needs this:
435 // ""C:\Program Files\something.exe" somearg "some other arg""
436 // Note: whitespace strings quoted by a pair of "" _and_ the
437 // entire string needs to be wrapped by "" too.
438 // The svn url needs forward slashes (/) as a path separator while
439 // the local path needs windows-native backslash as a path separator.
440 command = "\"" + buf.str() + "\"";
444 SG_LOG(SG_TERRAIN,SG_DEBUG, "sync command '" << command << "'");
447 // tbd: does Windows support "popen"?
448 int rc = system( command.c_str() );
450 FILE* pipe = popen( command.c_str(), "r");
452 // wait for external process to finish
459 SG_LOG(SG_TERRAIN,SG_ALERT,
460 "Failed to synchronize directory '" << dir << "', " <<
461 "error code= " << rc);
467 void SGTerraSync::SvnThread::run()
470 initCompletedTilesPersistentCache();
483 void SGTerraSync::SvnThread::runExternal()
487 SyncItem next = waitingTiles.pop_front();
491 if (isPathCached(next)) {
493 SG_LOG(SG_TERRAIN, SG_DEBUG,
494 "Cache hit for: '" << next._dir << "'");
495 next._status = SyncItem::Cached;
496 _freshTiles.push_back(next);
501 syncPathExternal(next);
503 if ((_allowed_errors >= 0)&&
504 (_consecutive_errors >= _allowed_errors))
509 } // of thread running loop
512 void SGTerraSync::SvnThread::syncPathExternal(const SyncItem& next)
515 SGPath path( _local_dir );
516 path.append( next._dir );
517 bool isNewDirectory = !path.exists();
520 if (isNewDirectory) {
521 int rc = path.create_dir( 0755 );
523 SG_LOG(SG_TERRAIN,SG_ALERT,
524 "Cannot create directory '" << path << "', return code = " << rc );
525 throw sg_exception("Cannot create directory for terrasync", path.str());
529 if (!runExternalSyncCommand(next._dir.c_str())) {
530 throw sg_exception("Running external sync command failed");
532 } catch (sg_exception& e) {
538 updated(next, isNewDirectory);
542 void SGTerraSync::SvnThread::updateSyncSlot(SyncSlot &slot)
544 if (slot.repository.get()) {
545 if (slot.repository->isDoingSync()) {
547 if (slot.stamp.elapsedMSec() > slot.nextWarnTimeout) {
548 SG_LOG(SG_TERRAIN, SG_INFO, "sync taking a long time:" << slot.currentItem._dir << " taken " << slot.stamp.elapsedMSec());
549 SG_LOG(SG_TERRAIN, SG_INFO, "HTTP status:" << _http.hasActiveRequests());
550 slot.nextWarnTimeout += 10000;
553 return; // easy, still working
557 SVNRepository::ResultCode res = slot.repository->failure();
558 if (res == SVNRepository::SVN_ERROR_NOT_FOUND) {
559 // this is fine, but maybe we should use a different return code
560 // in the future to higher layers can distinguish this case
561 slot.currentItem._status = SyncItem::NotFound;
562 _freshTiles.push_back(slot.currentItem);
564 } else if (res != SVNRepository::SVN_NO_ERROR) {
565 fail(slot.currentItem);
567 updated(slot.currentItem, slot.isNewDirectory);
568 SG_LOG(SG_TERRAIN, SG_DEBUG, "sync of " << slot.repository->baseUrl() << " finished ("
569 << slot.stamp.elapsedMSec() << " msec");
572 // whatever happened, we're done with this repository instance
574 slot.repository.reset();
577 // init and start sync of the next repository
578 if (!slot.queue.empty()) {
579 slot.currentItem = slot.queue.front();
582 SGPath path(_local_dir);
583 path.append(slot.currentItem._dir);
584 slot.isNewDirectory = !path.exists();
585 if (slot.isNewDirectory) {
586 int rc = path.create_dir( 0755 );
588 SG_LOG(SG_TERRAIN,SG_ALERT,
589 "Cannot create directory '" << path << "', return code = " << rc );
590 fail(slot.currentItem);
593 } // of creating directory step
595 string serverUrl(_svn_server);
596 if (slot.currentItem._type == SyncItem::AIData) {
597 serverUrl = _svn_data_server;
600 slot.repository.reset(new SVNRepository(path, &_http));
601 slot.repository->setBaseUrl(serverUrl + "/" + slot.currentItem._dir);
602 slot.repository->update();
604 slot.nextWarnTimeout = 20000;
607 SG_LOG(SG_TERRAIN, SG_DEBUG, "sync of " << slot.repository->baseUrl() << " started, queue size is " << slot.queue.size());
611 void SGTerraSync::SvnThread::runInternal()
615 _transfer_rate = _http.transferRateBytesPerSec();
616 // convert from bytes to kbytes
617 _total_kb_downloaded = static_cast<int>(_http.totalBytesDownloaded() / 1024);
622 // drain the waiting tiles queue into the sync slot queues.
623 while (!waitingTiles.empty()) {
624 SyncItem next = waitingTiles.pop_front();
625 if (isPathCached(next)) {
627 SG_LOG(SG_TERRAIN, SG_DEBUG, "TerraSync Cache hit for: '" << next._dir << "'");
628 next._status = SyncItem::Cached;
629 _freshTiles.push_back(next);
634 unsigned int slot = syncSlotForType(next._type);
635 _syncSlots[slot].queue.push(next);
638 bool anySlotBusy = false;
639 // update each sync slot in turn
640 for (unsigned int slot=0; slot < NUM_SYNC_SLOTS; ++slot) {
641 updateSyncSlot(_syncSlots[slot]);
642 anySlotBusy |= _syncSlots[slot].busy;
647 // wait on the blocking deque here, otherwise we spin
648 // the loop very fast, since _http::update with no connections
649 // active returns immediately.
650 waitingTiles.waitOnNotEmpty();
652 } // of thread running loop
655 bool SGTerraSync::SvnThread::isPathCached(const SyncItem& next) const
657 CompletedTiles::const_iterator ii = _completedTiles.find( next._dir );
658 if (ii == _completedTiles.end()) {
662 // check if the path still physically exists
663 SGPath p(_local_dir);
669 time_t now = time(0);
670 return (ii->second > now);
673 void SGTerraSync::SvnThread::fail(SyncItem failedItem)
675 time_t now = time(0);
676 _consecutive_errors++;
678 failedItem._status = SyncItem::Failed;
679 _freshTiles.push_back(failedItem);
680 _completedTiles[ failedItem._dir ] = now + UpdateInterval::FailedAttempt;
684 void SGTerraSync::SvnThread::updated(SyncItem item, bool isNewDirectory)
686 time_t now = time(0);
687 _consecutive_errors = 0;
689 SG_LOG(SG_TERRAIN,SG_INFO,
690 "Successfully synchronized directory '" << item._dir << "'");
692 item._status = SyncItem::Updated;
693 if (item._type == SyncItem::Tile) {
694 _updated_tile_count++;
697 _freshTiles.push_back(item);
698 _completedTiles[ item._dir ] = now + UpdateInterval::SuccessfulAttempt;
700 writeCompletedTilesPersistentCache();
703 void SGTerraSync::SvnThread::initCompletedTilesPersistentCache()
705 if (!_persistentCachePath.exists()) {
709 SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
710 time_t now = time(0);
713 readProperties(_persistentCachePath.str(), cacheRoot);
714 } catch (sg_exception& e) {
715 SG_LOG(SG_TERRAIN, SG_INFO, "corrupted persistent cache, discarding");
719 for (int i=0; i<cacheRoot->nChildren(); ++i) {
720 SGPropertyNode* entry = cacheRoot->getChild(i);
721 string tileName = entry->getStringValue("path");
722 time_t stamp = entry->getIntValue("stamp");
727 _completedTiles[tileName] = stamp;
731 void SGTerraSync::SvnThread::writeCompletedTilesPersistentCache() const
734 if (_persistentCachePath.isNull()) {
738 std::ofstream f(_persistentCachePath.c_str(), std::ios::trunc);
743 SGPropertyNode_ptr cacheRoot(new SGPropertyNode);
744 CompletedTiles::const_iterator it = _completedTiles.begin();
745 for (; it != _completedTiles.end(); ++it) {
746 SGPropertyNode* entry = cacheRoot->addChild("entry");
747 entry->setStringValue("path", it->first);
748 entry->setIntValue("stamp", it->second);
751 writeProperties(f, cacheRoot, true /* write_all */);
755 ///////////////////////////////////////////////////////////////////////////////
756 // SGTerraSync ////////////////////////////////////////////////////////////////
757 ///////////////////////////////////////////////////////////////////////////////
758 SGTerraSync::SGTerraSync() :
763 _svnThread = new SvnThread();
764 _log = new BufferedLogCallback(SG_TERRAIN, SG_INFO);
765 _log->truncateAt(255);
767 sglog().addCallback(_log);
770 SGTerraSync::~SGTerraSync()
774 sglog().removeCallback(_log);
776 _tiedProperties.Untie();
779 void SGTerraSync::setRoot(SGPropertyNode_ptr root)
781 _terraRoot = root->getNode("/sim/terrasync",true);
784 void SGTerraSync::init()
793 _terraRoot->setBoolValue("built-in-svn-available",svn_built_in_available);
798 void SGTerraSync::shutdown()
803 void SGTerraSync::reinit()
805 // do not reinit when enabled and we're already up and running
806 if ((_terraRoot->getBoolValue("enabled",false))&&
807 (_svnThread->_active && _svnThread->_running))
814 if (_terraRoot->getBoolValue("enabled",false))
816 _svnThread->setSvnServer(_terraRoot->getStringValue("svn-server",""));
817 _svnThread->setSvnDataServer(_terraRoot->getStringValue("svn-data-server",""));
818 _svnThread->setRsyncServer(_terraRoot->getStringValue("rsync-server",""));
819 _svnThread->setLocalDir(_terraRoot->getStringValue("scenery-dir",""));
820 _svnThread->setAllowedErrorCount(_terraRoot->getIntValue("max-errors",5));
821 _svnThread->setUseBuiltin(_terraRoot->getBoolValue("use-built-in-svn",true));
822 _svnThread->setCachePath(SGPath(_terraRoot->getStringValue("cache-path","")));
823 _svnThread->setCacheHits(_terraRoot->getIntValue("cache-hit", 0));
824 _svnThread->setUseSvn(_terraRoot->getBoolValue("use-svn",true));
825 _svnThread->setExtSvnUtility(_terraRoot->getStringValue("ext-svn-utility","svn"));
827 if (_svnThread->start())
829 syncAirportsModels();
833 _stalledNode->setBoolValue(_svnThread->_stalled);
836 void SGTerraSync::bind()
843 _tiedProperties.Tie( _terraRoot->getNode("busy", true), (bool*) &_svnThread->_busy );
844 _tiedProperties.Tie( _terraRoot->getNode("active", true), (bool*) &_svnThread->_active );
845 _tiedProperties.Tie( _terraRoot->getNode("update-count", true), (int*) &_svnThread->_success_count );
846 _tiedProperties.Tie( _terraRoot->getNode("error-count", true), (int*) &_svnThread->_fail_count );
847 _tiedProperties.Tie( _terraRoot->getNode("tile-count", true), (int*) &_svnThread->_updated_tile_count );
848 _tiedProperties.Tie( _terraRoot->getNode("cache-hits", true), (int*) &_svnThread->_cache_hits );
849 _tiedProperties.Tie( _terraRoot->getNode("transfer-rate-bytes-sec", true), (int*) &_svnThread->_transfer_rate );
851 // use kbytes here because propety doesn't support 64-bit and we might conceivably
852 // download more than 2G in a single session
853 _tiedProperties.Tie( _terraRoot->getNode("downloaded-kbytes", true), (int*) &_svnThread->_total_kb_downloaded );
855 _terraRoot->getNode("busy", true)->setAttribute(SGPropertyNode::WRITE,false);
856 _terraRoot->getNode("active", true)->setAttribute(SGPropertyNode::WRITE,false);
857 _terraRoot->getNode("update-count", true)->setAttribute(SGPropertyNode::WRITE,false);
858 _terraRoot->getNode("error-count", true)->setAttribute(SGPropertyNode::WRITE,false);
859 _terraRoot->getNode("tile-count", true)->setAttribute(SGPropertyNode::WRITE,false);
860 _terraRoot->getNode("use-built-in-svn", true)->setAttribute(SGPropertyNode::USERARCHIVE,false);
861 _terraRoot->getNode("use-svn", true)->setAttribute(SGPropertyNode::USERARCHIVE,false);
862 // stalled is used as a signal handler (to connect listeners triggering GUI pop-ups)
863 _stalledNode = _terraRoot->getNode("stalled", true);
864 _stalledNode->setBoolValue(_svnThread->_stalled);
865 _stalledNode->setAttribute(SGPropertyNode::PRESERVE,true);
868 void SGTerraSync::unbind()
871 _tiedProperties.Untie();
876 _stalledNode.clear();
880 void SGTerraSync::update(double)
882 static SGBucket bucket;
883 if (_svnThread->isDirty())
885 if (!_svnThread->_active)
887 if (_svnThread->_stalled)
889 SG_LOG(SG_TERRAIN,SG_ALERT,
890 "Automatic scenery download/synchronization stalled. Too many errors.");
894 // not really an alert - just always show this message
895 SG_LOG(SG_TERRAIN,SG_ALERT,
896 "Automatic scenery download/synchronization has stopped.");
898 _stalledNode->setBoolValue(_svnThread->_stalled);
901 while (_svnThread->hasNewTiles())
903 SyncItem next = _svnThread->getNewTile();
905 if ((next._type == SyncItem::Tile) || (next._type == SyncItem::AIData)) {
906 _activeTileDirs.erase(next._dir);
908 } // of freshly synced items
912 bool SGTerraSync::isIdle() {return _svnThread->isIdle();}
914 void SGTerraSync::syncAirportsModels()
916 static const char* bounds = "MZAJKL"; // airport sync order: K-L, A-J, M-Z
917 // note "request" method uses LIFO order, i.e. processes most recent request first
918 for( unsigned i = 0; i < strlen(bounds)/2; i++ )
920 for ( char synced_other = bounds[2*i]; synced_other <= bounds[2*i+1]; synced_other++ )
923 dir << "Airports/" << synced_other;
924 SyncItem w(dir.str(), SyncItem::AirportData);
925 _svnThread->request( w );
929 SyncItem w("Models", SyncItem::SharedModels);
930 _svnThread->request( w );
933 void SGTerraSync::syncAreaByPath(const std::string& aPath)
935 const char* terrainobjects[3] = { "Terrain/", "Objects/", 0 };
936 for (const char** tree = &terrainobjects[0]; *tree; tree++)
938 std::string dir = string(*tree) + aPath;
939 if (_activeTileDirs.find(dir) != _activeTileDirs.end()) {
943 _activeTileDirs.insert(dir);
944 SyncItem w(dir, SyncItem::Tile);
945 _svnThread->request( w );
949 bool SGTerraSync::scheduleTile(const SGBucket& bucket)
951 std::string basePath = bucket.gen_base_path();
952 syncAreaByPath(basePath);
956 bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
958 if (!_svnThread->_running) {
962 const char* terrainobjects[3] = { "Terrain/", "Objects/", 0 };
963 for (const char** tree = &terrainobjects[0]; *tree; tree++) {
964 string s = *tree + sceneryDir;
965 if (_activeTileDirs.find(s) != _activeTileDirs.end()) {
973 void SGTerraSync::scheduleDataDir(const std::string& dataDir)
975 if (_activeTileDirs.find(dataDir) != _activeTileDirs.end()) {
979 _activeTileDirs.insert(dataDir);
980 SyncItem w(dataDir, SyncItem::AIData);
981 _svnThread->request( w );
985 bool SGTerraSync::isDataDirPending(const std::string& dataDir) const
987 if (!_svnThread->_running) {
991 return (_activeTileDirs.find(dataDir) != _activeTileDirs.end());
994 void SGTerraSync::reposition()