Remove apache common text dependency

This commit is contained in:
TheMode 2021-05-15 08:31:24 +02:00
parent dfad9f2947
commit 88dba380f9
28 changed files with 221 additions and 66 deletions

View File

@ -129,9 +129,6 @@ dependencies {
api 'io.netty:netty-transport-native-kqueue:4.1.63.Final:osx-x86_64'
api 'io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.5.Final:linux-x86_64'
// https://mvnrepository.com/artifact/org.apache.commons/commons-text
compile group: 'org.apache.commons', name: 'commons-text', version: '1.9'
// https://mvnrepository.com/artifact/it.unimi.dsi/fastutil
api 'it.unimi.dsi:fastutil:8.5.4'

View File

@ -2,7 +2,8 @@ package net.minestom.server.color;
import net.kyori.adventure.util.RGBLike;
import net.minestom.server.chat.ChatColor;
import org.apache.commons.lang3.Validate;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@ -38,17 +39,15 @@ public class Color implements RGBLike {
/**
* Creates a color from red, green, and blue components.
*
* @param red the red component
* @param red the red component
* @param green the green component
* @param blue the blue component
*
* @param blue the blue component
* @throws IllegalArgumentException if any component value is not between 0-255 (inclusive)
*/
public Color(int red, int green, int blue) {
Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red);
Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green);
Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue);
Check.argCondition(!MathUtils.isBetween(red, 0, 255), "Red is not between 0-255: {0}", red);
Check.argCondition(!MathUtils.isBetween(green, 0, 255), "Green is not between 0-255: {0}", green);
Check.argCondition(!MathUtils.isBetween(blue, 0, 255), "Blue is not between 0-255: {0}", blue);
this.red = red;
this.green = green;
this.blue = blue;
@ -69,7 +68,7 @@ public class Color implements RGBLike {
* @param red the red component, from 0 to 255
*/
public void setRed(int red) {
Validate.isTrue(red >= 0 && red <= 255, "Red is not between 0-255: ", red);
Check.argCondition(!MathUtils.isBetween(red, 0, 255), "Red is not between 0-255: {0}", red);
this.red = red;
}
@ -88,7 +87,7 @@ public class Color implements RGBLike {
* @param green the red component, from 0 to 255
*/
public void setGreen(int green) {
Validate.isTrue(green >= 0 && green <= 255, "Green is not between 0-255: ", green);
Check.argCondition(!MathUtils.isBetween(green, 0, 255), "Green is not between 0-255: {0}", green);
this.green = green;
}
@ -107,7 +106,7 @@ public class Color implements RGBLike {
* @param blue the red component, from 0 to 255
*/
public void setBlue(int blue) {
Validate.isTrue(blue >= 0 && blue <= 255, "Blue is not between 0-255: ", blue);
Check.argCondition(!MathUtils.isBetween(blue, 0, 255), "Blue is not between 0-255: {0}", blue);
this.blue = blue;
}
@ -132,8 +131,6 @@ public class Color implements RGBLike {
* @param colors the colors
*/
public void mixWith(@NotNull RGBLike... colors) {
Validate.noNullElements(colors, "Colors cannot be null");
// store the current highest component
int max = Math.max(Math.max(this.red, this.green), this.blue);

View File

@ -7,7 +7,7 @@ import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.*;
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
import net.minestom.server.command.builder.condition.CommandCondition;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;

View File

@ -1,7 +1,7 @@
package net.minestom.server.command.builder;
import net.minestom.server.command.builder.arguments.Argument;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -10,7 +10,7 @@ import net.minestom.server.command.builder.parser.CommandParser;
import net.minestom.server.command.builder.parser.CommandQueryResult;
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -3,7 +3,7 @@ package net.minestom.server.command.builder;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.condition.CommandCondition;
import net.minestom.server.entity.Player;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -7,7 +7,7 @@ import net.minestom.server.command.builder.CommandResult;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
public class ArgumentCommand extends Argument<CommandResult> {

View File

@ -7,7 +7,7 @@ import net.minestom.server.command.builder.suggestion.SuggestionCallback;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.callback.validator.StringArrayValidator;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -6,9 +6,9 @@ import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.callback.validator.StringValidator;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -5,7 +5,7 @@ import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.parser.CommandParser;
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;

View File

@ -3,7 +3,7 @@ package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;

View File

@ -4,8 +4,8 @@ import io.netty.util.internal.StringUtil;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import org.apache.commons.text.StringEscapeUtils;
import org.jetbrains.annotations.NotNull;
/**
@ -75,7 +75,7 @@ public class ArgumentString extends Argument<String> {
}
}
return StringEscapeUtils.unescapeJava(input);
return StringUtils.unescapeJavaString(input);
}
@Override

View File

@ -2,8 +2,8 @@ package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.regex.Pattern;

View File

@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.validate.Check;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -10,7 +10,7 @@ import net.minestom.server.registry.Registries;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.entity.EntityFinder;
import net.minestom.server.utils.math.IntRange;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;

View File

@ -4,7 +4,7 @@ import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
public class ArgumentResourceLocation extends Argument<String> {

View File

@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.location.RelativeBlockPosition;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
/**

View File

@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.Vector;
import net.minestom.server.utils.location.RelativeVec;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
/**

View File

@ -5,7 +5,7 @@ import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.Vector;
import net.minestom.server.utils.location.RelativeVec;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
/**

View File

@ -11,7 +11,7 @@ import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBl
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec2;
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -7,7 +7,7 @@ import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.CommandSyntax;
import net.minestom.server.command.builder.arguments.Argument;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

View File

@ -1,5 +1,6 @@
package net.minestom.server.instance.block.rule.vanilla;
import it.unimi.dsi.fastutil.Pair;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
@ -7,7 +8,6 @@ import net.minestom.server.instance.block.BlockAlternative;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.instance.block.rule.BlockPlacementRule;
import net.minestom.server.utils.BlockPosition;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -113,10 +113,10 @@ public class StairsPlacementRule extends BlockPlacementRule {
@Nullable
private Shape getShapeFromSide(@NotNull Pair<Shape, Facing> side, @NotNull Facing facing, @NotNull Shape right, @NotNull Shape left) {
if (side.getLeft() == null) {
if (side.left() == null) {
return null;
}
Facing sideFacing = side.getRight();
Facing sideFacing = side.right();
if (facing.equals(Facing.NORTH)) {
if (sideFacing.equals(Facing.EAST)) {
return right;

View File

@ -11,7 +11,7 @@ import net.minestom.server.command.builder.suggestion.SuggestionCallback;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.client.play.ClientTabCompletePacket;
import net.minestom.server.network.packet.server.play.TabCompletePacket;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import java.util.Arrays;
@ -48,7 +48,7 @@ public class TabCompleteListener {
final int inputLength = input.length();
final int commandLength = Arrays.stream(split).map(String::length).reduce(0, Integer::sum) +
StringUtils.countMatches(args, StringUtils.SPACE);
StringUtils.countMatches(args, StringUtils.SPACE_CHAR);
final int trailingSpaces = !input.isEmpty() ? text.length() - text.trim().length() : 0;
final int start = commandLength - inputLength + 1 - trailingSpaces;

View File

@ -20,10 +20,10 @@ import net.minestom.server.network.packet.server.play.DisconnectPacket;
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
import net.minestom.server.network.player.NettyPlayerConnection;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.async.AsyncUtils;
import net.minestom.server.utils.callback.validator.PlayerValidator;
import net.minestom.server.utils.validate.Check;
import org.apache.commons.text.similarity.JaroWinklerDistance;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
/**
@ -83,7 +84,6 @@ public final class ConnectionManager {
/**
* Finds the closest player matching a given username.
* <p>
*
* @param username the player username (can be partial)
* @return the closest match, null if no players are online
@ -91,18 +91,17 @@ public final class ConnectionManager {
public @Nullable Player findPlayer(@NotNull String username) {
Player exact = getPlayer(username);
if (exact != null) return exact;
final String username1 = username.toLowerCase(Locale.ROOT);
String lowercase = username.toLowerCase();
double currentDistance = 0;
for (Player player : getOnlinePlayers()) {
final JaroWinklerDistance jaroWinklerDistance = new JaroWinklerDistance();
final double distance = jaroWinklerDistance.apply(lowercase, player.getUsername().toLowerCase());
if (distance > currentDistance) {
currentDistance = distance;
exact = player;
}
}
return exact;
Function<Player, Double> distanceFunction = player -> {
final String username2 = player.getUsername().toLowerCase(Locale.ROOT);
return StringUtils.jaroWinklerScore(username1, username2);
};
return getOnlinePlayers()
.stream()
.min(Comparator.comparingDouble(distanceFunction::apply))
.filter(player -> distanceFunction.apply(player) > 0)
.orElse(null);
}
/**

View File

@ -8,7 +8,7 @@ import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.utils.TickUtils;
import net.minestom.server.utils.binary.BinaryReader;
import net.minestom.server.utils.binary.BinaryWriter;
import org.apache.commons.lang3.Validate;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@ -32,13 +32,13 @@ public class TitlePacket implements ComponentHoldingServerPacket {
/**
* Constructs a new title packet from an action that can take a component argument.
*
* @param action the action
* @param action the action
* @param payload the payload
* @throws IllegalArgumentException if the action is not {@link Action#SET_TITLE},
* {@link Action#SET_SUBTITLE} or {@link Action#SET_ACTION_BAR}
* {@link Action#SET_SUBTITLE} or {@link Action#SET_ACTION_BAR}
*/
public TitlePacket(@NotNull Action action, @NotNull Component payload) {
Validate.isTrue(action == SET_TITLE || action == SET_SUBTITLE || action == SET_ACTION_BAR, "Invalid action type");
Check.argCondition(action != SET_TITLE && action != SET_SUBTITLE && action != SET_ACTION_BAR, "Invalid action type");
this.action = action;
this.payload = payload;
}
@ -48,7 +48,7 @@ public class TitlePacket implements ComponentHoldingServerPacket {
*
* @param action the action
* @throws IllegalArgumentException if the action is not {@link Action#RESET},
* or {@link Action#HIDE}
* or {@link Action#HIDE}
*/
public TitlePacket(@NotNull Action action) {
this.action = action;
@ -57,8 +57,8 @@ public class TitlePacket implements ComponentHoldingServerPacket {
/**
* Constructs a new title packet for {@link Action#SET_TIMES_AND_DISPLAY}.
*
* @param fadeIn the fade in time
* @param stay the stay time
* @param fadeIn the fade in time
* @param stay the stay time
* @param fadeOut the fade out time
*/
public TitlePacket(int fadeIn, int stay, int fadeOut) {

View File

@ -5,7 +5,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
import org.apache.commons.lang3.StringUtils;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -0,0 +1,161 @@
package net.minestom.server.utils;
import org.jetbrains.annotations.NotNull;
public class StringUtils {
public static final String SPACE = " ";
public static final char SPACE_CHAR = ' ';
public static int countMatches(@NotNull final CharSequence str, final char ch) {
if (str.length() == 0) {
return 0;
}
int count = 0;
// We could also call str.toCharArray() for faster look ups but that would generate more garbage.
for (int i = 0; i < str.length(); i++) {
if (ch == str.charAt(i)) {
count++;
}
}
return count;
}
/**
* Applies the Jaro-Winkler distance algorithm to the given strings, providing information about the
* similarity of them.
*
* @param s1 The first string that gets compared. May be <code>null</node> or empty.
* @param s2 The second string that gets compared. May be <code>null</node> or empty.
* @return The Jaro-Winkler score (between 0.0 and 1.0), with a higher value indicating larger similarity.
* @author Thomas Trojer <thomas@trojer.net>
*/
public static double jaroWinklerScore(final String s1, final String s2) {
// lowest score on empty strings
if (s1 == null || s2 == null || s1.isEmpty() || s2.isEmpty()) {
return 0;
}
// highest score on equal strings
if (s1.equals(s2)) {
return 1;
}
// some score on different strings
int prefixMatch = 0; // exact prefix matches
int matches = 0; // matches (including prefix and ones requiring transpostion)
int transpositions = 0; // matching characters that are not aligned but close together
int maxLength = Math.max(s1.length(), s2.length());
int maxMatchDistance = Math.max((int) Math.floor(maxLength / 2.0) - 1, 0); // look-ahead/-behind to limit transposed matches
// comparison
final String shorter = s1.length() < s2.length() ? s1 : s2;
final String longer = s1.length() >= s2.length() ? s1 : s2;
for (int i = 0; i < shorter.length(); i++) {
// check for exact matches
boolean match = shorter.charAt(i) == longer.charAt(i);
if (match) {
if (i < 4) {
// prefix match (of at most 4 characters, as described by the algorithm)
prefixMatch++;
}
matches++;
continue;
}
// check fro transposed matches
for (int j = Math.max(i - maxMatchDistance, 0); j < Math.min(i + maxMatchDistance, longer.length()); j++) {
if (i == j) {
// case already covered
continue;
}
// transposition required to match?
match = shorter.charAt(i) == longer.charAt(j);
if (match) {
transpositions++;
break;
}
}
}
// any matching characters?
if (matches == 0) {
return 0;
}
// modify transpositions (according to the algorithm)
transpositions = (int) (transpositions / 2.0);
// non prefix-boosted score
double score = 0.3334 * (matches / (double) longer.length() + matches / (double) shorter.length() + (matches - transpositions)
/ (double) matches);
if (score < 0.7) {
return score;
}
// we already have a good match, hence we boost the score proportional to the common prefix
return score + prefixMatch * 0.1 * (1.0 - score);
}
public static String unescapeJavaString(String st) {
StringBuilder sb = new StringBuilder(st.length());
for (int i = 0; i < st.length(); i++) {
char ch = st.charAt(i);
if (ch == '\\') {
char nextChar = (i == st.length() - 1) ? '\\' : st
.charAt(i + 1);
// Octal escape?
if (nextChar >= '0' && nextChar <= '7') {
String code = "" + nextChar;
i++;
if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
&& st.charAt(i + 1) <= '7') {
code += st.charAt(i + 1);
i++;
if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
&& st.charAt(i + 1) <= '7') {
code += st.charAt(i + 1);
i++;
}
}
sb.append((char) Integer.parseInt(code, 8));
continue;
}
switch (nextChar) {
case '\\':
ch = '\\';
break;
case 'b':
ch = '\b';
break;
case 'f':
ch = '\f';
break;
case 'n':
ch = '\n';
break;
case 'r':
ch = '\r';
break;
case 't':
ch = '\t';
break;
case '\"':
ch = '\"';
break;
case '\'':
ch = '\'';
break;
// Hex Unicode: u????
case 'u':
if (i >= st.length() - 5) {
ch = 'u';
break;
}
int code = Integer.parseInt(
"" + st.charAt(i + 2) + st.charAt(i + 3)
+ st.charAt(i + 4) + st.charAt(i + 5), 16);
sb.append(Character.toChars(code));
i += 5;
continue;
}
i++;
}
sb.append(ch);
}
return sb.toString();
}
}

View File

@ -1,7 +1,7 @@
package net.minestom.server.utils;
import net.minestom.server.MinecraftServer;
import org.apache.commons.lang3.Validate;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
@ -22,6 +22,7 @@ public class TickUtils {
/**
* Creates a number of ticks from a given duration, based on {@link MinecraftServer#TICK_MS}.
*
* @param duration the duration
* @return the number of ticks
* @throws IllegalArgumentException if duration is negative
@ -32,14 +33,14 @@ public class TickUtils {
/**
* Creates a number of ticks from a given duration.
* @param duration the duration
*
* @param duration the duration
* @param msPerTick the number of milliseconds per tick
* @return the number of ticks
* @throws IllegalArgumentException if duration is negative
*/
public static int fromDuration(@NotNull Duration duration, int msPerTick) {
Validate.isTrue(!duration.isNegative(), "Duration cannot be negative");
Check.argCondition(duration.isNegative(), "Duration cannot be negative");
return (int) (duration.toMillis() / msPerTick);
}
}