void GfxCountryMapGenerator::run() { const int maxHandshake = 1024; char handshake[maxHandshake]; TCPSocket* socket = waitForConnection(handshake, maxHandshake); if ( socket == NULL ) { mc2log << error << "No connection for GfxCountryMapGenerator" << endl; return; } DEBUG2(uint32 startTime = TimeUtility::getCurrentTime()); // We've got an request for a map... mc2dbg4 << "GfxCountryMapGenerator, sending map with ID " << mapIDs[0] << endl; CountryOverviewMap* theMap = dynamic_cast<CountryOverviewMap*>(mapHandler->getMap(mapIDs[0])); DEBUG8(cerr << "MAPID " << mapIDs[0] << endl); // Check if the map is valid or not if (theMap != NULL) { // OK, map valid! uint32 nbrMaps = theMap->getNbrMaps(); const GfxData* mapGfx = theMap->getGfxData(); // Calculate the size of the filtered map when written to a buffer. uint32 filtMapsBufSize = 0; const uint32 nbrFiltLevels = CountryOverviewMap::NBR_SIMPLIFIED_COUNTRY_GFX; for (uint32 i = 0; i < nbrFiltLevels; i++ ) { for (uint32 j = 0; j < mapGfx->getNbrPolygons(); j++ ) { const Stack* filtStack = theMap->getFilterStack(i,j); filtMapsBufSize += 4; // writes number of filt stack elements. for (uint32 k = 0; k < filtStack->getStackSize(); k++) { filtMapsBufSize += 4; // writes filt stack element. } } } // Claculate size of buffer needed. uint32 bufSize = 4; // nbrMaps bufSize += nbrMaps * 24; // nbrMaps * (mapId, creationTime, Bbox) bufSize += 4; // polygons closed, nbrPolygons bufSize += mapGfx->getNbrPolygons() * 4; // nbr coordinates per polygon. bufSize += theMap->getGfxData()->getTotalNbrCoordinates() * 8; // coords. bufSize += 4; // nbrFiltLevels bufSize += filtMapsBufSize; // Size of the filter data. DataBuffer idsAndGfx(bufSize); // Note that we multiply the size of the coordinates with two in // order to make space for the filtered versions of the country // GfxData as well. // DataBuffer idsAndGfx(nbrMaps * 24 + // ID + ver + Bbox // theMap->getGfxData()->getTotalNbrCoordinates()*8*2 + // 102400); // approx gfxdata idsAndGfx.writeNextLong(nbrMaps); for (uint32 i=0; i<nbrMaps; i++) { uint32 creationTime; int32 maxLat, minLat, maxLon, minLon; uint32 mapID = theMap->getMapData(i, creationTime, maxLat, minLon, minLat, maxLon); idsAndGfx.writeNextLong(mapID); idsAndGfx.writeNextLong(creationTime); idsAndGfx.writeNextLong(maxLat); idsAndGfx.writeNextLong(minLon); idsAndGfx.writeNextLong(minLat); idsAndGfx.writeNextLong(maxLon); mc2dbg1 << " Wrote mapID=" << mapID << ", version=" << creationTime << ", bbox=" << maxLat << ", " << minLon << ", " << minLat << ", " << maxLon << endl; } // Save the GfxData of the country //uint32 nbrPolygons = MIN(5, mapGfx->getNbrPolygons()); uint32 nbrPolygons = 0; // The polygons being sorted on nbrCoordinates, we an accept // to skip(send) 5 small polys with many coords, in order to // send larger polygons with fewer coordinates. uint32 maxNbrSkipPolys = 5; uint32 skipPolys[maxNbrSkipPolys]; for (uint32 i = 0; i < maxNbrSkipPolys;i++) { skipPolys[i] = MAX_UINT32; } bool cont = true; uint32 nbrSkip = 0; while (cont && (nbrPolygons < mapGfx->getNbrPolygons())) { mc2dbg8 << "poly " << nbrPolygons << " nbrC=" << mapGfx->getNbrCoordinates(nbrPolygons) << " length=" << mapGfx->getLength(nbrPolygons); if (mapGfx->getLength(nbrPolygons) < 35000) { skipPolys[nbrSkip] = nbrPolygons; mc2dbg8 << " - \"skipping\" "; nbrSkip++; if (nbrSkip >= maxNbrSkipPolys) { cont = false; } } mc2dbg8 << endl; nbrPolygons++; } // Don't send if any of the small polygons are the last ones.. uint32 p = maxNbrSkipPolys; while ( (p > 0) && (skipPolys[p-1] >= nbrPolygons-1)) { if (skipPolys[p-1] == nbrPolygons-1) { mc2dbg8 << " skip poly in the end of nbrPolygons" << endl; nbrPolygons--; } p--; } nbrPolygons=MAX(1, nbrPolygons); mc2dbg << "Sending " << nbrPolygons << " polygons of " << mapGfx->getNbrPolygons() << endl; idsAndGfx.writeNextBool(true); // polygons closed idsAndGfx.writeNextByte(0); // PAD idsAndGfx.writeNextShort(nbrPolygons);// # polygons for (uint32 p=0; p<nbrPolygons;p++) { uint32 nbrCoordinates = mapGfx->getNbrCoordinates(p); idsAndGfx.writeNextLong(nbrCoordinates); for (uint32 i = 0; i < nbrCoordinates; i++) { idsAndGfx.writeNextLong(mapGfx->getLat(p, i)); idsAndGfx.writeNextLong(mapGfx->getLon(p, i)); } } // Send filtered gfxdata information. // Nbr filtering levels idsAndGfx.writeNextLong(nbrFiltLevels); for (uint32 i = 0; i < nbrFiltLevels; i++ ) { // Nbr polygons already written before for (uint32 j = 0; j < nbrPolygons; j++ ) { const Stack* filtStack = theMap->getFilterStack(i,j); // Nbr indices in the filtering stack. idsAndGfx.writeNextLong(filtStack->getStackSize()); for (uint32 k = 0; k < filtStack->getStackSize(); k++) { // Index idsAndGfx.writeNextLong(filtStack->getElementAt(k)); } } } // Create and fill a buffer with the string items in this country DataBuffer stringItems(theMap->getMaximunSizeOfStringItems()*2 + 40000000); // length of items if (!theMap->saveStringItems(&stringItems)) { mc2log << error <<"Error saving string items for country" << endl; } // Create and fill buffer with items. // Nbr items. To be filled in later. uint32 offset = stringItems.getCurrentOffset(); stringItems.writeNextLong(0); uint32 maxCountryMapZoom = 3; uint32 nbrItems = 0; for (uint32 z = 0; z < maxCountryMapZoom; z++) { writeItemsInZoomLevel(theMap, &stringItems, z, nbrItems); } // Fill in nbr items stringItems.writeLong(nbrItems, offset); // Create and fill buffer with version and length DataBuffer versionAndLengthBuffer(8); versionAndLengthBuffer.writeNextLong(theMap->getCreationTime()); uint32 length = idsAndGfx.getCurrentOffset() + stringItems.getCurrentOffset(); versionAndLengthBuffer.writeNextLong(length); // Send the buffers via TCP uint32 nbrBytes = socket->writeAll( versionAndLengthBuffer.getBufferAddress(), versionAndLengthBuffer.getCurrentOffset() ); mc2dbg4 << " Sent " << nbrBytes << " with version (" << theMap->getCreationTime() << ") and length (" << length << ")" << endl; nbrBytes = socket->writeAll( idsAndGfx.getBufferAddress(), idsAndGfx.getCurrentOffset() ); mc2dbg4 << " Sent " << nbrBytes << " with map IDs and GfxData" << endl; nbrBytes = socket->writeAll( stringItems.getBufferAddress(), stringItems.getCurrentOffset() ); mc2dbg4<< " Sent " << nbrBytes << " with string items" << endl; // Delete the socket delete socket; } DEBUG2( mc2dbg << "GfxCountryMapGenerator sent all data for map " << mapIDs[0] << ", processing time " << TimeUtility::getCurrentTime()-startTime << " ms" << endl; );
TCPSocket* ModuleMap::getMapLoadingSocket( uint32 mapID, uint32 loadMapRequestType, const char* handshake, byte zoomlevel, MapSafeVector* loadedMaps, uint32* mapVersion, uint32* generatorVersion ) { uint32 mapVersionToUse = MAX_UINT32; uint32 generatorVersionToUse = MAX_UINT32; if ( mapVersion != NULL && generatorVersion != NULL ) { // Set the versions to use to the ones sent in mapVersionToUse = *mapVersion; generatorVersionToUse = *generatorVersion; // Set the ones sent in to MAX_UINT32 to detect errors *mapVersion = MAX_UINT32; *generatorVersion = MAX_UINT32; } DatagramReceiver receiver(8000, DatagramReceiver::FINDFREEPORT); mc2dbg4 << here << " localport " << receiver.getPort() << endl; uint32 mapip = MultiCastProperties::getNumericIP( MODULE_TYPE_MAP, true ); uint16 mapport = MultiCastProperties::getPort( MODULE_TYPE_MAP, true ); // check map set uint32 mapSet = Properties::getMapSet(); if (mapSet != MAX_UINT32) { // code also exists in PacketContainer.cpp, move to utility function? mc2dbg4 << "[ModuleMap] going to change IP and port due to mapSet being set. " "mapSet: " << mapSet << ", IP before: " << prettyPrintIP(mapip) << ", port before: " << mapport << endl; mapip = mapip + (mapSet << 8); mapport = mapport | (mapSet << 13); mc2dbg4 << "[ModuleMap] changed IP and port. IP now: " << prettyPrintIP(mapip) << ", port now: " << mapport << endl; } uint32 status = StringTable::NOT; int maxRetries = 10; int nbrRetries = 0; Packet _pack(65536); // For receiving the mapreply DatagramSender sock; const int originalwaittime = 2500000; // Wait 2.5 seconds. const int mapnotfoundwaittime = 2500000; // Wait 2.5 seconds. int waittime = originalwaittime; TCPSocket* TCPsock = NULL; while ( status != StringTable::OK && nbrRetries++ <= maxRetries ) { if ( nbrRetries > 1 ) { mc2log << info << here << " Retrying " << nbrRetries - 1 << " of " << maxRetries << endl; } if(loadedMaps != NULL) loadedMaps->jobThreadIsAlive(); // reset Jthread timeout clock. MapRequestPacket reqpack( uint16(1), // reqID uint16(1), // PacketID byte(loadMapRequestType), // Type of module mapID, // mapID zoomlevel, // Well, zoomlevel. mapVersionToUse, generatorVersionToUse ); reqpack.setOriginIP( NetUtility::getLocalIP() ); reqpack.setOriginPort( receiver.getPort() ); reqpack.setResendNbr((byte) nbrRetries-1); // Send request to open TCP connection between local and mapmodule if (!(sock.send(&reqpack, mapip, mapport))) { mc2log << error << here << " could not send - retrying" << endl; continue; // Go another round in the loop. } mc2dbg4 << "After send!" << endl; // Receive packet with ip and port to a mapModule if (!(receiver.receive(&_pack, waittime))) { mc2log << error << here << " error receiving ack - retrying" << endl; waittime = originalwaittime; continue; // Go another round in the loop. } bool timeout = false; while ( _pack.getSubType() == Packet::PACKETTYPE_ACKNOWLEDGE && ! timeout ) { AcknowledgeRequestReplyPacket* ackPack = static_cast<AcknowledgeRequestReplyPacket*>(&_pack); uint32 packtime = ((AcknowledgeRequestReplyPacket*)&_pack)->getETA() * 1000; mc2log << info << "Got ack with " << packtime << " us delay" << endl; if ( ackPack->getStatus() != StringTable::OK ) { return NULL; } timeout = !receiver.receive(&_pack, packtime); } if ( timeout ) { mc2log << error << "Got timeout after receiving ack-pack" << endl; continue; // Go around again } if ( _pack.getSubType() != Packet::PACKETTYPE_MAPREPLY ) { mc2log << error << "Got packet with subtype " << _pack.getSubTypeAsString() << " when expecting loadmapreply" << endl; continue; // Please try again. } MapReplyPacket* pack = (MapReplyPacket *)&_pack; status = pack->getStatus(); if ( status != StringTable::OK || pack->getMapID() != mapID ) { mc2log << warn << "Got status \"" << StringTable::getString( StringTable::stringCode(pack->getStatus()), StringTable::ENGLISH) << "\"-retrying. Wanted map = " << mapID << ", got reply for map = " << pack->getMapID() << endl; if ( status == StringTable::MAPNOTFOUND ) { // Wait longer if map not found ( loading? ) waittime = mapnotfoundwaittime; } if ( mapID != pack->getMapID() ) { status = StringTable::NOT; // Keep the loop running. } continue; // Go another round in the loop. } if ( mapVersion != NULL && generatorVersion != NULL ) { // Set the versions so that we know what we are caching. mc2dbg8 << "[ModuleMap]: Version from packet " << hex << pack->getMapVersion() << ":" << pack->getGeneratorVersion() << dec << endl; *mapVersion = pack->getMapVersion(); *generatorVersion = pack->getGeneratorVersion(); // Check if new map is needed. if ( ! pack->newMapNeeded(mapVersionToUse, generatorVersionToUse) ) { // Same version. Use the cached map. return NULL; } } TCPsock = new TCPSocket; mc2dbg4 << here << " opening socket" << endl; TCPsock->open(); uint32 ip = pack->getReplyIP(); uint16 port = pack->getReplyPort(); if ( ! TCPsock->connect(ip, port ) ) { mc2log << error << "Couldn't connect to " << ip << ":" << port << " - retrying" << endl; // Set status to not ok. status = StringTable::NOT; TCPsock->close(); delete TCPsock; TCPsock = NULL; continue; // Please try again. } // Handshaking mc2dbg4 << "Starting handshake" << endl; int length = strlen(handshake) + 1; if ( TCPsock->writeAll( (byte*)handshake, length ) != length ) { mc2log << error << here << " handshake failed " << endl; status = StringTable::NOT; TCPsock->close(); delete TCPsock; TCPsock = NULL; continue; // Please try again. } else { mc2dbg4 << "done" << endl; } } return TCPsock; // Should be NULL if we failed }