diff --git a/.editorconfig b/.editorconfig index 89a52330..772fe705 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,6 +15,8 @@ ij_java_class_count_to_use_import_on_demand = 1024 ij_java_names_count_to_use_import_on_demand = 1024 [*.yml] indent_size = 2 +[*.json] +indent_size = 2 [*.md] trim_trailing_whitespace=false [*.sk] diff --git a/.github/workflows/checkstyle.yml b/.github/workflows/checkstyle.yml deleted file mode 100644 index 1e583b60..00000000 --- a/.github/workflows/checkstyle.yml +++ /dev/null @@ -1,37 +0,0 @@ -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/README.md b/README.md index 2d8183b2..9a21742c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ![landscape](https://github.com/user-attachments/assets/2234f5ea-c2dc-4e79-b51c-4300b795e64f) -HySkript is a [**Skript**](https://github.com/SkriptLang/Skript) like plugin for Hytale servers. -It allows writing your own little plugins in English like syntaxes. -This way you can program your server to do marvelous things without having to learn Java. +**HySkript** brings the power of intuitive scripting to Hytale. +Inspired by the legendary Minecraft plugin [**Skript**](https://github.com/SkriptLang/Skript), HySkript allows you to customize your server using a natural, English-like syntax. +Whether you're creating custom commands or complex game mechanics, you can bring your vision to life instantly—no Java knowledge required. ## Docs/Tutorials: Check out our [**wiki**](https://github.com/SkriptDev/HySkript/wiki) diff --git a/build.gradle.kts b/build.gradle.kts index 2124b425..e02b7483 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,7 @@ java.sourceCompatibility = JavaVersion.VERSION_25 group = "com.github.SkriptDev" val projectVersion = "1.0.0-beta5" -val hytaleVersion = "2026.01.29-301e13929" +val hytaleVersion = "2026.02.06-aa1b071c2" // You can find Hytale versions on their maven repo: // https://maven.hytale.com/release/com/hypixel/hytale/Server/maven-metadata.xml // https://maven.hytale.com/pre-release/com/hypixel/hytale/Server/maven-metadata.xml @@ -26,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.4") { + implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { @@ -44,7 +44,7 @@ tasks { } processResources { filesNotMatching("assets/**") { - expand("pluginVersion" to projectVersion) + expand("pluginVersion" to projectVersion, "hytaleVersion" to hytaleVersion) } } compileJava { @@ -54,6 +54,7 @@ tasks { } shadowJar { archiveFileName = project.name + "-" + projectVersion + ".jar" + archiveClassifier.set("") relocate("io.github.syst3ms", "com.github.skriptdev.skript") relocate("fi.sulku.hytale", "com.github.skriptdev.skript.tinymessage") } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 83c6b26c..e2897427 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -5,7 +5,7 @@ - + diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/StoreUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/StoreUtils.java new file mode 100644 index 00000000..6481ded3 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/StoreUtils.java @@ -0,0 +1,22 @@ +package com.github.skriptdev.skript.api.hytale; + +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Store; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class StoreUtils { + + @SuppressWarnings("unchecked") + public static CommandBuffer getCommandBuffer(Store store) { + try { + Method method = store.getClass().getDeclaredMethod("takeCommandBuffer"); + method.setAccessible(true); + return (CommandBuffer) method.invoke(store); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/ErrorHandler.java b/src/main/java/com/github/skriptdev/skript/api/skript/ErrorHandler.java new file mode 100644 index 00000000..39eeaa7c --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/ErrorHandler.java @@ -0,0 +1,110 @@ +package com.github.skriptdev.skript.api.skript; + +import com.github.skriptdev.skript.api.utils.Utils; +import io.github.syst3ms.skriptparser.lang.Effect; +import io.github.syst3ms.skriptparser.lang.Statement; +import io.github.syst3ms.skriptparser.registration.SkriptEventInfo; +import io.github.syst3ms.skriptparser.registration.SyntaxInfo; +import io.github.syst3ms.skriptparser.registration.SyntaxManager; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +public class ErrorHandler { + + private static final Map> EVENT_MAP = new HashMap<>(); + private static final Map> EFFECT_MAP = new HashMap<>(); + + public static void init() { + for (SyntaxInfo effect : SyntaxManager.getEffects()) { + EFFECT_MAP.put(effect.getSyntaxClass().getSimpleName(), effect); + } + for (SkriptEventInfo event : SyntaxManager.getEvents()) { + EVENT_MAP.put(event.getSyntaxClass().getSimpleName(), event); + } + setup(); + } + + private static @Nullable SkriptEventInfo getEvent(String name) { + return EVENT_MAP.get(name); + } + + private static @Nullable SyntaxInfo getEffect(String name) { + return EFFECT_MAP.get(name); + } + + private static void setup() { + // Set up how HySkript handles IllegalStateExceptions + Statement.setIllegalStateHandler(e -> { + String message = e.getMessage(); + if (message.contains("Assert not in thread!")) { + // Hytale has threads for each world, effects involving a world must happen on a world 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", message); + } else { + Utils.logToAdmins(Level.WARNING, "Something was executed on the wrong thread."); + if (message.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", message); + } + } + } else if (message.contains("calling a store method from a system")) { + // Hytale doesn't allow store methods to be called within System-based events + String eventName = "unknown event"; + String effectName = "unknown effect"; + + for (StackTraceElement stackTraceElement : e.getStackTrace()) { + String fileName = stackTraceElement.getFileName(); + if (fileName == null) continue; + fileName = fileName.replace(".java", ""); + + // Try to find the event + if (eventName.contains("unknown")) { + SkriptEventInfo event = getEvent(fileName); + if (event != null) { + eventName = event.getDocumentation().getName(); + if (eventName == null) eventName = event.getClass().getSimpleName(); + } + } + + // Try to find the effect + if (effectName.contains("unknown")) { + SyntaxInfo effect = getEffect(fileName); + if (effect != null) { + effectName = effect.getDocumentation().getName(); + if (effectName == null) effectName = effect.getClass().getSimpleName(); + } + + } + + } + Utils.logToAdmins(Level.WARNING, "An effect was executed incorrectly, see console for more info."); + Utils.error("The effect '%s' was executed incorrectly in the event '%s'.", effectName, eventName); + Utils.error("Hytale doesn't allow Store-based effects to be executed within System-based events."); + Utils.warn("Consider waiting a tick before using this effect to prevent this."); + + } else { + Utils.error("An error ocrrured while executing a script: %s", message); + for (StackTraceElement stackTraceElement : e.getStackTrace()) { + Utils.error(" - %s", stackTraceElement); + } + } + }); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/ScriptsLoader.java b/src/main/java/com/github/skriptdev/skript/api/skript/ScriptsLoader.java index 5428cdb5..3502226a 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/ScriptsLoader.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/ScriptsLoader.java @@ -1,5 +1,6 @@ package com.github.skriptdev.skript.api.skript; +import com.github.skriptdev.skript.api.utils.FileUtils; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.Skript; import com.hypixel.hytale.server.core.receiver.IMessageReceiver; @@ -8,8 +9,12 @@ import io.github.syst3ms.skriptparser.parsing.ScriptLoader; import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; public class ScriptsLoader { @@ -26,14 +31,25 @@ public void loadScripts(IMessageReceiver receiver, Path directory, boolean reloa Utils.log(receiver, (reload ? "Reloading" : "Loading") + " scripts..."); long start = System.currentTimeMillis(); - File directoryFile = directory.toFile(); - if (!directoryFile.isDirectory()) { - if (!directoryFile.mkdirs()) { + File scriptsDirectory = directory.toFile(); + if (!scriptsDirectory.isDirectory()) { + if (!scriptsDirectory.mkdirs()) { Utils.error("Failed to create scripts directory!"); + return; + } + try { + // Add sample scripts when creating a new scripts folder + File scriptsDir = new File(scriptsDirectory, "-sample-scripts"); + if (!scriptsDir.mkdir()) { + throw new RuntimeException("Failed to create sample scripts directory"); + } + FileUtils.copyFromJar("/sample-scripts/", scriptsDir.toPath()); + } catch (IOException | URISyntaxException e) { + Utils.error("Failed to load sample scripts, message: %s", e.getMessage()); } } - List scriptNames = loadScriptsInDirectory(receiver, directoryFile); + List scriptNames = loadScriptsInDirectory(receiver, scriptsDirectory); long end = System.currentTimeMillis() - start; Utils.log(receiver, (reload ? "Reloaded" : "Loaded") + " %s scripts in %sms", this.loadedScriptCount, end); @@ -46,23 +62,32 @@ public void loadScripts(IMessageReceiver receiver, Path directory, boolean reloa } } - @SuppressWarnings("DataFlowIssue") public List loadScriptsInDirectory(IMessageReceiver receiver, File directory) { if (directory == null || !directory.isDirectory()) return List.of(); List loadedScripts = new ArrayList<>(); - for (File file : directory.listFiles()) { + File[] files = directory.listFiles(); + if (files == null) return loadedScripts; + + Arrays.sort(files, + Comparator.comparing(File::isDirectory).reversed() // Directories first + .thenComparing(File::getName, String.CASE_INSENSITIVE_ORDER)); // Then sort by name alphabetically + + for (File file : files) { + // Skip disabled files and hidden files + String fileName = file.getName(); + if (fileName.startsWith("-") || fileName.startsWith(".")) continue; if (file.isDirectory()) { loadedScripts.addAll(loadScriptsInDirectory(receiver, file)); } else { - if (!file.getName().endsWith(".sk")) continue; - Utils.log(receiver, "Loading script '" + file.getName() + "'..."); + if (!fileName.endsWith(".sk")) continue; + Utils.log(receiver, "Loading script '" + fileName + "'..."); List logEntries = ScriptLoader.loadScript(file.toPath(), false); this.loadedScriptCount++; for (LogEntry logEntry : logEntries) { Utils.log(receiver, logEntry); } - loadedScripts.add(file.getName().substring(0, file.getName().length() - 3)); + loadedScripts.add(fileName.substring(0, fileName.length() - 3)); } } return loadedScripts; @@ -106,4 +131,8 @@ public void reloadScript(IMessageReceiver receiver, String name) { } } + public void shutdown() { + // TODO clear triggers here + } + } 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 index c78f52c1..370f8722 100644 --- 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 @@ -90,7 +90,7 @@ private void loadAddon(File file) { } if (mainClassIntance instanceof HySkriptAddon addon) { addon.setManifest(manifest); - addon.setup(); + addon.start(); this.addonCount++; // Finalize registration and logging for (LogEntry logEntry : addon.getSkriptRegistration().register()) { 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 index 9ee51f7b..b446348e 100644 --- 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 @@ -25,7 +25,7 @@ public HySkriptAddon(String name) { * Called when the addon starts to load. * This is a good time to set up your syntaxes. */ - abstract public void setup(); + abstract public void start(); /** * Called when the addon is shutting down. 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 6369b7fb..954b19c1 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,9 @@ package com.github.skriptdev.skript.api.skript.command; import com.github.skriptdev.skript.plugin.HySk; +import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand.PlayerScriptCommandContext; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand.ScriptCommandContext; +import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand.WorldScriptCommandContext; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.command.system.AbstractCommand; @@ -10,6 +12,7 @@ import com.hypixel.hytale.server.core.command.system.arguments.system.Argument; import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg; import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg; +import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand; import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand; import com.hypixel.hytale.server.core.entity.entities.Player; @@ -21,6 +24,7 @@ import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.entries.SectionConfiguration; +import io.github.syst3ms.skriptparser.lang.entries.SectionLoader; import io.github.syst3ms.skriptparser.log.ErrorType; import io.github.syst3ms.skriptparser.log.SkriptLogger; import io.github.syst3ms.skriptparser.parsing.ParserState; @@ -55,7 +59,7 @@ public static ScriptCommandBuilder create(int commandType, SkriptLogger logger) .addOptionalKey("permission") .addOptionalKey("description") .addOptionalList("aliases") - .addSection("trigger") + .addLoader(new SectionLoader("trigger", true)) .build(); public ScriptCommandBuilder(int commandType, SkriptLogger logger) { @@ -90,19 +94,12 @@ public boolean parseCommandLine(String commandLine) { return true; } - public List setupCommand(FileSection section, ParserState parserState, SkriptLogger logger) { + public List setupCommand(FileSection section, ParserState parserState, SkriptLogger logger, int commandType) { + this.commandType = commandType; this.sec.loadConfiguration(null, section, parserState, logger); - Optional triggerSec = this.sec.getSection("trigger"); - if (triggerSec.isEmpty()) { - logger.error("Trigger section is missing", ErrorType.SEMANTIC_ERROR); - return List.of(); - } - CodeSection trigger = triggerSec.get(); - if (trigger.getItems().isEmpty()) { - logger.warn("Trigger section should not be empty."); - return List.of(); - } + Optional triggerSec = this.sec.getSection("trigger"); + boolean hasTrigger = triggerSec.isPresent(); Optional descOption = this.sec.getValue("description", String.class); if (descOption.isEmpty()) { @@ -114,49 +111,65 @@ public List setupCommand(FileSection section, ParserState parserState description = ""; } - this.hyCommand = switch (this.commandType) { - case 1 -> new AbstractPlayerCommand(this.commandName, description) { - @Override - protected void execute(@NotNull CommandContext commandContext, @NotNull Store store, - @NotNull Ref ref, @NotNull PlayerRef playerRef, @NotNull World world) { - - CommandSender sender = commandContext.sender(); - Player player = store.getComponent(ref, Player.getComponentType()); - ScriptCommandContext context = new ScriptCommandContext(ScriptCommandBuilder.this.commandName, sender, player, world); - createLocalVariables(commandContext, context); - Statement.runAll(trigger, context); - Variables.clearLocalVariables(context); - } - }; - case 2 -> new AbstractWorldCommand(this.commandName, description) { - - @Override - protected void execute(@NotNull CommandContext commandContext, @NotNull World world, @NotNull Store store) { - ScriptCommandContext context = new ScriptCommandContext(ScriptCommandBuilder.this.commandName, - commandContext.sender(), null, world); - createLocalVariables(commandContext, context); - Statement.runAll(trigger, context); - Variables.clearLocalVariables(context); - } - }; - default -> new AbstractCommand(this.commandName, description) { + if (hasTrigger) { + CodeSection trigger = triggerSec.get(); + if (trigger.getItems().isEmpty()) { + logger.warn("Trigger section should not be empty. Or remove it if you don't need it."); + return List.of(); + } + + this.hyCommand = switch (commandType) { + case 1 -> new AbstractPlayerCommand(this.commandName, description) { + @Override + protected void execute(@NotNull CommandContext commandContext, @NotNull Store store, + @NotNull Ref ref, @NotNull PlayerRef playerRef, @NotNull World world) { - @Override - protected @Nullable CompletableFuture execute(@NotNull CommandContext commandContext) { - CompletableFuture.runAsync(() -> { - CommandSender sender = commandContext.sender(); - Player player = null; - if (sender instanceof Player p) player = p; - ScriptCommandContext context = new ScriptCommandContext(ScriptCommandBuilder.this.commandName, sender, player, null); + Player player = store.getComponent(ref, Player.getComponentType()); + PlayerScriptCommandContext context = new PlayerScriptCommandContext( + ScriptCommandBuilder.this.commandName, player); createLocalVariables(commandContext, context); Statement.runAll(trigger, context); Variables.clearLocalVariables(context); - }); - return null; - } + } + }; + case 2 -> new AbstractWorldCommand(this.commandName, description) { + + @Override + protected void execute(@NotNull CommandContext commandContext, @NotNull World world, + @NotNull Store store) { + WorldScriptCommandContext context = new WorldScriptCommandContext( + ScriptCommandBuilder.this.commandName, commandContext.sender(), world); + + createLocalVariables(commandContext, context); + Statement.runAll(trigger, context); + Variables.clearLocalVariables(context); + } + }; + default -> new AbstractCommand(this.commandName, description) { + + @Override + protected @Nullable CompletableFuture execute(@NotNull CommandContext commandContext) { + CompletableFuture.runAsync(() -> { + CommandSender sender = commandContext.sender(); + Player player = null; + if (sender instanceof Player p) player = p; + ScriptCommandContext context = new ScriptCommandContext(ScriptCommandBuilder.this.commandName, + sender); + + createLocalVariables(commandContext, context); + Statement.runAll(trigger, context); + Variables.clearLocalVariables(context); + }); + return null; + } + }; }; - }; + } else { + this.hyCommand = new AbstractCommandCollection(this.commandName, description) { + + }; + } this.args.forEach((key, arg) -> { if (arg.isOptional()) { OptionalArg optionalArg = hyCommand.withOptionalArg(key, arg.getDescription(), arg.getType()); @@ -192,7 +205,7 @@ protected void execute(@NotNull CommandContext commandContext, @NotNull World wo } } }); - return List.of(trigger); + return List.of(); } private void setupArg(CommandArg arg) { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java b/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java index 88b9fa6a..ad0c3f9e 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/docs/JsonDocPrinter.java @@ -86,8 +86,10 @@ public void printDocs() { // FUNCTIONS Utils.log(this.sender, "Printing functions"); - // TODO functions need to be linked to an addon or something printFunctions(mainDocs, this.registration); + if (this.includeSkriptParser) { + printFunctions(mainDocs, parserRegistration); + } // EFFECTS Utils.log(this.sender, "Printing effects"); @@ -208,9 +210,8 @@ private void printExpressions(BsonDocument mainDocs, SkriptRegistration registra @SuppressWarnings("unchecked") private void printFunctions(BsonDocument mainDocs, SkriptRegistration registration) { - String addonKey = registration.getRegisterer().getAddonName().toLowerCase(Locale.ROOT).replace(" ", "_"); BsonArray functionsArray = mainDocs.getArray("functions", new BsonArray()); - Functions.getJavaFunctions().stream().sorted(Comparator.comparing(Function::getName)).forEach(function -> { + Functions.getJavaFunctions(registration).stream().sorted(Comparator.comparing(Function::getName)).forEach(function -> { if (function instanceof JavaFunction jf) { Documentation documentation = jf.getDocumentation(); if (documentation.isNoDoc()) return; @@ -219,10 +220,8 @@ private void printFunctions(BsonDocument mainDocs, SkriptRegistration registrati String name = documentation.getName(); if (name == null) name = function.getName(); - String id = "function:" + addonKey + ":" + name.toLowerCase(Locale.ROOT).replace(" ", "_"); - functionDoc.put("name", new BsonString(name)); - functionDoc.put("id", new BsonString(id)); + functionDoc.put("id", getId("function", name)); // Create params FunctionParameter[] parameters = jf.getParameters(); @@ -414,7 +413,11 @@ private void printDocumentation(String type, BsonDocument syntaxDoc, SyntaxInfo< List patterns = syntaxInfo.getPatterns(); if (!patterns.isEmpty()) { BsonArray patternArray = new BsonArray(); - patterns.forEach(pattern -> patternArray.add(new BsonString(pattern.toString()))); + patterns.forEach(pattern -> { + // Remove parse tags in patterns, ex: "(blah:blah|bloop:bloop)" + String removedParseTags = pattern.toString().replaceAll("[^()\\[\\]|:\\s]*:", ""); + patternArray.add(new BsonString(removedParseTags)); + }); syntaxDoc.put("patterns", patternArray); } 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 53e5b18b..b350506a 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 @@ -111,7 +111,8 @@ public static void printDocs() { Utils.log("Printing functions"); file = getFile("functions"); writer = new PrintWriter(file, StandardCharsets.UTF_8); - printFunctions(writer); + printFunctions(writer, registration); + printFunctions(writer, Parser.getMainRegistration()); writer.close(); Utils.log("Documentation printed!"); @@ -172,7 +173,8 @@ private static void printStructures(PrintWriter writer, SkriptRegistration regis }); } - private static void printExpressions(PrintWriter exprWriter, PrintWriter condWriter, PrintWriter exprEffWriter, SkriptRegistration registration) { + private static void printExpressions(PrintWriter exprWriter, PrintWriter condWriter, + PrintWriter exprEffWriter, SkriptRegistration registration) { List>> values = new ArrayList<>(registration.getExpressions().values()); values.sort(Comparator.comparing(k -> k.getFirst().getSyntaxClass().getSimpleName())); @@ -199,8 +201,8 @@ private static void printExpressions(PrintWriter exprWriter, PrintWriter condWri } @SuppressWarnings("unchecked") - private static void printFunctions(PrintWriter writer) { - Functions.getJavaFunctions().stream().sorted(Comparator.comparing(Function::getName)).forEach(function -> { + private static void printFunctions(PrintWriter writer, SkriptRegistration reg) { + Functions.getJavaFunctions(reg).stream().sorted(Comparator.comparing(Function::getName)).forEach(function -> { if (function instanceof JavaFunction jf) { FunctionParameter[] parameters = jf.getParameters(); @@ -299,7 +301,8 @@ private static void printDocumentation(String type, PrintWriter writer, Document // Asset store stuff gets really long, so plop them on another page // GitHub's wiki pages seem to have a limit Utils.log("Creating asset store link for: " + documentation.getName()); - writer.println("[Click Here](https://github.com/SkriptDev/HySkript/wiki/_usage-" + documentation.getName().replace(" ", "-") + ")"); + writer.println("[Click Here](https://github.com/SkriptDev/HySkript/wiki/_usage-" + + documentation.getName().replace(" ", "-") + ")"); File usageFile = getFile("_usage-" + documentation.getName()); try { List values = new ArrayList<>(); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/event/WorldContext.java b/src/main/java/com/github/skriptdev/skript/api/skript/event/WorldContext.java new file mode 100644 index 00000000..13437904 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/event/WorldContext.java @@ -0,0 +1,10 @@ +package com.github.skriptdev.skript.api.skript.event; + +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.TriggerContext; + +public interface WorldContext extends TriggerContext { + + World getWorld(); + +} 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 deleted file mode 100644 index 25dc47f8..00000000 --- a/src/main/java/com/github/skriptdev/skript/api/skript/registration/AssetStoreRegistry.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.github.skriptdev.skript.api.skript.registration; - -import com.hypixel.hytale.assetstore.JsonAsset; -import com.hypixel.hytale.assetstore.map.DefaultAssetMap; -import io.github.syst3ms.skriptparser.registration.SkriptRegistration; -import io.github.syst3ms.skriptparser.registration.SkriptRegistration.TypeRegistrar; - -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; - -/** - * Registry related to {@link com.hypixel.hytale.assetstore.AssetStore AssetStores}. - * - * @param Asset store type - */ -public class AssetStoreRegistry { - - private final Map assetStoreValues = new TreeMap<>(); - - /** - * Create a new {@link TypeRegistrar} with for an {@link com.hypixel.hytale.assetstore.AssetStore AssetStore}. - * Remember to register it with {@link TypeRegistrar#register()} - * - * @param registration The registration to register to - * @param c Asset store class - * @param assetMap Asset map for the store - * @param name Name of the new type - * @param pattern Pattern for the type - * @param Key type for the asset map - * @param Asset type - * @return New {@link TypeRegistrar} - */ - public static > TypeRegistrar register( - SkriptRegistration registration, - Class c, - DefaultAssetMap assetMap, - String name, - String pattern) { - - AssetStoreRegistry store = new AssetStoreRegistry<>(); - 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 deleted file mode 100644 index bc1dcd22..00000000 --- a/src/main/java/com/github/skriptdev/skript/api/skript/registration/EnumRegistry.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.github.skriptdev.skript.api.skript.registration; - -import io.github.syst3ms.skriptparser.registration.SkriptRegistration; -import io.github.syst3ms.skriptparser.registration.SkriptRegistration.TypeRegistrar; - -import java.util.Locale; -import java.util.Map; -import java.util.TreeMap; - -/** - * Registry related to enums. - * - * @param Enum class. - */ -public class EnumRegistry> { - - private final Map values = new TreeMap<>(); - - /** - * Create a new {@link TypeRegistrar} with for an enum. - * Remember to register it with {@link TypeRegistrar#register()} - * - * @param registration Registration to register to - * @param enumClass Enum class to register - * @param name Name of the new type - * @param pattern Pattern for the type - * @param Enum class - * @return New {@link TypeRegistrar} - */ - public static > TypeRegistrar register(SkriptRegistration registration, - Class enumClass, - String name, - String pattern) { - if (enumClass == null || !enumClass.isEnum()) { - throw new IllegalArgumentException("Cannot register null enum"); - } - EnumRegistry eEnumRegistry = new EnumRegistry<>(); - for (T e : enumClass.getEnumConstants()) { - eEnumRegistry.values.put(e.name(), e); - } - 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/SkriptRegistration.java b/src/main/java/com/github/skriptdev/skript/api/skript/registration/SkriptRegistration.java index 39ae7e25..ba0dd3e0 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,10 +1,26 @@ package com.github.skriptdev.skript.api.skript.registration; import com.github.skriptdev.skript.api.utils.Utils; +import com.hypixel.hytale.assetstore.JsonAsset; +import com.hypixel.hytale.assetstore.map.DefaultAssetMap; +import io.github.syst3ms.skriptparser.docs.Documentation; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import io.github.syst3ms.skriptparser.types.Type; +import io.github.syst3ms.skriptparser.types.TypeManager; +import io.github.syst3ms.skriptparser.types.changers.Arithmetic; +import io.github.syst3ms.skriptparser.types.changers.Changer; +import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; +import org.jetbrains.annotations.Nullable; +import java.util.Iterator; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import java.util.function.Function; +import java.util.function.Supplier; /** * An extension of {@link io.github.syst3ms.skriptparser.registration.SkriptRegistration} with additional features. @@ -23,4 +39,295 @@ public List register() { return List.of(); } + /** + * Register a new asset store type. + * + * @param assetClass Class of AssetStore + * @param assetMap AssetMap for the AssetStore + * @param name Name of the AssetStore + * @param pattern Pattern for the AssetStore + * @param Type of Asset + * @param Type of Asset ID + * @return AssetStoreRegistrar for further configuration + */ + public , K extends String> AssetStoreRegistrar newAssetStoreType(Class assetClass, + DefaultAssetMap assetMap, + String name, + String pattern) { + return new AssetStoreRegistrar<>(assetClass, assetMap, name, pattern); + } + + public class AssetStoreRegistrar, K extends String> { + private final Class assetClass; + private final String baseName; + private final String pattern; + private Function toStringFunction = o -> Objects.toString(o, TypeManager.NULL_REPRESENTATION); + private final Function literalParser; + @Nullable + private Changer defaultChanger; + @Nullable + private Arithmetic arithmetic; + @Nullable + private TypeSerializer serializer; + private final Supplier> supplier; + private final Documentation documentation = new Documentation(); + @SuppressWarnings("FieldCanBeLocal") + private final DefaultAssetMap assetMap; // Store for later use?!?! + private final Map assetStoreValues = new TreeMap<>(); + + public AssetStoreRegistrar(Class assetClass, DefaultAssetMap assetMap, String baseName, String pattern) { + this.assetClass = assetClass; + this.baseName = baseName; + this.pattern = pattern; + this.assetMap = assetMap; + assetMap.getAssetMap().forEach((key, value) -> this.assetStoreValues.put(key.toLowerCase(Locale.ROOT), value)); + this.supplier = () -> assetStoreValues.values().stream().iterator(); + this.literalParser = s -> this.assetStoreValues.get(s.toLowerCase(Locale.ROOT).replace(" ", "_")); + + } + + /** + * @param toStringFunction a function converting an instance of the type to a String + * @return the registrar + */ + public AssetStoreRegistrar toStringFunction(Function toStringFunction) { + this.toStringFunction = c -> c == null ? TypeManager.NULL_REPRESENTATION : toStringFunction.apply(c); + return this; + } + + /** + * @param defaultChanger a default {@link Changer} for this type + * @return the registrar + */ + public AssetStoreRegistrar defaultChanger(Changer defaultChanger) { + this.defaultChanger = defaultChanger; + return this; + } + + /** + * @param arithmetic a default {@link Arithmetic} for this type + * @return the registrar + */ + public AssetStoreRegistrar arithmetic(Arithmetic arithmetic) { + this.arithmetic = arithmetic; + return this; + } + + /** + * @param serializer add a type serializer that allows the type to be saved to databases. + * @return the registrar + */ + public AssetStoreRegistrar serializer(TypeSerializer serializer) { + this.serializer = serializer; + return this; + } + + public AssetStoreRegistrar noDoc() { + this.documentation.noDoc(); + return this; + } + + public AssetStoreRegistrar experimental() { + this.documentation.experimental(); + return this; + } + + public AssetStoreRegistrar experimental(String message) { + this.documentation.experimental(message); + return this; + } + + public AssetStoreRegistrar name(String name) { + this.documentation.setName(name); + return this; + } + + public AssetStoreRegistrar description(String... description) { + this.documentation.setDescription(description); + return this; + } + + public AssetStoreRegistrar examples(String... examples) { + this.documentation.setExamples(examples); + return this; + } + + public AssetStoreRegistrar usage(String usage) { + this.documentation.setUsage(usage); + return this; + } + + public AssetStoreRegistrar since(String since) { + this.documentation.setSince(since); + return this; + } + + /** + * Adds this type to the list of currently registered syntaxes + */ + public void register() { + AssetStoreType assetStoreType = new AssetStoreType<>(assetClass, baseName, pattern, literalParser, toStringFunction, + defaultChanger, arithmetic, documentation, serializer, this.supplier); + newTypes = true; + types.add(assetStoreType); + } + } + + public static class AssetStoreType, K extends String> extends Type { + + public AssetStoreType(Class c, String baseName, String pattern, Function literalParser, + Function toStringFunction, Changer defaultChanger, + Arithmetic arithmetic, Documentation documentation, TypeSerializer serializer, + Supplier> supplier) { + super(c, baseName, pattern, literalParser, toStringFunction, defaultChanger, + arithmetic, documentation, serializer, supplier); + } + } + + /** + * Create a new Enum type. + * + * @param enumClass Class of the Enum + * @param baseName Name of the Enum type + * @param pattern Pattern for the Enum type + * @param Type parameter for the Enum + * @return EnumRegistrar for configuring the Enum type + */ + public > EnumRegistrar newEnumType(Class enumClass, String baseName, String pattern) { + return new EnumRegistrar<>(enumClass, baseName, pattern); + } + + public class EnumRegistrar> { + private final Class enumClass; + private final String baseName; + private final String pattern; + + private Function toStringFunction; + private final Function literalParser; + @Nullable + private Changer defaultChanger; + @Nullable + private Arithmetic arithmetic; + @Nullable + private TypeSerializer serializer; + private final Supplier> supplier; + private final Documentation documentation = new Documentation(); + private final Map values = new TreeMap<>(); + + public EnumRegistrar(Class enumClass, String name, String pattern) { + for (E e : enumClass.getEnumConstants()) { + this.values.put(e.name().toLowerCase(Locale.ROOT), e); + } + this.enumClass = enumClass; + this.baseName = name; + this.pattern = pattern; + this.documentation.setUsage(String.join(", ", this.values.keySet())); + this.supplier = () -> this.values.values().iterator(); + this.literalParser = s -> this.values.get(s.toLowerCase(Locale.ROOT).replace(" ", "_")); + this.toStringFunction = e -> e.name().toLowerCase(Locale.ROOT); + } + + + /** + * @param toStringFunction a function converting an instance of the type to a String + * @return the registrar + */ + public EnumRegistrar toStringFunction(Function toStringFunction) { + this.toStringFunction = c -> c == null ? TypeManager.NULL_REPRESENTATION : toStringFunction.apply(c); + return this; + } + + /** + * @param defaultChanger a default {@link Changer} for this type + * @return the registrar + */ + public EnumRegistrar defaultChanger(Changer defaultChanger) { + this.defaultChanger = defaultChanger; + return this; + } + + /** + * @param arithmetic a default {@link Arithmetic} for this type + * @return the registrar + */ + public EnumRegistrar arithmetic(Arithmetic arithmetic) { + this.arithmetic = arithmetic; + return this; + } + + /** + * @param serializer add a type serializer that allows the type to be saved to databases. + * @return the registrar + */ + public EnumRegistrar serializer(TypeSerializer serializer) { + this.serializer = serializer; + return this; + } + + /** + * Prevent docs from being printed. + * + * @return the registrar + */ + public EnumRegistrar noDoc() { + this.documentation.noDoc(); + return this; + } + + public EnumRegistrar experimental() { + this.documentation.experimental(); + return this; + } + + public EnumRegistrar experimental(String message) { + this.documentation.experimental(message); + return this; + } + + public EnumRegistrar name(String name) { + this.documentation.setName(name); + return this; + } + + public EnumRegistrar description(String... description) { + this.documentation.setDescription(description); + return this; + } + + public EnumRegistrar examples(String... examples) { + this.documentation.setExamples(examples); + return this; + } + + public EnumRegistrar usage(String usage) { + this.documentation.setUsage(usage); + return this; + } + + public EnumRegistrar since(String since) { + this.documentation.setSince(since); + return this; + } + + /** + * Adds this type to the list of currently registered syntaxes + */ + public void register() { + EnumType enumType = new EnumType<>(this.enumClass, baseName, pattern, literalParser, toStringFunction, + defaultChanger, arithmetic, documentation, serializer, this.supplier); + newTypes = true; + types.add(enumType); + } + } + + public static class EnumType> extends Type { + public EnumType(Class c, String baseName, String pattern, Function literalParser, + Function toStringFunction, Changer defaultChanger, + Arithmetic arithmetic, Documentation documentation, TypeSerializer serializer, + Supplier> supplier) { + super(c, baseName, pattern, literalParser, toStringFunction, defaultChanger, + arithmetic, documentation, serializer, supplier); + } + } + } 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 0798ae98..f3335856 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 @@ -110,7 +110,6 @@ private void startFileWatcher() { } catch (InterruptedException e) { // Restore interrupt status and exit Thread.currentThread().interrupt(); - Utils.error("Variable Save Thread was interrupted, stopping..."); } },"HySkript-Variable-Save-Thread"); @@ -298,12 +297,14 @@ protected Object deserialize(String varName, @NotNull String typeName, @NotNull } 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); + 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); + 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); diff --git a/src/main/java/com/github/skriptdev/skript/api/utils/FileUtils.java b/src/main/java/com/github/skriptdev/skript/api/utils/FileUtils.java new file mode 100644 index 00000000..b947e486 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/utils/FileUtils.java @@ -0,0 +1,45 @@ +package com.github.skriptdev.skript.api.utils; + +import com.github.skriptdev.skript.plugin.HySk; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Collections; + +/** + * Utilities for working with files. + */ +public class FileUtils { + + public static void copyFromJar(String sourceInsideJar, Path targetDir) throws IOException, URISyntaxException { + // Get the URI of the resource inside the JAR + URI uri = HySk.class.getResource(sourceInsideJar).toURI(); + + // If the resource is inside a JAR, we need to create a FileSystem for it + try (FileSystem jarFileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + Path pathInJar = jarFileSystem.getPath(sourceInsideJar); + + // Walk the tree and copy everything + Files.walk(pathInJar).forEach(source -> { + try { + // Create the corresponding path in the target directory + Path destination = targetDir.resolve(pathInJar.relativize(source).toString()); + + if (Files.isDirectory(source)) { + if (!Files.exists(destination)) Files.createDirectories(destination); + } else { + Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + throw new RuntimeException("Failed to copy: " + source, e); + } + }); + } +} +} 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 54584b25..1a402090 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 @@ -44,7 +44,8 @@ public static void log(IMessageReceiver receiver, Level level, String message, O if (receiver == null) { HySk.getInstance().getLogger().at(level).log(message); } else { - Color color = level == Level.SEVERE ? Color.RED : level == Level.WARNING ? Color.YELLOW : level == Level.FINE ? Color.PINK : Color.WHITE; + Color color = level == Level.SEVERE ? Color.RED : level == Level.WARNING ? + Color.YELLOW : level == Level.FINE ? Color.PINK : Color.WHITE; Message coloredMessage = Message.raw(message).color(color); Message m = Message.empty().insert(CORE_PREFIX).insert(coloredMessage); 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 1f18166a..6b190307 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -1,5 +1,6 @@ package com.github.skriptdev.skript.plugin; +import com.github.skriptdev.skript.api.skript.ErrorHandler; 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; @@ -9,10 +10,10 @@ import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.command.EffectCommands; import com.github.skriptdev.skript.plugin.elements.ElementRegistration; +import com.github.skriptdev.skript.plugin.elements.events.EventHandler; 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.Statement; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.SkriptLogger; import io.github.syst3ms.skriptparser.registration.SkriptAddon; @@ -20,7 +21,6 @@ import org.jetbrains.annotations.NotNull; import java.nio.file.Path; -import java.util.logging.Level; /** * Main class for the Skript aspects of HySkript. @@ -85,6 +85,9 @@ private void setup() { this.addonLoader = new AddonLoader(); this.addonLoader.loadAddonsFromFolder(); + // SETUP ERROR HANDLER + ErrorHandler.init(); + // LOAD VARIABLES loadVariables(); @@ -99,37 +102,15 @@ private void setup() { 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 LISTENERS + EventHandler.shutdown(); + + // SHUTDOWN SCRIPTS + this.scriptsLoader.shutdown(); + // SHUTDOWN VARIABLES Utils.log("Saving variables..."); Variables.shutdown(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java b/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java index fc285e4e..0bfe8b7a 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/command/EffectCommands.java @@ -7,6 +7,7 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.player.PlayerChatEvent; 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 com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; @@ -17,6 +18,7 @@ import io.github.syst3ms.skriptparser.parsing.ParserState; import io.github.syst3ms.skriptparser.parsing.SyntaxParser; import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; +import io.github.syst3ms.skriptparser.variables.Variables; import java.util.List; import java.util.Optional; @@ -35,11 +37,22 @@ public static void register(Skript skript, String token, boolean allowOps, Strin PlayerRef sender = event.getSender(); // PERM CHECK - // Pretty sure the OP stuff doesn't work PermissionsModule perm = PermissionsModule.get(); - Set groupsForUser = perm.getGroupsForUser(sender.getUuid()); if (!allowOps) { - if (groupsForUser.contains("OP") && !perm.hasPermission(sender.getUuid(), permission)) { + PermissionProvider provider = perm.getFirstPermissionProvider(); + Set groupsForUser = perm.getGroupsForUser(sender.getUuid()); + if (groupsForUser.contains("OP")) { + boolean can = false; + // Check all groups + for (String group : groupsForUser) { + // If the group has the explicit permission, they can use effect commands + if (provider.getGroupPermissions(group).contains(permission)) { + can = true; + break; + } + } + if (!can) return; + } else { return; } } else if (!perm.hasPermission(sender.getUuid(), permission)) { @@ -83,14 +96,18 @@ public static void register(Skript skript, String token, boolean allowOps, Strin Utils.log(sender, logEntry); } - if (world.isInThread()) { + Runnable code = () -> { Player player = world.getEntityStore().getStore().getComponent(reference, Player.getComponentType()); - effect.walk(new PlayerEffectContext(player)); + PlayerEffectContext effectContext = new PlayerEffectContext(player); + effect.walk(effectContext); + // Clear local variables to prevent memory leaks + Variables.clearLocalVariables(effectContext); + }; + + if (world.isInThread()) { + code.run(); } else { - world.execute(() -> { - Player player = world.getEntityStore().getStore().getComponent(reference, Player.getComponentType()); - effect.walk(new PlayerEffectContext(player)); - }); + world.execute(code); } } 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 b38748aa..d6ffa182 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 @@ -37,8 +37,8 @@ public class SkriptCommand extends AbstractCommandCollection { * @hidden */ public SkriptCommand(CommandRegistry registry) { - super("skript", "Skript commands"); - addAliases("sk"); + super("skript", "HySkript commands"); + addAliases("sk", "hyskript", "hysk"); // Keep these in alphabetical order addSubCommand(addonsCommand()); @@ -56,7 +56,7 @@ private static class ReloadCommand extends AbstractCommand { protected ReloadCommand() { super("reload", "Reloads scripts."); this.stringRequiredArg = withRequiredArg("script", "A script to reload", ArgTypes.STRING); - addSubCommand(new AbstractCommand("all", "Reloads all scripts.") { + addSubCommand(new AbstractCommand("scripts", "Reloads all scripts.") { @Override protected CompletableFuture execute(@NotNull CommandContext commandContext) { return CompletableFuture.runAsync(() -> { 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 ac48ae54..7954536a 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 @@ -55,7 +55,7 @@ public void registerElements() { EventHandler.register(this.registration); // FUNCTIONS - DefaultFunctions.register(); + DefaultFunctions.register(this.registration); // COMMAND ScriptCommand.register(this.registration); 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 e8c95b26..7f5c4026 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 @@ -2,6 +2,8 @@ import com.github.skriptdev.skript.api.skript.command.ScriptCommandBuilder; import com.github.skriptdev.skript.api.skript.command.ScriptCommandParent; +import com.github.skriptdev.skript.api.skript.event.PlayerContext; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.world.World; @@ -30,14 +32,10 @@ public static class ScriptCommandContext implements TriggerContext { private final String command; private final CommandSender sender; - private final Player player; - private final World world; - public ScriptCommandContext(String command, CommandSender sender, Player player, World world) { + public ScriptCommandContext(String command, CommandSender sender) { this.command = command; this.sender = sender; - this.player = player; - this.world = world; } public String getCommand() { @@ -48,27 +46,56 @@ public CommandSender getSender() { return this.sender; } - public World getWorld() { - return this.world; + @Override + public String getName() { + return "command context"; } + } + + public static class PlayerScriptCommandContext extends ScriptCommandContext implements PlayerContext, WorldContext { + private final Player player; + + public PlayerScriptCommandContext(String command, Player player) { + super(command, player); + this.player = player; + } + + @Override public Player getPlayer() { - if (this.player == null && this.sender instanceof Player p) return p; return this.player; } @Override - public String getName() { - return "command context"; + public World getWorld() { + return this.player.getWorld(); } + } - public static void register(SkriptRegistration registration) { - registration.newEvent(ScriptCommand.class, + public static class WorldScriptCommandContext extends ScriptCommandContext implements WorldContext { + + private final World world; + + public WorldScriptCommandContext(String command, CommandSender sender, World world) { + super(command, sender); + this.world = world; + } + + @Override + public World getWorld() { + return this.world; + } + } + + public static void register(SkriptRegistration reg) { + reg.newEvent(ScriptCommand.class, "*[global] command <.+>", "*player command <.+>", "*world command <.+>") - .setHandledContexts(ScriptCommandContext.class) + .setHandledContexts(ScriptCommandContext.class, + PlayerScriptCommandContext.class, + WorldScriptCommandContext.class) .name("Command") .description("Create a command.", "**Command Format**:", @@ -88,7 +115,8 @@ public static void register(SkriptRegistration registration) { "**Entries**:", "- `Description` = The description for your command that will show in the commands gui (optional).", "- `Permission` = The permission required to execute the command (optional).", - "- `Aliases` = A list of aliases for the command (optional).") + "- `Aliases` = A list of aliases for the command (optional).", + "- `Trigger` = The code that will be executed when the command is executed (optional).") .examples("command /kill:", "\tdescription: Kill all the players", "\ttrigger:", @@ -119,26 +147,14 @@ public static void register(SkriptRegistration registration) { .since("1.0.0") .register(); - registration.newSingleContextValue(ScriptCommandContext.class, Player.class, - "player", ScriptCommandContext::getPlayer) - .setUsage(Usage.EXPRESSION_OR_ALONE) - .register(); - - registration.newSingleContextValue(ScriptCommandContext.class, CommandSender.class, + reg.newSingleContextValue(ScriptCommandContext.class, CommandSender.class, "sender", ScriptCommandContext::getSender) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); - - registration.newSingleContextValue(ScriptCommandContext.class, World.class, - "world", ScriptCommandContext::getWorld) - .setUsage(Usage.EXPRESSION_OR_ALONE) - .register(); - - registration.newSingleContextValue(ScriptCommandContext.class, String.class, + reg.newSingleContextValue(ScriptCommandContext.class, String.class, "command", ScriptCommandContext::getCommand) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); - } private ScriptCommandBuilder commandBuilder; @@ -154,7 +170,7 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, P @Override public List loadSection(@NotNull FileSection section, @NotNull ParserState parserState, @NotNull SkriptLogger logger) { - List statements = this.commandBuilder.setupCommand(section, parserState, logger); + List statements = this.commandBuilder.setupCommand(section, parserState, logger, this.commandType); this.commandBuilder.build(null); return statements; } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptSubCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptSubCommand.java index 991e54f1..1d2d2475 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptSubCommand.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/command/ScriptSubCommand.java @@ -39,8 +39,7 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, @ @Override public void loadChild(ScriptCommandBuilder parent, SkriptLogger logger) { - this.commandBuilder.setupCommand(this.section, this.parserState, logger); - this.commandBuilder.commandType = parent.getCommandType(); + this.commandBuilder.setupCommand(this.section, this.parserState, logger, parent.getCommandType()); this.commandBuilder.build(parent); } 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 4d7e02f2..d5851a83 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 @@ -2,10 +2,13 @@ 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.EffDamage; 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.player.EffConnect; +import com.github.skriptdev.skript.plugin.elements.effects.other.EffExecuteCommand; import com.github.skriptdev.skript.plugin.elements.effects.other.EffSendMessage; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffTeleport; import com.github.skriptdev.skript.plugin.elements.effects.player.EffBan; @@ -13,7 +16,10 @@ import com.github.skriptdev.skript.plugin.elements.effects.other.EffCancelEvent; import com.github.skriptdev.skript.plugin.elements.effects.other.EffDelay; import com.github.skriptdev.skript.plugin.elements.effects.player.EffKick; +import com.github.skriptdev.skript.plugin.elements.effects.other.EffSendTitle; import com.github.skriptdev.skript.plugin.elements.effects.world.EffChunkRegenerate; +import com.github.skriptdev.skript.plugin.elements.effects.world.EffExplosion; +import com.github.skriptdev.skript.plugin.elements.effects.entity.EffInteraction; public class EffectHandler { @@ -22,8 +28,10 @@ public static void register(SkriptRegistration registration) { EffBreakBlock.register(registration); // ENTITY + EffDamage.register(registration); EffDropItem.register(registration); EffEntityEffect.register(registration); + EffInteraction.register(registration); EffKill.register(registration); EffSpawnEntity.register(registration); EffTeleport.register(registration); @@ -32,14 +40,18 @@ public static void register(SkriptRegistration registration) { EffBroadcast.register(registration); EffCancelEvent.register(registration); EffDelay.register(registration); + EffExecuteCommand.register(registration); EffSendMessage.register(registration); + EffSendTitle.register(registration); // PLAYER EffBan.register(registration); + EffConnect.register(registration); EffKick.register(registration); // WORLD EffChunkRegenerate.register(registration); + EffExplosion.register(registration); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffDamage.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffDamage.java new file mode 100644 index 00000000..2f8051f5 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffDamage.java @@ -0,0 +1,145 @@ +package com.github.skriptdev.skript.plugin.elements.effects.entity; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.utils.Utils; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.modules.entity.damage.Damage; +import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; +import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems; +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.util.Optional; + +public class EffDamage extends Effect { + + @SuppressWarnings("NotNullFieldNotInitialized") + private static @NotNull DamageCause DEFAULT_CAUSE; + + public static void register(SkriptRegistration reg) { + DamageCause cause = DamageCause.getAssetMap().getAsset("Environment"); + if (cause == null) { + Utils.error("Default damage cause 'Environmental' not found. Skipping EffDamage registration."); + return; + } + DEFAULT_CAUSE = cause; + reg.newEffect(EffDamage.class, "damage %entities% by %number%", + "make %entity% damage %entities% by %number%", + "damage %entities% by %number% with cause %damagecause%", + "make %entity% damage %entities% by %number% with cause %damagecause%") + .name("Damage") + .description("Damages entities with a specified amount of damage.") + .examples("damage player by 5", + "make player damage {_e} by 5", + "damage entities in radius 5 around player by 5 with cause Environmental", + "make player damage {_e::*} by 5 with cause Environmental") + .since("INSERT VERSION") + .register(); + } + + private Expression entities; + private Expression sourceEntity; + private Expression damage; + private Expression cause; + private int pattern; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + switch (matchedPattern) { + case 0 -> { + this.entities = (Expression) expressions[0]; + this.damage = (Expression) expressions[1]; + } + case 1 -> { + this.sourceEntity = (Expression) expressions[0]; + this.entities = (Expression) expressions[1]; + this.damage = (Expression) expressions[2]; + } + case 2 -> { + this.entities = (Expression) expressions[0]; + this.damage = (Expression) expressions[1]; + this.cause = (Expression) expressions[2]; + } + case 3 -> { + this.sourceEntity = (Expression) expressions[0]; + this.entities = (Expression) expressions[1]; + this.damage = (Expression) expressions[2]; + this.cause = (Expression) expressions[3]; + } + default -> { + Utils.error("Invalid pattern for EffDamage effect: " + matchedPattern); + return false; + } + } + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + Number number = this.damage.getSingle(ctx).orElse(null); + if (number == null) return; + + float damage = number.floatValue(); + DamageCause cause; + if (this.cause != null) { + cause = this.cause.getSingle(ctx).orElse(null); + if (cause == null) cause = DEFAULT_CAUSE; + } else { + cause = DEFAULT_CAUSE; + } + + Damage.Source source = Damage.NULL_SOURCE; + if (this.sourceEntity != null) { + Optional single = this.sourceEntity.getSingle(ctx); + if (single.isPresent()) { + Entity sourceEntity = single.get(); + Ref reference = sourceEntity.getReference(); + if (reference != null) { + source = new Damage.EntitySource(sourceEntity.getReference()); + } + } + } + + for (Entity entity : this.entities.getArray(ctx)) { + if (entity instanceof LivingEntity livingEntity) { + causeDamage(livingEntity, source, cause, damage); + } + } + } + + private void causeDamage(@NotNull LivingEntity entity, @NotNull Damage.Source source, @NotNull DamageCause cause, float amount) { + Ref reference = entity.getReference(); + if (reference == null) return; + + Store store = reference.getStore(); + Damage damage = new Damage(source, cause, amount); + + DamageSystems.executeDamage(reference, store, damage); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return switch (this.pattern) { + case 0 -> "damage " + this.entities.toString(ctx, debug) + " by " + this.damage.toString(ctx, debug); + case 1 -> + "make " + this.sourceEntity.toString(ctx, debug) + " damage " + this.entities.toString(ctx, debug) + + " by " + this.damage.toString(ctx, debug); + case 2 -> "damage " + this.entities.toString(ctx, debug) + " by " + this.damage.toString(ctx, debug) + + " with cause " + this.cause.toString(ctx, debug); + case 3 -> + "make " + this.sourceEntity.toString(ctx, debug) + " damage " + this.entities.toString(ctx, debug) + + " by " + this.damage.toString(ctx, debug) + " with cause " + this.cause.toString(ctx, debug); + default -> throw new IllegalStateException("Unexpected value: " + this.pattern); + }; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffInteraction.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffInteraction.java new file mode 100644 index 00000000..fe028471 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffInteraction.java @@ -0,0 +1,138 @@ +package com.github.skriptdev.skript.plugin.elements.effects.entity; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.utils.Utils; +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.Vector3i; +import com.hypixel.hytale.protocol.BlockPosition; +import com.hypixel.hytale.protocol.InteractionType; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.InteractionChain; +import com.hypixel.hytale.server.core.entity.InteractionContext; +import com.hypixel.hytale.server.core.entity.InteractionManager; +import com.hypixel.hytale.server.core.modules.interaction.InteractionModule; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; +import com.hypixel.hytale.server.core.universe.world.World; +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.util.Objects; + +public class EffInteraction extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffInteraction.class, "perform [root] interaction %rootinteraction% on %entity%", + "perform [root] interaction %rootinteraction% on %entity% at %location%", + "perform %interactiontype% [root] interaction %rootinteraction% on %entity%", + "perform %interactiontype% [root] interaction %rootinteraction% on %entity% at %location%") + .name("Perform Interaction") + .description("Performs a root interaction on an entity.", + "If an InteractionType is not specified, the `primary` InteractionType will be performed.") + .examples("perform interaction Shovel_Dig on player") + .since("INSERT VERSION") + .register(); + } + + private Expression interactionType; + private Expression interaction; + private Expression entity; + private Expression location; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + if (matchedPattern == 0) { + this.interaction = (Expression) expressions[0]; + this.entity = (Expression) expressions[1]; + } else if (matchedPattern == 1) { + this.interaction = (Expression) expressions[0]; + this.entity = (Expression) expressions[1]; + this.location = (Expression) expressions[2]; + } else if (matchedPattern == 2) { + this.interactionType = (Expression) expressions[0]; + this.interaction = (Expression) expressions[1]; + this.entity = (Expression) expressions[2]; + } else { + this.interactionType = (Expression) expressions[0]; + this.interaction = (Expression) expressions[1]; + this.entity = (Expression) expressions[2]; + this.location = (Expression) expressions[3]; + } + return true; + } + + @SuppressWarnings("removal") + @Override + protected void execute(@NotNull TriggerContext ctx) { + RootInteraction interaction = this.interaction.getSingle(ctx).orElse(null); + if (interaction == null) return; + + InteractionType type; + if (this.interactionType != null) { + InteractionType interactionType1 = this.interactionType.getSingle(ctx).orElse(null); + type = Objects.requireNonNullElse(interactionType1, InteractionType.Primary); + } else { + type = InteractionType.Primary; + } + + Entity entity = this.entity.getSingle(ctx).orElse(null); + if (entity == null) return; + + World world = entity.getWorld(); + if (world == null) return; + + BlockPosition blockPosition; + if (this.location != null) { + Location location = this.location.getSingle(ctx).orElse(null); + if (location != null) { + Vector3i position = location.getPosition().toVector3i(); + blockPosition = new BlockPosition(position.getX(), position.getY(), position.getZ()); + } else { + blockPosition = null; + } + } else { + blockPosition = null; + } + + Runnable worldRunnable = () -> { + Ref ref = entity.getReference(); + if (ref == null) return; + + Store store = world.getEntityStore().getStore(); + InteractionManager manager = store.getComponent(ref, InteractionModule.get().getInteractionManagerComponent()); + if (manager == null) return; + + InteractionContext context = InteractionContext.forInteraction(manager, ref, type, store); + InteractionChain chain; + if (blockPosition == null) { + Utils.warn("no position specified for interaction"); + chain = manager.initChain(type, context, interaction, true); + } else { + Utils.warn("interaction at position " + blockPosition); + chain = manager.initChain(type, context, interaction, entity.getNetworkId(), blockPosition, true); + } + manager.queueExecuteChain(chain); + }; + + // Ensure running on the correct thread + if (world.isInThread()) { + worldRunnable.run(); + } else { + world.execute(worldRunnable); + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = this.interactionType != null ? this.interactionType.toString(ctx, debug) + " " : ""; + return "perform " + type + "interaction " + this.interaction.toString(ctx, debug) + + " on " + this.entity.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffDelay.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffDelay.java index 468ba060..05a6dd0d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffDelay.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffDelay.java @@ -1,5 +1,7 @@ package com.github.skriptdev.skript.plugin.elements.effects.other; +import com.github.skriptdev.skript.api.skript.event.PlayerContext; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.api.utils.Utils; import com.hypixel.hytale.server.core.HytaleServer; @@ -7,18 +9,18 @@ import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.lang.Effect; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Literal; import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.parsing.ParseContext; import io.github.syst3ms.skriptparser.util.DurationUtils; +import io.github.syst3ms.skriptparser.variables.VariableMap; +import io.github.syst3ms.skriptparser.variables.Variables; import org.jetbrains.annotations.NotNull; import java.time.Duration; import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; public class EffDelay extends Effect { @@ -32,30 +34,51 @@ public static void register(SkriptRegistration registration) { DurationUtils.overrideTickLength(defaultWorld.getTickStepNanos()); registration.newEffect(EffDelay.class, "(wait|halt) [for] %duration%", - "(wait|halt) (0:until|1:while) %=boolean% [for %*duration%]") + "(wait|halt) in [world] %world% [for] %duration%", + "(wait|halt) (0:until|1:while) %=boolean%", + "(wait|halt) in [world] %world% (0:until|1:while) %=boolean%") .name("Delay") - .description("Delays the execution of the next statements for a specified duration.") + .description("Delays the execution of the next statements for a specified duration.", + "Allows for conditional delays based on a boolean condition.", + "Supports execution in specific worlds and conditional.", + "If the world is not provided, a world will be chosen based on the event used.") .since("1.0.0") .register(); } private static final ScheduledExecutorService SCHEDULER = HytaleServer.SCHEDULED_EXECUTOR; private Expression duration; + private Expression world; private Expression condition; private boolean isConditional; private boolean negated; + private int pattern; @SuppressWarnings("unchecked") @Override public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { - this.isConditional = matchedPattern == 1; - if (this.isConditional) { - this.condition = (Expression) expressions[0]; - if (expressions.length == 2) - this.duration = (Literal) expressions[1]; - this.negated = parseContext.getNumericMark() == 0; - } else { - this.duration = (Expression) expressions[0]; + this.pattern = matchedPattern; + this.isConditional = matchedPattern > 1; + switch (matchedPattern) { + case 0 -> { + this.duration = (Expression) expressions[0]; + } + case 1 -> { + this.world = (Expression) expressions[0]; + this.duration = (Expression) expressions[1]; + } + case 2 -> { + this.condition = (Expression) expressions[0]; + this.negated = parseContext.getNumericMark() == 0; + } + case 3 -> { + this.world = (Expression) expressions[0]; + this.condition = (Expression) expressions[1]; + this.negated = parseContext.getNumericMark() == 0; + } + default -> { + return false; + } } return true; } @@ -65,54 +88,82 @@ protected void execute(@NotNull TriggerContext ctx) { throw new UnsupportedOperationException(); } - @SuppressWarnings("unchecked") @Override public Optional walk(@NotNull TriggerContext ctx) { - if (getNext().isEmpty()) - return Optional.empty(); + Optional nextOpt = getNext(); + if (nextOpt.isEmpty()) return Optional.empty(); + + Statement nextStatement = nextOpt.get(); + + World world; + if (this.world != null) { + world = this.world.getSingle(ctx).orElse(null); + } else if (ctx instanceof WorldContext worldContext) { + world = worldContext.getWorld(); + } else if (ctx instanceof PlayerContext playerContext) { + world = playerContext.getPlayer().getWorld(); + } else { + world = null; + } + // Copy local variables to reuse after our delay + VariableMap variableMap = Variables.copyLocalVariables(ctx); if (this.isConditional) { var cond = condition.getSingle(ctx); // The code we want to run each check. - Consumer code = exec -> { + Runnable code = () -> { if (cond.filter(b -> negated == b.booleanValue()).isPresent()) { - Statement.runAll(getNext().get(), ctx); + // Copy them back into the original context + Variables.setLocalVariables(ctx, variableMap); + Statement.runAll(nextStatement, ctx); + // Now delete it all + Variables.clearLocalVariables(ctx); + } + }; + Runnable worldTask = () -> { + if (world == null || world.isInThread()) { + code.run(); + } else { + world.execute(code); } }; - if (this.duration == null) { - SCHEDULER.scheduleAtFixedRate(() -> - code.accept(null), - 0, - DurationUtils.TICK, - TimeUnit.MILLISECONDS); - - } else { - Duration dur = ((Optional) ((Literal) this.duration).getSingle()).orElse(Duration.ZERO); - SCHEDULER.schedule(() -> { - Statement.runAll(getNext().get(), ctx); - }, dur.toMillis(), TimeUnit.MILLISECONDS); - } + SCHEDULER.scheduleAtFixedRate(worldTask, 0, DurationUtils.TICK, TimeUnit.MILLISECONDS); + } else { Optional dur = duration.getSingle(ctx); - if (dur.isEmpty()) - return getNext(); + if (dur.isEmpty()) return nextOpt; - SCHEDULER.schedule(() -> - Statement.runAll(getNext().get(), ctx), - dur.get().toMillis(), - TimeUnit.MILLISECONDS); + Runnable code = () -> { + // Copy them back into the original context + Variables.setLocalVariables(ctx, variableMap); + Statement.runAll(nextStatement, ctx); + // Now delete it all + Variables.clearLocalVariables(ctx); + }; + Runnable worldTask = () -> { + if (world == null || world.isInThread()) { + code.run(); + } else { + world.execute(code); + } + }; + + SCHEDULER.schedule(worldTask, dur.get().toMillis(), TimeUnit.MILLISECONDS); } return Optional.empty(); } @Override public String toString(@NotNull TriggerContext ctx, boolean debug) { - if (this.isConditional) { - String duration = this.duration == null ? "" : " for " + this.duration.toString(ctx, debug); - return "wait " + (this.negated ? "until" : "while") + " " + this.condition.toString(ctx, debug) + duration; - } - return "wait " + this.duration.toString(ctx, debug); + return switch (this.pattern) { + case 0 -> "wait " + this.duration.toString(ctx, debug); + case 1 -> "wait in " + this.world.toString(ctx, debug) + " for " + this.duration.toString(ctx, debug); + case 2 -> "wait " + (this.negated ? "until" : "while") + " " + this.condition.toString(ctx, debug); + case 3 -> "wait in " + this.world.toString(ctx, debug) + " " + (this.negated ? "until" : "while") + " " + + this.condition.toString(ctx, debug); + default -> "unknown"; + }; } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffExecuteCommand.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffExecuteCommand.java new file mode 100644 index 00000000..c0c79fd1 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffExecuteCommand.java @@ -0,0 +1,56 @@ +package com.github.skriptdev.skript.plugin.elements.effects.other; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.command.system.CommandManager; +import com.hypixel.hytale.server.core.command.system.CommandSender; +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; + +public class EffExecuteCommand extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffExecuteCommand.class, "execute %commandsenders% command %string%") + .name("Execute Command") + .description("Execute a command as a player or console.") + .examples("execute player command \"/inv clear\"", + "execute console command \"/stop\"") + .since("INSERT VERSION") + .register(); + } + + private Expression commandSenders; + private Expression command; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.commandSenders = (Expression) expressions[0]; + this.command = (Expression) expressions[1]; + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + String command = this.command.getSingle(ctx).orElse(null); + if (command == null) return; + + if (command.charAt(0) == '/') command = command.substring(1); + + CommandManager commandManager = CommandManager.get(); + for (CommandSender sender : commandSenders.getArray(ctx)) { + commandManager.handleCommand(sender, command); + } + + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return String.format("execute %s command %s", + this.commandSenders.toString(ctx, debug), + this.command.toString(ctx, debug)); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendTitle.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendTitle.java new file mode 100644 index 00000000..93f3e6d4 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/other/EffSendTitle.java @@ -0,0 +1,109 @@ +package com.github.skriptdev.skript.plugin.elements.effects.other; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.utils.Utils; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.PlayerRef; +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.EventTitleUtil; +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; + +public class EffSendTitle extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffSendTitle.class, "send [:major] title %string/message% to %players/playerrefs/worlds%", + "send [:major] title %string/message% with (subtitle|secondary title) %string/message% to %players/playerrefs/worlds%") + .name("Send Title") + .description("Sends a title to players/worlds with an optional subtitle.", + "The title can be major or minor, depending on the syntax used.", + "Not sure what `major` is for, it doesn't seem to do anything different.") + .examples("send major title \"Hello World!\" to player") + .since("INSERT VERSION") + .register(); + + } + + private boolean major; + private Expression title; + private Expression subtitle; + private Expression receivers; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.major = parseContext.hasMark("major"); + if (matchedPattern == 0) { + this.title = expressions[0]; + this.receivers = expressions[1]; + } else if (matchedPattern == 1) { + this.title = expressions[0]; + this.subtitle = expressions[1]; + this.receivers = expressions[2]; + } + + return true; + } + + @SuppressWarnings("removal") + @Override + protected void execute(@NotNull TriggerContext ctx) { + Object messageObject = this.title.getSingle(ctx).orElse(null); + if (messageObject == null) { + Utils.error("Cannot send title without a message."); + return; + } + + Message title; + if (messageObject instanceof String s) { + title = Message.raw(s); + } else if (messageObject instanceof Message m) { + title = m; + } else { + Utils.error("Cannot send title with invalid message type: %s", messageObject.getClass().getSimpleName()); + return; + } + + Message subtitle = Message.empty(); + if (this.subtitle != null) { + Object o = this.subtitle.getSingle(ctx).orElse(null); + if (o instanceof String s) { + subtitle = Message.raw(s); + } else if (o instanceof Message m) { + subtitle = m; + } + } + + // Defaults from the game + float duration = 4.0f; + float fadein = 1.5f; + float fadeout = 1.5f; + + for (Object object : this.receivers.getArray(ctx)) { + if (object instanceof Player player) { + EventTitleUtil.showEventTitleToPlayer(player.getPlayerRef(), title, subtitle, this.major); + } else if (object instanceof PlayerRef playerRef) { + EventTitleUtil.showEventTitleToPlayer(playerRef, title, subtitle, this.major); + } else if (object instanceof World world) { + Store store = world.getEntityStore().getStore(); + EventTitleUtil.showEventTitleToWorld(title, subtitle, this.major, null, duration, fadein, fadeout, store); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String sub = this.subtitle != null ? "with subtitle " + this.subtitle.toString(ctx, debug) : ""; + return String.format("send %s title %s %s to %s", + this.major ? "major" : "minor", + this.title.toString(ctx, debug), + sub, + this.receivers.toString(ctx, debug)); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffConnect.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffConnect.java new file mode 100644 index 00000000..751bf838 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffConnect.java @@ -0,0 +1,68 @@ +package com.github.skriptdev.skript.plugin.elements.effects.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.universe.PlayerRef; +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; + +public class EffConnect extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffConnect.class, "(connect|transfer|refer) %players/playerrefs% to %string%", + "(connect|transfer|refer) %players/playerrefs% to %string% on port %number%") + .name("Connect") + .description("Connects the specified players to another server.", + "If port is excluded, it defaults to `5520` (Hytale's default port).", + "See [PLAYER REFERRAL](https://support.hytale.com/hc/en-us/articles/45326769420827-Hytale-Server-" + + "Manual#multiserver-architecture) for more info.") + .examples("connect all players to \"https://someserver.net\"") + .register(); + } + + private Expression players; + private Expression address; + private Expression port; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.players = expressions[0]; + this.address = (Expression) expressions[1]; + if (matchedPattern == 1) { + this.port = (Expression) expressions[2]; + } + return true; + } + + @SuppressWarnings("removal") // Player#getPlayerRef (replacement?) + @Override + protected void execute(@NotNull TriggerContext ctx) { + String address = this.address.getSingle(ctx).orElse(null); + if (address == null) return; + + int port = 5520; + if (this.port != null) { + Number number = this.port.getSingle(ctx).orElse(null); + if (number != null) port = number.intValue(); + } + + for (Object o : this.players.getArray(ctx)) { + if (o instanceof PlayerRef playerRef) { + playerRef.referToServer(address, port); + } else if (o instanceof Player player) { + player.getPlayerRef().referToServer(address, port); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String port = this.port != null ? " on port " + this.port.toString(ctx, debug) : ""; + return "connect " + this.players.toString(ctx, debug) + " to " + this.address.toString(ctx, debug) + port; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/world/EffExplosion.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/world/EffExplosion.java new file mode 100644 index 00000000..dfcbdb11 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/world/EffExplosion.java @@ -0,0 +1,144 @@ +package com.github.skriptdev.skript.plugin.elements.effects.world; + +import com.github.skriptdev.skript.api.hytale.StoreUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemTool; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemToolSpec; +import com.hypixel.hytale.server.core.entity.ExplosionConfig; +import com.hypixel.hytale.server.core.entity.ExplosionUtils; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.ExplodeInteraction; +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.ChunkStore; +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.util.Optional; + +public class EffExplosion extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffExplosion.class, + "create explosion at %locations%", + "create explosion with radius %number% at %locations%", + "\"create explosion with block radius %number% [and] with entity radius %number% at %locations%\"") + .name("Explosion") + .description("Creates an explosion at the specified location(s) with " + + "customizable block and entity damage radii.") + .examples("create explosion at {location}", + "create explosion with radius 10 at {location}", + "create explosion with block radius 10 and with entity radius 15 at {location}") + .experimental("Currently no sound/particles.") + .since("INSERT VERSION") + .register(); + } + + private int pattern; + private Expression locations; + private Expression blockRadius; + private Expression entityRadius; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + this.locations = (Expression) expressions[matchedPattern]; + if (matchedPattern == 1) { + this.blockRadius = (Expression) expressions[0]; + } else if (matchedPattern == 2) { + this.blockRadius = (Expression) expressions[0]; + this.entityRadius = (Expression) expressions[1]; + } + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + Number blockRadius = 3.0f; + Number entityRadius = 5.0f; + if (this.blockRadius != null) { + Optional single = this.blockRadius.getSingle(ctx); + if (single.isPresent()) { + blockRadius = single.get(); + + // If no entity radius is provided, we just add 2 to the bock radius + // This will be fixed below if an entity radius IS provided + entityRadius = blockRadius.floatValue() + 2.0f; + } + } + + if (this.entityRadius != null) { + Optional single = this.entityRadius.getSingle(ctx); + if (single.isPresent()) { + entityRadius = single.get(); + } + } + + Config config = new Config(blockRadius, entityRadius); + + for (Location location : this.locations.getArray(ctx)) { + String worldName = location.getWorld(); + if (worldName == null) continue; + + World world = Universe.get().getWorld(worldName); + if (world == null) continue; + + Runnable worldRunnable = () -> { + Store entityStore = world.getEntityStore().getStore(); + Store chunkStoreStore = world.getChunkStore().getStore(); + CommandBuffer commandBuffer = StoreUtils.getCommandBuffer(entityStore); + + ExplosionUtils.performExplosion(ExplodeInteraction.DAMAGE_SOURCE_EXPLOSION, + location.getPosition(), + config, + null, + commandBuffer, + chunkStoreStore); + }; + + if (world.isInThread()) { + worldRunnable.run(); + } else { + world.execute(worldRunnable); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return switch (this.pattern) { + case 1 -> + "create explosion with radius " + this.blockRadius.toString(ctx, debug) + " at " + this.locations.toString(ctx, debug); + case 2 -> + "create explosion with block radius " + this.blockRadius.toString(ctx, debug) + " and entity radius " + + this.entityRadius.toString(ctx, debug) + " at " + this.locations.toString(ctx, debug); + default -> "create explosion at " + this.locations.toString(ctx, debug); + }; + } + + private static class Config extends ExplosionConfig { + + private Config(Number blockRadius, Number entityRadius) { + this.blockDamageRadius = blockRadius.intValue(); + this.entityDamageRadius = entityRadius.floatValue(); + // The stuff you have to do to make things work in Hytale... + ItemToolSpec[] specs = new ItemToolSpec[]{ + new ItemToolSpec("SoftBlocks", 2, 1), + new ItemToolSpec("Soils", 2, 1), + new ItemToolSpec("Woods", 2, 1), + new ItemToolSpec("Rocks", 2, 1), + new ItemToolSpec("Benches", 2, 1), + new ItemToolSpec("VolcanicRocks", 0.001f, 1), + }; + this.itemTool = new ItemTool(specs, 1, null); + } + } + +} 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 a04346eb..c5dce8a2 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 @@ -5,6 +5,7 @@ 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.event.WorldContext; 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; @@ -15,7 +16,9 @@ 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.EvtPlayerCraftRecipe; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDamageBlock; +import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDiscoverZone; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDrainFromWorld; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDropItem; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerDropItemRequest; @@ -31,6 +34,7 @@ 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 com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.registration.context.ContextValue.Usage; public class EventHandler { @@ -48,7 +52,9 @@ public static void register(SkriptRegistration registration) { EvtPlayerBreakBlock.register(registration); EvtPlayerChangeGameMode.register(registration); EvtPlayerChat.register(registration); + EvtPlayerCraftRecipe.register(registration); EvtPlayerDamageBlock.register(registration); + EvtPlayerDiscoverZone.register(registration); EvtPlayerDrainFromWorld.register(registration); EvtPlayerDropItem.register(registration); EvtPlayerDropItemRequest.register(registration); @@ -71,6 +77,11 @@ public static void register(SkriptRegistration registration) { registerGlobalContexts(registration); } + public static void shutdown() { + // Shutdown any events that are running (such as a periodical) + // Nothing yet for now + } + private static void registerGlobalContexts(SkriptRegistration reg) { reg.newSingleContextValue(BlockContext.class, Block.class, "block", BlockContext::getBlock) @@ -79,6 +90,10 @@ private static void registerGlobalContexts(SkriptRegistration reg) { "player", PlayerContext::getPlayer) .setUsage(Usage.EXPRESSION_OR_ALONE) .register(); + reg.newSingleContextValue(WorldContext.class, World.class, + "world", WorldContext::getWorld) + .setUsage(Usage.EXPRESSION_OR_ALONE) + .register(); reg.addSingleContextValue(PlayerRefContext.class, PlayerRef.class, "playerref", PlayerRefContext::getPlayerRef); reg.addSingleContextValue(PlayerRefContext.class, PlayerRef.class, 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 d5b4babe..18bc0a52 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 @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.plugin.elements.events.entity; import com.github.skriptdev.skript.api.skript.event.CancellableContext; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.component.ArchetypeChunk; @@ -12,6 +13,7 @@ import com.hypixel.hytale.server.core.modules.entity.damage.Damage; import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems; +import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; import io.github.syst3ms.skriptparser.lang.Expression; @@ -63,7 +65,7 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { } private record EntityDamageContext(Entity entity, Store store, Damage damage) - implements TriggerContext, CancellableContext { + implements TriggerContext, CancellableContext, WorldContext { public Entity getVictim() { return this.entity; @@ -94,6 +96,11 @@ public DamageCause getDamageCause() { return this.damage.getCause(); } + @Override + public World getWorld() { + return this.store.getExternalData().getWorld(); + } + @Override public String getName() { return "entity damage context"; 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 316b09a5..a42f4f41 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 @@ -1,11 +1,13 @@ package com.github.skriptdev.skript.plugin.elements.events.entity; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; 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.server.core.Message; 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; @@ -14,17 +16,21 @@ import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems; +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.MessageUtil; 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.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; 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; import java.util.Arrays; +import java.util.List; public class EvtEntityDeath extends SkriptEvent { @@ -57,8 +63,26 @@ public static void register(SkriptRegistration reg) { 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); + reg.newListContextValue(EntityDeathContext.class, + ItemStack.class, "lost-itemstacks", EntityDeathContext::getItemStacksLostOnDeath) + .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) + .addListSetter(EntityDeathContext::setItemStacksLostOnDeath) + .register(); + reg.newSingleContextValue(EntityDeathContext.class, + Boolean.class, "show-death-menu", EntityDeathContext::isShowDeathMenu) + .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) + .addSetter(EntityDeathContext::setShowDeathMenu) + .register(); + reg.newSingleContextValue(EntityDeathContext.class, + Message.class, "death-message", EntityDeathContext::getDeathMessage) + .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) + .addSetter(EntityDeathContext::setDeathMessage) + .register(); + reg.newSingleContextValue(EntityDeathContext.class, + String.class, "death-message-string", EntityDeathContext::getDeathMessageString) + .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) + .addSetter(EntityDeathContext::setDeathMessageString) + .register(); } private static EntityDeathListener LISTENER; @@ -107,7 +131,9 @@ public void onComponentAdded(@NotNull Ref ref, @NotNull DeathCompon } else if (npc != null) { pattern = 2; victim = npc; - } else return; + } else { + return; + } TriggerMap.callTriggersByContext(new EntityDeathContext(pattern, victim, deathComponent)); } @@ -119,7 +145,8 @@ public void onComponentAdded(@NotNull Ref ref, @NotNull DeathCompon } @SuppressWarnings("DataFlowIssue") - private record EntityDeathContext(int pattern, Entity victim, DeathComponent component) implements TriggerContext { + private record EntityDeathContext(int pattern, Entity victim, DeathComponent component) + implements TriggerContext, WorldContext { public Entity getVictim() { return this.victim; @@ -161,6 +188,44 @@ public ItemStack[] getItemStacksLostOnDeath() { return this.component.getItemsLostOnDeath(); } + public void setItemStacksLostOnDeath(ItemStack[] itemStacks) { + this.component.setItemsLostOnDeath(List.of(itemStacks)); + } + + public boolean isShowDeathMenu() { + return this.component.isShowDeathMenu(); + } + + public void setShowDeathMenu(boolean showDeathMenu) { + // this.component.setShowDeathMenu(showDeathMenu); (doesn't seem to work) + // So let's respawn instead + if (!showDeathMenu && this.getVictim() instanceof Player player) { + Ref reference = player.getReference(); + DeathComponent.respawn(reference.getStore(), reference); + } + } + + public Message getDeathMessage() { + return this.component.getDeathMessage(); + } + + public void setDeathMessage(Message deathMessage) { + this.component.setDeathMessage(deathMessage); + } + + public String getDeathMessageString() { + return MessageUtil.toAnsiString(this.component.getDeathMessage()).toAnsi(); + } + + public void setDeathMessageString(String deathMessage) { + this.component.setDeathMessage(Message.raw(deathMessage)); + } + + @Override + public World getWorld() { + return this.victim.getWorld(); + } + @Override public String getName() { return "entity-death-context"; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityPickupItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityPickupItem.java index 19b1a4f5..2e8a5815 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityPickupItem.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/entity/EvtEntityPickupItem.java @@ -3,6 +3,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; @@ -14,6 +15,7 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.ecs.InteractivelyPickupItemEvent; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; import io.github.syst3ms.skriptparser.lang.Expression; @@ -66,7 +68,7 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { } public record PickupItemContext(InteractivelyPickupItemEvent event, Entity entity, - int pattern) implements PlayerContext, CancellableContext { + int pattern) implements PlayerContext, CancellableContext, WorldContext { public Entity getEntity() { return this.entity; @@ -81,6 +83,11 @@ public Player getPlayer() { return null; } + @Override + public World getWorld() { + return this.entity.getWorld(); + } + @Override public boolean isCancelled() { return this.event.isCancelled(); 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 dcc6826c..ffd015e2 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 @@ -1,5 +1,6 @@ package com.github.skriptdev.skript.plugin.elements.events.entity; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.event.EventRegistration; @@ -7,8 +8,6 @@ import com.hypixel.hytale.server.core.event.events.entity.EntityRemoveEvent; import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Statement; -import io.github.syst3ms.skriptparser.lang.Trigger; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; @@ -26,7 +25,6 @@ public static void register(SkriptRegistration reg) { .setHandledContexts(EntityRemoveEventContext.class) .register(); reg.addSingleContextValue(EntityRemoveEventContext.class, Entity.class, "entity", EntityRemoveEventContext::getEntity); - reg.addSingleContextValue(EntityRemoveEventContext.class, World.class, "world", EntityRemoveEventContext::getWorld); } private static EventRegistration LISTENER; @@ -36,9 +34,7 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex if (LISTENER == null) { LISTENER = HySk.getInstance().getEventRegistry().registerGlobal(EntityRemoveEvent.class, event -> { EntityRemoveEventContext context = new EntityRemoveEventContext(event); - for (Trigger trigger : TriggerMap.getTriggersByContext(EntityRemoveEventContext.class)) { - Statement.runAll(trigger, context); - } + TriggerMap.callTriggersByContext(context); }); } return true; @@ -54,7 +50,7 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "entity remove event"; } - private record EntityRemoveEventContext(EntityRemoveEvent event) implements TriggerContext { + private record EntityRemoveEventContext(EntityRemoveEvent event) implements TriggerContext, WorldContext { @Override public String getName() { return "entity remove context"; @@ -64,7 +60,8 @@ private Entity getEntity() { return this.event.getEntity(); } - private World getWorld() { + @Override + public 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 1ec7f4e7..30afdaf5 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 @@ -1,13 +1,14 @@ package com.github.skriptdev.skript.plugin.elements.events.entity; +import com.github.skriptdev.skript.api.skript.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; 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 com.hypixel.hytale.server.core.universe.world.World; 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.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; @@ -37,7 +38,7 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex if (LISTENER == null) { LISTENER = HySk.getInstance().getEventRegistry().registerGlobal(LivingEntityInventoryChangeEvent.class, event -> { InvChangeContext ctx = new InvChangeContext(event); - TriggerMap.getTriggersByContext(InvChangeContext.class).forEach(trigger -> Statement.runAll(trigger, ctx)); + TriggerMap.callTriggersByContext(ctx); }); } return true; @@ -53,7 +54,7 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "living entity inventory change"; } - private record InvChangeContext(LivingEntityInventoryChangeEvent event) implements TriggerContext { + private record InvChangeContext(LivingEntityInventoryChangeEvent event) implements TriggerContext, WorldContext { private Entity getEntity() { return this.event.getEntity(); @@ -63,6 +64,11 @@ private ItemContainer getContainer() { return this.event.getItemContainer(); } + @Override + public World getWorld() { + return this.event.getEntity().getWorld(); + } + @Override public String getName() { return "living entity inventory change context"; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerAddToWorld.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerAddToWorld.java index 0e980df2..3c0b40e5 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerAddToWorld.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerAddToWorld.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.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.component.Holder; @@ -14,12 +15,14 @@ import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; public class EvtPlayerAddToWorld extends SkriptEvent { public static void register(SkriptRegistration reg) { reg.newEvent(EvtPlayerAddToWorld.class, "player added to world", "add player to world") + .setHandledContexts(AddContext.class) .name("Player Add To World") .description("Called when a player joins a world.") .examples("on player added to world:", @@ -27,7 +30,9 @@ public static void register(SkriptRegistration reg) { .since("1.0.0") .register(); - reg.addSingleContextValue(AddContext.class, World.class, "world", AddContext::getWorld); + reg.newSingleContextValue(AddContext.class, Boolean.class, "should-broadcast", AddContext::shouldBroadcast) + .addSetter(AddContext::setShouldBroadcast) + .register(); } private static EventRegistration LISTENER; @@ -49,20 +54,28 @@ public boolean check(TriggerContext triggerContext) { } @Override - public String toString(TriggerContext ctx, boolean debug) { + public String toString(@NotNull TriggerContext ctx, boolean debug) { return ""; } - private record AddContext(AddPlayerToWorldEvent event) implements PlayerContext { + private record AddContext(AddPlayerToWorldEvent event) implements PlayerContext, WorldContext { + @Override public World getWorld() { return this.event.getWorld(); } public Player getPlayer() { Holder holder = this.event.getHolder(); - Player component = holder.getComponent(Player.getComponentType()); - return component; + return holder.getComponent(Player.getComponentType()); + } + + public boolean shouldBroadcast() { + return this.event.shouldBroadcastJoinMessage(); + } + + public void setShouldBroadcast(boolean shouldBroadcast) { + this.event.setBroadcastJoinMessage(shouldBroadcast); } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java index 3b24bd9c..1cf6aecf 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerBreakBlock.java @@ -4,6 +4,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; @@ -40,15 +41,21 @@ public static void register(SkriptRegistration reg) { .setHandledContexts(BreakBlockEventContext.class) .register(); - reg.newSingleContextValue(BreakBlockEventContext.class, Block.class, "block", BreakBlockEventContext::getBlock) + reg.newSingleContextValue(BreakBlockEventContext.class, Block.class, + "block", BreakBlockEventContext::getBlock) .addSetter(BreakBlockEventContext::setTargetBlock).register(); - reg.addSingleContextValue(BreakBlockEventContext.class, World.class, "world", BreakBlockEventContext::getWorld); - reg.addSingleContextValue(BreakBlockEventContext.class, BlockType.class, "blocktype", BreakBlockEventContext::getBlockType); - reg.addSingleContextValue(BreakBlockEventContext.class, Item.class, "item-in-hand", BreakBlockEventContext::getItemInHand); - reg.addSingleContextValue(BreakBlockEventContext.class, World.class, "world", BreakBlockEventContext::getWorld); - reg.addSingleContextValue(BreakBlockEventContext.class, BlockType.class, "blocktype", BreakBlockEventContext::getBlockType); - reg.addSingleContextValue(BreakBlockEventContext.class, Item.class, "item-in-hand", BreakBlockEventContext::getItemInHand); - reg.addSingleContextValue(BreakBlockEventContext.class, ItemStack.class, "itemstack-in-hand", BreakBlockEventContext::getItemStackInHand); + reg.addSingleContextValue(BreakBlockEventContext.class, BlockType.class, + "blocktype", BreakBlockEventContext::getBlockType); + reg.addSingleContextValue(BreakBlockEventContext.class, Item.class, + "item-in-hand", BreakBlockEventContext::getItemInHand); + reg.addSingleContextValue(BreakBlockEventContext.class, World.class, + "world", BreakBlockEventContext::getWorld); + reg.addSingleContextValue(BreakBlockEventContext.class, BlockType.class, + "blocktype", BreakBlockEventContext::getBlockType); + reg.addSingleContextValue(BreakBlockEventContext.class, Item.class, + "item-in-hand", BreakBlockEventContext::getItemInHand); + reg.addSingleContextValue(BreakBlockEventContext.class, ItemStack.class, + "itemstack-in-hand", BreakBlockEventContext::getItemStackInHand); } private static BlockBreakEventSystem SYSTEM; @@ -74,7 +81,7 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { } private record BreakBlockEventContext(BreakBlockEvent event, Player player) - implements PlayerContext, CancellableContext { + implements PlayerContext, CancellableContext, WorldContext { public Player getPlayer() { return this.player; @@ -92,7 +99,8 @@ private void setTargetBlock(Block targetBlock) { this.event.setTargetBlock(targetBlock.getPos()); } - private World getWorld() { + @Override + public World getWorld() { return this.player.getWorld(); } 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 index 5b7c1921..f0939251 100644 --- 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 @@ -3,6 +3,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; @@ -14,6 +15,7 @@ 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.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.TriggerContext; @@ -33,11 +35,13 @@ public static void register(SkriptRegistration reg) { .since("1.0.0") .register(); - reg.newSingleContextValue(PlayerChangeGameModeContext.class, GameMode.class, "game[(-| )]mode", PlayerChangeGameModeContext::getOldGameMode) + 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) + reg.newSingleContextValue(PlayerChangeGameModeContext.class, GameMode.class, + "game[(-| )]mode", PlayerChangeGameModeContext::getNewGameMode) .setState(ContextValue.State.PRESENT) .setUsage(ContextValue.Usage.EXPRESSION_OR_ALONE) .addSetter(PlayerChangeGameModeContext::setNewGameMode) @@ -66,12 +70,17 @@ public String toString(@Nullable TriggerContext triggerContext, boolean debug) { } private record PlayerChangeGameModeContext(ChangeGameModeEvent event, Player player) - implements PlayerContext, CancellableContext { + implements PlayerContext, CancellableContext, WorldContext { public Player getPlayer() { return this.player; } + @Override + public World getWorld() { + return getPlayer().getWorld(); + } + public GameMode getNewGameMode() { return event.getGameMode(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChat.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChat.java index b8c15eaf..42f57613 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChat.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerChat.java @@ -8,8 +8,6 @@ import com.hypixel.hytale.server.core.event.events.player.PlayerChatEvent; import com.hypixel.hytale.server.core.universe.PlayerRef; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Statement; -import io.github.syst3ms.skriptparser.lang.Trigger; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; @@ -31,7 +29,8 @@ public static void register(SkriptRegistration reg) { "\t\tsend \"You said: %message% and we cancelled that!!!\" to context-sender", "", "on player chat:", - "\tset {_message} to formatted message from \"[MyServer] %name of context-playerref%: %context-message%\"", + "\tset {_message} to formatted message from \"[MyServer]" + + " %name of context-playerref%: %context-message%\"", "\tset context-message-format to {_message}") .since("1.0.0") .register(); @@ -63,9 +62,7 @@ public boolean init(Expression @NotNull [] expressions, int matchedPattern, @ LISTENER = HySk.getInstance().getEventRegistry().registerAsyncGlobal(PlayerChatEvent.class, future -> { future.thenAccept(event -> { PlayerChatContext ctx = new PlayerChatContext(event); - for (Trigger trigger : TriggerMap.getTriggersByContext(PlayerChatContext.class)) { - Statement.runAll(trigger, ctx); - } + TriggerMap.callTriggersByContext(ctx); }); return future; }); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerCraftRecipe.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerCraftRecipe.java new file mode 100644 index 00000000..659c5776 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerCraftRecipe.java @@ -0,0 +1,177 @@ +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.github.skriptdev.skript.plugin.HySk; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.ComponentRegistryProxy; +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.server.core.asset.type.item.config.CraftingRecipe; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.event.events.ecs.CraftRecipeEvent; +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class EvtPlayerCraftRecipe extends SystemEvent> { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtPlayerCraftRecipe.class, + "player craft recipe", + "pre player craft recipe", + "post player craft recipe") + .setHandledContexts(CraftRecipeContext.class) + .name("Player Craft Recipe") + .experimental("These events don't seem to be called right now, server issues?!?!") + .description("Called when a player crafts a recipe.") + .examples("on player craft recipe:", + "\tif context-recipe-id = \"Salvage_Rock_Lime_Cobble\":", + "\t\tcancel event") + .since("INSERT VERSION") + .register(); + + reg.addSingleContextValue(CraftRecipeContext.class, Integer.class, + "quantity", CraftRecipeContext::getQuantity); + reg.addSingleContextValue(CraftRecipeContext.class, CraftingRecipe.class, + "recipe", CraftRecipeContext::getRecipe); + reg.addSingleContextValue(CraftRecipeContext.class, String.class, + "recipe-id", CraftRecipeContext::getRecipeId); + } + + static final ComponentRegistryProxy REGISTRY = HySk.getInstance().getEntityStoreRegistry(); + private static PreCraftRecipeSystem PRE_SYSTEM; + private static PostCraftRecipeSystem POST_SYSTEM; + private int pattern; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + this.pattern = matchedPattern; + if (PRE_SYSTEM == null) { + PRE_SYSTEM = new PreCraftRecipeSystem(); + REGISTRY.registerSystem(PRE_SYSTEM); + } + if (POST_SYSTEM == null) { + POST_SYSTEM = new PostCraftRecipeSystem(); + REGISTRY.registerSystem(POST_SYSTEM); + } + return true; + } + + @Override + public boolean check(TriggerContext ctx) { + if (ctx instanceof CraftRecipeContext craftRecipeContext) { + if (this.pattern == 0) return true; + else if (this.pattern == 1) return craftRecipeContext.getPattern() == 0; + else if (this.pattern == 2) return craftRecipeContext.getPattern() == 1; + } + return false; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "player craft recipe"; + } + + public record CraftRecipeContext(CraftRecipeEvent event, + Player player) implements PlayerContext, CancellableContext { + private int getPattern() { + if (this.event instanceof CraftRecipeEvent.Post) { + return 1; + } + return 0; + } + + @Override + public Player getPlayer() { + return this.player; + } + + private int getQuantity() { + return this.event.getQuantity(); + } + + private CraftingRecipe getRecipe() { + return this.event.getCraftedRecipe(); + } + + private String getRecipeId() { + return this.event.getCraftedRecipe().getId(); + } + + @Override + public boolean isCancelled() { + return this.event.isCancelled(); + } + + @Override + public void setCancelled(boolean cancelled) { + this.event.setCancelled(cancelled); + } + + @Override + public String getName() { + return "craft-recipe-context"; + } + } + + public static class PreCraftRecipeSystem extends EntityEventSystem { + + protected PreCraftRecipeSystem() { + super(CraftRecipeEvent.Pre.class); + } + + @Override + public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, + @NotNull Store store, @NotNull CommandBuffer commandBuffer, + @NotNull CraftRecipeEvent.Pre event) { + + Ref ref = archetypeChunk.getReferenceTo(i); + Player player = store.getComponent(ref, Player.getComponentType()); + if (player == null) return; + + CraftRecipeContext context = new CraftRecipeContext(event, player); + TriggerMap.callTriggersByContext(context); + } + + @Override + public @Nullable Query getQuery() { + return Player.getComponentType(); + } + } + + public static class PostCraftRecipeSystem extends EntityEventSystem { + + protected PostCraftRecipeSystem() { + super(CraftRecipeEvent.Post.class); + } + + @Override + public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, + @NotNull Store store, @NotNull CommandBuffer commandBuffer, + @NotNull CraftRecipeEvent.Post event) { + + Ref ref = archetypeChunk.getReferenceTo(i); + Player player = store.getComponent(ref, Player.getComponentType()); + if (player == null) return; + + CraftRecipeContext context = new CraftRecipeContext(event, player); + TriggerMap.callTriggersByContext(context); + } + + @Override + public @Nullable Query getQuery() { + return Player.getComponentType(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDamageBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDamageBlock.java index 29dd7d66..f1af6639 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDamageBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDamageBlock.java @@ -4,6 +4,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; @@ -65,13 +66,18 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { } public record DamageBlockContext(DamageBlockEvent event, Player player) - implements PlayerContext, CancellableContext { + implements PlayerContext, CancellableContext, WorldContext { @Override public Player getPlayer() { return this.player; } + @Override + public World getWorld() { + return this.player.getWorld(); + } + public ItemStack getItemStackInHand() { return this.event.getItemInHand(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDiscoverZone.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDiscoverZone.java new file mode 100644 index 00000000..9cf09d6a --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDiscoverZone.java @@ -0,0 +1,120 @@ +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.SystemEvent; +import com.github.skriptdev.skript.api.skript.event.WorldContext; +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.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.event.events.ecs.DiscoverZoneEvent; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.WorldMapTracker; +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class EvtPlayerDiscoverZone extends SystemEvent> { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtPlayerDiscoverZone.class, "player discover zone") + .name("Player Discover Zone") + .experimental("This event doesn't seem to be called right now, server issues?!?!") + .setHandledContexts(DiscoverZoneContext.class) + .description("Called when a player discovers a zone.") + .since("INSERT VERSION") + .register(); + + reg.addSingleContextValue(DiscoverZoneContext.class, String.class, + "zone-name", DiscoverZoneContext::getZoneName); + reg.addSingleContextValue(DiscoverZoneContext.class, boolean.class, + "displaying", DiscoverZoneContext::isDisplaying); + reg.addSingleContextValue(DiscoverZoneContext.class, String.class, + "region-name", DiscoverZoneContext::getRegionName); + } + + private static DiscoverZoneSystem SYSTEM; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + if (SYSTEM == null) { + SYSTEM = new DiscoverZoneSystem(); + applySystem(SYSTEM); + } + return true; + } + + @Override + public boolean check(TriggerContext ctx) { + return ctx instanceof DiscoverZoneContext; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "player discover zone"; + } + + public record DiscoverZoneContext(DiscoverZoneEvent event, Player player) + implements PlayerContext, WorldContext { + + @Override + public Player getPlayer() { + return this.player; + } + + @Override + public World getWorld() { + return this.player.getWorld(); + } + + public String getZoneName() { + WorldMapTracker.ZoneDiscoveryInfo discoveryInfo = this.event.getDiscoveryInfo(); + return discoveryInfo.zoneName(); + } + + public boolean isDisplaying() { + return this.event.getDiscoveryInfo().display(); + } + + public String getRegionName() { + return this.event.getDiscoveryInfo().regionName(); + } + + @Override + public String getName() { + return "discover zone context"; + } + } + + public static class DiscoverZoneSystem extends EntityEventSystem { + + protected DiscoverZoneSystem() { + super(DiscoverZoneEvent.class); + } + + @Override + public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, + @NotNull Store store, @NotNull CommandBuffer commandBuffer, + @NotNull DiscoverZoneEvent discoverZoneEvent) { + + Ref ref = archetypeChunk.getReferenceTo(i); + Player player = store.getComponent(ref, Player.getComponentType()); + DiscoverZoneContext context = new DiscoverZoneContext(discoverZoneEvent, player); + TriggerMap.callTriggersByContext(context); + } + + @Override + public @Nullable Query getQuery() { + return Player.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 c2b63fd6..07bf1707 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 @@ -1,8 +1,13 @@ 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.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.event.EventRegistration; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.entity.EntityUtils; +import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.player.DrainPlayerFromWorldEvent; import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.lang.Expression; @@ -18,12 +23,9 @@ public static void register(SkriptRegistration reg) { reg.newEvent(EvtPlayerDrainFromWorld.class, "drain player from world", "player drained from world", "player drain from world") .name("Player Drain From World") - .description("Really not sure...") // TODO put real docs + .description("Called when a world is unloaded and players are moved out of it.") .since("1.0.0") .register(); - - reg.addSingleContextValue(DrainContext.class, World.class, - "world", DrainContext::getWorld); } private static EventRegistration LISTENER; @@ -49,12 +51,19 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "drain player from world"; } - private record DrainContext(DrainPlayerFromWorldEvent event) implements TriggerContext { + private record DrainContext(DrainPlayerFromWorldEvent event) implements PlayerContext, WorldContext { public World getWorld() { return this.event.getWorld(); } + @SuppressWarnings("deprecation") + @Override + public Player getPlayer() { + Entity entity = EntityUtils.getEntity(this.event.getHolder()); + return entity instanceof Player p ? p : null; + } + @Override public String getName() { return "drain context"; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java index 33f47049..931d075d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItem.java @@ -3,6 +3,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.component.ArchetypeChunk; @@ -16,6 +17,7 @@ import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent; import com.hypixel.hytale.server.core.event.events.ecs.DropItemEvent.Drop; import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.TriggerContext; @@ -63,12 +65,17 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "player drop item event"; } - private record DropItemContext(Player player, Drop drop) implements PlayerContext, CancellableContext { + private record DropItemContext(Player player, Drop drop) implements PlayerContext, WorldContext, CancellableContext { public Player getPlayer() { return this.player; } + @Override + public World getWorld() { + return this.player.getWorld(); + } + public Float getThrowSpeed() { return this.drop.getThrowSpeed(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java index b836a5bd..67a2a653 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerDropItemRequest.java @@ -3,6 +3,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.HySk; import com.hypixel.hytale.component.ArchetypeChunk; @@ -17,6 +18,7 @@ import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import io.github.syst3ms.skriptparser.lang.Expression; import io.github.syst3ms.skriptparser.lang.TriggerContext; @@ -38,10 +40,14 @@ public static void register(SkriptRegistration reg) { .since("1.0.0") .register(); - reg.addSingleContextValue(RequestDropItemContext.class, Integer.class, "slot-id", RequestDropItemContext::getSlotId); - reg.addSingleContextValue(RequestDropItemContext.class, Integer.class, "inventory-section-id", RequestDropItemContext::getInventorySectionId); - reg.addSingleContextValue(RequestDropItemContext.class, Item.class, "item", RequestDropItemContext::getItem); - reg.addSingleContextValue(RequestDropItemContext.class, ItemStack.class, "itemstack", RequestDropItemContext::getItemStack); + reg.addSingleContextValue(RequestDropItemContext.class, Integer.class, + "slot-id", RequestDropItemContext::getSlotId); + reg.addSingleContextValue(RequestDropItemContext.class, Integer.class, + "inventory-section-id", RequestDropItemContext::getInventorySectionId); + reg.addSingleContextValue(RequestDropItemContext.class, Item.class, + "item", RequestDropItemContext::getItem); + reg.addSingleContextValue(RequestDropItemContext.class, ItemStack.class, + "itemstack", RequestDropItemContext::getItemStack); } private static PlayerRequestSystem SYSTEM; @@ -66,12 +72,17 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { } private record RequestDropItemContext(Player player, DropItemEvent.PlayerRequest request) - implements PlayerContext, CancellableContext { + implements PlayerContext, WorldContext, CancellableContext { public Player getPlayer() { return this.player; } + @Override + public World getWorld() { + return this.player.getWorld(); + } + public int getSlotId() { return (int) this.request.getSlotId(); } 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 0bc5eeb2..94795397 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 @@ -13,8 +13,6 @@ import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.event.events.player.PlayerMouseButtonEvent; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Statement; -import io.github.syst3ms.skriptparser.lang.Trigger; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; @@ -46,9 +44,7 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex if (LISTENER == null) { LISTENER = HySk.getInstance().getEventRegistry().registerGlobal(PlayerMouseButtonEvent.class, event -> { MouseClickContext context = new MouseClickContext(event); - for (Trigger trigger : TriggerMap.getTriggersByContext(MouseClickContext.class)) { - Statement.runAll(trigger, context); - } + TriggerMap.callTriggersByContext(context); }); } return true; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerPlaceBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerPlaceBlock.java index 35606c99..e2e574c0 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerPlaceBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerPlaceBlock.java @@ -6,6 +6,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; @@ -73,13 +74,18 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "player place block"; } - public record PlaceBlockContext(PlaceBlockEvent event, - Player player) implements PlayerContext, BlockContext, CancellableContext { + public record PlaceBlockContext(PlaceBlockEvent event, Player player) + implements PlayerContext, BlockContext, WorldContext, CancellableContext { public Player getPlayer() { return this.player; } + @Override + public World getWorld() { + return this.player.getWorld(); + } + public Item getItemInHand() { ItemStack itemInHand = this.event.getItemInHand(); if (itemInHand == null) return null; 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 f3ed8d7d..5b71ef2f 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 @@ -4,6 +4,7 @@ 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.event.WorldContext; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.ArchetypeChunk; import com.hypixel.hytale.component.CommandBuffer; @@ -78,7 +79,7 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { return "player use block"; } - public static abstract class UseBlockContext implements PlayerContext { + public static abstract class UseBlockContext implements PlayerContext, WorldContext { final T event; final Player player; @@ -93,6 +94,11 @@ public Player getPlayer() { return this.player; } + @Override + public World getWorld() { + return this.player.getWorld(); + } + public BlockType getBlockType() { return this.event.getBlockType(); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/server/EvtBoot.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/server/EvtBoot.java index c5b2685b..31be021e 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/server/EvtBoot.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/server/EvtBoot.java @@ -5,8 +5,6 @@ import com.hypixel.hytale.event.EventRegistration; import com.hypixel.hytale.server.core.event.events.BootEvent; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Statement; -import io.github.syst3ms.skriptparser.lang.Trigger; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; @@ -30,9 +28,7 @@ public boolean init(Expression[] expressions, int matchedPattern, ParseContex if (LISTENER == null) { LISTENER = HySk.getInstance().getEventRegistry().registerGlobal(BootEvent.class, event -> { BootContext bootContext = new BootContext(event); - for (Trigger trigger : TriggerMap.getTriggersByContext(BootContext.class)) { - Statement.runAll(trigger, bootContext); - } + TriggerMap.callTriggersByContext(bootContext); }); } return true; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/skript/EvtLoad.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/skript/EvtLoad.java index 433ef35d..f08a9955 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/skript/EvtLoad.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/skript/EvtLoad.java @@ -1,9 +1,9 @@ package com.github.skriptdev.skript.plugin.elements.events.skript; import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.Statement; import io.github.syst3ms.skriptparser.lang.Trigger; import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.TriggerMap; import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; import io.github.syst3ms.skriptparser.lang.event.StartOnLoadEvent; import io.github.syst3ms.skriptparser.parsing.ParseContext; @@ -38,7 +38,7 @@ public String toString(@Nullable TriggerContext ctx, boolean debug) { @Override public void onInitialLoad(Trigger trigger) { - Statement.runAll(trigger, new ScriptLoadContext()); + TriggerMap.callTriggersByContext(new ScriptLoadContext()); } private static class ScriptLoadContext implements TriggerContext { 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 a1477ecb..c7f332ce 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 @@ -6,6 +6,7 @@ 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.DefaultEntityStatTypes; +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.lang.properties.PropertyExpression; @@ -66,38 +67,50 @@ public Optional[]> acceptsChange(@NotNull ChangeMode mode) { public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { Optional single = getOwner().getSingle(ctx); if (single.isEmpty()) return; - LivingEntity entity = single.get(); - EntityStatMap statMap = EntityComponentUtils.getEntityStatMap(entity); - if (statMap == null) return; + LivingEntity entity = single.get(); + World world = entity.getWorld(); + if (world == null) return; - if (changeMode == ChangeMode.RESET) { - statMap.resetStatValue(HEALTH_STAT_INDEX); - return; - } + Runnable healthRunnable = () -> { - float newValue; - if (changeWith.length > 0 && changeWith[0] instanceof Number number) { - newValue = number.floatValue(); - } else { - newValue = 0f; - } + EntityStatMap statMap = EntityComponentUtils.getEntityStatMap(entity); + if (statMap == null) return; - if (changeMode != ChangeMode.SET) { - EntityStatValue healthStat = statMap.get(HEALTH_STAT_INDEX); - if (healthStat == null) return; - float oldHealthValue = healthStat.get(); + if (changeMode == ChangeMode.RESET) { + statMap.resetStatValue(HEALTH_STAT_INDEX); + return; + } - if (changeMode == ChangeMode.ADD) { - newValue += oldHealthValue; - } else if (changeMode == ChangeMode.REMOVE) { - newValue = oldHealthValue - newValue; - } else if (changeMode == ChangeMode.DELETE) { + float newValue; + if (changeWith.length > 0 && changeWith[0] instanceof Number number) { + newValue = number.floatValue(); + } else { newValue = 0f; } - } - statMap.setStatValue(HEALTH_STAT_INDEX, newValue); + if (changeMode != ChangeMode.SET) { + EntityStatValue healthStat = statMap.get(HEALTH_STAT_INDEX); + if (healthStat == null) return; + float oldHealthValue = healthStat.get(); + + if (changeMode == ChangeMode.ADD) { + newValue += oldHealthValue; + } else if (changeMode == ChangeMode.REMOVE) { + newValue = oldHealthValue - newValue; + } else if (changeMode == ChangeMode.DELETE) { + newValue = 0f; + } + } + + statMap.setStatValue(HEALTH_STAT_INDEX, newValue); + }; + + if (world.isInThread()) { + healthRunnable.run(); + } else { + world.execute(healthRunnable); + } } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprNPCType.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprNPCType.java index 6be76ccc..1641ca0e 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprNPCType.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprNPCType.java @@ -2,22 +2,34 @@ import com.github.skriptdev.skript.api.skript.registration.NPCRegistry; import com.github.skriptdev.skript.api.skript.registration.NPCRegistry.NPCRole; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; +import com.hypixel.hytale.server.npc.role.Role; +import com.hypixel.hytale.server.npc.systems.RoleChangeSystem; +import io.github.syst3ms.skriptparser.lang.Expression; +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 ExprNPCType extends PropertyExpression { public static void register(SkriptRegistration registration) { registration.newPropertyExpression(ExprNPCType.class, NPCRole.class, "npc (type|role)", "entities") - .name("NPC Type of Entity") - .description("Returns the NPC type of an NPC entity.") + .name("NPC Role of Entity") + .description("Get/set the NPC role of an NPC entity.") .examples("set {_type} to npc type of target entity", - "if npc type of {_entity} = sheep:") + "if npc type of {_entity} = sheep:", + "set npc role of target entity of player to cow") .since("1.0.0") .register(); } @@ -30,4 +42,41 @@ public static void register(SkriptRegistration registration) { return null; } + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{NPCRole.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + Expression owner = getOwner(); + if (owner == null || changeWith == null) return; + + if (changeMode == ChangeMode.SET) { + if (changeWith.length == 1 && changeWith[0] instanceof NPCRole role) { + for (Entity entity : owner.getArray(ctx)) { + if (entity instanceof NPCEntity npcEntity) { + changeRole(npcEntity, role); + } + } + } + } + } + + private void changeRole(NPCEntity npcEntity, NPCRole npcRole) { + Ref reference = npcEntity.getReference(); + if (reference == null) return; + + World world = npcEntity.getWorld(); + if (world == null) return; + + Store store = world.getEntityStore().getStore(); + Role role = npcEntity.getRole(); + if (role == null || role.isRoleChangeRequested()) return; + + RoleChangeSystem.requestRoleChange(reference, role, npcRole.index(), true, store); + } + } 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 index 3ed3a382..84f3a249 100644 --- 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 @@ -83,7 +83,8 @@ public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, if (reference == null) continue; Store store = reference.getStore(); - EffectControllerComponent component = EntityComponentUtils.getComponent(livingEntity, EffectControllerComponent.getComponentType()); + EffectControllerComponent component = EntityComponentUtils.getComponent(livingEntity, + EffectControllerComponent.getComponentType()); if (component == null) continue; if (changeMode == ChangeMode.DELETE) { 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 index 601f2c3c..bd709d83 100644 --- 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 @@ -1,7 +1,10 @@ package com.github.skriptdev.skript.plugin.elements.expressions.player; +import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import io.github.syst3ms.skriptparser.lang.TriggerContext; import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; import io.github.syst3ms.skriptparser.registration.SkriptRegistration; @@ -35,15 +38,27 @@ public Optional[]> acceptsChange(@NotNull ChangeMode mode) { @SuppressWarnings("ConstantConditions") @Override - public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, @NotNull Object[] changeWith) { + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] 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; + GameMode newGameMode = changeWith[0] instanceof GameMode gm ? gm : null; + if (newGameMode == null) { + return; + } - Player.setGameMode(player.getReference(), newGameMode, player.getReference().getStore()); + for (Player player : getOwner().getArray(ctx)) { + if (player == null) continue; + + Ref ref = player.getReference(); + Runnable gamemodeRunnable = () -> Player.setGameMode(ref, newGameMode, ref.getStore()); + + World world = player.getWorld(); + if (world.isInThread()) { + gamemodeRunnable.run(); + } else { + world.execute(gamemodeRunnable); + } + } } @Override diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/functions/DefaultFunctions.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/functions/DefaultFunctions.java index 373135a0..9074ad04 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/functions/DefaultFunctions.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/functions/DefaultFunctions.java @@ -1,5 +1,6 @@ package com.github.skriptdev.skript.plugin.elements.functions; +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; @@ -15,13 +16,13 @@ public class DefaultFunctions { - public static void register() { - itemFunctions(); - positionFunctions(); + public static void register(SkriptRegistration reg) { + itemFunctions(reg); + positionFunctions(reg); } - private static void itemFunctions() { - Functions.newJavaFunction(new JavaFunction<>( + private static void itemFunctions(SkriptRegistration reg) { + Functions.newJavaFunction(reg, new JavaFunction<>( "itemstack", new FunctionParameter[]{ new FunctionParameter<>("type", Item.class, true), @@ -50,8 +51,8 @@ public ItemStack[] executeSimple(Object[][] params) { .register(); } - private static void positionFunctions() { - Functions.newJavaFunction(new JavaFunction<>( + private static void positionFunctions(SkriptRegistration reg) { + Functions.newJavaFunction(reg, new JavaFunction<>( "location", new FunctionParameter[]{ new FunctionParameter<>("x", Number.class, true), @@ -76,7 +77,7 @@ public Location[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(new JavaFunction<>( + Functions.newJavaFunction(reg,new JavaFunction<>( "vector3i", new FunctionParameter[]{ new FunctionParameter<>("x", Number.class, true), @@ -99,7 +100,7 @@ public Vector3i[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(new JavaFunction<>( + Functions.newJavaFunction(reg, new JavaFunction<>( "vector3f", new FunctionParameter[]{ new FunctionParameter<>("x", Number.class, true), @@ -122,7 +123,7 @@ public Vector3f[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(new JavaFunction<>( + Functions.newJavaFunction(reg, new JavaFunction<>( "vector3d", new FunctionParameter[]{ new FunctionParameter<>("x", Number.class, true), @@ -145,7 +146,7 @@ public Vector3d[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(new JavaFunction<>( + Functions.newJavaFunction(reg, new JavaFunction<>( "world", new FunctionParameter[]{ new FunctionParameter<>("name", String.class, true) 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 d347853d..eb688517 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 @@ -44,8 +44,10 @@ public static void register(SkriptRegistration reg) { .since("1.0.0") .register(); - reg.addSingleContextValue(ItemComponentContext.class, ItemComponent.class, "item-component", ItemComponentContext::getItemComponent); - reg.addSingleContextValue(ItemComponentContext.class, Entity.class, "item-entity", ItemComponentContext::getEntity); + reg.addSingleContextValue(ItemComponentContext.class, ItemComponent.class, + "item-component", ItemComponentContext::getItemComponent); + reg.addSingleContextValue(ItemComponentContext.class, Entity.class, + "item-entity", ItemComponentContext::getEntity); } private Expression items; 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 f6e42aea..634852fe 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 @@ -106,14 +106,16 @@ public void playSoundToPlayer(@NotNull PlayerRef[] players, @Nullable Location l if (reference == null || !reference.isValid()) continue; Store store = reference.getStore(); - SoundUtil.playSoundEvent3dToPlayer(reference, soundEvent, category, pos.getX(), pos.getY(), pos.getZ(), volume, pitch, store); + SoundUtil.playSoundEvent3dToPlayer(reference, soundEvent, category, + pos.getX(), pos.getY(), pos.getZ(), volume, pitch, store); } else { SoundUtil.playSoundEvent2dToPlayer(player, soundEvent, category, volume, pitch); } } } - public void playSoundAtLocation(@NotNull Location location, int soundEvent, SoundCategory category, boolean is3d, float volume, float pitch) { + public void playSoundAtLocation(@NotNull Location location, int soundEvent, SoundCategory category, + boolean is3d, float volume, float pitch) { String worldName = location.getWorld(); if (worldName == null) return; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSendNotification.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSendNotification.java new file mode 100644 index 00000000..da5c5cce --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SecSendNotification.java @@ -0,0 +1,161 @@ +package com.github.skriptdev.skript.plugin.elements.sections; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.protocol.ItemWithAllMetadata; +import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.io.PacketHandler; +import com.hypixel.hytale.server.core.universe.PlayerRef; +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.NotificationUtil; +import io.github.syst3ms.skriptparser.file.FileSection; +import io.github.syst3ms.skriptparser.lang.CodeSection; +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.lang.entries.SectionConfiguration; +import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.parsing.ParserState; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class SecSendNotification extends CodeSection { + + public static void register(SkriptRegistration reg) { + reg.newSection(SecSendNotification.class, + "send notification %string/message% [to %players/playerrefs/worlds%]") + .name("Send Notification") + .description("Send a notification to players, worlds, or the universe.", + "**Entries**:", + "- `secondary-message`: Optional secondary message to display in the notification.", + "- `itemstack`: Optional itemstack to display in the notification.", + "- `icon`: Optional icon to display in the notification.", + "- `style`: Optional style to use for the notification.", + "**NOTE**: Regarding ItemStacks, if you send multiple notifications with the same ItemStack, " + + "the client will stack them and just keep adding quantity.") + .examples("send notification \"Hello Player!\" to player:", + "\titemstack: itemstack of ingredient_stick", + "send notification \"You better be careful!!!\" to world of player:", + "\titemstack: itemstack of deco_fire", + "\tstyle: danger") + .since("INSERT VERSION") + .register(); + } + + SectionConfiguration config = new SectionConfiguration.Builder() + .addOptionalExpression("secondary-message", Object.class, false) + .addOptionalExpression("itemstack", ItemStack.class, false) + .addOptionalExpression("icon", String.class, false) + .addOptionalExpression("style", NotificationStyle.class, false) + .build(); + + private Expression message; + private Expression receivers; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.message = expressions[0]; + if (expressions.length > 1) { + this.receivers = expressions[1]; + } + return true; + } + + @Override + public boolean loadSection(@NotNull FileSection section, @NotNull ParserState parserState, @NotNull SkriptLogger logger) { + return this.config.loadConfiguration(null, section, parserState, logger); + } + + @SuppressWarnings({"removal"}) + @Override + public Optional walk(@NotNull TriggerContext ctx) { + Optional nextStatement = getNext(); + + Object o = this.message.getSingle(ctx).orElse(null); + Message message; + if (o instanceof String s) { + message = Message.raw(s); + } else if (o instanceof Message m) { + message = m; + } else { + return nextStatement; + } + + Message secondary = null; + Optional> sExp = this.config.getExpression("secondary-message", Object.class); + if (sExp.isPresent()) { + Optional single = sExp.get().getSingle(ctx); + if (single.isPresent()) { + Object o1 = single.get(); + if (o1 instanceof String s1) { + secondary = Message.raw(s1); + } else if (o1 instanceof Message m1) { + secondary = m1; + } + } + } + + ItemStack itemStack = null; + Optional> isExp = this.config.getExpression("itemstack", ItemStack.class); + if (isExp.isPresent()) { + Optional single = isExp.get().getSingle(ctx); + if (single.isPresent()) { + itemStack = single.get(); + } + } + ItemWithAllMetadata itemWithAllMetadata = itemStack != null ? itemStack.toPacket() : null; + + String icon = null; + Optional> iconExp = this.config.getExpression("icon", String.class); + if (iconExp.isPresent()) { + Optional single = iconExp.get().getSingle(ctx); + if (single.isPresent()) { + icon = single.get(); + } + } + + NotificationStyle style = NotificationStyle.Default; + Optional> styleExp = this.config.getExpression("style", NotificationStyle.class); + if (styleExp.isPresent()) { + Optional single = styleExp.get().getSingle(ctx); + if (single.isPresent()) { + style = single.get(); + } + } + + if (this.receivers != null) { + for (Object object : this.receivers.getArray(ctx)) { + if (object instanceof World world) { + Store store = world.getEntityStore().getStore(); + NotificationUtil.sendNotificationToWorld(message, secondary, icon, + itemWithAllMetadata, style, store); + } else if (object instanceof Player player) { + PacketHandler packetHandler = player.getPlayerRef().getPacketHandler(); + NotificationUtil.sendNotification(packetHandler, message, + secondary, icon, itemWithAllMetadata, style); + } else if (object instanceof PlayerRef playerRef) { + PacketHandler packetHandler = playerRef.getPacketHandler(); + NotificationUtil.sendNotification(packetHandler, message, + secondary, icon, itemWithAllMetadata, style); + } + } + } else { + NotificationUtil.sendNotificationToUniverse(message, secondary, icon, itemWithAllMetadata, style); + } + + return nextStatement; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String to = this.receivers != null ? this.receivers.toString(ctx, debug) : "the universe"; + return "Send notification " + this.message.toString(ctx, debug) + " to " + to; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java index 0c25581e..0f5be780 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/sections/SectionHandler.java @@ -8,6 +8,7 @@ public static void register(SkriptRegistration registration) { SecDropItem.register(registration); SecExecuteInWorld.register(registration); SecPlaySound.register(registration); + SecSendNotification.register(registration); SecSpawnNPC.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 0052ee4d..14a2d477 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 @@ -1,6 +1,5 @@ package com.github.skriptdev.skript.plugin.elements.types; -import com.github.skriptdev.skript.api.skript.registration.AssetStoreRegistry; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.google.gson.Gson; import com.google.gson.JsonElement; @@ -22,6 +21,7 @@ import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause; import com.hypixel.hytale.server.core.modules.entitystats.asset.EntityStatType; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; +import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; import org.bson.BsonDocument; import org.bson.BsonValue; @@ -30,76 +30,77 @@ 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") + static void register(SkriptRegistration reg) { + // Please keep in alphabetical order + reg.newAssetStoreType(BiomeAsset.class, BiomeAsset.getAssetStore().getAssetMap(), "biome", "biome@s") .name("Biome") - .description("Represents the types of biomes in the game.", autoGenMessage()) + .description("Represents the types of biomes in the game.") .since("1.0.0") .toStringFunction(BiomeAsset::getId) .register(); - AssetStoreRegistry.register(registration, BlockGroup.class, AssetRegistry.getAssetStore(BlockGroup.class).getAssetMap(), + reg.newAssetStoreType(BlockGroup.class, AssetRegistry.getAssetStore(BlockGroup.class).getAssetMap(), "blockgroup", "blockgroup@s") .name("Block Group") - .description("Represents the groups of blocks in the game.", autoGenMessage()) + .description("Represents the groups of blocks in the game.") .since("1.0.0") .toStringFunction(BlockGroup::getId) .register(); - AssetStoreRegistry.register(registration, CraftingRecipe.class, CraftingRecipe.getAssetMap(), "craftingrecipe", "craftingrecipe@s") - .name("Crafting Recipe") - .description("Represents the crafting recipes in the game.", autoGenMessage()) - .since("1.0.0") - .toStringFunction(CraftingRecipe::getId) - .register(); - AssetStoreRegistry.register(registration, BlockType.class, BlockType.getAssetMap(), "blocktype", "blockType@s") + reg.newAssetStoreType(BlockType.class, BlockType.getAssetMap(), "blocktype", "blockType@s") .name("BlockType") - .description("Represents the types of blocks in the game.", autoGenMessage()) + .description("Represents the types of blocks in the game.") .examples("set {_block} to blocktype of block at player") .since("1.0.0") .toStringFunction(BlockType::getId) .register(); - AssetStoreRegistry.register(registration, Fluid.class, Fluid.getAssetMap(), "fluid", "fluid@s") - .name("Fluid") - .description("Represents the types of fluids in the game.", autoGenMessage()) + reg.newAssetStoreType(CraftingRecipe.class, CraftingRecipe.getAssetMap(), "craftingrecipe", "craftingrecipe@s") + .name("Crafting Recipe") + .description("Represents the crafting recipes in the game.") .since("1.0.0") - .toStringFunction(Fluid::getId) + .toStringFunction(CraftingRecipe::getId) .register(); - AssetStoreRegistry.register(registration, EntityEffect.class, EntityEffect.getAssetMap(), + reg.newAssetStoreType(DamageCause.class, DamageCause.getAssetMap(), + "damagecause", "damageCause@s") + .name("Damage Cause") + .description("Represents the types of damage that can be caused to entities.") + .since("1.0.0") + .toStringFunction(DamageCause::getId) + .register(); + reg.newAssetStoreType(EntityEffect.class, EntityEffect.getAssetMap(), "entityeffect", "entityEffect@s") .name("Entity Effect") - .description("Represents the types of effects that can be applied to entities.", autoGenMessage()) + .description("Represents the types of effects that can be applied to entities.") .since("1.0.0") .toStringFunction(EntityEffect::getId) .register(); - AssetStoreRegistry.register(registration, EntityStatType.class, EntityStatType.getAssetMap(), + reg.newAssetStoreType(EntityStatType.class, EntityStatType.getAssetMap(), "entitystattype", "entityStatType@s") .name("Entity Stat Type") - .description("Represents the types of stats that can be applied to entities.", autoGenMessage()) + .description("Represents the types of stats that can be applied to entities.") .since("1.0.0") .register(); - AssetStoreRegistry.register(registration, Environment.class, Environment.getAssetMap(), + reg.newAssetStoreType(Environment.class, Environment.getAssetMap(), "environment", "environment@s") .name("Environment") - .description("Represents the types of environments in the game.", autoGenMessage()) + .description("Represents the types of environments in the game.") .since("1.0.0") .toStringFunction(Environment::getId) .register(); - AssetStoreRegistry.register(registration, DamageCause.class, DamageCause.getAssetMap(), - "damagecause", "damageCause@s") - .name("Damage Cause") - .description("Represents the types of damage that can be caused to entities.", autoGenMessage()) + reg.newAssetStoreType(Fluid.class, Fluid.getAssetMap(), "fluid", "fluid@s") + .name("Fluid") + .description("Represents the types of fluids in the game.") .since("1.0.0") - .toStringFunction(DamageCause::getId) + .toStringFunction(Fluid::getId) .register(); - AssetStoreRegistry.register(registration, Interaction.class, Interaction.getAssetMap(), + reg.newAssetStoreType(Interaction.class, Interaction.getAssetMap(), "interaction", "interaction@s") .name("Interaction") - .description("Represents the types of interactions that can be performed by entities.", autoGenMessage()) + .description("Represents the types of interactions that can be performed by entities.") .since("1.0.0") .toStringFunction(Interaction::getId) .register(); - AssetStoreRegistry.register(registration, Item.class, Item.getAssetMap(), "item", "item@s") + reg.newAssetStoreType(Item.class, Item.getAssetMap(), "item", "item@s") .name("Item") - .description("Represents the types of items in the game.", autoGenMessage()) + .description("Represents the types of items in the game.") .examples("set {_i} to itemstack of Food_Fish_Grilled") .since("1.0.0") .toStringFunction(Item::getId) @@ -116,32 +117,39 @@ public Item deserialize(@NotNull Gson gson, @NotNull JsonElement element) { } }) .register(); - AssetStoreRegistry.register(registration, Projectile.class, Projectile.getAssetMap(), "projectile", "projectile@s") + reg.newAssetStoreType(Projectile.class, Projectile.getAssetMap(), "projectile", "projectile@s") .name("Projectile") - .description("Represents the types of projectiles in the game.", autoGenMessage()) + .description("Represents the types of projectiles in the game.") .since("1.0.0") .toStringFunction(Projectile::getId) .register(); - AssetStoreRegistry.register(registration, ResourceType.class, ResourceType.getAssetMap(), "resourcetype", "resourceType@s") + reg.newAssetStoreType(ResourceType.class, ResourceType.getAssetMap(), "resourcetype", "resourceType@s") .name("Resource Type") - .description("Represents the types of resources in the game, such as woods and stones.", autoGenMessage()) + .description("Represents the types of resources in the game, such as woods and stones.") .since("1.0.0") .toStringFunction(ResourceType::getId) .register(); - AssetStoreRegistry.register(registration, SoundEvent.class, SoundEvent.getAssetMap(), "soundevent", "soundevent@s") + reg.newAssetStoreType(RootInteraction.class, RootInteraction.getAssetMap(), + "rootinteraction", "rootinteraction@s") + .name("Interaction - Root") + .description("Represents the types of root interactions that can be performed by entities.") + .since("INSERT VERSION") + .register(); + reg.newAssetStoreType(SoundEvent.class, SoundEvent.getAssetMap(), "soundevent", "soundevent@s") .name("Sound Event") - .description("Represents the types of sounds in the game.", autoGenMessage()) + .description("Represents the types of sounds in the game.") .since("1.0.0") .toStringFunction(SoundEvent::getId) .register(); - AssetStoreRegistry.register(registration, Weather.class, Weather.getAssetMap(), "weather", "weather@s") + reg.newAssetStoreType(Weather.class, Weather.getAssetMap(), "weather", "weather@s") .name("Weather") - .description("Represents the types of weather in the game.", autoGenMessage()) + .description("Represents the types of weather in the game.") .since("1.0.0") .toStringFunction(Weather::getId) .register(); } + // Keep for later, might use down the road! private static String autoGenMessage() { String serverVersion = ManifestUtil.getImplementationVersion(); String patchline = ManifestUtil.getPatchline(); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java index 5547fbac..921ee231 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesBlock.java @@ -1,20 +1,19 @@ package com.github.skriptdev.skript.plugin.elements.types; import com.github.skriptdev.skript.api.hytale.Block; -import com.github.skriptdev.skript.api.skript.registration.EnumRegistry; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.protocol.InteractionType; public class TypesBlock { - static void register(SkriptRegistration registration) { - registration.newType(Block.class, "block", "block@s") + static void register(SkriptRegistration reg) { + reg.newType(Block.class, "block", "block@s") .name("Block") .description("Represents a block in a world.") .since("1.0.0") .toStringFunction(Block::toTypeString) .register(); - EnumRegistry.register(registration, InteractionType.class, "interactiontype", "interactiontype@s") + reg.newEnumType(InteractionType.class, "interactiontype", "interactiontype@s") .name("Interaction Type") .description("Represents the types of interactions that can be performed on a block.") .since("1.0.0") 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 27bec0cb..997400cc 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 @@ -2,10 +2,18 @@ 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.RemoveReason; import com.hypixel.hytale.server.core.entity.Entity; import com.hypixel.hytale.server.core.entity.LivingEntity; import com.hypixel.hytale.server.core.entity.effect.ActiveEntityEffect; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.npc.entities.NPCEntity; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import io.github.syst3ms.skriptparser.types.changers.Changer; +import org.jetbrains.annotations.NotNull; public class TypesEntity { @@ -20,8 +28,28 @@ static void register(SkriptRegistration reg) { 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.") + .description("Represents any Entity in the game, including Players and NPCs.") .since("1.0.0") + .defaultChanger(new Changer<>() { + @Override + public Class[] acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.DELETE) return new Class[] {Entity.class}; + return null; + } + + @Override + public void change(Entity @NotNull [] toChange, Object @NotNull [] changeWith, @NotNull ChangeMode mode) { + if (mode != ChangeMode.DELETE) return; + for (Entity entity : toChange) { + if (entity instanceof Player) continue; + Ref reference = entity.getReference(); + if (reference == null) continue; + World world = entity.getWorld(); + if (world == null) continue; + world.getEntityStore().getStore().removeEntity(reference, RemoveReason.REMOVE); + } + } + }) .register(); reg.newType(LivingEntity.class, "livingentity", "livingEntit@y@ies") .name("Living Entity") diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java index d30a07f8..bfc5aca1 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/types/TypesItem.java @@ -1,6 +1,5 @@ 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.github.skriptdev.skript.api.skript.variables.SerializerUtils; import com.google.gson.Gson; @@ -19,16 +18,16 @@ public class TypesItem { - static void register(SkriptRegistration registration) { + static void register(SkriptRegistration reg) { // Please keep in alphabetical order - registration.newType(ItemComponent.class, "itemcomponent", "itemComponent@s") + reg.newType(ItemComponent.class, "itemcomponent", "itemComponent@s") .name("Item Component") .description("Represents the component of a dropped item.") .since("1.0.0") .toStringFunction(ic -> String.format("ItemComponent{itemstack=%s}", ic.getItemStack())) .serializer(SerializerUtils.getCodecSerializer(ItemComponent.CODEC)) .register(); - registration.newType(ItemContainer.class, "itemcontainer", "itemContainer@s") + reg.newType(ItemContainer.class, "itemcontainer", "itemContainer@s") .name("Item Container") .description("Represents an item container within an inventory (such as the armor container).") .since("1.0.0") @@ -64,7 +63,7 @@ public ItemContainer deserialize(@NotNull Gson gson, @NotNull JsonElement elemen } }) .register(); - registration.newType(ItemStack.class, "itemstack", "itemStack@s") + reg.newType(ItemStack.class, "itemstack", "itemStack@s") .name("Item Stack") .description("Represents an item in an inventory slot.") .examples("set {_i} to itemstack of Food_Fish_Grilled") @@ -87,7 +86,7 @@ public ItemStack deserialize(@NotNull Gson gson, @NotNull JsonElement element) { } }) .register(); - registration.newType(Inventory.class, "inventory", "inventor@y@ies") + reg.newType(Inventory.class, "inventory", "inventor@y@ies") .name("Inventory") .description("Represents an inventory of an entity or block.") .since("1.0.0") @@ -105,7 +104,7 @@ public Inventory deserialize(@NotNull Gson gson, @NotNull JsonElement element) { } }) .register(); - EnumRegistry.register(registration, InventoryActionType.class, "inventoryactiontype", "inventoryActionType@s") + reg.newEnumType(InventoryActionType.class, "inventoryactiontype", "inventoryActionType@s") .name("Inventory Action Type") .description("Represents the types of actions that can be performed in an inventory.") .since("1.0.0") 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 index 5b094170..7efb63b1 100644 --- 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 @@ -1,6 +1,5 @@ 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; @@ -10,7 +9,7 @@ public class TypesPlayer { public static void register(SkriptRegistration reg) { // Keep these in alphabetical order - EnumRegistry.register(reg, GameMode.class, "gamemode", "gamemode@s") + reg.newEnumType(GameMode.class, "gamemode", "gamemode@s") .name("GameMode") .description("Represents a game mode.") .since("1.0.0") 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 775c9f53..9cfded47 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 @@ -10,6 +10,7 @@ import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.protocol.Vector2f; +import com.hypixel.hytale.protocol.packets.interface_.NotificationStyle; import com.hypixel.hytale.server.core.HytaleServer; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.command.system.CommandSender; @@ -70,6 +71,11 @@ public Message deserialize(@NotNull Gson gson, @NotNull JsonElement element) { } }) .register(); + registration.newEnumType(NotificationStyle.class, "notificationstyle", "notificationStyle@s") + .name("Notification Style") + .description("Represents the style of a notification sent to players.") + .since("INSERT VERSION") + .register(); registration.newType(HytaleServer.class, "server", "server@s") .name("Server") .description("Represents the Hytale server.") 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 2d89c1b5..2da4b6e8 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 @@ -1,6 +1,5 @@ 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.SoundCategory; import com.hypixel.hytale.server.core.command.system.arguments.types.RelativeDoublePosition; @@ -29,7 +28,7 @@ static void register(SkriptRegistration reg) { .description("Represents a block position relative to another block position.") .since("1.0.0") .register(); - EnumRegistry.register(reg, SoundCategory.class, "soundcategory", "soundcategor@y@ies") + reg.newEnumType(SoundCategory.class, "soundcategory", "soundcategor@y@ies") .name("Sound Category") .description("Represents a sound category.") .since("1.0.0") diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 2d0f390e..c6fb6722 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -11,7 +11,6 @@ effect-commands: allow-ops: false # Whether or not ops can use effect commands. # If false, they will require the permission specified below in 'required-permission'. - # This currently does not work as Hytale does not support permissions this way. required-permission: hyskript.effect-commands # The permission required to use effect commands (if allow-ops is false). diff --git a/src/main/resources/manifest.json b/src/main/resources/manifest.json index 5b9de4a7..eafc5508 100644 --- a/src/main/resources/manifest.json +++ b/src/main/resources/manifest.json @@ -2,14 +2,19 @@ "Group": "skript", "Name": "HySkript", "Version": "$pluginVersion", - "Description": "Skripty Skripty gonna make a script!", + "Description": "Scripting the most out of your Hytale server.", "Authors": [ { - "Name": "ShaneBee" + "Name": "ShaneBee", + "Url": "https://github.com/ShaneBeee" + }, + { + "Name": "SkriptDev Contributors", + "Url": "https://github.com/SkriptDev/HySkript/graphs/contributors" } ], - "Website": "null", - "ServerVersion": "*", + "Website": "https://github.com/SkriptDev/HySkript", + "ServerVersion": ">=$hytaleVersion", "Dependencies": {}, "OptionalDependencies": {}, "DisabledByDefault": false, diff --git a/src/main/resources/sample-scripts/-file-starting-with-hyphen-is-disabled.sk b/src/main/resources/sample-scripts/-file-starting-with-hyphen-is-disabled.sk new file mode 100644 index 00000000..90d1fdb9 --- /dev/null +++ b/src/main/resources/sample-scripts/-file-starting-with-hyphen-is-disabled.sk @@ -0,0 +1 @@ +# Any script file or directory starting with a hyphen is disabled. diff --git a/src/main/resources/sample-scripts/chat-format.sk b/src/main/resources/sample-scripts/chat-format.sk new file mode 100644 index 00000000..7c0a84fc --- /dev/null +++ b/src/main/resources/sample-scripts/chat-format.sk @@ -0,0 +1,5 @@ +# Chat Format +# A simple way to make your chat look a little nicer + +on player chat: + set context-message-format to formatted "[MyServer] %name of context-playerref%: %context-message%" diff --git a/src/main/resources/sample-scripts/home-command.sk b/src/main/resources/sample-scripts/home-command.sk new file mode 100644 index 00000000..418c013a --- /dev/null +++ b/src/main/resources/sample-scripts/home-command.sk @@ -0,0 +1,40 @@ +# Home Command +# A little home command where players can set/delete homes and teleport to them + +player command /home: + description: "Access to your homes" + permission: "myscript.homes" + sub command /set : + description: "Set a home" + permission: "myscript.homes.set" + trigger: + set {homes::%uuid of player%::%{_name}%} to location of player + send "Set home %{_name}% to your location" + sub command /delete : + description: "Delete a home" + permission: "myscript.homes.delete" + trigger: + if {homes::%uuid of player%::%{_name}%} is set: + delete {homes::%uuid of player%::%{_name}%} + send "Home %{_name}% delete" + else: + send "No home named %{_name}% was found" + sub command /tp : + description: "Teleport to a home" + permission: "myscript.homes.teleport" + trigger: + if {homes::%uuid of player%::%{_name}%} is set: + teleport player to {homes::%uuid of player%::%{_name}%} + send "Teleport to %{_name}%" + else: + send "No home named %{_name}% was found" + sub command /list: + description: "List all your homes" + permission: "myscript.homes.list" + trigger: + if {homes::%uuid of player%::*} is empty: + send "You have no homes set" + else: + send "Your homes:" + loop {homes::%uuid of player%::*}: + send " - %loop-index%" diff --git a/src/main/resources/sample-scripts/join-message.sk b/src/main/resources/sample-scripts/join-message.sk new file mode 100644 index 00000000..5d56b6c8 --- /dev/null +++ b/src/main/resources/sample-scripts/join-message.sk @@ -0,0 +1,10 @@ +# Join Message +# Be welcoming to your players + +on player added to world: + # This prevents the player joined world message + set context-should-broadcast to false + +on player ready: + # When they actually join, let's send a nice message + send formatted "[MyServer] Welcome to our server %name of context-player%"