/** @file /world/world_server.cpp Implementace hlavickoveho souboru /world/world_server.h @author Petr Wolf */ #include "net/server.h" #include "common/rm/rminit.h" #include "common/rm/rmmap.h" #include "common/TCL/tcl_script.h" #include "common/xml/package.h" #include "common/xml/packsmall.h" #include "common/Interface.h" #include "common/Log.h" #include "common/Msg.h" #include "world/typedefs.h" #include "world/world_server.h" #include "world/useful.h" #include "world/world_client.h" #include "ai/Diplomacy/diptypes.h" #pragma warning( disable : 4311 ) using namespace std; using namespace ai_ns::diplomacy_ns; using namespace ai_ns::pathfind_ns; using namespace rm; namespace World { int game_start_disconnections[MAX_PLAYERS]; TWorldServer * world_server = NULL; /////////////////////////////////////////////////////////////////////////////// // staticky cleny /////////////////////////////////////////////////////////////////////////////// void TWorldServer::initWorldServer() { world_server = new TWorldServer(); } void TWorldServer::destroyWorldServer() { world_server->shutdown(); // delete world_server; } /////////////////////////////////////////////////////////////////////////////// // init & destroy /////////////////////////////////////////////////////////////////////////////// TWorldServer::TWorldServer() : world(), history(), engine(&world, &history) { // zarazeni do fronty zprav transceiver.fnc = WORLD_SERVER_MSG_HANDLER; transceiver.iActived = false; transceiver.iPriority = WORLD_SERVER_PRIORITY; transceiver.PrimaryID = MOD_WORLD_SERVER; KRegisterGlobalTransceiver(&transceiver, MOD_WORLD_SERVER); state = WSS_EMPTY; mapId = 0; } TWorldServer::~TWorldServer() { } void TWorldServer::init() { // inicializace transcieveru transceiver.iActived = true; // PathFind se ted inicializuje az v AIInit(); //pathFindTransciever = new ai_ns::pathfind_ns::CPathFindEngine(); // inicializace sveta world.init(); // inicializace world_engine engine.init(&world); // registrace zprav z TCL engine.interpreter.createCommand("KSendMessage", (Tcl_CmdProc *)WorldServer_SendMessage); // inkluze knihoven pro TCL (spusteni inicializacniho skriptu) engine.interpreter.init(world.rules->scripts[TS_INIT]); } void TWorldServer::shutdown() { transceiver.iActived = false; state = WSS_EMPTY; } void TWorldServer::synchronize() { THistoryIterator iterAction; TPlayerIterator iterPlayer; for (iterPlayer = world.players.begin(); iterPlayer != world.players.end(); iterPlayer++) { if (iterPlayer->second->data().client_data.type == PT_HUMAN) { for (iterAction = history.begin(); iterAction != history.end(); iterAction++) { if (!iterAction->second->isPlayerSynchronized(iterPlayer->first)) { // hrac se o teto akci jeste nedozvedel, poslu mu synzhronizacni balicek synchro(iterPlayer->first, iterAction->first, iterAction->second->data()); } } } } } void TWorldServer::synchro(PLAYER_ID player_id, ACTION_ID action_id, TAction & action) { // zamceni engine, aby se nic neposralo engine.lock(); // zalozeni zpravy TPackage package(MSG_WORLD_CLIENT_SERVER); package.setInt(MSG_SYNCHRO, PACKET_WORLD::type, 0, NULL); TPackSmall * xml = NULL; // naplneni daty switch (action.type()) { case AT_GAME_START: // celkova synchronizace na zacatku hry - zaslani vsech udaju { // ulozeni hracu package.setEmpty("players", 0, NULL); xml = new TPackSmall(package.getSubtag("players", 0, NULL)); world.players.writeToXML(xml, "player"); delete xml; // ulozeni mapy package.setEmpty("map", 0, NULL); xml = new TPackSmall(package.getSubtag("map", 0, NULL)); world.map.writeToXML(xml); delete xml; // ulozeni masek viditelnosti package.setEmpty("visibility_maps", 0, NULL); xml = new TPackSmall(package.getSubtag("visibility_maps", 0, NULL)); world.visibility_maps.writeToXML(xml); delete xml; // ulozeni kralovstvi package.setEmpty("kingdoms", 0, NULL); xml = new TPackSmall(package.getSubtag("kingdoms", 0, NULL)); world.kingdoms.writeToXML(xml, "kingdom"); delete xml; // ulozeni mest package.setEmpty("towns", 0, NULL); xml = new TPackSmall(package.getSubtag("towns", 0, NULL)); world.towns.writeToXML(xml, "town"); delete xml; // ulozeni zivoucich jednotek package.setEmpty("units", 0, NULL); xml = new TPackSmall(package.getSubtag("units", 0, NULL)); world.units.writeToXML(xml, "unit"); delete xml; // ulozeni zivoucich budov package.setEmpty("buildings", 0, NULL); xml = new TPackSmall(package.getSubtag("buildings", 0, NULL)); world.buildings.writeToXML(xml, "building"); delete xml; xml = NULL; // pocasi package.setEmpty("weather", 0, NULL); xml = new TPackSmall(package.getSubtag("weather", 0, NULL)); world.weather.writeToXML(xml); delete xml; xml = NULL; // hrac na tahu package.setInt(world.players.playerIdOnTurn(), PACKET_NET_GAME_START::player_on_turn, 0, NULL); // cislo kola package.setInt(world.turn_id, PACKET_NET_GAME_START::turn_id, 0, NULL); } break; case AT_ENDGAME: // konec hry { TPacket_END_GAME * data = (TPacket_END_GAME *)action.data(); data->writeToXML(&package); } break; case AT_PLAYER_DEFEATED: // porazeni hrace { TPacket_PLAYER_DEFEATED * data = (TPacket_PLAYER_DEFEATED *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_SUICIDE: // sebezniceni jednotky { TPacket_RCT_UNIT_SUICIDE * data = (TPacket_RCT_UNIT_SUICIDE *)action.data(); data->writeToXML(&package); } break; case AT_BUILDING_SUICIDE: // sebezniceni budovy { TPacket_RCT_BUILDING_SUICIDE * data = (TPacket_RCT_BUILDING_SUICIDE *)action.data(); data->writeToXML(&package); } break; case AT_BUILDING_RECRUIT_UNIT: // vyroba jednotky { TPacket_RCT_BUILDING_RECRUIT_UNIT * data = (TPacket_RCT_BUILDING_RECRUIT_UNIT *)action.data(); data->writeToXML(&package); } break; case AT_BUILDING_SELL_BONUS: // prodej bonusu { TPacket_RCT_SELL_BONUS * data = (TPacket_RCT_SELL_BONUS *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_BUILD_START: // zahajeni stavby { TPacket_RCT_START_BUILDING * data = (TPacket_RCT_START_BUILDING *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_BUILD_STOP: // preruseni stavby { TPacket_RCT_STOP_BUILDING * data = (TPacket_RCT_STOP_BUILDING *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_REPAIR_START: // oprava budovy { TPacket_RCT_REPAIR_BUILDING * data = (TPacket_RCT_REPAIR_BUILDING *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_MOVE: // pohyb jednotky { // ulozeni cesty TPacket_RCT_UNIT_MOVE * data = (TPacket_RCT_UNIT_MOVE *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_ATTACK: // utok { TPacket_RCT_UNIT_ATTACK * data = (TPacket_RCT_UNIT_ATTACK *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_HEAL: // leceni jednotky { TPacket_RCT_UNIT_HEAL * data = (TPacket_RCT_UNIT_HEAL *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_RECRUIT: // doplneni jednotky (neelitni) { TPacket_RCT_UNIT_RECRUIT * data = (TPacket_RCT_UNIT_RECRUIT *)action.data(); data->writeToXML(&package); } break; case AT_UNIT_RECRUIT_ELITE: // doplneni jednotky (elitni) { TPacket_RCT_UNIT_RECRUIT_ELITE * data = (TPacket_RCT_UNIT_RECRUIT_ELITE *)action.data(); data->writeToXML(&package); } break; case AT_NEXT_TURN: // konec tahu case AT_NEXT_ROUND: // konec kola { // printf("KONEC KOLA"); TPacket_RCT_NEXT_TURN * data = (TPacket_RCT_NEXT_TURN *)action.data(); data->writeToXML(&package); } break; case AT_BUILDING_OCCUPIED: // obsazeni budovy { TPacket_BUILDING_OCCUPIED * data = (TPacket_BUILDING_OCCUPIED *)action.data(); data->writeToXML(&package); } break; case AT_TOWN_OCCUPIED: // obsazeni mesta { TPacket_TOWN_OCCUPIED * data = (TPacket_TOWN_OCCUPIED *)action.data(); data->writeToXML(&package); } break; case AT_KINGDOM_OCCUPIED: // obsazeni kralovstvi { TPacket_KINGDOM_OCCUPIED * data = (TPacket_KINGDOM_OCCUPIED *)action.data(); data->writeToXML(&package); } break; case AT_VISIBILITY_CHANGE: // zmena viditelnosti { TPacket_VISIBILITY_CHANGE * data = (TPacket_VISIBILITY_CHANGE *)action.data(); data->writeToXML(&package); } break; case AT_DIPLOMACY_RELATION_CHANGE: // zmena diplomatickych vztahu { TPacket_DIPLOMACY_RELATION_CHANGE * data = (TPacket_DIPLOMACY_RELATION_CHANGE *)action.data(); data->writeToXML(&package); } break; } if (xml) delete xml; // nastaveni player_id, action_id a action_type tagu package.setInt(action_id, PACKET_SYNCHRO::action_id, 0, NULL); package.setInt(action.type(), PACKET_SYNCHRO::action_type, 0, NULL); package.setInt(player_id, PACKET_WORLD::player_id, 0, NULL); // odeslani package.send(TO_SERVER, world.players[player_id]->data().client_data.client_id, MOD_WORLD_CLIENT); engine.unlock(); } void TWorldServer::loadMap(int map_id) { int i; // nacteni mapy TRM_map_i * RMmapi = (TRM_map_i *)KSendGlobalMessage(MSG_GET_RM_MAP_I, ANNONYMOUS, BROADCAST, NULL); // nacteni hlavicky TMapInfo * info; RMmapi->getMapInfo(&info, map_id); if (info == NULL) { // mapu se nepodarilo nacist THROW(E_8K_INVALID_REQUEST, "Unable to load map of given id"); } // kontrola hracu for (i = 1; i <= MAX_PLAYERS; i++) { if (info->roles[i] == NULL) { // role neni v mape if (world_server->world.players.find(i) != world_server->world.players.end()) { // presto se mi prihlasil hrac s takovym id -> odstranim ho world_server->world.players.erase(i); } } else { if (world_server->world.players.find(i) != world_server->world.players.end()) { // mam hrace pro danou roli -> nactu mu data strncpy(world_server->world.players[i]->data().game_data.name, info->roles[i]->name, MAX_STRLEN); world_server->world.players[i]->data().game_data.money = info->roles[i]->money; world_server->world.players[i]->data().game_data.state = (PLAYER_STATE)info->roles[i]->state; // relations - nacteni world_server->world.players[i]->_relations_self_allocated = true; for (int j = 1; j <= MAX_PLAYERS; j++) { if (info->roles[i]->relations[j]) { world_server->world.players[i]->data().game_data.relations[j] = (TDipRelation *)KMemAlloc(sizeof(TDipRelation)); *world_server->world.players[i]->data().game_data.relations[j] = *info->roles[i]->relations[j]; } } // stats world_server->world.players[i]->data().statistics = info->roles[i]->stats; } else { // nemam hrace pro danou roli -> nahradim ji umelou inteligenci // to zatim neni -> misto toho hodim vyjimku THROW(E_8K_WORLD_INVALID_DATA, "Received list of players does not match roles from selcted map.\n"); } } } // nacteni tela MAP * map = (MAP *)KMemAlloc(sizeof(MAP)); map->width = info->sizex; map->height = info->sizey; RMmapi->request(map_id); RMmapi->loadBuffer(); RMmapi->getHexes(&map->hexes); // ulozeni mapy do struktury sveta world_server->world.map.load(map); world_server->world.map.self_allocated(true); // stav hry TStateInfo * state; RMmapi->getCurrentState(&state); world_server->world.players.setPlayerOnTurn(state->player_on_turn); world_server->world.weather.data().state = state->weather; world_server->world.weather.data().duration = state->duration; world_server->world.turn_id = state->turn_id; // nacteni kralovstvi KINGDOM ** kingdoms; RMmapi->getKingdoms(&kingdoms); world_server->world.kingdoms.load(kingdoms, MAX_KINGDOMS + 1); // nacteni mest DA * towns; RMmapi->getCities(&towns); world_server->world.towns.load(towns); // nacteni zivych jednotek DA * living_units; RMmapi->getLivingUnits(&living_units); // ulozeni zivych jednotek do struktur sveta world_server->world.units.load(living_units); // nacteni zivych budov DA * living_buildings; RMmapi->getLivingBuildings(&living_buildings); // ulozeni zivych budov do struktur sveta world_server->world.buildings.load(living_buildings); // nacteni masek viditelnosti HISTORY_HEX *** hm; RMmapi->getHistoryHexes(&hm); world.visibility_maps.load(hm, world.map); // nacteni AI dat RMmapi->getAIPlan(&world.players.ai_data); delete(RMmapi); // vyplneni redundantnich relaci world_server->world.fillRelations(); // spocitalni aktualni viditelnosti pro vsechny hrace engine.calculateVisibility(); } void TWorldServer::saveMap(char * filename) { if (state != WSS_GAME_STARTED) THROW(E_8K_INVALID_REQUEST, "World not loaded"); // aktualizace diplomatickych koeficientu TPacket_Diplomacy_GetOtherRelationProperties packet; TRelationshipProperties * properties; for (TPlayerIterator it = world.players.begin(); it != world.players.end(); it++) { packet.source_diplomat_world_id = it->first; for (int i = 0; i < MAX_PLAYERS; i++) { if ((it->second->data().game_data.state == PS_ACTIVE) && (it->second->data().game_data.relations[i])) { packet.target_diplomat_world_id = i; properties = (TRelationshipProperties *)KSendGlobalMessage(MSG_DIPLOMACY_GET_OTHER_RELATION_PROPERTIES, MOD_WORLD_SERVER, MOD_DIPLOMACY, &packet); it->second->data().game_data.relations[i]->relationship_properties.belief = properties->belief; it->second->data().game_data.relations[i]->relationship_properties.guess_belief = properties->guess_belief; it->second->data().game_data.relations[i]->relationship_properties.offer_seen_by_enemy = properties->offer_seen_by_enemy; } } } TRM_map_i * rmmapi = (TRM_map_i *)KSendGlobalMessage(MSG_GET_RM_MAP_I, ANNONYMOUS, BROADCAST, NULL); // 1. ulozeni dat do RM rmmapi->setHexes(world.map.save()); rmmapi->setLivingBuildings(world.buildings.save()); rmmapi->setLivingUnits(world.units.save()); rmmapi->setCities(world.towns.save()); rmmapi->setKingdoms(world.kingdoms.save(MAX_KINGDOMS + 1)); rmmapi->setAIPlan(world.players.ai_data); // ulozeni historickych map HISTORY_HEX *** history = world.visibility_maps.save(); rmmapi->setHistoryHexes(history); // ulozeni TMapInfo TMapInfo * info; int map_id = rmmapi->idOfLoadedMap(); rmmapi->getMapInfo(&info, map_id); world.players.save(info->roles); // ulozeni TStateInfo TStateInfo * state = (TStateInfo *)KMemAlloc(sizeof(TStateInfo)); state->weather = world.weather.data().state; state->duration = world.weather.data().duration; state->player_on_turn = world.players.playerIdOnTurn(); state->turn_id = world.turn_id; rmmapi->setCurrentState(state); // 2. ulozeni do souboru rmmapi->saveMap(MAP_TYPE_SAVE_GAME, filename); // 3. nacteni zpet z RM // nacteni hexu HEX ** hexes; rmmapi->getHexes(&hexes); world.map.reload(hexes); // nacteni kralovstvi KINGDOM ** kingdoms; rmmapi->getKingdoms(&kingdoms); world.kingdoms.load(kingdoms, MAX_KINGDOMS + 1); // nacteni mest DA * towns; rmmapi->getCities(&towns); world.towns.load(towns); // nacteni zivych jednotek DA * living_units; rmmapi->getLivingUnits(&living_units); world.units.load(living_units); // nacteni zivych budov DA * living_buildings; rmmapi->getLivingBuildings(&living_buildings); world.buildings.load(living_buildings); // nacteni TStateInfo rmmapi->getCurrentState(&state); // nacteni masek viditelnosti HISTORY_HEX *** hm; rmmapi->getHistoryHexes(&hm); world.visibility_maps.load(hm, world.map); // nacteni AI dat rmmapi->getAIPlan(&world.players.ai_data); delete(rmmapi); // vyplneni redundantnich relaci world.fillRelations(); // spocitalni aktualni viditelnosti pro vsechny hrace engine.calculateVisibility(); } void TWorldServer::loadPlayers(PACKET_PLAYER_SETTINGS players[MAX_PLAYERS]) { PLAYER * player; bool human_player = false; // nasazim hrace do seznamu for (int i = 0; i < MAX_PLAYERS; i++) { if (players[i].player_id > 0) { player = (PLAYER *)KMemAlloc(sizeof(PLAYER)); PLAYER_Init(player); player->game_data.state = PS_ACTIVE; player->client_data.acommands=players[i].acommands; if ((player->client_data.type = players[i].type) == PT_HUMAN) { human_player = true; player->client_data.client_id = players[i].client_id; } else { player->client_data.client_id = TO_SERVER; } player->client_data.connected = false; player->client_data.guimap_ready = false; strncpy(player->client_data.name, players[i].name, MAX_STRLEN); world_server->world.players[players[i].player_id] = new TPlayer(player, players[i].player_id); world_server->world.players[players[i].player_id]->self_allocated(true); } else { // konec cyklu break; } } // prizpusobeni kontejneru historie poctu hracu world_server->history.init(world_server->world.players); if (!human_player) THROW(E_8K, "No human player"); } void TWorldServer::connectPlayer(int client_id) { TPlayerIterator i; int ccc; // reset klientu for (i = world.players.begin(); i != world.players.end(); i++) { ccc=i->second->data().client_data.client_id; if ((i->second->data().client_data.type == PT_HUMAN) && (i->second->data().client_data.client_id==client_id)) { TPackage reset(MSG_GAME_PREPARE); reset.send(TO_SERVER, i->second->data().client_data.client_id, MOD_WORLD_HANDLER); } } // hracum ze seznamu rozposilam prikazy k pripojeni for (i = world_server->world.players.begin(); i != world_server->world.players.end(); i++) { ccc=i->second->data().client_data.client_id; if ((i->second->data().client_data.type == PT_HUMAN) && (i->second->data().client_data.client_id==client_id)) { // hrac je clovek a aktivni - poslu mu pozvanku TPackage package(MSG_JOIN_GAME); package.setInt(i->first, PACKET_WORLD::player_id, 0, NULL); package.send(TO_SERVER, i->second->data().client_data.client_id, MOD_WORLD_HANDLER); } else { if ((i->second->data().client_data.client_id==client_id)) { // hrac je AI nebo jiz vyrazen, oznacim jej rovnou za pripojeneho i->second->data().client_data.connected = true; } } } } void TWorldServer::connectPlayers() { TPlayerIterator i; // reset klientu for (i = world.players.begin(); i != world.players.end(); i++) { if (i->second->data().client_data.type == PT_HUMAN) { TPackage reset(MSG_GAME_PREPARE); reset.send(TO_SERVER, i->second->data().client_data.client_id, MOD_WORLD_HANDLER); } } // hracum ze seznamu rozposilam prikazy k pripojeni for (i = world_server->world.players.begin(); i != world_server->world.players.end(); i++) { if (i->second->data().client_data.type == PT_HUMAN) { // hrac je clovek a aktivni - poslu mu pozvanku TPackage package(MSG_JOIN_GAME); package.setInt(i->first, PACKET_WORLD::player_id, 0, NULL); package.send(TO_SERVER, i->second->data().client_data.client_id, MOD_WORLD_HANDLER); } else { // hrac je AI nebo jiz vyrazen, oznacim jej rovnou za pripojeneho i->second->data().client_data.connected = true; } } } void TWorldServer::prepareGame() { // zapoceti historie history.add(new TAction(AT_GAME_START)); // inicializace pathfind KSendGlobalMessage(MSG_PATHFIND_INIT, MOD_WORLD_SERVER, MOD_PATHFIND, NULL); // inicializace diplomacie KSendGlobalMessage(MSG_DIPLOMACY_INIT, MOD_WORLD_SERVER, MOD_DIPLOMACY, NULL); int i; TPlayerIterator it; TPacket_Diplomacy_AddDiplomat add; TPacket_Diplomacy_SetRelation set; TPacket_Diplomacy_SetOfferRelation offer; TPacket_Diplomacy_SetOtherRelationProperties properties; // pridani diplomatu for (it = world.players.begin(); it != world.players.end(); it++) { add.diplomat_world_id = it->first; add.diplomat_type = (it->second->data().client_data.type == PT_HUMAN) ? DT_HUMAN : DT_AI; KSendGlobalMessage(MSG_DIPLOMACY_ADD_DIPLOMAT, MOD_WORLD_SERVER, MOD_DIPLOMACY, &add); } // nastaveni vazeb for (it = world.players.begin(); it != world.players.end(); it++) { set.source_diplomat_world_id = it->first; offer.source_diplomat_world_id = it->first; properties.source_diplomat_world_id = it->first; for (i = 1; i <= MAX_PLAYERS; i++) { if (it->second->data().game_data.relations[i] != NULL) { // oficialni vztah set.target_diplomat_world_id = i; set.set_relationship = it->second->data().game_data.relations[i]->rs; KSendGlobalMessage(MSG_DIPLOMACY_SET_RELATION, MOD_WORLD_SERVER, MOD_DIPLOMACY, &set); // navrhovany vztah offer.target_diplomat_world_id = i; offer.offered_relationship = it->second->data().game_data.relations[i]->offeredrs; KSendGlobalMessage(MSG_DIPLOMACY_SET_OFFER_RELATION, MOD_WORLD_SERVER, MOD_DIPLOMACY, &offer); // vlastnosti vztahu properties.target_diplomat_world_id = i; properties.relationship_properties.belief = it->second->data().game_data.relations[i]->relationship_properties.belief; properties.relationship_properties.guess_belief = it->second->data().game_data.relations[i]->relationship_properties.guess_belief; properties.relationship_properties.offer_seen_by_enemy = it->second->data().game_data.relations[i]->relationship_properties.offer_seen_by_enemy; KSendGlobalMessage(MSG_DIPLOMACY_SET_OTHER_RELATION_PROPERTIES, MOD_WORLD_SERVER, MOD_DIPLOMACY, &properties); } } } KSendGlobalMessage(MSG_DIPLOMACY_SYNC, MOD_WORLD_SERVER, MOD_DIPLOMACY, NULL); // inicializace AI TPacket_AI_LetsGo packet; packet.corpses = &world.corpses; packet.kingdoms = &world.kingdoms; packet.living_buildings = &world.buildings; packet.living_units = &world.units; packet.map = &world.map; packet.players = &world.players; packet.ruins = &world.ruins; packet.rules = world.rules; packet.towns = &world.towns; packet.visibility_maps = &world.visibility_maps; // printf("AI LETS GO"); KSendGlobalMessage(MSG_AI_LETS_GO, MOD_WORLD_SERVER, MOD_STRATEGY, &packet); // hra je pripravena state = WSS_GAME_PREPARED; // rozeslani prvotni sycnhronizace synchronize(); } void TWorldServer::startGame() { // hra zacala // printf("START GAME FCE"); state = WSS_GAME_STARTED; // rozeslu informace o zahajeni hry TPlayerIterator it; for (it = world.players.begin(); it != world.players.end(); it++) { if (it->second->data().client_data.type == PT_HUMAN) { TPackage package(MSG_GAME_STARTED); package.send(TO_SERVER, it->second->data().client_data.client_id, MOD_GUI); package.send(TO_SERVER, it->second->data().client_data.client_id, MOD_WORLD_CLIENT); } } // Provedu test konce hry. Pokud skonci jeste pred zacaatkem, rozeslu hned end_game. // Pokud hra pokracuje a je na tahu AI, predam ji tah, jinak uz nic delat nemusim, // jen cekat na akce hrace if (world_server->engine.finalTest()) { world_server->synchronize(); world_server->endGame(); } else { world_server->synchronize(); if (world_server->world.players.playerOnTurn()->data().client_data.type != PT_HUMAN) { WORLD_SERVER_AI_ON_TURN; } } } void TWorldServer::endGame() { // zmena stavu state = WSS_GAME_ENDED; // vraceni dat do RM TRM_map_i * rmmapi = (TRM_map_i *)KSendGlobalMessage(MSG_GET_RM_MAP_I, ANNONYMOUS, BROADCAST, NULL); rmmapi->setHexes(world.map.save()); rmmapi->setLivingBuildings(world.buildings.save()); rmmapi->setLivingUnits(world.units.save()); rmmapi->setCities(world.towns.save()); rmmapi->setKingdoms(world.kingdoms.save(MAX_KINGDOMS + 1)); rmmapi->setHistoryHexes(world.visibility_maps.save()); delete(rmmapi); // dealokace vsech struktur sveta world.reset(); } int game_start_thread(void * data) { PACKET_GAME_START * packet = (PACKET_GAME_START *)data; try { // nacteni hracu world_server->loadPlayers(packet->players); // nacteni mapy world_server->loadMap(packet->map_id); world_server->state = WSS_WORLD_LOADED; world_server->end_game_condition = packet->end_game_condition; // rozeslani pozvanek k pripojeni world_server->connectPlayers(); world_server->state = WSS_INVITATIONS_SENT; // vyreseni odpojenych klientu for (int i = 0; i < MAX_PLAYERS; i++) { if (game_start_disconnections[i] != 0) { // doslo k odpojeni klienta behem nahravani - nahravani se musi opakovat world_server->engine.endGame(ER_SHUTDOWN); world_server->synchronize(); world_server->endGame(); break; } else break; } // dealokace paketu a navrat KMemFree(packet); return 0; } catch (E_8K &e) { // chyba pri nahravani KMemFree(packet); KSendGlobalMessage(MSG_EXCEPTION_IN_THREAD, MOD_WORLD_SERVER, BROADCAST, &e); return 1; } } /////////////////////////////////////////////////////////////////////////////// // server handler /////////////////////////////////////////////////////////////////////////////// RVAL WORLD_SERVER_MSG_HANDLER(MESSAGE_ID mid, SENDER s, PARAM p) { switch (mid) { case MSG_WORLD_AI: // Komunikace s AI { PACKET_WORLD_AI * packet = (PACKET_WORLD_AI *)p; #ifndef _K_EDITOR_ // kontrola opravnenosti AI neco delat if (world_server->state != WSS_GAME_STARTED) return new TPacket_SyncResult(ERR_GameOver); if (!world_server->world.playerOperational(packet->player_id) || (world_server->state != WSS_GAME_STARTED)) return new TPacket_SyncResult(ERR_NotOperational); #endif switch (packet->type) { case MSG_GET_INVERSE_ATTACK_RANGE: // oblast, ze ktere je mozne napadnout hex { PACKET_GET_INVERSE_ATTACK_RANGE * package = (PACKET_GET_INVERSE_ATTACK_RANGE *)p; TPacket_RET_INVERSE_ATTACK_RANGE * result = new TPacket_RET_INVERSE_ATTACK_RANGE(); result->attack_range = world_server->engine.getInverseAttackRange(package->attacker, package->target); return result; } break; case MSG_AI_GET_HISTORY_PIECE: // ziskani nasledujici akce z kontejneru historie { PACKET_AI_GET_HISTORY_PIECE * packet = (PACKET_AI_GET_HISTORY_PIECE *)p; TPacket_AI_RET_HISTORY_PIECE * result = new TPacket_AI_RET_HISTORY_PIECE(); TActionContainer * container; if ((packet->max_action_id < 0) || (packet->max_action_id > world_server->history.maxId())) { // dotaz mimo rozsah result->status = ERR_InvalidInput; } else if (packet->max_action_id == world_server->history.maxId()) { // uz jsem dosahl vrcholu zasobniku result->status = ERR_NoMoreActions; } else { // v poradku, platny dotaz result->action_id = packet->max_action_id + 1; container = world_server->history.get(packet->max_action_id + 1); result->action_type = container->data().type(); result->data = container->data().data(); } return result; } break; case MSG_ACT_NEXT_TURN: // konec tahu { WORLD_SERVER_NEXT_TURN; } break; // +------------------------------------------------------+ // | | // | A K C E B U D O V | // | | // +------------------------------------------------------+ case MSG_ACT_BUILDING_RECRUIT_UNIT: // vyroba jednotky { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_BUILDING_RECRUIT_UNIT * packet = (PACKET_ACT_BUILDING_RECRUIT_UNIT *)p; // kontrola if (!WORLD_SERVER_AI_BUILDING_OPERATIONAL(packet->building_id)) result->status = ERR_NotOperational; else { // vyhodnoceni vyroby result->status = world_server->engine.buildingRecruitUnit(packet->building_id, packet->unit_type); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_BUILDING_SUICIDE: // sebezniceni budovy { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_BUILDING_SUICIDE * packet = (PACKET_ACT_BUILDING_SUICIDE *)p; // kontrola if (!WORLD_SERVER_AI_BUILDING_OPERATIONAL(packet->building_id)) result->status = ERR_NotOperational; else { // vyhodnoceni sebezniceni world_server->engine.buildingSuicide(packet->building_id); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_SELL_BONUS: // prodej bonusu jednotce v budove { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_SELL_BONUS * packet = (PACKET_ACT_SELL_BONUS *)p; // kontrola if (!WORLD_SERVER_AI_BUILDING_OPERATIONAL(packet->building_id)) result->status = ERR_NotOperational; else { // vyhodnoceni prodeje result->status = world_server->engine.sellBonus(packet->building_id, packet->bonus_id); } // finalni test world_server->engine.finalTest(); return result; } break; // +------------------------------------------------------+ // | | // | A K C E J E D N O T E K | // | | // +------------------------------------------------------+ case MSG_ACT_REPAIR_BUILDING: // oprava budovy { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_REPAIR_BUILDING * packet = (PACKET_ACT_REPAIR_BUILDING *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni oprav result->status = world_server->engine.repairBuilding(packet->unit_id, packet->building_id); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_START_BUILDING: // zahajeni stavby { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_START_BUILDING * packet = (PACKET_ACT_START_BUILDING *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni vyroby result->status = world_server->engine.startBuilding(packet->unit_id, packet->building_type, packet->location, packet->orientation); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_STOP_BUILDING: { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_STOP_BUILDING * packet = (PACKET_ACT_STOP_BUILDING *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni preruseni stavby result->status = world_server->engine.stopBuilding(packet->unit_id); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_UNIT_ATTACK: // utok jednotky { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_UNIT_ATTACK * packet = (PACKET_ACT_UNIT_ATTACK *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->attacker)) result->status = ERR_NotOperational; else { // vyhodnoceni utoku result->status = world_server->engine.unitAttack(packet->attacker, packet->target); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_UNIT_HEAL: // leceni jednotky { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_UNIT_HEAL * packet = (PACKET_ACT_UNIT_HEAL *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni leceni result->status = world_server->engine.unitHeal(packet->unit_id); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_UNIT_MOVE: // pohyb jednotky { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_UNIT_MOVE * packet = (PACKET_ACT_UNIT_MOVE *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // zmenim polohu jednotky //result->status = world_server->engine.unitMove(packet->unit_id, packet->target, PFT_BridgeAnalysis); result->status = world_server->engine.unitMove(packet->unit_id, packet->target, packet->transparency); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_UNIT_RECRUIT: // doplneni jednotky (neelitni) { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_UNIT_RECRUIT * packet = (PACKET_ACT_UNIT_RECRUIT *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni doplneni result->status = world_server->engine.unitRecruit(packet->unit_id); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_UNIT_RECRUIT_ELITE: // doplneni jednotky (elitni) { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_UNIT_RECRUIT_ELITE * packet = (PACKET_ACT_UNIT_RECRUIT_ELITE *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni elitniho doplneni result->status = world_server->engine.unitRecruitElite(packet->unit_id); } // finalni test world_server->engine.finalTest(); return result; } break; case MSG_ACT_UNIT_SUICIDE: // sebezniceni jednotky { // pretypovani a nacteni TPacket_SyncResult * result = new TPacket_SyncResult(); PACKET_ACT_UNIT_SUICIDE * packet = (PACKET_ACT_UNIT_SUICIDE *)p; // kontrola if (!WORLD_SERVER_AI_UNIT_OPERATIONAL(packet->unit_id)) result->status = ERR_NotOperational; else { // vyhodnoceni sebezniceni result->status = world_server->engine.unitSuicide(packet->unit_id); } // finalni test world_server->engine.finalTest(); return result; } break; } } break; // +------------------------------------------------------------------+ // | | // | W O R L D _ C L I E N T - W O R L D _ S E R V E R | // | | // +------------------------------------------------------------------+ case MSG_WORLD_CLIENT_SERVER: { TPackage * package = (TPackage *)p; int player_id = package->getInt(PACKET_WORLD::player_id, 0, NULL); int type = package->getInt(PACKET_WORLD::type, 0, NULL); int error; try { switch (type) { case MSG_JOIN_CONFIRMATION: // potvrzeni pripojeni klienta { if (world_server->state == WSS_INVITATIONS_SENT) { // pripojim hrace world_server->world.players[player_id]->data().client_data.connected = true; // zkontroluji, jestli uz jsou pripojeni vsichni bool connected = true; for (TPlayerIterator i = world_server->world.players.begin(); i != world_server->world.players.end(); i++) { if (i->second->data().client_data.connected == 0) { connected = false; break; } } // pokud ano, spustim hru if (connected) { world_server->state = WSS_PLAYERS_CONNECTED; world_server->prepareGame(); } } } break; case MSG_SYNCHRO_CONFIRMATION: // potvrzeni synchronizace od klienta { TPackage * package = (TPackage *)p; int action_id = package->getInt(PACKET_SYNCHRO_CONFIRMATION::action_id, 0, NULL); int player_id = package->getInt(PACKET_SYNCHRO_CONFIRMATION::player_id, 0, NULL); world_server->history[action_id]->setPlayerSynchronized(player_id); } break; case MSG_GUIMAP_READY: // potvrzeni nahrani mapy od klienta { if (world_server->state == WSS_GAME_PREPARED) { TPackage * package = (TPackage *)p; int player_id = package->getInt(PACKET_SYNCHRO_CONFIRMATION::player_id, 0, NULL); // oznacim hrace jako pripraveneho world_server->world.players[player_id]->data().client_data.guimap_ready = true; // zkontroluji, jestli uz jsou pripraveni vsichni bool ready = true; for (TPlayerIterator i = world_server->world.players.begin(); i != world_server->world.players.end(); i++) { if ((i->second->data().client_data.type == PT_HUMAN) && (i->second->data().client_data.guimap_ready == false)) { ready = false; break; } } // pokud jsou vsichni hotovi, zacnu hrat if (ready) { world_server->startGame(); } } else if (world_server->state == WSS_GAME_STARTED)// In case of reconnection { int player_id = package->getInt(PACKET_SYNCHRO_CONFIRMATION::player_id, 0, NULL); TPackage package(MSG_GAME_STARTED); package.send(TO_SERVER, player_id, MOD_GUI); package.send(TO_SERVER, player_id, MOD_WORLD_CLIENT); } } break; case MSG_NET_END_GAME_GUI: // ukonceni hry { if (world_server->state == WSS_GAME_STARTED) { int final = package->getInt(PACKET_NET_END_GAME_GUI::final, 0, NULL); int reason = ER_CANCEL; // zkontroluji, kde je server if (world_client[player_id] != NULL) { // na tomto PC soucasne hraje hrac, ktery se odpojil - hra konci final = true; reason = ER_SHUTDOWN; } // zkontroluji, jestli po odstraneni hrace jeste nejaci zivi hraci if ((final == false) && (world_server->world.players.numberOfHumanPlayers(player_id) > 0)) { // poslu odpojovaci zpravu konkretnimu hraci TPackage close(MSG_END_CLIENT); TPacket_END_GAME end_client; end_client.reason = ER_CANCEL; end_client.winner = 0; end_client.writeToXML(&close); close.send(TO_SERVER, world_server->world.players[player_id]->data().client_data.client_id, MOD_WORLD_CLIENT); // zmenim jej na AI world_server->world.players[player_id]->data().client_data.type = PT_AI_3; world_server->world.players[player_id]->data().client_data.oldclient_id = world_server->world.players[player_id]->data().client_data.client_id; world_server->world.players[player_id]->data().client_data.client_id = TO_SERVER; // dam o tom vedet diplomacii TPacket_Diplomacy_ChangeDiplomatType packet; packet.diplomat_world_id = player_id; packet.new_diplomat_type = DT_AI; KSendGlobalMessage(MSG_DIPLOMACY_CHANGE_DIPLOMAT_TYPE, MOD_WORLD_SERVER, MOD_DIPLOMACY, &packet); // pokud byl doted na tahu, predam tah dalsimu hraci if (world_server->world.players.playerIdOnTurn() == player_id) { WORLD_SERVER_NEXT_TURN } } else { // ukoncim hru world_server->engine.endGame(ER_CANCEL); // zavolam synchronizaci world_server->synchronize(); // zrusim zdroje world_server->endGame(); } } } break; case MSG_NET_ACT_UNIT_SUICIDE: // sebezniceni jednotky { TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_UNIT_SUICIDE::unit_id, 0, NULL); if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) world_server->sendErrorReply(package, ERR_NotOperational); else { // vyhodnoceni sebezniceni world_server->engine.unitSuicide(unit_id); // vyhodnoceni konce hry a synchronizace WORLD_SERVER_FINAL_TEST; } } break; case MSG_NET_ACT_BUILDING_SUICIDE: // sebezniceni budovy { TPackage * package = (TPackage *)p; int building_id = package->getInt(PACKET_NET_ACT_BUILDING_SUICIDE::building_id, 0, NULL); if (!WORLD_SERVER_CLIENT_BUILDING_OPERATIONAL(building_id)) world_server->sendErrorReply(package, ERR_NotOperational); else { // vyhodnoceni utoku world_server->engine.buildingSuicide(building_id); // vyhodnoceni konce hry a synchronizace WORLD_SERVER_FINAL_TEST; } } break; case MSG_NET_ACT_BUILDING_RECRUIT_UNIT: // vyroba jednotky { TPackage * package = (TPackage *)p; int building_id = package->getInt(PACKET_NET_ACT_BUILDING_RECRUIT_UNIT::building_id, 0, NULL); int unit_type = package->getInt(PACKET_NET_ACT_BUILDING_RECRUIT_UNIT::unit_type, 0, NULL); // vyhodnoceni vyroby if (!WORLD_SERVER_CLIENT_BUILDING_OPERATIONAL(building_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.buildingRecruitUnit(building_id, unit_type)) { // doslo k chybe - odpovim klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_SELL_BONUS: // prodej bonusu jednotce v budove { // pretypovani a nacteni TPackage * package = (TPackage *)p; int building_id = package->getInt(PACKET_NET_ACT_SELL_BONUS::building_id, 0, NULL); int bonus_id = package->getInt(PACKET_NET_ACT_SELL_BONUS::bonus_id, 0, NULL); // vyhodnoceni if (!WORLD_SERVER_CLIENT_BUILDING_OPERATIONAL(building_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.sellBonus(building_id, bonus_id)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_START_BUILDING: // zahajeni stavby { // pretypovani a nacteni TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_START_BUILDING::unit_id, 0, NULL); int building_type = package->getInt(PACKET_NET_ACT_START_BUILDING::building_type, 0, NULL); int location = package->getInt(PACKET_NET_ACT_START_BUILDING::location, 0, NULL); int orientation = package->getInt(PACKET_NET_ACT_START_BUILDING::orientation, 0, NULL); // vyhodnoceni vyroby if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.startBuilding(unit_id, building_type, location, orientation)) { // doslo k chybe - odpovim klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_REPAIR_BUILDING: // oprava budovy { // pretypovani a nacteni TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_REPAIR_BUILDING::unit_id, 0, NULL); int building_id = package->getInt(PACKET_NET_ACT_REPAIR_BUILDING::building_id, 0, NULL); // vyhodnoceni if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.repairBuilding(unit_id, building_id)) { // doslo k chybe - odpovim klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_STOP_BUILDING: { // pretypovani a nacteni TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_STOP_BUILDING::unit_id, 0, NULL); // vyhodnoceni if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.stopBuilding(unit_id)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_UNIT_MOVE: // zadost o pohyb jednotky { TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_UNIT_MOVE::unit_id, 0, NULL); int to = package->getInt(PACKET_NET_ACT_UNIT_MOVE::to, 0, NULL); // zmenim polohu jednotky if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.unitMove(unit_id, to)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // vyhodnoceni konce hry a synchronizace WORLD_SERVER_FINAL_TEST; } } // case break; case MSG_NET_ACT_UNIT_ATTACK: // utok jednotky { TPackage * package = (TPackage *)p; int attacker = package->getInt(PACKET_NET_ACT_UNIT_ATTACK::attacker, 0, NULL); int target = package->getInt(PACKET_NET_ACT_UNIT_ATTACK::target, 0, NULL); // vyhodnoceni utoku if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(attacker)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.unitAttack(attacker, target)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // vyhodnoceni konce hry a synchronizace WORLD_SERVER_FINAL_TEST; } } // case break; case MSG_NET_ACT_NEXT_TURN: // ukonceni tahu { // autosave #if SERVER_AUTOSAVE_X TRM_conf_i * rmconfi = (TRM_conf_i *)KSendGlobalMessage(MSG_GET_RM_CONF_I, MOD_WORLD_SERVER, MOD_RM, NULL); DA *rm_profiles; rmconfi->getProfiles(&rm_profiles); TProfile * prof=(*rm_profiles)[rmconfi->getLastUsedProfile()]; if (prof->autosave==1) { char filename[MAX_STRLEN]; sprintf(filename, "turn_%d_%d.xml", world_server->world.turn_id, world_server->world.players.playerIdOnTurn()); world_server->saveMap(filename); } delete(rmconfi); #endif // If somebody has reconnected, let him in! for (TPlayerIterator it = world_server->world.players.begin(); it != world_server->world.players.end(); it++) { int temp=it->second->data().client_data.oldclient_id; if (it->second->data().client_data.newclient_id != 0) { // pripojil se znovu hrac it->first if (it->second->data().client_data.type != PT_HUMAN) { int cid=it->second->data().client_data.newclient_id; it->second->data().client_data.type = PT_HUMAN; it->second->data().client_data.client_id = it->second->data().client_data.newclient_id; // Its in the 2nd part it->second->data().client_data.newclient_id=0; it->second->data().client_data.oldclient_id=0; it->second->data().client_data.connected = true; // Let the diplomacy know TPacket_Diplomacy_ChangeDiplomatType packet; packet.diplomat_world_id = it->first; packet.new_diplomat_type = DT_HUMAN; KSendGlobalMessage(MSG_DIPLOMACY_CHANGE_DIPLOMAT_TYPE, MOD_WORLD_SERVER, MOD_DIPLOMACY, &packet); // Either here, or just when he/she reconnects /* // Change the context of GUI ... to Loading TPackage *mypacket = new TPackage(MSG_NET_ROLESETTINGDATA); mypacket->setInt(3, "msgtype", 0, NULL); mypacket->send(TO_SERVER, cid); delete mypacket; */ } } } if (!((world_server->state == WSS_GAME_STARTED) && (world_server->world.players.playerIdOnTurn() == player_id))) { world_server->sendErrorReply(package, ERR_NotOperational); } else { // predam tah dalsimu hraci world_server->engine.nextTurn(); if (world_server->engine.finalTest()) { // hra skoncila // zavolam synchronizaci world_server->synchronize(); // uvolneni zdroju world_server->endGame(); } else { // hra pokracuje // zavolam synchronizaci world_server->synchronize(); // je-li na tahu AI, vzbudim ji if (world_server->world.players.playerOnTurn()->data().client_data.type != PT_HUMAN) WORLD_SERVER_AI_ON_TURN; } } // This part will send the map with right player_on_turn number for (TPlayerIterator it2 = world_server->world.players.begin(); it2 != world_server->world.players.end(); it2++) { int temp=it2->second->data().client_data.oldclient_id; if (it2->second->data().client_data.newclient_id != 0) { int cid=it2->second->data().client_data.newclient_id; it2->second->data().client_data.newclient_id=0; world_server->connectPlayer(cid); TAction action(AT_GAME_START); world_server->synchro(cid,1,action); } } } break; case MSG_NET_ACT_UNIT_HEAL: // leceni jednotky { TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_UNIT_HEAL::unit_id, 0, NULL); // vyhodnoceni if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.unitHeal(unit_id)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_UNIT_RECRUIT: // doplneni jednotky (neelitni) { TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_UNIT_RECRUIT::unit_id, 0, NULL); // vyhodnoceni leceni if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.unitRecruit(unit_id)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_UNIT_RECRUIT_ELITE: // doplneni jednotky (elitni) { TPackage * package = (TPackage *)p; int unit_id = package->getInt(PACKET_NET_ACT_UNIT_RECRUIT_ELITE::unit_id, 0, NULL); // vyhodnoceni leceni if (!WORLD_SERVER_CLIENT_UNIT_OPERATIONAL(unit_id)) { world_server->sendErrorReply(package, ERR_NotOperational); } else if (error = world_server->engine.unitRecruitElite(unit_id)) { // doslo k chybe - odpovim primo klientovi, ostatni vynecham world_server->sendErrorReply(package, error); } else { // zavolam synchronizaci world_server->synchronize(); } } break; case MSG_NET_ACT_OFFER_RELATION: // nabidnuti diplomatickeho vztahu { // nacteni dat TPackage * package = (TPackage *)p; TPacket_Diplomacy_SetOfferRelation packet; packet.offered_relationship = (TRelationship)package->getInt(PACKET_NET_ACT_OFFER_RELATION::offered_relationship, 0, NULL); packet.source_diplomat_world_id = package->getInt(PACKET_NET_ACT_OFFER_RELATION::source_diplomat_world_id, 0, NULL); packet.target_diplomat_world_id = package->getInt(PACKET_NET_ACT_OFFER_RELATION::target_diplomat_world_id, 0, NULL); if (!WORLD_SERVER_CLIENT_PLAYER_OPERATIONAL) { world_server->sendErrorReply(package, ERR_NotOperational); } else { // predani modulu diplomacie TPacket_DIPLOMACY_CHANGE * result = (TPacket_DIPLOMACY_CHANGE *)KSendGlobalMessage(MSG_DIPLOMACY_SET_OFFER_RELATION, MOD_WORLD_SERVER, MOD_DIPLOMACY, &packet); // aktualizace if (result) KSendGlobalMessage(MSG_DIPLOMACY_CHANGE, MOD_WORLD_SERVER, MOD_WORLD_SERVER, result); } } break; } // switch } catch (E_8K_TCL_Error &e) { // doslo k chybe pri pousteni TCL world_server->engine.unlock(); world_server->sendErrorReply(package, ERR_General); } catch (std::exception) { // doslo k chybe pri zpracovavani zpravy world_server->engine.unlock(); world_server->sendErrorReply(package, ERR_General); } } // case break; // +------------------------------------------------------------------+ // | | // | K o m u n i k a c e s T C L | // | | // +------------------------------------------------------------------+ case MSG_WORLD_TCL: // zpravy zasilane z TCL { PACKET_WORLD_TCL * packet = (PACKET_WORLD_TCL *)p; switch (packet->type) { case MSG_GET_HEX: // dotaz na hex { if ((packet->argc == 2) && (packet->argv[0] != NULL) && (packet->argv[1] != NULL)) { THex * hex = world_server->world.map.getHexAt(atoi(packet->argv[0]), atoi(packet->argv[1])); if (hex) hex->writeToTCL(world_server->engine.interpreter, packet->varName); else THROW(E_8K_TCL_Error, "Invalid hex coords"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_HEX_BY_ID: { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { THex * hex = world_server->world.map.getHexById(atoi(packet->argv[0])); if (hex) hex->writeToTCL(world_server->engine.interpreter, packet->varName); else THROW(E_8K_TCL_Error, "Invalid hex ID"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_WEATHER: // ziskani pocasi { world_server->world.weather.writeToTCL(world_server->engine.interpreter, packet->varName); } break; case MSG_GET_LIVING_UNIT: // dotaz na "zivou" jendotku { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { if (world_server->world.units.count(atoi(packet->argv[0]))) world_server->world.units[atoi(packet->argv[0])]->writeToTCL(world_server->engine.interpreter, packet->varName); else THROW(E_8K_TCL_Error, "Invalid unit ID"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_LIVING_BUILDING: // dotaz na "zivou" budovu { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { if (world_server->world.buildings.count(atoi(packet->argv[0]))) world_server->world.buildings[atoi(packet->argv[0])]->writeToTCL(world_server->engine.interpreter, packet->varName); else THROW(E_8K_TCL_Error, "Invalid building ID"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_PLAYER: // dotaz na hrace { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int id = atoi(packet->argv[0]); if (world_server->world.players.count(id)) { TPlayer * player = world_server->world.players[id]; player->writeToTCL(world_server->engine.interpreter, packet->varName); } else THROW(E_8K_TCL_Error, "Invalid player ID"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_PLAYERS: // dotaz na seznam hracu { TTCL_Int_List players; TPlayerIterator it; for (it = world_server->world.players.begin(); it != world_server->world.players.end(); it++) { if (it->second->data().game_data.state == PS_ACTIVE) players.push_back(it->first); } world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, &players); } break; case MSG_GET_LIVING_UNITS: // dotaz na seznam jednotek { TTCL_Int_List units; TLivingUnitsIterator it; if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int player_id = atoi(packet->argv[0]); // chci jednotky jen konkretniho hrace for (it = world_server->world.units.begin(); it != world_server->world.units.end(); it++) { if (it->second->data().player == player_id) units.push_back(it->first); } } else { // chci vsechny jednotky for (it = world_server->world.units.begin(); it != world_server->world.units.end(); it++) { units.push_back(it->first); } } world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, &units); } break; case MSG_GET_LIVING_BUILDINGS: // dotaz na seznam budov { TTCL_Int_List buildings; TLivingBuildingsIterator it; if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int player_id = atoi(packet->argv[0]); // chci budovy jen konkretniho hrace for (it = world_server->world.buildings.begin(); it != world_server->world.buildings.end(); it++) { if (it->second->data().player == player_id) buildings.push_back(it->first); } } else { // chci vsechny jednotky for (it = world_server->world.buildings.begin(); it != world_server->world.buildings.end(); it++) { buildings.push_back(it->first); } } world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, &buildings); } break; case MSG_GET_KINGDOM: // dotaz na kralovstvi { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int id = atoi(packet->argv[0]); if (world_server->world.kingdoms.count(id)) { TKingdom * kingdom = world_server->world.kingdoms[id]; kingdom->writeToTCL(world_server->engine.interpreter, packet->varName); } else THROW(E_8K_TCL_Error, "Invalid kingdom ID"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_KINGDOMS: // dotaz na seznam kralovstvi { TTCL_Int_List kingdoms; TKingdomsIterator it; if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int player_id = atoi(packet->argv[0]); // chci kralovstvi konkretniho hrace for (it = world_server->world.kingdoms.begin(); it != world_server->world.kingdoms.end(); it++) { if (it->second->data().roleid == player_id) kingdoms.push_back(it->first); } } else { // chci vsechna kralovstvi for (it = world_server->world.kingdoms.begin(); it != world_server->world.kingdoms.end(); it++) { kingdoms.push_back(it->first); } } world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, &kingdoms); } break; case MSG_GET_TOWN: // dotaz na mesto { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int id = atoi(packet->argv[0]); if (world_server->world.towns.count(id)) { TTown * town = world_server->world.towns[id]; town->writeToTCL(world_server->engine.interpreter, packet->varName); } else THROW(E_8K_TCL_Error, "Invalid town ID"); } else return (void *)ERR_InvalidInput; } break; case MSG_GET_TOWNS: // dotaz na seznam mest { TTCL_Int_List towns; TTownsIterator it; THex * hex; if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int kingdom_id = atoi(packet->argv[0]); // chci mesta jen konkretniho kralovstvi for (it = world_server->world.towns.begin(); it != world_server->world.towns.end(); it++) { // projdu si hexy mesta a pokud alespon jeden lezi na aktualnim // kralovstvi, zaradim jej do seznamu for (int i = 0; i < it->second->data().citysize; i++) { hex = world_server->world.map.getHexById(it->second->data().position[i]); if (hex->data().kingdom == kingdom_id) { // mesto lezi alespon jednim hexem na aktualnim kralovstvi towns.push_back(it->first); break; } } } } else { // chci vsechna mesta for (it = world_server->world.towns.begin(); it != world_server->world.towns.end(); it++) { towns.push_back(it->first); } } world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, &towns); } break; case MSG_GET_HEX_NEIGHBOUR: // dotaz na konkretni sousedni hex { if ((packet->argc == 2) && (packet->argv[0] != NULL) && (packet->argv[1] != NULL)) { int hex_id = atoi(packet->argv[0]); int orientation = atoi(packet->argv[1]); TIntContainer neighbours = world_server->world.map.getNeighboursOrientation(hex_id, orientation); if (neighbours.size() > 0) { world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, (TTCL_Int_List *)&neighbours); } else { return (void *)ERR_InvalidInput; } } else return (void *)ERR_InvalidInput; } break; case MSG_GET_HEX_NEIGHBOURS: // dotaz na seznam okolnich hexu { if ((packet->argc == 1) && (packet->argv[0] != NULL)) { int hex_id = atoi(packet->argv[0]); TIntContainer * neighbours = world_server->world.map.getNeighbours(hex_id); world_server->engine.interpreter.setVar(packet->varName, TVT_INT_LIST, (TTCL_Int_List *)neighbours); delete neighbours; } else { return (void *)ERR_InvalidInput; } } break; } } break; default: // ostatni nebo nespecifikovana komunikace switch (mid) { case MSG_GAME_START: // spusteni hry, nacteni mapy a nastaveni hracu { PACKET_GAME_START * packet = (PACKET_GAME_START *)KMemAlloc(sizeof(PACKET_GAME_START)); PACKET_GAME_START_Copy((PACKET_GAME_START *)p, packet); for (int i = 0; i < MAX_PLAYERS; i++) game_start_disconnections[i] = 0; world_server->state = WSS_LOAD_STARTED; SDL_CreateThread(&game_start_thread, packet); } break; case MSG_SAVE_GAME: // ulozeni hry { PACKET_SAVE_GAME * packet = (PACKET_SAVE_GAME *)p; try { world_server->saveMap(packet->filename); return new TPacket_SyncResult(); } catch (E_8K) { return new TPacket_SyncResult(ERR_General); } } break; case MSG_CLIENT_HAS_RECONNECTED: if (world_server->state == WSS_GAME_ENDED) { // Nic se nedeje, uz jsem skoncil break; } else if (world_server->state < WSS_INVITATIONS_SENT) { // Hra se teprve nahrava. Takze se budu tvarit, ze se nic nestalo :), nevim jestli funguje for (int i = 0; i < MAX_PLAYERS; i++) { if (game_start_disconnections[i] == ((int) p)) { game_start_disconnections[i] = 0; break; } } break; } else if (world_server->state == WSS_GAME_STARTED) { // Hra jiz bezi, tedy doslo k znovu pripojeni int client_id = (int)p; for (TPlayerIterator it = world_server->world.players.begin(); it != world_server->world.players.end(); it++) { int temp=it->second->data().client_data.oldclient_id; // v oldclient je puvodni clientid, soucasne clientid je 0 if (it->second->data().client_data.oldclient_id == client_id) { // pripojil se znovu hrac it->first if (it->second->data().client_data.type != PT_HUMAN) { // just memorize, that someone wants to reconnect ... allow him in when the turn ends it->second->data().client_data.newclient_id = client_id; // Change the context of GUI ... to Loading TPackage *mypacket = new TPackage(MSG_NET_ROLESETTINGDATA); mypacket->setInt(3, "msgtype", 0, NULL); mypacket->send(TO_SERVER, client_id); delete mypacket; } else { // nezbyde nikdo zivy, kdo by ocenil krasu hry 8K world_server->engine.endGame(ER_SHUTDOWN); // zavolam synchronizaci world_server->synchronize(); // zrusim zdroje world_server->endGame(); } break; } } } // if (world_server->world.players.playerOnTurn()->data().client_data.type != PT_HUMAN) break; case MSG_CLIENT_HAS_DISCONNECTED: // odpojeni klienta { if (world_server->state < WSS_LOAD_STARTED) { // Nic se nedeje, jeste jsem nezacal break; } else if (world_server->state == WSS_GAME_ENDED) { // Nic se nedeje, uz jsem skoncil break; } else if (world_server->state < WSS_INVITATIONS_SENT) { // Hra se teprve nahrava. Poznamenam si, ze doslo k chybe. // Az skonci nahravani hry, odpojeni klienta se vyresi dodatecne for (int i = 0; i < MAX_PLAYERS; i++) if (game_start_disconnections[i] == 0) game_start_disconnections[i] = (int)p; break; } else if (world_server->state < WSS_GAME_STARTED) { // Hra jeste nezacala world_server->engine.endGame(ER_SHUTDOWN); world_server->synchronize(); world_server->endGame(); } else if (world_server->state == WSS_GAME_STARTED) { // Hra jiz bezi int client_id = (int)p; for (TPlayerIterator it = world_server->world.players.begin(); it != world_server->world.players.end(); it++) { if (it->second->data().client_data.client_id == client_id) { // odpojil se hrac it->first if (world_server->world.players.numberOfHumanPlayers(it->first) > 0) { // ve hre zustanou i nejaci dalsi "zivi" hraci a ma cenu hrat dal if (it->second->data().client_data.type == PT_HUMAN) { // zmenim hrace na AI it->second->data().client_data.type = PT_AI_3; it->second->data().client_data.oldclient_id = it->second->data().client_data.client_id; it->second->data().client_data.client_id = TO_SERVER; it->second->data().client_data.connected = true; // dam o tom vedet diplomacii TPacket_Diplomacy_ChangeDiplomatType packet; packet.diplomat_world_id = it->first; packet.new_diplomat_type = DT_AI; KSendGlobalMessage(MSG_DIPLOMACY_CHANGE_DIPLOMAT_TYPE, MOD_WORLD_SERVER, MOD_DIPLOMACY, &packet); // pokud byl doted na tahu, predam tah dalsimu hraci if (world_server->world.players.playerIdOnTurn() == it->first) WORLD_SERVER_NEXT_TURN } } else { // nezbyde nikdo zivy, kdo by ocenil krasu hry 8K world_server->engine.endGame(ER_SHUTDOWN); // zavolam synchronizaci world_server->synchronize(); // zrusim zdroje world_server->endGame(); } break; } } } } break; case MSG_GET_MAP: // ziskani mapy { TPacket_RET_MAP * result = new TPacket_RET_MAP(); result->map = &world_server->world.map.data(); return result; } break; case MSG_GET_DISTANCE: // dotaz na vzdalenost { TPacket_RET_DISTANCE * result = new TPacket_RET_DISTANCE(); try { PACKET_GET_DISTANCE * msg = (PACKET_GET_DISTANCE *)p; result->cost = world_server->engine.getDistance(msg->unit_id, msg->points_of_movement, msg->from, msg->to, msg->getcost_transparency); } catch (E_8K) { result->status = ERR_General; } return result; } break; case MSG_DIPLOMACY_CHANGE: { TPacket_DIPLOMACY_CHANGE * msg = (TPacket_DIPLOMACY_CHANGE *)p; // zaznamenam do sveta a do historie TPacket_DIPLOMACY_RELATION_CHANGE * change; for (vector::iterator it = msg->changes.begin(); it != msg->changes.end(); it++) { // zmeny v reprezentaci sveta if (it->target_diplomat_penalty) continue; if (world_server->world.diplomacyChange(it->source_diplomat_world_id, it->target_diplomat_world_id, it->new_relationship.rs, it->new_relationship.offeredrs, it->source_diplomat_penalty, it->target_diplomat_penalty)) { // zaznam do historie change = new TPacket_DIPLOMACY_RELATION_CHANGE(*it); world_server->history.add(AT_DIPLOMACY_RELATION_CHANGE, change); } } // dam vedet klientum world_server->synchronize(); } break; case MSG_AI_EDITOR_RELOAD: // obnoveni dat z editoru { PACKET_EDITOR_RELOAD * msg = (PACKET_EDITOR_RELOAD*)p; // hlavicka mapy if (msg->map_info) { // nacteni hracu PLAYER * player_data; int i, j, k; for (i = 1; i <= MAX_PLAYERS; i++) { if (msg->map_info->roles[i] != NULL) { // i-ta role obsazena if (world_server->world.players[i] != NULL) player_data = &world_server->world.players[i]->data(); else player_data = (PLAYER *)KMemAlloc(sizeof(PLAYER)); // implicitni dosazeni hodnot - client_data player_data->client_data.newclient_id = 0; player_data->client_data.client_id = 0; player_data->client_data.oldclient_id = 0; player_data->client_data.type = PT_AI_3; player_data->client_data.connected = true; player_data->client_data.guimap_ready = true; sprintf(player_data->client_data.name, "Player %d", i); // prevzeti hodnot - game_data strncpy(player_data->game_data.name, msg->map_info->roles[i]->name, MAX_STRLEN); player_data->game_data.money = msg->map_info->roles[i]->money; if (world_server->world.players[i] == NULL) world_server->world.players[i] = new TPlayer(player_data, i); } else { // i-ta role volna if (world_server->world.players[i] != NULL) { KMemFree(world_server->world.players[i]); world_server->world.players[i] = NULL; } } } // rozmery mapy // TODO rozmyslet re/dealokaci MAP * map = (MAP *)KMemAlloc(sizeof(MAP)); map->width = msg->map_info->sizex; map->height = msg->map_info->sizey; map->hexes = msg->hexes; world_server->world.map.load(map); // masky viditelnosti // projdu pole masek viditelnosti for (i = 1; i < MAX_PLAYERS + 1; i++) { if (msg->map_info->roles[i] != NULL) { world_server->world.visibility_maps[i] = new TVisibilityMap(world_server->world.map); for (j = 0; j < world_server->world.map.data().width; j++) { for (k = 0; k < world_server->world.map.data().height; k++) { world_server->world.visibility_maps[i]->getHexAt(j,k).setActual(); } } } } } // nacteni kralovstvi world_server->world.kingdoms.load(msg->kingdoms, MAX_KINGDOMS); // nacteni mest world_server->world.towns.load(msg->cities); // ulozeni zivych jednotek do struktur sveta world_server->world.units.load(msg->living_units); // nacteni zivych budov world_server->world.buildings.load(msg->living_buildings); // vyplneni redundantnich relaci world_server->world.fillRelations(); if (msg->confirm) { TPacket_EDITOR_RELOADED * result = new TPacket_EDITOR_RELOADED(); result->corpses = &world_server->world.corpses; result->kingdoms = &world_server->world.kingdoms; result->living_buildings = &world_server->world.buildings; result->living_units = &world_server->world.units; result->map = &world_server->world.map; result->players = &world_server->world.players; result->ruins = &world_server->world.ruins; result->rules = world_server->world.rules; result->towns = &world_server->world.towns; result->visibility_maps = &world_server->world.visibility_maps; return result; } } break; } // switch } //switch return NULL; } void TWorldServer::sendErrorReply(TPackage * package, int error) { int player_id, client_id; try { player_id = package->getInt(PACKET_WORLD::player_id, 0, NULL); client_id = world.players[player_id]->data().client_data.client_id; } catch (E_8K) { player_id = 0; client_id = TO_CLIENTS; } TPackage errpack(MSG_WORLD_CLIENT_SERVER); errpack.setInt(MSG_RCT_ERROR, PACKET_WORLD::type, 0, NULL); errpack.setInt(error, PACKET_NET_ERROR_REPLY::error, 0, NULL); errpack.setInt(player_id, PACKET_WORLD::player_id, 0, NULL); errpack.send(TO_SERVER, client_id, MOD_WORLD_CLIENT); } int WorldServer_SendMessage(ClientData Data, Tcl_Interp * pInterp, int argc, char * argv[]) { void * result; PACKET_WORLD_TCL packet; // nactu typ zpravy if (argc < 3) { // nedostatecny pocet parametru return TCL_ERROR; } // pripravim packet packet.type = atoi(argv[1]); packet.varName = argv[2]; packet.argc = argc - 3; packet.argv = &argv[3]; result = KSendGlobalMessage(MSG_WORLD_TCL, 0, MOD_WORLD_SERVER, &packet); if (result == NULL) return TCL_OK; else return TCL_ERROR; } }