1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-24 21:57:44 +00:00

Added basic capture replay

This commit is contained in:
goaaats 2018-01-08 21:42:44 +01:00
parent 8519f118fe
commit da906586ea
5 changed files with 128 additions and 12 deletions

View file

@ -45,16 +45,17 @@ extern Core::ServerZone g_serverZone;
Core::DebugCommandHandler::DebugCommandHandler()
{
// Push all commands onto the register map ( command name - function - description - required GM level )
registerCommand( "set", &DebugCommandHandler::set, "Loads and injects a premade Packet.", 1 );
registerCommand( "get", &DebugCommandHandler::get, "Loads and injects a premade Packet.", 1 );
registerCommand( "add", &DebugCommandHandler::add, "Loads and injects a premade Packet.", 1 );
registerCommand( "set", &DebugCommandHandler::set, "Executes SET commands.", 1 );
registerCommand( "get", &DebugCommandHandler::get, "Executes GET commands.", 1 );
registerCommand( "add", &DebugCommandHandler::add, "Executes ADD commands.", 1 );
registerCommand( "inject", &DebugCommandHandler::injectPacket, "Loads and injects a premade packet.", 1 );
registerCommand( "injectc", &DebugCommandHandler::injectChatPacket, "Loads and injects a premade chat packet.", 1 );
registerCommand( "nudge", &DebugCommandHandler::nudge, "Nudges you forward/up/down", 1 );
registerCommand( "info", &DebugCommandHandler::serverInfo, "Send server info", 0 );
registerCommand( "unlock", &DebugCommandHandler::unlockCharacter, "Unlock character", 1 );
registerCommand( "help", &DebugCommandHandler::help, "Shows registered commands", 0 );
registerCommand( "script", &DebugCommandHandler::script, "Server script utilities", 1 );
registerCommand( "replay", &DebugCommandHandler::replay, "Replays a saved capture folder.", 1 );
registerCommand( "nudge", &DebugCommandHandler::nudge, "Nudges you forward/up/down.", 1 );
registerCommand( "info", &DebugCommandHandler::serverInfo, "Show server info.", 0 );
registerCommand( "unlock", &DebugCommandHandler::unlockCharacter, "Unlock character.", 1 );
registerCommand( "help", &DebugCommandHandler::help, "Shows registered commands.", 0 );
registerCommand( "script", &DebugCommandHandler::script, "Server script utilities.", 1 );
}
// clear all loaded commands
@ -455,6 +456,50 @@ void Core::DebugCommandHandler::injectChatPacket( char * data, Entity::Player& p
pSession->getChatConnection()->injectPacket( data + 8, player );
}
void Core::DebugCommandHandler::replay( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command )
{
std::string subCommand;
std::string params = "";
// check if the command has parameters
std::string tmpCommand = std::string( data + command->getName().length() + 1 );
std::size_t pos = tmpCommand.find_first_of( " " );
if( pos != std::string::npos )
// command has parameters, grab the first part
subCommand = tmpCommand.substr( 0, pos );
else
// no subcommand given
subCommand = tmpCommand;
if( command->getName().length() + 1 + pos + 1 < strlen( data ) )
params = std::string( data + command->getName().length() + 1 + pos + 1 );
g_log.debug( "[" + std::to_string( player.getId() ) + "] " +
"subCommand " + subCommand + " params: " + params );
if( subCommand == "start" )
{
auto pSession = g_serverZone.getSession( player.getId() );
if( pSession )
pSession->startReplay( params );
}
else if( subCommand == "stop" )
{
auto pSession = g_serverZone.getSession( player.getId() );
if( pSession )
pSession->stopReplay();
}
else
{
player.sendUrgent( subCommand + " is not a valid replay command." );
}
}
void Core::DebugCommandHandler::nudge( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command )
{
std::string subCommand;

View file

@ -38,6 +38,7 @@ public:
void injectPacket( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command );
void injectChatPacket( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command );
void replay( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command );
void nudge( char* data, Entity::Player& player, boost::shared_ptr< DebugCommand > command );
void serverInfo( char * data, Entity::Player& player, boost::shared_ptr< DebugCommand > command );

View file

@ -307,7 +307,7 @@ void Core::Network::GameConnection::sendSinglePacket( Packets::GamePacket* pPack
sendPackets( &pRP );
}
void Core::Network::GameConnection::injectPacket( const std::string& packetpath, Core::Entity::Player& pPlayer )
void Core::Network::GameConnection::injectPacket( const std::string& packetpath, Core::Entity::Player& player )
{
char packet[0x11570];
@ -318,7 +318,7 @@ void Core::Network::GameConnection::injectPacket( const std::string& packetpath,
fp = fopen( packetpath.c_str(), "rb" );
if( fp == nullptr )
{
g_log.error( "Packet " + packetpath + " not found!" );
player.sendDebug( "Packet " + packetpath + " not found!" );
return;
}
@ -328,7 +328,7 @@ void Core::Network::GameConnection::injectPacket( const std::string& packetpath,
rewind( fp );
if ( fread( packet, sizeof( char ), size, fp ) != size )
{
g_log.error( "Packet " + packetpath + " did not read full size: " + std::to_string( size ) );
player.sendDebug( "Packet " + packetpath + " did not read full size: " + std::to_string( size ) );
return;
}
fclose( fp );
@ -336,7 +336,7 @@ void Core::Network::GameConnection::injectPacket( const std::string& packetpath,
// cycle through the packet entries and queue each one
for( int32_t k = 0x18; k < size; )
{
uint32_t tmpId = pPlayer.getId();
uint32_t tmpId = player.getId();
// replace ids in the entryheader if needed
if( !memcmp( packet + k + 0x04, packet + k + 0x08, 4 ) )
{

View file

@ -6,6 +6,10 @@
#include "Session.h"
#include "Actor/Player.h"
#include <boost/filesystem/operations.hpp>
#include <common/Logging/Logger.h>
extern Core::Logger g_log;
Core::Session::Session( uint32_t sessionId )
: m_sessionId( sessionId )
@ -107,8 +111,68 @@ void Core::Session::updateLastSqlTime()
m_lastSqlTime = static_cast< uint32_t >( Util::getTimeSeconds() );
}
void Core::Session::startReplay( const std::string& folderpath )
{
if( !boost::filesystem::exists(folderpath) )
{
getPlayer()->sendDebug( "Couldn't find folder." );
return;
}
m_replayCache.clear();
std::vector<std::tuple<uint64_t, std::string>> loadedSets;
for( auto it = boost::filesystem::directory_iterator( boost::filesystem::path( folderpath ) ); it != boost::filesystem::directory_iterator(); ++it )
{
// Get the filename of the current element
auto filename = it->path().filename().string();
auto unixtime = atoi( filename.substr( 0, 10 ).c_str() );
if( unixtime > 1000000000)
{
loadedSets.push_back( std::tuple<uint64_t, std::string>( unixtime, it->path().string() ) );
}
}
sort( loadedSets.begin(), loadedSets.end(), [ ]( const std::tuple<uint64_t, std::string>& left, const std::tuple<uint64_t, std::string>& right)
{
return std::get<0>( left ) < std::get<0>( right );
} );
int startTime = std::get<0>( loadedSets.at( 0 ) );
for( auto thing : loadedSets )
{
m_replayCache.push_back( std::tuple<uint64_t, std::string>( Util::getTimeSeconds() + ( std::get<0>( thing ) - startTime ), std::get<1>( thing ) ) );
g_log.info( "Registering " + std::get<1>( thing ) + " for " + std::to_string( std::get<0>( thing ) - startTime ) );
}
getPlayer()->sendDebug( "Registered " + std::to_string( m_replayCache.size() ) + " sets for replay" );
m_isReplaying = true;
}
void Core::Session::stopReplay()
{
m_isReplaying = false;
m_replayCache.clear();
}
void Core::Session::update()
{
if( m_isReplaying )
{
int at = 0;
for( auto const& set : m_replayCache ) {
if( std::get<0>( set ) == Util::getTimeSeconds() )
{
m_pZoneConnection->injectPacket( std::get<1>( set ), *getPlayer().get() );
m_replayCache.erase( m_replayCache.begin() + at );
}
at++;
}
}
if( m_pZoneConnection )
{
m_pZoneConnection->processInQueue();

View file

@ -26,6 +26,9 @@ namespace Core {
void updateLastDataTime();
void updateLastSqlTime();
void startReplay( const std::string& folderpath );
void stopReplay();
void close();
uint32_t getId() const;
@ -48,6 +51,9 @@ namespace Core {
uint32_t m_lastSqlTime;
bool m_isValid;
bool m_isReplaying;
std::vector<std::tuple<uint64_t, std::string>> m_replayCache;
Network::GameConnectionPtr m_pZoneConnection;
Network::GameConnectionPtr m_pChatConnection;