]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/referencecache.cpp
do not rename() .map symlinks, but just overwrite them
[xonotic/netradiant.git] / radiant / referencecache.cpp
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "referencecache.h"
23 #include "globaldefs.h"
24
25 #include "debugging/debugging.h"
26
27 #include "iscenegraph.h"
28 #include "iselection.h"
29 #include "iundo.h"
30 #include "imap.h"
31
32 MapModules& ReferenceAPI_getMapModules();
33
34 #include "imodel.h"
35
36 ModelModules& ReferenceAPI_getModelModules();
37
38 #include "ifilesystem.h"
39 #include "iarchive.h"
40 #include "ifiletypes.h"
41 #include "ireference.h"
42 #include "ientity.h"
43 #include "qerplugin.h"
44
45 #include <list>
46
47 #include "container/cache.h"
48 #include "container/hashfunc.h"
49 #include "os/path.h"
50 #include "stream/textfilestream.h"
51 #include "nullmodel.h"
52 #include "maplib.h"
53 #include "stream/stringstream.h"
54 #include "os/file.h"
55 #include "moduleobserver.h"
56 #include "moduleobservers.h"
57
58 #include "mainframe.h"
59 #include "map.h"
60 #include "filetypes.h"
61
62 extern bool g_writeMapComments;
63
64 bool References_Saved();
65
66 void MapChanged(){
67         Map_SetModified( g_map, !References_Saved() );
68 }
69
70
71 EntityCreator* g_entityCreator = 0;
72
73 bool MapResource_loadFile( const MapFormat& format, scene::Node& root, const char* filename ){
74         globalOutputStream() << "Open file " << filename << " for read...";
75         TextFileInputStream file( filename );
76         if ( !file.failed() ) {
77                 globalOutputStream() << "success\n";
78                 ScopeDisableScreenUpdates disableScreenUpdates( path_get_filename_start( filename ), "Loading Map" );
79                 ASSERT_NOTNULL( g_entityCreator );
80                 format.readGraph( root, file, *g_entityCreator );
81                 return true;
82         }
83         else
84         {
85                 globalErrorStream() << "failure\n";
86                 return false;
87         }
88 }
89
90 NodeSmartReference MapResource_load( const MapFormat& format, const char* path, const char* name ){
91         NodeSmartReference root( NewMapRoot( name ) );
92
93         StringOutputStream fullpath( 256 );
94         fullpath << path << name;
95
96         if ( path_is_absolute( fullpath.c_str() ) ) {
97                 MapResource_loadFile( format, root, fullpath.c_str() );
98         }
99         else
100         {
101                 globalErrorStream() << "map path is not fully qualified: " << makeQuoted( fullpath.c_str() ) << "\n";
102         }
103
104         return root;
105 }
106
107 bool MapResource_saveFile( const MapFormat& format, scene::Node& root, GraphTraversalFunc traverse, const char* filename ){
108         //ASSERT_MESSAGE(path_is_absolute(filename), "MapResource_saveFile: path is not absolute: " << makeQuoted(filename));
109         globalOutputStream() << "Open file " << filename << " for write...";
110         TextFileOutputStream file( filename );
111         if ( !file.failed() ) {
112                 globalOutputStream() << "success\n";
113                 ScopeDisableScreenUpdates disableScreenUpdates( path_get_filename_start( filename ), "Saving Map" );
114                 format.writeGraph( root, traverse, file, g_writeMapComments );
115                 return true;
116         }
117
118         globalErrorStream() << "failure\n";
119         return false;
120 }
121
122 bool file_saveBackup( const char* path ){
123         if ( file_writeable( path ) ) {
124                 StringOutputStream backup( 256 );
125                 backup << StringRange( path, path_get_extension( path ) ) << "bak";
126
127                 return file_move( path, backup.c_str() ); // rename current to backup
128         }
129
130         globalErrorStream() << "map path is not writeable: " << makeQuoted( path ) << "\n";
131         return false;
132 }
133
134 bool MapResource_save( const MapFormat& format, scene::Node& root, const char* path, const char* name ){
135         StringOutputStream fullpath( 256 );
136         fullpath << path << name;
137
138         if ( path_is_absolute( fullpath.c_str() ) ) {
139                 /* We don't want a backup + rename operation if the .map file is
140                  * a symlink. Otherwise we'll break the user's careful symlink setup.
141                  * Just overwrite the original file. Assume the user has versioning. */
142                 bool make_backup;
143                 struct stat st;
144                 if ( lstat(fullpath.c_str(), &st) == 0 ) {
145                         make_backup = true;             // file exists
146                         if ( (st.st_mode & S_IFMT) == S_IFLNK ) {
147                                 make_backup = false;    // .. but it is a symlink
148                         }
149                 } else {
150                         make_backup = false;            // nothing to move
151                 }
152
153                 if ( !make_backup || file_saveBackup( fullpath.c_str() ) ) {
154                         return MapResource_saveFile( format, root, Map_Traverse, fullpath.c_str() );
155                 }
156
157                 globalErrorStream() << "failed to save a backup map file: " << makeQuoted( fullpath.c_str() ) << "\n";
158                 return false;
159         }
160
161         globalErrorStream() << "map path is not fully qualified: " << makeQuoted( fullpath.c_str() ) << "\n";
162         return false;
163 }
164
165 namespace
166 {
167 NodeSmartReference g_nullNode( NewNullNode() );
168 NodeSmartReference g_nullModel( g_nullNode );
169 }
170
171 class NullModelLoader : public ModelLoader
172 {
173 public:
174 scene::Node& loadModel( ArchiveFile& file ){
175         return g_nullModel;
176 }
177 };
178
179 namespace
180 {
181 NullModelLoader g_NullModelLoader;
182 }
183
184
185 /// \brief Returns the model loader for the model \p type or 0 if the model \p type has no loader module
186 ModelLoader* ModelLoader_forType( const char* type ){
187         const char* moduleName = findModuleName( &GlobalFiletypes(), ModelLoader::Name(), type );
188         if ( string_not_empty( moduleName ) ) {
189                 ModelLoader* table = ReferenceAPI_getModelModules().findModule( moduleName );
190                 if ( table != 0 ) {
191                         return table;
192                 }
193                 else
194                 {
195                         globalErrorStream() << "ERROR: Model type incorrectly registered: \"" << moduleName << "\"\n";
196                         return &g_NullModelLoader;
197                 }
198         }
199         return 0;
200 }
201
202 NodeSmartReference ModelResource_load( ModelLoader* loader, const char* name ){
203         ScopeDisableScreenUpdates disableScreenUpdates( path_get_filename_start( name ), "Loading Model" );
204
205         NodeSmartReference model( g_nullModel );
206
207         {
208                 ArchiveFile* file = GlobalFileSystem().openFile( name );
209
210                 if ( file != 0 ) {
211                         globalOutputStream() << "Loaded Model: \"" << name << "\"\n";
212                         model = loader->loadModel( *file );
213                         file->release();
214                 }
215                 else
216                 {
217                         globalErrorStream() << "Model load failed: \"" << name << "\"\n";
218                 }
219         }
220
221         model.get().m_isRoot = true;
222
223         return model;
224 }
225
226
227 inline hash_t path_hash( const char* path, hash_t previous = 0 ){
228 #if GDEF_OS_WINDOWS
229         return string_hash_nocase( path, previous );
230 #else // UNIX
231         return string_hash( path, previous );
232 #endif
233 }
234
235 struct PathEqual
236 {
237         bool operator()( const CopiedString& path, const CopiedString& other ) const {
238                 return path_equal( path.c_str(), other.c_str() );
239         }
240 };
241
242 struct PathHash
243 {
244         typedef hash_t hash_type;
245
246         hash_type operator()( const CopiedString& path ) const {
247                 return path_hash( path.c_str() );
248         }
249 };
250
251 typedef std::pair<CopiedString, CopiedString> ModelKey;
252
253 struct ModelKeyEqual
254 {
255         bool operator()( const ModelKey& key, const ModelKey& other ) const {
256                 return path_equal( key.first.c_str(), other.first.c_str() ) && path_equal( key.second.c_str(), other.second.c_str() );
257         }
258 };
259
260 struct ModelKeyHash
261 {
262         typedef hash_t hash_type;
263
264         hash_type operator()( const ModelKey& key ) const {
265                 return hash_combine( path_hash( key.first.c_str() ), path_hash( key.second.c_str() ) );
266         }
267 };
268
269 typedef HashTable<ModelKey, NodeSmartReference, ModelKeyHash, ModelKeyEqual> ModelCache;
270 ModelCache g_modelCache;
271 bool g_modelCache_enabled = true;
272
273 ModelCache::iterator ModelCache_find( const char* path, const char* name ){
274         if ( g_modelCache_enabled ) {
275                 return g_modelCache.find( ModelKey( path, name ) );
276         }
277         return g_modelCache.end();
278 }
279
280 ModelCache::iterator ModelCache_insert( const char* path, const char* name, scene::Node& node ){
281         if ( g_modelCache_enabled ) {
282                 return g_modelCache.insert( ModelKey( path, name ), NodeSmartReference( node ) );
283         }
284         return g_modelCache.insert( ModelKey( "", "" ), g_nullModel );
285 }
286
287 void ModelCache_flush( const char* path, const char* name ){
288         ModelCache::iterator i = g_modelCache.find( ModelKey( path, name ) );
289         if ( i != g_modelCache.end() ) {
290                 //ASSERT_MESSAGE((*i).value.getCount() == 0, "resource flushed while still in use: " << (*i).key.first.c_str() << (*i).key.second.c_str());
291                 g_modelCache.erase( i );
292         }
293 }
294
295 void ModelCache_clear(){
296         g_modelCache_enabled = false;
297         g_modelCache.clear();
298         g_modelCache_enabled = true;
299 }
300
301 NodeSmartReference Model_load( ModelLoader* loader, const char* path, const char* name, const char* type ){
302         if ( loader != 0 ) {
303                 return ModelResource_load( loader, name );
304         }
305         else
306         {
307                 const char* moduleName = findModuleName( &GlobalFiletypes(), MapFormat::Name(), type );
308                 if ( string_not_empty( moduleName ) ) {
309                         const MapFormat* format = ReferenceAPI_getMapModules().findModule( moduleName );
310                         if ( format != 0 ) {
311                                 return MapResource_load( *format, path, name );
312                         }
313                         else
314                         {
315                                 globalErrorStream() << "ERROR: Map type incorrectly registered: \"" << moduleName << "\"\n";
316                                 return g_nullModel;
317                         }
318                 }
319                 else
320                 {
321                         if ( string_not_empty( type ) ) {
322                                 globalErrorStream() << "Model type not supported: \"" << name << "\"\n";
323                         }
324                         return g_nullModel;
325                 }
326         }
327 }
328
329 namespace
330 {
331 bool g_realised = false;
332
333 // name may be absolute or relative
334 const char* rootPath( const char* name ){
335         return GlobalFileSystem().findRoot(
336                            path_is_absolute( name )
337                            ? name
338                            : GlobalFileSystem().findFile( name )
339                            );
340 }
341 }
342
343 struct ModelResource : public Resource
344 {
345         NodeSmartReference m_model;
346         const CopiedString m_originalName;
347         CopiedString m_path;
348         CopiedString m_name;
349         CopiedString m_type;
350         ModelLoader* m_loader;
351         ModuleObservers m_observers;
352         std::time_t m_modified;
353         std::size_t m_unrealised;
354
355         ModelResource( const CopiedString& name ) :
356                 m_model( g_nullModel ),
357                 m_originalName( name ),
358                 m_type( path_get_extension( name.c_str() ) ),
359                 m_loader( 0 ),
360                 m_modified( 0 ),
361                 m_unrealised( 1 ){
362                 m_loader = ModelLoader_forType( m_type.c_str() );
363
364                 if ( g_realised ) {
365                         realise();
366                 }
367         }
368
369         ~ModelResource(){
370                 if ( realised() ) {
371                         unrealise();
372                 }
373                 ASSERT_MESSAGE( !realised(), "ModelResource::~ModelResource: resource reference still realised: " << makeQuoted( m_name.c_str() ) );
374         }
375
376         // NOT COPYABLE
377         ModelResource( const ModelResource& );
378
379         // NOT ASSIGNABLE
380         ModelResource& operator=( const ModelResource& );
381
382         void setModel( const NodeSmartReference& model ){
383                 m_model = model;
384         }
385
386         void clearModel(){
387                 m_model = g_nullModel;
388         }
389
390         void loadCached(){
391                 if ( g_modelCache_enabled ) {
392                         // cache lookup
393                         ModelCache::iterator i = ModelCache_find( m_path.c_str(), m_name.c_str() );
394                         if ( i == g_modelCache.end() ) {
395                                 i = ModelCache_insert(
396                                         m_path.c_str(),
397                                         m_name.c_str(),
398                                         Model_load( m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str() )
399                                         );
400                         }
401
402                         setModel( ( *i ).value );
403                 }
404                 else
405                 {
406                         setModel( Model_load( m_loader, m_path.c_str(), m_name.c_str(), m_type.c_str() ) );
407                 }
408         }
409
410         void loadModel(){
411                 loadCached();
412                 connectMap();
413                 mapSave();
414         }
415
416         bool load(){
417                 ASSERT_MESSAGE( realised(), "resource not realised" );
418                 if ( m_model == g_nullModel ) {
419                         loadModel();
420                 }
421
422                 return m_model != g_nullModel;
423         }
424
425         bool save(){
426                 if ( !mapSaved() ) {
427                         const char* moduleName = findModuleName( GetFileTypeRegistry(), MapFormat::Name(), m_type.c_str() );
428                         if ( string_not_empty( moduleName ) ) {
429                                 const MapFormat* format = ReferenceAPI_getMapModules().findModule( moduleName );
430                                 if ( format != 0 && MapResource_save( *format, m_model.get(), m_path.c_str(), m_name.c_str() ) ) {
431                                         mapSave();
432                                         return true;
433                                 }
434                         }
435                 }
436                 return false;
437         }
438
439         void flush(){
440                 if ( realised() ) {
441                         ModelCache_flush( m_path.c_str(), m_name.c_str() );
442                 }
443         }
444
445         scene::Node* getNode(){
446                 //if(m_model != g_nullModel)
447                 {
448                         return m_model.get_pointer();
449                 }
450                 //return 0;
451         }
452
453         void setNode( scene::Node* node ){
454                 ModelCache::iterator i = ModelCache_find( m_path.c_str(), m_name.c_str() );
455                 if ( i != g_modelCache.end() ) {
456                         ( *i ).value = NodeSmartReference( *node );
457                 }
458                 setModel( NodeSmartReference( *node ) );
459
460                 connectMap();
461         }
462
463         void attach( ModuleObserver& observer ){
464                 if ( realised() ) {
465                         observer.realise();
466                 }
467                 m_observers.attach( observer );
468         }
469
470         void detach( ModuleObserver& observer ){
471                 if ( realised() ) {
472                         observer.unrealise();
473                 }
474                 m_observers.detach( observer );
475         }
476
477         bool realised(){
478                 return m_unrealised == 0;
479         }
480
481         void realise(){
482                 ASSERT_MESSAGE( m_unrealised != 0, "ModelResource::realise: already realised" );
483                 if ( --m_unrealised == 0 ) {
484                         m_path = rootPath( m_originalName.c_str() );
485                         m_name = path_make_relative( m_originalName.c_str(), m_path.c_str() );
486
487                         //globalOutputStream() << "ModelResource::realise: " << m_path.c_str() << m_name.c_str() << "\n";
488
489                         m_observers.realise();
490                 }
491         }
492
493         void unrealise(){
494                 if ( ++m_unrealised == 1 ) {
495                         m_observers.unrealise();
496
497                         //globalOutputStream() << "ModelResource::unrealise: " << m_path.c_str() << m_name.c_str() << "\n";
498                         clearModel();
499                 }
500         }
501
502         bool isMap() const {
503                 return Node_getMapFile( m_model ) != 0;
504         }
505
506         void connectMap(){
507                 MapFile* map = Node_getMapFile( m_model );
508                 if ( map != 0 ) {
509                         map->setChangedCallback( makeCallbackF(MapChanged) );
510                 }
511         }
512
513         std::time_t modified() const {
514                 StringOutputStream fullpath( 256 );
515                 fullpath << m_path.c_str() << m_name.c_str();
516                 return file_modified( fullpath.c_str() );
517         }
518
519         void mapSave(){
520                 m_modified = modified();
521                 MapFile* map = Node_getMapFile( m_model );
522                 if ( map != 0 ) {
523                         map->save();
524                 }
525         }
526
527         bool mapSaved() const {
528                 MapFile* map = Node_getMapFile( m_model );
529                 if ( map != 0 ) {
530                         return m_modified == modified() && map->saved();
531                 }
532                 return true;
533         }
534
535         bool isModified() const {
536                 return ( ( !string_empty( m_path.c_str() ) // had or has an absolute path
537                                    && m_modified != modified() ) // AND disk timestamp changed
538                                  || !path_equal( rootPath( m_originalName.c_str() ), m_path.c_str() ) ); // OR absolute vfs-root changed
539         }
540
541         void refresh(){
542                 if ( isModified() ) {
543                         flush();
544                         unrealise();
545                         realise();
546                 }
547         }
548 };
549
550 class HashtableReferenceCache : public ReferenceCache, public ModuleObserver
551 {
552 typedef HashedCache<CopiedString, ModelResource, PathHash, PathEqual> ModelReferences;
553 ModelReferences m_references;
554 std::size_t m_unrealised;
555
556 class ModelReferencesSnapshot
557 {
558 ModelReferences& m_references;
559 typedef std::list<ModelReferences::iterator> Iterators;
560 Iterators m_iterators;
561 public:
562 typedef Iterators::iterator iterator;
563
564 ModelReferencesSnapshot( ModelReferences& references ) : m_references( references ){
565         for ( ModelReferences::iterator i = m_references.begin(); i != m_references.end(); ++i )
566         {
567                 m_references.capture( i );
568                 m_iterators.push_back( i );
569         }
570 }
571
572 ~ModelReferencesSnapshot(){
573         for ( Iterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i )
574         {
575                 m_references.release( *i );
576         }
577 }
578
579 iterator begin(){
580         return m_iterators.begin();
581 }
582
583 iterator end(){
584         return m_iterators.end();
585 }
586 };
587
588 public:
589
590 typedef ModelReferences::iterator iterator;
591
592 HashtableReferenceCache() : m_unrealised( 1 ){
593 }
594
595 iterator begin(){
596         return m_references.begin();
597 }
598
599 iterator end(){
600         return m_references.end();
601 }
602
603 void clear(){
604         m_references.clear();
605 }
606
607 Resource* capture( const char* path ){
608         //globalOutputStream() << "capture: \"" << path << "\"\n";
609         return m_references.capture( CopiedString( path ) ).get();
610 }
611
612 void release( const char* path ){
613         m_references.release( CopiedString( path ) );
614         //globalOutputStream() << "release: \"" << path << "\"\n";
615 }
616
617 void setEntityCreator( EntityCreator& entityCreator ){
618         g_entityCreator = &entityCreator;
619 }
620
621 bool realised() const {
622         return m_unrealised == 0;
623 }
624
625 void realise(){
626         ASSERT_MESSAGE( m_unrealised != 0, "HashtableReferenceCache::realise: already realised" );
627         if ( --m_unrealised == 0 ) {
628                 g_realised = true;
629
630                 {
631                         ModelReferencesSnapshot snapshot( m_references );
632                         for ( ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i )
633                         {
634                                 ModelReferences::value_type& value = *( *i );
635                                 if ( value.value.count() != 1 ) {
636                                         value.value.get()->realise();
637                                 }
638                         }
639                 }
640         }
641 }
642
643 void unrealise(){
644         if ( ++m_unrealised == 1 ) {
645                 g_realised = false;
646
647                 {
648                         ModelReferencesSnapshot snapshot( m_references );
649                         for ( ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i )
650                         {
651                                 ModelReferences::value_type& value = *( *i );
652                                 if ( value.value.count() != 1 ) {
653                                         value.value.get()->unrealise();
654                                 }
655                         }
656                 }
657
658                 ModelCache_clear();
659         }
660 }
661
662 void refresh(){
663         ModelReferencesSnapshot snapshot( m_references );
664         for ( ModelReferencesSnapshot::iterator i = snapshot.begin(); i != snapshot.end(); ++i )
665         {
666                 ModelResource* resource = ( *( *i ) ).value.get();
667                 if ( !resource->isMap() ) {
668                         resource->refresh();
669                 }
670         }
671 }
672 };
673
674 namespace
675 {
676 HashtableReferenceCache g_referenceCache;
677 }
678
679 #if 0
680 class ResourceVisitor
681 {
682 public:
683 virtual void visit( const char* name, const char* path, const
684                                         };
685 #endif
686
687 void SaveReferences(){
688         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Saving Map" );
689         for ( HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i )
690         {
691                 ( *i ).value->save();
692         }
693         MapChanged();
694 }
695
696 bool References_Saved(){
697         for ( HashtableReferenceCache::iterator i = g_referenceCache.begin(); i != g_referenceCache.end(); ++i )
698         {
699                 scene::Node* node = ( *i ).value->getNode();
700                 if ( node != 0 ) {
701                         MapFile* map = Node_getMapFile( *node );
702                         if ( map != 0 && !map->saved() ) {
703                                 return false;
704                         }
705                 }
706         }
707         return true;
708 }
709
710 void RefreshReferences(){
711         ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Refreshing Models" );
712         g_referenceCache.refresh();
713 }
714
715
716 void FlushReferences(){
717         ModelCache_clear();
718
719         g_referenceCache.clear();
720 }
721
722 ReferenceCache& GetReferenceCache(){
723         return g_referenceCache;
724 }
725
726
727 #include "modulesystem/modulesmap.h"
728 #include "modulesystem/singletonmodule.h"
729 #include "modulesystem/moduleregistry.h"
730
731 class ReferenceDependencies :
732         public GlobalRadiantModuleRef,
733         public GlobalFileSystemModuleRef,
734         public GlobalFiletypesModuleRef
735 {
736 ModelModulesRef m_model_modules;
737 MapModulesRef m_map_modules;
738 public:
739 ReferenceDependencies() :
740         m_model_modules( GlobalRadiant().getRequiredGameDescriptionKeyValue( "modeltypes" ) ),
741         m_map_modules( GlobalRadiant().getRequiredGameDescriptionKeyValue( "maptypes" ) )
742 {
743 }
744
745 ModelModules& getModelModules(){
746         return m_model_modules.get();
747 }
748
749 MapModules& getMapModules(){
750         return m_map_modules.get();
751 }
752 };
753
754 class ReferenceAPI : public TypeSystemRef
755 {
756 ReferenceCache* m_reference;
757 public:
758 typedef ReferenceCache Type;
759
760 STRING_CONSTANT( Name, "*" );
761
762 ReferenceAPI(){
763         g_nullModel = NewNullModel();
764
765         GlobalFileSystem().attach( g_referenceCache );
766
767         m_reference = &GetReferenceCache();
768 }
769
770 ~ReferenceAPI(){
771         GlobalFileSystem().detach( g_referenceCache );
772
773         g_nullModel = g_nullNode;
774 }
775
776 ReferenceCache* getTable(){
777         return m_reference;
778 }
779 };
780
781 typedef SingletonModule<ReferenceAPI, ReferenceDependencies> ReferenceModule;
782 typedef Static<ReferenceModule> StaticReferenceModule;
783 StaticRegisterModule staticRegisterReference( StaticReferenceModule::instance() );
784
785 ModelModules& ReferenceAPI_getModelModules(){
786         return StaticReferenceModule::instance().getDependencies().getModelModules();
787 }
788
789 MapModules& ReferenceAPI_getMapModules(){
790         return StaticReferenceModule::instance().getDependencies().getMapModules( );
791 }