Browse Source

working mail listing, item retrieval, send mail (with items)

Fix #246
Image 3 years ago
parent
commit
0c338ee6ff

+ 39 - 3
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -2157,8 +2157,44 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			break;
 									  }
 		case COMMAND_SET_MAIL_ITEM: {
-			LogWrite(MISC__TODO, 1, "TODO", " received set_mail_item\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
-			if (sep && sep->arg[0]) LogWrite(COMMAND__DEBUG, 0, "Command", "%s\n", sep->argplus[0]);
+			if(sep && sep->IsNumber(0))
+			{
+				Item* item = client->GetPlayer()->item_list.GetItemFromIndex(atoul(sep->arg[0]));
+				if(item)
+				{
+					int16 quantity = atoul(sep->arg[1]);
+					if(item->CheckFlag(NO_TRADE) || item->CheckFlag(ATTUNED) || item->CheckFlag(ARTIFACT) || item->CheckFlag2(HEIRLOOM))
+					{
+						return;
+					}
+					if(item->IsBag())
+					{
+						vector<Item*>* bag_items = player->GetPlayerItemList()->GetItemsInBag(item);
+						if(bag_items && bag_items->size() > 0)
+						{
+							client->SimpleMessage(CHANNEL_COLOR_RED,"You cannot mail a bag with items inside it.");
+							safe_delete(bag_items);
+							return;
+						}
+						safe_delete(bag_items);
+					}
+					Item* itemtoadd = item;
+					if(quantity > 0)
+					{
+						if(quantity > item->details.count)
+							return;
+
+						Item* tmpItem = new Item(item);
+						tmpItem->details.count = quantity;
+						itemtoadd = tmpItem;
+					}
+						
+					if(client->AddMailItem(itemtoadd))
+					{
+						client->RemoveItem(item, quantity, true);
+					}
+				}
+			}
 			break;
 							   }
 		case COMMAND_REMOVE_MAIL_PLAT: {
@@ -2187,7 +2223,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			break;
 											}
 		case COMMAND_CANCEL_SEND_MAIL: {
-			client->CancelSendMail();
+			client->ResetSendMail();
 			break;
 									   }
 		case COMMAND_DELETE_MAIL_MESSAGE: {

+ 2 - 2
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -4254,8 +4254,8 @@ void WorldDatabase::SavePlayerMail(Mail* mail) {
 	Query query_insert;
 	if (mail) {
 		if(mail->mail_id > 0)
-			query_update.RunQuery2(Q_UPDATE, "UPDATE `character_mail` SET `already_read`=%u, `coin_copper`=%u, `coin_silver`=%u, `coin_gold`=%u, `coin_plat`=%u WHERE `id`=%u", mail->already_read, mail->coin_copper, mail->coin_silver, mail->coin_gold, mail->coin_plat, mail->mail_id);
-		if (mail->mail_id == 0 || query_update.GetAffectedRows() == 0)
+			query_update.RunQuery2(Q_UPDATE, "UPDATE `character_mail` SET `already_read`=%u, `coin_copper`=%u, `coin_silver`=%u, `coin_gold`=%u, `coin_plat`=%u, `char_item_id`=%u, `stack`=%u WHERE `id`=%u", mail->already_read, mail->coin_copper, mail->coin_silver, mail->coin_gold, mail->coin_plat, mail->char_item_id, mail->stack, mail->mail_id);
+		else if (mail->mail_id == 0 || query_update.GetAffectedRows() == 0)
 		{
 			query_insert.RunQuery2(Q_INSERT, "INSERT INTO `character_mail` (`player_to_id`, `player_from`, `subject`, `mail_body`, `already_read`, `mail_type`, `coin_copper`, `coin_silver`, `coin_gold`, `coin_plat`, `stack`, `postage_cost`, `attachment_cost`, `char_item_id`, `time_sent`, `expire_time`) VALUES (%u, '%s', '%s', '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", mail->player_to_id, mail->player_from.c_str(), getSafeEscapeString(mail->subject.c_str()).c_str(), getSafeEscapeString(mail->mail_body.c_str()).c_str(), mail->already_read, mail->mail_type, mail->coin_copper, mail->coin_silver, mail->coin_gold, mail->coin_plat, mail->stack, mail->postage_cost, mail->attachment_cost, mail->char_item_id, mail->time_sent, mail->expire_time);
 			

+ 151 - 40
EQ2/source/WorldServer/client.cpp

@@ -127,7 +127,8 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
 	ip = eqs->GetrIP();
 	port = ntohs(eqs->GetrPort());
 	merchant_transaction = nullptr;
-	SetMailTransaction(nullptr);
+	mail_window.item = nullptr; // don't want this to be set(loose ptr) when using ResetSendMail to provide rest of the defaults
+	ResetSendMail(false);
 	timestamp_flag = 0;
 	current_quest_id = 0;
 	last_update_time = 0;
@@ -2969,6 +2970,7 @@ bool Client::Process(bool zone_process) {
 	if (quest_pos_timer.Check())
 		CheckPlayerQuestsLocationUpdate();
 	if (camp_timer && camp_timer->Check() && getConnection()) {
+		ResetSendMail();
 		getConnection()->SendDisconnect(false);
 		safe_delete(camp_timer);
 		disconnect_timer = new Timer(2000);
@@ -3910,6 +3912,7 @@ void Client::Zone(ZoneServer* new_zone, bool set_coords) {
 	player->SaveSpellEffects();
 	player->SetSaveSpellEffects(true);
 	MSaveSpellStateMutex.unlock();
+	ResetSendMail();
 	// Remove players pet from zone if there is one
 	((Entity*)player)->DismissAllPets();
 
@@ -6311,7 +6314,7 @@ bool Client::AddItemToBank(Item* item) {
 
 	return true;
 }
-bool Client::RemoveItem(Item* item, int16 quantity) {
+bool Client::RemoveItem(Item* item, int16 quantity, bool force_override_no_delete) {
 	EQ2Packet* outapp;
 	bool delete_item = false;
 
@@ -6327,6 +6330,9 @@ bool Client::RemoveItem(Item* item, int16 quantity) {
 		delete_item = true;
 	}
 
+	if(force_override_no_delete)
+		delete_item = false;
+	
 	if ((outapp = player->SendInventoryUpdate(version))) {
 		QueuePacket(outapp);
 		if (item->GetItemScript() && lua_interface)
@@ -6363,13 +6369,10 @@ Spawn* Client::GetMerchantTransaction() {
 }
 
 void Client::SetMailTransaction(Spawn* spawn) {
+	ResetSendMail(spawn ? false : true);
+	MMailWindowMutex.lock();
 	mail_transaction = spawn;
-	mail_window.coin_copper = 0;
-	mail_window.coin_silver = 0;
-	mail_window.coin_gold = 0;
-	mail_window.coin_plat = 0;
-	mail_window.char_item_id = 0;
-
+	MMailWindowMutex.unlock();
 }
 
 Spawn* Client::GetMailTransaction() {
@@ -7559,13 +7562,25 @@ void Client::SendMailList() {
 				p->setArrayDataByName("coin_silver", mail->coin_silver, i);
 				p->setArrayDataByName("coin_gold", mail->coin_gold, i);
 				p->setArrayDataByName("coin_plat", mail->coin_plat, i);
-				if(mail->stack)
-					p->setArrayDataByName("num_items", mail->stack, i);
 
 				//p->setArrayDataByName("unknown2", 0, i);
 
-				if(mail->stack)
+				bool successItemAdd = false;
+				if(mail->stack && mail->char_item_id)
 				{
+					Item* item = master_item_list.GetItem(mail->char_item_id);
+					if(item)
+					{
+						item->stack_count = mail->stack > 1 ? mail->stack : 0;
+						if (version < 860)
+							p->setItemArrayDataByName("item", item, player, i, 0, -1);
+						else if (version < 1193)
+							p->setItemArrayDataByName("item", item, player, i);
+						else
+							p->setItemArrayDataByName("item", item, player, i, 0, 2);
+						
+						successItemAdd = true;
+					}
 					// need double item packet support for serialize, LE was working on it for crafting so don't want to double down the work
 					//Item* item = master_item_list.GetItem(mail->char_item_id);
 					//EQ2Packet* pack = item->serialize(GetVersion(), true, GetPlayer(), true, GetItemPacketType(GetVersion()), 0, false, false);
@@ -7574,10 +7589,14 @@ void Client::SendMailList() {
 					//p->setItemArrayDataByName("item", item, GetPlayer(), i, 0, 2, true, true);
 					//DumpPacket(pack);
 				}
-				else
+				
+				if(!successItemAdd)
 				{
-					p->setArrayDataByName("packettype", GetItemPacketType(GetVersion()), i);
-					p->setArrayDataByName("packetsubtype", 0xFF, i);
+					//p->setArrayDataByName("packettype", GetItemPacketType(GetVersion()), i);
+					//p->setArrayDataByName("packetsubtype", 0xFF, i);
+					p->setArrayDataByName("end_tag1", 0xFFFFFFFF, i);
+					p->setArrayDataByName("end_tag2", GetItemPacketType(GetVersion()), i);
+					p->setArrayDataByName("end_tag3", 0xFF, i);
 				}
 				//p->setArrayDataByName("item_id", 5, i, 0);
 				//Item* item = master_item_list.GetItem(5);
@@ -7603,7 +7622,7 @@ void Client::SendMailList() {
 			p->setDataByName("unknown3", 0x01F4);
 			p->setDataByName("unknown4", 0x01000000);
 			EQ2Packet* pack = p->serialize();
-			//DumpPacket(pack->pBuffer, pack->size);
+			DumpPacket(pack->pBuffer, pack->size);
 			QueuePacket(pack);
 			safe_delete(p);
 		}
@@ -7643,16 +7662,33 @@ void Client::DisplayMailMessage(int32 mail_id) {
 				packet->setDataByName("coin_silver", mail->coin_silver);
 				packet->setDataByName("coin_gold", mail->coin_gold);
 				packet->setDataByName("coin_plat", mail->coin_plat);
-				packet->setDataByName("stack", mail->stack);
-				packet->setDataByName("packettype", GetItemPacketType(GetVersion()));
-				packet->setDataByName("packetsubtype", 0xFF);
-				packet->setDataByName("unknown4", 0);
-				packet->setDataByName("unknown5", 0);
-				packet->setDataByName("unknown6", 0);
-				packet->setDataByName("unknown7", 0);
+				if(mail->stack || mail->char_item_id)
+				{
+					Item* item = master_item_list.GetItem(mail->char_item_id);
+					item->stack_count = mail->stack > 1 ? mail->stack : 0;
+					if (version < 860)
+						packet->setItemByName("item", item, player, 0, -1);
+					else if (version < 1193)
+						packet->setItemByName("item", item, player, 0, 0);
+					else
+						packet->setItemByName("item", item, player, 0, 2);
+				}
+				else
+				{
+					packet->setDataByName("end_tag", 0xFFFFFFFF);
+
+					/*packet->setDataByName("packettype", GetItemPacketType(GetVersion()));
+					packet->setDataByName("packetsubtype", 0xFF);
+					packet->setDataByName("unknown4", 0);
+					packet->setDataByName("unknown5", 0);
+					packet->setDataByName("unknown6", 0);
+					packet->setDataByName("unknown7", 0);*/
+				}
 				mail->already_read = true;
 				mail->save_needed = true;
-				QueuePacket(packet->serialize());
+				EQ2Packet* pack = packet->serialize();
+				DumpPacket(pack);
+				QueuePacket(pack);
 				safe_delete(packet);
 				// trying to update this causes the window not to open
 				//SendMailList();
@@ -7673,6 +7709,7 @@ void Client::HandleSentMail(EQApplicationPacket* app) {
 		string player_to = packet->getType_EQ2_16BitString_ByName("player_to").data;
 		PacketStruct* reply_packet = configReader.getStruct("WS_MailSendMessageReply", GetVersion());
 		vector<int32>* ids = 0;
+		MMailWindowMutex.lock();
 		if (reply_packet) {
 			int8 reply_type = MAIL_SEND_RESULT_UNKNOWN_ERROR;
 			if (player_to.length() == 0)
@@ -7682,8 +7719,8 @@ void Client::HandleSentMail(EQApplicationPacket* app) {
 			else if (GetAdminStatus() == 0 && !player->RemoveCoins(10))
 				reply_type = MAIL_SEND_RESULT_NOT_ENOUGH_COIN;
 			else {
-				if (player_to.compare("<all>") == 0) {
-					if (mail_window.coin_copper + mail_window.coin_silver + mail_window.coin_gold + mail_window.coin_plat == 0)
+				if (GetAdminStatus() > 200 && player_to.compare("<all>") == 0) {
+					if (mail_window.char_item_id == 0 && (mail_window.coin_copper + mail_window.coin_silver + mail_window.coin_gold + mail_window.coin_plat) == 0)
 						ids = database.GetAllPlayerIDs();
 					else
 						SimpleMessage(CHANNEL_NARRATIVE, "You may not mail gifts to multiple players.");
@@ -7709,7 +7746,8 @@ void Client::HandleSentMail(EQApplicationPacket* app) {
 							mail->coin_silver = mail_window.coin_silver;
 							mail->coin_gold = mail_window.coin_gold;
 							mail->coin_plat = mail_window.coin_plat;
-							mail->stack = 0;
+							mail->char_item_id = mail_window.char_item_id;
+							mail->stack = mail_window.stack;
 
 							// GM's send mail for free!
 							if (GetAdminStatus() > 0)
@@ -7722,7 +7760,6 @@ void Client::HandleSentMail(EQApplicationPacket* app) {
 								mail->postage_cost = 10;
 								mail->attachment_cost = 50;
 							}
-							mail->char_item_id = 0;
 							mail->time_sent = Timer::GetUnixTimeStamp();
 							mail->expire_time = mail->time_sent + 2592000;	//30 days in seconds
 							//int16 packettype = packet->getType_int16_ByName("packettype");
@@ -7738,10 +7775,7 @@ void Client::HandleSentMail(EQApplicationPacket* app) {
 								to_client->SimpleMessage(CHANNEL_NARRATIVE, "You've got mail! :)");
 							}
 							database.SavePlayerMail(mail);
-							mail_window.coin_copper = 0;
-							mail_window.coin_silver = 0;
-							mail_window.coin_gold = 0;
-							mail_window.coin_plat = 0;
+							ResetSendMail(false, false);
 						}
 						else
 							reply_type = MAIL_SEND_RESULT_UNKNOWN_PLAYER;
@@ -7764,6 +7798,7 @@ void Client::HandleSentMail(EQApplicationPacket* app) {
 			safe_delete(ids);
 		}
 		safe_delete(packet);
+		MMailWindowMutex.unlock();
 	}
 
 }
@@ -7773,11 +7808,53 @@ void Client::DeleteMail(int32 mail_id, bool from_database) {
 	player->DeleteMail(mail_id, from_database);
 
 }
-
+bool Client::AddMailItem(Item* item)
+{
+	bool ret = false;
+	if (GetMailTransaction()) {
+		MMailWindowMutex.lock();
+		if(mail_window.char_item_id == 0)
+		{
+			mail_window.item = item;
+			mail_window.char_item_id = item->details.item_id;
+			mail_window.stack = item->details.count;
+			ret = true;
+		PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
+				packet->setDataByName("coin_copper", mail_window.coin_copper);
+				packet->setDataByName("coin_silver", mail_window.coin_silver);
+				packet->setDataByName("coin_gold", mail_window.coin_gold);
+				packet->setDataByName("coin_plat", mail_window.coin_plat);
+					if(item)
+					{
+					packet->setDataByName("stack", mail_window.stack);
+					item->stack_count = mail_window.stack;
+					if (version < 860)
+						packet->setItemByName("item", item, player, 0, -1);
+					else if (version < 1193)
+						packet->setItemByName("item", item, player, 0, 0);
+					else
+						packet->setItemByName("item", item, player, 0, 2);
+					}
+					else
+					{
+						packet->setDataByName("end_tag2", GetItemPacketType(GetVersion()));
+						packet->setDataByName("end_tag3", 0xFF);
+					}
+				//packet->setDataByName("packettype", GetItemPacketType(version));
+				//packet->setDataByName("packetsubtype", 0xFF);
+				//packet->setDataByName("unknown2", 0);
+				//packet->PrintPacket();
+				QueuePacket(packet->serialize());
+		}
+		MMailWindowMutex.unlock();
+	}
+	return ret;
+}
 bool Client::AddMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
 
 	bool ret = false;
 	if (GetMailTransaction()) {
+	MMailWindowMutex.lock();
 		PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
 		if (packet) {
 			if (copper > 0) {
@@ -7813,10 +7890,23 @@ bool Client::AddMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
 				packet->setDataByName("coin_silver", mail_window.coin_silver);
 				packet->setDataByName("coin_gold", mail_window.coin_gold);
 				packet->setDataByName("coin_plat", mail_window.coin_plat);
-				packet->setDataByName("stack", 0);
-				packet->setDataByName("packettype", GetItemPacketType(version));
-				packet->setDataByName("packetsubtype", 0xFF);
-				packet->setDataByName("unknown2", 0);
+					Item* item = master_item_list.GetItem(mail_window.char_item_id);
+					if(item)
+					{
+					packet->setDataByName("stack", mail_window.stack);
+					item->stack_count = mail_window.stack;
+					if (version < 860)
+						packet->setItemByName("item", item, player, 0, -1);
+					else if (version < 1193)
+						packet->setItemByName("item", item, player, 0, 0);
+					else
+						packet->setItemByName("item", item, player, 0, 2);
+					}
+					else
+					{
+						packet->setDataByName("end_tag2", GetItemPacketType(GetVersion()));
+						packet->setDataByName("end_tag3", 0xFF);
+					}
 				//packet->PrintPacket();
 				QueuePacket(packet->serialize());
 			}
@@ -7824,6 +7914,7 @@ bool Client::AddMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
 				SimpleMessage(CHANNEL_NARRATIVE, "You don't have that much money.");
 			safe_delete(packet);
 		}
+		MMailWindowMutex.unlock();
 	}
 	else
 		SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
@@ -7834,6 +7925,7 @@ bool Client::AddMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
 bool Client::RemoveMailCoin(int32 copper, int32 silver, int32 gold, int32 plat) {
 	bool ret = false;
 	if (GetMailTransaction()) {
+		MMailWindowMutex.lock();
 		PacketStruct* packet = configReader.getStruct("WS_UpdatePlayerMail", GetVersion());
 		if (packet) {
 			if (copper > 0) {
@@ -7873,6 +7965,7 @@ bool Client::RemoveMailCoin(int32 copper, int32 silver, int32 gold, int32 plat)
 			}
 			safe_delete(packet);
 		}
+		MMailWindowMutex.unlock();
 	}
 	else
 		SimpleMessage(CHANNEL_NARRATIVE, "You are currently not in a mail transaction.");
@@ -7902,7 +7995,9 @@ void Client::TakeMailAttachments(int32 mail_id) {
 				mail->coin_plat = 0;
 			}
 			if (mail->char_item_id > 0) {
-				LogWrite(MISC__TODO, 1, "TODO", "Items by Mail\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
+				AddItem(mail->char_item_id, mail->stack);
+				mail->char_item_id = 0;
+				mail->stack = 0;
 			}
 			/*	Can't find the right packet to send to update the player's mail.  This packet below updates the mail the player is sending, not
 				the mail the player is getting attachments from.  There is an opcode OP_MailRemoveAttachFromMailMsg with opcode 328 but i can't
@@ -7934,15 +8029,31 @@ void Client::TakeMailAttachments(int32 mail_id) {
 
 }
 
-void Client::CancelSendMail() {
-	SimpleMessage(CHANNEL_NARRATIVE, "You cancel sending a letter.");
-	player->AddCoins(mail_window.coin_copper + (mail_window.coin_silver * 100) + (mail_window.coin_gold * 10000) + (mail_window.coin_plat * 1000000));
+void Client::ResetSendMail(bool cancel, bool needslock) {
+	if(cancel && mail_transaction)
+		SimpleMessage(CHANNEL_NARRATIVE, "You cancel sending a letter.");
+	if(needslock)
+		MMailWindowMutex.lock();
+	if(cancel)
+		player->AddCoins(mail_window.coin_copper + (mail_window.coin_silver * 100) + (mail_window.coin_gold * 10000) + (mail_window.coin_plat * 1000000));
+	if(!cancel)
+		mail_transaction = 0;
 	mail_window.coin_copper = 0;
 	mail_window.coin_silver = 0;
 	mail_window.coin_gold = 0;
 	mail_window.coin_plat = 0;
 	mail_window.char_item_id = 0;
+	mail_window.stack = 0;
 
+	if(mail_window.item){
+		if(cancel)
+			AddItem(mail_window.item);
+		else
+			safe_delete(mail_window.item);
+	}
+	mail_window.item = nullptr;
+	if(needslock)
+		MMailWindowMutex.unlock();
 }
 
 bool Client::GateAllowed() {

+ 6 - 2
EQ2/source/WorldServer/client.h

@@ -99,7 +99,9 @@ struct MailWindow {
 	int32	coin_silver;
 	int32	coin_gold;
 	int32	coin_plat;
+	Item*	item;
 	int32	char_item_id;
+	int32	stack;
 };
 
 struct PendingGuildInvite {
@@ -197,7 +199,7 @@ public:
 	bool	AddItem(Item* item);
 	bool	AddItemToBank(int32 item_id, int16 quantity = 0);
 	bool	AddItemToBank(Item* item);
-	bool	RemoveItem(Item *item, int16 quantity);
+	bool	RemoveItem(Item *item, int16 quantity, bool force_override_no_delete = false);
 	void	ProcessTeleport(Spawn* spawn, vector<TransportDestination*>* destinations, int32 transport_id = 0);
 	void	ProcessTeleportLocation(EQApplicationPacket* app); 
 
@@ -318,10 +320,11 @@ public:
 	void	DisplayMailMessage(int32 mail_id);
 	void	HandleSentMail(EQApplicationPacket* app);
 	void	DeleteMail(int32 mail_id, bool from_database = false);
+	bool	AddMailItem(Item* item);
 	bool	AddMailCoin(int32 copper, int32 silver = 0, int32 gold = 0, int32 plat = 0);
 	bool	RemoveMailCoin(int32 copper, int32 silver = 0, int32 gold = 0, int32 plat = 0);
 	void	TakeMailAttachments(int32 mail_id);
-	void	CancelSendMail();
+	void	ResetSendMail(bool cancel = true, bool needslock = true);
 	bool	GateAllowed();
 	bool	BindAllowed();
 	bool	Bind();
@@ -556,6 +559,7 @@ private:
 	ZoneServer* current_zone;
 	int32	name_crc;
 	MailWindow	mail_window;
+	std::mutex	MMailWindowMutex;
 	PendingGuildInvite	pending_guild_invite;
 	PendingResurrection current_rez;
 	string* pending_last_name;