/*
* Copyright (C) 2004-2014 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server 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.
*
* L2J Server 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 this program. If not, see .
*/
package com.l2jserver.gameserver.model.entity;
import static com.l2jserver.gameserver.model.itemcontainer.Inventory.ADENA_ID;
import static com.l2jserver.gameserver.model.itemcontainer.Inventory.MAX_ADENA;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Calendar;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastMap;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.datatables.ClanTable;
import com.l2jserver.gameserver.enums.AuctionItemType;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.instancemanager.AuctionManager;
import com.l2jserver.gameserver.instancemanager.ClanHallManager;
import com.l2jserver.gameserver.model.L2Clan;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.SystemMessageId;
public class Auction
{
protected static final Logger _log = Logger.getLogger(Auction.class.getName());
private int _id = 0;
private long _endDate;
private int _highestBidderId = 0;
private String _highestBidderName = "";
private long _highestBidderMaxBid = 0;
private int _itemId = 0;
private String _itemName = "";
private int _itemObjectId = 0;
private final long _itemQuantity = 0;
private String _itemType = "";
private int _sellerId = 0;
private String _sellerClanName = "";
private String _sellerName = "";
private long _currentBid = 0;
private long _startingBid = 0;
private final Map _bidders = new FastMap<>();
private static final String[] ItemTypeName =
{
"ClanHall"
};
public static class Bidder
{
private final String _name; // TODO replace with objid
private final String _clanName;
private long _bid;
private final Calendar _timeBid;
public Bidder(String name, String clanName, long bid, long timeBid)
{
_name = name;
_clanName = clanName;
_bid = bid;
_timeBid = Calendar.getInstance();
_timeBid.setTimeInMillis(timeBid);
}
public String getName()
{
return _name;
}
public String getClanName()
{
return _clanName;
}
public long getBid()
{
return _bid;
}
public Calendar getTimeBid()
{
return _timeBid;
}
public void setTimeBid(long timeBid)
{
_timeBid.setTimeInMillis(timeBid);
}
public void setBid(long bid)
{
_bid = bid;
}
}
/** Task Sheduler for endAuction */
public class AutoEndTask implements Runnable
{
public AutoEndTask()
{
}
@Override
public void run()
{
try
{
endAuction();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "", e);
}
}
}
/**
* Constructor
* @param auctionId
*/
public Auction(int auctionId)
{
_id = auctionId;
load();
startAutoTask();
}
public Auction(int itemId, L2Clan Clan, long delay, long bid, String name)
{
_id = itemId;
_endDate = System.currentTimeMillis() + delay;
_itemId = itemId;
_itemName = name;
_itemType = "ClanHall";
_sellerId = Clan.getLeaderId();
_sellerName = Clan.getLeaderName();
_sellerClanName = Clan.getName();
_startingBid = bid;
}
/** Load auctions */
private void load()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("Select * from auction where id = ?"))
{
statement.setInt(1, getId());
try (ResultSet rs = statement.executeQuery())
{
while (rs.next())
{
_currentBid = rs.getLong("currentBid");
_endDate = rs.getLong("endDate");
_itemId = rs.getInt("itemId");
_itemName = rs.getString("itemName");
_itemObjectId = rs.getInt("itemObjectId");
_itemType = rs.getString("itemType");
_sellerId = rs.getInt("sellerId");
_sellerClanName = rs.getString("sellerClanName");
_sellerName = rs.getString("sellerName");
_startingBid = rs.getLong("startingBid");
}
}
loadBid();
}
catch (Exception e)
{
_log.log(Level.WARNING, "Exception: Auction.load(): " + e.getMessage(), e);
}
}
/** Load bidders **/
private void loadBid()
{
_highestBidderId = 0;
_highestBidderName = "";
_highestBidderMaxBid = 0;
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("SELECT bidderId, bidderName, maxBid, clan_name, time_bid FROM auction_bid WHERE auctionId = ? ORDER BY maxBid DESC"))
{
statement.setInt(1, getId());
try (ResultSet rs = statement.executeQuery())
{
while (rs.next())
{
if (rs.isFirst())
{
_highestBidderId = rs.getInt("bidderId");
_highestBidderName = rs.getString("bidderName");
_highestBidderMaxBid = rs.getLong("maxBid");
}
_bidders.put(rs.getInt("bidderId"), new Bidder(rs.getString("bidderName"), rs.getString("clan_name"), rs.getLong("maxBid"), rs.getLong("time_bid")));
}
}
}
catch (Exception e)
{
_log.log(Level.WARNING, "Exception: Auction.loadBid(): " + e.getMessage(), e);
}
}
/** Task Manage */
private void startAutoTask()
{
long currentTime = System.currentTimeMillis();
long taskDelay = 0;
if (_endDate <= currentTime)
{
_endDate = currentTime + (7 * 24 * 3600000);
saveAuctionDate();
}
else
{
taskDelay = _endDate - currentTime;
}
ThreadPoolManager.getInstance().scheduleGeneral(new AutoEndTask(), taskDelay);
}
public static String getItemTypeName(AuctionItemType value)
{
return ItemTypeName[value.ordinal()];
}
/** Save Auction Data End */
private void saveAuctionDate()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("Update auction set endDate = ? where id = ?"))
{
statement.setLong(1, _endDate);
statement.setInt(2, _id);
statement.execute();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Exception: saveAuctionDate(): " + e.getMessage(), e);
}
}
/**
* Set a bid
* @param bidder
* @param bid
*/
public synchronized void setBid(L2PcInstance bidder, long bid)
{
long requiredAdena = bid;
if (getHighestBidderName().equals(bidder.getClan().getLeaderName()))
{
requiredAdena = bid - getHighestBidderMaxBid();
}
if (((getHighestBidderId() > 0) && (bid > getHighestBidderMaxBid())) || ((getHighestBidderId() == 0) && (bid >= getStartingBid())))
{
if (takeItem(bidder, requiredAdena))
{
updateInDB(bidder, bid);
bidder.getClan().setAuctionBiddedAt(_id, true);
return;
}
}
if ((bid < getStartingBid()) || (bid <= getHighestBidderMaxBid()))
{
bidder.sendPacket(SystemMessageId.BID_PRICE_MUST_BE_HIGHER);
}
}
/**
* Return Item in WHC
* @param Clan
* @param quantity
* @param penalty
*/
private void returnItem(String Clan, long quantity, boolean penalty)
{
if (penalty)
{
quantity *= 0.9; // take 10% tax fee if needed
}
// avoid overflow on return
final long limit = MAX_ADENA - ClanTable.getInstance().getClanByName(Clan).getWarehouse().getAdena();
quantity = Math.min(quantity, limit);
ClanTable.getInstance().getClanByName(Clan).getWarehouse().addItem("Outbidded", ADENA_ID, quantity, null, null);
}
/**
* Take Item in WHC
* @param bidder
* @param quantity
* @return
*/
private boolean takeItem(L2PcInstance bidder, long quantity)
{
if ((bidder.getClan() != null) && (bidder.getClan().getWarehouse().getAdena() >= quantity))
{
bidder.getClan().getWarehouse().destroyItemByItemId("Buy", ADENA_ID, quantity, bidder, bidder);
return true;
}
bidder.sendPacket(SystemMessageId.NOT_ENOUGH_ADENA_IN_CWH);
return false;
}
/**
* Update auction in DB
* @param bidder
* @param bid
*/
private void updateInDB(L2PcInstance bidder, long bid)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection())
{
if (getBidders().get(bidder.getClanId()) != null)
{
try (PreparedStatement statement = con.prepareStatement("UPDATE auction_bid SET bidderId=?, bidderName=?, maxBid=?, time_bid=? WHERE auctionId=? AND bidderId=?"))
{
statement.setInt(1, bidder.getClanId());
statement.setString(2, bidder.getClan().getLeaderName());
statement.setLong(3, bid);
statement.setLong(4, System.currentTimeMillis());
statement.setInt(5, getId());
statement.setInt(6, bidder.getClanId());
statement.execute();
}
}
else
{
try (PreparedStatement statement = con.prepareStatement("INSERT INTO auction_bid (id, auctionId, bidderId, bidderName, maxBid, clan_name, time_bid) VALUES (?, ?, ?, ?, ?, ?, ?)"))
{
statement.setInt(1, IdFactory.getInstance().getNextId());
statement.setInt(2, getId());
statement.setInt(3, bidder.getClanId());
statement.setString(4, bidder.getName());
statement.setLong(5, bid);
statement.setString(6, bidder.getClan().getName());
statement.setLong(7, System.currentTimeMillis());
statement.execute();
}
if (L2World.getInstance().getPlayer(_highestBidderName) != null)
{
L2World.getInstance().getPlayer(_highestBidderName).sendMessage("You have been out bidded");
}
}
_highestBidderId = bidder.getClanId();
_highestBidderMaxBid = bid;
_highestBidderName = bidder.getClan().getLeaderName();
if (_bidders.get(_highestBidderId) == null)
{
_bidders.put(_highestBidderId, new Bidder(_highestBidderName, bidder.getClan().getName(), bid, Calendar.getInstance().getTimeInMillis()));
}
else
{
_bidders.get(_highestBidderId).setBid(bid);
_bidders.get(_highestBidderId).setTimeBid(Calendar.getInstance().getTimeInMillis());
}
bidder.sendPacket(SystemMessageId.BID_IN_CLANHALL_AUCTION);
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Exception: Auction.updateInDB(L2PcInstance bidder, int bid): " + e.getMessage(), e);
}
}
/** Remove bids */
private void removeBids()
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM auction_bid WHERE auctionId=?"))
{
statement.setInt(1, getId());
statement.execute();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Exception: Auction.deleteFromDB(): " + e.getMessage(), e);
}
for (Bidder b : _bidders.values())
{
if (ClanTable.getInstance().getClanByName(b.getClanName()).getHideoutId() == 0)
{
returnItem(b.getClanName(), b.getBid(), true); // 10 % tax
}
else
{
if (L2World.getInstance().getPlayer(b.getName()) != null)
{
L2World.getInstance().getPlayer(b.getName()).sendMessage("Congratulation you have won ClanHall!");
}
}
ClanTable.getInstance().getClanByName(b.getClanName()).setAuctionBiddedAt(0, true);
}
_bidders.clear();
}
/** Remove auctions */
public void deleteAuctionFromDB()
{
AuctionManager.getInstance().getAuctions().remove(this);
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM auction WHERE itemId=?"))
{
statement.setInt(1, _itemId);
statement.execute();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Exception: Auction.deleteFromDB(): " + e.getMessage(), e);
}
}
/** End of auction */
public void endAuction()
{
if (ClanHallManager.getInstance().loaded())
{
if ((_highestBidderId == 0) && (_sellerId == 0))
{
startAutoTask();
return;
}
if ((_highestBidderId == 0) && (_sellerId > 0))
{
/**
* If seller haven't sell ClanHall, auction removed, THIS MUST BE CONFIRMED
*/
int aucId = AuctionManager.getInstance().getAuctionIndex(_id);
AuctionManager.getInstance().getAuctions().remove(aucId);
return;
}
if (_sellerId > 0)
{
returnItem(_sellerClanName, _highestBidderMaxBid, true);
returnItem(_sellerClanName, ClanHallManager.getInstance().getAuctionableHallById(_itemId).getLease(), false);
}
deleteAuctionFromDB();
L2Clan Clan = ClanTable.getInstance().getClanByName(_bidders.get(_highestBidderId).getClanName());
_bidders.remove(_highestBidderId);
Clan.setAuctionBiddedAt(0, true);
removeBids();
ClanHallManager.getInstance().setOwner(_itemId, Clan);
}
else
{
/** Task waiting ClanHallManager is loaded every 3s */
ThreadPoolManager.getInstance().scheduleGeneral(new AutoEndTask(), 3000);
}
}
/**
* Cancel bid
* @param bidder
*/
public synchronized void cancelBid(int bidder)
{
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM auction_bid WHERE auctionId=? AND bidderId=?"))
{
statement.setInt(1, getId());
statement.setInt(2, bidder);
statement.execute();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Exception: Auction.cancelBid(String bidder): " + e.getMessage(), e);
}
returnItem(_bidders.get(bidder).getClanName(), _bidders.get(bidder).getBid(), true);
ClanTable.getInstance().getClanByName(_bidders.get(bidder).getClanName()).setAuctionBiddedAt(0, true);
_bidders.clear();
loadBid();
}
/** Cancel auction */
public void cancelAuction()
{
deleteAuctionFromDB();
removeBids();
}
/** Confirm an auction */
public void confirmAuction()
{
AuctionManager.getInstance().getAuctions().add(this);
try (Connection con = L2DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("INSERT INTO auction (id, sellerId, sellerName, sellerClanName, itemType, itemId, itemObjectId, itemName, itemQuantity, startingBid, currentBid, endDate) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"))
{
statement.setInt(1, getId());
statement.setInt(2, _sellerId);
statement.setString(3, _sellerName);
statement.setString(4, _sellerClanName);
statement.setString(5, _itemType);
statement.setInt(6, _itemId);
statement.setInt(7, _itemObjectId);
statement.setString(8, _itemName);
statement.setLong(9, _itemQuantity);
statement.setLong(10, _startingBid);
statement.setLong(11, _currentBid);
statement.setLong(12, _endDate);
statement.execute();
statement.close();
}
catch (Exception e)
{
_log.log(Level.SEVERE, "Exception: Auction.load(): " + e.getMessage(), e);
}
}
/**
* Get var auction
* @return
*/
public final int getId()
{
return _id;
}
public final long getCurrentBid()
{
return _currentBid;
}
public final long getEndDate()
{
return _endDate;
}
public final int getHighestBidderId()
{
return _highestBidderId;
}
public final String getHighestBidderName()
{
return _highestBidderName;
}
public final long getHighestBidderMaxBid()
{
return _highestBidderMaxBid;
}
public final int getItemId()
{
return _itemId;
}
public final String getItemName()
{
return _itemName;
}
public final int getItemObjectId()
{
return _itemObjectId;
}
public final long getItemQuantity()
{
return _itemQuantity;
}
public final String getItemType()
{
return _itemType;
}
public final int getSellerId()
{
return _sellerId;
}
public final String getSellerName()
{
return _sellerName;
}
public final String getSellerClanName()
{
return _sellerClanName;
}
public final long getStartingBid()
{
return _startingBid;
}
public final Map getBidders()
{
return _bidders;
}
}