Exemple #1
0
class Server(GameSide):
    def __init__(self):
        '''Initializes the server.'''
        self.mode = "server"

        self.oP = OutPipe("Server", 0)
        self.nC = None

        self.cvars = {
            "sv_addr": "0.0.0.0",
            "sv_port": 7777,
            "sv_netlog": 0,
            "sv_level": "",
            "sv_game": 0,
            "sv_singleplayer": 0,
            "sv_gamemode": "singleplayer",
            "sv_startscript": "",
            "sv_master": "",
            "sv_dedicated": 0,
            "sv_chat": 1,
            "sv_background_red": 0,
            "sv_background_green": 0,
            "sv_background_blue": 0,
            "sv_background_alpha": 0,
            "sv_password": "",
            "cl_language": "en",
            "cl_subtitles": 1,
            "cl_width": 1280,
            "cl_height": 720,
            "cl_fullscreen": 0,
            "cl_motionblur": 0,
            "cl_motionblur_amount": 0,
            "cl_anisotropic": 1,
            "cl_mipmap": "none",
            "cl_vsync": "off",
            "cl_musicvolume": 10,
            "cl_dialogvolume": 10,
            "cl_soundvolume": 10,
            "cl_netping": 0,
        }

        self.netVars = {}

        self.oldNetVars = self.netVars
        self.oldcvars = self.cvars

        self.gameMode = None
        self.level = None

        self.unassignedPlayers = []

        self.entities = []

        self.events = []

        self.saveFile = None

        self.chatMessages = []

        self.eI = EngineInterface(True)
        self.sE = ScriptExecuter()
        self.pR = PhysicsReader(self)
        self.sH = ShaderHandler()
        self.l = Localizer(self)

        self.updateNetwork()

        self.forceUpdateCVars()

        self.keepAliveTicker = 0
        self.trackedProperties = []

        loadServer(self)

        self.oP("Initialized.")

    def forceUpdateCVars(self):
        '''Forces all the cVars to run their updaters as though they had just been set.'''
        for key in self.cvars.keys():
            if not "cl_" == key[:3]:
                self.configure(key, self.get(key), override=True)
        self.oP("Force updated cVars.")

    def configureNetVar(self, var, val):
        '''Configures a NetVar and updates clients about it.'''
        self.netVars[var] = val
        self.events.append([None, ["SYSTEM", "NETVARS", [var, val]]])
        self.oP("Configured netVar %s to %s." % (str(var), str(val)))

    def configure(self, key, val, override=False):
        '''Configure a cvar.'''
        changed = False

        if key in self.cvars.keys():
            #Switch for int
            if type(self.cvars[key]) == type(0):
                val = int(val)

            #Used for functions
            if type(self.cvars[key]) == type(self.configure):
                self.cvars[key](val)
                self.oP("CVar %s executed." % key)
            else:
                if val != self.cvars[key] or override:
                    changed = True
                    self.cvars[key] = val
                    self.sendEvent([None, ["SYSTEM", "CVARS", [key, val]]])
                    self.oP("CVar %s configured to %s (%s)." %
                            (key, val, str(type(val)).replace(
                                "<class '", "").replace("'>", "")))
        else:
            self.oP("CVar %s not present." % key)

        if changed:
            self.updateGame(key)

    def endGame(self):
        '''Ends the game instance.'''
        self.oP("Ending game instance.")
        self.endGameMode()
        self.endLevel()

    def endGameMode(self):
        '''Ends the game mode.'''
        if hasattr(self.gameMode, "kill"):
            self.gameMode.kill()
        self.gameMode = None

    def replaceMesh(self, ent, meshName):
        '''Replaces the mesh of an entity.'''
        cR = self.load("Mesh", meshName)

        name = cR.get("name")
        resourcePath = cR.get("resourcePath")

        self.loadLibrary("Mesh", resourcePath, mesh=True)

        for obj in self.eI.getMainScene().objectsInactive:
            if obj.name == meshName:
                meshName = obj.meshes[0]
                break

        ent.gameObject.replaceMesh(meshName, True, True)
        self.sendEvent([None, ["PHYSICS", "MESH", [ent.GUID, meshName]]])
        self.oP("Replaced mesh of %s with %s." % (ent.GUID, meshName))

    def quitGame(self):
        '''Ends the game and kills network connections.'''
        self.oP("Shutting down...")
        self.endGame()
        self.purgeNetwork()

    def setGameMode(self, name):
        '''Load a gamemode into the engine.'''
        self.sE.addContext("Server", self)
        self.sE.execute(GAMEMODE_PATH + name)
        self.gameMode = self.newGamemode(self)

    def updateNetwork(self):
        '''Update the network module for changes to port or addr'''
        self.purgeNetwork()

        self.nC = NetCore()
        if self.get("sv_netlog"):
            self.nC.configure("DEBUG", True)
            self.nC.configure("VERBOSE", True)
        else:
            self.nC.configure("DEBUG", False)
            self.nC.configure("VERBOSE", False)

        self.nC.pingcount = self.get("cl_netping")

        self.nC.configure("NAME", "Server")
        self.nC.setProtocol("UDP")
        self.nC.configure("HOST", self.get("sv_addr"))
        self.nC.configure("PORT", self.get("sv_port"))
        self.nC.initialize()

    def purgeNetwork(self):
        '''Destroys the NetCore once and for all.'''
        if self.nC:
            self.nC.clear()
            self.nC.destroy()
            self.nC = None

    def setMusic(self, music):
        '''Sets the music track.'''
        launcher = self.eI.getGlobal("launcher")

        launcher.sound.playMusic(music)
        self.events.append([None, ["SYSTEM", "MUSIC_PLAY", [music, 0.0]]])
        self.oP("Set music to %s." % music)

    def stopMusic(self):
        '''Stops the music track.'''
        launcher = self.eI.getGlobal("launcher")

        launcher.sound.stopMusic()
        self.events.append([None, ["SYSTEM", "MUSIC_STOP", None]])

    def playSound(self, sound, emitter=None):
        '''Plays a sound.'''
        launcher = self.eI.getGlobal("launcher")

        if emitter:
            launcher.sound.playSound(sound, emitter.gameObject)
            self.events.append(
                [None, ["SYSTEM", "SOUND_PLAY", [emitter.GUID, sound]]])
        else:
            launcher.sound.playSound(sound)
            self.events.append([None, ["SYSTEM", "SOUND_PLAY", [None, sound]]])

        self.oP("Started sound %s." % sound)

    def stopSound(self, handle):
        '''Stops a sound.'''
        launcher = self.eI.getGlobal("launcher")

        GUID, sound = launcher.sound.stopSound(handle)

        self.events.append(["SYSTEM", "SOUND_STOP", [emitter.GUID, sound]])
        self.oP("Stopped sound %s." % sound)

    def stopSoundByGUID(self, GUID, name):
        '''Stops a sound.'''
        launcher = self.eI.getGlobal("launcher")

        GUID, sound = launcher.sound.stopSoundByGUID(GUID, name)
        self.oP("Stopped sound %s." % sound)

        self.events.append(["SYSTEM", "SOUND_STOP", [emitter.GUID, sound]])
        self.oP("Stopped sound %s." % sound)

    def playDialog(self, sound, emitter=None):
        '''Plays a dialog line.'''
        launcher = self.eI.getGlobal("launcher")

        if emitter:
            launcher.sound.playDialog(sound, emitter.gameObject)
            self.events.append(
                [None, ["SYSTEM", "DIALOG_PLAY", [emitter.GUID, sound]]])
        else:
            launcher.sound.playDialog(sound)
            self.events.append(
                [None, ["SYSTEM", "DIALOG_PLAY", [None, sound]]])

        self.oP("Started dialog %s." % sound)

    def stopDialog(self, handle):
        '''Stops a dialog line.'''
        launcher = self.eI.getGlobal("launcher")

        GUID, sound = launcher.sound.stopDialog(handle)

        self.events.append(["SYSTEM", "DIALOG_STOP", [emitter.GUID, sound]])
        self.oP("Stopped dialog %s." % sound)

    def stopDialogByGUID(self, GUID, name):
        '''Stops a dialog line.'''
        launcher = self.eI.getGlobal("launcher")

        GUID, sound = launcher.sound.stopDialogByGUID(GUID, name)
        self.oP("Stopped dialog %s." % sound)

        self.events.append(["SYSTEM", "DIALOG_STOP", [emitter.GUID, sound]])
        self.oP("Stopped dialog %s." % sound)

    def enableShader(self, index, name, mode):
        '''Enables a shader as a filter for the entire screen.'''
        self.sH.enableShader(index, name, mode)

    def disableShader(self, index):
        '''Disables a shader that was filtering the entire screen.'''
        self.sH.disableShader(index)

    def updateGame(self, key):
        '''Reacts to changes to the cVars.'''
        if key == "sv_gamemode" and self.get("sv_game"):
            self.setGameMode(self.get("sv_gamemode"))
        elif key == "sv_level" and self.get("sv_game"):
            self.configure("sv_game", 0)
            self.configure("sv_game", 1)
        elif key == "sv_game":
            if self.get("sv_game"):
                self.setGameMode(self.get("sv_gamemode"))
                self.setLevel(self.get("sv_level"))
            else:
                self.endGame()
        elif key == "sv_port" or key == "sv_addr":
            self.updateNetwork()
        elif key == "sv_startscript":
            self.sE.addContext("Server", self)
            self.sE.execute(self.get("sv_startscript"))

        elif key in [
                "sv_background_red", "sv_background_green",
                "sv_background_blue", "sv_background_alpha"
        ] and not self.get("cl_master"):
            self.eI.setBackgroundColor((self.getBackgroundColor()))

        elif key == "sv_netlog":
            if self.get("sv_netlog"):
                self.nC.configure("DEBUG", True)
                self.nC.configure("VERBOSE", True)
            else:
                self.nC.configure("DEBUG", False)
                self.nC.configure("VERBOSE", False)

        elif key == "cl_width" or key == "cl_height":
            self.eI.setResolution(self.get("cl_width"), self.get("cl_height"))
        elif key == "cl_fullscreen":
            self.eI.setFullscreen(self.get("cl_fullscreen"))
        elif key == "cl_motionblur" or key == "cl_motionblur_amount":
            self.eI.setMotionBlur(self.get("cl_motionblur"),
                                  self.get("cl_motionblur_amount"))
        elif key == "cl_anisotropic":
            self.eI.setAnisotropic(self.get("cl_anisotropic"))
        elif key == "cl_mipmap":
            self.eI.setMipmapping(self.get("cl_mipmap"))
        elif key == "cl_vsync":
            self.eI.setVSync(self.get("cl_vsync"))

        elif key == "cl_mastervolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setMasterVolume(self.get("cl_mastervolume"))

        elif key == "cl_musicvolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setMusicVolume(self.get("cl_musicvolume"))

        elif key == "cl_dialogvolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setDialogVolume(self.get("cl_dialogvolume"))

        elif key == "cl_soundvolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setSoundVolume(self.get("cl_soundvolume"))

        elif key == "cl_subtitles":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.subtitles = self.get("cl_subtitles")

        elif key == "cl_language":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.language = self.get("cl_language")

        elif key == "cl_netping":
            self.nC.pingcount = self.get("cl_netping")

    def getEmergencyUpdate(self, cli):
        '''Gets a special update to repair desynced clients.'''
        events = []

        for var in self.cvars:
            if not "cl_" == var[:3]:
                events.append(
                    [cli, ["SYSTEM", "CVARS", [var, self.cvars[var]]]])

        for var in self.netVars:
            events.append(
                [cli, ["SYSTEM", "NETVARS", [var, self.netVars[var]]]])

        return events

    def setPlayerCamera(self, cli, GUID):
        '''Set a client's current camera.'''
        self.events.append([cli, ["SYSTEM", "SET_CAMERA", GUID]])
        if cli:
            self.oP("Set client %s camera to be %s" % (str(cli.addr), GUID))
        else:
            self.oP("Set all client cameras to be %s" % GUID)

    def checkServer(self):
        '''Runs server loops and checks for events.'''
        events = []

        #CVar based events
        for key in self.cvars.keys():
            if not key in self.oldcvars or self.cvars[key] != self.oldcvars[
                    key]:
                events.append(["SYSTEM", "CVARS", [key, self.cvars[key]]])

        self.oldcvars = self.cvars

        if self.gameMode:
            self.gameMode.loop()

        #Other game events
        events += self.events
        self.events = []

        return events

    def sendChatMessage(self, msg, client=None):
        '''Sends a chat message to the client(s).'''
        self.sendEvent([client, ["SYSTEM", "CHAT", msg]])
        self.chatMessages.append(msg)

    def sendEvent(self, event):
        '''Sends an event to the client(s).'''
        if (event[1][0] == "PHYSICS" and not event[1][1]
                in ["CREATE", "DELETE"]) or event[1] == "KEEPALIVE":
            mode = "FAF"
        else:
            mode = "RT"

        #Send global events
        if event[0] == None:
            for cli in self.nC.clients:
                if cli.userProps["confirmed"] or not self.get("sv_password"):
                    cli.send(event[1], mode)
        #Send one user events
        else:
            if event[0].userProps["confirmed"] or not self.get("sv_password"):
                event[0].send(event[1], mode)

    def recvEvent(self, cli, event):
        '''Handles an event from a client.'''
        if type(event) == type([]) and len(event) >= 3:
            if event[0] == "INPUT":

                if event[1] == "COMMAND" or event[1] == "INTERFACE":
                    if cli.userProps["player"]:
                        cli.userProps["player"].pushCommand(event[2])

            elif event[0] == "SYSTEM":

                if event[1] == "PHYSICS":
                    if event[2] == "FORCEUPDATE" and not cli.userProps[
                            "synced"]:
                        events = self.pR.getEmergencyUpdate(cli)
                        events += self.getEmergencyUpdate(cli)
                        for event in events:
                            self.sendEvent(event)
                        cli.userProps["synced"] = True

                elif event[1] == "CVARS":
                    if cli.addr[0] == self.get("sv_master"):
                        self.configure(event[2][0], event[2][1])

                elif event[1] == "CHAT" and self.get("sv_chat"):
                    self.chatMessages.append(event[2])
                    self.sendEvent([None, event])

                elif event[1] == "NAME":
                    if cli.userProps["player"]:
                        cli.userProps["player"].username = event[2]
                    cli.userProps["username"] = event[2]

                elif event[1] == "PASSWORD":
                    if event[2] == self.get("sv_password"):
                        cli.userProps["confirmed"] = True

            elif event[0] == "GAME":
                self.gameMode.pushGameEvent([event[1], event[2]])

    def openSaveFile(self, name):
        '''Opens the save file.'''
        try:
            self.saveFile = shelve.open(SAVE_PATH + name + SAVE_EXT)
            self.oP("Opened save file %s." % (SAVE_PATH + name + SAVE_EXT))
            return True
        except:
            self.saveFile = None
            self.oP("Failed to open save file %s." %
                    (SAVE_PAT + name + SAVE_EXT))
            return False

    def closeSaveFile(self):
        '''Closes the save file.'''
        if self.saveFile != None:
            self.saveFile.close()
            self.oP("Closed the save file.")
            self.saveFile = None

    def saveSaveFile(self):
        '''Saves the save file.'''
        if self.saveFile != None:
            self.oP("Saved the save file.")
            self.saveFile.sync()

    def saveState(self):
        '''Saves the game state.'''
        self.oP("Saving entity state.")
        self.openSaveFile("save")

        if self.saveFile != None:
            data = Sarcophagus()

            for ent in self.entities:
                #if ent.name != "avatar":
                self.saveEntity(ent, data)

            for cli in self.nC.clients:
                if cli.userProps["player"]:
                    self.savePlayer(cli.userProps["player"], data)

            for player in self.unassignedPlayers:
                self.savePlayer(player, data)

            records, playerRecords = data.cleanUp()

            try:
                self.saveFile["data"] = data
                self.oP("Saved data to save file.")
            except:
                self.oP("Failed to save data to save file.")

            self.saveSaveFile()

        self.closeSaveFile()

        data.restore(records, playerRecords)

        for ent in self.entities:
            #if ent.name != "avatar":
            data.reconstructEntity(ent)

        for cli in self.nC.clients:
            if cli.userProps["player"]:
                data.reconstructPlayer(cli.userProps["player"])

        for player in self.unassignedPlayers:
            data.reconstructPlayer(player)

    def loadState(self):
        '''Loads the game state.'''
        self.oP("Loading entity state.")
        self.openSaveFile("save")

        if self.saveFile != None:
            if "data" in self.saveFile.keys():
                container = self.saveFile["data"]
                for entry in container.entities:
                    self.loadEntity(entry)
                for player in container.players:
                    self.loadPlayer(player)

        self.closeSaveFile()

    def saveEntity(self, ent, data):
        '''Saves an Entity to the sarcophagus.'''
        objData = self.pR.getObjectData(ent)
        data.deconstructEntity(ent)
        data.addEntity(ent, objData)

    def savePlayer(self, player, data):
        '''Saves a Player to the sarcophagus.'''
        data.deconstructPlayer(player)
        data.addPlayer(player)

    def loadEntity(self, entry):
        '''Reconstructs an Entity.'''
        ent = entry[0]

        ent.reconstructObject(entry[1])

        self.oP("Retrieved entity %s from save file." % ent.GUID)

        self.entities.append(ent)

    def loadPlayer(self, player):
        '''Reconstructs a player.'''
        player.reconstructPlayer(self)

        self.oP("Retrieved player %s from save file." % player.username)

        self.unassignedPlayers.append(player)

    def getOldPlayer(self, name):
        '''Checks for an unassigned player.'''
        for player in self.unassignedPlayers:
            if player.username == name:
                return player

    def removeOldPlayer(self, player):
        '''Removes a player from the unassigned list.'''
        self.unassignedPlayers.remove(player)

    def disconnectPlayer(self, cli):
        '''Handles the disconnection of a player.'''
        if cli.userProps["player"]:
            cli.userProps["player"].cli = None
            self.unassignedPlayers.append(cli.userProps["player"])

    def loop(self):
        '''Does everything basically.'''

        #cl = OptiClock()

        #Detector Index
        self.updateDetectorIndex()

        #cl.clockIn("DetectorIndex")

        #KEEP ALIVE
        self.keepAliveTicker += 1
        if self.keepAliveTicker % 600 == 0:
            self.sendEvent([None, "KEEPALIVE"])

        #cl.clockIn("KeepAlive")

        #RECV THINGS###############################################

        for cli in self.nC.clients:
            #Make sure only one client gets through on singleplayer
            if not self.get("sv_singleplayer") or len(self.nC.clients) <= 1:
                #Create a player once the username is collected
                if not cli.userProps["player"] and "username" in cli.userProps:
                    player = self.getOldPlayer(cli.userProps["username"])
                    if player:
                        self.oP("Client's previous avatar found, reassigning.")
                        cli.userProps["player"] = player
                        player.cli = cli
                        player.reLinkPlayer()
                        self.removeOldPlayer(cli.userProps["player"])
                    else:
                        self.oP("Client gets a new avatar.")
                        cli.userProps["player"] = Player(self, cli)
                    cli.disconnectFunc = cli.userProps["player"].destruct
                #If a player has been created
                elif cli.userProps["player"]:
                    cli.userProps["player"].loop()

            packet = cli.recv()

            if packet:
                payload = packet.getPayload()

                try:
                    data = eval(payload)
                except:
                    data = None

                if data:
                    self.recvEvent(cli, data)

        #cl.clockIn("RecvLoop")

        #CHANGE THINGS###########################################

        #Apply Shaders
        self.sH.loop()

        #cl.clockIn("Shaders")

        #Localize text
        self.l.loop()

        #cl.clockIn("Localization")

        #Entities loop
        for ent in self.entities:
            ent.checkPhysics()
            ent.checkAnimation()

            if hasattr(ent, "loop"):
                #print(ent)
                ent.loop(self, "server")

        #cl.clockIn("Entities")

        #Physics Read Loop
        events = self.pR.loop()

        #cl.clockIn("Physics")

        #Self Read Loop

        events += self.checkServer()

        #cl.clockIn("GameEvents")

        #Gamerules Loop
        if hasattr(self.gameMode, "loop"):
            self.gameMode.loop()

        #cl.clockIn("GameMode")

        #Level loop
        if self.level and hasattr(self.level, "loop"):
            self.level.loop(self, "server")

        #cl.clockIn("Level")

        #SEND THINGS###############################################

        #Send Events
        for event in events:
            self.sendEvent(event)

        #cl.clockIn("SendLoop")

        #KILL THINGS###################################################
        kill = self.nC.pullKillQueue()

        if kill and self.gameMode:
            kill()

        #cl.clockIn("Kill")

        self.nC.loop()
