uox3/source/CPacketReceive.cpp
Xoduz 69a8c090cd 0.99.4x
Fixed an issue where the color of NPC corpse names would not always match up with the NPC's flag color
Fixed a bug where the baseRange and maxRange properties of ranged weapons would not get saved to world files. These values are now saved in the format of RANGE=baseRange,maxRange
Changed the way items are added to containers, to ensure that the order in which the items are displayed to the player always reflects the order in which they were added; now the most recent item added/moved in a container will always be rendered on top of older items.
Made adjustments to how items are randomly added to containers; should use more of a given container gump's available area now
Exposed Item property to JS engine, which contains serial of the creator of an item:
	.creator 	// contains serial of the creator of an item. If set, maker's marks will show up in the item's tooltip
Fixed an issue where JS engine would lose track of script context if one script used CreateDFNItem() or SpawnNPC() functions and the new objects had events that triggered upon creation. UOX3 now restores the original script context at the tail end of these functions.
Added new JS Event to allow inserting custom tooltip text for objects, which will be displayed in tooltips right after the object name. Any text returned from the event will be displayed, and the text can make use of the same HTML tags as gumps to make changes to color, font, etc.
	onTooltip( myObj ) 		// Triggers for objects right before the object's tooltip properties are sent to client
Updated 'tweak and 'set commands to refresh item being modified if movable state of item changes, so the change is reflected in nearby clients
Fixed an issue with NPC vendors and items bought from players not being properly removed from the vendor's "bought container" when players buy them again
Ported Item Identification skill from code to JS (js/skill/itemid.js) and removed hard-coded version
Implemented magic item generator in JS (js/item/magic_item.js). When this script is attached to an NPC, it has a chance to generate magic weapons, armors, wands/staffs and rings as loot on the NPC's corpse when slain. The chance of getting quality magical items increases with the fame level of the NPC in question. The types of magical items generated follows the pattern of such items as implemented in UO prior to the AoS expansion.
	Examples:
		exceedingly accurate war hammer of vanquishing
		substantial, accurate axe of power and Daemon's Breath
		silver long sword
		massive platemail arms of Protection
		metal shield of defense
		durable ringmail tunic of guarding
	All magic items have a chance to have spell effects attached (with rings/wands being guaranteed to have these), with a limited amount of charges available. For weapons, these effects activate on successful hits in combat, while for armors they activate on equip, and then periodically as long as the item is worn. Rings and wands/staffs have to be manually activated and targeted - with the exception of rings of invisibility, which activate the moment you equip them!
	Related scripts:
		js/item/magic_armor_equipeffects.js
		js/item/magic_weapon_accbonus.js
		js/item/magic_weapon_equip.js
		js/item/magic_weapon_spell_attack.js
	The magic item loot generation script has been attached to a number of different NPCs, all of which now have a chance to drop magic items as loot when defeated.
A bonus magical item - the glacial staff - has also been implemented (js/item/magic_glacial_staff.js), and has a chance to drop as loot from Giant Ice Serpents!
Fixed an issue with CustomTarget where the target message would not be displayed if the cursorType parameter was provided
Fixed bug with 'rename command, which referenced a non-existing variable that caused a script crash
Added additional object properties to 'get and 'set commands:
	.shouldSave 		// Determines if an item should be saved in worldfiles or not
	.baseRange 			// Determines the base range of a ranged weapon, less than which it becomes less effective
	.maxRange 			// Determines the max range of a ranged weapon, beyond which it cannot reach its target
The feature that allows stats like Strength, Dexterity and Intelligence to provide bonuses to skill checks has been turned into a UOX.INI setting, which is disabled by default. The bonuses have also been nerfed; they will no longer contribute more to the success of a skill check than the actual skill being checked!
	STATSAFFECTSKILLCHECKS=0/1 	// If enabled, stats can provide bonuses to skill checks based on the weighting for those stats as setup in dfndata/skills/skills.dfn
