Exemple #1
0
class GameEngine(object):
    """The main game engine."""
    def __init__(self, config = None):
        """
        Constructor.

        @param config:  L{Config} instance for settings
        """

        if not config:
            config = Config.load()

        self.config  = config

        self.fps          = self.config.get("video", "fps")

        pygame.init()

        self.title             = _("Frets on Fire")
        self.restartRequested  = False
        self.handlingException = False
        self.video             = Video(self.title)
        self.audio             = Audio()

        log.debug("Initializing audio.")
        frequency    = self.config.get("audio", "frequency")
        bits         = self.config.get("audio", "bits")
        stereo       = self.config.get("audio", "stereo")
        bufferSize   = self.config.get("audio", "buffersize")

        self.audio.pre_open(frequency = frequency, bits = bits, stereo = stereo, bufferSize = bufferSize)
        pygame.init()
        self.audio.open(frequency = frequency, bits = bits, stereo = stereo, bufferSize = bufferSize)

        log.debug("Initializing video.")
        width, height = [int(s) for s in self.config.get("video", "resolution").split("x")]
        fullscreen    = self.config.get("video", "fullscreen")
        multisamples  = self.config.get("video", "multisamples")
        self.video.setMode((width, height), fullscreen = fullscreen, multisamples = multisamples)

        viewport = glGetIntegerv(GL_VIEWPORT)
        h = viewport[3] - viewport[1]
        w = viewport[2] - viewport[0]
        geometry = (0, 0, w, h)
        self.img = ImgContext(geometry)
        glViewport(int(viewport[0]), int(viewport[1]), int(viewport[2]), int(viewport[3]))

        self.input     = Input()
        self.view      = View(self, geometry)
        self.resizeScreen(w, h)

        self.resource  = Resource(Version.dataPath())
        self.server    = None
        self.mainloop  = self.loading

        # Load game modifications
        Mod.init(self)
        theme = Config.load(self.resource.fileName("theme.ini"))
        Theme.open(theme)

        # Make sure we are using the new upload URL
        if self.config.get("game", "uploadurl").startswith("http://kempele.fi"):
            self.config.set("game", "uploadurl", "http://fretsonfire.sourceforge.net/play")

        self.running = True
        self.timer = FpsTimer()
        self.tickDelta = 0
        self.task = TaskEngine(self)

        self.task.addTask(self.input, synced = False)
        self.task.addTask(self.view)
        self.task.addTask(self.resource, synced = False)
        self.data = Data(self.resource, self.img)

        self.input.addKeyListener(FullScreenSwitcher(self), priority = True)
        self.input.addSystemEventListener(SystemEventHandler(self))

        self.debugLayer         = None
        self.startupLayer       = None
        self.loadingScreenShown = False

        log.debug("Ready.")

    def enableGarbageCollection(self, enabled):
        """
        Enable or disable garbage collection whenever a random garbage
        collection run would be undesirable. Disabling the garbage collector
        has the unfortunate side-effect that your memory usage will skyrocket.
        """
        if enabled:
            gc.enable()
        else:
            gc.disable()

    def collectGarbage(self):
        """
        Run a garbage collection run.
        """
        gc.collect()

    def setStartupLayer(self, startupLayer):
        """
        Set the L{Layer} that will be shown when the all
        the resources have been loaded. See L{Data}

        @param startupLayer:    Startup L{Layer}
        """
        self.startupLayer = startupLayer

    def isDebugModeEnabled(self):
        return bool(self.debugLayer)

    def setDebugModeEnabled(self, enabled):
        """
        Show or hide the debug layer.

        @type enabled: bool
        """
        if enabled:
            self.debugLayer = DebugLayer(self)
        else:
            self.debugLayer = None

    def toggleFullscreen(self):
        """
        Toggle between fullscreen and windowed mode.

        @return: True on success
        """
        if not self.video.toggleFullscreen():
            # on windows, the fullscreen toggle kills our textures, se we must restart the whole game
            self.input.broadcastSystemEvent("restartRequested")
            self.config.set("video", "fullscreen", not self.video.fullscreen)
            return True
        self.config.set("video", "fullscreen", self.video.fullscreen)
        return True

    def restart(self):
        """Restart the game."""
        if not self.restartRequested:
            self.restartRequested = True
            self.input.broadcastSystemEvent("restartRequested")
        else:
            self.quit()

    def quit(self):
        self.audio.close()
        self.task.exit()
        self.running = False

    def resizeScreen(self, width, height):
        """
        Resize the game screen.

        @param width:   New width in pixels
        @param height:  New height in pixels
        """
        self.view.setGeometry((0, 0, width, height))
        self.img.setGeometry((0, 0, width, height))

    def startWorld(self):
        self.world = World(self)

    def finishGame(self):
        self.world.finishGame()
        self.world = None
        self.view.pushLayer(MainMenu.MainMenu(self))

    def loadImgDrawing(self, target, name, fileName, textureSize = None):
        """
        Load an SVG drawing synchronously.

        @param target:      An object that will own the drawing
        @param name:        The name of the attribute the drawing will be assigned to
        @param fileName:    The name of the file in the data directory
        @param textureSize  Either None or (x, y), in which case the file will
                            be rendered to an x by y texture
        @return:            L{ImgDrawing} instance
        """
        return self.data.loadImgDrawing(target, name, fileName, textureSize)

    def loading(self):
        """Loading state loop."""

        if self.data.essentialResourcesLoaded():
            if not self.loadingScreenShown:
                self.loadingScreenShown = True
                Dialogs.showLoadingScreen(self, self.data.resourcesLoaded)
                if self.startupLayer:
                    self.view.pushLayer(self.startupLayer)
                self.mainloop = self.main
            self.view.render()

    def clearScreen(self):
        self.img.clear(*Theme.backgroundColor)

    def main(self):
        """Main state loop."""

        self.view.render()
        if self.debugLayer:
            self.debugLayer.render(1.0, True)

    def run(self):
        try:
            self.tickDelta = self.timer.tick()
            done = self.task.run()
            self.clearScreen()

            self.mainloop()

            self.video.flip()

            # Calculate FPS every 2 seconds
            if self.timer.fpsTime >= 2000:
                self.fpsEstimate = self.timer.get_fps()
                print ("%.2f fps" % self.fpsEstimate)

            self.timer.delay(self.fps)

            return done
        except KeyboardInterrupt:
            sys.exit(0)
        except SystemExit:
            sys.exit(0)
        except Exception, e:
            def clearMatrixStack(stack):
                try:
                    glMatrixMode(stack)
                    for i in range(16):
                        glPopMatrix()
                except:
                    pass

            if self.handlingException:
                # A recursive exception is fatal as we can't reliably reset the GL state
                sys.exit(1)

            self.handlingException = True
            log.error("%s: %s" % (e.__class__, e))
            import traceback
            traceback.print_exc()

            clearMatrixStack(GL_PROJECTION)
            clearMatrixStack(GL_MODELVIEW)

            Dialogs.showMessage(self, unicode(e))
            self.handlingException = False
            return True
