srv-players.cpp

Go to the documentation of this file.
00001 /*
00002  * srv-players.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  * Player management.  See srv-players.h
00031  */
00032 
00033 // includes -------------------------------------------------------------------
00034 #include "srv-players.h"                // always include our own header first!
00035 
00036 #include "common/wave_ex.h"
00037 
00038 #include "threadsafe/smart_mutex.h"
00039 
00040 
00041 namespace aesop {
00042 
00043 
00044 PlayerManager::~PlayerManager(void) throw() { }
00045 
00046 
00047 
00048 
00050 //
00051 //      PlayerMgr -- object that implements the PlayerManager interface
00052 //
00054 
00055 class PlayerMgr : public PlayerManager {
00056 public:
00057         PlayerMgr(void) throw();
00058         ~PlayerMgr(void) throw() { }
00059 
00060         // public class methods ------------------------------------------------
00061         void initialize(IN smart_ptr<UserManager>& userMgr);
00062 
00063         // aesop::PlayerManager class interface methods -----------------------
00064         void updatePlayer(IN player_rec_t& pr);
00065         bool getPlayerByHostAndPlayerId(IN conn_id_t udpConnId,
00066                                 IN int playerId,
00067                                 OUT player_rec_t& pr);
00068         void getIterator(IN conn_id_t udpConnId, OUT iterator_t& iterator);
00069         bool getNextPlayer(IO iterator_t& iterator,
00070                                 OUT player_rec_t& pr);
00071 
00072 private:
00073         // private typedefs ----------------------------------------------------
00074         typedef std::map<int, player_rec_t> player_map_t;
00075         struct host_rec_t {
00076                 host_rec_t(void) throw() { playerRvn = 0; }
00077 
00078                 player_map_t            players;
00079                 long                    playerRvn;
00080         };
00081 
00082         typedef std::map<conn_id_t, smart_ptr<host_rec_t> > host_map_t;
00083 
00084         struct real_iter_t {
00085                 conn_id_t               udpConnId;      // can be null
00086                 dword_t                 rvn;    // record version number
00087                 bool                    playerValid;
00088                 host_map_t::iterator    hostIter;
00089                 player_map_t::iterator  playerIter;
00090         };
00091 
00092         // private helper methods ----------------------------------------------
00093         host_rec_t * getHostRecord(IN conn_id_t udpConnId,
00094                                 IN bool createIfNecessary = false);
00095         player_rec_t * getPlayerRecord(IN host_rec_t * hr,
00096                                 IN int playerId,
00097                                 IN bool createIfNecessary = false);
00098         static real_iter_t * castIterator(IN iterator_t& i) throw()
00099                         { return (real_iter_t *) &i; }
00100 
00101         // private member data -------------------------------------------------
00102         smart_mutex                     m_mutex;
00103         smart_ptr<UserManager>          m_userMgr;
00104         host_map_t                      m_hosts;
00105         dword_t                         m_rvn;  // record version number
00106 };
00107 
00108 
00109 
00110 PlayerMgr::PlayerMgr(void)
00111 throw()
00112 {
00113         m_rvn = 0;
00114 }
00115 
00116 
00117 
00118 void
00119 PlayerMgr::initialize
00120 (
00121 IN smart_ptr<UserManager>& userMgr
00122 )
00123 {
00124         ASSERT(userMgr, "null");
00125 
00126         // save manager
00127         m_userMgr = userMgr;
00128 
00129         // verify public iterator size
00130         ASSERT2(sizeof(iterator_t) >= sizeof(real_iter_t),
00131             "public iterator is too small!  " << sizeof(iterator_t)
00132             << " bytes vs. " << sizeof(real_iter_t) << " bytes.");
00133 }
00134 
00135 
00136 
00138 //
00139 //      PlayerMgr -- aesop::PlayerManager class interface methods
00140 //
00142 
00143 void
00144 PlayerMgr::updatePlayer
00145 (
00146 IN player_rec_t& pr
00147 )
00148 {
00149         ASSERT(pr.udpConnId, "null");
00150         ASSERT(pr.playerId > 0, "bad player id: %d", pr.playerId);
00151 
00152         // threadsafe!
00153         mlock l(m_mutex);
00154 
00155         // look up host (and create record if necessary)
00156         host_rec_t * hr = this->getHostRecord(pr.udpConnId, true);
00157         ASSERT(hr, "failed to create host record?");
00158 
00159         // look up player
00160         player_rec_t * prec = this->getPlayerRecord(hr, pr.playerId, true);
00161         ASSERT(prec, "failed to create player record?");
00162 
00163         // copy over
00164         *prec = pr;
00165 }
00166 
00167 
00168 
00169 bool
00170 PlayerMgr::getPlayerByHostAndPlayerId
00171 (
00172 IN conn_id_t udpConnId,
00173 IN int playerId,
00174 OUT player_rec_t& pr
00175 )
00176 {
00177         ASSERT(udpConnId, "null");
00178         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00179 
00180         pr.clear();
00181 
00182         // threadsafe!
00183         mlock l(m_mutex);
00184 
00185         // look up host
00186         host_rec_t * hr = this->getHostRecord(udpConnId);
00187         if (!hr)
00188                 return false;
00189 
00190         // look up player
00191         player_rec_t * rec = this->getPlayerRecord(hr, playerId);
00192         if (!rec)
00193                 return false;
00194 
00195         // found player!
00196         pr = *rec;
00197         return true;
00198 }
00199 
00200 
00201 
00202 void
00203 PlayerMgr::getIterator
00204 (
00205 IN conn_id_t udpConnId,
00206 OUT iterator_t& iterator
00207 )
00208 {
00209         // ASSERT(udpConnId) -- can be null!
00210         real_iter_t * ri = castIterator(iterator);
00211         ASSERT(ri, "null");
00212 
00213         // threadsafe!
00214         mlock l(m_mutex);
00215 
00216         // copy over important interation state
00217         ri->udpConnId = udpConnId;
00218         ri->rvn = m_rvn;
00219         ri->playerValid = false;        // player iterator not valid
00220 
00221         if (udpConnId) {
00222                 ri->hostIter = m_hosts.find(udpConnId);
00223         } else {
00224                 ri->hostIter = m_hosts.begin();
00225         }
00226 }
00227 
00228 
00229 
00230 bool
00231 PlayerMgr::getNextPlayer
00232 (
00233 IO iterator_t& iterator,
00234 OUT player_rec_t& pr
00235 )
00236 {
00237         pr.clear();
00238 
00239         real_iter_t * ri = castIterator(iterator);
00240         ASSERT(ri, "null");
00241 
00242         // threadsafe!
00243         mlock l(m_mutex);
00244 
00245         // TODO: better iteration strategy?
00246         // At the moment, this algorithm will faithfully iterate over all
00247         //   hosts and players, but will stop the whole thing if there are
00248         //   any changes anywhere.
00249         // If updates are relatively infrequent, this is a fine strategy.
00250         // If updates are frequent, we could end up where a client is never
00251         //   able to iterate over hosts/players later in the maps.  The
00252         //   client would always get partway through, then have iteration
00253         //   invalidated by an update.
00254         // For the moment, this algorithm is okay, because updates should
00255         //   be very infrequent (new hosts/players every minute at most,
00256         //   not multiple times per second!).
00257 
00258         // if rvn has changed, disallow further iteration
00259         if (ri->rvn != m_rvn)
00260                 return false;    // records have been added/removed!
00261 
00262         // we'll iterate here until we find the next valid player
00263         for (;;) {
00264                 // is host iterator still valid?
00265                 if (m_hosts.end() == ri->hostIter)
00266                         return false;   // end of iteration!
00267 
00268                 // we trust the host iterator, so get the host record
00269                 host_rec_t * hr = ri->hostIter->second;
00270                 ASSERT(hr, "null host record in map?");
00271 
00272                 // is this a new iteration for the host?
00273                 if (!ri->playerValid) {
00274                         ri->playerIter = hr->players.begin();
00275                         ri->playerValid = true;
00276                 }
00277 
00278                 // player iterator is set.  valid?
00279                 if (hr->players.end() != ri->playerIter) {
00280                         // yes, valid!
00281                         pr = ri->playerIter->second;
00282                         ++ri->playerIter;
00283                         return true;
00284                 }
00285 
00286                 // okay, we hit the end of that host.
00287                 // can we iterate to the next host?
00288                 if (ri->udpConnId) {
00289                         // client just wanted to iterate that host
00290                         return false;
00291                 }
00292 
00293                 // okay, iterate to next host!
00294                 ri->playerValid = false;
00295                 ++ri->hostIter;
00296         }
00297 
00298         // should never get here!
00299         ASSERT(false, "invalid");
00300         return false;
00301 }
00302 
00303 
00304 
00306 //
00307 //      PlayerMgr -- private helper methods
00308 //
00310 
00311 PlayerMgr::host_rec_t *
00312 PlayerMgr::getHostRecord
00313 (
00314 IN conn_id_t udpConnId,
00315 IN bool createIfNecessary
00316 )
00317 {
00318         ASSERT(udpConnId, "null");
00319 
00320         host_map_t::iterator i = m_hosts.find(udpConnId);
00321         if (m_hosts.end() == i) {
00322                 if (!createIfNecessary)
00323                         return NULL;
00324 
00325                 // increment RVN
00326                 ++m_rvn;
00327 
00328                 smart_ptr<host_rec_t> hr = new host_rec_t;
00329                 ASSERT(hr, "null");
00330 
00331                 m_hosts[udpConnId] = hr;
00332                 return hr;
00333         }
00334         host_rec_t * phr = i->second;
00335         ASSERT(phr, "null host record in map");
00336         return phr;
00337 }
00338 
00339 
00340 
00341 player_rec_t *
00342 PlayerMgr::getPlayerRecord
00343 (
00344 IN host_rec_t * hr,
00345 IN int playerId,
00346 IN bool createIfNecessary
00347 )
00348 {
00349         ASSERT(hr, "null");
00350         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00351 
00352         player_map_t::iterator i = hr->players.find(playerId);
00353         if (hr->players.end() == i) {
00354                 if (!createIfNecessary)
00355                         return NULL;
00356 
00357                 // increment RVN
00358                 ++m_rvn;
00359 
00360                 player_rec_t pr;
00361                 hr->players[playerId] = pr;
00362                 return &hr->players[playerId];
00363         }
00364         return &i->second;
00365 }
00366 
00367 
00368 
00370 //
00371 //      public API
00372 //
00374 
00375 smart_ptr<PlayerManager>
00376 PlayerManager::create
00377 (
00378 IN smart_ptr<UserManager>& userMgr
00379 )
00380 {
00381         ASSERT(userMgr, "null");
00382 
00383         smart_ptr<PlayerMgr> local = new PlayerMgr;
00384         ASSERT(local, "out of memory");
00385 
00386         local->initialize(userMgr);
00387 
00388         return local;
00389 }
00390 
00391 
00392 
00393 };      // aesop namespace
00394