Updated damage tracking code to include the type of damage that was dealt (PHYSICAl vs HEAT vs COLD, etc)
Updated Character JS Method Damage() to include a new parameter that can specify the type of damage that was dealt to the character. The new parameter must always be included if other optional parameters are used. Note that updates might be required for scripts making use of this method.Updated syntax:
	.Damage( amount )
	.Damage( amount, damageType )
	.Damage( amount, damageType, attacker )
	.Damage( amount, damageType, attacker, doRepsys )
	Supported damageTypes:
		PHYSICAL 	= 1
		LIGHT 		= 2
		RAIN 		= 3
		COLD 		= 4
		HEAT 		= 5
		LIGHTNING 	= 6 // magic damage
		POISON 		= 7
		SNOW 		= 8
Updated JS Event OnDamage to include an additional parameter that describes the type of damage that was dealt. New syntax:
	onDamage( damaged, attacker, damageValue, damageType )
Updated JS Event OnSpellTarget to allow rejecting a spell being cast on a target by returning a value of 2 from the script
Split the character priv flag for magic reflection into a temporary one (one-time magic reflection spell) and a permanent one (innate permanent reflection ability). The temporary effect is put in place by Magic Reflection spell, and is removed after a successful spell reflect. The permanent one is an innate ability of a character and is not removed even after a spell is reflected.
Added new Character JS property to get/set state of a character's permanent magic reflect ability:
	.permanentMagicReflect 		// 0 = disable, 1 = enable
Added special abilities/effects for the following NPCs:
	Acid Elementals now have a chance to damage the melee weapons of their attackers
	Dull Copper Elementals now explode on death
	Shadow Iron Elemental is immune to targeted spell damage
	Copper Elementals have permanent magic reflect, and reflect some physical damage back at their attacker
	Bronze Elementals deal passive area damage to nearby players every 5 to 10 seconds
	Valorite Elementals have permanent magic reflect, reflect some physical damage back at their attacker
	Snow/Ice Elementals deal passive area damage to nearby players every 5 to 10 seconds
	Lava Serpents deal passive area damage to nearby players every 5 to 10 seconds
	Phoenixes deal passive area damage to nearby players every 5 to 10 seconds
	Pixies have a chance to cast a random spell upon receiving a death blow:
		Bless (target)
		Curse (target)
		Explosion (target)
		Greater Poison (target)
		Greater Heal (self, prevents death)
	Ethereal Warriors will now resurrect dead players with positive karma
	Ethereal Warriors now have a chance to drain target's health, stamina or mana on hit
	Fire Breath special ability added to the following NPCs, with the random ability damage scaling with the NPCs current health:
		Hell Cat, 5 to 8 dmg at max health
		Fire Steed, 6 to 9 dmg at max health
		Hell Hounds, 8 to 11 dmg at max health
		Lava Lizard, 8 to 11 dmg at max health
		Predator Hell Cat, 9 to 14 dmg at max health
		Sea Serpent, 11 to 17 dmg at max health
		Swamp Dragon, 15 to 22 dmg at max health
		Armored Swamp Dragon, 15 to 22 dmg at max health
		Fire Gargoyle, 20 to 30 dmg at max health
		Deep Sea Serpent, 21 to 32 dmg at max health
		Serpentine Dragon, 22 to 32 dmg at max health
		Drake (Gray), 22 to 32 dmg at max health
		Drake (Red), 22 to 32 dmg at max health
		Nightmare, 26 to 39 dmg at max health
		Dark Steed, 26 to 39 dmg at max health
		Silver Steed, 26 to 39 dmg at max health
		Kraken, 39 to 59 dmg at max health
		Dragon (Red), 41 to 62 dmg at max health
		Dragon (Gray), 41 to 62 dmg at max health
		Shadow Wyrm, 50 to 75 dmg at max health
		Reptalon, 51 to 77 dmg at max health
		Skeletal Dragon, 52 to 77 dmg at max health
		Ancient Wyrm, 60 to 90 dmg at max health
