client-game-logic.cpp

Go to the documentation of this file.
00001 /*
00002  * client-game-logic.cpp
00003  *
00004  * Copyright (C) 2008,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  *
00031  * Reference implementation of an object that contains client-side game logic.
00032  */
00033 
00034 // includes -------------------------------------------------------------------
00035 #include "client-game-logic.h"          // always include our own header 1st!
00036 
00037 #include "datahash/datahash_util.h"
00038 #include "gamepad/gamepad.h"
00039 #include "map-dynamics/map-dynamics.h"
00040 #include "model-loader/model-loader.h"
00041 #include "threadsafe/smart_mutex.h"
00042 
00043 
00044 
00045 namespace aesop {
00046 
00047 
00048 
00049 ////////////////////////////////////////////////////////////////////////////////
00050 //
00051 //      static helper methods
00052 //
00053 ////////////////////////////////////////////////////////////////////////////////
00054 
00055 
00056 /// \ingroup client_game_logic_impl
00057 ///
00058 /// This is a reference implementation of a \ref aesop::ClientGameLogic object.
00059 ///
00060 /// \n
00061 /// In most cases, game creators will want to create their own game logic!
00062 /// But this can be used as a starting point.
00063 ///
00064 /// \b TODO: make this a visible class?  At the moment, it is an implementation-
00065 /// only class hidden in a .cpp file.  I could make it public so people could
00066 /// inherit from it.  However, I suspect that game logic is complex enough
00067 /// that people will either roll their own entirely, or use composition rather
00068 /// than inheritance.
00069 class ClibGameLogic : public ClientGameLogic {
00070 public:
00071         // constructor, destructor ---------------------------------------------
00072         ClibGameLogic(void) throw();
00073         ~ClibGameLogic(void) throw() { }
00074 
00075         // public class methods ------------------------------------------------
00076         void initialize(void) { }
00077 
00078         // aesop::ClientGameLogic class interface methods ----------------------
00079         void setHost(IN ClientGameLogicHost * host) { m_host = host; }
00080         bool localPlayerJoined(IN int playerId);
00081         void localPlayerLeft(IN int playerId);
00082         void notifyInput(IN int playerId,
00083                                 IN const event_t& event);
00084         void tick(IN float dt);
00085         void appendGameData(IN xdrbuf::Output * outbuf);
00086 
00087 private:
00088         // private typedefs ----------------------------------------------------
00089         struct local_player_rec_t {
00090                 // constructor, manipulators
00091                 local_player_rec_t(void) throw() { this->clear(); }
00092                 void clear(void) throw() {
00093                                 m_target = 0;
00094                         }
00095 
00096                 // data fields
00097                 long            m_target;               // looking at...
00098         };
00099 
00100         typedef std::map<int, smart_ptr<local_player_rec_t> > local_map_t;
00101 
00102         // private helper methods ----------------------------------------------
00103         local_player_rec_t * getLocal(IN int playerId);
00104         bool getMapAndPlacement(IN int playerId,
00105                                 OUT MapDynamics ** dyn,
00106                                 OUT placement_t& placement);
00107 
00108         // private member data -------------------------------------------------
00109         ClientGameLogicHost *           m_host;         // weak reference
00110         local_map_t                     m_locals;       // local players
00111         float                           m_msg[7];       // TODO: genericize!
00112         int                             m_msgPlayerId;  // see above TODO
00113         bool                            m_haveMessage;
00114 };
00115 
00116 
00117 
00118 ClibGameLogic::ClibGameLogic(void)
00119 throw()
00120 {
00121         m_host = NULL;
00122         m_haveMessage = false;
00123 }
00124 
00125 
00126 
00127 ////////////////////////////////////////////////////////////////////////////////
00128 //
00129 //      ClibGameLogic -- aesop::GameLogic class interface methods
00130 //
00131 ////////////////////////////////////////////////////////////////////////////////
00132 
00133 bool
00134 ClibGameLogic::localPlayerJoined
00135 (
00136 IN int playerId
00137 )
00138 {
00139         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00140 
00141         if (!this->getLocal(playerId)) {
00142                 // we don't already know about this one!  add it...
00143                 smart_ptr<local_player_rec_t> lpr = new local_player_rec_t;
00144                 ASSERT(lpr, "out of memory");
00145 
00146                 m_locals[playerId] = lpr;
00147         }
00148 
00149         // for now, we let anyone join!
00150         return true;
00151 }
00152 
00153 
00154 
00155 void
00156 ClibGameLogic::localPlayerLeft
00157 (
00158 IN int playerId
00159 )
00160 {
00161         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00162 
00163         // nuke it if we've got it
00164         local_map_t::iterator i = m_locals.find(playerId);
00165         if (m_locals.end() != i) {
00166                 m_locals.erase(i);
00167         }
00168 }
00169 
00170 
00171 
00172 void
00173 ClibGameLogic::notifyInput
00174 (
00175 IN int playerId,
00176 IN const event_t& event
00177 )
00178 {
00179         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00180 
00181         DPRINTF("Game logic received input event");
00182 
00183         // TODO: should have a map from input buttons --> game actions
00184         // That way, players can customize controls
00185 
00186         // for this reference engine, we just hard-code button behavior
00187 
00188         if (eDevice_Gamepad == event.device) {
00189                 if (eEvent_Press == event.event) {
00190                         if (event.item >= 0) {
00191                                 DPRINTF("  Some gamepad thing was pressed!");
00192 
00193                                 // okay, get map and placement for player!
00194                                 MapDynamics * dyn;
00195                                 placement_t placement;
00196                                 if (!this->getMapAndPlacement(playerId, &dyn, placement)) {
00197                                         DPRINTF("Ignoring event: Player not in map");
00198                                         return;
00199                                 }
00200 
00201                                 // got placement!
00202 
00203                                 // TODO: have a header file with game events
00204                                 m_msg[0] = placement.position.x;
00205                                 m_msg[1] = placement.position.y;
00206                                 m_msg[2] = placement.position.z;
00207                                 m_msg[3] = placement.rotation.x;
00208                                 m_msg[4] = placement.rotation.y;
00209                                 m_msg[5] = placement.rotation.z;
00210                                 m_msg[6] = placement.rotation.w;
00211                                 m_msgPlayerId = playerId;
00212                                 m_haveMessage = true;
00213                         }
00214                 }
00215         }
00216 }
00217 
00218 
00219 
00220 void
00221 ClibGameLogic::tick
00222 (
00223 IN float dt
00224 )
00225 {
00226         ASSERT(dt >= 0, "bad dt: %f", dt);
00227 
00228 /*
00229         // loop through local players, see what they are looking at!
00230         for (local_map_t::iterator i = m_locals.begin(); i != m_locals.end();
00231              ++i) {
00232                 int playerId = i->first;
00233                 local_player_rec_t * lpr = i->second;
00234                 ASSERT(lpr, "null local player record");
00235 
00236                 // DPRINTF("Checking player %d", playerId);
00237                 MapDynamics * dyn;
00238                 placement_t placement;
00239                 if (!this->getMapAndPlacement(playerId, &dyn, placement))
00240                         continue;       // skip this player
00241 
00242                 // okay, have position and orientation!
00243                 //placement.dump("Player placement");
00244                 smart_ptr<PhysicsObject> obj =
00245                     getObjectHitFromPlacement(dyn->getPhysics(),
00246                                         placement,
00247                                         0.5);
00248 
00249                 if (obj) {
00250                         const char * typeId = obj->getInstance().typeId;
00251                         DPRINTF("Hitting instance of type: %s", typeId);
00252                 }
00253         }
00254  */
00255 }
00256 
00257 
00258 
00259 void
00260 ClibGameLogic::appendGameData
00261 (
00262 IN xdrbuf::Output * outbuf
00263 )
00264 {
00265         ASSERT(outbuf, "null");
00266 
00267         // here we have an opportunity to add our own data/events to an outbound
00268         //   UDP packet.
00269 
00270         // anything to add?
00271 
00272         // TODO: need generic message queues!
00273         if (m_haveMessage) {
00274                 m_haveMessage = false;
00275 
00276                 outbuf->openPacklet('s');               // 's' -- shoot!
00277                 int32_t l = m_msgPlayerId;
00278                 outbuf->addInt32Packlet('p', &l, 1);    // 'p' -- player ID
00279                 outbuf->addFloatPacklet('o', m_msg, 7); // 'o' -- orientation
00280                 outbuf->closePacklet('s');
00281         }
00282 
00283         // loop through and send animation state
00284         std::string animState;
00285         for (local_map_t::iterator i = m_locals.begin(); i != m_locals.end();
00286              ++i) {
00287                 int playerId = i->first;
00288 
00289                 // get instance for this player
00290                 smart_ptr<Instance> instance =
00291                     m_host->getPlayerInstance(playerId);
00292                 if (!instance) {
00293                         //DPRINTF("No instance?");
00294                         continue;       // this player has no instance yet
00295                 }
00296 
00297                 // get model for this player
00298                 smart_ptr<glut::Renderable> model = getModel(instance);
00299                 if (!model) {
00300                         //DPRINTF("No model?");
00301                         continue;       // player has no model yet
00302                 }
00303 
00304                 if (!model->isAnimateable()) {
00305                         //DPRINTF("Model does not animate?");
00306                         continue;
00307                 }
00308                 model->getAnimationState(animState);
00309 
00310                 // okay, have animation state!  send to server
00311                 outbuf->openPacklet('a');               // 'a' -- animation
00312                 int32_t pid32 = playerId;
00313                 outbuf->addInt32Packlet('p', &pid32, 1);// 'p' -- player ID
00314                 int nLen = animState.length();
00315                 outbuf->addStringPacklet('s', animState.c_str(), nLen);
00316                 outbuf->closePacklet('a');
00317         }
00318 }
00319 
00320 
00321 
00322 ////////////////////////////////////////////////////////////////////////////////
00323 //
00324 //      ClibGameLogic -- private helper methods
00325 //
00326 ////////////////////////////////////////////////////////////////////////////////
00327 
00328 ClibGameLogic::local_player_rec_t *
00329 ClibGameLogic::getLocal
00330 (
00331 IN int playerId
00332 )
00333 {
00334         ASSERT(playerId > 0, "bad player id: %d", playerId);
00335 
00336         local_map_t::iterator i = m_locals.find(playerId);
00337         if (m_locals.end() == i)
00338                 return NULL;
00339 
00340         ASSERT(i->second, "null local player record in map");
00341         return i->second;
00342 }
00343 
00344 
00345 
00346 bool
00347 ClibGameLogic::getMapAndPlacement
00348 (
00349 IN int playerId,
00350 OUT MapDynamics ** dyn,
00351 OUT placement_t& placement
00352 )
00353 {
00354         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00355         ASSERT(dyn, "null pointer to pointer!");
00356         ASSERT(m_host, "null");
00357         placement.clear();
00358 
00359         // see if player is even in a map
00360         *dyn = m_host->getMapForPlayer(playerId);
00361         if (!*dyn)
00362                 return false;   // player isn't in map
00363 
00364         // get position and orientation of this player
00365         if (!m_host->getPlacement(playerId, placement))
00366                 return false;   // skip this player
00367 
00368         // got a map and placement
00369         return true;
00370 }
00371 
00372 
00373 
00374 ////////////////////////////////////////////////////////////////////////////////
00375 //
00376 //      public API
00377 //
00378 ////////////////////////////////////////////////////////////////////////////////
00379 
00380 smart_ptr<ClientGameLogic>
00381 createClientGameLogic
00382 (
00383 void
00384 )
00385 {
00386         smart_ptr<ClibGameLogic> rp = new ClibGameLogic;
00387         ASSERT(rp, "out of memory");
00388 
00389         rp->initialize();
00390 
00391         return rp;
00392 }
00393 
00394 
00395 };      // aesop namespace
00396