Skip to content

Commit 740a811

Browse files
committed
Infite momentum glitch fix, and many tweaks.
- Fix infinite momentum bug due to minecraft's minecart movement code being a hot mess. See GitHub issue #6 - Default hard brake block changed from obsidian to sand soul. This souldn't affect existing configurations. - Speed multiplier range now starts from 1 instead of 0. - Hard brake fallback value now 8 instead of 1 when specifying invalid values in config. - Build target changed from Java 8 to Java 11
1 parent 84dfed4 commit 740a811

File tree

10 files changed

+189
-36
lines changed

10 files changed

+189
-36
lines changed

.idea/misc.xml

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# hsrails
22
### High Speed Rails
33

4-
A spigot/bukkit plugin to make minecarts worth building again.
4+
A spigot/bukkit plugin to make minecarts worth building.
55

6-
For Minecraft version **1.14** and tested working on **1.15, 1.16.3, 1.19**. Will most likely work just fine on **newer versions as well**.
6+
For Minecraft version **1.21.1** and tested working on **1.14, 1.15, 1.16.3, 1.19**. Will most likely work just fine on **newer versions as well**.
77

88
Place a powered rail on a _boost block_ (`redstone block` by default) to build high-speed rail. Place on any other block to get a regular powered rail.
99

@@ -14,7 +14,7 @@ High-speed rails are by default 4x faster than regular powered rails, ie. 32 m/s
1414
The high-speed rail multiplier can be temporarily changed with the `/hsrails` command, or permanently changed in the config.
1515
The boost block is also configurable. See _Usage_ section for commands and config options.
1616

17-
To help cope with the higher speeds, there is also a _hard brake block_ available (`obsidian` by default). If you place an
17+
To help cope with the higher speeds, there is also a _hard brake block_ available (`soul sand` by default). If you place an
1818
_unpowered_ power rail on a hard brake block, the cart will decelerate faster than default. See _Usage_ section
1919
for configuration options.
2020

@@ -29,7 +29,7 @@ That means the carts will coast for longer, even though they appear to have the
2929
#### Commands
3030

3131
Use `/hsrails <multiplier>` to tweak how fast high-speed rails are.
32-
Multiplier must be between 0 and 8.
32+
Multiplier must be between 1 and 8.
3333

3434
Example: set multiplier to 4
3535
```
@@ -44,13 +44,13 @@ This is the default `HsRails/config.yml`:
4444
speedMultiplier: 4.0
4545
boostBlock: "minecraft:redstone_block"
4646
hardBrakeMultiplier: 8.0
47-
hardBrakeBlock: "minecraft:obsidian"
47+
hardBrakeBlock: "minecraft:soul_sand"
4848
```
4949

5050
Allowed values are:
5151

