feat(items): secure bound ability inventory
This commit is contained in:
@@ -10,17 +10,20 @@ public final class AbilityItemRefreshTask implements Runnable {
|
||||
private final AbilityItemService items;
|
||||
private final Server server;
|
||||
private final Clock clock;
|
||||
private final CapturedMobInventoryService capturedMobInventory;
|
||||
|
||||
public AbilityItemRefreshTask(
|
||||
TyrantStateManager stateManager,
|
||||
AbilityReadinessService readiness,
|
||||
AbilityItemService items,
|
||||
CapturedMobInventoryService capturedMobInventory,
|
||||
Server server,
|
||||
Clock clock
|
||||
) {
|
||||
this.stateManager = stateManager;
|
||||
this.readiness = readiness;
|
||||
this.items = items;
|
||||
this.capturedMobInventory = capturedMobInventory;
|
||||
this.server = server;
|
||||
this.clock = clock;
|
||||
}
|
||||
@@ -40,6 +43,7 @@ public final class AbilityItemRefreshTask implements Runnable {
|
||||
}
|
||||
items.removeInvalid(player, after);
|
||||
items.recover(player, after);
|
||||
capturedMobInventory.reconcile(player, after);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public final class BoundItemListener implements Listener {
|
||||
if (items.isBoundAbilityItem(event.getCurrentItem())
|
||||
|| items.isBoundAbilityItem(event.getCursor())) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)
|
||||
|| event.isShiftClick()
|
||||
|| event.getClickedInventory() == null
|
||||
|| !event.getClickedInventory().equals(player.getInventory())) {
|
||||
event.setCancelled(true);
|
||||
|
||||
@@ -81,11 +81,17 @@ public final class BukkitAbilityItemService implements AbilityItemService {
|
||||
@Override
|
||||
public void removeInvalid(Player player, PlayerState state) {
|
||||
ItemStack[] contents = player.getInventory().getContents();
|
||||
java.util.Set<Ability> retained = java.util.EnumSet.noneOf(Ability.class);
|
||||
for (int index = 0; index < contents.length; index++) {
|
||||
ItemStack item = contents[index];
|
||||
if (isBoundAbilityItem(item)
|
||||
&& (owner(item).filter(state.playerId()::equals).isEmpty()
|
||||
|| ability(item).filter(state.readyAbilityItems()::contains).isEmpty())) {
|
||||
if (!isBoundAbilityItem(item)) {
|
||||
continue;
|
||||
}
|
||||
Optional<Ability> itemAbility = ability(item);
|
||||
boolean valid = owner(item).filter(state.playerId()::equals).isPresent()
|
||||
&& itemAbility.filter(state.readyAbilityItems()::contains).isPresent()
|
||||
&& itemAbility.filter(retained::add).isPresent();
|
||||
if (!valid) {
|
||||
player.getInventory().setItem(index, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public final class CapturedMobInventoryService {
|
||||
private final CapturedMobItemService items;
|
||||
private final PluginSettings settings;
|
||||
|
||||
public CapturedMobInventoryService(
|
||||
CapturedMobItemService items,
|
||||
PluginSettings settings
|
||||
) {
|
||||
this.items = items;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public void reconcile(Player player, PlayerState state) {
|
||||
Map<UUID, CapturedMob> stored = new java.util.HashMap<>();
|
||||
for (CapturedMob mob : state.capturedMobs()) {
|
||||
try {
|
||||
UUID captureId = UUID.fromString(mob.data().get("capture-id"));
|
||||
stored.putIfAbsent(captureId, mob);
|
||||
} catch (IllegalArgumentException | NullPointerException ignored) {
|
||||
// Invalid custody records are not materialized as items.
|
||||
}
|
||||
}
|
||||
Set<UUID> retained = new HashSet<>();
|
||||
ItemStack[] contents = player.getInventory().getContents();
|
||||
for (int index = 0; index < contents.length; index++) {
|
||||
ItemStack item = contents[index];
|
||||
if (!items.isCapturedMob(item)) {
|
||||
continue;
|
||||
}
|
||||
UUID captureId = items.captureId(item).orElseThrow();
|
||||
boolean valid = items.owner(item).filter(state.playerId()::equals).isPresent()
|
||||
&& stored.containsKey(captureId) && retained.add(captureId);
|
||||
if (!valid) {
|
||||
player.getInventory().setItem(index, null);
|
||||
}
|
||||
}
|
||||
for (Map.Entry<UUID, CapturedMob> entry : stored.entrySet()) {
|
||||
if (retained.contains(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
Map<Integer, ItemStack> leftovers = player.getInventory().addItem(
|
||||
items.create(
|
||||
state.playerId(), entry.getKey(), entry.getValue().entityType()
|
||||
)
|
||||
);
|
||||
if (!leftovers.isEmpty()) {
|
||||
player.sendMessage(ChatColor.RED + settings.inventoryFullMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,9 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
|
||||
this, settings
|
||||
);
|
||||
CapturedMobService capturedMobs = new CapturedMobService();
|
||||
CapturedMobInventoryService capturedMobInventory = new CapturedMobInventoryService(
|
||||
capturedMobItems, settings
|
||||
);
|
||||
AssassinAbilityService assassinAbilities = new AssassinAbilityService(
|
||||
readiness, settings
|
||||
);
|
||||
@@ -170,7 +173,8 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
|
||||
getServer().getScheduler().runTaskTimer(
|
||||
this,
|
||||
new AbilityItemRefreshTask(
|
||||
stateManager, readiness, abilityItems, getServer(), clock
|
||||
stateManager, readiness, abilityItems, capturedMobInventory,
|
||||
getServer(), clock
|
||||
),
|
||||
20L,
|
||||
20L
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntitySnapshot;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Tameable;
|
||||
@@ -159,6 +160,7 @@ public final class TamerListener implements Listener {
|
||||
if (capturedItems.isCapturedMob(event.getCurrentItem())
|
||||
|| capturedItems.isCapturedMob(event.getCursor())) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)
|
||||
|| event.isShiftClick()
|
||||
|| event.getClickedInventory() == null
|
||||
|| !event.getClickedInventory().equals(player.getInventory())) {
|
||||
event.setCancelled(true);
|
||||
@@ -166,6 +168,16 @@ public final class TamerListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onFrame(PlayerInteractEntityEvent event) {
|
||||
if (event.getRightClicked() instanceof ItemFrame
|
||||
&& capturedItems.isCapturedMob(
|
||||
event.getPlayer().getInventory().getItem(event.getHand())
|
||||
)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDispense(BlockDispenseEvent event) {
|
||||
if (capturedItems.isCapturedMob(event.getItem())) {
|
||||
|
||||
Reference in New Issue
Block a user