테스트

aion-server 4.8

Gitteol
최고관리자 · 1 · 💬 0 클론/새로받기
 4.8 61f661d · 1 commits 새로받기(Pull)
game-server/src/com/aionemu/gameserver/services/HousingBidService.java
package com.aionemu.gameserver.services;

import java.time.DayOfWeek;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BinaryOperator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aionemu.gameserver.configs.main.HousingConfig;
import com.aionemu.gameserver.configs.main.LoggingConfig;
import com.aionemu.gameserver.dao.HouseBidsDAO;
import com.aionemu.gameserver.model.Race;
import com.aionemu.gameserver.model.gameobjects.Letter;
import com.aionemu.gameserver.model.gameobjects.Persistable;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.player.PlayerCommonData;
import com.aionemu.gameserver.model.house.House;
import com.aionemu.gameserver.model.house.HouseBids;
import com.aionemu.gameserver.network.aion.serverpackets.SM_RECEIVE_BIDS;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.services.mail.AuctionResult;
import com.aionemu.gameserver.services.mail.MailFormatter;
import com.aionemu.gameserver.services.player.PlayerService;
import com.aionemu.gameserver.taskmanager.tasks.housing.AuctionEndTask;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.time.ServerTime;

/**
 * @author Rolandas, Neon
 */
public class HousingBidService {

	private static final Logger log = LoggerFactory.getLogger("HOUSE_AUCTION_LOG");
	private static final HousingBidService instance = new HousingBidService();
	private final Map<Integer, HouseBids> bids = new ConcurrentHashMap<>();

	private HousingBidService() {
		Set<Integer> deletedPlayerIds = HouseBidsDAO.loadBids(bids);
		deletedPlayerIds.forEach(this::disableBids);
		setBidInfoToHouses();
		log.info("Loaded bids for " + bids.size() + " houses");
	}

	private void setBidInfoToHouses() {
		HousingService.getInstance().getCustomHouses().forEach(house -> {
			house.setBids(getBidInfo(house), true);
			if (house.getBids() != null && house.isInactive())
				log.warn(house + " is for auction but inactive.");
		});
	}

	public static HousingBidService getInstance() {
		return instance;
	}

	public boolean isRegisteringAllowed() {
		if (!HousingConfig.ENABLE_HOUSE_AUCTIONS)
			return false;
		int today = ServerTime.now().getDayOfWeek().getValue();
		int from = HousingConfig.HOUSE_AUCTION_REGISTER_DAYS[0];
		int to = HousingConfig.HOUSE_AUCTION_REGISTER_DAYS[1];
		if (from > to) // e.g. saturday (6) to wednesday (3)
			return from <= today || to >= today;
		else // e.g. monday (1) to friday (5)
			return from <= today && to >= today;
	}

	public boolean auction(House house, long initialPrice) {
		HouseBids houseBids = new HouseBids(house.getObjectId(), initialPrice);
		HouseBids.Bid bid = houseBids.getHighestBid();
		if (bids.putIfAbsent(house.getObjectId(), houseBids) != null)
			return false;
		if (house.getPersistentState() == Persistable.PersistentState.NEW) // house must exist in DB before saving a bid due to foreign key
			house.save();
		if (!HouseBidsDAO.addBid(bid)) {
			bids.remove(house.getObjectId(), houseBids);
			return false;
		}
		house.setBids(houseBids, true);
		house.getController().updateSign();
		house.getController().updateAppearance();
		return true;
	}

	private boolean isAuctionOpen(int houseObjectId) {
		return bids.containsKey(houseObjectId) && isBiddingTime(houseObjectId);
	}

	private boolean isBiddingTime(int houseObjectId) {
		ZonedDateTime now = ServerTime.now();
		return now.getDayOfWeek() != DayOfWeek.SUNDAY || now.getHour() < 12 || AuctionEndTask.getInstance().isAuctionProlonged(houseObjectId);
	}

	public HouseBids getBidInfo(House house) {
		return bids.get(house.getObjectId());
	}

