srv-physics.cpp

Go to the documentation of this file.
00001 /*
00002  * srv-physics.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  * Helpful routines for managing and communicating physics state back down
00031  * to the client.  See srv-physics.h
00032  */
00033 
00034 // includes -------------------------------------------------------------------
00035 #include "srv-physics.h"                // always include our own header first!
00036 
00037 #include "srv-hosts.h"
00038 #include "srv-object.h"
00039 #include "srv-players.h"
00040 
00041 #include "aesop-physics/aesop-physics.h"
00042 #include "map-manager/map-manager.h"
00043 
00044 #include "netrq/netrq.h"
00045 
00046 
00047 
00048 namespace aesop {
00049 
00050 
00051 static const int s_maxPorIdBytes                = 64;
00052 
00053 
00054 // Size of a Physics Object Request:
00055 //      id:                     1 long   =  4 bytes
00056 //      position:               3 floats = 12 bytes
00057 //      orientation:            4 floats = 16 bytes
00058 //      linear velocity:        3 floats = 12 bytes
00059 //      angular velocity:       3 floats = 12 bytes
00060 //              TOTAL DATA                 56
00061 //      packlet headers         7 headers= 14 bytes
00062 //              TOTAL                      70
00063 static const int s_maxPORBytes                  = 70;
00064 
00065 
00066 struct object_context_t {
00067         object_context_t(void) throw() { this->clear(); }
00068         void clear(void) throw() {
00069                         dyn = NULL;
00070                         queue = NULL;
00071                         clock = 0;
00072                         recipient = 0;
00073                 }
00074 
00075         // data fields
00076         MapDynamics *           dyn;
00077         netrq::Queue *          queue;
00078         dword_t                 clock;
00079         conn_id_t               recipient;
00080 };
00081 
00082 
00083 
00085 //
00086 //      static helper methods
00087 //
00089 
00090 class PhysicsObjectRequest : public netrq::Request {
00091 public:
00092         ~PhysicsObjectRequest(void) throw() { }
00093 
00094         // netrq::Request class interface methods ------------------------------
00095         const char * getId(void) const throw() { return m_id; }
00096         int getMaxBytes(void) const throw() { return s_maxPORBytes; }
00097         void write(IO xdrbuf::Output * output);
00098 
00099         // static factory methods ----------------------------------------------
00100         static smart_ptr<netrq::Request> create(
00101                                 IN conn_id_t recipientUdpConn,
00102                                 IN smart_ptr<PhysicsObject> obj,
00103                                 IN const char * id) {
00104                         ASSERT(recipientUdpConn, "null");
00105                         ASSERT(obj, "null");
00106                         ASSERT(id && id[0], "null or empty");
00107 
00108                         smart_ptr<PhysicsObjectRequest> local =
00109                             new PhysicsObjectRequest;
00110                         ASSERT(local, "out of memory");
00111 
00112                         strcpy(local->m_id, id);
00113                         local->m_obj = obj;
00114                         local->m_recipient = recipientUdpConn;
00115 
00116                         return local;
00117                 }
00118 
00119 private:
00120         PhysicsObjectRequest(void) throw() { }
00121 
00122         // private helper methods ----------------------------------------------
00123 
00124         // private member data -------------------------------------------------
00125         char                            m_id[s_maxPorIdBytes];
00126         conn_id_t                       m_recipient;
00127         smart_ptr<PhysicsObject>        m_obj;
00128 };
00129 
00130 
00131 
00132 void
00133 PhysicsObjectRequest::write
00134 (
00135 IO xdrbuf::Output * out
00136 )
00137 {
00138         ASSERT(out, "null");
00139         ASSERT(m_obj, "null");
00140 
00141         //DPRINTF("Writing...");
00142 
00143         // get ID.  If null, don't send!
00144         dword_t id = m_obj->getId();
00145         if (!id) {
00146                 return;
00147         }
00148 
00149         // prefix
00150         out->openPacklet('o');
00151 
00152         // send ID
00153         out->addInt32Packlet('i', (int32_t *) &id, 1);
00154 
00155         // send position
00156         point3d_t pos = m_obj->getPosition();
00157         out->addFloatPacklet('p', &pos.x, 3);
00158 
00159         // send orientation
00160         quaternion_t orient = m_obj->getOrientation();
00161         out->addFloatPacklet('o', &orient.x, 4);
00162 
00163         // send linear velocity
00164         point3d_t linear = m_obj->getLinearVelocity();
00165         out->addFloatPacklet('l', &linear.x, 3);
00166 
00167         // send angular velocity
00168         point3d_t angular = m_obj->getAngularVelocity();
00169         out->addFloatPacklet('a', &angular.x, 3);
00170 
00171         // get the instance
00172         smart_ptr<Instance> instance = getInstanceFromPhysicsObject(m_obj);
00173         if (instance) {
00174                 smart_ptr<ServerObject> so = getServerObject(instance);
00175                 if (so) {
00176                         const char * state = so->getAnimationState(m_recipient);
00177                         if (state) {
00178                                 int len = strlen(state);
00179 
00180                                 // 'n'--animation
00181                                 out->addStringPacklet('n', state, len);
00182                         }
00183                 }
00184         }
00185 
00186         // all done
00187         out->closePacklet('o');
00188         //DPRINTF("  ...all done");
00189 }
00190 
00191 
00192 
00193 void
00194 addObjectRequestToQueue
00195 (
00196 IN smart_ptr<PhysicsObject> obj,
00197 IN conn_id_t recipientUdp,
00198 IN netrq::Queue * queue,
00199 IN dword_t clock
00200 )
00201 {
00202         ASSERT(obj, "null");
00203         ASSERT(recipientUdp, "null");
00204         ASSERT(queue, "null");
00205 
00206         // get the ID
00207         long id = obj->getId();
00208         if (!id) {
00209                 return; // object is going away!
00210         }
00211         char buffer[s_maxPorIdBytes];
00212         sprintf(buffer, "por-%u-%ld", recipientUdp, id);
00213 
00214         // already in queue?
00215         if (queue->containsRequest(buffer))
00216                 return; // already in queue
00217 
00218         // create a network request object
00219         smart_ptr<netrq::Request> por =
00220             PhysicsObjectRequest::create(recipientUdp, obj, buffer);
00221         ASSERT(por, "out of memory");
00222 
00223         // add this to the queue
00224         // ignore failures for now
00225         queue->addRequest(clock, por);
00226 }
00227 
00228 
00229 
00230 static void
00231 sendObject
00232 (
00233 IN smart_ptr<Instance>& instance,
00234 IN smart_ptr<PhysicsObject>& obj,
00235 IN void * ctx
00236 )
00237 {
00238         ASSERT(instance, "null");
00239         ASSERT(obj, "null");
00240         object_context_t * poc = (object_context_t *) ctx;
00241         ASSERT(poc, "null");
00242         ASSERT(poc->dyn, "null");
00243         ASSERT(poc->queue, "null");
00244         ASSERT(poc->recipient, "null");
00245 
00246         if (obj->getMass() <= 0.0) {
00247                 return; // skip immobile objects
00248         }
00249 
00250         // add these to the queue with some offset
00251         addObjectRequestToQueue(obj, poc->recipient, poc->queue,
00252             poc->clock + (rand() % 3));
00253 }
00254 
00255 
00256 
00257 static void
00258 sendObjects
00259 (
00260 IN LeafZone * lz,
00261 IN void * context
00262 )
00263 {
00264         ASSERT(lz, "null");
00265         object_context_t * poc = (object_context_t *) context;
00266         ASSERT(poc, "null");
00267         ASSERT(poc->dyn, "null");
00268 
00269         // iterate over all objects (static and dynamic) in this zone
00270         poc->dyn->iterateInstancesInZone(lz, sendObject, context);
00271 }
00272 
00273 
00274 
00276 //
00277 //      public API
00278 //
00280 
00281 void
00282 addPhysicsMessagesToQueue
00283 (
00284 IN dword_t clock,
00285 IN HostManager * hostMgr,
00286 IN PlayerManager * playerMgr,
00287 IN MapManager * mapMgr
00288 )
00289 {
00290         ASSERT(hostMgr, "null");
00291         ASSERT(playerMgr, "null");
00292         ASSERT(mapMgr, "null");
00293 
00294         // iterate over all hosts
00295         HostManager::iterator_t i;
00296         host_rec_t hr;
00297         hostMgr->getIterator(i);
00298         while (hostMgr->getNextHost(i, hr)) {
00299 
00300                 // verify this host has a queue!
00301                 if (!hr.netQueue) {
00302                         continue;       // host isn't ready for this yet
00303                 }
00304 
00305                 // iterate over all players on this host
00306                 // first loop: send player objects with high priority
00307                 PlayerManager::iterator_t j;
00308                 playerMgr->getIterator(hr.udpConn, j);
00309                 player_rec_t pr;
00310                 while (playerMgr->getNextPlayer(j, pr)) {
00311                         if (!pr.obj) {
00312                                 continue;       // player not yet in map
00313                         }
00314 
00315                         addObjectRequestToQueue(pr.obj, hr.udpConn, hr.netQueue,
00316                             clock);
00317                 }
00318 
00319                 // second loop: send all objects of interest to player
00320                 playerMgr->getIterator(hr.udpConn, j);
00321                 while (playerMgr->getNextPlayer(j, pr)) {
00322 
00323                         if (pr.mapId.isEmpty() || !pr.obj) {
00324                                 // player isn't in a map yet
00325                                 continue;
00326                         }
00327 
00328                         // get the map this player is in
00329                         smart_ptr<MapDynamics> dyn = mapMgr->getMap(pr.mapId);
00330                         if (!dyn)
00331                                 continue;       // no such map?
00332                         Map * map = dyn->getMap();
00333                         ASSERT(map, "null");
00334 
00335                         // get the leaf zone that contains player
00336                         Zone * root = map->getRootZone();
00337                         LeafZone * leaf = root->getLeafZone(pr.obj->getPosition());
00338                         if (!leaf) {
00339                                 DPRINTF("No leaf zone contains player!");
00340                                 continue;       // next player
00341                         }
00342 
00343                         // construct object context
00344                         object_context_t oc;
00345                         oc.dyn = dyn;
00346                         oc.queue = hr.netQueue;
00347                         oc.clock = clock;
00348                         oc.recipient = hr.udpConn;
00349 
00350                         // iterate over only visible zones for this player
00351                         leaf->iterateVisibleZones(sendObjects, (void *) &oc);
00352                 }
00353         }
00354 }
00355 
00356 
00357 
00358 };      // aesop namespace
00359