SimpleGameClient.cc

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2006 Institut fuer Telematik, Universitaet Karlsruhe (TH)
00003 //
00004 // This program is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU General Public License
00006 // as published by the Free Software Foundation; either version 2
00007 // of the License, or (at your option) any later version.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with this program; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 //
00018 
00025 #include "SimpleGameClient.h"
00026 
00027 Define_Module(SimpleGameClient);
00028 
00029 void SimpleGameClient::initializeApp(int stage)
00030 {
00031     // all initialization is done in the first stage
00032     if (stage != MIN_STAGE_APP) {
00033         return;
00034     }
00035 
00036     // fetch parameters
00037     areaDimension = par("areaDimension");
00038     useScenery = par("useScenery");
00039     movementRate = par("movementRate");
00040     movementSpeed = par("movementSpeed");
00041     movementDelay= 1.0 / movementRate;
00042     AOIWidth = 0.0;
00043     logAOI = false;
00044     int groupSize = (int)par("groupSize");
00045     if(groupSize < 1) {
00046         groupSize = 1;
00047     }
00048     GeneratorType = par("movementGenerator").stdstringValue();
00049 
00050     WATCH_MAP(Neighbors);
00051     WATCH_LIST(CollisionRect);
00052     WATCH(position);
00053     WATCH(GeneratorType);
00054     WATCH(overlayReady);
00055 
00056     doRealworld = false;
00057     coordinator = check_and_cast<GlobalCoordinator*>(simulation.getModuleByPath("globalObserver.globalFunctions[0].function.coordinator"));
00058     scheduler = NULL;
00059     packetNotification = NULL;
00060 
00061     frozen = false;
00062 
00063     useHotspots = false;
00064     lastInHotspot = false;
00065     lastFarFromHotspot = false;
00066     lastAOImeasure = simTime();
00067     startAOI = 0;
00068     avgAOI = 0;
00069     hotspotTime = 0;
00070     avgHotspotAOI = 0;
00071     nonHotspotTime = 0;
00072     avgFarFromHotspotAOI = 0;
00073     farFromHotspotTime = 0;
00074 
00075     CollisionList* listPtr = NULL;
00076     if(useScenery == true) {
00077         listPtr = &CollisionRect;
00078     }
00079 
00080     double speed = SIMTIME_DBL(movementDelay) * movementSpeed;
00081     if(strcmp(GeneratorType.c_str(), "greatGathering") == 0) {
00082         if(debugOutput) {
00083             EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00084                << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00085                << "    SIMPLECLIENT: Movement generator: greatGathering"
00086                << endl;
00087         }
00088         Generator = new greatGathering(areaDimension, speed, &Neighbors, coordinator, listPtr);
00089     }
00090     else if(strcmp(GeneratorType.c_str(), "groupRoaming") == 0) {
00091         if(debugOutput) {
00092             EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00093                << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00094                << "    SIMPLECLIENT: Movement generator: groupRoaming"
00095                << endl;
00096         }
00097         Generator = new groupRoaming(areaDimension, speed, &Neighbors, coordinator, listPtr, groupSize);
00098     }
00099     else if(strcmp(GeneratorType.c_str(), "hotspotRoaming") == 0) {
00100         if(debugOutput) {
00101             EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00102                << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00103                << "    SIMPLECLIENT: Movement generator: hotspotRoaming"
00104                << endl;
00105         }
00106         Generator = new hotspotRoaming(areaDimension, speed, &Neighbors, coordinator, listPtr);
00107         useHotspots = true;
00108     }
00109     else if(strcmp(GeneratorType.c_str(), "traverseRoaming") == 0) {
00110         if(debugOutput) {
00111             EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00112                << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00113                << "    SIMPLECLIENT: Movement generator: traverseRoaming"
00114                << endl;
00115         }
00116         logAOI = true;
00117         Generator = new traverseRoaming(areaDimension, speed, &Neighbors, coordinator, listPtr);
00118     }
00119     else if(strcmp(GeneratorType.c_str(), "realWorldRoaming") == 0) {
00120         if(debugOutput) {
00121             EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00122                << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00123                << "    SIMPLECLIENT: Movement generator: realWorldRoaming"
00124                << endl;
00125         }
00126         Generator = new realWorldRoaming(areaDimension, movementSpeed, &Neighbors, coordinator, listPtr);
00127         mtu = par("mtu");
00128         packetNotification = new cMessage("packetNotification");
00129         scheduler = check_and_cast<RealtimeScheduler*>(simulation.getScheduler());
00130         scheduler->setInterfaceModule(this, packetNotification, &packetBuffer, mtu, true);
00131         appFd = INVALID_SOCKET;
00132     }
00133     else {
00134         // debug output
00135         if(debugOutput) {
00136             if(strcmp(GeneratorType.c_str(), "randomRoaming") == 0) {
00137                 EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00138                    << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00139                    << "    SIMPLECLIENT: Movement generator: randomRoaming"
00140                    << endl;
00141             }
00142             else {
00143                 EV << "[SimpleGameClient::initializeApp() @ " << overlay->getThisNode().getIp()
00144                    << " (" << overlay->getThisNode().getKey().toString(16) << ")]\n"
00145                    << "    SIMPLECLIENT: Movement generator: <unknown>. Defaulting to: randomRoaming"
00146                    << endl;
00147             }
00148         }
00149         Generator = new randomRoaming(areaDimension, speed, &Neighbors, coordinator, listPtr);
00150     }
00151 
00152     // self-messages
00153     move_timer = new cMessage("move_timer");
00154     overlayReady = false;
00155 }
00156 
00157 void SimpleGameClient::handleTimerEvent(cMessage* msg)
00158 {
00159     if(msg->isName("move_timer")) {
00160         //reset timer
00161         cancelEvent(move_timer);
00162         if(overlayReady) {
00163             scheduleAt(simTime() + movementDelay, msg);
00164         }
00165         // handle event
00166         updatePosition();
00167     }
00168     else if(msg->isName("packetNotification")) {
00169         while(packetBuffer.size() > 0) {
00170             // get packet from buffer and parse it
00171             RealtimeScheduler::PacketBufferEntry packet = *(packetBuffer.begin());
00172             packetBuffer.pop_front();
00173             switch (packet.func) {
00174                 case RealtimeScheduler::PacketBufferEntry::PACKET_DATA: {
00175                     if(packet.fd != appFd) {
00176                         error("SimpleClient::handleMessage(): Received packet from unknown socket!");
00177                     }
00178                     handleRealworldPacket(packet.data, packet.length);
00179                 } break;
00180                 case RealtimeScheduler::PacketBufferEntry::PACKET_FD_NEW: {
00181                     if(appFd != INVALID_SOCKET) {
00182                         error("SimpleClient::handleMessage(): Multiple connections not supported yet!");
00183                     }
00184                     if (packet.addr != NULL) {
00185                         delete packet.addr;
00186                         packet.addr = NULL;
00187                     }
00188                     appFd = packet.fd;
00189                 } break;
00190                 case RealtimeScheduler::PacketBufferEntry::PACKET_FD_CLOSE: {
00191                     if(packet.fd != appFd) {
00192                         error("SimpleClient::handleMessage(): Trying to close unknown connection!");
00193                     }
00194                     appFd = INVALID_SOCKET;
00195                 } break;
00196                 default:
00197                     // Unknown socket function, ignore
00198                     break;
00199             }
00200             delete packet.data;
00201         }
00202     }
00203     else if(msg->isName("Snowball landing")) {
00204         SCSnowTimer* snowTimer = check_and_cast<SCSnowTimer*>(msg);
00205         if(!frozen && position.distanceSqr(snowTimer->getPosition() / 32 ) < 2) {
00206             // freeze me!
00207             frozen = true;
00208             cMessage* unfreeze = new cMessage("unfreeze me");
00209             scheduleAt(simTime() + 5, unfreeze);
00210 
00211             GameAPIFrozenMessage* freeze = new GameAPIFrozenMessage("I'm frozen");
00212             freeze->setCommand(GAMEEVENT_FROZEN);
00213             freeze->setSrc(overlay->getThisNode());
00214             freeze->setThrower(snowTimer->getIp());
00215             freeze->setTimeSec(5);
00216             freeze->setTimeUsec(0);
00217             sendMessageToLowerTier(freeze);
00218 
00219             if(doRealworld) {
00220                 SCFrozenPacket scfreeze;
00221                 scfreeze.packetType = SC_EV_FROZEN;
00222                 scfreeze.ip = overlay->getThisNode().getIp().get4().getInt();
00223                 scfreeze.source = snowTimer->getIp();
00224                 scfreeze.time_sec = 5;
00225                 scfreeze.time_usec = 0;
00226                 int packetlen = sizeof(scfreeze);
00227                 scheduler->sendBytes((const char*)&packetlen, sizeof(int), 0, 0, true, appFd);
00228                 scheduler->sendBytes((const char*)&scfreeze, packetlen, 0, 0, true, appFd);
00229             }
00230         }
00231         delete msg;
00232     }
00233     else if(msg->isName("unfreeze me")) {
00234         frozen = false;
00235         delete msg;
00236     }
00237 }
00238 
00239 void SimpleGameClient::handleLowerMessage(cMessage* msg)
00240 {
00241     if(dynamic_cast<GameAPIMessage*>(msg)) {
00242         GameAPIMessage* gameAPIMsg = check_and_cast<GameAPIMessage*>(msg);
00243         if(gameAPIMsg->getCommand() == MOVEMENT_REQUEST) {
00244             updatePosition();
00245         }
00246         else if(gameAPIMsg->getCommand() == NEIGHBOR_UPDATE) {
00247             GameAPIListMessage* gameAPIListMsg = check_and_cast<GameAPIListMessage*>(msg);
00248             updateNeighbors(gameAPIListMsg);
00249         }
00250         else if(gameAPIMsg->getCommand() == RESIZE_AOI) {
00251             GameAPIResizeAOIMessage* gameAPIResizeMsg = check_and_cast<GameAPIResizeAOIMessage*>(msg);
00252             
00253             // Measure average AOI sizes
00254             if( startAOI != 0 )
00255             {
00256                 RECORD_STATS(
00257                     simtime_t elapsed = simTime() - lastAOImeasure;
00258                     if( lastInHotspot ){
00259                         avgHotspotAOI += AOIWidth*elapsed.dbl();
00260                         hotspotTime += elapsed;
00261                     } else {
00262                         avgAOI += AOIWidth*elapsed.dbl();
00263                         nonHotspotTime += elapsed;
00264                         if( lastFarFromHotspot ){
00265                             avgFarFromHotspotAOI += AOIWidth*elapsed.dbl();
00266                             farFromHotspotTime += elapsed;
00267                         }
00268                     }
00269                 );
00270             } else {
00271                 startAOI = gameAPIResizeMsg->getAOIsize();
00272             }
00273             lastAOImeasure = simTime();
00274             if( useHotspots ){
00275                 lastInHotspot = dynamic_cast<hotspotRoaming*>(Generator)->getDistanceFromHotspot() <= 0;
00276                 lastFarFromHotspot = dynamic_cast<hotspotRoaming*>(Generator)->getDistanceFromHotspot() > startAOI;
00277             }
00278 
00279             AOIWidth = gameAPIResizeMsg->getAOIsize();
00280             if( logAOI ){
00281                 GlobalStatisticsAccess().get()->recordOutVector( "SimpleGameClient: AOI width", AOIWidth );
00282             }
00283             if(doRealworld) {
00284                 SCAOIPacket packet;
00285                 packet.packetType = SC_RESIZE_AOI;
00286                 packet.AOI = AOIWidth;
00287                 int packetlen = sizeof(SCAOIPacket);
00288                 scheduler->sendBytes((const char*)&packetlen, sizeof(int), 0, 0, true, appFd);
00289                 scheduler->sendBytes((const char*)&packet, packetlen, 0, 0, true, appFd);
00290             }
00291         }
00292         else if(gameAPIMsg->getCommand() == GAMEEVENT_CHAT) {
00293             if(doRealworld) {
00294                 GameAPIChatMessage* chatMsg = check_and_cast<GameAPIChatMessage*>(msg);
00295                 const char* msg = chatMsg->getMsg();
00296                 int packetlen = sizeof(SCChatPacket) + strlen(msg) + 1;
00297                 SCChatPacket* pack = (SCChatPacket*)malloc(packetlen);
00298                 pack->packetType = SC_EV_CHAT;
00299                 pack->ip = chatMsg->getSrc().getIp().get4().getInt();
00300                 strcpy(pack->msg, msg);
00301                 scheduler->sendBytes((const char*)&packetlen, sizeof(int), 0, 0, true, appFd);
00302                 scheduler->sendBytes((const char*)pack, packetlen, 0, 0, true, appFd);
00303                 free(pack);
00304             }
00305         }
00306         else if(gameAPIMsg->getCommand() == GAMEEVENT_SNOW) {
00307             GameAPISnowMessage* snowMsg = check_and_cast<GameAPISnowMessage*>(gameAPIMsg);
00308             if(doRealworld) {
00309                 SCSnowPacket packet;
00310                 packet.packetType = SC_EV_SNOWBALL;
00311                 packet.ip = snowMsg->getSrc().getIp().get4().getInt();
00312                 packet.startX = snowMsg->getStart().x;
00313                 packet.startY = snowMsg->getStart().y;
00314                 packet.endX = snowMsg->getEnd().x;
00315                 packet.endY = snowMsg->getEnd().y;
00316                 packet.time_sec = snowMsg->getTimeSec();
00317                 packet.time_usec = snowMsg->getTimeUsec();
00318                 int packetlen = sizeof(SCSnowPacket);
00319                 scheduler->sendBytes((const char*)&packetlen, sizeof(int), 0, 0, true, appFd);
00320                 scheduler->sendBytes((const char*)&packet, packetlen, 0, 0, true, appFd);
00321             }
00322             SCSnowTimer* snowTimer = new SCSnowTimer("Snowball landing");
00323             snowTimer->setPosition(snowMsg->getEnd());
00324             snowTimer->setIp(snowMsg->getSrc().getIp().get4().getInt());
00325             timeval snowTime;
00326             snowTime.tv_sec = snowMsg->getTimeSec();
00327             snowTime.tv_usec = snowMsg->getTimeUsec();
00328             simtime_t snow_t = snowTime.tv_sec + snowTime.tv_usec / 1000000.0;
00329             scheduleAt(simTime() + snow_t, snowTimer);
00330         }
00331         else if(gameAPIMsg->getCommand() == GAMEEVENT_FROZEN) {
00332             if(doRealworld) {
00333                 GameAPIFrozenMessage* frozenMsg = check_and_cast<GameAPIFrozenMessage*>(gameAPIMsg);
00334                 SCFrozenPacket scfreeze;
00335                 scfreeze.packetType = SC_EV_FROZEN;
00336                 scfreeze.ip = frozenMsg->getSrc().getIp().get4().getInt();
00337                 scfreeze.source = frozenMsg->getThrower();
00338                 scfreeze.time_sec = frozenMsg->getTimeSec();
00339                 scfreeze.time_usec = frozenMsg->getTimeUsec();
00340                 int packetlen = sizeof(scfreeze);
00341                 scheduler->sendBytes((const char*)&packetlen, sizeof(int), 0, 0, true, appFd);
00342                 scheduler->sendBytes((const char*)&scfreeze, packetlen, 0, 0, true, appFd);
00343             }
00344         }
00345     }
00346     delete msg;
00347 }
00348 
00349 void SimpleGameClient::handleReadyMessage(CompReadyMessage* msg)
00350 {
00351     // process only ready messages from the tier below
00352     if( getThisCompType() - msg->getComp() == 1 ){
00353         if(msg->getReady()) {
00354             overlayReady = true;
00355             if(!move_timer->isScheduled()) {
00356                 scheduleAt(simTime() + movementDelay, move_timer);
00357             }
00358         }
00359         else {
00360             overlayReady = false;
00361         }
00362     }
00363     delete msg;
00364 }
00365 
00366 void SimpleGameClient::handleRealworldPacket(char *buf, uint32_t len)
00367 {
00368     SCBasePacket *type = (SCBasePacket*)buf;
00369     if(type->packetType == SC_PARAM_REQUEST) {
00370         SCParamPacket packet;
00371         packet.packetType = SC_PARAM_RESPONSE;
00372         packet.speed = movementSpeed;
00373         packet.dimension = areaDimension;
00374         packet.AOI = AOIWidth;
00375         packet.delay = SIMTIME_DBL(movementDelay);
00376         packet.startX = position.x;
00377         packet.startY = position.y;
00378         packet.ip = thisNode.getIp().get4().getInt();
00379         packet.seed = coordinator->getSeed();
00380         int packetSize = sizeof(packet);
00381         scheduler->sendBytes((const char*)&packetSize, sizeof(int), 0, 0, true, appFd);
00382         scheduler->sendBytes((const char*)&packet, packetSize, 0, 0, true, appFd);
00383         doRealworld = true;
00384     }
00385     else if(type->packetType == SC_MOVE_INDICATION) {
00386         SCMovePacket *packet = (SCMovePacket*)type;
00387         Vector2D temp;
00388         temp.x = packet->posX;
00389         temp.y = packet->posY;
00390         dynamic_cast<realWorldRoaming*>(Generator)->setPosition(temp);
00391     }
00392     else if(type->packetType == SC_EV_CHAT) {
00393         SCChatPacket* packet = (SCChatPacket*)type;
00394         GameAPIChatMessage* chatMsg = new GameAPIChatMessage("ChatMsg");
00395         chatMsg->setCommand(GAMEEVENT_CHAT);
00396         chatMsg->setSrc(thisNode);
00397         chatMsg->setMsg(packet->msg);
00398         sendMessageToLowerTier(chatMsg);
00399     }
00400     else if(type->packetType == SC_EV_SNOWBALL) {
00401         SCSnowPacket* packet = (SCSnowPacket*)type;
00402         GameAPISnowMessage* snowMsg = new GameAPISnowMessage("Throw Snowball");
00403         snowMsg->setCommand(GAMEEVENT_SNOW);
00404         snowMsg->setSrc(thisNode);
00405         snowMsg->setStart(Vector2D(packet->startX, packet->startY));
00406         snowMsg->setEnd(Vector2D(packet->endX, packet->endY));
00407         snowMsg->setTimeSec(packet->time_sec);
00408         snowMsg->setTimeUsec(packet->time_usec);
00409         sendMessageToLowerTier(snowMsg);
00410     }
00411 }
00412 
00413 void SimpleGameClient::updateNeighbors(GameAPIListMessage* sgcMsg)
00414 {
00415     unsigned int i;
00416 
00417     if(doRealworld) {
00418         int packetSize;
00419 
00420         SCRemovePacket removePacket;
00421         removePacket.packetType = SC_REMOVE_NEIGHBOR;
00422         packetSize = sizeof(removePacket);
00423 
00424         for(i=0; i<sgcMsg->getRemoveNeighborArraySize(); ++i) {
00425             removePacket.ip = sgcMsg->getRemoveNeighbor(i).getIp().get4().getInt();
00426             scheduler->sendBytes((const char*)&packetSize, sizeof(int), 0, 0, true, appFd);
00427             scheduler->sendBytes((const char*)&removePacket, packetSize, 0, 0, true, appFd);
00428         }
00429 
00430         SCAddPacket addPacket;
00431         addPacket.packetType = SC_ADD_NEIGHBOR;
00432         packetSize = sizeof(addPacket);
00433 
00434         for(i=0; i<sgcMsg->getAddNeighborArraySize(); ++i) {
00435             addPacket.ip = sgcMsg->getAddNeighbor(i).getIp().get4().getInt();
00436             if( addPacket.ip == thisNode.getIp().get4().getInt() ) continue;
00437             addPacket.posX = sgcMsg->getNeighborPosition(i).x;
00438             addPacket.posY = sgcMsg->getNeighborPosition(i).y;
00439             scheduler->sendBytes((const char*)&packetSize, sizeof(int), 0, 0, true, appFd);
00440             scheduler->sendBytes((const char*)&addPacket, packetSize, 0, 0, true, appFd);
00441         }
00442     }
00443     for(i=0; i<sgcMsg->getRemoveNeighborArraySize(); ++i)
00444         Neighbors.erase(sgcMsg->getRemoveNeighbor(i));
00445 
00446     for(i=0; i<sgcMsg->getAddNeighborArraySize(); ++i) {
00447         Vector2D newPos;
00448         newPos = sgcMsg->getNeighborPosition(i);
00449         itNeighbors = Neighbors.find(sgcMsg->getAddNeighbor(i));
00450         if(itNeighbors != Neighbors.end()) {
00451             Vector2D temp = newPos - itNeighbors->second.position;
00452             temp.normalize();
00453             itNeighbors->second.direction = temp;
00454             itNeighbors->second.position = newPos;
00455         }
00456         else {
00457             NeighborMapEntry entry;
00458             entry.position = newPos;
00459             Neighbors.insert(std::make_pair(sgcMsg->getAddNeighbor(i), entry));
00460         }
00461     }
00462 }
00463 
00464 void SimpleGameClient::updatePosition()
00465 {
00466     if(!frozen) {
00467         Generator->move();
00468     }
00469     position = Generator->getPosition();
00470     GameAPIPositionMessage *posMsg = new GameAPIPositionMessage("MOVEMENT_INDICATION");
00471     posMsg->setCommand(MOVEMENT_INDICATION);
00472     posMsg->setPosition(position);
00473     sendMessageToLowerTier(posMsg);
00474 }
00475 
00476 void SimpleGameClient::finishApp()
00477 {
00478     if (nonHotspotTime >= GlobalStatistics::MIN_MEASURED) {
00479         globalStatistics->addStdDev("SimpleGameClient: average non-hotspot AOI",
00480                 avgAOI / nonHotspotTime);
00481     }
00482     if (farFromHotspotTime >= GlobalStatistics::MIN_MEASURED) {
00483         globalStatistics->addStdDev("SimpleGameClient: average far-from-hotspot AOI",
00484                 avgFarFromHotspotAOI / farFromHotspotTime);
00485     }
00486     if (hotspotTime >= GlobalStatistics::MIN_MEASURED) {
00487         globalStatistics->addStdDev("SimpleGameClient: average hotspot AOI",
00488                 avgHotspotAOI / hotspotTime);
00489     }
00490 }
00491 
00492 SimpleGameClient::~SimpleGameClient()
00493 {
00494     // destroy self timer messages
00495     cancelAndDelete(move_timer);
00496     cancelAndDelete(packetNotification);
00497     delete Generator;
00498 }