Update stats, skills and loot for all currently implemented NPCs
Removed an ancient piece of code that prevented corpses from being generated for various elementals and blade spirits on death. This was originally put in place because these creatures had no corpses, and would replace it with a backpack instead, but these creatures now all have corpses in all client versions supported by UOX3
Updated potions script (js/item/potion.js) with updated formula for amount of hitpoints healed by healing potions, and added restrictions for using them when at full health and/or if poisoned (Dragon Slayer)
Fixed a bug where creatures (and humans, if FORCENEWANIMATIONPACKET was disabled in uox.ini) would play animations with wrong frame count, causing the animations to either freeze for a few frames at the end, or get cut off a couple of frames early
Implemented bonus hit chance for Archery skill, based on mention of such a bonus in Publish 5 patch notes and related UO House of Commons chat. This has been exposed as a UOX.INI setting where this bonus can be tweaked:
	ARCHERYHITBONUS=10 	// Bonus hit chance for Archery skill added to regular hit chance in combat. Defaults to 10%
Implemented optional extra delay between moving and being able to shoot with ranged weapons in combat (in addition to whatever delay is there due to speed of the ranged weapon). This is exposed as a UOX.INI setting:
	ARCHERYSHOOTDELAY=0.5 // Minimum delay in seconds from a player stops moving until they can start to fire their ranged weapon. Defaults to 0.5s
