Exemple #1
0
 def startWebsocketRemoteConsole(self,
                                 ip='127.0.0.1',
                                 port=54321,
                                 staticglobals=False):
     debug("Enabling Websocket Remote Console on port %d" % port)
     self.remoteConsole = QueueWebsocketServer(ConsoleHandler, globals(),
                                               staticglobals)
     self._startRemoteConsoleThread(ip, port)
Exemple #2
0
 def startSocketRemoteConsole(self, ip='127.0.0.1', port=54321, staticglobals=False):
     debug("Enabling Socket Remote Console on port %d" % port)
     self.remoteConsole = QueueWebsocketServer(ConsoleHandler, globals(), staticglobals, websocket=False)
     self._startRemoteConsoleThread(ip, port)
Exemple #3
0
class Gilbert:
    __metaclass__ = Singleton

    def __init__(self):
        # Import all components so they are registered with the system and can be instantiated by their class name
        from ignifuga.components import Component, Action, Text

        self.remoteConsole = None
        usage = "game [options]"
        self.parser = OptionParser(usage=usage,
                                   version="Ignifuga Build Utility 1.0")
        self.parser.add_option("-d",
                               "--display",
                               dest="display",
                               default=0,
                               help="Display (default: 0)")
        self.parser.add_option("--width",
                               dest="width",
                               default=None,
                               help="Resolution Width")
        self.parser.add_option("--height",
                               dest="height",
                               default=None,
                               help="Resolution Height")
        self.parser.add_option("-w",
                               "--windowed",
                               action="store_true",
                               dest="windowed",
                               default=False,
                               help="Start in windowed mode (default: no)")
        self.parser.add_option(
            "-p",
            "--profile",
            action="store_true",
            dest="profile",
            default=False,
            help="Do a profile (ignored by the engine, useful for apps)")
        self.parser.add_option("-c",
                               "--capture",
                               action="store_true",
                               dest="capture",
                               default=False,
                               help="Start paused (useful for video capture)")
        self.parser.add_option(
            "-r",
            "--remote",
            action="store_true",
            dest="remote",
            default=False,
            help="Enable Remote Console (http://code.google.com/p/rfoo/)")
        self.parser.add_option("-t",
                               "--telnetremote",
                               action="store_true",
                               dest="telnetremote",
                               default=False,
                               help="Enable A Telnet Remote Console")
        self.parser.add_option("-j",
                               "--jsremote",
                               action="store_true",
                               dest="jsremote",
                               default=False,
                               help="Enable A Websockets Remote Console")
        self.parser.add_option(
            "-s",
            "--remotescreen",
            action="store_true",
            dest="remotescreen",
            default=False,
            help="Create a MJPEG stream of the screen at port+1")
        #if __LINUX__ or __OSX_ or __MINGW__
        # Dont pause by default on desktop
        self.parser.add_option(
            "-e",
            "--pauseonfocuslost",
            action="store_true",
            dest="pauseonfocuslost",
            default=False,
            help="Pause the engine when input focus is lost")
        #else
        # Pause by default on mobile
        self.parser.add_option(
            "-e",
            "--dontpauseonfocuslost",
            action="store_false",
            dest="pauseonfocuslost",
            default=True,
            help="Don't pause the engine when input focus is lost")
        #endif
        self.parser.add_option(
            "--staticglobals",
            action="store_true",
            dest="staticglobals",
            default=False,
            help=
            "Dont update the remote console globals to match the current scene"
        )
        self.parser.add_option(
            "--port",
            dest="port",
            default=54321,
            help="Remote Console Port base (default: 54321)")
        self.parser.add_option(
            "--ip",
            dest="ip",
            default='0.0.0.0',
            help="Remote Console IP to bind to (default: 0.0.0.0)")

    def init(self, backend, firstScene, scenesFile=None):
        """
        backend: 'sdl'
        firstScene: The first scene ID (a string) in which case it'll be loaded from scenesFile or a Scene object
        scenesFile: A File where to load scenes from
        """
        self.backend = backend
        debug('Initializing Gilbert Overlord')

        (options, args) = self.parser.parse_args()

        # Set up dynamic imports
        global GameLoop
        global DataManager
        global _Canvas
        global Renderer

        self.platform = sys.platform
        if self.platform.startswith('linux'):
            self.platform = 'linux'
            self.distro_name, self.distro_version, self.distro_id = platform.linux_distribution(
            )
            if self.distro_name.lower() == 'android':
                self.platform = 'android'
        elif self.platform == 'darwin':
            self.distro_name, self.distro_version, self.distro_id = platform.mac_ver(
            )
        elif self.platform.startswith('win32'):
            self.distro_name, self.distro_version, self.distro_id, self.multi_cpu = platform.win32_ver(
            )

        debug('Platform: %s' % self.platform)
        debug('Distro: %s' % self.distro_name)
        debug('Distro Ver: %s ID: %s' % (self.distro_version, self.distro_id))

        if self.backend == BACKENDS.sdl:
            debug('Initializing backend %s' % (backend, ))
            from backends.sdl import initializeBackend
            from backends.sdl.GameLoop import GameLoop as gameloop
            from backends.sdl.DataManager import DataManager as datamanager
            from backends.sdl.Canvas import Canvas as canvas
            from backends.sdl.Renderer import Renderer as renderer
            # Import all SDL components so they are registered with the system and can be instantiated by their class name
            from ignifuga.backends.sdl.components import Rocket, Music, Sound
            initializeBackend()
            GameLoop = gameloop
            DataManager = datamanager
            _Canvas = canvas
            Renderer = renderer
            debug('Backend %s initialized' % (backend, ))
        else:
            error('Unknown backend %s. Aborting' % (backend, ))
            exit()

        # The only dictionaries that keeps strong references to the entities
        self.scenes = {}
        # A pointer to the current scene stored in self.scenes
        self.scene = None
        # These dictionaries keep weakrefs via WeakSet, contain the current scene entities
        self.entitiesByTag = {}

        self._touches = {}
        self._touchCaptured = False
        self._touchCaptor = None
        self._lastEvent = None

        self.renderer = Renderer(width=options.width,
                                 height=options.height,
                                 fullscreen=not options.windowed,
                                 display=options.display,
                                 autoflip=not options.remotescreen)
        self.dataManager = DataManager()

        options.port = int(options.port)
        if options.remote:
            self.startRemoteConsole(options.ip, options.port,
                                    options.staticglobals)
        elif options.jsremote:
            self.startWebsocketRemoteConsole(options.ip, options.port,
                                             options.staticglobals)
        elif options.telnetremote:
            self.startSocketRemoteConsole(options.ip, options.port,
                                          options.staticglobals)
        self.gameLoop = GameLoop(remoteConsole=self.remoteConsole,
                                 remoteScreen=options.remotescreen,
                                 ip=options.ip,
                                 port=options.port + 1,
                                 pauseOnFocusLost=options.pauseonfocuslost)

        if options.capture:
            print "System paused, press Enter to continue"
            ch = sys.stdin.read(1)

        if not self.loadState():
            debug('Failed loading previous state')
            if scenesFile is not None:
                self.loadScenesFromFile(scenesFile)

            if isinstance(firstScene, Scene):
                self.scenes[firstScene.id] = firstScene
                self._firstScene = firstScene.id
            else:
                self._firstScene = firstScene

