conversation.h

Go to the documentation of this file.
00001 /*
00002  * conversation.h
00003  *
00004  * Copyright (C) 2007,2009,2010  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  */
00031 
00032 #ifndef WAVEPACKET_CONVERSATION_H__
00033 #define WAVEPACKET_CONVERSATION_H__
00034 
00035 // includes --------------------------------------------------------------------
00036 #include "common/common.h"
00037 #include "threadsafe/smart_ptr.h"
00038 
00039 
00040 // forward declarations
00041 class Datahash;
00042 
00043 
00044 namespace converse {
00045 
00046 ////////////////////////////////////////////////////////////////////////////////
00047 ///
00048 /// \ingroup lib
00049 /// \defgroup conversation Conversation Library
00050 ///
00051 /// \n
00052 /// \b NOTE: this documentation talks a lot about "clients" and "servers".
00053 /// That's because the original implementation was in fact designed with a
00054 /// particular remote client/server use case in mind.  However, the concept has
00055 /// broadened significantly since then.  At this point, the Conversation Library
00056 /// is a convenient way to help the user through multiple dialogs related to
00057 /// a particular task (signing into a remote server, configuration local
00058 /// hardware, etc.).  The library also has a few concepts (unique IDs and
00059 /// central state management) that makes it easer to drive dialogs remotely, but
00060 /// many use cases involve the client (user-displayed dialogs) and server
00061 /// (conversation management) all on the same machine.
00062 ///
00063 /// This library helps track conversations (chains of dialogs) but does not
00064 /// manage any rendering itself!  That is up to the client.
00065 ///
00066 /// \n
00067 /// A Conversation is a long-running communication channel between the server
00068 /// and a player.  The concept of a Conversation exists so that client machines
00069 /// can be sure to keep in sync even during network or other communication
00070 /// drops.
00071 ///
00072 /// Some properties of Conversations:
00073 ///  - Only the server can initiate Conversations.  Clients can sometimes
00074 ///     request that conversations be started, but that is usually as part of
00075 ///     specific message protocols and is not a general capability.
00076 ///  - Conversations are only between the server and human players.
00077 ///  - The server maintains all state for a given Conversation.
00078 ///
00079 /// The fact that Conversations are between the server and human players means
00080 /// that the contents of conversations are always Dialogs (see \ref dialog).
00081 /// The server will always send dialogs to the player, and the player will
00082 /// always send dialog responses back to the server.
00083 ///
00084 /// Just as Dialogs can be thought of as HTML pages (or forms), a Conversation
00085 /// can be thought of as a series of forms.  The ConversationManager helps
00086 /// coordinate general state and flow of a player moving through a series of
00087 /// forms, and the ConversationHost object manages form handling for the
00088 /// specific conversation.
00089 ///
00090 /// The fact that only the server can initiate Conversations, and maintains all
00091 /// Conversation state, helps minimize race conditions and makes synchronization
00092 /// easy.  As a consequence, however, all clients should be aware that they may
00093 /// have stale Conversation state and should occasionally refresh (to pick up
00094 /// any new conversations they missed), and pay attention to return codes in
00095 /// case their local view of a particular conversation is out-of-date.
00096 ///
00097 /// Clients may receive duplicate messages about a conversation, and so should
00098 /// de-dupe messages based on the conversation and dialog GUIDs supplied.  The
00099 /// server's latest message should be considered authoritative if the client
00100 /// discovers that the server's dialog ID is different than the local dialog
00101 /// ID for a given conversation.
00102 ///
00103 /// Note that conversations must have a globally unique identifier (GUID).
00104 /// Dialog identifiers are only unique within a particular conversation, and
00105 /// so dialogs just have regular IDs (no need to be globally unique).  Hence
00106 /// the key variables conversationGuid (globally-unique conversation identifier)
00107 /// and dialogId (identifier for dialog that is unique within the context of a
00108 /// particular conversation).
00109 ///
00110 /// A player may have multiple conversations going at once!  For instance, they
00111 /// may be conversing with another player in one, buying items from a shop in
00112 /// another, and overhearing other people talking nearby in another.  However, a
00113 /// given conversation will only have one dialog active at any one time.
00114 ///
00115 /// \n
00116 /// There are three main objects in the Conversation data model:
00117 ///  - ConversationManager : manages high-level conversation state for all
00118 ///     conversations currently underway.
00119 ///  - ConversationHost : manages dialog state for a particular conversation
00120 ///  - ConversationRouter : knows how to get dialogs presented to the player.
00121 ///
00122 /// Host objects are the most common objects, since any kind of conversation
00123 /// (player joining a game, chat, local system menus, buying from a shop) will
00124 /// require a dedicated host.
00125 ///
00126 /// Hosts do not need to know how to get the dialogs passed over the network or
00127 /// displayed to the client.  The ConversationRouter will do that.  On the
00128 /// server, the ConversationRouter knows how to send dialog messages over the
00129 /// network.  On the client, the ConversationRouter can get them displayed.
00130 ///
00131 /// \n
00132 /// Although we say "Conversations are between the server and a player",
00133 /// conversations can also be run on the client.  This is helpful to present
00134 /// the player with local dialogs such as system menus etc.  In this case, the
00135 /// client often runs its own ConversationManager with a local
00136 /// ConversationRouter.
00137 ///
00138 ////////////////////////////////////////////////////////////////////////////////
00139 /*@{*/
00140 
00141 
00142 /// The connection ID used by the Conversation library is used for validation
00143 /// (client can ensure that only the proper endpoint is participating in
00144 /// conversations) but is otherwise not used.  This library makes no networking
00145 /// calls, for instance.
00146 typedef dword_t conn_id_t;
00147 
00148 
00149 /// anyone that wants to start a conversation must supply one of these.
00150 /// Note that all methods must be threadsafe!
00151 class ConversationHost {
00152 public:
00153         // virtual destructor --------------------------------------------------
00154         virtual ~ConversationHost(void) throw();
00155 
00156         // converse::ConversationHost class interface methods ------------------
00157 
00158         /// The host should return the current dialog ID -- 0 to end
00159         virtual int getCurrentDialogIdTS(void) = 0;
00160 
00161         /// This method is called when the player has completed a given dialog
00162         /// and has sent the reply back to the ConversationManager.  The 
00163         /// Manager forwards the reply to the host so it can take a look at
00164         /// the reply and figure out the next step.  After this, the host will
00165         /// be called on getCurrentDialogIdTS() so the conversation can
00166         /// continue.
00167         virtual void handleReplyTS(IN int dialogId,
00168                                 IN const Datahash * reply) = 0;
00169 
00170         /// Host should return the raw dialog data for the current dialog ID
00171         /// (dialog data is a text string -- see \ref dialog).
00172         virtual std::string getDialogDataTS(void) = 0;
00173 
00174         /// This is called "frequently", on the order of 100 times/second,
00175         ///     although no particular rate is guaranteed.  The Host should
00176         ///     update internal state as necessary, and return true if the
00177         ///     Host would like the client to refresh its view of the
00178         ///     conversation.  Default implementation simply returns false.
00179         virtual bool updateState(void);
00180 };
00181 
00182 
00183 
00184 /// Object that knows how to route conversation dialogs.  On the server, this
00185 /// object knows how to send dialogs over the network.  On the client, this
00186 /// object knows how to render local dialogs.  Clients must supply this.
00187 /// Must be threadsafe!
00188 class ConversationRouter {
00189 public:
00190         // converse::ConversationRouter class interface methods ----------------
00191         virtual void routeConversationTS(IN const char * guid,
00192                                 IN int dialogId,
00193                                 IN int playerId,
00194                                 IN conn_id_t connId,
00195                                 IN const char * dialogData) = 0;
00196 
00197 protected:
00198         // virtual destructor --------------------------------------------------
00199         /// cannot delete through this interface!
00200         virtual ~ConversationRouter(void) throw();
00201 };
00202 
00203 
00204 
00205 /// object that manages conversations on the server
00206 /// This object must be threadsafe!
00207 class ConversationManager {
00208 public:
00209         // virtual destructor --------------------------------------------------
00210         virtual ~ConversationManager(void) throw();
00211 
00212         // converse::ConversationManager class interface methods ---------------
00213         virtual bool updateConversationTS(IN const char * guid,
00214                                 IN conn_id_t connId,
00215                                 IN int playerId,
00216                                 IN smart_ptr<ConversationHost> host) = 0;
00217 
00218         virtual bool handleDialogReplyTS(IN const char * guid,
00219                                 IN int dialogId,
00220                                 IN conn_id_t connId,
00221                                 IN int playerId,
00222                                 IN const Datahash * reply) = 0;
00223 
00224         virtual void updateAll(void) = 0;
00225 
00226         // static factory methods ----------------------------------------------
00227         static smart_ptr<ConversationManager> create(
00228                                 IN ConversationRouter * router);
00229 };
00230 
00231 
00232 
00233 };      // converse namespace
00234 
00235 #endif  // WAVEPACKET_CONVERSATION_H__
00236