object-sync.cpp

Go to the documentation of this file.
00001 /*
00002  * object-sync.cpp
00003  *
00004  * Copyright (C) 2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  * See object-sync.h
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "object-sync.h"                // always include our own header first!
00035 
00036 // REMINDER: no rendering or input dependencies allowed!
00037 #include "common/wave_ex.h"
00038 #include "map-manager/map-manager.h"
00039 #include "perf/perf.h"
00040 #include "util/circular.h"
00041 
00042 namespace aesop {
00043 
00044 
00045 /// \ingroup object_sync
00046 /*@{*/
00047 
00048 
00049 // interface destructors
00050 ObjectSync::~ObjectSync(void) throw() { }
00051 
00052 
00053 // distance thresholds.  Note that these are actually distances SQUARED
00054 static const float s_largeDifference            = 1.5;
00055 static const float s_maxLocalMove               = 1.0;
00056 
00057 static const int s_clientSendHistory            = 16;
00058 
00059 
00060 ////////////////////////////////////////////////////////////////////////////////
00061 //
00062 //      static helper methods
00063 //
00064 ////////////////////////////////////////////////////////////////////////////////
00065 
00066 class ObjectExistsRequest : public netrq::Request {
00067 public:
00068         ~ObjectExistsRequest(void) throw() { }
00069 
00070         // netrq::Request class interface methods ------------------------------
00071         const char * getId(void) const throw() { return m_requestId; }
00072         int getMaxBytes(void) const throw() { return 6; }
00073         void write(IO xdrbuf::Output * output) {
00074                         ASSERT(output, "null");
00075 
00076                         //DPRINTF("Asking if object exists: %ld", m_objectId);
00077                         output->addInt32Packlet('e',
00078                             (int32_t *) &m_objectId, 1);
00079                 }
00080 
00081         // static factory methods ----------------------------------------------
00082         static smart_ptr<netrq::Request> create(IN const char * requestId,
00083                                 IN dword_t objectId) {
00084                         ASSERT(requestId, "null");
00085                         ASSERT(objectId, "null");
00086 
00087                         smart_ptr<ObjectExistsRequest> local =
00088                             new ObjectExistsRequest;
00089                         ASSERT(local, "out of memory");
00090 
00091                         strcpy(local->m_requestId, requestId);
00092                         local->m_objectId = objectId;
00093 
00094                         return local;
00095                 }
00096 
00097 private:
00098         // private helper methods ----------------------------------------------
00099         ObjectExistsRequest(void) throw() { }
00100 
00101         // private member data -------------------------------------------------
00102         char            m_requestId[64];
00103         dword_t         m_objectId;
00104 };
00105 
00106 
00107 
00108 ////////////////////////////////////////////////////////////////////////////////
00109 //
00110 //      SyncImpl -- class that implements the ObjectSync interface
00111 //
00112 ////////////////////////////////////////////////////////////////////////////////
00113 
00114 class SyncImpl : public ObjectSync {
00115 public:
00116         // constructor, destructor ---------------------------------------------
00117         ~SyncImpl(void) throw() { }
00118 
00119         // public class methods ------------------------------------------------
00120         void initialize(IN smart_ptr<MapManager> mapMgr) throw() {
00121                         ASSERT(mapMgr, "null");
00122                         m_mapMgr = mapMgr;
00123                 }
00124 
00125         // aesop::ObjectSync class interface methods --------------------------
00126         void serverUpdate(IN dword_t id,
00127                                 IN dword_t lastClientClock,
00128                                 IN const state_update_t& state);
00129         void clientUpdate(IN dword_t clientClock);
00130         bool clientMoveRequest(IN dword_t id,
00131                                 IN const point3d_t& delta,
00132                                 IN float dt,
00133                                 OUT point3d_t& newPosition);
00134         bool getState(IN dword_t id,
00135                                 OUT object_state_t& state);
00136         bool setTypeId(IN dword_t id,
00137                                 IN const char * typeId);
00138         bool setMapId(IN dword_t id,
00139                                 IN const char * mapId);
00140         void addRequests(IN netrq::Queue * queue,
00141                                 IN dword_t clock);
00142         void removeObject(IN dword_t id);
00143 
00144 private:
00145         // private typedefs ----------------------------------------------------
00146         struct client_send_t {
00147                 dword_t                 localClock;
00148                 state_update_t          update;
00149         };
00150 
00151         typedef circular_buffer_t<client_send_t> send_records_t;
00152 
00153         struct state_record_t {
00154                 // constructor
00155                 state_record_t(void) throw() : sends(s_clientSendHistory) {
00156                                 lupsu = 0;
00157                                 correctCount = 0;
00158                                 dyn = NULL;
00159                         }
00160 
00161                 // data fields
00162                 state_update_t          bestGuess;      // this is most current
00163                 point3d_t               lastKnownGood;  // most recent from srv
00164                 send_records_t          sends;
00165                 int                     lupsu;// local updates per server update
00166                 point3d_t               correction;     // correct to match srv
00167                 int                     correctCount;   // apply correction?
00168                 AESOPIdBuffer           typeId;
00169                 AESOPIdBuffer           mapId;
00170                 smart_ptr<MapDynamics>  dyn;
00171                 smart_ptr<PhysicsObject> physicsObj;
00172         };
00173 
00174         typedef std::map<dword_t, smart_ptr<state_record_t> > obj_map_t;
00175 
00176         // private helper methods ----------------------------------------------
00177         state_record_t * getRecord(IN dword_t id, IN bool create = false);
00178 
00179         // private member data -------------------------------------------------
00180         obj_map_t               m_map;
00181         smart_ptr<MapManager>   m_mapMgr;
00182 };
00183 
00184 
00185 
00186 void
00187 SyncImpl::serverUpdate
00188 (
00189 IN dword_t id,
00190 IN dword_t lastClientClock,
00191 IN const state_update_t& update
00192 )
00193 {
00194         ASSERT(id, "null");
00195         ASSERT(lastClientClock, "null");
00196 
00197 //      DPRINTF("Server update!");
00198 
00199         state_record_t * psr = this->getRecord(id, true);
00200         ASSERT(psr, "null");
00201         PhysicsObject * obj = psr->physicsObj;
00202         if (!obj) {
00203                 // no physics object yet, so just accept update wholesale
00204                 psr->bestGuess = update;
00205                 return;
00206         }
00207 
00208         // do we recognize this sample?
00209         // walk backwards because the server is usually up-to-date
00210         int N = psr->sends.size();
00211         for (int i = N - 1; i >= 0; --i) {
00212                 const client_send_t& cs = psr->sends.getSample(i);
00213                 if (cs.localClock == lastClientClock) {
00214                         // Okay, we thought we knew where we were N clocks ago.
00215                         // Now we find out where the server thought we were.
00216                         // Adjust current position to account for the delta back
00217                         //    then.
00218                         // DPRINTF("Found clock %lu", lastClientClock);
00219 
00220                         // what is the absolute position difference?
00221                         point3d_t delta = update.position - cs.update.position;
00222 
00223                         // walk forward through remaining sends
00224                         for (i = i + 1; i < N; ++i) {
00225                                 client_send_t& csLater =
00226                                     psr->sends.getSample(i);
00227                                 csLater.update.position =
00228                                     csLater.update.position + delta;
00229                         }
00230 
00231                         // debug only
00232                         if (!(lastClientClock % 250)) {
00233                                 DPRINTF("Comparing local + server view of position at clock %lu",
00234                                     (long) lastClientClock);
00235                                 cs.update.position.dump("    local");
00236                                 update.position.dump(   "   server");
00237                                 delta.dump(             "    delta");
00238                         }
00239 
00240                         // we'll apply this over the next several client updates
00241                         int count = psr->lupsu;
00242                         if (count < 1) {
00243                                 count = 1;
00244                         }
00245                         count = 5;      // reset!
00246                         psr->correction = 0.5 * delta;
00247                         psr->correctCount = count;
00248                         break;
00249                 }
00250         }
00251 
00252         // always set these fields
00253         obj->setOrientation(update.orientation);
00254         obj->setLinearVelocity(update.linear);
00255         obj->setAngularVelocity(update.angular);
00256 
00257         point3d_t diff = psr->bestGuess.position - update.position;
00258         float r2 = dotProduct(diff, diff);
00259 //      DPRINTF("  server r2: %f    dt=%f", r2, dt);
00260         if (r2 > s_largeDifference) {
00261                 DPRINTF("Large difference!");
00262                 update.position.dump(        "  server");
00263                 psr->bestGuess.position.dump("   local");
00264                 psr->bestGuess = update;
00265                 obj->setPosition(update.position);
00266                 psr->correctCount = 0;  // do not apply corrections
00267         }
00268 
00269         // always update last known good
00270         psr->lastKnownGood = update.position;
00271         psr->lupsu = 0; // always reset local-updates-per-server-updates counter
00272 }
00273 
00274 
00275 
00276 void
00277 SyncImpl::clientUpdate
00278 (
00279 IN dword_t clientClock
00280 )
00281 {
00282         ASSERT(clientClock, "null");
00283 
00284         // walk through all objects and update!
00285         for (obj_map_t::iterator i = m_map.begin(); i != m_map.end(); ++i) {
00286                 state_record_t * psr = i->second;
00287                 ASSERT(psr, "null state record in map?");
00288 
00289                 // update position based on physics object (can be null!)
00290                 PhysicsObject * obj = psr->physicsObj;
00291                 if (obj) {
00292                         psr->bestGuess.position = obj->getPosition();
00293                 } else {
00294                         // no physics object yet
00295                         // can we create one?
00296                         if (!psr->typeId.isEmpty() &&
00297                             !psr->mapId.isEmpty()) {
00298                                 // is map loaded yet?
00299                                 smart_ptr<MapDynamics> dyn = m_mapMgr->getMap(
00300                                     psr->mapId);
00301                                 if (dyn) {
00302                                         // yes, have map dynamics!
00303                                         smart_ptr<Instance> instance =
00304                                             Instance::create(NULL, psr->typeId);
00305                                         placement_t& p = instance->getPlacement();
00306                                         p.position = psr->bestGuess.position;
00307 
00308                                         psr->physicsObj =
00309                                            dyn->addInstance(instance);
00310 
00311                                         // update our record, just in case
00312                                         psr->dyn = dyn;
00313                                 }
00314                         }
00315                 }
00316 
00317                 client_send_t cs;
00318                 cs.localClock = clientClock;
00319                 cs.update = psr->bestGuess;
00320                 psr->sends.addSample(cs);
00321         }
00322 }
00323 
00324 
00325 
00326 bool
00327 SyncImpl::clientMoveRequest
00328 (
00329 IN dword_t id,
00330 IN const point3d_t& delta,
00331 IN float dt,
00332 OUT point3d_t& newPosition
00333 )
00334 {
00335         ASSERT(id, "null");
00336         newPosition.clear();
00337 
00338         // skip everything if dt is zero!
00339         if (dt <= 0.0)
00340                 return true;
00341 
00342 //      delta.dump("Requested client move");
00343 
00344         state_record_t * psr = this->getRecord(id);
00345         if (!psr) {
00346                 DPRINTF("Client requesting update to non-existent object");
00347                 return false;   // object not found
00348         }
00349         PhysicsObject * obj = psr->physicsObj;
00350         if (!obj) {
00351                 return true;    // object found, but not allowed to move
00352         }
00353 
00354         // update count of local updates per server update (lupsu)
00355         psr->lupsu++;
00356 
00357         // see what the new position would be
00358         point3d_t newPos = psr->bestGuess.position + delta;
00359         point3d_t diff = psr->lastKnownGood - newPos;
00360         float r2 = dotProduct(diff, diff);
00361 //      diff.dump("  delta from lkg");
00362 //      DPRINTF("  client r2: %f", r2);
00363 
00364         // determine what motion we will request of physics engine
00365         point3d_t motion;
00366         if (r2 > s_maxLocalMove) {
00367                 // we are too far from the last known good position from server
00368                 // no change allowed
00369                 DPRINTF("  No move!");
00370                 motion.clear();
00371         } else {
00372                 // fine!
00373                 motion = delta;
00374         }
00375 
00376         // apply correction?
00377         if (psr->correctCount > 0) {
00378                 --psr->correctCount;
00379                 motion += psr->correction;
00380                 psr->correction = 0.5 * psr->correction;
00381         }
00382 
00383         // okay, tell physics object to move
00384         // Shorten time interval so we are likely to complete full move
00385         obj->requestMove(motion, 0.8 * dt);
00386 
00387         // give client our current location
00388         newPosition = psr->bestGuess.position;
00389         return true;
00390 }
00391 
00392 
00393 
00394 bool
00395 SyncImpl::getState
00396 (
00397 IN dword_t id,
00398 OUT object_state_t& state
00399 )
00400 {
00401         ASSERT(id, "null");
00402         state.clear();
00403 
00404         state_record_t * psr = this->getRecord(id);
00405         if (!psr) {
00406                 DPRINTF("Requesting state for non-existent object");
00407                 return false;
00408         }
00409 
00410         // provide best guess!
00411         state_update_t * update = (state_update_t *) &state;
00412         *update = psr->bestGuess;
00413 
00414         // add additional data
00415         state.physicsObject = psr->physicsObj;
00416         state.typeId = psr->typeId;
00417         state.dyn = psr->dyn;
00418 
00419         return true;
00420 }
00421 
00422 
00423 
00424 bool
00425 SyncImpl::setTypeId
00426 (
00427 IN dword_t id,
00428 IN const char * typeId
00429 )
00430 {
00431         ASSERT(id, "null");
00432         ASSERT(typeId, "null");
00433 
00434         state_record_t * psr = this->getRecord(id);
00435         if (!psr) {
00436                 return false;   // id not found
00437         }
00438 
00439         // update typeID
00440         psr->typeId.set(typeId);
00441         return true;    
00442 }
00443 
00444 
00445 
00446 bool
00447 SyncImpl::setMapId
00448 (
00449 IN dword_t id,
00450 IN const char * mapId
00451 )
00452 {
00453         ASSERT(id, "null");
00454         ASSERT(mapId, "null");
00455 
00456         state_record_t * psr = this->getRecord(id);
00457         if (!psr) {
00458                 return false;   // id not found
00459         }
00460 
00461         // update map ID
00462         psr->mapId.set(mapId);
00463 
00464         // request that this map be loaded
00465         // does nothing if map is loaded
00466         // if map is not already loaded, it will load in a background thread
00467         m_mapMgr->requestLoad(mapId);
00468 
00469         // all set
00470         return true;
00471 }
00472 
00473 
00474 
00475 void
00476 SyncImpl::addRequests
00477 (
00478 IN netrq::Queue * queue,
00479 IN dword_t clock
00480 )
00481 {
00482         ASSERT(queue, "null");
00483 
00484         // loop over all objects and see if server knows about them
00485         for (obj_map_t::iterator i = m_map.begin(); i != m_map.end(); ++i) {
00486                 long id = i->first;
00487 
00488                 char buffer[64];
00489                 sprintf(buffer, "oe-%ld", id);
00490                 if (queue->containsRequest(buffer))
00491                         continue;       // already asking about this one
00492 
00493                 smart_ptr<netrq::Request> request = 
00494                     ObjectExistsRequest::create(buffer, id);
00495                 ASSERT(request, "null");
00496 
00497                 queue->addRequest(clock + 5 + (rand() % 32), request);
00498         }
00499 }
00500 
00501 
00502 
00503 void
00504 SyncImpl::removeObject
00505 (
00506 IN dword_t id
00507 )
00508 {
00509         ASSERT(id, "bad object id");
00510 
00511         obj_map_t::iterator i = m_map.find(id);
00512         if (m_map.end() == i) {
00513                 DPRINTF("Object not found to remove: %lu", (long) id);
00514                 return;
00515         }
00516 
00517         state_record_t * psr = i->second;
00518         if (psr->physicsObj && psr->dyn) {
00519                 // remove from map etc
00520                 psr->dyn->removeObject(psr->physicsObj);
00521         } else {
00522                 DPRINTF("Removing partially constructed object! %lu",
00523                     (long) id);
00524         }
00525 
00526         // all done: erase our record
00527         m_map.erase(i);
00528 }
00529 
00530 
00531 
00532 ////////////////////////////////////////////////////////////////////////////////
00533 //
00534 //      SyncImpl -- private helper methods
00535 //
00536 ////////////////////////////////////////////////////////////////////////////////
00537 
00538 SyncImpl::state_record_t *
00539 SyncImpl::getRecord
00540 (
00541 IN dword_t id,
00542 IN bool create
00543 )
00544 {
00545         ASSERT(id, "null");
00546 
00547         obj_map_t::iterator i = m_map.find(id);
00548         if (m_map.end() != i) {
00549                 // already in map!
00550                 state_record_t * psr = i->second;
00551                 ASSERT(psr, "null record in map");
00552                 return psr;
00553         }
00554 
00555         // not in map!
00556         if (!create)
00557                 return NULL;
00558 
00559         // not in map, and we have been asked to create if necessary
00560         smart_ptr<state_record_t> psr = new state_record_t;
00561         ASSERT(psr, "null");
00562         m_map[id] = psr;
00563         return psr;
00564 }
00565 
00566 
00567 
00568 ////////////////////////////////////////////////////////////////////////////////
00569 //
00570 //      public API
00571 //
00572 ////////////////////////////////////////////////////////////////////////////////
00573 
00574 smart_ptr<ObjectSync>
00575 ObjectSync::create
00576 (
00577 IN smart_ptr<MapManager> mapMgr
00578 )
00579 {
00580         ASSERT(mapMgr, "null");
00581 
00582         smart_ptr<SyncImpl> local = new SyncImpl;
00583         ASSERT(local, "out of memory");
00584 
00585         local->initialize(mapMgr);
00586 
00587         return local;
00588 }
00589 
00590 
00591 
00592 };      // aesop namespace
00593