converse-new-player.cpp

Go to the documentation of this file.
00001 /*
00002  * converse-new-player.cpp
00003  *
00004  * Copyright (C) 2008,2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  * Handles conversation for new players.
00031  */
00032 
00033 // includes -------------------------------------------------------------------
00034 #include "srv-converse.h"               // always include our own header first!
00035 
00036 #include "srv-hosts.h"
00037 #include "srv-msg-router.h"
00038 #include "srv-users.h"
00039 
00040 #include "common/wave_ex.h"
00041 #include "crypto/crypto.h"
00042 #include "datahash/datahash_util.h"
00043 #include "dialog/request.h"
00044 #include "perf/perf.h"
00045 
00046 
00047 namespace aesop {
00048 
00049 
00050 
00051 enum eDialogs {
00052         eDialog_First           = 1,            // initial screen
00053         eDialog_SignIn          = 2,            // provide password
00054         eDialog_Create          = 3,            // create new user account
00055         eDialog_Fail            = 4,            // notify failure!
00056 
00057         // keep this last!
00058         eDialog_Done            = 0
00059 };
00060 
00061 
00062 
00064 //
00065 //      NPHost -- implements the ConversationHost interface for New Player
00066 //                      conversations.
00067 //
00069 
00070 class NPHost : public converse::ConversationHost {
00071 public:
00072         NPHost(void) throw();
00073         ~NPHost(void) throw() { }
00074 
00075         // public class methods ------------------------------------------------
00076         void initialize(IN smart_ptr<crypto::DESKey>& desKey,
00077                                 IN new_player_conv_data_t& data);
00078 
00079         // aesop::ConversationHost class interface methods --------------------
00080         int getCurrentDialogIdTS(void);
00081         void handleReplyTS(IN int dialogId,
00082                                 IN const Datahash * reply);
00083         std::string getDialogDataTS(void);
00084 
00085 private:
00086         // private helper methods ----------------------------------------------
00087 
00088         // private member data -------------------------------------------------
00089         smart_ptr<crypto::DESKey>       m_desKey;
00090         new_player_conv_data_t  m_data;
00091         int                     m_dialog;       // where are we?
00092         std::string             m_header;
00093         std::string             m_username;     // player is attempting to be
00094         std::string             m_diagnostic;
00095 };
00096 
00097 
00098 
00099 NPHost::NPHost(void)
00100 throw()
00101 {
00102         m_dialog = eDialog_First;
00103 }
00104 
00105 
00106 
00107 void
00108 NPHost::initialize
00109 (
00110 IN smart_ptr<crypto::DESKey>& desKey,
00111 IN new_player_conv_data_t& data
00112 )
00113 {
00114         ASSERT(desKey, "null");
00115         ASSERT(data.isValid(), "invalid new player data");
00116 
00117         ASSERT(!m_desKey, "Already have symmetric encryption key?");
00118         m_desKey = desKey;
00119 
00120         m_data = data;
00121         ASSERT(m_data.isValid(), "copied data is invalid?");
00122 
00123         // other info
00124         const int bufSize = 64;
00125         char buffer[bufSize];
00126         gethostname(buffer, bufSize - 1);
00127         buffer[bufSize - 1] = 0;        // force null
00128 
00129         m_header = "Server: ";
00130         m_header += buffer;
00131 }
00132 
00133 
00134 
00136 //
00137 //      NPHost -- aesop::ConversationHost class interface methods
00138 //
00140 
00141 int
00142 NPHost::getCurrentDialogIdTS
00143 (
00144 void
00145 )
00146 {
00147         return m_dialog;
00148 }
00149 
00150 
00151 
00152 void
00153 NPHost::handleReplyTS
00154 (
00155 IN int dialogId,
00156 IN const Datahash * reply
00157 )
00158 {
00159         ASSERT(dialogId > 0, "Bad dialog id: %d", dialogId);
00160         ASSERT(reply, "null");
00161         ASSERT(m_data.isValid(), "invalid new player data");
00162 
00163         std::string submit = getString(reply, "submit");
00164         DPRINTF("Received submit='%s' for dialog=%d", submit.c_str(), dialogId);
00165         std::string password;
00166 
00167         if (dialogId != m_dialog) {
00168                 // player had wrong version!  Do nothing and force refresh
00169                 // conversation framework should catch this...  threading issue?
00170                 DPRINTF("ERROR: called on wrong dialog id?");
00171                 return;
00172         }
00173 
00174         // determine next state
00175         if (eDialog_First == m_dialog) {
00176                 if ("_new" == submit) {
00177                         m_dialog = eDialog_Create;
00178                 } else {
00179                         m_username = submit;
00180                         // selected a user account: need to sign in?
00181                         if (m_data.userMgr->passwordRequiredTS()) {
00182                                 m_dialog = eDialog_SignIn;
00183                         } else {
00184                                 m_dialog = eDialog_Done;
00185                         }
00186                 }
00187         } else if (eDialog_SignIn == m_dialog) {
00188                 std::string enc = getString(reply, "password");
00189 //              DPRINTF("Encrypted password: %s", enc.c_str());
00190                 password = m_desKey->decrypt(enc.c_str());
00191                 m_dialog = eDialog_Done;
00192         } else if (eDialog_Create == m_dialog) {
00193                 std::string username = getString(reply, "username");
00194                 if (m_data.userMgr->createUserTS(username.c_str(), m_diagnostic)) {
00195                         // success!
00196                         m_username = username;
00197                         if (m_data.userMgr->passwordRequiredTS()) {
00198                                 m_dialog = eDialog_SignIn;
00199                         } else {
00200                                 m_dialog = eDialog_Done;
00201                         }
00202                 } else {
00203                         // failure!
00204                         DPRINTF("Failed: %s", m_diagnostic.c_str());
00205                         m_dialog = eDialog_Fail;
00206                 }
00207         } else if (eDialog_Fail == m_dialog) {
00208                 // don't care what was submitted!  Just start over
00209                 m_dialog = eDialog_First;
00210         } else {
00211                 m_dialog = eDialog_Done;
00212         }
00213 
00214         // think we're done?  Try it...
00215         if (eDialog_Done == m_dialog) {
00216                 // attempt to log in with current credentials
00217                 std::string playerGuid;
00218                 if (!m_data.userMgr->logInAsUserTS(m_username.c_str(),
00219                     password.c_str(), playerGuid, m_diagnostic)) {
00220                         DPRINTF("Failed!  %s", m_diagnostic.c_str());
00221                         m_dialog = eDialog_Fail;
00222                 } else {
00223                         // logged in!
00224                         DPRINTF("Player guid: '%s'", playerGuid.c_str());
00225 
00226                         // will server allow it?
00227                         if (!m_data.updates->setPlayerUserAccountTS(
00228                             m_data.connId, m_data.playerId, m_username.c_str(),
00229                             playerGuid.c_str(), m_diagnostic)) {
00230                                 DPRINTF("Server disallowed: %s",
00231                                     m_diagnostic.c_str());
00232                                 m_dialog = eDialog_Fail;
00233                         }
00234                 }
00235         }
00236 }
00237 
00238 
00239 
00240 std::string
00241 NPHost::getDialogDataTS
00242 (
00243 void
00244 )
00245 {
00246         ASSERT(m_data.isValid(), "invalid new player data");
00247 
00248         // quick check
00249         if (eDialog_Done == m_dialog)
00250                 return "";
00251 
00252         dialog::ContainerRequest c;
00253         c.addLabel(m_header.c_str());
00254         if (eDialog_First == m_dialog) {
00255                 c.addLabel("Select a user account");
00256                 SetString names;
00257                 m_data.userMgr->getUsernamesTS(names);
00258                 for (SetString::iterator i = names.begin(); i != names.end();
00259                      ++i) {
00260                         const char * login = i->c_str();
00261                         c.addButton(login, login);
00262                 }
00263                 if (m_data.userMgr->canCreateNewUsersTS()) {
00264                         c.addButton("_new", "New User");
00265                 }
00266         } else if (eDialog_SignIn == m_dialog) {
00267                 std::string text = "Provide password for ";
00268                 text += m_username;
00269                 c.addLabel(text.c_str());
00270                 c.addTextbox("password", "", 8, true);
00271         } else if (eDialog_Create == m_dialog) {
00272                 c.addLabel("Provide new username");
00273                 c.addTextbox("username", "", 8);
00274         } else if (eDialog_Fail == m_dialog) {
00275                 std::string error = "Error: ";
00276                 error += m_diagnostic;
00277                 c.addLabel(error.c_str());
00278                 c.addButton("retry", "Retry");
00279         } else {
00280                 ASSERT(false, "bad dialog id? %d", m_dialog);
00281         }
00282 
00283         return c.get();
00284 }
00285 
00286 
00287 
00288 
00290 //
00291 //      NPHost -- private helper methods
00292 //
00294 
00295 
00297 //
00298 //      public API
00299 //
00301 
00302 smart_ptr<converse::ConversationHost>
00303 createNewPlayerConversationHost
00304 (
00305 IN smart_ptr<crypto::DESKey>& desKey,
00306 IN new_player_conv_data_t& data
00307 )
00308 {
00309         ASSERT(desKey, "null");
00310         ASSERT(data.isValid(), "invalid new player data");
00311 
00312         smart_ptr<NPHost> local = new NPHost;
00313         ASSERT(local, "null");
00314 
00315         local->initialize(desKey, data);
00316 
00317         return local;
00318 }
00319 
00320 
00321 
00322 };      // aesop namespace
00323