1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-25 05:57:45 +00:00

Merge pull request #142 from goaaats/master

Initial mounts implementation, nicer debug packet printing
This commit is contained in:
Mordred 2017-10-19 19:01:08 +02:00 committed by GitHub
commit 9df21f92eb
19 changed files with 310 additions and 21 deletions

View file

@ -602,7 +602,8 @@ namespace Core {
MpGain = 11,
TpLoss = 12,
TpGain = 13,
GpGain = 14
GpGain = 14,
Mount = 38
};
enum class ActionHitSeverityType : uint8_t
@ -951,7 +952,8 @@ namespace Core {
GearSetEquipMsg = 0x321,
ToggleOrchestrionUnlock = 0x396
ToggleOrchestrionUnlock = 0x396,
Dismount = 0x3a0
};
enum struct ChatType : uint16_t
@ -1068,6 +1070,12 @@ namespace Core {
Visor = 0x40,
};
enum SkillType : uint8_t
{
Normal = 0x1,
MountSkill = 0xD,
};
struct ServerEntry
{
uint32_t serverId;

View file

@ -5,6 +5,7 @@
#include <time.h>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include "Server_Common/Util/Util.h"
Core::Network::Packets::GamePacket::GamePacket( uint16_t subType, uint16_t size, uint32_t id1, uint32_t id2, uint16_t type )
{
@ -97,16 +98,5 @@ void Core::Network::Packets::GamePacket::savePacket()
std::string Core::Network::Packets::GamePacket::toString() const
{
std::string str = "\n";
for( uint32_t i = 0; i < getSize(); i++ )
{
str += boost::str( boost::format( "%|02X|" ) % ( int32_t ) ( m_dataBuf[i] & 0xFF ) ) + " ";
if( ( i + 1 ) % 16 == 0 )
str += "\n";
}
str += "\n";
return str;
return Core::Util::binaryToHexDump( const_cast<uint8_t *>( &m_dataBuf[0] ), getSize() );
}

View file

@ -116,6 +116,7 @@ namespace Packets {
ActorSpawn = 0x0190, // todo: split into playerspawn/actorspawn and use opcode 0x110/0x111
ActorFreeSpawn = 0x0191, // unchanged for sb
InitZone = 0x019A, // unchanged for sb
Mount = 0x019F,
WeatherChange = 0x01AF, // updated for sb
PlayerTitleList = 0x01BD, // updated for 4.1
Discovery = 0x01BE, // updated for 4.1

View file

@ -604,7 +604,8 @@ struct FFXIVIpcActorSetPos : FFXIVIpcBasePacket<ActorSetPos>
struct FFXIVIpcActorCast : FFXIVIpcBasePacket<ActorCast>
{
uint16_t action_id;
uint16_t unknown;
Common::SkillType skillType;
uint8_t unknown;
uint32_t unknown_1; // Also action id
float cast_time;
uint32_t target_id;
@ -1303,6 +1304,15 @@ struct FFXIVIpcEquipDisplayFlags : FFXIVIpcBasePacket<EquipDisplayFlags>
uint8_t bitmask;
};
/**
* Structural representation of the packet sent by the server
* to mount a player
*/
struct FFXIVIpcMount : FFXIVIpcBasePacket<Mount>
{
uint32_t id;
};
} /* Server */
} /* Packets */

View file

@ -1,9 +1,9 @@
#include "Util.h"
#include <chrono>
#include <boost/variant/detail/substitute.hpp>
std::string Core::Util::binaryToHexString( uint8_t* pBinData, uint16_t size )
{
std::string outStr;
for( uint32_t i = 0; i < size; i++ )
@ -15,6 +15,67 @@ std::string Core::Util::binaryToHexString( uint8_t* pBinData, uint16_t size )
}
std::string Core::Util::binaryToHexDump( uint8_t* pBinData, uint16_t size )
{
int bytesPerLine = 16;
constexpr char hexChars[] = "0123456789ABCDEF";
int offsetBlock = 8 + 3;
int byteBlock = offsetBlock + bytesPerLine * 3 + ( bytesPerLine - 1 ) / 8 + 2;
int lineLength = byteBlock + bytesPerLine + 1;
std::string line ( lineLength, ' ' );
int numLines = ( size + bytesPerLine - 1 ) / bytesPerLine;
std::string outStr;
for( uint32_t i = 0; i < size; i += bytesPerLine )
{
line[0] = hexChars[( i >> 28 ) & 0xF];
line[1] = hexChars[( i >> 24 ) & 0xF];
line[2] = hexChars[( i >> 20 ) & 0xF];
line[3] = hexChars[( i >> 16 ) & 0xF];
line[4] = hexChars[( i >> 12 ) & 0xF];
line[5] = hexChars[( i >> 8 ) & 0xF];
line[6] = hexChars[( i >> 4 ) & 0xF];
line[7] = hexChars[( i >> 0 ) & 0xF];
int hexColumn = offsetBlock;
int charColumn = byteBlock;
for( int j = 0; j < bytesPerLine; j++ )
{
if( j > 0 && ( j & 7 ) == 0)
{
hexColumn++;
}
if( i + j >= size )
{
line[hexColumn] = ' ';
line[hexColumn + 1] = ' ';
line[charColumn] = ' ';
}
else
{
uint8_t by = pBinData[i + j];
line[hexColumn] = hexChars[( by >> 4 ) & 0xF];
line[hexColumn + 1] = hexChars[by & 0xF];
line[charColumn] = by < 32 ? '.' : static_cast<char>( by );
}
hexColumn += 3;
charColumn++;
}
outStr += line + "\n";
}
return outStr;
}
uint64_t Core::Util::getTimeMs()
{
std::chrono::milliseconds epoch = std::chrono::duration_cast< std::chrono::milliseconds >(std::chrono::system_clock::now().time_since_epoch());

View file

@ -9,6 +9,8 @@ namespace Util {
std::string binaryToHexString( uint8_t* pBinData, uint16_t size );
std::string binaryToHexDump( uint8_t* pBinData, uint16_t size );
uint64_t getTimeMs();
uint64_t getTimeSeconds();

View file

@ -53,7 +53,7 @@ void Core::Action::ActionCast::onStart()
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( m_pSource->getId() );
castPacket.data().action_id = m_id;
castPacket.data().unknown = 1;
castPacket.data().skillType = Common::SkillType::Normal;
castPacket.data().unknown_1 = m_id;
castPacket.data().cast_time = static_cast< float >( m_castTime / 1000 ); // This is used for the cast bar above the target bar of the caster.
castPacket.data().target_id = m_pTarget->getId();

View file

@ -0,0 +1,111 @@
#include "ActionMount.h"
#include <src/servers/Server_Common/Common.h>
#include <src/servers/Server_Common/Util/Util.h>
#include <src/servers/Server_Common/Util/UtilMath.h>
#include <src/servers/Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Logging/Logger.h>
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket144.h"
#include "src/servers/Server_Zone/Actor/Player.h"
#include "src/servers/Server_Zone/Script/ScriptManager.h"
using namespace Core::Common;
using namespace Core::Network;
using namespace Core::Network::Packets;
using namespace Core::Network::Packets::Server;
extern Core::Data::ExdData g_exdData;
extern Core::Logger g_log;
extern Core::Scripting::ScriptManager g_scriptMgr;
Core::Action::ActionMount::ActionMount()
{
m_handleActionType = Common::HandleActionType::Event;
}
Core::Action::ActionMount::ActionMount( Entity::ActorPtr pActor, uint32_t mountId )
{
m_startTime = 0;
m_id = mountId;
m_handleActionType = HandleActionType::Spell;
m_castTime = 1000;
m_pSource = pActor;
m_bInterrupt = false;
}
Core::Action::ActionMount::~ActionMount()
{
}
void Core::Action::ActionMount::onStart()
{
if( !m_pSource )
return;
m_pSource->getAsPlayer()->sendDebug( "ActionMount::onStart()" );
m_startTime = Util::getTimeMs();
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( m_pSource->getId() );
castPacket.data().action_id = m_id;
castPacket.data().skillType = Common::SkillType::MountSkill;
castPacket.data().unknown_1 = m_id;
castPacket.data().cast_time = static_cast< float >( m_castTime / 1000 ); // This is used for the cast bar above the target bar of the caster.
castPacket.data().target_id = m_pSource->getAsPlayer()->getId();
m_pSource->sendToInRangeSet( castPacket, true );
m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Casting );
m_pSource->getAsPlayer()->sendStateFlags();
}
void Core::Action::ActionMount::onFinish()
{
if( !m_pSource )
return;
auto pPlayer = m_pSource->getAsPlayer();
pPlayer->sendDebug( "ActionMount::onFinish()" );
pPlayer->unsetStateFlag( PlayerStateFlag::Casting );
pPlayer->sendStateFlags();
GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket(pPlayer->getId());
effectPacket.data().targetId = pPlayer->getId();
effectPacket.data().actionAnimationId = m_id;
effectPacket.data().unknown_62 = 13; // Affects displaying action name next to number in floating text
effectPacket.data().actionTextId = 4;
effectPacket.data().numEffects = 1;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot(pPlayer->getRotation());
effectPacket.data().effectTarget = INVALID_GAME_OBJECT_ID;
effectPacket.data().effects[0].effectType = ActionEffectType::Mount;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::CritDamage;
effectPacket.data().effects[0].value = m_id;
pPlayer->sendToInRangeSet(effectPacket, true);
pPlayer->mount( m_id );
}
void Core::Action::ActionMount::onInterrupt()
{
if( !m_pSource )
return;
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 );
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Casting );
m_pSource->getAsPlayer()->sendStateFlags();
auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt,
0x219, 1, m_id, 0 );
// Note: When cast interrupt from taking too much damage, set the last value to 1. This enables the cast interrupt effect. Example:
// auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, 0x219, 1, m_id, 0 );
m_pSource->sendToInRangeSet( control, true );
}

