diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java index cfb973a0..cf4c7a5c 100644 --- a/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java +++ b/src/main/java/com/eternalcode/parcellockers/ParcelLockers.java @@ -126,15 +126,16 @@ public void onEnable() { parcelContentRepository, scheduler, config, - this.economy + this.economy, + server ); UserValidationService userValidationService = new UserValidator(); - UserManager userManager = new UserManagerImpl(userRepository, userValidationService); + UserManager userManager = new UserManagerImpl(userRepository, userValidationService, server); LockerValidationService lockerValidationService = new LockerValidator(); - LockerManager lockerManager = new LockerManager(config, lockerRepository, lockerValidationService, parcelRepository); + LockerManager lockerManager = new LockerManager(config, lockerRepository, lockerValidationService, parcelRepository, server); ParcelContentManager parcelContentManager = new ParcelContentManager(parcelContentRepository); - ItemStorageManager itemStorageManager = new ItemStorageManager(itemStorageRepository); + ItemStorageManager itemStorageManager = new ItemStorageManager(itemStorageRepository, server); DeliveryManager deliveryManager = new DeliveryManager(deliveryRepository); ParcelDispatchService parcelDispatchService = new ParcelDispatchService( diff --git a/src/main/java/com/eternalcode/parcellockers/itemstorage/ItemStorageManager.java b/src/main/java/com/eternalcode/parcellockers/itemstorage/ItemStorageManager.java index 3ad125e1..0695e6e2 100644 --- a/src/main/java/com/eternalcode/parcellockers/itemstorage/ItemStorageManager.java +++ b/src/main/java/com/eternalcode/parcellockers/itemstorage/ItemStorageManager.java @@ -1,5 +1,6 @@ package com.eternalcode.parcellockers.itemstorage; +import com.eternalcode.parcellockers.itemstorage.event.ItemStorageUpdateEvent; import com.eternalcode.parcellockers.itemstorage.repository.ItemStorageRepository; import com.eternalcode.parcellockers.notification.NoticeService; import com.github.benmanes.caffeine.cache.Cache; @@ -9,6 +10,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.inventory.ItemStack; @@ -17,9 +19,11 @@ public class ItemStorageManager { private final Cache cache; private final ItemStorageRepository itemStorageRepository; + private final Server server; - public ItemStorageManager(ItemStorageRepository itemStorageRepository) { + public ItemStorageManager(ItemStorageRepository itemStorageRepository, Server server) { this.itemStorageRepository = itemStorageRepository; + this.server = server; this.cache = Caffeine.newBuilder() .expireAfterWrite(6, TimeUnit.HOURS) @@ -45,13 +49,22 @@ public ItemStorage getOrCreate(UUID owner, List items) { } public ItemStorage create(UUID owner, List items) { - ItemStorage itemStorage = new ItemStorage(owner, items); - if (this.cache.getIfPresent(owner) != null) { - throw new IllegalStateException("ItemStorage for owner " + owner + " already exists. Use ItemStorageManager#getOrCreate method instead."); + ItemStorage oldItemStorage = this.cache.getIfPresent(owner); + ItemStorage newItemStorage = new ItemStorage(owner, items); + + if (oldItemStorage != null) { + // This is an update operation - fire ItemStorageUpdateEvent + ItemStorageUpdateEvent event = new ItemStorageUpdateEvent(oldItemStorage, newItemStorage); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + throw new IllegalStateException("ItemStorage update was cancelled by event"); + } } - this.cache.put(owner, itemStorage); - this.itemStorageRepository.save(itemStorage); - return itemStorage; + + this.cache.put(owner, newItemStorage); + this.itemStorageRepository.save(newItemStorage); + return newItemStorage; } private void cacheAll() { diff --git a/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java b/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java index b55b1c6c..430e2ccb 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/LockerManager.java @@ -1,6 +1,8 @@ package com.eternalcode.parcellockers.locker; import com.eternalcode.parcellockers.configuration.implementation.PluginConfig; +import com.eternalcode.parcellockers.locker.event.LockerCreateEvent; +import com.eternalcode.parcellockers.locker.event.LockerDeleteEvent; import com.eternalcode.parcellockers.locker.repository.LockerRepository; import com.eternalcode.parcellockers.locker.validation.LockerValidationService; import com.eternalcode.parcellockers.notification.NoticeService; @@ -18,6 +20,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.function.Function; +import org.bukkit.Server; import org.bukkit.command.CommandSender; public class LockerManager { @@ -26,6 +29,7 @@ public class LockerManager { private final LockerRepository lockerRepository; private final LockerValidationService validationService; private final ParcelRepository parcelRepository; + private final Server server; private final Cache lockersByUUID; private final Cache lockersByPosition; @@ -34,12 +38,14 @@ public LockerManager( PluginConfig config, LockerRepository lockerRepository, LockerValidationService validationService, - ParcelRepository parcelRepository + ParcelRepository parcelRepository, + Server server ) { this.config = config; this.lockerRepository = lockerRepository; this.validationService = validationService; this.parcelRepository = parcelRepository; + this.server = server; this.lockersByUUID = Caffeine.newBuilder() .expireAfterAccess(Duration.ofHours(2)) @@ -96,7 +102,7 @@ public CompletableFuture> get(Page page) { return this.lockerRepository.findPage(page); } - public CompletableFuture create(UUID uniqueId, String name, Position position) { + public CompletableFuture create(UUID uniqueId, String name, Position position, UUID playerUUID) { return CompletableFuture.supplyAsync(() -> { ValidationResult validation = this.validationService.validateCreateParameters(uniqueId, name, position); @@ -115,6 +121,15 @@ public CompletableFuture create(UUID uniqueId, String name, Position pos } Locker locker = new Locker(uniqueId, name, position); + + // Fire LockerCreateEvent + LockerCreateEvent event = new LockerCreateEvent(locker, playerUUID); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + throw new ValidationException("Locker creation cancelled by event"); + } + this.lockersByUUID.put(uniqueId, locker); this.lockersByPosition.put(position, locker); @@ -122,11 +137,53 @@ public CompletableFuture create(UUID uniqueId, String name, Position pos }).thenCompose(Function.identity()); } - public CompletableFuture delete(UUID uniqueId) { + public CompletableFuture delete(UUID uniqueId, UUID playerUUID) { + // Get locker from cache first for the event + Locker locker = this.lockersByUUID.getIfPresent(uniqueId); + + // If not in cache, try to fetch from database + if (locker == null) { + return this.lockerRepository.find(uniqueId).thenCompose(optionalLocker -> { + if (optionalLocker.isEmpty()) { + // Locker doesn't exist, nothing to delete + return CompletableFuture.completedFuture(null); + } + + Locker foundLocker = optionalLocker.get(); + + // Fire LockerDeleteEvent before deletion + LockerDeleteEvent event = new LockerDeleteEvent(foundLocker, playerUUID); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + // Event was cancelled, don't delete + return CompletableFuture.completedFuture(null); + } + + // Proceed with deletion + return this.lockerRepository.delete(uniqueId).thenAccept(deleted -> { + if (deleted > 0) { + this.lockersByUUID.invalidate(uniqueId); + this.lockersByPosition.asMap().values().removeIf(l -> l.uuid().equals(uniqueId)); + } + }); + }); + } + + // Fire LockerDeleteEvent before deletion + LockerDeleteEvent event = new LockerDeleteEvent(locker, playerUUID); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + // Event was cancelled, don't delete + return CompletableFuture.completedFuture(null); + } + + // Proceed with deletion return this.lockerRepository.delete(uniqueId).thenAccept(deleted -> { if (deleted > 0) { this.lockersByUUID.invalidate(uniqueId); - this.lockersByPosition.asMap().values().removeIf(locker -> locker.uuid().equals(uniqueId)); + this.lockersByPosition.asMap().values().removeIf(l -> l.uuid().equals(uniqueId)); } }); } diff --git a/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerBreakController.java b/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerBreakController.java index df1daf6a..03180901 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerBreakController.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerBreakController.java @@ -57,7 +57,7 @@ public void onBlockBreak(BlockBreakEvent event) { return; } - this.lockerManager.delete(locker.get().uuid()); + this.lockerManager.delete(locker.get().uuid(), player.getUniqueId()); this.noticeService.player(player.getUniqueId(), messages -> messages.locker.deleted); diff --git a/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerPlaceController.java b/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerPlaceController.java index 499ba76e..21f1206b 100644 --- a/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerPlaceController.java +++ b/src/main/java/com/eternalcode/parcellockers/locker/controller/LockerPlaceController.java @@ -128,7 +128,7 @@ public void onBlockPlace(BlockPlaceEvent event) { location.getWorld().getBlockAt(location).setBlockData(data); }); - this.lockerManager.create(UUID.randomUUID(), description, PositionAdapter.convert(location)) + this.lockerManager.create(UUID.randomUUID(), description, PositionAdapter.convert(location), player.getUniqueId()) .thenAccept(locker -> { this.noticeService.create() .player(player.getUniqueId()) diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java index bd977458..554acb1f 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/service/ParcelServiceImpl.java @@ -9,6 +9,8 @@ import com.eternalcode.parcellockers.content.repository.ParcelContentRepository; import com.eternalcode.parcellockers.notification.NoticeService; import com.eternalcode.parcellockers.parcel.Parcel; +import com.eternalcode.parcellockers.parcel.event.ParcelCollectEvent; +import com.eternalcode.parcellockers.parcel.event.ParcelSendEvent; import com.eternalcode.parcellockers.parcel.repository.ParcelRepository; import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.PageResult; @@ -23,6 +25,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import net.milkbowl.vault.economy.Economy; +import org.bukkit.Server; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; @@ -42,6 +45,7 @@ public class ParcelServiceImpl implements ParcelService { private final Scheduler scheduler; private final PluginConfig config; private final Economy economy; + private final Server server; private final Cache parcelsByUuid; @@ -51,7 +55,8 @@ public ParcelServiceImpl( ParcelContentRepository parcelContentRepository, Scheduler scheduler, PluginConfig config, - Economy economy + Economy economy, + Server server ) { this.noticeService = noticeService; this.parcelRepository = parcelRepository; @@ -59,6 +64,7 @@ public ParcelServiceImpl( this.scheduler = scheduler; this.config = config; this.economy = economy; + this.server = server; this.parcelsByUuid = Caffeine.newBuilder() .expireAfterAccess(CACHE_EXPIRE_HOURS, TimeUnit.HOURS) @@ -73,6 +79,15 @@ public CompletableFuture send(Player sender, Parcel parcel, List messages.parcel.cannotSend); + return CompletableFuture.completedFuture(false); + } + List itemsCopy = items.stream() .map(ItemStack::clone) .toList(); @@ -158,6 +173,15 @@ public CompletableFuture collect(Player player, Parcel parcel) { Objects.requireNonNull(player, "Player cannot be null"); Objects.requireNonNull(parcel, "Parcel cannot be null"); + // Fire ParcelCollectEvent + ParcelCollectEvent event = new ParcelCollectEvent(parcel); + this.server.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.cannotCollect); + return CompletableFuture.completedFuture(null); + } + return this.parcelContentRepository.find(parcel.uuid()).thenCompose(optional -> { if (optional.isEmpty()) { this.noticeService.player(player.getUniqueId(), messages -> messages.parcel.cannotCollect); diff --git a/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java b/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java index 43bbc01a..98adac43 100644 --- a/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java +++ b/src/main/java/com/eternalcode/parcellockers/parcel/task/ParcelSendTask.java @@ -3,8 +3,10 @@ import com.eternalcode.parcellockers.delivery.DeliveryManager; import com.eternalcode.parcellockers.parcel.Parcel; import com.eternalcode.parcellockers.parcel.ParcelStatus; +import com.eternalcode.parcellockers.parcel.event.ParcelDeliverEvent; import com.eternalcode.parcellockers.parcel.service.ParcelService; import java.util.logging.Logger; +import org.bukkit.Bukkit; import org.bukkit.scheduler.BukkitRunnable; public class ParcelSendTask extends BukkitRunnable { @@ -40,6 +42,15 @@ public void run() { ParcelStatus.DELIVERED ); + // Fire ParcelDeliverEvent + ParcelDeliverEvent event = new ParcelDeliverEvent(updated); + Bukkit.getPluginManager().callEvent(event); + + if (event.isCancelled()) { + LOGGER.info("ParcelDeliverEvent was cancelled for parcel " + updated.uuid()); + return; + } + this.parcelService.update(updated) .exceptionally(throwable -> { LOGGER.severe("Failed to update parcel " + updated.uuid() + " to DELIVERED status: " + throwable.getMessage()); diff --git a/src/main/java/com/eternalcode/parcellockers/user/UserManager.java b/src/main/java/com/eternalcode/parcellockers/user/UserManager.java index e7daaaf2..fa386f12 100644 --- a/src/main/java/com/eternalcode/parcellockers/user/UserManager.java +++ b/src/main/java/com/eternalcode/parcellockers/user/UserManager.java @@ -17,4 +17,6 @@ public interface UserManager { CompletableFuture> get(UUID uniqueId); CompletableFuture> getPage(Page page); + + CompletableFuture changeName(UUID uuid, String newName); } diff --git a/src/main/java/com/eternalcode/parcellockers/user/UserManagerImpl.java b/src/main/java/com/eternalcode/parcellockers/user/UserManagerImpl.java index 8238c579..2c65ae91 100644 --- a/src/main/java/com/eternalcode/parcellockers/user/UserManagerImpl.java +++ b/src/main/java/com/eternalcode/parcellockers/user/UserManagerImpl.java @@ -3,6 +3,8 @@ import com.eternalcode.parcellockers.shared.Page; import com.eternalcode.parcellockers.shared.PageResult; import com.eternalcode.parcellockers.shared.validation.ValidationResult; +import com.eternalcode.parcellockers.user.event.UserChangeNameEvent; +import com.eternalcode.parcellockers.user.event.UserCreateEvent; import com.eternalcode.parcellockers.user.repository.UserRepository; import com.eternalcode.parcellockers.user.validation.UserValidationService; import com.github.benmanes.caffeine.cache.Cache; @@ -12,18 +14,21 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import org.bukkit.Server; public class UserManagerImpl implements UserManager { private final UserRepository userRepository; private final UserValidationService validationService; + private final Server server; private final Cache usersByUUID; private final Cache usersByName; - public UserManagerImpl(UserRepository userRepository, UserValidationService validationService) { + public UserManagerImpl(UserRepository userRepository, UserValidationService validationService, Server server) { this.userRepository = userRepository; this.validationService = validationService; + this.server = server; this.usersByUUID = Caffeine.newBuilder() .expireAfterAccess(2, TimeUnit.HOURS) .maximumSize(10_000) @@ -98,6 +103,11 @@ public CompletableFuture create(UUID uuid, String name) { } User user = new User(uuid, name); + + // Fire UserCreateEvent + UserCreateEvent event = new UserCreateEvent(user); + this.server.getPluginManager().callEvent(event); + this.usersByUUID.put(uuid, user); this.usersByName.put(name, user); this.userRepository.save(user); @@ -105,4 +115,30 @@ public CompletableFuture create(UUID uuid, String name) { return user; }); } + + @Override + public CompletableFuture changeName(UUID uuid, String newName) { + return CompletableFuture.supplyAsync(() -> { + User oldUser = this.usersByUUID.getIfPresent(uuid); + + if (oldUser == null) { + throw new ValidationException("User not found with UUID: " + uuid); + } + + String oldName = oldUser.name(); + + // Fire UserChangeNameEvent + UserChangeNameEvent event = new UserChangeNameEvent(oldUser, oldName); + this.server.getPluginManager().callEvent(event); + + // Update cache + User updatedUser = new User(uuid, newName); + this.usersByUUID.put(uuid, updatedUser); + this.usersByName.invalidate(oldName); + this.usersByName.put(newName, updatedUser); + + // Update in repository + return this.userRepository.changeName(uuid, newName); + }).thenCompose(future -> future); + } }