########################################################################################################################
# SPLASH SCENE CODE
# ANY MODIFICATION OF THE SCENE DEFINITION AND RELATED CODE AND ARTWORK RENDERS THE LICENSE TO USE THIS ENGINE VOID.
########################################################################################################################
            self.loadScene('splash', copy.deepcopy(SPLASH_SCENE))
            if not self.startScene('splash'):
                error('Could not load splash scene')
                return
########################################################################################################################
# SPLASH SCENE CODE ENDS.
########################################################################################################################

        debug('Starting Game Loop')
        self.startLoop()
        # Nothing after this will get executed until the engine exits
        debug('Ignifuga Game Engine Exits...')

    def startLoop(self):
        """Set up the game loop"""
        self.gameLoop.run()

        # Engine is exiting from here onwards
        debug('Saving state')
        self.saveState()

        self.resetScenes()
        self.gameLoop.cleanup()

        # Free all data
        self.renderer.free()
        if self.backend == BACKENDS.sdl:
            from backends.sdl import terminateBackend
        del self.renderer

        self.gameLoop.free()
        del self.gameLoop

        # DEBUG - See what's holding on to what
        gc.collect()
        debug("--------->GC REMNANTS<------------")
        objs = gc.get_objects()
        for n in objs:
            if isinstance(n, Entity) or isinstance(n, Component):
                debug(
                    '================================================================='
                )
                debug('%s: %s' % (n.__class__.__name__, n))
                referrers = gc.get_referrers(n)
                for ref in referrers:
                    if ref != objs and ref != referrers:
                        debug('    REFERRER: %s %s' % (ref.__class__, id(ref)))
                        if isinstance(ref, dict):
                            debug("    DICT: %s" % ref)
                        elif isinstance(ref, list):
                            debug("    LIST: %s items" % len(ref))
                        elif isinstance(ref, tuple):
                            debug("    TUPLE: %s" % str(ref))
                            referrers1 = gc.get_referrers(ref)
                            debug("NUMBER OF REFERRERS", len(referrers1))
                            for ref1 in referrers1:
                                if ref1 != referrers and ref1 != objs:
                                    debug(ref1)

                            del referrers1
                        elif callable(ref):
                            debug("    INSTANCEMETHOD: %s %s" %
                                  (hash(ref), str(ref)))
                            referrers1 = gc.get_referrers(ref)
                            debug("NUMBER OF REFERRERS %d" % len(referrers1))
                            for ref1 in referrers1:
                                if ref1 != referrers and ref1 != objs:
                                    debug(ref1)

                            del referrers1
                        else:
                            debug("    OTHER: %s" % str(ref))

                del referrers

                debug(
                    '================================================================='
                )
                debug(' ')

        del objs
        self.dataManager.cleanup(True)
        # Break any remaining cycles that prevent garbage collection
        del self.dataManager
        debug('Terminating backend %s' % (self.backend, ))
        terminateBackend()

    def endLoop(self):
        """ End the game loop, free stuff """
        self.gameLoop.quit = True

    def refreshEntityTags(self, entity, added_tags=[], removed_tags=[]):
        for tag in added_tags:
            if tag not in self.entitiesByTag:
                self.entitiesByTag[tag] = weakref.WeakSet()
            if entity not in self.entitiesByTag[tag]:
                self.entitiesByTag[tag].add(entity)

        for tag in removed_tags:
            if tag in self.entitiesByTag:
                if entity in self.entitiesByTag[tag]:
                    self.entitiesByTag[tag].remove(entity)

    def saveState(self):
        """ Serialize the current status of the engine """
        debug('Waiting for entities to finish loading before saving state')
        tries = 0
