Skip to content

Commit

Permalink
Fixed the plugin requires the chests to be in valid worlds when it lo…
Browse files Browse the repository at this point in the history
…ads data (#208)
  • Loading branch information
OmerBenGera committed Sep 29, 2023
1 parent 5060e71 commit 86678b3
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 86 deletions.
147 changes: 117 additions & 30 deletions src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java
Expand Up @@ -12,6 +12,7 @@
import com.bgsoftware.wildchests.objects.chests.WRegularChest;
import com.bgsoftware.wildchests.objects.chests.WStorageChest;
import com.bgsoftware.wildchests.objects.data.WChestData;
import com.bgsoftware.wildchests.utils.BlockPosition;
import com.bgsoftware.wildchests.utils.ChunkPosition;
import com.bgsoftware.wildchests.utils.Executor;
import com.bgsoftware.wildchests.utils.LocationUtils;
Expand All @@ -21,11 +22,13 @@
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.inventory.ItemStack;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand All @@ -39,56 +42,40 @@ public final class ChestsHandler implements ChestsManager {
private static final WildChestsPlugin plugin = WildChestsPlugin.getPlugin();
private final Map<String, ChestData> chestsData = new HashMap<>();

private final Map<Location, Chest> chests = Maps.newConcurrentMap();
private final Map<BlockPosition, Chest> chests = Maps.newConcurrentMap();
private final Map<ChunkPosition, Set<Chest>> chestsByChunks = Maps.newConcurrentMap();
private final Map<ChunkPosition, Map<BlockPosition, UnloadedChest>> unloadedChests = Maps.newConcurrentMap();

@Override
@Nullable

public Chest getChest(Location location) {
return getChest(location, RegularChest.class);
return getChest(BlockPosition.of(location), RegularChest.class);
}

@Override
@Nullable
public LinkedChest getLinkedChest(Location location) {
return getChest(location, LinkedChest.class);
return getChest(BlockPosition.of(location), LinkedChest.class);
}

@Override
@Nullable
public StorageChest getStorageChest(Location location) {
return getChest(location, StorageChest.class);
return getChest(BlockPosition.of(location), StorageChest.class);
}

@Override
public Chest addChest(UUID placer, Location location, ChestData chestData) {
WChest chest = loadChest(placer, location, chestData);
WChest chest = createChestInternal(placer, location, chestData);
plugin.getDataHandler().insertChest(chest);
Executor.sync(() -> plugin.getNMSInventory().updateTileEntity(chest));
return chest;
}

public WChest loadChest(UUID placer, Location location, ChestData chestData) {
WChest chest;

switch (chestData.getChestType()) {
case CHEST:
chest = new WRegularChest(placer, location, chestData);
break;
case LINKED_CHEST:
chest = new WLinkedChest(placer, location, chestData);
break;
case STORAGE_UNIT:
chest = new WStorageChest(placer, location, chestData);
break;
default:
throw new IllegalArgumentException("Invalid chest at " + location);
}

chests.put(location, chest);
chestsByChunks.computeIfAbsent(ChunkPosition.of(location), s -> Sets.newConcurrentHashSet()).add(chest);

return chest;
public void loadUnloadedChest(UUID placer, BlockPosition position, ChestData chestData, String[] extendedData) {
UnloadedChest unloadedChest = new UnloadedChest(placer, position, chestData, extendedData);
unloadedChests.computeIfAbsent(ChunkPosition.of(position), s -> new LinkedHashMap<>()).put(position, unloadedChest);
}

public void loadChestsData(Map<String, ChestData> chestsData) {
Expand All @@ -108,7 +95,7 @@ public void loadChestsData(Map<String, ChestData> chestsData) {

@Override
public void removeChest(Chest chest) {
chests.remove(chest.getLocation());
chests.remove(BlockPosition.of(chest.getLocation()));

Set<Chest> chunkChests = chestsByChunks.get(ChunkPosition.of(chest.getLocation()));
if (chunkChests != null)
Expand Down Expand Up @@ -185,12 +172,32 @@ public List<ChestData> getAllChestData() {
}

@Nullable
private <T extends Chest> T getChest(Location location, Class<T> chestClass) {
Chest chest = chests.get(location);
private <T extends Chest> T getChest(BlockPosition blockPosition, Class<T> chestClass) {
World world = Bukkit.getWorld(blockPosition.getWorldName());

if (chest == null)
if (world == null)
return null;

Chest chest = chests.get(blockPosition);

if (chest == null) {
ChunkPosition chunkPosition = ChunkPosition.of(blockPosition);
Map<BlockPosition, UnloadedChest> unloadedChests = this.unloadedChests.get(chunkPosition);
if (unloadedChests == null)
return null;

UnloadedChest unloadedChest = unloadedChests.remove(blockPosition);
if (unloadedChest == null)
return null;

if (unloadedChests.isEmpty())
this.unloadedChests.remove(chunkPosition);

chest = loadChestInternal(unloadedChest);
}

Location location = new Location(world, blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());

if (Bukkit.isPrimaryThread() && location.getBlock().getType() != Material.CHEST) {
removeChest(chest);
return null;
Expand All @@ -203,4 +210,84 @@ private <T extends Chest> T getChest(Location location, Class<T> chestClass) {
}
}

public void loadChestsForChunk(Chunk chunk) {
Map<BlockPosition, UnloadedChest> unloadedChests = this.unloadedChests.remove(ChunkPosition.of(chunk));
if (unloadedChests != null)
unloadedChests.values().forEach(this::loadChestInternal);
}

private WChest createChestInternal(UUID placer, Location location, ChestData chestData) {
WChest chest;

switch (chestData.getChestType()) {
case CHEST:
chest = new WRegularChest(placer, location, chestData);
break;
case LINKED_CHEST:
chest = new WLinkedChest(placer, location, chestData);
break;
case STORAGE_UNIT:
chest = new WStorageChest(placer, location, chestData);
break;
default:
throw new IllegalArgumentException("Invalid chest at " + location);
}

chests.put(BlockPosition.of(location), chest);
chestsByChunks.computeIfAbsent(ChunkPosition.of(location), s -> Sets.newConcurrentHashSet()).add(chest);

return chest;
}

private WChest loadChestInternal(UnloadedChest unloadedChest) {
World world = Bukkit.getWorld(unloadedChest.position.getWorldName());

if (world == null) {
throw new IllegalArgumentException("Tried to load chest for an invalid world: " +
unloadedChest.position.getWorldName());
}

Location location = new Location(world, unloadedChest.position.getX(),
unloadedChest.position.getY(), unloadedChest.position.getZ());

WChest chest = createChestInternal(unloadedChest.placer, location, unloadedChest.chestData);

if (chest instanceof StorageChest) {
String item = unloadedChest.extendedData[0];
String amount = unloadedChest.extendedData[1];
String maxAmount = unloadedChest.extendedData[2];
((WStorageChest) chest).loadFromData(item, amount, maxAmount);
} else {
String serialized = unloadedChest.extendedData[0];
if (chest instanceof LinkedChest) {
String linkedChest = unloadedChest.extendedData[1];
((WLinkedChest) chest).loadFromData(serialized, linkedChest);
} else {
((WRegularChest) chest).loadFromData(serialized);
}

if (!serialized.isEmpty() && serialized.toCharArray()[0] != '*') {
chest.executeUpdateStatement(true);
}
}

return chest;
}

private static class UnloadedChest {

private final UUID placer;
private final BlockPosition position;
private final ChestData chestData;
private final String[] extendedData;

UnloadedChest(UUID placer, BlockPosition position, ChestData chestData, String[] extendedData) {
this.placer = placer;
this.position = position;
this.chestData = chestData;
this.extendedData = extendedData;
}

}

}
97 changes: 41 additions & 56 deletions src/main/java/com/bgsoftware/wildchests/handlers/DataHandler.java
Expand Up @@ -3,18 +3,14 @@
import com.bgsoftware.wildchests.WildChestsPlugin;
import com.bgsoftware.wildchests.api.objects.ChestType;
import com.bgsoftware.wildchests.api.objects.chests.Chest;
import com.bgsoftware.wildchests.api.objects.chests.LinkedChest;
import com.bgsoftware.wildchests.api.objects.chests.StorageChest;
import com.bgsoftware.wildchests.api.objects.data.ChestData;
import com.bgsoftware.wildchests.database.DatabaseObject;
import com.bgsoftware.wildchests.database.Query;
import com.bgsoftware.wildchests.database.SQLHelper;
import com.bgsoftware.wildchests.database.StatementHolder;
import com.bgsoftware.wildchests.listeners.ChunksListener;
import com.bgsoftware.wildchests.objects.chests.WChest;
import com.bgsoftware.wildchests.objects.chests.WLinkedChest;
import com.bgsoftware.wildchests.objects.chests.WRegularChest;
import com.bgsoftware.wildchests.objects.chests.WStorageChest;
import com.bgsoftware.wildchests.utils.BlockPosition;
import com.bgsoftware.wildchests.utils.Executor;
import com.bgsoftware.wildchests.utils.LocationUtils;
import org.bukkit.Bukkit;
Expand Down Expand Up @@ -102,14 +98,10 @@ private void loadDatabase() {
SQLHelper.executeUpdate("CREATE TABLE IF NOT EXISTS storage_units (location VARCHAR PRIMARY KEY, placer VARCHAR, chest_data VARCHAR, item VARCHAR, amount VARCHAR, max_amount VARCHAR);");
SQLHelper.executeUpdate("CREATE TABLE IF NOT EXISTS offline_payment (uuid VARCHAR PRIMARY KEY, payment VARCHAR);");

List<Chest> updateContentsChests = new ArrayList<>();

//Loading all tables
SQLHelper.executeQuery("SELECT * FROM chests;", resultSet -> loadResultSet(resultSet, "chests", updateContentsChests));
SQLHelper.executeQuery("SELECT * FROM linked_chests;", resultSet -> loadResultSet(resultSet, "linked_chests", updateContentsChests));
SQLHelper.executeQuery("SELECT * FROM storage_units;", resultSet -> loadResultSet(resultSet, "storage_units", updateContentsChests));

updateContentsChests.forEach(chest -> ((WChest) chest).executeUpdateStatement(false));
SQLHelper.executeQuery("SELECT * FROM chests;", resultSet -> loadResultSet(resultSet, "chests"));
SQLHelper.executeQuery("SELECT * FROM linked_chests;", resultSet -> loadResultSet(resultSet, "linked_chests"));
SQLHelper.executeQuery("SELECT * FROM storage_units;", resultSet -> loadResultSet(resultSet, "storage_units"));

Executor.sync(() -> {
for (World world : Bukkit.getWorlds()) {
Expand All @@ -119,59 +111,52 @@ private void loadDatabase() {
}, 1L);
}

private void loadResultSet(ResultSet resultSet, String tableName, List<Chest> updateContentsChests) throws SQLException {
private void loadResultSet(ResultSet resultSet, String tableName) throws SQLException {
while (resultSet.next()) {
UUID placer = UUID.fromString(resultSet.getString("placer"));
String stringLocation = resultSet.getString("location");
String errorMessage = null;

try {
if (Bukkit.getWorld(stringLocation.split(", ")[0]) == null) {
errorMessage = "Null world.";
} else {
Location location = LocationUtils.fromString(stringLocation);

String chestDataName = resultSet.getString("chest_data");
ChestData chestData = plugin.getChestsManager().getChestData(chestDataName);

if(chestData == null) {
WildChestsPlugin.log("Couldn't load the location " + stringLocation);
WildChestsPlugin.log("The chest data `" + chestDataName + "` does not exist.");
continue;
}

WChest chest = plugin.getChestsManager().loadChest(placer, location, chestData);

if (chest instanceof StorageChest) {
String item = resultSet.getString("item");
String amount = resultSet.getString("amount");
String maxAmount = resultSet.getString("max_amount");
((WStorageChest) chest).loadFromData(item, amount, maxAmount);
} else {
String serialized = resultSet.getString("inventories");
if (chest instanceof LinkedChest) {
String linkedChest = resultSet.getString("linked_chest");
((WLinkedChest) chest).loadFromData(serialized, linkedChest);
} else {
((WRegularChest) chest).loadFromData(serialized);
}

if (!serialized.isEmpty() && serialized.toCharArray()[0] != '*')
updateContentsChests.add(chest);
}
}
} catch (Exception ex) {
errorMessage = ex.getMessage();
}
UUID placer = UUID.fromString(resultSet.getString("placer"));

String chestDataName = resultSet.getString("chest_data");
ChestData chestData = plugin.getChestsManager().getChestData(chestDataName);

if (errorMessage != null) {
if (chestData == null) {
WildChestsPlugin.log("Couldn't load the location " + stringLocation);
WildChestsPlugin.log(errorMessage);
if (errorMessage.contains("Null") && plugin.getSettings().invalidWorldDelete) {
WildChestsPlugin.log("The chest data `" + chestDataName + "` does not exist.");
continue;
}

BlockPosition position = BlockPosition.deserialize(stringLocation);

if (plugin.getSettings().invalidWorldDelete) {
World world = Bukkit.getWorld(position.getWorldName());
if (world == null) {
SQLHelper.executeUpdate("DELETE FROM " + tableName + " WHERE location = '" + stringLocation + "';");
WildChestsPlugin.log("Deleted spawner (" + stringLocation + ") from database.");
continue;
}
}

String[] extendedData;

ChestType chestType = chestData.getChestType();
if (chestType == ChestType.STORAGE_UNIT) {
extendedData = new String[3];
extendedData[0] = resultSet.getString("item");
extendedData[1] = resultSet.getString("amount");
extendedData[2] = resultSet.getString("max_amount");
} else {
if (chestType == ChestType.LINKED_CHEST) {
extendedData = new String[2];
extendedData[1] = resultSet.getString("linked_chest");
} else {
extendedData = new String[1];
}

extendedData[0] = resultSet.getString("inventories");
}

plugin.getChestsManager().loadUnloadedChest(placer, position, chestData, extendedData);
}
}

Expand Down
Expand Up @@ -30,6 +30,8 @@ public void onChunkUnload(ChunkUnloadEvent e){
}

public static void handleChunkLoad(WildChestsPlugin plugin, Chunk chunk){
plugin.getChestsManager().loadChestsForChunk(chunk);

plugin.getChestsManager().getChests(chunk).forEach(chest -> {
Location location = chest.getLocation();
Material blockType = location.getBlock().getType();
Expand Down

0 comments on commit 86678b3

Please sign in to comment.