Exemple #2
0
class GameEngine(Engine):
    """The main game engine."""
    def __init__(self, config = None):
        """
        Constructor.

        @param config:  L{Config} instance for settings
        """

        if not config:
            config = Config.load()

        self.config  = config

        fps          = self.config.get("video", "fps")
        tickrate     = self.config.get("engine", "tickrate")
        Engine.__init__(self, fps = fps, tickrate = tickrate)

        pygame.init()

        self.title             = _("Frets on Fire")
        self.restartRequested  = False
        self.handlingException = False
        self.video             = Video(self.title)
        self.audio             = Audio()

        Log.debug("Initializing audio.")
        frequency    = self.config.get("audio", "frequency")
        bits         = self.config.get("audio", "bits")
        stereo       = self.config.get("audio", "stereo")
        bufferSize   = self.config.get("audio", "buffersize")

        self.audio.pre_open(frequency = frequency, bits = bits, stereo = stereo, bufferSize = bufferSize)
        pygame.init()
        self.audio.open(frequency = frequency, bits = bits, stereo = stereo, bufferSize = bufferSize)

        Log.debug("Initializing video.")
        width, height = [int(s) for s in self.config.get("video", "resolution").split("x")]
        fullscreen    = self.config.get("video", "fullscreen")
        multisamples  = self.config.get("video", "multisamples")
        self.video.setMode((width, height), fullscreen = fullscreen, multisamples = multisamples)

        # Enable the high priority timer if configured
        if self.config.get("engine", "highpriority"):
            Log.debug("Enabling high priority timer.")
            self.timer.highPriority = True

        viewport = glGetIntegerv(GL_VIEWPORT)
        h = viewport[3] - viewport[1]
        w = viewport[2] - viewport[0]
        geometry = (0, 0, w, h)
        self.img = ImgContext(geometry)
        glViewport(int(viewport[0]), int(viewport[1]), int(viewport[2]), int(viewport[3]))

        self.input     = Input()
        self.view      = View(self, geometry)
        self.resizeScreen(w, h)

        self.resource  = Resource(Version.dataPath())
        self.server    = None
        self.sessions  = []
        self.mainloop  = self.loading

        # Load game modifications
        Mod.init(self)
        theme = Config.load(self.resource.fileName("theme.ini"))
        Theme.open(theme)

        # Make sure we are using the new upload URL
        if self.config.get("game", "uploadurl").startswith("http://kempele.fi"):
            self.config.set("game", "uploadurl", "http://fretsonfire.sourceforge.net/play")

        self.addTask(self.input, synchronized = False)
        self.addTask(self.view)
        self.addTask(self.resource, synchronized = False)
        self.data = Data(self.resource, self.img)

        self.input.addKeyListener(FullScreenSwitcher(self), priority = True)
        self.input.addSystemEventListener(SystemEventHandler(self))

        self.debugLayer         = None
        self.startupLayer       = None
        self.loadingScreenShown = False

        Log.debug("Ready.")

    def setStartupLayer(self, startupLayer):
        """
        Set the L{Layer} that will be shown when the all
        the resources have been loaded. See L{Data}

        @param startupLayer:    Startup L{Layer}
        """
        self.startupLayer = startupLayer

    def isDebugModeEnabled(self):
        return bool(self.debugLayer)

    def setDebugModeEnabled(self, enabled):
        """
        Show or hide the debug layer.

        @type enabled: bool
        """
        if enabled:
            self.debugLayer = DebugLayer(self)
        else:
            self.debugLayer = None

    def toggleFullscreen(self):
        """
        Toggle between fullscreen and windowed mode.

        @return: True on success
        """
        if not self.video.toggleFullscreen():
            # on windows, the fullscreen toggle kills our textures, se we must restart the whole game
            self.input.broadcastSystemEvent("restartRequested")
            self.config.set("video", "fullscreen", not self.video.fullscreen)
            return True
        self.config.set("video", "fullscreen", self.video.fullscreen)
        return True

    def restart(self):
        """Restart the game."""
        if not self.restartRequested:
            self.restartRequested = True
            self.input.broadcastSystemEvent("restartRequested")
        else:
                # evilynux - With self.audio.close(), calling self.quit() results in
                #            a crash. Calling the parent directly as a workaround.
            Engine.quit(self)

    def quit(self):
        self.audio.close()
        Engine.quit(self)

    def resizeScreen(self, width, height):
        """
        Resize the game screen.

        @param width:   New width in pixels
        @param height:  New height in pixels
        """
        self.view.setGeometry((0, 0, width, height))
        self.img.setGeometry((0, 0, width, height))

    def isServerRunning(self):
        return bool(self.server)

    def startServer(self):
        """Start the game server."""
        if not self.server:
            Log.debug("Starting server.")
            self.server = Server(self)
            self.addTask(self.server, synchronized = False)

    def connect(self, host):
        """
        Connect to a game server.

        @param host:  Name of host to connect to
        @return:      L{Session} connected to remote server
        """
        Log.debug("Connecting to host %s." % host)
        session = ClientSession(self)
        session.connect(host)
        self.addTask(session, synchronized = False)
        self.sessions.append(session)
        return session

    def stopServer(self):
        """Stop the game server."""
        if self.server:
            Log.debug("Stopping server.")
            self.removeTask(self.server)
            self.server = None

    def disconnect(self, session):
        """
        Disconnect a L{Session}

        param session:    L{Session} to disconnect
        """
        if session in self.sessions:
            Log.debug("Disconnecting.")
            self.removeTask(session)
            self.sessions.remove(session)

    def loadImgDrawing(self, target, name, fileName, textureSize = None):
        """
        Load an SVG drawing synchronously.

        @param target:      An object that will own the drawing
        @param name:        The name of the attribute the drawing will be assigned to
        @param fileName:    The name of the file in the data directory
        @param textureSize  Either None or (x, y), in which case the file will
                            be rendered to an x by y texture
        @return:            L{ImgDrawing} instance
        """
        return self.data.loadImgDrawing(target, name, fileName, textureSize)

    def loading(self):
        """Loading state loop."""
        done = Engine.run(self)
        self.clearScreen()

        if self.data.essentialResourcesLoaded():
            if not self.loadingScreenShown:
                self.loadingScreenShown = True
                Dialogs.showLoadingScreen(self, self.data.resourcesLoaded)
                if self.startupLayer:
                    self.view.pushLayer(self.startupLayer)
                self.mainloop = self.main
            self.view.render()
        self.video.flip()
        return done

    def clearScreen(self):
        self.img.clear(*Theme.backgroundColor)

    def main(self):
        """Main state loop."""

        # Tune the scheduler priority so that transitions are as smooth as possible
        if self.view.isTransitionInProgress():
            self.boostBackgroundThreads(False)
        else:
            self.boostBackgroundThreads(True)

        done = Engine.run(self)
        self.clearScreen()
        self.view.render()
        if self.debugLayer:
            self.debugLayer.render(1.0, True)
        self.video.flip()
        return done

    def run(self):
        try:
            return self.mainloop()
        except KeyboardInterrupt:
            sys.exit(0)
        except SystemExit:
            sys.exit(0)
        except Exception, e:
            def clearMatrixStack(stack):
                try:
                    glMatrixMode(stack)
                    for i in range(16):
                        glPopMatrix()
                except:
                    pass

            if self.handlingException:
                # A recursive exception is fatal as we can't reliably reset the GL state
                sys.exit(1)

            self.handlingException = True
            Log.error("%s: %s" % (e.__class__, e))
            import traceback
            traceback.print_exc()

            clearMatrixStack(GL_PROJECTION)
            clearMatrixStack(GL_MODELVIEW)

            Dialogs.showMessage(self, unicode(e))
            self.handlingException = False
            return True