diff --git a/.github/workflows/checkstyle.yml b/.github/workflows/checkstyle.yml new file mode 100644 index 00000000..1e583b60 --- /dev/null +++ b/.github/workflows/checkstyle.yml @@ -0,0 +1,37 @@ +name: checkstyle + +on: + push: + branches: + - master + - 'dev/**' + pull_request: + +jobs: + build: + if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + - name: validate gradle wrapper + uses: gradle/actions/wrapper-validation@v5 + - name: Set up JDK 25 + uses: actions/setup-java@v5 + with: + java-version: '25' + distribution: 'adopt' + cache: gradle + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Run checkstyle + run: ./gradlew clean checkstyleMain + - name: Upload checkstyle report + uses: actions/upload-artifact@v6 + if: success() + with: + name: checkstyle-report + path: | + build/reports/checkstyle/*.xml + build/reports/checkstyle/*.html diff --git a/build.gradle.kts b/build.gradle.kts index fba53dce..1a0125c4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { id("java") id("com.gradleup.shadow") version "9.2.0" id("maven-publish") + id("checkstyle") } java.sourceCompatibility = JavaVersion.VERSION_25 @@ -25,7 +26,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:1.0.3") { + implementation("com.github.SkriptDev:skript-parser:1.0.4") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { @@ -78,6 +79,22 @@ tasks { } } +tasks.withType { + // This one class is causing an issue on like 37 (not sure why) + exclude("**/Block.java") +} + +checkstyle { + // Specify the version of the Checkstyle tool to use + toolVersion = "10.21.0" // Use a recent version of Checkstyle + + isIgnoreFailures = false + + // Point to your custom Checkstyle configuration file + // The default location is config/checkstyle/checkstyleMain.xml + configFile = file("${rootProject.projectDir}/config/checkstyle/checkstyle.xml") +} + publishing { publications { create("mavenShadow") { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..83c6b26c --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java b/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java index c2ebefd6..4465846e 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java @@ -11,6 +11,7 @@ import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import org.jetbrains.annotations.NotNull; @@ -22,7 +23,7 @@ * This class provides a wrapper around Hytale's block system, allowing for easy interaction with blocks in a world. * This may be changed/removed in the future. */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "deprecation"}) public class Block { private final @NotNull World world; @@ -57,14 +58,21 @@ public void setType(@NotNull BlockType type, int settings) { public byte getFluidLevel() { long index = ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ()); - Ref columnRef = this.world.getChunk(index).getReference(); + WorldChunk chunk = this.world.getChunk(index); + if (chunk == null) return 0; + + Ref columnRef = chunk.getReference(); Store store = columnRef.getStore(); ChunkColumn column = store.getComponent(columnRef, ChunkColumn.getComponentType()); + if (column == null) return 0; + Ref section = column.getSection(ChunkUtil.chunkCoordinate(this.pos.getY())); if (section == null) { return 0; } else { FluidSection fluidSection = store.getComponent(section, FluidSection.getComponentType()); + if (fluidSection == null) return 0; + return fluidSection.getFluidLevel(this.pos.getX(), this.pos.getY(), this.pos.getZ()); } } diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java index c8b07171..702101c3 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/EntityComponentUtils.java @@ -79,7 +79,8 @@ public class EntityComponentUtils { } @SuppressWarnings({"DataFlowIssue", "deprecation"}) - public static @NotNull Pair dropItem(Store store, ItemStack itemStack, Location location, Vector3f velocity, float pickupDelay) { + public static @NotNull Pair dropItem(Store store, ItemStack itemStack, + Location location, Vector3f velocity, float pickupDelay) { if (itemStack.isEmpty() || !itemStack.isValid()) { return new Pair<>(null, null); } @@ -87,11 +88,11 @@ public class EntityComponentUtils { Vector3d position = location.getPosition(); Vector3f rotation = location.getRotation(); - Holder itemEntityHolder = ItemComponent.generateItemDrop(store, itemStack, position, rotation, velocity.getX(), velocity.getY(), velocity.getZ()); + Holder itemEntityHolder = ItemComponent.generateItemDrop(store, itemStack, position, rotation, + velocity.getX(), velocity.getY(), velocity.getZ()); if (itemEntityHolder == null) { return new Pair<>(null, null); } - ; ItemComponent itemComponent = itemEntityHolder.getComponent(ItemComponent.getComponentType()); if (itemComponent != null) { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/addon/AddonLoader.java b/src/main/java/com/github/skriptdev/skript/api/skript/addon/AddonLoader.java new file mode 100644 index 00000000..c78f52c1 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/addon/AddonLoader.java @@ -0,0 +1,113 @@ +package com.github.skriptdev.skript.api.skript.addon; + +import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.HySk; +import com.hypixel.hytale.codec.ExtraInfo; +import com.hypixel.hytale.codec.util.RawJsonReader; +import io.github.syst3ms.skriptparser.log.ErrorType; +import io.github.syst3ms.skriptparser.log.LogEntry; +import io.github.syst3ms.skriptparser.registration.SkriptAddon; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class AddonLoader { + + private int addonCount = 0; + + public void loadAddonsFromFolder() { + Utils.log("Loading addons..."); + Path resolve = HySk.getInstance().getDataDirectory().resolve("addons"); + File addonFolder = resolve.toFile(); + if (!addonFolder.exists()) { + if (!addonFolder.mkdirs()) { + Utils.error("Failed to create addons folder"); + return; + } + } else if (!addonFolder.isDirectory()) { + Utils.error("Addons folder is not a directory"); + return; + } + File[] files = addonFolder.listFiles(); + if (files == null) { + Utils.error("Failed to list files in addons folder"); + return; + } + + for (File file : Arrays.stream(files) + .filter(File::isFile) + .filter(f -> f.getName().endsWith(".jar")) + .toList()) { + loadAddon(file); + } + String plural = this.addonCount == 1 ? "" : "s"; + Utils.log("Finished loading %s addon%s!", this.addonCount, plural); + } + + private void loadAddon(File file) { + try (JarFile jarFile = new JarFile(file)) { + JarEntry jarEntry = jarFile.getJarEntry("manifest.json"); + if (jarEntry == null) { + Utils.error("Manifest.json not found in addon " + file.getName()); + return; + } + + InputStream inputStream = jarFile.getInputStream(jarEntry); + InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + char[] buffer = RawJsonReader.READ_BUFFER.get(); + RawJsonReader rawJsonReader = new RawJsonReader(reader, buffer); + + Manifest manifest = Manifest.CODEC.decodeJson(rawJsonReader, new ExtraInfo()); + if (manifest == null) { + Utils.error("Failed to decode manifest.json in addon " + file.getName()); + return; + } + + URL[] urls = {file.toURI().toURL()}; + URLClassLoader classLoader = new URLClassLoader(urls, HySk.getInstance().getClassLoader()); + Class externalClass; + try { + externalClass = classLoader.loadClass(manifest.getMainClass()); + } catch (ClassNotFoundException e) { + Utils.error("Main class not found in addon " + file.getName()); + return; + } + Object mainClassIntance; + try { + mainClassIntance = externalClass.getDeclaredConstructor(String.class).newInstance(manifest.getName()); + } catch (ReflectiveOperationException e) { + Utils.error("Failed to create instance of addon " + file.getName(), ErrorType.EXCEPTION); + return; + } + if (mainClassIntance instanceof HySkriptAddon addon) { + addon.setManifest(manifest); + addon.setup(); + this.addonCount++; + // Finalize registration and logging + for (LogEntry logEntry : addon.getSkriptRegistration().register()) { + Utils.log(null, logEntry); + } + } + } catch (IOException e) { + Utils.error("Failed to load addon " + file.getName(), ErrorType.EXCEPTION); + } + } + + public void shutdownAddons() { + for (SkriptAddon addon : SkriptAddon.getAddons()) { + if (addon instanceof HySkriptAddon hySkriptAddon) { + hySkriptAddon.shutdown(); + } + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/addon/HySkriptAddon.java b/src/main/java/com/github/skriptdev/skript/api/skript/addon/HySkriptAddon.java new file mode 100644 index 00000000..9ee51f7b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/addon/HySkriptAddon.java @@ -0,0 +1,64 @@ +package com.github.skriptdev.skript.api.skript.addon; + +import com.hypixel.hytale.logger.HytaleLogger; +import com.hypixel.hytale.server.core.Message; +import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Base class for addons for HySkript. + */ +public abstract class HySkriptAddon extends SkriptAddon { + + private Manifest manifest; + private final HytaleLogger hytaleLogger; + + public HySkriptAddon(String name) { + super(name); + this.hytaleLogger = HytaleLogger.get("HySkript|" + name + "|A"); + } + + /** + * Called when the addon starts to load. + * This is a good time to set up your syntaxes. + */ + abstract public void setup(); + + /** + * Called when the addon is shutting down. + * This is a good time to clean up resources. + */ + abstract public void shutdown(); + + public HytaleLogger getHytaleLogger() { + return this.hytaleLogger; + } + + final void setManifest(Manifest manifest) { + this.manifest = manifest; + } + + public final Manifest getManifest() { + return manifest; + } + + public final Message[] getInfo() { + List info = new ArrayList<>(); + info.add(Message.raw("Version: " + this.manifest.getVersion())); + + String description = this.manifest.getDescription(); + if (description != null) info.add(Message.raw("Description: " + description)); + + @Nullable String[] authors = this.manifest.getAuthors(); + if (authors != null) info.add(Message.raw("Authors: " + String.join(", ", authors))); + + String website = this.manifest.getWebsite(); + if (website != null) info.add(Message.raw("Website: ").insert(Message.raw(website).link(website))); + + return info.toArray(Message[]::new); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java b/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java new file mode 100644 index 00000000..ec09a061 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java @@ -0,0 +1,74 @@ +package com.github.skriptdev.skript.api.skript.addon; + +import com.hypixel.hytale.codec.Codec; +import com.hypixel.hytale.codec.KeyedCodec; +import com.hypixel.hytale.codec.builder.BuilderCodec; + +import java.util.Arrays; + +public class Manifest { + + private static final BuilderCodec.Builder BUILDER = BuilderCodec.builder(Manifest.class, Manifest::new); + public static final Codec CODEC = BUILDER + .append(new KeyedCodec<>("Main", Codec.STRING), + ((manifest, string) -> manifest.mainClass = string), + (manifest -> manifest.mainClass)) + .add().append(new KeyedCodec<>("Name", Codec.STRING), + ((manifest, string) -> manifest.name = string), + (manifest -> manifest.name)) + .add().append(new KeyedCodec<>("Version", Codec.STRING), + ((manifest, string) -> manifest.version = string), + (manifest -> manifest.version)) + .add().append(new KeyedCodec<>("Description", Codec.STRING), + ((manifest, string) -> manifest.description = string), + (manifest -> manifest.description)) + .add().append(new KeyedCodec<>("Authors", Codec.STRING_ARRAY), + ((manifest, strings) -> manifest.authors = strings), + (manifest -> manifest.authors)) + .add().append(new KeyedCodec<>("Website", Codec.STRING), + ((manifest, string) -> manifest.website = string), + (manifest -> manifest.website)) + .add().build(); + private String mainClass; + private String name; + private String version; + private String description; + private String[] authors; + private String website; + + public String getMainClass() { + return this.mainClass; + } + + public String getName() { + return this.name; + } + + public String getVersion() { + return this.version; + } + + public String getDescription() { + return this.description; + } + + public String[] getAuthors() { + return this.authors; + } + + public String getWebsite() { + return this.website; + } + + @Override + public String toString() { + return "Manifest{" + + "name='" + name + '\'' + + ", version='" + version + '\'' + + ", description='" + description + '\'' + + ", authors=" + Arrays.toString(authors) + + ", website='" + website + '\'' + + '}'; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/command/ArgUtils.java b/src/main/java/com/github/skriptdev/skript/api/skript/command/ArgUtils.java index a0c20e4c..f8aae5df 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/command/ArgUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/command/ArgUtils.java @@ -29,7 +29,16 @@ public static void init() { register(NPCCommand.NPC_ROLE, "role", "npcrole", "npc_role"); register(ArgTypes.PLAYER_REF, "player_ref", "playerref"); + // ASSETS + register(ArgTypes.BLOCK_TYPE_ASSET, "block_type", "blocktype"); + register(ArgTypes.EFFECT_ASSET, "entity_effect", "entityeffect"); + register(ArgTypes.ITEM_ASSET, "item"); + register(ArgTypes.SOUND_CATEGORY, "sound_category"); + register(ArgTypes.SOUND_EVENT_ASSET, "sound_event", "soundevent", "sound"); + // WORLD + register(ArgTypes.RELATIVE_BLOCK_POSITION, "relative_block_position"); + register(ArgTypes.RELATIVE_POSITION, "relative_position"); register(ArgTypes.ROTATION, "rotation", "vector3f"); register(ArgTypes.VECTOR3I, "vector3i"); register(ArgTypes.WORLD, "world"); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/command/CommandArg.java b/src/main/java/com/github/skriptdev/skript/api/skript/command/CommandArg.java index 0004a748..d3c88ddf 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/command/CommandArg.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/command/CommandArg.java @@ -13,10 +13,10 @@ public class CommandArg { private final boolean optional; /** - * @param name Name of the argument + * @param name Name of the argument * @param description Description of the argument - * @param type Type of the argument - * @param optional Whether the argument is optional + * @param type Type of the argument + * @param optional Whether the argument is optional */ private CommandArg(String name, String description, ArgumentType type, boolean optional) { this.name = name; @@ -51,7 +51,9 @@ public String toString() { '}'; } - /** Parse a string into a CommandArg. + /** + * Parse a string into a CommandArg. + * * @param a String to parse in the format of {@code []} or {@code } * @return CommandArg */ diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/command/ScriptCommandBuilder.java b/src/main/java/com/github/skriptdev/skript/api/skript/command/ScriptCommandBuilder.java index a3bd3871..6369b7fb 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/command/ScriptCommandBuilder.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/command/ScriptCommandBuilder.java @@ -1,7 +1,7 @@ package com.github.skriptdev.skript.api.skript.command; import com.github.skriptdev.skript.plugin.HySk; -import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; +import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand.ScriptCommandContext; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.command.system.AbstractCommand; @@ -122,7 +122,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull Store store) { - ScriptCommand.ScriptCommandContext context = new ScriptCommand.ScriptCommandContext(ScriptCommandBuilder.this.commandName, commandContext.sender(), null, world); + ScriptCommandContext context = new ScriptCommandContext(ScriptCommandBuilder.this.commandName, + commandContext.sender(), null, world); createLocalVariables(commandContext, context); Statement.runAll(trigger, context); Variables.clearLocalVariables(context); @@ -146,7 +147,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo CommandSender sender = commandContext.sender(); Player player = null; if (sender instanceof Player p) player = p; - ScriptCommand.ScriptCommandContext context = new ScriptCommand.ScriptCommandContext(ScriptCommandBuilder.this.commandName, sender, player, null); + ScriptCommandContext context = new ScriptCommandContext(ScriptCommandBuilder.this.commandName, sender, player, null); createLocalVariables(commandContext, context); Statement.runAll(trigger, context); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java b/src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java index d57d2670..53e5b18b 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/docs/MarkdownDocPrinter.java @@ -223,7 +223,7 @@ private static void printFunctions(PrintWriter writer) { if (documentation.isNoDoc()) return; // Create a pattern for a function - String pattern = String.format("%s(%s)", jf.getName(), String.join(", ", parameterNames)); + String pattern = String.format("%s(%s)", jf.getName(), String.join(", ", parameterNames)); TextElement textElement = new TextElement(pattern); printDocumentation("Function", writer, documentation, List.of(textElement)); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerContext.java b/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerContext.java index 839237a1..c098ee69 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerContext.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerContext.java @@ -4,7 +4,7 @@ import io.github.syst3ms.skriptparser.lang.TriggerContext; /** - * Represents a {@link TriggerContext} which includes a player + * Represents a {@link TriggerContext} which includes a {@link Player} */ public interface PlayerContext extends TriggerContext { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerRefContext.java b/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerRefContext.java new file mode 100644 index 00000000..e03347c6 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/event/PlayerRefContext.java @@ -0,0 +1,13 @@ +package com.github.skriptdev.skript.api.skript.event; + +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.TriggerContext; + +/** + * Represents a {@link TriggerContext} which includes a {@link PlayerRef} + */ +public interface PlayerRefContext extends TriggerContext { + + PlayerRef getPlayerRef(); + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/registration/AssetStoreRegistry.java b/src/main/java/com/github/skriptdev/skript/api/skript/registration/AssetStoreRegistry.java index 3677b80b..25dc47f8 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/registration/AssetStoreRegistry.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/registration/AssetStoreRegistry.java @@ -12,11 +12,11 @@ /** * Registry related to {@link com.hypixel.hytale.assetstore.AssetStore AssetStores}. * - * @param Asset store type + * @param Asset store type */ -public class AssetStoreRegistry { +public class AssetStoreRegistry { - private final Map assetStoreValues = new TreeMap<>(); + private final Map assetStoreValues = new TreeMap<>(); /** * Create a new {@link TypeRegistrar} with for an {@link com.hypixel.hytale.assetstore.AssetStore AssetStore}. @@ -42,6 +42,7 @@ public static > TypeRegistrar regist assetMap.getAssetMap().forEach((key, value) -> store.assetStoreValues.put(key.toLowerCase(Locale.ROOT), value)); return registration.newType(c, name, pattern) .usage(String.join(", ", store.assetStoreValues.keySet())) + .supplier(() -> store.assetStoreValues.values().stream().iterator()) .literalParser(s -> store.assetStoreValues.get(s.toLowerCase(Locale.ROOT).replace(" ", "_"))) .toStringFunction(Object::toString); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/registration/EnumRegistry.java b/src/main/java/com/github/skriptdev/skript/api/skript/registration/EnumRegistry.java index 8c92e65a..bc1dcd22 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/registration/EnumRegistry.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/registration/EnumRegistry.java @@ -40,6 +40,7 @@ public static > TypeRegistrar register(SkriptRegistration r } return registration.newType(enumClass, name, pattern) .usage(String.join(", ", eEnumRegistry.values.keySet())) + .supplier(() -> eEnumRegistry.values.values().iterator()) .literalParser(s -> eEnumRegistry.values.get(s.toLowerCase(Locale.ROOT).replace(" ", "_"))) .toStringFunction(Enum::name); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/registration/NPCRegistry.java b/src/main/java/com/github/skriptdev/skript/api/skript/registration/NPCRegistry.java index 5a688a4d..b46ed37f 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/registration/NPCRegistry.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/registration/NPCRegistry.java @@ -3,6 +3,7 @@ import com.hypixel.hytale.server.npc.NPCPlugin; import com.hypixel.hytale.server.npc.entities.NPCEntity; +import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.TreeMap; @@ -34,6 +35,10 @@ public static NPCRole getByIndex(int index) { return ROLE_INDEX_MAP.get(index); } + public static Iterator iterator() { + return ROLE_NAME_MAP.values().iterator(); + } + public static String getTypeUsage() { return String.join(", ", ROLE_NAME_MAP.keySet()); } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/registration/SkriptRegistration.java b/src/main/java/com/github/skriptdev/skript/api/skript/registration/SkriptRegistration.java index a573dc6f..39ae7e25 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/registration/SkriptRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/registration/SkriptRegistration.java @@ -1,7 +1,11 @@ package com.github.skriptdev.skript.api.skript.registration; +import com.github.skriptdev.skript.api.utils.Utils; +import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import java.util.List; + /** * An extension of {@link io.github.syst3ms.skriptparser.registration.SkriptRegistration} with additional features. */ @@ -11,4 +15,12 @@ public SkriptRegistration(SkriptAddon registerer) { super(registerer); } + @Override + public List register() { + for (LogEntry logEntry : super.register()) { + Utils.log(null, logEntry); + } + return List.of(); + } + } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java b/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java index 093f9f65..0798ae98 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/variables/JsonVariableStorage.java @@ -5,11 +5,12 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.util.BsonUtil; import io.github.syst3ms.skriptparser.config.Config.ConfigSection; import io.github.syst3ms.skriptparser.log.ErrorType; import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.types.TypeManager; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; import io.github.syst3ms.skriptparser.variables.VariableStorage; import io.github.syst3ms.skriptparser.variables.Variables; import org.bson.BsonBinaryReader; @@ -38,9 +39,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Locale; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; /** * Instance of {@link VariableStorage} that stores variables in a JSON file. @@ -48,7 +50,7 @@ public class JsonVariableStorage extends VariableStorage { public enum Type { - JSON, BSON; + JSON, BSON } private File file; @@ -56,30 +58,28 @@ public enum Type { private BsonDocument bsonDocument; private final AtomicInteger changes = new AtomicInteger(0); private final int changesToSave = 500; - ScheduledFuture schedule; - private final SkriptLogger logger; + private Thread saveThread; public JsonVariableStorage(SkriptLogger logger, String name) { super(logger, name); - this.logger = logger; } @Override protected boolean load(@NotNull ConfigSection section) { String fileType = section.getString("file-type"); if (fileType == null) { - this.logger.error("No 'file-type' specified for database '" + this.name + "'!", ErrorType.EXCEPTION); + Utils.error("No 'file-type' specified for database '" + this.name + "'!", ErrorType.EXCEPTION); return false; } this.type = switch (fileType.toLowerCase(Locale.ROOT)) { case "json" -> Type.JSON; case "bson" -> Type.BSON; default -> { - this.logger.error("Unknown file-type '" + fileType + "' in database '" + this.name + "'", ErrorType.EXCEPTION); + Utils.error("Unknown file-type '" + fileType + "' in database '" + this.name + "'", ErrorType.EXCEPTION); yield null; } }; - this.logger.info("Database '" + this.name + "' loaded with filetype '" + this.type + "'"); + Utils.log("Database '" + this.name + "' loaded with filetype '" + this.type + "'"); return this.type != null; } @@ -90,21 +90,37 @@ protected void allLoaded() { } private void startFileWatcher() { - this.schedule = HytaleServer.SCHEDULED_EXECUTOR.scheduleAtFixedRate(() -> { - if (this.changes.get() >= this.changesToSave) { - try { - saveVariables(false); - this.changes.set(0); - } catch (IOException e) { - this.logger.error("Failed to save variable file", ErrorType.EXCEPTION); - throw new RuntimeException(e); + this.saveThread = new Thread( () -> { + try { + while (!Thread.currentThread().isInterrupted()) { + // Sleep for 5 minutes + TimeUnit.MINUTES.sleep(5); + + // Start checking for variables to save + if (this.changes.get() >= this.changesToSave) { + try { + saveVariables(false); + this.changes.set(0); + } catch (IOException e) { + Utils.error("Failed to save variable file", ErrorType.EXCEPTION); + throw new RuntimeException(e); + } + } } + } catch (InterruptedException e) { + // Restore interrupt status and exit + Thread.currentThread().interrupt(); + Utils.error("Variable Save Thread was interrupted, stopping..."); } - }, 5, 5, TimeUnit.MINUTES); + + },"HySkript-Variable-Save-Thread"); + this.saveThread.setDaemon(true); + this.saveThread.start(); } private void loadVariablesFromFile() { - this.logger.info("Loading variables from file..."); + Utils.log("Loading variables from file..."); + AtomicBoolean markForBackup = new AtomicBoolean(false); try { if (this.type == Type.JSON) { @@ -121,7 +137,7 @@ private void loadVariablesFromFile() { } else { if (!this.bsonDocument.isEmpty() && !this.bsonDocument.containsKey("data")) { // Legacy file format (TODO remove before first release) - this.logger.warn("Your variables file is outdated. HySkript will create a backup then convert for you."); + Utils.warn("Your variables file is outdated. HySkript will create a backup then convert for you."); Files.move(this.file.toPath(), this.file.toPath().resolveSibling(this.file.getName() + ".bak")); variablesDocument = this.bsonDocument.clone(); this.bsonDocument.clear(); @@ -129,9 +145,10 @@ private void loadVariablesFromFile() { } else { variablesDocument = this.bsonDocument.getDocument("variables", new BsonDocument()); } - //this.bsonDocument = new BsonDocument(); } JsonElement jsonElement = BsonUtil.translateBsonToJson(variablesDocument); + AtomicInteger count = new AtomicInteger(); + AtomicLong start = new AtomicLong(System.currentTimeMillis()); if (jsonElement instanceof JsonObject jsonObject) { jsonObject.entrySet().forEach(entry -> { String name = entry.getKey(); @@ -139,17 +156,30 @@ private void loadVariablesFromFile() { String type = value.get("type").getAsString(); JsonElement jsonValue = value.get("value"); if (jsonValue == null) { - this.logger.error("Skipping variable '" + name + "' due to missing value", ErrorType.STRUCTURE_ERROR); + Utils.error("Skipping variable '%s' due to missing value", name); return; } - this.logger.debug("Loading variable '" + name + "' of type '" + type + "' from file. With data '" + jsonValue.toString() + "'"); - loadVariable(name, type, jsonValue); + if (!tryLoadVariable(name, type, jsonValue)) { + markForBackup.set(true); + variablesDocument.remove(name); + } + count.getAndIncrement(); + if (System.currentTimeMillis() - start.get() > 500) { + // If it's taking too long, log progress + start.set(System.currentTimeMillis()); + Utils.log(" - Loaded " + count.get() + " variables so far..."); + } }); + Utils.log("Loaded %s variables from file!", count.get()); + } + if (markForBackup.get()) { + Utils.warn("Failed to load some variables from file. Creating backup..."); + Files.copy(this.file.toPath(), this.file.toPath().resolveSibling(this.file.getName() + ".bak")); } } catch (IOException e) { - this.logger.error("Failed to load variables from file", ErrorType.EXCEPTION); + Utils.error("Failed to load variables from file", ErrorType.EXCEPTION); throw new RuntimeException(e); } } @@ -166,9 +196,9 @@ protected boolean requiresFile() { if (!varFile.exists()) { try { if (varFile.createNewFile()) { - this.logger.info("Created " + fileName + " file!"); + Utils.log("Created " + fileName + " file!"); } else { - this.logger.error("Failed to create " + fileName + " file!", ErrorType.EXCEPTION); + Utils.error("Failed to create " + fileName + " file!", ErrorType.EXCEPTION); } } catch (IOException e) { throw new RuntimeException(e); @@ -234,7 +264,7 @@ public void close() throws IOException { private void saveVariables(boolean finalSave) throws IOException { if (finalSave) { - this.schedule.cancel(true); + this.saveThread.interrupt(); } try { Variables.getLock().lock(); @@ -246,6 +276,39 @@ private void saveVariables(boolean finalSave) throws IOException { } } + @SuppressWarnings("ConstantValue") + private boolean tryLoadVariable(@NotNull String name, @NotNull String type, @NotNull JsonElement value) { + if (value == null || type == null) { // These shouldn't be null, but things happen + Utils.error("value and/or typeName cannot be null"); + return false; + } + Object deserialize = deserialize(name, type, value); + if (deserialize == null) { + return false; + } + Variables.getVariableMap().setVariable(name, deserialize); + return true; + } + + @SuppressWarnings("ConstantValue") + protected Object deserialize(String varName, @NotNull String typeName, @NotNull JsonElement value) { + if (value == null || typeName == null) { + Utils.error("value and/or typeName cannot be null"); + return null; + } + io.github.syst3ms.skriptparser.types.Type type = TypeManager.getByExactName(typeName).orElse(null); + if (type == null) { + Utils.error("Variable '%s' with type '%s' cannot be deserialized. No type registered. This variable will be removed.", varName, typeName); + return null; + } + TypeSerializer serializer = type.getSerializer().orElse(null); + if (serializer == null) { + Utils.error("Variable '%s' cannot be deserialized. The type '%s' has no serializer. This variable will be removed.", varName, typeName); + return null; + } + return serializer.deserialize(this.gson, value); + } + private void readJsonFile() throws IOException { String jsonContent = Files.readString(this.file.toPath()); if (jsonContent.isBlank()) { diff --git a/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java b/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java index 23c02a37..54584b25 100644 --- a/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java +++ b/src/main/java/com/github/skriptdev/skript/api/utils/Utils.java @@ -2,11 +2,14 @@ import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.permissions.PermissionsModule; import com.hypixel.hytale.server.core.receiver.IMessageReceiver; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.Universe; import fi.sulku.hytale.TinyMsg; import io.github.syst3ms.skriptparser.log.LogEntry; -import java.awt.*; +import java.awt.Color; import java.util.logging.Level; /** @@ -83,4 +86,20 @@ public static void warn(IMessageReceiver receiver, String message, Object... arg log(receiver, Level.WARNING, message, args); } + /** + * Log a message to admin players who have the permission "skript.hyskript.admin.messages" + * + * @param level Level of logging + * @param message Message to log + * @param args Arguments for message formatting + */ + public static void logToAdmins(Level level, String message, Object... args) { + PermissionsModule perms = PermissionsModule.get(); + for (PlayerRef player : Universe.get().getPlayers()) { + if (perms.hasPermission(player.getUuid(), "skript.hyskript.admin.messages")) { + log(player, level, message, args); + } + } + } + } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java index 62022aef..1f18166a 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin; import com.github.skriptdev.skript.api.skript.ScriptsLoader; +import com.github.skriptdev.skript.api.skript.addon.AddonLoader; import com.github.skriptdev.skript.api.skript.command.ArgUtils; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.api.skript.variables.JsonVariableStorage; @@ -11,17 +12,15 @@ import io.github.syst3ms.skriptparser.Parser; import io.github.syst3ms.skriptparser.config.Config; import io.github.syst3ms.skriptparser.config.Config.ConfigSection; -import io.github.syst3ms.skriptparser.lang.Structure; -import io.github.syst3ms.skriptparser.log.ErrorType; +import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.SkriptLogger; import io.github.syst3ms.skriptparser.registration.SkriptAddon; -import io.github.syst3ms.skriptparser.registration.SkriptEventInfo; -import io.github.syst3ms.skriptparser.structures.functions.Functions; import io.github.syst3ms.skriptparser.variables.Variables; import org.jetbrains.annotations.NotNull; import java.nio.file.Path; +import java.util.logging.Level; /** * Main class for the Skript aspects of HySkript. @@ -33,52 +32,58 @@ public class Skript extends SkriptAddon { private final HySk hySk; private final Config skriptConfig; private final Path scriptsPath; - private final SkriptLogger logger; private SkriptRegistration registration; private ElementRegistration elementRegistration; + private AddonLoader addonLoader; private ScriptsLoader scriptsLoader; Skript(HySk hySk) { super("HySkript"); + long start = System.currentTimeMillis(); INSTANCE = this; this.hySk = hySk; this.scriptsPath = hySk.getDataDirectory().resolve("scripts"); - this.logger = new SkriptLogger(); - - Path skriptConfigPath = hySk.getDataDirectory().resolve("config.sk"); - this.skriptConfig = new Config(skriptConfigPath, "/config.sk", this.logger); - this.logger.setDebug(this.skriptConfig.getBoolean("debug")); + Utils.log(" "); Utils.log("Setting up HySkript!"); - setup(); - this.logger.finalizeLogs(); - for (LogEntry logEntry : this.logger.close()) { + Utils.log(" "); + + // LOAD CONFIG + Path skriptConfigPath = hySk.getDataDirectory().resolve("config.sk"); + SkriptLogger logger = new SkriptLogger(); + this.skriptConfig = new Config(skriptConfigPath, "/config.sk", logger); + logger.finalizeLogs(); + for (LogEntry logEntry : logger.close()) { Utils.log(null, logEntry); } - Utils.log("HySkript setup complete!"); + + // SETUP SKRIPT + setup(); + + // ALL DONE + Utils.log(" "); + long fin = System.currentTimeMillis() - start; + Utils.log("HySkript loading completed in %sms!", fin); + Utils.log(" "); } private void setup() { - ReflectionUtils.init(); - ArgUtils.init(); + long start = System.currentTimeMillis(); + preSetup(); this.registration = new SkriptRegistration(this); this.elementRegistration = new ElementRegistration(this.registration); this.elementRegistration.registerElements(); - ConfigSection effectCommandSection = this.skriptConfig.getConfigSection("effect-commands"); - if (effectCommandSection != null) { - if (effectCommandSection.getBoolean("enabled")) { - EffectCommands.register(this, - effectCommandSection.getString("token"), - effectCommandSection.getBoolean("allow-ops"), - effectCommandSection.getString("required-permission")); - } - } - // FINALIZE SETUP - this.registration.register(); + // SETUP EFFECT COMMANDS + setupEffectCommands(); + + // FINISH SETUP + long fin = System.currentTimeMillis() - start; + Utils.log("HySkript setup completed in %sms!", fin); - printSyntaxCount(); - Utils.log("HySkript setup complete!"); + // LOAD ADDONS + this.addonLoader = new AddonLoader(); + this.addonLoader.loadAddonsFromFolder(); // LOAD VARIABLES loadVariables(); @@ -91,59 +96,78 @@ private void setup() { Parser.getMainRegistration().getRegisterer().finishedLoading(); } + private void preSetup() { + ReflectionUtils.init(); + ArgUtils.init(); + + // Set up how HySkript handles IllegalStateExceptions + Statement.setIllegalStateHandler(e -> { + if (e.getMessage().contains("Assert not in thread!")) { + boolean failedInCommand = false; + for (StackTraceElement ste : e.getStackTrace()) { + if (ste.getClassName().contains("ScriptCommandBuilder")) { + failedInCommand = true; + break; + } + } + if (failedInCommand) { + Utils.logToAdmins(Level.WARNING, "A command was executed on the wrong thread, see console for more info."); + Utils.error("A command was executed on the wrong thread!"); + Utils.warn("If you have a regular/global command that a player is running, which executes code in a world, consider:"); + Utils.warn(" - Using the 'player command' or 'world command' command types."); + Utils.warn(" - Using 'execute in %world%' section."); + Utils.error("Original error message: %s", e.getMessage()); + } else { + Utils.logToAdmins(Level.WARNING, "Something was executed on the wrong thread."); + if (e.getMessage().contains("World")) { + Utils.error("A world was accessed on the wrong thread!"); + Utils.warn("Consider using 'execute in %world%' section."); + Utils.error("Original error message: %s", e.getMessage()); + } + } + } + }); + } + public void shutdown() { + // SHUTDOWN VARIABLES Utils.log("Saving variables..."); Variables.shutdown(); Utils.log("Variable saving complete!"); - } - private void printSyntaxCount() { - var mainRegistration = Parser.getMainRegistration(); + // SHUTDOWN ADDONS + this.addonLoader.shutdownAddons(); + } - int structureSize = 0; - int eventSize = 0; - for (SkriptEventInfo event : this.registration.getEvents()) { - if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { - structureSize++; - } else { - eventSize++; - } - } - for (SkriptEventInfo event : mainRegistration.getEvents()) { - if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { - structureSize++; - } else { - eventSize++; + private void setupEffectCommands() { + ConfigSection effectCommandSection = this.skriptConfig.getConfigSection("effect-commands"); + if (effectCommandSection != null) { + if (effectCommandSection.getBoolean("enabled")) { + EffectCommands.register(this, + effectCommandSection.getString("token"), + effectCommandSection.getBoolean("allow-ops"), + effectCommandSection.getString("required-permission")); } } - int effectSize = this.registration.getEffects().size() + mainRegistration.getEffects().size(); - int expsSize = this.registration.getExpressions().size() + mainRegistration.getExpressions().size(); - int secSize = this.registration.getSections().size() + mainRegistration.getSections().size(); - int typeSize = this.registration.getTypes().size() + mainRegistration.getTypes().size(); - int funcSize = Functions.getAllFunctions().size(); - - int total = structureSize + eventSize + effectSize + expsSize + secSize + typeSize + funcSize; - - Utils.log("Loaded HySkript %s elements:", total); - Utils.log("- Types: %s", typeSize); - Utils.log("- Structures: %s", structureSize); - Utils.log("- Events: %s ", eventSize); - Utils.log("- Effects: %s", effectSize); - Utils.log("- Expressions: %s", expsSize); - Utils.log("- Sections: %s", secSize); - Utils.log("- Functions: %s", funcSize); } private void loadVariables() { + long start = System.currentTimeMillis(); Utils.log("Loading variables..."); Variables.registerStorage(JsonVariableStorage.class, "json-database"); ConfigSection databases = this.skriptConfig.getConfigSection("databases"); if (databases == null) { - this.logger.error("Databases section not found in config.sk", ErrorType.STRUCTURE_ERROR); + Utils.error("Databases section not found in config.sk"); return; } - Variables.load(this.logger, databases); - Utils.log("Finished loading variables!"); + SkriptLogger skriptLogger = new SkriptLogger(); + Variables.load(skriptLogger, databases); + skriptLogger.finalizeLogs(); + for (LogEntry logEntry : skriptLogger.close()) { + Utils.log(null, logEntry); + } + long fin = System.currentTimeMillis() - start; + Utils.log("Finished loading variables in %sms!", fin); } /** @@ -173,10 +197,6 @@ private void loadVariables() { return this.scriptsPath; } - public SkriptLogger getLogger() { - return this.logger; - } - /** * Get the registration for Skript elements. * @@ -194,4 +214,13 @@ public ScriptsLoader getScriptsLoader() { return this.scriptsLoader; } + /** + * Get an instance of Skript. + * + * @return Instance of Skript. + */ + public static Skript getInstance() { + return INSTANCE; + } + } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java index 1e50b08d..b38748aa 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/command/SkriptCommand.java @@ -1,5 +1,6 @@ package com.github.skriptdev.skript.plugin.command; +import com.github.skriptdev.skript.api.skript.addon.HySkriptAddon; import com.github.skriptdev.skript.api.skript.docs.JsonDocPrinter; import com.github.skriptdev.skript.api.skript.docs.MarkdownDocPrinter; import com.github.skriptdev.skript.api.utils.Utils; @@ -10,6 +11,7 @@ import com.hypixel.hytale.server.core.command.system.AbstractCommand; import com.hypixel.hytale.server.core.command.system.CommandContext; import com.hypixel.hytale.server.core.command.system.CommandRegistry; +import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.arguments.system.FlagArg; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; @@ -39,6 +41,7 @@ public SkriptCommand(CommandRegistry registry) { addAliases("sk"); // Keep these in alphabetical order + addSubCommand(addonsCommand()); addSubCommand(new DocsCommand()); addSubCommand(infoCommand()); addSubCommand(new ReloadCommand()); @@ -109,7 +112,9 @@ protected JsonDocsCommand() { protected @Nullable CompletableFuture execute(@NotNull CommandContext commandContext) { if (this.all.provided(commandContext)) { return CompletableFuture.runAsync(() -> { - for (SkriptAddon addon : SkriptAddon.getAddons().stream().filter(addon -> !addon.getAddonName().equalsIgnoreCase("skript-parser")).toList()) { + for (SkriptAddon addon : SkriptAddon.getAddons().stream() + .filter(addon -> !addon.getAddonName().equalsIgnoreCase("skript-parser")) + .toList()) { JsonDocPrinter jsonDocPrinter = new JsonDocPrinter(commandContext.sender(), addon); jsonDocPrinter.printDocs(); } @@ -139,6 +144,31 @@ protected CompletableFuture execute(@NotNull CommandContext commandContext }; } + private AbstractCommand addonsCommand() { + return new AbstractCommand("addons", "Get info about loaded addons.") { + @Override + protected CompletableFuture execute(@NotNull CommandContext commandContext) { + return CompletableFuture.runAsync(() -> { + List addons = SkriptAddon.getAddons().stream() + .filter(addon -> !addon.getAddonName().equalsIgnoreCase("skript-parser") && + !addon.getAddonName().equalsIgnoreCase("HySkript")) + .toList(); + if (addons.isEmpty()) return; + CommandSender sender = commandContext.sender(); + Utils.sendMessage(sender, "Loaded Addons:"); + addons.forEach(addon -> { + if (addon instanceof HySkriptAddon hySkriptAddon) { + Utils.sendMessage(sender, " - %s:", hySkriptAddon.getAddonName()); + for (Message s : hySkriptAddon.getInfo()) { + sender.sendMessage(Message.raw(" ").insert(s)); + } + } + }); + }); + } + }; + } + private void printInfo(IMessageReceiver sender) { Utils.sendMessage(sender, "HySkript Version: %s", HySk.getInstance().getManifest().getVersion()); Utils.sendMessage(sender, "Hytale Version: %s (%s)", ManifestUtil.getImplementationVersion(), ManifestUtil.getPatchline()); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java index 5bb6c578..ac48ae54 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.elements; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import com.github.skriptdev.skript.plugin.elements.command.ScriptSubCommand; import com.github.skriptdev.skript.plugin.elements.types.DefaultComparators; @@ -13,6 +14,9 @@ import com.github.skriptdev.skript.plugin.elements.sections.SectionHandler; import com.github.skriptdev.skript.plugin.elements.types.Types; import io.github.syst3ms.skriptparser.Parser; +import io.github.syst3ms.skriptparser.lang.Structure; +import io.github.syst3ms.skriptparser.registration.SkriptEventInfo; +import io.github.syst3ms.skriptparser.structures.functions.Functions; public class ElementRegistration { @@ -23,6 +27,9 @@ public ElementRegistration(SkriptRegistration registration) { } public void registerElements() { + Utils.log("Loading HySkript elements..."); + + // INITIALIZE BASE SKRIPT-PARSER Parser.init(new String[0], new String[0], new String[0], true); // TYPES @@ -53,6 +60,48 @@ public void registerElements() { // COMMAND ScriptCommand.register(this.registration); ScriptSubCommand.register(this.registration); + + // FINALIZE SETUP + this.registration.register(); + + printSyntaxCount(); + } + + private void printSyntaxCount() { + var mainRegistration = Parser.getMainRegistration(); + + int structureSize = 0; + int eventSize = 0; + for (SkriptEventInfo event : this.registration.getEvents()) { + if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { + structureSize++; + } else { + eventSize++; + } + } + for (SkriptEventInfo event : mainRegistration.getEvents()) { + if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { + structureSize++; + } else { + eventSize++; + } + } + int effectSize = this.registration.getEffects().size() + mainRegistration.getEffects().size(); + int expsSize = this.registration.getExpressions().size() + mainRegistration.getExpressions().size(); + int secSize = this.registration.getSections().size() + mainRegistration.getSections().size(); + int typeSize = this.registration.getTypes().size() + mainRegistration.getTypes().size(); + int funcSize = Functions.getAllFunctions().size(); + + int total = structureSize + eventSize + effectSize + expsSize + secSize + typeSize + funcSize; + + Utils.log("Loaded %s HySkript elements:", total); + Utils.log("- Types: %s", typeSize); + Utils.log("- Structures: %s", structureSize); + Utils.log("- Events: %s ", eventSize); + Utils.log("- Effects: %s", effectSize); + Utils.log("- Expressions: %s", expsSize); + Utils.log("- Sections: %s", secSize); + Utils.log("- Functions: %s", funcSize); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java index 668b07d9..e8c95b26 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptCommand.java @@ -10,7 +10,6 @@ import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.Structure; import io.github.syst3ms.skriptparser.lang.TriggerContext; -import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; import io.github.syst3ms.skriptparser.log.SkriptLogger; import io.github.syst3ms.skriptparser.parsing.ParseContext; import io.github.syst3ms.skriptparser.parsing.ParserState; @@ -45,17 +44,17 @@ public String getCommand() { return this.command; } - public CommandSender[] getSender() { - return new CommandSender[]{this.sender}; + public CommandSender getSender() { + return this.sender; } - public World[] getWorld() { - return new World[]{this.world}; + public World getWorld() { + return this.world; } - public Player[] getPlayer() { - if (this.player == null && this.sender instanceof Player p) return new Player[]{p}; - return new Player[]{this.player}; + public Player getPlayer() { + if (this.player == null && this.sender instanceof Player p) return p; + return this.player; } @Override @@ -120,23 +119,23 @@ public static void register(SkriptRegistration registration) { .since("1.0.0") .register(); - registration.newContextValue(ScriptCommandContext.class, Player.class, true, + registration.newSingleContextValue(ScriptCommandContext.class, Player.class, "player", ScriptCommandContext::getPlayer) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); - registration.newContextValue(ScriptCommandContext.class, CommandSender.class, true, + registration.newSingleContextValue(ScriptCommandContext.class, CommandSender.class, "sender", ScriptCommandContext::getSender) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); - registration.newContextValue(ScriptCommandContext.class, World.class, true, + registration.newSingleContextValue(ScriptCommandContext.class, World.class, "world", ScriptCommandContext::getWorld) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); - registration.newContextValue(ScriptCommandContext.class, String.class, true, - "command", ct -> new String[]{ct.getCommand()}) + registration.newSingleContextValue(ScriptCommandContext.class, String.class, + "command", ScriptCommandContext::getCommand) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java index e7db02e5..4d7e02f2 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/EffectHandler.java @@ -3,6 +3,7 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.effects.block.EffBreakBlock; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffDropItem; +import com.github.skriptdev.skript.plugin.elements.effects.entity.EffEntityEffect; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffKill; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffSpawnEntity; import com.github.skriptdev.skript.plugin.elements.effects.other.EffSendMessage; @@ -22,6 +23,7 @@ public static void register(SkriptRegistration registration) { // ENTITY EffDropItem.register(registration); + EffEntityEffect.register(registration); EffKill.register(registration); EffSpawnEntity.register(registration); EffTeleport.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffEntityEffect.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffEntityEffect.java new file mode 100644 index 00000000..c7205cc2 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffEntityEffect.java @@ -0,0 +1,89 @@ +package com.github.skriptdev.skript.plugin.elements.effects.entity; + +import com.github.skriptdev.skript.api.hytale.EntityComponentUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.OverlapBehavior; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Effect; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.time.Duration; + +public class EffEntityEffect extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffEntityEffect.class, + "apply [entity[ ]effect] %entityeffect% to %livingentities% for %duration% [to (:extend|:overwrite)]", + "apply infinite [entity[ ]effect] %entityeffect% to %livingentities%") + .name("Apply Entity Effect") + .description("Apply an entity effect to an entity for a given duration.", + "You have the option to extend or overwrite any current effect (Default will ignore).", + "You can also apply an infinite effect, which will last forever.") + .examples("apply freeze to player for 1 minute", + "apply infinite entity effect freeze to all players") + .since("INSERT VERSION") + .register(); + } + + private Expression effects; + private Expression entities; + private Expression duration; + private OverlapBehavior behavior; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.effects = (Expression) expressions[0]; + this.entities = (Expression) expressions[1]; + if (matchedPattern == 0) { + this.duration = (Expression) expressions[2]; + } + this.behavior = parseContext.hasMark("extend") ? OverlapBehavior.EXTEND : + parseContext.hasMark("overwrite") ? OverlapBehavior.OVERWRITE : OverlapBehavior.IGNORE; + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + Duration duration = this.duration.getSingle(ctx).orElse(null); + + EntityEffect entityEffect = this.effects.getSingle(ctx).orElse(null); + if (entityEffect == null) return; + int index = EntityEffect.getAssetMap().getIndex(entityEffect.getId()); + + for (LivingEntity entity : this.entities.getArray(ctx)) { + EffectControllerComponent component = EntityComponentUtils.getComponent(entity, EffectControllerComponent.getComponentType()); + if (component == null) continue; + + Ref reference = entity.getReference(); + if (reference == null) continue; + + Store store = reference.getStore(); + if (duration == null) { + component.addInfiniteEffect(reference, index, entityEffect, store); + } else { + float seconds = Math.max(duration.toSeconds(), 0); + component.addEffect(reference, entityEffect, seconds, this.behavior, store); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + if (this.duration == null) { + return "apply infinite entity effect " + this.effects.toString(ctx, debug) + " to " + this.entities.toString(ctx, debug); + } + String behavior = " to " + this.behavior.name(); + return "apply entity effect " + this.effects.toString(ctx, debug) + " to " + this.entities.toString(ctx, debug) + + " for " + this.duration.toString(ctx, debug) + behavior; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffKill.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffKill.java index c5b3acc5..20cb1632 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffKill.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffKill.java @@ -43,6 +43,7 @@ public boolean init(Expression[] expressions, int matchedPattern, @NotNull Pa return true; } + @SuppressWarnings("deprecation") @Override protected void execute(@NotNull TriggerContext ctx) { DamageCause cause = DamageCause.COMMAND; @@ -52,6 +53,7 @@ protected void execute(@NotNull TriggerContext ctx) { } if (cause == null) cause = DamageCause.COMMAND; + assert cause != null; Damage damage = new Damage(Damage.NULL_SOURCE, cause, 1000.0f); for (Entity entity : this.entities.getArray(ctx)) { killEntity(entity, damage); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendMessage.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendMessage.java index ec43e251..78927a5c 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendMessage.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendMessage.java @@ -19,7 +19,9 @@ public static void register(SkriptRegistration registration) { registration.newEffect(EffSendMessage.class, "send [message[s]] %objects% [to %-messagereceivers%]") .name("Send Message") - .description("Sends a message to a command sender such as a player or the console.") + .description("Sends a message to a command sender such as a player or the console.", + "See [Message Format](https://github.com/SkriptDev/HySkript/wiki/Tutorial-Message-Format) on the wiki" + + "for info about formatting messages.") .examples("send \"Welcome to the server\" to player") .since("1.0.0") .register(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java index 5f8363fe..a04346eb 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/EventHandler.java @@ -4,6 +4,7 @@ import com.github.skriptdev.skript.api.hytale.Block; import com.github.skriptdev.skript.api.skript.event.BlockContext; import com.github.skriptdev.skript.api.skript.event.PlayerContext; +import com.github.skriptdev.skript.api.skript.event.PlayerRefContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.events.entity.EvtEntityDamage; import com.github.skriptdev.skript.plugin.elements.events.entity.EvtEntityDeath; @@ -12,6 +13,7 @@ import com.github.skriptdev.skript.plugin.elements.events.entity.EvtLivingEntityInvChange; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerAddToWorld; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerBreakBlock; +import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerChangeGameMode; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerChat; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDamageBlock; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDrainFromWorld; @@ -28,6 +30,7 @@ import com.github.skriptdev.skript.plugin.elements.events.server.EvtShutdown; import com.github.skriptdev.skript.plugin.elements.events.skript.EvtLoad; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; public class EventHandler { @@ -43,6 +46,7 @@ public static void register(SkriptRegistration registration) { // PLAYER EvtPlayerAddToWorld.register(registration); EvtPlayerBreakBlock.register(registration); + EvtPlayerChangeGameMode.register(registration); EvtPlayerChat.register(registration); EvtPlayerDamageBlock.register(registration); EvtPlayerDrainFromWorld.register(registration); @@ -68,11 +72,17 @@ public static void register(SkriptRegistration registration) { } private static void registerGlobalContexts(SkriptRegistration reg) { - reg.newSingleContextValue(BlockContext.class, Block.class, "block", BlockContext::getBlock) + reg.newSingleContextValue(BlockContext.class, Block.class, + "block", BlockContext::getBlock) .register(); - reg.newSingleContextValue(PlayerContext.class, Player.class, "player", PlayerContext::getPlayer) + reg.newSingleContextValue(PlayerContext.class, Player.class, + "player", PlayerContext::getPlayer) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); + reg.addSingleContextValue(PlayerRefContext.class, PlayerRef.class, + "playerref", PlayerRefContext::getPlayerRef); + reg.addSingleContextValue(PlayerRefContext.class, PlayerRef.class, + "player-ref", PlayerRefContext::getPlayerRef); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDamage.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDamage.java index 315a5635..d5b4babe 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDamage.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDamage.java @@ -114,7 +114,9 @@ private static class EntityDamageSystem extends DamageSystems.ApplyDamage { @SuppressWarnings("DataFlowIssue") @Override - public void handle(int index, @NotNull ArchetypeChunk archetypeChunk, @NotNull Store store, @NotNull CommandBuffer commandBuffer, @NotNull Damage damage) { + public void handle(int index, @NotNull ArchetypeChunk archetypeChunk, + @NotNull Store store, @NotNull CommandBuffer commandBuffer, + @NotNull Damage damage) { Ref ref = archetypeChunk.getReferenceTo(index); NPCEntity npc = store.getComponent(ref, NPCEntity.getComponentType()); Player player = store.getComponent(ref, Player.getComponentType()); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java index 9ba2c1b8..316b09a5 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityDeath.java @@ -28,8 +28,8 @@ public class EvtEntityDeath extends SkriptEvent { - public static void register(SkriptRegistration registration) { - registration.newEvent(EvtEntityDeath.class, "death", "death of player", "death of npc") + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtEntityDeath.class, "death", "death of player", "death of npc") .setHandledContexts(EntityDeathContext.class) .name("Entity Death") .description("Called when an entity dies.", @@ -45,20 +45,20 @@ public static void register(SkriptRegistration registration) { .since("1.0.0") .register(); - registration.addContextValue(EntityDeathContext.class, - Entity.class, true, "victim", EntityDeathContext::getVictim); - registration.addContextValue(EntityDeathContext.class, - Entity.class, true, "attacker", EntityDeathContext::getAttacker); - registration.addContextValue(EntityDeathContext.class, - Damage.Source.class, true, "damage-source", EntityDeathContext::getDamageSource); - registration.addContextValue(EntityDeathContext.class, - DamageCause.class, true, "death-cause", EntityDeathContext::getDamageCause); - registration.addContextValue(EntityDeathContext.class, - Damage.class, true, "death-info", EntityDeathContext::getDamage); - registration.addContextValue(EntityDeathContext.class, - Item.class, false, "lost-items", EntityDeathContext::getItemsLostOnDeath); - registration.addContextValue(EntityDeathContext.class, - ItemStack.class, false, "lost-itemstacks", EntityDeathContext::getItemStacksLostOnDeath); + reg.addSingleContextValue(EntityDeathContext.class, + Entity.class, "victim", EntityDeathContext::getVictim); + reg.addSingleContextValue(EntityDeathContext.class, + Entity.class, "attacker", EntityDeathContext::getAttacker); + reg.addSingleContextValue(EntityDeathContext.class, + Damage.Source.class, "damage-source", EntityDeathContext::getDamageSource); + reg.addSingleContextValue(EntityDeathContext.class, + DamageCause.class, "death-cause", EntityDeathContext::getDamageCause); + reg.addSingleContextValue(EntityDeathContext.class, + Damage.class, "death-info", EntityDeathContext::getDamage); + reg.addListContextValue(EntityDeathContext.class, + Item.class, "lost-items", EntityDeathContext::getItemsLostOnDeath); + reg.addListContextValue(EntityDeathContext.class, + ItemStack.class, "lost-itemstacks", EntityDeathContext::getItemStacksLostOnDeath); } private static EntityDeathListener LISTENER; @@ -94,7 +94,8 @@ private static class EntityDeathListener extends DeathSystems.OnDeathSystem { @SuppressWarnings("DataFlowIssue") @Override - public void onComponentAdded(@NotNull Ref ref, @NotNull DeathComponent deathComponent, @NotNull Store store, @NotNull CommandBuffer buffer) { + public void onComponentAdded(@NotNull Ref ref, @NotNull DeathComponent deathComponent, + @NotNull Store store, @NotNull CommandBuffer buffer) { NPCEntity npc = buffer.getComponent(ref, NPCEntity.getComponentType()); Player player = buffer.getComponent(ref, Player.getComponentType()); @@ -120,11 +121,11 @@ public void onComponentAdded(@NotNull Ref ref, @NotNull DeathCompon @SuppressWarnings("DataFlowIssue") private record EntityDeathContext(int pattern, Entity victim, DeathComponent component) implements TriggerContext { - public Entity[] getVictim() { - return new Entity[]{this.victim}; + public Entity getVictim() { + return this.victim; } - public Entity[] getAttacker() { + public Entity getAttacker() { Entity attacker = null; if (this.component.getDeathInfo().getSource() instanceof Damage.EntitySource entitySource) { Ref attackerRef = entitySource.getRef(); @@ -137,19 +138,19 @@ public Entity[] getAttacker() { if (npc != null) attacker = npc; } } - return new Entity[]{attacker}; + return attacker; } - public Damage.Source[] getDamageSource() { - return new Damage.Source[]{this.component.getDeathInfo().getSource()}; + public Damage.Source getDamageSource() { + return this.component.getDeathInfo().getSource(); } - public DamageCause[] getDamageCause() { - return new DamageCause[]{this.component.getDeathCause()}; + public DamageCause getDamageCause() { + return this.component.getDeathCause(); } - public Damage[] getDamage() { - return new Damage[]{this.component.getDeathInfo()}; + public Damage getDamage() { + return this.component.getDeathInfo(); } public Item[] getItemsLostOnDeath() { diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityRemove.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityRemove.java index c4f7a562..dcc6826c 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityRemove.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityRemove.java @@ -25,8 +25,8 @@ public static void register(SkriptRegistration reg) { .since("1.0.0") .setHandledContexts(EntityRemoveEventContext.class) .register(); - reg.addContextValue(EntityRemoveEventContext.class, Entity.class, true, "entity", EntityRemoveEventContext::getEntity); - reg.addContextValue(EntityRemoveEventContext.class, World.class, true, "world", EntityRemoveEventContext::getWorld); + reg.addSingleContextValue(EntityRemoveEventContext.class, Entity.class, "entity", EntityRemoveEventContext::getEntity); + reg.addSingleContextValue(EntityRemoveEventContext.class, World.class, "world", EntityRemoveEventContext::getWorld); } private static EventRegistration LISTENER; @@ -60,12 +60,12 @@ public String getName() { return "entity remove context"; } - private Entity[] getEntity() { - return new Entity[]{this.event.getEntity()}; + private Entity getEntity() { + return this.event.getEntity(); } - private World[] getWorld() { - return new World[]{this.event.getEntity().getWorld()}; + private World getWorld() { + return getEntity().getWorld(); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtLivingEntityInvChange.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtLivingEntityInvChange.java index 2bec849f..1ec7f4e7 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtLivingEntityInvChange.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtLivingEntityInvChange.java @@ -5,6 +5,7 @@ import com.hypixel.hytale.event.EventRegistration; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.event.events.entity.LivingEntityInventoryChangeEvent; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.TriggerContext; @@ -24,8 +25,9 @@ public static void register(SkriptRegistration reg) { .setHandledContexts(InvChangeContext.class) .register(); - reg.addContextValue(InvChangeContext.class, Entity.class, true, "entity", InvChangeContext::getEntity); - // TODO more contexts for this event + reg.addSingleContextValue(InvChangeContext.class, Entity.class, "entity", InvChangeContext::getEntity); + reg.addSingleContextValue(InvChangeContext.class, ItemContainer.class, "item-container", InvChangeContext::getContainer); + // TODO add transaction } private static EventRegistration LISTENER; @@ -51,10 +53,14 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "living entity inventory change"; } - private static record InvChangeContext(LivingEntityInventoryChangeEvent event) implements TriggerContext { + private record InvChangeContext(LivingEntityInventoryChangeEvent event) implements TriggerContext { - private Entity[] getEntity() { - return new Entity[]{this.event.getEntity()}; + private Entity getEntity() { + return this.event.getEntity(); + } + + private ItemContainer getContainer() { + return this.event.getItemContainer(); } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChangeGameMode.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChangeGameMode.java new file mode 100644 index 00000000..d4b5f50b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChangeGameMode.java @@ -0,0 +1,126 @@ +package com.github.skriptdev.skript.plugin.elements.events.player; + +import com.github.skriptdev.skript.api.skript.event.CancellableContext; +import com.github.skriptdev.skript.api.skript.event.PlayerContext; +import com.github.skriptdev.skript.api.skript.event.SystemEvent; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.event.events.ecs.ChangeGameModeEvent; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.TriggerMap; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.registration.context.ContextValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class EvtPlayerChangeGameMode extends SystemEvent> { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtPlayerChangeGameMode.class, "[player(s| )] game[(-| )]mode change[s]") + .setHandledContexts(PlayerChangeGameModeContext.class) + .name("Player Change GameMode") + .description("Called when a player's game-mode is changed.") + .since("INSERT VERSION") + .register(); + + reg.newSingleContextValue(PlayerChangeGameModeContext.class, GameMode.class, "game[(-| )]mode", PlayerChangeGameModeContext::getOldGameMode) + .setState(ContextValue.State.PAST) + .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) + .register(); + reg.newSingleContextValue(PlayerChangeGameModeContext.class, GameMode.class, "game[(-| )]mode", PlayerChangeGameModeContext::getNewGameMode) + .setState(ContextValue.State.PRESENT) + .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) + .addSetter(PlayerChangeGameModeContext::setNewGameMode) + .register(); + } + + private static ChangeGameModeEventSystem SYSTEM; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + if (SYSTEM == null) { + SYSTEM = new ChangeGameModeEventSystem(ChangeGameModeEvent.class); + applySystem(SYSTEM); + } + return true; + } + + @Override + public boolean check(@NotNull TriggerContext triggerContext) { + return triggerContext instanceof PlayerChangeGameModeContext; + } + + @Override + public String toString(@Nullable TriggerContext triggerContext, boolean debug) { + return "player change gamemode"; + } + + private record PlayerChangeGameModeContext(ChangeGameModeEvent event, Player player) + implements PlayerContext, CancellableContext { + + public Player getPlayer() { + return this.player; + } + + public GameMode getNewGameMode() { + return event.getGameMode(); + } + + public GameMode getOldGameMode() { + return getPlayer().getGameMode(); + } + + public void setNewGameMode(GameMode gameMode) { + event.setGameMode(gameMode); + } + + @Override + public String getName() { + return "player change gamemode context"; + } + + @Override + public boolean isCancelled() { + return event.isCancelled(); + } + + @Override + public void setCancelled(boolean cancelled) { + event.setCancelled(cancelled); + } + } + + private static final class ChangeGameModeEventSystem extends EntityEventSystem { + + private ChangeGameModeEventSystem(@NotNull Class eventType) { + super(eventType); + } + + @Override + public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, @NotNull Store store, + @NotNull CommandBuffer commandBuffer, @NotNull ChangeGameModeEvent event) { + + Ref ref = archetypeChunk.getReferenceTo(i); + Player player = store.getComponent(ref, Player.getComponentType()); + if (player == null) return; + + TriggerMap.callTriggersByContext(new PlayerChangeGameModeContext(event, player)); + } + + @Override + public @NotNull Query getQuery() { + return PlayerRef.getComponentType(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDrainFromWorld.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDrainFromWorld.java index b2bccb95..c2b63fd6 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDrainFromWorld.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDrainFromWorld.java @@ -21,7 +21,9 @@ public static void register(SkriptRegistration reg) { .description("Really not sure...") // TODO put real docs .since("1.0.0") .register(); - reg.addContextValue(DrainContext.class, World.class, true, "world", DrainContext::getWorld); + + reg.addSingleContextValue(DrainContext.class, World.class, + "world", DrainContext::getWorld); } private static EventRegistration LISTENER; @@ -49,8 +51,8 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { private record DrainContext(DrainPlayerFromWorldEvent event) implements TriggerContext { - public World[] getWorld() { - return new World[]{this.event.getWorld()}; + public World getWorld() { + return this.event.getWorld(); } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerJoin.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerJoin.java index ea4c61d1..07bc037d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerJoin.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerJoin.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.elements.events.player; import com.github.skriptdev.skript.api.skript.event.PlayerContext; +import com.github.skriptdev.skript.api.skript.event.PlayerRefContext; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.event.EventRegistration; import com.hypixel.hytale.event.EventRegistry; @@ -8,6 +9,7 @@ import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent; +import com.hypixel.hytale.server.core.universe.PlayerRef; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.TriggerMap; @@ -19,12 +21,12 @@ public class EvtPlayerJoin extends SkriptEvent { - public static void register(SkriptRegistration registration) { - registration.newEvent(EvtPlayerJoin.class, "player connect", "player ready", "player quit") + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtPlayerJoin.class, "player connect", "player ready", "player quit") .name("Player Join/Quit") .description("Events triggered when a player joins or quits the server.") .since("1.0.0") - .setHandledContexts(PlayerEventContext.class) + .setHandledContexts(PlayerConnectContext.class) .register(); } @@ -33,6 +35,7 @@ public static void register(SkriptRegistration registration) { private static EventRegistration DISCONNECT_LISTENER; private int pattern; + @SuppressWarnings("removal") @Override public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { this.pattern = matchedPattern; @@ -41,17 +44,20 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, @ if (CONNECT_LISTENER == null) { CONNECT_LISTENER = registry.register(PlayerConnectEvent.class, playerConnectEvent -> { Player player = playerConnectEvent.getHolder().getComponent(Player.getComponentType()); - TriggerMap.callTriggersByContext(new PlayerEventContext(player, 0)); + TriggerMap.callTriggersByContext(new PlayerConnectContext(playerConnectEvent.getPlayerRef(), player, 0)); }); } if (READY_LISTENER == null) { READY_LISTENER = registry.registerGlobal(PlayerReadyEvent.class, playerReadyEvent -> { Player player = playerReadyEvent.getPlayer(); - TriggerMap.callTriggersByContext(new PlayerEventContext(player, 1)); + TriggerMap.callTriggersByContext(new PlayerConnectContext(player.getPlayerRef(), player, 1)); }); } if (DISCONNECT_LISTENER == null) { DISCONNECT_LISTENER = registry.register(PlayerDisconnectEvent.class, playerDisconnectEvent -> { + PlayerRef playerRef = playerDisconnectEvent.getPlayerRef(); + PlayerConnectContext playerConnectContext = new PlayerConnectContext(playerRef, null, 2); + TriggerMap.callTriggersByContext(playerConnectContext); }); } return true; @@ -59,9 +65,8 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, @ @Override public boolean check(@NotNull TriggerContext ctx) { - if (!(ctx instanceof PlayerEventContext playerEventContext)) return false; - if (this.pattern != playerEventContext.getPattern()) return false; - return true; + if (!(ctx instanceof PlayerConnectContext playerConnectContext)) return false; + return this.pattern == playerConnectContext.getPattern(); } public int getPattern() { @@ -79,16 +84,24 @@ public String toString(@Nullable TriggerContext ctx, boolean debug) { return "player " + t; } - public static class PlayerEventContext implements PlayerContext { + public static class PlayerConnectContext implements PlayerContext, PlayerRefContext { + private final PlayerRef playerRef; private final Player player; private final int pattern; - public PlayerEventContext(Player player, int pattern) { + public PlayerConnectContext(PlayerRef playerRef, Player player, int pattern) { + this.playerRef = playerRef; this.player = player; this.pattern = pattern; } + @Override + public PlayerRef getPlayerRef() { + return this.playerRef; + } + + @Override public Player getPlayer() { return this.player; } @@ -99,7 +112,7 @@ public int getPattern() { @Override public String getName() { - return "player-join"; + return "player-connect-context"; } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java index 2fbdc0f2..0bc5eeb2 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseClick.java @@ -36,7 +36,7 @@ public static void register(SkriptRegistration reg) { reg.addSingleContextValue(MouseClickContext.class, Entity.class, "target-entity", MouseClickContext::getTargetEntity); reg.addSingleContextValue(MouseClickContext.class, Vector3i.class, "target-block", MouseClickContext::getTargetBlock); reg.addSingleContextValue(MouseClickContext.class, Vector2f.class, "screen-point", MouseClickContext::getScreenPoint); - reg.addSingleContextValue(MouseClickContext.class, MouseButtonEvent.class, "mouse-button", MouseClickContext::getMouseButton); + //reg.addSingleContextValue(MouseClickContext.class, MouseButtonEvent.class, "mouse-button", MouseClickContext::getMouseButton); } private static EventRegistration LISTENER; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java index 52bb6f0b..32d5b949 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerMouseMove.java @@ -34,7 +34,7 @@ public static void register(SkriptRegistration reg) { reg.addSingleContextValue(MouseMoveContext.class, Entity.class, "target-entity", MouseMoveContext::getTargetEntity); reg.addSingleContextValue(MouseMoveContext.class, Vector3i.class, "target-block", MouseMoveContext::getTargetBlock); reg.addSingleContextValue(MouseMoveContext.class, Vector2f.class, "screen-point", MouseMoveContext::getScreenPoint); - reg.addSingleContextValue(MouseMoveContext.class, MouseMotionEvent.class, "mouse-motion", MouseMoveContext::getMouseMotion); + //reg.addSingleContextValue(MouseMoveContext.class, MouseMotionEvent.class, "mouse-motion", MouseMoveContext::getMouseMotion); } private static EventRegistration LISTENER; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupConnect.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupConnect.java index 6f30ca8b..f75152e5 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupConnect.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupConnect.java @@ -24,9 +24,12 @@ public static void register(SkriptRegistration reg) { .setHandledContexts(PlayerSetupConnectContext.class) .register(); - reg.addContextValue(PlayerSetupConnectContext.class, String.class, true, "name", PlayerSetupConnectContext::getUsername); - reg.addContextValue(PlayerSetupConnectContext.class, UUID.class, true, "uuid", PlayerSetupConnectContext::getUuid); - reg.addContextValue(PlayerSetupConnectContext.class, String.class, true, "reason", PlayerSetupConnectContext::getReason); + reg.addSingleContextValue(PlayerSetupConnectContext.class, String.class, + "name", PlayerSetupConnectContext::getUsername); + reg.addSingleContextValue(PlayerSetupConnectContext.class, UUID.class, + "uuid", PlayerSetupConnectContext::getUuid); + reg.addSingleContextValue(PlayerSetupConnectContext.class, String.class, + "reason", PlayerSetupConnectContext::getReason); } private static EventRegistration LISTENER; @@ -55,16 +58,16 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { private record PlayerSetupConnectContext( PlayerSetupConnectEvent event) implements TriggerContext, CancellableContext { - public String[] getUsername() { - return new String[]{this.event.getUsername()}; + public String getUsername() { + return this.event.getUsername(); } - public UUID[] getUuid() { - return new UUID[]{this.event.getUuid()}; + public UUID getUuid() { + return this.event.getUuid(); } - public String[] getReason() { - return new String[]{this.event.getReason()}; + public String getReason() { + return this.event.getReason(); } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupDisconnect.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupDisconnect.java index 89527623..a987556c 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupDisconnect.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSetupDisconnect.java @@ -23,9 +23,12 @@ public static void register(SkriptRegistration reg) { .setHandledContexts(PlayerSetupDisconnectContext.class) .register(); - reg.addContextValue(PlayerSetupDisconnectContext.class, String.class, true, "name", PlayerSetupDisconnectContext::getUsername); - reg.addContextValue(PlayerSetupDisconnectContext.class, UUID.class, true, "uuid", PlayerSetupDisconnectContext::getUuid); - reg.addContextValue(PlayerSetupDisconnectContext.class, String.class, true, "reason", PlayerSetupDisconnectContext::getDisconnectReason); + reg.addSingleContextValue(PlayerSetupDisconnectContext.class, String.class, + "name", PlayerSetupDisconnectContext::getUsername); + reg.addSingleContextValue(PlayerSetupDisconnectContext.class, UUID.class, + "uuid", PlayerSetupDisconnectContext::getUuid); + reg.addSingleContextValue(PlayerSetupDisconnectContext.class, String.class, + "reason", PlayerSetupDisconnectContext::getDisconnectReason); } private static EventRegistration LISTENER; @@ -53,16 +56,16 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { private record PlayerSetupDisconnectContext(PlayerSetupDisconnectEvent event) implements TriggerContext { - public String[] getUsername() { - return new String[]{this.event.getUsername()}; + public String getUsername() { + return this.event.getUsername(); } - public UUID[] getUuid() { - return new UUID[]{this.event.getUuid()}; + public UUID getUuid() { + return this.event.getUuid(); } - public String[] getDisconnectReason() { - return new String[]{this.event.getDisconnectReason().getServerDisconnectReason()}; + public String getDisconnectReason() { + return this.event.getDisconnectReason().getServerDisconnectReason(); } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerUseBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerUseBlock.java index 83781610..f3ed8d7d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerUseBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerUseBlock.java @@ -62,7 +62,8 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex applySystem(POST_SYSTEM); } this.pre = matchedPattern < 3; - parseContext.getParserState().setCurrentContexts(this.pre ? Collections.singleton(PreUseBlockContext.class) : Collections.singleton(PostUseBlockContext.class)); + parseContext.getParserState().setCurrentContexts(this.pre ? Collections.singleton(PreUseBlockContext.class) : + Collections.singleton(PostUseBlockContext.class)); return true; } @@ -153,7 +154,8 @@ protected PreUseBlockSystem() { } @Override - public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, @NotNull Store store, @NotNull CommandBuffer commandBuffer, @NotNull UseBlockEvent.Pre pre) { + public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, @NotNull Store store, + @NotNull CommandBuffer commandBuffer, @NotNull UseBlockEvent.Pre pre) { Ref ref = archetypeChunk.getReferenceTo(i); Player player = store.getComponent(ref, Player.getComponentType()); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index b9784e1b..0bc119cf 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -10,6 +10,9 @@ import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockFluidLevel; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprTargetBlockOfPlayer; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprActiveSlot; +import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntitiesInRadius; +import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffectDuration; +import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffects; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHealth; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityStat; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityVelocity; @@ -17,6 +20,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprNPCType; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprName; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprTargetEntityOfEntity; +import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffectEffect; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventory; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlot; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlots; @@ -26,6 +30,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemsInInventory; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCast; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprClassInfoOf; +import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCurrentContext; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprDistance; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationDirection; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationOf; @@ -41,9 +46,13 @@ import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprVector3i; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprAllPlayers; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprChatMessage; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprGameMode; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerPermissionGroup; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerPermissions; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerSpawns; import com.github.skriptdev.skript.plugin.elements.expressions.server.ExprConsole; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprChunkAtLocation; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprRelativePositionResolve; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorld; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldOf; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldSpawn; @@ -63,6 +72,7 @@ public static void register(SkriptRegistration registration) { // ENTITY ExprActiveSlot.register(registration); + ExprEntitiesInRadius.register(registration); ExprEntityHealth.register(registration); ExprEntityStat.register(registration); ExprEntityVelocity.register(registration); @@ -71,6 +81,11 @@ public static void register(SkriptRegistration registration) { ExprNPCType.register(registration); ExprTargetEntityOfEntity.register(registration); + // ENTITY EFFECT + ExprActiveEntityEffectDuration.register(registration); + ExprActiveEntityEffectEffect.register(registration); + ExprActiveEntityEffects.register(registration); + // ITEM ExprInventory.register(registration); ExprInventorySlot.register(registration); @@ -100,6 +115,9 @@ public static void register(SkriptRegistration registration) { // PLAYER ExprAllPlayers.register(registration); ExprChatMessage.register(registration); + ExprGameMode.register(registration); + ExprPlayerPermissionGroup.register(registration); + ExprPlayerPermissions.register(registration); ExprPlayerSpawns.register(registration); // SERVER @@ -107,10 +125,14 @@ public static void register(SkriptRegistration registration) { // WORLD ExprChunkAtLocation.register(registration); + ExprRelativePositionResolve.register(registration); ExprWorld.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); + // TEST + ExprCurrentContext.register(registration); + } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntitiesInRadius.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntitiesInRadius.java new file mode 100644 index 00000000..38ffa7cc --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntitiesInRadius.java @@ -0,0 +1,163 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entity; + +import com.github.skriptdev.skript.api.skript.registration.NPCRegistry; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.EntityUtils; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import com.hypixel.hytale.server.core.util.TargetUtil; +import com.hypixel.hytale.server.npc.entities.NPCEntity; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +public class ExprEntitiesInRadius implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprEntitiesInRadius.class, Entity.class, false, + "[all] entities in radius %number% (around|of) %location%", + "[all] entities of type[s] %npcroles% in radius %number% (around|of) %location%", + "[all] entities within %location% and %location%", + "[all] entities of type[s] %npcroles% within %location% and %location%") + .name("Entities in Radius/Cuboid") + .description("Get all entities within a radius around a location or within a cuboid.", + "You can optionally include an NPCRole to filter the entities by type.") + .examples("loop entities in radius 10 around player:", + "\tif npctype of loop-entity is sheep:", + "\t\tkill loop-entity", + "loop entities of type cow in radius 5 around {_loc}:", + "loop entities within {_loc1} and {_loc2}:", + "loop entities of type sheep within {_loc1} and {_loc2}") + .since("INSERT VERSION") + .register(); + } + + private int pattern; + private Expression radius; + private Expression location; + private Expression loc1, loc2; + private Expression roles; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + switch (matchedPattern) { + case 0 -> { + this.radius = (Expression) expressions[0]; + this.location = (Expression) expressions[1]; + } + case 1 -> { + this.roles = (Expression) expressions[0]; + this.radius = (Expression) expressions[1]; + this.location = (Expression) expressions[2]; + } + case 2 -> { + this.loc1 = (Expression) expressions[0]; + this.loc2 = (Expression) expressions[1]; + } + case 3 -> { + this.roles = (Expression) expressions[0]; + this.loc1 = (Expression) expressions[1]; + this.loc2 = (Expression) expressions[2]; + } + } + return true; + } + + @SuppressWarnings("deprecation") + @Override + public Entity[] getValues(@NotNull TriggerContext ctx) { + List entities = new ArrayList<>(); + + List roles = new ArrayList<>(); + if (this.roles != null) { + roles.addAll(Arrays.asList(this.roles.getArray(ctx))); + } + + Predicate rolePredicate = entity -> { + if (roles.isEmpty()) return true; + if (entity instanceof NPCEntity npcEntity) { + return roles.contains(NPCRegistry.getByIndex(npcEntity.getRoleIndex())); + } + return false; + }; + + if (this.location != null) { + Location loc = this.location.getSingle(ctx).orElse(null); + if (loc == null) return null; + + Number number = this.radius.getSingle(ctx).orElse(null); + if (number == null) return null; + + double radius = number.doubleValue(); + + String worldName = loc.getWorld(); + if (worldName == null) return null; + + World world = Universe.get().getWorld(worldName); + if (world == null) return null; + Store store = world.getEntityStore().getStore(); + + + for (Ref entityStoreRef : TargetUtil.getAllEntitiesInSphere(loc.getPosition(), radius, store)) { + Entity entity = EntityUtils.getEntity(entityStoreRef, store); + if (entity == null) continue; + if (!rolePredicate.test(entity)) continue; + entities.add(entity); + } + } else { + Location loc1 = this.loc1.getSingle(ctx).orElse(null); + Location loc2 = this.loc2.getSingle(ctx).orElse(null); + if (loc1 == null || loc2 == null) return null; + + String worldName = loc1.getWorld(); + if (worldName == null) return null; + + World world = Universe.get().getWorld(worldName); + if (world == null) return null; + Store store = world.getEntityStore().getStore(); + + Vector3d min = Vector3d.min(loc1.getPosition(), loc2.getPosition()); + Vector3d max = Vector3d.max(loc1.getPosition(), loc2.getPosition()); + + for (Ref allEntitiesInBox : TargetUtil.getAllEntitiesInBox(min, max, store)) { + Entity entity = EntityUtils.getEntity(allEntitiesInBox, store); + if (entity == null) continue; + if (!rolePredicate.test(entity)) continue; + entities.add(entity); + } + } + + return entities.toArray(Entity[]::new); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return switch (this.pattern) { + case 0 -> + "entities in radius " + this.radius.toString(ctx, debug) + " around " + this.location.toString(ctx, debug); + case 1 -> + "entities of type " + this.roles.toString(ctx, debug) + " in radius " + this.radius.toString(ctx, debug) + + " around " + this.location.toString(ctx, debug); + case 2 -> "entities within " + this.loc1.toString(ctx, debug) + " and " + this.loc2.toString(ctx, debug); + case 3 -> + "entities of type " + this.roles.toString(ctx, debug) + " within " + this.loc1.toString(ctx, debug) + + " and " + this.loc2.toString(ctx, debug); + default -> null; + }; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHealth.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHealth.java index 69715e5a..a1477ecb 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHealth.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHealth.java @@ -5,7 +5,7 @@ import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap; import com.hypixel.hytale.server.core.modules.entitystats.EntityStatValue; -import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; +import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntityStatTypes; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; @@ -18,7 +18,7 @@ public class ExprEntityHealth extends PropertyExpression { - private static final int HEALTH_STATE_INDEX = EntityStatType.getAssetMap().getIndex("Health"); + private static final int HEALTH_STAT_INDEX = DefaultEntityStatTypes.getHealth(); public static void register(SkriptRegistration registration) { registration.newPropertyExpression(ExprEntityHealth.class, Number.class, @@ -46,7 +46,7 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, P public @Nullable Number getProperty(@NotNull LivingEntity entity) { EntityStatMap entityStatMap = EntityComponentUtils.getEntityStatMap(entity); if (entityStatMap == null) return null; - EntityStatValue health = entityStatMap.get(HEALTH_STATE_INDEX); + EntityStatValue health = entityStatMap.get(HEALTH_STAT_INDEX); if (health == null) return null; if (this.pattern == 1) return health.getMin(); @@ -72,7 +72,7 @@ public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, if (statMap == null) return; if (changeMode == ChangeMode.RESET) { - statMap.resetStatValue(HEALTH_STATE_INDEX); + statMap.resetStatValue(HEALTH_STAT_INDEX); return; } @@ -84,7 +84,7 @@ public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, } if (changeMode != ChangeMode.SET) { - EntityStatValue healthStat = statMap.get(HEALTH_STATE_INDEX); + EntityStatValue healthStat = statMap.get(HEALTH_STAT_INDEX); if (healthStat == null) return; float oldHealthValue = healthStat.get(); @@ -97,7 +97,7 @@ public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, } } - statMap.setStatValue(HEALTH_STATE_INDEX, newValue); + statMap.setStatValue(HEALTH_STAT_INDEX, newValue); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprName.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprName.java index dafb1883..ae6dae16 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprName.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprName.java @@ -1,5 +1,6 @@ package com.github.skriptdev.skript.plugin.elements.expressions.entity; +import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.PlayerRef; @@ -21,6 +22,7 @@ public static void register(SkriptRegistration registration) { .register(); } + @SuppressWarnings("removal") // #getLegacyDisplayName TODO (find a better solution?) @Override public @Nullable String getProperty(Object object) { return switch (object) { @@ -28,6 +30,7 @@ public static void register(SkriptRegistration registration) { case Player player -> player.getDisplayName(); case Entity entity -> entity.getLegacyDisplayName(); case World world -> world.getName(); + case GameMode gameMode -> gameMode.name(); default -> null; }; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffectDuration.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffectDuration.java new file mode 100644 index 00000000..86d2854e --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffectDuration.java @@ -0,0 +1,45 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entityeffect; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; + +public class ExprActiveEntityEffectDuration extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprActiveEntityEffectDuration.class, Duration.class, + "[(remaining|:initial)] effect duration", "activeentityeffects") + .name("Active Entity Effect - Duration") + .description("Returns the duration of an ActiveEntityEffect.", + "You can get the initial duration or the remaining duration. Will default to remaining if not explicitly specified.") + .examples("if remaining effect duration of {_effect} is less than 10 seconds:") + .since("INSERT VERSION") + .register(); + } + + private boolean initial; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { + this.initial = parseContext.hasMark("initial"); + return super.init(expressions, matchedPattern, parseContext); + } + + @Override + public @Nullable Duration getProperty(@NotNull ActiveEntityEffect effect) { + float duration; + if (this.initial) { + duration = effect.getInitialDuration(); + } else { + duration = effect.getRemainingDuration(); + } + return Duration.ofMillis((long) duration * 1000); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffectEffect.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffectEffect.java new file mode 100644 index 00000000..5f8626be --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffectEffect.java @@ -0,0 +1,26 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entityeffect; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import org.jetbrains.annotations.Nullable; + +public class ExprActiveEntityEffectEffect extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprActiveEntityEffectEffect.class, EntityEffect.class, + "entity effect", "activeentityeffects") + .name("Active Entity Effect - Entity Effect") + .description("Returns the EntityEffect of an ActiveEntityEffect.") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable EntityEffect getProperty(ActiveEntityEffect effect) { + int index = effect.getEntityEffectIndex(); + return EntityEffect.getAssetMap().getAsset(index); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffects.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffects.java new file mode 100644 index 00000000..56a4b6d8 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entityeffect/ExprActiveEntityEffects.java @@ -0,0 +1,104 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entityeffect; + +import com.github.skriptdev.skript.api.hytale.EntityComponentUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect; +import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExprActiveEntityEffects implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprActiveEntityEffects.class, ActiveEntityEffect.class, false, + "active entity effects of %livingentities%") + .name("Active Entity Effects") + .description("Returns all active effects of an entity.", + "You can also remove an EntityEffect from this list or clear all effects at once.") + .examples("set {_a::*} to active entity effects of player", + "remove freeze from active entity effects of player", + "clear active entity effects of all players") + .since("INSERT VERSION") + .register(); + } + + private Expression entities; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.entities = (Expression) expressions[0]; + return true; + } + + @Override + public ActiveEntityEffect[] getValues(@NotNull TriggerContext ctx) { + List effects = new ArrayList<>(); + + for (LivingEntity entity : this.entities.getArray(ctx)) { + EffectControllerComponent component = EntityComponentUtils.getComponent(entity, EffectControllerComponent.getComponentType()); + if (component == null) continue; + + component.getActiveEffects().forEach(effects::add); + } + return effects.toArray(ActiveEntityEffect[]::new); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.DELETE || mode == ChangeMode.REMOVE) { + return Optional.of(new Class[]{EntityEffect[].class}); + } + return Optional.empty(); + } + + @SuppressWarnings({"ConstantValue", "RedundantLengthCheck"}) + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + + List effectIndexes = new ArrayList<>(); + if (changeMode != null && changeWith.length > 0) { + for (Object o : changeWith) { // TODO gotta figure out why we keep getting casting errors from Object to type + if (o instanceof EntityEffect entityEffect) { + int index = EntityEffect.getAssetMap().getIndex(entityEffect.getId()); + effectIndexes.add(index); + } + } + } + + for (LivingEntity livingEntity : this.entities.getArray(ctx)) { + Ref reference = livingEntity.getReference(); + if (reference == null) continue; + + Store store = reference.getStore(); + EffectControllerComponent component = EntityComponentUtils.getComponent(livingEntity, EffectControllerComponent.getComponentType()); + if (component == null) continue; + + if (changeMode == ChangeMode.DELETE) { + component.clearEffects(reference, store); + } else if (changeMode == ChangeMode.REMOVE && !effectIndexes.isEmpty()) { + for (int index : effectIndexes) { + component.removeEffect(reference, index, store); + } + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "active entity effects of " + this.entities.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java index f50b8a7e..5e7d6b66 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemsInInventory.java @@ -6,7 +6,6 @@ import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.parsing.ParseContext; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCurrentContext.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCurrentContext.java new file mode 100644 index 00000000..25647eaa --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCurrentContext.java @@ -0,0 +1,36 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.other; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class ExprCurrentContext implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprCurrentContext.class, String.class, true, + "current trigger context", "current thread") + .noDoc() // Used for debugging + .register(); + } + + private int pattern; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + return true; + } + + @Override + public String[] getValues(@NotNull TriggerContext ctx) { + return new String[]{this.pattern == 0 ? ctx.getName() : Thread.currentThread().getName()}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "current trigger context"; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessage.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessage.java index a5f750c2..d003c3a1 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessage.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessage.java @@ -21,8 +21,9 @@ public static void register(SkriptRegistration registration) { .name("Message") .description("Create a new message from a string.", "The translated option will use a translation key from the game's lang file instead of a raw string.", - "The formatted option will parse the string as a TinyMessage string.") - // TODO link to a wiki page regarding messages + "The formatted option will parse the string as a TinyMessage string.", + "See [Message Format](https://github.com/SkriptDev/HySkript/wiki/Tutorial-Message-Format) on the wiki" + + "for more info.") .examples("on player ready:", "\tset {_message} to message from \"Welcome to the server %context-player%\"", "\tset message color of {_message} to \"##0CB1F7\"", diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessageProperties.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessageProperties.java index 78a27a44..10b35d8a 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessageProperties.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprMessageProperties.java @@ -15,11 +15,11 @@ public class ExprMessageProperties implements Expression { public static void register(SkriptRegistration registration) { registration.newExpression(ExprMessageProperties.class, Boolean.class, true, - "bold [message[ property of %message%", - "italic [message[ property of %message%", - "monospace [message[ property of %message%", - "underlined [message[ property of %message%", - "markup enabled [message[ property of %message%") + "bold [message] property of %message%", + "italic [message] property of %message%", + "monospace [message] property of %message%", + "underlined [message] property of %message%", + "markup enabled [message] property of %message%") .name("Message Properties") .description("Get/set different properties of a message.") .examples("set bold property of {_msg} to true", diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprUUID.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprUUID.java index 4420db51..b45a8bc7 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprUUID.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprUUID.java @@ -21,6 +21,7 @@ public static void register(SkriptRegistration registration) { .register(); } + @SuppressWarnings("removal") // Entity#getUuid TODO (what else would you use?) @Override public @Nullable UUID getProperty(@NotNull Object owner) { return switch (owner) { diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprGameMode.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprGameMode.java new file mode 100644 index 00000000..b0e66ebb --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprGameMode.java @@ -0,0 +1,54 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.server.core.entity.entities.Player; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprGameMode extends PropertyExpression { + + public static void register(SkriptRegistration registration) { + registration.newPropertyExpression(ExprGameMode.class, GameMode.class, "game[(-| )]mode", "players") + .name("GameMode of a player") + .description("Returns the game mode of a player.") + .examples("set {_gm} to game-mode of context-player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable GameMode getProperty(@NotNull Player player) { + return player.getGameMode(); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{GameMode.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantConditions") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, @NotNull Object[] changeWith) { + if (changeMode != ChangeMode.SET || changeWith == null) return; + GameMode newGameMode = changeWith[0] instanceof GameMode ? (GameMode) changeWith[0] : null; + if (newGameMode == null) return; + + Optional owner = getOwner().getSingle(ctx); + if (!(owner.isPresent() && owner.get() instanceof Player player)) return; + + Player.setGameMode(player.getReference(), newGameMode, player.getReference().getStore()); + } + + @Override + public Class getReturnType() { + return GameMode.class; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerPermissionGroup.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerPermissionGroup.java new file mode 100644 index 00000000..bb96ac37 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerPermissionGroup.java @@ -0,0 +1,103 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.permissions.PermissionsModule; +import com.hypixel.hytale.server.core.permissions.provider.PermissionProvider; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +public class ExprPlayerPermissionGroup implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprPlayerPermissionGroup.class, String.class, false, + "permission group[s] of %players/playerrefs/uuids%") + .name("Player Permission Groups") + .description("Returns the permission groups of a player.") + .examples("set {_groups::*} to permission groups of player", + "add \"some.group\" to permission groups of player", + "remove \"some.group\" from permission groups of player") + .since("INSERT VERSION") + .register(); + } + + private Expression permissables; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.permissables = expressions[0]; + return true; + } + + @SuppressWarnings("removal") + @Override + public String[] getValues(@NotNull TriggerContext ctx) { + List groups = new ArrayList<>(); + + PermissionProvider provider = PermissionsModule.get().getFirstPermissionProvider(); + for (Object o : this.permissables.getArray(ctx)) { + if (o instanceof UUID u) { + groups.addAll(provider.getGroupsForUser(u)); + } else if (o instanceof Player player) { + UUID uuid = player.getUuid(); + if (uuid == null) continue; + groups.addAll(provider.getGroupsForUser(uuid)); + } else if (o instanceof PlayerRef ref) { + groups.addAll(provider.getGroupsForUser(ref.getUuid())); + } + } + + return groups.toArray(new String[0]); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) return Optional.of(new Class[]{String[].class}); + return Optional.empty(); + } + + @SuppressWarnings({"ConstantValue", "removal"}) + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null) return; + + Set permissions = Set.of((String[]) changeWith[0]); + + PermissionProvider provider = PermissionsModule.get().getFirstPermissionProvider(); + for (Object permissable : permissables.getValues(ctx)) { + if (permissable instanceof UUID uuid) { + permChange(changeMode, provider, uuid, permissions); + } else if (permissable instanceof PlayerRef ref) { + permChange(changeMode, provider, ref.getUuid(), permissions); + } else if (permissable instanceof Player player) { + UUID uuid = player.getUuid(); + if (uuid == null) continue; + permChange(changeMode, provider, uuid, permissions); + } + } + } + + private void permChange(ChangeMode mode, PermissionProvider provider, UUID uuid, Set permissions) { + if (mode == ChangeMode.ADD) { + permissions.forEach(p -> provider.addUserToGroup(uuid, p)); + } else if (mode == ChangeMode.REMOVE) { + permissions.forEach(p -> provider.removeUserFromGroup(uuid, p)); + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "permission groups of " + this.permissables.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerPermissions.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerPermissions.java new file mode 100644 index 00000000..d74fa03b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerPermissions.java @@ -0,0 +1,124 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.permissions.PermissionsModule; +import com.hypixel.hytale.server.core.permissions.provider.PermissionProvider; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +public class ExprPlayerPermissions implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprPlayerPermissions.class, String.class, false, + "[all] permissions of %players/playerrefs/uuids%", + "[all] permissions of group[s] %strings%") + .name("Player Permissions") + .description("Get/add/remove all permissions of a Player, PlayerRef, UUID or String (a group).") + .examples("loop all permissions of player:", + "remove \"some.perm\" from permissions of player", + "add \"some.perm\" to permissions of player", + "add \"some.perm\" to permssions of group \"some.group\"") + .since("INSERT VERSION") + .register(); + } + + private boolean group; + private Expression permissables; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.group = matchedPattern == 1; + this.permissables = expressions[0]; + return true; + } + + @SuppressWarnings("removal") + @Override + public String[] getValues(@NotNull TriggerContext ctx) { + List permissions = new ArrayList<>(); + + PermissionProvider provider = PermissionsModule.get().getFirstPermissionProvider(); + for (Object o : this.permissables.getArray(ctx)) { + if (o instanceof Player player) { + UUID uuid = player.getUuid(); + if (uuid == null) continue; + permissions.addAll(provider.getUserPermissions(uuid)); + } else if (o instanceof PlayerRef playerRef) { + permissions.addAll(provider.getUserPermissions(playerRef.getUuid())); + } else if (o instanceof UUID uuid) { + permissions.addAll(provider.getUserPermissions(uuid)); + } else if (o instanceof String s) { + permissions.addAll(provider.getGroupPermissions(s)); + } + } + + return permissions.toArray(String[]::new); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) return Optional.of(new Class[]{String[].class}); + return Optional.empty(); + } + + @SuppressWarnings({"ConstantValue", "removal"}) + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null) return; + + HashSet permissions = new HashSet<>(); + for (Object o : changeWith) { + if (o instanceof String s) permissions.add(s); + } + + PermissionProvider provider = PermissionsModule.get().getFirstPermissionProvider(); + for (Object permissable : this.permissables.getArray(ctx)) { + if (permissable instanceof Player player) { + UUID uuid = player.getUuid(); + if (uuid == null) continue; + permChange(changeMode, provider, uuid, permissions); + } else if (permissable instanceof PlayerRef playerRef) { + permChange(changeMode, provider, playerRef.getUuid(), permissions); + } else if (permissable instanceof UUID uuid) { + permChange(changeMode, provider, uuid, permissions); + } else if (permissable instanceof String s) { + permChange(changeMode, provider, s, permissions); + } + } + } + + private void permChange(ChangeMode mode, PermissionProvider provider, UUID uuid, Set permissions) { + if (mode == ChangeMode.ADD) { + provider.addUserPermissions(uuid, permissions); + } else if (mode == ChangeMode.REMOVE) { + provider.removeUserPermissions(uuid, permissions); + } + } + + private void permChange(ChangeMode mode, PermissionProvider provider, String group, Set permissions) { + if (mode == ChangeMode.ADD) { + provider.addGroupPermissions(group, permissions); + } else if (mode == ChangeMode.REMOVE) { + provider.removeGroupPermissions(group, permissions); + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String group = this.group ? "group[s] " : ""; + return "all permissions of " + group + this.permissables.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprConsole.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprConsole.java index 33962b85..ee12b350 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprConsole.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprConsole.java @@ -25,7 +25,7 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex } @Override - public CommandSender[] getValues(TriggerContext ctx) {; + public CommandSender[] getValues(TriggerContext ctx) { return new CommandSender[]{ConsoleSender.INSTANCE}; } @@ -33,4 +33,5 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex public String toString(TriggerContext ctx, boolean debug) { return "console"; } + } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprRelativePositionResolve.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprRelativePositionResolve.java new file mode 100644 index 00000000..633b05c4 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprRelativePositionResolve.java @@ -0,0 +1,73 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeDoublePosition; +import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntPosition; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class ExprRelativePositionResolve implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprRelativePositionResolve.class, Location.class, true, + "resolve %relativeposition/relativeblockposition% for %location%") + .name("Resolve Relative Position") + .description("Resolves a relative position to a location.") + .examples("player command /LeTP :", + "\ttrigger:", + "\t\tset {_loc} to resolve {_relative} for location of player", + "\t\tteleport player to {_loc}", + "# Typing '/letp ~ ~10 ~' would teleport you 10 blocks above your location") + .since("INSERT VERSION") + .register(); + } + + private Expression relativePosition; + private Expression location; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.relativePosition = expressions[0]; + this.location = (Expression) expressions[1]; + return true; + } + + @Override + public Location[] getValues(@NotNull TriggerContext ctx) { + Object o = this.relativePosition.getSingle(ctx).orElse(null); + Location location = this.location.getSingle(ctx).orElse(null); + if (o == null || location == null) return null; + + World world = Universe.get().getWorld(location.getWorld()); + if (world == null) return null; + + Vector3f rotation = location.getRotation().clone(); + Location relativeLocation; + if (o instanceof RelativeDoublePosition d) { + Vector3d pos = d.getRelativePosition(location.getPosition().clone(), world); + relativeLocation = new Location(location.getWorld(), pos, rotation); + } else if (o instanceof RelativeIntPosition i) { + Vector3i pos = i.getBlockPosition(location.getPosition().clone(), world.getChunkStore()); + relativeLocation = new Location(location.getWorld(), pos.toVector3d(), rotation); + } else { + return null; + } + + return new Location[]{relativeLocation}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "resolve " + this.relativePosition.toString(ctx, debug) + " for " + this.location.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java index cf9c8a2c..d347853d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecDropItem.java @@ -19,6 +19,7 @@ import io.github.syst3ms.skriptparser.parsing.ParseContext; import io.github.syst3ms.skriptparser.parsing.ParserState; import io.github.syst3ms.skriptparser.util.Pair; +import io.github.syst3ms.skriptparser.variables.Variables; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -74,18 +75,18 @@ public boolean init(Expression[] expressions, int matchedPattern, @NotNull Pa @Override public Optional walk(@NotNull TriggerContext ctx) { - Optional next = getNext(); + Optional nextStatement = getNext(); Location location = this.location.getSingle(ctx).orElse(null); - if (location == null) return next; + if (location == null) return nextStatement; String worldName = location.getWorld(); World world = Universe.get().getWorld(worldName); - if (world == null) return next; + if (world == null) return nextStatement; Store store = world.getEntityStore().getStore(); Optional single1 = this.items.getSingle(ctx); - if (single1.isEmpty()) return next; + if (single1.isEmpty()) return nextStatement; Object o = single1.get(); ItemStack itemStack; @@ -94,7 +95,7 @@ public Optional walk(@NotNull TriggerContext ctx) { } else if (o instanceof Item item) { itemStack = new ItemStack(item.getId()); } else { - return next; + return nextStatement; } Vector3f velocity; @@ -123,20 +124,37 @@ public Optional walk(@NotNull TriggerContext ctx) { } Optional first = getFirst(); + // If there's nothing in the section, let's bounce + if (first.isEmpty()) return nextStatement; + + Statement firstStatement = first.get(); + // Prevent the statement after our section from running. + // This prevents the wrong TriggerContext being used + setNext(null); + + Runnable dropRunnable = () -> { + Pair pair = EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); + ItemComponentContext itemContext = new ItemComponentContext(pair.getFirst(), pair.getSecond()); + + // Copy variables from the previous context into our section context + Variables.copyLocalVariables(ctx, itemContext); + Statement.runAll(firstStatement, itemContext); + + // After the section runs, copy variables back into the previous context and clear variables from our section + Variables.copyLocalVariables(itemContext, ctx); + Variables.clearLocalVariables(itemContext); + + // If the next statement exists, run it + nextStatement.ifPresent(statement -> Statement.runAll(statement, ctx)); + }; + + // Make sure we're running on the correct thread. if (world.isInThread()) { - world.execute(() -> { - Pair pair = EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); - first.ifPresent(statement -> { - Statement.runAll(statement, new ItemComponentContext(pair.getFirst(), pair.getSecond())); - }); - }); + world.execute(dropRunnable); } else { - Pair pair = EntityComponentUtils.dropItem(store, itemStack, location, velocity, pickupDelay); - first.ifPresent(statement -> { - Statement.runAll(statement, new ItemComponentContext(pair.getFirst(), pair.getSecond())); - }); + dropRunnable.run(); } - return next; + return Optional.empty(); } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecPlaySound.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecPlaySound.java index 02418a77..f6e42aea 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecPlaySound.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecPlaySound.java @@ -97,7 +97,8 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return ""; } - public void playSoundToPlayer(@NotNull PlayerRef[] players, @Nullable Location location, int soundEvent, SoundCategory category, boolean is3d, float volume, float pitch) { + public void playSoundToPlayer(@NotNull PlayerRef[] players, @Nullable Location location, int soundEvent, + SoundCategory category, boolean is3d, float volume, float pitch) { for (PlayerRef player : players) { if (is3d && location != null) { Vector3d pos = location.getPosition(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSpawnNPC.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSpawnNPC.java index 4e1a8ef4..bf00f913 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSpawnNPC.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSpawnNPC.java @@ -35,8 +35,8 @@ public SpawnMobContext(Entity entity) { this.entity = entity; } - public Entity[] getEntity() { - return new Entity[]{this.entity}; + public Entity getEntity() { + return this.entity; } @Override @@ -57,11 +57,11 @@ public static void register(SkriptRegistration registration) { .since("1.0.0") .register(); - registration.newContextValue(SpawnMobContext.class, Entity.class, true, + registration.newSingleContextValue(SpawnMobContext.class, Entity.class, "spawned-entity", SpawnMobContext::getEntity) .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) .register(); - registration.newContextValue(SpawnMobContext.class, Entity.class, true, + registration.newSingleContextValue(SpawnMobContext.class, Entity.class, "spawned-npc", SpawnMobContext::getEntity) .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) .register(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java index cc3528bb..9551473b 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/DefaultConverters.java @@ -1,9 +1,16 @@ package com.github.skriptdev.skript.plugin.elements.types; +import com.github.skriptdev.skript.api.hytale.EntityComponentUtils; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.types.conversions.Converters; import java.util.Optional; @@ -11,10 +18,45 @@ public class DefaultConverters { public static void register() { + entity(); inventory(); } @SuppressWarnings("removal") + private static void entity() { + // Player to PlayerRef + Converters.registerConverter(Player.class, PlayerRef.class, (player) -> + Optional.ofNullable(player.getPlayerRef())); + + // Entity to Location + Converters.registerConverter(Entity.class, Location.class, entity -> { + World world = entity.getWorld(); + if (world == null) return Optional.empty(); + + TransformComponent component = EntityComponentUtils.getComponent(entity, TransformComponent.getComponentType()); + if (component == null) return Optional.empty(); + + Vector3d pos = component.getPosition(); + Vector3f rotation = component.getRotation(); + Location location = new Location(world.getName(), pos, rotation); + return Optional.of(location); + }); + + // Player to Location + Converters.registerConverter(Player.class, Location.class, entity -> { + World world = entity.getWorld(); + if (world == null) return Optional.empty(); + + TransformComponent component = EntityComponentUtils.getComponent(entity, TransformComponent.getComponentType()); + if (component == null) return Optional.empty(); + + Vector3d pos = component.getPosition(); + Vector3f rotation = component.getRotation(); + Location location = new Location(world.getName(), pos, rotation); + return Optional.of(location); + }); + } + private static void inventory() { // Item to BlockType Converters.registerConverter(Item.class, BlockType.class, (item) -> { @@ -33,10 +75,7 @@ private static void inventory() { if (item != null) return Optional.of(item); return Optional.empty(); }); - - // Player to PlayerRef - Converters.registerConverter(Player.class, PlayerRef.class, (player) -> - Optional.ofNullable(player.getPlayerRef())); } + } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java index 34926e0d..b960817e 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/Types.java @@ -1,21 +1,21 @@ package com.github.skriptdev.skript.plugin.elements.types; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; -import com.github.skriptdev.skript.api.utils.Utils; import io.github.syst3ms.skriptparser.types.TypeManager; public class Types { public static void register(SkriptRegistration registration) { - Utils.log("Setting up Types"); - TypesJava.register(registration); + // Keep these in alphabetical order + TypesAssetStore.register(registration); + TypesBlock.register(registration); TypesCustom.register(registration); - TypesServer.register(registration); TypesEntity.register(registration); TypesItem.register(registration); - TypesBlock.register(registration); + TypesJava.register(registration); + TypesPlayer.register(registration); + TypesServer.register(registration); TypesWorld.register(registration); - TypesAssetStore.register(registration); TypeManager.register(registration); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java index d70b41fa..0052ee4d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesAssetStore.java @@ -29,6 +29,7 @@ public class TypesAssetStore { + @SuppressWarnings("deprecation") // BlockGroup (maybe we dont keep this?!?!) static void register(SkriptRegistration registration) { AssetStoreRegistry.register(registration, BiomeAsset.class, BiomeAsset.getAssetStore().getAssetMap(), "biome", "biome@s") .name("Biome") diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java index 96d89dc6..3d129f2c 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesEntity.java @@ -4,51 +4,46 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.LivingEntity; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.universe.PlayerRef; +import com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect; import com.hypixel.hytale.server.npc.entities.NPCEntity; public class TypesEntity { - static void register(SkriptRegistration registration) { - registration.newType(NPCRegistry.NPCRole.class, "npcrole", "npcrole@s") - .name("NPC Role") - .description("Represents the type of NPCs in the game.") - .examples("coming soon") // TODO - .usage(NPCRegistry.getTypeUsage()) - .since("1.0.0") - .toStringFunction(NPCRegistry.NPCRole::name) - .literalParser(NPCRegistry::parse) + @SuppressWarnings("removal") // LivingEntity::getLegacyDisplayName + static void register(SkriptRegistration reg) { + reg.newType(ActiveEntityEffect.class, "activeentityeffect", "activeEntityEffect@s") + .name("Active Entity Effect") + .description("Represents an active EntityEffect applied to an entity.") + .since("INSERT VERSION") + .toStringFunction(ActiveEntityEffect::toString) .register(); - registration.newType(Entity.class, "entity", "entit@y@ies") + reg.newType(Entity.class, "entity", "entit@y@ies") .toStringFunction(Entity::toString) // TODO get its name or something .name("Entity") .description("Represents any entity in the game, including players and mobs.") .since("1.0.0") .register(); - registration.newType(LivingEntity.class, "livingentity", "livingEntit@y@ies") + reg.newType(LivingEntity.class, "livingentity", "livingEntit@y@ies") .name("Living Entity") .description("Represents any living entity in the game, including players and mobs.") .since("1.0.0") .toStringFunction(LivingEntity::getLegacyDisplayName) .register(); - registration.newType(NPCEntity.class, "npcentity", "npcEntit@y@ies") + reg.newType(NPCEntity.class, "npcentity", "npcEntit@y@ies") .name("NPC Entity") .description("Represents an NPC entity in the game.") .since("1.0.0") .toStringFunction(NPCRegistry::stringify) .register(); - registration.newType(Player.class, "player", "player@s") - .name("Player") - .description("Represents a player in the game.") - .since("1.0.0") - .toStringFunction(Player::getDisplayName) - .register(); - registration.newType(PlayerRef.class, "playerref", "playerRef@s") - .name("Player Ref") - .description("Represents a reference to a player in the game.") + reg.newType(NPCRegistry.NPCRole.class, "npcrole", "npcrole@s") + .name("NPC Role") + .description("Represents the type of NPCs in the game.") + .examples("coming soon") // TODO + .usage(NPCRegistry.getTypeUsage()) .since("1.0.0") - .toStringFunction(PlayerRef::getUsername) + .toStringFunction(NPCRegistry.NPCRole::name) + .supplier(NPCRegistry::iterator) + .literalParser(NPCRegistry::parse) .register(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesPlayer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesPlayer.java new file mode 100644 index 00000000..17938a31 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesPlayer.java @@ -0,0 +1,32 @@ +package com.github.skriptdev.skript.plugin.elements.types; + +import com.github.skriptdev.skript.api.skript.registration.EnumRegistry; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.protocol.GameMode; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; + +public class TypesPlayer { + + public static void register(SkriptRegistration reg) { + // Keep these in alphabetical order + EnumRegistry.register(reg, GameMode.class, "gamemode", "gamemode@s") + .name("GameMode") + .description("Represents a game mode.") + .since("INSERT VERSION") + .register(); + reg.newType(Player.class, "player", "player@s") + .name("Player") + .description("Represents a player in the game.") + .since("1.0.0") + .toStringFunction(Player::getDisplayName) + .register(); + reg.newType(PlayerRef.class, "playerref", "playerRef@s") + .name("Player Ref") + .description("Represents a reference to a player in the game.") + .since("1.0.0") + .toStringFunction(PlayerRef::getUsername) + .register(); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java index 858548bf..c97ca8bc 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesServer.java @@ -9,6 +9,7 @@ import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.protocol.Vector2f; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandSender; @@ -74,6 +75,12 @@ public Message deserialize(@NotNull Gson gson, @NotNull JsonElement element) { .description("Represents the Hytale server.") .since("1.0.0") .register(); + registration.newType(Vector2f.class, "vector2f", "vector2f@s") + .name("Vector2f") + .description("Represents a vector in 2D space using floats.", + "Often used for mouse position on screen.") + .since("INSERT VERSION") + .register(); registration.newType(Vector3f.class, "vector3f", "vector3f@s") .name("Vector3f") .description("Represents a vector in 3D space using floats.", diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java index 1d463dd3..290dad01 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesWorld.java @@ -3,6 +3,8 @@ import com.github.skriptdev.skript.api.skript.registration.EnumRegistry; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.protocol.SoundCategory; +import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeDoublePosition; +import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeIntPosition; import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.worldgen.zone.Zone; @@ -14,7 +16,18 @@ static void register(SkriptRegistration reg) { .name("Chunk") .description("Represents a chunk in a world. A chunk is a 32x32x(world height) set of blocks.") .since("1.0.0") - .toStringFunction(worldChunk -> "chunk (x=" + worldChunk.getX() + ",z=" + worldChunk.getZ() + ") in world '" + worldChunk.getWorld().getName() + "'") + .toStringFunction(worldChunk -> "chunk (x=" + worldChunk.getX() + ",z=" + worldChunk.getZ() + + ") in world '" + worldChunk.getWorld().getName() + "'") + .register(); + reg.newType(RelativeDoublePosition.class, "relativeposition", "relativePosition@s") + .name("Relative Position") + .description("Represents a position relative to another position.") + .since("INSERT VERSION") + .register(); + reg.newType(RelativeIntPosition.class, "relativeblockposition", "relativeBlockPosition@s") + .name("Relative Block Position") + .description("Represents a block position relative to another block position.") + .since("INSERT VERSION") .register(); EnumRegistry.register(reg, SoundCategory.class, "soundcategory", "soundcategor@y@ies") .name("Sound Category")