Fixed a bug where players who never entered combat mode could fire ranged weapons while moving
Updated Add-menu with some improvements for usability:
	Menu now directly on the Objects (previously "Shard") tab for quicker access, with a small welcome text and quick-link button for UOX3 docs
	Settings tab contains some (persistent, will be saved with character) options for how the Add-menu behaves, along with a few quick-access buttons to some useful commands. Available options:
		Option to add chosen item at specific location instead of in GM's backpack
		Option to add chosen item repeatedly until cancelled
		Option to automatically reopen Add-menu on last menu that was open when a selection is made
		Option to force decayable state of all added items to either off (doesn't decay), on (decays) or nothing (use item default)
		Option to force movable state of all added items to either off (not movable), on (movable) or nothing (use item default)
	Added a Home button at the bottom of the menu, that takes the user back to the front page of the Add-menu regardless of which menu page they're on
Updated Character/Socket JS Method SysMessage() with an optional parameter to specify the color used to display the system message. Updated syntax:
	.SysMessage( "Text" ) 									// Display "Text" to user as a system message with default system message color from ini
	.SysMessage( "Text %s %s", txtArg1, txtArg2 ) 			// Display "Text" to player with string arguments injected into text
	.SysMessage( txtColor, "Text" ) 						// Display "Text" to user with a specified color
	.SysMessage( txtColor, "Text %s %s", txtArg1, txtArg2 ) // Combination of text color, text and string arguments
Fixed a bug where hair/beard items could show up inside corpses
Fixed a bug where items could sometimes vanish (visually) from containers when bouncing back because player was not able to pick them up
Fixed a bug where players would be unable to pick up a freely movable item from a locked down container
Updated handling of item bouncing on pickup to use UOX3 dictionary messages instead of hard-coded client messages
Added support for new Dictionary language:
	dictionary.POL - Polish (SERVERLANGUAGE=8 in uox.ini, or client language 83 if SERVERLANGUAGE is set to 0)
Added taming restrictions for Unicorn, Ki-Rin and Cu Sidhe (js/skill/taming.js)
Added modification of some creatures stats after they've been tamed (js/skill/taming.js)
Added restrictions for who can ride certain creatures (Unicorn, Ki-Rin, Cu Sidhe)
Updated words of power for Meteor Swarm spell to follow the same logic as other spells using Kal (Summon): Kal Des Flam Ylem
Added new Item/Character JS Methods to get/set temporary custom tags which don't persist across worldsaves (or across reconnects, for players):
	.GetTempTag( "tagName" )
	.SetTempTag( "tagName", tagValue )
When applying HPMAX, STAMINAMAX AND MANAMAX DFN tags to new NPCs, their current HP, STAMINA and MANA properties will now be automatically updated to match
Fixed a server crash caused by empty speech messages sent from certain clients
Fixed a bug that prevented teleport locations in Felucca/Trammel from working properly
Updated calculations in code for duration and damage of existing poison strengths (1 - Lesser, 2 - Normal, 3 - Greater, 4 - Deadly) to match with ~Publish 15 (LBR/pre-AoS), and added another poison strength for monster usage (5 - Lethal)
The strength of the Poison and Poison Field spells is now based on the average of the caster's Magery and Poisoning skills, the distance from the target (Poison spell only) and the target's Resisting Spells skill. If caster is further away than 2 tiles from target, the poison strength always equals Lesser Poison, otherwise the following rules apply:
	If caster's combined skill is higher than 100.0, poison strength equals Greater Poison, with 5% chance of Deadly Poison (if Poison spell) or Deadly Poison (if Poison Field spell)
	If caster's combined skill is higher than 70.2, poison strength equals Greater Poison
	If caster's combined skill is higher than 30.2, poison strength equals Normal Poison
	If caster's combined skill is 30.2 or lower, poison strength equals Lesser Poison
	If target resists spell, poison strength is reduced by 1 level, unless it's already at the lowest level
Fixed an issue that would would let players move faster than they should have been allowed to
Fixed misc issues with Party System:
	Mana and Stamina should now update for Party Members when added to the party, when going in/out of range and when their stats update while in range
	Players who get disconnected or relog should now find themselves back in the same party they were in previously
	Added system messages to inform party members about updates to their party, and to let invited players know they've successfully joined a party (or rejoined, if relogging)
World saves now operate on the principle of only saving changes done since the last world save. Combined with the fact that NPCs in UOX3 are only active if there are players in the same/neighbouring map regions, shard owners may expect to see a decrease in world save times ranging from ~11% to ~99%, depending on how active and how spread out their player base is. If no changes have taken place since last save, saves are virtually instantaneous!
2021-09-20 21:35:17 +08:00

5214 lines
174 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 - void pSplit( const std::string pass0, std::string &pass1, std::string &pass2 )
//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 )
{
auto 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] = (UI08)((value&0xFF00)>>8);
toPack[offset+1] = (UI08)((value&0x00FF)%256);
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CPInputBuffer *WhichLoginPacket( UI08 packetID, CSocket *s )
//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; // ConnectUO 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 - CPInputBuffer *WhichPacket( UI08 packetID, CSocket *s )
//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 nullptr; // 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 &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 : " << (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 * 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 = strutil::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 = IP4Address::respond( tSock->ipaddress );
CPGameServerList toSend(1);
toSend.addEntry( cwmWorldState->ServerData()->ServerName(), address.bigEndian() );
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 = IP4Address::respond( tSock->ipaddress );
auto name = cwmWorldState->ServerData()->ServerName();
auto port = cwmWorldState->ServerData()->ServerPort();
CPRelay toSend( ip.littleEndian(), 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& 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( (ClientTypes)actbTemp.dwLastClientType );
tSock->ClientVerShort( (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
{
//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 = strutil::number( majorVersion ) + std::string(".") + strutil::number( minorVersion ) + std::string(".") + strutil::number( clientRevision ) + std::string(".") + strutil::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 >= 0 && 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 < 61 )
tSock->ClientVerShort( CVS_704565 );
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 = strutil::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 );
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 );
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 );
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 < 61 )
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 : " << (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 - 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 );
}
bool CPITips::Handle( void )
{
ScriptSection *Tips = FileLookup->FindEntry( "TIPS", misc_def );
if( Tips != nullptr )
{
UI16 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() && strutil::upper( tag ) == "TIP" )
--x;
if( x <= 0 )
break;
}
if( x > 0 )
tag = Tips->Prev();
data = Tips->GrabData();
sect = "TIP " + strutil::trim( strutil::removeTrailing( data, "//" ));
Tips = FileLookup->FindEntry( sect, misc_def );
if( Tips != nullptr )
{
std::string tipData = "";
for( tag = Tips->First(); !Tips->AtEnd(); tag = Tips->Next() )
{
tipData += Tips->GrabData() + " ";
}
CPUpdScroll toSend( 0, (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 : " << (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 );
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 );
if( cmd == 0x01 )
tSock->sysmessage( 767 );
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;
CSocket *tmpSock = nullptr;
switch( ourChar->GetSpeechMode() )
{
case 3: // Player vendor item pricing
j = static_cast<UI32>(std::stoul(std::string(Text()), nullptr, 0));
if( j >= 0 )
{
speechItem->SetBuyValue( j );
tSock->sysmessage( 753, j );
}
else
tSock->sysmessage( 754, speechItem->GetBuyValue() );
tSock->sysmessage( 755 );
ourChar->SetSpeechMode( 4 );
break;
case 4: // Player vendor item describing
speechItem->SetDesc( Text() );
tSock->sysmessage( 756, Text() );
ourChar->SetSpeechMode( 0 );
break;
case 7: // Rune renaming
speechItem->SetName( Text() );
tSock->sysmessage( 757, Text() );
ourChar->SetSpeechMode( 0 );
break;
case 6: // Name deed
ourChar->SetName( Text() );
tSock->sysmessage( 758, Text() );
ourChar->SetSpeechMode( 0 );
speechItem->Delete();
ourChar->SetSpeechItem( nullptr );
break;
case 5: // Key renaming
case 8: // Sign renaming
speechItem->SetName( Text() );
tSock->sysmessage( 759, Text() );
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() ) ) )
{
HelpRequest *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 = strutil::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;
{
//std::scoped_lock lock(Network->internallock);
Network->pushConn();
for( tmpSock = Network->FirstSocket(); !Network->FinishedSockets(); tmpSock = Network->NextSocket() )
{
CChar *tChar = tmpSock->CurrcharObj();
if( !ValidateObject( tChar ) )
continue;
if( tChar->IsGMPageable() )
{
x = true;
tmpSock->sysmessage( temp );
}
}
Network->popConn();
}
if( x )
tSock->sysmessage( 363 );
else
tSock->sysmessage( 364 );
}
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() ) ) )
{
HelpRequest *tempPageCns;
tempPageCns = CounselorQueue->Current();
tempPageCns->Reason( Text() );
tempPageCns->WhoPaging( ourChar->GetSerial() );
auto temp = strutil::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;
{
//std::scoped_lock lock(Network->internallock);
Network->pushConn();
for( tmpSock = Network->FirstSocket(); !Network->FinishedSockets(); tmpSock = Network->NextSocket() )
{
CChar *tChar = tmpSock->CurrcharObj();
if( !ValidateObject( tChar ) )
continue;
if( tChar->IsGMPageable() )
{
x = true;
tmpSock->sysmessage( temp );
}
}
Network->popConn();
}
if( x )
tSock->sysmessage( 360 );
else
tSock->sysmessage( 361 );
}
ourChar->SetSpeechMode( 0 );
break;
case 0: // normal speech
default:
if( ourChar->GetSquelched() && !ourChar->IsGM() )
tSock->sysmessage( 760 );
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 = (SpeechType)tSock->GetByte( 3 );
textColour = tSock->GetWord( 4 );
fontUsed = tSock->GetWord( 6 );
strcpy( txtSaid, (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 = (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;
// 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; )
{
tSock->AddTrigWord( ((tSock->GetByte( byteNum+tWord )%0x10)<<8) + tSock->GetByte( byteNum+tWord+1 ) );
if( tWord+2 <= numTrigWords )
tSock->AddTrigWord( (tSock->GetByte( byteNum+tWord+2 )<<4) + (tSock->GetByte( byteNum+tWord+3 )>>4));
tWord += 2;
byteNum++;
}
myoffset = 15;
if( (numTrigWords % 2) == 1 ) // odd number ?
myoffset += ( numTrigWords / 2 ) * 3;
else
myoffset += ( ( 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 )
{
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( 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 : " << (SI32)slot << std::endl;
outStream << "Character Name : " << charName << std::endl;
outStream << "Password : " << password << std::endl;
outStream << "Profession : " << (SI32)profession << std::endl;
outStream << "Client Flags : " << (SI32)clientFlags << std::endl;
outStream << "Sex : " << (SI32)sex << std::endl;
outStream << "Race : " << (SI32)race << std::endl;
outStream << "Strength : " << (SI32)str << std::endl;
outStream << "Dexterity : " << (SI32)dex << std::endl;
outStream << "Intelligence : " << (SI32)intel << std::endl;
outStream << "Skin Colour : " << std::hex << skinColour << std::dec << std::endl;
outStream << "Unknown1 : " << (SI32)unknown1 << std::endl;
outStream << "Unknown2 : " << (SI32)unknown2 << std::endl;
outStream << "Skills : " << (SI32)skill[0] << " " << (SI32)skill[1] << " " << (SI32)skill[2] << " " << (SI32)skill[3] << std::endl;
outStream << "Skills Values : " << (SI32)skillValue[0] << " " << (SI32)skillValue[1] << " " << (SI32)skillValue[2] << " " << (SI32)skillValue[3] << std::endl;
SI32 temp1;
for( temp1 = 0; temp1 < 25; temp1++ )
{
outStream << "Unknown3 index " << temp1 << " is hex value: " << std::hex << (UI32)( unknown3[temp1] ) << std::endl;
}
outStream << "Unknown4 : " << (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 : " << (SI32)unknown5 << std::endl;
outStream << "Unknown6 : " << (SI32)unknown6 << std::endl;
outStream << "Unknown7 : " << (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 : " << (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 : " << (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 : " << (SI32)pattern3 << std::endl;
outStream << "Character Name : " << charName << std::endl;
outStream << "Password : " << password << std::endl;
outStream << "Sex : " << (SI32)sex << std::endl;
outStream << "Strength : " << (SI32)str << std::endl;
outStream << "Dexterity : " << (SI32)dex << std::endl;
outStream << "Intelligence : " << (SI32)intel << std::endl;
outStream << "Skills : " << (SI32)skill[0] << " " << (SI32)skill[1] << " " << (SI32)skill[2] << std::endl;
outStream << "Skills Values : " << (SI32)skillValue[0] << " " << (SI32)skillValue[1] << " " << (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 : " << (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& 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 : " << (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 : " << (SI32)type << std::endl;
outStream << "Index : " << (SI32)index << std::endl;
outStream << "Unknown : " << (SI32)unk[0] << " " << (SI32)unk[1] << " " << (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, 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( strutil::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->target( 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->target( 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 )
{
PartyEntry *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 );
else if( !objInRange( mChar, targChar, DIST_NEARBY ) )
tSock->sysmessage( 382 );
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 );
tSock->openPack( pack, true );
}
else
tSock->openPack( pack );
}
else if( targChar->GetNPCAiType() == AI_PLAYERVENDOR )
{
targChar->TextMessage( tSock, 385, TALK, false );
tSock->openPack( pack, true );
}
else
Skills->Snooping( tSock, targChar, pack );
}
else
Console.warning( strutil::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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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->target( 0, TARGET_REMOVEFRIEND, 0, 1852 ); // Select player to remove as friend:
}
else
{
tSock->sysmessage( 393 ); // That is too far away
}
break;
default:
Console.print(strutil::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 );
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( (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 );
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 ? (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 );
else
tSock->sysmessage( 763 );
}
else
{
tSock->CurrentSpellType( 0 );
Magic->SelectSpell( tSock, book );
}
}
else
tSock->sysmessage( 764 );
}
else
tSock->sysmessage( 765 );
}
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( (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 );
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 OnSpecialMove returned true - let's check global script!
cScript *toExecute = JSMapping->GetScript( (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 );
}