Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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 @@ -128,7 +128,7 @@
this.dropService = new DropServiceImpl();
this.dropKeepInventoryService = new DropKeepInventoryServiceImpl();

UpdaterService updaterService = new UpdaterService(this.getDescription());

Check warning on line 131 in eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java

View workflow job for this annotation

GitHub Actions / build

[deprecation] getDescription() in JavaPlugin has been deprecated

MiniMessage miniMessage = MiniMessage.builder()
.postProcessor(new AdventureLegacyColorPostProcessor())
Expand Down Expand Up @@ -208,8 +208,7 @@
new CommandsBlocker(this.fightManager, noticeService, pluginConfig),
new ElytraBlocker(this.fightManager, pluginConfig),
new ElytraEquipBlocker(this.fightManager, noticeService, pluginConfig, server),
new FlyingBlocker(this.fightManager, pluginConfig, server),
new PlaceBlockBlocker(this.fightManager, noticeService, pluginConfig)
new FlyingBlocker(this.fightManager, pluginConfig, server)
);

new KnockbackMountController(noticeService, this.regionProvider, this.fightManager).register(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,9 @@ public FightManagerImpl(EventManager eventManager) {

@Override
public boolean isInCombat(UUID player) {
if (!this.fights.containsKey(player)) {
return false;
}

FightTag fightTag = this.fights.get(player);

return !fightTag.isExpired();
return fightTag != null && !fightTag.isExpired();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ void tagMultiple(@Context CommandSender sender, @Arg Player firstTarget, @Arg Pl
return;
}

boolean firstAlreadyInCombat = this.fightManager.isInCombat(firstTarget.getUniqueId());

FightTagEvent firstTagEvent = this.fightManager.tag(firstTarget.getUniqueId(), combatTime, CauseOfTag.COMMAND);
FightTagEvent secondTagEvent = this.fightManager.tag(secondTarget.getUniqueId(), combatTime, CauseOfTag.COMMAND);

if (firstTagEvent.isCancelled()) {
CancelTagReason cancelReason = firstTagEvent.getCancelReason();
Expand All @@ -100,15 +101,19 @@ void tagMultiple(@Context CommandSender sender, @Arg Player firstTarget, @Arg Pl
return;
}

FightTagEvent secondTagEvent = this.fightManager.tag(secondTarget.getUniqueId(), combatTime, CauseOfTag.COMMAND);

if (secondTagEvent.isCancelled()) {
CancelTagReason cancelReason = secondTagEvent.getCancelReason();

this.tagoutReasonHandler(sender, cancelReason, messagesSettings);
// Only roll back the first tag when this command actually created it;
// never untag a player who was already in combat beforehand.
if (!firstAlreadyInCombat) {
this.fightManager.untag(firstTarget.getUniqueId(), CauseOfUnTag.COMMAND);
}

return;
}
this.tagoutReasonHandler(sender, cancelReason, messagesSettings);

if (firstTagEvent.isCancelled() && secondTagEvent.isCancelled()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void run() {

if (fightTag.isExpired()) {
this.fightManager.untag(playerUniqueId, CauseOfUnTag.TIME_EXPIRED);
return;
continue;
}

Duration remaining = fightTag.getRemainingDuration();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
return;
}

String command = event.getMessage().substring(1);
String original = event.getMessage().substring(1).stripLeading();
String command = normalizeCommand(original);

boolean isAnyMatch = this.config.commands.restrictedCommands.stream()
.anyMatch(restrictedCommand -> StringUtil.startsWithIgnoreCase(command, restrictedCommand));
.anyMatch(restrictedCommand -> StringUtil.startsWithIgnoreCase(original, restrictedCommand)
|| StringUtil.startsWithIgnoreCase(command, restrictedCommand));

WhitelistBlacklistMode mode = this.config.commands.commandRestrictionMode;

Expand All @@ -51,4 +53,21 @@ void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
}
}

// Trim leading spaces and strip any namespace prefix (e.g. "minecraft:tp" -> "tp")
Comment thread
Jakubk15 marked this conversation as resolved.
// from the command label so blacklisted commands cannot be bypassed with those forms.
private static String normalizeCommand(String rawCommand) {
String command = rawCommand.stripLeading();

int spaceIndex = command.indexOf(' ');
String label = spaceIndex == -1 ? command : command.substring(0, spaceIndex);

int colonIndex = label.indexOf(':');
if (colonIndex == -1) {
return command;
}

String rest = spaceIndex == -1 ? "" : command.substring(spaceIndex);
return label.substring(colonIndex + 1) + rest;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ private void spawnFlare(Player player) {
}

private void scheduleParticles(Firework flare, World world) {
this.scheduler.runLaterAsync(
this.scheduler.runLater(

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.

I feel like this needs testing - wrote it that way so it does not lag - dont know what happens in this scenario

() -> {
if (flare.isDead() || !flare.isValid()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.commons.adventure.AdventureUtil;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
Expand All @@ -16,6 +17,7 @@
import org.bukkit.inventory.meta.SkullMeta;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

Expand Down Expand Up @@ -85,8 +87,8 @@
return false;
}

if (this.dropSettings.headDropOnlyInCombat && inCombat) {
return true;
if (this.dropSettings.headDropOnlyInCombat && !inCombat) {
return false;
}

if (this.dropSettings.headDropChance <= 0.0) {
Expand All @@ -106,7 +108,7 @@
.replace("{KILLER}", killerName);

meta.setOwningPlayer(player);
meta.setDisplayName(AdventureUtil.SECTION_SERIALIZER.serialize(miniMessage.deserialize(displayName)));

Check warning on line 111 in eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/drop/DropController.java

View workflow job for this annotation

GitHub Actions / build

[deprecation] setDisplayName(String) in ItemMeta has been deprecated

if (!this.dropSettings.headDropLore.isEmpty()) {
List<String> lore = this.dropSettings.headDropLore.stream()
Expand All @@ -116,7 +118,7 @@
.map(line -> AdventureUtil.SECTION_SERIALIZER.serialize(miniMessage.deserialize(line)))
.toList();

meta.setLore(lore);

Check warning on line 121 in eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/drop/DropController.java

View workflow job for this annotation

GitHub Actions / build

[deprecation] setLore(List<String>) in ItemMeta has been deprecated
}

meta.addEnchant(Enchantment.LURE, 1, true);
Expand All @@ -139,7 +141,13 @@
ItemStack[] itemsToGive = this.keepInventoryManager.nextItems(playerUniqueId)
.toArray(new ItemStack[0]);

playerInventory.addItem(itemsToGive);
// The player is not teleported yet during PlayerRespawnEvent, so
// player.getLocation() would still be the death location.
Location respawnLocation = event.getRespawnLocation();

Map<Integer, ItemStack> leftover = playerInventory.addItem(itemsToGive);
leftover.values().forEach(item ->
respawnLocation.getWorld().dropItemNaturally(respawnLocation, item));
Comment thread
Jakubk15 marked this conversation as resolved.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ void onVehicleMove(VehicleMoveEvent event) {
void onTag(FightTagEvent event) {
Player player = this.server.getPlayer(event.getPlayer());
if (player == null) {
throw new IllegalStateException("Player cannot be null!");
return;
}

Optional<Region> regionOptional = this.regionProvider.getRegion(player.getLocation());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void handleDelay(Player player) {
this.scheduler.runLater(
() -> player.setCooldown(
Material.ENDER_PEARL,
(int) this.pluginConfig.pearl.pearlThrowDelay.toMillis() / 50
(int) (this.pluginConfig.pearl.pearlThrowDelay.toMillis() / 50)
), Duration.ofMillis(50)
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
package com.eternalcode.combat.fight.tagout;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Expiry;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class FightTagOutServiceImpl implements FightTagOutService {

private final Map<UUID, Instant> tagOuts = new HashMap<>();
// Self-expiring cache so entries are evicted at their end time instead of
// accumulating for every player that has ever tagged out.
private final Cache<UUID, Instant> tagOuts = Caffeine.newBuilder()
Comment thread
Jakubk15 marked this conversation as resolved.
.expireAfter(new Expiry<UUID, Instant>() {
@Override
public long expireAfterCreate(UUID key, Instant endTime, long currentTime) {
return Math.max(0, Duration.between(Instant.now(), endTime).toNanos());
}

@Override
public long expireAfterUpdate(UUID key, Instant endTime, long currentTime, long currentDuration) {
return this.expireAfterCreate(key, endTime, currentTime);
}

@Override
public long expireAfterRead(UUID key, Instant endTime, long currentTime, long currentDuration) {
return currentDuration;
}
})
.build();

@Override
public void tagOut(UUID player, Duration duration) {
Expand All @@ -19,12 +39,12 @@ public void tagOut(UUID player, Duration duration) {

@Override
public void unTagOut(UUID player) {
this.tagOuts.remove(player);
this.tagOuts.invalidate(player);
}

@Override
public boolean isTaggedOut(UUID player) {
Instant endTime = this.tagOuts.get(player);
Instant endTime = this.tagOuts.getIfPresent(player);

if (endTime == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,15 @@ public void onRiptide(PlayerRiptideEvent event) {

@EventHandler(ignoreCancelled = true)
public void onUntag(FightUntagEvent event) {
Player player = server.getPlayer(event.getPlayer());
UUID playerId = event.getPlayer();
this.tridentService.removeDelay(playerId);

Player player = server.getPlayer(playerId);
if (player == null) {
return;
}

player.setCooldown(Material.TRIDENT, 0);
this.tridentService.removeDelay(player.getUniqueId());
}

private boolean isRiptideInteract(PlayerInteractEvent event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,46 @@ public static RemoveItemResult removeRandomItems(List<ItemStack> list, int items
List<ItemStack> currentItems = new ArrayList<>(list);
List<ItemStack> removedItems = new ArrayList<>();

int currentItemsToDelete = itemsToDelete;
while (currentItemsToDelete > 0) {
int randomIndex = RANDOM.nextInt(list.size());
int totalAvailable = 0;
for (ItemStack item : currentItems) {
if (item != null) {
totalAvailable += Math.max(0, item.getAmount());
}
}

// Never try to remove more than is actually available, otherwise the loop
// could never reach zero and would spin forever.
int currentItemsToDelete = Math.min(itemsToDelete, totalAvailable);

while (currentItemsToDelete > 0 && !currentItems.isEmpty()) {
int randomIndex = RANDOM.nextInt(currentItems.size());
ItemStack currentItem = currentItems.get(randomIndex);

int amount = currentItem.getAmount();
int randomAmount = RANDOM.nextInt(0, Math.min(currentItemsToDelete, amount) + 1);
// Bukkit inventory lists can contain null entries for empty slots.
if (currentItem == null) {
currentItems.remove(randomIndex);
continue;
}

if (amount <= 0 || randomAmount <= 0) {
int amount = currentItem.getAmount();
Comment thread
Jakubk15 marked this conversation as resolved.
if (amount <= 0) {
currentItems.remove(randomIndex);
continue;
}

int maxRemovable = Math.min(currentItemsToDelete, amount);
int randomAmount = RANDOM.nextInt(1, maxRemovable + 1);

ItemStack removedItem = currentItem.clone();
removedItem.setAmount(randomAmount);
removedItems.add(removedItem);

currentItem.setAmount(amount - randomAmount);
int remaining = amount - randomAmount;
if (remaining <= 0) {
currentItems.remove(randomIndex);
} else {
currentItem.setAmount(remaining);
}

currentItemsToDelete -= randomAmount;
}
Expand Down
Loading