uox3/source/CPacketReceive.cpp
Xoduz a48d9ee299 UOX3 0.99.6-RC6
Removed arbitrary and hidden restrictions on refreshing list of online players shown with 'wholist command
Fixed an issue where player ghosts that get teleported would not see themselves get updated to new location
Fixed a server crash from clients disconnecting while running server in debug mode
Improved how container updates are sent to players; now only sends to clients that have actually opened the container, and who are still within range
Added more information about UO data files being loaded during server startup
Enhanced the GM 'add menu to allow clicking directly on images of items/npcs to add them, and adjusted size of menus to accommodate this
Fixed an issue with fleeing NPCs that could cause them to attempt to flee across time and space, regardless of distance to target/attacker in combat
Added NPC AI that runs away from players if they get too close (even outside combat). Applied to hind, rabbits, squirrels, ferrets and various birds by default:
	AI_ANIMAL_SCARED (aitype 12)
Fixed issues with NPCs fleeing forever, or getting stuck in flee/don't flee loop, by introducing max fleeing distance (50 tiles) and cooldown (30 sec) on fleeing
Updated onNameRequest JS Event to include third parameter with origin of name request, so script responses can be tailored appropriately:
	onNameRequest( myObj, requestedBy, requestSource )
		Potential values for requestSource:
			0 - Speech/System Messages
			1 - Guild Menus
			2 - Stat Window (self)
			3 - Stat Window (other)
			4 - Tooltip
			5 - Paperdoll Journal
			6 - Paperdoll
			7 - Single Click / All-Names
			8 - System
			9 - Secure Trade Window
Added Adaptive Performance System (APS) that dynamically adjust how often NPC AI/movement is checked based on overall shard performance. If performance drops below defined threshold, UOX3 gradually slows down checks for NPC AI/movement to prioritize player movement/speech/command responsiveness. If performance climbs back up above threshold, slowdowns are gradually removed. The following UOX.INI options have been added under [system] category to support this system:
	APSPERFTHRESHOLD=50 	// Performance threshold (measured in simulation cycles/sec) below which the APS kicks in
	APSINTERVAL=100 		// How often (in milliseconds) the APS checks performance and makes adjustments (if needed) to balance out shard performance
	APSDELAYSTEP=50 		// How much the delay timer is modified by (in milliseconds) each time APS makes adjustments
	APSDELAYMAXCAP=2000 	// Max amount of of delay APS can introduce for NPC AI/movement handling when attempting to restore shard performance
Added new JS Methods for Region objects:
	.GetOrePrefs( oreType )	// Get ore preference data for ore type found in town region. Returned as an array containing the following data:
		orePrefData -> [
			oreName,	// name of ore
			color,		// color of ore
			minskill,	// min skill to mine ore
			ingotName,	// name of ingot created from ore
			makemenu,	// makemenu entry for crafting something from ingot
			oreChance,	// default global chance of finding this ore type
			scriptID 	// script attached to mined ore
		],
		orePrefChance 	// Chance of finding this ore type in given town region
	.GetOreChance() 	// Get base chance of finding any ore in town region
