Skip to content
Merged
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
4 changes: 2 additions & 2 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ Fixes:
Hooks:
---------------
- Vault hook to buy cannons (was there even before fork)
- Movecraft-Cannons support is now integrated (Movecraft Combat hook + Movecraft hook)
- [PlaceholderAPI](./PLACEHOLDERS.md) hook
- [Movecraft](https://github.com/Intybyte/Cannons/wiki/Hooks-&-Integrations#movecraft): Movecraft-Cannons support is now integrated (Movecraft Combat hook + Movecraft hook)
- [PlaceholderAPI](https://github.com/Intybyte/Cannons/wiki/Hooks-&-Integrations#placeholder-api) hook

Optimizations:
---------------
Expand Down
13 changes: 6 additions & 7 deletions cannons-bukkit/src/main/java/at/pavlov/cannons/Cannons.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import at.pavlov.cannons.exchange.ExchangeLoader;
import at.pavlov.cannons.hooks.VaultHook;
import at.pavlov.cannons.hooks.movecraft.MovecraftHook;
import at.pavlov.cannons.hooks.movecraft.type.MaxCannonsProperty;
import at.pavlov.cannons.hooks.movecraft.type.properties.CannonProperties;
import at.pavlov.cannons.hooks.movecraftcombat.MovecraftCombatHook;
import at.pavlov.cannons.hooks.papi.PlaceholderAPIHook;
import at.pavlov.cannons.listener.BlockListener;
Expand All @@ -34,15 +34,12 @@
import at.pavlov.cannons.utils.CannonSelector;
import at.pavlov.cannons.utils.TimeUtils;
import at.pavlov.internal.CLogger;
import at.pavlov.internal.Hook;
import at.pavlov.internal.HookManager;
import at.pavlov.internal.Key;
import at.pavlov.internal.ModrinthUpdateChecker;
import at.pavlov.internal.key.registries.Registries;
import at.pavlov.internal.projectile.definition.KeyedDefaultProjectile;
import lombok.Getter;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedPie;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
Expand All @@ -59,8 +56,6 @@
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Logger;

Expand Down Expand Up @@ -127,7 +122,7 @@ public void onLoad() {
if (config.isMovecraftEnabled()) {
try {
Class.forName("net.countercraft.movecraft.craft.type.property.Property");
MaxCannonsProperty.register();
CannonProperties.register();
} catch (Exception ignored) {
}

Expand Down Expand Up @@ -481,4 +476,8 @@ public boolean isDebugMode() {
public void setDebugMode(boolean debugMode) {
this.debugMode = debugMode;
}

public static NamespacedKey nsKey(String key) {
return new NamespacedKey(getPlugin(), key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import at.pavlov.cannons.Cannons;
import at.pavlov.cannons.hooks.BukkitHook;
import at.pavlov.cannons.hooks.movecraft.listener.CraftDetectListener;
import at.pavlov.cannons.hooks.movecraft.listener.ReleaseListener;
import at.pavlov.cannons.hooks.movecraft.listener.RotationListener;
import at.pavlov.cannons.hooks.movecraft.listener.SinkListener;
import at.pavlov.cannons.hooks.movecraft.listener.TranslationListener;
import at.pavlov.internal.Hook;
import net.countercraft.movecraft.Movecraft;
import org.bukkit.ChatColor;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;

Expand Down Expand Up @@ -42,6 +42,7 @@ public void onEnable() {
pluginManager.registerEvents(new TranslationListener(), plugin);
pluginManager.registerEvents(new RotationListener(), plugin);
pluginManager.registerEvents(new SinkListener(), plugin);
pluginManager.registerEvents(new ReleaseListener(), plugin);
plugin.logInfo(ChatColor.GREEN + enabledMessage());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@

import at.pavlov.cannons.cannon.Cannon;
import at.pavlov.cannons.cannon.CannonManager;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.craft.PilotedCraft;
import net.countercraft.movecraft.craft.SubCraft;
import net.countercraft.movecraft.util.Pair;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import static net.countercraft.movecraft.craft.type.TypeData.NUMERIC_PREFIX;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MovecraftUtils {

public static Set<Cannon> getCannons(Craft craft) {
Expand Down Expand Up @@ -49,4 +56,32 @@ public static UUID getPlayerFromCraft(Craft craft) {
// Return null if all else fails
return null;
}

public static @NotNull Pair<Boolean, ? extends Number> parseLimit(@NotNull Object input) {
if (!(input instanceof String str)) {
return new Pair<>(false, (double) input);
}

if (!str.contains(NUMERIC_PREFIX)) {
return new Pair<>(false, Double.valueOf(str));
}

String[] parts = str.split(NUMERIC_PREFIX);
int val = Integer.parseInt(parts[1]);
return new Pair<>(true, val);
}

public static @NotNull List<@NotNull String> parseKey(Object key) {
if (key instanceof String string) {
return List.of(string);
} else if (key instanceof ArrayList<?> array) {
if (array.get(0) instanceof String) {
return (ArrayList<String>) array;
}

throw new IllegalArgumentException("Invalid parsed key");
} else {
throw new IllegalArgumentException("Invalid parsed key");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,97 +1,111 @@
package at.pavlov.cannons.hooks.movecraft.listener;

import at.pavlov.cannons.Cannons;
import at.pavlov.cannons.Enum.BreakCause;
import at.pavlov.cannons.cannon.Cannon;
import at.pavlov.cannons.cannon.CannonManager;
import at.pavlov.cannons.cannon.CannonDesign;
import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
import at.pavlov.cannons.hooks.movecraft.type.MaxCannonsEntry;
import at.pavlov.cannons.hooks.movecraft.type.CannonCheck;
import at.pavlov.cannons.hooks.movecraft.type.properties.CannonProperties;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.craft.PlayerCraft;
import net.countercraft.movecraft.craft.type.CraftType;
import net.countercraft.movecraft.events.CraftDetectEvent;
import net.countercraft.movecraft.events.CraftSinkEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static at.pavlov.cannons.hooks.movecraft.type.MaxCannonsProperty.MAX_CANNONS;

public class CraftDetectListener implements Listener {
private static final Set<CraftType> notifyError = new HashSet<>();
private static final Cannons cannon = Cannons.getPlugin();
private static final Cannons cannonPlugin = Cannons.getPlugin();

@EventHandler
@EventHandler(ignoreCancelled = true)
public void onCraftDetect(CraftDetectEvent e) {
Craft craft = e.getCraft();
CraftType type = craft.getType();
if (notifyError.contains(type))
return;

if (!(craft instanceof PlayerCraft))
return;

Set<?> maxCannons = getCannonProperty(type);
if (maxCannons.isEmpty())
return; // Return if empty set to improve performance
if (!(craft instanceof PlayerCraft)) return;

// Sum up counts of each cannon design
Set<Cannon> cannons = MovecraftUtils.getCannons(craft);
Map<String, Integer> cannonCount = new HashMap<>();
for (var cannon : cannons) {
String design = cannon.getCannonDesign().getDesignName().toLowerCase();
cannonCount.compute(design, (key, value) -> (value == null) ? 1 : value + 1);

if (cannons.isEmpty()) return;

if (checkMaxMin(e, type, cannons, craft)) return;

if (checkMass(e, cannons, type)) return;

boolean useShip = CannonProperties.USE_SHIP_ANGLES.get(type) == Boolean.TRUE;
if (useShip) {
cannons.forEach(it -> it.setOnShip(true));
}
}

private boolean checkMass(CraftDetectEvent e, Set<Cannon> cannons, CraftType type) {
int cannonsMassCount = 0;
Set<String> exclude = CannonProperties.EXCLUDE_FROM_MASS.get(type);
for (Cannon cannon : cannons) {
CannonDesign design = cannon.getCannonDesign();

if (!exclude.contains(design.getDesignID())) {
cannonsMassCount += design.getMassOfCannon();
}
}

cannonPlugin.logDebug("MassCount " + cannonsMassCount);
Integer maxMass = CannonProperties.MAX_MASS.get(type);
if (maxMass != null && maxMass < cannonsMassCount) {
e.setCancelled(true);
e.setFailMessage(
String.format(
"Detection Failed! Too much cannon mass on board! %d > %d", cannonsMassCount, maxMass
)
);
return true;
}
printCannonCount(cannonCount);

// Check designs against maxCannons
int size = craft.getOrigBlockCount();
for (var entry : maxCannons) {
if (!(entry instanceof MaxCannonsEntry max))
throw new IllegalStateException("maxCannons must be a set of MaxCannonsEntry.");

var cannonName = max.getName();
var count = cannonCount.get(cannonName.toLowerCase());
if (count == null)
continue;

var result = max.detect(count, size);

result.ifPresent( error -> {
e.setCancelled(true);
e.setFailMessage("Detection Failed! You have too many cannons of the following type on this craft: "
+ cannonName + ": " + error);
});

Integer minMass = CannonProperties.MIN_MASS.get(type);
if (minMass != null && minMass > cannonsMassCount) {
e.setCancelled(true);
e.setFailMessage(
String.format(
"Detection Failed! Not enough cannon mass on board! %d < %d", cannonsMassCount, minMass
)
);
return true;
}

return false;
}

private void printCannonCount(Map<String, Integer> cannonCount) {
private boolean checkMaxMin(CraftDetectEvent e, CraftType type, Set<Cannon> cannons, Craft craft) {
Set<? extends CannonCheck> cannonMaxMinChecks = CannonProperties.MAX_CANNONS.get(type);
cannonMaxMinChecks.addAll(CannonProperties.MIN_CANNONS.get(type));

if (cannonMaxMinChecks.isEmpty()) return false;

Map<String, Integer> cannonCount = new HashMap<>();
for (Cannon cannon : cannons) {
String design = cannon.getCannonDesign().getDesignID();
cannonCount.compute(design, (key, value) -> (value == null) ? 1 : value + 1);
}

for (var entry : cannonCount.entrySet()) {
cannon.logDebug("Cannon found: " + entry.getKey() + " | " + entry.getValue());
cannonPlugin.logDebug("Cannon found: " + entry.getKey() + " | " + entry.getValue());
}
}

private Set<MaxCannonsEntry> getCannonProperty(CraftType type) {
try {
Object objectProperty = type.getObjectProperty(MAX_CANNONS);
if (objectProperty instanceof Set<?> property) {
return (Set<MaxCannonsEntry>) property;
} else {
throw new IllegalStateException("maxCannons must be a set.");
}
} catch (Exception exception) {
notifyError.add(type);
cannon.logSevere(
"Failed to get maxCannons property from craft " +
type.getStringProperty(CraftType.NAME) +
" maxCannons won't be applied. - Cause: " +
exception.getMessage()
);
return Set.of();
for (CannonCheck check : cannonMaxMinChecks) {
Optional<String> result = check.check(craft, cannonCount);

if (result.isEmpty()) continue;

String error = result.get();
e.setCancelled(true);
e.setFailMessage("Detection Failed! " + error);
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package at.pavlov.cannons.hooks.movecraft.listener;

import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
import net.countercraft.movecraft.events.CraftReleaseEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;

public class ReleaseListener implements Listener {

@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onRelease(CraftReleaseEvent event) {
MovecraftUtils.getCannons(event.getCraft()).forEach(it -> it.setOnShip(false));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
import at.pavlov.cannons.hooks.movecraft.MovecraftUtils;
import net.countercraft.movecraft.events.CraftSinkEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;

import java.util.Set;

public class SinkListener implements Listener {

@EventHandler
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onCraftSink(CraftSinkEvent event) {
Set<Cannon> cannons = MovecraftUtils.getCannons(event.getCraft());
cannons.forEach(cannon -> CannonManager.getInstance().removeCannon(cannon.getUID(), false, true, BreakCause.Explosion));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package at.pavlov.cannons.hooks.movecraft.type;

import net.countercraft.movecraft.craft.Craft;

import java.util.Map;
import java.util.Optional;

public interface CannonCheck {
Optional<String> check(Craft craft, Map<String, Integer> cannonCountMap);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package at.pavlov.cannons.hooks.movecraft.type;

import at.pavlov.cannons.Cannons;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bukkit.NamespacedKey;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CraftKeys {
public static final NamespacedKey MAX_CANNONS = Cannons.nsKey("max_cannons");
public static final NamespacedKey MIN_CANNONS = Cannons.nsKey("min_cannons");

public static final NamespacedKey MAX_MASS = Cannons.nsKey("max_mass");
public static final NamespacedKey MIN_MASS = Cannons.nsKey("min_mass");
public static final NamespacedKey EXCLUDE_FROM_MASS = Cannons.nsKey("exclude_from_mass");

public static final NamespacedKey USE_SHIP_ANGLES = Cannons.nsKey("use_ship_angles");
}
Loading