mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-11-10 09:19:01 +00:00
571 lines
No EOL
15 KiB
C++
571 lines
No EOL
15 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
/////////////
|
|
// INCLUDE //
|
|
/////////////
|
|
#include "simulated_editor.h"
|
|
#include "client_cfg.h"
|
|
//#include "game_share/generic_xml_msg_mngr.h"
|
|
//#include "game_share/msg_client_server.h"
|
|
#include "nel/misc/common.h"
|
|
#include "game_share/utils.h" // for BOMB_IF
|
|
|
|
#include "game_share/generic_xml_msg_mngr.h"
|
|
|
|
#include "nel/net/module_manager.h"
|
|
#include "r2_share/object.h"
|
|
#include "r2_share/scenario.h"
|
|
|
|
// test file write as text
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
extern CGenericXmlMsgHeaderManager GenericMsgHeaderMngr;
|
|
|
|
///////////
|
|
// USING //
|
|
///////////
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
using namespace NLNET;
|
|
using namespace R2;
|
|
|
|
// impulse callbacks
|
|
void impulseShardId(NLMISC::CBitMemStream &impulse);
|
|
void impulseUserBars(NLMISC::CBitMemStream &impulse);
|
|
void impulseNop(NLMISC::CBitMemStream &impulse);
|
|
|
|
/*
|
|
* CSimulatedEditor
|
|
*/
|
|
CSimulatedEditor::CSimulatedEditor( uint id ) :
|
|
CSimulatedClient( id ),
|
|
_DMC( NULL ),
|
|
_ses( sesUninitialized ),
|
|
_bLoggedIn( false )
|
|
{
|
|
}
|
|
|
|
CSimulatedEditor::~CSimulatedEditor()
|
|
{
|
|
delete _DMC;
|
|
_DMC = NULL;
|
|
_ses = sesUninitialized;
|
|
}
|
|
|
|
/*static*/ void CSimulatedEditor::initImpulseCallbacks()
|
|
{
|
|
// initialize impulse message handlers
|
|
GenericMsgHeaderMngr.setCallback( "CONNECTION:SHARD_ID", impulseShardId );
|
|
|
|
GenericMsgHeaderMngr.setCallback( "USER:BARS", impulseNop );
|
|
|
|
// AJM: NOP these for now,
|
|
// AJM TODO: double-check that we really want to do this for each of these
|
|
GenericMsgHeaderMngr.setCallback("STRING_MANAGER:PHRASE_SEND", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("STRING:DYN_STRING", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("STRING_MANAGER:RELOAD_CACHE", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("PHRASE:DOWNLOAD", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("GUILD:UPDATE_PLAYER_TITLE", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("HARVEST:CLOSE_TEMP_INVENTORY", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("DB_GROUP:INIT_BANK", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("DEATH:RESPAWN_POINT", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("TEAM:CONTACT_INIT", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("PVP_FACTION:FACTION_WARS", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("DB_INIT:INV", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("ENCYCLOPEDIA:INIT", impulseNop);
|
|
GenericMsgHeaderMngr.setCallback("DB_UPD_PLR", impulseNop);
|
|
|
|
// on receipt of this message, assume login is completed and it is safe to connect to DSS
|
|
GenericMsgHeaderMngr.setCallback("DB_INIT:PLR", impulseDatabaseInitPlayer);
|
|
}
|
|
|
|
void CSimulatedEditor::init()
|
|
{
|
|
BOMB_IF( ClientCfg.Local, "Config file set to local mode!", return )
|
|
|
|
_ses = sesInitialized;
|
|
}
|
|
|
|
// login to frontend server
|
|
void CSimulatedEditor::login()
|
|
{
|
|
_CurrentContext = this;
|
|
|
|
IModuleManager &mm = IModuleManager::getInstance();
|
|
|
|
// login
|
|
bool res = autoLogin( "", "", true );
|
|
_Direction = (TDirection)(uint)frand( DMaxDir+1 );
|
|
if ( _Direction > DMaxDir )
|
|
_Direction = DMaxDir;
|
|
|
|
// set the unique name root
|
|
IModuleManager::getInstance().setUniqueNameRoot( toString("U%u", _Id ) );
|
|
string gwName = toString( "simEditorGw%u", _Id );
|
|
|
|
// create a local gateway module
|
|
_ModuleGateway = mm.createModule( "StandardGateway", gwName, "" );
|
|
nlassert( _ModuleGateway != NULL );
|
|
|
|
// connect the client gateway to the FES
|
|
CCommandRegistry::getInstance().execute( gwName + ".transportAdd FEClient fec", *InfoLog );
|
|
CCommandRegistry::getInstance().execute( gwName + ".transportCmd fec(open)", *InfoLog );
|
|
|
|
_CurrentContext = NULL;
|
|
}
|
|
|
|
// connect client editor to dynamic scenario service
|
|
void CSimulatedEditor::connectToDSS()
|
|
{
|
|
_CurrentContext = this;
|
|
|
|
IModuleManager &mm = IModuleManager::getInstance();
|
|
|
|
// get the gateway socket
|
|
string gwName = toString( "simEditorGw%u", _Id );
|
|
NLNET::IModuleSocket *socketGw = mm.getModuleSocket( gwName );
|
|
nlassert( socketGw != NULL );
|
|
|
|
// create a simulated dynamic map client
|
|
string simEditorName = toString( "simEditor%u", _Id );
|
|
_DMC = new CDynamicMapClient( simEditorName, socketGw, NULL ); // getLua().getStatePointer() );
|
|
|
|
// create the simulated client edition and animation modules
|
|
_DMC->init( _Id, NULL );
|
|
|
|
_CurrentContext = NULL;
|
|
}
|
|
|
|
// test
|
|
void CSimulatedEditor::test()
|
|
{
|
|
if( false )
|
|
{
|
|
// load and run a lua test script
|
|
nlassert( _DMC );
|
|
_DMC->doFile( "testing123.lua" );
|
|
}
|
|
nlinfo( "SimEditor %d started", _Id );
|
|
}
|
|
|
|
bool CSimulatedEditor::isLoggedIn()
|
|
{
|
|
return getNetworkConnection().isConnected();
|
|
}
|
|
|
|
bool CSimulatedEditor::isConnected() const
|
|
{
|
|
if( !_DMC )
|
|
return false;
|
|
return _DMC->isSEMConnected();
|
|
}
|
|
|
|
// test creating and uploading a scenario to the ServerEditionModule
|
|
void CSimulatedEditor::testCreateScenario( const std::string &fileName )
|
|
{
|
|
if( !isConnected() )
|
|
{
|
|
nlwarning( "Server Edition Module is not connected!" );
|
|
return;
|
|
}
|
|
|
|
// load a scenario
|
|
CObject *pData = loadScenarioData( fileName );
|
|
if( !pData )
|
|
{
|
|
nlwarning( "testCreateScenario: could not load scenario." );
|
|
return;
|
|
}
|
|
|
|
// load scenario rtdata
|
|
nlinfo("reading rtdata from %s.rt.bin", fileName.c_str());
|
|
CObject *pRtData = loadScenarioRtData( fileName );
|
|
if( !pRtData )
|
|
{
|
|
nlwarning( "testCreateScenario: could not load scenario rtdata." );
|
|
return;
|
|
}
|
|
|
|
if( !checkScenarioRtData( pRtData ) )
|
|
{
|
|
nlwarning( "testCreateScenario: scenario rtdata failed validity check." );
|
|
delete pData;
|
|
delete pRtData;
|
|
return;
|
|
}
|
|
|
|
// create scenario stub (high-level)
|
|
CObject *pScenarioStub = createScenarioStub( pData );
|
|
if( !pScenarioStub )
|
|
{
|
|
nlwarning( "testCreateScenario: scenario description failed validity check." );
|
|
delete pData;
|
|
delete pRtData;
|
|
delete pScenarioStub;
|
|
return;
|
|
}
|
|
|
|
// send a CreateScenario message to the SEM to get a slot
|
|
_DMC->requestCreateScenario( pScenarioStub );
|
|
|
|
// send an UploadRtData message to the SEM to upload the scenario
|
|
_DMC->requestUpdateRtScenario( pRtData );
|
|
|
|
delete pData;
|
|
delete pScenarioStub;
|
|
delete pRtData;
|
|
}
|
|
|
|
// load scenario data file, and create and return scenario stub (description only)
|
|
CObject *CSimulatedEditor::loadScenarioStub( const std::string &filename )
|
|
{
|
|
// load scenario (high-level)
|
|
nlinfo("reading data from %s", filename.c_str());
|
|
CObject *pData = loadScenarioData( filename );
|
|
if( !pData )
|
|
{
|
|
nlwarning( "could not load scenario %s", filename.c_str() );
|
|
return NULL;
|
|
}
|
|
|
|
// create scenario stub (high-level) with just the description
|
|
CObject *pScenarioStub = createScenarioStub( pData );
|
|
if( !pScenarioStub )
|
|
{
|
|
nlwarning( "failed to create stub for scenario %s", filename.c_str() );
|
|
delete pData;
|
|
delete pScenarioStub;
|
|
return NULL;
|
|
}
|
|
|
|
delete pData;
|
|
return pScenarioStub;
|
|
}
|
|
|
|
// load scenario rtdata file, validate, and return rtdata
|
|
CObject *CSimulatedEditor::loadRtData( const std::string &filename )
|
|
{
|
|
// load scenario rtdata
|
|
nlinfo("reading rtdata for scenario %s", filename.c_str());
|
|
CObject *pRtData = loadScenarioRtData( filename );
|
|
if( !pRtData )
|
|
{
|
|
nlwarning( "could not load rtdata for scenario %s", filename.c_str() );
|
|
return NULL;
|
|
}
|
|
|
|
if( !checkScenarioRtData( pRtData ) )
|
|
{
|
|
nlwarning( "rtdata failed validity check for scenario %s", filename.c_str() );
|
|
delete pRtData;
|
|
return NULL;
|
|
}
|
|
return pRtData;
|
|
}
|
|
|
|
// upload scenario stub and rtdata to the ServerEditionModule
|
|
void CSimulatedEditor::uploadScenario( CObject *pStub, CObject *pRtData )
|
|
{
|
|
// send a CreateScenario message to the SEM to get a slot
|
|
_DMC->requestCreateScenario( pStub );
|
|
|
|
// send an UploadRtData message to the SEM to upload the scenario
|
|
_DMC->requestUpdateRtScenario( pRtData );
|
|
}
|
|
|
|
// run scenario (aka "start test" or "go live") and enter "animation mode"
|
|
void CSimulatedEditor::runScenario()
|
|
{
|
|
_DMC->requestGoTest();
|
|
}
|
|
|
|
// start act
|
|
void CSimulatedEditor::startAct( uint actId )
|
|
{
|
|
_DMC->requestStartAct( actId );
|
|
}
|
|
|
|
// end scenario and return to "edition mode"
|
|
void CSimulatedEditor::endScenario()
|
|
{
|
|
_DMC->requestStopTest();
|
|
}
|
|
|
|
// create a scenario stub, with just enough data to pass validation checks on the SEM
|
|
CObject *CSimulatedEditor::createScenarioStub( const R2::CObject *pData ) const
|
|
{
|
|
CObject *pStub = new CObjectTable();
|
|
|
|
if( pData && pData->findAttr("Description") )
|
|
{
|
|
CObject *description = pData->findAttr("Description");
|
|
pStub->add( "Description", description->clone() );
|
|
}
|
|
else
|
|
{
|
|
CObject *pDesc = new CObjectTable();
|
|
pStub->add( "Description", pDesc );
|
|
pDesc->add( "LevelId", (uint)0 );
|
|
pDesc->add( "LocationId", 1 );
|
|
pDesc->add( "EntryPointId", (uint)0 );
|
|
pDesc->add( "RuleId", (uint)0 );
|
|
pDesc->add( "MaxEntities", 50 );
|
|
pDesc->add( "MaxPlayers", 5 );
|
|
pDesc->add( "Title", "my Title");
|
|
pDesc->add( "ShortDescription", "my ShortDescription");
|
|
}
|
|
|
|
// see if this will pass SEM validation
|
|
if( true )
|
|
{
|
|
CObject* description = pStub->findAttr("Description");
|
|
|
|
if (!description)
|
|
{
|
|
nlwarning("Invalid scenario");
|
|
}
|
|
CObject* levelId = description->findAttr("LevelId");
|
|
CObject* locationId = description->findAttr("LocationId");
|
|
CObject* entryPointId = description->findAttr("EntryPointId");
|
|
CObject* ruleId = description->findAttr("RuleId");
|
|
CObject* maxEntities = description->findAttr("MaxEntities");
|
|
CObject* maxPlayers = description->findAttr("MaxPlayers");
|
|
CObject* title = description->findAttr("Title");
|
|
CObject* shortDescription = description->findAttr("ShortDescription");
|
|
|
|
|
|
if (!locationId || !levelId || !entryPointId || !ruleId || !maxEntities || !maxPlayers
|
|
|| !title || !shortDescription
|
|
|
|
|| !levelId->isNumber() || !ruleId->isNumber()
|
|
|| !locationId->isNumber() || !entryPointId->isNumber()
|
|
|| !maxEntities->isNumber() || !maxPlayers->isNumber()
|
|
|| !title->isString() || !shortDescription->isString()
|
|
)
|
|
{
|
|
nlwarning("Invalid scenario");
|
|
}
|
|
|
|
}
|
|
return pStub;
|
|
}
|
|
|
|
// load scenario data from a lua file
|
|
CObject *CSimulatedEditor::loadScenarioData( const std::string &fileName ) const
|
|
{
|
|
string fileplusr2(fileName);
|
|
uint len = fileplusr2.size();
|
|
const char *szfn = fileplusr2.c_str();
|
|
if( (len < 4) || (0 != stricmp(&szfn[len-3], ".r2")) )
|
|
fileplusr2.append(".r2");
|
|
|
|
// try to read a scenario from a lua file
|
|
nlinfo( "loading scenario file %s...", fileplusr2.c_str() );
|
|
CObject *pData = _DMC->loadScenario( fileplusr2 );
|
|
if( !pData )
|
|
{
|
|
nlwarning("Error while loading scenario %s", fileplusr2.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
nlinfo( "...scenario loaded.", fileplusr2.c_str() );
|
|
return pData;
|
|
}
|
|
|
|
// load rtdata for a scenario from an xxx,rt.bin file
|
|
CObject *CSimulatedEditor::loadScenarioRtData( const std::string &fileName ) const
|
|
{
|
|
CObject *pRtData = NULL;
|
|
|
|
string fileplusr2(fileName);
|
|
uint len = fileplusr2.size();
|
|
const char *szfn = fileplusr2.c_str();
|
|
if( (len>3) && !stricmp(&szfn[len-3], ".r2") )
|
|
{
|
|
fileplusr2.erase(len-3);
|
|
fileplusr2.append(".bin");
|
|
}
|
|
else if( (len<8) || (0 != stricmp(&szfn[len-7], ".rt.bin")) )
|
|
fileplusr2.append(".rt.bin");
|
|
|
|
// try to open the file for read
|
|
CIFile input;
|
|
bool fileIsOpen = false;
|
|
|
|
try
|
|
{
|
|
if( !input.open( fileplusr2 ) )
|
|
{
|
|
nlwarning( "Scenario rtdata file %s not found.", fileplusr2.c_str() );
|
|
return NULL;
|
|
}
|
|
fileIsOpen=true;
|
|
|
|
CObjectSerializer serializer;
|
|
input.serial( serializer );
|
|
pRtData = serializer.takeData();
|
|
|
|
// try writing the data back out, for comparison with the original
|
|
if( false )
|
|
{
|
|
// as binary
|
|
COFile output;
|
|
output.open("outpout.rt.check.bin");
|
|
CObjectSerializer serializer( pRtData );
|
|
serializer.serial(output);
|
|
output.flush();
|
|
output.close();
|
|
}
|
|
|
|
if( false )
|
|
{
|
|
// as text
|
|
COFile output;
|
|
std::stringstream ss;
|
|
output.open("outpout.rt.check.txt");
|
|
pRtData->serialize(ss);
|
|
std::string str = ss.str();
|
|
output.serial(str);
|
|
output.flush();
|
|
output.close();
|
|
}
|
|
}
|
|
catch(NLMISC::Exception &)
|
|
{
|
|
nlwarning( "Problem reading rtdata file %s.", fileplusr2.c_str() );
|
|
}
|
|
// close file outside exception handling in case exception thrown somewhere strange
|
|
if( fileIsOpen )
|
|
input.close();
|
|
|
|
return pRtData;
|
|
}
|
|
|
|
// check rtdata for validity
|
|
// AJM: this is basically duplicated from Server edition module
|
|
bool CSimulatedEditor::checkScenarioRtData( CObject *rtData ) const
|
|
{
|
|
#define MAX_NPCS 50 // hardcoded in Server edition module
|
|
|
|
uint32 nbActs = 0;
|
|
uint32 nbStates = 0;
|
|
uint32 maxStates = 0;
|
|
uint32 maxNpcs = 0;
|
|
uint32 baseActCost = 0;
|
|
uint32 baseActStates = 0;
|
|
|
|
CObject* acts = rtData->findAttr("Acts");
|
|
if( acts && acts->isTable() )
|
|
{
|
|
uint32 nbActs = acts->getSize();
|
|
for(uint32 i = 0;i<nbActs;i++)
|
|
{
|
|
CObject * act = acts->getValue(i);
|
|
CObject * npcs = act->findAttr("Npcs");
|
|
CObject * states = act->findAttr("AiStates");
|
|
if (states && states->isTable())
|
|
{
|
|
uint32 max2 = states->getSize();
|
|
if (i==0)
|
|
{
|
|
baseActStates = max2;
|
|
}
|
|
else
|
|
{
|
|
if(baseActStates + max2 > maxStates)
|
|
{
|
|
maxStates = baseActStates + max2;
|
|
}
|
|
}
|
|
nlinfo("states count in act %d : %d",i,max2);
|
|
}
|
|
if(npcs && npcs->isTable())
|
|
{
|
|
uint32 max2 = npcs->getSize();
|
|
if(i==0)
|
|
{
|
|
baseActCost = max2;
|
|
}
|
|
else
|
|
{
|
|
if (baseActCost+max2 > MAX_NPCS)
|
|
{
|
|
nlwarning("too many npcs: %d",(baseActCost+max2));
|
|
return false;
|
|
}
|
|
|
|
if (maxNpcs < baseActCost+max2)
|
|
{
|
|
maxNpcs = baseActCost+max2;
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlwarning("checkScenario: no npcs in act %d!", i);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nlwarning("checkScenarioRtData: no acts!");
|
|
return false;
|
|
}
|
|
nlinfo("Number of Acts : %d",maxNpcs);
|
|
nlinfo("Max number of Npcs : %d",maxNpcs);
|
|
nlinfo("Max number of States : %d",maxStates);
|
|
return true;
|
|
}
|
|
|
|
uint CSimulatedEditor::getNumActs( R2::CObject *rtData )
|
|
{
|
|
uint numActs = 0;
|
|
CObject* acts = rtData->findAttr("Acts");
|
|
if( acts && acts->isTable() )
|
|
{
|
|
numActs = acts->getSize();
|
|
}
|
|
return numActs;
|
|
}
|
|
|
|
// impulse message handlers
|
|
void impulseNop(NLMISC::CBitMemStream &impulse)
|
|
{
|
|
// just NOP, we don't care about these messages
|
|
}
|
|
|
|
void impulseShardId(NLMISC::CBitMemStream &impulse)
|
|
{
|
|
// received SHARD_ID
|
|
uint32 shardId;
|
|
impulse.serial(shardId);
|
|
|
|
std::string webHost;
|
|
impulse.serial(webHost);
|
|
|
|
nlinfo("WEB: Received SHARD_ID %d, web hosted at '%s'", shardId, webHost.c_str());
|
|
}
|
|
|
|
// on receipt of this message, assume login is completed and it is safe to connect to DSS
|
|
void impulseDatabaseInitPlayer(NLMISC::CBitMemStream &impulse)
|
|
{
|
|
uint32 userId = CSimulatedClient::currentContext()->getNetworkConnection().getUserId();
|
|
nlinfo( "DB_INIT:PLR received for user %d", userId );
|
|
} |