#        while self.loading:
#            self.update()
#            tries += 1
#            if tries > 100:
#                debug('Still waiting for loading entities: %s' % self.loading)
#                tries = 0

#f = open('ignifuga.state', 'w')
#state = GilbertPickler(f, -1).dump(self.nodes)
#f.close()

    def loadState(self):
        pass
        # TODO!
#        try:
#            if os.path.isfile('ignifuga.state'):
#                f = open('ignifuga.state', 'r')
#                self.nodes = pickle.load(f)
#                f.close()
#
#                for node in self.nodes:
#                    task = Task(weakref.ref(node), node.init, parent=Task.getcurrent())
#                    self.loading.append((task, None, None))
#
#                return True
#            else:
#                return False
#        except:
#            return False

    def loadScenesFromFile(self, scenesFile):
        url, scenes = self.dataManager.loadSceneData(scenesFile)
        self.loadScenes(scenes, data_url=url)

    def loadScenes(self, scenes, data_url=None):
        """ Load scenes from a dictionary like structure. The data format is...
        """
        if not isinstance(scenes, dict):
            return False

        # As scene data may be cached or still referenced when the loop ends,
        # we iterate over a deepcopy of it to avoid a reference circle with entities
        # that prevents them from being garbage collected
        for scene_id, scene_data in copy.deepcopy(scenes).iteritems():
            self.loadScene(scene_id, scene_data, data_url)

    def loadScene(self, scene_id, scene_data, data_url=None):
        scene = Scene(id=scene_id, data_url=data_url, **scene_data)
        self.scenes[scene_id] = scene

    def startScene(self, scene_id):
        if scene_id in self.scenes:
            self.scene = self.scenes[scene_id]
            self.startEntity(self.scene)
            return True

        error("COULD NOT FIND SCENE %s" % scene_id)
        return False

    def startFirstScene(self):
        if not self.changeScene(self._firstScene):
            error('Error loading first scene: %s' % self._firstScene)

    def resetScenes(self):
        """ Reset all scene, garbage collect, get ready to leave! """
        #        for scene_id in self.scenes.iterkeys():
        #            self.resetScene(scene_id)

        self.resetScene()
        self.scenes = {}
        # Clean up cache
        self.dataManager.cleanup()
        gc.collect()

    def resetScene(self):
        """ Reset all the scene information """
        # Make sure all entities finished loading and running
        debug('Waiting for entities to finish loading/running')
        tries = 0
        self.gameLoop.freezeRenderer = True

        if self.scene is not None:
            self.scene.reset()

        del self.scene
        del self.entitiesByTag

        # Really remove nodes and data
        gc.collect()
        self.scene = None
        self.entitiesByTag = {}

        # Clean up cache
        # Do not clean the cache here, there may be useful data for other scenes -> self.dataManager.cleanup()
        gc.collect()

        self.renderer.cleanup()

    def changeScene(self, scene_id):
        debug("Switching scene to: %s " % scene_id)
        self.resetScene()
        self.renderer.scrollTo(0, 0)
        return self.startScene(scene_id)

    def startEntity(self, entity):
        # Add it to the loading queue
        self.gameLoop.startEntity(entity)

    def stopEntity(self, entity):
        """ Remove node, release its data """
        self.gameLoop.stopEntity(entity)

    def getEmbedded(self, url):
        url = str(url)
        if url in EMBEDDED_IMAGES:
            return base64.b64decode(EMBEDDED_IMAGES[url])

        return None

    def freezeRenderer(self):
        self.gameLoop.freezeRenderer = True

    def unfreezeRenderer(self):
        self.gameLoop.freezeRenderer = False

    def startRemoteConsole(self,
                           ip='127.0.0.1',
                           port=54321,
                           staticglobals=False):
        debug("Enabling Remote Console on port %d" % port)
        self.remoteConsole = QueueInetServer(ConsoleHandler, globals(),
                                             staticglobals)
        self._startRemoteConsoleThread(ip, port)

    def startWebsocketRemoteConsole(self,
                                    ip='127.0.0.1',
                                    port=54321,
                                    staticglobals=False):
        debug("Enabling Websocket Remote Console on port %d" % port)
        self.remoteConsole = QueueWebsocketServer(ConsoleHandler, globals(),
                                                  staticglobals)
        self._startRemoteConsoleThread(ip, port)

    def startSocketRemoteConsole(self,
                                 ip='127.0.0.1',
                                 port=54321,
                                 staticglobals=False):
        debug("Enabling Socket Remote Console on port %d" % port)
        self.remoteConsole = QueueWebsocketServer(ConsoleHandler,
                                                  globals(),
                                                  staticglobals,
                                                  websocket=False)
        self._startRemoteConsoleThread(ip, port)

    def _startRemoteConsoleThread(self, ip, port):
        def _wrapper():
            try:
                self.remoteConsole.start(ip, port)
            except socket.error:
                error('Failed to bind rconsole to socket port, port=%r.' %
                      port)

        thread.start_new_thread(_wrapper, ())
