Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,13 @@ public interface ChestsManager {
*/
List<ChestData> getAllChestData();

}
/**
* Get the count of chests placed by a specific player of a specific type.
*
* @param placer The UUID of the player who placed the chests
* @param chestDataName The name of the chest data to count
* @return The number of chests of the specified type placed by the player
*/
int getChestCount(UUID placer, String chestDataName);

}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert changes

Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,4 @@ public interface ChestData {
*/
void setParticles(List<String> particles);

}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert changes

Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void tick(Level level, BlockPos blockPos, BlockState blockState, WildChes
double z = blockPos.getZ() + level.getRandom().nextFloat();
for (String particle : chestData.getChestParticles()) {
try {
this.serverLevel.sendParticlesSource(null,
this.serverLevel.sendParticles(
CraftParticle.createParticleParam(Particle.valueOf(particle), null),
false, false, x, y, z, 0, 0.0, 0.0, 0.0, 1.0);
} catch (Exception ignored) {
Expand Down Expand Up @@ -335,7 +335,7 @@ private void handleSuctionItems(ChestData chestData) {
}

private void handleItemSuctionRemoval(ItemEntity itemEntity) {
this.serverLevel.sendParticlesSource(null,
this.serverLevel.sendParticles(
CraftParticle.createParticleParam(Particle.CLOUD, null),
false, false, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(),
0, 0.0, 0.0, 0.0, 1.0);
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert changes

Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ include 'NMS:v1_20_4'
include 'NMS:v1_21'
include 'NMS:v1_21_3'
include 'NMS:v1_21_4'
include 'NMS:v1_21_5'
include 'NMS:v1_21_5'
1 change: 1 addition & 0 deletions src/main/java/com/bgsoftware/wildchests/Locale.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public final class Locale {
public static Locale FORMAT_THOUSANDS = new Locale("FORMAT_THOUSANDS");
public static Locale MONEY_EARNED_OFFLINE = new Locale("MONEY_EARNED_OFFLINE");
public static Locale LEFTOVERS_ITEMS_WARNING = new Locale("LEFTOVERS_ITEMS_WARNING");
public static Locale CHEST_LIMIT_REACHED = new Locale("CHEST_LIMIT_REACHED");


private Locale(String identifier) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ public List<Chest> getChests(World world) {
Collections.unmodifiableList(new LinkedList<>(chunkChests));
}

@Override
public int getChestCount(UUID placer, String chestDataName) {
ChestData chestData = getChestData(chestDataName);
if (chestData == null)
return 0;
return (int) chests.values().stream()
.filter(chest -> chest.getPlacer().equals(placer) && chest.getData() == chestData)
.count();
}

@Override
public List<Chest> getNearbyChests(Location location) {
return getChests(location.getWorld()).stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public final class SettingsHandler {
public final boolean wildStackerHook;
public final int maximumPickupDelay;
public final int maxStacksOnDrop;
public final boolean enableChestLimits;
public final Map<String, Integer> defaultChestLimits;

public SettingsHandler(WildChestsPlugin plugin) {
WildChestsPlugin.log("Loading configuration started...");
Expand Down Expand Up @@ -72,6 +74,16 @@ public SettingsHandler(WildChestsPlugin plugin) {
wildStackerHook = cfg.getBoolean("hooks.wildstacker", true);
maximumPickupDelay = cfg.getInt("maximum-pickup-delay", 32767);
maxStacksOnDrop = cfg.getInt("max-stacks-on-drop", -1);
enableChestLimits = cfg.getBoolean("chest-limits.enabled", false);

defaultChestLimits = new HashMap<>();
if (cfg.contains("chest-limits.default")) {
ConfigurationSection limitsSection = cfg.getConfigurationSection("chest-limits.default");
for (String chestType : limitsSection.getKeys(false)) {
int limit = limitsSection.getInt(chestType, -1);
defaultChestLimits.put(chestType, limit);
}
}

Map<String, Double> prices = new HashMap<>();

Expand Down Expand Up @@ -252,5 +264,4 @@ private static ChestData loadChestFromSection(WildChestsPlugin plugin, String ch

return chestData;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.bgsoftware.wildchests.WildChestsPlugin;
import com.bgsoftware.wildchests.api.objects.chests.Chest;
import com.bgsoftware.wildchests.api.objects.data.ChestData;
import com.bgsoftware.wildchests.utils.ChestLimitUtils;
import com.bgsoftware.wildchests.utils.ItemUtils;
import org.bukkit.GameMode;
import org.bukkit.Material;
Expand Down Expand Up @@ -79,6 +80,23 @@ public void onChestPlace(BlockPlaceEvent e) {
if (chestData == null)
return;

if (plugin.getSettings().enableChestLimits) {
String chestDataName = chestData.getName();
Player player = e.getPlayer();

ChestLimitUtils.ChestLimitResult limitResult = ChestLimitUtils.getPlayerChestLimitResult(player, chestDataName);
if (limitResult.hasLimit()) {
int limit = limitResult.getLimit();
int currentCount = plugin.getChestsManager().getChestCount(player.getUniqueId(), chestDataName);

if (limit != Integer.MAX_VALUE && currentCount >= limit) {
e.setCancelled(true);
Locale.CHEST_LIMIT_REACHED.send(player, limit, chestDataName);
return;
}
}
}

Chest chest = plugin.getChestsManager().addChest(e.getPlayer().getUniqueId(), e.getBlockPlaced().getLocation(), chestData);

plugin.getProviders().notifyChestPlaceListeners(chest);
Expand Down Expand Up @@ -161,4 +179,4 @@ private static EntityType lookupEntityType(String name) {
}
}

}
}
72 changes: 72 additions & 0 deletions src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.bgsoftware.wildchests.utils;

import com.bgsoftware.wildchests.WildChestsPlugin;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachmentInfo;

import java.util.OptionalInt;

public final class ChestLimitUtils {

private ChestLimitUtils() {}

/**
* Result class to hold both limit existence and value information
*/
public static class ChestLimitResult {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to Result and move this down, below the method to check for limit

private final boolean hasLimit;
private final int limit;

private ChestLimitResult(boolean hasLimit, int limit) {
this.hasLimit = hasLimit;
this.limit = limit;
}

public boolean hasLimit() {
return hasLimit;
}

public int getLimit() {
return limit;
}

public static ChestLimitResult noLimit() {
return new ChestLimitResult(false, Integer.MAX_VALUE);
}

public static ChestLimitResult withLimit(int limit) {
return new ChestLimitResult(true, limit);
}
}

public static ChestLimitResult getPlayerChestLimitResult(Player player, String chestDataName) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to checkChestLimit

String permissionPrefix = "wildchests.limit." + chestDataName + ".";

OptionalInt maxPermissionLimit = player.getEffectivePermissions().stream()
.filter(PermissionAttachmentInfo::getValue)
.map(PermissionAttachmentInfo::getPermission)
.filter(permission -> permission.startsWith(permissionPrefix))
.map(permission -> permission.substring(permissionPrefix.length()))
.mapToInt(limitStr -> {
try {
return Integer.parseInt(limitStr);
} catch (NumberFormatException e) {
return -1;
}
})
.filter(limit -> limit >= 0)
.max();

if (maxPermissionLimit.isPresent()) {
return ChestLimitResult.withLimit(maxPermissionLimit.getAsInt());
}

Integer defaultLimit = WildChestsPlugin.getPlugin().getSettings().defaultChestLimits.get(chestDataName);
if (defaultLimit != null && defaultLimit != -1) {
int actualLimit = defaultLimit == 0 ? Integer.MAX_VALUE : defaultLimit;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should support limit 0 - this means players won't be able to place that chest
Instead, check if the limit is non-negative.

return ChestLimitResult.withLimit(actualLimit);
}

return ChestLimitResult.noLimit();
}
}
20 changes: 20 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ maximum-pickup-delay: 32767
# You can disable this feature by setting it to -1.
max-stacks-on-drop: -1

# Permission-based chest placement limits configuration
chest-limits:
# Enable chest placement limits
# When enabled, players can only place a limited number of each chest type based on permissions.
# Permission format: wildchests.limit.<type>.<amount>
# For example: wildchests.limit.linked_chest.5 allows placing up to 5 linked chests
enabled: false

# Default limits for each chest type when no specific permission is given.
# Set to 0 for unlimited, or any positive number for the default limit.
# Set to -1 to disable this chest type entirely.
# These only apply when chest-limits.enabled is true.
default:
linked_chest: 0
large_chest: 0
sell_chest: 0
auto_crafter: 0
storage_unit: 0
chunk_collector: 0

# The plugin brings tons of new custom and unique chests to your server. All the chests are configurable, and
# you can create and mix between them. You can create chests that stores infinite amount of items, chests that
# are connected to player vaults or factions, linked chests and many more!
Expand Down
5 changes: 4 additions & 1 deletion src/main/resources/lang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,7 @@ RELOAD_SUCCESS: '&5&lWildChests &7Successfully reloaded the configuration files.
# Called when selling items into sell-chest
SOLD_CHEST_HEADER: '&5&lWildChests &7Your transactions in the last 10 minutes:'
SOLD_CHEST_LINE: '&5&lWildChests &7x{0} {1} for ${2}'
SOLD_CHEST_FOOTER: '&5&lWildChests &7You earned a total of ${0}'
SOLD_CHEST_FOOTER: '&5&lWildChests &7You earned a total of ${0}'

# Called when a player tries to place a chest but has reached the limit for that type
CHEST_LIMIT_REACHED: '&5&lWildChests &7You have reached the maximum limit of {0} {1} chests!'