Files
src/engine/server/application/PlanetServer/src/shared/PreloadManager.cpp
T

494 lines
16 KiB
C++

// ======================================================================
//
// PreloadManager.cpp
// copyright (c) 2001 Sony Online Entertainment
//
// ======================================================================
#include "FirstPlanetServer.h"
#include "PreloadManager.h"
#include "ConfigPlanetServer.h"
#include "GameServerData.h"
#include "PlanetProxyObject.h"
#include "QuadtreeNode.h"
#include "Scene.h"
#include "serverUtility/PopulationList.h"
#include "sharedFoundation/ExitChain.h"
#include "sharedUtility/DataTable.h"
#include "sharedUtility/DataTableManager.h"
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cstdio>
// ======================================================================
PreloadManager::PreloadManager() :
Singleton2<PreloadManager>(),
m_serverMap(new ServerMapType),
m_preloadList(new PreloadListType),
m_serversWaiting(new ServersWaitingType),
m_listReceived(false)
{
ExitChain::add(&remove, "PreloadManager::remove");
}
// ----------------------------------------------------------------------
PreloadManager::~PreloadManager()
{
delete m_serverMap;
delete m_preloadList;
delete m_serversWaiting;
m_serverMap=0;
m_preloadList=0;
m_serversWaiting=0;
}
// ----------------------------------------------------------------------
/**
* Set up the preload list, based on the scene and config options.
*/
void PreloadManager::handlePreloadList()
{
m_listReceived=true;
WARNING(ConfigPlanetServer::getLoadWholePlanet() && ConfigPlanetServer::getLoadWholePlanetMultiserver(),("You have specified both loadWholePlanet and loadWholePlanetMultiserver. LoadWholePlanet (single-server) will take precedence."));
if (ConfigPlanetServer::getLoadWholePlanet() || PlanetServer::getInstance().isInSpaceMode() || !PlanetServer::getInstance().getEnablePreload())
{
// Set up a single server to handle the whole planet
PreloadListData fakeBeacon;
fakeBeacon.m_topLeftChunkX = 0;
fakeBeacon.m_topLeftChunkZ = 0;
fakeBeacon.m_bottomRightChunkX = 100;
fakeBeacon.m_bottomRightChunkZ = 100;
fakeBeacon.m_cityServerId = 1;
fakeBeacon.m_wildernessServerId = 1;
m_preloadList->clear();
m_preloadList->push_back(fakeBeacon);
m_serversWaiting->insert(1);
PreloadServerInformation & psi = (*m_serverMap)[1];
psi.m_actualServerId = 0;
psi.m_timeLoadStarted = time(0);
psi.m_loadTime = -1;
}
// WTF is this ... the tutorial planet bypasses the preload multiserver setting in favor of this pie slice algorithm
// We should not need this anymore with the NPE tutorial
// NPE will use the preload tables for multiserver and the 'NewbieTutorial' manager for placing the tutotial instances
/*
else if(PlanetServer::getInstance().isInTutorialMode())
{
m_preloadList->clear();
int n=ConfigPlanetServer::getNumTutorialServers();
if ((ConfigPlanetServer::getMaxGameServers() != 0) && (n > ConfigPlanetServer::getMaxGameServers()))
n=ConfigPlanetServer::getMaxGameServers();
for (int i=0 ; i<n; ++i)
{
float angle = PI_TIMES_2 * (static_cast<float>(i)/static_cast<float>(n));
// Make a circle of load beacons. The effect will be to divide the map into wedges
PreloadListData fakeBeacon;
fakeBeacon.m_topLeftChunkX = Node::roundToNode(static_cast<int>(cos(angle) * 1000.0f));
fakeBeacon.m_topLeftChunkZ = Node::roundToNode(static_cast<int>(sin(angle) * 1000.0f));
fakeBeacon.m_bottomRightChunkX = fakeBeacon.m_topLeftChunkX + Node::getNodeSize();
fakeBeacon.m_bottomRightChunkZ = fakeBeacon.m_topLeftChunkZ + Node::getNodeSize();
fakeBeacon.m_cityServerId = i+1;
fakeBeacon.m_wildernessServerId = i+1;
DEBUG_REPORT_LOG(true,("Loadbeacon at %i, %i\n",fakeBeacon.m_topLeftChunkX, fakeBeacon.m_topLeftChunkZ));
m_preloadList->push_back(fakeBeacon);
PreloadServerInformation & psi = (*m_serverMap)[i+1];
psi.m_actualServerId = 0;
psi.m_timeLoadStarted = time(0);
psi.m_loadTime = -1;
m_serversWaiting->insert(i+1);
}
}*/
else
{
if (PlanetServer::getInstance().getEnablePreload())
{
DataTable * data = DataTableManager::getTable(ConfigPlanetServer::getPreloadDataTableName(),true);
FATAL(data==NULL,("Could not find data table %s, needed for preload",ConfigPlanetServer::getPreloadDataTableName()));
int numRows = data->getNumRows();
for (int row=0; row<numRows; ++row)
{
if (data->getStringValue("Scene",row)==Scene::getInstance().getSceneId())
{
PreloadListData item;
item.m_topLeftChunkX=data->getIntValue("Top Left Chunk X",row);
item.m_topLeftChunkZ=data->getIntValue("Top Left Chunk Z",row);
item.m_bottomRightChunkX=data->getIntValue("Bottom Right Chunk X",row);
item.m_bottomRightChunkZ=data->getIntValue("Bottom Right Chunk Z",row);
item.m_cityServerId=data->getIntValue("City Server Id",row);
item.m_wildernessServerId=data->getIntValue("Wilderness Server Id",row);
if (ConfigPlanetServer::getMaxGameServers() != 0)
{
// Reduce the total number of game servers
item.m_cityServerId = ((item.m_cityServerId - 1) % ConfigPlanetServer::getMaxGameServers()) + 1;
if (item.m_wildernessServerId != 0)
item.m_wildernessServerId = ((item.m_wildernessServerId - 1) % ConfigPlanetServer::getMaxGameServers()) + 1;
}
m_preloadList->push_back(item);
ServerMapType::iterator mapIter = m_serverMap->find(item.m_cityServerId);
if (mapIter == m_serverMap->end())
{
PreloadServerInformation & psi = (*m_serverMap)[item.m_cityServerId];
psi.m_actualServerId = 0;
psi.m_timeLoadStarted = time(0);
psi.m_loadTime = -1;
m_serversWaiting->insert(item.m_cityServerId);
}
if (item.m_wildernessServerId != 0)
{
mapIter = m_serverMap->find(item.m_wildernessServerId);
if (mapIter == m_serverMap->end())
{
PreloadServerInformation & psi = (*m_serverMap)[item.m_wildernessServerId];
psi.m_actualServerId = 0;
psi.m_timeLoadStarted = time(0);
psi.m_loadTime = -1;
m_serversWaiting->insert(item.m_wildernessServerId);
}
}
}
}
}
}
FATAL(PlanetServer::getInstance().getEnablePreload() && m_serversWaiting->size()==0,("Planet server started for scene %s, which was not listed in the preload data table",
Scene::getInstance().getSceneId().c_str()));
std::set<PreloadServerId> startGameServerList;
if (m_serversWaiting->size() < 1)
{
IGNORE_RETURN(startGameServerList.insert(1));
}
else
{
for (ServersWaitingType::const_iterator i = m_serversWaiting->begin(); i != m_serversWaiting->end(); ++i)
IGNORE_RETURN(startGameServerList.insert(*i));
}
Scene::getInstance().assignPreloadsToNodes();
PlanetServer::getInstance().startGameServer(startGameServerList, 0);
}
// ----------------------------------------------------------------------
/**
* Called when a game server reports that it is ready to receive objects
*/
bool PreloadManager::onGameServerReady(uint32 serverId, PreloadServerId preloadServerId)
{
bool result = true;
bool noPreload = true;
// attempt to assign the game server the specified role in the preload list, if possible
bool assigned = false;
for (ServerMapType::iterator i=m_serverMap->begin(); i!=m_serverMap->end(); ++i)
{
if (i->first == preloadServerId)
{
if (i->second.m_actualServerId == 0)
{
i->second.m_actualServerId = serverId;
sendPreloadsToGameServer(serverId, i->first);
noPreload = false;
assigned = true;
}
break;
}
}
if (!assigned)
{
for (ServerMapType::iterator j=m_serverMap->begin(); j!=m_serverMap->end(); ++j)
{
if (j->second.m_actualServerId == 0)
{
// assign the new game server to this role in the preload list
j->second.m_actualServerId = serverId;
sendPreloadsToGameServer(serverId, j->first);
noPreload = false;
break;
}
}
}
if (noPreload)
{
// this server has nothing to preload, so advance its status right away
GameServerData *data = PlanetServer::getInstance().getGameServerData(serverId);
if(! PlanetServer::getInstance().getEnablePreload())
{
if (data)
data->preloadComplete();
// send PreloadRequestCompleteMessage, because things on the game server wait for it
PlanetServer::getInstance().sendPreloadRequestCompleteMessage(serverId, 0);
}
else
{
if(! PlanetServer::getInstance().isInTutorialMode())
{
result = false; // disconnect the game server, it's extraneous.
}
}
}
// if the preload list is empty, preloading is finished as soon as at least 1 game server is up
if (m_serverMap->empty())
{
DEBUG_REPORT_LOG(ConfigPlanetServer::getLogPreloading(),("Preloading is finished on %s because there was nothing to preload.\n",Scene::getInstance().getSceneId().c_str()));
PlanetServer::getInstance().preloadCompleteOnAllServers();
}
return result;
}
// ----------------------------------------------------------------------
void PreloadManager::sendPreloadsToGameServer(uint32 realServerId, PreloadServerId preloadServerId) const
{
if (PlanetServer::getInstance().getEnablePreload())
Scene::getInstance().loadAllNodesForServer(realServerId, preloadServerId);
PlanetServer::getInstance().sendPreloadRequestCompleteMessage(realServerId, preloadServerId);
}
// ----------------------------------------------------------------------
/**
* Called when a game server drops connection. Remove it from the preload assignments.
*/
void PreloadManager::removeGameServer(uint32 serverId)
{
for (ServerMapType::iterator i=m_serverMap->begin(); i!=m_serverMap->end(); ++i)
{
if (i->second.m_actualServerId == serverId)
{
i->second.m_actualServerId = 0;
i->second.m_timeLoadStarted = time(0);
i->second.m_loadTime = -1;
m_serversWaiting->insert(i->first);
break;
}
}
}
// ----------------------------------------------------------------------
/**
* Called when a game server indicates it has all the preload objects
*/
void PreloadManager::preloadCompleteOnServer(uint32 serverId)
{
DEBUG_REPORT_LOG(ConfigPlanetServer::getLogPreloading(),("Preload is finished on server %lu (planet %s)\n",serverId, Scene::getInstance().getSceneId().c_str()));
PreloadServerId const preloadId = getPreloadServerId(serverId);
m_serversWaiting->erase(preloadId);
// mark how long it took the server to load
ServerMapType::iterator i = m_serverMap->find(preloadId);
if ((i != m_serverMap->end()) && (i->second.m_loadTime == -1))
i->second.m_loadTime = std::max(static_cast<int>(0), static_cast<int>(time(0) - i->second.m_timeLoadStarted));
if (isPreloadComplete())
{
if (ConfigPlanetServer::getEnableStartupCreateProxies())
{
Scene::getInstance().createAllProxies();
}
DEBUG_REPORT_LOG(ConfigPlanetServer::getLogPreloading(),("Preloading is finished on all servers for %s.\n", Scene::getInstance().getSceneId().c_str()));
PlanetServer::getInstance().preloadCompleteOnAllServers();
}
}
// ----------------------------------------------------------------------
/**
* Finds the preload game server for the specified spot.
*/
PreloadServerId PreloadManager::getPreloadGameServer(int x, int z)
{
PreloadServerId bestSoFar = 0;
int bestDistance = 0;
for (std::vector<PreloadListData>::const_iterator item=m_preloadList->begin(); item!=m_preloadList->end(); ++item)
{
if (item->m_cityServerId != 0)
{
int minX=Node::roundToNode(item->m_topLeftChunkX);
int minZ=Node::roundToNode(item->m_topLeftChunkZ);
int maxX=Node::roundToNode(item->m_bottomRightChunkX);
int maxZ=Node::roundToNode(item->m_bottomRightChunkZ);
if ((x >= minX) && (x < maxX) && (z >= minZ) && (z < maxZ))
return item->m_cityServerId;
}
if (item->m_wildernessServerId != 0)
{
// Pretend there is a "beacon" in the middle of the preload area
int beaconX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2;
int beaconZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2;
int distanceToBeacon = ((x-beaconX)*(x-beaconX) + (z-beaconZ)*(z-beaconZ));
if (bestSoFar == 0 || distanceToBeacon < bestDistance)
{
bestDistance = distanceToBeacon;
bestSoFar = item->m_wildernessServerId;
}
}
}
return bestSoFar;
}
// ----------------------------------------------------------------------
/**
* Finds the closest operational game server to the specified spot. Used
* when the server specified in the preload list isn't available, because
* either it crashed or it hasn't started yet.
* Considers wilderness first, because city servers are probably more
* heavily loaded.
*/
uint32 PreloadManager::getClosestGameServer (int x, int z)
{
uint32 bestSoFar = 0;
int bestDistance = 0;
std::vector<PreloadListData>::const_iterator item;
for (item=m_preloadList->begin(); item!=m_preloadList->end(); ++item)
{
if (item->m_wildernessServerId != 0)
{
int beaconX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2;
int beaconZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2;
int distanceToBeacon = ((x-beaconX)*(x-beaconX) + (z-beaconZ)*(z-beaconZ));
uint32 realServerId = getRealServerId(item->m_wildernessServerId);
if ((realServerId != 0) && (bestSoFar == 0 || distanceToBeacon < bestDistance))
{
bestDistance = distanceToBeacon;
bestSoFar = realServerId;
}
}
}
if (bestSoFar==0)
{
// No wilderness servers. Try city servers
for (item=m_preloadList->begin(); item!=m_preloadList->end(); ++item)
{
if (item->m_cityServerId != 0)
{
int beaconX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2;
int beaconZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2;
int distanceToBeacon = ((x-beaconX)*(x-beaconX) + (z-beaconZ)*(z-beaconZ));
uint32 realServerId = getRealServerId(item->m_cityServerId);
if ((realServerId != 0) && (bestSoFar == 0 || distanceToBeacon < bestDistance))
{
bestDistance = distanceToBeacon;
bestSoFar = realServerId;
}
}
}
}
return bestSoFar; // returning 0 is possible
}
// ----------------------------------------------------------------------
/**
* Adds the loadbeacon locations to a PopulationList structure.
*/
void PreloadManager::updatePopulationList(PopulationList &theList) const
{
for (std::vector<PreloadListData>::const_iterator item=m_preloadList->begin(); item!=m_preloadList->end(); ++item)
{
int chunkX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2;
int chunkZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2;
theList.setPopulation(Scene::getInstance().getSceneId(),chunkX, chunkZ, Scene::getInstance().getServerPopulationByLocation(chunkX, chunkZ));
}
}
// ----------------------------------------------------------------------
PreloadServerId PreloadManager::getPreloadServerId(uint32 realServerId) const
{
for (ServerMapType::iterator i=m_serverMap->begin(); i!=m_serverMap->end(); ++i)
if (i->second.m_actualServerId == realServerId)
return i->first;
return 0;
}
// ----------------------------------------------------------------------
bool PreloadManager::isPreloadComplete() const
{
return (m_listReceived && m_serversWaiting->empty());
}
// ----------------------------------------------------------------------
void PreloadManager::getDebugString(std::string &output) const
{
if (isPreloadComplete())
output="finished";
else
{
output="loading";
for (ServersWaitingType::const_iterator i=m_serversWaiting->begin(); i!=m_serversWaiting->end(); ++i)
{
uint32 realServerId=0;
int loadTime=0;
ServerMapType::const_iterator j=m_serverMap->find(*i);
if (j!=m_serverMap->end())
{
realServerId=j->second.m_actualServerId;
if (j->second.m_loadTime != -1)
loadTime = j->second.m_loadTime;
else
loadTime = time(0) - j->second.m_timeLoadStarted;
}
char temp[100];
sprintf(temp," %lu-%ds (%lu)",*i,loadTime,realServerId);
output+=temp;
}
}
}
// ----------------------------------------------------------------------
uint32 PreloadManager::getRealServerId(PreloadServerId preloadServerId) const
{
ServerMapType::iterator i=m_serverMap->find(preloadServerId);
if (i!=m_serverMap->end())
return i->second.m_actualServerId;
return 0;
}
// ======================================================================