Exemple #4
0
class Gilbert:    
    __metaclass__ = Singleton

    def __init__(self):
        # Import all components so they are registered with the system and can be instantiated by their class name
        from ignifuga.components import Component, Action, Text

        self.remoteConsole = None
        usage = "game [options]"
        self.parser = OptionParser(usage=usage, version="Ignifuga Build Utility 1.0")
        self.parser.add_option("-d", "--display", dest="display", default=0,help="Display (default: 0)")
        self.parser.add_option("--width", dest="width", default=None,help="Resolution Width")
        self.parser.add_option("--height", dest="height", default=None,help="Resolution Height")
        self.parser.add_option("-w", "--windowed", action="store_true", dest="windowed", default=False,help="Start in windowed mode (default: no)")
        self.parser.add_option("-p", "--profile", action="store_true", dest="profile", default=False,help="Do a profile (ignored by the engine, useful for apps)")
        self.parser.add_option("-c", "--capture", action="store_true", dest="capture", default=False,help="Start paused (useful for video capture)")
        self.parser.add_option("-r", "--remote", action="store_true", dest="remote", default=False,help="Enable Remote Console (http://code.google.com/p/rfoo/)")
        self.parser.add_option("-t", "--telnetremote", action="store_true", dest="telnetremote", default=False,help="Enable A Telnet Remote Console")
        self.parser.add_option("-j", "--jsremote", action="store_true", dest="jsremote", default=False,help="Enable A Websockets Remote Console")
        self.parser.add_option("-s", "--remotescreen", action="store_true", dest="remotescreen", default=False,help="Create a MJPEG stream of the screen at port+1")
