aesop-client.cpp

Go to the documentation of this file.
00001 /*
00002  * aesop-client.cpp
00003  *
00004  * Copyright (C) 2008-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  * See aesop-client.h
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "aesop-client.h"               // always include our own header first!
00035 
00036 #include "object-sync.h"
00037 
00038 // REMINDER: no rendering or input dependencies allowed!
00039 #include "common/wave_ex.h"
00040 #include "conversation/conversation.h"
00041 #include "aesop-map/map.h"
00042 #include "aesop-proto/message.h"
00043 #include "aesop-proto/protocol.h"       // network protocol
00044 #include "crypto/crypto.h"
00045 #include "datahash/datahash_text.h"
00046 #include "datahash/datahash_util.h"
00047 #include "dialog/request.h"
00048 #include "map-manager/map-manager.h"
00049 #include "netrq/netrq.h"                // network request queue
00050 #include "perf/perf.h"
00051 #include "story/story.h"
00052 #include "typeinst/typeinst.h"
00053 #include "xdrbuf/xdrbuf.h"
00054 
00055 
00056 namespace aesop {
00057 
00058 
00059 /// \ingroup aesop_clib
00060 /*@{*/
00061 
00062 
00063 typedef netlib::conn_id_t conn_id_t;
00064 typedef netlib::envelope_t envelope_t;
00065 typedef netlib::MessageBuffer MessageBuffer;
00066 
00067 static const int s_maxIdLength                  = 32;
00068 
00069 // countdowns to keep network traffic sane
00070 static const int s_bigNumber                    = 32000;
00071 static const int s_smallNumber                  = 2;
00072 
00073 static int s_authCountdown                      = 0;
00074 
00075 static const float s_maxTimeDelta               = 0.1;  // max dt
00076 static const float s_minTimeDelta               = 1.0e-4; // 0.05ms
00077 
00078 // outgoing UDP message buffer size (public key is ~400 bytes!)
00079 static const int s_udpOutSize                   = 824;
00080 
00081 
00082 // interface class destructors
00083 ClientHost::~ClientHost(void) throw() { }
00084 Client::~Client(void) throw() { }
00085 
00086 
00087 // every player has one of these...
00088 struct client_player_record_t {
00089         // constructor, manipulators
00090         client_player_record_t(void) throw() { this->clear(); }
00091         void clear(void) throw() {
00092                         objectId = 0;
00093                         delta.clear();
00094                         euler.clear();
00095                 }
00096 
00097         // data fields
00098         dword_t         objectId;          // object this player is bound to
00099         point3d_t       delta;             // request to move by this delta
00100         point3d_t       euler;             // euler rotation (orientation)
00101 };
00102 
00103 
00104 
00105 static const float s_minStateMessageWaitTime            = 0.5;
00106 static const float s_maxStateMessageWaitTime            = 3.0;
00107 
00108 
00109 typedef std::map<int, smart_ptr<client_player_record_t> > player_map_t;
00110 
00111 
00112 // loaded maps
00113 typedef std::map<std::string, smart_ptr<Map> > id_map_t;
00114 
00115 
00116 // conversation notes
00117 struct converse_rec_t {
00118         // constructor, manipulators
00119         converse_rec_t(void) throw() { this->clear(); }
00120         void clear(void) throw() {
00121                         dialogId = 0;
00122                         playerId = 0;
00123                         connId = 0;
00124                 }
00125 
00126         // data fields
00127         int                     dialogId;
00128         int                     playerId;
00129         conn_id_t               connId;
00130 };
00131 
00132 typedef std::map<std::string, smart_ptr<converse_rec_t> > conversation_map_t;
00133 
00134 
00135 ////////////////////////////////////////////////////////////////////////////////
00136 //
00137 //      static helper methods
00138 //
00139 ////////////////////////////////////////////////////////////////////////////////
00140 
00141 static const char *
00142 getServerHashKey
00143 (
00144 IN const server_info_t& si,
00145 IO char * buffer                // needs to be at least 32 bytes!!
00146 )
00147 {
00148         ASSERT2(si.isValid(), "invalid");
00149         ASSERT(buffer, "null");
00150 
00151         const byte_t * pb = si.address.ip.addr;
00152         sprintf(buffer, "%d.%d.%d.%d:%d",
00153             pb[0], pb[1], pb[2], pb[3], si.address.port);
00154 
00155         return buffer;
00156 }
00157 
00158 
00159 
00160 ////////////////////////////////////////////////////////////////////////////////
00161 //
00162 //      Request objects -- these know how to construct requests to the server
00163 //       (actually, they inject request packlets into the larger UDP packet)
00164 //
00165 ////////////////////////////////////////////////////////////////////////////////
00166 
00167 static const char * s_reqIdConnect      =       "connect";
00168 static const char * s_reqIdAuthorize    =       "auth";
00169 
00170 
00171 class QueryObjectRequest: public netrq::Request {
00172 public:
00173         ~QueryObjectRequest(void) throw() { }
00174 
00175         // netrq::Request class interface methods ------------------------------
00176         void write(IN xdrbuf::Output * output) {
00177                         ASSERT(output, "null");
00178         //              DPRINTF("Asking about object %ld", m_objectId);
00179                         output->addInt32Packlet('q',
00180                             (int32_t *) &m_objectId, 1);
00181                 }
00182 
00183         const char * getId(void) const throw() { return m_reqId; }
00184 
00185         int getMaxBytes(void) const throw() {
00186                         return 4 + 2;   // int32 plus header
00187                 }
00188 
00189         // yes, keep resending
00190         int getRetryInterval(void) const throw() { return 24; }
00191 
00192         // static factory methods ----------------------------------------------
00193         static smart_ptr<netrq::Request> create(IN long objectId) {
00194                         ASSERT(objectId, "null");
00195 
00196                         smart_ptr<QueryObjectRequest> local =
00197                             new QueryObjectRequest;
00198                         ASSERT(local, "out of memory");
00199 
00200                         sprintf(local->m_reqId, "qo%ld", objectId);
00201                         local->m_objectId = objectId;
00202 
00203                         return local;
00204                 }
00205 
00206 private:
00207         // private helper methods ----------------------------------------------
00208         QueryObjectRequest(void) throw() { }
00209 
00210         // private data members ------------------------------------------------
00211         dword_t         m_objectId;
00212         char            m_reqId[64];
00213 };
00214 
00215 
00216 
00217 class ConnectContext : public netrq::Request {
00218 public:
00219         ~ConnectContext(void) throw() { }
00220 
00221         void write(IN xdrbuf::Output * output) {
00222                         ASSERT(output, "null");
00223                         output->openPacklet('c');
00224                         int32_t l[3];
00225                         l[0] = eProtocol_Long;
00226                         l[1] = m_token;
00227                         l[2] = m_port;
00228                         output->addInt32Packlet('v', l, 3);
00229                         output->openPacklet('p');
00230                         const char * key = m_key.c_str();
00231                         int len = m_key.length();
00232                         while (len) {
00233                                 int send = len;
00234                                 if (send > 255) {
00235                                         send = 255;
00236                                 }
00237                                 //DPRINTF("Sending %d bytes", send);
00238                                 output->addStringPacklet('a', key, send);
00239                                 key += send;
00240                                 len -= send;
00241                         }
00242                         output->closePacklet('p');
00243                         output->closePacklet('c');
00244                 }
00245 
00246         int getMaxBytes(void) const throw() {
00247                         return 384;
00248                 }
00249 
00250         const char * getId(void) const throw()
00251                         { return s_reqIdConnect; }
00252 
00253         static smart_ptr<netrq::Request> create(IN long token,
00254                                 IN int port,
00255                                 IN const char * publicKey) {
00256                         ASSERT(token, "null");
00257                         ASSERT(port, "null");
00258                         ASSERT(publicKey, "null");
00259 
00260                         smart_ptr<ConnectContext> cc = new ConnectContext;
00261                         ASSERT(cc, "out of memory");
00262 
00263                         cc->m_token = token;
00264                         cc->m_port = port;
00265                         cc->m_key = publicKey;
00266 
00267                         return cc;
00268                 }
00269 
00270 private:
00271         ConnectContext(void) { }
00272 
00273         long            m_token;
00274         int             m_port;         // local UDP port server should use
00275         std::string     m_key;          // public key
00276 };
00277 
00278 
00279 
00280 class AuthorizeContext : public netrq::Request {
00281 public:
00282         AuthorizeContext(IN const char * password) {
00283                         ASSERT(password, "null");
00284                         m_password = password;
00285                 }
00286         ~AuthorizeContext(void) throw() { }
00287 
00288         void write(IN xdrbuf::Output * output) {
00289                         ASSERT(output, "null");
00290 
00291                         output->addStringPacklet('a',
00292                             m_password.c_str(),
00293                             m_password.length());
00294                 }
00295 
00296         int getMaxBytes(void) const throw() {
00297                         return 259 + 2;
00298                 }
00299 
00300         const char * getId(void) const throw()
00301                 { return s_reqIdAuthorize; }
00302 private:
00303         std::string     m_password;     // encrypted
00304 };
00305 
00306 
00307 
00308 ////////////////////////////////////////////////////////////////////////////////
00309 //
00310 //      ClientImpl - class that implements the Client interface
00311 //
00312 ////////////////////////////////////////////////////////////////////////////////
00313 
00314 class ClientImpl : public Client,
00315                         public dialog::Host,
00316                         public converse::ConversationRouter {
00317 public:
00318         ClientImpl(void) throw();
00319         ~ClientImpl(void) throw() { }
00320 
00321         // public class methods ------------------------------------------------
00322         void initialize(IN ClientHost * host,
00323                                 IN smart_ptr<story::Story>& story,
00324                                 IN smart_ptr<Datahash>& params);
00325 
00326         // aesop::Client class interface methods -------------------------------
00327         story::Story * getStory(void) throw() { return m_story; }
00328         eClientState getState(void) throw() { return m_clientState; }
00329         bool getServer(OUT server_info_t& si);
00330         const server_map_t& getDiscoveredServers(void) { return m_servers; }
00331         bool requestConnect(IN const char * serverKey);
00332         float tick(void);
00333         bool createPlayer(IN int playerId);
00334         void startLocalConversation(IN const char * guid,
00335                                 IN int playerId,
00336                                 IN smart_ptr<converse::ConversationHost> host);
00337         bool isConversationUnderway(IN const char * guid);
00338         void newGame(IN int playerId);
00339         void requestMove(IN int playerId,
00340                                 IN const point3d_t& delta,
00341                                 IN const point3d_t& euler,
00342                                 OUT point3d_t& newPosition);
00343         bool getPlacement(IN int playerId,
00344                                 OUT placement_t& placement);
00345         smart_ptr<PhysicsObject>  getPlayerObject(IN int playerId);
00346 
00347         // dialog::Host class interface methods --------------------------------
00348         void notifySubmit(IN const char * id,
00349                                 IN const Datahash * data);
00350 
00351         // converse::ConversationRouter class interface methods ----------------
00352         void routeConversationTS(IN const char * guid,
00353                                 IN int dialogId,
00354                                 IN int playerId,
00355                                 IN conn_id_t connId,
00356                                 IN const char * dialogData);
00357 
00358 private:
00359         // private helper methods ----------------------------------------------
00360         client_player_record_t * getPlayer(IN int playerId);
00361         converse_rec_t * getConversation(IN const char * conversationGuid);
00362         void deleteConversation(IN const char * guid,
00363                                 IN int playerId);
00364         void checkHostState(void);
00365         void handleMessage(IN const envelope_t& envelope,
00366                                 IN const MessageBuffer * msg);
00367         void handleUDP(IN const envelope_t& envelope,
00368                                 IN const MessageBuffer * msg);
00369         void handleBroadcast(IN xdrbuf::PackletHeader ph,
00370                                 IN const envelope_t& env);
00371         void readConnectUDP(IO xdrbuf::Input * input);
00372         void readDestroyUDP(IO xdrbuf::Input * input);
00373         void readPlayerUDP(IO xdrbuf::Input * input);
00374         void readQueryUDP(IN const xdrbuf::PackletHeader& ph,
00375                                 IO xdrbuf::Input * input);
00376         void readObjectUDP(IO xdrbuf::Input * input);
00377         void setKeys(IN const char * encryptedDesKey);
00378         void sendUdpPacket(IN float dt);
00379 
00380         // private data members ------------------------------------------------
00381         ClientHost *                            m_host; // weak reference!
00382         server_map_t                            m_servers; // discovered servers
00383         server_info_t                           m_server; // target server
00384         eClientState                            m_clientState;
00385         conn_id_t                               m_tcpConnID;
00386         conn_id_t                               m_localUdpConnID;
00387         conn_id_t                               m_remoteUdpConnID;
00388         player_map_t                            m_players;
00389         conversation_map_t                      m_conversations;
00390         smart_ptr<converse::ConversationManager> m_conversationMgr;
00391         eHostMode                               m_hMode;
00392         smart_ptr<crypto::RSAKey>               m_rsaKey;       // pub/priv key
00393         smart_ptr<crypto::DESKey>               m_desKey;       // symmetric
00394         bool                                    m_haveProtocol;
00395         int                                     m_localUdpPort;
00396         int                                     m_remoteUdpPort;
00397         int                                     m_serverTcpPort;
00398         dword_t                                 m_udpMostRecentClock; //server
00399         dword_t                                 m_udpClock;     // our udp clock
00400         dword_t                                 m_lastUdpServerSaw;
00401         long                                    m_udpConnToken;
00402         id_map_t                                m_maps;
00403         smart_ptr<xdrbuf::Input>                m_udpIn;
00404         smart_ptr<xdrbuf::Output>               m_udpOut;
00405         smart_ptr<ObjectSync>                   m_syncMgr;
00406         smart_ptr<MapManager>                   m_mapMgr;
00407         smart_ptr<story::Story>                 m_story;
00408         smart_ptr<netrq::Queue>                 m_netQueue;
00409         perf::time_t                            m_lastTime;
00410         float                                   m_lastdt;     // last time delta
00411         float                                   m_lastUdpSent;
00412 };
00413 
00414 
00415 
00416 ClientImpl::ClientImpl(void) throw()
00417 {
00418         m_host = NULL;  // weak reference -- do not delete!
00419         m_server.clear();       // not connected yet
00420         m_clientState = eClientState_Invalid;
00421         m_tcpConnID = 0;
00422         m_localUdpConnID = 0;
00423         m_remoteUdpConnID = 0;
00424         m_haveProtocol = false;
00425         m_localUdpPort = 0;
00426         m_remoteUdpPort = 0;
00427         m_serverTcpPort = 0;
00428         m_hMode = eHostMode_Invalid;
00429         m_udpMostRecentClock = 0;
00430         m_udpClock = 0;
00431         m_lastUdpServerSaw = 0;
00432         m_udpConnToken = 0;
00433         m_lastdt = 0.0;
00434         m_lastUdpSent = 0.0;
00435 }
00436 
00437 
00438 
00439 void
00440 ClientImpl::initialize
00441 (
00442 IN ClientHost * host,
00443 IN smart_ptr<story::Story>& story,
00444 IN smart_ptr<Datahash>& params
00445 )
00446 {
00447         ASSERT(host, "null");
00448         ASSERT(story, "null");
00449         ASSERT(params, "null");
00450 
00451         // save host
00452         ASSERT(!m_host, "already have a host?");
00453         m_host = host;
00454 
00455         // save story object
00456         ASSERT(!m_story, "already have a story?");
00457         m_story = story;
00458 
00459         // set up our outgoing UDP buffer
00460         ASSERT(s_udpOutSize > 31 && s_udpOutSize < 1025,
00461             "Bad udp output buffer size: %d", s_udpOutSize);
00462         ASSERT(!m_udpOut, "Already have a udp output buffer?");
00463         m_udpOut = xdrbuf::Output::create(s_udpOutSize);
00464         ASSERT(m_udpOut, "failed to create xdr output buffer");
00465 
00466         // set up incoming UDP buffer
00467         ASSERT(!m_udpIn, "Already have a udp input buffer?");
00468         m_udpIn = xdrbuf::Input::create();
00469         ASSERT(m_udpIn, "Failed to create xdr input parser");
00470 
00471         // get local UDP port
00472         m_localUdpPort = eDefaultPort_clientUdp;
00473         DPRINTF("Default client UDP port: %d", m_localUdpPort);
00474         const char * udpOverride =
00475             getString(params, "udpPort", eDatahash_Optional);
00476         if (udpOverride) {
00477                 DPRINTF("Overriding client UDP port: %s", udpOverride);
00478                 m_localUdpPort = atoi(udpOverride);
00479         }
00480         if (m_localUdpPort <= 0) {
00481                 WAVE_EX(wex);
00482                 wex << "Bad local UDP port: " << m_localUdpPort;
00483         }
00484 
00485         // construct RSA key (public/private keys)
00486         m_rsaKey = crypto::RSAKey::create();
00487         ASSERT(m_rsaKey, "failed to create RSA key");
00488 
00489         // construct a network request queue
00490         ASSERT(!m_netQueue, "already have a network request queue?");
00491         m_netQueue = netrq::Queue::create();
00492         ASSERT(m_netQueue, "failed to create network request queue");
00493 
00494         // create a local UDP connection for sending and receiving
00495         netlib::address_t udpAddress;
00496         udpAddress.setlocal(m_localUdpPort);
00497         udpAddress.dump("udp address");
00498         m_localUdpConnID = netlib::createUdpLocal(udpAddress);
00499         ASSERT(m_localUdpConnID, "failed to create local udp port?");
00500 
00501         // create map manager
00502         ASSERT(!m_mapMgr, "already have a map manager?");
00503         m_mapMgr = MapManager::create(params, m_story);
00504         ASSERT(m_mapMgr, "null");
00505 
00506         // create synchronization manager
00507         ASSERT(!m_syncMgr, "already have an object synchronization manager?");
00508         m_syncMgr = ObjectSync::create(m_mapMgr);
00509         ASSERT(m_syncMgr, "null");
00510 
00511         // create local conversation manager
00512         ASSERT(!m_conversationMgr, "Already have a conversation manager?");
00513         m_conversationMgr = converse::ConversationManager::create(this);
00514         ASSERT(m_conversationMgr, "null");
00515 
00516         // initialize type instance library
00517         initializeTypeInstanceLibrary(m_story);
00518 
00519         // okay, all set
00520         m_clientState = eClientState_Searching;
00521 }
00522 
00523 
00524 
00525 ////////////////////////////////////////////////////////////////////////////////
00526 //
00527 //      ClientImpl -- aesop::Client class interface methods
00528 //
00529 ////////////////////////////////////////////////////////////////////////////////
00530 
00531 bool
00532 ClientImpl::getServer
00533 (
00534 OUT server_info_t& si
00535 )
00536 {
00537         si = m_server;
00538         return si.isValid();
00539 }
00540 
00541 
00542 
00543 bool
00544 ClientImpl::requestConnect
00545 (
00546 IN const char * serverKey
00547 )
00548 {
00549         ASSERT(serverKey, "null");
00550 
00551         // already connected?
00552         if (m_server.isValid()) {
00553                 DPRINTF("Ignoring request to connect: we are connected!");
00554                 return false;
00555         }
00556 
00557         DPRINTF("Player wants this server: %s", serverKey);
00558         m_server.clear();
00559         bool found = m_servers.lookup(serverKey, m_server);
00560         if (!found) {
00561                 DPRINTF("No such server: %s", serverKey);
00562                 return false;
00563         }
00564 
00565         // okay, set up udp connection
00566         ASSERT_THROW(!m_remoteUdpConnID,
00567             "Not connected but have a remote UDP connection?");
00568         m_remoteUdpConnID =
00569             netlib::createUdpRemote(m_localUdpConnID, m_server.address);
00570         ASSERT(m_remoteUdpConnID, "failed to create remote udp connection");
00571 
00572         // all good
00573         return true;
00574 }
00575 
00576 
00577 
00578 float
00579 ClientImpl::tick
00580 (
00581 void
00582 )
00583 {
00584         // need to send anything to server?
00585         this->checkHostState();
00586 
00587         // tick all conversations
00588         m_conversationMgr->updateAll();
00589 
00590         // receive any messages from server
00591         // (and call getNextMessage() which is core message pump)
00592         envelope_t env;
00593         smart_ptr<MessageBuffer> msg;
00594         long microseconds = 500;                // 500us == 0.5ms
00595         try {
00596                 usleep(10 * microseconds);
00597                 if (netlib::getNextMessage(microseconds, env, msg)) {
00598                         this->handleMessage(env, msg);
00599                 }
00600         } catch (std::exception& e) {
00601                 netlib::dumpMessage(std::cerr, "Error handling this message",
00602                     env, msg);
00603                 DPRINTF("Exception: %s", e.what());
00604         }
00605 
00606         // get elapsed time
00607         perf::time_t now = perf::getNow();
00608         perf::time_t delta = now;
00609         delta.decrement(m_lastTime);
00610         m_lastTime = now;
00611         float dt = (float) delta.getSeconds();
00612         if (dt > s_maxTimeDelta) {
00613                 dt = s_maxTimeDelta;
00614         } else if (dt <= s_minTimeDelta) {
00615                 // set to very small but positive amount
00616                 dt = s_minTimeDelta;
00617         }
00618         m_lastdt = dt;  // remember this...
00619 
00620         // tick maps!
00621         m_mapMgr->tickMaps(dt);
00622 
00623         // send UDP packet 
00624         // NOTE: this assumes tick() isn't being called too frequently!
00625         m_lastUdpSent += dt;
00626         const float s_sendHz = 50.0;    // TODO: config-driven send rate!
00627         const float s_sendRate = 1.0 / s_sendHz;
00628         if (m_lastUdpSent > s_sendRate) {
00629                 this->sendUdpPacket(m_lastUdpSent);
00630                 m_lastUdpSent = 0.0;
00631         }
00632 
00633         // update local object state
00634         if (m_udpClock)
00635                 m_syncMgr->clientUpdate(m_udpClock);
00636 
00637         // return elapsed time to callers
00638         return dt;
00639 }
00640 
00641 
00642 
00643 bool
00644 ClientImpl::createPlayer
00645 (
00646 IN int playerId
00647 )
00648 {
00649         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00650 
00651         // already exists?
00652         if (this->getPlayer(playerId)) {
00653                 // already exists!
00654                 return false;
00655         }
00656 
00657         // okay, create
00658         DPRINTF("New client player");
00659         smart_ptr<client_player_record_t> player = new client_player_record_t;
00660         ASSERT(player, "out of memory");
00661 
00662         m_players[playerId] = player;
00663         ASSERT(2 == player.get_ref_count(), "Bad count");
00664 
00665         // all done
00666         return true;
00667 }
00668 
00669 
00670 
00671 void
00672 ClientImpl::startLocalConversation
00673 (
00674 IN const char * guid,
00675 IN int playerId,
00676 IN smart_ptr<converse::ConversationHost> host
00677 )
00678 {
00679         ASSERT(guid, "null");
00680         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00681         ASSERT(host, "null");
00682         ASSERT(m_conversationMgr, "null");
00683 
00684         // already have this?
00685         if (this->getConversation(guid)) {
00686                 return;         // conversation is already underway
00687         }
00688 
00689         // create conversation record
00690         smart_ptr<converse_rec_t> cr = new converse_rec_t;
00691         ASSERT(cr, "out of memory");
00692 
00693         cr->playerId = playerId;
00694         cr->connId = 0;         // local conversation
00695         cr->dialogId = host->getCurrentDialogIdTS();
00696         m_conversations[guid] = cr;
00697 
00698         // create conversation with null connection ID to denote local
00699         m_conversationMgr->updateConversationTS(guid, 0, playerId, host);
00700 }
00701 
00702 
00703 
00704 bool
00705 ClientImpl::isConversationUnderway
00706 (
00707 IN const char * guid
00708 )
00709 {
00710         ASSERT(guid, "null");
00711 
00712         // already have this?
00713         if (this->getConversation(guid))
00714                 return true;
00715         return false;
00716 }
00717 
00718 
00719 
00720 void
00721 ClientImpl::newGame
00722 (
00723 IN int playerId
00724 )
00725 {
00726         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00727 
00728         DPRINTF("Requesting new game!");
00729 
00730         smart_ptr<netlib::MessageBuffer> msg = createNewGameMessage(playerId);
00731         ASSERT(msg, "null");
00732 
00733         netlib::enqueueMessage(m_tcpConnID, msg);
00734 }
00735 
00736 
00737 
00738 void
00739 ClientImpl::requestMove
00740 (
00741 IN int playerId,
00742 IN const point3d_t& delta,
00743 IN const point3d_t& euler,
00744 OUT point3d_t& newPosition
00745 )
00746 {
00747         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00748 
00749         client_player_record_t * prec = this->getPlayer(playerId);
00750         if (!prec) {
00751                 // this shouldn't happen!
00752                 DPRINTF("Unrecognized player id: %d", playerId);
00753                 return;
00754         }
00755         if (!prec->objectId) {
00756                 // happens a lot!  when joining, between maps, etc
00757                 // DPRINTF("Player is not yet bound to an object");
00758                 return;
00759         }
00760 
00761         // let state manager know
00762         if (!m_syncMgr->clientMoveRequest(prec->objectId, delta, m_lastdt,
00763             newPosition)) {
00764                 DPRINTF("Object ID not found? %lu", (long) prec->objectId);
00765         }
00766 
00767         // update record with player's wish
00768         prec->delta += delta;   // accumulate requested moves
00769         prec->euler = euler;    // always honor last known orientation
00770 }
00771 
00772 
00773 
00774 bool
00775 ClientImpl::getPlacement
00776 (
00777 IN int playerId,
00778 OUT placement_t& placement
00779 )
00780 {
00781         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00782         placement.clear();
00783 
00784         client_player_record_t * prec = this->getPlayer(playerId);
00785         if (!prec || !prec->objectId) {
00786                 return false;
00787         }
00788 
00789         // get position from object synchronizer
00790         object_state_t state;
00791         if (!m_syncMgr->getState(prec->objectId, state) ||
00792             !state.physicsObject) {
00793                 return false;
00794         }
00795         placement.position = state.physicsObject->getPosition();
00796 
00797         // TODO: could get euler angles from physics object as well!
00798         // For now, we purposely don't trust the physics engine for that.
00799         // get euler angles from our local copy
00800         placement.rotation.setEulerZYX(prec->euler);
00801 
00802         // all done
00803         return true;
00804 }
00805 
00806 
00807 
00808 smart_ptr<PhysicsObject>
00809 ClientImpl::getPlayerObject
00810 (
00811 IN int playerId
00812 )
00813 {
00814         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00815 
00816         client_player_record_t * prec = this->getPlayer(playerId);
00817         if (!prec || !prec->objectId) {
00818         //      DPRINTF("No player object?");
00819                 return NULL;
00820         }
00821 
00822         // okay, we have our object ID.  But this is the object ID on the
00823         //      server!  need to get local physics object...
00824         ASSERT(m_syncMgr, "null");
00825         object_state_t os;
00826         if (!m_syncMgr->getState(prec->objectId, os))
00827                 return NULL;
00828 
00829         return os.physicsObject;
00830 }
00831 
00832 
00833 
00834 ////////////////////////////////////////////////////////////////////////////////
00835 //
00836 //      ClientImpl -- private helper methods
00837 //
00838 ////////////////////////////////////////////////////////////////////////////////
00839 
00840 void
00841 ClientImpl::notifySubmit
00842 (
00843 IN const char * id,
00844 IN const Datahash * data
00845 )
00846 {
00847         ASSERT(id, "null");
00848         ASSERT(data, "null");
00849 
00850         DPRINTF("Received submit for conversation: %s", id);
00851 
00852         // look up this conversation
00853         converse_rec_t * cr = this->getConversation(id);
00854         if (!cr) {
00855                 DPRINTF("Received submit for unknown conversation?");
00856                 DPRINTF("  id='%s'", id);
00857                 return;
00858         }
00859 
00860         // local?  Then we handle all the forwarding here
00861         if (!cr->connId) {
00862                 DPRINTF("  Local conversation!");
00863                 ASSERT(m_conversationMgr, "null");
00864 
00865                 m_conversationMgr->handleDialogReplyTS(id, cr->dialogId,
00866                     0, cr->playerId, data);
00867 
00868                 return;
00869         }
00870 
00871         // not local!  Need to tell server about the reply
00872         // construct reply
00873         std::ostringstream reply;
00874         writeHashToStream(data, reply);
00875 
00876         DPRINTF("Constructed reply:\n%s", reply.str().c_str());
00877 
00878         // tell the server!
00879         smart_ptr<MessageBuffer> msg =
00880             createConversationReplyMessage(cr->playerId, id, cr->dialogId,
00881             reply.str().c_str());
00882 
00883         netlib::enqueueMessage(m_tcpConnID, msg);
00884 }
00885 
00886 
00887 
00888 ////////////////////////////////////////////////////////////////////////////////
00889 //
00890 //      ClientImpl -- converse::ConversationRouter class interface methods
00891 //
00892 ////////////////////////////////////////////////////////////////////////////////
00893 
00894 void
00895 ClientImpl::routeConversationTS
00896 (
00897 IN const char * guid,
00898 IN int dialogId,
00899 IN int playerId,
00900 IN conn_id_t connId,
00901 IN const char * dialogData
00902 )
00903 {
00904         ASSERT(guid, "null");
00905         ASSERT(dialogId >= 0, "Bad dialog id: %d", dialogId);
00906         ASSERT(playerId > 0, "bad player id: %d", playerId);
00907         ASSERT(!connId, "Should only be used for local conversations!");
00908         ASSERT(dialogData, "null");
00909         ASSERT(m_host, "null");
00910         ASSERT(m_conversationMgr, "null");
00911 
00912         //DPRINTF("Routing conversation '%s', dialog=%d", guid, dialogId);
00913 
00914         // end of conversation?
00915         if (!dialogId) {
00916                 DPRINTF("Destroying this conversation/dialog: %s", guid);
00917                 this->deleteConversation(guid, playerId);
00918                 return;
00919         }
00920 
00921         // update our view of dialog ID
00922         converse_rec_t * cr = this->getConversation(guid);
00923         if (!cr) {
00924                 DPRINTF("Unable to find conversation: '%s'", guid);
00925         } else {
00926                 cr->dialogId = dialogId;
00927         }
00928 
00929         // construct datahash with request
00930         std::istringstream iss(dialogData);
00931         smart_ptr<Datahash> hash = readHashFromStream("local dialog", iss);
00932 
00933         // forward dialog request
00934         m_host->requestDialog(guid, playerId, hash, this);
00935 }
00936 
00937 
00938 
00939 ////////////////////////////////////////////////////////////////////////////////
00940 //
00941 //      ClientImpl -- private helper methods
00942 //
00943 ////////////////////////////////////////////////////////////////////////////////
00944 
00945 client_player_record_t *
00946 ClientImpl::getPlayer
00947 (
00948 IN int id
00949 )
00950 {
00951         ASSERT(id > 0, "Bad input id: %d", id);
00952 
00953         player_map_t::iterator i = m_players.find(id);
00954         if (m_players.end() == i) {
00955                 //DPRINTF("No player with id=%d", id);
00956                 return NULL;
00957         }
00958         ASSERT(i->second, "null player in map");
00959 
00960         return i->second;
00961 }
00962 
00963 
00964 
00965 converse_rec_t *
00966 ClientImpl::getConversation
00967 (
00968 IN const char * guid
00969 )
00970 {
00971         ASSERT(guid, "null");
00972 
00973         conversation_map_t::iterator i = m_conversations.find(guid);
00974         if (m_conversations.end() == i) {
00975                 return NULL;            // conversation not found
00976         }
00977 
00978         return i->second;
00979 }
00980 
00981 
00982 
00983 void
00984 ClientImpl::deleteConversation
00985 (
00986 IN const char * guid,
00987 IN int playerId
00988 )
00989 {
00990         ASSERT(guid, "null");
00991         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00992 
00993         conversation_map_t::iterator i = m_conversations.find(guid);
00994         if (m_conversations.end() == i) {
00995                 DPRINTF("Cannot delete unknown conversation: %s", guid);
00996                 return;
00997         }
00998 
00999         // real conversation!  Tell host
01000         ASSERT(m_host, "null");
01001         m_host->destroyDialog(guid, playerId);
01002 
01003         // clear map
01004         m_conversations.erase(guid);
01005 }
01006 
01007 
01008 // doxygen comment block
01009 /// \ingroup aesop_clib
01010 /// \defgroup host_connect Host Connection Strategy
01011 ///
01012 /// \n
01013 /// Don't think of host connection as a one-time setup.  Instead, each
01014 /// client tries to maintain a "fully connected" state.  If a client
01015 /// finds that it isn't in the state it needs to be, it will communicate
01016 /// with the server to build up the "fully connected" state.  This way,
01017 /// dealing with dropped connections is straightforward (just reconnect
01018 /// and rebuild state when the network comes back), and it is the same
01019 /// code path as the initial connection.
01020 
01021 
01022 void
01023 ClientImpl::checkHostState
01024 (
01025 void
01026 )
01027 {
01028         //DPRINTF("mode = %d", m_hMode);
01029 
01030         // walk through and assemble basic state
01031         ASSERT(eClientState_Invalid != m_clientState,
01032             "why is state invalid now?");
01033 
01034         // are we connected?
01035         if (m_remoteUdpConnID) {
01036                 // yes!  (or at least connecting...)
01037                 m_clientState = eClientState_Connecting;
01038         } else {
01039                 m_clientState = eClientState_Searching;
01040                 return;
01041         }
01042 
01043         // walk through our host state, and see if we need to enqueue messages
01044         if (eHostMode_Invalid == m_hMode) {
01045                 // DPRINTF("Server doesn't know about us!");
01046                 // server doesn't know anything about us!
01047                 // are we already attempting to connect?
01048                 if (m_netQueue->containsRequest(s_reqIdConnect))
01049                         return; // yup, keep trying
01050 
01051                 // need to attempt to connect!  get our public key
01052                 // DPRINTF("Attempting to connect to server...");
01053                 ASSERT(m_rsaKey, "null");
01054                 smart_ptr<crypto::RSAPublicKey> pubkey =
01055                     m_rsaKey->getPublicKey();
01056                 ASSERT(pubkey, "null");
01057                 std::string publicKey = pubkey->serialize();
01058 
01059                 // create request context
01060                 long token = m_remoteUdpConnID; // use remote UDP conn ID
01061                 smart_ptr<netrq::Request> rc =
01062                     ConnectContext::create(token, m_localUdpPort,
01063                         publicKey.c_str());
01064                 ASSERT(rc, "null");
01065 
01066                 // add to network request queue
01067                 m_netQueue->addRequest(m_udpClock + 1, rc);
01068                 return;
01069         } else if (eHostMode_Connected == m_hMode && m_desKey) {
01070                 DPRINTF("Connected!  Now need to authorize...");
01071 
01072                 // need to authorize
01073                 ASSERT(m_desKey, "null");
01074 
01075                 // already attempting to authorize?
01076                 if (s_authCountdown) {
01077                         --s_authCountdown;
01078                         return;
01079                 }
01080                 s_authCountdown = 10;
01081 
01082                 // TODO: allow client to specify password
01083                 DPRINTF("Sending host level password...");
01084                 std::string encryptedPassword = m_desKey->encrypt("", 12);
01085                 DPRINTF("Encrypted password: %s", encryptedPassword.c_str());
01086                 smart_ptr<netrq::Request> rc =
01087                     new AuthorizeContext(encryptedPassword.c_str());
01088                 m_netQueue->addRequest(m_udpClock + 1, rc);
01089                 return;
01090         } else if (eHostMode_Authorized == m_hMode) {
01091                 // looks good--make sure we have no auth requests pending
01092                 // m_netQueue->removeRequest(s_reqIdAuthorize);
01093         }
01094 
01095         // don't do anything else unless host is authorized
01096         if (eHostMode_Authorized != m_hMode)
01097                 return;
01098 
01099         // need to set up TCP connection?
01100         if (m_udpConnToken && !m_tcpConnID && m_serverTcpPort) {
01101                 ASSERT2(m_server.isValid(), "Invalid server");
01102                 netlib::address_t address = m_server.address;
01103                 address.port = m_serverTcpPort;
01104                 address.dump("Attempting TCP connection here");
01105                 m_tcpConnID = netlib::createTcpConnection(address);
01106                 ASSERT2(m_tcpConnID, "Failed to create TCP connection");
01107 
01108                 // must provide the (UDP) token when making TCP connections
01109                 smart_ptr<netlib::MessageBuffer> msg =
01110                     createTcpConnectMessage(m_udpConnToken);
01111                 ASSERT(msg, "failed to create tcp connect message");
01112                 netlib::enqueueMessage(m_tcpConnID, msg);
01113         }
01114 
01115         // connected?
01116         if (m_udpConnToken && m_tcpConnID && m_remoteUdpConnID) {
01117                 m_clientState = eClientState_Connected;
01118         }
01119 }
01120 
01121 
01122 
01123 void
01124 ClientImpl::handleMessage
01125 (
01126 IN const envelope_t& env,
01127 IN const MessageBuffer * msg
01128 )
01129 {
01130         ASSERT(!env.is_empty(), "empty");
01131         ASSERT(msg, "null");
01132 
01133         // TODO: need a proper dispatch map here!
01134         if (netlib::eType_UDPRemote == env.type) {
01135                 this->handleUDP(env, msg);
01136                 return;
01137         }
01138 
01139         // TCP?  get payload
01140         tcp_payload_t payload;
01141         getTcpPayload(msg->getData(), payload);
01142         ASSERT(!payload.is_empty(), "empty");
01143 
01144         // dispatch based on type of message...
01145         const char * namespace_ = payload.namespace_.c_str();
01146         ASSERT(namespace_, "null");
01147         if (strcmp("client", namespace_)) {
01148                 DPRINTF("Invalid message namespace: '%s'", namespace_);
01149                 return;
01150         }
01151 
01152         const char * command = payload.command.c_str();
01153         ASSERT(command, "null");
01154         const Datahash * data = payload.arguments;
01155         ASSERT(data, "null");
01156 
01157         if (!strcmp(command, "converse-dialog")) {
01158                 DPRINTF("Got a dialog request!");
01159                 smart_ptr<Datahash> dialog = getSubhash(data, "dialog");
01160                 int playerId = getInt(data, "playerId");
01161                 if (playerId < 1) {
01162                         WAVE_EX(wex);
01163                         wex << "Bad player id: " << playerId;
01164                 }
01165                 std::string conGuid = getString(data, "conversationGuid");
01166                 int dialogId = getInt(data, "dialogId");
01167                 if (dialogId <= 0) {
01168                         WAVE_EX(wex);
01169                         wex << "Bad dialog id: " << dialogId;
01170                 }
01171 
01172                 // add to our conversation map
01173                 smart_ptr<converse_rec_t> cr = new converse_rec_t;
01174                 ASSERT(cr, "out of memory");
01175                 cr->dialogId = dialogId;
01176                 cr->playerId = playerId;
01177                 cr->connId = m_tcpConnID;
01178                 m_conversations[conGuid] = cr;
01179 
01180                 // tell host to display
01181                 m_host->requestDialog(conGuid.c_str(), playerId, dialog, this);
01182         } else if (!strcmp(command, "converse-end")) {
01183                 DPRINTF("Got a request to end a conversation");
01184                 std::string guid = getString(data, "conversationGuid");
01185                 int playerId = getInt(data, "playerId");
01186                 if (playerId < 1) {
01187                         WAVE_EX(wex);
01188                         wex << "Bad player id: " << playerId;
01189                 }
01190 
01191                 // destroy from map
01192                 this->deleteConversation(guid.c_str(), playerId);
01193 
01194         } else if (!strcmp(command, "error")) {
01195                 DPRINTF("Received error from server!");
01196                 eErrorCode code = (eErrorCode) getLong(data, "errorCode");
01197                 DPRINTF("  Error code: 0x%05x", code);
01198                 std::string message = getString(data, "message");
01199                 DPRINTF("  Message: %s", message.c_str());
01200                 if (eErrorCode_Fatal & code) {
01201                         ASSERT(false, "Got fatal error, stopping: %s",
01202                             message.c_str());
01203                 }
01204 
01205         } else {
01206                 DPRINTF("Unknown command: '%s'", command);
01207         }
01208 }
01209 
01210 
01211 
01212 void
01213 ClientImpl::handleUDP
01214 (
01215 IN const envelope_t& env,
01216 IN const MessageBuffer * msg
01217 )
01218 {
01219         perf::Timer("clib::handleUDP");
01220         ASSERT(!env.is_empty(), "empty");
01221         ASSERT(msg, "null");
01222 
01223 //      DPRINTF("Received %ld bytes from UDP", msg->getBytes());
01224 
01225         // resurrect as XDR input buffer
01226         ASSERT(m_udpIn, "should have input buffer now");
01227         m_udpIn->reset((const byte_t *) msg->getData(), msg->getBytes());
01228 
01229         // grab header
01230         xdrbuf::PackletHeader ph = m_udpIn->getNextPackletHeader();
01231 
01232         // is this a broadcast packet?
01233         if ('s' == ph.getName()) {
01234                 this->handleBroadcast(ph, env);
01235                 return;
01236         }
01237 
01238         // not a broadcast packet?  Assume it is a standard state packet
01239         if ('h' != ph.getName()) {
01240                 DPRINTF("Malformed input UDP packet?");
01241                 return;
01242         }
01243 
01244         // are we even allowed to handle UDP packets?
01245         if (eClientState_Connected != m_clientState &&
01246             eClientState_Connecting != m_clientState) {
01247                 DPRINTF("Ignoring incoming UDP packets");
01248                 return;
01249         }
01250 
01251         // header is 6 longs right now
01252         //   1: token
01253         //   2: server clock
01254         //   3: last client udp clock the server received
01255         //   4: server state
01256         //   5: host state
01257         //   6: server TCP listening port
01258         int32_t l[6];
01259         m_udpIn->readInt32s(l, 6);
01260 
01261         // verify that sender is legit
01262         dword_t token = l[0];
01263         netlib::connection_info_t ci;
01264         if (!netlib::getConnectionInfo(token, ci)) {
01265                 DPRINTF("Input udp connection token is invalid");
01266                 return;
01267         }
01268         if (ci.address.ip != env.address.ip) {
01269                 DPRINTF("UDP sender does not match connection token");
01270                 DPRINTF("From token:");
01271                 ci.address.dump("  our record");
01272                 DPRINTF("From envelope:");
01273                 env.address.dump("  msg record");
01274                 return;
01275         }
01276 
01277         // appears to be a legit UDP packet from server!
01278 
01279         // is this a stale clock?
01280         dword_t clock = l[1];
01281         if (clock < m_udpMostRecentClock) {
01282                 DPRINTF("Message is old!");
01283                 return;
01284         }
01285         m_udpMostRecentClock = clock;
01286 //      DPRINTF("Most recent clock: %lu", m_udpMostRecentClock);
01287 
01288         // what was the last message of ours the server saw?
01289         m_lastUdpServerSaw = l[2];
01290         dword_t delta = m_udpClock - m_lastUdpServerSaw;
01291         if (delta > 3) {
01292                 DPRINTF("Server is %lu messages behind!", (long) delta);
01293         }
01294 
01295         // update local state based on this state from server
01296         eServerMode smode = (eServerMode) l[3];
01297         ASSERT(eServerMode_Idle == smode, "mode: %d", smode);
01298         m_hMode = (eHostMode) l[4];
01299 
01300         // remember server's TCP port
01301         if (m_serverTcpPort != l[5]) {
01302                 DPRINTF("Got new server TCP port!");
01303                 if (m_tcpConnID) {
01304                         netlib::closeConnection(m_tcpConnID);
01305                         m_tcpConnID = 0;
01306                 }
01307                 m_serverTcpPort = l[5];
01308         }
01309 
01310         // parse packlets
01311         while (!m_udpIn->endOfStream()) {
01312                 xdrbuf::PackletHeader ph = m_udpIn->getNextPackletHeader();
01313                 char name = ph.getName();
01314 //              DPRINTF("  Found packlet with name '%c'", name);
01315 
01316                 switch (name) {
01317 
01318                 case 'c':
01319                         this->readConnectUDP(m_udpIn);
01320                         break;
01321 
01322                 case 'd':
01323                         this->readDestroyUDP(m_udpIn);
01324                         break;
01325 
01326                 case 'o':
01327                         this->readObjectUDP(m_udpIn);
01328                         break;
01329 
01330                 case 'p':
01331                         this->readPlayerUDP(m_udpIn);
01332                         break;
01333 
01334                 case 'q':
01335                         this->readQueryUDP(ph, m_udpIn);
01336                         break;
01337 
01338                 default:
01339                         {
01340                                 WAVE_EX(wex);
01341                                 wex << "Unknown packlet name: '" << name << "'";
01342                         }
01343                 }
01344         }
01345 }
01346 
01347 
01348 
01349 void
01350 ClientImpl::handleBroadcast
01351 (
01352 IN xdrbuf::PackletHeader ph,
01353 IN const envelope_t& env
01354 )
01355 {
01356 //      DPRINTF("Handing UDP broadcast");
01357 
01358         const int bufsize = 255;
01359         char buffer[bufsize + 1];
01360 
01361         // construct local server info object
01362         server_info_t si;
01363         std::string uuid;
01364         int port = 0;
01365 
01366         // NOTE: we assume the caller already read the first packlet header, and
01367         //      that's what was passed in.
01368         bool firstPass = true;
01369 
01370         // parse packlets
01371         while (!m_udpIn->endOfStream()) {
01372                 char name = 's';
01373                 if (!firstPass) {
01374                         ph = m_udpIn->getNextPackletHeader();
01375                         name = ph.getName();
01376                 }
01377                 firstPass = false;
01378         //      DPRINTF("  Found packlet with name '%c'", name);
01379 
01380                 switch (name) {
01381                 case 's':               // public server name
01382                         {
01383                                 int len = ph.getDataCount();
01384                                 ASSERT2(len <= bufsize, "server public name is "
01385                                     << "too big: " << len << " characters");
01386                                 m_udpIn->readString(buffer, len);
01387                                 buffer[len] = 0;        // null terminate
01388                                 si.publicName = buffer;
01389                         }
01390                         break;
01391 
01392                 case 'u':               // story UUID
01393                         {
01394                                 int len = ph.getDataCount();
01395                                 ASSERT2(len <= bufsize, "server UUID is "
01396                                     << "too big: " << len << " characters");
01397                                 m_udpIn->readString(buffer, len);
01398                                 buffer[len] = 0;        // null terminate
01399                                 uuid = buffer;
01400                         }
01401                         break;
01402 
01403                 case 'p':               // server UDP port
01404                         {
01405                                 int32_t i32;
01406                                 m_udpIn->readInt32s(&i32, 1);
01407                                 port = i32;
01408                         }
01409                         break;
01410 
01411                 default:
01412                         {
01413                                 // got something we didn't recognize?  Abandon
01414                                 //      parsing, but don't throw.  Could be
01415                                 //      garbage broadcasts on network.
01416                                 DPRINTF("Unknown packlet in broadcast: '%c'",
01417                                     name);
01418                                 return;         // just return
01419                         }
01420                 }
01421         }
01422 
01423         // did we get a valid server UDP port?
01424         if (port < 1) {
01425                 DPRINTF("Unspecified or invalid port: %d", port);
01426                 return;
01427         }
01428 
01429         // set up address
01430         si.address = env.address;
01431         si.address.port = port;
01432         if (!si.isValid()) {
01433                 DPRINTF("Invalid server information--ignoring broadcast");
01434                 return;
01435         }
01436 
01437         // can we use this?
01438         if (strcmp(uuid.c_str(), m_story->getUuid())) {
01439                 DPRINTF("Ignoring broadcast from server with different story.");
01440                 DPRINTF("  Our uuid: '%s'", m_story->getUuid());
01441                 DPRINTF("  Server uuid: '%s'", uuid.c_str());
01442                 si.address.dump("  Server address");
01443                 return;
01444         }
01445 
01446         // yes, appears to match!
01447         // DPRINTF("Got a valid server broadcast packet!");
01448         getServerHashKey(si, buffer);
01449         // DPRINTF("  hash key: '%s'", buffer);
01450 
01451         // insert (or overwrite)
01452         m_servers.insert(buffer, si);
01453         // DPRINTF("Have discovered %d servers", m_servers.size());
01454 }
01455 
01456 
01457 
01458 void
01459 ClientImpl::readConnectUDP
01460 (
01461 IO xdrbuf::Input * input
01462 )
01463 {
01464         ASSERT(input, "null");
01465 
01466         // should get token now
01467         xdrbuf::PackletHeader ph = input->getNextPackletHeader();
01468         if ('t' != ph.getName() || xdrbuf::ePacklet_Int32s != ph.getType()) {
01469                 WAVE_EX(wex);
01470                 wex << "Invalid packlet while parsing connection response";
01471         }
01472         input->readInt32s((int32_t *) &m_udpConnToken, 1);
01473 
01474         // server is sending us the encrypted DES key
01475         ph = input->getNextPackletHeader();
01476         if ('k' != ph.getName() ||
01477             xdrbuf::ePacklet_ParentBegin != ph.getType()) {
01478                 WAVE_EX(wex);
01479                 wex << "Invalid packlet when expecting encrypted DES key";
01480         }
01481         int maxSize = 1024;
01482         char buffer[maxSize];
01483         char * offset = buffer;
01484         int read = 0;
01485 
01486         while (true) {
01487                 xdrbuf::PackletHeader ph = input->getNextPackletHeader();
01488                 if ('k' == ph.getName() &&
01489                     xdrbuf::ePacklet_ParentEnd == ph.getType())
01490                         break;          // end of des key
01491 
01492                 if (xdrbuf::ePacklet_String != ph.getType()) {
01493                         WAVE_EX(wex);
01494                         wex << "Invalid packlet while reading des key";
01495                 }
01496 
01497                 int bytes = ph.getDataCount();
01498                 if (read + bytes > maxSize - 1) {
01499                         WAVE_EX(wex);
01500                         wex << "Encrypted DES key is too big!";
01501                 }
01502 
01503                 input->readString(offset, bytes);
01504                 read += bytes;
01505                 offset += bytes;
01506         }
01507         buffer[read] = 0;               // null-terminate
01508 
01509         // that should be it
01510         ph = input->getNextPackletHeader();
01511         if ('c' != ph.getName() || xdrbuf::ePacklet_ParentEnd != ph.getType()) {
01512                 WAVE_EX(wex);
01513                 wex << "Expected end of connection response packlet";
01514         }
01515 
01516         // okay, have the encrypted DES key.  Decrypt!
01517         this->setKeys(buffer);
01518 }
01519 
01520 
01521 
01522 void
01523 ClientImpl::readDestroyUDP
01524 (
01525 IO xdrbuf::Input * input
01526 )
01527 {
01528         ASSERT(input, "null");
01529 
01530         // object has been destroyed!  which one?
01531         dword_t objectId;
01532         input->readInt32s((int32_t *) &objectId, 1);
01533         if (objectId < 1) {
01534                 WAVE_EX(wex);
01535                 wex << "Bad object ID: " << objectId;
01536         }
01537 
01538         //DPRINTF("We've been asked to remove object: %ld", objectId);
01539 
01540         // remove this from our map!
01541         m_syncMgr->removeObject(objectId);
01542 }
01543 
01544 
01545 
01546 void
01547 ClientImpl::readPlayerUDP
01548 (
01549 IO xdrbuf::Input * inbuf
01550 )
01551 {
01552         ASSERT(inbuf, "null");
01553 
01554         // DPRINTF("Got player data");
01555 
01556         // at the moment, player state is a set of 3 longs
01557         int32_t l[3];
01558         inbuf->readInt32s(l, 3);
01559 
01560         // this is data we care about
01561         int playerId = (int) l[0];
01562 //      int state = (int) l[1]; // basically ignore this for now...
01563         dword_t objectId = l[2];
01564 
01565         // okay, examine the data
01566         if (playerId < 1) {
01567                 WAVE_EX(wex);
01568                 wex << "Bad player id from server: " << playerId;
01569         }
01570 
01571         client_player_record_t * cpr = this->getPlayer(playerId);
01572         if (!cpr) {
01573                 DPRINTF("Server has player we don't?: %d", playerId);
01574                 return;
01575         }
01576         ASSERT(cpr, "null");
01577 
01578         // update with object ID
01579         cpr->objectId = objectId;
01580 
01581         //DPRINTF("Player %d has object ID %u", playerId, objectId);
01582 
01583         // get current state for this object
01584         ASSERT(m_syncMgr, "null");
01585         ASSERT(m_host, "null");
01586         if (!objectId) {
01587                 // player is not bound to an object, therefore not in map!
01588                 smart_ptr<MapDynamics> nullMap;
01589                 m_host->notifyPlayerMap(playerId, nullMap);
01590                 return;
01591         }
01592 
01593         // okay, player is bound to an object.  Get the state if possible
01594         object_state_t state;
01595         if (!m_syncMgr->getState(objectId, state)) {
01596                 return; // player is bound, but object is uninitialized
01597         }
01598 
01599         // tell host about map, in case it changed
01600         m_host->notifyPlayerMap(playerId, state.dyn);
01601 }
01602 
01603 
01604 
01605 void
01606 ClientImpl::readQueryUDP
01607 (
01608 IN const xdrbuf::PackletHeader& ph_in,
01609 IO xdrbuf::Input * input
01610 )
01611 {
01612         ASSERT(input, "null");
01613 
01614         DPRINTF("Got query response");
01615 
01616         // this is the response to an object query that we sent the server
01617 
01618         // verify this is a well-formed packlet
01619         if (xdrbuf::ePacklet_ParentBegin != ph_in.getType()) {
01620                 WAVE_EX(wex);
01621                 wex << "invalid (non-parent) packlet type for query response";
01622         }
01623 
01624         // next packlet should be ID
01625         dword_t objectId;
01626         xdrbuf::PackletHeader ph = input->getNextPackletHeader();
01627         if ('i' != ph.getName() ||
01628             xdrbuf::ePacklet_Int32s != ph.getType() ||
01629             1 != ph.getDataCount()) {
01630                 WAVE_EX(wex);
01631                 wex << "invalid object ID packlet in query response";
01632         }
01633         input->readInt32s((int32_t *) &objectId, 1);
01634 
01635         // next packlet should be typeID
01636         ph = input->getNextPackletHeader();
01637         int c = ph.getDataCount();
01638         if ('t' != ph.getName() ||
01639             xdrbuf::ePacklet_String != ph.getType() ||
01640             c > eAESOP_MaxIdLength) {
01641                 WAVE_EX(wex);
01642                 wex << "invalid object type ID in query response";
01643         }
01644         char buffer[eAESOP_IdBufferSize];
01645         input->readString(buffer, c);
01646         buffer[c] = 0;  // force null-termination
01647 
01648         // next packlet should be mapID
01649         ph = input->getNextPackletHeader();
01650         c = ph.getDataCount();
01651         if ('m' != ph.getName() ||
01652             xdrbuf::ePacklet_String != ph.getType() ||
01653             c > eAESOP_MaxIdLength) {
01654                 WAVE_EX(wex);
01655                 wex << "invalid map ID in query response";
01656         }
01657         char mapId[eAESOP_IdBufferSize];
01658         input->readString(mapId, c);
01659         mapId[c] = 0;   // force null-termination
01660 
01661         // and next should be the close
01662         ph = input->getNextPackletHeader();
01663         if ('q' != ph.getName() ||
01664             xdrbuf::ePacklet_ParentEnd != ph.getType()) {
01665                 WAVE_EX(wex);
01666                 wex << "Unrecognized packlet in query response";
01667         }
01668 
01669         // okay, have ID and type ID.  Update
01670         DPRINTF("Have a type ID for object %ld: '%s'",
01671             (long) objectId, buffer);
01672         DPRINTF("  mapId = '%s'", mapId);
01673         m_syncMgr->setTypeId(objectId, buffer);
01674         m_syncMgr->setMapId(objectId, mapId);
01675 
01676         // remove any pending requests for this
01677         sprintf(buffer, "qo%ld", (long) objectId);
01678         m_netQueue->removeRequest(buffer); 
01679 }
01680 
01681 
01682 
01683 void
01684 ClientImpl::readObjectUDP
01685 (
01686 IO xdrbuf::Input * inbuf
01687 )
01688 {
01689         ASSERT(inbuf, "null");
01690 
01691         // DPRINTF("Object update");
01692 
01693         // prepare to read object information
01694         dword_t objectId = 0;
01695         state_update_t update;
01696         bool havePosition = false;
01697         bool haveAnimation = false;
01698 
01699         const int bufsize = 512;
01700         char buffer[bufsize];
01701         buffer[0] = 0;
01702 
01703         // object is structured, keep parsing
01704         bool halt = false;
01705         while (!halt) {
01706                 xdrbuf::PackletHeader ph = inbuf->getNextPackletHeader();
01707                 char name = ph.getName();
01708 
01709                 switch (name) {
01710                 case 'i':
01711                         // this is the ID
01712                         inbuf->readInt32s((int32_t *) &objectId, 1);
01713                         break;
01714 
01715                 case 'p':
01716                         // this is the position
01717                         inbuf->readFloats(&update.position.x, 3);
01718                         havePosition = true;
01719                         break;
01720 
01721                 case 'o':
01722                         // orientation or end of object!
01723                         if (xdrbuf::ePacklet_ParentEnd == ph.getType()) {
01724                                 halt = true;
01725                         } else {
01726                                 inbuf->readFloats(&update.orientation.x, 4);
01727                         }
01728                         break;
01729 
01730                 case 'l':
01731                         // linear velocity
01732                         inbuf->readFloats(&update.linear.x, 3);
01733                         break;
01734 
01735                 case 'a':
01736                         // angular velocity
01737                         inbuf->readFloats(&update.angular.x, 3);
01738                         break;
01739 
01740                 case 'n':
01741                         // animation state
01742                         {
01743                                 int len = ph.getDataCount();
01744                                 if (len >= bufsize) {
01745                                         DPRINTF("ERROR: animation state too big?");
01746                                 } else {
01747                                         haveAnimation = true;
01748                                         inbuf->readString(buffer, len);
01749                                         buffer[len] = 0;
01750                                         //DPRINTF("Read animation state: '%s'",
01751                                         //    buffer);
01752                                 }
01753                         }
01754                         break;
01755 
01756                 default:
01757                         {
01758                                 WAVE_EX(wex);
01759                                 wex << "Unrecognized packlet name '" << name;
01760                                 wex << "' while parsing map object";
01761                         }
01762                 }
01763         }
01764 
01765         // verify what we received
01766         if (!objectId) {
01767                 WAVE_EX(wex);
01768                 wex << "No or null object ID received";
01769         }
01770         if (!havePosition) {
01771                 WAVE_EX(wex);
01772                 wex << "No position received for object: " << objectId;
01773         }
01774 
01775         //DPRINTF("Server said we are object: %ld", objectId);
01776 
01777         // okay, have updated object position!  Update state
01778         ASSERT(m_syncMgr, "null");
01779         m_syncMgr->serverUpdate(objectId, m_lastUdpServerSaw, update);
01780 
01781         // have a physics object?
01782         object_state_t state;
01783         ASSERT(m_syncMgr->getState(objectId, state),
01784             "Just updated this object, it should exist!  id=%lu",
01785             (long) objectId);
01786         if (state.physicsObject) {
01787                 // attempt to update animation
01788                 if (haveAnimation) {
01789                         m_host->updateAnimation(state.physicsObject, buffer);
01790                 }
01791                 return;         // good, all set
01792         }
01793 
01794 //      DPRINTF("Need a physics object!");
01795 
01796         // request more info about object from server
01797         smart_ptr<netrq::Request> ctx = QueryObjectRequest::create(objectId);
01798         ASSERT(ctx, "null");
01799 
01800         // add this request to our network request queue
01801         if (netrq::Queue::eQueue_Success == m_netQueue->addRequest(m_udpClock + 3, ctx)) {
01802                 ASSERT(ctx.get_ref_count() > 1, "bad ref count");
01803         }
01804 }
01805 
01806 
01807 
01808 void
01809 ClientImpl::setKeys
01810 (
01811 IN const char * encryptedDesKey
01812 )
01813 {
01814         ASSERT(encryptedDesKey, "null");
01815 
01816         std::string desKey = m_rsaKey->decrypt(encryptedDesKey);
01817         DPRINTF("decrypted DES key: %s", desKey.c_str());
01818 
01819         m_desKey = crypto::DESKey::create(desKey.c_str());
01820         ASSERT(m_desKey, "null");
01821 
01822         // notify host!
01823         m_host->notifyKey(m_desKey);
01824 }
01825 
01826 
01827 
01828 // **** KEEP THIS COMMENT BLOCK ***
01829 /// \ingroup aesop_clib
01830 /// \defgroup clib_move Client/Server Motion Requests 
01831 /// \n
01832 /// How do you keep the client and server in sync on object
01833 /// locations?  Obviously, this is a deep problem, see
01834 /// references such as
01835 /// http://gafferongames.com/2009/01/25/game-networking-resources/
01836 /// .
01837 ///
01838 /// AESOP uses a combination of predictive physics and
01839 /// interpolation.  See \ref ObjectSync for more details.
01840 ///
01841 /// There is another question: what do you send over the wire
01842 /// to the server?  The client's actual requested position, or
01843 /// just the position delta?
01844 ///
01845 /// Sending the position has one drawback: the client sometimes
01846 /// has a very bad position idea.  For instance, when there is a
01847 /// large change in position due to non-client input.  This can
01848 /// lead to oscillations as the client and server try to agree
01849 /// on a position and overshoot due to network lag.
01850 ///
01851 /// The position has a nice property: it deals well with dropped
01852 /// packets.  Regardless of how many packets are missed, once
01853 /// the server gets a position request from the client, it knows
01854 /// exactly where the client wants to go.  Also, sending only
01855 /// position deltas requires accumulating deltas on both the
01856 /// client and server, which can lead to rounding errors.
01857 ///
01858 /// For all of that, I've found that sending deltas is more
01859 /// stable.  Rounding errors and occasional packet drops can
01860 /// require correction more often than sending absolute
01861 /// positions, but it tends to be a high rate of very small
01862 /// corrections, rather than the occasional very bad
01863 /// oscillations seen with sending absolute positions.  So the
01864 /// player experience is better with deltas.
01865 
01866 
01867 void
01868 ClientImpl::sendUdpPacket
01869 (
01870 IN float dt
01871 )
01872 {
01873         perf::Timer timer("Client::sendUdpPacket");
01874         ASSERT(dt > 0.0, "Bad time delta: %f", dt);
01875         ASSERT(m_udpOut, "Should have a udp output buffer by now");
01876         if (!m_remoteUdpConnID) {
01877                 return;         // don't yet have a UDP connection with server
01878         }
01879 
01880         // update our udp clock
01881         ++m_udpClock;
01882 
01883         // let synchronizer add requests to outgoing request queue
01884         m_syncMgr->addRequests(m_netQueue, m_udpClock);
01885 
01886         // this is where we send all interesting (dynamic) client state up to
01887         // the server.
01888         m_udpOut->clear();      // reset output buffer
01889 
01890         // send header first.  This is 2 longs
01891         //    0: token
01892         //    1: our udp clock value
01893         int32_t l[3];
01894         l[0] = m_udpConnToken;
01895         l[1] = m_udpClock;
01896         m_udpOut->addInt32Packlet('h', l, 2);   // 'h' -- header
01897 
01898         if (eHostMode_Authorized == m_hMode) {
01899         // loop through all players and send per-player information
01900         for (player_map_t::iterator i = m_players.begin(); i != m_players.end();
01901              ++i)
01902         {
01903                 int id = i->first;
01904                 client_player_record_t * pr = i->second;
01905                 ASSERT(pr, "null player record in map");
01906 
01907                 // open a packlet for this player record
01908                 m_udpOut->openPacklet('p');
01909 
01910                 // send ID
01911                 dword_t dwId = id;
01912                 m_udpOut->addInt32Packlet('i', (int32_t *) &dwId, 1);
01913 
01914                 // send the desired motion (move by delta over time interval dt)
01915                 object_state_t state;
01916                 if (pr->objectId &&
01917                     m_syncMgr->getState(pr->objectId, state) &&
01918                     state.physicsObject) {
01919                         // we send 7 floats to represent position and orientation
01920                         // TODO: trim this down somehow?  lower resolution?
01921                         // Doesn't matter much for sending data up
01922                         float f[7];
01923                         f[0] = pr->delta.x; // first 3 floats: position delta
01924                         f[1] = pr->delta.y;
01925                         f[2] = pr->delta.z;
01926                         f[3] = dt;          // fourth float: time delta
01927                         f[4] = pr->euler.x; // last 3 floats: euler angles
01928                         f[5] = pr->euler.y;
01929                         f[6] = pr->euler.z;
01930                         m_udpOut->addFloatPacklet('m', f, 7);
01931 
01932                         // now that we've sent the delta to the server, reset
01933                         pr->delta.clear();
01934                 }
01935 
01936                 // all done with player record
01937                 m_udpOut->closePacklet('p');
01938         }
01939         }
01940 
01941         // add any pending messages
01942 //      DPRINTF("Looking for pending messages");
01943         netrq::sendMessagesFromQueue(m_udpOut, m_udpClock, m_netQueue);
01944 
01945         // final step: allow host to send game data
01946         m_udpOut->openPacklet('g');     // 'g' -- game data
01947         {
01948                 perf::Timer timer("appendGameData");
01949                 m_host->appendGameData(m_udpOut);
01950         }
01951         m_udpOut->closePacklet('g');
01952 
01953         // all done with message construction
01954 //      DPRINTF("  done!");
01955         // TODO: no allocations here!  Pull message buffer from freelist
01956         smart_ptr<MessageBuffer> mb = MessageBuffer::create();
01957         ASSERT(mb, "failed to create message buffer");
01958         const byte_t * pb = m_udpOut->getData();
01959         int bytes = m_udpOut->getDataBytes();
01960         ASSERT(bytes > 0, "Bad udp byte count: %d", bytes);
01961         mb->reserve(bytes);
01962         mb->appendData((const char *) pb, bytes);
01963         mb->close();
01964 
01965         // send udp message
01966         netlib::enqueueMessage(m_remoteUdpConnID, mb);
01967 }
01968 
01969 
01970 
01971 ////////////////////////////////////////////////////////////////////////////////
01972 //
01973 //      factory methods
01974 //
01975 ////////////////////////////////////////////////////////////////////////////////
01976 
01977 smart_ptr<Client>
01978 Client::create
01979 (
01980 IN ClientHost * host,
01981 IN smart_ptr<story::Story>& story,
01982 IN smart_ptr<Datahash>& params
01983 )
01984 {
01985         ASSERT(host, "null");
01986         ASSERT(story, "null");
01987         ASSERT(params, "null");
01988 
01989         smart_ptr<ClientImpl> local = new ClientImpl;
01990         ASSERT(local, "null");
01991 
01992         local->initialize(host, story, params);
01993 
01994         return local;
01995 }
01996 
01997 
01998 
01999 };      // aesop namespace
02000