Skip to content

Commit a5c6914

Browse files
Merge branch 'dev/feature' into feature/remove-inf-value
2 parents 0be6027 + 53e98af commit a5c6914

37 files changed

Lines changed: 1250 additions & 148 deletions

src/main/java/ch/njol/skript/ScriptLoader.java

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ch.njol.skript.config.SectionNode;
66
import ch.njol.skript.config.SimpleNode;
77
import ch.njol.skript.events.bukkit.PreScriptLoadEvent;
8+
import ch.njol.skript.lang.ExecutionIntent;
89
import ch.njol.skript.lang.Section;
910
import ch.njol.skript.lang.SkriptParser;
1011
import ch.njol.skript.lang.Statement;
@@ -20,7 +21,7 @@
2021
import ch.njol.skript.util.SkriptColor;
2122
import ch.njol.skript.util.Task;
2223
import ch.njol.skript.util.Timespan;
23-
import ch.njol.skript.variables.TypeHints;
24+
import ch.njol.skript.variables.HintManager;
2425
import ch.njol.util.NonNullPair;
2526
import ch.njol.util.OpenCloseable;
2627
import ch.njol.util.StringUtils;
@@ -951,6 +952,14 @@ public static ArrayList<TriggerItem> loadItems(SectionNode node) {
951952

952953
ArrayList<TriggerItem> items = new ArrayList<>();
953954

955+
// Begin local variable type hints
956+
parser.getHintManager().enterScope(true);
957+
// Track if the scope has been frozen
958+
// This is the case when a statement that stops further execution is encountered
959+
// Further statements would not run, meaning the hints from them are inaccurate
960+
// When true, before exiting the scope, its hints are cleared to avoid passing them up
961+
boolean freezeScope = false;
962+
954963
boolean executionStops = false;
955964
for (Node subNode : node) {
956965
parser.setNode(subNode);
@@ -983,11 +992,18 @@ public static ArrayList<TriggerItem> loadItems(SectionNode node) {
983992

984993
items.add(item);
985994
} else if (subNode instanceof SectionNode subSection) {
986-
TypeHints.enterScope(); // Begin conditional type hints
987995

988996
RetainingLogHandler handler = SkriptLogger.startRetainingLog();
989997
find_section:
990998
try {
999+
// enter capturing scope
1000+
// it is possible that the line may successfully parse and initialize (via init), but some other issue
1001+
// prevents it from being able to load. for example:
1002+
// - a statement with a section that has no expression to claim the section
1003+
// - a statement with a section that has multiple expressions attempting to claim the section
1004+
// thus, hints may be added, but we do not want to save them as the line is invalid
1005+
parser.getHintManager().enterScope(false);
1006+
9911007
item = Section.parse(expr, "Can't understand this section: " + expr, subSection, items);
9921008
if (item != null)
9931009
break find_section;
@@ -1014,6 +1030,13 @@ public static ArrayList<TriggerItem> loadItems(SectionNode node) {
10141030
}
10151031
continue;
10161032
} finally {
1033+
// exit hint scope (see above)
1034+
HintManager hintManager = parser.getHintManager();
1035+
if (item == null) { // unsuccessful, wipe out hints
1036+
hintManager.clearScope(0, false);
1037+
}
1038+
hintManager.exitScope();
1039+
10171040
RetainingLogHandler afterParse = handler.backup();
10181041
handler.clear();
10191042
handler.printLog();
@@ -1023,9 +1046,6 @@ public static ArrayList<TriggerItem> loadItems(SectionNode node) {
10231046
}
10241047

10251048
items.add(item);
1026-
1027-
// Destroy these conditional type hints
1028-
TypeHints.exitScope();
10291049
} else {
10301050
continue;
10311051
}
@@ -1037,7 +1057,24 @@ public static ArrayList<TriggerItem> loadItems(SectionNode node) {
10371057
Skript.warning("Unreachable code. The previous statement stops further execution.");
10381058
}
10391059
executionStops = item.executionIntent() != null;
1060+
1061+
if (executionStops && !freezeScope) {
1062+
freezeScope = true;
1063+
// Execution might stop for some sections but not all
1064+
// We want to pass hints up to the scope that execution resumes in
1065+
if (item.executionIntent() instanceof ExecutionIntent.StopSections intent) {
1066+
parser.getHintManager().mergeScope(0, intent.levels(), true);
1067+
}
1068+
}
1069+
}
1070+
1071+
// If the scope was frozen, then we need to clear it to prevent passing up inaccurate hints
1072+
// They will have already been copied as necessary
1073+
if (freezeScope) {
1074+
parser.getHintManager().clearScope(0, false);
10401075
}
1076+
// Destroy local variable type hints for this section
1077+
parser.getHintManager().exitScope();
10411078

10421079
for (int i = 0; i < items.size() - 1; i++)
10431080
items.get(i).setNext(items.get(i + 1));

src/main/java/ch/njol/skript/Skript.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,6 @@ public void onDisable() {
12641264
if (disabled)
12651265
return;
12661266
disabled = true;
1267-
this.experimentRegistry = null;
12681267

12691268
if (!partDisabled) {
12701269
beforeDisable();
@@ -1279,6 +1278,8 @@ public void onDisable() {
12791278
Skript.exception(e, "An error occurred while shutting down.", "This might or might not cause any issues.");
12801279
}
12811280
}
1281+
1282+
this.experimentRegistry = null;
12821283
}
12831284

12841285
// ================ CONSTANTS, OPTIONS & OTHER ================

src/main/java/ch/njol/skript/aliases/ItemType.java

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ch.njol.skript.aliases;
22

3+
import ch.njol.skript.Skript;
34
import ch.njol.skript.aliases.ItemData.OldItemData;
45
import ch.njol.skript.bukkitutil.BukkitUnsafe;
56
import ch.njol.skript.bukkitutil.ItemUtils;
@@ -21,6 +22,7 @@
2122
import ch.njol.yggdrasil.Fields;
2223
import ch.njol.yggdrasil.Fields.FieldContext;
2324
import ch.njol.yggdrasil.YggdrasilSerializable.YggdrasilExtendedSerializable;
25+
import com.google.common.collect.Iterators;
2426
import org.bukkit.*;
2527
import org.bukkit.block.Block;
2628
import org.bukkit.block.BlockState;
@@ -50,6 +52,8 @@
5052
public class ItemType implements Unit, Iterable<ItemData>, Container<ItemStack>, YggdrasilExtendedSerializable,
5153
AnyNamed, AnyAmount {
5254

55+
private static final boolean IS_RUNNING_1_21 = Skript.isRunningMinecraft(1, 21);
56+
5357
static {
5458
// This handles updating ItemType and ItemData variable records
5559
Variables.yggdrasil.registerFieldHandler(new FieldHandler() {
@@ -710,20 +714,18 @@ public static ItemStack[] getCopiedContents(Inventory invi) {
710714
}
711715

712716
/**
713-
* Gets copy of storage contents, i.e. ignores armor and off hand. This is due to Spigot 1.9
714-
* added armor slots, and off hand to default inventory index.
715-
* @param invi Inventory
717+
* Gets copy of storage contents, i.e. ignores armor and off hand.
718+
* This method simply calls {@link Inventory#getStorageContents()} and clones the items contained within the array.
719+
* @param inventory The inventory to obtain contents from.
716720
* @return Copied storage contents
717721
*/
718-
public static ItemStack[] getStorageContents(final Inventory invi) {
719-
if (invi instanceof PlayerInventory) {
720-
ItemStack[] buf = invi.getContents();
721-
ItemStack[] tBuf = new ItemStack[36];
722-
for (int i = 0; i < 36; i++)
723-
if (buf[i] != null)
724-
tBuf[i] = buf[i].clone();
725-
return tBuf;
726-
} else return getCopiedContents(invi);
722+
public static ItemStack[] getStorageContents(Inventory inventory) {
723+
ItemStack[] buf = inventory.getStorageContents();
724+
for (int i = 0; i < buf.length; i++) {
725+
if (buf[i] != null)
726+
buf[i] = buf[i].clone();
727+
}
728+
return buf;
727729
}
728730

729731
/**
@@ -969,33 +971,46 @@ public void addTo(final List<ItemStack> list) {
969971
/**
970972
* Tries to add this ItemType to the given inventory. Does not call updateInventory for players.
971973
*
972-
* @param invi
974+
* @param inventory The inventory to add this the {@link ItemStack}(s) represented by this ItemType to.
973975
* @return Whether everything could be added to the inventory
974976
*/
975-
public boolean addTo(final Inventory invi) {
976-
// important: don't use inventory.add() - it ignores max stack sizes
977-
ItemStack[] buf = invi.getContents();
978-
979-
ItemStack[] tBuf = buf.clone();
980-
if (invi instanceof PlayerInventory) {
981-
buf = new ItemStack[36];
982-
for(int i = 0; i < 36; ++i) {
983-
buf[i] = tBuf[i];
977+
public boolean addTo(Inventory inventory) {
978+
// TODO remove this when applicable
979+
// On newer versions, such as 1.21.6, this legacy method of manually rewriting inventory content arrays risks
980+
// accidental item deletion and fails to respect properties such as stack size.
981+
// Thus, we switch to use the API methods. However, these API methods do not work properly on older versions
982+
// such as 1.20.6. For those versions, we continue to use this legacy method.
983+
// See https://github.com/SkriptLang/Skript/pull/7986
984+
if (!IS_RUNNING_1_21) {
985+
// important: don't use inventory.add() - it ignores max stack sizes
986+
ItemStack[] buf = inventory.getContents();
987+
988+
ItemStack[] tBuf = buf.clone();
989+
if (inventory instanceof PlayerInventory) {
990+
buf = new ItemStack[36];
991+
for(int i = 0; i < 36; ++i) {
992+
buf[i] = tBuf[i];
993+
}
984994
}
985-
}
986995

987-
final boolean b = addTo(buf);
996+
final boolean b = addTo(buf);
988997

989-
if (invi instanceof PlayerInventory) {
990-
buf = Arrays.copyOf(buf, tBuf.length);
991-
for (int i = tBuf.length - 5; i < tBuf.length; ++i) {
992-
buf[i] = tBuf[i];
998+
if (inventory instanceof PlayerInventory) {
999+
buf = Arrays.copyOf(buf, tBuf.length);
1000+
for (int i = tBuf.length - 5; i < tBuf.length; ++i) {
1001+
buf[i] = tBuf[i];
1002+
}
9931003
}
994-
}
9951004

996-
assert buf != null;
997-
invi.setContents(buf);
998-
return b;
1005+
assert buf != null;
1006+
inventory.setContents(buf);
1007+
return b;
1008+
}
1009+
if (!isAll()) {
1010+
ItemStack random = getItem().getRandom();
1011+
return random == null || inventory.addItem(random).isEmpty();
1012+
}
1013+
return inventory.addItem(Iterators.toArray(getItem().getAll().iterator(), ItemStack.class)).isEmpty();
9991014
}
10001015

10011016
private static boolean addTo(@Nullable ItemStack is, ItemStack[] buf) {

src/main/java/ch/njol/skript/command/Argument.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ public void set(final ScriptCommandEvent e, final Object[] o) {
130130
public T[] getCurrent(final Event e) {
131131
return current.get(e);
132132
}
133+
134+
public @Nullable String getName() {
135+
return name;
136+
}
133137

134138
public Class<T> getType() {
135139
return type.getC();

src/main/java/ch/njol/skript/command/ScriptCommand.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import ch.njol.skript.lang.Expression;
99
import ch.njol.skript.lang.SkriptParser;
1010
import ch.njol.skript.lang.Trigger;
11+
import ch.njol.skript.lang.Variable;
1112
import ch.njol.skript.lang.VariableString;
13+
import ch.njol.skript.lang.parser.ParserInstance;
1214
import ch.njol.skript.lang.util.SimpleEvent;
1315
import ch.njol.skript.lang.util.SimpleLiteral;
1416
import ch.njol.skript.localization.Language;
@@ -23,6 +25,7 @@
2325
import ch.njol.skript.util.Utils;
2426
import ch.njol.skript.util.chat.BungeeConverter;
2527
import ch.njol.skript.util.chat.MessageComponent;
28+
import ch.njol.skript.variables.HintManager;
2629
import ch.njol.skript.variables.Variables;
2730
import ch.njol.util.StringUtils;
2831
import com.google.common.base.Preconditions;
@@ -204,8 +207,24 @@ public ScriptCommand(
204207
this.pattern = pattern;
205208
this.arguments = arguments;
206209

207-
trigger = new Trigger(script, "command /" + name, new SimpleEvent(), ScriptLoader.loadItems(node));
208-
trigger.setLineNumber(node.getLine());
210+
HintManager hintManager = ParserInstance.get().getHintManager();
211+
try {
212+
hintManager.enterScope(false);
213+
for (Argument<?> argument : arguments) {
214+
String hintName = argument.getName();
215+
if (hintName == null) {
216+
continue;
217+
}
218+
if (!argument.isSingle()) {
219+
hintName += Variable.SEPARATOR + "*";
220+
}
221+
hintManager.set(hintName, argument.getType());
222+
}
223+
trigger = new Trigger(script, "command /" + name, new SimpleEvent(), ScriptLoader.loadItems(node));
224+
trigger.setLineNumber(node.getLine());
225+
} finally {
226+
hintManager.exitScope();
227+
}
209228

210229
bukkitCommand = setupBukkitCommand();
211230
}

src/main/java/ch/njol/skript/effects/EffChange.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import ch.njol.skript.lang.SyntaxStringBuilder;
1616
import ch.njol.skript.lang.Variable;
1717
import ch.njol.skript.util.LiteralUtils;
18+
import ch.njol.skript.variables.HintManager;
1819
import org.skriptlang.skript.lang.script.ScriptWarning;
1920
import org.bukkit.event.Event;
2021
import org.jetbrains.annotations.Nullable;
@@ -180,6 +181,12 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
180181
flatAcceptedTypes[i] = type;
181182
}
182183

184+
// Hint handling for deleting
185+
if (changed instanceof Variable<?> variable && mode == ChangeMode.DELETE && HintManager.canUseHints(variable)) {
186+
// Remove type hints in this scope only for a deleted variable
187+
getParser().getHintManager().delete(variable);
188+
}
189+
183190
if (changer == null) { // Safe to reset/delete
184191
return true;
185192
}
@@ -271,13 +278,24 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
271278
}
272279

273280
// Print warning if attempting to save a non-serializable type in a global variable
274-
if (!variable.isLocal() && (mode == ChangeMode.SET || (variable.isList() && mode == ChangeMode.ADD))) {
275-
ClassInfo<?> changerInfo = Classes.getSuperClassInfo(changer.getReturnType());
276-
if (changerInfo.getC() != Object.class && changerInfo.getSerializer() == null && changerInfo.getSerializeAs() == null
277-
&& !SkriptConfig.disableObjectCannotBeSavedWarnings.value()
278-
&& getParser().isActive() && !getParser().getCurrentScript().suppressesWarning(ScriptWarning.VARIABLE_SAVE)) {
279-
Skript.warning(changerInfo.getName().withIndefiniteArticle() + " cannot be saved. That is, the contents of the variable "
280-
+ changed.toString(null, Skript.debug()) + " will be lost when the server stops.");
281+
if (mode == ChangeMode.SET || (variable.isList() && mode == ChangeMode.ADD)) {
282+
if (HintManager.canUseHints(variable)) { // Hint handling
283+
HintManager hintManager = getParser().getHintManager();
284+
Class<?>[] hints = changer.possibleReturnTypes();
285+
if (mode == ChangeMode.SET) { // Override existing hints in scope
286+
hintManager.set(variable, hints);
287+
} else {
288+
hintManager.add(variable, hints);
289+
}
290+
}
291+
if (!variable.isLocal()) {
292+
ClassInfo<?> changerInfo = Classes.getSuperClassInfo(changer.getReturnType());
293+
if (changerInfo.getC() != Object.class && changerInfo.getSerializer() == null && changerInfo.getSerializeAs() == null
294+
&& !SkriptConfig.disableObjectCannotBeSavedWarnings.value()
295+
&& getParser().isActive() && !getParser().getCurrentScript().suppressesWarning(ScriptWarning.VARIABLE_SAVE)) {
296+
Skript.warning(changerInfo.getName().withIndefiniteArticle() + " cannot be saved. That is, the contents of the variable "
297+
+ changed.toString(null, Skript.debug()) + " will be lost when the server stops.");
298+
}
281299
}
282300
}
283301
}

src/main/java/ch/njol/skript/effects/EffCopy.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ch.njol.skript.lang.SkriptParser.ParseResult;
1414
import ch.njol.skript.lang.Variable;
1515
import ch.njol.skript.registrations.Classes;
16+
import ch.njol.skript.variables.HintManager;
1617
import ch.njol.skript.variables.Variables;
1718
import ch.njol.util.Kleenean;
1819
import org.bukkit.event.Event;
@@ -64,6 +65,16 @@ public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelaye
6465
return false;
6566
}
6667
}
68+
69+
// set type hints for destination(s) if applicable
70+
Class<?>[] sourceHints = source.possibleReturnTypes();
71+
HintManager hintManager = getParser().getHintManager();
72+
for (Variable<?> destination : destinations) {
73+
if (HintManager.canUseHints(destination)) {
74+
hintManager.set(destination, sourceHints);
75+
}
76+
}
77+
6778
return true;
6879
}
6980

0 commit comments

Comments
 (0)