View file

@ -0,0 +1,28 @@
#ifndef _ACTIONMOUNT_H_
#define _ACTIONMOUNT_H_
#include "src/servers/Server_Zone/Forwards.h"
#include "Action.h"
namespace Core { namespace Action {
class ActionMount : public Action
{
private:
public:
ActionMount();
~ActionMount();
ActionMount( Entity::ActorPtr pActor, uint32_t mountId );
void onStart() override;
void onFinish() override;
void onInterrupt() override;
};
}
}
#endif

View file

@ -1486,6 +1486,31 @@ uint8_t Core::Entity::Player::getEquipDisplayFlags() const
return m_equipDisplayFlags;
}
void Core::Entity::Player::mount( uint32_t id )
{
m_mount = id;
sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Mounted )), true );
sendToInRangeSet( ActorControlPacket143( getId(), 0x39e, 12 ), true ); //?
GamePacketNew< FFXIVIpcMount, ServerZoneIpcType > mountPacket( getId() );
mountPacket.data().id = m_mount;
sendToInRangeSet( mountPacket, true );
setSyncFlag( PlayerSyncFlags::Status );
}
void Core::Entity::Player::dismount()
{
sendToInRangeSet( ActorControlPacket142( getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Entity::Actor::ActorStatus::Idle )), true );
sendToInRangeSet( ActorControlPacket143( getId(), ActorControlType::Dismount, 1 ), true );
m_mount = 0;
setSyncFlag( PlayerSyncFlags::Status );
}
uint8_t Core::Entity::Player::getCurrentMount() const
{
return m_mount;
}
void Core::Entity::Player::autoAttack( ActorPtr pTarget )
{

View file

@ -338,8 +338,14 @@ public:
void setTitle( uint16_t titleId );
/*! change gear param state */
void setEquipDisplayFlags( uint8_t state );
/*! get gear param state and send update to inRangeSet */
/*! get gear param state */
uint8_t getEquipDisplayFlags() const;
/*! mount the specified mount and send the packets */
void mount( uint32_t id );
/*! dismount the current mount and send the packets */
void dismount();
/*! get the current mount */
uint8_t getCurrentMount() const;
void calculateStats() override;
void sendStats();

View file

@ -85,7 +85,8 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
"cd.EquipDisplayFlags, "
"cd.ActiveTitle, "
"cd.TitleList, " // 40
"cd.Orchestrion "
"cd.Orchestrion, "
"c.Mount "
"FROM charabase AS c "
" INNER JOIN charadetail AS cd "
" ON c.CharacterId = cd.CharacterId "
@ -180,10 +181,13 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
m_equipDisplayFlags = field[38].get< uint8_t >();
m_title = field[39].get< uint8_t >();
field[40].getBinary( reinterpret_cast< char* >( m_titleList ), sizeof( m_titleList ) );
field[41].getBinary( reinterpret_cast< char* >( m_orchestrion ), sizeof( m_orchestrion ) );
m_mount = field[42].get< uint8_t >();
m_pCell = nullptr;
if( !loadActiveQuests() || !loadClassData() || !loadSearchInfo() )
@ -371,6 +375,7 @@ void Core::Entity::Player::createUpdateSql()
charaDetailSet.insert( " Class = " + std::to_string( static_cast< uint32_t >( getClass() ) ) );
charaDetailSet.insert( " Status = " + std::to_string( static_cast< uint8_t >( getStatus() ) ) );
charaDetailSet.insert( " EquipDisplayFlags = " + std::to_string( static_cast< uint8_t >( getEquipDisplayFlags() ) ) );
charaBaseSet.insert( " Mount = " + std::to_string( getCurrentMount() ) );
}
if( m_updateFlags & PlayerSyncFlags::OpeningSeq )

