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