	public List<HouseBids> getBidInfo(Race race) {
		List<HouseBids> houseBids = new ArrayList<>();
		for (HouseBids bidInfo : bids.values()) {
			if (HousingService.getInstance().findHouse(bidInfo.getHouseObjectId()).matchesLandRace(race))
				houseBids.add(bidInfo);
		}
		return houseBids;
	}

	public HouseBids.Bid bid(Player player, int listIndex, long bidOffer) {
		HouseBids houseBids = bids.values().stream().filter(b -> b.getListIndex() == listIndex).findAny().orElse(null);
		if (!isAllowedToBid(player, houseBids, bidOffer))
			return null; // bid too low or bidding not allowed
		HouseBids.Bid previousBid = houseBids.getHighestBid();
		HouseBids.Bid bid = houseBids.bid(player, bidOffer);
		if (bid == null) { // another bidder just bid more
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_LOWER());
			PacketSendUtility.sendPacket(player, new SM_RECEIVE_BIDS(0));
			return null;
		}
		if (AuctionEndTask.getInstance().tryProlongAuction(bid.getHouseObjectId()))
			HouseBidsDAO.addBid(bid); // no need to save the bid if prolongation failed (the auction just ended)
		player.getInventory().decreaseKinah(bid.getKinah());
		House bidHouse = HousingService.getInstance().findHouse(bid.getHouseObjectId());
		if (previousBid != houseBids.getInitialOffer() && previousBid.getPlayerObjectId() != 0) {
			PlayerCommonData prevPcd = PlayerService.getOrLoadPlayerCommonData(previousBid.getPlayerObjectId());
			if (prevPcd.isOnline()) {
				PacketSendUtility.sendPacket(prevPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_CANCEL());
				PacketSendUtility.sendPacket(prevPcd.getPlayer(), new SM_RECEIVE_BIDS(0));
			}
			MailFormatter.sendHouseAuctionMail(bidHouse, prevPcd, AuctionResult.FAILED_BID, bid.getTime(), previousBid.getKinah());
		}
		PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_SUCCESS(bidHouse.getAddress().getId()));
		PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_PRICE_CHANGE(bidOffer));
		PacketSendUtility.sendPacket(player, new SM_RECEIVE_BIDS(0));
		return bid;
	}

	private boolean isAllowedToBid(Player player, HouseBids houseBids, long bidOffer) {
		if (!HousingService.getInstance().canOwnHouse(player, true))
			return false;
		if (houseBids == null) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_FAIL());
			return false;
		}
		if (!isAuctionOpen(houseBids.getHouseObjectId())) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_TIMEOUT());
			return false;
		}
		House bidHouse = HousingService.getInstance().findHouse(houseBids.getHouseObjectId());
		if (player.getObjectId() == bidHouse.getOwnerId()) { // client usually already checks this, but we want to make sure
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_MY_HOUSE());
			return false;
		}

		if (HousingService.getInstance().findInactiveHouse(player.getObjectId()) != null) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_GRACE_HOUSE());
			return false;
		}
		House playerHouse = player.getActiveHouse();
		if (playerHouse != null && !playerHouse.isFeePaid() && HousingConfig.ENABLE_HOUSE_PAY) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_OVERDUE());
			return false;
		}
		int minBidLevel = getMinBidLevel(bidHouse);
		if (player.getLevel() < minBidLevel) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_LOW_LEVEL(minBidLevel));
			return false;
		}
		HouseBids.Bid highestBid = houseBids.getHighestBid();
		if (highestBid.getPlayerObjectId() == player.getObjectId()) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_SUCC_BID_HOUSE());
			return false;
		}
		if (bids.values().stream().anyMatch(b -> b.isHighestBidder(player))) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_OTHER_HOUSE());
			return false;
		}
		if (player.getInventory().getKinah() < bidOffer) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_NOT_ENOUGH_MONEY(bidOffer));
			return false;
		}
		long currentBid = highestBid.getKinah();
		if (bidOffer - currentBid >= currentBid * HousingConfig.AUCTION_BID_STEP_LIMIT / 100f) {
			PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_EXCESS_ACCOUNT());
			return false;
		}
		return true;
	}

	public void endAuctions() {
		bids.keySet().forEach(houseObjectId -> {
			if (!AuctionEndTask.getInstance().isAuctionProlonged(houseObjectId))
				endAuction(houseObjectId);
		});
		impoundAndAuctionOldPlayerHouses();
	}

	public boolean endAuction(int houseObjectId) {
		AuctionEndTask.getInstance().onAuctionEnd(houseObjectId);
		HouseBids bids;
		if (!HouseBidsDAO.deleteHouseBids(houseObjectId) || (bids = this.bids.remove(houseObjectId)) == null)
			return false;
		House house = HousingService.getInstance().findHouse(houseObjectId);
		house.setBids(null, false);
		int sellerId = house.getOwnerId();
		PlayerCommonData sellerPcd = sellerId == 0 ? null : PlayerService.getOrLoadPlayerCommonData(sellerId);
		HouseBids.Bid highestBid = bids.getHighestBid();
		if (highestBid == bids.getInitialOffer()) {
			AuctionResult result = AuctionResult.FAILED_SALE;
			long time = bids.getInitialOffer().getTime(); // registration time
			long compensation = 0;

			House inactiveHouse = sellerId == 0 ? null : HousingService.getInstance().findInactiveHouse(sellerId);
			if (inactiveHouse != null && inactiveHouse.secondsUntilGraceEnd() == 0) {
				HousingService.getInstance().changeOwner(house, 0); // inactive house will also be activated automatically by this
				result = AuctionResult.GRACE_FAIL;
				time = System.currentTimeMillis();
				compensation = (long) (bids.getInitialOffer().getKinah() * HousingConfig.AUCTION_GRACE_END_REFUND_PERCENT);
			} else {
				house.getController().updateSign();
			}
			if (sellerPcd != null) {
				if (sellerPcd.isOnline())
					PacketSendUtility.sendPacket(sellerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_FAIL(house.getAddress().getId()));
				MailFormatter.sendHouseAuctionMail(house, sellerPcd, result, time, compensation);
			}
			if (LoggingConfig.LOG_HOUSE_AUCTION) {
				log.info("Address " + house.getAddress().getId() + " not sold for " + bids.getInitialOffer().getKinah() + " kinah (result: " + result
					+ "; return: " + compensation + " kinah)");
			}
		} else {
			PlayerCommonData buyerPcd = PlayerService.getOrLoadPlayerCommonData(highestBid.getPlayerObjectId());

			if (buyerPcd == null) {
				if (highestBid.getPlayerObjectId() == 0)
					log.info(house + " wasn't sold because the winning bidder deleted his character.");
				else
					log.warn(house + " could not be sold to player " + highestBid.getPlayerObjectId() + " because the player couldn't be found");
				house.getController().updateSign();
				return true;
			}
			if (buyerPcd.getPlayerObjId() == sellerId) {
				log.warn("Sold " + house + " to its own owner (" + sellerId + "), cancelling!");
				house.getController().updateSign();
				return true;
			}

			House studio = HousingService.getInstance().getPlayerStudio(buyerPcd.getPlayerObjId());
			if (studio != null)
				HousingService.getInstance().changeOwner(studio, 0);
			HousingService.getInstance().changeOwner(house, buyerPcd.getPlayerObjId());

			AuctionResult result = AuctionResult.WIN_BID;
			long time = System.currentTimeMillis();
			if (buyerPcd.isOnline())
				PacketSendUtility.sendPacket(buyerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_WIN(house.getAddress().getId()));
			if (house.isInactive()) { // buyer has another house
				if (buyerPcd.isOnline())
					PacketSendUtility.sendPacket(buyerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_GRACE_START(house.getAddress().getId()));
				result = AuctionResult.GRACE_START;
				MailFormatter.sendHouseAuctionMail(house, buyerPcd, result, System.currentTimeMillis() + house.secondsUntilGraceEnd() * 1000, 0);
			} else {
				MailFormatter.sendHouseAuctionMail(house, buyerPcd, result, time, 0);
			}

			if (sellerPcd != null) {
				if (sellerPcd.isOnline())
					PacketSendUtility.sendPacket(sellerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_SUCCESS(house.getAddress().getId()));
				House newHouse = HousingService.getInstance().findActiveHouse(sellerPcd.getPlayerObjId());
				if (newHouse != null) { // seller got his new house activated because the old one is sold
					if (sellerPcd.isOnline())
						PacketSendUtility.sendPacket(sellerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_GRACE_SUCCESS(house.getAddress().getId()));
					MailFormatter.sendHouseAuctionMail(newHouse, sellerPcd, AuctionResult.GRACE_SUCCESS, time, highestBid.calculateSaleRewardKinah());
				} else
					MailFormatter.sendHouseAuctionMail(house, sellerPcd, AuctionResult.SUCCESS_SALE, time, highestBid.calculateSaleRewardKinah());
			}

			if (LoggingConfig.LOG_HOUSE_AUCTION) {
				String sellerInfo = sellerPcd == null ? "" : " by player " + sellerPcd.getPlayerObjId();
				log.info("Address " + house.getAddress().getId() + " sold" + sellerInfo + " for " + highestBid.getKinah() + " kinah (" + bids.getBidCount()
					+ " bids; result: " + result + ") to player " + buyerPcd.getPlayerObjId());
			}
		}
		return true;
	}

	private void impoundAndAuctionOldPlayerHouses() {
		for (House house : HousingService.getInstance().getCustomHouses()) {
			if (house.isInactive() && house.secondsUntilGraceEnd() == 0) {
				House oldHouse = HousingService.getInstance().findActiveHouse(house.getOwnerId());
				HousingService.getInstance().changeOwner(oldHouse, 0);
				PlayerCommonData pcd = PlayerService.getOrLoadPlayerCommonData(house.getOwnerId());
				if (pcd.isOnline())
					PacketSendUtility.sendPacket(pcd.getPlayer(),
						SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_GRACE_FAIL(house.getAddress().getId(), oldHouse.getAddress().getId()));
				if (auction(oldHouse, oldHouse.getDefaultAuctionPrice()))
					MailFormatter.sendHouseAuctionMail(house, pcd, AuctionResult.GRACE_FAIL, System.currentTimeMillis(),
						(long) (oldHouse.getDefaultAuctionPrice() * HousingConfig.AUCTION_GRACE_END_REFUND_PERCENT));
			}
		}
	}

	private int getMinBidLevel(House house) {
		switch (house.getHouseType()) {
			case HOUSE:
				if (HousingConfig.HOUSE_MIN_BID_LEVEL > 0)
					return HousingConfig.HOUSE_MIN_BID_LEVEL;
				break;
			case MANSION:
				if (HousingConfig.MANSION_MIN_BID_LEVEL > 0)
					return HousingConfig.MANSION_MIN_BID_LEVEL;
				break;
			case ESTATE:
				if (HousingConfig.ESTATE_MIN_BID_LEVEL > 0)
					return HousingConfig.ESTATE_MIN_BID_LEVEL;
				break;
			case PALACE:
				if (HousingConfig.PALACE_MIN_BID_LEVEL > 0)
					return HousingConfig.PALACE_MIN_BID_LEVEL;
				break;
		}
		return house.getLand().getSaleOptions().getMinLevel();
	}

	public void disableBids(int playerObjId) {
		List<HouseBids.Bid> deletedBids = new ArrayList<>();
		bids.values().forEach(b -> deletedBids.addAll(b.deleteOrDisableBids(playerObjId)));
		HouseBidsDAO.deleteOrDisableBids(playerObjId, deletedBids);
	}

	public HouseBids.Bid findLastBid(Player player) {
		return bids.values().stream().map(bids -> bids.getLatestBid(player)).filter(Objects::nonNull)
			.reduce(BinaryOperator.maxBy(Comparator.comparing(HouseBids.Bid::getTime))).orElse(null);
	}

	public HouseBids findBidsForRegisteredHouse(Player player) {
		for (House house : player.getHouses()) {
			if (house.getBids() != null)
				return house.getBids();
		}
		return null;
	}

	public boolean cancelAuction(House house) {
		HouseBids bids = this.bids.remove(house.getObjectId());
		if (bids == null)
			return false;

		HouseBidsDAO.deleteHouseBids(house.getObjectId());
		house.setBids(null, true);
		house.getController().updateSign();
		house.getController().updateAppearance();

		if (house.getOwnerId() != 0) {
			PlayerCommonData pcd = PlayerService.getOrLoadPlayerCommonData(house.getOwnerId());
			if (pcd.isOnline()) {
				PacketSendUtility.sendPacket(pcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_FAIL(house.getAddress().getId()));
				PacketSendUtility.sendPacket(pcd.getPlayer(), new SM_RECEIVE_BIDS(1));
			}
			MailFormatter.sendHouseAuctionMail(house, pcd, AuctionResult.CANCELED_BID, System.currentTimeMillis(), 0);
		}
		HouseBids.Bid highestBid = bids.getHighestBid();
		if (highestBid != bids.getInitialOffer() && highestBid.getPlayerObjectId() != 0) {
			// return bid price only to the last bidder (previous bidders already get their money back when another player bids more)
			PlayerCommonData pcd = PlayerService.getOrLoadPlayerCommonData(highestBid.getPlayerObjectId());
			MailFormatter.sendHouseAuctionMail(house, pcd, AuctionResult.CANCELED_BID, System.currentTimeMillis(), highestBid.getKinah());
		}

		return true;
	}

	/**
	 * Notify once about new auction results, based on system mail checks and login time
	 */
	public void onPlayerLogin(Player player) {
		List<Letter> letters = player.getMailbox().getNewSystemLetters("$$HS_AUCTION_MAIL");
		boolean needsRefresh = false;

		for (Letter letter : letters) {
			String[] titleParts = letter.getTitle().split(",");
			String[] bodyParts = letter.getMessage().split(",");
			AuctionResult result = AuctionResult.getResultFromId(Integer.parseInt(titleParts[0]));
			if (result == AuctionResult.FAILED_BID) {
				needsRefresh = true;
				PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_CANCEL());
			} else if (result == AuctionResult.WIN_BID || result == AuctionResult.GRACE_START) {
				needsRefresh = true;
				int address = Integer.parseInt(bodyParts[1]);
				PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_WIN(address));
			} else if (result == AuctionResult.FAILED_SALE) {
				needsRefresh = true;
				int address = Integer.parseInt(bodyParts[1]);
				PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_FAIL(address));
			} else if (result == AuctionResult.SUCCESS_SALE || result == AuctionResult.GRACE_SUCCESS) {
				needsRefresh = true;
				int address = Integer.parseInt(bodyParts[1]);
				PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_SUCCESS(address));
			}
		}

		if (needsRefresh)
			PacketSendUtility.sendPacket(player, new SM_RECEIVE_BIDS(0));
	}
}

📎 첨부파일

댓글 작성 권한이 없습니다.
🏆 포인트 랭킹 TOP 10
순위 닉네임 포인트
1 no_profile 타키야겐지쪽지보내기 자기소개 아이디로 검색 전체게시물 102,949
2 no_profile 동가리쪽지보내기 자기소개 아이디로 검색 전체게시물 63,733
3 no_profile 라프텔쪽지보내기 자기소개 아이디로 검색 전체게시물 51,771
4 no_profile 불멸의행복쪽지보내기 자기소개 아이디로 검색 전체게시물 36,923
5 서번트쪽지보내기 자기소개 아이디로 검색 전체게시물 35,011
6 no_profile 닥터스쪽지보내기 자기소개 아이디로 검색 전체게시물 29,470
7 no_profile 검은고양이쪽지보내기 자기소개 아이디로 검색 전체게시물 29,077
8 no_profile Revolution쪽지보내기 자기소개 아이디로 검색 전체게시물 28,199
9 no_profile 보거스쪽지보내기 자기소개 아이디로 검색 전체게시물 26,731
10 no_profile 호롤롤로쪽지보내기 자기소개 아이디로 검색 전체게시물 17,020
알림 0