Skip to content

Commit ba291c6

Browse files
authored
Feat: rework plugin to use attributes instead of potion effects (#2)
* feat: basic structure with base modules code * fix: correct some of the modules * feat: add move_speed from attributes, make code more robust, update config.yml
1 parent e759d53 commit ba291c6

6 files changed

Lines changed: 382 additions & 107 deletions

File tree

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>gemesil</groupId>
88
<artifactId>FasterPathways</artifactId>
9-
<version>1.3</version>
9+
<version>1.4</version>
1010
<packaging>jar</packaging>
1111

1212
<name>FasterPathways</name>
@@ -70,7 +70,7 @@
7070
<dependency>
7171
<groupId>org.spigotmc</groupId>
7272
<artifactId>spigot-api</artifactId>
73-
<version>1.21.4-R0.1-SNAPSHOT</version>
73+
<version>1.21.8-R0.1-SNAPSHOT</version>
7474
<scope>provided</scope>
7575
</dependency>
7676
</dependencies>
Lines changed: 33 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,51 @@
11
package gemesil.fasterpathways;
22

3-
import net.md_5.bungee.api.ChatMessageType;
4-
import net.md_5.bungee.api.chat.TextComponent;
3+
import gemesil.fasterpathways.core.BoostService;
4+
import gemesil.fasterpathways.core.ConfigManager;
5+
import gemesil.fasterpathways.core.MovementListener;
56
import org.bukkit.Bukkit;
6-
import org.bukkit.ChatColor;
7-
import org.bukkit.Material;
8-
import org.bukkit.World;
9-
import org.bukkit.block.Block;
10-
import org.bukkit.block.BlockFace;
11-
import org.bukkit.configuration.file.FileConfiguration;
7+
import org.bukkit.attribute.Attribute;
128
import org.bukkit.entity.Player;
13-
import org.bukkit.event.EventHandler;
14-
import org.bukkit.event.Listener;
15-
import org.bukkit.event.player.PlayerMoveEvent;
169
import org.bukkit.plugin.java.JavaPlugin;
17-
import org.bukkit.potion.PotionEffect;
18-
import org.bukkit.potion.PotionEffectType;
1910

20-
import java.util.HashSet;
21-
import java.util.Set;
22-
import java.util.logging.Level;
11+
/**
12+
* Entry point for the FasterPathways plugin.
13+
* Keeps lifecycle minimal and delegates to the core package.
14+
*/
15+
public final class FasterPathways extends JavaPlugin {
2316

24-
public final class FasterPathways extends JavaPlugin implements Listener {
25-
26-
private PotionEffect speedEffect;
27-
private Set<Material> speedBlocks;
28-
private Set<String> disabledWorlds;
29-
private String actionBarMessage;
30-
private boolean showActionBar;
17+
private ConfigManager configManager;
18+
private BoostService boostService;
3119

3220
@Override
3321
public void onEnable() {
3422
saveDefaultConfig();
35-
loadConfiguration();
36-
getServer().getPluginManager().registerEvents(this, this);
37-
getLogger().info("FasterPathways | Plugin has been enabled!");
38-
}
39-
40-
private void loadConfiguration() {
41-
reloadConfig();
42-
FileConfiguration config = getConfig();
43-
44-
// Load speed effect settings
45-
int speedLevel = config.getInt("speed.level", 1);
46-
int speedDuration = config.getInt("speed.duration", 2);
47-
speedEffect = new PotionEffect(PotionEffectType.SPEED, speedDuration * 20, speedLevel - 1);
48-
49-
// Load action bar settings
50-
actionBarMessage = ChatColor.translateAlternateColorCodes('&',
51-
config.getString("messages.actionBar", "&eYou're moving faster!"));
52-
showActionBar = config.getBoolean("messages.showActionBar", true);
23+
reloadAll();
5324

54-
// Load disabled worlds
55-
disabledWorlds = new HashSet<>(config.getStringList("disabledWorlds"));
56-
for (String worldName : disabledWorlds) {
57-
if (Bukkit.getWorld(worldName) == null) {
58-
getLogger().warning("FasterPathways | World not found: " + worldName);
59-
}
60-
}
25+
// Register movement listener
26+
Bukkit.getPluginManager().registerEvents(
27+
new MovementListener(configManager, boostService),
28+
this
29+
);
30+
}
6131

62-
// Load speed blocks
63-
speedBlocks = new HashSet<>();
64-
for (String blockName : config.getStringList("speedBlocks")) {
65-
try {
66-
Material material = Material.valueOf(blockName.toUpperCase());
67-
speedBlocks.add(material);
68-
} catch (IllegalArgumentException e) {
69-
getLogger().log(Level.WARNING, "FasterPathways | Invalid block material in config: " + blockName);
32+
@Override
33+
public void onDisable() {
34+
// Ensure no one remains boosted after disable/reload
35+
for (Player onlinePlayer : getServer().getOnlinePlayers()) {
36+
if (onlinePlayer.getAttribute(Attribute.MOVEMENT_SPEED) != null) {
37+
boostService.removeSpeedMultiplier(onlinePlayer);
7038
}
7139
}
72-
73-
if (speedBlocks.isEmpty()) {
74-
getLogger().warning("FasterPathways | No valid speed blocks configured! Adding DIRT_PATH as default.");
75-
speedBlocks.add(Material.DIRT_PATH);
76-
}
7740
}
7841

79-
@EventHandler
80-
public void onPlayerMove(PlayerMoveEvent event) {
81-
if (event.getTo() != null && event.getTo().getBlock().equals(event.getFrom().getBlock())) {
82-
return;
83-
}
84-
85-
final Player player = event.getPlayer();
86-
87-
// Check if world is disabled
88-
if (disabledWorlds.contains(player.getWorld().getName())) {
89-
return;
90-
}
91-
92-
final Block block = player.getLocation().getBlock();
93-
final Block relativeBlock = block.getRelative(BlockFace.DOWN);
94-
95-
if (speedBlocks.contains(block.getType()) || speedBlocks.contains(relativeBlock.getType())) {
96-
player.addPotionEffect(speedEffect);
97-
98-
if (showActionBar) {
99-
player.spigot().sendMessage(ChatMessageType.ACTION_BAR,
100-
TextComponent.fromLegacyText(actionBarMessage));
101-
}
102-
}
42+
/**
43+
* Reloads configuration and re-initializes services.
44+
* Call this if you later add a /reload command.
45+
*/
46+
public void reloadAll() {
47+
reloadConfig();
48+
this.configManager = new ConfigManager(getConfig(), getLogger(), getServer());
49+
this.boostService = new BoostService();
10350
}
104-
}
51+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package gemesil.fasterpathways.core;
2+
3+
import org.bukkit.attribute.Attribute;
4+
import org.bukkit.attribute.AttributeInstance;
5+
import org.bukkit.attribute.AttributeModifier;
6+
import org.bukkit.entity.Player;
7+
8+
import java.nio.charset.StandardCharsets;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Objects;
12+
import java.util.UUID;
13+
14+
/**
15+
* Manages a player's movement-speed attribute modifier (Spigot 1.21.8).
16+
* Uses a deterministic UUID per player to avoid duplicates and ensure safe replace/remove.
17+
*/
18+
public final class BoostService {
19+
20+
private static final String SPEED_MODIFIER_NAME_PREFIX = "fasterpathways.speed:";
21+
private static final String LEGACY_NAME = "FasterPathways"; // cleanup from older builds
22+
23+
/**
24+
* Apply or update a +X% speed modifier. No-ops if the same value is already applied.
25+
*/
26+
public void applySpeedMultiplier(final Player player, final double multiplier) {
27+
final AttributeInstance attr = player.getAttribute(Attribute.MOVEMENT_SPEED);
28+
if (attr == null) return;
29+
30+
final double amount = Math.max(0.0, multiplier);
31+
final UUID targetId = stableModifierUuid(player);
32+
final String targetName = speedModifierNameFor(player);
33+
34+
// Check if the correct modifier is already present (same UUID + amount).
35+
final AttributeModifier current = findByUuid(attr, targetId);
36+
if (current != null
37+
&& current.getOperation() == AttributeModifier.Operation.ADD_SCALAR
38+
&& Math.abs(current.getAmount() - amount) < 1e-9) {
39+
return; // already correct
40+
}
41+
42+
// Remove any prior copies that match our UUID or known names, then add the new value.
43+
removeSpeedModifiers(attr, targetId, targetName);
44+
45+
// Spigot exposes only the UUID constructor at runtime
46+
final AttributeModifier updated = new AttributeModifier(
47+
targetId,
48+
targetName,
49+
amount,
50+
AttributeModifier.Operation.ADD_SCALAR
51+
);
52+
attr.addModifier(updated);
53+
}
54+
55+
/**
56+
* Remove the speed modifier for this player (if present).
57+
*/
58+
public void removeSpeedMultiplier(final Player player) {
59+
final AttributeInstance attr = player.getAttribute(Attribute.MOVEMENT_SPEED);
60+
if (attr == null) return;
61+
62+
final UUID targetId = stableModifierUuid(player);
63+
final String targetName = speedModifierNameFor(player);
64+
removeSpeedModifiers(attr, targetId, targetName);
65+
}
66+
67+
// ---------- helpers ----------
68+
69+
private static String speedModifierNameFor(final Player player) {
70+
return SPEED_MODIFIER_NAME_PREFIX + player.getUniqueId();
71+
}
72+
73+
private static UUID stableModifierUuid(final Player player) {
74+
final String seed = "fasterpathways:" + player.getUniqueId();
75+
return UUID.nameUUIDFromBytes(seed.getBytes(StandardCharsets.UTF_8));
76+
}
77+
78+
/**
79+
* Find an existing modifier by UUID (uses deprecated getter, isolated here).
80+
*/
81+
private static AttributeModifier findByUuid(final AttributeInstance attr, final UUID uuid) {
82+
for (AttributeModifier m : attr.getModifiers()) {
83+
if (uuidEquals(m, uuid)) {
84+
return m;
85+
}
86+
}
87+
return null;
88+
}
89+
90+
/**
91+
* Remove any modifier that matches our UUID or our known names (current + legacy).
92+
*/
93+
private static void removeSpeedModifiers(final AttributeInstance attr, final UUID uuid, final String name) {
94+
final List<AttributeModifier> toRemove = new ArrayList<>();
95+
for (AttributeModifier m : attr.getModifiers()) {
96+
final boolean sameUuid = uuidEquals(m, uuid);
97+
final boolean sameName = Objects.equals(m.getName(), name) || Objects.equals(m.getName(), LEGACY_NAME);
98+
if (sameUuid || sameName) {
99+
toRemove.add(m);
100+
}
101+
}
102+
for (AttributeModifier m : toRemove) {
103+
attr.removeModifier(m);
104+
}
105+
}
106+
107+
/**
108+
* Compare via deprecated getter; isolated so the rest of the code stays modern.
109+
*/
110+
private static boolean uuidEquals(final AttributeModifier modifier, final UUID expected) {
111+
return modifier.getUniqueId().equals(expected);
112+
}
113+
}

0 commit comments

Comments
 (0)