Skip to content

Commit f917fde

Browse files
Merge branch 'dev/feature' into dev/LiteralMultipleWarning
2 parents 97c3843 + 85beea7 commit f917fde

12 files changed

Lines changed: 752 additions & 169 deletions

File tree

src/main/java/ch/njol/skript/classes/data/BukkitClasses.java

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,15 @@
1111
import ch.njol.skript.classes.*;
1212
import ch.njol.skript.classes.registry.RegistryClassInfo;
1313
import ch.njol.skript.entity.EntityData;
14-
import ch.njol.skript.entity.WolfData;
14+
import ch.njol.skript.entity.PigData.PigVariantDummy;
15+
import ch.njol.skript.entity.WolfData.WolfVariantDummy;
1516
import ch.njol.skript.expressions.ExprDamageCause;
1617
import ch.njol.skript.expressions.base.EventValueExpression;
1718
import ch.njol.skript.lang.ParseContext;
1819
import ch.njol.skript.lang.util.SimpleLiteral;
1920
import ch.njol.skript.localization.Language;
2021
import ch.njol.skript.registrations.Classes;
21-
import ch.njol.skript.util.BlockUtils;
22-
import ch.njol.skript.util.PotionEffectUtils;
23-
import ch.njol.skript.util.StringMode;
24-
import ch.njol.skript.util.Utils;
22+
import ch.njol.skript.util.*;
2523
import ch.njol.yggdrasil.Fields;
2624
import io.papermc.paper.world.MoonPhase;
2725
import org.bukkit.*;
@@ -52,6 +50,7 @@
5250
import org.bukkit.event.player.PlayerQuitEvent.QuitReason;
5351
import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status;
5452
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
53+
import org.bukkit.inventory.BlockInventoryHolder;
5554
import org.bukkit.inventory.*;
5655
import org.bukkit.metadata.Metadatable;
5756
import org.bukkit.potion.PotionEffect;
@@ -1417,14 +1416,15 @@ public String toVariableNameString(EnchantmentOffer eo) {
14171416
.description("Represents the cause of the action of a potion effect on an entity, e.g. arrow, command")
14181417
.since("2.10"));
14191418