Exemple #2
0
class Client(GameSide):
    def __init__(self):
        '''Initializes the client.'''

        self.mode = "client"

        self.oP = OutPipe("Client", 0)

        self.eI = EngineInterface(objectMode=True)
        self.vP = VideoPlayer()
        self.sE = ScriptExecuter()
        self.iR = InputReceiver()
        self.l = Localizer(self)
        self.pA = PhysicsApplicator(self)
        self.sH = ShaderHandler()
        self.nC = None

        self.sE.addContext("Client", self)

        self.cvars = {
            #Low level settings
            "cl_update": 1,
            "cl_synced": 0,
            "cl_addr": "0.0.0.0",
            "cl_oport": 7777,
            "cl_iport": 7778,
            "cl_netlog": 0,
            "cl_game": 0,
            "cl_startscript": "",
            "cl_master": 0,
            "cl_predict": 1,
            "cl_smooth": 0,
            "cl_name": "Player",
            "cl_password": "",
            "cl_camera": "",
            "cl_lockcontrols": 1,
            "cl_showmouse": 1,
            "cl_xsens": 50,
            "cl_ysens": 50,
            "cl_inverted": 0,
            "cl_netping": 0,

            #High level settings
            "cl_language": "en",
            "cl_subtitles": 1,
            "cl_width": 1280,
            "cl_height": 720,
            "cl_fullscreen": 0,
            "cl_motionblur": 0,
            "cl_motionblur_amount": 0,
            "cl_anisotropic": 1,
            "cl_mipmap": "none",
            "cl_vsync": "off",
            "cl_musicvolume": 10,
            "cl_dialogvolume": 10,
            "cl_soundvolume": 10,
            "cl_mastervolume": 10,

            #Server shared settings
            "sv_level": "",
            "sv_gamemode": "",
            "sv_game": 0,
            "sv_background_red": 0,
            "sv_background_green": 0,
            "sv_background_blue": 0,
            "sv_background_alpha": 0,
        }

        self.netVars = {}

        self.chatMessages = []

        self.interfaces = []
        self.entities = []
        self.level = None

        self.gameEvents = []
        self.updateNetwork()

        #self.startLoop()
        self.forceUpdateCVars()

        self.keepAliveTicker = 0
        self.trackedProperties = []

        loadClient(self)

        self.oP("Initialized.")

    def forceUpdateCVars(self):
        '''Forces all the cVars to run their updaters as though they had just been set.'''
        for key in self.cvars.keys():
            if not "sv_" == key[:3]:
                self.configure(key, self.get(key), override=True)
        self.oP("Force updated cVars.")

    def configure(self, key, val, fromServer=False, override=False):
        '''Configure a cVar.'''
        changed = False

        if key in self.cvars.keys() and (not "sv_" == key[:3] or fromServer):
            #Switch for int
            if type(self.cvars[key]) == type(0):
                val = int(val)

            #Used for functions
            if type(self.cvars[key]) == type(self.configure):
                self.cvars[key](val)
                self.oP("CVar %s executed." % key)
            else:
                if val != self.cvars[key] or override:
                    changed = True
                    self.cvars[key] = val
                    self.oP("CVar %s configured to %s (%s)." %
                            (key, val, str(type(val)).replace(
                                "<class '", "").replace("'>", "")))
        elif "sv_" == key[:3] and not fromServer and key in self.cvars.keys(
        ) and self.cvars[key] != val:
            self.sendEvent(["SYSTEM", "CVARS", [key, val]])
            self.oP("Asking server to change CVar %s." % key)
        else:
            self.oP("CVar %s not present." % key)

        if changed:
            self.updateGame(key)

    def connectGame(self):
        '''Connects to a game.'''
        self.oP("Connecting to game.")
        self.nC.connect((self.get("cl_addr"), self.get("cl_oport")))
        self.configure("cl_game", 1)
        self.configure("cl_synced", 0)

    def disconnectGame(self):
        '''Disconnects from a game.'''
        self.oP("Disconnecting from a game.")
        self.updateNetwork()
        self.configure("cl_game", 0)
        self.configure("cl_synced", 0)
        self.configure("cl_update", 1)
        self.oP("Disconnected from game.")

    def updateGame(self, key):
        '''Reacts to changes to the cVars.'''
        if key == "cl_startscript":
            self.sE.addContext("Client", self)
            self.sE.execute(self.get("cl_startscript"))

        elif key in ["cl_iport"]:
            self.updateNetwork()

        elif key == "cl_addr":
            if self.get("cl_addr") == "0.0.0.0":
                self.configure("cl_addr", "127.0.0.1")

        elif key == "cl_name":
            self.sendEvent(["SYSTEM", "NAME", self.get("cl_name")])

        elif key == "sv_level" and not self.get("cl_master"):
            if self.get("sv_game"):
                self.setLevel(self.get("sv_level"))

        elif key == "sv_game" and not self.get("cl_master"):
            if self.get("sv_game"):
                self.setLevel(self.get("sv_level"))
            else:
                self.endLevel()

        elif key in [
                "sv_background_red", "sv_background_green",
                "sv_background_blue", "sv_background_alpha"
        ] and not self.get("cl_master"):
            self.eI.setBackgroundColor((self.getBackgroundColor()))

        elif key == "cl_width" or key == "cl_height":
            self.eI.setResolution(self.get("cl_width"), self.get("cl_height"))
        elif key == "cl_fullscreen":
            self.eI.setFullscreen(self.get("cl_fullscreen"))
        elif key == "cl_motionblur" or key == "cl_motionblur_amount":
            self.eI.setMotionBlur(self.get("cl_motionblur"),
                                  self.get("cl_motionblur_amount"))
        elif key == "cl_anisotropic":
            self.eI.setAnisotropic(self.get("cl_anisotropic"))
        elif key == "cl_mipmap":
            self.eI.setMipmapping(self.get("cl_mipmap"))
        elif key == "cl_vsync":
            self.eI.setVSync(self.get("cl_vsync"))
        elif key == "cl_camera" and self.get("cl_camera") != "":
            self.eI.setCamera(self.get("cl_camera"))

        elif key == "cl_mastervolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setMasterVolume(self.get("cl_mastervolume"))

        elif key == "cl_musicvolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setMusicVolume(self.get("cl_musicvolume"))

        elif key == "cl_dialogvolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setDialogVolume(self.get("cl_dialogvolume"))

        elif key == "cl_soundvolume":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.setSoundVolume(self.get("cl_soundvolume"))

        elif key == "cl_subtitles":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.subtitles = self.get("cl_subtitles")

        elif key == "cl_language":
            launcher = self.eI.getGlobal("launcher")

            if launcher:
                launcher.sound.language = self.get("cl_language")

        elif key == "cl_lockcontrols":
            self.iR.locked = self.get("cl_lockcontrols")

        elif key == "cl_showmouse":
            self.eI.setMouseState(self.get("cl_showmouse"))

        elif key == "cl_xsens":
            self.iR.xsens = self.get("cl_xsens")

        elif key == "cl_ysens":
            self.iR.ysens = self.get("cl_ysens")

        elif key == "cl_inverted":
            self.iR.inverted = self.get("cl_inverted")

        elif key == "cl_predict":
            self.iR.predict = self.get("cl_predict")

            if not self.get("cl_predict"):
                for GUID in self.pA.blacklistedGUIDs:
                    self.removeFromRotationBlacklist(GUID)

        elif key == "cl_smooth":
            self.pA.smooth = self.get("cl_smooth")

        elif key == "cl_netlog":
            if self.get("cl_netlog"):
                self.nC.configure("DEBUG", True)
                self.nC.configure("VERBOSE", True)
            else:
                self.nC.configure("DEBUG", False)
                self.nC.configure("VERBOSE", False)

        elif key == "cl_netping":
            self.nC.pingcount = self.get("cl_netping")

    def updateNetwork(self):
        '''Update the network module for changes to port or addr'''
        self.purgeNetwork()

        self.nC = NetCore()
        if self.get("cl_netlog"):
            self.nC.configure("DEBUG", True)
            self.nC.configure("VERBOSE", True)
        else:
            self.nC.configure("DEBUG", False)
            self.nC.configure("VERBOSE", False)

        self.nC.pingcount = self.get("cl_netping")

        self.nC.configure("NAME", "Client")
        self.nC.setProtocol("UDP")
        self.nC.configure("PORT", self.get("cl_iport"))
        self.nC.initialize()

    def purgeNetwork(self):
        '''Destroys the NetCore once and for all.'''
        if self.nC:
            self.nC.clear()
            self.nC.destroy()
            self.nC = None

    def startGameRemote(self):
        '''Used to connect to another person's game.'''
        self.oP("Starting game (remote)...")
        self.configure("cl_master", 0)
        self.configure("cl_update", 1)
        self.configure("cl_predict", 1)

        self.connectGame()

    def startGameFull(self):
        '''Used if you want the lobby when you start up a game.'''
        self.oP("Starting game (full)...")
        self.configure("cl_master", 1)
        self.configure("cl_update", 0)
        self.configure("cl_predict", 0)

        launcher = self.eI.getGlobal("launcher")

        if launcher:
            launcher.bootServer()
            self.oP("Booted server from client (Full).")
            self.configure("cl_addr", launcher.s.get("sv_addr"))
            self.connectGame()

    def startGameFast(self, level, gamemode, singleplayer=True):
        '''Used if you want to go straight to the game, usually used for singleplayer.'''
        self.oP("Starting game (fast)...")
        self.configure("cl_master", 1)
        self.configure("cl_update", 0)
        self.configure("cl_predict", 0)
        self.configure("cl_game", 1)

        self.updateNetwork()

        launcher = self.eI.getGlobal("launcher")

        if launcher:
            launcher.bootServer()
            self.oP("Booted server from client (Fast).")
            launcher.s.configure("sv_level", level)
            launcher.s.configure("sv_gamemode", gamemode)
            launcher.s.configure("sv_game", 1)
            if singleplayer:
                launcher.s.configure("sv_singleplayer", 1)
            else:
                launcher.s.configure("sv_singleplayer", 0)
            self.configure("cl_addr", launcher.s.get("sv_addr"))
            self.connectGame()

    def endGame(self):
        '''Ends the game instance and disconnects.'''
        self.oP("Ending game session...")
        self.disconnectGame()

        self.endLevel()

        launcher = self.eI.getGlobal("launcher")

        if launcher and self.get("cl_master") and launcher.s:
            launcher.s.quitGame()
            launcher.s = None

    def quitGame(self):
        '''Quits the game completely.'''
        self.oP("Shutting down...")
        self.endGame()
        self.purgeNetwork()
        self.eI.quitGame()

    def setMusic(self, music):
        '''Sets the music track.'''
        launcher = self.eI.getGlobal("launcher")

        launcher.sound.playMusic(music)
        self.oP("Set music to %s." % music)

    def stopMusic(self):
        '''Stops the music track.'''
        launcher = self.eI.getGlobal("launcher")

        launcher.sound.stopMusic()
        self.oP("Stopped music.")

    def playSound(self, sound, emitter=None):
        '''Plays the sound.'''
        launcher = self.eI.getGlobal("launcher")

        if emitter:
            launcher.sound.playSound(sound, emitter.gameObject)
        else:
            launcher.sound.playSound(sound)

        self.oP("Started playing sound %s." % sound)

    def stopSound(self, handle):
        '''Stops a sound.'''
        launcher = self.eI.getGlobal("launcher")

        GUID, name = launcher.sound.stopSound(handle)

        self.oP("Stopped sound %s." % name)

    def stopSoundByGUID(self, GUID, name):
        '''Stops a sound.'''
        launcher = self.eI.getGlobal("launcher")

        launcher.sound.stopSoundByGUID(GUID, name)
        self.oP("Stopped sound %s." % name)

    def playDialog(self, sound, emitter):
        '''Plays a dialog line.'''
        launcher = self.eI.getGlobal("launcher")

        if emitter:
            launcher.sound.playDialog(sound, emitter.gameObject)
        else:
            launcher.sound.playDialog(sound)

        self.oP("Playing dialog %s." % sound)

    def stopDialog(self, handle):
        '''Stops a dialog line.'''
        launcher = self.eI.getGlobal("launcher")

        GUID, name = launcher.sound.stopDialog(handle)

        self.oP("Stopped dialog %s." % name)

    def stopDialogByGUID(self, GUID, name):
        '''Stops a dialog line.'''
        launcher = self.eI.getGlobal("launcher")

        launcher.sound.stopDialogByGUID(GUID, name)

        self.oP("Stopped dialog %s." % name)

    def enableShader(self, index, name, mode):
        '''Enables a shader as a filter for the entire screen.'''
        self.sH.enableShader(index, name, mode)

    def disableShader(self, index):
        '''Disables a shader that was filtering the entire screen.'''
        self.sH.disableShader(index)

    def playVideo(self, video):
        '''Plays a video.'''
        self.vP.playVideo(video)
        self.oP("Started video %s." % video)

    def stopVideo(self):
        '''Stops a video.'''
        self.vP.stopVideo()
        self.oP("Stopped video.")

    def replaceMesh(self, ent, meshName):
        '''Replaces the mesh of an Entity.'''
        cR = self.load("Mesh", meshName)

        name = cR.get("name")
        resourcePath = cR.get("resourcePath")

        self.loadLibrary("Mesh", resourcePath, mesh=True)

        ent.gameObject.replaceMesh(meshName, True, True)
        self.oP("Replaced mesh of %s with %s." % (ent.GUID, meshName))

    def addInterface(self, name):
        '''Adds an interface.'''
        if not self.getInterfaceByName(name):
            cR = self.load("UI", name)

            name = cR.get("name")
            resource = cR.get("resourcePath")
            scriptName = cR.get("scriptPath")

            #self.loadLibrary("UI", resource)

            self.oP("Creating interface %s." % name)
            self.interfaces.append(Interface(name, resource, scriptName))
        else:
            self.oP("Interface %s already exists, not created." % name)

    def removeInterface(self, name):
        '''Removes an interface.'''
        interface = self.getInterfaceByName(name)

        if interface:
            self.oP("Removing interface %s." % name)
            self.interfaces.remove(interface)
            interface.kill()

    def removeAllInterfaces(self):
        '''Removes all interfaces.'''
        self.oP("Removing all interfaces.")
        names = []
        for interface in self.interfaces:
            names.append(interface.name)

        for name in names:
            self.removeInterface(name)

    def getInterfaceByName(self, name):
        '''Gets an interface by name.'''
        for interface in self.interfaces:
            if interface.name == name:
                return interface

    def addMarker(self, name, GUID):
        '''Adds a marker tracking the specified Entity.'''
        cR = self.load("UI", name)

        name = cR.get("name")
        resource = cR.get("resourcePath")

        self.eI.getGlobal("addToWaypoints")(resource, name, GUID)

        self.oP("Added waypoint %s tracking %s." % (name, GUID))

    def removeMarker(self, GUID):
        '''Removes all markers tracking an Entity.'''
        for obj in self.eI.getWaypointsScene().objects:
            if "targetGUID" in obj and obj["targetGUID"] == GUID:
                obj.endObject()

        self.oP("Removed waypoints tracking %s." % GUID)

    def inputClick(self, keyCode, pos):
        '''Checks for the interface clicks.'''
        obj, scene = self.eI.getMouseOverObjectScene(pos)

        if obj and scene:
            for interface in self.interfaces:
                if interface.name == self.eI.getTerminalParent(obj).name:
                    interface.onClick(obj.name)
                    return True

            for i in self.entities:
                if obj == i.gameObject or obj in i.gameObject.childrenRecursive:
                    i.onClick(obj.name)
                    return True

        return False

    def getDisconnectReaction(self):
        '''Gets the reaction function for disconnecting from the server.'''
        self.sE.execute(NETSCRIPT_PATH + "disconnect")
        return self.disconnectReaction

    def addToRotationBlacklist(self, GUID):
        '''Adds a GUID to the rotation blacklist.'''
        if not GUID in self.pA.blacklistedGUIDs:
            self.pA.blacklistedGUIDs.append(GUID)

    def removeFromRotationBlacklist(self, GUID):
        '''Removes a GUID from the rotation blacklist.'''
        if GUID in self.pA.blacklistedGUIDs:
            self.pA.blacklistedGUIDs.remove(GUID)

    def sendInterfaceEvent(self, event, aux=None):
        '''Sends an interface event.'''
        self.sendEvent(["INPUT", "INTERFACE", [event, aux]])

    def sendChatMessage(self, msg):
        '''Sends a chat message.'''
        self.sendEvent(["SYSTEM", "CHAT", msg])

    def sendGameEvent(self, mode, data):
        '''Sends a game event.'''
        self.sendEvent(["GAME", mode, data])

    def sendEvent(self, event):
        '''Sends an event to the server.'''
        if event[0] == "INPUT" or event == "KEEPALIVE":
            mode = "FAF"
        elif event[0] in ["SYSTEM", "GAME", "NETWORK"]:
            mode = "RT"

        if self.nC.clients:
            server = self.nC.clients[0]
            server.send(event, mode)

    def recvEvent(self, event):
        '''Handles an event from the server.'''
        if event[0] == "PHYSICS" and self.get("cl_update"):
            self.pA.update(event)
        if event[0] == "SYSTEM":
            if event[1] == "CVARS":
                self.configure(event[2][0], event[2][1], fromServer=True)
            elif event[1] == "NETVARS":
                self.netVars[event[2][0]] = event[2][1]

            elif event[1] == "SOUND_PLAY" and self.get("cl_update"):
                entity = self.getEntityByGUID(event[2][0])
                self.playSound(event[2][1], entity)
            elif event[1] == "SOUND_STOP" and self.get("cl_update"):
                self.stopSoundByGUID(event[2][0], event[2][1])

            elif event[1] == "DIALOG_PLAY" and self.get("cl_update"):
                entity = self.getEntityByGUID(event[2][0])
                self.playDialog(event[2][1], entity)
            elif event[1] == "DIALOG_STOP" and self.get("cl_update"):
                self.stopDialogByGUID(event[2][0], event[2][1])

            elif event[1] == "MUSIC_PLAY" and self.get("cl_update"):
                self.setMusic(event[2][0])
            elif event[1] == "MUSIC_STOP" and self.get("cl_update"):
                self.stopMusic()

            elif event[1] == "SET_CAMERA":
                self.configure("cl_camera", event[2])

            elif event[1] == "INTERFACE_CREATE":
                self.addInterface(event[2])
            elif event[1] == "INTERFACE_REMOVE":
                self.removeInterface(event[2])

            elif event[1] == "CHAT":
                self.chatMessages.append(event[2])

    def loop(self):
        '''Does everything basically.'''

        #cl = OptiClock()

        #Detector Index
        if not self.get("cl_master"):
            self.updateDetectorIndex()

        #cl.clockIn("DetectorIndex")

        #KEEP ALIVE
        self.keepAliveTicker += 1
        if self.keepAliveTicker % 600 == 0:
            self.sendEvent("KEEPALIVE")

        #cl.clockIn("KeepAlive")

        #Do Things#######################

        #Read in controls

        self.iR.checkControls()

        #cl.clockIn("Input")

        #Update Video Player

        self.vP.loop()

        #cl.clockIn("VideoPlayer")

        if self.get("cl_update"):
            #Update localization
            self.l.loop()

            #cl.clockIn("Localization")
            #Apply shaders
            self.sH.loop()

        #cl.clockIn("Shaders")
        #Run object code

        for ent in self.entities:
            if hasattr(ent, "loop"):
                ent.checkAnimation()
                ent.loop(self, "client")
        #Run interface code

        #cl.clockIn("Entities")

        if self.level:
            if hasattr(self.level, "loop"):
                self.level.loop(self, "client")

        #cl.clockIn("Level")

        for interface in self.interfaces:
            if hasattr(interface, "loop"):
                try:
                    interface.loop()
                except:
                    pass

        #cl.clockIn("Interface")

        if self.nC:
            #Send Things#####################
            for key in self.iR.keyEvents:
                self.sendEvent(key)

            for event in self.gameEvents:
                self.sendEvent(event)

            self.gameEvents = []
            self.iR.keyEvents = []

            #cl.clockIn("SendLoop")

            #Recv Things#####################

            if self.nC.clients:
                server = self.nC.clients[0]

                packet = server.recv()

                if packet:
                    payload = packet.getPayload()

                    try:
                        data = eval(payload)
                    except:
                        data = None

                    if data:
                        self.recvEvent(data)

            #cl.clockIn("RecvLoop")

            #Initial sync
            if self.nC.clients:
                server = self.nC.clients[0]

                if not self.get("cl_synced"):
                    server.disconnectFunc = self.getDisconnectReaction()
                    self.sendEvent(["SYSTEM", "NAME", self.get("cl_name")])
                    self.sendEvent(
                        ["SYSTEM", "PASSWORD",
                         self.get("cl_password")])
                    self.sendEvent(["SYSTEM", "PHYSICS", "FORCEUPDATE"])
                    self.configure("cl_synced", 1)

            #cl.clockIn("Sync")

            #KILL THINGS###################################################
            kill = self.nC.pullKillQueue()

            if kill:
                kill()

            #cl.clockIn("Kill")

            self.nC.loop()