Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

Network.cpp

Go to the documentation of this file.
00001 /*
00002 CS Senior Project 2003
00003 Team : Leftfield
00004 Project : ModernWarfare
00005 Members :
00006 - Russ Christensen              <rchriste@cs.utah.edu>
00007 - Todd Smith                    <tcsmith@cs.utah.edu>
00008 - Usit Duongsaa                 <duongsaa@cs.utah.edu>
00009 Copyright 2003 Russ Christensen, Usit Duongsaa, and Todd Smith. All rights reserved.
00010 
00011 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
00012 
00013 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
00014 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 
00015 THIS SOFTWARE IS PROVIDED BY RUSS CHRISTENSEN, USIT DUONGSAA, AND TODD SMITH ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RUSS, USIT, TODD OR OTHER CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00016 */
00017 
00029 #include "Network.h"
00030 #include "..\AEngine\AEngine.h"
00031 #include "MilitaryUnit.h"
00032 #include "Globals.h"
00033 #include "HelperFunctions.h"
00034 #include "BattleUnitStat.h"
00035 #include "City.h"
00036 #include "BattleEntry.h"
00037 #include "GameState.h"
00038 #include <iostream>
00039 #include <hash_map>
00040 
00041 using namespace std;
00042 using namespace NetworkSubsystem;
00043 using namespace NetworkMessages;
00044 using namespace Helper;
00045 
00046 namespace Overhead {
00047         void deleteMilitaryUnit(MilitaryUnit*);
00048 }
00049 
00050 namespace {
00051         typedef pair<uint, vector<string> > NDataPair;
00052         typedef hash_map<uint, vector<string> >::iterator NDataPairIter;
00053         typedef vector<string> VecString;
00054         typedef VecString::iterator VecStringIter;
00055 
00056         hash_map<int,int> randomNumberList;
00057 
00058         int getGameTick(const string & data)
00059         {
00060                 istringstream sstr(data);
00061                 string tmp;
00062                 sstr >> tmp;
00063                 sstr >> tmp;
00064                 if (tmp != COMMIT_TIME)
00065                 {
00066                         sys_console() << WARNING << "assert(tmp == COMMIT_TIME && \"Second command should always be COMMIT_TIME\");" << endl;
00067                         sys_flushConsole();
00068                         return 0;
00069                 }
00070                 int result;
00071                 sstr >> result;
00072                 return result;
00073         }
00074 
00075         class NetworkData {
00076         public:
00077                 NetworkData(void);
00078                 ~NetworkData(void);
00079                 void addData(string data);
00082                 vector<string> getData(int gameTickWanted);
00083                 bool hasDataForGameTickArrived(int gameTick);
00084                 void clean(int gameTickToClean);
00085         private:
00087                 hash_map<uint, vector<string> > m_data;
00088                 hash_map<uint, uint> m_syncData;
00089         };
00090 
00091         NetworkData netData;
00092 
00093         NetworkData::NetworkData(): m_data(), m_syncData()
00094         {
00095         }
00096 
00097         NetworkData::~NetworkData()
00098         {
00099                 //Nothing to do
00100         }
00101 
00102         void NetworkData::addData(string data)
00103         {
00104                 int gameTickToCommit = getGameTick(data);
00105                 stringstream sstr(data);
00106                 string command;
00107                 sstr >> command;
00108                 if (command == COMMAND_SYNC)
00109                 {
00110                         hash_map<uint,uint>::iterator iter = m_syncData.find(gameTickToCommit);
00111                         if (iter == m_syncData.end())
00112                         {
00113                                 m_syncData.insert(pair<uint,uint>(gameTickToCommit, 1));
00114                         } else
00115                         {
00116                                 ++iter->second;
00117                         }
00118                 } else
00119                 {
00120                         if (gameTickToCommit < currGameTick) 
00121                         {
00122                                 sys_console() << WARNING << "Error received command that should have happened in the past command is:" << data << endl;
00123                                 sys_flushConsole();
00124                         } else
00125                         {
00126                                 NDataPairIter iter = m_data.find(gameTickToCommit);
00127                                 if (iter == m_data.end()) 
00128                                 {
00129                                         vector<string> vstr;
00130                                         vstr.push_back(data);
00131                                         m_data.insert(NDataPair(gameTickToCommit, vstr));
00132                                 } else 
00133                                 {
00134                                         iter->second.push_back(data);
00135                                 }
00136                         }
00137                 }
00138         }
00139 
00140         vector<string> NetworkData::getData(int gameTickWanted)
00141         {
00142                 NDataPairIter iter = m_data.find(gameTickWanted);
00143                 if (iter == m_data.end()) return vector<string>();
00144                 else return iter->second;
00145         }
00146 
00147         bool NetworkData::hasDataForGameTickArrived(int gameTick)
00148         {
00149                 //round down to the closest gameTick sync point
00150                 gameTick /= NETWORK_SYNC_RATE;
00151                 gameTick *= NETWORK_SYNC_RATE;
00152                 gameTick += NETWORK_SYNC_RATE;
00153                 assert(gameTick % NETWORK_SYNC_RATE == 0 && "Programming Error: Code above is being optimized out");
00154                 hash_map<uint,uint>::iterator iter = m_syncData.find(gameTick);
00155                 if (iter == m_syncData.end()) return false;
00156                 else 
00157                 {
00158                         return iter->second >= net_getAllPlayers().size();
00159                 }
00160         }
00161 
00162         void NetworkData::clean(int gameTickToClean)
00163         {
00164                 int num(gameTickToClean - Globals::NETWORK_GAME_TICK_LAG);
00165                 if (num > 0)
00166                 {
00167                         m_syncData.erase(num);
00168                         m_data.erase(num);
00169                 }
00170         }
00171 
00172         void sendGameTickEnd(int gameTickToEnd) 
00173         {
00174                 ostringstream sstr;
00175                 sstr << COMMAND_OVERHEAD << " " << END_GAME_TICK << " " << gameTickToEnd;
00176                 net_send(sstr.str());
00177         }
00178         
00179         bool existsGameTickEnd(const string & str) 
00180         {
00181                 stringstream sstr(str);
00182                 string cmd;
00183                 sstr >> cmd;
00184                 if (cmd != COMMAND_OVERHEAD) return false;
00185                 sstr >> cmd;
00186                 if (cmd == END_GAME_TICK) return true;
00187                 else return false;
00188         }
00189 } //end namespace {}
00190 
00191 namespace NetworkSubsystem {
00192 
00193 bool hasNetworkDataForCurrentTickBeenReceived(void)
00194 {
00195         if (currGameTick < NETWORK_GAME_TICK_LAG) return true;
00196         else return netData.hasDataForGameTickArrived(currGameTick);
00197 }
00198 
00199 void processNetworkData(void)
00200 {
00201         static internalGameTick = 0;
00202         assert(++internalGameTick == currGameTick && "processNetworkData is not getting called once for every game tick");
00203         VecString commands(netData.getData(currGameTick));
00204         for(VecStringIter iter = commands.begin(); iter != commands.end(); ++iter)
00205         {
00206                 //sys_console() << "processNetworkData:" << *iter << endl;
00207                 stringstream sstr(*iter);
00208                 string tmp;
00209                 sstr >> tmp;
00210                 if (tmp == COMMAND_OVERHEAD)
00211                 {
00212                         sstr >> tmp;
00213                         //if (tmp != COMMIT_TIME) throw Error("Invalid time in COMMAND_OVERHEAD");
00214                         int itmp;
00215                         sstr >> itmp;
00216                         //if (itmp != currGameTick) throw Error("Dealing with command that is not for currGameTick");
00217                         sstr >> tmp;
00218                         if (tmp == COMMAND_GOAL)
00219                         {
00220                                 uint uID;
00221                                 sstr >> uID;
00222                                 MilitaryUnit* unit(GameState::getMilitaryUnit(uID));
00223                                 if (unit == 0) 
00224                                 {
00225                                         stringstream sstr;
00226                                         sstr << "Network Warning: Unit with ID of " << uID << " not found";
00227                                         sys_console() << sstr.str() << endl;
00228                                 } else
00229                                 {
00230                                         unit->goal(unserializeVec3D(sstr));
00231                                 }
00232                         } else if (tmp == COMMAND_BUILD_LIGHTTANK)
00233                         {
00234                                 uint uID;
00235                                 sstr >> uID;
00236                                 City* city(GameState::getCity(uID));
00237                                 if (city == 0) 
00238                                 {
00239                                         stringstream sstr;
00240                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00241                                         throw Error(sstr.str());
00242                                 }
00243                                 city->trainUnit(UnitTypes::LIGHT_TANK);
00244                         } else if (tmp == COMMAND_BUILD_HEAVYTANK)
00245                         {
00246                                 uint uID;
00247                                 sstr >> uID;
00248                                 City* city(GameState::getCity(uID));
00249                                 if (city == 0) 
00250                                 {
00251                                         stringstream sstr;
00252                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00253                                         throw Error(sstr.str());
00254                                 }
00255                                 city->trainUnit(UnitTypes::HEAVY_TANK);
00256                         } else if (tmp == COMMAND_BUILD_ROCKET_LAUNCHER)
00257                         {
00258                                 uint uID;
00259                                 sstr >> uID;
00260                                 City* city(GameState::getCity(uID));
00261                                 if (city == 0) 
00262                                 {
00263                                         stringstream sstr;
00264                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00265                                         throw Error(sstr.str());
00266                                 }
00267                                 city->trainUnit(UnitTypes::LAUNCHER);
00268                         } else if (tmp == COMMAND_BUILD_ARTILLERY)
00269                         {
00270                                 uint uID;
00271                                 sstr >> uID;
00272                                 City* city(GameState::getCity(uID));
00273                                 if (city == 0) 
00274                                 {
00275                                         stringstream sstr;
00276                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00277                                         throw Error(sstr.str());
00278                                 }
00279                                 city->trainUnit(UnitTypes::ARTILLERY);
00280                         } else if (tmp == COMMAND_BUILD_JET)
00281                         {
00282                                 uint uID;
00283                                 sstr >> uID;
00284                                 City* city(GameState::getCity(uID));
00285                                 if (city == 0) 
00286                                 {
00287                                         stringstream sstr;
00288                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00289                                         throw Error(sstr.str());
00290                                 }
00291                                 city->trainUnit(UnitTypes::JET);
00292                         } else if( tmp == COMMAND_BUILD_ECON )
00293                         {
00294                                 uint uID;
00295                                 sstr >> uID;
00296                                 City* city(GameState::getCity(uID));
00297                                 if (city == 0) 
00298                                 {
00299                                         stringstream sstr;
00300                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00301                                         throw Error(sstr.str());
00302                                 }
00303                                 city->upgradeEcon();
00304                         } else if (tmp == COMMAND_SET_RALLY_POINT)
00305                         {
00306                                 uint uID;
00307                                 sstr >> uID;
00308                                 City* city(GameState::getCity(uID));
00309                                 if (city == 0) 
00310                                 {
00311                                         stringstream sstr;
00312                                         sstr << "Networking Error: City with ID of " << uID << " not found";
00313                                         throw Error(sstr.str());
00314                                 }
00315                                 city->setRallyPoint(unserializeVec3D(sstr));
00316                         } else 
00317                         {
00318                                 sys_console() << WARNING << "throw Error(\"Invalid COMMAND_OVERHEAD command\");" << endl;
00319                                 sys_flushConsole();
00320                         }
00321                 } else if (tmp == COMMAND_BATTLEMODE)
00322                 {
00323                         uint battleNumber;
00324                         sstr >> tmp; /*COMMIT_TIME*/
00325                         sstr >> tmp; /* dido */
00326                         sstr >> tmp;
00327                         if (tmp == COMMAND_BATTLE_ID)
00328                         {
00329                                 sstr >> battleNumber;
00330                                 BattleEntry* battle(GameState::getBattle(battleNumber));
00331                                 if (battle)
00332                                 {
00333                                         battle->enQueueInput(*iter);
00334                                 } else 
00335                                 {
00336                                         sys_console() << WARNING << COMMAND_BATTLEMODE << " received and there is no active battle with that ID" << endl;
00337                                 }
00338                         } else
00339                         {
00340                                 sys_console() << WARNING << "assert(tmp == COMMAND_BATTLE_ID);" << endl;
00341                                 sys_flushConsole();
00342                         }
00343                 } else if (tmp == COMMAND_BEGINBATTLE)
00344                 {
00345                         sys_console() << WARNING << "throw Error(COMMAND_BEGINBATTLE + \" received\");" << endl;
00346                 } else if (tmp == COMMAND_PRINT_A_RANDOM_NUMBER_TO_THE_CONSOLE)
00347                 {
00348                         sys_console() << "Random Number:" << rand() << endl;
00349                 } else if (tmp == COMMAND_TEXT_MESSAGE)
00350                 {
00351                         sstr >> tmp >> tmp; //COMMIT TIME
00352                         getline(sstr,tmp);
00353                         GameState::messagesReceived().addMessage(tmp);
00354                 } else if (tmp == COMMAND_MORE_MONEY)
00355                 {
00356                         const int moreMoneyAmount(10000);
00357                         for(vector<Player*>::iterator iter(GameState::players().begin()); iter != GameState::players().end(); ++iter)
00358                         {
00359                                 (*iter)->bank().deposit(moreMoneyAmount);
00360                         }
00361                 } else if (tmp == COMMAND_SEND_MONEY)
00362                 {
00363                         sstr >> tmp >> tmp; //COMMIT TIME
00364                         int amount;
00365                         sstr >> amount;
00366                         string player;
00367                         getline(sstr, player);
00368                         player = Helper::toLower(Helper::eatEarlyWhiteSpace(player));
00369                         if (player == Helper::toLower(net_getPlayerName())) 
00370                         {
00371                                 GameState::consolePlayer()->bank().deposit(amount);
00372                                 stringstream outMsg;
00373                                 outMsg << "You just received " << amount;
00374                                 GameState::messagesReceived().addMessage(outMsg.str());
00375                         }
00376                 } else if (tmp == COMMAND_ASK_FOR_RANDOM_NUMBER)
00377                 {
00378                         sstr >> tmp >> tmp; //COMMIT TIME
00379                         int number;
00380                         sstr >> number;
00381                         stringstream netOut;
00382                         int randomNumber(sys_randInt());
00383                         netOut << COMMAND_RANDOM_NUMBER_ANSWER << " " << COMMIT_TIME << " " << currGameTick + NETWORK_GAME_TICK_LAG
00384                                    << " " << randomNumber << " " << number;
00385                         net_send(netOut.str());
00386                 } else if (tmp == COMMAND_RANDOM_NUMBER_ANSWER)
00387                 {
00388                         if (net_amIServer())
00389                         {
00390                                 sstr >> tmp >> tmp; //COMMIT TIME
00391                                 int number;
00392                                 int randomNumber;
00393                                 sstr >> randomNumber >> number;
00394                                 hash_map<int,int>::iterator iter = randomNumberList.find(number);
00395                                 if (iter == randomNumberList.end())
00396                                 {
00397                                         randomNumberList.insert(pair<int,int>(number,randomNumber));
00398                                 } else if (iter->second != randomNumber)
00399                                 {
00400                                         stringstream msg;
00401                                         msg << COMMAND_TEXT_MESSAGE << " " << COMMIT_TIME << " " << currGameTick + NETWORK_GAME_TICK_LAG << " "
00402                                                 << "MAJOR ERROR! Games are out of sync at " << currGameTick;
00403                                         net_send(msg.str());
00404                                 }
00405                         }
00406                 } else if (tmp == COMMAND_SEND_JET_REINFORCEMENT)
00407                 {
00408                         sstr >> tmp >> tmp;
00409                         int battleNumber;
00410                         sstr >> battleNumber;
00411                         int unitNumber;
00412                         sstr >> unitNumber;
00413                         BattleEntry* entry(GameState::getBattle(battleNumber));
00414                         MilitaryUnit* unit(GameState::getMilitaryUnit(unitNumber));
00415                         if (entry != 0 && unit != 0)
00416                         {
00417                                 entry->sendAirReinforcement( unit->color(), unit->armySize(), dynamic_cast<Jet*>(unit)->getCityID() );
00418                                 Overhead::deleteMilitaryUnit(unit);
00419                         } else 
00420                         {
00421                                 sys_console() << WARNING << " entry or unit not found" << endl;
00422                         }
00423                 } else if (tmp == COMMAND_SEND_UNIT_REINFORCEMENT)
00424                 {
00425                         sstr >> tmp >> tmp;
00426                         int battleNumber;
00427                         sstr >> battleNumber;
00428                         int unitNumber;
00429                         sstr >> unitNumber;
00430                         BattleEntry* entry(GameState::getBattle(battleNumber));
00431                         MilitaryUnit* unit(GameState::getMilitaryUnit(unitNumber));
00432                         if (entry != 0 && unit != 0)
00433                         {
00434                                 entry->sendGroundReinforcement( unit->color(), unit->unitType(), unit->armySize(), unit->shortcutKey() );
00435                                 Overhead::deleteMilitaryUnit(unit);
00436                         } else
00437                         {
00438                                 sys_console() << WARNING << " entry or unit not found" << endl;
00439                         }
00440                 } else 
00441                 {
00442                         sys_console() << WARNING 
00443                                 << "throw Error(\"Don't know what to do with command received over the network.  Command received is:\" + *iter);" 
00444                                 << *iter << endl;
00445                         sys_flushConsole();
00446                 }
00447         }
00448         commands.clear();
00449         netData.clean(currGameTick);
00450 }
00451 
00452 void enQueueMsg(string s)
00453 {
00454         netData.addData(s);
00455 }
00456 
00457 
00458 void receiveNetworkData(void)
00459 {
00460         string str;
00461         while((str = net_recv()).size() != 0) 
00462         {
00463                 istringstream strm( str.c_str() );
00464                 string cmd;
00465                 strm >> cmd;
00466                 if( cmd!="CommandSync" ) sys_console() << NETWORK_DATA << str << endl;          // don't print all sync messages
00467                 sys_flushConsole();
00468                 netData.addData(str);
00469         }
00470 }
00471 
00472 void loadNetworkData(ifstream & in)
00473 {
00474         sys_console() << "Loading Network Data from a file" << endl;
00475         string line;
00476         do 
00477         {
00478                 getline(in,line);
00479                 if(line.find(NETWORK_DATA) != string::npos)
00480                 {
00481                         sys_console() << "loading data:" << line.substr(NETWORK_DATA.size()) << endl;
00482                         netData.addData(line.substr(NETWORK_DATA.size()));
00483                 }
00484         } while(in.good());
00485         sys_console() << "Done Loading Data" << endl;
00486 }
00487 
00488 } //end namespace

Generated on Wed Apr 23 05:50:16 2003 for Modern Warfare by doxygen1.3-rc2