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 pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>gemesil</groupId>
<artifactId>FasterPathways</artifactId>
<version>1.3</version>
<version>1.4</version>
<packaging>jar</packaging>

<name>FasterPathways</name>
Expand Down Expand Up @@ -70,7 +70,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21.4-R0.1-SNAPSHOT</version>
<version>1.21.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
119 changes: 33 additions & 86 deletions src/main/java/gemesil/fasterpathways/FasterPathways.java
Original file line number Diff line number Diff line change
@@ -1,104 +1,51 @@
package gemesil.fasterpathways;

import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import gemesil.fasterpathways.core.BoostService;
import gemesil.fasterpathways.core.ConfigManager;
import gemesil.fasterpathways.core.MovementListener;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
/**
* Entry point for the FasterPathways plugin.
* Keeps lifecycle minimal and delegates to the core package.
*/
public final class FasterPathways extends JavaPlugin {

public final class FasterPathways extends JavaPlugin implements Listener {

private PotionEffect speedEffect;
private Set<Material> speedBlocks;
private Set<String> disabledWorlds;
private String actionBarMessage;
private boolean showActionBar;
private ConfigManager configManager;
private BoostService boostService;

@Override
public void onEnable() {
saveDefaultConfig();
loadConfiguration();
getServer().getPluginManager().registerEvents(this, this);
getLogger().info("FasterPathways | Plugin has been enabled!");
}

private void loadConfiguration() {
reloadConfig();
FileConfiguration config = getConfig();

// Load speed effect settings
int speedLevel = config.getInt("speed.level", 1);
int speedDuration = config.getInt("speed.duration", 2);
speedEffect = new PotionEffect(PotionEffectType.SPEED, speedDuration * 20, speedLevel - 1);

// Load action bar settings
actionBarMessage = ChatColor.translateAlternateColorCodes('&',
config.getString("messages.actionBar", "&eYou're moving faster!"));
showActionBar = config.getBoolean("messages.showActionBar", true);
reloadAll();

// Load disabled worlds
disabledWorlds = new HashSet<>(config.getStringList("disabledWorlds"));
for (String worldName : disabledWorlds) {
if (Bukkit.getWorld(worldName) == null) {
getLogger().warning("FasterPathways | World not found: " + worldName);
}
}
// Register movement listener
Bukkit.getPluginManager().registerEvents(
new MovementListener(configManager, boostService),
this
);
}

// Load speed blocks
speedBlocks = new HashSet<>();
for (String blockName : config.getStringList("speedBlocks")) {
try {
Material material = Material.valueOf(blockName.toUpperCase());
speedBlocks.add(material);
} catch (IllegalArgumentException e) {
getLogger().log(Level.WARNING, "FasterPathways | Invalid block material in config: " + blockName);
@Override
public void onDisable() {
// Ensure no one remains boosted after disable/reload
for (Player onlinePlayer : getServer().getOnlinePlayers()) {
if (onlinePlayer.getAttribute(Attribute.MOVEMENT_SPEED) != null) {
boostService.removeSpeedMultiplier(onlinePlayer);
}
}

if (speedBlocks.isEmpty()) {
getLogger().warning("FasterPathways | No valid speed blocks configured! Adding DIRT_PATH as default.");
speedBlocks.add(Material.DIRT_PATH);
}
}

@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (event.getTo() != null && event.getTo().getBlock().equals(event.getFrom().getBlock())) {
return;
}

final Player player = event.getPlayer();

// Check if world is disabled
if (disabledWorlds.contains(player.getWorld().getName())) {
return;
}

final Block block = player.getLocation().getBlock();
final Block relativeBlock = block.getRelative(BlockFace.DOWN);

if (speedBlocks.contains(block.getType()) || speedBlocks.contains(relativeBlock.getType())) {
player.addPotionEffect(speedEffect);

if (showActionBar) {
player.spigot().sendMessage(ChatMessageType.ACTION_BAR,
TextComponent.fromLegacyText(actionBarMessage));
}
}
/**
* Reloads configuration and re-initializes services.
* Call this if you later add a /reload command.
*/
public void reloadAll() {
reloadConfig();
this.configManager = new ConfigManager(getConfig(), getLogger(), getServer());
this.boostService = new BoostService();
}
}
}
113 changes: 113 additions & 0 deletions src/main/java/gemesil/fasterpathways/core/BoostService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package gemesil.fasterpathways.core;

import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.entity.Player;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
* Manages a player's movement-speed attribute modifier (Spigot 1.21.8).
* Uses a deterministic UUID per player to avoid duplicates and ensure safe replace/remove.
*/
public final class BoostService {

private static final String SPEED_MODIFIER_NAME_PREFIX = "fasterpathways.speed:";
private static final String LEGACY_NAME = "FasterPathways"; // cleanup from older builds

/**
* Apply or update a +X% speed modifier. No-ops if the same value is already applied.
*/
public void applySpeedMultiplier(final Player player, final double multiplier) {
final AttributeInstance attr = player.getAttribute(Attribute.MOVEMENT_SPEED);
if (attr == null) return;

final double amount = Math.max(0.0, multiplier);
final UUID targetId = stableModifierUuid(player);
final String targetName = speedModifierNameFor(player);

// Check if the correct modifier is already present (same UUID + amount).
final AttributeModifier current = findByUuid(attr, targetId);
if (current != null
&& current.getOperation() == AttributeModifier.Operation.ADD_SCALAR
&& Math.abs(current.getAmount() - amount) < 1e-9) {
return; // already correct
}

// Remove any prior copies that match our UUID or known names, then add the new value.
removeSpeedModifiers(attr, targetId, targetName);

// Spigot exposes only the UUID constructor at runtime
final AttributeModifier updated = new AttributeModifier(
targetId,
targetName,
amount,
AttributeModifier.Operation.ADD_SCALAR
);
attr.addModifier(updated);
}

/**
* Remove the speed modifier for this player (if present).
*/
public void removeSpeedMultiplier(final Player player) {
final AttributeInstance attr = player.getAttribute(Attribute.MOVEMENT_SPEED);
if (attr == null) return;

final UUID targetId = stableModifierUuid(player);
final String targetName = speedModifierNameFor(player);
removeSpeedModifiers(attr, targetId, targetName);
}

// ---------- helpers ----------

private static String speedModifierNameFor(final Player player) {
return SPEED_MODIFIER_NAME_PREFIX + player.getUniqueId();
}

private static UUID stableModifierUuid(final Player player) {
final String seed = "fasterpathways:" + player.getUniqueId();
return UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8));
}

/**
* Find an existing modifier by UUID (uses deprecated getter, isolated here).
*/
private static AttributeModifier findByUuid(final AttributeInstance attr, final UUID uuid) {
for (AttributeModifier m : attr.getModifiers()) {
if (uuidEquals(m, uuid)) {
return m;
}
}
return null;
}

/**
* Remove any modifier that matches our UUID or our known names (current + legacy).
*/
private static void removeSpeedModifiers(final AttributeInstance attr, final UUID uuid, final String name) {
final List<AttributeModifier> toRemove = new ArrayList<>();
for (AttributeModifier m : attr.getModifiers()) {
final boolean sameUuid = uuidEquals(m, uuid);
final boolean sameName = Objects.equals(m.getName(), name) || Objects.equals(m.getName(), LEGACY_NAME);
if (sameUuid || sameName) {
toRemove.add(m);
}
}
for (AttributeModifier m : toRemove) {
attr.removeModifier(m);
}
}

/**
* Compare via deprecated getter; isolated so the rest of the code stays modern.
*/
private static boolean uuidEquals(final AttributeModifier modifier, final UUID expected) {
return modifier.getUniqueId().equals(expected);
}
}
Loading