Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import ch.njol.skript.Skript;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.KeyedValue;
import org.skriptlang.skript.common.function.DefaultFunction;
import ch.njol.skript.lang.function.Functions;
import ch.njol.skript.lang.function.Parameter;
import ch.njol.skript.lang.function.SimpleJavaFunction;
Expand All @@ -26,6 +25,7 @@
import org.joml.Quaternionf;
import org.joml.Vector3f;
import org.skriptlang.skript.addon.SkriptAddon;
import org.skriptlang.skript.common.function.DefaultFunction;
import org.skriptlang.skript.common.function.Parameter.Modifier;

import java.math.BigDecimal;
Expand Down Expand Up @@ -353,6 +353,83 @@ public Class<?> getReturnType(Expression<?>... arguments) {
"set {_clamped::*} to clamp({_values::*}, 0, 10)")
.since("2.8.0");

Functions.register(DefaultFunction.builder(skript, "toBase", String[].class)
.description("""
Turns a number in a string using a specific base (decimal, hexadecimal, octal).
For example, converting 32 to hexadecimal (base 16) would be 'toBase(32, 16)', which would return "20".
You can use any base between 2 and 36.
""")
.examples(
"send \"Decode this binary number for a prize! %toBase({_guess}, 2)%\""
)
.since("INSERT VERSION")
.parameter("n", Long[].class)
.parameter("base", Long.class, Modifier.ranged(2, 36))
.contract(new Contract() {
@Override
public boolean isSingle(Expression<?>... arguments) {
return arguments[0].isSingle();
}

@Override
public Class<?> getReturnType(Expression<?>... arguments) {
return String.class;
}
})
.build(args -> {
Long[] n = args.get("n");
Long base = args.get("base");
String[] results = new String[n.length];
for (int i = 0; i < n.length; i++) {
results[i] = Long.toString(n[i], base.intValue());
}
return results;
}));

Functions.register(DefaultFunction.builder(skript, "fromBase", Long[].class)
.description("""
Turns a text version of a number in a specific base (decimal, hexadecimal, octal) into an actual number.
For example, converting "20" in hexadecimal (base 16) would be 'fromBase("20", 16)', which would return 32.
You can use any base between 2 and 36.
""")
.examples("""
# /binaryText 01110011 01101011 01110010 01101001 01110000 01110100 00100001
# sends "skript!"
command binaryText <text>:
trigger:
set {_characters::*} to argument split at " " without trailing empty string
transform {_characters::*} with fromBase(input, 2) # convert to codepoints
transform {_characters::*} with character from codepoint input # convert to characters
send join {_characters::*}
""")
.since("INSERT VERSION")
.parameter("string value", String[].class)
.parameter("base", Long.class, Modifier.ranged(2, 36))
.contract(new Contract() {
@Override
public boolean isSingle(Expression<?>... arguments) {
return arguments[0].isSingle();
}

@Override
public Class<?> getReturnType(Expression<?>... arguments) {
return Long.class;
}
})
.build(args -> {
String[] n = args.get("string value");
Long base = args.get("base");
Long[] results = new Long[n.length];
try {
for (int i = 0; i < n.length; i++) {
results[i] = Long.parseLong(n[i], base.intValue());
}
} catch (NumberFormatException e) {
return null;
}
return results;
}));

// misc

Functions.registerFunction(new SimpleJavaFunction<World>("world", new Parameter[] {
Expand Down
52 changes: 40 additions & 12 deletions src/main/java/ch/njol/skript/lang/SkriptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import ch.njol.skript.lang.function.FunctionReference;
import ch.njol.skript.lang.function.FunctionRegistry;
import ch.njol.skript.lang.function.Functions;
import ch.njol.skript.lang.function.Signature;
import ch.njol.skript.lang.parser.DefaultValueData;
import ch.njol.skript.lang.parser.ParseStackOverflowException;
import ch.njol.skript.lang.parser.ParserInstance;
Expand Down Expand Up @@ -49,24 +48,18 @@
import org.skriptlang.skript.lang.converter.Converters;
import org.skriptlang.skript.lang.experiment.ExperimentSet;
import org.skriptlang.skript.lang.experiment.ExperimentalSyntax;
import org.skriptlang.skript.lang.script.Script;
import org.skriptlang.skript.lang.script.ScriptWarning;
import org.skriptlang.skript.log.runtime.RuntimeErrorCatcher;
import org.skriptlang.skript.registration.SyntaxInfo;
import org.skriptlang.skript.registration.SyntaxRegistry;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Deque;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -280,9 +273,12 @@ public boolean hasTag(String tag) {
break;
}
log.printLog();
if (doSimplification && element instanceof Simplifiable<?> simplifiable)
if (doSimplification && element instanceof Simplifiable<?> simplifiable) {
//noinspection unchecked
return (T) simplifiable.simplify();
element = (T) simplify(simplifiable);
if (element == null)
continue;
}
return element;
}
}
Expand All @@ -294,6 +290,38 @@ public boolean hasTag(String tag) {
}
}

