Java tutorial
/** * This file is part of Aion-Lightning <aion-lightning.org>. * * Aion-Lightning is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Aion-Lightning is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * You should have received a copy of the GNU General Public License * along with Aion-Lightning. * If not, see <http://www.gnu.org/licenses/>. * * * Credits goes to all Open Source Core Developer Groups listed below * Please do not change here something, ragarding the developer credits, except the "developed by XXXX". * Even if you edit a lot of files in this source, you still have no rights to call it as "your Core". * Everybody knows that this Emulator Core was developed by Aion Lightning * @-Aion-Unique- * @-Aion-Lightning * @Aion-Engine * @Aion-Extreme * @Aion-NextGen * @Aion-Core Dev. */ package com.aionemu.gameserver.services; import java.sql.Timestamp; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javolution.util.FastMap; import org.joda.time.DateTime; import org.quartz.CronExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.aionemu.commons.database.dao.DAOManager; import com.aionemu.commons.utils.Rnd; import com.aionemu.gameserver.configs.main.HousingConfig; import com.aionemu.gameserver.configs.main.LoggingConfig; import com.aionemu.gameserver.dao.HouseBidsDAO; import com.aionemu.gameserver.dao.PlayerDAO; import com.aionemu.gameserver.dao.ServerVariablesDAO; import com.aionemu.gameserver.dataholders.DataManager; import com.aionemu.gameserver.model.Race; import com.aionemu.gameserver.model.TribeClass; import com.aionemu.gameserver.model.gameobjects.Letter; import com.aionemu.gameserver.model.gameobjects.player.Player; import com.aionemu.gameserver.model.gameobjects.player.PlayerCommonData; import com.aionemu.gameserver.model.gameobjects.player.PlayerHouseOwnerFlags; import com.aionemu.gameserver.model.house.House; import com.aionemu.gameserver.model.house.HouseBidEntry; import com.aionemu.gameserver.model.house.HouseStatus; import com.aionemu.gameserver.model.house.PlayerHouseBid; import com.aionemu.gameserver.model.templates.housing.HouseType; import com.aionemu.gameserver.model.templates.housing.HousingLand; import com.aionemu.gameserver.model.templates.housing.Sale; import com.aionemu.gameserver.network.aion.serverpackets.SM_HOUSE_ACQUIRE; import com.aionemu.gameserver.network.aion.serverpackets.SM_HOUSE_OWNER_INFO; 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.taskmanager.AbstractCronTask; import com.aionemu.gameserver.utils.PacketSendUtility; import com.aionemu.gameserver.utils.ThreadPoolManager; import com.aionemu.gameserver.world.World; import com.aionemu.gameserver.world.WorldMapType; /** * @author Rolandas */ public class HousingBidService extends AbstractCronTask { private static final Logger log = LoggerFactory.getLogger("HOUSE_AUCTION_LOG"); private static final String registerEndExpression = HousingConfig.HOUSE_REGISTER_END; private static CronExpression registerDateExpr; private static final FastMap<Integer, HouseBidEntry> houseBids; private static final FastMap<Integer, HouseBidEntry> playerBids; private static final FastMap<Integer, HouseBidEntry> bidsByIndex; private static int timeProlonged = 0; private static boolean isDataLoaded = false; private static HousingBidService instance; static { houseBids = FastMap.newInstance(); playerBids = FastMap.newInstance(); bidsByIndex = FastMap.newInstance(); try { instance = new HousingBidService(HousingConfig.HOUSE_AUCTION_TIME); } catch (ParseException pe) { } } private HousingBidService(String auctionTime) throws ParseException { super(auctionTime); } public static final HousingBidService getInstance() { return instance; } @Override protected long getRunDelay() { return (long) getSecondsTillAuction() * 1000; } @Override protected String getServerTimeVariable() { return "auctionTime"; } @Override protected boolean canRunOnInit() { return true; } @Override protected void postInit() { try { registerDateExpr = new CronExpression(registerEndExpression); } catch (ParseException e) { } ServerVariablesDAO dao = DAOManager.getDAO(ServerVariablesDAO.class); timeProlonged = dao.load("auctionProlonged"); } private HousingBidService() throws ParseException { super(HousingConfig.HOUSE_AUCTION_TIME); } public void start() { log.info("Loading house bids..."); loadBidData(); if (HousingConfig.FILL_HOUSE_BIDS_AUTO) { log.info("HousingBidService: auction auto filling enabled."); int added = fillBidData(); log.info("HousingBidService: added " + added + " new house bids."); } log.info("HousingBidService loaded. Minutes till start: " + getMinutesTillAuction()); isDataLoaded = true; } private int fillBidData() { int count = 0; List<House> houses = HousingService.getInstance().getCustomHouses(); while (!houses.isEmpty()) { House house = houses.get(Rnd.get(houses.size())); houses.remove(house); if (house.getOwnerId() != 0 || houseBids.containsKey(house.getObjectId())) { continue; } if (!checkAutoFillingLimits(house.getPlayerRace(), house.getHouseType())) { continue; } addHouseToAuction(house, house.getDefaultAuctionPrice()); count++; } return count; } private boolean checkAutoFillingLimits(Race race, HouseType type) { int bidsCount = getBidsCountByType(race, type); switch (type) { case HOUSE: if (bidsCount >= HousingConfig.FILL_AUTO_HOUSES_COUNT) { return false; } break; case MANSION: if (bidsCount >= HousingConfig.FILL_AUTO_MANSION_COUNT) { return false; } break; case ESTATE: if (bidsCount >= HousingConfig.FILL_AUTO_ESTATE_COUNT) { return false; } break; case PALACE: if (bidsCount >= HousingConfig.FILL_AUTO_PALACE_COUNT) { return false; } break; default: break; } return true; } private int getBidsCountByType(Race race, HouseType type) { int count = 0; for (HouseBidEntry entry : houseBids.values()) { HousingLand land = DataManager.HOUSE_DATA.getLand(entry.getLandId()); Race entryRace = DataManager.NPC_DATA.getNpcTemplate(land.getManagerNpcId()) .getTribe() == TribeClass.GENERAL ? Race.ELYOS : Race.ASMODIANS; if (entryRace == race && entry.getHouseType() == type) { count++; } } return count; } private void loadBidData() { Set<PlayerHouseBid> playerBidData = DAOManager.getDAO(HouseBidsDAO.class).loadBids(); List<PlayerHouseBid> sortedBids = new ArrayList<PlayerHouseBid>(playerBidData); Collections.sort(sortedBids); FastMap<Integer, House> housesById = FastMap.newInstance(); for (House house : HousingService.getInstance().getCustomHouses()) { housesById.put(house.getObjectId(), house); } int entryIndex = 1; for (PlayerHouseBid playerBid : sortedBids) { House house = housesById.get(playerBid.getHouseId()); if (house == null) { log.warn( "Missing house " + playerBid.getHouseId() + " player " + playerBid.getPlayerId() + " bid."); continue; } HouseBidEntry entry = houseBids.get(house.getObjectId()); if (entry == null) { entry = new HouseBidEntry(house, entryIndex, playerBid.getBidOffer()); houseBids.put(house.getObjectId(), entry); bidsByIndex.put(entryIndex++, entry); } else if (entry.getBidPrice() < playerBid.getBidOffer()) { // find max price entry.setBidPrice(playerBid.getBidOffer()); } if (playerBid.getPlayerId() != 0) { HouseBidEntry playerEntry = (HouseBidEntry) entry.Clone(); playerEntry.setBidPrice(playerBid.getBidOffer()); playerBids.put(playerBid.getPlayerId(), playerEntry); entry.setLastBiddingPlayer(playerBid.getPlayerId()); entry.setLastBidTime(playerBid.getTime().getTime()); entry.incrementBidCount(); } } // check to see if bids were not removed from DB manually for (House house : housesById.values()) { if (house.getOwnerId() == 0) { continue; } if (house.getStatus() == HouseStatus.SELL_WAIT && !houseBids.containsKey(house.getObjectId())) { log.warn("House address=" + house.getAddress().getId() + " has status SELL_WAIT but no bid exists. Activated."); house.setStatus(HouseStatus.ACTIVE); house.setSellStarted(null); house.save(); } } } @Override protected void executeTask() { if (!HousingConfig.ENABLE_HOUSE_AUCTIONS) { return; } while (!isDataLoaded) { try { Thread.sleep(500); } catch (InterruptedException e) { return; } } Map<HouseBidEntry, Integer> winners = new HashMap<HouseBidEntry, Integer>(); Map<HouseBidEntry, Integer> successSell = new HashMap<HouseBidEntry, Integer>(); Map<HouseBidEntry, Integer> failedSell = new HashMap<HouseBidEntry, Integer>(); for (Entry<Integer, HouseBidEntry> playerBid : playerBids.entrySet()) { int playerId = playerBid.getKey(); HouseBidEntry houseBid = getBidByEntryIndex(playerBid.getValue().getEntryIndex()); House house = HousingService.getInstance().getHouseByAddress(houseBid.getAddress()); if (playerBid.getValue().getBidPrice() == houseBid.getBidPrice()) { // The player can not be top 1 for both bids, no need to check other if (house.getOwnerId() == 0) { winners.put(houseBid, playerId); // our sold house } else { successSell.put(houseBid, playerId); // player sold house } } } for (HouseBidEntry houseBid : houseBids.values()) { House house = HousingService.getInstance().getHouseByAddress(houseBid.getAddress()); if (houseBid.getBidCount() > 0) { continue; } if (house.getOwnerId() != 0) { // add only player sold houses failedSell.put(houseBid, house.getOwnerId()); } } // send mails + messages if players are online if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("##### Houses sold by admins #####"); } // check houses sold by administrators for (Entry<HouseBidEntry, Integer> winData : winners.entrySet()) { House wonHouse = HousingService.getInstance().getHouseByAddress(winData.getKey().getAddress()); if (getPlayerData(winData.getValue()) == null) { log.warn("Missing Player with ID:" + winData.getValue() + " for Housebid on address:" + winData.getKey().getAddress()); continue; } AuctionResult result = completeHouseSell(getPlayerData(winData.getValue()), wonHouse); if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("Address " + wonHouse.getAddress().getId() + " sold for price " + winData.getKey().getBidPrice() + " (bid count: " + winData.getKey().getBidCount() + "; result: " + result + ") to player " + winData.getKey().getLastBiddingPlayer()); } } // check houses sold by players long time = System.currentTimeMillis(); if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("##### Houses auctioned by players #####"); } for (Entry<HouseBidEntry, Integer> sellData : successSell.entrySet()) { House soldHouse = HousingService.getInstance().getHouseByAddress(sellData.getKey().getAddress()); PlayerCommonData buyerPcd = getPlayerData(sellData.getValue()); PlayerCommonData sellerPcd = getPlayerData(soldHouse.getOwnerId()); if (buyerPcd.getPlayerObjId() == soldHouse.getOwnerId()) { log.warn("Selling house to its own owner, cancelling!"); continue; } if (sellerPcd.isOnline()) { PacketSendUtility.sendPacket(sellerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_SUCCESS(sellData.getKey().getAddress())); } if (buyerPcd.isOnline()) { PacketSendUtility.sendPacket(buyerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_WIN(sellData.getKey().getAddress())); } long returnKinah = sellData.getKey().getBidPrice() + sellData.getKey().getRefundKinah(); if (soldHouse.isInGracePeriod()) { soldHouse.revokeOwner(); // activate previously obtained house House newHouse = HousingService.getInstance().activateBoughtHouse(sellerPcd.getPlayerObjId()); if (sellerPcd.isOnline()) { PacketSendUtility.sendPacket(sellerPcd.getPlayer(), new SM_HOUSE_ACQUIRE(sellerPcd.getPlayerObjId(), newHouse.getAddress().getId(), true)); PacketSendUtility.sendPacket(sellerPcd.getPlayer(), new SM_HOUSE_OWNER_INFO(sellerPcd.getPlayer(), newHouse)); sellerPcd.getPlayer().setHouseRegistry(newHouse.getRegistry()); } MailFormatter.sendHouseAuctionMail(newHouse, sellerPcd, AuctionResult.GRACE_SUCCESS, time, returnKinah); } else { MailFormatter.sendHouseAuctionMail(soldHouse, sellerPcd, AuctionResult.SUCCESS_SALE, time, returnKinah); soldHouse.revokeOwner(); } AuctionResult result = completeHouseSell(buyerPcd, soldHouse); if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("Address " + soldHouse.getAddress().getId() + " sold by player " + sellerPcd.getPlayerObjId() + " for price " + sellData.getKey().getBidPrice() + " (bid count: " + sellData.getKey().getBidCount() + "; result: " + result + ") to player " + sellData.getKey().getLastBiddingPlayer()); } } // not sold houses for (Entry<HouseBidEntry, Integer> notSoldData : failedSell.entrySet()) { HouseBidEntry bidEntry = notSoldData.getKey(); PlayerCommonData sellerPcd = getPlayerData(notSoldData.getValue()); House bidHouse = HousingService.getInstance().getHouseByAddress(bidEntry.getAddress()); if (sellerPcd.isOnline()) { PacketSendUtility.sendPacket(sellerPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_AUCTION_FAIL(bidHouse.getAddress().getId())); } AuctionResult result = AuctionResult.FAILED_SALE; long compensation = 0; if (bidHouse.isInGracePeriod()) { long timePassed = (getAuctionStartTime() - bidHouse.getSellStarted().getTime()) / 1000; if (timePassed > 7 * 24 * 3600) { // more than one week, i.e. 2 weeks passed bidHouse.revokeOwner(); House activatedHouse = HousingService.getInstance() .activateBoughtHouse(sellerPcd.getPlayerObjId()); if (sellerPcd.isOnline()) { if (activatedHouse != null) { PacketSendUtility.sendPacket(sellerPcd.getPlayer(), new SM_HOUSE_ACQUIRE( sellerPcd.getPlayerObjId(), activatedHouse.getAddress().getId(), true)); sellerPcd.getPlayer().setHouseRegistry(activatedHouse.getRegistry()); } PacketSendUtility.sendPacket(sellerPcd.getPlayer(), new SM_HOUSE_OWNER_INFO(sellerPcd.getPlayer(), activatedHouse)); } result = AuctionResult.GRACE_FAIL; time = System.currentTimeMillis(); compensation = bidHouse.getDefaultAuctionPrice(); } } else { bidHouse.setStatus(HouseStatus.ACTIVE); time = bidHouse.getSellStarted().getTime(); } bidHouse.save(); MailFormatter.sendHouseAuctionMail(bidHouse, sellerPcd, result, time, compensation); if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("Address " + bidHouse.getAddress().getId() + " not sold for price " + bidEntry.getBidPrice() + " (result: " + result + "; return: " + compensation + " kinah) by player " + sellerPcd.getPlayerObjId()); } } List<HouseBidEntry> copy = new ArrayList<HouseBidEntry>(); copy.addAll(houseBids.values()); houseBids.clear(); playerBids.clear(); bidsByIndex.clear(); // add back auto auctioned houses (with grace period ended) + admin houses not sold if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("##### Houses added back to auction #####"); } for (HouseBidEntry houseBid : copy) { House house = HousingService.getInstance().getHouseByAddress(houseBid.getAddress()); DAOManager.getDAO(HouseBidsDAO.class).deleteHouseBids(house.getObjectId()); if (house.getOwnerId() == 0) { house.setStatus(HouseStatus.NOSALE); addHouseToAuction(house); house.save(); if (LoggingConfig.LOG_HOUSE_AUCTION) { log.info("Address " + houseBid.getAddress() + " not sold for price " + houseBid.getBidPrice()); } } } } @Override protected void postRun() { ServerVariablesDAO dao = DAOManager.getDAO(ServerVariablesDAO.class); timeProlonged = 0; dao.store("auctionProlonged", timeProlonged); } /** * Milliseconds of auction start time * * @return one week ago before the auction time */ public long getAuctionStartTime() { return (long) (getRunTime() - 7 * 24 * 3600) * 1000; } public int getSecondsTillAuction() { int left = (int) (getRunTime() - System.currentTimeMillis() / 1000); left += timeProlonged * 60; if (left < 0) { return 0; } return left; } public int getMinutesTillAuction() { return (int) (getSecondsTillAuction() / 60); } public boolean isBiddingAllowed() { DateTime now = DateTime.now(); DateTime auctionEnd = new DateTime(((long) getRunTime() + timeProlonged * 60) * 1000); if (now.getDayOfWeek() == auctionEnd.getDayOfWeek() && auctionEnd.minusDays(1).isAfterNow()) { // Auction is unavailable from Sunday 12 PM to Monday return false; } return true; } public boolean isRegisteringAllowed() { DateTime now = DateTime.now(); DateTime registerEnd = new DateTime(registerDateExpr.getTimeAfter(now.toDate())); DateTime auctionEnd = new DateTime(((long) getRunTime() + timeProlonged * 60) * 1000); if (now.getDayOfWeek() == registerEnd.getDayOfWeek() && now.getHourOfDay() >= registerEnd.getHourOfDay() || (now.getDayOfWeek() == auctionEnd.getDayOfWeek() && now.getHourOfDay() <= auctionEnd.getHourOfDay())) { return false; } return true; } private PlayerCommonData getPlayerData(int objectId) { Player player = World.getInstance().findPlayer(objectId); if (player == null) { return DAOManager.getDAO(PlayerDAO.class).loadPlayerCommonData(objectId); } return player.getCommonData(); } public AuctionResult completeHouseSell(PlayerCommonData winner, House obtainedHouse) { House winnerHouse = HousingService.getInstance().getPlayerStudio(winner.getPlayerObjId()); AuctionResult result = AuctionResult.WIN_BID; long time = System.currentTimeMillis(); if (winnerHouse != null) { winnerHouse.revokeOwner(); } else { int address = HousingService.getInstance().getPlayerAddress(winner.getPlayerObjId()); if (address > 0) { // old house exists winnerHouse = HousingService.getInstance().getHouseByAddress(address); // grace period start, sell time remains from this auction winnerHouse.setSellStarted(new Timestamp(getAuctionStartTime())); // make the new house inactive until the old one is sold obtainedHouse.setStatus(HouseStatus.INACTIVE); result = AuctionResult.GRACE_START; // time = new DateTime((long) (getRunTime() * 1000)).plusWeeks(2).getMillis(); time = new DateTime(getRunTime() * 1000L).plusWeeks(2).getMillis(); } } obtainedHouse.setOwnerId(winner.getPlayerObjId()); if (result == AuctionResult.WIN_BID) { obtainedHouse.setAcquiredTime(new Timestamp(System.currentTimeMillis())); obtainedHouse.setStatus(HouseStatus.ACTIVE); obtainedHouse.setFeePaid(true); obtainedHouse.setNextPay(null); obtainedHouse.setSellStarted(null); obtainedHouse.reloadHouseRegistry(); obtainedHouse.save(); } if (winner.isOnline()) { if (result == AuctionResult.WIN_BID) { winner.getPlayer().setHouseRegistry(obtainedHouse.getRegistry()); winner.getPlayer().setBuildingOwnerState(PlayerHouseOwnerFlags.HOUSE_OWNER.getId()); PacketSendUtility.sendPacket(winner.getPlayer(), new SM_HOUSE_ACQUIRE(winner.getPlayerObjId(), obtainedHouse.getAddress().getId(), true)); PacketSendUtility.sendPacket(winner.getPlayer(), new SM_HOUSE_OWNER_INFO(winner.getPlayer(), obtainedHouse)); } PacketSendUtility.sendPacket(winner.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_WIN(obtainedHouse.getAddress().getId())); } MailFormatter.sendHouseAuctionMail(obtainedHouse, winner, result, time, 0); obtainedHouse.getController().kickVisitors(null, true, true); return result; } public boolean addHouseToAuction(House house) { return addHouseToAuction(house, house.getDefaultAuctionPrice()); } public boolean addHouseToAuction(House house, long initialPrice) { if (house.getStatus() == HouseStatus.SELL_WAIT) { return false; } house.setStatus(HouseStatus.SELL_WAIT); int maxIndex = 1; HouseBidEntry bidEntry = null; synchronized (bidsByIndex) { for (Integer index : bidsByIndex.keySet()) { if (index > maxIndex) { maxIndex = index; } } bidEntry = new HouseBidEntry(house, ++maxIndex, initialPrice); bidsByIndex.putEntry(maxIndex, bidEntry); } synchronized (houseBids) { houseBids.put(house.getObjectId(), bidEntry); } Timestamp time = new Timestamp(System.currentTimeMillis()); if (house.getSellStarted() == null) { // dont overwrite grace start time house.setSellStarted(time); } house.save(); return DAOManager.getDAO(HouseBidsDAO.class).addBid(0, house.getObjectId(), initialPrice, time); } /** * Remove house from auction. Currently from admin command, both for player * and non-player houses. Returns kinah for the selling player and the last * bidder if such exist */ public boolean removeHouseFromAuction(House house, boolean noSale) { if (house.getStatus() != HouseStatus.SELL_WAIT) { return false; } HouseBidEntry bidEntry = null; HouseBidEntry playerBid = null; Integer lastPlayer = null; synchronized (houseBids) { bidEntry = houseBids.remove(house.getObjectId()); if (bidEntry == null) { return false; } lastPlayer = bidEntry.getLastBiddingPlayer(); playerBid = playerBids.remove(lastPlayer); } synchronized (bidsByIndex) { bidsByIndex.remove(bidEntry.getEntryIndex()); } PlayerCommonData pcd = null; if (house.getOwnerId() != 0) { // player put this house himself, refund everything if (house.isInGracePeriod()) { house.setSellStarted(null); } pcd = getPlayerData(house.getOwnerId()); MailFormatter.sendHouseAuctionMail(house, pcd, AuctionResult.CANCELED_BID, System.currentTimeMillis(), bidEntry.getBidPrice() + bidEntry.getRefundKinah()); house.setStatus(HouseStatus.ACTIVE); } else { house.setStatus(noSale ? HouseStatus.NOSALE : HouseStatus.ACTIVE); } if (lastPlayer != null) { // return bid price only to the last bidder pcd = getPlayerData(lastPlayer); MailFormatter.sendHouseAuctionMail(house, pcd, AuctionResult.CANCELED_BID, System.currentTimeMillis(), playerBid.getBidPrice()); } DAOManager.getDAO(HouseBidsDAO.class).deleteHouseBids(house.getObjectId()); house.save(); return true; } public synchronized void placeBid(Player player, int entryIndex, long bidOffer) { // prevent this earlier (problem are house signs which allow bidding) if ((player.getBuildingOwnerStates() & PlayerHouseOwnerFlags.BIDDING_ALLOWED.getId()) == 0) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE .STR_MSG_HOUSING_CANT_OWN_NOT_COMPLETE_QUEST(player.getRace() == Race.ELYOS ? 18802 : 28802)); return; } int minutesLeft = getMinutesTillAuction(); if (minutesLeft == 0) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_TIMEOUT); return; } HouseBidEntry entry = getBidByEntryIndex(entryIndex); if (entry == null) { return; } if (player.getInventory().getKinah() < bidOffer) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_NOT_ENOUGH_MONEY); return; } // Server side check for own house. Client side exists, but to be sure House bidHouse = HousingService.getInstance().getHouseByAddress(entry.getAddress()); if (player.getObjectId() == bidHouse.getOwnerId()) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_MY_HOUSE); return; } int playerAddress = HousingService.getInstance().getPlayerAddress(player.getObjectId()); House playerHouse = HousingService.getInstance().getHouseByAddress(playerAddress); if (playerHouse != null && playerHouse.isInGracePeriod()) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_GRACE_HOUSE); return; } int minLevel = getMinBidLevel(player, entry.getMapId(), entry.getLandId()); if (minLevel > player.getLevel()) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_LOW_LEVEL(minLevel)); return; } if (playerHouse != null && !playerHouse.isFeePaid() && HousingConfig.ENABLE_HOUSE_PAY) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_OVERDUE); return; } if (bidOffer - entry.getBidPrice() >= entry.getBidPrice() * HousingConfig.HOUSE_AUCTION_BID_LIMIT / 100f) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_EXCESS_ACCOUNT); return; } HouseBidEntry currentBid = playerBids.get(player.getObjectId()); if (currentBid != null) { if (entry.getLastBiddingPlayer() == player.getObjectId()) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_SUCC_BID_HOUSE); return; } HouseBidEntry houseBid = getBidByEntryIndex(currentBid.getEntryIndex()); if (houseBid.getBidPrice() == currentBid.getBidPrice()) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_OTHER_HOUSE); return; } } if (minutesLeft < 5 && timeProlonged < 30) { timeProlonged += 5; // save time ThreadPoolManager.getInstance().execute(new Runnable() { @Override public void run() { DAOManager.getDAO(ServerVariablesDAO.class).store("auctionProlonged", timeProlonged); } }); } else if (!isBiddingAllowed()) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_CANT_BID_TIMEOUT); return; } if (bidOffer > entry.getBidPrice() || entry.getBidCount() == 0) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_SUCCESS(entry.getAddress())); Timestamp time = new Timestamp(System.currentTimeMillis()); player.getInventory().decreaseKinah(bidOffer); int previousPlayer = entry.getLastBiddingPlayer(); if (previousPlayer > 0) { PlayerCommonData prevPcd = getPlayerData(previousPlayer); if (prevPcd.isOnline()) { PacketSendUtility.sendPacket(prevPcd.getPlayer(), SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_BID_CANCEL); } MailFormatter.sendHouseAuctionMail(bidHouse, prevPcd, AuctionResult.FAILED_BID, time.getTime(), entry.getBidPrice()); } entry.incrementBidCount(); entry.setLastBiddingPlayer(player.getObjectId()); entry.setLastBidTime(time.getTime()); entry.setBidPrice(bidOffer); HouseBidEntry playerBid = (HouseBidEntry) entry.Clone(); playerBids.put(player.getObjectId(), playerBid); DAOManager.getDAO(HouseBidsDAO.class).addBid(player.getObjectId(), bidHouse.getObjectId(), bidOffer, time); PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_PRICE_CHANGE(bidOffer)); PacketSendUtility.sendPacket(player, new SM_RECEIVE_BIDS(0)); } } /** * Notify about new auction results or maintenance checks but only once * (based on system mail checks and login time) */ public void onPlayerLogin(Player player) { if (player.getMailbox() == null) { return; } 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)); } letters = player.getMailbox().getNewSystemLetters("$$HS_OVERDUE_"); for (Letter letter : letters) { if (letter.getSenderName().endsWith("FINAL") || letter.getSenderName().endsWith("3RD")) { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_SEQUESTRATE); } else { PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_MSG_HOUSING_OVERDUE); } } } public HouseBidEntry getHouseBid(int houseObjectId) { synchronized (houseBids) { return houseBids.get(houseObjectId); } } public List<HouseBidEntry> getHouseBidEntries(Race playerRace) { synchronized (houseBids) { List<HouseBidEntry> bids = new ArrayList<HouseBidEntry>(); for (HouseBidEntry bid : houseBids.values()) { HousingLand land = DataManager.HOUSE_DATA.getLand(bid.getLandId()); boolean isEly = DataManager.NPC_DATA.getNpcTemplate(land.getManagerNpcId()) .getTribe() == TribeClass.GENERAL; if (isEly && playerRace == Race.ELYOS) { bids.add(bid); } else if (!isEly && playerRace == Race.ASMODIANS) { bids.add(bid); } } return bids; } } public HouseBidEntry getLastPlayerBid(int playerId) { return playerBids.get(playerId); } public HouseBidEntry getBidByEntryIndex(int index) { synchronized (bidsByIndex) { return bidsByIndex.get(index); } } private static int getMinBidLevel(Player player, int mapId, int landId) { HousingLand land = DataManager.HOUSE_DATA.getLand(landId); Sale saleOptions = land.getSaleOptions(); // all types are the same for the land, take the first HouseType houseType = HouseType.fromValue(land.getBuildings().get(0).getSize()); if (player.getRace() == Race.ELYOS) { if (mapId == WorldMapType.HEIRON.getId() || mapId == WorldMapType.INGGISON.getId()) { return saleOptions.getMinLevel(); } } else if (mapId == WorldMapType.BELUSLAN.getId() || mapId == WorldMapType.GELKMAROS.getId()) { return saleOptions.getMinLevel(); } switch (houseType) { 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; default: break; } return saleOptions.getMinLevel(); } public static boolean canBidHouse(Player player, int mapId, int landId) { return player.getLevel() >= getMinBidLevel(player, mapId, landId); } };