application.cpp

Go to the documentation of this file.
00001 /*
00002  * application.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  * See application.h
00031  */
00032 
00033 // includes --------------------------------------------------------------------
00034 #include "application.h"                // always include our own header first!
00035 #include "client-converse.h"
00036 #include "terminal.h"
00037 
00038 #include "common/wave_ex.h"
00039 #include "aesop-clib/aesop-client.h"
00040 #include "datahash/datahash_text.h"
00041 #include "datahash/datahash_util.h"
00042 #include "gamepad/manager.h"
00043 #include "gamepad-config-dialog/gamepad-config-dialog.h"
00044 #include "glut/glut.h"
00045 #include "map-kdtree/map-kdtree.h"
00046 #include "model-loader/model-loader.h"
00047 #include "netlib/netlib.h"
00048 #include "perf/perf.h"
00049 #include "story/story.h"
00050 #include "viewport/viewport.h"
00051 
00052 
00053 namespace aesop {
00054 
00055 
00056 // interface class destructor
00057 Application::~Application(void) throw() { }
00058 ClientGameLogic::~ClientGameLogic(void) throw() { }
00059 ClientGameLogicHost::~ClientGameLogicHost(void) throw() { }
00060 
00061 
00062 static const int s_nMaxQueueSlots               = 256;
00063 
00064 static const int s_rediscoverEvery              = 128;
00065 
00066 
00067 // every player has one of these...
00068 struct player_record_t {
00069         // constructor, manipulators
00070         player_record_t(void) throw() { this->clear(); }
00071         ~player_record_t(void) throw() { }
00072         void clear(void) throw() {
00073                         terminal = NULL;
00074                         instance = NULL;
00075                         gamepadId.clear();
00076                         map = NULL;
00077                         playerId = 0;
00078                 }
00079 
00080         // data fields
00081         smart_ptr<Terminal>     terminal;
00082         smart_ptr<Instance>     instance;
00083         std::string             gamepadId;
00084         int                     playerId;
00085         MapKdTree *             map;
00086 };
00087 
00088 
00089 typedef std::map<int, smart_ptr<player_record_t> > player_map_t;
00090 
00091 
00092 ////////////////////////////////////////////////////////////////////////////////
00093 //
00094 //      static helper methods
00095 //
00096 ////////////////////////////////////////////////////////////////////////////////
00097 
00098 
00099 ////////////////////////////////////////////////////////////////////////////////
00100 //
00101 //      AppImpl - class that implements the Application interface
00102 //
00103 ////////////////////////////////////////////////////////////////////////////////
00104 
00105 class AppImpl : public Application,
00106                         public ClientHost,
00107                         public ClientGameLogicHost,
00108                         public TerminalHost {
00109 public:
00110         AppImpl(void) throw();
00111         ~AppImpl(void) throw() {
00112                         DPRINTF("Destroying application");
00113                         m_viewportManager = NULL;
00114                         m_players.clear();
00115                         DPRINTF("Client...");
00116                         m_client = NULL;
00117                         DPRINTF("App done");
00118                 }
00119 
00120         // public class methods ------------------------------------------------
00121         void initialize(IN const char * config_dir,
00122                                 IN smart_ptr<Datahash>& params,
00123                                 IN smart_ptr<ClientGameLogic>& gameLogic,
00124                                 IN smart_ptr<story::Story>& story);
00125 
00126         // aesop::Application class interface methods --------------------------
00127         story::Story * getStory(void);
00128         bool getPlayerStats(IN int playerId,
00129                                 OUT player_stats_t& stats);
00130         void takeKeyboard(IN int playerId);
00131         void newGame(IN int playerId);
00132         void addPlayer(void);
00133         bool dropPlayer(IN int playerId);
00134         void configureControls(IN int playerId);
00135         const server_map_t& getServers(void);
00136         bool requestConnect(IN const char * serverKey);
00137 
00138         // glut::Host class interface methods ----------------------------------
00139         void init(void);
00140         eBehavior tick(void);
00141         void display(IN int width, IN int height);
00142         eBehavior mouseMove(IN int x, IN int y);
00143         eBehavior mouseButton(IN int button, IN int state, IN int x, IN int y);
00144         eBehavior keyboard(IN int key, IN int mods);
00145         int shutdown(void);
00146 
00147         // aesop::ClientHost class interface methods ---------------------------
00148         void notifyKey(IN smart_ptr<crypto::DESKey>& desKey);
00149         void requestDialog(IN const char * id,
00150                                 IN int playerId,
00151                                 IN const Datahash * dialog,
00152                                 IN dialog::Host * host);
00153         void destroyDialog(IN const char * id,
00154                                 IN int playerId);
00155         void notifyPlayerMap(IN int playerId,
00156                                 IN smart_ptr<MapDynamics>& dyn);
00157         void appendGameData(IN xdrbuf::Output * outbuf);
00158         void updateAnimation(IN smart_ptr<PhysicsObject>& obj,
00159                                 IN const char * animationState);
00160 
00161         // aesop::ClientGameLogicHost class interface methods ------------------
00162         MapDynamics * getMapForPlayer(IN int playerId);
00163         bool getPlacement(IN int playerId,
00164                                 OUT placement_t& placement);
00165         smart_ptr<Instance> getPlayerInstance(IN int playerId);
00166 
00167         // aesop::TerminalHost class interface methods -------------------------
00168         void requestMove(IN int playerId,
00169                                 IN const point3d_t& req_pos,
00170                                 IN const point3d_t& euler,
00171                                 OUT point3d_t& newPosition);
00172         void notifyInput(IN int playerId,
00173                                 IN const event_t& event);
00174 
00175 private:
00176         // private typedefs ----------------------------------------------------
00177         typedef std::map<smart_ptr<MapDynamics>, smart_ptr<MapKdTree> >
00178                 kdtree_map_t;
00179 
00180         // private helper methods ----------------------------------------------
00181         player_record_t * getPlayer(IN int playerId);
00182         player_record_t * getPlayer(IN int x, IN int y,
00183                                 OUT int& ownerX, OUT int& ownerY);
00184         void doSystemMenu(IN player_record_t * prec);
00185         int getNewPlayerId(void);
00186         player_record_t * addGamepad(IN const char * gamepadId);
00187 
00188         // private data members ------------------------------------------------
00189         smart_ptr<Client>                       m_client;
00190         smart_ptr<view::Manager>                m_viewportManager;
00191         smart_ptr<crypto::DESKey>               m_desKey;
00192         smart_ptr<perf::Timer>                  m_timer;
00193         smart_ptr<ClientGameLogic>              m_gameLogic;
00194         smart_ptr<glut::RenderQueue>            m_rQueue;
00195         smart_ptr<gamepad::Manager>             m_gamepadMgr;
00196         player_map_t                            m_players;
00197         kdtree_map_t                            m_kdTrees;
00198         int                                     m_keyboardOwner;
00199         int                                     m_width;
00200         int                                     m_height;
00201         int                                     m_playerCounter;
00202         eBehavior                               m_behavior;
00203         int                                     m_tickCounter;
00204 };
00205 
00206 
00207 
00208 AppImpl::AppImpl(void) throw()
00209 {
00210         m_keyboardOwner = 0;            // no one gets the keyboard!
00211         m_width = 128;                  // starting values
00212         m_height = 128;
00213         m_behavior = eContinue;
00214         m_playerCounter = 0;            // no players yet
00215         m_tickCounter = 0;
00216 }
00217 
00218 
00219 
00220 void
00221 AppImpl::initialize
00222 (
00223 IN const char * config_dir,
00224 IN smart_ptr<Datahash>& params,
00225 IN smart_ptr<ClientGameLogic>& gameLogic,
00226 IN smart_ptr<story::Story>& story
00227 )
00228 {
00229         ASSERT(config_dir, "null");
00230         ASSERT(params, "null");
00231         ASSERT(gameLogic, "null");
00232         ASSERT(story, "null");
00233         ASSERT(!m_playerCounter, "have players?");
00234 
00235         // overall timer
00236         m_timer = new perf::Timer("overall application timer");
00237         ASSERT(m_timer, "out of memory");
00238 
00239         // create aesop client
00240         ASSERT(!m_client, "Already have a client?");
00241         m_client = Client::create(this, story, params);
00242         ASSERT(m_client, "failed to create client");
00243 
00244         // save game logic
00245         ASSERT(!m_gameLogic, "already have game logic");
00246         m_gameLogic = gameLogic;
00247         m_gameLogic->setHost(this);
00248 
00249         // initialize gamepads
00250         const char * dataDir = getString(params, "gamepadDataDir");
00251         ASSERT_THROW(dataDir, "no data directory for gamepads?");
00252 
00253         smart_ptr<nstream::Manager> fsMgr =
00254             nstream::getFilesystemManager(dataDir);
00255         ASSERT(fsMgr, "failed to create filesystem stream manager");
00256         smart_ptr<nstream::Folder> root = fsMgr->getRoot();
00257         ASSERT(root, "null");
00258 
00259         m_gamepadMgr = gamepad::Manager::create(root);
00260         ASSERT(m_gamepadMgr, "failed to create gamepad manager");
00261 
00262         // construct viewport manager
00263         ASSERT(!m_viewportManager, "already have a viewport manager?");
00264         m_viewportManager = view::Manager::create();
00265         ASSERT(m_viewportManager, "failed to create viewport manager object");
00266 
00267         // create render queue for transparent polygons
00268         ASSERT(!m_rQueue, "already have a render queue?");
00269         m_rQueue = glut::RenderQueue::create(s_nMaxQueueSlots);
00270         ASSERT(m_rQueue, "failed to create render queue");
00271 
00272         // last thing: create player!
00273         this->addPlayer();
00274 }
00275 
00276 
00277 
00278 ////////////////////////////////////////////////////////////////////////////////
00279 //
00280 //      AppImpl -- aesop::Application class interface methods
00281 //
00282 ////////////////////////////////////////////////////////////////////////////////
00283 
00284 story::Story *
00285 AppImpl::getStory
00286 (
00287 void
00288 )
00289 {
00290         ASSERT(m_client, "null");
00291 
00292         return m_client->getStory();
00293 }
00294 
00295 
00296 
00297 bool
00298 AppImpl::getPlayerStats
00299 (
00300 IN int playerId,
00301 OUT player_stats_t& stats
00302 )
00303 {
00304         ASSERT(playerId > 0, "bad player id: %d", playerId);
00305         ASSERT(m_client, "null");
00306         stats.clear();
00307 
00308         player_record_t * prec = this->getPlayer(playerId);
00309         if (!prec)
00310                 return false;   // no such player?
00311 
00312         stats.haveKeyboard = (playerId == m_keyboardOwner);
00313         stats.clientState = m_client->getState();
00314         if (!prec->gamepadId.empty()) {
00315                 stats.gamepad =
00316                     m_gamepadMgr->getGamepad(prec->gamepadId.c_str());
00317         }
00318 
00319         return true;
00320 }
00321 
00322 
00323 
00324 void
00325 AppImpl::takeKeyboard
00326 (
00327 IN int playerId
00328 )
00329 {
00330         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00331 
00332         m_keyboardOwner = playerId;
00333 }
00334 
00335 
00336 
00337 void
00338 AppImpl::newGame
00339 (
00340 IN int playerId
00341 )
00342 {
00343         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00344         ASSERT(m_client, "no client?");
00345 
00346         m_client->newGame(playerId);
00347 }
00348 
00349 
00350 
00351 void
00352 AppImpl::addPlayer
00353 (
00354 void
00355 )
00356 {
00357         ASSERT(m_rQueue, "null");
00358 
00359         // create new player record
00360         DPRINTF("New app player");
00361         smart_ptr<player_record_t> player = new player_record_t;
00362         ASSERT(player, "out of memory");
00363 
00364         int id = this->getNewPlayerId();
00365         DPRINTF("Creating new player id: %d", id);
00366 
00367         // new terminal
00368         player->terminal = Terminal::create(this, id, m_rQueue);
00369         ASSERT(player->terminal, "failed to create terminal for new player");
00370 
00371         // update with current key
00372         player->terminal->setKeys(m_desKey, m_gamepadMgr);
00373 
00374         // will client allow it?
00375         ASSERT(m_client, "no client?");
00376         if (!m_client->createPlayer(id)) {
00377                 WAVE_EX(wex);
00378                 wex << "player already exists?";
00379         }
00380         player->playerId = id;
00381 
00382         // will game logic allow it?
00383         if (!m_gameLogic->localPlayerJoined(player->playerId)) {
00384                 DPRINTF("Game logic disallowed join of player: %d",
00385                     player->playerId);
00386                 return;
00387         }
00388 
00389         // create new viewport
00390         ASSERT(m_viewportManager->createViewport(id, player->terminal),
00391             "Failed to create new viewport for player");
00392 
00393         // complete registration
00394         m_players[id] = player;
00395         DPRINTF("There are now %d local players", (int) m_players.size());
00396 
00397         // player gets keyboard if no one else has it
00398         if (!m_keyboardOwner) {
00399                 m_keyboardOwner = id;
00400         }
00401 }
00402 
00403 
00404 
00405 bool
00406 AppImpl::dropPlayer
00407 (
00408 IN int playerId
00409 )
00410 {
00411         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00412 
00413         player_map_t::iterator i = m_players.find(playerId);
00414         if (m_players.end() == i) {
00415                 DPRINTF("Player ID not found? %d", playerId);
00416                 return false;
00417         }
00418 
00419         if (!m_viewportManager->removeViewport(playerId)) {
00420                 DPRINTF("WARNING: viewport mgr didn't know about player? %d",
00421                     playerId);
00422         }
00423 
00424         m_players.erase(i);
00425 
00426         if (!m_players.size()) {
00427                 DPRINTF("Everyone has left the game!");
00428                 m_behavior = eExit;
00429         }
00430 
00431         return true;
00432 }
00433 
00434 
00435 
00436 void
00437 AppImpl::configureControls
00438 (
00439 IN int playerId
00440 )
00441 {
00442         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00443         ASSERT(m_gamepadMgr, "null");
00444 
00445         player_record_t * pr = this->getPlayer(playerId);
00446         if (!pr) {
00447                 return;
00448         }
00449 
00450         // construct GUID
00451         char guid[128];
00452         sprintf(guid, "LOCAL-ctlconfig-%d", pr->playerId);
00453 
00454         // already going?
00455         if (m_client->isConversationUnderway(guid))
00456                 return;         // already going!
00457 
00458         // have a gamepad already?
00459         int gamepadId = -1;
00460         if (!pr->gamepadId.empty()) {
00461                 const char * id = pr->gamepadId.c_str();
00462                 int nGamepads = m_gamepadMgr->getGamepadCount();
00463                 for (int i = 0; i < nGamepads; ++i) {
00464                         smart_ptr<gamepad::Gamepad> gp =
00465                             m_gamepadMgr->getGamepad(i);
00466                         if (!strcmp(gp->getId(), id)) {
00467                                 gamepadId = i;
00468                                 break;
00469                         }
00470                 }
00471         }
00472 
00473         // create a host
00474         smart_ptr<converse::ConversationHost> host =
00475             gamepad::createConfigurationHost(m_gamepadMgr, gamepadId);
00476         ASSERT(host, "failed to create gamepad configuration host");
00477 
00478         // start conversation
00479         m_client->startLocalConversation(guid, pr->playerId, host);
00480 }
00481 
00482 
00483 
00484 const server_map_t&
00485 AppImpl::getServers
00486 (
00487 void
00488 )
00489 {
00490         ASSERT(m_client, "null");
00491 
00492         return m_client->getDiscoveredServers();
00493 }
00494 
00495 
00496 
00497 bool
00498 AppImpl::requestConnect
00499 (
00500 IN const char * serverKey
00501 )
00502 {
00503         ASSERT(serverKey, "null");
00504 
00505         DPRINTF("User wants to connect to server: '%s'", serverKey);
00506 
00507         if (eClientState_Searching != m_client->getState()) {
00508                 DPRINTF("Cannot select server--already connected!");
00509                 return false;
00510         }
00511 
00512         ASSERT(m_client, "null");
00513         return m_client->requestConnect(serverKey);
00514 }
00515 
00516 
00517 
00518 ////////////////////////////////////////////////////////////////////////////////
00519 //
00520 //      AppImpl -- glut::Host class interface methods
00521 //
00522 ////////////////////////////////////////////////////////////////////////////////
00523 
00524 void
00525 AppImpl::init
00526 (
00527 void
00528 )
00529 {
00530         glShadeModel(GL_SMOOTH);
00531         glFrontFace(GL_CCW);
00532         glCullFace(GL_BACK);
00533 }
00534 
00535 
00536 
00537 Application::eBehavior
00538 AppImpl::tick
00539 (
00540 void
00541 )
00542 {
00543         perf::Timer timer("client::tick");
00544         ASSERT(m_client, "no client?");
00545         ASSERT(m_gamepadMgr, "no gamepad manager?");
00546 
00547         // handle state
00548         float dt = m_client->tick();
00549 
00550         ++m_tickCounter;
00551 
00552         // rediscover devices?
00553         if (0 == (m_tickCounter % s_rediscoverEvery)) {
00554                 if (m_gamepadMgr->rediscover()) {
00555                         gamepad::autoconfigureGamepads(m_gamepadMgr);
00556                 }
00557         }
00558 
00559         // see if we've got any gamepad input
00560         m_gamepadMgr->update();
00561 
00562         // anyone want in?
00563         int nGamepads = m_gamepadMgr->getGamepadCount();
00564         for (int i = 0; i < nGamepads; ++i) {
00565                 smart_ptr<gamepad::Gamepad> gp = m_gamepadMgr->getGamepad(i);
00566                 if (!gp)
00567                         continue;       // gamepad is now disconnected?
00568 
00569                 // get type
00570                 gamepad::Type * type = gp->getType();
00571                 ASSERT(type, "null type for gamepad");
00572 
00573                 // get the index for the buttons we care about
00574                 int iHome = type->getLogicalIndex("home", gamepad::eInput_Button);
00575                 int iStart = type->getLogicalIndex("start", gamepad::eInput_Button);
00576 
00577                 // was either the start or home button pushed?
00578                 if ((iHome >= 0 &&
00579                      (gamepad::eButtonWasReleased & gp->getButton(iHome))) ||
00580                     (iStart >= 0 &&
00581                      (gamepad::eButtonWasReleased & gp->getButton(iStart)))) {
00582                         player_record_t * pr = this->addGamepad(gp->getId());
00583                         if (pr) {
00584                                 // player already owns this one: system menu
00585                                 this->doSystemMenu(pr);
00586                         }
00587                 }
00588         }
00589 
00590         // route input to all attached terminals
00591         for (player_map_t::iterator i = m_players.begin(); i != m_players.end();
00592              ++i) {
00593                 player_record_t * prec = i->second;
00594                 // is gamepad still valid?
00595                 smart_ptr<gamepad::Gamepad> gp;
00596                 if (!prec->gamepadId.empty()) {
00597                         gp = m_gamepadMgr->getGamepad(prec->gamepadId.c_str());
00598                         if (!gp) {
00599                                 DPRINTF("Player %d has lost gamepad!",
00600                                     i->first);
00601                                 prec->gamepadId.clear();
00602                         }
00603                 }
00604                 prec->terminal->input(gp, dt);
00605         }
00606 
00607         // let game logic have a crack
00608         {
00609                 perf::Timer timer("client game logic--tick()");
00610                 m_gameLogic->tick(dt);
00611         }
00612 
00613         // keep going!
00614         return m_behavior;
00615 }
00616 
00617 
00618 
00619 void
00620 AppImpl::display
00621 (
00622 IN int width,
00623 IN int height
00624 )
00625 {
00626         ASSERT(width > 0, "Bad display window width: %d", width);
00627         ASSERT(height > 0, "Bad display window height: %d", height);
00628         //DPRINTF("Application: %d x %d", width, height);
00629 
00630         // update our last-seen height and width!
00631         m_width = width;
00632         m_height = height;
00633 
00634         m_viewportManager->render(width, height);
00635 }
00636 
00637 
00638 
00639 Application::eBehavior
00640 AppImpl::mouseMove
00641 (
00642 IN int x,
00643 IN int y
00644 )
00645 {
00646         int oX, oY;     // owner x and y
00647         player_record_t * pr = this->getPlayer(x, y, oX, oY);
00648         if (!pr)
00649                 return m_behavior;      // no one owns
00650 
00651         pr->terminal->mouseMove(oX, oY);
00652 
00653         return m_behavior;
00654 }
00655 
00656 
00657 
00658 Application::eBehavior
00659 AppImpl::mouseButton
00660 (
00661 IN int button,
00662 IN int state,
00663 IN int x,
00664 IN int y
00665 )
00666 {
00667         int oX, oY;     // owner x and y
00668         player_record_t * pr = this->getPlayer(x, y, oX, oY);
00669         if (!pr)
00670                 return m_behavior;      // no one owns
00671 
00672         // intercept right-clicks: these are system menu requests
00673         if (2 == button) {
00674                 if (state) {
00675                         this->doSystemMenu(pr);
00676                 }
00677         } else {
00678                 // not a right-click
00679                 pr->terminal->mouseButton(button, state, oX, oY);
00680         }
00681 
00682         return m_behavior;
00683 }
00684 
00685 
00686 
00687 Application::eBehavior
00688 AppImpl::keyboard
00689 (
00690 IN int key,
00691 IN int mods
00692 )
00693 {
00694         // who has the keyboard?
00695         if (!m_keyboardOwner)
00696                 return m_behavior;
00697 
00698         player_record_t * pr = this->getPlayer(m_keyboardOwner);
00699         if (!pr) {
00700                 m_keyboardOwner = 0;    // must be stale!
00701                 return m_behavior;
00702         }
00703 
00704         ASSERT(pr->terminal, "null");
00705         pr->terminal->keyboard(key, mods);
00706         return m_behavior;
00707 }
00708 
00709 
00710 
00711 int
00712 AppImpl::shutdown
00713 (
00714 void
00715 )
00716 {
00717         // display network stats
00718         netlib::dumpStats();
00719 
00720         // display timing information
00721         m_timer = NULL;         // nuke overall timer if we had one
00722         perf::dumpTimingSummary(std::cerr);
00723 
00724         // for now, we don't return any error codes
00725         return 0;
00726 }
00727 
00728 
00729 
00730 ////////////////////////////////////////////////////////////////////////////////
00731 //
00732 //      AppImpl -- aesop::ClientHost class interface methods
00733 //
00734 ////////////////////////////////////////////////////////////////////////////////
00735 
00736 void
00737 AppImpl::notifyKey
00738 (
00739 IN smart_ptr<crypto::DESKey>& desKey
00740 )
00741 {
00742         ASSERT(desKey, "null");
00743 
00744         // TODO: if a new key is supplied, other components (such as the
00745         // dialog manager for each Terminal) need to be updated!
00746         if (m_desKey) {
00747                 WAVE_EX(wex);
00748                 DPRINTF("ERROR: new symmetric encryption key.");
00749                 DPRINTF("  Components will probably fail!");
00750                 wex << "FAILURE: cannot handled updated encryption keys.";
00751         }
00752 
00753         m_desKey = desKey;
00754 }
00755 
00756 
00757 
00758 void
00759 AppImpl::requestDialog
00760 (
00761 IN const char * id,
00762 IN int playerId,
00763 IN const Datahash * dialog,
00764 IN dialog::Host * host
00765 )
00766 {
00767         ASSERT(id, "null");
00768         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00769         ASSERT(dialog, "null");
00770         ASSERT(host, "null");
00771 
00772         player_record_t * player = this->getPlayer(playerId);
00773         if (!player) {
00774                 DPRINTF("Got dialog request for non-existent player?");
00775                 return;
00776         }
00777         ASSERT(player->terminal, "null");
00778 
00779         player->terminal->showDialog(id, dialog, host);
00780 }
00781 
00782 
00783 
00784 void
00785 AppImpl::destroyDialog
00786 (
00787 IN const char * id,
00788 IN int playerId
00789 )
00790 {
00791         ASSERT(id, "null");
00792         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00793 
00794         player_record_t * player = this->getPlayer(playerId);
00795         if (!player) {
00796                 // this can happen as players exit the game...
00797                 // DPRINTF("Destroy dialog for non-existent player?");
00798                 return;
00799         }
00800         ASSERT(player->terminal, "null");
00801 
00802         player->terminal->destroyDialog(id);
00803 }
00804 
00805 
00806 
00807 void
00808 AppImpl::notifyPlayerMap
00809 (
00810 IN int playerId,
00811 IN smart_ptr<MapDynamics>& dyn
00812 )
00813 {
00814         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00815         // ASSERT(dyn) -- can be null!
00816 
00817         player_record_t * pr = this->getPlayer(playerId);
00818         if (!pr) {
00819                 WAVE_EX(wex);
00820                 wex << "No such player? " << playerId;
00821         }
00822 
00823         // look up the kd tree for this map
00824         smart_ptr<MapKdTree> mapKdTree;         // null to start
00825         if (dyn) {
00826                 kdtree_map_t::iterator i = m_kdTrees.find(dyn);
00827                 if (m_kdTrees.end() != i) {
00828                         mapKdTree = i->second;
00829                         ASSERT(mapKdTree, "null kdtree in map");
00830                 } else {
00831                         DPRINTF("Need to create kd-tree for map!");
00832                         mapKdTree = MapKdTree::create(dyn);
00833                         ASSERT(mapKdTree, "failed to create kdtree");
00834                         m_kdTrees[dyn] = mapKdTree;
00835                 }
00836         }
00837 
00838         // update our record
00839         pr->map = mapKdTree;
00840 
00841         // ASSERT(dyn) -- can be null!  Player not in map...
00842         if (pr->terminal) {
00843                 pr->terminal->notifyMap(mapKdTree);
00844 
00845                 // are we getting/losing instance?
00846                 if (!dyn) {
00847                         // losing map!
00848                         pr->instance = NULL;
00849                 } else {
00850                         smart_ptr<PhysicsObject> obj =
00851                             m_client->getPlayerObject(playerId);
00852                         if (obj) {
00853                                 pr->instance =
00854                                     getInstanceFromPhysicsObject(obj);
00855                         }
00856                 }
00857                 pr->terminal->notifyInstance(pr->instance);
00858         }
00859 }
00860 
00861 
00862 
00863 void
00864 AppImpl::appendGameData
00865 (
00866 IN xdrbuf::Output * outbuf
00867 )
00868 {
00869         ASSERT(outbuf, "null");
00870 
00871         // the client object is letting us add additional data to an outbound
00872         //   UDP packet.  Delegate to the game logic object, if we have one.
00873         if (m_gameLogic) {
00874                 m_gameLogic->appendGameData(outbuf);
00875         }
00876 }
00877 
00878 
00879 
00880 void
00881 AppImpl::updateAnimation
00882 (
00883 IN smart_ptr<PhysicsObject>& obj,
00884 IN const char * animationState
00885 )
00886 {
00887         ASSERT(obj, "null");
00888         ASSERT(animationState, "can be empty but not null");
00889 
00890         smart_ptr<Instance> instance = getInstanceFromPhysicsObject(obj);
00891         if (!instance)
00892                 return;
00893 
00894         smart_ptr<glut::Renderable> model = getModel(instance);
00895         if (!model)
00896                 return;
00897 
00898         model->setAnimationState(animationState);
00899 }
00900 
00901 
00902 
00903 ////////////////////////////////////////////////////////////////////////////////
00904 //
00905 //      AppImpl -- aesop::ClientGameLogic class interface methods
00906 //
00907 ////////////////////////////////////////////////////////////////////////////////
00908 
00909 MapDynamics *
00910 AppImpl::getMapForPlayer
00911 (
00912 IN int playerId
00913 )
00914 {
00915         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00916 
00917         player_record_t * pr = this->getPlayer(playerId);
00918         if (!pr || !pr->map) {
00919                 return NULL;    // no player, or no map
00920         }
00921         return pr->map->getMapDynamics();
00922 }
00923 
00924 
00925 
00926 bool
00927 AppImpl::getPlacement
00928 (
00929 IN int playerId,
00930 OUT placement_t& placement
00931 )
00932 {
00933         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00934         return m_client->getPlacement(playerId, placement);
00935 }
00936 
00937 
00938 
00939 smart_ptr<Instance>
00940 AppImpl::getPlayerInstance
00941 (
00942 IN int playerId
00943 )
00944 {
00945         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00946 
00947         player_record_t * pr = this->getPlayer(playerId);
00948         if (!pr)
00949                 return NULL;
00950 
00951         return pr->instance;
00952 }
00953 
00954 
00955 
00956 ////////////////////////////////////////////////////////////////////////////////
00957 //
00958 //      AppImpl -- aesop::TerminalHost class interface methods
00959 //
00960 ////////////////////////////////////////////////////////////////////////////////
00961 
00962 void
00963 AppImpl::requestMove
00964 (
00965 IN int playerId,
00966 IN const point3d_t& req_pos,
00967 IN const point3d_t& euler,
00968 OUT point3d_t& newPosition
00969 )
00970 {
00971         ASSERT(playerId > 0, "Bad player id: %d", playerId);
00972         m_client->requestMove(playerId, req_pos, euler, newPosition);
00973 }
00974 
00975 
00976 
00977 void
00978 AppImpl::notifyInput
00979 (
00980 IN int playerId,
00981 IN const event_t& event
00982 )
00983 {
00984 //      DPRINTF("Got a player input event!");
00985 
00986         // delegate to local (client-side) game logic
00987         m_gameLogic->notifyInput(playerId, event);
00988 }
00989 
00990 
00991 
00992 ////////////////////////////////////////////////////////////////////////////////
00993 //
00994 //      AppImpl -- private helper methods
00995 //
00996 ////////////////////////////////////////////////////////////////////////////////
00997 
00998 player_record_t *
00999 AppImpl::getPlayer
01000 (
01001 IN int playerId
01002 )
01003 {
01004         ASSERT(playerId > 0, "Bad player id: %d", playerId);
01005 
01006         player_map_t::iterator i = m_players.find(playerId);
01007         if (m_players.end() == i) {
01008                 return NULL;
01009         }
01010         return i->second;
01011 }
01012 
01013 
01014 
01015 player_record_t *
01016 AppImpl::getPlayer
01017 (
01018 IN int x,
01019 IN int y,
01020 OUT int& ownerX,
01021 OUT int& ownerY
01022 )
01023 {
01024         int playerId =
01025             m_viewportManager->getOwnerOfCoordinate(x, y, m_width, m_height,
01026                 ownerX, ownerY);
01027         if (-1 == playerId)
01028                 return NULL;    // no one owns this
01029 
01030         // get player record
01031         return this->getPlayer(playerId);
01032 }
01033 
01034 
01035 
01036 void
01037 AppImpl::doSystemMenu
01038 (
01039 IN player_record_t * prec
01040 )
01041 {
01042         ASSERT(prec, "null");
01043         ASSERT(m_client, "null");
01044 
01045         // construct GUID
01046         char guid[128];
01047         sprintf(guid, "LOCAL-sysmenu-%d", prec->playerId);
01048 
01049         // already going?
01050         if (m_client->isConversationUnderway(guid))
01051                 return;         // already going!
01052 
01053         DPRINTF("Attempting system menu...");
01054         // create a host
01055         smart_ptr<converse::ConversationHost> host =
01056             createSystemMenuHost(prec->playerId, this);
01057         ASSERT(host, "null");
01058 
01059         // start conversation
01060         m_client->startLocalConversation(guid, prec->playerId, host);
01061 }
01062 
01063 
01064 
01065 int
01066 AppImpl::getNewPlayerId
01067 (
01068 void
01069 )
01070 {
01071         ++m_playerCounter;
01072         return m_playerCounter;
01073 }
01074 
01075 
01076 
01077 player_record_t *
01078 AppImpl::addGamepad
01079 (
01080 IN const char * id
01081 )
01082 {
01083         ASSERT(id, "null");
01084 
01085         // does anyone already own this?
01086         for (player_map_t::iterator i = m_players.begin(); i != m_players.end();
01087              ++i) {
01088                 player_record_t * pr = i->second;
01089                 if (pr->gamepadId == id) {
01090                         // DPRINTF("Gamepad is in use...");
01091                         return pr;
01092                 }
01093         }
01094 
01095         // if we got here, no one owns this gamepad yet!
01096         // give it to the first open player
01097         DPRINTF("Found an unowned gamepad!");
01098         for (player_map_t::iterator i = m_players.begin(); i != m_players.end();
01099              ++i) {
01100                 player_record_t * pr = i->second;
01101                 if (pr->gamepadId.empty()) {
01102                         pr->gamepadId = id;
01103                         DPRINTF("Giving gamepad to player %d", i->first);
01104                         this->configureControls(i->first);
01105                         return NULL;
01106                 }
01107         }
01108 
01109         // if we got here, all players already have gamepads!
01110         this->addPlayer();
01111 
01112         // give this to the most recently added player
01113         player_map_t::reverse_iterator i = m_players.rbegin();
01114         if (m_players.rend() != i) {
01115                 player_record_t * pr = i->second;
01116                 if (pr->gamepadId.empty()) {
01117                         pr->gamepadId = id;
01118                         this->configureControls(i->first);
01119                         return NULL;
01120                 }
01121         }
01122         DPRINTF("Unable to give gamepad to anyone!");
01123         return NULL;
01124 }
01125 
01126 
01127 ////////////////////////////////////////////////////////////////////////////////
01128 //
01129 //      factory methods
01130 //
01131 ////////////////////////////////////////////////////////////////////////////////
01132 
01133 smart_ptr<Application>
01134 Application::create
01135 (
01136 IN const char * config_dir,
01137 IN smart_ptr<Datahash>& params,
01138 IN smart_ptr<ClientGameLogic>& gameLogic,
01139 IN smart_ptr<story::Story>& story
01140 )
01141 {
01142         ASSERT(config_dir, "null");
01143         ASSERT(params, "null");
01144         ASSERT(gameLogic, "null");
01145         ASSERT(story, "null");
01146 
01147         smart_ptr<AppImpl> local = new AppImpl;
01148         ASSERT(local, "null");
01149 
01150         local->initialize(config_dir, params, gameLogic, story);
01151 
01152         return local;
01153 }
01154 
01155 
01156 
01157 };      // aesop namespace
01158