Moved Mining skill plus gravedigging feature from hard code to scripts (js/skill/mining.js) to make it easier to maintain and/or customize, and removed hard coded variants
Gravedigging now relies on the ore resource system behind the scenes to restrict how often graves in a given area can be dug up
Fixed some incorrect references to .worldNumber Character property in misc scripts (should be .worldnumber)
Made some "hard-scripted" system messages in misc scripts use dictionary system instead
Added spawn region for banker NPCs in Serpent's Hold (dfndata/spawn/felucca/spawn_town_serpents_hold.dfn, dfndata/spawn/trammel/spawn_town_serpents_hold.dfn)
Fixed region definition of Ocllo to actually cover the entire town (dfndata/regions/regions.dfn)
Added numerous additional locations accessible with 'goplace # command, for key locations in Ilshenar, Malas, Tokuno Islands and Ter Mur (dfndata/location/location.dfn)
Revised travel-menu portion of GM menu (shortcut: 'travel) to include more travel options based on new locations (dfndata/items/travelmenu.dfn and travelmenu.bulk.dfn)
Fixed an issue where NPCs could attempt to attack targets in other worlds/instances
Added new JS Event that triggers for characters who are about to deal damage in combat. Complimentary to onDamage, which triggers for chars receiving damage:
	onDamageDeal( dmgDealer, dmgReceiver, damageValue, damageType )
Added new JS Event that can trigger in global script upon creation of new player chars. Note that this will trigger in place of onCreateDFN event for player characters:
	onCreatePlayer( pChar )
Added new JS Event that triggers for characters selecting a target with a spell. Complimentary to onSpellTarget, which triggers for targets selected with a spell:
	onSpellTargetSelect( caster, target, spellNum )
Updated onCombatStart and onCombatEnd JS Events to also trigger for the other party in combat
Updated onPickup JS Event to include a third parameter - the potential container item was picked up from. Event now also triggers event in scripts attached to said containers:
	onPickup( iPickedUp, pChar, iCont )
Added tracking of total playtime per individual character, and per account across all characters, and exposed these properties to JS engine:
	.totalPlayTime		// Account property, total playtime across all chars
	.playTime 			// Character property, total playtime on given character only
Added new player-accessible command ('playtime) to spit out the playtime of current character/account as a whole (js/commands/playtime.js)
Implemented first version of Young Player System:
	Replaced the UNUSED9 account flag with YOUNG, to be used by Young/New Player System
	Added new JS Account property that gets/sets whether player account is considered Young:
		.isYoung
	Added new Char timers:
		TIMER_YOUNGHEAL		// Restricts how often Young players are healed by NPC healers
		TIMER_YOUNGMESSAGE	// Restricts how often Young players are warned about dangerous looking monsters in overworld
	Updated GM commands 'get and 'set to get/set .isYoung property of player's account (js/commands/targeting/get.js and set.js)
	Added new UOX.INI setting to enable/disable Young Player System (enabled by default):
		YOUNGPLAYERSYSTEM=1
	If Young Player System is enabled, all newly created player accounts are automatically marked with Young flag
	Added new script to handle various restrictions and functions related to Young characters (js/player/young_player.js):
		Young characters will have [Young] displayed over their head
		Young characters will have their Young status checked and verified on every login + every stat/skill gain, to revoke the Young status if any of the following is true:
			Account has a total playtime of more than 40 hours
			Any character has more than 350 total (base) skill points
			Any character has more than 70 skill points in a single skill
			Any character has more than 150 total stat points
			Any character has more than 80 stat points in a single stat
		Young characters can renounce their Young status manually by saying the words "I renounce my young player status"
		Young characters get two additional items upon creation:
			a new player ticket (can be combined with any other players new player ticket for both players to get a reward)
			a sextant (shows Young players directions to nearest moongate/bank regardless of where they are, as long as outdoors)
		Young characters cannot target other player characters with hostile spells or skills
		Young characters cannot be the target of other player characters' hostile spells or skills
		Young characters can only cast beneficial spells upon themselves or other Young characters
		Young characters can only be the target of beneficial spells from other Young players
		Young players (and their pets/followers) cannot attack or harm any other players (or their pets/followers), nor themselves be attacked or harmed by any other players (or their pets/followers), whether from combat, spells, skills or items
		Young players don't lose any of their items on death, and are teleported to the nearest healer upon death with all their items intact
		Young players receive a warning upon entering dungeons that monsters there will be hostile
	Updated global script (js/server/global.js) to:
		Check/Verify Young status of players upon login
		Attach young player script on login/creation for players on Young accounts
		Give specific items to Young players upon creation
	Added new script (js/item/corpse.js) that is assigned to all freshly created corpses. This script handles restrictions related to Young players interactions with corpses, and the interactions of other players with corpses of Young players
	Updated JS Method .Carve() to return true/false depending on whether carving a corpse was successful
	Updated Healer AI in code to heal nearby injured Young players
	Updated Evil AI and Evil Caster AI in code to avoid selecting Young players as targets in combat outside of dungeons
	Added a new section to UOX.INI - [young player starting locations] - that can be used to define starting locations for Young players. If only one such location is provided, all players start there. Otherwise, it matches the same starting location setup as the regular one - with one entry per town in Britannia.
	Restricted transferring and/or friending of pets (code) and hirelings (hirelings.js) between Young and non-Young players
	Restricted Young players from recalling or gating to Felucca facet
	Young players can instantly logout from anywhere, at any time
2023-07-01 20:02:40 +08:00

5797 lines
186 KiB
C++
Raw Blame History

#include "uox3.h"
#include "CPacketSend.h"
#include "movement.h"
#include "cServerDefinitions.h"
#include "ssection.h"
#include "cThreadQueue.h"
#include "combat.h"
#include "cScript.h"
#include "CJSMapping.h"
#include "PageVector.h"
#include "cEffects.h"
#include "Dictionary.h"
#include "books.h"
#include "cMagic.h"
#include "skills.h"
#include "PartySystem.h"
#include "cGuild.h"
#include "CResponse.h"
#include "StringUtility.hpp"
#include "cRaces.h"
#include <chrono>
#include "IP4Address.hpp"
//o------------------------------------------------------------------------------------------------o
//| Function - pSplit()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Split login password into UOX password and UO password (???)
//o------------------------------------------------------------------------------------------------o
void pSplit( const std::string pass0, std::string &pass1, std::string &pass2 )
{
size_t i = 0;
pass1 = "";
auto pass0Len = pass0.length();
while( i < pass0Len && pass0[i] != '/' )
{
++i;
}
pass1 = pass0.substr( 0, i );
if( i < pass0Len )
{
pass2 = pass0.substr( i );
}
}
void PackShort( UI08 *toPack, SI32 offset, UI16 value )
{
toPack[offset + 0] = static_cast<UI08>(( value & 0xFF00 ) >> 8 );
toPack[offset + 1] = static_cast<UI08>(( value & 0x00FF ) % 256 );
}
//o------------------------------------------------------------------------------------------------o
//| Function - WhichLoginPacket()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles login packets from client
//o------------------------------------------------------------------------------------------------o
CPInputBuffer *WhichLoginPacket( UI08 packetId, CSocket *s )
{
switch( packetId )
{
case 0x00: return ( new CPICreateCharacter( s )); // Character Create
case 0x01: return nullptr; // Main Menu on the character select screen
case 0x04: return ( new CPIGodModeToggle( s ));
case 0x5D: return ( new CPIPlayCharacter( s )); // Character Select
case 0x73: return ( new CPIKeepAlive( s ));
case 0x80: return ( new CPIFirstLogin( s ));
case 0x83: return ( new CPIDeleteCharacter( s )); // Character Delete
case 0x8D: return ( new CPICreateCharacter( s )); // Character creation for 3D/Enhanced clients
case 0x91: return ( new CPISecondLogin( s ));
case 0xA0: return ( new CPIServerSelect( s ));
case 0xA4: return ( new CPISpy( s ));
case 0xBB: return nullptr; // Ultima Messenger - old, no longer used
case 0xBD: return ( new CPIClientVersion( s ));
case 0xBF: return ( new CPISubcommands( s )); // general overall packet
case 0xC0: return ( new CPINewClientVersion( s )); // LoginSeed/New client-version clients before 6.0.x
case 0xD9: return ( new CPIMetrics( s )); // Client Hardware / Metrics
case 0xEF: return ( new CPINewClientVersion( s )); // LoginSeed/New client-version clients after 6.0.x
//case 0xE4: return ( new CPIKREncryptionVerification( s )); // KR Encryption Response verification
//case 0xF1: return nullptr; // Freeshard Server Poll Packet - handled in packet hook JS script
case 0xF8: return ( new CPICreateCharacter( s )); // New Character Create - minor difference from original
case 0xFF: return nullptr; // new CPIKRSeed( s ) - KR client request for encryption response
default: break;
}
throw socket_error( "Bad packet request" );
return nullptr;
}
//o------------------------------------------------------------------------------------------------o
//| Function - WhichPacket()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles packets received from client
//o------------------------------------------------------------------------------------------------o
CPInputBuffer *WhichPacket( UI08 packetId, CSocket *s )
{
switch( packetId )
{
case 0x00: return ( new CPICreateCharacter( s ));
case 0x02: return ( new CPIMoveRequest( s ));
case 0x03: return ( new CPITalkRequestAscii( s ));
case 0x04: return ( new CPIGodModeToggle( s ));
case 0x05: return ( new CPIAttack( s ));
case 0x06: return ( new CPIDblClick( s ));
case 0x07: return ( new CPIGetItem( s ));
case 0x08: return ( new CPIDropItem( s ));
case 0x09: return ( new CPISingleClick( s ));
case 0x12: return nullptr; // Request Skill / Action / Magic Usage
case 0x13: return ( new CPIEquipItem( s ));
case 0x22: return ( new CPIResyncReq( s ));
case 0x2C: return ( new CPIResMenuChoice( s ));
case 0x34: return ( new CPIStatusRequest( s ));
case 0x3A: return nullptr; // Skills management packet
case 0x3B: return ( new CPIBuyItem( s ));
case 0x56: return nullptr; // Map Related
case 0x5D: return ( new CPIPlayCharacter( s ));
case 0x66: return ( new CPIBookPage( s )); // Player Turns the Page (or closes) a Book
case 0x69: return nullptr; // Client text change
case 0x6C: return ( new CPITargetCursor( s ));
case 0x6F: return ( new CPITradeMessage( s ));
case 0x71: return ( new CPIMsgBoardEvent( s )); // Message Board Item
case 0x72: return nullptr; // Combat mode
case 0x73: return ( new CPIKeepAlive( s ));
case 0x75: return ( new CPIRename( s ));
case 0x7D: return ( new CPIGumpChoice( s ));
case 0x80: return ( new CPIFirstLogin( s ));
case 0x83: return ( new CPIDeleteCharacter( s ));
case 0x8D: return ( new CPICreateCharacter( s )); // Character creation for 3D/Enhanced clients
case 0x91: return ( new CPISecondLogin( s ));
case 0x93: return nullptr; // Books - title page
case 0x95: return ( new CPIDyeWindow( s ));
case 0x98: return ( new CPIAllNames3D( s ));
case 0x9B: return ( new CPIHelpRequest( s ));
case 0x9F: return ( new CPISellItem( s ));
case 0xA0: return ( new CPIServerSelect( s ));
case 0xA4: return ( new CPISpy( s ));
case 0xA7: return ( new CPITips( s ));
case 0xAC: return ( new CPIGumpInput( s ));
case 0xAD: return ( new CPITalkRequestUnicode( s ));
case 0xB1: return ( new CPIGumpMenuSelect( s ));
case 0xB6: return nullptr; // T2A Popup Help Request
case 0xB8: return nullptr; // T2A Profile Request
case 0xBB: return nullptr; // Ultima Messenger
case 0xBD: return ( new CPIClientVersion( s ));
case 0xBF: return ( new CPISubcommands( s )); // Assorted commands
case 0xC8: return ( new CPIUpdateRangeChange( s ));
case 0xC0: return ( new CPINewClientVersion( s )); // LoginSeed/New client-version clients before 6.0.x
case 0xD0: return nullptr; // Configuration File
case 0xD1: return ( new CPILogoutStatus( s )); // Logout Status
case 0xD4: return ( new CPINewBookHeader( s )); // New Book Header
case 0xD6: return ( new CPIToolTipRequestAoS( s )); // AoS Request for tooltip data
case 0xD7: return ( new CPIAOSCommand( s )); // AOS Command
case 0xD9: return ( new CPIMetrics( s )); // Client Hardware / Metrics
case 0xF0: return ( new CPIKrriosClientSpecial( s )); // Responses to special client packet also used by assistant tools to negotiate features with server
case 0xF3: return nullptr; // TODO - Object Information (SA)
case 0xF5: return nullptr; // TODO - New Map Message
case 0xF8: return ( new CPICreateCharacter( s )); // New Character Create
default: return nullptr;
}
return nullptr;
}
void CheckBanTimer( CAccountBlock_st &actbTemp )
{
// Unban player if bantime is up, but don't do anything if bantime is zero - maybe player is permanently banned!
if( actbTemp.wFlags.test( AB_FLAGS_BANNED ) && ( actbTemp.wTimeBan > 0 && actbTemp.wTimeBan <= GetMinutesSinceEpoch() ))
{
actbTemp.wTimeBan = 0x0;
actbTemp.wFlags.set( AB_FLAGS_BANNED, false );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIFirstLogin()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for client first login - login server
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x80 (Login Request)
//| Size: 62 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[30] userid
//| BYTE[30] password
//| BYTE unknown1 (not usually 0x00 - so not nullptr)
//o------------------------------------------------------------------------------------------------o
void CPIFirstLogin::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIFirstLogin 0x80 --> Length: 62" << TimeStamp() << std::endl;
}
outStream << "UserId : " << Name() << std::endl;
outStream << "Password : " << Pass() << std::endl;
outStream << "Unknown : " << static_cast<SI16>( Unknown() ) << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
bool CPIFirstLogin::Handle( void )
{
tSock->AcctNo( AB_INVALID_ID );
LoginDenyReason t = LDR_NODENY;
std::string username = Name();
CAccountBlock_st * actbTemp = &Accounts->GetAccountByName( username );
if( actbTemp->wAccountIndex != AB_INVALID_ID )
{
tSock->SetAccount(( *actbTemp ));
}
std::string pass0, pass1, pass2;
pass0 = Pass();
pSplit( pass0, pass1, pass2 );
// Need auto account creation code here
if( tSock->AcctNo() == AB_INVALID_ID )
{
if( cwmWorldState->ServerData()->InternalAccountStatus() )
{
Accounts->AddAccount( username, pass1, "" );
actbTemp = &Accounts->GetAccountByName( username );
if( actbTemp->wAccountIndex != AB_INVALID_ID ) // grab our account number
{
tSock->SetAccount(( *actbTemp ));
}
}
}
if( tSock->AcctNo() != AB_INVALID_ID )
{
// Check if player's bantime is up, and if so, unban player
CheckBanTimer( *actbTemp );
if( actbTemp->wFlags.test( AB_FLAGS_BANNED ))
{
t = LDR_ACCOUNTDISABLED;
}
else if( actbTemp->sPassword != pass1 )
{
t = LDR_BADPASSWORD;
}
else
{
// Handle client-restrictions, disconnect if client-version isn't supported
// Client-versions below 6.0.5.0 can only be verified after they're ingame, so can only
// be blocked in FirstLogin if _all_ client versions below 6.0.5.0 are blocked
if( tSock->ClientType() == CV_T2A )
{
if( !cwmWorldState->ServerData()->ClientSupport4000() && !cwmWorldState->ServerData()->ClientSupport5000() && !cwmWorldState->ServerData()->ClientSupport6000() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (4.0.0 - 6.0.4.x). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientType() <= CV_KR3D && tSock->ClientType() != CV_DEFAULT )
{
if( !cwmWorldState->ServerData()->ClientSupport6050() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (6.0.5.0 - 6.0.14.2). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientType() <= CV_SA3D && tSock->ClientType() != CV_DEFAULT )
{
if( !cwmWorldState->ServerData()->ClientSupport7000() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.0.0 - 7.0.8.2). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientType() <= CV_HS3D && tSock->ClientType() != CV_DEFAULT )
{
if( tSock->ClientVerShort() < CVS_70160 )
{
if( !cwmWorldState->ServerData()->ClientSupport7090() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.9.0 - 7.0.15.1). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientVerShort() < CVS_70240 )
{
if( !cwmWorldState->ServerData()->ClientSupport70160() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.16.0 - 7.0.23.1). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientVerShort() < CVS_70300 )
{
if( !cwmWorldState->ServerData()->ClientSupport70240() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.24.0+). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientVerShort() < CVS_70331 )
{
if( !cwmWorldState->ServerData()->ClientSupport70300() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.30.0+). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientVerShort() < CVS_704565 )
{
if( !cwmWorldState->ServerData()->ClientSupport70331() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.33.1+). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientVerShort() < CVS_70610 )
{
if( !cwmWorldState->ServerData()->ClientSupport704565() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.45.65+). See UOX.INI..." << myendl;
}
}
else if( tSock->ClientVerShort() >= CVS_70610 )
{
if( !cwmWorldState->ServerData()->ClientSupport70610() )
{
t = LDR_COMMSFAILURE;
Console << "Login denied - unsupported client (7.0.61.0+). See UOX.INI..." << myendl;
}
}
}
}
}
else
{
t = LDR_UNKNOWNUSER;
}
if( t == LDR_NODENY && actbTemp->wFlags.test( AB_FLAGS_ONLINE ))
{
t = LDR_ACCOUNTINUSE;
}
if( t != LDR_NODENY )
{
CPLoginDeny pckDeny( t );
tSock->Send( &pckDeny );
tSock->AcctNo( AB_INVALID_ID );
Network->LoginDisconnect( tSock );
return true;
}
if( tSock->AcctNo() != AB_INVALID_ID )
{
actbTemp->dwLastIP = CalcSerial( tSock->ClientIP4(), tSock->ClientIP3(), tSock->ClientIP2(), tSock->ClientIP1() );
auto temp = oldstrutil::format( "Client [%i.%i.%i.%i] connected using Account '%s'.", tSock->ClientIP4(), tSock->ClientIP3(), tSock->ClientIP2(), tSock->ClientIP1(), username.c_str() );
Console.Log( temp , "server.log" );
messageLoop << temp;
actbTemp->wFlags.set( AB_FLAGS_ONLINE, true );
//Add temp-clientversion info to account here, to be used for second login
if( actbTemp->dwLastClientVer == 0 || tSock->ClientType() != CV_DEFAULT )
{
actbTemp->dwLastClientVer = tSock->ClientVersion();
actbTemp->dwLastClientType = tSock->ClientType();
actbTemp->dwLastClientVerShort = tSock->ClientVerShort();
}
// change for IP4Address
auto address = cwmWorldState->ServerData()->matchIP( tSock->ipaddress );
CPGameServerList toSend( 1 );
toSend.addEntry( cwmWorldState->ServerData()->ServerName(), address.ipaddr( true ));
tSock->Send( &toSend );
}
// If socket's ClientType is still CV_DEFAULT, it's an old client,
// since there was no packet before 0x80 where ClientType changed
if( tSock->ClientType() == CV_DEFAULT )
{
tSock->ClientType( CV_T2A );
}
//CPEnableClientFeatures ii;
//tSock->Send( &ii );
return true;
}
CPIFirstLogin::CPIFirstLogin()
{
InternalReset();
}
CPIFirstLogin::CPIFirstLogin( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPIFirstLogin::Receive( void )
{
tSock->FirstPacket( false );
tSock->Receive( 62, false );
// Copy data over into internal variables
char temp[30];
// Grab our username
memcpy( temp, &tSock->Buffer()[1], 30 );
userId = temp;
// Grab our password
memcpy( temp, &tSock->Buffer()[31], 30 );
password = temp;
unknown = tSock->GetByte( 61 );
// Done with our buffer, we can clear it out now
}
void CPIFirstLogin::InternalReset( void )
{
userId.reserve( 30 );
password.reserve( 30 );
unknown = 0;
}
const std::string CPIFirstLogin::Name( void )
{
return userId;
}
const std::string CPIFirstLogin::Pass( void )
{
return password;
}
UI08 CPIFirstLogin::Unknown( void )
{
return unknown;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIServerSelect()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for server selection
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xA0 (Select Server)
//| Size: 3 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] server # chosen
//| 0x80 Packet
//o------------------------------------------------------------------------------------------------o
void CPIServerSelect::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIServerSelect 0xA0 --> Length: 3" << TimeStamp() << std::endl;
}
outStream << "Server : " << ServerNum() << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPIServerSelect::InternalReset( void )
{
}
CPIServerSelect::CPIServerSelect()
{
InternalReset();
}
CPIServerSelect::CPIServerSelect( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPIServerSelect::Receive( void )
{
tSock->Receive( 3, false );
}
SI16 CPIServerSelect::ServerNum( void )
{
// Sept 19, 2002
// Someone said that there was an issue with False logins that request server 0. Default to server 1.
SI16 temp = tSock->GetWord( 1 );
if( temp == 0 )
{
return 1;
}
else
{
return temp;
}
}
bool CPIServerSelect::Handle( void )
{
auto ip = cwmWorldState->ServerData()->matchIP( tSock->ipaddress );
auto name = cwmWorldState->ServerData()->ServerName();
auto port = cwmWorldState->ServerData()->ServerPort();
CPRelay toSend( ip.ipaddr( false ), port );
tSock->Send( &toSend );
// Mark packet as sent. No we need to change how we network.
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPISecondLogin()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for client second login - game server
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x91 (Game Server Login)
//| Size: 65 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] key used
//| BYTE[30] sid
//| BYTE[30] password
//o------------------------------------------------------------------------------------------------o
void CPISecondLogin::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISecondLogin 0x91 --> Length: 65" << TimeStamp() << std::endl;
}
outStream << "Key Used : " << Account() << std::endl;
outStream << "SID : " << Name() << std::endl;
outStream << "Password : " << Pass() << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPISecondLogin::InternalReset( void )
{
sid.reserve( 30 );
password.reserve( 30 );
keyUsed = 0;
}
CPISecondLogin::CPISecondLogin()
{
InternalReset();
}
CPISecondLogin::CPISecondLogin( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPISecondLogin::Receive( void )
{
tSock->FirstPacket( false );
tSock->Receive( 65, false );
tSock->CryptClient( true );
// Copy data over into internal variables
char temp[30];
keyUsed = tSock->GetDWord( 1 );
// Grab our username
memcpy( temp, &tSock->Buffer()[5], 30 );
sid = temp;
// Grab our password
memcpy( temp, &tSock->Buffer()[35], 30 );
password = temp;
// Done with our buffer, we can clear it out now
}
UI32 CPISecondLogin::Account( void )
{
return keyUsed;
}
const std::string CPISecondLogin::Name( void )
{
return sid;
}
const std::string CPISecondLogin::Pass( void )
{
return password;
}
bool CPISecondLogin::Handle( void )
{
LoginDenyReason t = LDR_NODENY;
tSock->AcctNo( AB_INVALID_ID );
CAccountBlock_st& actbTemp = Accounts->GetAccountByName( Name() );
if( actbTemp.wAccountIndex != AB_INVALID_ID )
{
tSock->SetAccount( actbTemp );
}
// Add socket version info from first login, stored in account, to new socket!
tSock->ClientVersion( actbTemp.dwLastClientVer );
tSock->ClientType( static_cast<ClientTypes>( actbTemp.dwLastClientType ));
tSock->ClientVerShort( static_cast<ClientVersions>( actbTemp.dwLastClientVerShort ));
std::string pass0, pass1, pass2;
pass0 = Pass();
pSplit( pass0, pass1, pass2 );
if( tSock->AcctNo() != AB_INVALID_ID )
{
// Check if player's bantime is up, and if so, unban player
CheckBanTimer( actbTemp );
if( actbTemp.wFlags.test( AB_FLAGS_BANNED ))
{
t = LDR_ACCOUNTDISABLED;
}
else if( pass1 != actbTemp.sPassword )
{
t = LDR_BADPASSWORD;
}
}
else
{
t = LDR_UNKNOWNUSER;
}
if( t != LDR_NODENY )
{
tSock->AcctNo( AB_INVALID_ID );
CPLoginDeny pckDeny( t );
tSock->Send( &pckDeny );
Network->LoginDisconnect( tSock );
return true;
}
else
{
// If first login timestamp has not been set, set it here
if( actbTemp.wFirstLogin == 0 )
{
actbTemp.wFirstLogin = GetMinutesSinceEpoch();
}
//Send supported client features before character-list stuff
CPEnableClientFeatures ii( tSock );
tSock->Send( &ii );
UI08 charCount = 0;
for( UI08 i = 0; i < 7; ++i )
{
if( actbTemp.dwCharacters[i] != INVALIDSERIAL )
{
++charCount;
}
}
CServerData *sd = cwmWorldState->ServerData();
size_t serverCount = sd->NumServerLocations();
CPCharAndStartLoc toSend( actbTemp, charCount, static_cast<UI08>( serverCount ), tSock );
for( size_t j = 0; j < serverCount; ++j )
{
if( tSock->ClientType() >= CV_HS2D && tSock->ClientVersionSub() >= 13 )
{
toSend.NewAddStartLocation( sd->ServerLocation( j ), static_cast<UI08>( j ));
}
else
{
toSend.AddStartLocation( sd->ServerLocation( j ), static_cast<UI08>( j ));
}
}
tSock->Send( &toSend );
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPINewClientVersion()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for client login/seed, with initial client info
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xEF (KR/2D Client Login/Seed)
//| Size: 21 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] seed, usually the client local ip
//| BYTE[4] client major version
//| BYTE[4] client minor version
//| BYTE[4] client revision version
//| BYTE[4] client prototype version
//|
//| Notes
//| Normally older client send a 4 byte seed (local ip).
//| Newer clients 2.48.0.3+ (KR) and 6.0.5.0+ (2D) are sending this packet.
//o------------------------------------------------------------------------------------------------o
void CPINewClientVersion::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPINewClientVersion 0xEF --> Length: " << std::dec << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "Version : " << tSock->ClientVersion() << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPINewClientVersion::InternalReset( void )
{
// len = 0;
seed = 0;
majorVersion = 0;
minorVersion = 0;
clientRevision = 0;
clientPrototype = 0;
}
CPINewClientVersion::CPINewClientVersion()
{
InternalReset();
}
CPINewClientVersion::CPINewClientVersion( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
// Handle();
}
void CPINewClientVersion::Receive( void )
{
if( tSock->GetByte( 0 ) == 0xC0 )
{
tSock->ClientType( CV_T2A );
}
else if( tSock->GetByte( 0 ) == 0xEF )
{
tSock->Receive( 21, false );
seed = tSock->GetDWord( 1 );
majorVersion = tSock->GetDWord( 5 );
minorVersion = tSock->GetDWord( 9 );
clientRevision = tSock->GetDWord( 13 );
clientPrototype = tSock->GetDWord( 17 );
tSock->ClientVersion( majorVersion, minorVersion, clientRevision, clientPrototype );
std::string verString = oldstrutil::number( majorVersion ) + std::string( "." ) +
oldstrutil::number( minorVersion ) + std::string( ". ") + oldstrutil::number( clientRevision ) +
std::string( "." ) + oldstrutil::number( clientPrototype );
Console << verString << myendl;
// Set client-version based on information received so far. We need this to be able to send the correct info during login
// Needs to be refined in second client-version pass (CPIClientVersion)
if( tSock->ClientVersion() <= 100666881 ) // If under or equal to 6.0.14.1, which in reality means between 6.0.5.0 and 6.0.14.1
{
tSock->ClientType( CV_KR2D );
}
else if( tSock->ClientVersion() >= 10066881 )
{
if( tSock->ClientVersion() >= 1000000000 ) // 1124079360 is 4.0.23.1?
{
if( tSock->ClientVersion() == 1110912768 )
{
// Kingdom Reborn 3D client, build 2.53.0.2
tSock->ClientType( CV_KR3D );
tSock->ClientVerShort( CVS_25302 );
}
else
{
//UO Enhanced client 4.0.23.1 and above
//should use same version numbering scheme as classic client internally
tSock->ClientType( CV_HS3D );
if( clientRevision <= 15 )
{
tSock->ClientVerShort( CVS_70151 );
}
else if( clientRevision < 24 )
{
tSock->ClientVerShort( CVS_70160 );
}
else
{
tSock->ClientVerShort( CVS_70240 );
}
}
}
else if( tSock->ClientVersion() == 100666882 ) // 6.0.14.2, but technically first SA2D client
{
tSock->ClientType( CV_SA2D );
}
else if( tSock->ClientVersion() <= 117442562 && clientRevision < 9 )
{
tSock->ClientType( CV_SA2D );
}
else if( tSock->ClientVersion() >= 117440814 && clientRevision >= 9 )
{
tSock->ClientType( CV_HS2D );
// Set temporary client-versions to be used by client-support option during login
if( clientRevision <= 15 )
{
tSock->ClientVerShort( CVS_70151 );
}
else if( clientRevision < 24 )
{
tSock->ClientVerShort( CVS_70160 );
}
else if( clientRevision < 30 )
{
tSock->ClientVerShort( CVS_70240 );
}
else if( clientRevision < 33 )
{
tSock->ClientVerShort( CVS_70300 );
}
else if( clientRevision < 45 )
{
tSock->ClientVerShort( CVS_70331 );
}
else if( clientRevision < 46 )
{
tSock->ClientVerShort( CVS_704565 );
}
else if( clientRevision < 61 )
{
tSock->ClientType( CV_TOL2D );
tSock->ClientVerShort( CVS_705527 );
}
else
{
tSock->ClientVerShort( CVS_70610 );
}
}
// ??? - 7.0.61.0 - Endless Journey
// ??? - 7.0.61.0 - Endless Journey
// 117462287 - 7.0.85.15
}
tSock->ReceivedVersion( true );
}
}
bool CPINewClientVersion::Handle( void )
{
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIClientVersion()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for client version string
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBD (Client Version Message)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] len
//| If (client-version of packet)
//| BYTE[len-3] string stating the client version (0 terminated) (like: <20>1.26.4<EFBFBD>)
//|
//| Notes
//| Client + Server packet
//| Client version: client sends its version string (e.g <20>3.0.8j<EFBFBD>)
//| Server version: 0xbd 0x0 0x3 (client replies with client version of this packet)
//| Clients sends a client version of this packet ONCE at login (without server request.)
//o------------------------------------------------------------------------------------------------o
void CPIClientVersion::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIClientVersion 0xBD --> Length: " << std::dec << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "Version : " << Offset() << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPIClientVersion::InternalReset( void )
{
len = 0;
}
CPIClientVersion::CPIClientVersion()
{
InternalReset();
}
CPIClientVersion::CPIClientVersion( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPIClientVersion::Receive( void )
{
tSock->Receive( 3, false );
len = tSock->GetWord( 1 );
tSock->Receive( len, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - UI08 ShiftValue( UI08 toShift, UI08 base, UI08 upper, bool extra)
//| Date - 21st November, 2001
//o------------------------------------------------------------------------------------------------o
//| Purpose - If the value is between base and upper (inclusive)
//| it shifts the data down by base (if extra) or base - 1 (if not extra)
//o------------------------------------------------------------------------------------------------o
UI08 ShiftValue( UI08 toShift, UI08 base, UI08 upper, bool extra )
{
if( extra )
{
if( toShift >= base && toShift <= upper )
{
toShift -= base;
}
}
else
{
if( toShift >= base && toShift <= upper )
{
toShift = toShift - base + 1;
}
}
return toShift;
}
char *CPIClientVersion::Offset( void )
{
return ( char * ) & ( tSock->Buffer()[3] );
}
bool CPIClientVersion::Handle( void )
{
char *verString = Offset();
verString[len] = 0;
// Only need this bit for clients prior to 6.0.5.0 (6.0.0.0 to 6.0.4.x are classified as CV_T2A up until this point)
// Version already received in packet 0xEF for 6.0.5.0+
if( tSock->ClientType() < CV_KR2D )
{
UI08 major, minor, sub, letter;
std::string s( verString );
std::string us = std::string( s );
auto dsecs = oldstrutil::sections( s, "." );
UI08 secCount = static_cast<UI08>( dsecs.size() - 1 );
std::istringstream ss( s );
char period;
ss >> major >> period;
ss >> minor >> period;
if( secCount == 3 )
{
ss >> sub >> period;
ss >> letter;
}
else
{
UI08 temp;
ss >> sub;
ss >> temp;
if( isalpha( temp ))
{
letter = temp;
}
else
{
std::string tempSubString;
std::stringstream tempHackSS;
tempHackSS << sub;
tempHackSS >> tempSubString;
tempHackSS.str("");
tempHackSS.clear();
tempHackSS << ( tempSubString += temp );
SI32 tempSubInt;
tempHackSS >> tempSubInt;
sub = tempSubInt;
ss >> letter;
}
}
major = ShiftValue( major, '0', '9', true );
minor = ShiftValue( minor, '0', '9', true );
sub = ShiftValue( sub, '0', '9', true );
if( secCount == 3 )
{
letter = ShiftValue( letter, '0', '9', true );
}
else
{
letter = ShiftValue( letter, 'a', 'z', false );
letter = ShiftValue( letter, 'A', 'Z', false );
}
tSock->ClientVersion( major, minor, sub, letter );
Console << verString << myendl;
}
if( strstr( verString, "Dawn" ))
{
tSock->ClientType( CV_UO3D );
}
else if( strstr( verString, "Krrios" ))
{
tSock->ClientType( CV_KRRIOS );
}
else
{
SetClientVersionShortAndType( tSock, verString );
}
tSock->ReceivedVersion( true );
return true;
}
void CPIClientVersion::SetClientVersionShortAndType( CSocket *tSock, char *verString )
{
UI08 CliVerMajor = tSock->ClientVersionMajor();
//UI08 CliVerMinor = tSock->ClientVersionMinor(); //uncomment if needed
UI08 CliVerSub = tSock->ClientVersionSub();
UI08 CliVerLetter = tSock->ClientVersionLetter();
UI32 CliVer = tSock->ClientVersion();
if( CliVer < 100663559 )
{
if( CliVerMajor == 4 )
{
tSock->ClientType( CV_T2A );
if( tSock->ClientVersionSub() < 7 )
{
tSock->ClientVerShort( CVS_400 );
}
else if( CliVerSub < 11 || ( CliVerSub == 11 && CliVerLetter < 2 ))
{
tSock->ClientVerShort( CVS_407a );
}
else if( CliVerSub == 11 && CliVerLetter >= 2 ) // 4.0.11f really belongs in the CV_ML type though...
{
tSock->ClientVerShort( CVS_4011c );
}
if( !cwmWorldState->ServerData()->ClientSupport4000() )
{
tSock->ForceOffline( true );
tSock->IdleTimeout( cwmWorldState->GetUICurrentTime() + 200 );
tSock->SysMessage( 1796, verString ); // Your current client-version (%s) is not supported by this shard. You will be disconnected.
Console << "Login denied - unsupported client (4.0.0.0 - 4.0.11f). See UOX.INI..." << myendl;
}
}
else if( CliVerMajor == 5 )
{
tSock->ClientType( CV_ML );
if( tSock->ClientVersionSub() < 2 )
{
tSock->ClientVerShort( CVS_500a );
}
else if( CliVerSub < 8 || ( CliVerSub == 8 && CliVerLetter < 2 ))
{
tSock->ClientVerShort( CVS_502a );
}
else if( CliVerSub > 8 || ( CliVerSub == 8 && CliVerLetter >= 2 ))
{
tSock->ClientVerShort( CVS_5082 );
}
if( !cwmWorldState->ServerData()->ClientSupport5000() )
{
tSock->ForceOffline( true );
tSock->IdleTimeout( cwmWorldState->GetUICurrentTime() + 200 );
tSock->SysMessage( 1796, verString ); // Your current client-version (%s) is not supported by this shard. You will be disconnected.
Console << "Login denied - unsupported client (5.0.0.0 - 5.0.9.1). See UOX.INI..." << myendl;
}
}
}
else if( CliVer >= 100663559 && CliVer <= 100666881 )
{
tSock->ClientType( CV_KR2D );
if( CliVerSub >= 5 && CliVerSub <= 14 )
{
tSock->ClientVerShort( CVS_6050 );
}
else
{
if( CliVerSub < 1 || ( CliVerSub == 1 && CliVerLetter < 7 ))
{
tSock->ClientVerShort( CVS_6000 );
}
else if( CliVerSub < 5 )
{
tSock->ClientVerShort( CVS_6017 );
}
if( !cwmWorldState->ServerData()->ClientSupport6000() )
{
tSock->ForceOffline( true );
tSock->IdleTimeout( cwmWorldState->GetUICurrentTime() + 200 );
tSock->SysMessage( 1796, verString ); // Your current client-version (%s) is not supported by this shard. You will be disconnected.
Console << "Login denied - unsupported client (6.0.0.0 - 6.0.4.0). See UOX.INI..." << myendl;
}
}
}
else if( CliVer > 100666881 )
{
if( CliVer <= 117442562 && tSock->ClientType() == CV_SA2D )
{
tSock->ClientType( CV_SA2D );
if( CliVerMajor == 6 && CliVerSub == 14 && CliVerLetter >= 2 )
{
tSock->ClientVerShort( CVS_60142 );
}
else if( CliVerMajor == 7 && CliVerSub < 9 )
{
tSock->ClientVerShort( CVS_7000 );
}
}
else if( CliVer >= 117440814 && tSock->ClientType() == CV_HS2D )
{
tSock->ClientType( CV_HS2D );
if( CliVerSub < 13 )
{
tSock->ClientVerShort( CVS_7090 );
}
else if( CliVerSub < 14 )
{
tSock->ClientVerShort( CVS_70130 );
}
else if( CliVerSub < 16 )
{
tSock->ClientVerShort( CVS_70151 );
}
else if( CliVerSub < 24 )
{
tSock->ClientVerShort( CVS_70160 );
}
else if( CliVerSub < 30 )
{
tSock->ClientVerShort( CVS_70240 );
}
else if( CliVerSub < 33 )
{
tSock->ClientVerShort( CVS_70300 );
}
else if( CliVerSub < 45 )
{
tSock->ClientVerShort( CVS_70331 );
}
else if( CliVerSub < 46)
{
tSock->ClientVerShort( CVS_704565 );
}
else if( CliVerSub < 61 )
{
tSock->ClientType( CV_TOL2D );
tSock->ClientVerShort( CVS_704565 );
}
else
{
tSock->ClientVerShort( CVS_70610 );
}
}
else if( CliVer >= 1000000000 && tSock->ClientType() == CV_HS3D ) // 1124079360 is 4.0.23.1?
{
//UO Enhanced client 4.0.23.1 and above
//should use same version numbering scheme as classic client internally
tSock->ClientType( CV_HS3D );
if( CliVerSub < 13 )
{
tSock->ClientVerShort( CVS_7090 );
}
else if( CliVerSub <= 15 && CliVerLetter == 0 )
{
tSock->ClientVerShort( CVS_70130 );
}
else if( CliVerSub == 15 && CliVerLetter == 1 )
{
tSock->ClientVerShort( CVS_70151 );
}
else if( CliVerSub >= 16 && CliVerSub < 24 )
{
tSock->ClientVerShort( CVS_70160 );
}
else if( CliVerSub >= 24 )
{
tSock->ClientVerShort( CVS_70240 );
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIUpdateRangeChange()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for client view range
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xC8 (Client View Range)
//| Size: 2 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE range
//|
//| Notes
//| Client + Server packet
//| Range: how far away client wants to see (=get send) items/npcs
//| Update: since client 3.0.8o it actually got activated.(in a useful way)
//| When increase/decrease macro send, client sends a 0xc8.
//| If and only if server *relays* the packet (sending back the same data)
//| range stuff gets activated client side.
//| "Greying" has no packet, purely client internal.
//| Minimal value:5, maximal: 18
//o------------------------------------------------------------------------------------------------o
void CPIUpdateRangeChange::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIUpdateRangeChange 0xC8 --> Length: 2 " << std::endl;
}
outStream << "Range : " << static_cast<SI32>( tSock->GetByte( 1 )) << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPIUpdateRangeChange::InternalReset( void )
{
}
CPIUpdateRangeChange::CPIUpdateRangeChange()
{
InternalReset();
}
CPIUpdateRangeChange::CPIUpdateRangeChange( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPIUpdateRangeChange::Receive( void )
{
tSock->Receive( 2, false );
}
bool CPIUpdateRangeChange::Handle( void )
{
// Byte 0 == 0xC8
// Byte 1 == 0x0F (by default) This is the distance, we believe
switch( tSock->ClientType() )
{
case CV_DEFAULT:
case CV_T2A:
case CV_UO3D:
case CV_ML:
case CV_KR2D:
case CV_KR3D:
case CV_SA2D:
case CV_SA3D:
case CV_HS2D:
case CV_HS3D:
tSock->Range( tSock->GetByte( 1 ));
break;
default:
tSock->Range( tSock->GetByte( 1 ));
break;
}
tSock->Send( tSock->Buffer(), 2 ); // we want to echo it back to the client
#if defined( _MSC_VER )
#pragma note( "Flush location" )
#endif
tSock->FlushBuffer();
tSock->CurrcharObj()->Teleport(); // TODO - Could this be an Update() instead of Teleport?
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPILogoutStatus()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for logout requests
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xD1 (Logout Status)
//| Size: 2 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE extra
//|
//| Notes
//| Client + Server packet
//| Client will send this packet without 0x01 Byte when the server sends FLAG & 0x02 in the 0xA9 Packet during logon.
//| Server responds with same packet, plus the 0x01 Byte, allowing client to finish logging out.
//o------------------------------------------------------------------------------------------------o
void CPILogoutStatus::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPILogoutStatus 0xD1 --> Length: 2 " << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPILogoutStatus::InternalReset( void )
{
}
CPILogoutStatus::CPILogoutStatus()
{
InternalReset();
}
CPILogoutStatus::CPILogoutStatus( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPILogoutStatus::Receive( void )
{
tSock->Receive( 2, false );
}
bool CPILogoutStatus::Handle( void )
{
// Byte 0 == 0xD1
// Byte 1 == 0x01 in response to logout request sent by server
// Construct the response, and send it!
CPLogoutResponse logoutResponse( 0x01 );
tSock->Send( &logoutResponse );
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPITips()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with client request for tips/notice windows
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xA7 (Request Tips/Notice)
//| Size: 4 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] last tip #
//| BYTE flag
//| 0x00 - tips window
//| 0x01 - notice window
//o------------------------------------------------------------------------------------------------o
void CPITips::InternalReset( void )
{
}
CPITips::CPITips()
{
InternalReset();
}
CPITips::CPITips( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPITips::Receive( void )
{
tSock->Receive( 4, false );
}
auto CPITips::Handle() -> bool
{
auto tips = FileLookup->FindEntry( "TIPS", misc_def );
if( tips )
{
auto i = static_cast<UI16>( tSock->GetWord( 1 ) + 1 );
if( i == 0 )
{
i = 1;
}
SI32 x = i;
std::string tag, data, sect;
for( tag = tips->First(); !tips->AtEnd(); tag = tips->Next() )
{
if( !tag.empty() && oldstrutil::upper( tag ) == "TIP" )
{
--x;
}
if( x <= 0 )
{
break;
}
}
if( x > 0 )
{
tag = tips->Prev();
}
data = tips->GrabData();
sect = "TIP " + oldstrutil::trim( oldstrutil::removeTrailing( data, "//" ));
tips = FileLookup->FindEntry( sect, misc_def );
if( tips )
{
std::string tipData = "";
for( tag = tips->First(); !tips->AtEnd(); tag = tips->Next() )
{
tipData += tips->GrabData() + " ";
}
CPUpdScroll toSend( 0, static_cast<UI08>( i ));
toSend.AddString( tipData.c_str() );
toSend.Finalize();
tSock->Send( &toSend );
}
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIRename()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to rename character
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x75 (Rename Character)
//| Size: 35 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] id
//| BYTE[30] new name
//o------------------------------------------------------------------------------------------------o
void CPIRename::InternalReset( void )
{
}
CPIRename::CPIRename()
{
InternalReset();
}
CPIRename::CPIRename( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPIRename::Receive( void )
{
tSock->Receive( 0x23, false );
}
bool CPIRename::Handle( void )
{
SERIAL serial = tSock->GetDWord( 1 );
if( serial == INVALIDSERIAL )
return true;
CChar *c = CalcCharObjFromSer( serial );
if( ValidateObject( c ))
{
c->SetName(( char * )&tSock->Buffer()[5] );
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIKeepAlive()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with ping message request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x73 (Ping Message)
//| Size: 2 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE seq
//o------------------------------------------------------------------------------------------------o
CPIKeepAlive::CPIKeepAlive()
{
}
CPIKeepAlive::CPIKeepAlive( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIKeepAlive::Receive( void )
{
tSock->Receive( 2, false );
}
bool CPIKeepAlive::Handle( void )
{
tSock->Send( tSock->Buffer(), 2 );
#if defined( _MSC_VER )
#pragma note( "Flush location" )
#endif
tSock->FlushBuffer();
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIStatusRequest()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet requesting player status
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x34 (Get Player Status)
//| Size: 10 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] pattern (0xedededed)
//| BYTE getType
//| 0x00 - GodClient command
//| 0x04 - Basic Stats (Packet 0x11 Response)
//| 0x05 = Request Skills (Packet 0x3A Response)
//| BYTE[4] playerId
//o------------------------------------------------------------------------------------------------o
CPIStatusRequest::CPIStatusRequest()
{
}
CPIStatusRequest::CPIStatusRequest( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIStatusRequest::Receive( void )
{
tSock->Receive( 10, false );
pattern = tSock->GetDWord( 1 );
getType = tSock->GetByte( 5 );
playerId = tSock->GetDWord( 6 );
}
void CPIStatusRequest::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIStatusRequest 0x34 --> Length: 10" << TimeStamp() << std::endl;
}
outStream << "Pattern : " << pattern << std::endl;
outStream << "Request Type : " << static_cast<SI32>( getType ) << std::endl;
outStream << "PlayerID : " << std::hex << "0x" << playerId << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
bool CPIStatusRequest::Handle( void )
{
if( getType == 4 )
{
if( playerId >= BASEITEMSERIAL )
{
tSock->StatWindow( CalcItemObjFromSer( playerId )); // Item
}
else
{
tSock->StatWindow( CalcCharObjFromSer( playerId )); // Character
}
}
if( getType == 5 )
{
// Check if onSkillGump event exists
CChar *myChar = tSock->CurrcharObj();
std::vector<UI16> scriptTriggers = myChar->GetScriptTriggers();
for( auto i : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( i );
if( toExecute != nullptr )
{
UI08 retStatus = toExecute->OnSkillGump( myChar );
if( retStatus == 0 ) // if it exists and we don't want hard code, return
return true;
}
}
CPSkillsValues toSend;
toSend.NumSkills( ALLSKILLS );
toSend.SetCharacter( *( tSock->CurrcharObj() ));
tSock->Send( &toSend );
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPISpy()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with client hardware details
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xA4 (Spy on Client)
//| Size: 149 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[148] Unknown (previously, this has had info such as your graphics card
//| name, free HD space, number of processors, etc.)
//o------------------------------------------------------------------------------------------------o
CPISpy::CPISpy()
{
}
CPISpy::CPISpy( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPISpy::Receive( void )
{
tSock->Receive( 0x95, false );
}
bool CPISpy::Handle( void )
{
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIKRSeed()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet by KR client with client signal, server responds with
//| encryption request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xFF (KR Client Signal/Seed)
//| Size: 4 bytes
//|
//| Packet Build
//| Byte cmd (0xff)
//| Byte 0xff
//| Byte 0xff
//| Byte 0xff
//o------------------------------------------------------------------------------------------------o
CPIKRSeed::CPIKRSeed()
{
}
CPIKRSeed::CPIKRSeed( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIKRSeed::Receive( void )
{
// This would need to be a server option. No way to tell which client is sending this packet
CPKREncryptionRequest encryptionRequest( tSock );
tSock->Send( &encryptionRequest );
}
bool CPIKRSeed::Handle( void )
{
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIKREncryptionVerification()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet by KR client response to encryption request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xE4 (KR Encryption Verification/Response)
//| Size: 23 bytes
//|
//| Packet Build
//| Byte ID (E4)
//| BYTE[2] Size
//| dword length A
//| byte[lengthA] A
//o------------------------------------------------------------------------------------------------o
CPIKREncryptionVerification::CPIKREncryptionVerification()
{
}
CPIKREncryptionVerification::CPIKREncryptionVerification( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIKREncryptionVerification::Receive( void )
{
tSock->Receive( 23, false );
}
bool CPIKREncryptionVerification::Handle( void )
{
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIGodModeToggle()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to enable God Client features
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x04 (Request God Mode)
//| Size: 2 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE mode (0=off, 1=on)
//o------------------------------------------------------------------------------------------------o
CPIGodModeToggle::CPIGodModeToggle()
{
}
CPIGodModeToggle::CPIGodModeToggle( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIGodModeToggle::Receive( void )
{
tSock->Receive( 2, false );
}
bool CPIGodModeToggle::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
if( ourChar->IsGM() )
{
CPGodModeToggle cpgmt = tSock;
tSock->Send( &cpgmt );
}
else
{
tSock->SysMessage( 1641 ); // You don't have the privs needed to run the Godclient on this server!
Console << "Godclient detected - Account[" << ourChar->GetAccount().wAccountIndex << "] Username[" << ourChar->GetAccount().sUsername << ". Client disconnected!" << myendl;
Network->Disconnect( tSock );
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIDblClick()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with double-click request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x06 (Double Click)
//| Size: 5 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] ID of double clicked object
//|
//| NOTE: bool CPIDblClick::Handle() is implemented in cplayeraction.cpp
//o------------------------------------------------------------------------------------------------o
CPIDblClick::CPIDblClick()
{
}
CPIDblClick::CPIDblClick( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIDblClick::Receive( void )
{
tSock->Receive( 5, false );
objectId = tSock->GetDWord( 1 );
}
void CPIDblClick::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIDblClick 0x06 --> Length: 5" << TimeStamp() << std::endl;
}
outStream << "ClickedID : " << std::hex << "0x" << objectId << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPISingleClick()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with single-click (and All-Names macro) request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x09 (Single Click)
//| Size: 5 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] ID of single clicked object
//|
//| NOTE: bool CPISingleClick::Handle() is implemented in cplayeraction.cpp
//o------------------------------------------------------------------------------------------------o
CPISingleClick::CPISingleClick()
{
}
CPISingleClick::CPISingleClick( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPISingleClick::Receive( void )
{
tSock->Receive( 5, false );
objectId = tSock->GetDWord( 1 );
}
void CPISingleClick::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISingleClick 0x09 --> Length: 5" << TimeStamp() << std::endl;
}
outStream << "ClickedID : " << std::hex << "0x" << objectId << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIMoveRequest()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request for movement
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x02 (Move Request)
//| Size: 7 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE direction
//| BYTE sequence number
//| BYTE[4] fastwalk prevention key
//|
//| Note: sequence number starts at 0, after a reset. However, if 255 is reached,
//| the next seq # is 1, not 0.
//| Fastwalk prevention notes: each 0x02 pops the top element from fastwalk key stack.
//| (0xbf sub1 init. fastwalk stack, 0xbf sub2 pushes an element to stack)
//| If stack is empty key value is 0 (never set keys to 0 in 0xbf sub 1/2)
//| Because client sometimes sends bursts of 0x02<30>s DON<4F>T check for a certain top stack value.
//| The only safe way to detect fastwalk:
//| push a key after EACH x021, 0x22, (=send 0xbf sub 2) check in 0x02 for stack emptyness.
//| If empty -> fastwalk alert.
//| Note that actual key values are irrelevant. (just don<6F>t use 0)
//| Of course without perfect 0x02/0x21/0x22 synch (serverside) it<69>s useless to use fastwalk detection.
//| Last but not least: fastwalk detection adds 9 bytes per step and player!
//o------------------------------------------------------------------------------------------------o
CPIMoveRequest::CPIMoveRequest()
{
}
CPIMoveRequest::CPIMoveRequest( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIMoveRequest::Receive( void )
{
tSock->Receive( 7, false );
}
bool CPIMoveRequest::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
Movement->Walking( tSock, ourChar, tSock->GetByte( 1 ), tSock->GetByte( 2 ));
ourChar->BreakConcentration( tSock );
return true;
}
void CPIMoveRequest::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIMoveRequest 0x02 --> Length: 7" << TimeStamp() << std::endl;
}
outStream << "Direction : " << tSock->GetByte( 1 ) << std::endl;
outStream << "Sequence Number: " << tSock->GetByte( 2 ) << std::endl;
outStream << "FW Prevent Key : " << tSock->GetDWord( 3 ) << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIResyncReq()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with character move acknowledgement/resync request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x22 (Character Move Ack/Resync Request)
//| Size: 3 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE sequence (matches sent sequence)
//| BYTE (0x00)
//o------------------------------------------------------------------------------------------------o
CPIResyncReq::CPIResyncReq()
{
}
CPIResyncReq::CPIResyncReq( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIResyncReq::Receive( void )
{
tSock->Receive( 3, false );
}
bool CPIResyncReq::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
ourChar->Dirty( UT_LOCATION );
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIResMenuChoice()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with resurrection menu choice (old clients)
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x2C (Resurrection Menu Choice)
//| Size: 2 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE action (2=ghost, 1=resurrect, 0=from server)
//|
//| Note: Client and Server Message
//| Note: Resurrection menu has been removed from UO [when?]
//o------------------------------------------------------------------------------------------------o
CPIResMenuChoice::CPIResMenuChoice()
{
}
CPIResMenuChoice::CPIResMenuChoice( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIResMenuChoice::Receive( void )
{
tSock->Receive( 2, false );
}
bool CPIResMenuChoice::Handle( void )
{
UI08 cmd = tSock->GetByte( 1 );
if( cmd == 0x02 )
{
tSock->SysMessage( 766 ); // You are now a ghost.
}
if( cmd == 0x01 )
{
tSock->SysMessage( 767 ); // The connection between your spirit and the world is too weak.
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIAttack()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with attack request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x05 (Attack Request)
//| Size: 5 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] ID to be attacked
//|
//| Note: Client Message
//o------------------------------------------------------------------------------------------------o
CPIAttack::CPIAttack()
{
}
CPIAttack::CPIAttack( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIAttack::Receive( void )
{
tSock->Receive( 5, false );
}
bool CPIAttack::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
ourChar->BreakConcentration( tSock );
Combat->PlayerAttack( tSock );
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPITargetCursor()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with targeting cursor commands
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x6C (Targeting Cursor Commandst)
//| Size: 19 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE type
//| 0x00 = Select Object
//| 0x01 = Select X, Y, Z
//| BYTE[4] cursorID
//| BYTE Cursor Type
//| Always 0 now
//| The following are always sent but are only valid if sent by client
//| BYTE[4] Clicked On ID
//| BYTE[2] click xLoc
//| BYTE[2] click yLoc
//| BYTE unknown (0x00)
//| BYTE click zLoc
//| BYTE[2] model # (if a static tile, 0 if a map/landscape tile)
//|
//| NOTE: The model # shouldn<64>t be trusted.
//| NOTE: bool CPITargetCursor::Handle() in targeting.cpp
//o------------------------------------------------------------------------------------------------o
CPITargetCursor::CPITargetCursor()
{
}
CPITargetCursor::CPITargetCursor( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPITargetCursor::Receive( void )
{
tSock->Receive( 19, false );
}
void CPITargetCursor::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPITargetCursor 0x6C --> Length: 19" << TimeStamp() << std::endl;
}
outStream << "Type : " << static_cast<UI16>( tSock->GetByte( 1 )) << std::endl;
outStream << "CursorID : " << tSock->GetDWord( 2 ) << std::endl;
outStream << "Cursor Type : " << static_cast<UI16>( tSock->GetByte( 6 )) << std::endl;
outStream << "Target ID : " << "0x" << std::hex << tSock->GetDWord( 7 ) << std::endl;
outStream << "Target X : " << std::dec << tSock->GetWord( 11 ) << std::endl;
outStream << "Target Y : " << tSock->GetWord( 13 ) << std::endl;
outStream << "Unknown : " << static_cast<UI16>( tSock->GetByte( 15 )) << std::endl;
outStream << "Target Z : " << static_cast<SI16>( tSock->GetByte( 16 )) << std::endl;
outStream << "Model # : " << "0x" << std::hex << tSock->GetWord( 17 ) << std::endl;
outStream << " Raw dump :" << std::dec << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIEquipItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with targeting cursor commands
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x13 (Drop->Wear Item)
//| Size: 10 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] itemid
//| BYTE layer (see layer list at top)
//| BYTE[4] playerId
//|
//| NOTE: The layer byte should not be trusted.
//| NOTE: bool CPIEquipItem::Handle() implemented in cplayeraction.cpp
//o------------------------------------------------------------------------------------------------o
CPIEquipItem::CPIEquipItem()
{
}
CPIEquipItem::CPIEquipItem( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIEquipItem::Receive( void )
{
tSock->Receive( 10, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIGetItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to pick up item(s)
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x07 (Pick Up Item(s))
//| Size: 7 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] item id
//| BYTE[2] # of items in stack
//|
//| NOTE: Client Message
//| NOTE: The layer byte should not be trusted.
//|
//| NOTE: bool CPIGetItem::Handle() implemented in cplayeraction.cpp
//o------------------------------------------------------------------------------------------------o
CPIGetItem::CPIGetItem()
{
}
CPIGetItem::CPIGetItem( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIGetItem::Receive( void )
{
tSock->Receive( 7, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIDropItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to drop item(s)
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x08 (Drop Item(s))
//| Size: 14 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] item id
//| BYTE[2] X Location
//| BYTE[2] Y Location
//| BYTE Z Location
//| BYTE[4] Move Into Container ID (FF FF FF FF if normal world)
//|
//| NOTE: Client Message
//| NOTE: 3D clients sometimes sends 2 of them (bursts) for ONE drop action.
//| The last one having <20>1<EFBFBD>s in X/Y locs. Be very careful how to handle this
//| odd <20>bursts<74> server side, neither always process, nor always skipping is correct.
//|
//| NOTE: bool CPIDropItem::Handle() implemented in cplayeraction.cpp
//o------------------------------------------------------------------------------------------------o
CPIDropItem::CPIDropItem()
{
}
CPIDropItem::CPIDropItem( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIDropItem::Receive( void )
{
uokrFlag = false;
if( tSock->ClientVerShort() >= CVS_6017 )
{
uokrFlag = true;
}
tSock->Receive(( uokrFlag ? 15 : 14 ), false );
item = tSock->GetDWord( 1 );
x = tSock->GetWord( 5 );
y = tSock->GetWord( 7 );
z = tSock->GetByte( 9 );
if( uokrFlag )
{
gridLoc = tSock->GetByte( 10 );
dest = tSock->GetDWord( 11 );
}
else
{
dest = tSock->GetDWord( 10 );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIGumpMenuSelect()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with gump menu selection data
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xB1 (Gump Menu Selection)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd 0
//| BYTE[2] blockSize 1
//| BYTE[4] id (first Id in 0xb0) 3
//| BYTE[4] gumpId (second Id in 0xb0) 7
//| BYTE[4] buttonId (which button perssed ? 0 if closed) 11
//| BYTE[4] switchcount (response info for radio buttons and checkboxes, any
//| switches listed here are switched on) 15
//| For each switch
//| BYTE[4] SwitchId
//| BYTE[4] textcount (response info for textentries) 19 + switchcount * 4
//| For each textentry
//| BYTE[2] textId
//| BYTE[2] textlength
//| BYTE[length*2] Unicode text (not nullterminated)
//|
//| NOTE: bool CPIGumpMenuSelect::Handle() implemented in gumps.cpp
//o------------------------------------------------------------------------------------------------o
CPIGumpMenuSelect::CPIGumpMenuSelect()
{
}
CPIGumpMenuSelect::CPIGumpMenuSelect( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIGumpMenuSelect::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
SERIAL CPIGumpMenuSelect::ButtonId( void ) const
{
return buttonId;
}
SERIAL CPIGumpMenuSelect::GumpId( void ) const
{
return gumpId;
}
SERIAL CPIGumpMenuSelect::ID( void ) const
{
return id;
}
UI32 CPIGumpMenuSelect::SwitchCount( void ) const
{
return switchCount;
}
UI32 CPIGumpMenuSelect::TextCount( void ) const
{
return textCount;
}
UI32 CPIGumpMenuSelect::SwitchValue( UI32 index ) const
{
if( index >= switchCount )
return INVALIDSERIAL;
// we use a 0 based counter, which means that the first switch is at 19, not 15
return tSock->GetDWord( 19 + 4 * static_cast<size_t>( index ));
}
UI16 CPIGumpMenuSelect::GetTextId( UI08 number ) const
{
if( number >= textCount )
return 0xFFFF;
return tSock->GetWord( textLocationOffsets[number] );
}
UI16 CPIGumpMenuSelect::GetTextLength( UI08 number ) const
{
if( number >= textCount )
return 0xFFFF;
return tSock->GetWord( static_cast<size_t>( textLocationOffsets[number] ) + 2 );
}
std::string CPIGumpMenuSelect::GetTextString( UI08 number ) const
{
if( number >= textCount )
return "";
std::string toReturn = "";
UI16 offset = textLocationOffsets[number] + 4;
for( UI16 counter = 0; counter < GetTextLength( number ); ++counter )
{
toReturn += tSock->GetByte( static_cast<size_t>( offset ) + static_cast<size_t>( counter ) * 2 + 1 );
}
return toReturn;
}
void CPIGumpMenuSelect::BuildTextLocations( void )
{
if( textCount > 0 )
{
size_t i = static_cast<size_t>( textOffset ) + 4; // first is textOffset + 4, to walk past the number of text strings
textLocationOffsets.resize( textCount );
for( size_t j = 0; j < textCount; ++j )
{
textLocationOffsets[j] = static_cast<wchar_t>( i );
i += 2; // skip the text ID
UI16 textLen = tSock->GetWord( i );
i += ( 2 * static_cast<size_t>( textLen ) + 2 ); // we need to add the + 2 for the text len field
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPITalkRequest()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with talk request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x03 (Talk Request)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] length
//| BYTE SpeechType
//| BYTE[2] Color
//| BYTE[2] SpeechFont
//| BYTE[?] msg - Null Terminated (blockSize - 8)
//|
//| The various types of text is as follows:
//| 0x00 - Normal
//| 0x01 - Broadcast/System
//| 0x02 - Emote
//| 0x06 - System/Lower Corner
//| 0x07 - Message/Corner With Name
//| 0x08 - Whisper
//| 0x09 - Yell
//| 0x0A - Spell
//| 0x0D - Guild Chat
//| 0x0E - Alliance Chat
//| 0x0F - Command Prompts
//| 0xC0 - Encoded Commands
//|
//| NOTE: bool CPITalkRequest::Handle() is located in speech.cpp
//o------------------------------------------------------------------------------------------------o
CPITalkRequest::CPITalkRequest()
{
InternalReset();
}
CPITalkRequest::CPITalkRequest( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
}
void CPITalkRequest::InternalReset( void )
{
textColour = 0x3B2;
strLen = 0;
fontUsed = 0;
typeUsed = UNKNOWN;
memset( txtSaid, 0, 4096 );
isUnicode = false;
}
COLOUR CPITalkRequest::TextColour( void ) const
{
return textColour;
}
UI16 CPITalkRequest::Length( void ) const
{
return strLen;
}
UI16 CPITalkRequest::Font( void ) const
{
return fontUsed;
}
SpeechType CPITalkRequest::Type( void ) const
{
return typeUsed;
}
bool CPITalkRequest::IsUnicode( void ) const
{
return isUnicode;
}
std::string CPITalkRequest::TextString( void ) const
{
return txtSaid;
}
char * CPITalkRequest::Text( void ) const
{
return ( char * )&txtSaid[0];
}
bool CPITalkRequest::HandleCommon( void )
{
CChar *ourChar = tSock->CurrcharObj();
ourChar->BreakConcentration( tSock );
ourChar->SetUnicode( false );
CItem *speechItem = ourChar->GetSpeechItem();
UI32 j = 0;
switch( ourChar->GetSpeechMode() )
{
case 3: // Player vendor item pricing
{
// Set default value
j = speechItem->GetBuyValue();
// Grab some references to container speechItem is in, and the root container as well
auto iCont = static_cast<CItem *>( speechItem->GetCont() );
auto rootCont = FindRootContainer( speechItem );
if( !ValidateObject( iCont ) || !ValidateObject( rootCont ))
return false;
try
{
j = static_cast<UI32>( std::stoul( std::string( Text() ), nullptr, 0 ));
if( j > 0 )
{
// If the price set is higher than 0, allow it
speechItem->SetVendorPrice( j );
tSock->SysMessage( 753, j ); // This item's price has been set to %i.
}
else
{
// Price is 0, so user is trying to mark item as 'Not for Sale'
// Only books, keyrings and unlocked containers can be marked as 'Not for Sale' in root vendor pack
// While any item can be marked as such within another container - if that container is for sale
bool isInRoot = ( iCont == rootCont );
auto itemId = speechItem->GetId();
bool canBeNotForSale = true;
if( isInRoot && (( itemId != 0x1769 && itemId != 0x176a && itemId != 0x176b )
&& speechItem->GetType() != IT_BOOK && speechItem->GetType() != IT_CONTAINER ))
{
// In root, not a book, keyring or container. Disallow 'Not for Sale'
canBeNotForSale = false;
}
else if( !isInRoot
|| ( isInRoot && (( itemId == 0x1769 || itemId == 0x176a || itemId == 0x176b )
|| speechItem->GetType() == IT_BOOK || speechItem->GetType() == IT_CONTAINER )))
{
if( speechItem->GetType() == IT_CONTAINER )
{
// Iterate through all items in container and ensure that there are no items marked 'not for sale'
std::vector<CItem *> contItems;
auto iContList = speechItem->GetContainsList();
for( const auto &contItem : iContList->collection() )
{
if( !ValidateObject( contItem ))
continue;
if( contItem->GetVendorPrice() == 0 )
{
// If item is a container, check if _that_ container contains any items with items marked not for sale
if( contItem->GetType() == IT_CONTAINER )
{
std::vector<CItem *> subContItems;
auto iSubContList = contItem->GetContainsList();
for( const auto &subContItem : iSubContList->collection() )
{
if( !ValidateObject( subContItem ))
continue;
if( subContItem->GetVendorPrice() == 0 )
{
// Found an item inside sub-container marked not for sale
canBeNotForSale = false;
break;
}
}
}
else
{
if( contItem->GetVendorPrice() == 0 )
{
// Found item inside container marked not for sale
canBeNotForSale = false;
break;
}
}
}
// Break out of outer for loop as well, if necessary
if( !canBeNotForSale )
{
break;
}
}
}
else if( iCont->GetVendorPrice() == 0 )
{
// Not in root, but parent container is not for sale - disallow 'Not for Sale'
canBeNotForSale = false;
}
}
if( canBeNotForSale )
{
// Item marked as 'Not for Sale'
tSock->SysMessage( 9181, speechItem->GetBuyValue() ); // The item has been marked as 'not for sale'
speechItem->SetVendorPrice( static_cast<UI32>( 0 ));
}
else
{
// Item cannot be marked as 'Not for Sale' - default buy price set instead
tSock->SysMessage( 9178, speechItem->GetBuyValue() ); // The item cannot be marked as 'not for sale'! Default price applied.
if( speechItem->GetBuyValue() > 0 )
{
// Set default buy value as vendor price
speechItem->SetVendorPrice( speechItem->GetBuyValue() );
}
else
{
// No default buy value set on item, setting vendor price to 500 instead
speechItem->SetVendorPrice( static_cast<UI32>( 500 ));
}
}
}
}
catch( ... )
{
tSock->SysMessage( 9177, speechItem->GetBuyValue() ); // Invalid price entered, using default item value of %i
if( speechItem->GetBuyValue() > 0 )
{
// Set default buy value as vendor price
speechItem->SetVendorPrice( speechItem->GetBuyValue() );
}
else
{
// No default buy value set on item, setting vendor price to 500 instead
speechItem->SetVendorPrice( static_cast<UI32>( 500 ));
}
}
tSock->SysMessage( 755 ); // Enter a description for this item.
ourChar->SetSpeechMode( 4 );
break;
}
case 4: // Player vendor item describing
{
std::string speechItemDesc = Text();
if( !speechItemDesc.empty() )
{
speechItem->SetDesc( Text() );
}
else
{
speechItem->SetDesc( speechItem->GetName() );
}
speechItem->Dirty( UT_UPDATE );
tSock->SysMessage( 756, speechItem->GetDesc().c_str() ); // This item is now described as %s,
ourChar->SetSpeechMode( 0 );
break;
}
case 7: // Rune renaming
speechItem->SetName( Text() );
tSock->SysMessage( 757, Text() ); // Rune renamed to: Rune to %s.
ourChar->SetSpeechMode( 0 );
break;
case 6: // Name deed
ourChar->SetName( Text() );
tSock->SysMessage( 758, Text() ); // Your new name is: %s.
ourChar->SetSpeechMode( 0 );
speechItem->Delete();
ourChar->SetSpeechItem( nullptr );
break;
case 5: // Key renaming
case 8: // Sign renaming
speechItem->SetName( Text() );
tSock->SysMessage( 759, Text() ); // Renamed to: %s.
ourChar->SetSpeechMode( 0 );
break;
case 9: // JavaScript speech
{
cScript *myScript = ourChar->GetSpeechCallback();
if( myScript != nullptr )
{
myScript->OnSpeechInput( ourChar, speechItem, Text() );
}
ourChar->SetSpeechMode( 0 );
break;
}
case 1: // GM Page
UI08 a1, a2, a3, a4;
if( GMQueue->GotoPos( GMQueue->FindCallNum( ourChar->GetPlayerCallNum() )))
{
CHelpRequest *tempPage = nullptr;
tempPage = GMQueue->Current();
a1 = ourChar->GetSerial( 1 );
a2 = ourChar->GetSerial( 2 );
a3 = ourChar->GetSerial( 3 );
a4 = ourChar->GetSerial( 4 );
tempPage->Reason( Text() );
tempPage->WhoPaging( ourChar->GetSerial() );
auto temp = oldstrutil::format( "GM Page from %s [%x %x %x %x]: %s", ourChar->GetName().c_str(), a1, a2, a3, a4, tempPage->Reason().c_str() );
bool x = false;
{
for( auto &tmpSock : Network->connClients )
{
CChar *tChar = tmpSock->CurrcharObj();
if( ValidateObject( tChar ))
{
if( tChar->IsGMPageable() )
{
x = true;
tmpSock->SysMessage( temp );
}
}
}
}
if( x )
{
tSock->SysMessage( 363 ); // Your request for assistance has been logged, and will be handled by a GM as soon as possible!
}
else
{
tSock->SysMessage( 364 ); // Your request for assistance has been logged, and will be handled by a GM as soon as possible!
}
}
ourChar->SetSpeechMode( 0 );
break;
case 2: // Counselor Page
a1 = ourChar->GetSerial( 1 );
a2 = ourChar->GetSerial( 2 );
a3 = ourChar->GetSerial( 3 );
a4 = ourChar->GetSerial( 4 );
if( CounselorQueue->GotoPos( CounselorQueue->FindCallNum( ourChar->GetPlayerCallNum() )))
{
CHelpRequest *tempPageCns;
tempPageCns = CounselorQueue->Current();
tempPageCns->Reason( Text() );
tempPageCns->WhoPaging( ourChar->GetSerial() );
auto temp = oldstrutil::format( "Counselor Page from %s [%x %x %x %x]: %s", ourChar->GetName().c_str(), a1, a2, a3, a4, tempPageCns->Reason().c_str() );
bool x = false;
{
for( auto &tmpSock : Network->connClients )
{
CChar *tChar = tmpSock->CurrcharObj();
if( ValidateObject( tChar ))
{
if( tChar->IsGMPageable() )
{
x = true;
tmpSock->SysMessage( temp );
}
}
}
}
if( x )
{
tSock->SysMessage( 360 ); // Your request for info has been logged, and will be handled by a Counselor as soon as possible!
}
else
{
tSock->SysMessage( 361 ); // Your request for info has been logged, and will be handled by a Counselor as soon as possible!
}
}
ourChar->SetSpeechMode( 0 );
break;
case 0: // normal speech
default:
if( ourChar->GetSquelched() && !ourChar->IsGM() )
{
tSock->SysMessage( 760 ); // You have been squelched.
}
else
{
return false;
}
break;
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPITalkRequestAscii()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles ASCII talk request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x03 (Talk Request)
//| Size: Variable
//|
//| See CPITalkRequest() for details
//o------------------------------------------------------------------------------------------------o
CPITalkRequestAscii::CPITalkRequestAscii() : CPITalkRequest()
{
}
CPITalkRequestAscii::CPITalkRequestAscii( CSocket *s ) : CPITalkRequest( s )
{
Receive();
}
void CPITalkRequestAscii::Receive( void )
{
tSock->Receive( 3, false );
UI16 blockLen = tSock->GetWord( 1 );
tSock->Receive( blockLen, false );
strLen = blockLen - 8;
typeUsed = static_cast<SpeechType>( tSock->GetByte( 3 ));
textColour = tSock->GetWord( 4 );
fontUsed = tSock->GetWord( 6 );
strcopy( txtSaid, 4096, ( char * ) & ( tSock->Buffer()[8] ));
}
UnicodeTypes FindLanguage( CSocket *s, UI16 offset );
//o------------------------------------------------------------------------------------------------o
//| Function - CPITalkRequestUnicode()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles Unicode/Ascii speech requests
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xAD (Unicode/Ascii Speech Request)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd 0
//| BYTE[2] blockSize 1
//| BYTE Type 3
//| BYTE[2] Color 4
//| BYTE[2] Font 6
//| BYTE[4] Language (Null Terminated) 8
//| <09>enu<6E> - United States English
//| <09>des<65> - German Swiss
//| <09>dea<65> - German Austria
//| <09>deu<65> - German Germany
//| ... for a complete list see langcode.iff
//| if (Type & 0xc0)
//| BYTE[1,5] Number of distinct Trigger words (NUMWORDS).
//| 12 Bit number, Byte #13 = Bit 11<31>4 of NUMWORDS, Hi-Nibble of Byte #14 (Bit 7<>4) = Bit 0<>3 of NUMWORDS
//| BYTE[1,5] Index to speech.mul
//| 12 Bit number, Low Nibble of Byte #14 (Bits 3<>0) = Bits 11..8 of Index, Byte #15 = Bits 7<>0 of Index
//| UNKNOWNS = ( (NUMWORDS div 2) *3 ) + (NUMWORDS % 2) <20> 1
//| div = Integeger division, % = modulo operation, NUMWORDS >= 1
//| examples: UNKNOWNS(1)=0, UNKNOWNS(2)=2, UNKNOWNS(3)=3, UNKNOWNS(4)=5, UNKNOWNS(5)=6,
//| UNKNOWNS(6)=8, UNKNOWNS(7)=9, UNKNOWNS(8)=11, UNKNOWNS(9)=12
//| BYTE[UNKNOWNS]
//| Idea behind this is getting speech parsing load client side.
//| Thus this contains data OSI server use for easier parsing.
//| It<49>s client side hardcoded and exact details are unkown.
//| BYTE[?] Ascii Msg <20> Null Terminated (blockSize <20> (15+UNKNOWNS) )
//| else
//| BYTE[?][2] Unicode Msg - Null Terminated (blockSize - 12)
//|
//| NOTE1: For pre 2.0.7 clients Type is always < 0xc0
//| NOTE2: Uox based emus convert post 2.0.7 data of this packet to pre 2.0.7
//| data if Type >=0xc0
//| NOTE3: (different view of it)
//| If Mode&0xc0 then there are keywords (from speech.mul) present.
//| Keywords:
//| The first 12 bits = the number of keywords present. The keywords are
//| included right after this, each one is 12 bits also.
//| The keywords are padded to the closest byte. For example, if there are 2
//| keywords, it will take up 5 bytes. 12bits for the number, and 12 bits for each keyword. 12+12+12=36. Which will be padded 4 bits to 40 bits or 5 bytes.
//o------------------------------------------------------------------------------------------------o
CPITalkRequestUnicode::CPITalkRequestUnicode()
{
isUnicode = true;
memset( unicodeTxt, 0, 8192 );
}
CPITalkRequestUnicode::CPITalkRequestUnicode( CSocket *s ) : CPITalkRequest( s )
{
isUnicode = true;
memset( unicodeTxt, 0, 8192 );
Receive();
}
void CPITalkRequestUnicode::Receive( void )
{
tSock->Receive( 3, false );
UI16 blockLen = tSock->GetWord( 1 );
tSock->Receive( blockLen, false );
tSock->ClearTrigWords();
typeUsed = static_cast<SpeechType>( tSock->GetByte( 3 ));
textColour = tSock->GetWord( 4 );
fontUsed = tSock->GetWord( 6 );
if( tSock->Language() == ZERO )
{
tSock->Language( FindLanguage( tSock, 8 ));
}
CChar *mChar = tSock->CurrcharObj();
mChar->SetUnicode( true );
// Check for command word versions of this packet
UI08 *buffer = tSock->Buffer();
UI16 j = 0;
for( j = 8; j <= 10; ++j )
{
langCode[j - 8] = tSock->GetByte( j );
}
langCode[3] = 0;
if(( typeUsed & 0xC0 ) == 0xC0 )
{
SI32 myoffset = 13;
SI32 myj = 12;
size_t numTrigWords = 0;
// Let's keep track of trigger words we've already added, so we don't add same one twice
std::unordered_set<uint16_t> trigWords;
// number of distinct trigger words
numTrigWords = ( static_cast<size_t>( tSock->GetByte( 12 )) << 4 ) + ( static_cast<size_t>( tSock->GetByte( 13 )) >> 4 );
size_t byteNum = 13;
for( size_t tWord = 0; tWord < numTrigWords; )
{
uint16_t word = (( tSock->GetByte( byteNum + tWord ) % 0x10 ) << 8 ) + tSock->GetByte( byteNum + tWord + 1 );
if( trigWords.find( word ) == trigWords.end() )
{
tSock->AddTrigWord( word );
trigWords.insert( word );
}
if( tWord + 2 <= numTrigWords )
{
word = ( tSock->GetByte( byteNum + tWord + 2 ) << 4 ) + ( tSock->GetByte( byteNum + tWord + 3 ) >> 4 );
if( trigWords.find( word ) == trigWords.end() )
{
tSock->AddTrigWord( word );
trigWords.insert( word );
}
}
tWord += 2;
byteNum++;
}
myoffset = 15;
if(( numTrigWords % 2 ) == 1 ) // odd number ?
{
myoffset += ( static_cast<SI32>( numTrigWords ) / 2 ) * 3;
}
else
{
myoffset += (( static_cast<SI32>( numTrigWords ) / 2 ) * 3 ) - 1;
}
myj = 12;
SI32 mysize = blockLen - myoffset;
for( j = 0; j < mysize; ++j )
{
unicodeTxt[j] = buffer[j + myoffset];
}
for( j = 0; j < mysize; ++j )
{ // we would overwrite our buffer, so we need to catch it before we do that.
buffer[myj++] = 0;
buffer[myj++] = unicodeTxt[j];
}
buffer[myj - 1] = 0;
blockLen = (( blockLen - myoffset ) * 2 ) + 12;
PackShort( buffer, 1, blockLen );
// update our unicode text
memcpy( unicodeTxt, &( tSock->Buffer()[12] ), blockLen - 12 );
}
else // NOT a weird configuration, it is unicode however!
{
// starts at 12
memcpy( unicodeTxt, &( tSock->Buffer()[12] ), blockLen - 12 );
}
strLen = 0;
for( j = 13; j < blockLen; j += 2 )
{
txtSaid[( j - 13 ) / 2] = buffer[j];
++strLen;
}
}
std::string CPITalkRequestUnicode::UnicodeTextString( void ) const
{
return unicodeTxt;
}
char * CPITalkRequestUnicode::UnicodeText( void ) const
{
return (char *)unicodeTxt;
}
char * CPITalkRequestUnicode::Language( void ) const
{
return (char *)langCode;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIAllNames3D()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to show names of all nearby characters
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x98 (All Names (3D Client Only))
//| Size: 7 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] blocksize
//| BYTE[4] ID
//| If (server-reply)
//| BYTE[30] name (0 terminated)
//|
//| Only 3D clients send this packet. Server and client packet.
//| Unsure as of when, 2D client added this ability also.
//| Client asks for name of object with ID x. Server has to reply with ID +
//| name. Client automatically knows names of items. Hence it only asks only
//| for NPC/Player names nearby, but shows bars of items plus NPCs.
//| Client request has 7 bytes, server-reply 37. Triggered by Crtl + Shift.
//o------------------------------------------------------------------------------------------------o
CPIAllNames3D::CPIAllNames3D() : CPInputBuffer()
{
}
CPIAllNames3D::CPIAllNames3D( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIAllNames3D::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
bool CPIAllNames3D::Handle( void )
{
SERIAL objSer = tSock->GetDWord( 3 );
CBaseObject *toName = nullptr;
if( objSer >= BASEITEMSERIAL )
{
toName = CalcItemObjFromSer( objSer );
}
else if( objSer != INVALIDSERIAL )
{
toName = CalcCharObjFromSer( objSer );
}
if( ValidateObject( toName ))
{
CPAllNames3D toSend(( *toName ));
tSock->Send( &toSend );
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIGumpChoice()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with choice made in a Gump dialog
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x7D (Make a choice in a Gump)
//| Size: 13 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] dialogID (echoed back from 7C packet)
//| BYTE[2] menuid (echoed back from 7C packet)
//| BYTE[2] 1-based index of choice
//| BYTE[2] model # of choice
//| BYTE[2] color
//|
//| NOTE: void CPGumpChoice::Handle() implemented in gumps.cpp
//o------------------------------------------------------------------------------------------------o
CPIGumpChoice::CPIGumpChoice()
{
}
CPIGumpChoice::CPIGumpChoice( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIGumpChoice::Receive( void )
{
tSock->Receive( 13, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIBuyItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to buy items from vendor
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x3B (Buy Item(s))
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] blockSize
//| BYTE[4] vendorID
//| BYTE flag
//| 0x00 - no items following
//| 0x02 - items following
//| For each item
//| BYTE (0x1A)
//| BYTE[4] itemID (from 3C packet)
//| BYTE[2] # bought
//|
//| NOTE: void CPIBuyItem::Handle() implemented in vendor.cpp
//o------------------------------------------------------------------------------------------------o
CPIBuyItem::CPIBuyItem()
{
}
CPIBuyItem::CPIBuyItem( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIBuyItem::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPISellItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to sell items to vendor
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x9F (Sell Reply)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] blockSize
//| BYTE[4] shopkeeperID
//| BYTE[2] itemCount
//| For each item, a structure containing:
//| BYTE[4] itemID
//| BYTE[2] quantity
//|
//| void CPISellItem::Handle() implemented in vendor.cpp
//o------------------------------------------------------------------------------------------------o
CPISellItem::CPISellItem()
{
}
CPISellItem::CPISellItem( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPISellItem::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIDeleteCharacter()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to delete a specific character
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x83 (Delete Character)
//| Size: 39 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[30] password
//| BYTE[4] charIndex
//| BYTE[4] clientIP
//|
//| void CPIDeleteCharacter::Handle() implemented in pcmanage.cpp
//o------------------------------------------------------------------------------------------------o
CPIDeleteCharacter::CPIDeleteCharacter()
{
}
CPIDeleteCharacter::CPIDeleteCharacter( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIDeleteCharacter::Receive( void )
{
tSock->Receive( 0x27, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPICreateCharacter()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to create a new character
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x00 (Create Character)
//| Packet: 0xF8 (Create Character, clients 7.0.16+)
//| See Create2DCharacter() for details
//|
//| Packet: 0x8D (Create Character, KR + SA 3D clients)
//| See Create3DCharacter() for details
//o------------------------------------------------------------------------------------------------o
CPICreateCharacter::CPICreateCharacter()
{
}
CPICreateCharacter::CPICreateCharacter( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPICreateCharacter::Receive( void )
{
if( tSock->ClientType() == CV_SA3D || tSock->ClientType() == CV_HS3D )
{
Create3DCharacter();
}
else
{
Create2DCharacter();
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - Create3DCharacter()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to create character in 3D clients
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x8D (Create Character, KR + SA 3D clients)
//| Size: 146 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] packet length
//| BYTE[4] pattern1 (0xedededed)
//| BYTE[4] character index/slot
//| BYTE[30] character name
//| BYTE[30] unknown (character password?)
//| BYTE profession
//| BYTE client flags (0x01 = Felucca Facet, 0x02 = Trammel Facet,
//| 0x04 = Ilshenar Facet, 0x08 = Malas Facet, 0x10 = Tokuno Facet,
//| 0x20 = Ter Mur Facet, 0x40 = UO3D Client, 0x80 = Reserved for Facet06,
//| 0x100 = UOTD. Also, starting locations? Hm)
//| BYTE gender (male=0, female=1)
//| BYTE race (human=0, elf=1, gargoyle=2)
//| BYTE strength
//| BYTE dexterity
//| BYTE intelligence
//| BYTE[2] skin color
//| BYTE[4] unknown (0)
//| BYTE[4] unknown (0)
//| BYTE skill1
//| BYTE skill1 value
//| BYTE skill2
//| BYTE skill2 value
//| BYTE skill3
//| BYTE skill3 value
//| BYTE skill4
//| BYTE skill4 value
//| BYTE[25] unknown (0)
//| BYTE unknown (0x0B)
//| BYTE[2] hair color
//| BYTE[2] hair style
//| BYTE unknown (0x0C)
//| BYTE[4] unknown (0)
//| BYTE unknown (0x0D)
//| BYTE[2] shirt color
//| BYTE[2] shirt style/item id
//| BYTE unknown (0x0F)
//| BYTE[2] face color
//| BYTE[2] face style/item id
//| BYTE unknown (0x10)
//| BYTE[2] beard color // the last two might be reversed
//| BYTE[2] beard style/item id*/
//o------------------------------------------------------------------------------------------------o
void CPICreateCharacter::Create3DCharacter( void )
{
if( ValidateObject( tSock->CurrcharObj() ))
{
Console.Error( oldstrutil::format( "CreateCharacter packet 0x8D detected for socket with pre-existing character (%i) attached. Disconnecting socket as safeguard against corruption!", tSock->CurrcharObj()->GetSerial() ));
Network->Disconnect( tSock );
return;
}
tSock->Receive( 146, false );
Network->Transfer( tSock );
packetSize = tSock->GetWord( 1 ); // Byte[2]
pattern1 = tSock->GetDWord( 3 ); // Byte[4]
slot = tSock->GetDWord( 7 ); // Byte[4]
profession = tSock->GetByte( 71 ); // Byte[1]
clientFlags = tSock->GetByte( 72 ); // Byte[1]
sex = tSock->GetByte( 73 ); // Byte[1]
race = tSock->GetByte( 74 ); // Byte[1]
str = tSock->GetByte( 75 ); // Byte[1]
dex = tSock->GetByte( 76 ); // Byte[1]
intel = tSock->GetByte( 77 ); // Byte[1]
skinColour = tSock->GetWord( 78 ); // Byte[2]
unknown1 = tSock->GetDWord( 80 ); // Byte[4]
unknown2 = tSock->GetDWord( 84 ); // Byte[4]
skill[0] = tSock->GetByte( 88 ); // Byte[1]
skillValue[0] = tSock->GetByte( 89 ); // Byte[1]
skill[1] = tSock->GetByte( 90 ); // Byte[1]
skillValue[1] = tSock->GetByte( 91 ); // Byte[1]
skill[2] = tSock->GetByte( 92 ); // Byte[1]
skillValue[2] = tSock->GetByte( 93 ); // Byte[1]
UI08 byteNum = 94;
if( tSock->ClientType() >= CV_HS2D && tSock->ClientVersionSub() >= 16 )
{
skill[3] = tSock->GetByte( 94 ); // Byte[1]
skillValue[3] = tSock->GetByte( 95 ); // Byte[1]
byteNum = 96;
}
unknown4 = tSock->GetByte( static_cast<size_t>( byteNum ) +25 ); // Byte[1] // 121
hairColour = tSock->GetWord( static_cast<size_t>( byteNum ) +26 ); // Byte[2] // 122
hairStyle = tSock->GetWord( static_cast<size_t>( byteNum ) +28 ); // Byte[2] // 124
unknown5 = tSock->GetByte( static_cast<size_t>( byteNum ) +30 ); // Byte[1]
unknown6 = tSock->GetDWord( static_cast<size_t>( byteNum ) +31 ); // Byte[4]
unknown7 = tSock->GetByte( static_cast<size_t>( byteNum ) +35 ); // Byte[1]
shirtColour = tSock->GetWord( static_cast<size_t>( byteNum ) +36 ); // Byte[2]
shirtId = tSock->GetWord( static_cast<size_t>( byteNum ) +38 ); // Byte[2]
unknown8 = tSock->GetByte( static_cast<size_t>( byteNum ) +40 ); // Byte[1]
faceColour = tSock->GetWord( static_cast<size_t>( byteNum ) +41 ); // Byte[2]
faceId = tSock->GetWord( static_cast<size_t>( byteNum ) +43 ); // Byte[2]
unknown9 = tSock->GetByte( static_cast<size_t>( byteNum ) +45 ); // Byte[1]
facialHairColour= tSock->GetWord( static_cast<size_t>( byteNum ) +46 ); // Byte[2]
facialHair = tSock->GetWord( static_cast<size_t>( byteNum ) +48 ); // Byte[2]
memcpy( charName, &tSock->Buffer()[11], 30 ); // Byte[30]
memcpy( password, &tSock->Buffer()[41], 30 ); // Byte[30]
memcpy( unknown3, &tSock->Buffer()[96], 25 ); // Byte[25]
}
//o------------------------------------------------------------------------------------------------o
//| Function - Create2DCharacter()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to create character in 2D clients
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x00 (Create Character)
//| Packet: 0xF8 (Create Character, clients 7.0.16+)
//| Size: 104 or 106 Bytes
//|
//| Packet Build
//| BYTE cmd 0
//| BYTE[4] pattern1 (0xedededed) 1
//| BYTE[4] pattern2 (0xffffffff) 5
//| BYTE pattern3 (0x00) 9
//| BYTE[30] char name 10
//| BYTE[30] char password 40
//| BYTE sex (0=male, 1=female) 70
//| BYTE str 71
//| BYTE dex 72
//| BYTE int 73
//| BYTE skill1 74
//| BYTE skill1value 75
//| BYTE skill2 76
//| BYTE skill2value 77
//| BYTE skill3 78
//| BYTE skill3value 79
//| if clientVer > 7.0.16
//| BYTE skill4 80
//| BYTE skill4value 81
//| BYTE[2] skinColor 80/82
//| BYTE[2] hairStyle 82/84
//| BYTE[2] hairColor 84/86
//| BYTE[2] facial hair 86/88
//| BYTE[2] facial hair color 88/90
//| BYTE[2] location # from starting list 90/92
//| BYTE[2] unknown1 92/94
//| BYTE[2] slot 94/96
//| BYTE[4] clientIP 96/98
//| BYTE[2] shirt color 100/102
//| BYTE[2] pants color 102/104
//o------------------------------------------------------------------------------------------------o
void CPICreateCharacter::Create2DCharacter( void )
{
if( ValidateObject( tSock->CurrcharObj() ))
{
Console.Error( oldstrutil::format( "CreateCharacter packet 0x00 or 0xF8 detected for socket with pre-existing character (%i) attached. Disconnecting socket as safeguard against corruption!", tSock->CurrcharObj()->GetSerial() ));
Network->Disconnect( tSock );
return;
}
if( tSock->ClientType() >= CV_HS2D && tSock->ClientVersionSub() >= 16 )
{
tSock->Receive( 106, true );
}
else
{
tSock->Receive( 104, false );
}
Network->Transfer( tSock );
pattern1 = tSock->GetDWord( 1 );
pattern2 = tSock->GetDWord( 5 );
pattern3 = tSock->GetByte( 9 );
profession = tSock->GetByte( 54 ); // Byte[54]
// Fix for ClassicUO client
if( profession == 255 )
{
profession = 0;
}
sex = tSock->GetByte( 70 );
str = tSock->GetByte( 71 );
dex = tSock->GetByte( 72 );
intel = tSock->GetByte( 73 );
skill[0] = tSock->GetByte( 74 );
skillValue[0] = tSock->GetByte( 75 );
skill[1] = tSock->GetByte( 76 );
skillValue[1] = tSock->GetByte( 77 );
skill[2] = tSock->GetByte( 78 );
skillValue[2] = tSock->GetByte( 79 );
UI08 byteNum = 80;
if( tSock->ClientType() >= CV_HS2D && tSock->ClientVersionSub() >= 16 )
{
skill[3] = tSock->GetByte( 80 );
skillValue[3] = tSock->GetByte( 81 );
byteNum = 82;
}
skinColour = tSock->GetWord( byteNum );
hairStyle = tSock->GetWord( static_cast<size_t>( byteNum ) + 2 );
hairColour = tSock->GetWord( static_cast<size_t>( byteNum ) + 4 );
facialHair = tSock->GetWord( static_cast<size_t>( byteNum ) + 6 );
facialHairColour= tSock->GetWord( static_cast<size_t>( byteNum ) + 8 );
unknown = tSock->GetByte( static_cast<size_t>( byteNum ) + 10 );
locationNumber = tSock->GetByte( static_cast<size_t>( byteNum ) + 11 );
slot = tSock->GetDWord( static_cast<size_t>( byteNum ) + 12 );
ipAddress = tSock->GetDWord( static_cast<size_t>( byteNum ) + 16 );
shirtColour = tSock->GetWord( static_cast<size_t>( byteNum ) + 20 );
pantsColour = tSock->GetWord( static_cast<size_t>( byteNum ) + 22 );
memcpy( charName, &tSock->Buffer()[10], 30 );
memcpy( password, &tSock->Buffer()[40], 30 ); // Does this really have anything to do with passwords?
}
void CPICreateCharacter::Log( std::ofstream &outStream, bool fullHeader )
{
if( tSock->ClientType() == CV_SA3D || tSock->ClientType() == CV_HS3D )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPICreateCharacter 0x8D --> Length: 146" << TimeStamp() << std::endl;
}
outStream << "Pattern1 : " << pattern1 << std::endl;
outStream << "Slot : " << static_cast<SI32>( slot ) << std::endl;
outStream << "Character Name : " << charName << std::endl;
outStream << "Password : " << password << std::endl;
outStream << "Profession : " << static_cast<SI32>( profession ) << std::endl;
outStream << "Client Flags : " << static_cast<SI32>( clientFlags ) << std::endl;
outStream << "Sex : " << static_cast<SI32>( sex ) << std::endl;
outStream << "Race : " << static_cast<SI32>( race ) << std::endl;
outStream << "Strength : " << static_cast<SI32>( str ) << std::endl;
outStream << "Dexterity : " << static_cast<SI32>( dex ) << std::endl;
outStream << "Intelligence : " << static_cast<SI32>( intel ) << std::endl;
outStream << "Skin Colour : " << std::hex << skinColour << std::dec << std::endl;
outStream << "Unknown1 : " << static_cast<SI32>( unknown1 ) << std::endl;
outStream << "Unknown2 : " << static_cast<SI32>( unknown2 ) << std::endl;
outStream << "Skills : " << static_cast<SI32>( skill[0] ) << " " << static_cast<SI32>( skill[1] ) << " " << static_cast<SI32>( skill[2] ) << " " << static_cast<SI32>( skill[3] ) << std::endl;
outStream << "Skills Values : " << static_cast<SI32>( skillValue[0] ) << " " << static_cast<SI32>( skillValue[1] ) << " " << static_cast<SI32>( skillValue[2] ) << " " << static_cast<SI32>( skillValue[3] ) << std::endl;
SI32 temp1;
for( temp1 = 0; temp1 < 25; temp1++ )
{
outStream << "Unknown3 index " << temp1 << " is hex value: " << std::hex << static_cast<UI32>( unknown3[temp1] ) << std::endl;
}
outStream << "Unknown4 : " << static_cast<SI32>( unknown4 ) << std::endl;
outStream << "Hair Colour : " << std::hex << hairColour << std::dec << std::endl;
outStream << "Hair Style : " << std::hex << hairStyle << std::dec << std::endl;
outStream << "Unknown5 : " << static_cast<SI32>( unknown5 ) << std::endl;
outStream << "Unknown6 : " << static_cast<SI32>( unknown6 ) << std::endl;
outStream << "Unknown7 : " << static_cast<SI32>( unknown7 ) << std::endl;
outStream << "Shirt Colour : " << std::hex << shirtColour << std::dec << std::endl;
outStream << "Shirt ID : " << std::hex << shirtId << std::dec << std::endl;
outStream << "Unknown8 : " << static_cast<SI32>( unknown8 ) << std::endl;
outStream << "Face Colour : " << std::hex << faceColour << std::dec << std::endl;
outStream << "Face ID : " << std::hex << faceId << std::dec << std::endl;
outStream << "Unknown9 : " << static_cast<SI32>( unknown9 ) << std::endl;
outStream << "Facial Hair : " << std::hex << facialHair << std::dec << std::endl;
outStream << "Facial Hair Colour: " << std::hex << facialHairColour << std::dec << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
else
{
if( fullHeader )
{
if( tSock->GetByte( 0 ) == 0xF8 )
{
outStream << "[RECV]Packet : CPICreateCharacter 0xF8 --> Length: 106" << TimeStamp() << std::endl;
}
else
{
outStream << "[RECV]Packet : CPICreateCharacter 0x00 --> Length: 104" << TimeStamp() << std::endl;
}
}
outStream << "Pattern1 : " << pattern1 << std::endl;
outStream << "Pattern2 : " << pattern2 << std::endl;
outStream << "Pattern3 : " << static_cast<SI32>( pattern3 ) << std::endl;
outStream << "Character Name : " << charName << std::endl;
outStream << "Password : " << password << std::endl;
outStream << "Sex : " << static_cast<SI32>( sex ) << std::endl;
outStream << "Strength : " << static_cast<SI32>( str ) << std::endl;
outStream << "Dexterity : " << static_cast<SI32>( dex ) << std::endl;
outStream << "Intelligence : " << static_cast<SI32>( intel ) << std::endl;
outStream << "Skills : " << static_cast<SI32>( skill[0] ) << " " << static_cast<SI32>( skill[1] ) << " " << static_cast<SI32>( skill[2] ) << std::endl;
outStream << "Skills Values : " << static_cast<SI32>( skillValue[0] ) << " " << static_cast<SI32>( skillValue[1] ) << " " << static_cast<SI32>( skillValue[2] ) << std::endl;
outStream << "Skin Colour : " << std::hex << skinColour << std::dec << std::endl;
outStream << "Hair Style : " << std::hex << hairStyle << std::dec << std::endl;
outStream << "Hair Colour : " << std::hex << hairColour << std::dec << std::endl;
outStream << "Facial Hair : " << std::hex << facialHair << std::dec << std::endl;
outStream << "Facial Hair Colour: " << std::hex << facialHairColour << std::dec << std::endl;
outStream << "Location Number: " << locationNumber << std::endl;
outStream << "Unknown : " << unknown << std::endl;
outStream << "Slot : " << static_cast<SI32>( slot ) << std::endl;
outStream << "IP Address : " << ipAddress << std::endl;
outStream << "Shirt Colour : " << std::hex << shirtColour << std::dec << std::endl;
outStream << "Pants Colour : " << std::hex << pantsColour << std::dec << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
}
// void CPICreateCharacter::Handle() implimented in pcmanage.cpp
//o------------------------------------------------------------------------------------------------o
//| Function - CPIPlayCharacter()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to login with a specific character
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x5D (Login Character)
//| Size: 73 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] pattern1 (0xedededed)
//| BYTE[30] char name (0 terminated)
//| BYTE[33] unknown, mostly 0<>s (Byte# 0x27, 0x28, 0x30 seem to be the only
//| non 0<>s of these 33 bytes. Perhaps password data that<61>s not send anymore)
//| BYTE slot choosen (0-based)
//| BYTE[4] clientIP
//|
//| void CPIPlayCharacter::Handle() implimented in pcmanage.cpp
//o------------------------------------------------------------------------------------------------o
CPIPlayCharacter::CPIPlayCharacter()
{
}
CPIPlayCharacter::CPIPlayCharacter( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIPlayCharacter::Receive( void )
{
tSock->Receive( 0x49, false );
Network->Transfer( tSock );
// Let's store our data more meaningfully
pattern = tSock->GetDWord( 1 );
slotChosen = tSock->GetByte( 68 );
ipAddress = tSock->GetDWord( 69 );
//Reset client-info stored in account, so it's ready for login with a new client
//NOTE: We can't do this until after character-selection, in case user
//goes back to server-selection instead - in which case the socket's clientversion info is lost!
// tSock->AcctNo( AB_INVALID_ID );
CAccountBlock_st& actbTemp = tSock->GetAccount();
actbTemp.dwLastClientVer = 0;
actbTemp.dwLastClientType = 0;
actbTemp.dwLastClientVerShort = 0;
memcpy( charName, &tSock->Buffer()[5], 30 );
memcpy( unknown, &tSock->Buffer()[35], 33 );
}
void CPIPlayCharacter::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIPlayCharacter 0x5D --> Length: 73" << TimeStamp() << std::endl;
}
outStream << "Pattern1 : " << pattern << std::endl;
outStream << "Char name : " << charName << std::endl;
outStream << "Slot chosen : " << static_cast<SI32>( slotChosen ) << std::endl;
outStream << "Client IP : " << ipAddress << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIGumpInput()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with reply data for gump text entry dialog
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xAC (Gump Text Entry Dialog Reply)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] length
//| BYTE[4] ID
//| BYTE type
//| BYTE index
//| BYTE[3] unk
//| BYTE[?] reply
//|
//| void CPIGumpInput::Handle() implimented in gumps.cpp
//o------------------------------------------------------------------------------------------------o
CPIGumpInput::CPIGumpInput()
{
}
CPIGumpInput::CPIGumpInput( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIGumpInput::Receive( void )
{
tSock->Receive( 3, false );
UI16 length = tSock->GetWord( 1 );
tSock->Receive( length, false );
id = tSock->GetDWord( 3 );
type = tSock->GetByte( 7 );
index = tSock->GetByte( 8 );
unk[0] = tSock->GetByte( 9 ); // 1 == ok, 0 == cancel
unk[1] = tSock->GetByte( 10 );
unk[2] = tSock->GetByte( 11 ); // Length of reply
reply = ( char * ) & ( tSock->Buffer()[12] );
// If no value was actually returned, treat it as a cancel!
if( unk[0] == 1 && reply == "" )
{
unk[0] = 0;
}
}
UI32 CPIGumpInput::ID( void ) const
{
return id;
}
UI08 CPIGumpInput::Type( void ) const
{
return type;
}
UI08 CPIGumpInput::Index( void ) const
{
return index;
}
UI08 CPIGumpInput::Unk( SI32 offset ) const
{
assert( offset >= 0 && offset <=2 );
return unk[offset];
}
const std::string CPIGumpInput::Reply( void ) const
{
return reply;
}
void CPIGumpInput::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIGumpInput 0xAC --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "ID : " << id << std::endl;
outStream << "Type : " << static_cast<SI32>( type ) << std::endl;
outStream << "Index : " << static_cast<SI32>( index ) << std::endl;
outStream << "Unknown : " << static_cast<SI32>( unk[0] ) << " " << static_cast<SI32>( unk[1] ) << " " << static_cast<SI32>( unk[2] ) << std::endl;
outStream << "Reply : " << reply << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIHelpRequest()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with help request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x9B (Request Help)
//| Size: 258 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[257] (0x00)
//|
//| void CPIHelpRequest::Handle() implimented in gumps.cpp
//o------------------------------------------------------------------------------------------------o
CPIHelpRequest::CPIHelpRequest( CSocket *s, UI16 menuVal ) : CPInputBuffer( s )
{
menuNum = menuVal;
}
CPIHelpRequest::CPIHelpRequest( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIHelpRequest::Receive( void )
{
tSock->Receive( 0x102, false );
menuNum = 1;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPITradeMessage()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request for secure trading window
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x6F (Secure Trading)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] blockSize
//| BYTE action
//| BYTE[4] id1
//| BYTE[4] id2
//| BYTE[4] id3
//| BYTE nameFollowing (0 or 1)
//| If (nameFollowing = 1)
//| BYTE[?] charName
//|
//| void CPITradeMessage::Handle() implimented in gumps.cpp
//o------------------------------------------------------------------------------------------------o
CPITradeMessage::CPITradeMessage()
{
}
CPITradeMessage::CPITradeMessage( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPITradeMessage::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIDyeWindow()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to open dye window
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x95 (Dye Window)
//| Size: 9 bytes
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] itemID of dye target
//| BYTE[2] ignored on send, model on return
//| BYTE[2] model on send, color on return (default on server send is 0x0FAB)
//o------------------------------------------------------------------------------------------------o
CPIDyeWindow::CPIDyeWindow()
{
}
CPIDyeWindow::CPIDyeWindow( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIDyeWindow::Receive( void )
{
tSock->Receive( 9, false );
changing = tSock->GetDWord( 1 );
modelId = tSock->GetWord( 5 );
newValue = tSock->GetWord( 7 );
}
void CPIDyeWindow::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIDyeWindow 0x95 --> Length: 9" << TimeStamp() << std::endl;
}
outStream << "ItemID : " << changing << std::endl;
outStream << "Model : " << modelId << std::endl;
outStream << "Colour : " << newValue << std::endl;
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
bool CPIDyeWindow::Handle( void )
{
if( changing >= BASEITEMSERIAL )
{
CItem *i = CalcItemObjFromSer( changing );
if( ValidateObject( i ))
{
if( !tSock->DyeAll() )
{
if( newValue < 0x0002 || newValue > 0x03E9 )
{
newValue = 0x03E9;
}
}
i->SetColour( newValue );
}
}
else
{
CChar *mChar = tSock->CurrcharObj();
CChar *c = CalcCharObjFromSer( changing );
if( ValidateObject( c ))
{
if( !mChar->IsGM() )
return true;
UI16 body = c->GetId();
SI32 b = newValue&0x4000;
if((( newValue >> 8 ) < 0x80 ) && (( body >= 0x0190 && body <= 0x0193 ) || ( body >= 0x025D && body <= 0x0260 ) ||
( body >= 0x00B7 && body <= 0x00BA ) || (body == 0x02EE || body == 0x02EF )))
{
newValue += 0x8000;
}
if( b == 16384 && ( body >= 0x0190 && body <= 0x03e1 ))
{
newValue = 0xF000; // but assigning the only "transparent" value that works, namly semi-trasnparency.
}
if( newValue != 0x8000 )
{
c->SetSkin( newValue );
c->SetOrgSkin( newValue );
}
}
}
Effects->PlaySound( tSock, 0x023E, true );
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIMsgBoardEvent()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for bulletin board interaction
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x71 (Bulletin Board Message)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] len
//| BYTE subcmd
//| BYTE[len-4] submessage
//|
//| bool CPIMsgBoardEvent::Handle() implemented in msgboard.cpp
//o------------------------------------------------------------------------------------------------o
CPIMsgBoardEvent::CPIMsgBoardEvent()
{
}
CPIMsgBoardEvent::CPIMsgBoardEvent( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
CPIMsgBoardEvent::CPIMsgBoardEvent( CSocket *s, [[maybe_unused]] bool receive ) : CPInputBuffer( s )
{
}
void CPIMsgBoardEvent::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPINewBookHeader()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for book header
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xD4 (Book Header (New))
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] length
//| BYTE[4] book ID
//| BYTE flag1 (0 when sent by client)
//| BYTE flag2 (0 when sent by client)
//| BYTE[2] number of pages (0 when sent by client)
//| BYTE[2] length of author (0 terminator included)
//| BYTE[?][2] author (Unicode, 0 terminated)
//| BYTE[2] length of title (0 terminator included)
//| BYTE[?][2] title (Unicode, 0 terminated)
//|
//| bool CPINewBookHeader::Handle() implemented in books.cpp
//o------------------------------------------------------------------------------------------------o
CPINewBookHeader::CPINewBookHeader()
{
}
CPINewBookHeader::CPINewBookHeader( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPINewBookHeader::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIToolTipRequestAoS()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to show gump entry tooltip
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xD6 (Mega Cliloc)
//| Size: Variable
//|
//| Packet Build
//| Client Request
//| BYTE[1] 0xD6
//| BYTE[2] Length
//| Loop for each serial being sent to request tooltip detail about
//| BYTE[4] Serial
//|
//| Server Response
//| BYTE[1] 0xD6
//| BYTE[2] Length
//| BYTE[2] 0x0001
//| BYTE[4] Serial of item/creature
//| BYTE[2] 0x0000
//| BYTE[4] Serial of item/creature in all tests. This could be the serial of the item the entry to appear over.
//|
//| Loop of all the item/creature's properties to display in the order to display them. The name is always the first entry.
//| BYTE[4] Cliloc ID
//| BYTE[2] Length of (if any) Text to add into/with the cliloc
//| BYTE[?] Unicode text to be added into the cliloc. Not sent if Length of text above is 0
//|
//| BYTE[4] 00000000 - Sent as end of packet/loop
//o------------------------------------------------------------------------------------------------o
CPIToolTipRequestAoS::CPIToolTipRequestAoS() : getSer( INVALIDSERIAL )
{
}
CPIToolTipRequestAoS::CPIToolTipRequestAoS( CSocket *s ) : CPInputBuffer( s ), getSer( INVALIDSERIAL )
{
Receive();
}
void CPIToolTipRequestAoS::Receive( void )
{
tSock->Receive( 3, false );
UI16 blockLen = tSock->GetWord( 1 );
if( blockLen == 0 || ( blockLen - 3 ) % 4 != 0 )
return;
tSock->Receive( blockLen, false );
UI16 objCount = ( blockLen - 3 ) / 4;
size_t offset = 3;
for( UI16 i = 0; i < objCount; i++ )
{
getSer = tSock->GetDWord( offset + ( i * 4 ));
// Only send tooltip if server feature for tooltips is enabled
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_AOS ))
{
CPToolTip tSend( getSer, tSock );
tSock->Send( &tSend );
}
}
}
bool CPIToolTipRequestAoS::Handle( void )
{
return true;
}
void CPIToolTipRequestAoS::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : 0xD6 Tooltip Request --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, true );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIBookPage()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for book pages
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0x66 (Books (Pages))
//| Size: Variable
//|
//| Packet Build
//| BYTE 0x66
//| BYTE[2] Length
//| BYTE[4] Book Serial
//| BYTE[2] Page Count
//| Page Loop
//| BYTE[2] Page #
//| BYTE[2] Line Count
//| Line Loop ( if used old packet 0x93 to open the book )
//| BYTE[var] Line Text, Null Terminated
//| Line Loop ( if used new packet 0xD4 to open the book )
//| BYTE[var][2] Unicode Line Text, Null Terminated
//|
//| bool CPIBookPage::Handle() implemented in books.cpp
//o------------------------------------------------------------------------------------------------o
CPIBookPage::CPIBookPage()
{
}
CPIBookPage::CPIBookPage( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIBookPage::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIMetrics()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with client hardware / metrics
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xD9 (Spy on Client)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE unknown (02) Always 0x02 in my tests
//| BYTE[4] Unique Instance ID of UO
//| BYTE[4] OS Major
//| BYTE[4] OS Minor
//| BYTE[4] OS Revision
//| BYTE CPU Manufacturer
//| BYTE[4] CPU Family
//| BYTE[4] CPU Model
//| BYTE[4] CPU Clock Speed
//| BYTE CPU Quantity
//| BYTE[4] Memory
//| BYTE[4] Screen Width
//| BYTE[4] Screen Height
//| BYTE[4] Screen Depth
//| BYTE[2] Direct X Version
//| BYTE[2] Direct X Minor
//| BYTE[76?] Video Card Description
//| BYTE[4] Video Card Vendor ID
//| BYTE[4] Video Card Device ID
//| BYTE[4] Video Card Memory
//| BYTE Distribution
//| BYTE Clients Running
//| BYTE Clients Installed
//| BYTE Partial Insstalled
//| BYTE Unknown
//| BYTE[4] Language Code
//| BYTE[67] Unknown Ending
//|
//| Older clients, from mid-late 2003 was set at 149 bytes length, whereas newest
//| clients 4.01+ are set to 199 ??
//o------------------------------------------------------------------------------------------------o
CPIMetrics::CPIMetrics()
{
}
CPIMetrics::CPIMetrics( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIMetrics::Receive( void )
{
tSock->Receive( 0x010C, false );
}
bool CPIMetrics::Handle()
{
// we're going to silently swallow this packet, really
return true;
}
void CPIMetrics::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIMetrics 0xD9 --> Length: 268" << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
// UI16 subCmd;
// UI08 subSubCmd;
void PaperDoll( CSocket *s, CChar *pdoll );
bool BuyShop( CSocket *s, CChar *c );
CPISubcommands::CPISubcommands() : skipOver( false ), subPacket( nullptr )
{
}
CPISubcommands::CPISubcommands( CSocket *s ) : CPInputBuffer( s )
{
skipOver = false;
subPacket = nullptr;
Receive();
}
// Subcommand 1: Initialize Fast Walk Prevention
// BYTE[4] key1
// BYTE[4] key2
// BYTE[4] key3
// BYTE[4] key4
// BYTE[4] key5
// BYTE[4] key6
// Subcommand 2: Add key to Fast Walk Stack
// BYTE[4] newkey
// Subcommand 5: Screen Size
// BYTE[2] unknown1, always 0?
// BYTE[2] X
// BYTE[2] Y
// BYTE[2] unknown2
// Subcommand 8: Set cursor hue / Set MAP
// BYTE hue (0 = Felucca, unhued / BRITANNIA map. 1 = Trammel, hued gold / BRITANNIA map, 2 = (switch to) ILSHENAR map)
// Subcommand 0x0a: wrestling stun
// Sent by using the client's Wrestle Stun Macro key in Options. This is no longer used since AoS was introduced. The Macro selection that used it was removed.
// Subcommand 0x0b: Client Language
// BYTE[3] language (ENU, for English)
// Subcommand 0x0c: Closed Status Gump
// BYTE[4] id (character id)
// Subcommand 0x0e: 3D Client Action
// BYTE[4] Animation ID. See Notes for list.
// Subcommand 0x0f: unknown , send once at login
// BYTE[5] unknown (0a 00 00 00 07) here
// Subcommand 0x10: unknown, related to 0xD6 Mega Cliloc somehow
// BYTE[4] Item ID
// BYTE[4] Unknown
// Subcommand 0x13: Request popup menu
// BYTE[4] id (character id)
// Subcommand 0x14: Display Popup menu
// BYTE[2] Unknown(always 00 01)
// BYTE[4] Serial
// BYTE Number of entries in the popup
// For each Entry
// <09> BYTE[2] Entry Tag (this will be returned by the client on selection)
// <09> BYTE[2] Text ID ID is the file number for intloc#.language e.g intloc6.enu and the index into that
// <09> BYTE[2] Flags 0x01 = locked, 0x02 = arrow, 0x20 = color
// <09> If (Flags &0x20)
// <09> BYTE[2] color; // rgb 1555 color (ex, 0 = transparent, 0x8000 = solid black, 0x1F = blue, 0x3E0 = green, 0x7C00 = red)
// Subcommand 0x15: Popup Entry Selection
// BYTE[4] Character ID
// BYTE[2] Entry Tag for line selected provided in subcommand 0x14
// Subcommand 0x17: Codex of wisdom
// BYTE unknown, always 1. if not 1, packet seems to have no effect
// BYTE[4] msg number
// BYTE presentation (0: <20>?<3F> flashing, 1: directly opening)
// Subcommand 0x18: Enable map-diff (files)
// BYTE[4] Number of maps
// For each map
// <09> BYTE[4] Number of map patches in this map
// <09> BYTE[4] Number of static patches in this map
// Subcommand: 0x19: Extended stats
// BYTE type // always 2 ? never seen other value
// BYTE[4] serial
// BYTE unknown // always 0 ?
// BYTE lockBits // Bits: XXSS DDII (s=strength, d=dex, i=int), 0 = up, 1 = down, 2 = locked
// Subcommand: 0x1a: Extended stats
// BYTE stat // 0: str, 1: dex, 2:int
// BYTE status // 0: up, 1:down, 2: locked
// Subcommand 0x1b: New Spellbook
// BYTE[2] unknown, always 1
// BYTE[4] Spellbook serial
// BYTE[2] Item Id
// BYTE[2] scroll offset // 1==regular, 101=necro, 201=paladin
// BYTE[8] spellbook content // first bit of first byte = spell #1, second bit of first byte = spell #2, first bit of second byte = spell #8, etc
// Subcommand 0x1c: Spell selected, client side
// BYTE[2] unknown, always 2
// BYTE[2] selected spell(0-indexed)+scroll offset from sub 0x1b
// Subcommand 0x1D: Send House Revision State
// byte[4] houseserial
// byte[4] revision state
// Subcommand 0x1E:
// byte[4] houseserial
// Subcommand 0x21: (AOS) Ability icon confirm.
// Note: no data, just (bf 0 5 21)
// Subcommand 0x22: Damage
// BYTE[2] unknown, always 1
// BYTE[4] Serial
// BYTE Damage // how much damage was done ?
// Subcommand 0x24: UnKnown
// BYTE unknown. UOSE Introduced
// Subcommand 0x2C : Bandage macro
// BYTE[4] Item Serial
// BYTE[4] Target Serial
void CPISubcommands::Receive( void )
{
tSock->Receive( 3, false );
tSock->Receive( tSock->GetWord( 1 ), false );
subCmd = tSock->GetWord( 3 );
switch( subCmd )
{
case 0x01: skipOver = true; break; // Server message sent to client. 6 keys, each a long, Initialize Fast Walk Prevention
case 0x02: skipOver = true; break; // Server message. 1 long, new key. Add key to fast walk stack
case 0x05: skipOver = true; break; // Screen size
case 0x0A: skipOver = true; break; // Wrestling stun
//case 0x0C: skipOver = true; break; // Closed Status Gump
case 0x0C: { subPacket = new CPIClosedStatusGump( tSock ); } break; // Closed Status Gump
case 0x0F: skipOver = true; break; // Unknown, Sent once at Login
case 0x24: skipOver = true; break;
default: Console.Print( oldstrutil::format( "Packet 0xBF: Unhandled Subcommand: 0x%X\n", subCmd )); skipOver = true; break;
case 0x06: { subPacket = new CPIPartyCommand( tSock ); } break;
case 0x07: { subPacket = new CPITrackingArrow( tSock ); } break; // Click on Quest/Tracking Arrow
case 0x0B: { subPacket = new CPIClientLanguage( tSock ); } break; // Client language. 3 bytes. "ENU" for english
case 0x0E: { subPacket = new CPIUOTDActions( tSock ); } break; // UOTD actions
case 0x10: { subPacket = new CPIToolTipRequest( tSock ); } break; // Request for tooltip data
case 0x13: { subPacket = new CPIPopupMenuRequest( tSock ); } break; // 0x13 (Request popup menu, 4 byte serial ID) -> Respond with 0x14
case 0x15: { subPacket = new CPIPopupMenuSelect( tSock ); } break; // Popup Menu Selection
case 0x1A: { subPacket = new CPIExtendedStats( tSock ); } break; // Extended Stats
case 0x1C: { subPacket = new CPISpellbookSelect( tSock ); } break; // New SpellBook Selection
case 0x2C: { subPacket = new CPIBandageMacro( tSock ); } break; // Bandage macro
case 0x32: { subPacket = new CPIToggleFlying( tSock ); } break; // Toggle Flying on/off
}
}
bool CPISubcommands::Handle( void )
{
bool retVal = skipOver;
if( subPacket != nullptr )
{
retVal = subPacket->Handle();
delete subPacket;
}
return retVal;
}
void CPISubcommands::Log( std::ofstream &outStream, bool fullHeader )
{
if( subPacket != nullptr )
{
subPacket->Log( outStream, fullHeader );
}
else
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
}
// Party Command
//o------------------------------------------------------------------------------------------------o
//| Function - CPIPartyCommand()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with party command
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x06 (Party Command)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x06)
//| Subcommand details
//| Subsubcommand 1 - Add a party member
//| Client
//| BYTE[4] id (if 0, a targeting cursor appears)
//| Server
//| BYTE memberCount
//| For each member
//| BYTE[4] memberID
//| Subsubcommand 2 - Remove a party member
//| Client
//| BYTE[4] id (if 0, a targeting cursor appears)
//| Server
//| BYTE membersInNewParty
//| BYTE[4] idOfRemovedPlayer
//| For each member
//| BYTE[4] memberID
//| Subsubcommand 3 - Tell a party member a message
//| BYTE[4] id
//| BYTE[n][2] Null terminated Unicode message
//| Subsubcommand 4 - Tell full party a message
//| Client
//| BYTE[n][2] Null terminated unicode message
//| Server
//| BYTE[4] srcMember
//| BYTE[n][2] Null terminated Unicode message
//| Subsubcommand 6 - Party can loot me?
//| Client
//| BYTE canLoot ( 0 == no, 1 == yes )
//| Subsubcommand 7 - Send invitation to party
//| Server
//| BYTE[4] serial of invited player
//| Subsubcommand 8 - Accept join party invitation
//| Client
//| BYTE[4] leaderID
//| Subsubcommand 9 - Reject join party invitation
//| Client
//| BYTE[4] leaderID
//o------------------------------------------------------------------------------------------------o
CPIPartyCommand::CPIPartyCommand()
{
}
CPIPartyCommand::CPIPartyCommand( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIPartyCommand::Receive( void )
{
}
bool CPIPartyCommand::Handle( void )
{
UI08 partyCmd = tSock->GetByte( 5 );
const SI32 PARTY_ADD = 1;
const SI32 PARTY_REMOVE = 2;
const SI32 PARTY_TELLINDIV = 3;
const SI32 PARTY_TELLALL = 4;
const SI32 PARTY_LOOT = 6;
const SI32 PARTY_INVITE = 7;
const SI32 PARTY_ACCEPT = 8;
const SI32 PARTY_DECLINE = 9;
const SI32 BASE_OFFSET = 6;
switch( partyCmd )
{
case PARTY_ADD:
{
SERIAL charToAdd = tSock->GetDWord( BASE_OFFSET );
if( charToAdd != 0 )
{ // it really is a serial
tSock->SetDWord( 7, charToAdd );
PartyFactory::GetSingleton().CreateInvite( tSock );
}
else
{
tSock->SendTargetCursor( 0, TARGET_PARTYADD, Dictionary->GetEntry( 9003, tSock->Language() )); // Select the character to add to the party
}
break;
}
case PARTY_REMOVE:
{
Party *ourParty = PartyFactory::GetSingleton().Get( tSock->CurrcharObj() );
if( ourParty != nullptr )
{
SERIAL charToRemove = tSock->GetDWord( BASE_OFFSET );
if(( ourParty->Leader() == tSock->CurrcharObj() ) || ( tSock->CurrcharObj()->GetSerial() == charToRemove ))
{
if( charToRemove != 0 )
{ // it really is a serial
tSock->SetDWord( 7, charToRemove );
PartyFactory::GetSingleton().Kick( tSock );
}
else
{ // Crap crap crap, what do we do here?
tSock->SendTargetCursor( 0, TARGET_PARTYREMOVE, Dictionary->GetEntry( 9004, tSock->Language() )); // Select the character to remove from the party
}
}
else
{
tSock->SysMessage( 9005 ); // You have to be the leader to do that
}
}
else
{
tSock->SysMessage( 9006 ); // You are not in a party
}
break;
}
case PARTY_TELLINDIV:
{
Party *toTellTo = PartyFactory::GetSingleton().Get( tSock->CurrcharObj() );
if( toTellTo != nullptr )
{
CPPartyTell toTell( this, tSock );
SERIAL personToTell = tSock->GetDWord( BASE_OFFSET );
CChar *charToTell = CalcCharObjFromSer( personToTell );
CSocket *charTell = charToTell->GetSocket();
if( charTell != nullptr )
{
toTellTo->SendPacket( &toTell, charTell );
}
}
else
{
tSock->SysMessage( 9006 ); // You are not in a party
}
}
break;
case PARTY_TELLALL:
{
Party *toTellTo = PartyFactory::GetSingleton().Get( tSock->CurrcharObj() );
if( toTellTo != nullptr )
{
CPPartyTell toTell( this, tSock );
toTellTo->SendPacket( &toTell );
}
else
{
tSock->SysMessage( 9006 ); // You are not in a party
}
break;
}
case PARTY_LOOT:
{
Party *toAddTo = PartyFactory::GetSingleton().Get( tSock->CurrcharObj() );
if( toAddTo != nullptr )
{
CPartyEntry *mEntry = toAddTo->Find( tSock->CurrcharObj() );
if( mEntry != nullptr )
{
mEntry->IsLootable( tSock->GetByte( BASE_OFFSET ) != 0 );
}
tSock->SysMessage( 9007 ); // You have changed your loot setting
}
break;
}
case PARTY_INVITE:
{ // THIS SHOULD NEVER HAPPEN -> Server message!
break;
}
case PARTY_ACCEPT:
{
SERIAL leaderSerial = tSock->GetDWord( BASE_OFFSET );
Party *toAddTo = PartyFactory::GetSingleton().Get( CalcCharObjFromSer( leaderSerial ));
if( toAddTo != nullptr )
{
toAddTo->AddMember( tSock->CurrcharObj() );
}
else
{
tSock->SysMessage( 9008 ); // That party does not exist any more
}
break;
}
case PARTY_DECLINE:
{
SERIAL leaderSerial = tSock->GetDWord( BASE_OFFSET );
CChar *leader = CalcCharObjFromSer( leaderSerial );
if( leader != nullptr )
{
CSocket *leaderSock = leader->GetSocket();
if( leaderSock != nullptr )
{ // is it a PC leader?
leaderSock->SysMessage( 9009 ); // The player has declined your invitation to party
}
}
break;
}
}
return true;
}
void CPIPartyCommand::Log( std::ofstream &outStream, bool fullHeader )
{
UI08 partyCmd = tSock->GetByte( 5 );
const SI32 PARTY_ADD = 1;
const SI32 PARTY_REMOVE = 2;
const SI32 PARTY_TELLINDIV = 3;
const SI32 PARTY_TELLALL = 4;
const SI32 PARTY_LOOT = 6;
const SI32 PARTY_ACCEPT = 8;
const SI32 PARTY_DECLINE = 9;
const SI32 BASE_OFFSET = 6;
switch( partyCmd )
{
case PARTY_ADD:
{
// Subcommand 1 Add a party member
// Client
// BYTE[4] id (if 0, a targeting cursor appears)
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_ADD --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "To add : 0x" << std::hex << tSock->GetDWord( BASE_OFFSET ) << std::dec << std::endl;
break;
}
case PARTY_REMOVE:
{
// Subcommand 2 Remove a party member
// Client
// BYTE[4] id (if 0, a targeting cursor appears)
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_REMOVE --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "To remove : 0x" << std::hex << tSock->GetDWord( BASE_OFFSET ) << std::dec << std::endl;
break;
}
case PARTY_TELLINDIV:
{
// Subcommand 3 Tell a party member a message
// BYTE[4] id
// BYTE[n][2] Null terminated Unicode message
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_TELLINDIV --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
break;
}
case PARTY_TELLALL:
{
// Subcommand 4 Tell full party a message
// Client
// BYTE[n][2] Null terminated unicode message
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_TELLALL --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
break;
}
case PARTY_LOOT:
{
// Subcommand 6 Party can loot me?
// Client
// BYTE canLoot ( 0 == no, 1 == yes )
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_LOOT --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "Can loot : " << (( tSock->GetByte( BASE_OFFSET ) == 1 ) ? "yes" : "no" ) << std::endl;
break;
}
case PARTY_ACCEPT:
{
// Subcommand 8 Accept join party invitation
// Client
// BYTE[4] leaderID
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_ACCEPT --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "Leader : 0x" << std::hex << tSock->GetDWord( BASE_OFFSET ) << std::dec << std::endl;
break;
}
case PARTY_DECLINE:
{
// Subcommand 9 Reject join party invitation
// Client
// BYTE[4] leaderID
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Party Command Subcommand PARTY_DECLINE --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "Character : 0x" << std::hex << tSock->GetDWord( BASE_OFFSET ) << std::dec << std::endl;
break;
}
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPITrackingArrow()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to click on Quest/Tracking arrow
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x07 (Quest/Tracking Arrow)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x07)
//| Subcommand details
//| BYTE Right Click (0x01 = yes, 0x00 = no)
//o------------------------------------------------------------------------------------------------o
CPITrackingArrow::CPITrackingArrow()
{
}
CPITrackingArrow::CPITrackingArrow( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPITrackingArrow::Receive( void )
{
}
bool CPITrackingArrow::Handle( void )
{
CChar *mChar = tSock->CurrcharObj();
if( ValidateObject( mChar ) && tSock->GetByte( 5 ) == 0x01 )
{
tSock->SetTimer( tPC_TRACKING, 0 );
CChar *trackingTarg = mChar->GetTrackingTarget();
if( ValidateObject( trackingTarg ))
{
CPTrackingArrow tSend = ( *trackingTarg );
tSend.Active( 0 );
if( tSock->ClientVersion() >= CV_HS2D )
{
tSend.AddSerial( trackingTarg->GetSerial() );
}
tSock->Send( &tSend );
}
else
{
CPTrackingArrow tSend;
tSend.Active( 0 );
if( tSock->ClientVersion() >= CV_HS2D )
{
tSend.AddSerial( mChar->GetTrackingTargetSerial() );
}
tSock->Send( &tSend );
}
}
return true;
}
void CPITrackingArrow::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Tracking Arrow --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIClientLanguage()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to set/change client language (?)
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x0B (Client Language)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x0B)
//| Subcommand details
//| BYTE[3] language (ENU, for English)
//o------------------------------------------------------------------------------------------------o
CPIClientLanguage::CPIClientLanguage()
{
}
CPIClientLanguage::CPIClientLanguage( CSocket *s ) : CPInputBuffer( s )
{
newLang = ZERO;
Receive();
}
void CPIClientLanguage::Receive( void )
{
newLang = FindLanguage( tSock, 5 );
}
bool CPIClientLanguage::Handle( void )
{
tSock->Language( newLang );
return true;
}
void CPIClientLanguage::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Client Language --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIUOTDActions()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to perform an action in TD/3D client
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x0E (TD/3D Client Action)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x0E)
//| Subcommand details
//| BYTE[4] Animation ID
//| 00 00 00 06 - Yawn
//| 00 00 00 15 - Faint
//| 00 00 00 20 - Bow
//| 00 00 00 21 - Salute
//| 00 00 00 64 - Applaud
//| 00 00 00 66 - Argue
//| 00 00 00 68 - Blow Kiss
//| 00 00 00 69 - Formal Bow
//| 00 00 00 6B - Cover Ears
//| 00 00 00 6C - Curtsey
//| 00 00 00 6D - Jig
//| 00 00 00 6E - Folk Dance
//| 00 00 00 6F - Dance
//| 00 00 00 70 - Tribal Dance
//| 00 00 00 71 - Fold Arms
//| 00 00 00 72 - Impatient
//| 00 00 00 73 - Lecture
//| 00 00 00 74 - Nod
//| 00 00 00 75 - Point
//| 00 00 00 77 - Greet Salute
//| 00 00 00 79 - Shake Head
//| 00 00 00 7B - Victory
//| 00 00 00 7C - Celebrate
//| 00 00 00 7D - Wave
//| 00 00 00 7E - Two Handed Wave
//| 00 00 00 7F - Long Distance Wave
//| 00 00 00 80 - What?
//o------------------------------------------------------------------------------------------------o
CPIUOTDActions::CPIUOTDActions()
{
}
CPIUOTDActions::CPIUOTDActions( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIUOTDActions::Receive( void )
{
action = tSock->GetWord( 7 );
}
bool CPIUOTDActions::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
Effects->PlayCharacterAnimation( ourChar, action, 1 );
return true;
}
void CPIUOTDActions::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket UOTD Actions --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIToolTipRequest()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to show gump entry tooltip
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x10 (Popup Entry Select)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x10)
//| Subcommand details
//| BYTE[4] Item ID
//| BYTE[4] Unknown
//o------------------------------------------------------------------------------------------------o
CPIToolTipRequest::CPIToolTipRequest() : getSer( INVALIDSERIAL )
{
}
CPIToolTipRequest::CPIToolTipRequest( CSocket *s ) : CPInputBuffer( s ), getSer( INVALIDSERIAL )
{
Receive();
}
void CPIToolTipRequest::Receive( void )
{
getSer = tSock->GetDWord( 5 );
}
bool CPIToolTipRequest::Handle( void )
{
if( getSer != INVALIDSERIAL )
{
// Only send tooltip if server feature for tooltips is enabled
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_AOS ))
{
CPToolTip tSend( getSer, tSock );
tSock->Send( &tSend );
}
}
return true;
}
void CPIToolTipRequest::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Tooltip Request --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIPopupMenuRequest()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to show popup/context menu
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x13 (Request Popup Menu)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x13)
//| Subcommand details
//| BYTE[4] id (character id)
//o------------------------------------------------------------------------------------------------o
CPIPopupMenuRequest::CPIPopupMenuRequest() : mSer( INVALIDSERIAL )
{
}
CPIPopupMenuRequest::CPIPopupMenuRequest( CSocket *s ) : CPInputBuffer( s ), mSer( INVALIDSERIAL )
{
Receive();
}
void CPIPopupMenuRequest::Receive( void )
{
mSer = tSock->GetDWord( 5 );
}
bool CPIPopupMenuRequest::Handle( void )
{
// Only show context menus if enabled in ini
if( !cwmWorldState->ServerData()->ServerContextMenus() )
return true;
if( mSer < BASEITEMSERIAL )
{
CChar *myChar = CalcCharObjFromSer( mSer );
if( myChar == nullptr )
return true;
if( !LineOfSight( tSock, tSock->CurrcharObj(), myChar->GetX(), myChar->GetY(), ( myChar->GetZ() + 15 ), WALLS_CHIMNEYS + DOORS + FLOORS_FLAT_ROOFING, false ))
return true;
CPPopupMenu toSend(( *myChar ), ( *tSock ));
tSock->Send( &toSend );
}
return true;
}
void CPIPopupMenuRequest::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Popup Menu Request --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIPopupMenuSelect()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request select entry from popup/context menu
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x15 (Popup Entry Select)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x15)
//| Subcommand details
//| BYTE[4] Character ID
//| BYTE[2] Entry Tag for line selected provided in subcommand 0x14
//o------------------------------------------------------------------------------------------------o
CPIPopupMenuSelect::CPIPopupMenuSelect() : popupEntry( 0 ), targChar( nullptr )
{
}
CPIPopupMenuSelect::CPIPopupMenuSelect( CSocket *s ) : CPInputBuffer( s ), popupEntry( 0 ), targChar( nullptr )
{
Receive();
}
void CPIPopupMenuSelect::Receive( void )
{
popupEntry = tSock->GetWord( 9 );
targChar = CalcCharObjFromSer( tSock->GetDWord( 5 ));
}
bool WhichResponse( CSocket *mSock, CChar *mChar, std::string text, CChar *tChar = nullptr );
bool CPIPopupMenuSelect::Handle( void )
{
// Only show context menus if enabled in ini
if( !cwmWorldState->ServerData()->ServerContextMenus() )
return true;
CChar *mChar = tSock->CurrcharObj();
if( !ValidateObject( targChar ) || !ValidateObject( mChar ))
return true;
switch( popupEntry )
{
case 0x000A: // Open Paperdoll
if( cwmWorldState->creatures[targChar->GetId()].IsHuman() )
{
PaperDoll( tSock, targChar );
}
break;
case 0x000B: // Open Backpack
if( mChar->GetCommandLevel() >= CL_CNS || cwmWorldState->creatures[targChar->GetId()].IsHuman()
|| targChar->GetId() == 0x0123 || targChar->GetId() == 0x0124 || targChar->GetId() == 0x0317 ) // Only Humans and Pack Animals have Packs
{
if( mChar->IsDead() )
{
tSock->SysMessage( 392 ); // You are dead and cannot do that.
}
else if( !ObjInRange( mChar, targChar, DIST_NEARBY ))
{
tSock->SysMessage( 382 ); // You need to get closer.
}
else
{
CItem *pack = targChar->GetPackItem();
if( ValidateObject( pack ))
{
if( mChar == targChar || targChar->GetOwnerObj() == mChar || mChar->GetCommandLevel() >= CL_CNS )
{
if( targChar->GetNpcAiType() == AI_PLAYERVENDOR )
{
targChar->TextMessage( tSock, 385, TALK, false ); // Take a look at my goods.
tSock->OpenPack( pack, true );
}
else
{
tSock->OpenPack( pack );
}
}
else if( targChar->GetNpcAiType() == AI_PLAYERVENDOR )
{
targChar->TextMessage( tSock, 385, TALK, false ); // Take a look at my goods.
tSock->OpenPack( pack, true );
}
else
{
Skills->Snooping( tSock, targChar, pack );
}
}
else
{
Console.Warning( oldstrutil::format( "Character 0x%X has no backpack!", targChar->GetSerial() ));
}
}
}
break;
case 0x000C: // Buy Window
if( targChar->IsShop() )
{
if( ObjInRange( mChar, targChar, 8 ))
{
targChar->SetTimer( tNPC_MOVETIME, BuildTimeValue( 60 ));
BuyShop( tSock, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
}
break;
case 0x000D: // Sell Window
if( targChar->IsShop() )
{
if( ObjInRange( mChar, targChar, 8 ))
{
targChar->SetTimer( tNPC_MOVETIME, BuildTimeValue( 60 ));
CPSellList toSend;
if( toSend.CanSellItems(( *mChar ), ( *targChar )))
{
tSock->Send( &toSend );
}
else
{
targChar->TextMessage( tSock, 1341, TALK, false ); // Thou doth posses nothing of interest to me.
}
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
}
break;
case 0x000E: // Open Bankbox
if( targChar->GetNpcAiType() == AI_BANKER )
{
if( ObjInRange( mChar, targChar, 8 ))
{
tSock->OpenBank( mChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
}
break;
case 0x000F: // Tame
if( cwmWorldState->creatures[targChar->GetId()].IsAnimal() )
{
if( ObjInRange( mChar, targChar, 8 ))
{
// Set a tag on the player to reference the animal they're about to tame
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "tameSerial", targCharSerial );
Skills->SkillUse( tSock, TAMING );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
}
break;
case 0x0010: // Command: Kill (Pet)
{
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_KILL );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
}
case 0x0011: // Command: Stop (Pet)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_STOP );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0012: // Command: Follow (Pet)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_FOLLOW );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0013: // Command: Stay (Pet)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_STAY );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0014: // Command: Guard (Pet)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_GUARD );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0015: // Add Friend (Pet/Hireling)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_FRIEND );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0016: // Transfer (Pet)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_TRANSFER );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0017: // Release (Pet)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "petCommandObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_RELEASE );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0018: // Dismiss (Hireling)
if( ObjInRange( mChar, targChar, 12 ))
{
// Set a tag on the player to reference the pet they are commanding
TAGMAPOBJECT targCharSerial;
targCharSerial.m_Destroy = false;
targCharSerial.m_IntValue = targChar->GetSerial();
targCharSerial.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerial.m_StringValue = "";
mChar->SetTag( "hirelingObj", targCharSerial );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_DISMISS );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0019: // Claim All Pets (Stablemaster)
if( ObjInRange( mChar, targChar, 8 ))
{
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_CLAIM );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x001a: // Stable Pet (Stablemaster)
if( ObjInRange( mChar, targChar, 8 ))
{
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_STABLE );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x001b: // Ask Destination (NPC Escort)
if( ObjInRange( mChar, targChar, 3 ))
{
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_QUESTDEST );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x001c: // Accept Escort (NPC Escort)
if( ObjInRange( mChar, targChar, 3 ))
{
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_QUESTTAKE );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x001d: // Abandon Escort (NPC Escort)
if( targChar->IsNpc() && targChar->GetQuestType() == 0xFF && targChar->GetOwnerObj() == mChar )
{
targChar->SetNpcWander( WT_FREE ); // Wander freely
targChar->SetFTarg( nullptr ); // Reset follow target
targChar->SetQuestType( 0 ); // Reset quest type
targChar->SetQuestDestRegion( 0 ); // Reset quest destination region
// Set a timer to automatically delete the NPC
targChar->SetTimer( tNPC_SUMMONTIME, cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_ESCORTDONE ));
targChar->SetOwner( nullptr );
targChar->TextMessage( nullptr, 1797, TALK, false ); // Send out the rant about being abandoned
}
break;
case 0x001e: // Hire (Hireling)
if( targChar->IsNpc() && targChar->CanBeHired() )
{
if( ObjInRange( mChar, targChar, 3 ))
{
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_HIRE );
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
}
break;
case 0x001f: // Remove Friend (Pet/Hireling)
if( ObjInRange( mChar, targChar, 12 ))
{
// Store reference to pet in tempObj
tSock->TempObj( targChar );
tSock->SendTargetCursor( 0, TARGET_REMOVEFRIEND, 0, 1852 ); // Select player to remove as friend:
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
case 0x0100: // Bulk Order Info
if( ObjInRange( mChar, targChar, 8 ))
{
// Set a tag on the player to reference the NPC they're requesting bulk order from
TAGMAPOBJECT targCharSerialTag;
targCharSerialTag.m_Destroy = false;
targCharSerialTag.m_IntValue = targChar->GetSerial();
targCharSerialTag.m_ObjectType = TAGMAP_TYPE_INT;
targCharSerialTag.m_StringValue = "";
mChar->SetTempTag( "bodShopkeeperSerial", targCharSerialTag );
tSock->ClearTrigWords();
tSock->AddTrigWord( TW_BODINFO ); // Custom UOX3 trigger word
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
else
{
tSock->SysMessage( 393 ); // That is too far away
}
break;
default:
Console.Print( oldstrutil::format( "Popup Menu Selection Called, Player: 0x%X Selection: 0x%X\n", tSock->GetDWord( 5 ), tSock->GetWord( 9 )));
break;
}
// Let's handle skill training
if(( popupEntry >= 0x006d && popupEntry <= 0x006f ) || ( popupEntry >= 0x006D && popupEntry <= 0x009c )
|| popupEntry == 0x154 || ( popupEntry >= 0x017c && popupEntry <= 0x017e ))
{
tSock->ClearTrigWords();
tSock->AddTrigWord( static_cast<TriggerWords>( popupEntry ));
std::string targName = GetNpcDictName( targChar, tSock, NRS_SYSTEM );
WhichResponse( tSock, mChar, targName, targChar );
}
return true;
}
void CPIPopupMenuSelect::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Popup Menu Select --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIExtendedStats()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with request to lock/unlock extended stats
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x1A (Extended stats)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x1A)
//| Subcommand details
//| BYTE stat // 0: str, 1: dex, 2:int
//| BYTE status // 0: up, 1:down, 2: locked
//o------------------------------------------------------------------------------------------------o
CPIExtendedStats::CPIExtendedStats()
{
}
CPIExtendedStats::CPIExtendedStats( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIExtendedStats::Receive( void )
{
statToSet = tSock->GetByte( 5 ) + ( ALLSKILLS + 1 );
value = tSock->GetByte( 6 );
}
bool CPIExtendedStats::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
ourChar->SetSkillLock( static_cast<SkillLock>( value ), statToSet );
return true;
}
void CPIExtendedStats::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Extended Stats --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIBandageMacro()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with bandage macro request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x1C (Bandage Macro/Use targeted item)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x1C)
//| Subcommand details
//| Byte[4] Item Serial
//| Byte[4] Target Serial
//o------------------------------------------------------------------------------------------------o
CPIBandageMacro::CPIBandageMacro()
{
}
CPIBandageMacro::CPIBandageMacro( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIBandageMacro::Receive(void)
{
Handle();
}
bool CPIBandageMacro::Handle(void)
{
CChar *ourChar = tSock->CurrcharObj();
if( ValidateObject( ourChar ))
{
// Check if onUseBandageMacro event exists
std::vector<UI16> scriptTriggers = ourChar->GetScriptTriggers();
for( auto i : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( i );
if( toExecute != nullptr )
{
SERIAL bandageSerial = tSock->GetDWord( 5 );
SERIAL targetSerial = tSock->GetDWord( 9 );
if( bandageSerial == INVALIDSERIAL )
{
Console << "Bandage Macro detected, but no bandage found!\n";
return false;
}
if( targetSerial == INVALIDSERIAL )
{
Console << "Bandage Macro detected, but no target found!\n";
return false;
}
CItem *bandageItem = CalcItemObjFromSer( bandageSerial );
CChar *targetChar = CalcCharObjFromSer( targetSerial );
if( !ValidateObject( bandageItem ) || !ValidateObject( targetChar ))
return false;
if( toExecute->OnUseBandageMacro( tSock, targetChar, bandageItem ) == 1 ) // if it exists and we don't want hard code, return
{
return true;
}
}
}
}
return false;
}
void CPIBandageMacro::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Bandage Macro --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIClosedStatusGump()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with bandage macro request
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x0C (Closed Status Gump)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x0C)
//| Subcommand details
//| Byte[4] Character Serial
//o------------------------------------------------------------------------------------------------o
CPIClosedStatusGump::CPIClosedStatusGump()
{
}
CPIClosedStatusGump::CPIClosedStatusGump( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIClosedStatusGump::Receive( void )
{
Handle();
}
bool CPIClosedStatusGump::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
if( ValidateObject( ourChar ))
{
SERIAL statusGumpCharSerial = tSock->GetDWord( 5 );
CChar *statusGumpChar = CalcCharObjFromSer( statusGumpCharSerial );
if( ValidateObject( statusGumpChar ))
{
//tSock->StatWindow( statusGumpChar ); // Send statwindow update on closing the gump? Why? Doesn't seem like it's needed...
return true;
}
}
return false;
}
void CPIClosedStatusGump::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Bandage Macro --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIToggleFlying()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Toggles flying on/off for gargoyle characters
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x32 (Toggle Gargoyle Flying)
//| Size: 11 bytes?
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x32)
//| Subcommand details
//| Byte[4] unk1 (always 0x0100)
//| Byte[2] unk2(always 0x0)
//o------------------------------------------------------------------------------------------------o
CPIToggleFlying::CPIToggleFlying()
{
}
CPIToggleFlying::CPIToggleFlying( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIToggleFlying::Receive(void)
{
Handle();
}
bool CPIToggleFlying::Handle(void)
{
CChar *ourChar = tSock->CurrcharObj();
if( ValidateObject( ourChar ))
{
ourChar->ToggleFlying();
}
return true;
}
void CPIToggleFlying::Log(std::ofstream &outStream, bool fullHeader)
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Toggle Gargoyle Flying --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIKrriosClientSpecial()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet for party and guild tracking, assistant feature
//| negotiation, and potentially other purposes (like new movement mode)
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xF0 (KrriosClientSpecial)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[2] len
//| BYTE subcmd
//| 0x00 - Party info
//| 0x01 - Guild Tracker
//| BYTE unknown // always 1?
//| 0xff - Response to server's request for Razor to negotiate features
//| Byte 0 == 0xf0
//| Byte 1 == 0x00
//| Byte 2 == 0x04
//| Byte 3 == 0xff
//| If both ASSISTANTNEGOTIATION and KICKONASSISTANTSILENCE uox.ini
//| settings are enabled, player will get kicked within 30 seconds
//| if this packet is not received by the server.
//o------------------------------------------------------------------------------------------------o
void CPIKrriosClientSpecial::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPKrriosClientSpecial 0xF0 --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
void CPIKrriosClientSpecial::InternalReset( void )
{
}
CPIKrriosClientSpecial::CPIKrriosClientSpecial()
{
InternalReset();
}
CPIKrriosClientSpecial::CPIKrriosClientSpecial( CSocket *s ) : CPInputBuffer( s )
{
InternalReset();
Receive();
}
void CPIKrriosClientSpecial::Receive( void )
{
tSock->Receive( 4, false );
blockLen = tSock->GetWord( 1 );
tSock->Receive( blockLen, false );
if( blockLen > 5 )
{
// New Movement Request Packet
type = 0xaa;
}
else
{
type = tSock->GetByte( 3 );
}
}
bool CPIKrriosClientSpecial::Handle( void )
{
locations = false;
switch( type )
{
case 0x00: // custom party info
case 0x01: // guild track info
{
// If ini-setting for worldmap packets is enabled
if( cwmWorldState->ServerData()->GetClassicUOMapTracker() )
{
CChar *mChar = tSock->CurrcharObj();
if( type == 0x00 )
{
// Character is not in a party - nothing to track
if( !mChar->InParty() )
break;
}
else if( type == 0x01 )
{
locations = ( blockLen == 5 ? static_cast<bool>( tSock->GetByte( 4 )) : false );
// Character is not in a guild - nothing to track
if( mChar->GetGuildNumber() == -1 )
{
break;
}
const CGuild * const mGuild = GuildSys->Guild( mChar->GetGuildNumber() );
if( mGuild != nullptr )
{
// Count the number of recruits and regular members, only proceeed
// if there's more than one (that would be the player themselves)
size_t numRecruits = mGuild->NumRecruits();
size_t numMembers = mGuild->NumMembers();
if( numRecruits + numMembers <= 1 )
break;
}
}
// Construct the response, and send it!
CPKrriosClientSpecial krriosResponse( tSock, mChar, type, locations );
tSock->Send( &krriosResponse );
}
break;
}
case 0xaa: // New Movement Request Packet
break;
case 0xff: // razor feature negotiation
{
if( tSock->GetByte( 1 ) == 0x00 && tSock->GetByte( 2 ) == 0x04 )
{
tSock->NegotiatedWithAssistant( true );
tSock->NegotiateTimeout( -1 );
tSock->SysMessage( 9010 ); // Features successfully negotiated with assistant tool.
return true;
}
break;
}
default: // default
break;
}
return locations;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPISpellbookSelect()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with info on selected spell from spellbook
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xBF (General Information Packet)
//| Subcommand: 0x1D (Spell selected)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd (0xBF)
//| BYTE[2] Length
//| BYTE[2] Subcommand (0x1D)
//| Subcommand details
//| BYTE[2] unknown, always 2
//| BYTE[2] selected spell(0-indexed)+scroll offset from sub 0x1b
//o------------------------------------------------------------------------------------------------o
CPISpellbookSelect::CPISpellbookSelect()
{
}
CPISpellbookSelect::CPISpellbookSelect( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPISpellbookSelect::Receive( void )
{
}
bool CPISpellbookSelect::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
CItem *sBook = FindItemOfType( ourChar, IT_SPELLBOOK );
CItem *p = ourChar->GetPackItem();
UI08 *buffer = tSock->Buffer();
if( ValidateObject( sBook ))
{
bool validLoc = false;
if( sBook->GetCont() == ourChar )
{
validLoc = true;
}
else if( ValidateObject( p ) && sBook->GetCont() == p )
{
validLoc = true;
}
if( validLoc )
{
SI32 book = ( buffer[7] << 8 ) + ( buffer[8] );
if( Magic->CheckBook((( book - 1 ) / 8 ) + 1, ( book - 1 ) % 8, sBook ))
{
if( ourChar->IsFrozen() )
{
if( ourChar->IsCasting() )
{
tSock->SysMessage( 762 ); // You are already casting a spell.
}
else
{
tSock->SysMessage( 763 ); // You cannot cast spells while frozen.
}
}
else
{
tSock->CurrentSpellType( 0 );
Magic->SelectSpell( tSock, book );
}
}
else
{
tSock->SysMessage( 764 ); // You do not have that spell.
}
}
else
{
tSock->SysMessage( 765 ); // =To cast spells, your spellbook must be in your hands or in the first layer of your pack.
}
}
return true;
}
void CPISpellbookSelect::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPISubcommands 0xBF Subpacket Spellbook Select --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << " Raw dump :" << std::endl;
CPInputBuffer::Log( outStream, false );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CPIAOSCommand()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Handles incoming packet with requests for various AoS features
//o------------------------------------------------------------------------------------------------o
//| Notes - Packet: 0xD7 (Generic AOS Commands)
//| Size: Variable
//|
//| Packet Build
//| BYTE cmd
//| BYTE[4] id
//| BYTE[30] new name
//|
//| BYTE cmd
//| BYTE[2] length
//| BYTE[4] player serial
//| BYTE[2] subcommand
//| 0x02 - House Customization :: Backup
//| BYTE unknown (0x07)
//| 0x03 - House Customization :: Restore
//| BYTE unknown (0x07)
//| 0x04 - House Customization :: Commit
//| BYTE unknown (0x07)
//| 0x05 - House Customization :: Destroy Item
//| BYTE unknown (0)
//| BYTE[2] unknown (0)
//| BYTE[2] item graphic
//| BYTE unknown (0)
//| BYTE[4] X Pos
//| BYTE unknown (0)
//| BYTE[4] Y Pos
//| BYTE unknown (0)
//| BYTE[4] z Pos (7)
//| BYTE terminator (0x07)
//| 0x06 - House Customization :: Place Item
//| BYTE unknown (0)
//| BYTE[2] unknown (0)
//| BYTE[2] itemGraphic
//| BYTE unknown (0)
//| BYTE[4] XPos
//| BYTE unknown (0)
//| BYTE[4] YPos
//| BYTE terminator (0x07)
//| 0x0C - House Customization :: Exit
//| BYTE unknown (0x07)
//| 0x0D - House Customization :: Place Multi (Stairs)
//| BYTE unknown (0x00)
//| BYTE[2] X Position (Relative to center of house)
//| BYTE unknown (0x00)
//| BYTE[2] Y Position (Relative to center of house)
//| BYTE unknown (0x00)
//| 0x0E - House Customization :: Synch
//| BYTE unknown (0x07)
//| 0x10 - House Customization :: Clear
//| BYTE unknown (0x07)
//| 0x12 - House Customization :: Switch Floors
//| BYTE[4] unknown (0)
//| BYTE Floor#
//| BYTE terminator (0x07)
//| 0x19 - Special Moves :: Activate/Deactivate
//| Sent when client pushes the icones from the combat book.
//| The server uses an 0xBF Subcommand 0x21 Packet to cancel the red color of
//| icons, and reset the status of them on client.
//| Valid Ability Numbers:
//| 0x00 = Cancel Ability Attempt
//| 0x01 = Armor Ignore
//| 0x02 = Bleed Attack
//| 0x03 = Concusion Blow
//| 0x04 = Crushing Blow
//| 0x05 = Disarm
//| 0x06 = Dismount
//| 0x07 = Double Strike
//| 0x08 = Infecting
//| 0x09 = Mortal Strike
//| 0x0A = Moving Shot
//| 0x0B = Paralyzing Blow
//| 0x0C = Shadow Strike
//| 0x0D = Whirlwind Attack
//| 0x0E = Riding Swipe
//| 0x0F = Frenzied Whirlwind
//| 0x10 = Block
//| 0x11 = Defense Mastery
//| 0x12 = Nerve Strike
//| 0x13 = Talon Strike
//| 0x14 = Feint
//| 0x15 = Dual Wield
//| 0x16 = Double shot
//| 0x17 = Armor Peirce
//| 0x18 = Bladeweave
//| 0x19 = Force Arrow
//| 0x1A = Lightning Arrow
//| 0x1B = Psychic Attack
//| 0x1C = Serpent Arrow
//| 0x1D = Force of Nature
//| 0x1A - House Customization :: Revert
//| BYTE unknown (0x07)
//| 0x28 - Quests :: Paperdoll button
//| BYTE unknown (0x07)
//| 0x32 - Guild :: Paperdoll button
//| BYTE unknown (0x07)
//o------------------------------------------------------------------------------------------------o
CPIAOSCommand::CPIAOSCommand()
{
}
CPIAOSCommand::CPIAOSCommand( CSocket *s ) : CPInputBuffer( s )
{
Receive();
}
void CPIAOSCommand::Receive( void )
{
tSock->Receive( 3, false );
UI16 len = tSock->GetWord( 1 );
tSock->Receive( len, false );
}
bool CPIAOSCommand::Handle( void )
{
switch( tSock->GetWord( 7 )) // Which subcommand?
{
/*case 0x0002: break; //House Customisation :: Backup
case 0x0003: break; //House Customisation :: Restore
case 0x0004: break; //House Customisation :: Commit
case 0x0005: break; //House Customisation :: Destroy Item
case 0x0006: break; //House Customisation :: Place Item
case 0x000C: break; //House Customisation :: Exit
case 0x000D: break; //House Customisation :: Place Multi (Stairs)
case 0x000E: break; //House Customisation :: Synch
case 0x0010: break; //House Customisation :: Clear
case 0x0012: break; //House Customisation :: Switch Floors*/
case 0x0019: //Special Moves :: Activate / Deactivate
{
//UI32 unknown = tSock->GetDWord( 9 );
UI08 abilityId = tSock->GetByte( 13 );
//UI08 unknown2 = tSock->GetByte( 15 );
CChar *myChar = tSock->CurrcharObj();
std::vector<UI16> scriptTriggers = myChar->GetScriptTriggers();
for( auto i : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( i );
if( toExecute != nullptr )
{
if( toExecute->OnSpecialMove( myChar, abilityId ) == 1 )
{
return true;
}
}
}
// No individual scripts handling OnSpecialMove return true - let's check global script!
cScript *toExecute = JSMapping->GetScript( static_cast<UI16>( 0 ));
if( toExecute != nullptr )
{
toExecute->OnSpecialMove( myChar, abilityId );
}
return true;
}
/*case 0x001A: break; //House Customisation :: Revert*/
case 0x0028: //Guild :: Paperdoll button
if( cwmWorldState->ServerData()->PaperdollGuildButton() )
{
if( tSock->CurrcharObj()->GetGuildNumber() != - 1 )
{
GuildSys->Menu( tSock, BasePage + 1, static_cast<GUILDID>( tSock->CurrcharObj()->GetGuildNumber() ));
}
else
{
tSock->SysMessage( 1793 ); // You are not currently in a guild. Go buy a guildstone!
}
return true;
}
break;
case 0x0032: //Quests :: Unknown
{
CChar *myChar = tSock->CurrcharObj();
std::vector<UI16> scriptTriggers = myChar->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
if( toExecute->OnQuestGump( myChar ) == 1 )
{
return true;
}
}
}
// No individual scripts handling OnQuestGump returned true - let's check global script!
cScript *toExecute = JSMapping->GetScript( static_cast<UI16>( 0 ));
if( toExecute != nullptr )
{
toExecute->OnQuestGump( myChar );
}
return true;
}
default: break;
}
return false;
}
void CPIAOSCommand::Log( std::ofstream &outStream, bool fullHeader )
{
if( fullHeader )
{
outStream << "[RECV]Packet : CPIAOSCommand 0xD7 --> Length: " << tSock->GetWord( 1 ) << TimeStamp() << std::endl;
}
outStream << "Character : 0x" << std::hex << tSock->GetDWord( 3 ) << std::dec << std::endl;
outStream << "Command : ";
switch( tSock->GetWord( 7 ))
{
case 0x0002: outStream << "House Customisation :: Backup"; break;
case 0x0003: outStream << "House Customisation :: Restore"; break;
case 0x0004: outStream << "House Customisation :: Commit"; break;
case 0x0005: outStream << "House Customisation :: Destroy Item"; break;
case 0x0006: outStream << "House Customisation :: Place Item"; break;
case 0x000C: outStream << "House Customisation :: Exit"; break;
case 0x000D: outStream << "House Customisation :: Place Multi (Stairs)"; break;
case 0x000E: outStream << "House Customisation :: Synch"; break;
case 0x0010: outStream << "House Customisation :: Clear"; break;
case 0x0012: outStream << "House Customisation :: Switch Floors"; break;
case 0x0019: outStream << "Special Moves :: Activate / Deactivate"; break;
case 0x001A: outStream << "House Customisation :: Revert"; break;
case 0x0028: outStream << "Guild :: Unknown"; break;
case 0x0032: outStream << "Quests :: Unknown"; break;
default: outStream << "Unknown (0x" << std::hex << tSock->GetWord( 7 ) << std::dec << ")"; break;
}
outStream << std::endl;
outStream << " Raw dump : " << std::endl;
CPInputBuffer::Log( outStream, false );
}