1420-
ClassInfo<?> wolfVariantClassInfo;
1421-
if (Skript.classExists("org.bukkit.entity.Wolf$Variant") && BukkitUtils.registryExists("WOLF_VARIANT")) {
1422-
wolfVariantClassInfo = new RegistryClassInfo<>(Wolf.Variant.class, Registry.WOLF_VARIANT, "wolfvariant", "wolf variants");
1423-
} else {
1424-
/*
1425-
* Registers a dummy/placeholder class to ensure working operation on MC versions that do not have `Wolf.Variant`
1426-
*/
1427-
wolfVariantClassInfo = new ClassInfo<>(WolfData.VariantDummy.class, "wolfvariant");
1419+
ClassInfo<?> wolfVariantClassInfo = getRegistryClassInfo(
1420+
"org.bukkit.entity.Wolf$Variant",
1421+
"WOLF_VARIANT",
1422+
"wolfvariant",
1423+
"wolf variants"
1424+
);
1425+
if (wolfVariantClassInfo == null) {
1426+
// Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Wolf.Variant' (1.20.4-)
1427+
wolfVariantClassInfo = new ClassInfo<>(WolfVariantDummy.class, "wolfvariant");
14281428
}
14291429
Classes.registerClass(wolfVariantClassInfo
14301430
.user("wolf ?variants?")
@@ -1557,14 +1557,75 @@ public String toVariableNameString(WorldBorder border) {
15571557
.changer(DefaultChangers.entityChanger)
15581558
);
15591559

1560-
15611560
Classes.registerClass(new EnumClassInfo<>(EquipmentSlot.class, "equipmentslot", "equipment slots")
15621561
.user("equipment ?slots?")
15631562
.name("Equipment Slot")
15641563
.description("Represents an equipment slot of an entity.")
15651564
.since("2.11")
15661565
);
15671566

1567+
ClassInfo<?> pigVariantClassInfo = getRegistryClassInfo(
1568+
"org.bukkit.entity.Pig$Variant",
1569+
"PIG_VARIANT",
1570+
"pigvariant",
1571+
"pig variants"
1572+
);
1573+
if (pigVariantClassInfo == null) {
1574+
// Registers a dummy/placeholder class to ensure working operation on MC versions that do not have 'Pig.Variant' (1.21.4-)
1575+
pigVariantClassInfo = new ClassInfo<>(PigVariantDummy.class, "pigvariant");
1576+
}
1577+
Classes.registerClass(pigVariantClassInfo
1578+
.user("pig ?variants?")
1579+
.name("Pig Variant")
1580+
.description("Represents the variant of a pig entity.",
1581+
"NOTE: Minecraft namespaces are supported, ex: 'minecraft:warm'.")
1582+
.since("INSERT VERSION")
1583+
.requiredPlugins("Minecraft 1.21.5+")
1584+
.documentationId("PigVariant"));
1585+
1586+
}
1587+
1588+
/**
1589+
* Gets a {@link RegistryClassInfo} by checking if the {@link Class} from {@code classPath} exists
1590+
* and {@link Registry} or {@link io.papermc.paper.registry.RegistryKey} contains {@code registryName}.
1591+
* @param classPath The {@link String} representation of the desired {@link Class}.
1592+
* @param registryName The {@link String} representation of the desired {@link Registry}.
1593+
* @param codeName The name used in patterns.
1594+
* @param languageNode The language node of the type.
1595+
* @return {@link RegistryClassInfo} if the class and registry exists, otherwise {@code null}.
1596+
*/
1597+
private static <R extends Keyed> @Nullable RegistryClassInfo<?> getRegistryClassInfo(
1598+
String classPath,
1599+
String registryName,
1600+
String codeName,
1601+
String languageNode
1602+
) {
1603+
if (!Skript.classExists(classPath))
1604+
return null;
1605+
Registry<R> registry = null;
1606+
if (BukkitUtils.registryExists(registryName)) {
1607+
try {
1608+
//noinspection unchecked
1609+
registry = (Registry<R>) Registry.class.getField(registryName).get(null);
1610+
} catch (NoSuchFieldException | IllegalAccessException e) {
1611+
throw new RuntimeException(e);
1612+
}
1613+
} else if (PaperUtils.registryExists(registryName)) {
1614+
registry = PaperUtils.getBukkitRegistry(registryName);
1615+
}
1616+
if (registry != null) {
1617+
Class<R> registryClass;
1618+
try {
1619+
//noinspection unchecked
1620+
registryClass = (Class<R>) Class.forName(classPath);
1621+
} catch (ClassNotFoundException e) {
1622+
Skript.debug("Could not retrieve the class with the path: '" + classPath + "'.");
1623+
throw new RuntimeException(e);
1624+
}
1625+
return new RegistryClassInfo<>(registryClass, registry, codeName, languageNode);
1626+
}
1627+
Skript.debug("There were no registries found for '" + registryName + "'.");
1628+
return null;
15681629
}
15691630

15701631
}
Lines changed: 141 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,181 @@
11
package ch.njol.skript.effects;
22

3-
import java.lang.invoke.MethodHandle;
4-
import java.lang.invoke.MethodHandles;
5-
import java.lang.invoke.MethodType;
3+
import java.util.function.Function;
64

75
import org.bukkit.block.Block;
8-
import org.bukkit.block.BlockFace;
96
import org.bukkit.block.data.BlockData;
107
import org.bukkit.block.data.Openable;
118
import org.bukkit.block.data.Powerable;
129
import org.bukkit.event.Event;
10+
import org.jetbrains.annotations.NotNull;
1311
import org.jetbrains.annotations.Nullable;
1412

