diff --git a/src/common/Network/PacketDef/Chat/ServerChatDef.h b/src/common/Network/PacketDef/Chat/ServerChatDef.h index e2ebe0ba..bbec5794 100644 --- a/src/common/Network/PacketDef/Chat/ServerChatDef.h +++ b/src/common/Network/PacketDef/Chat/ServerChatDef.h @@ -21,7 +21,7 @@ struct FFXIVIpcTell : uint16_t u2b; uint8_t preName; uint8_t u3a; - uint8_t u3b; //Setting this to 1 seems to mark the tell as a GM tell (More research needed) + bool isGm; char receipientName[32]; char msg[1031]; }; diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index ccec3b79..6556ccda 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -1441,7 +1441,7 @@ struct FFXIVIpcPrepareZoning : uint8_t fadeOut; uint8_t param7; uint8_t fadeOutTime; - uint8_t unknown; + uint8_t unknown; // this changes whether or not the destination zone's name displays during the loading screen. Seems to always be 9 (=hidden) when going to an instance and certain zones, 0 otherwise. uint16_t padding; }; diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 1ff34bfb..92a575e7 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -534,7 +534,7 @@ namespace Sapphire::Entity void teleportQuery( uint16_t aetheryteId, FrameworkPtr pFw ); /*! prepares zoning / fades out the screen */ - void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadoutTime = 0, uint16_t animation = 0 ); + void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime = 0, uint16_t animation = 0 ); /*! get player's title list (available titles) */ uint8_t* getTitleList(); diff --git a/src/world/Network/Handlers/GMCommandHandlers.cpp b/src/world/Network/Handlers/GMCommandHandlers.cpp index 4690761a..8291d24d 100644 --- a/src/world/Network/Handlers/GMCommandHandlers.cpp +++ b/src/world/Network/Handlers/GMCommandHandlers.cpp @@ -142,8 +142,11 @@ void Sapphire::Network::GameConnection::gm1Handler( FrameworkPtr pFw, auto inRange = targetPlayer->getInRangeActors(); for( auto actor : inRange ) { - targetPlayer->despawn( actor->getAsPlayer() ); - targetPlayer->spawn( actor->getAsPlayer() ); + if( actor->isPlayer() ) + { + targetPlayer->despawn( actor->getAsPlayer() ); + targetPlayer->spawn( actor->getAsPlayer() ); + } } break; } @@ -155,8 +158,11 @@ void Sapphire::Network::GameConnection::gm1Handler( FrameworkPtr pFw, auto inRange = targetPlayer->getInRangeActors(); for( auto actor : inRange ) { - targetPlayer->despawn( actor->getAsPlayer() ); - targetPlayer->spawn( actor->getAsPlayer() ); + if( actor->isPlayer() ) + { + targetPlayer->despawn( actor->getAsPlayer() ); + targetPlayer->spawn( actor->getAsPlayer() ); + } } break; } @@ -168,8 +174,11 @@ void Sapphire::Network::GameConnection::gm1Handler( FrameworkPtr pFw, auto inRange = targetActor->getInRangeActors(); for( auto actor : inRange ) { - targetPlayer->despawn( actor->getAsPlayer() ); - targetPlayer->spawn( actor->getAsPlayer() ); + if( actor->isPlayer() ) + { + targetPlayer->despawn( actor->getAsPlayer() ); + targetPlayer->spawn( actor->getAsPlayer() ); + } } break; } @@ -230,8 +239,11 @@ void Sapphire::Network::GameConnection::gm1Handler( FrameworkPtr pFw, for( auto actor : player.getInRangeActors() ) { - player.despawn( actor->getAsPlayer() ); - player.spawn( actor->getAsPlayer() ); + if( actor->isPlayer() ) + { + targetPlayer->despawn( actor->getAsPlayer() ); + targetPlayer->spawn( actor->getAsPlayer() ); + } } break; } @@ -610,12 +622,25 @@ void Sapphire::Network::GameConnection::gm2Handler( FrameworkPtr pFw, } case GmCommand::Jump: { - if( targetPlayer->getZoneId() != player.getZoneId() ) + player.prepareZoning( targetPlayer->getZoneId(), true, 1, 0 ); + if( player.getCurrentInstance() ) { - player.setZone( targetPlayer->getZoneId() ); + player.exitInstance(); + } + if( targetPlayer->getCurrentZone()->getGuId() != player.getCurrentZone()->getGuId() ) + { + // Checks if the target player is in an InstanceContent to avoid binding to a Zone or PublicContent + if( targetPlayer->getCurrentInstance() ) + { + auto pInstanceContent = targetPlayer->getCurrentInstance()->getAsInstanceContent(); + // Not sure if GMs actually get bound to an instance they jump to on retail. It's mostly here to avoid a crash for now + pInstanceContent->bindPlayer( player.getId() ); + } + player.setInstance( targetPlayer->getCurrentZone()->getGuId() ); } player.changePosition( targetActor->getPos().x, targetActor->getPos().y, targetActor->getPos().z, targetActor->getRot() ); + player.sendZoneInPackets( 0x00, 0x00, 0, 0, false ); player.sendNotice( "Jumping to {0}", targetPlayer->getName() ); break; } @@ -627,10 +652,17 @@ void Sapphire::Network::GameConnection::gm2Handler( FrameworkPtr pFw, player.sendUrgent( "You are unable to call a player while bound to a battle instance." ); return; } - - targetPlayer->setInstance( player.getCurrentZone() ); - + targetPlayer->prepareZoning( player.getZoneId(), true, 1, 0 ); + if( targetPlayer->getCurrentInstance() ) + { + targetPlayer->exitInstance(); + } + if( targetPlayer->getCurrentZone()->getGuId() != player.getCurrentZone()->getGuId() ) + { + targetPlayer->setInstance( player.getCurrentZone()->getGuId() ); + } targetPlayer->changePosition( player.getPos().x, player.getPos().y, player.getPos().z, player.getRot() ); + targetPlayer->sendZoneInPackets( 0x00, 0x00, 0, 0, false ); player.sendNotice( "Calling {0}", targetPlayer->getName() ); break; } diff --git a/src/world/Network/Handlers/PacketHandlers.cpp b/src/world/Network/Handlers/PacketHandlers.cpp index 17278588..7e79b963 100644 --- a/src/world/Network/Handlers/PacketHandlers.cpp +++ b/src/world/Network/Handlers/PacketHandlers.cpp @@ -656,6 +656,10 @@ void Sapphire::Network::GameConnection::tellHandler( FrameworkPtr pFw, //tellPacket.data().u1 = 0x92CD7337; //tellPacket.data().u2a = 0x2E; //tellPacket.data().u2b = 0x40; + if( player.isActingAsGm() ) + { + tellPacket->data().isGm = true; + } pTargetPlayer->queueChatPacket( tellPacket ); }