package com.aionemu.gameserver.services.mail;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aionemu.gameserver.configs.main.LoggingConfig;
import com.aionemu.gameserver.dao.*;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.Letter;
import com.aionemu.gameserver.model.gameobjects.LetterType;
import com.aionemu.gameserver.model.gameobjects.Persistable.PersistentState;
import com.aionemu.gameserver.model.gameobjects.player.BlockList;
import com.aionemu.gameserver.model.gameobjects.player.Mailbox;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.player.PlayerCommonData;
import com.aionemu.gameserver.model.items.storage.Storage;
import com.aionemu.gameserver.model.items.storage.StorageType;
import com.aionemu.gameserver.model.templates.item.Disposition;
import com.aionemu.gameserver.model.templates.mail.MailMessage;
import com.aionemu.gameserver.network.aion.serverpackets.SM_DELETE_ITEM;
import com.aionemu.gameserver.network.aion.serverpackets.SM_MAIL_SERVICE;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.services.AdminService;
import com.aionemu.gameserver.services.item.ItemFactory;
import com.aionemu.gameserver.services.item.ItemPacketService;
import com.aionemu.gameserver.services.trade.PricesService;
import com.aionemu.gameserver.taskmanager.tasks.ExpireTimerTask;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.audit.AuditLogger;
import com.aionemu.gameserver.utils.collections.DynamicServerPacketBodySplitList;
import com.aionemu.gameserver.utils.collections.SplitList;
import com.aionemu.gameserver.utils.idfactory.IDFactory;
import com.aionemu.gameserver.world.World;
/**
* @author kosyachok
*/
public class MailService {
private static final Logger log = LoggerFactory.getLogger("MAIL_LOG");
private MailService() {
}
public static void sendMail(Player sender, String recipientName, String title, String message, int attachedItemObjId, long attachedItemCount,
long attachedKinah, LetterType letterType) {
if (sender.isTrading() || recipientName.length() > 16)
return;
if (letterType == LetterType.BLACKCLOUD || attachedKinah < 0) {
AuditLogger.log(sender, "tried to send letter of type " + letterType + " with " + attachedKinah + " Kinah");
return;
}
if (title.length() > 20)
title = title.substring(0, 20);
if (message.length() > 1000)
message = message.substring(0, 1000);
PlayerCommonData recipientCommonData = PlayerDAO.loadPlayerCommonDataByName(recipientName);
MailMessage status = validateRecipient(sender, recipientCommonData);
if (status != MailMessage.MAIL_SEND_SUCCESS) {
PacketSendUtility.sendPacket(sender, new SM_MAIL_SERVICE(status));
return;
}
Item senderItem = null;
int baseCost = letterType == LetterType.EXPRESS ? 500 : 10;
int costFactor = letterType == LetterType.EXPRESS ? 5 : 1;
long kinahMailCommission = 0;
long itemMailCommission = 0;
Storage senderInventory = sender.getInventory();
if (attachedItemObjId != 0 && attachedItemCount > 0) {
senderItem = senderInventory.getItemByObjId(attachedItemObjId);
if (senderItem == null || senderItem.getItemCount() < attachedItemCount) {
PacketSendUtility.sendPacket(sender, SM_SYSTEM_MESSAGE.STR_MAIL_SEND_USED_ITEM());
return;
}
if (senderItem.isEquipped()) {
PacketSendUtility.sendPacket(sender, SM_SYSTEM_MESSAGE.STR_MAIL_SEND_CAN_NOT_SEND_EQUIPPED_ITEM());
return;
}
if (!AdminService.getInstance().canOperate(sender, null, senderItem, "mail"))
return;
itemMailCommission = (long) (senderItem.getItemTemplate().getPrice() * getQualityPriceRate(senderItem) * attachedItemCount * costFactor);
}
if (attachedKinah > 0)
kinahMailCommission = (long) (attachedKinah * 0.01f * costFactor);
long finalMailKinah = PricesService.getPriceForService(baseCost + kinahMailCommission + itemMailCommission, sender.getRace()) + attachedKinah;
if (senderInventory.getKinah() < finalMailKinah) {
PacketSendUtility.sendPacket(sender, SM_SYSTEM_MESSAGE.STR_NOT_ENOUGH_MONEY());
return;
}
Item attachedItem = null;
if (senderItem != null) {
// Check Mailing untradables with Cash items (Special courier passes)
if (senderItem.getPackCount() <= 0 && !senderItem.isTradeable()) {
Disposition dispo = senderItem.getItemTemplate().getDisposition();
if (dispo == null || dispo.getId() == 0 || dispo.getCount() == 0) // can not be traded, hack
return;
if (senderInventory.getItemCountByItemId(dispo.getId()) >= dispo.getCount()) {
senderInventory.decreaseByItemId(dispo.getId(), dispo.getCount());
} else
return;
}
// reuse item in case of full decrease of count
if (senderItem.getItemCount() == attachedItemCount) {
senderInventory.remove(senderItem);
PacketSendUtility.sendPacket(sender, new SM_DELETE_ITEM(attachedItemObjId));
attachedItem = senderItem;
} else if (senderItem.getItemCount() > attachedItemCount) {
attachedItem = ItemFactory.newItem(senderItem.getItemTemplate().getTemplateId(), attachedItemCount);
senderInventory.decreaseItemCount(senderItem, attachedItemCount);
}
if (attachedItem == null)
return;
// unpack
if (attachedItem.getPackCount() > 0)
attachedItem.setPackCount(attachedItem.getPackCount() * -1);
attachedItem.setItemLocation(StorageType.MAILBOX.getId());
}
senderInventory.decreaseKinah(finalMailKinah);
Letter newLetter = new Letter(IDFactory.getInstance().nextId(), recipientCommonData.getPlayerObjId(), attachedItem, attachedKinah, title, message,
sender.getName(), new Timestamp(System.currentTimeMillis()), true, letterType);
// first save attached item for FK consistency
if (attachedItem != null) {
if (!InventoryDAO.store(attachedItem, recipientCommonData.getPlayerObjId()))
return;
// save item stones too
ItemStoneListDAO.save(Collections.singletonList(attachedItem));
}
// save letter
if (!MailDAO.storeLetter(newLetter))
return;
if (attachedItem != null && LoggingConfig.LOG_MAIL)
log.info("Player: " + sender.getName() + " sent item " + attachedItem.getItemId() + " [" + attachedItem.getItemName() + "] (count: "
+ attachedItem.getItemCount() + ") to player " + recipientName);
PacketSendUtility.sendPacket(sender, new SM_MAIL_SERVICE(status));
SystemMailService.updateRecipientMailbox(recipientCommonData, newLetter);
}
private static MailMessage validateRecipient(Player sender, PlayerCommonData recipientCommonData) {
if (recipientCommonData == null)
return MailMessage.NO_SUCH_CHARACTER_NAME;
if (recipientCommonData.getRace() != sender.getRace() && !sender.isStaff())
return MailMessage.MAIL_IS_ONE_RACE_ONLY;
if (recipientCommonData.getMailboxLetters() >= 100)
return MailMessage.RECIPIENT_MAILBOX_FULL;
Player p = World.getInstance().getPlayer(recipientCommonData.getPlayerObjId());
BlockList blockList = p != null ? p.getBlockList() : BlockListDAO.load(recipientCommonData.getPlayerObjId());
if (blockList.contains(sender.getObjectId()))
return MailMessage.YOU_ARE_IN_RECIPIENT_IGNORE_LIST;
return MailMessage.MAIL_SEND_SUCCESS;
}
private static float getQualityPriceRate(Item senderItem) {
switch (senderItem.getItemTemplate().getItemQuality()) {
case MYTHIC:
case EPIC:
return 0.05f;
case UNIQUE:
case LEGEND:
return 0.04f;
case RARE:
return 0.03f;
default:
return 0.02f;
}
}
/**
* Read letter with specified letter id
*/
public static void readMail(Player player, int letterId) {
Letter letter = player.getMailbox().getLetterFromMailbox(letterId);
if (letter == null) {
log.warn("Cannot read mail " + player.getObjectId() + " " + letterId);
return;
}
PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE(player, letter, letter.getTimeStamp().getTime()));
letter.setReadLetter();
}
public static void getAttachments(Player player, int letterId, byte attachmentType) {
Letter letter = player.getMailbox().getLetterFromMailbox(letterId);
if (letter == null)
return;
switch (attachmentType) {
case 0:
Item attachedItem = letter.getAttachedItem();
if (attachedItem == null)
return;
if (player.getInventory().isFull()) {
PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MAIL_TAKE_ALL_CANCEL());
return;
}
if (attachedItem.getExpireTime() != 0 && attachedItem.getExpireTime() <= System.currentTimeMillis() / 1000) {
attachedItem.setPersistentState(PersistentState.DELETED);
InventoryDAO.store(attachedItem, player);
} else {
if (player.getInventory().add(attachedItem, ItemPacketService.ItemAddType.MAIL) == null)
return;
ExpireTimerTask.getInstance().registerExpirable(attachedItem, player);
}
PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE(letterId, attachmentType));
letter.setAttachedItem(null);
break;
case 1:
// TODO normal fix
// fix for kinah dupe
long attachedKinahCount = letter.getAttachedKinah();
letter.removeAttachedKinah();
if (!MailDAO.storeLetter(letter)) {
AuditLogger.log(player, "tried to use kinah mail exploit. Location: " + player.getPosition() + ", kinah count: " + attachedKinahCount);
return;
}
player.getInventory().increaseKinah(attachedKinahCount);
PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE(letterId, attachmentType));
break;
}
}
public static void deleteMail(Player player, int[] mailObjId) {
Mailbox mailbox = player.getMailbox();
for (int letterId : mailObjId) {
mailbox.removeLetter(letterId);
MailDAO.deleteLetter(letterId);
}
PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE(mailObjId));
}
public static void onPlayerLogin(Player player) {
player.setMailbox(MailDAO.loadPlayerMailbox(player));
PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE());
}
public static void sendMailList(Player player, boolean isExpress, boolean sendRefreshPacket) {
if (sendRefreshPacket)
PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE());
Stream<Letter> letterStream = player.getMailbox().getLetters().stream();
if (isExpress)
letterStream = letterStream.filter(letter -> letter.isExpress() && letter.isUnread());
List<Letter> letters = letterStream.sorted(Comparator.comparing(Letter::getTimeStamp).reversed()).collect(Collectors.toList());
SplitList<Letter> mailSplitList = new DynamicServerPacketBodySplitList<>(letters, true, SM_MAIL_SERVICE.STATIC_BODY_SIZE,
SM_MAIL_SERVICE.DYNAMIC_BODY_PART_SIZE_CALCULATOR);
mailSplitList.forEach(part -> PacketSendUtility.sendPacket(player, new SM_MAIL_SERVICE(player, part, part.isLast())));
}
}