612 lines
26 KiB
Java
612 lines
26 KiB
Java
package org.maxgamer.QuickShop.Shop;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.Set;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.ChatColor;
|
|
import org.bukkit.Chunk;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.World;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.block.BlockFace;
|
|
import org.bukkit.block.BlockState;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.block.Action;
|
|
import org.bukkit.event.player.PlayerInteractEvent;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.material.Sign;
|
|
import org.maxgamer.QuickShop.QuickShop;
|
|
import org.maxgamer.QuickShop.Database.Database;
|
|
import org.maxgamer.QuickShop.Util.MsgUtil;
|
|
import org.maxgamer.QuickShop.Util.Util;
|
|
|
|
import cn.citycraft.PluginHelper.kit.PluginKit;
|
|
|
|
public class ShopManager {
|
|
private final HashMap<String, Info> actions = new HashMap<String, Info>();
|
|
|
|
private final QuickShop plugin;
|
|
private final HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>> shops = new HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>>();
|
|
|
|
public ShopManager(final QuickShop plugin) {
|
|
this.plugin = plugin;
|
|
}
|
|
|
|
/**
|
|
* Checks other plugins to make sure they can use the chest they're making a
|
|
* shop.
|
|
*
|
|
* @param p
|
|
* The player to check
|
|
* @param b
|
|
* The block to check
|
|
* @return True if they're allowed to place a shop there.
|
|
*/
|
|
public boolean canBuildShop(final Player p, final Block b, final BlockFace bf) {
|
|
if (plugin.getConfigManager().isLimit()) {
|
|
int owned = 0;
|
|
final Iterator<Shop> it = getShopIterator();
|
|
while (it.hasNext()) {
|
|
if (it.next().getOwner().equals(p.getName())) {
|
|
owned++;
|
|
}
|
|
}
|
|
final int max = plugin.getShopLimit(p);
|
|
if (owned + 1 > max) {
|
|
p.sendMessage(MsgUtil.p("you-cant-create-more-shop", owned));
|
|
return false;
|
|
}
|
|
}
|
|
/* 修复其他插件调用产生的报错... */
|
|
try {
|
|
final PlayerInteractEvent pie = new PlayerInteractEvent(p, Action.RIGHT_CLICK_BLOCK, new ItemStack(Material.AIR), b, bf); // PIE = PlayerInteractEvent - What else?
|
|
Bukkit.getPluginManager().callEvent(pie);
|
|
pie.getPlayer().closeInventory(); // If the player has chat open, this will close their chat.
|
|
if (pie.isCancelled()) {
|
|
return false;
|
|
}
|
|
} catch (final Exception e) {
|
|
}
|
|
final ShopPreCreateEvent spce = new ShopPreCreateEvent(p, b.getLocation());
|
|
Bukkit.getPluginManager().callEvent(spce);
|
|
if (spce.isCancelled()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes all shops from memory and the world. Does not delete them from
|
|
* the database. Call this on plugin disable ONLY.
|
|
*/
|
|
public void clear() {
|
|
if (plugin.getConfigManager().isDisplay()) {
|
|
for (final World world : Bukkit.getWorlds()) {
|
|
for (final Chunk chunk : world.getLoadedChunks()) {
|
|
final HashMap<Location, Shop> inChunk = this.getShops(chunk);
|
|
if (inChunk == null) {
|
|
continue;
|
|
}
|
|
for (final Shop shop : inChunk.values()) {
|
|
shop.onUnload();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.actions.clear();
|
|
this.shops.clear();
|
|
}
|
|
|
|
public void createShop(final Shop shop) {
|
|
final Location loc = shop.getLocation();
|
|
final ItemStack item = shop.getItem();
|
|
try {
|
|
// Write it to the database
|
|
final String q = "INSERT INTO shops (owner, price, itemConfig, x, y, z, world, unlimited, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
plugin.getDB().execute(q,
|
|
shop.getOwner().toString(),
|
|
shop.getPrice(),
|
|
Util.serialize(item),
|
|
loc.getBlockX(),
|
|
loc.getBlockY(),
|
|
loc.getBlockZ(),
|
|
loc.getWorld().getName(),
|
|
(shop.isUnlimited() ? 1 : 0),
|
|
shop.getShopType().toID());
|
|
// Add it to the world
|
|
addShop(loc.getWorld().getName(), shop);
|
|
} catch (final Exception e) {
|
|
plugin.getLogger().warning("无法保存商店到数据库! 下次重启商店将会消失!");
|
|
plugin.getLogger().warning("错误信息: " + e.getMessage());
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public String format(final double d) {
|
|
return plugin.getEcon().format(d);
|
|
}
|
|
|
|
/**
|
|
* @return Returns the HashMap<Player name, shopInfo>. Info contains what
|
|
* their last question etc was.
|
|
*/
|
|
public HashMap<String, Info> getActions() {
|
|
return this.actions;
|
|
}
|
|
|
|
public Database getDatabase() {
|
|
return plugin.getDB();
|
|
}
|
|
|
|
/**
|
|
* Gets a shop in a specific location
|
|
*
|
|
* @param loc
|
|
* The location to get the shop from
|
|
* @return The shop at that location
|
|
*/
|
|
public Shop getShop(final Location loc) {
|
|
final HashMap<Location, Shop> inChunk = getShops(loc.getChunk());
|
|
if (inChunk == null) {
|
|
return null;
|
|
}
|
|
// We can do this because WorldListener updates the world reference so
|
|
// the world in loc is the same as world in inChunk.get(loc)
|
|
return inChunk.get(loc);
|
|
}
|
|
|
|
/**
|
|
* Returns a new shop iterator object, allowing iteration over shops easily,
|
|
* instead of sorting through a 3D hashmap.
|
|
*
|
|
* @return a new shop iterator object.
|
|
*/
|
|
public Iterator<Shop> getShopIterator() {
|
|
return new ShopIterator();
|
|
}
|
|
|
|
/**
|
|
* Returns a hashmap of World -> Chunk -> Shop
|
|
*
|
|
* @return a hashmap of World -> Chunk -> Shop
|
|
*/
|
|
public HashMap<String, HashMap<ShopChunk, HashMap<Location, Shop>>> getShops() {
|
|
return this.shops;
|
|
}
|
|
|
|
/**
|
|
* Returns a hashmap of Shops
|
|
*
|
|
* @param c
|
|
* The chunk to search. Referencing doesn't matter, only
|
|
* coordinates and world are used.
|
|
* @return
|
|
*/
|
|
public HashMap<Location, Shop> getShops(final Chunk c) {
|
|
// long start = System.nanoTime();
|
|
final HashMap<Location, Shop> shops = getShops(c.getWorld().getName(), c.getX(), c.getZ());
|
|
// long end = System.nanoTime();
|
|
// System.out.println("Chunk lookup in " + ((end - start)/1000000.0) +
|
|
// "ms.");
|
|
return shops;
|
|
}
|
|
|
|
/**
|
|
* Returns a hashmap of Chunk -> Shop
|
|
*
|
|
* @param world
|
|
* The name of the world (case sensitive) to get the list of
|
|
* shops from
|
|
* @return a hashmap of Chunk -> Shop
|
|
*/
|
|
public HashMap<ShopChunk, HashMap<Location, Shop>> getShops(final String world) {
|
|
return this.shops.get(world);
|
|
}
|
|
|
|
public HashMap<Location, Shop> getShops(final String world, final int chunkX, final int chunkZ) {
|
|
final HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = this.getShops(world);
|
|
if (inWorld == null) {
|
|
return null;
|
|
}
|
|
final ShopChunk shopChunk = new ShopChunk(world, chunkX, chunkZ);
|
|
return inWorld.get(shopChunk);
|
|
}
|
|
|
|
public void handleChat(final Player p, final String msgs) {
|
|
final String message = ChatColor.stripColor(msgs);
|
|
final HashMap<String, Info> actions = getActions();
|
|
// They wanted to do something.
|
|
final Info info = actions.remove(p.getName());
|
|
if (info == null) {
|
|
return; // multithreaded means this can happen
|
|
}
|
|
/* Creation handling */
|
|
if (info.getAction() == ShopAction.CREATE) {
|
|
try {
|
|
// Checking the shop can be created
|
|
if (plugin.getShopManager().getShop(info.getLocation()) != null) {
|
|
p.sendMessage(MsgUtil.p("shop-already-owned"));
|
|
return;
|
|
}
|
|
if (Util.getSecondHalf(info.getLocation().getBlock()) != null && !p.hasPermission("quickshop.create.double")) {
|
|
p.sendMessage(MsgUtil.p("no-double-chests"));
|
|
return;
|
|
}
|
|
if (Util.canBeShop(info.getLocation().getBlock()) == false) {
|
|
p.sendMessage(MsgUtil.p("chest-was-removed"));
|
|
return;
|
|
}
|
|
// Price per item
|
|
double price;
|
|
if (plugin.getConfig().getBoolean("whole-number-prices-only")) {
|
|
price = Integer.parseInt(message);
|
|
} else {
|
|
price = Double.parseDouble(message);
|
|
}
|
|
if (price < 0.01) {
|
|
p.sendMessage(MsgUtil.p("price-too-cheap"));
|
|
return;
|
|
}
|
|
final double tax = plugin.getConfig().getDouble("shop.cost");
|
|
// Tax refers to the cost to create a shop. Not actual
|
|
// tax, that would be silly
|
|
if (tax != 0 && plugin.getEcon().getBalance(p.getName()) < tax) {
|
|
p.sendMessage(MsgUtil.p("you-cant-afford-a-new-shop", format(tax)));
|
|
return;
|
|
}
|
|
// Create the sample shop.
|
|
final Shop shop = new ContainerShop(info.getLocation(), price, info.getItem(), p.getName());
|
|
// This must be called after the event has been called.
|
|
// Else, if the event is cancelled, they won't get their
|
|
// money back.
|
|
if (tax != 0) {
|
|
if (!plugin.getEcon().withdraw(p.getName(), tax)) {
|
|
p.sendMessage(MsgUtil.p("you-cant-afford-a-new-shop", format(tax)));
|
|
return;
|
|
}
|
|
plugin.getEcon().deposit(plugin.getConfig().getString("tax-account"), tax);
|
|
}
|
|
final ShopCreateEvent e = new ShopCreateEvent(shop, p);
|
|
Bukkit.getPluginManager().callEvent(e);
|
|
if (e.isCancelled()) {
|
|
return;
|
|
}
|
|
shop.onLoad();
|
|
/* The shop has hereforth been successfully created */
|
|
createShop(shop);
|
|
p.sendMessage(MsgUtil.p("success-created-shop"));
|
|
final Location loc = shop.getLocation();
|
|
plugin.log(String.format("玩家: %s 创建了一个 %s 商店 在 (%s - %s, %s, %s)", p.getName(), shop.getDataName(), loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ()));
|
|
if (!plugin.getConfig().getBoolean("shop.lock")) {
|
|
// Warn them if they haven't been warned since
|
|
// reboot
|
|
final Set<String> warings = plugin.getConfigManager().getWarnings();
|
|
if (!warings.contains(p.getName())) {
|
|
p.sendMessage(MsgUtil.p("shops-arent-locked"));
|
|
warings.add(p.getName());
|
|
}
|
|
}
|
|
// Figures out which way we should put the sign on and
|
|
// sets its text.
|
|
if (info.getSignBlock() != null && info.getSignBlock().getType() == Material.AIR && plugin.getConfig().getBoolean("shop.auto-sign")) {
|
|
final BlockState bs = info.getSignBlock().getState();
|
|
final BlockFace bf = info.getLocation().getBlock().getFace(info.getSignBlock());
|
|
bs.setType(Material.WALL_SIGN);
|
|
final Sign sign = (Sign) bs.getData();
|
|
sign.setFacingDirection(bf);
|
|
PluginKit.runTask(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
bs.update(true);
|
|
}
|
|
});
|
|
shop.setSignText();
|
|
}
|
|
if (shop instanceof ContainerShop) {
|
|
final ContainerShop cs = (ContainerShop) shop;
|
|
if (cs.isDoubleShop()) {
|
|
final Shop nextTo = cs.getAttachedShop();
|
|
if (nextTo.getPrice() > shop.getPrice()) {
|
|
// The one next to it must always be a
|
|
// buying shop.
|
|
p.sendMessage(MsgUtil.p("buying-more-than-selling"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* They didn't enter a number. */
|
|
catch (final NumberFormatException ex) {
|
|
p.sendMessage(MsgUtil.p("shop-creation-cancelled"));
|
|
return;
|
|
}
|
|
}
|
|
/* Purchase Handling */
|
|
else if (info.getAction() == ShopAction.BUY)
|
|
|
|
{
|
|
int amount = 0;
|
|
try {
|
|
amount = Integer.parseInt(message);
|
|
} catch (final NumberFormatException e) {
|
|
p.sendMessage(MsgUtil.p("shop-purchase-cancelled"));
|
|
return;
|
|
}
|
|
// Get the shop they interacted with
|
|
final Shop shop = plugin.getShopManager().getShop(info.getLocation());
|
|
// It's not valid anymore
|
|
if (shop == null || Util.canBeShop(info.getLocation().getBlock()) == false) {
|
|
p.sendMessage(MsgUtil.p("chest-was-removed"));
|
|
return;
|
|
}
|
|
if (info.hasChanged(shop)) {
|
|
p.sendMessage(MsgUtil.p("shop-has-changed"));
|
|
return;
|
|
}
|
|
if (shop.isSelling()) {
|
|
final int stock = shop.getRemainingStock();
|
|
if (stock < amount) {
|
|
p.sendMessage(MsgUtil.p("shop-stock-too-low", "" + shop.getRemainingStock(), shop.getDataName()));
|
|
return;
|
|
}
|
|
if (amount == 0) {
|
|
// Dumb.
|
|
MsgUtil.sendPurchaseSuccess(p, shop, amount);
|
|
return;
|
|
} else if (amount < 0) {
|
|
// & Dumber
|
|
p.sendMessage(MsgUtil.p("negative-amount"));
|
|
return;
|
|
}
|
|
final int pSpace = Util.countSpace(p.getInventory(), shop.getItem());
|
|
if (amount > pSpace) {
|
|
p.sendMessage(MsgUtil.p("not-enough-space", "" + pSpace));
|
|
return;
|
|
}
|
|
final ShopPurchaseEvent e = new ShopPurchaseEvent(shop, p, amount);
|
|
Bukkit.getPluginManager().callEvent(e);
|
|
if (e.isCancelled()) {
|
|
return; // Cancelled
|
|
}
|
|
// Money handling
|
|
if (!p.getName().equals(shop.getOwner())) {
|
|
// Check their balance. Works with *most* economy
|
|
// plugins*
|
|
if (plugin.getEcon().getBalance(p.getName()) < amount * shop.getPrice()) {
|
|
p.sendMessage(MsgUtil.p("you-cant-afford-to-buy", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(p.getName()))));
|
|
return;
|
|
}
|
|
// Don't tax them if they're purchasing from
|
|
// themselves.
|
|
// Do charge an amount of tax though.
|
|
final double tax = plugin.getConfigManager().getTax();
|
|
final double total = amount * shop.getPrice();
|
|
if (!plugin.getEcon().withdraw(p.getName(), total)) {
|
|
p.sendMessage(MsgUtil.p("you-cant-afford-to-buy", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(p.getName()))));
|
|
return;
|
|
}
|
|
if (!shop.isUnlimited() || plugin.getConfig().getBoolean("shop.pay-unlimited-shop-owners")) {
|
|
plugin.getEcon().deposit(shop.getOwner(), total * (1 - tax));
|
|
if (tax != 0) {
|
|
plugin.getEcon().deposit(plugin.getConfigManager().getTaxAccount(), total * tax);
|
|
}
|
|
}
|
|
// Notify the shop owner
|
|
if (plugin.getConfigManager().isShowTax()) {
|
|
String msg = MsgUtil.p("player-bought-from-your-store-tax", p.getName(), "" + amount, shop.getDataName(), Util.format((tax * total)));
|
|
if (stock == amount) {
|
|
msg += "\n"
|
|
+ MsgUtil.p("shop-out-of-stock", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ(), shop.getDataName());
|
|
}
|
|
MsgUtil.send(shop.getOwner(), msg);
|
|
} else {
|
|
String msg = MsgUtil.p("player-bought-from-your-store", p.getName(), "" + amount, shop.getDataName());
|
|
if (stock == amount) {
|
|
msg += "\n"
|
|
+ MsgUtil.p("shop-out-of-stock", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ(), shop.getDataName());
|
|
}
|
|
MsgUtil.send(shop.getOwner(), msg);
|
|
}
|
|
}
|
|
// Transfers the item from A to B
|
|
shop.sell(p, amount);
|
|
MsgUtil.sendPurchaseSuccess(p, shop, amount);
|
|
plugin.log(String.format("玩家: %s 从 %s 购买了 %s 件商品 花费 %s", p.getName(), shop.toString(), amount, shop.getPrice() * amount));
|
|
} else if (shop.isBuying()) {
|
|
final int space = shop.getRemainingSpace();
|
|
if (space < amount) {
|
|
p.sendMessage(MsgUtil.p("shop-has-no-space", "" + space, shop.getDataName()));
|
|
return;
|
|
}
|
|
final int count = Util.countItems(p.getInventory(), shop.getItem());
|
|
// Not enough items
|
|
if (amount > count) {
|
|
p.sendMessage(MsgUtil.p("you-dont-have-that-many-items", "" + count, shop.getDataName()));
|
|
return;
|
|
}
|
|
if (amount == 0) {
|
|
// Dumb.
|
|
MsgUtil.sendPurchaseSuccess(p, shop, amount);
|
|
return;
|
|
} else if (amount < 0) {
|
|
// & Dumber
|
|
p.sendMessage(MsgUtil.p("negative-amount"));
|
|
return;
|
|
}
|
|
// Money handling
|
|
if (!p.getName().equals(shop.getOwner())) {
|
|
// Don't tax them if they're purchasing from
|
|
// themselves.
|
|
// Do charge an amount of tax though.
|
|
final double tax = plugin.getConfigManager().getTax();
|
|
final double total = amount * shop.getPrice();
|
|
if (!shop.isUnlimited() || plugin.getConfig().getBoolean("shop.pay-unlimited-shop-owners")) {
|
|
// Tries to check their balance nicely to see if
|
|
// they can afford it.
|
|
if (plugin.getEcon().getBalance(shop.getOwner()) < amount * shop.getPrice()) {
|
|
p.sendMessage(MsgUtil.p("the-owner-cant-afford-to-buy-from-you", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(shop.getOwner()))));
|
|
return;
|
|
}
|
|
// Check for plugins faking econ.has(amount)
|
|
if (!plugin.getEcon().withdraw(shop.getOwner(), total)) {
|
|
p.sendMessage(MsgUtil.p("the-owner-cant-afford-to-buy-from-you", format(amount * shop.getPrice()), format(plugin.getEcon().getBalance(shop.getOwner()))));
|
|
return;
|
|
}
|
|
if (tax != 0) {
|
|
plugin.getEcon().deposit(plugin.getConfigManager().getTaxAccount(), total * tax);
|
|
}
|
|
}
|
|
// Give them the money after we know we succeeded
|
|
plugin.getEcon().deposit(p.getName(), total * (1 - tax));
|
|
// Notify the owner of the purchase.
|
|
String msg = MsgUtil.p("player-sold-to-your-store", p.getName(), "" + amount, shop.getDataName());
|
|
if (space == amount) {
|
|
msg += "\n" + MsgUtil.p("shop-out-of-space", "" + shop.getLocation().getBlockX(), "" + shop.getLocation().getBlockY(), "" + shop.getLocation().getBlockZ());
|
|
}
|
|
MsgUtil.send(shop.getOwner(), msg);
|
|
}
|
|
shop.buy(p, amount);
|
|
MsgUtil.sendSellSuccess(p, shop, amount);
|
|
plugin.log(String.format("玩家: %s 出售了 %s 件商品 到 %s 获得 %s", p.getName(), amount, shop.toString(), shop.getPrice() * amount));
|
|
}
|
|
shop.setSignText(); // Update the signs count
|
|
}
|
|
/* If it was already cancelled (from destroyed) */
|
|
else
|
|
|
|
{
|
|
return; // It was cancelled, go away.
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Loads the given shop into storage. This method is used for loading data
|
|
* from the database. Do not use this method to create a shop.
|
|
*
|
|
* @param world
|
|
* The world the shop is in
|
|
* @param shop
|
|
* The shop to load
|
|
*/
|
|
public void loadShop(final String world, final Shop shop) {
|
|
this.addShop(world, shop);
|
|
}
|
|
|
|
/**
|
|
* Removes a shop from the world. Does NOT remove it from the database. *
|
|
* REQUIRES * the world to be loaded
|
|
*
|
|
* @param shop
|
|
* The shop to remove
|
|
*/
|
|
public void removeShop(final Shop shop) {
|
|
final Location loc = shop.getLocation();
|
|
final String world = loc.getWorld().getName();
|
|
final HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = this.getShops().get(world);
|
|
final int x = (int) Math.floor((shop.getLocation().getBlockX()) / 16.0);
|
|
final int z = (int) Math.floor((shop.getLocation().getBlockZ()) / 16.0);
|
|
final ShopChunk shopChunk = new ShopChunk(world, x, z);
|
|
final HashMap<Location, Shop> inChunk = inWorld.get(shopChunk);
|
|
inChunk.remove(loc);
|
|
}
|
|
|
|
/**
|
|
* Adds a shop to the world. Does NOT require the chunk or world to be
|
|
* loaded
|
|
*
|
|
* @param world
|
|
* The name of the world
|
|
* @param shop
|
|
* The shop to add
|
|
*/
|
|
private void addShop(final String world, final Shop shop) {
|
|
HashMap<ShopChunk, HashMap<Location, Shop>> inWorld = this.getShops().get(world);
|
|
// There's no world storage yet. We need to create that hashmap.
|
|
if (inWorld == null) {
|
|
inWorld = new HashMap<ShopChunk, HashMap<Location, Shop>>(3);
|
|
// Put it in the data universe
|
|
this.getShops().put(world, inWorld);
|
|
}
|
|
// Calculate the chunks coordinates. These are 1,2,3 for each chunk, NOT
|
|
// location rounded to the nearest 16.
|
|
final int x = (int) Math.floor((shop.getLocation().getBlockX()) / 16.0);
|
|
final int z = (int) Math.floor((shop.getLocation().getBlockZ()) / 16.0);
|
|
// Get the chunk set from the world info
|
|
final ShopChunk shopChunk = new ShopChunk(world, x, z);
|
|
HashMap<Location, Shop> inChunk = inWorld.get(shopChunk);
|
|
// That chunk data hasn't been created yet - Create it!
|
|
if (inChunk == null) {
|
|
inChunk = new HashMap<Location, Shop>(1);
|
|
// Put it in the world
|
|
inWorld.put(shopChunk, inChunk);
|
|
}
|
|
// Put the shop in its location in the chunk list.
|
|
inChunk.put(shop.getLocation(), shop);
|
|
}
|
|
|
|
public class ShopIterator implements Iterator<Shop> {
|
|
private Iterator<HashMap<Location, Shop>> chunks;
|
|
private Shop current;
|
|
private Iterator<Shop> shops;
|
|
private final Iterator<HashMap<ShopChunk, HashMap<Location, Shop>>> worlds;
|
|
|
|
public ShopIterator() {
|
|
worlds = getShops().values().iterator();
|
|
}
|
|
|
|
/**
|
|
* Returns true if there is still more shops to iterate over.
|
|
*/
|
|
@Override
|
|
public boolean hasNext() {
|
|
if (shops == null || !shops.hasNext()) {
|
|
if (chunks == null || !chunks.hasNext()) {
|
|
if (!worlds.hasNext()) {
|
|
return false;
|
|
}
|
|
chunks = worlds.next().values().iterator();
|
|
return hasNext();
|
|
}
|
|
shops = chunks.next().values().iterator();
|
|
return hasNext();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Fetches the next shop. Throws NoSuchElementException if there are no
|
|
* more shops.
|
|
*/
|
|
@Override
|
|
public Shop next() {
|
|
if (shops == null || !shops.hasNext()) {
|
|
if (chunks == null || !chunks.hasNext()) {
|
|
if (!worlds.hasNext()) {
|
|
throw new NoSuchElementException("No more shops to iterate over!");
|
|
}
|
|
chunks = worlds.next().values().iterator();
|
|
}
|
|
shops = chunks.next().values().iterator();
|
|
}
|
|
if (!shops.hasNext()) {
|
|
return this.next(); // Skip to the next one (Empty iterator?)
|
|
}
|
|
current = shops.next();
|
|
return current;
|
|
}
|
|
|
|
/**
|
|
* Removes the current shop. This method will delete the shop from
|
|
* memory and the database.
|
|
*/
|
|
@Override
|
|
public void remove() {
|
|
current.delete(false);
|
|
shops.remove();
|
|
}
|
|
}
|
|
} |