#if __LINUX__ or __OSX_ or __MINGW__
        # Dont pause by default on desktop
        self.parser.add_option("-e", "--pauseonfocuslost", action="store_true", dest="pauseonfocuslost", default=False,help="Pause the engine when input focus is lost")
#else
        # Pause by default on mobile
        self.parser.add_option("-e", "--dontpauseonfocuslost", action="store_false", dest="pauseonfocuslost", default=True,help="Don't pause the engine when input focus is lost")
#endif
        self.parser.add_option("--staticglobals", action="store_true", dest="staticglobals", default=False,help="Dont update the remote console globals to match the current scene")
        self.parser.add_option("--port", dest="port", default=54321,help="Remote Console Port base (default: 54321)")
        self.parser.add_option("--ip", dest="ip", default='0.0.0.0',help="Remote Console IP to bind to (default: 0.0.0.0)")

    
    def init(self, backend, firstScene, scenesFile=None):
        """
        backend: 'sdl'
        firstScene: The first scene ID (a string) in which case it'll be loaded from scenesFile or a Scene object
        scenesFile: A File where to load scenes from
        """
        self.backend = backend
        debug ('Initializing Gilbert Overlord')

        (options, args) = self.parser.parse_args()


        # Set up dynamic imports
        global GameLoop
        global DataManager
        global _Canvas
        global Renderer

        self.platform = sys.platform
        if self.platform.startswith('linux'):
            self.platform = 'linux'
            self.distro_name, self.distro_version, self.distro_id = platform.linux_distribution()
            if self.distro_name.lower() == 'android':
                self.platform = 'android'
        elif self.platform == 'darwin':
            self.distro_name, self.distro_version, self.distro_id = platform.mac_ver()
        elif self.platform.startswith('win32'):
            self.distro_name, self.distro_version, self.distro_id, self.multi_cpu = platform.win32_ver()

        debug('Platform: %s' % self.platform)
        debug('Distro: %s' % self.distro_name)
        debug('Distro Ver: %s ID: %s' % (self.distro_version, self.distro_id) )

        if self.backend == BACKENDS.sdl:
            debug('Initializing backend %s' % (backend,))
            from backends.sdl import initializeBackend
            from backends.sdl.GameLoop import GameLoop as gameloop
            from backends.sdl.DataManager import DataManager as datamanager
            from backends.sdl.Canvas import Canvas as canvas
            from backends.sdl.Renderer import Renderer as renderer
            # Import all SDL components so they are registered with the system and can be instantiated by their class name
            from ignifuga.backends.sdl.components import Rocket, Music, Sound
            initializeBackend()
            GameLoop = gameloop
            DataManager = datamanager
            _Canvas = canvas
            Renderer = renderer
            debug('Backend %s initialized' % (backend,))
        else:
            error('Unknown backend %s. Aborting' % (backend,))
            exit()

        # The only dictionaries that keeps strong references to the entities
        self.scenes = {}
        # A pointer to the current scene stored in self.scenes
        self.scene = None
        # These dictionaries keep weakrefs via WeakSet, contain the current scene entities
        self.entitiesByTag = {}

        self._touches = {}
        self._touchCaptured = False
        self._touchCaptor = None
        self._lastEvent = None

        self.renderer = Renderer(width=options.width, height=options.height, fullscreen= not options.windowed, display=options.display, autoflip=not options.remotescreen)
        self.dataManager = DataManager()

        options.port = int(options.port)
        if options.remote:
            self.startRemoteConsole(options.ip, options.port, options.staticglobals)
        elif options.jsremote:
            self.startWebsocketRemoteConsole(options.ip, options.port, options.staticglobals)
        elif options.telnetremote:
            self.startSocketRemoteConsole(options.ip, options.port, options.staticglobals)
        self.gameLoop = GameLoop(remoteConsole = self.remoteConsole, remoteScreen = options.remotescreen, ip=options.ip, port=options.port+1, pauseOnFocusLost = options.pauseonfocuslost)

        if options.capture:
            print "System paused, press Enter to continue"
            ch = sys.stdin.read(1)

        if not self.loadState():
            debug('Failed loading previous state')
            if scenesFile is not None:
                self.loadScenesFromFile(scenesFile)

            if isinstance(firstScene, Scene):
                self.scenes[firstScene.id] = firstScene
                self._firstScene = firstScene.id
            else:
                self._firstScene = firstScene