View file

@ -258,6 +258,14 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
pPlayer->sendModel();
pPlayer->sendDebug( "Model updated" );
}
else if ( subCommand == "mount" )
{
int32_t id;
sscanf( params.c_str(), "%d", &id );
pPlayer->dismount();
pPlayer->mount( id );
}
else
{
pPlayer->sendUrgent( subCommand + " is not a valid SET command." );

View file

@ -44,6 +44,7 @@ namespace Core
TYPE_FORWARD( Action );
TYPE_FORWARD( ActionTeleport );
TYPE_FORWARD( ActionCast );
TYPE_FORWARD( ActionMount );
TYPE_FORWARD( EventAction );
}

View file

@ -206,7 +206,7 @@ void Core::Network::GameConnection::handleZonePacket( const Packets::GamePacket&
g_log.debug( sessionStr + " Undefined Zone IPC : Unknown ( " +
boost::str( boost::format( "%|04X|" ) %
static_cast< uint32_t >( pPacket.getSubType() & 0xFFFF ) ) + " )" );
g_log.debug( pPacket.toString() );
g_log.debug( "\n" + pPacket.toString() );
}
}

View file

@ -106,6 +106,11 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in
pPlayer->changeTarget( targetId );
break;
}
case 0x65:
{
pPlayer->dismount();
break;
}
case 0x68: // Remove status (clicking it off)
{
// todo: check if status can be removed by client from exd

View file

@ -25,9 +25,10 @@
#include "src/servers/Server_Zone/Forwards.h"
#include "src/servers/Server_Zone/Action/Action.h"
#include "src/servers/Server_Zone/Action/ActionTeleport.h"
#include "src/servers/Server_Zone/Action/ActionCast.h"
#include "src/servers/Server_Zone/Action/ActionMount.h"
#include "src/servers/Server_Zone/Script/ScriptManager.h"
#include "Server_Zone/Network/PacketWrappers/MoveActorPacket.h"
extern Core::Scripting::ScriptManager g_scriptMgr;
@ -41,12 +42,19 @@ using namespace Core::Network::Packets::Server;
void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inPacket,
Entity::PlayerPtr pPlayer )
{
uint8_t type = inPacket.getValAt< uint32_t >( 0x21 );
uint32_t action = inPacket.getValAt< uint32_t >( 0x24 );
uint32_t useCount = inPacket.getValAt< uint32_t >( 0x28 );
uint64_t targetId = inPacket.getValAt< uint64_t >( 0x30 );
pPlayer->sendDebug( "Skill type:" + std::to_string( type ) );
switch( type )
{
case Common::SkillType::Normal:
if( action < 1000000 ) // normal action
{
std::string actionIdStr = boost::str( boost::format( "%|04X|" ) % action );
@ -104,4 +112,19 @@ void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inP
}
break;
case Common::SkillType::MountSkill:
pPlayer->sendDebug( "Request mount " + std::to_string( action ) );
Action::ActionMountPtr pActionMount( new Action::ActionMount(pPlayer, action) );
pPlayer->setCurrentAction( pActionMount );
pPlayer->sendDebug("setCurrentAction()");
pPlayer->getCurrentAction()->onStart();
break;
}
}

View file

@ -64,6 +64,9 @@ private:
memcpy( m_data.orchestrionMask, player->getOrchestrionBitmask(), sizeof( m_data.orchestrionMask ) );
memset( m_data.mountGuideMask, 0xFF, sizeof( m_data.mountGuideMask) );
memset( m_data.fishingGuideMask, 0xFF, sizeof( m_data.fishingGuideMask ) );
memcpy( m_data.unlockBitmask, player->getUnlockBitmask(), sizeof( m_data.unlockBitmask ) );
memcpy( m_data.discovery, player->getDiscoveryBitmask(), sizeof( m_data.discovery ) );

View file

@ -111,6 +111,8 @@ namespace Server {
m_data.displayFlags |= Entity::Actor::DisplayFlags::Visor;
}
m_data.currentMount = pPlayer->getCurrentMount();
m_data.targetId = pPlayer->getTargetId();
//m_data.type = 1;
//m_data.unknown_33 = 4;