// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
//-----------------------------------------------------------------------------
// includes
//-----------------------------------------------------------------------------
//nel
#include "nel/misc/command.h"
// game share
#include "game_share/utils.h"
// local
#include "rfr_ryzom_file_retriever.h"
#include "remote_saves_interface.h"
//-----------------------------------------------------------------------------
// Namespaces
//-----------------------------------------------------------------------------
using namespace std;
using namespace NLMISC;
using namespace SAVES;
//-----------------------------------------------------------------------------
// some handy globals
//-----------------------------------------------------------------------------
static NLMISC::CSString WorkDirectory="./";
//-----------------------------------------------------------------------------
// some handy utility routines
//-----------------------------------------------------------------------------
static NLMISC::CSString buildRemoteSavesInterfaceSumary(CRemoteSavesInterface* saves)
{
BOMB_IF(saves==NULL,"BUG: buildRemoteSavesInterfaceSumary() called with NULL pointer",return "");
NLMISC::CSString txt="(";
if (saves->isReady())
{
CFileDescriptionContainer fdc;
saves->getFileList(fdc);
txt+= NLMISC::toString("FILES: %u,",fdc.size());
}
else
{
txt+= "NOT READY,";
}
CRemoteSavesInterface::TCallbackSet cbs;
saves->getCallbacks(cbs);
txt+= NLMISC::toString("USES: %d)",cbs.size());
return txt;
}
static void displayCharacterFileList(uint32 account, uint32 slot,NLMISC::CLog& log)
{
NLMISC::CSString charIdString= NLMISC::toString("_%d_%d",account,slot);
// get hold of a pointer to the file retriever singletgon
CRyzomFileRetriever* rfr= CRyzomFileRetriever::getInstance();
// setup a little set to make seraching easier later
std::set shardSet;
// iterate over the used shard list
CVectorSString usedShardList= rfr->getUsedShardList();
for (uint32 i=0;igetShardSavesInterface(usedShardList[i]);
DROP_IF(shardSaves== NULL,"BUG: getShardSavesInterface() returned NULL for shard: "+usedShardList[i],return);
// get the list of character save files
CFileDescriptionContainer charFiles;
shardSaves->getCharacterFileList(charFiles);
for (uint32 j=0;jgetSaleStoreFileList(saleStoreFiles);
for (uint32 i=0;igetOfflineCharacterCommandsFileList(offlineCommandFiles);
for (uint32 i=0;i shardSet;
// iterate over the used shard list
CVectorSString usedShardList= rfr->getUsedShardList();
for (uint32 i=0;igetShardSavesInterface(usedShardList[i]));
// add a clause to the description text foe the www saves
txt+=" www"+buildRemoteSavesInterfaceSumary(rfr->getWwwSavesInterface(usedShardList[i]));
// add a clause to the description text foe the incremental backups
txt+=" bak"+buildRemoteSavesInterfaceSumary(rfr->getBakSavesInterface(usedShardList[i]));
// display the text
log.displayNL("Used remote shard: %s",txt.c_str());
// add these shard save components to the set of 'used interfaces'
shardSet.insert(usedShardList[i]);
}
// allocate a map to hold the list of modules found that don't correspond to used shards
typedef std::map TNewShards;
TNewShards newShards;
// iterate over the remote saves modules reported by the rfr singleton looking for modules that
// aren't present in the used shards list
CVectorSString detectedModules= rfr->getSavesModules();
for (uint32 i=0;i")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=1)
return false;
CRyzomFileRetriever::getInstance()->useShard(args[0]);
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrStopUsingShard,"stop using shard save modules relating to a given shard name","")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=1)
return false;
CRyzomFileRetriever::getInstance()->stopUsingShard(args[0]);
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrListCharacterFiles,"display a list of the files belonging to a given character"," ")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=2)
return false;
// convert args to numbers and check that they are valid
uint32 account= NLMISC::CSString(args[0]).atoui();
uint32 slot= NLMISC::CSString(args[1]).atoui();
DROP_IF(account==0 && args[0]!="0","account Id is not a valid unsigned number: "+args[0],return false);
DROP_IF(slot==0 && args[1]!="0","slot Id is not a valid unsigned number: "+args[1],return false);
DROP_IF(slot>15,"slot Id is not valid (should be <=15): "+args[1],return false);
// do the displaying
displayCharacterFileList(account,slot,log);
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrListNamedCharacterFiles,"display a list of the files belonging to a given character"," ")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=2)
return false;
uint32 account=0;
uint32 slot=0;
// look up character name and ensure that it exists
bool found= CRyzomFileRetriever::getInstance()->getCharIdFromName(args[0],args[1],account,slot);
DROP_IF(found==false,"named character not found: "+args[1]+" on shard: "+args[0],return false);
// do the displaying
displayCharacterFileList(account,slot,log);
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrListAccountFiles,"display a list of the files belonging to a given player","")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=1)
return false;
// make sure the account number variable is valid
uint32 account= NLMISC::CSString(args[0]).atoui();
DROP_IF(account==0 && args[0]!="0","account Id is not a valid unsigned number: "+args[0],return false);
// display the files for each possible slot
for (uint32 i=0;i<16;++i)
{
displayCharacterFileList(account,i,log);
}
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrListNamedAccountFiles,"display a list of the files belonging to a given player","")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=1)
return false;
// see whether we have an account name that matches
uint32 account=0;
bool found= CRyzomFileRetriever::getInstance()->getAccountIdFromName(args[0],account);
DROP_IF(found==false,"named account not found: "+args[0],return false);
// provoke execution of another NLMISC command to do the work
NLMISC::ICommand::execute(NLMISC::toString("rfrListAccountFiles %u",account),log,quiet,human);
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrWorkDirectory,"get or set the directory that files are downloaded to or uploaded from","[]")
{
CNLSmartLogOverride logOverride(&log);
switch (args.size())
{
case 0:
log.displayNL("rfr work directory: %s",WorkDirectory.c_str());
return true;
case 1:
{
// setup the global to point to the new directory
WorkDirectory= NLMISC::CPath::standardizePath(args[0]);
// if the directory dosn't exist then create it
if (!NLMISC::CFile::isDirectory(WorkDirectory))
{
NLMISC::CFile::createDirectoryTree(WorkDirectory);
}
}
return true;
}
return false;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrGetCharacterFile,"download a character file"," ( | )")
{
CNLSmartLogOverride logOverride(&log);
uint32 account=0;
uint32 slot=0;
switch (args.size())
{
case 2:
{
// single argument after shard name must be a character name so look it up and ensure that it exists
bool found= CRyzomFileRetriever::getInstance()->getCharIdFromName(args[0],args[1],account,slot);
DROP_IF(found==false,"named character not found: "+args[1],return false);
}
break;
case 3:
// 2 args after shard name must be so convert to numbers and check that they are valid
account= NLMISC::CSString(args[1]).atoui();
slot= NLMISC::CSString(args[2]).atoui();
DROP_IF(account==0 && args[1]!="0","account Id is not a valid unsigned number: "+args[1],return false);
DROP_IF(slot==0 && args[2]!="0","slot Id is not a valid unsigned number: "+args[2],return false);
DROP_IF(slot>15,"slot Id is not valid (should be <=15): "+args[2],return false);
break;
default:
return false;
}
// compose local and remote file names and queue up the upload
NLMISC::CSString remoteFileName= CShardSavesInterface::getCharacterSaveFileName(account,slot);
NLMISC::CSString localFileName= WorkDirectory+NLMISC::CFile::getFilename(remoteFileName);
return CRyzomFileRetriever::getInstance()->downloadFile(args[0],remoteFileName,localFileName);
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrGetOldCharacterFileSet,"download the complete set of backups for a given character"," ( | )")
{
CNLSmartLogOverride logOverride(&log);
uint32 account=0;
uint32 slot=0;
switch (args.size())
{
case 2:
{
// single argument after shard name must be a character name so look it up and ensure that it exists
bool found= CRyzomFileRetriever::getInstance()->getCharIdFromName(args[0],args[1],account,slot);
DROP_IF(found==false,"named character not found: "+args[1],return false);
}
break;
case 3:
// 2 args after shard name must be so convert to numbers and check that they are valid
account= NLMISC::CSString(args[1]).atoui();
slot= NLMISC::CSString(args[2]).atoui();
DROP_IF(account==0 && args[1]!="0","account Id is not a valid unsigned number: "+args[1],return false);
DROP_IF(slot==0 && args[2]!="0","slot Id is not a valid unsigned number: "+args[2],return false);
DROP_IF(slot>15,"slot Id is not valid (should be <=15): "+args[2],return false);
break;
default:
return false;
}
// compose local and remote file names and queue up the upload
NLMISC::CSString remoteFileName= CShardSavesInterface::getCharacterSaveFileName(account,slot);
NLMISC::CSString localFileName= WorkDirectory+NLMISC::CFile::getFilename(remoteFileName);
return CRyzomFileRetriever::getInstance()->downloadBackupFiles(args[0],remoteFileName,WorkDirectory);
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrUploadFile,"upload a charcter save file (or other file)"," ")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=2)
return false;
// extract the file name from the full path supplied
NLMISC::CSString fileName= NLMISC::CFile::getFilename(args[1]);
// see if we have a character save file
if (fileName.left(8)=="account_" && fileName.right(8)=="_pdr.bin")
{
nlinfo("Uploading a character file: %s => %s",args[1].c_str(),(WorkDirectory+fileName).c_str());
return true;
}
WARN("Failed to upload file due to unrecognised file type: "<getUsedShardList();
for (uint32 i=0;igetShardSavesInterface(usedShardList[i]);
DROP_IF(shardSaves== NULL,"BUG: getShardSavesInterface() returned NULL for shard: "+usedShardList[i],return false);
if (shardSaves->isReady())
{
// get hold of the shard's file list
CFileDescriptionContainer fdc;
shardSaves->getFileList(fdc);
// iterate over the shard's file list looking for the key files that interest us
for (uint32 j=0;jgetAccountNamesFileName()
|| fileName== shardSaves->getCharacterNamesFileName()
|| fileName== shardSaves->getGameCycleFileName()
|| fileName== shardSaves->getGMPendingTPFileName())
{
log.displayNL("\t%s",fdc[j].toString().c_str());
}
}
}
}
return true;
}
NLMISC_CATEGORISED_COMMAND(RyzomFileRetriever,rfrListGuildFiles,"list the guild files for a given shard","")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=1)
return false;
// get hold of a pointer to the file retriever singletgon
CRyzomFileRetriever* rfr= CRyzomFileRetriever::getInstance();
// get a pointer to the shard saves interface and make sure that it's ready for use
CShardSavesInterface* shardSaves= rfr->getShardSavesInterface(args[0]);
DROP_IF(shardSaves== NULL,"shard not found: "+args[0],return false);
DROP_IF(!shardSaves->isReady(),"remote shard saves manager not ready: "+args[0],return true);
// get the list of guild save files
CFileDescriptionContainer fdc;
shardSaves->getGuildFileList(fdc);
// build a list of file descriptions to display
CVectorSString files;
for (uint32 i=0;i ")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=2)
return false;
// get hold of a pointer to the file retriever singletgon
CRyzomFileRetriever* rfr= CRyzomFileRetriever::getInstance();
log.displayNL("Mail / Forum files from shard: %s for character / guild: %s",args[0].c_str(),args[0].c_str());
// get a pointer to the shard's mail and forum interface and make sure it's ready for use
CMailSavesInterface* mailSaves= rfr->getWwwSavesInterface(args[0]);
DROP_IF(mailSaves==NULL,"shard not found: "+args[0],return false);
DROP_IF(!mailSaves->isReady(),"remote shard saves manager not ready: "+args[0],return true);
// get the list of mail and forum save files for the given entity
CFileDescriptionContainer fdc;
mailSaves->getEntityFileList(args[1],fdc);
// display the files
for (uint32 i=0;i ")
{
CNLSmartLogOverride logOverride(&log);
if (args.size()!=3)
return false;
// get hold of a pointer to the file retriever singletgon
CRyzomFileRetriever* rfr= CRyzomFileRetriever::getInstance();
// get a pointer to the shard's mail and forum interface and make sure it's ready for use
CMailSavesInterface* mailSaves= rfr->getWwwSavesInterface(args[0]);
DROP_IF(mailSaves==NULL,"shard not found: "+args[0],return false);
DROP_IF(!mailSaves->isReady(),"remote shard saves manager not ready: "+args[0],return true);
// get the list of mail and forum save files for the source and destination entity names
CFileDescriptionContainer fdcSrc, fdcDest;
mailSaves->getEntityFileList(args[1],fdcSrc);
mailSaves->getEntityFileList(args[2],fdcDest);
// ensure that no destination files exist and that aat least some source files exist
DROP_IF(!fdcDest.empty(),"FAILED to move mail / forum files because the destination directory is not empty: move files away first: "+args[2],return true);
DROP_IF(fdcSrc.empty(),"FAILED to move mail / forum files because the source directory is empty: "+args[1],return true);
// do the moving
mailSaves->moveEntityFiles(args[1],args[2],false);
return true;
}
//-----------------------------------------------------------------------------