/**
* Returns a simplified version of element, unless a runtime error is thrown, in which case a parse error is printed
* and null is returned.
* @param element The element to simplify
* @return The simplified element, or null if simplification failed. Elements unable to simplify will return themselves.
* @param <T> The element type.
*/
private <T extends SyntaxElement> @Nullable T simplify(@NotNull Simplifiable<T> element) {
// add runtime consumer to catch runtime errors and turn them into parse time errors
T simplified;
try (RuntimeErrorCatcher catcher = new RuntimeErrorCatcher().start()) {
simplified = element.simplify();
// we can assume that if a single simplification throws many errors, the first will be at least somewhat representative
AtomicBoolean error = new AtomicBoolean(false);
catcher.getCachedErrors().stream()
.filter(err -> err.level() == Level.SEVERE)
.findFirst()
.ifPresent(err -> {
Skript.error(err.error());
error.set(true);
});
// same for warnings.
catcher.getCachedErrors().stream()
.filter(err -> err.level() == Level.WARNING)
.findFirst()
.ifPresent(warning -> Skript.warning(warning.error()));
if (error.get())
return null;
return simplified;
}
}

/**
* Checks whether the given element is restricted to specific events, and if so, whether the current event is allowed.
* Prints errors.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import ch.njol.skript.lang.*;
import ch.njol.skript.lang.function.FunctionRegistry.Retrieval;
import ch.njol.skript.lang.function.FunctionRegistry.RetrievalResult;
import ch.njol.skript.lang.parser.ParserInstance;
import ch.njol.skript.log.RetainingLogHandler;
import ch.njol.skript.log.SkriptLogger;
import ch.njol.skript.registrations.Classes;
Expand All @@ -16,8 +15,9 @@
import ch.njol.util.StringUtils;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.lang.converter.Converters;
import org.skriptlang.skript.common.function.Parameter.Modifier;
import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier;
import org.skriptlang.skript.lang.converter.Converters;
import org.skriptlang.skript.util.Executable;

import java.util.*;
Expand Down Expand Up @@ -229,17 +229,17 @@ public boolean validateFunction(boolean first) {

// Check parameter types
for (int i = 0; i < parameters.length; i++) {
Parameter<?> p = sign.parameters[singleListParam ? 0 : i];
Parameter<?> signatureParam = sign.parameters[singleListParam ? 0 : i];
RetainingLogHandler log = SkriptLogger.startRetainingLog();
try {
//noinspection unchecked
Expression<?> e = parameters[i].getConvertedExpression(p.type());
if (e == null) {
Expression<?> exprParam = parameters[i].getConvertedExpression(signatureParam.type());
if (exprParam == null) {
if (first) {
if (LiteralUtils.hasUnparsedLiteral(parameters[i])) {
Skript.error("Can't understand this expression: " + parameters[i].toString());
} else {
String type = Classes.toString(getClassInfo(p.type()));
String type = Classes.toString(getClassInfo(signatureParam.type()));

Skript.error("The " + StringUtils.fancyOrderNumber(i + 1) + " argument given to the function '" + stringified + "' is not of the required type " + type + "."
+ " Check the correct order of the arguments and put lists into parentheses if appropriate (e.g. 'give(player, (iron ore and gold ore))')."
Expand All @@ -251,7 +251,7 @@ public boolean validateFunction(boolean first) {
function = previousFunction;
}
return false;
} else if (p.single && !e.isSingle()) {
} else if (signatureParam.single && !exprParam.isSingle()) {
if (first) {
Skript.error("The " + StringUtils.fancyOrderNumber(i + 1) + " argument given to the function '" + functionName + "' is plural, "
+ "but a single argument was expected");
Expand All @@ -262,7 +262,19 @@ public boolean validateFunction(boolean first) {
}
return false;
}
parameters[i] = e;

// check ranged parameters
if (signatureParam.hasModifier(Modifier.RANGED) && exprParam instanceof Literal<?> literalParam) {
RangedModifier<?> range = signatureParam.getModifier(RangedModifier.class);
if (!range.inRange(literalParam.getArray())) {
Skript.error("The argument '" + signatureParam.name() +"' only accepts values between "
+ Classes.toString(range.getMin()) + " and " + Classes.toString(range.getMax()) + ". "
+ "Provided: " + literalParam.toString(null, Skript.debug()));
return false;
}
}

parameters[i] = exprParam;
} finally {
log.printLog();
}
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/ch/njol/skript/lang/function/Parameter.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.skriptlang.skript.common.function.DefaultFunction;
import org.skriptlang.skript.common.function.Parameter.Modifier.RangedModifier;

import java.util.*;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -280,8 +281,19 @@ public String toString() {
return toString(Skript.debug());
}

// toString output format:
// name: type between min and max = default
//
// Example:
// ns: numbers between 0 and 100 = 3
public String toString(boolean debug) {
return name + ": " + Utils.toEnglishPlural(type.getCodeName(), !single) + (def != null ? " = " + def.toString(null, debug) : "");
String result = name + ": " + Utils.toEnglishPlural(type.getCodeName(), !single);
if (this.hasModifier(Modifier.RANGED)) {
RangedModifier<?> range = this.getModifier(RangedModifier.class);
result += " between " + Classes.toString(range.getMin()) + " and " + Classes.toString(range.getMax());
}
result += (def != null ? " = " + def.toString(null, debug) : "");
return result;
}

@Override
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/ch/njol/skript/sections/SecCatchErrors.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ public boolean isSatisfiedBy(ExperimentSet experimentSet) {

@Override
protected @Nullable TriggerItem walk(Event event) {
RuntimeErrorCatcher catcher = new RuntimeErrorCatcher().start();
last.setNext(null);
TriggerItem.walk(first, event);
ExprCaughtErrors.lastErrors = catcher.getCachedErrors().stream().map(RuntimeError::error).toArray(String[]::new);
catcher.clearCachedErrors()
.stop();
// don't try to run the section if we are uncertain about its boundaries
if (first != null && last != null) {
try (RuntimeErrorCatcher catcher = new RuntimeErrorCatcher().start()) {
last.setNext(null);
TriggerItem.walk(first, event);
ExprCaughtErrors.lastErrors = catcher.getCachedErrors().stream().map(RuntimeError::error).toArray(String[]::new);
}
}
return walk(event, false);
}

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/ch/njol/skript/util/Color.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,11 @@ default int asARGB() {
return asBukkitColor().asARGB();
}

/**
* @return the colour as an RGB hex value: RRGGBB
*/
default String toHexString() {
return String.format("%02X%02X%02X", getRed(), getGreen(), getBlue());
}

}
17 changes: 17 additions & 0 deletions src/main/java/ch/njol/skript/util/ColorRGB.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,23 @@ public String getName() {
);
}

/**
* @param hex A [AA]RRGGBB hex string to parse into ARGB values. Must be either a length of 6 or 8. Omitting alpha will default it to 255 (FF).
* @return a color with the provided ARGB values, or null if parsing failed.
*/
public static @Nullable ColorRGB fromHexString(String hex) {
if (hex.length() != 6 && hex.length() != 8)
return null;
if (hex.length() == 6)
hex = "FF" + hex; // default alpha to 255
try {
int argb = Integer.parseUnsignedInt(hex, 16);
return ColorRGB.fromRGBA(argb >> 16 & 255, argb >> 8 & 255, argb & 255, argb >> 24 & 255);
} catch (NumberFormatException e) {
return null;
}
}

@Override
public Fields serialize() throws NotSerializableException {
return new Fields(this, Variables.yggdrasil);
Expand Down
Loading