# -*- test-case-name: mv3d.test.test_clientstate -*- # Copyright (C) 2012 Mortal Coil Games # See LICENSE for details. """ @author: mike """ import os from twisted.internet.defer import inlineCallbacks, returnValue from twisted.python.log import deferr from twisted.internet import reactor from mv3d.util.statemachine import StateMachine, State from mv3d.util.iservice import IPlayerClient, IAssetClient from mv3d.util.guide import ChangeNotifier, NotifierProperty from mv3d.net.client import ServiceLoc from ConfigParser import ConfigParser class MainMenu(State, ChangeNotifier): """ The client is in the main menu state. This is the first state that comes up. """ def __init__(self, client, previousState): State.__init__(self, client, previousState) self.window = client.parser.load(os.path.join("templates", "guide", "client", "mainmenu.xml"), self) self.window.show() self.client = client def onConnect(self, _obj, _prop): """ Show the connect screen """ self.client.changeState(ConnectWindow) self.window.destroy() def onNewUser(self, _obj, _prop): """ Show the new user screen """ self.client.changeState(NewUserWindow) self.window.destroy() def onSettings(self, _obj, _prop): """ Show the config screen """ self.client.changeState(SettingsWindow) self.window.destroy() def onExit(self, _obj, _prop): """ The user hit the exit button. """ reactor.stop() #@UndefinedVariable class ConnectWindow(State, ChangeNotifier): """ The client shows the connect window and will connect """ serviceLocs = NotifierProperty("serviceLocs") service = NotifierProperty("service", default="") username = NotifierProperty("username", default="") password = NotifierProperty("password", default="") def __init__(self, client, previousState): State.__init__(self, client, previousState) player = client.conductor.getLocalService(IPlayerClient) self.serviceLocs = [server.toString() for server in player.loginservers] self.window = client.parser.load(os.path.join("templates", "guide", "client", "login.xml"), self) if len(self.serviceLocs): self.service = self.serviceLocs[0] self.username = player.login self.password = player.password self.window.show() self.client = client @inlineCallbacks def onLogin(self, loginButton, _prop): """ """ loginButton.enabled = False service = ServiceLoc(self.service) service.creds = [self.username, self.password] try: self.client.svc = yield self.client.conductor.getService(service) self.window.destroy() self.client.changeState(CharacterSelectWindow) except: deferr() loginButton.enabled = True def onCancel(self, _obj, _prop): """ """ self.window.destroy() self.client.changeState(MainMenu) class CharacterSelectWindow(State, ChangeNotifier): """ Shows the character selection window """ characters = NotifierProperty("characters") selectedCharacter = NotifierProperty("selectedCharacter") def __init__(self, client, previousState): State.__init__(self, client, previousState) self.window = client.parser.load(os.path.join("templates", "guide", "client", "charselect.xml"), self) client.scheduleEvent(0, "getCharacters") self.player = client.conductor.getLocalService(IPlayerClient) self.client = client @inlineCallbacks def _matchesRendererType(self, rtype, assetIDs): """ Check if the list of assets match the renderer type. TODO: Refactor with the same function in ui/player.py """ asvc = self.player.parent.getLocalService(IAssetClient) assets = yield asvc.getAssets(assetIDs) for asset in assets: if not rtype in asset.getCompatibleRenderers(): returnValue(False) depsMatch = yield self._matchesRendererType(rtype, asset.dependencies) if not depsMatch: returnValue(False) returnValue(True) @inlineCallbacks def event_getCharacters(self, client): try: yield self._refreshCharacterList() self.window.show() self.client = client except: deferr() self.window.destroy() client.changeState(MainMenu) @inlineCallbacks def onSelect(self, _obj, _prop): try: self.window.destroy() yield self.player.connectPC(self.selectedCharacter[0]) yield self.player.connectedPC(None) self.client.changeState(InGame) except: deferr() self.client.changeState(MainMenu) def onCancel(self, _obj, _prop): """ The user hit the cancel button. """ self.window.destroy() self.client.changeState(MainMenu) def onCreate(self, _obj, _prop): """ The user hit the create button """ self.window.destroy() self.client.changeState(CreateCharacterWindow) @inlineCallbacks def _refreshCharacterList(self): """ Refreshes the list of available characters. """ pids = yield self.player.getPCs() svr = yield self.player.parent.getOneService(self.player.loginservers) characters = [] for pid in pids: characters.append((yield svr.getPCInfo(pid))) self.characters = [] rtype = self.player.renderer.getType() for pid, character in zip(pids, characters): if len(character) > 3: matchesType = yield self._matchesRendererType(rtype, character[3]) else: matchesType = True if matchesType: self.characters.append([pid] + list(character)) self.propertyChanged("characters") class CreateCharacterWindow(State): """ Show the character creation window for a realm. """ def __init__(self, client, previousState): self.player = client.conductor.getLocalService(IPlayerClient) client.scheduleEvent(0, "start") @inlineCallbacks def event_start(self, client): """ Show the UI for the selected realm. Err, hard coded to 0 for now. """ try: yield self.player.createCharacter(0) client.changeState(CharacterSelectWindow) except: deferr() class NewUserWindow(State, ChangeNotifier): """ In this state, the user can create an account on the login server """ username = NotifierProperty("username") password = NotifierProperty("password") passwordVerify = NotifierProperty("passwordVerify") email = NotifierProperty("email") def __init__(self, client, previousState): State.__init__(self, client, previousState) self.client = client self.username = "" self.password = "" self.passwordVerify = "" self.email = "" self.window = client.parser.load(os.path.join("templates", "guide", "client", "createaccount.xml"), self) self.player = client.conductor.getLocalService(IPlayerClient) def onCancel(self, _obj, _prop): """ The cancel button was clicked """ self.client.changeState(MainMenu) self.window.destroy() def onCreate(self, button, _prop): """ The create button was clicked """ if not self.username or not self.password or not self.email: return if self.password != self.passwordVerify: return button.enabled = False try: return self.player.createAccount(self.username, self.password, self.email) except: deferr() finally: button.enabled = True self.client.changeState(MainMenu) self.window.destroy() class SettingsWindow(State, ChangeNotifier): """ The user is configuring the client. """ renderer = NotifierProperty("renderer") renderers = NotifierProperty("renderers") fullScreen = NotifierProperty("fullScreen") screenWidth = NotifierProperty("screenWidth") screenHeight = NotifierProperty("screenHeight") loginServices = NotifierProperty("loginServices") playerServices = NotifierProperty("playerServices") username = NotifierProperty("username") password = NotifierProperty("password") def __init__(self, client, previousState): State.__init__(self, client, previousState) self.client = client self.window = client.parser.load(os.path.join("templates", "guide", "client", "settings.xml"), self) self.config = self.client.conductor.config self.localConfig = ConfigParser() self.localConfig.read(os.path.join(self.client.conductor.dataRoot, "client.conf")) self.renderers = [] try: from ogre import renderer self.renderers.append("mv3d.client.ui.ogre3d.OgreRenderer") except ImportError: pass try: from panda3d import core self.renderers.append("mv3d.client.ui.panda.PandaRenderer") except ImportError: pass self.propertyChanged("renderers") self.readConfig() def readConfig(self): """ Read the config from the configParser """ if self.config.has_option("Player", "Renderer"): self.renderer = self.config.get("Player", "Renderer") else: self.renderer = self.renderers[0] if self.config.has_option("Graphics", "fullScreen"): self.fullScreen = self.config.getboolean("Graphics", "fullScreen") if self.config.has_option("Graphics", "resolution"): rez = self.config.get("Graphics", "resolution").lower().split("x") self.screenWidth = rez[0] self.screenHeight = rez[1] if self.config.has_option("Player", "servers"): self.playerServices = self.config.get("Player", "servers").split( ",") if self.config.has_option("PBClient", "loginServices"): self.loginServices = self.config.get("PBClient", "loginServices").split(",") if self.config.has_option("Player", "defaultLogin"): login = self.config.get("Player", "defaultLogin").split(":") self.username = login[0] self.password = login[1] def writeConfig(self): """ Write the config to the configParser """ if not self.localConfig.has_section("Player"): self.localConfig.add_section("Player") if not self.localConfig.has_section("Graphics"): self.localConfig.add_section("Graphics") if not self.localConfig.has_section("PBClient"): self.localConfig.add_section("PBClient") self.localConfig.set("Player", "Renderer", self.renderer) self.localConfig.set("Graphics", "fullScreen", str(self.fullScreen).lower()) self.localConfig.set("Graphics", "resolution", "%sx%s" % ( self.screenWidth, self.screenHeight)) self.localConfig.set("Player", "servers", ",".join( [str(svc) for svc in self.playerServices])) self.localConfig.set("PBClient", "loginServices", ",".join( [str(svc) for svc in self.loginServices])) self.localConfig.set("Player", "defaultLogin", "%s:%s" % (self.username, self.password)) self.config.set("Player", "Renderer", self.renderer) self.config.set("Graphics", "fullScreen", str(self.fullScreen).lower()) self.config.set("Graphics", "resolution", "%sx%s" % ( self.screenWidth, self.screenHeight)) self.config.set("Player", "servers", ",".join( [str(svc) for svc in self.playerServices])) self.config.set("PBClient", "loginServices", ",".join( [str(svc) for svc in self.loginServices])) self.config.set("Player", "defaultLogin", "%s:%s" % (self.username, self.password)) def onCancel(self, _obj, _prop): """ The cancel button was clicked """ self.window.hide() self.window.destroy() self.client.changeState(MainMenu) def onSave(self, _obj, _prop): """ The save button was clicked """ self.writeConfig() pfile = open(os.path.join(self.client.conductor.dataRoot, "client.conf"), "w") self.localConfig.write(pfile) pfile.close() self.window.hide() self.window.destroy() self.client.changeState(MainMenu) def onAddLoginService(self, _obj, _prop): """ Add the typed in login service. """ tbox = self.window.findChild("loginServiceText") self.loginServices = self.loginServices + [tbox.text] tbox.text = "" def onRemoveLoginService(self, _obj, _prop): """ Get the selection and delete it """ lbox = self.window.findChild("loginServiceList") sel = lbox.selection lbox.selection = None self.loginServices = [mod for mod in self.loginServices if mod != sel] def onAddService(self, _obj, _prop): """ Add the typed in login service. """ tbox = self.window.findChild("serviceText") self.playerServices = self.playerServices + [tbox.text] tbox.text = "" def onRemoveService(self, _obj, _prop): """ Get the selection and delete it """ lbox = self.window.findChild("serviceList") sel = lbox.selection lbox.selection = None self.playerServices = [mod for mod in self.playerServices if mod != sel] class InGame(State, ChangeNotifier): """ When the user is in the game. """ class ClientState(StateMachine): """ Keeps track of the main state of the client. """ svc = None def __init__(self, conductor, callLater=None, timer=None): StateMachine.__init__(self, callLater, timer) self.conductor = conductor self.parser = conductor.getLocalService(IPlayerClient).getUI() self.changeState(MainMenu)