########################################################################################################################
# SPLASH SCENE CODE
# ANY MODIFICATION OF THE SCENE DEFINITION AND RELATED CODE AND ARTWORK RENDERS THE LICENSE TO USE THIS ENGINE VOID.
########################################################################################################################
            self.loadScene('splash', copy.deepcopy(SPLASH_SCENE))
            if not self.startScene('splash'):
                error('Could not load splash scene')
                return
########################################################################################################################
# SPLASH SCENE CODE ENDS.
########################################################################################################################

        debug('Starting Game Loop')
        self.startLoop()
        # Nothing after this will get executed until the engine exits
        debug('Ignifuga Game Engine Exits...')

    def startLoop(self):
        """Set up the game loop"""
        self.gameLoop.run()

        # Engine is exiting from here onwards
        debug('Saving state')
        self.saveState()

        self.resetScenes()
        self.gameLoop.cleanup()

        # Free all data
        self.renderer.free()
        if self.backend == BACKENDS.sdl:
            from backends.sdl import terminateBackend
        del self.renderer

        self.gameLoop.free()
        del self.gameLoop

        # DEBUG - See what's holding on to what
        gc.collect()
        debug("--------->GC REMNANTS<------------")
        objs = gc.get_objects()
        for n in objs:
            if isinstance(n, Entity) or isinstance(n,Component):
                debug ('=================================================================')
                debug ('%s: %s' % (n.__class__.__name__, n))
                referrers = gc.get_referrers(n)
                for ref in referrers:
                    if ref != objs and ref != referrers:
                        debug('    REFERRER: %s %s'  % (ref.__class__, id(ref)))
                        if isinstance(ref, dict):
                            debug("    DICT: %s" % ref)
                        elif isinstance(ref, list):
                            debug("    LIST: %s items" % len(ref))
                        elif isinstance(ref, tuple):
                            debug("    TUPLE: %s" % str(ref))
                            referrers1 = gc.get_referrers(ref)
                            debug("NUMBER OF REFERRERS", len(referrers1))
                            for ref1 in referrers1:
                                if ref1 != referrers and ref1 != objs:
                                    debug(ref1)

                            del referrers1
                        elif callable(ref):
                            debug("    INSTANCEMETHOD: %s %s" % (hash(ref), str(ref)))
                            referrers1 = gc.get_referrers(ref)
                            debug("NUMBER OF REFERRERS %d" % len(referrers1))
                            for ref1 in referrers1:
                                if ref1 != referrers and ref1 != objs:
                                    debug(ref1)

                            del referrers1
                        else:
                            debug("    OTHER: %s" % str(ref))

                del referrers

                debug ('=================================================================')
                debug(' ')

        del objs
        self.dataManager.cleanup(True)
        # Break any remaining cycles that prevent garbage collection
        del self.dataManager
        debug('Terminating backend %s' % (self.backend,))
        terminateBackend()

    def endLoop(self):
        """ End the game loop, free stuff """
        self.gameLoop.quit = True

    def refreshEntityTags(self, entity, added_tags=[], removed_tags=[]):
        for tag in added_tags:
            if tag not in self.entitiesByTag:
                self.entitiesByTag[tag] = weakref.WeakSet()
            if entity not in self.entitiesByTag[tag]:
                self.entitiesByTag[tag].add(entity)

        for tag in removed_tags:
            if tag in self.entitiesByTag:
                if entity in self.entitiesByTag[tag]:
                    self.entitiesByTag[tag].remove(entity)


    def saveState(self):
        """ Serialize the current status of the engine """
        debug('Waiting for entities to finish loading before saving state')
        tries = 0
#        while self.loading:
#            self.update()
#            tries += 1
#            if tries > 100:
#                debug('Still waiting for loading entities: %s' % self.loading)
#                tries = 0


        #f = open('ignifuga.state', 'w')
        #state = GilbertPickler(f, -1).dump(self.nodes)
        #f.close()

    def loadState(self):
        pass
        # TODO!