5252
- speedMultiplier:
53-
- `> 0`
53+
- `>= 1`
5454
- `<= 8`
5555
- boostBlock:
5656
- Namespaced block. Look up the ID name in [the id list](https://www.minecraftinfo.com/idnamelist.htm)
@@ -81,6 +81,7 @@ These are my recommendations for building efficient high-speed tracks:
8181
- Minimize number of slopes: build tunnels, bridges, etc. to stay on the same level.
8282
- Before turns and slopes, put one (or sometimes a couple of) regular powered rails to slow down and avoid derailment.
8383
- After turns and slopes, allow room for acceleration again.
84+
- To maintain top speed in high speed state, place a high speed rail every 30 blocks (ie. 1 powered rail + 29 iron rails; rinse and repeat)
8485

8586
To maintain high speeds you must of course build your tracks out of high-speed rails, because regular powered rails will slow you down. Only mix in regular powered rails in turns and slopes as mentioned above.
8687

pom.xml

+3-5
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@
66

77
<groupId>no.netb.mc.hsrails</groupId>
88
<artifactId>hsrails</artifactId>
9-
<version>1.3.1</version>
9+
<version>1.4.0</version>
1010

1111
<properties>
12-
<java.version>1.8</java.version>
13-
<maven.compiler.target>1.8</maven.compiler.target>
14-
<maven.compiler.source>1.8</maven.compiler.source>
12+
<maven.compiler.release>11</maven.compiler.release>
1513
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1614
</properties>
1715

@@ -26,7 +24,7 @@
2624
<dependency>
2725
<groupId>org.spigotmc</groupId>
2826
<artifactId>spigot-api</artifactId>
29-
<version>1.20.2-R0.1-SNAPSHOT</version>
27+
<version>1.21.1-R0.1-SNAPSHOT</version>
3028
<scope>provided</scope>
3129
</dependency>
3230
</dependencies>

src/main/java/no/netb/mc/hsrails/Configuration.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ private void readHardBrakeBlock(FileConfiguration fileConfig, Logger logger) {
8484

8585
private void readSpeedMultiplier(FileConfiguration fileConfig, Logger logger) {
8686
double speedMultiplier = fileConfig.getDouble("speedMultiplier");
87-
if (speedMultiplier <= 0) {
88-
logger.warning("Warning: speed multiplier set to 0 or below in config. Using value of 0.1 as fallback.");
89-
speedMultiplier = 0.1;
87+
if (speedMultiplier < 1d) {
88+
logger.warning("Warning: speed multiplier set below 1 in config. Using value of 1 as fallback.");
89+
speedMultiplier = 1d;
9090
} else if (speedMultiplier > 8) {
9191
logger.warning("Warning: speed multiplier set above 8 in config. Using value of 8 as fallback.");
9292
speedMultiplier = 8d;
@@ -104,8 +104,8 @@ private void readSpeedMultiplier(FileConfiguration fileConfig, Logger logger) {
104104
private void readHardBrakeMultiplier(FileConfiguration fileConfig, Logger logger) {
105105
double hardBrakeMultiplier = fileConfig.getDouble("hardBrakeMultiplier");
106106
if (hardBrakeMultiplier < 1.0) {
107-
logger.warning("Warning: brake multiplier not set or set to below 1 in config. Using value of 1 as fallback.");
108-
hardBrakeMultiplier = 1.0;
107+
logger.warning("Warning: brake multiplier not set or set to below 1 in config. Using value of 8 as fallback.");
108+
hardBrakeMultiplier = 8.0;
109109
}
110110
else {
111111
logger.info("Setting brake multiplier to " + hardBrakeMultiplier);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package no.netb.mc.hsrails;
2+
3+
import org.bukkit.entity.Player;
4+
5+
public class DebugSubscription {
6+
private final Player player;
7+
private final boolean acceptsVerbose;
8+
9+
public DebugSubscription(Player player, boolean acceptsVerbose) {
10+
this.player = player;
11+
this.acceptsVerbose = acceptsVerbose;
12+
}
13+
14+
public Player getPlayer() {
15+
return player;
16+
}
17+
18+
public boolean getAcceptsVerbose() {
19+
return acceptsVerbose;
20+
}
21+
}

src/main/java/no/netb/mc/hsrails/HsRails.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
import org.bukkit.plugin.PluginManager;
88
import org.bukkit.plugin.java.JavaPlugin;
99

10+
import java.util.HashMap;
1011
import java.util.HashSet;
12+
import java.util.Map;
13+
import java.util.Optional;
1114
import java.util.Set;
1215
import java.util.logging.Logger;
1316

@@ -19,7 +22,10 @@ public static Configuration getConfiguration() {
1922
return CONFIGURATION;
2023
}
2124

22-
private static Set<CommandSender> receivedHeadsUp = new HashSet<>();
25+
private static final Set<CommandSender> receivedHeadsUp = new HashSet<>();
26+
27+
public static Map<Player, DebugSubscription> debuggers = new HashMap<>();
28+
public static double coastFactor = 30d;
2329

2430
@Override
2531
public void onEnable() {
@@ -36,6 +42,7 @@ public void onEnable() {
3642
CONFIGURATION.getHardBrakeBlock(),
3743
CONFIGURATION.isCheatMode()
3844
), this);
45+
pm.registerEvents(new PlayerDisconnectListener(logger), this);
3946
}
4047

4148
@Override
@@ -53,6 +60,23 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
5360
player.sendMessage(ChatColor.RED + "You don't have permission to use this command");
5461
return true;
5562
}
63+
64+
if (args.length > 0 && (args[0].equalsIgnoreCase("d") || args[0].equalsIgnoreCase("dd"))) {
65+
Boolean currentMode = Optional.ofNullable(debuggers.get(player))
66+
.map(DebugSubscription::getAcceptsVerbose)
67+
.orElse(null);
68+
69+
boolean verbose = args[0].equalsIgnoreCase("dd");
70+
if (currentMode == null || currentMode != verbose) {
71+
debuggers.put(player, new DebugSubscription(player, verbose));
72+
player.sendMessage("debug mode: " + (verbose ? "ON [VERBOSE]" : "ON [NORMAL]"));
73+
return true;
74+
}
75+
76+
debuggers.remove(player);
77+
player.sendMessage("debug mode: OFF");
78+
return true;
79+
}
5680
}
5781

5882
try {
@@ -64,7 +88,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
6488
}
6589

6690
double speedMultiplier = CONFIGURATION.getSpeedMultiplier();
67-
if (speedMultiplier > 0 && speedMultiplier <= 8) {
91+
if (speedMultiplier >= 1d && speedMultiplier <= 8d) {
6892
String message = ChatColor.AQUA + "Speed multiplier set to: " + speedMultiplier;
6993
String headsUp =
7094
ChatColor.YELLOW + "\nNote: multiplier set to more than 4x. Servers often struggle to provide max speeds above 4x,"
@@ -79,7 +103,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St
79103
return true;
80104
}
81105

82-
sender.sendMessage(ChatColor.RED + "multiplier must be greater than 0 and max 8");
106+
sender.sendMessage(ChatColor.RED + "multiplier must be at least 1 and at most 8");
83107
return true;
84108
}
85109

src/main/java/no/netb/mc/hsrails/MinecartListener.java

+97-12
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,39 @@
99
import org.bukkit.event.EventHandler;
1010
import org.bukkit.event.EventPriority;
1111
import org.bukkit.event.Listener;
12+
import org.bukkit.event.vehicle.VehicleDestroyEvent;
1213
import org.bukkit.event.vehicle.VehicleMoveEvent;
1314
import org.bukkit.util.Vector;
1415

15-
import java.util.HashSet;
16-
import java.util.Set;
17-
16+
import java.util.HashMap;
17+
import java.util.Map;
18+
import java.util.Objects;
1819

1920
public class MinecartListener implements Listener {
2021

22+
static class MinecartState {
23+
int blocksCoasted = 0;
24+
25+
@Override
26+
public boolean equals(Object obj) {
27+
if (obj instanceof MinecartState) {
28+
MinecartState s = (MinecartState) obj;
29+
return s.blocksCoasted == this.blocksCoasted;
30+
}
31+
return super.equals(obj);
32+
}
33+
34+
@Override
35+
public int hashCode() {
36+
return Objects.hash(blocksCoasted);
37+
}
38+
}
39+
2140
/**
2241
* Default speed, in meters per tick. A tick is 0.05 seconds, thus 0.4 * 1/0.05 = 8 m/s
2342
*/
2443
private static final double DEFAULT_SPEED_METERS_PER_TICK = 0.4d;
25-
private final Set<Integer> minecartBoosted = new HashSet<>();
44+
private final Map<Integer, MinecartState> boostedMinecarts = new HashMap<>();
2645
private final Material boostBlock;
2746
private final Material hardBrakeBlock;
2847
private final boolean isCheatMode;
@@ -35,28 +54,46 @@ public MinecartListener(Material boostBlock, Material hardBrakeBlock, boolean is
3554

3655
@EventHandler(priority = EventPriority.NORMAL)
3756
public void onVehicleMove(VehicleMoveEvent event) {
38-
3957
if (event.getVehicle() instanceof Minecart) {
4058
Minecart cart = (Minecart) event.getVehicle();
41-
Location cartLocation = cart.getLocation();
42-
World cartsWorld = cart.getWorld();
59+
final boolean isEnteredNewBlock = !event.getTo().getBlock().equals(event.getFrom().getBlock());
60+
if (!HsRails.debuggers.isEmpty() && isEnteredNewBlock) {
61+
Vector v = cart.getVelocity();
62+
log(true, "velocity: [%f %f] |%f|", v.getX(), v.getZ(), v.length());
63+
}
64+
65+
final Integer entityId = event.getVehicle().getEntityId();
66+
final Location cartLocation = cart.getLocation();
67+
final World cartsWorld = cart.getWorld();
4368

44-
Block rail = cartsWorld.getBlockAt(cartLocation);
69+
final Block rail = cartsWorld.getBlockAt(cartLocation);
70+
final double speedMultiplier = HsRails.getConfiguration().getSpeedMultiplier();
71+
final double boostedMaxSpeed = DEFAULT_SPEED_METERS_PER_TICK * speedMultiplier;
4572

4673
if (rail.getType() == Material.POWERED_RAIL) {
4774
Block blockBelow = cartsWorld.getBlockAt(cartLocation.add(0, -1, 0));
4875

4976
if (isCheatMode || blockBelow.getType() == boostBlock) {
50-
if (!minecartBoosted.contains(event.getVehicle().getEntityId())) {
51-
minecartBoosted.add(event.getVehicle().getEntityId());
52-
cart.setMaxSpeed(DEFAULT_SPEED_METERS_PER_TICK * HsRails.getConfiguration().getSpeedMultiplier());
77+
if (!boostedMinecarts.containsKey(entityId)) { // if cart is not in high speed state then make it high speed
78+
boostedMinecarts.put(entityId, new MinecartState());
79+
cart.setMaxSpeed(boostedMaxSpeed);
80+
log(false, "minecart [%d] added", entityId);
81+
} else { // the cart is already in high speed state; refresh the values since we are on a boost block.
82+
boostedMinecarts.get(entityId).blocksCoasted = 0;
83+
if (cart.getMaxSpeed() != boostedMaxSpeed) {
84+
cart.setMaxSpeed(boostedMaxSpeed);
85+
log(false, "minecart [%d] max speed refreshed", entityId);
86+
}
5387
}
5488
} else {
89+
// carts should NOT be in high speed state when passing over regular power rails; clear it.
5590
if (cart.getMaxSpeed() != DEFAULT_SPEED_METERS_PER_TICK) {
56-
minecartBoosted.remove(event.getVehicle().getEntityId());
91+
boostedMinecarts.remove(entityId);
5792
cart.setMaxSpeed(DEFAULT_SPEED_METERS_PER_TICK);
93+
log(false, "minecart [%d] evicted", entityId);
5894
}
5995
}
96+
6097
RedstoneRail railBlockData = (RedstoneRail) rail.getBlockData();
6198
if (!railBlockData.isPowered()
6299
&& blockBelow.getType() == hardBrakeBlock) {
@@ -65,6 +102,54 @@ public void onVehicleMove(VehicleMoveEvent event) {
65102
cart.setVelocity(cartVelocity);
66103
}
67104
}
105+
// This handles the infinite momentum bug as reported by GitHub issue #6.
106+
else if (isEnteredNewBlock) {
107+
MinecartState state = boostedMinecarts.get(entityId);
108+
switch (rail.getType()) {
109+
case RAIL:
110+
case ACTIVATOR_RAIL:
111+
case DETECTOR_RAIL:
112+
if (state != null) { // state != null means the cart is in the high speed state.
113+
if (cart.getVelocity().length() < DEFAULT_SPEED_METERS_PER_TICK) {
114+
cart.setMaxSpeed(DEFAULT_SPEED_METERS_PER_TICK);
115+
boostedMinecarts.remove(entityId);
116+
log(false, "momentum: too slow, clearing boost state");
117+
break;
118+
}
119+
state.blocksCoasted++;
120+
if (state.blocksCoasted > HsRails.coastFactor) {
121+
double factor = Math.max(1d, speedMultiplier - ((state.blocksCoasted - HsRails.coastFactor) / HsRails.coastFactor));
122+
cart.setMaxSpeed(DEFAULT_SPEED_METERS_PER_TICK * factor);
123+
if (factor == 1) {
124+
boostedMinecarts.remove(entityId);
125+
log(false, "momentum: factor reached 1, clearing boost state");
126+
break;
127+
}
128+
log(false, "momentum: adjusting speed factor: %f", factor);
129+
}
130+
}
131+
break;
132+
}
133+
}
134+
}
135+
}
136+
137+
@EventHandler(priority = EventPriority.NORMAL)
138+
public void onVehicleDestroyed(VehicleDestroyEvent event) {
139+
Integer vehicleId = event.getVehicle().getEntityId();
140+
boolean wasPresent = boostedMinecarts.remove(vehicleId) != null;
141+
if (wasPresent) {
142+
log(false, "minecart [%d] evicted", vehicleId);
143+
} else {
144+
log(false, "minecart [%d] was already cleared", vehicleId);
145+
}
146+
}
147+
148+
private void log(boolean verbose, String template, Object... args) {
149+
for (DebugSubscription subscriber : HsRails.debuggers.values()) {
150+
if (!verbose || subscriber.getAcceptsVerbose()) {
151+
subscriber.getPlayer().sendMessage(String.format(template, args));
152+
}
68153
}
69154
}
70155
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package no.netb.mc.hsrails;
2+
3+
import org.bukkit.event.EventHandler;
4+
import org.bukkit.event.EventPriority;
5+
import org.bukkit.event.Listener;
6+
import org.bukkit.event.player.PlayerQuitEvent;
7+
8+
import java.util.logging.Logger;
9+
10+
public class PlayerDisconnectListener implements Listener {
11+
12+
private final Logger logger;
13+
14+
public PlayerDisconnectListener(Logger logger) {
15+
this.logger = logger;
16+
}
17+
18+
@EventHandler(priority = EventPriority.NORMAL)
19+
public void onPlayerDisconnect(PlayerQuitEvent event) {
20+
boolean cleared = HsRails.debuggers.remove(event.getPlayer()) != null;
21+
if (cleared) {
22+
logger.info(String.format("removed %s from debuggers list", event.getPlayer().getDisplayName()));
23+
}
24+
}
25+
}

src/main/resources/config.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
speedMultiplier: 4.0
22
boostBlock: "minecraft:redstone_block"
33
hardBrakeMultiplier: 8.0
4-
hardBrakeBlock: "minecraft:obsidian"
4+
hardBrakeBlock: "minecraft:soul_sand"

0 commit comments

Comments
 (0)