mirror of
https://github.com/EpicEricEE/ShopChest.git
synced 2025-02-21 15:01:24 +01:00
Dynamic shop loading based on chunk loading
This commit is contained in:
parent
9d8a38a2da
commit
2d832e587a
@ -21,7 +21,7 @@ import de.epiceric.shopchest.api.shop.ShopProduct;
|
||||
public interface ShopManager {
|
||||
|
||||
/**
|
||||
* Gets all shops
|
||||
* Gets all currently loaded shops
|
||||
*
|
||||
* @return a collection of shops
|
||||
*/
|
||||
@ -31,7 +31,7 @@ public interface ShopManager {
|
||||
* Gets the shop by its ID
|
||||
*
|
||||
* @param id the shop's ID
|
||||
* @return the shop or an empty optional if there is no shop
|
||||
* @return the shop or an empty optional if there is no shop loaded
|
||||
* @since 1.13
|
||||
*/
|
||||
Optional<Shop> getShop(int id);
|
||||
@ -40,13 +40,13 @@ public interface ShopManager {
|
||||
* Gets the shop at the given location
|
||||
*
|
||||
* @param location the shop's chest location
|
||||
* @return the shop or an empty optional if there is no shop
|
||||
* @return the shop or an empty optional if there is no shop loaded
|
||||
* @since 1.13
|
||||
*/
|
||||
Optional<Shop> getShop(Location location);
|
||||
|
||||
/**
|
||||
* Gets all shops by the given player
|
||||
* Gets all loaded shops by the given player
|
||||
*
|
||||
* @param vendor the player
|
||||
* @return a collection of shops
|
||||
@ -56,7 +56,7 @@ public interface ShopManager {
|
||||
Collection<Shop> getShops(OfflinePlayer vendor);
|
||||
|
||||
/**
|
||||
* Gets all shops in the given world
|
||||
* Gets all loaded shops in the given world
|
||||
*
|
||||
* @param world the world
|
||||
* @return a collection of shops
|
||||
@ -114,7 +114,7 @@ public interface ShopManager {
|
||||
void removeShop(Shop shop, Consumer<Void> callback, Consumer<Throwable> errorCallback);
|
||||
|
||||
/**
|
||||
* Asynchronously reloads all shops from the database
|
||||
* Removes all shops and reloads the shops in currently loaded chunks
|
||||
* <p>
|
||||
* This does not trigger the {@link ShopReloadEvent}.
|
||||
*
|
||||
|
@ -1,38 +0,0 @@
|
||||
package de.epiceric.shopchest.api.event;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
/**
|
||||
* Called after all shops are initialized after the plugin is enabled
|
||||
*
|
||||
* @since 1.13
|
||||
*/
|
||||
public class ShopInitializedEvent extends Event {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private int amount;
|
||||
|
||||
public ShopInitializedEvent(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of shops that were initialized
|
||||
*
|
||||
* @return the amount of shops
|
||||
* @since 1.13
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package de.epiceric.shopchest.api.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
import de.epiceric.shopchest.api.shop.Shop;
|
||||
|
||||
/**
|
||||
* Called after shops are loaded from the database due to a chunk load
|
||||
*
|
||||
* @since 1.13
|
||||
*/
|
||||
public class ShopLoadedEvent extends Event {
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
private Collection<Shop> shops;
|
||||
|
||||
public ShopLoadedEvent(Collection<Shop> shops) {
|
||||
this.shops = Collections.unmodifiableCollection(shops);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shops that have been loaded
|
||||
*
|
||||
* @return the shops
|
||||
* @since 1.13
|
||||
*/
|
||||
public Collection<Shop> getShops() {
|
||||
return shops;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
@ -94,7 +94,17 @@ public interface ShopPlayer {
|
||||
int getShopLimit();
|
||||
|
||||
/**
|
||||
* Gets the shops this player owns
|
||||
* Gets the amount of shops the given player currently has
|
||||
* <p>
|
||||
* This number includes shops that are not loaded.
|
||||
*
|
||||
* @return the amount of shops
|
||||
* @since 1.13
|
||||
*/
|
||||
int getShopAmount();
|
||||
|
||||
/**
|
||||
* Gets the loaded shops this player owns
|
||||
*
|
||||
* @return a collection of shops
|
||||
* @since 1.13
|
||||
|
@ -5,12 +5,17 @@ import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
@ -22,7 +27,7 @@ import de.epiceric.shopchest.api.ShopManager;
|
||||
import de.epiceric.shopchest.api.command.ShopCommand;
|
||||
import de.epiceric.shopchest.api.config.Config;
|
||||
import de.epiceric.shopchest.api.database.DatabaseType;
|
||||
import de.epiceric.shopchest.api.event.ShopInitializedEvent;
|
||||
import de.epiceric.shopchest.api.event.ShopLoadedEvent;
|
||||
import de.epiceric.shopchest.api.player.ShopPlayer;
|
||||
import de.epiceric.shopchest.command.ShopCommandImpl;
|
||||
import de.epiceric.shopchest.config.ConfigManager;
|
||||
@ -170,13 +175,29 @@ public class ShopChestImpl extends ShopChest {
|
||||
database = new MySQL(this);
|
||||
}
|
||||
|
||||
((ShopManagerImpl) getShopManager()).loadShops(
|
||||
((ShopManagerImpl) getShopManager()).loadShopAmounts(
|
||||
shopAmounts -> {
|
||||
Logger.info("Loaded shop amounts from the database");
|
||||
},
|
||||
error -> {
|
||||
Logger.severe("Failed to load shops amounts from the database");
|
||||
Logger.severe("Shop limits will not be working correctly");
|
||||
Logger.severe(error);
|
||||
}
|
||||
);
|
||||
|
||||
List<Chunk> chunks = new ArrayList<>();
|
||||
for (World world : getServer().getWorlds()) {
|
||||
chunks.addAll(Arrays.asList(world.getLoadedChunks()));
|
||||
}
|
||||
|
||||
((ShopManagerImpl) getShopManager()).loadShops(chunks.toArray(new Chunk[chunks.size()]),
|
||||
shops -> {
|
||||
getServer().getPluginManager().callEvent(new ShopInitializedEvent(shops.size()));
|
||||
getServer().getPluginManager().callEvent(new ShopLoadedEvent(shops));
|
||||
Logger.info("Loaded {0} shops from the database", shops.size());
|
||||
},
|
||||
error -> {
|
||||
Logger.severe("Failed to load shops from database");
|
||||
Logger.severe("Failed to load shops from the database");
|
||||
Logger.severe(error);
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
@ -259,12 +280,17 @@ public class ShopChestImpl extends ShopChest {
|
||||
});
|
||||
}
|
||||
|
||||
/* package-private */ Database getDatabase() {
|
||||
/**
|
||||
* Gets an instance of the plugin's database
|
||||
*
|
||||
* @return the database
|
||||
*/
|
||||
public Database getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance to the config manager
|
||||
* Gets an instance of the config manager
|
||||
*
|
||||
* @return the config manager
|
||||
* @see ConfigManager#get(ShopChestImpl)
|
||||
|
@ -1,23 +1,32 @@
|
||||
package de.epiceric.shopchest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Chest;
|
||||
|
||||
import de.epiceric.shopchest.api.ShopManager;
|
||||
import de.epiceric.shopchest.api.event.ShopLoadedEvent;
|
||||
import de.epiceric.shopchest.api.player.ShopPlayer;
|
||||
import de.epiceric.shopchest.api.shop.Shop;
|
||||
import de.epiceric.shopchest.api.shop.ShopProduct;
|
||||
import de.epiceric.shopchest.shop.ShopImpl;
|
||||
import de.epiceric.shopchest.util.Counter;
|
||||
|
||||
public class ShopManagerImpl implements ShopManager {
|
||||
private static ShopManagerImpl instance;
|
||||
@ -32,6 +41,7 @@ public class ShopManagerImpl implements ShopManager {
|
||||
private ShopChestImpl plugin;
|
||||
|
||||
private Map<String, Map<Location, Shop>> shopsInWorld = new HashMap<>();
|
||||
private Map<UUID, Counter> shopAmounts = new HashMap<>();
|
||||
|
||||
private ShopManagerImpl(ShopChestImpl plugin) {
|
||||
this.plugin = plugin;
|
||||
@ -66,29 +76,40 @@ public class ShopManagerImpl implements ShopManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and caches shops from the database
|
||||
* Loads shops in the given chunks from the database
|
||||
* <p>
|
||||
* Cache will be cleared before new shops are cached.
|
||||
* This will fire a {@link ShopLoadedEvent}.
|
||||
*
|
||||
* @param chunks a collection
|
||||
*/
|
||||
public void loadShops(Consumer<Collection<Shop>> callback, Consumer<Throwable> errorCallback) {
|
||||
public void loadShops(Chunk[] chunks, Consumer<Collection<Shop>> callback, Consumer<Throwable> errorCallback) {
|
||||
plugin.getDatabase().connect(
|
||||
amount -> {
|
||||
plugin.getDatabase().getShops(
|
||||
plugin.getDatabase().getShops(chunks,
|
||||
shops -> {
|
||||
clearShops();
|
||||
shops.stream().forEach(shop -> {
|
||||
((ShopImpl) shop).create();
|
||||
for (Iterator<Shop> it = shops.iterator(); it.hasNext();) {
|
||||
Shop shop = it.next();
|
||||
|
||||
if (getShop(shop.getLocation()).isPresent()) {
|
||||
// A shop is already loaded at the location, which should be the same.
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
String worldName = shop.getWorld().getName();
|
||||
if (!shopsInWorld.containsKey(worldName)) {
|
||||
shopsInWorld.put(worldName, new HashMap<>());
|
||||
}
|
||||
|
||||
((ShopImpl) shop).create();
|
||||
|
||||
shopsInWorld.get(worldName).put(toBlockLocation(shop.getLocation()), shop);
|
||||
|
||||
((ShopImpl) shop).getOtherLocation().ifPresent(otherLoc ->
|
||||
shopsInWorld.get(worldName).put(toBlockLocation(otherLoc), shop));;
|
||||
});
|
||||
callback.accept(shops);
|
||||
shopsInWorld.get(worldName).put(toBlockLocation(otherLoc), shop));
|
||||
}
|
||||
|
||||
callback.accept(Collections.unmodifiableCollection(shops));
|
||||
plugin.getServer().getPluginManager().callEvent(new ShopLoadedEvent(shops));
|
||||
},
|
||||
errorCallback
|
||||
);
|
||||
@ -97,6 +118,31 @@ public class ShopManagerImpl implements ShopManager {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all players' shop amounts from the database
|
||||
*/
|
||||
public void loadShopAmounts(Consumer<Map<UUID, Integer>> callback, Consumer<Throwable> errorCallback) {
|
||||
plugin.getDatabase().getShopAmounts(
|
||||
shopAmounts -> {
|
||||
this.shopAmounts.clear();
|
||||
shopAmounts.forEach((uuid, amount) -> this.shopAmounts.put(uuid, new Counter(amount)));
|
||||
callback.accept(shopAmounts);
|
||||
},
|
||||
errorCallback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the amount of shops a player has
|
||||
*
|
||||
* @param player the player
|
||||
* @return the amount of shops
|
||||
* @see ShopPlayer#getShopAmount()
|
||||
*/
|
||||
public int getShopAmount(OfflinePlayer player) {
|
||||
return shopAmounts.getOrDefault(player.getUniqueId(), new Counter()).get();
|
||||
}
|
||||
|
||||
/* API Implementation */
|
||||
|
||||
@Override
|
||||
@ -152,6 +198,12 @@ public class ShopManagerImpl implements ShopManager {
|
||||
((ShopImpl) shop).getOtherLocation().ifPresent(otherLoc ->
|
||||
shopsInWorld.get(worldName).put(toBlockLocation(otherLoc), shop));
|
||||
|
||||
if (vendor != null) {
|
||||
shopAmounts.compute(vendor.getUniqueId(), (uuid, counter) -> {
|
||||
return counter == null ? new Counter(1) : counter.increment();
|
||||
});
|
||||
}
|
||||
|
||||
callback.accept(shop);
|
||||
},
|
||||
errorCallback
|
||||
@ -168,8 +220,14 @@ public class ShopManagerImpl implements ShopManager {
|
||||
((ShopImpl) shop).destroy();
|
||||
shopsInWorld.get(shop.getWorld().getName()).remove(shop.getLocation());
|
||||
|
||||
getRemainingShopLocation(shop.getId(), shop.getWorld()).ifPresent(otherLoc -> {
|
||||
shopsInWorld.get(shop.getWorld().getName()).remove(otherLoc);
|
||||
getRemainingShopLocation(shop.getId(), shop.getWorld()).ifPresent(otherLoc ->
|
||||
shopsInWorld.get(shop.getWorld().getName()).remove(otherLoc));
|
||||
|
||||
|
||||
shop.getVendor().ifPresent(vendor -> {
|
||||
shopAmounts.compute(vendor.getUniqueId(), (uuid, counter) -> {
|
||||
return counter == null ? new Counter() : counter.decrement();
|
||||
});
|
||||
});
|
||||
|
||||
plugin.getDatabase().removeShop(shop, callback, errorCallback);
|
||||
@ -177,6 +235,14 @@ public class ShopManagerImpl implements ShopManager {
|
||||
|
||||
@Override
|
||||
public void reloadShops(Consumer<Integer> callback, Consumer<Throwable> errorCallback) {
|
||||
List<Chunk> chunks = new ArrayList<>();
|
||||
for (World world : plugin.getServer().getWorlds()) {
|
||||
chunks.addAll(Arrays.asList(world.getLoadedChunks()));
|
||||
}
|
||||
|
||||
loadShops(chunks.toArray(new Chunk[chunks.size()]),
|
||||
shops -> callback.accept(shops.size()),
|
||||
errorCallback
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package de.epiceric.shopchest.database;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
@ -29,8 +30,10 @@ import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
@ -41,6 +44,8 @@ public abstract class Database {
|
||||
private final Set<String> notFoundWorlds = new HashSet<>();
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private boolean initialized;
|
||||
|
||||
String tableShops;
|
||||
String tableLogs;
|
||||
String tableLogouts;
|
||||
@ -344,6 +349,7 @@ public abstract class Database {
|
||||
ResultSet rs = s.executeQuery("SELECT COUNT(id) FROM " + tableShops);
|
||||
if (rs.next()) {
|
||||
int count = rs.getInt(1);
|
||||
initialized = true;
|
||||
callSyncResult(callback, count);
|
||||
} else {
|
||||
throw new SQLException("Count result set has no entries");
|
||||
@ -378,6 +384,32 @@ public abstract class Database {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shop amounts for each player
|
||||
*
|
||||
* @param callback Callback that returns a map of each player's shop amount
|
||||
*/
|
||||
public void getShopAmounts(Consumer<Map<UUID, Integer>> callback, Consumer<Throwable> errorCallback) {
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
Statement s = con.createStatement()) {
|
||||
ResultSet rs = s.executeQuery("SELECT vendor, COUNT(*) AS count FROM " + tableShops + " WHERE shoptype = 'NORMAL' GROUP BY vendor");
|
||||
|
||||
Map<UUID, Integer> result = new HashMap<>();
|
||||
while (rs.next()) {
|
||||
UUID uuid = UUID.fromString(rs.getString("vendor"));
|
||||
result.put(uuid, rs.getInt("count"));
|
||||
}
|
||||
|
||||
callSyncResult(callback, result);
|
||||
} catch (SQLException e) {
|
||||
callSyncError(errorCallback, e);
|
||||
Logger.severe("Failed to get shop amounts from database");
|
||||
Logger.severe(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all shops from the database
|
||||
*
|
||||
@ -387,54 +419,103 @@ public abstract class Database {
|
||||
* collection of all shops (as
|
||||
* {@code Collection<Shop>})
|
||||
*/
|
||||
public void getShops(Consumer<Collection<Shop>> callback, Consumer<Throwable> errorCallback) {
|
||||
public void getShops(Chunk[] chunks, Consumer<Collection<Shop>> callback, Consumer<Throwable> errorCallback) {
|
||||
// Split chunks into packages containing each {splitSize} chunks at max
|
||||
int splitSize = 80;
|
||||
int parts = (int) Math.ceil(chunks.length / (double) splitSize);
|
||||
Chunk[][] splitChunks = new Chunk[parts][];
|
||||
for (int i = 0; i < parts; i++) {
|
||||
int size = i < parts - 1 ? splitSize : chunks.length % splitSize;
|
||||
Chunk[] tmp = new Chunk[size];
|
||||
System.arraycopy(chunks, i * splitSize, tmp, 0, size);
|
||||
splitChunks[i] = tmp;
|
||||
}
|
||||
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
List<Shop> shops = new ArrayList<>();
|
||||
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM " + tableShops + "")) {
|
||||
ResultSet rs = ps.executeQuery();
|
||||
// Send a request for each chunk package
|
||||
for (Chunk[] newChunks : splitChunks) {
|
||||
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
|
||||
String worldName = rs.getString("world");
|
||||
World world = plugin.getServer().getWorld(worldName);
|
||||
|
||||
if (world == null) {
|
||||
if (!notFoundWorlds.contains(worldName)) {
|
||||
Logger.warning("Could not find world with name \"{0}\"", worldName);
|
||||
notFoundWorlds.add(worldName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean admin = rs.getString("shoptype").equalsIgnoreCase("ADMIN");
|
||||
OfflinePlayer vendor = admin ? null
|
||||
: plugin.getServer().getOfflinePlayer(UUID.fromString(rs.getString("vendor")));
|
||||
|
||||
int x = rs.getInt("x");
|
||||
int y = rs.getInt("y");
|
||||
int z = rs.getInt("z");
|
||||
Location location = new Location(world, x, y, z);
|
||||
|
||||
ItemStack itemStack = decodeItemStack(rs.getString("product"));
|
||||
int amount = rs.getInt("amount");
|
||||
ShopProduct product = new ShopProductImpl(itemStack, amount);
|
||||
|
||||
double buyPrice = rs.getDouble("buyprice");
|
||||
double sellPrice = rs.getDouble("sellprice");
|
||||
|
||||
shops.add(new ShopImpl(id, vendor, product, location, buyPrice, sellPrice));
|
||||
// Map chunks by world
|
||||
Map<String, Set<Chunk>> chunksByWorld = new HashMap<>();
|
||||
for (Chunk chunk : newChunks) {
|
||||
String world = chunk.getWorld().getName();
|
||||
Set<Chunk> chunksForWorld = chunksByWorld.getOrDefault(world, new HashSet<>());
|
||||
chunksForWorld.add(chunk);
|
||||
chunksByWorld.put(world, chunksForWorld);
|
||||
}
|
||||
|
||||
callSyncResult(callback, Collections.unmodifiableCollection(shops));
|
||||
} catch (SQLException e) {
|
||||
callSyncError(errorCallback, e);
|
||||
Logger.severe("Failed to get shops from database");
|
||||
Logger.severe(e);
|
||||
// Create query dynamically
|
||||
String query = "SELECT * FROM " + tableShops + " WHERE ";
|
||||
for (String world : chunksByWorld.keySet()) {
|
||||
query += "(world = ? AND (";
|
||||
int chunkNum = chunksByWorld.get(world).size();
|
||||
for (int i = 0; i < chunkNum; i++) {
|
||||
query += "((x BETWEEN ? AND ?) AND (z BETWEEN ? AND ?)) OR ";
|
||||
}
|
||||
query += "1=0)) OR ";
|
||||
}
|
||||
query += "1=0";
|
||||
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement(query)) {
|
||||
int index = 0;
|
||||
for (String world : chunksByWorld.keySet()) {
|
||||
ps.setString(++index, world);
|
||||
for (Chunk chunk : chunksByWorld.get(world)) {
|
||||
int minX = chunk.getX() * 16;
|
||||
int minZ = chunk.getZ() * 16;
|
||||
ps.setInt(++index, minX);
|
||||
ps.setInt(++index, minX + 15);
|
||||
ps.setInt(++index, minZ);
|
||||
ps.setInt(++index, minZ + 15);
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt("id");
|
||||
|
||||
String worldName = rs.getString("world");
|
||||
World world = plugin.getServer().getWorld(worldName);
|
||||
|
||||
if (world == null) {
|
||||
if (!notFoundWorlds.contains(worldName)) {
|
||||
Logger.warning("Could not find world with name \"{0}\"", worldName);
|
||||
notFoundWorlds.add(worldName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean admin = rs.getString("shoptype").equalsIgnoreCase("ADMIN");
|
||||
OfflinePlayer vendor = admin ? null
|
||||
: plugin.getServer().getOfflinePlayer(UUID.fromString(rs.getString("vendor")));
|
||||
|
||||
int x = rs.getInt("x");
|
||||
int y = rs.getInt("y");
|
||||
int z = rs.getInt("z");
|
||||
Location location = new Location(world, x, y, z);
|
||||
|
||||
ItemStack itemStack = decodeItemStack(rs.getString("product"));
|
||||
int amount = rs.getInt("amount");
|
||||
ShopProduct product = new ShopProductImpl(itemStack, amount);
|
||||
|
||||
double buyPrice = rs.getDouble("buyprice");
|
||||
double sellPrice = rs.getDouble("sellprice");
|
||||
|
||||
shops.add(new ShopImpl(id, vendor, product, location, buyPrice, sellPrice));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
callSyncError(errorCallback, e);
|
||||
Logger.severe("Failed to get shops from database");
|
||||
Logger.severe(e);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callSyncResult(callback, Collections.unmodifiableCollection(shops));
|
||||
});
|
||||
}
|
||||
|
||||
@ -664,4 +745,11 @@ public abstract class Database {
|
||||
dataSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the database has been fully initialized
|
||||
*/
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package de.epiceric.shopchest.listener;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
|
||||
import de.epiceric.shopchest.ShopChestImpl;
|
||||
import de.epiceric.shopchest.ShopManagerImpl;
|
||||
import de.epiceric.shopchest.api.ShopChest;
|
||||
import de.epiceric.shopchest.util.Logger;
|
||||
|
||||
public class ChunkLoadListener implements Listener {
|
||||
private ShopChest plugin;
|
||||
|
||||
private final Set<Chunk> newLoadedChunks = new HashSet<>();
|
||||
|
||||
public ChunkLoadListener(ShopChest plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onChunkLoad(ChunkLoadEvent e) {
|
||||
if (!((ShopChestImpl) plugin).getDatabase().isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait 10 ticks after first event is triggered, so that multiple
|
||||
// chunk loads can be handled at the same time without having to
|
||||
// send a database request for each chunk.
|
||||
|
||||
if (newLoadedChunks.isEmpty()) {
|
||||
plugin.getServer().getScheduler().runTaskLater(plugin, () -> {
|
||||
int chunkCount = newLoadedChunks.size();
|
||||
((ShopManagerImpl) plugin.getShopManager()).loadShops(newLoadedChunks.toArray(new Chunk[chunkCount]),
|
||||
shops -> {},
|
||||
error -> {
|
||||
Logger.severe("Failed to load shops in newly loaded chunks");
|
||||
Logger.severe(error);
|
||||
}
|
||||
);
|
||||
newLoadedChunks.clear();
|
||||
}, 10L);
|
||||
}
|
||||
newLoadedChunks.add(e.getChunk());
|
||||
}
|
||||
}
|
@ -82,7 +82,7 @@ public class ShopCommandListener implements Listener {
|
||||
}
|
||||
|
||||
// Check shop limit
|
||||
if (player.getShops().size() >= player.getShopLimit()) {
|
||||
if (player.getShopAmount() >= player.getShopLimit()) {
|
||||
e.setCancelled(true);
|
||||
player.sendMessage("§cYou don't have permission to create any more shops."); // TODO: i18n
|
||||
return;
|
||||
@ -99,7 +99,7 @@ public class ShopCommandListener implements Listener {
|
||||
boolean allowDecimals = Config.SHOP_CREATION_ALLOW_DECIMAL_PRICES.get();
|
||||
if (!allowDecimals && (!isInt(buyPrice) || !isInt(buyPrice))) {
|
||||
e.setCancelled(true);
|
||||
player.sendMessage("§cThe prices must not contain decimals.");
|
||||
player.sendMessage("§cThe prices must not contain decimals."); // TODO: i18n
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import java.util.UUID;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import de.epiceric.shopchest.ShopManagerImpl;
|
||||
import de.epiceric.shopchest.api.ShopChest;
|
||||
import de.epiceric.shopchest.api.config.Config;
|
||||
import de.epiceric.shopchest.api.flag.Flag;
|
||||
@ -76,6 +77,11 @@ public class ShopPlayerImpl implements ShopPlayer {
|
||||
return Config.CORE_DEFAULT_SHOP_LIMIT.get(); // TODO: permissions based
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getShopAmount() {
|
||||
return ((ShopManagerImpl) plugin.getShopManager()).getShopAmount(getBukkitPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Shop> getShops() {
|
||||
return plugin.getShopManager().getShops(getBukkitPlayer());
|
||||
|
@ -0,0 +1,55 @@
|
||||
package de.epiceric.shopchest.util;
|
||||
|
||||
/**
|
||||
* Represents a counter for integers greather than or equal to zero.
|
||||
*/
|
||||
public final class Counter {
|
||||
private int value;
|
||||
|
||||
/**
|
||||
* Creates a counter with a starting value of zero
|
||||
*/
|
||||
public Counter() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a counter with the given starting value
|
||||
* @param value the starting value of this counter
|
||||
*/
|
||||
public Counter(int value) {
|
||||
set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the counter by one and returns itself
|
||||
*/
|
||||
public final Counter increment() {
|
||||
this.value++;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the counter by one if its value is greater than zero and returns itself
|
||||
*/
|
||||
public final Counter decrement() {
|
||||
this.value = Math.max(0, this.value - 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the counter's value to the given value or zero if the given value is negative
|
||||
* @param value the value to set the counter to
|
||||
*/
|
||||
public final Counter set(int value) {
|
||||
this.value = Math.max(0, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value
|
||||
*/
|
||||
public final int get() {
|
||||
return value;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user