00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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
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 }
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
00207 stringstream sstr(*iter);
00208 string tmp;
00209 sstr >> tmp;
00210 if (tmp == COMMAND_OVERHEAD)
00211 {
00212 sstr >> tmp;
00213
00214 int itmp;
00215 sstr >> itmp;
00216
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;
00325 sstr >> tmp;
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;
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;
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;
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;
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;
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 }