1513
import ch.njol.skript.Skript;
14+
import ch.njol.skript.classes.Changer.ChangeMode;
15+
import ch.njol.skript.classes.Changer.ChangerUtils;
1616
import ch.njol.skript.doc.Description;
1717
import ch.njol.skript.doc.Examples;
1818
import ch.njol.skript.doc.Name;
1919
import ch.njol.skript.doc.Since;
2020
import ch.njol.skript.lang.Effect;
2121
import ch.njol.skript.lang.Expression;
2222
import ch.njol.skript.lang.SkriptParser.ParseResult;
23+
import ch.njol.skript.util.Patterns;
2324
import ch.njol.util.Kleenean;
2425

25-
/**
26-
* @author Peter Güttinger
27-
*/
28-
@SuppressWarnings("deprecation")
2926
@Name("Toggle")
30-
@Description("Toggle the state of a block.")
27+
@Description("Toggle the state of a block or boolean.")
3128
@Examples({"# use arrows to toggle switches, doors, etc.",
3229
"on projectile hit:",
3330
"\tprojectile is arrow",
34-
"\ttoggle the block at the arrow"})
35-
@Since("1.4")
31+
"\ttoggle the block at the arrow",
32+
"",
33+
"# With booleans",
34+
"toggle gravity of player"
35+
})
36+
@Since("1.4, INSERT VERSION (booleans)")
3637
public class EffToggle extends Effect {
37-
38+
39+
private enum Action {
40+
ACTIVATE, DEACTIVATE, TOGGLE;
41+
42+
public boolean apply(boolean current) {
43+
return switch(this) {
44+
case ACTIVATE -> true;
45+
case DEACTIVATE -> false;
46+
case TOGGLE -> !current;
47+
};
48+
}
49+
}
50+
51+
private enum Type {
52+
BLOCKS, BOOLEANS, MIXED;
53+
54+
/**
55+
* Determines the appropriate Type based on the return type of an expression.
56+
* @param returnType The class representing the return type
57+
* @return The corresponding Type
58+
*/
59+
public static Type fromClass(Class<?> returnType) {
60+
boolean isBlockType = Block.class.isAssignableFrom(returnType);
61+
boolean isBooleanType = Boolean.class.isAssignableFrom(returnType);
62+
63+
if (isBlockType && !isBooleanType) {
64+
return BLOCKS;
65+
} else if (isBooleanType && !isBlockType) {
66+
return BOOLEANS;
67+
} else {
68+
return MIXED;
69+
}
70+
}
71+
}
72+
73+
private static final Patterns<Action> patterns = new Patterns<>(new Object[][]{
74+
{"(open|turn on|activate) %blocks%", Action.ACTIVATE},
75+
{"(close|turn off|de[-]activate) %blocks%", Action.DEACTIVATE},
76+
{"(toggle|switch) [[the] state of] %blocks/booleans%", Action.TOGGLE}
77+
});
78+
3879
static {
39-
Skript.registerEffect(EffToggle.class, "(close|turn off|de[-]activate) %blocks%", "(toggle|switch) [[the] state of] %blocks%", "(open|turn on|activate) %blocks%");
80+
Skript.registerEffect(EffToggle.class, patterns.getPatterns());
4081
}
4182

42-
@SuppressWarnings("null")
43-
private Expression<Block> blocks;
44-
private int toggle;
45-
46-
@SuppressWarnings({"unchecked", "null"})
83+
private Expression<?> togglables;
84+
private Action action;
85+
private Type type;
86+
4787
@Override
48-
public boolean init(final Expression<?>[] vars, final int matchedPattern, final Kleenean isDelayed, final ParseResult parseResult) {
49-
blocks = (Expression<Block>) vars[0];
50-
toggle = matchedPattern - 1;
88+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
89+
togglables = expressions[0];
90+
action = patterns.getInfo(matchedPattern);
91+
92+
// Determine expression type using the enum method
93+
type = Type.fromClass(togglables.getReturnType());
94+
95+
// Validate based on type
96+
if (type == Type.BOOLEANS &&
97+
!ChangerUtils.acceptsChange(togglables, ChangeMode.SET, Boolean.class)) {
98+
Skript.error("Cannot toggle '" + togglables + "' as it cannot be set to booleans.");
99+
return false;
100+
} else if (type == Type.MIXED && !ChangerUtils.acceptsChange(togglables, ChangeMode.SET, Block.class, Boolean.class)) {
101+
Skript.error("Cannot toggle '" + togglables + "' as it cannot be set to both blocks and booleans.");
102+
return false;
103+
}
104+
51105
return true;
52106
}
53107

54108
@Override
55-
protected void execute(final Event e) {
56-
for (Block b : blocks.getArray(e)) {
57-
BlockData data = b.getBlockData();
58-
if (toggle == -1) {
59-
if (data instanceof Openable)
60-
((Openable) data).setOpen(false);
61-
else if (data instanceof Powerable)
62-
((Powerable) data).setPowered(false);
63-
} else if (toggle == 1) {
64-
if (data instanceof Openable)
65-
((Openable) data).setOpen(true);
66-
else if (data instanceof Powerable)
67-
((Powerable) data).setPowered(true);
68-
} else {
69-
if (data instanceof Openable) // open = NOT was open
70-
((Openable) data).setOpen(!((Openable) data).isOpen());
71-
else if (data instanceof Powerable) // power = NOT power
72-
((Powerable) data).setPowered(!((Powerable) data).isPowered());
109+
protected void execute(Event event) {
110+
switch (type) {
111+
case BOOLEANS -> toggleBooleans(event);
112+
case BLOCKS -> toggleBlocks(event);
113+
case MIXED -> toggleMixed(event);
114+
}
115+
}
116+
117+
/**
118+
* Toggles blocks by opening/closing or powering/unpowering them.
119+
* @param event the event used for evaluation
120+
*/
121+
private void toggleBlocks(Event event) {
122+
for (Object obj : togglables.getArray(event)) {
123+
if (obj instanceof Block block) {
124+
toggleSingleBlock(block);
73125
}
74-
75-
b.setBlockData(data);
76126
}
77127
}
78128

129+
/**
130+
* Toggles a single block, either by opening/closing or powering/unpowering it.
131+
* @param block The block to toggle
132+
*/
133+
private void toggleSingleBlock(@NotNull Block block) {
134+
BlockData data = block.getBlockData();
135+
if (data instanceof Openable openable) {
136+
openable.setOpen(action.apply(openable.isOpen()));
137+
} else if (data instanceof Powerable powerable) {
138+
powerable.setPowered(action.apply(powerable.isPowered()));
139+
}
140+
block.setBlockData(data);
141+
}
142+
143+
/**
144+
* Uses {@link Expression#changeInPlace(Event, Function)} to toggle booleans.
145+
* @param event the event used for evaluation
146+
*/
147+
private void toggleBooleans(Event event) {
148+
togglables.changeInPlace(event, (obj) -> {
149+
if (!(obj instanceof Boolean bool)) {
150+
return null;
151+
}
152+
return action.apply(bool);
153+
});
154+
}
155+
156+
/**
157+
* Uses {@link Expression#changeInPlace(Event, Function)} to toggle both blocks and booleans.
158+
* @param event the event used for evaluation
159+
*/
160+
private void toggleMixed(Event event) {
161+
togglables.changeInPlace(event, (obj) -> {
162+
if (obj instanceof Block block) {
163+
toggleSingleBlock(block);
164+
return block;
165+
} else if (obj instanceof Boolean bool) {
166+
return action.apply(bool);
167+
}
168+
return obj;
169+
});
170+
}
171+
79172
@Override
80-
public String toString(final @Nullable Event e, final boolean debug) {
81-
return "toggle " + blocks.toString(e, debug);
173+
public String toString(@Nullable Event event, boolean debug) {
174+
String actionText = switch (action) {
175+
case ACTIVATE -> "activate";
176+
case DEACTIVATE -> "deactivate";
177+
case TOGGLE -> "toggle";
178+
};
179+
return actionText + " " + togglables.toString(event, debug);
82180
}
83-
84181
}

0 commit comments

Comments
 (0)