#        try:
#            if os.path.isfile('ignifuga.state'):
#                f = open('ignifuga.state', 'r')
#                self.nodes = pickle.load(f)
#                f.close()
#
#                for node in self.nodes:
#                    task = Task(weakref.ref(node), node.init, parent=Task.getcurrent())
#                    self.loading.append((task, None, None))
#
#                return True
#            else:
#                return False
#        except:
#            return False

    def loadScenesFromFile(self, scenesFile):
        url, scenes = self.dataManager.loadSceneData(scenesFile)
        self.loadScenes(scenes, data_url=url)

    def loadScenes(self, scenes, data_url = None):
        """ Load scenes from a dictionary like structure. The data format is...
        """
        if not isinstance(scenes, dict):
            return False

        # As scene data may be cached or still referenced when the loop ends,
        # we iterate over a deepcopy of it to avoid a reference circle with entities
        # that prevents them from being garbage collected
        for scene_id, scene_data in copy.deepcopy(scenes).iteritems():
            self.loadScene(scene_id, scene_data, data_url)

    def loadScene(self, scene_id, scene_data, data_url = None):
        scene = Scene(id=scene_id, data_url = data_url, **scene_data)
        self.scenes[scene_id] = scene

    def startScene(self, scene_id):
        if scene_id in self.scenes:
            self.scene = self.scenes[scene_id]
            self.startEntity(self.scene)
            return True

        error("COULD NOT FIND SCENE %s" % scene_id)
        return False

    def startFirstScene(self):
        if not self.changeScene(self._firstScene):
            error('Error loading first scene: %s' % self._firstScene)

    def resetScenes(self):
        """ Reset all scene, garbage collect, get ready to leave! """
#        for scene_id in self.scenes.iterkeys():
#            self.resetScene(scene_id)

        self.resetScene()
        self.scenes = {}
        # Clean up cache
        self.dataManager.cleanup()
        gc.collect()

    def resetScene(self):
        """ Reset all the scene information """
        # Make sure all entities finished loading and running
        debug('Waiting for entities to finish loading/running')
        tries = 0
        self.gameLoop.freezeRenderer = True

        if self.scene is not None:
            self.scene.reset()

        del self.scene
        del self.entitiesByTag

        # Really remove nodes and data
        gc.collect()
        self.scene = None
        self.entitiesByTag = {}

        # Clean up cache
        # Do not clean the cache here, there may be useful data for other scenes -> self.dataManager.cleanup()
        gc.collect()

        self.renderer.cleanup()

    def changeScene(self, scene_id):
        debug("Switching scene to: %s " % scene_id)
        self.resetScene()
        self.renderer.scrollTo(0,0)
        return self.startScene(scene_id)

    def startEntity(self, entity):
        # Add it to the loading queue
        self.gameLoop.startEntity(entity)

    def stopEntity(self, entity):
        """ Remove node, release its data """
        self.gameLoop.stopEntity(entity)


    def getEmbedded(self, url):
        url = str(url)
        if url in EMBEDDED_IMAGES:
            return base64.b64decode(EMBEDDED_IMAGES[url])

        return None

    def freezeRenderer(self):
        self.gameLoop.freezeRenderer = True

    def unfreezeRenderer(self):
        self.gameLoop.freezeRenderer = False

    def startRemoteConsole(self, ip='127.0.0.1', port=54321, staticglobals=False):
        debug("Enabling Remote Console on port %d" % port)
        self.remoteConsole = QueueInetServer(ConsoleHandler, globals(), staticglobals)
        self._startRemoteConsoleThread(ip, port)

    def startWebsocketRemoteConsole(self, ip='127.0.0.1', port=54321, staticglobals=False):
        debug("Enabling Websocket Remote Console on port %d" % port)
        self.remoteConsole = QueueWebsocketServer(ConsoleHandler, globals(), staticglobals)
        self._startRemoteConsoleThread(ip, port)

    def startSocketRemoteConsole(self, ip='127.0.0.1', port=54321, staticglobals=False):
        debug("Enabling Socket Remote Console on port %d" % port)
        self.remoteConsole = QueueWebsocketServer(ConsoleHandler, globals(), staticglobals, websocket=False)
        self._startRemoteConsoleThread(ip, port)

    def _startRemoteConsoleThread(self, ip, port ):
        def _wrapper():
            try:
                self.remoteConsole.start(ip, port)
            except socket.error:
                error('Failed to bind rconsole to socket port, port=%r.' % port)

        thread.start_new_thread(_wrapper, ())