From 3f67d344b804b0d1ce7ff08cf29eaf64439b369b Mon Sep 17 00:00:00 2001 From: Shane Bee Date: Wed, 11 Feb 2026 20:54:48 -0800 Subject: [PATCH 001/106] Add a testing suite (#14) * initial test work --- trying it out, this will fail im sure * test - add temp auth * test - lets try this again * delete test yml * gradle.yml - remove github test - can only do this locally since we cant get the assets.zip * More work on tests * more test stuff * MORE test stuff * MORE test stuff... again * build.gradle.kts - didnt mean to commit my test parser * Skript - change when test start * TestResults - use TreeMap to sort results * add a few more basic tests * add more tests * More test changes * ExprLocationDirection - fixed location offset * TestRunner - run finish loading on addons to make sure functions register - this is stupid * EffReturn.sk - functions fixed * more tests * TestRunner - more changes * Add test readme * Add test readme - wrong wordr * contributing.md - add link to test readme --- .github/contributing.md | 5 + .github/workflows/gradle.yml | 2 +- .gitignore | 3 + build.gradle.kts | 19 +++ .../api/skript/testing/TestProperties.java | 8 + .../api/skript/testing/TestResults.java | 85 ++++++++++ .../skript/api/skript/testing/TestRunner.java | 148 +++++++++++++++++ .../api/skript/testing/TestRunnerMain.java | 155 ++++++++++++++++++ .../skript/testing/elements/EffAssert.java | 85 ++++++++++ .../testing/elements/ElementHandler.java | 12 ++ .../api/skript/testing/elements/EvtTest.java | 79 +++++++++ .../skriptdev/skript/api/utils/Utils.java | 5 + .../skriptdev/skript/plugin/Skript.java | 10 +- .../plugin/elements/ElementRegistration.java | 7 + src/test/README.md | 64 ++++++++ .../hyskript/expressions/block/ExprBlockAt.sk | 13 ++ .../expressions/entity/ExprNPCType.sk | 6 + .../hyskript/expressions/other/ExprCast.sk | 7 + .../expressions/other/ExprClassInfoOf.sk | 12 ++ .../expressions/other/ExprDistance.sk | 4 + .../other/ExprLocationDirection.sk | 5 + .../expressions/other/ExprLocationOf.sk | 4 + .../hyskript/expressions/world/ExprWorld.sk | 3 + .../expressions/world/ExprWorldSpawn.sk | 8 + .../elements/hyskript/sections/SecDropItem.sk | 6 + .../elements/hyskript/sections/SecSpawnNPC.sk | 7 + .../hyskript/structures/ScriptCommand.sk | 8 + .../conditions/CondExprCompare.sk | 10 ++ .../conditions/CondExprContains.sk | 7 + .../conditions/CondExprIsDivisible.sk | 3 + .../conditions/CondExprIsEmpty.sk | 4 + .../skript-parser/conditions/CondExprIsSet.sk | 6 + .../conditions/CondExprStartsEnds.sk | 4 + .../skript-parser/effects/EffChange.sk | 13 ++ .../elements/skript-parser/effects/EffDoIf.sk | 6 + .../elements/skript-parser/effects/EffExit.sk | 7 + .../skript-parser/effects/EffReturn.sk | 6 + .../skript-parser/expressions/ExprAmount.sk | 4 + .../expressions/ExprArithmeticOperators.sk | 3 + .../expressions/ExprBooleanOperators.sk | 3 + .../skript-parser/expressions/ExprElement.sk | 4 + .../skript-parser/expressions/ExprLength.sk | 2 + .../expressions/ExprMutableList.sk | 7 + .../skript-parser/expressions/ExprParseAs.sk | 5 + .../skript-parser/expressions/ExprTernary.sk | 7 + src/test/skript/tests/test-script.sk | 7 + 46 files changed, 876 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java create mode 100644 src/test/README.md create mode 100644 src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk create mode 100644 src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk create mode 100644 src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk create mode 100644 src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk create mode 100644 src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk create mode 100644 src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk create mode 100644 src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk create mode 100644 src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk create mode 100644 src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk create mode 100644 src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk create mode 100644 src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk create mode 100644 src/test/skript/tests/elements/skript-parser/effects/EffChange.sk create mode 100644 src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk create mode 100644 src/test/skript/tests/elements/skript-parser/effects/EffExit.sk create mode 100644 src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk create mode 100644 src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk create mode 100644 src/test/skript/tests/test-script.sk diff --git a/.github/contributing.md b/.github/contributing.md index d18757a7..d30758ac 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -23,6 +23,8 @@ This way the team can discuss with you whether or not we want this in HySkript. - Use descriptive commit messages - Use descriptive PR titles - Ensure you follow the code style of this project +- Do make sure you add tests and run the test to make sure it works. + - See the [Testing Guide](https://github.com/SkriptDev/HySkript/tree/master/src/test/README.md) for more information. ### Don't: - Don't commit directly to `master` @@ -60,3 +62,6 @@ This way the team can discuss with you whether or not we want this in HySkript. - For expressions, please provide an example of using the getter as well as each changer you have applied. - For all others, please provide at least one example per pattern. - Please see other examples in HySkript for further inspiration. + +### Tests: +See the [Testing Guide](https://github.com/SkriptDev/HySkript/tree/master/src/test/README.md) for more information. diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1c4a8308..2c39e737 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -5,7 +5,7 @@ # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle -name: Java CI with Gradle +name: Build and Upload Artifact on: push: diff --git a/.gitignore b/.gitignore index 8098e8e9..95247ca2 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ bin/ ### Mac OS ### .DS_Store **/.DS_Store + +### Run Folder ### +run/ diff --git a/build.gradle.kts b/build.gradle.kts index 62c02447..a18fcff7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,11 @@ val hytaleVersion = "2026.02.06-aa1b071c2" // https://maven.hytale.com/pre-release/com/hypixel/hytale/Server/maven-metadata.xml // (Pre-releases shouldn't be used for production) +// Location of the Hytale Server Assets +// This is used in testing +// Change this to wherever you have it on your computer +val assetLocation = "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip" + repositories { mavenCentral() mavenLocal() @@ -34,6 +39,12 @@ dependencies { } } +// This is used to enable Gson in the test environment via HytaleServer +val testRunnerClasspath by configurations.creating { + extendsFrom(configurations.compileOnly.get()) + isCanBeResolved = true +} + tasks { register("server", Copy::class) { dependsOn("jar") @@ -42,6 +53,14 @@ tasks { destinationDir = file("/Users/ShaneBee/Desktop/Server/Hytale/Creative/mods/") } } + register("testRunner") { + dependsOn("jar") + group = "application" + mainClass.set("com.github.skriptdev.skript.api.skript.testing.TestRunnerMain") + args(hytaleVersion, assetLocation) + + classpath = sourceSets["main"].runtimeClasspath + testRunnerClasspath + } processResources { filesNotMatching("assets/**") { expand("pluginVersion" to projectVersion, "hytaleVersion" to hytaleVersion) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java new file mode 100644 index 00000000..34d4b6ab --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java @@ -0,0 +1,8 @@ +package com.github.skriptdev.skript.api.skript.testing; + +public class TestProperties { + + public static final boolean ENABLED = Boolean.parseBoolean(System.getProperty("skript.test.enabled", "false")); + public static final String TEST_SCRIPTS_FOLDER = System.getProperty("skript.test.scripts", "mods/skript_HySkript/tests"); + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java new file mode 100644 index 00000000..d44c0bb7 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -0,0 +1,85 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.HySk; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class TestResults { + + private boolean success = true; + private int failCount = 0; + private final Map> successMap = new TreeMap<>(); + private final Map> failureMap = new TreeMap<>(); + + public boolean isSuccess() { + return this.success; + } + + public int getFailCount() { + return this.failCount; + } + + public Map> getSuccessMap() { + return this.successMap; + } + + public Map> getFailureMap() { + return this.failureMap; + } + + public void addSuccess(String test, String value) { + this.successMap.computeIfAbsent(test, _ -> new ArrayList<>()).add(value); + } + + public void addFailure(String test, String value) { + this.success = false; + this.failCount++; + this.failureMap.computeIfAbsent(test, _ -> new ArrayList<>()).add(value); + } + + public void process() { + this.failureMap.forEach((test, _) -> { + // We don't care about success if other tests failed in that test + this.successMap.remove(test); + }); + + } + + public void clear() { + this.success = true; + this.successMap.clear(); + this.failureMap.clear(); + } + + @SuppressWarnings({"CallToPrintStackTrace"}) + public void printToJsonFile() { + Path resolve = HySk.getInstance().getDataDirectory().resolve("test-results.json"); + + try { + Files.createDirectories(resolve.getParent()); + } catch (IOException e) { + throw new RuntimeException("Failed to create directories for " + resolve.toAbsolutePath(), e); + } + + Gson gson = new GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create(); + + try (BufferedWriter writer = Files.newBufferedWriter(resolve, StandardCharsets.UTF_8)) { + gson.toJson(this, writer); + Utils.log("Test-Results successfully written to " + resolve.toAbsolutePath()); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java new file mode 100644 index 00000000..47413325 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -0,0 +1,148 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import com.github.skriptdev.skript.api.skript.testing.elements.EvtTest.TestContext; +import com.github.skriptdev.skript.api.utils.Utils; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.util.MessageUtil; +import fi.sulku.hytale.TinyMsg; +import io.github.syst3ms.skriptparser.lang.Statement; +import io.github.syst3ms.skriptparser.lang.Trigger; +import io.github.syst3ms.skriptparser.lang.TriggerMap; +import io.github.syst3ms.skriptparser.log.LogEntry; +import io.github.syst3ms.skriptparser.log.LogType; +import io.github.syst3ms.skriptparser.parsing.ScriptLoader; +import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import io.github.syst3ms.skriptparser.variables.Variables; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TestRunner { + + private static final ScheduledExecutorService SCHEDULED = HytaleServer.SCHEDULED_EXECUTOR; + private final TestResults testResults = new TestResults(); + private final World world = Universe.get().getWorld("default"); + + @SuppressWarnings("DataFlowIssue") + public void start() { + Runnable runTestsRunnable = () -> { + Utils.log("Running tests in world 'default'..."); + runTests(); + }; + Runnable loadTestsRunnable = () -> { + Utils.log("Testing has started!"); + Utils.log("Loading test scripts..."); + loadTests(); + Utils.log("Finished loading test scripts!"); + + // Make sure the world isn't paused + if (this.world.isPaused()) this.world.setPaused(false); + + // Run our tests in the world to make sure we have access to blocks/entities + this.world.execute(runTestsRunnable); + }; + + // Delay start to make sure the server has finished loading + SCHEDULED.schedule(loadTestsRunnable, 1, TimeUnit.SECONDS); + } + + private void loadTests() { + Path path = Path.of(TestProperties.TEST_SCRIPTS_FOLDER); + loadScripts(path); + } + + private void runTests() { + // Catch exceptions and treat them as failures + Statement.setExceptionHandler(e -> + this.testResults.addFailure("Exception", + e.getClass().getSimpleName() + ": " + e.getMessage())); + + // Run all the test triggers + for (Trigger allTrigger : TriggerMap.getAllTriggers()) { + TestContext context = new TestContext(this.testResults, this.world); + Statement.runAll(allTrigger, context); + Variables.clearLocalVariables(context); + } + + Runnable finishingRunnable = () -> { + // Process results + this.testResults.process(); + + // Print results + if (this.testResults.isSuccess()) { + Message message = TinyMsg.parse("All tests passed!"); + Utils.log(MessageUtil.toAnsiString(message).toAnsi()); + } else { + Utils.error(this.testResults.getFailCount() + " tests failed!"); + this.testResults.getFailureMap().forEach((test, failure) -> + Utils.error(" - [" + test + "]: " + failure)); + } + + Utils.log("Finished running tests!"); + + // Print results to file + this.testResults.printToJsonFile(); + this.testResults.clear(); + + // Shutdown server + // Give it a bit of delay to really make sure we're finished + SCHEDULED.schedule(() -> HytaleServer.get().shutdownServer(), 1, TimeUnit.SECONDS); + }; + + // Delay finish to allow waits in tests + SCHEDULED.schedule(finishingRunnable, 1, TimeUnit.SECONDS); + } + + private void loadScripts(Path directory) { + File scriptsDirectory = directory.toFile(); + Utils.log("Loading test directory '" + scriptsDirectory.getAbsolutePath() + "'..."); + List scriptNames = loadScriptsInDirectory(scriptsDirectory); + SkriptAddon.getAddons().forEach(SkriptAddon::finishedLoading); + + Utils.log("Loaded " + scriptNames.size() + " scripts!"); + } + + private List loadScriptsInDirectory(File directory) { + if (directory == null || !directory.isDirectory()) return List.of(); + + List loadedScripts = new ArrayList<>(); + + 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(file)); + } else { + if (!fileName.endsWith(".sk")) continue; + Utils.log("Loading script '" + fileName + "'..."); + List logEntries = ScriptLoader.loadScript(file.toPath(), false); + for (LogEntry logEntry : logEntries) { + Utils.log(null, logEntry); + if (logEntry.getType() == LogType.ERROR) { + this.testResults.addFailure("Parsing Error:" + fileName, logEntry.getMessage()); + } + } + loadedScripts.add(fileName.substring(0, fileName.length() - 3)); + } + } + return loadedScripts; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java new file mode 100644 index 00000000..64e1b476 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java @@ -0,0 +1,155 @@ +package com.github.skriptdev.skript.api.skript.testing; + +import com.google.gson.Gson; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +public class TestRunnerMain { + + public static final String GREEN = "\u001B[92m"; + public static final String LIGHT_GREY = "\u001B[37m"; + public static final String RED = "\u001B[91m"; + public static final String RESET = "\u001B[0m"; + + private static String serverVersion; + private static String assetPath; + + static void main(String[] args) { + serverVersion = args[0]; + assetPath = args[1]; + + System.out.println("Downloading Hytale Server..."); + downloadHytaleServer(); + System.out.println("Download complete!"); + + System.out.println("Moving plugin to mods folder..."); + movePlugin(); + System.out.println("Plugin moved!"); + + System.out.println("Starting Hytale server..."); + runServer(); + } + + @SuppressWarnings({"resource", "CallToPrintStackTrace"}) + private static void downloadHytaleServer() { + String url = "https://maven.hytale.com/release/com/hypixel/hytale/Server/" + + serverVersion + "/Server-" + serverVersion + ".jar"; + String targetDir = "run/testServer/"; + String newName = "HytaleServer.jar"; + + try { + // 1. Create the directory if it doesn't exist + Path directoryPath = Paths.get(targetDir); + if (!Files.exists(directoryPath)) { + Files.createDirectories(directoryPath); + } + + // 2. Download the file to a temporary location + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + // We download directly to a path to save memory + Path tempFile = Files.createTempFile("tempDownload", ".tmp"); + client.send(request, HttpResponse.BodyHandlers.ofFile(tempFile)); + + // 3. Move and Rename the file + Path finalPath = directoryPath.resolve(newName); + Files.move(tempFile, finalPath, StandardCopyOption.REPLACE_EXISTING); + + System.out.println("File saved to: " + finalPath.toAbsolutePath()); + + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private static void movePlugin() { + File file = new File("run/testServer/mods"); + file.mkdirs(); + try { + Files.copy(Path.of("build/libs/HySkript-1.0.0-pre-release-3.jar"), + Path.of("run/testServer/mods/HySkript-1.0.0-pre-release-3.jar"), + StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("CallToPrintStackTrace") + private static void runServer() { + try { + File serverFolder = new File("run/testServer/"); + ProcessBuilder processBuilder = new ProcessBuilder( + "java", + "-Xms2G", + "-Xmx2G", + "-Dskript.test.enabled=true", + "-Dskript.test.scripts=../../src/test/skript/tests", + "-jar", "HytaleServer.jar", + "--assets", assetPath + ); + processBuilder.inheritIO(); + processBuilder.directory(serverFolder); + Process process = processBuilder.start(); + int exitCode = process.waitFor(); + + // Read results written by the plugin (no Gson required). + Path resultsPath = Path.of("run/testServer/mods/skript_HySkript/test-results.json"); + if (!Files.exists(resultsPath)) { + throw new IllegalStateException( + "Test results file not found at " + resultsPath.toAbsolutePath() + + " (server exit code was " + exitCode + ")" + ); + } + + Gson gson = new Gson(); + TestResults results; + + try (BufferedReader reader = Files.newBufferedReader(resultsPath, StandardCharsets.UTF_8)) { + results = gson.fromJson(reader, TestResults.class); + + System.out.println("Successfully loaded results from: " + resultsPath.getFileName()); + } catch (Exception e) { + System.err.println("Could not read test results: " + e.getMessage()); + e.printStackTrace(); + return; + } + + System.out.println("Succeeded:"); + if (!results.getSuccessMap().isEmpty()) { + results.getSuccessMap().forEach((test, _) -> + System.out.println(" - " + GREEN + test + RESET)); + } else { + System.out.println(" - none"); + } + + System.out.println("Failed:"); + if (results.getFailCount() > 0) { + results.getFailureMap().forEach((test, errorList) -> + errorList.forEach(error -> + System.out.println(" - " + RED + test + LIGHT_GREY + ": " + error + RESET))); + } else { + System.out.println(" - none"); + } + + System.exit(results.getFailCount()); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java new file mode 100644 index 00000000..e3d9ffca --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EffAssert.java @@ -0,0 +1,85 @@ +package com.github.skriptdev.skript.api.skript.testing.elements; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestResults; +import com.github.skriptdev.skript.api.skript.testing.elements.EvtTest.TestContext; +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.log.SkriptLogger; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.parsing.SyntaxParser; +import io.github.syst3ms.skriptparser.types.PatternType; +import io.github.syst3ms.skriptparser.types.TypeManager; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class EffAssert extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffAssert.class, "assert <.+> with %string%") + .noDoc() + .register(); + } + + private String fileName; + private int lineNumber; + private Expression condition; + private String conditionString; + private Expression message; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.message = (Expression) expressions[0]; + + SkriptLogger logger = parseContext.getLogger(); + this.fileName = logger.getFileName(); + this.lineNumber = logger.getLine() + 1; // I think it gets the last line?!?! + this.conditionString = parseContext.getMatches().getFirst().group(); + Optional> patternType = TypeManager.getPatternType("boolean"); + if (patternType.isEmpty()) { + return false; + } + + Optional> expression = SyntaxParser.parseExpression(this.conditionString, + patternType.get(), + parseContext.getParserState(), + parseContext.getLogger()); + if (expression.isEmpty() || expression.get().getReturnType() != Boolean.class) { + return false; + } + this.condition = (Expression) expression.get(); + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + if (!(ctx instanceof TestContext context)) return; + + Optional b = this.condition.getSingle(ctx).filter(Boolean::booleanValue); + TestResults testResults = context.getTestResults(); + + if (b.isEmpty()) { + // Test failed + String message = this.message.getSingle(ctx).orElseThrow(); + String failure = String.format("assert '%s' failed with message \"%s\" {%s:%d}", + this.conditionString, + message, + this.fileName, + this.lineNumber); + testResults.addFailure(context.getTestSubject(), failure); + } else { + String success = String.format("assert '%s' passed", + this.conditionString); + testResults.addSuccess(context.getTestSubject(), success); + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "assert " + this.condition.toString(ctx, debug) + " with " + this.message.toString(ctx, debug); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java new file mode 100644 index 00000000..9ab1a652 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/ElementHandler.java @@ -0,0 +1,12 @@ +package com.github.skriptdev.skript.api.skript.testing.elements; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; + +public class ElementHandler { + + public static void register(SkriptRegistration reg) { + EffAssert.register(reg); + EvtTest.register(reg); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java new file mode 100644 index 00000000..1cb48831 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/elements/EvtTest.java @@ -0,0 +1,79 @@ +package com.github.skriptdev.skript.api.skript.testing.elements; + +import com.github.skriptdev.skript.api.skript.event.WorldContext; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestResults; +import com.github.skriptdev.skript.api.utils.Utils; +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.VariableString; +import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class EvtTest extends SkriptEvent { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtTest.class, "test %*string%") + .setHandledContexts(TestContext.class) + .noDoc() + .register(); + } + + private String testSubject; + + @SuppressWarnings({"OptionalGetWithoutIsPresent"}) + @Override + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + this.testSubject = ((VariableString) expressions[0]).getSingle(TriggerContext.DUMMY).get(); + return true; + } + + @Override + public boolean check(TriggerContext triggerContext) { + if (!(triggerContext instanceof TestContext context)) return false; + context.setTestSubject(this.testSubject); + Utils.logColored("Running test: '" + this.testSubject + "'"); + return true; + } + + @Override + public String toString(@NotNull TriggerContext triggerContext, boolean b) { + return "test " + this.testSubject; + } + + public static final class TestContext implements WorldContext { + private final TestResults testResults; + private String testSubject; + private final World world; + + public TestContext(TestResults testResults, World world) { + this.testResults = testResults; + this.world = world; + } + + public void setTestSubject(String testSubject) { + this.testSubject = testSubject; + } + + public String getTestSubject() { + return testSubject; + } + + public TestResults getTestResults() { + return this.testResults; + } + + @Override + public World getWorld() { + return this.world; + } + + @Override + public String getName() { + return "test context"; + } + } + +} 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 bbbd17f5..0963c6bc 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 @@ -79,6 +79,11 @@ public static void log(String message, Object... args) { log(null, Level.INFO, message, args); } + public static void logColored(String message, Object... args) { + String ansi = MessageUtil.toAnsiString(TinyMsg.parse(message)).toAnsi(); + log(null, Level.INFO, ansi, args); + } + public static void log(IMessageReceiver receiver, String message, Object... args) { log(receiver, Level.INFO, message, args); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java index e15bad3e..0cce07af 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/Skript.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/Skript.java @@ -6,6 +6,8 @@ import com.github.skriptdev.skript.api.skript.command.ArgUtils; import com.github.skriptdev.skript.api.skript.config.SkriptConfig; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestProperties; +import com.github.skriptdev.skript.api.skript.testing.TestRunner; import com.github.skriptdev.skript.api.skript.variables.JsonVariableStorage; import com.github.skriptdev.skript.api.utils.ReflectionUtils; import com.github.skriptdev.skript.api.utils.Utils; @@ -106,6 +108,12 @@ private void setupSkript() { Utils.debug("Hytale finished booting, starting post-load triggers..."); // Start any post-load triggers after Hytale finishes booting. getAddons().forEach(SkriptAddon::finishedLoading); + + // RUN TESTS + if (TestProperties.ENABLED) { + TestRunner testRunner = new TestRunner(); + testRunner.start(); + } }); } @@ -189,7 +197,7 @@ private void loadVariables() { * * @return The Skript registration. */ - public @NotNull io.github.syst3ms.skriptparser.registration.SkriptRegistration getSkriptRegistration() { + public @NotNull SkriptRegistration getSkriptRegistration() { return this.registration; } 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 7954536a..8fb341be 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/ElementRegistration.java @@ -1,6 +1,8 @@ package com.github.skriptdev.skript.plugin.elements; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.api.skript.testing.TestProperties; +import com.github.skriptdev.skript.api.skript.testing.elements.ElementHandler; import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import com.github.skriptdev.skript.plugin.elements.command.ScriptSubCommand; @@ -61,6 +63,11 @@ public void registerElements() { ScriptCommand.register(this.registration); ScriptSubCommand.register(this.registration); + // TEST ELEMENTS + if (TestProperties.ENABLED) { + ElementHandler.register(this.registration); + } + // FINALIZE SETUP this.registration.register(); diff --git a/src/test/README.md b/src/test/README.md new file mode 100644 index 00000000..cc64be41 --- /dev/null +++ b/src/test/README.md @@ -0,0 +1,64 @@ +# TESTS +> [!IMPORTANT] +> This page is a work in progress. + +## Running: + +To run tests, there are a few steps: + +1) In the build.gradle.kts file, you will need to change this line: + `val assetLocation = "/Users/ShaneBee/Desktop/Server/Hytale/Assets/Assets.zip"` + This will need to be changed to the location of your Assets.zip file on your computer. +2) After that you can run tests with `./gradlew testRunner` + The server will start up, the tests will run and you will see the outcome. + +## Making Tests: + +### Location of Tests: + +Tests are in this package under `skript/tests`. +Please make sure to add your test in a matching package to their respective Java packages. + +### Naming Tests: + +The file should match the name of the class you are testing for. +Ex: `ExprSomeThing.java` -> `ExprSomeThing.sk` + +In the test file, the test name should match the class/file name. +Ex: + +```applescript +test "ExprSomeThing": + some test of some sort +``` + +### Available Syntaxes: + +#### Test Event: + +The test event is the top level structure used for your tests to run. +See above for naming conventions. +You can use commands/functions in your tests, but other events will not be called. + +#### Test Event Context Values: + +- `event-world` = The world the tests happen in (which is "default") + +#### Assert Effect: + +The assert effect is used to test conditions and make sure the test can pass. + +Pattern: `assert %=boolean% with %string%` +Example: `assert 1 = 1 with "1 should equal 1"` + +If the test fails, your message and the condition will be sent to the TestResults class, which the TestRunner will use +to decide the outcome of all tests. + +#### Others: + +For the most part, all other syntaxes will be available. +Some notes: + +- Do not use async effects/sections (They'll most likely cause issues) +- Use wait/delay effect with extreme caution + - Delays must be less than a second, or the server is going to stop before your test finishes diff --git a/src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk b/src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk new file mode 100644 index 00000000..a6258936 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/block/ExprBlockAt.sk @@ -0,0 +1,13 @@ +test "ExprBlockAt": + set {_loc} to location(0, 200, 0, event-world) + assert block at {_loc} is set with "The block should be set" + assert blocktype of block at {_loc} == Empty with "High chance a block that high is empty/air, but found: %block type of block at {_loc}%" + + set blocktype of block at {_loc} to rock_stone + assert blocktype of block at {_loc} == rock_stone with "Blocktype should be rock_stone" + + # Let's set it back in case we re-run a test on the same server + set blocktype of block at {_loc} to empty + + set {_loc} to location(0, 0, 0, event-world) + assert blocktype of block at {_loc} == rock_bedrock with "It should be bedrock down there" diff --git a/src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk b/src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk new file mode 100644 index 00000000..5ecdeb9b --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/entity/ExprNPCType.sk @@ -0,0 +1,6 @@ +test "ExprNPCType": + set {_loc} to world spawn of event-world + spawn a sheep at {_loc}: + set {_e} to event-spawned-npc + + assert npc type of {_e} == Sheep with "Should be a sheep, but found %npc type of {_e}%" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk new file mode 100644 index 00000000..769f1a2d --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprCast.sk @@ -0,0 +1,7 @@ +test "ExprCast": + set {_a} to ingredient_poop as BlockType + assert class info of {_a} is "BlockType" with "The type should be BlockType" + + set {_a} to 1 as float + assert {_a} is 1.0 with "The cast should work" + assert class info of {_a} is "Float" with "The type should be Float" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk new file mode 100644 index 00000000..98801802 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprClassInfoOf.sk @@ -0,0 +1,12 @@ +test "ExprClassInfoOf": + set {_i} to itemstack of ingredient_poop + assert classinfo of {_i} = "Item Stack" with "Should be an itemstack but found %classinfo of {_i}%" + + set {_loc} to location(0, 0, 0, event-world) + assert classinfo of {_loc} = "Location" with "Should be a Location, but found %classinfo of {_loc}%" + + set {_b} to block at {_loc} + assert classinfo of {_b} = "Block" with "Should be a block, but found %classinfo of {_b}%" + + set {_bt} to blocktype of {_b} + assert classinfo of {_bt} = "BlockType" with "Should be a BlockType but found %classinfo of {_bt}%" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk new file mode 100644 index 00000000..ae270e3b --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprDistance.sk @@ -0,0 +1,4 @@ +test "ExprDistance": + set {_a} to location(0, 0, 0, event-world) + set {_b} to location(0, 100, 0, event-world) + assert distance between {_a} and {_b} is 100 with "The locations are 100 blocks apart" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk new file mode 100644 index 00000000..3542a6d6 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationDirection.sk @@ -0,0 +1,5 @@ +test "ExprLocationDirection": + set {_l1} to location(0, 0, 0, event-world) + set {_l2} to location 10 blocks north of {_l1} + set {_distance} to distance between {_l1} and {_l2} + assert {_distance} is 10 with "The new location should be 10 blocks away, but found %{_distance}%" diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk new file mode 100644 index 00000000..eb649bfa --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOf.sk @@ -0,0 +1,4 @@ +test "ExprLocationOf": + set {_loc} to location(0, 0, 0, event-world) + set {_b} to block at {_loc} + assert location of {_b} = {_loc} with "The location of the block should be the same as the original location" diff --git a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk new file mode 100644 index 00000000..6ff36373 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorld.sk @@ -0,0 +1,3 @@ +test "ExprWorld": + set {_w} to world named "default" + assert {_w} = event-world with "The default world should match our event-world" diff --git a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk new file mode 100644 index 00000000..e4aecd7c --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldSpawn.sk @@ -0,0 +1,8 @@ +test "ExprWorldSpawn": + set {_a} to world spawn of event-world + assert {_a} is set with "The variable should be set to spawn" + + set {_w} to world("default") + assert {_w} = event-world with "The world should match" + set {_b} to world spawn of {_w} + assert {_b} = {_a} with "The spawn should be the same" diff --git a/src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk b/src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk new file mode 100644 index 00000000..efdb103d --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/sections/SecDropItem.sk @@ -0,0 +1,6 @@ +test "SecDropItem": + set {_l} to world spawn of event-world + drop ingredient_stick at {_l}: + set {_i} to event-item-component + + assert {_i} is set with "The item should have dropped" diff --git a/src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk b/src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk new file mode 100644 index 00000000..3e94869e --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/sections/SecSpawnNPC.sk @@ -0,0 +1,7 @@ +test "SecSpawnNPC": + set {_l} to world spawn location of event-world + spawn a sheep at {_l}: + set {_e} to event-spawned-npc + + assert {_e} is set with "The sheep should have spawned" + assert "%{_e}%" = "NPCEntity{role=Sheep}" with "It should be a sheep" diff --git a/src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk b/src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk new file mode 100644 index 00000000..29726d7f --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/structures/ScriptCommand.sk @@ -0,0 +1,8 @@ +command /letest: + trigger: + set {-var::in_command} to true + +test "ScriptCommand": + execute console command "/letest" + wait 1 tick # The command is async so we have to wait + assert {-var::in_command} is true with "The command should run and set the var" diff --git a/src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk new file mode 100644 index 00000000..06522661 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprCompare.sk @@ -0,0 +1,10 @@ +test "skript-parser-CondCompare": + assert 1 = 1 with "1 should equal 1" + assert 1 != 2 with "1 should not equal 2" + + assert 1 < 2 with "1 should be less than 2" + assert 2 > 1 with "2 should be greater than 1" + + set {_a} to ingredient_poop + set {_b} to ingredient_stick + assert {_a} != {_b} with "Poop and a stick should not be equal" diff --git a/src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk new file mode 100644 index 00000000..4bfa5b86 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprContains.sk @@ -0,0 +1,7 @@ +test "skript-parser-CondExprContains": + assert {_blah::*} does not contain "blah" with "The variable shouldn't contain the string" + add "blah" to {_blah::*} + assert {_blah::*} contains "blah" with "The variable should contain the string" + + assert "blah blah blah" does not contain "cheese" with "The string shouldn't contain the substring" + assert "I love cheese" contains "cheese" with "The string should contain the substring" diff --git a/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk new file mode 100644 index 00000000..974cca24 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsDivisible.sk @@ -0,0 +1,3 @@ +test "skript-parser-CondExprIsDivisible": + #assert 10 is not divisible by 7 with "10 should not be divisible by 7" TODO bug, CondExprIsDivisible isnt doing a neg check + assert 10 is divisible by 2 with "10 should be divisible by 2" diff --git a/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk new file mode 100644 index 00000000..8c7a77ce --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsEmpty.sk @@ -0,0 +1,4 @@ +test "skript-parser-CondExprIsEmpty": + #assert {_a::*} is empty with "The unset variable should be empty" TODO this fails, tries to compare to "empty" (Item/BlockType probably) + set {_a::*} to 1, 2 and 3 + assert {_a::*} is not empty with "The variable should not be empty after setting it" diff --git a/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk new file mode 100644 index 00000000..386d8ff7 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprIsSet.sk @@ -0,0 +1,6 @@ +test "skript-parser-CondExprIsSet": + assert {_a} is not set with "The variable should not be set yet" + set {_a} to "blah" + assert {_a} is set with "The variable should now be set" + delete {_a} + assert {_a} is not set with "The variable should not be set after deleting" diff --git a/src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk b/src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk new file mode 100644 index 00000000..9a65bdc2 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/conditions/CondExprStartsEnds.sk @@ -0,0 +1,4 @@ +test "skript-parser-CondExprStartsEnds": + # TODO neither of these seem to work + #assert ("blah" does not start with "cheese") with "Blah shouldn't start with cheese" + #assert ("cheese is the best" starts with "cheese") with "Cheese is the best should start with cheese" diff --git a/src/test/skript/tests/elements/skript-parser/effects/EffChange.sk b/src/test/skript/tests/elements/skript-parser/effects/EffChange.sk new file mode 100644 index 00000000..d1f950b9 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/effects/EffChange.sk @@ -0,0 +1,13 @@ +test "skript-parser-EffChange": + set {_a} to "ayy" + assert {_a} is "ayy" with "The var should be set" + + set {_b} to 0 + add 10 to {_b} + assert {_b} is 10 with "The var should be 10 after adding" + + remove 5 from {_b} + assert {_b} is 5 with "The var should be 5 after removing" + + delete {_b} + assert {_b} is not set with "The var should be deleted" diff --git a/src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk b/src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk new file mode 100644 index 00000000..db60cb57 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/effects/EffDoIf.sk @@ -0,0 +1,6 @@ +test "skript-parser-EffDoIf": + do set {_a} to 1 if 1 = 10 + assert {_a} is not set with "The do should have been skipped" + + do set {_a} to 1 if 1 = 1 + assert {_a} is 1 with "The do should have been executed" diff --git a/src/test/skript/tests/elements/skript-parser/effects/EffExit.sk b/src/test/skript/tests/elements/skript-parser/effects/EffExit.sk new file mode 100644 index 00000000..2ee7c5cb --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/effects/EffExit.sk @@ -0,0 +1,7 @@ +test "skript-parser-EffExit": + assert {_a} is not set with "The variable should not be set yet" + loop 10 times: + set {_a} to loop-value + exit loop + + assert {_a} = 1 with "The loop should have stopped after 1 iteration" diff --git a/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk b/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk new file mode 100644 index 00000000..67158a74 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/effects/EffReturn.sk @@ -0,0 +1,6 @@ +function testFunc(s: string) :: string: + return "this is a test %{_s}%" + +test "skript-parser-EffReturn": + set {_a} to testFunc("blah") + assert {_a} is "this is a test blah" with "The function should return the value, but found: %{_a}%" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk new file mode 100644 index 00000000..70e33646 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprAmount.sk @@ -0,0 +1,4 @@ +test "skript-parser-ExprAmount": + assert size of {_a::*} = 0 with "The unset variable should be empty" + set {_a::*} to 1, 2 and 3 + assert size of {_a::*} = 3 with "The variable should have three elements" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk new file mode 100644 index 00000000..85120fe5 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprArithmeticOperators.sk @@ -0,0 +1,3 @@ +test "skript-parser-ExprArithmeticOperators": + assert 1 + 2 == 3 with "1 + 2 should equal 3" + assert 1 + 2 != 5 with "1 + 2 should not equal 5" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk new file mode 100644 index 00000000..b90c24b4 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprBooleanOperators.sk @@ -0,0 +1,3 @@ +test "skript-parser-ExprBooleanOperators": + assert ((1 + 1 == 2) && (2 + 2 == 4)) with "Both booleans should match" + assert ((1 + 1 == 5) || (2 + 2 == 4)) with "One of the booleans (the second) should match" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk new file mode 100644 index 00000000..7b999af5 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprElement.sk @@ -0,0 +1,4 @@ +test "skript-parser-ExprElement": + set {_a::*} to "one", "two" and "three" + set {_b} to 2nd element of {_a::*} + assert {_b} is "two" with "The second element should have been 'two'" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk new file mode 100644 index 00000000..025560e2 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprLength.sk @@ -0,0 +1,2 @@ +test "skript-parser-ExprLength": + assert length of "hi bob" = 6 with "There should be 6 characters in the string." diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk new file mode 100644 index 00000000..82f27b35 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprMutableList.sk @@ -0,0 +1,7 @@ +test "skript-parser-ExprMutableList": + set {_a::*} to "nine", "three", "four" and "ten" + set {_a::*} to sorted {_a::*} + assert 1st element of {_a::*} = "four" with "Four should have been the first after sorting" + + set {_a::*} to reversed {_a::*} + assert 1st element of {_a::*} = "three" with "Three should have been the first after reversing" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk new file mode 100644 index 00000000..ea2f94ef --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprParseAs.sk @@ -0,0 +1,5 @@ +test "skript-parser-ExprParseAs": + set {_b} to "1" parsed as Number + + # You broken too? TODO fix + # assert {_b} = 1 with "The string should have parsed as the number 1, but found %{_b}%//%class info of {_b}%" diff --git a/src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk b/src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk new file mode 100644 index 00000000..bc3a8769 --- /dev/null +++ b/src/test/skript/tests/elements/skript-parser/expressions/ExprTernary.sk @@ -0,0 +1,7 @@ +test "skript-parser-ExprTernary": + set {_a} to "test" + set {_b} to {_a} is set ? {_a} : "something else" + assert {_b} = "test" with "It should have picked up {_a}" + + set {_b} to {_c} is set ? {_c} : "something else" + assert {_b} = "something else" with "It should not have picked up {_c}" diff --git a/src/test/skript/tests/test-script.sk b/src/test/skript/tests/test-script.sk new file mode 100644 index 00000000..363fe81d --- /dev/null +++ b/src/test/skript/tests/test-script.sk @@ -0,0 +1,7 @@ +test "Basic HySkript Test": + set {_spawn} to world spawn of world("default") + assert {_spawn} is set with "The main spawn point of the world should be set" + + spawn a sheep at {_spawn}: + set {_sheep} to event-spawned-entity + assert {_sheep} is set with "The sheep entity should be set" From ea1ef486f070784025c56c6fb7a3a3af5cbc899f Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 09:09:17 -0800 Subject: [PATCH 002/106] Block - add methods for block health and damage handling --- .../skriptdev/skript/api/hytale/Block.java | 84 ++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java b/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java index 949cc67f..36e62e48 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/Block.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.api.hytale; -import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.api.hytale.utils.StoreUtils; +import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Store; import com.hypixel.hytale.math.util.ChunkUtil; @@ -8,15 +9,26 @@ import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.fluid.Fluid; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealth; +import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealthChunk; +import com.hypixel.hytale.server.core.modules.blockhealth.BlockHealthModule; +import com.hypixel.hytale.server.core.modules.interaction.BlockHarvestUtils; +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; import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; +import java.util.function.Predicate; + /** * Represents a block in a world. * Hytale doesn't appear to have a representation of a block in the world. @@ -137,7 +149,6 @@ public void setFluid(@NotNull Fluid fluid, @Nullable Integer level) { if (fluidLevel <= 0) fluidLevel = (byte) fluid.getMaxFluidLevel(); } fluidLevel = (byte) Math.clamp((int) fluidLevel, 0, fluid.getMaxFluidLevel()); - Utils.log("Set fluid level to %s", fluidLevel); fluidSection.setFluid(this.pos.getX(), this.pos.getY(), this.pos.getZ(), fluid, fluidLevel); } return chunk; @@ -148,6 +159,75 @@ public void breakBlock(int settings) { this.world.breakBlock(this.pos.getX(), this.pos.getY(), this.pos.getZ(), settings); } + public void damage(@Nullable LivingEntity performer, @Nullable ItemStack itemStack, float damage) { + WorldChunk chunk = this.world.getChunk(ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ())); + if (chunk == null) return; + + Ref ref = chunk.getReference(); + Store chunkStore = this.world.getChunkStore().getStore(); + CommandBuffer commandBuffer = StoreUtils.getCommandBuffer(this.world.getEntityStore().getStore()); + + if (performer == null) { + BlockHarvestUtils.performBlockDamage( + this.pos, + null, + null, + damage, + 0, + ref, + commandBuffer, + chunkStore); + } else { + BlockHarvestUtils.performBlockDamage( + performer, + performer.getReference(), + this.pos, + itemStack, + null, + null, // TODO figure out how to get this + false, + damage, + 0, + ref, + commandBuffer, + chunkStore); + } + } + + public float getBlockHealth() { + WorldChunk chunk = this.world.getChunk(ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ())); + if (chunk == null) return 0; + + Ref ref = chunk.getReference(); + Store chunkStore = this.world.getChunkStore().getStore(); + + BlockHealthChunk component = chunkStore.getComponent(ref, BlockHealthModule.get().getBlockHealthChunkComponentType()); + if (component == null) return 0; + + return component.getBlockHealth(this.pos); + } + + public void setBlockHealth(float health) { + WorldChunk chunk = this.world.getChunk(ChunkUtil.indexChunkFromBlock(this.pos.getX(), this.pos.getZ())); + if (chunk == null) return; + + Ref ref = chunk.getReference(); + Store chunkStore = this.world.getChunkStore().getStore(); + BlockHealthChunk component = chunkStore.getComponent(ref, BlockHealthModule.get().getBlockHealthChunkComponentType()); + if (component == null) return; + + Map blockHealthMap = component.getBlockHealthMap(); + BlockHealth blockHealth = blockHealthMap.getOrDefault(this.pos, new BlockHealth()); + blockHealth.setHealth(health); + blockHealthMap.put(this.pos, blockHealth); + + if (!blockHealth.isDestroyed()) { + Predicate filter = (player) -> true; + world.getNotificationHandler().updateBlockDamage(this.pos.getX(), this.pos.getY(), + this.pos.getZ(), blockHealth.getHealth(), health, filter); + } + } + public @NotNull World getWorld() { return this.world; } From a1e00d72d6287fb550f9895a642b2a121357c6b8 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 09:09:31 -0800 Subject: [PATCH 003/106] ExprBlockHealth - add block health expression --- .../expressions/ExpressionHandler.java | 2 + .../expressions/block/ExprBlockHealth.java | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockHealth.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 953c272d..1f75a495 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -3,6 +3,7 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockAt; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockFluid; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockHealth; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockIterator; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockSphere; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockTypeAtLocation; @@ -64,6 +65,7 @@ public static void register(SkriptRegistration registration) { ExprBlockAt.register(registration); ExprBlockFluid.register(registration); ExprBlockFluidLevel.register(registration); + ExprBlockHealth.register(registration); ExprBlockIterator.register(registration); ExprBlockSphere.register(registration); ExprBlockTypeAtLocation.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockHealth.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockHealth.java new file mode 100644 index 00000000..113671be --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockHealth.java @@ -0,0 +1,75 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ExprBlockHealth implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprBlockHealth.class, Number.class, true, + "block health of %blocks%") + .name("Block Health") + .description("Get/set the health of a block.", + "The values range from 0 to 1") + .examples("set block health of target block of player to 0.5", + "if block health of {_block} > 0:") + .since("INSERT VERSION") + .register(); + } + + private Expression blocks; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.blocks = (Expression) expressions[0]; + return true; + } + + @Override + public Number[] getValues(@NotNull TriggerContext ctx) { + List health = new ArrayList<>(); + + for (Block block : this.blocks.getArray(ctx)) { + health.add(block.getBlockHealth()); + } + + return health.toArray(Number[]::new); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{Number.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Number num)) return; + + for (Block block : this.blocks.getArray(ctx)) { + block.setBlockHealth(num.floatValue()); + } + } + + @Override + public boolean isSingle() { + return this.blocks.isSingle(); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "block health of " + this.blocks.toString(ctx, debug); + } + +} From c308de8944dccca11e8b481c8b598a384766107d Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 09:09:45 -0800 Subject: [PATCH 004/106] EffDamageBlock - add block damage effect --- .../elements/effects/EffectHandler.java | 2 + .../effects/block/EffDamageBlock.java | 82 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/effects/block/EffDamageBlock.java 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 d5851a83..6b0f4bb2 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,6 +2,7 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.effects.block.EffBreakBlock; +import com.github.skriptdev.skript.plugin.elements.effects.block.EffDamageBlock; 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; @@ -26,6 +27,7 @@ public class EffectHandler { public static void register(SkriptRegistration registration) { // BLOCK EffBreakBlock.register(registration); + EffDamageBlock.register(registration); // ENTITY EffDamage.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/block/EffDamageBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/block/EffDamageBlock.java new file mode 100644 index 00000000..a9273d9d --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/block/EffDamageBlock.java @@ -0,0 +1,82 @@ +package com.github.skriptdev.skript.plugin.elements.effects.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.inventory.ItemStack; +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 EffDamageBlock extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffDamageBlock.class, "block damage %blocks% by %number%", + "make %livingEntity% block damage %blocks% by %number%", + "make %livingEntity% block damage %blocks% by %number% using %itemstack%") + .name("Damage Block") + .description("Damages the specified blocks by the specified amount.", + "You can optionally include a LivingEntity to destroy the block,", + "and an ItemStack to simulate the damage as if the entity was holding that item.") + .examples("block damage {_block} by 10", + "make player block damage {_block} by 10", + "make player block damage target block of player by 10 using hotbar item of player") + .experimental("Currently the ItemStack thing doesn't work.") + .since("INSERT VERSION") + .register(); + } + + private Expression livingEntity; + private Expression blocks; + private Expression damage; + private Expression itemstack; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + if (matchedPattern == 0) { + this.blocks = (Expression) expressions[0]; + this.damage = (Expression) expressions[1]; + } else if (matchedPattern == 1) { + this.livingEntity = (Expression) expressions[0]; + this.blocks = (Expression) expressions[1]; + this.damage = (Expression) expressions[2]; + } else if (matchedPattern == 2) { + this.livingEntity = (Expression) expressions[0]; + this.blocks = (Expression) expressions[1]; + this.damage = (Expression) expressions[2]; + this.itemstack = (Expression) expressions[3]; + } + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + Number number = this.damage.getSingle(ctx).orElse(null); + if (number == null) return; + + LivingEntity performer = null; + if (this.livingEntity != null) { + performer = this.livingEntity.getSingle(ctx).orElse(null); + } + ItemStack itemStack = null; + if (this.itemstack != null) { + itemStack = this.itemstack.getSingle(ctx).orElse(null); + } + + float damage = number.floatValue(); + for (Block block : this.blocks.getArray(ctx)) { + block.damage(performer, itemStack, damage); + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String performer = this.livingEntity == null ? "" : "make " + this.livingEntity.toString(ctx, debug) + " "; + String item = this.itemstack == null ? "" : "using " + this.itemstack.toString(ctx, debug); + return performer + "block damage " + this.blocks.toString(ctx, debug) + " by " + this.damage.toString(ctx, debug) + item; + } + +} From 6221c689344a7183a646585e016f4cca6b7448b5 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 09:45:52 -0800 Subject: [PATCH 005/106] ExprWorldTime - add world date/time expression --- .../expressions/ExpressionHandler.java | 2 + .../expressions/world/ExprWorldTime.java | 72 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 1f75a495..9708a574 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -57,6 +57,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorld; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldOf; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldSpawn; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldTime; public class ExpressionHandler { @@ -131,6 +132,7 @@ public static void register(SkriptRegistration registration) { ExprWorld.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); + ExprWorldTime.register(registration); // TEST ExprCurrentContext.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java new file mode 100644 index 00000000..ed79d69b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java @@ -0,0 +1,72 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.util.SkriptDate; +import io.github.syst3ms.skriptparser.util.Time; +import org.jetbrains.annotations.NotNull; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.TimeZone; + +public class ExprWorldTime implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprWorldTime.class, Object.class, true, + "world (:time|date) (in|of) %world%") + .name("World Date/Time") + .description("Gets the date or time of a world.") + .examples("set {_date} to world date of world of player", + "set {_time} to world time of world of player") + .since("INSERT VERSION") + .register(); + } + + private boolean time; + private Expression world; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.world = (Expression) expressions[0]; + this.time = parseContext.hasMark("time"); + return true; + } + + @Override + public Object[] getValues(@NotNull TriggerContext ctx) { + World world = this.world.getSingle(ctx).orElse(null); + if (world == null) return null; + + WorldTimeResource worldTimeResource = world.getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()); + Instant gameTime = worldTimeResource.getGameTime(); + + LocalDateTime gameDateTime = worldTimeResource.getGameDateTime(); + + if (this.time) { + return new Time[]{Time.of(gameDateTime.toLocalTime())}; + } else { + SkriptDate skriptDate = SkriptDate.of(gameTime.toEpochMilli(), TimeZone.getTimeZone(ZoneId.systemDefault())); + return new SkriptDate[]{skriptDate}; + } + } + + @Override + public Class getReturnType() { + if (this.time) return Time.class; + return SkriptDate.class; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = this.time ? "time" : "date"; + return "world " + type + " of " + this.world.toString(ctx, debug); + } + +} From 48e690d0fdec4c098553e147bc8a62346ece10e5 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 14:50:02 -0800 Subject: [PATCH 006/106] ExprWorldTime - rename and add setter --- .../expressions/ExpressionHandler.java | 4 +- ...rWorldTime.java => ExprWorldDateTime.java} | 57 ++++++++++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) rename src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/{ExprWorldTime.java => ExprWorldDateTime.java} (50%) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 9708a574..6719aca6 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -57,7 +57,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorld; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldOf; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldSpawn; -import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldTime; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldDateTime; public class ExpressionHandler { @@ -130,9 +130,9 @@ public static void register(SkriptRegistration registration) { ExprChunkAtLocation.register(registration); ExprRelativePositionResolve.register(registration); ExprWorld.register(registration); + ExprWorldDateTime.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); - ExprWorldTime.register(registration); // TEST ExprCurrentContext.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java similarity index 50% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java index ed79d69b..24f072d6 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTime.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java @@ -1,11 +1,14 @@ package com.github.skriptdev.skript.plugin.elements.expressions.world; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Store; import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; 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; import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; import io.github.syst3ms.skriptparser.util.SkriptDate; import io.github.syst3ms.skriptparser.util.Time; import org.jetbrains.annotations.NotNull; @@ -13,29 +16,32 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Optional; import java.util.TimeZone; -public class ExprWorldTime implements Expression { +public class ExprWorldDateTime implements Expression { public static void register(SkriptRegistration reg) { - reg.newExpression(ExprWorldTime.class, Object.class, true, + reg.newExpression(ExprWorldDateTime.class, Object.class, true, "world (:time|date) (in|of) %world%") .name("World Date/Time") - .description("Gets the date or time of a world.") + .description("Get/set the date or time of a world.") .examples("set {_date} to world date of world of player", - "set {_time} to world time of world of player") + "set {_time} to world time of world of player", + "set world time of world of player to 12:00 pm") .since("INSERT VERSION") .register(); } - private boolean time; + private boolean isTime; private Expression world; @SuppressWarnings("unchecked") @Override public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { this.world = (Expression) expressions[0]; - this.time = parseContext.hasMark("time"); + this.isTime = parseContext.hasMark("time"); return true; } @@ -49,7 +55,7 @@ public Object[] getValues(@NotNull TriggerContext ctx) { LocalDateTime gameDateTime = worldTimeResource.getGameDateTime(); - if (this.time) { + if (this.isTime) { return new Time[]{Time.of(gameDateTime.toLocalTime())}; } else { SkriptDate skriptDate = SkriptDate.of(gameTime.toEpochMilli(), TimeZone.getTimeZone(ZoneId.systemDefault())); @@ -57,15 +63,48 @@ public Object[] getValues(@NotNull TriggerContext ctx) { } } + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) { + if (this.isTime) return Optional.of(new Class[]{Time.class}); + return Optional.of(new Class[]{SkriptDate.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0) return; + + World world = this.world.getSingle(ctx).orElse(null); + if (world == null) return; + + Store store = world.getEntityStore().getStore(); + WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType()); + + + if (this.isTime && changeWith[0] instanceof Time time) { + double dayTime = (double) time.toMillis() / 1000 / 60 / 60 / 24; + worldTimeResource.setDayTime(dayTime, world, store); + } else if (!this.isTime && changeWith[0] instanceof SkriptDate date) { + LocalDateTime localDateTime = date.toLocalDateTime(); + Instant instant = Instant.ofEpochMilli(localDateTime + .toInstant(ZoneOffset.UTC.getRules().getOffset(localDateTime)) + .toEpochMilli()); + worldTimeResource.setGameTime(instant, world, store); + } + } + @Override public Class getReturnType() { - if (this.time) return Time.class; + if (this.isTime) return Time.class; return SkriptDate.class; } @Override public String toString(@NotNull TriggerContext ctx, boolean debug) { - String type = this.time ? "time" : "date"; + String type = this.isTime ? "time" : "date"; return "world " + type + " of " + this.world.toString(ctx, debug); } From 138b38c6c87b8be23b2fcbbda00e6eab2c74c2cd Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 15:21:22 -0800 Subject: [PATCH 007/106] DefaultFunctions - add some date/time functions --- .../elements/functions/DefaultFunctions.java | 94 ++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) 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 9074ad04..ae6e6064 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 @@ -12,15 +12,107 @@ import io.github.syst3ms.skriptparser.structures.functions.FunctionParameter; import io.github.syst3ms.skriptparser.structures.functions.Functions; import io.github.syst3ms.skriptparser.structures.functions.JavaFunction; +import io.github.syst3ms.skriptparser.util.SkriptDate; +import io.github.syst3ms.skriptparser.util.Time; import org.bson.BsonDocument; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + public class DefaultFunctions { public static void register(SkriptRegistration reg) { + dateTimeFunctions(reg); itemFunctions(reg); positionFunctions(reg); } + private static void dateTimeFunctions(SkriptRegistration reg) { + Functions.newJavaFunction(reg, new JavaFunction<>( + "date", + new FunctionParameter[]{ + new FunctionParameter<>("year", Number.class, true), + new FunctionParameter<>("month", Number.class, true), + new FunctionParameter<>("day", Number.class, true) + }, + SkriptDate.class, + true + ) { + @Override + public SkriptDate[] executeSimple(Object[][] params) { + Number year = (Number) params[0][0]; + Number month = (Number) params[1][0]; + Number day = (Number) params[2][0]; + LocalDateTime localDateTime = LocalDateTime.of(year.intValue(), month.intValue(), + day.intValue(), 0, 0); + long epochSecond = localDateTime.toEpochSecond(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); + return new SkriptDate[]{SkriptDate.of(epochSecond * 1000)}; + } + }) + .name("Date") + .description("Creates a new Date with the given parameters.") + .examples("set {_date} to date(2026, 1, 1)") + .since("INSERT VERSION") + .register(); + + Functions.newJavaFunction(reg, new JavaFunction<>( + "dateTime", + new FunctionParameter[]{ + new FunctionParameter<>("year", Number.class, true), + new FunctionParameter<>("month", Number.class, true), + new FunctionParameter<>("day", Number.class, true), + new FunctionParameter<>("hour", Number.class, true), + new FunctionParameter<>("minute", Number.class, true), + new FunctionParameter<>("second", Number.class, true) + }, + SkriptDate.class, + true) { + @Override + public SkriptDate[] executeSimple(Object[][] params) { + Number year = (Number) params[0][0]; + Number month = (Number) params[1][0]; + Number day = (Number) params[2][0]; + Number hour = (Number) params[3][0]; + Number minute = (Number) params[4][0]; + Number second = (Number) params[5][0]; + LocalDateTime localDateTime = LocalDateTime.of(year.intValue(), month.intValue(), + day.intValue(), hour.intValue(), minute.intValue(), second.intValue()); + long epochSecond = localDateTime.toEpochSecond(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); + return new SkriptDate[]{SkriptDate.of(epochSecond * 1000)}; + } + }) + .name("DateTime") + .description("Creates a new Date with a time with the given parameters.", + "Reminder this is on a 24 hour clock.") + .examples("set {_date} to dateTime(2026, 1, 1, 12, 30, 0)") + .since("INSERT VERSION") + .register(); + + Functions.newJavaFunction(reg, new JavaFunction<>( + "time", + new FunctionParameter[]{ + new FunctionParameter<>("hour", Number.class, true), + new FunctionParameter<>("minute", Number.class, true), + new FunctionParameter<>("second", Number.class, true) + }, + Time.class, + true) { + @Override + public Time[] executeSimple(Object[][] params) { + Number hour = (Number) params[0][0]; + Number minute = (Number) params[1][0]; + Number second = (Number) params[2][0]; + return new Time[]{Time.of(hour.intValue(), minute.intValue(), second.intValue(), 0)}; + } + }) + .name("Time") + .description("Creates a new Time with the given parameters.", + "Reminder this is on a 24 hour clock.") + .examples("set {_time} to time(12, 0, 0)") + .since("INSERT VERSION") + .register(); + } + private static void itemFunctions(SkriptRegistration reg) { Functions.newJavaFunction(reg, new JavaFunction<>( "itemstack", @@ -77,7 +169,7 @@ public Location[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(reg,new JavaFunction<>( + Functions.newJavaFunction(reg, new JavaFunction<>( "vector3i", new FunctionParameter[]{ new FunctionParameter<>("x", Number.class, true), From cd7d4a4ecad7c92634a0587f5ae1ebf5fb12aa72 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 15:31:07 -0800 Subject: [PATCH 008/106] SkriptConfig - add default date/time formats --- .../skript/api/skript/config/SkriptConfig.java | 12 ++++++++++++ src/main/resources/config.sk | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java b/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java index 5c321361..e001e18c 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java @@ -8,6 +8,8 @@ import io.github.syst3ms.skriptparser.log.ErrorType; import io.github.syst3ms.skriptparser.log.LogEntry; import io.github.syst3ms.skriptparser.log.SkriptLogger; +import io.github.syst3ms.skriptparser.util.SkriptDate; +import io.github.syst3ms.skriptparser.util.Time; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; @@ -88,6 +90,16 @@ public SkriptConfig(Skript skript) { // Set up commands generate permissions this.commandsGeneratePermissions = this.config.getBoolean("commands-generate-permissions", true); + // Setup Date/Time formats + String time = this.config.getString("default-time-format", "HH:mm:ss"); + if (time != null) { + Time.setDefaultTimeFormat(time); + } + String date = this.config.getString("default-date-format", "EEEE dd MMMM yyyy HH:mm:ss"); + if (date != null) { + SkriptDate.setDefaultDateFormat(date); + } + logger.finalizeLogs(); for (LogEntry logEntry : logger.close()) { Utils.log(null, logEntry); diff --git a/src/main/resources/config.sk b/src/main/resources/config.sk index 9c803be6..f64af395 100644 --- a/src/main/resources/config.sk +++ b/src/main/resources/config.sk @@ -18,6 +18,13 @@ max-target-block-distance: 160 # You can also disable it per command in the command entries. commands-generate-permissions: true +# The default time and date formats. +# Time: default would show as "17:30:00" +default-time-format: HH:mm:ss + +# Date: default would show as "Thursday 12 February 2026 17:30:00" +default-date-format: EEEE dd MMMM yyyy HH:mm:ss + # Effect commands allow you to execute effects in chat. effect-commands: # Whether or not effect commands are enabled. From 032f9003621f33b60c6029c195deead9058293c3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 15:35:20 -0800 Subject: [PATCH 009/106] EffServerShutdown - add shutdown effect --- .../elements/effects/EffectHandler.java | 4 ++ .../effects/server/EffServerShutdown.java | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/effects/server/EffServerShutdown.java 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 6b0f4bb2..4b2d9243 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 @@ -18,6 +18,7 @@ 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.server.EffServerShutdown; 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; @@ -51,6 +52,9 @@ public static void register(SkriptRegistration registration) { EffConnect.register(registration); EffKick.register(registration); + // SERVERE + EffServerShutdown.register(registration); + // WORLD EffChunkRegenerate.register(registration); EffExplosion.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/server/EffServerShutdown.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/server/EffServerShutdown.java new file mode 100644 index 00000000..0c34e1ab --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/server/EffServerShutdown.java @@ -0,0 +1,39 @@ +package com.github.skriptdev.skript.plugin.elements.effects.server; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.HytaleServer; +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 EffServerShutdown extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffServerShutdown.class, "shutdown server") + .name("Shutdown Server") + .description("Shuts down the server.") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + if (HytaleServer.get().isShuttingDown()) { + return; + } + HytaleServer.get().shutdownServer(); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "shutdown server"; + } + +} From d23eab0856243461f7d843d130ced1faed846f51 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 18:14:20 -0800 Subject: [PATCH 010/106] ExprItemContainer - add more combined types --- .../expressions/item/ExprItemContainer.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java index 77e2b4e3..92ee97ff 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java @@ -20,9 +20,14 @@ public static void register(SkriptRegistration reg) { "utility item container of %inventory%", "tools item container of %inventory%", "backpack item container of %inventory%", - "combined everything item container of %inventory%") + "combined everything item container of %inventory%", + "combined hotbar first item container of %inventory%", + "combined storage first item container of %inventory%", + "combined backpack storage hotbar item container of %inventory%", + "combined armor hotbar storage item container of %inventory%") .name("Item Container of Inventory") - .description("Returns different item containers of an inventory.") + .description("Returns different item containers of an inventory.", + "Theres also a few combined options (These are from Hytale).") .since("1.0.0") .register(); } @@ -52,6 +57,10 @@ public ItemContainer[] getValues(@NotNull TriggerContext ctx) { case 4 -> inventory.getTools(); case 5 -> inventory.getBackpack(); case 6 -> inventory.getCombinedEverything(); + case 7 -> inventory.getCombinedHotbarFirst(); + case 8 -> inventory.getCombinedStorageFirst(); + case 9 -> inventory.getCombinedBackpackStorageHotbar(); + case 10 -> inventory.getCombinedArmorHotbarStorage(); default -> null; }; return new ItemContainer[]{container}; @@ -67,6 +76,10 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { case 4 -> "tools"; case 5 -> "backpack"; case 6 -> "combined everything"; + case 7 -> "combined hotbar first"; + case 8 -> "combined storage first"; + case 9 -> "combined backpack storage hotbar"; + case 10 -> "combined armor hotbar storage"; default -> "unknown"; }; return type + " item container of " + this.inventory.toString(ctx, debug); From c15ef490da94138ae0ac36d075a1749257472d60 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 18:19:51 -0800 Subject: [PATCH 011/106] EffOpenItemContainer - add effect to open an item container --- .../elements/effects/EffectHandler.java | 4 +- .../effects/player/EffOpenItemContainer.java | 80 +++++++++++++++++++ .../plugin/elements/types/TypesItem.java | 6 ++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffOpenItemContainer.java 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 4b2d9243..b10e649f 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 @@ -18,6 +18,7 @@ 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.player.EffOpenItemContainer; import com.github.skriptdev.skript.plugin.elements.effects.server.EffServerShutdown; import com.github.skriptdev.skript.plugin.elements.effects.world.EffChunkRegenerate; import com.github.skriptdev.skript.plugin.elements.effects.world.EffExplosion; @@ -51,8 +52,9 @@ public static void register(SkriptRegistration registration) { EffBan.register(registration); EffConnect.register(registration); EffKick.register(registration); + EffOpenItemContainer.register(registration); - // SERVERE + // SERVER EffServerShutdown.register(registration); // WORLD diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffOpenItemContainer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffOpenItemContainer.java new file mode 100644 index 00000000..113e4b8e --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/player/EffOpenItemContainer.java @@ -0,0 +1,80 @@ +package com.github.skriptdev.skript.plugin.elements.effects.player; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.protocol.packets.interface_.Page; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.windows.ContainerWindow; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +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; + +public class EffOpenItemContainer extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffOpenItemContainer.class, + "open %itemContainer% to %players%", + "open %itemContainer% to %players% (with|using) page %page%") + .name("Open ItemContainer") + .description("Opens an ItemContainer to the specified players.", + "You can optionally choose a page type to use (Default is Bench).", + "Don't use the `custom` page, it'll kick the player.") + .examples("open storage item container of inventory of player to player") + .since("INSERT VERSION") + .register(); + } + + private Expression itemContainer; + private Expression players; + private Expression page; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.itemContainer = (Expression) expressions[0]; + this.players = (Expression) expressions[1]; + if (matchedPattern == 1) { + this.page = (Expression) expressions[2]; + } + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + ItemContainer container = this.itemContainer.getSingle(ctx).orElse(null); + if (container == null) return; + + Page page = Page.Bench; + if (this.page != null) { + Page page1 = this.page.getSingle(ctx).orElse(null); + if (page1 != null) page = page1; + } + + for (Player player : this.players.getArray(ctx)) { + openContainer(container, player, page); + } + } + + private void openContainer(ItemContainer container, Player player, Page page) { + Ref ref = player.getReference(); + if (ref == null) return; + + Store store = ref.getStore(); + + player.getPageManager().setPageWithWindows(ref, store, page, + true, new ContainerWindow(container)); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String page = this.page != null ? " with page " + this.page.toString(ctx, debug) : ""; + return "open item container " + this.itemContainer.toString(ctx, debug) + + " to " + players.toString(ctx, debug) + page; + } + +} 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 1d87078c..531c966b 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 @@ -6,6 +6,7 @@ import com.google.gson.JsonElement; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.protocol.InventoryActionType; +import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; @@ -116,6 +117,11 @@ public Inventory deserialize(@NotNull Gson gson, @NotNull JsonElement element) { .description("Represents the types of actions that can be performed in an inventory.") .since("1.0.0") .register(); + reg.newEnumType(Page.class, "page", "page@s") + .name("Page") + .description("Represents a page type of an inventory.") + .since("INSERT VERSION") + .register(); } } From 97bf293a170699e92994c4a04a33eb3a1e1b5478 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 18:48:04 -0800 Subject: [PATCH 012/106] EffFreeze - add effect to freeze --- .../elements/effects/EffectHandler.java | 2 + .../elements/effects/entity/EffFreeze.java | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffFreeze.java 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 b10e649f..f2fbe60d 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 @@ -6,6 +6,7 @@ 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.EffFreeze; 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; @@ -35,6 +36,7 @@ public static void register(SkriptRegistration registration) { EffDamage.register(registration); EffDropItem.register(registration); EffEntityEffect.register(registration); + EffFreeze.register(registration); EffInteraction.register(registration); EffKill.register(registration); EffSpawnEntity.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffFreeze.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffFreeze.java new file mode 100644 index 00000000..849b55da --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffFreeze.java @@ -0,0 +1,61 @@ +package com.github.skriptdev.skript.plugin.elements.effects.entity; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.entity.Frozen; +import com.hypixel.hytale.server.core.entity.LivingEntity; +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; + +public class EffFreeze extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffFreeze.class, "freeze %livingentities%", + "unfreeze %livingentities%") + .name("Freeze Entity") + .description("Freeze or unfreeze the specified entities.", + "This doesn't appear to work on players.") + .examples("freeze entities in radius 10 around player", + "unfreeze {_e}") + .since("INSERT VERSION") + .register(); + } + + private boolean freeze; + private Expression livingEntities; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.freeze = matchedPattern == 0; + this.livingEntities = (Expression) expressions[0]; + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + for (LivingEntity livingEntity : this.livingEntities.getArray(ctx)) { + Ref reference = livingEntity.getReference(); + if (reference == null) continue; + + Store store = reference.getStore(); + if (this.freeze) { + store.ensureComponent(reference, Frozen.getComponentType()); + } else { + store.removeComponentIfExists(reference, Frozen.getComponentType()); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String freeze = this.freeze ? "freeze" : "unfreeze"; + return freeze + " " + this.livingEntities.toString(ctx, debug); + } + +} From a70027e399ace012b98b1a8bcaf4246df72bfaee Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 19:04:03 -0800 Subject: [PATCH 013/106] CondEntityIsFrozen - add condition to check if frozen --- .../conditions/CondEntityIsFrozen.java | 39 +++++++++++++++++++ .../elements/conditions/ConditionHandler.java | 1 + .../hyskript/effects/entity/EffFreeze.sk | 9 +++++ 3 files changed, 49 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java create mode 100644 src/test/skript/tests/elements/hyskript/effects/entity/EffFreeze.sk diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java new file mode 100644 index 00000000..ab0252fe --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java @@ -0,0 +1,39 @@ +package com.github.skriptdev.skript.plugin.elements.conditions; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.entity.Frozen; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import org.jetbrains.annotations.NotNull; + +public class CondEntityIsFrozen extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondEntityIsFrozen.class, + "livingentities", + ConditionalType.BE, + "frozen") + .name("Entity is Frozen") + .description("Checks if the living entities are frozen.") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.getPerformer().check(ctx, livingEntity -> { + Ref reference = livingEntity.getReference(); + if (reference == null) return false; + + Store store = reference.getStore(); + Frozen component = store.getComponent(reference, Frozen.getComponentType()); + return component != null; + }, isNegated()); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 2404495f..e1202e72 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -6,6 +6,7 @@ public class ConditionHandler { public static void register(SkriptRegistration registration) { + CondEntityIsFrozen.register(registration); CondHasPermission.register(registration); CondInventoryCanHold.register(registration); CondPlayerIsCrouching.register(registration); diff --git a/src/test/skript/tests/elements/hyskript/effects/entity/EffFreeze.sk b/src/test/skript/tests/elements/hyskript/effects/entity/EffFreeze.sk new file mode 100644 index 00000000..3fef9cf2 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/effects/entity/EffFreeze.sk @@ -0,0 +1,9 @@ +test "EffFreeze": + spawn a sheep at world spawn of event-world: + set {_e} to event-spawned-entity + + assert {_e} is not frozen with "The sheep shouldn't be frozen yet" + freeze {_e} + assert {_e} is frozen with "The sheep should now be frozen" + unfreeze {_e} + assert {_e} is not frozen with "The sheep should no longer be frozen" From 678bd185a81a1aff253684837207c4926e60e265 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 19:34:45 -0800 Subject: [PATCH 014/106] ExprWorldDateTime - add support for adding/removing duration --- .../expressions/world/ExprWorldDateTime.java | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java index 24f072d6..3f395ad8 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldDateTime.java @@ -13,6 +13,7 @@ import io.github.syst3ms.skriptparser.util.Time; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -26,10 +27,13 @@ public static void register(SkriptRegistration reg) { reg.newExpression(ExprWorldDateTime.class, Object.class, true, "world (:time|date) (in|of) %world%") .name("World Date/Time") - .description("Get/set the date or time of a world.") + .description("Get/set the date or time of a world.", + "You can also add/remove a duration to the time/date.") .examples("set {_date} to world date of world of player", "set {_time} to world time of world of player", - "set world time of world of player to 12:00 pm") + "set world time of world of player to 12:00 pm", + "add 3 hours to world time of world of player", + "add 1 year to world date of world of player") .since("INSERT VERSION") .register(); } @@ -68,6 +72,8 @@ public Optional[]> acceptsChange(@NotNull ChangeMode mode) { if (mode == ChangeMode.SET) { if (this.isTime) return Optional.of(new Class[]{Time.class}); return Optional.of(new Class[]{SkriptDate.class}); + } else if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) { + return Optional.of(new Class[]{Duration.class}); } return Optional.empty(); } @@ -84,15 +90,27 @@ public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType()); - if (this.isTime && changeWith[0] instanceof Time time) { - double dayTime = (double) time.toMillis() / 1000 / 60 / 60 / 24; - worldTimeResource.setDayTime(dayTime, world, store); - } else if (!this.isTime && changeWith[0] instanceof SkriptDate date) { - LocalDateTime localDateTime = date.toLocalDateTime(); - Instant instant = Instant.ofEpochMilli(localDateTime - .toInstant(ZoneOffset.UTC.getRules().getOffset(localDateTime)) - .toEpochMilli()); - worldTimeResource.setGameTime(instant, world, store); + if (changeMode == ChangeMode.SET) { + if (this.isTime && changeWith[0] instanceof Time time) { + double dayTime = (double) time.toMillis() / 1000 / 60 / 60 / 24; + worldTimeResource.setDayTime(dayTime, world, store); + } else if (!this.isTime && changeWith[0] instanceof SkriptDate date) { + LocalDateTime localDateTime = date.toLocalDateTime(); + Instant instant = Instant.ofEpochMilli(localDateTime + .toInstant(ZoneOffset.UTC.getRules().getOffset(localDateTime)) + .toEpochMilli()); + worldTimeResource.setGameTime(instant, world, store); + } + } else if (changeMode == ChangeMode.ADD || changeMode == ChangeMode.REMOVE) { + if (changeWith[0] instanceof Duration duration) { + Instant newTime; + if (changeMode == ChangeMode.ADD) { + newTime = worldTimeResource.getGameTime().plus(duration); + } else { + newTime = worldTimeResource.getGameTime().minus(duration); + } + worldTimeResource.setGameTime(newTime, world, store); + } } } From 41888583bb78c3ea246752d7f048fff7ce24e187 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 19:43:06 -0800 Subject: [PATCH 015/106] SkriptConfig - add a date/time pattern validator --- .../api/skript/config/SkriptConfig.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java b/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java index e001e18c..54f788d0 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java @@ -20,6 +20,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -91,13 +92,31 @@ public SkriptConfig(Skript skript) { this.commandsGeneratePermissions = this.config.getBoolean("commands-generate-permissions", true); // Setup Date/Time formats - String time = this.config.getString("default-time-format", "HH:mm:ss"); + String defaultTime = "HH:mm:ss"; + String time = this.config.getString("default-time-format", defaultTime); if (time != null) { - Time.setDefaultTimeFormat(time); + try { + // Validate the pattern before using it + DateTimeFormatter.ofPattern(time); + Time.setDefaultTimeFormat(time); + } catch (IllegalArgumentException e) { + logger.error("Invalid default time format: '" + time + "' Reason: '" + e.getMessage() + "'", + ErrorType.STRUCTURE_ERROR); + Time.setDefaultTimeFormat(defaultTime); + } } - String date = this.config.getString("default-date-format", "EEEE dd MMMM yyyy HH:mm:ss"); + String defaultDate = "EEEE dd MMMM yyyy HH:mm:ss"; + String date = this.config.getString("default-date-format", defaultDate); if (date != null) { - SkriptDate.setDefaultDateFormat(date); + try { + // Validate the pattern before using it + DateTimeFormatter.ofPattern(date); + SkriptDate.setDefaultDateFormat(date); + } catch (IllegalArgumentException e) { + logger.error("Invalid default date format: '" + date + "' Reason: '" + e.getMessage() + "'", + ErrorType.STRUCTURE_ERROR); + SkriptDate.setDefaultDateFormat(defaultDate); + } } logger.finalizeLogs(); From bfc4e53f51d030f4219c89ec365f0f3182c5c98d Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Thu, 12 Feb 2026 20:46:58 -0800 Subject: [PATCH 016/106] ExprEnvironmentAtLocation - add expression for environment at location --- .../expressions/ExpressionHandler.java | 2 + .../world/ExprEnvironmentAtLocation.java | 96 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprEnvironmentAtLocation.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 6719aca6..72de4936 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -53,6 +53,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerRespawnLocations; import com.github.skriptdev.skript.plugin.elements.expressions.server.ExprConsole; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprChunkAtLocation; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprEnvironmentAtLocation; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprRelativePositionResolve; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorld; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldOf; @@ -128,6 +129,7 @@ public static void register(SkriptRegistration registration) { // WORLD ExprChunkAtLocation.register(registration); + ExprEnvironmentAtLocation.register(registration); ExprRelativePositionResolve.register(registration); ExprWorld.register(registration); ExprWorldDateTime.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprEnvironmentAtLocation.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprEnvironmentAtLocation.java new file mode 100644 index 00000000..fc7d6356 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprEnvironmentAtLocation.java @@ -0,0 +1,96 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.hytale.utils.AssetStoreUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class ExprEnvironmentAtLocation implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprEnvironmentAtLocation.class, Environment.class, true, + "environment at %location%") + .name("Environment at Location") + .description("Get/set the environment at a location.") + .since("INSERT VERSION") + .experimental("Setting does not appear to work.") + .register(); + } + + private Expression location; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.location = (Expression) expressions[0]; + return true; + } + + @Override + public Environment[] getValues(@NotNull TriggerContext ctx) { + Location location = this.location.getSingle(ctx).orElse(null); + if (location == null) return null; + + BlockChunk blockChunk = getBlockChunk(location); + if (blockChunk == null) return null; + + Vector3i position = location.getPosition().toVector3i(); + int id = blockChunk.getEnvironment(position); + return new Environment[]{AssetStoreUtils.getEnvironment(id)}; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) return Optional.of(new Class[]{Environment.class}); + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Environment env)) return; + + Location location = this.location.getSingle(ctx).orElse(null); + if (location == null) return; + + BlockChunk blockChunk = getBlockChunk(location); + if (blockChunk == null) return; + + int index = AssetStoreUtils.getEnvironmentIndex(env); + Vector3i position = location.getPosition().toVector3i(); + + blockChunk.setEnvironment(position.getX(), position.getY(), position.getX(), index); + } + + private BlockChunk getBlockChunk(Location location) { + String worldName = location.getWorld(); + World world = Universe.get().getWorld(worldName); + if (world == null) return null; + + Vector3i position = location.getPosition().toVector3i(); + + WorldChunk chunk = world.getChunk(ChunkUtil.indexChunk(position.getX(), position.getZ())); + if (chunk == null) return null; + + return chunk.getBlockChunk(); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "environment at " + this.location.toString(ctx, debug); + } + +} From 147e7ea6ba4e6a6f4b2d6454d28ab8adf71e298a Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 14 Feb 2026 13:18:34 -0800 Subject: [PATCH 017/106] AssetStoreUtils - add environment methods - I guess I forgot to commit these before and lost them, WHOOPS! --- .../api/hytale/utils/AssetStoreUtils.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/AssetStoreUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/AssetStoreUtils.java index b7db9bac..e54d4632 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/AssetStoreUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/AssetStoreUtils.java @@ -1,6 +1,7 @@ package com.github.skriptdev.skript.api.hytale.utils; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import com.hypixel.hytale.server.core.asset.type.environment.config.Environment; import com.hypixel.hytale.server.core.asset.type.item.config.Item; import com.hypixel.hytale.server.core.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -42,4 +43,24 @@ public class AssetStoreUtils { return null; } + /** + * Get the AssetStore index of an Environment + * + * @param environment Environment to get index from + * @return Index from Environment + */ + public static int getEnvironmentIndex(Environment environment) { + return Environment.getAssetMap().getIndex(environment.getId()); + } + + /** + * Get an Environment from its AssetStore index. + * + * @param index Index to grab Environment from + * @return Environment from index, or null if not found + */ + public static Environment getEnvironment(int index) { + return Environment.getAssetMap().getAsset(index); + } + } From 0a8fce2d919a187b0f06968c9e7aedbc9a62925c Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 14 Feb 2026 23:18:08 -0800 Subject: [PATCH 018/106] Add view radius expressions --- .../expressions/ExpressionHandler.java | 4 ++ .../player/ExprPlayerClientViewRadius.java | 61 +++++++++++++++++ .../server/ExprServerViewRadius.java | 67 +++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerClientViewRadius.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprServerViewRadius.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 72de4936..945c69b2 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -48,10 +48,12 @@ import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprAllPlayers; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprChatMessage; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprGameMode; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerClientViewRadius; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerPermissionGroup; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerPermissions; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerRespawnLocations; import com.github.skriptdev.skript.plugin.elements.expressions.server.ExprConsole; +import com.github.skriptdev.skript.plugin.elements.expressions.server.ExprServerViewRadius; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprChunkAtLocation; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprEnvironmentAtLocation; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprRelativePositionResolve; @@ -120,12 +122,14 @@ public static void register(SkriptRegistration registration) { ExprAllPlayers.register(registration); ExprChatMessage.register(registration); ExprGameMode.register(registration); + ExprPlayerClientViewRadius.register(registration); ExprPlayerPermissionGroup.register(registration); ExprPlayerPermissions.register(registration); ExprPlayerRespawnLocations.register(registration); // SERVER ExprConsole.register(registration); + ExprServerViewRadius.register(registration); // WORLD ExprChunkAtLocation.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerClientViewRadius.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerClientViewRadius.java new file mode 100644 index 00000000..d13ba1ff --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerClientViewRadius.java @@ -0,0 +1,61 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprPlayerClientViewRadius extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprPlayerClientViewRadius.class, Number.class, + "client view radius", "players") + .name("Player Client View Radius") + .description("Get/set the client view radius of players.", + "Do note setting a value higher than the server view radius will not override.") + .examples("set client view radius of player to 5", + "add 1 to client view radius of all players", + "remove 2 from client view radius of {_p}") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(Player player) { + return player.getClientViewRadius(); + } + + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Number number)) { + return; + } + + for (Player player : getOwner().getValues(ctx)) { + int oldValue = player.getClientViewRadius(); + int changeValue = number.intValue(); + int newValue = switch (changeMode) { + case ADD -> oldValue + changeValue; + case REMOVE -> oldValue - changeValue; + default -> changeValue; + }; + player.setClientViewRadius(newValue); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprServerViewRadius.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprServerViewRadius.java new file mode 100644 index 00000000..74ca73de --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/server/ExprServerViewRadius.java @@ -0,0 +1,67 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.server; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.HytaleServerConfig; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class ExprServerViewRadius implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprServerViewRadius.class, Number.class, true, "server view radius") + .name("Server View Radius") + .description("Get/set the max view radius (in chunks) of players on the server.") + .examples("set server view radius to 10", + "add 1 to server view radius", + "remove 3 from server view radius") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + return true; + } + + @Override + public Number[] getValues(@NotNull TriggerContext ctx) { + return new Number[]{HytaleServer.get().getConfig().getMaxViewRadius()}; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Number number)) { + return; + } + HytaleServerConfig config = HytaleServer.get().getConfig(); + int oldValue = config.getMaxViewRadius(); + int change = number.intValue(); + int newValue = switch (changeMode) { + case ADD -> oldValue + change; + case REMOVE -> oldValue - change; + default -> change; + }; + config.setMaxViewRadius(newValue); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "server view radius"; + } + +} From 1f542b61238f0084b984d5116b82361d5a44a893 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 14 Feb 2026 23:32:39 -0800 Subject: [PATCH 019/106] TestRunnerMain - pass thru plugin version --- build.gradle.kts | 2 +- .../skript/api/skript/testing/TestRunnerMain.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 36554cee..eb25e439 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -57,7 +57,7 @@ tasks { dependsOn("jar") group = "application" mainClass.set("com.github.skriptdev.skript.api.skript.testing.TestRunnerMain") - args(hytaleVersion, assetLocation) + args(hytaleVersion, projectVersion, assetLocation) classpath = sourceSets["main"].runtimeClasspath + testRunnerClasspath } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java index 64e1b476..b8b99f9f 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java @@ -23,11 +23,13 @@ public class TestRunnerMain { public static final String RESET = "\u001B[0m"; private static String serverVersion; + private static String pluginVersion; private static String assetPath; static void main(String[] args) { serverVersion = args[0]; - assetPath = args[1]; + pluginVersion = args[1]; + assetPath = args[2]; System.out.println("Downloading Hytale Server..."); downloadHytaleServer(); @@ -81,8 +83,8 @@ private static void movePlugin() { File file = new File("run/testServer/mods"); file.mkdirs(); try { - Files.copy(Path.of("build/libs/HySkript-1.0.0-pre-release-3.jar"), - Path.of("run/testServer/mods/HySkript-1.0.0-pre-release-3.jar"), + Files.copy(Path.of("build/libs/HySkript-" + pluginVersion + ".jar"), + Path.of("run/testServer/mods/HySkript-" + pluginVersion + ".jar"), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException(e); From eb67a49c77f003462714084376b5a4bb01aea89a Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sat, 14 Feb 2026 23:48:07 -0800 Subject: [PATCH 020/106] ExprWorldTPS - add WorldTPS expression --- .../expressions/ExpressionHandler.java | 2 + .../expressions/world/ExprWorldTPS.java | 69 +++++++++++++++++++ .../expressions/world/ExprWorldTPS.sk | 6 ++ 3 files changed, 77 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTPS.java create mode 100644 src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldTPS.sk diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 945c69b2..806d4a0b 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -61,6 +61,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldOf; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldSpawn; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldDateTime; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldTPS; public class ExpressionHandler { @@ -139,6 +140,7 @@ public static void register(SkriptRegistration registration) { ExprWorldDateTime.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); + ExprWorldTPS.register(registration); // TEST ExprCurrentContext.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTPS.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTPS.java new file mode 100644 index 00000000..daf79706 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTPS.java @@ -0,0 +1,69 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprWorldTPS extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprWorldTPS.class, Number.class, + "world tps", "worlds") + .name("World TPS") + .description("Get/set the ticks per second of a world.", + "This is the rate your world will tick at. This value is not persistent and will reset after restart.", + "Default = 30 TPS, min = 1, max = 2048") + .examples("set world tps of world of player to 20", + "add 5 to world tps of {_world}", + "remove 10 from world tps of world of player", + "reset world tps of all worlds") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(World world) { + return world.getTps(); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeMode == ChangeMode.RESET) { + for (World world : getOwner().getArray(ctx)) { + world.setTps(30); + } + return; + } + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Number number)) { + return; + } + + for (World world : getOwner().getArray(ctx)) { + int oldValue = world.getTps(); + int change = number.intValue(); + int newValue = switch (changeMode) { + case ADD -> oldValue + change; + case REMOVE -> oldValue - change; + default -> change; + }; + + world.setTps(Math.clamp(newValue, 1, 2048)); + } + } + +} diff --git a/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldTPS.sk b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldTPS.sk new file mode 100644 index 00000000..dd52a9ba --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/world/ExprWorldTPS.sk @@ -0,0 +1,6 @@ +test "ExprWorldTPS": + assert world tps of event-world = 30 with "The default should be 30" + set world tps of event-world to 10 + assert world tps of event-world = 10 with "The tps should now be changed" + reset world tps of event-world + assert world tps of event-world = 30 with "The default should be 30 after reset" From a285c435be2a46389144817411041c9bc4717798 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 15 Feb 2026 14:54:19 -0800 Subject: [PATCH 021/106] TypesItem - reorganize and fix up ItemContainer serializers --- .../plugin/elements/types/TypesItem.java | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) 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 9ee049c1..c004ca3c 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 @@ -6,11 +6,12 @@ import com.google.gson.JsonElement; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.protocol.InventoryActionType; -import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; +import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer; import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import io.github.syst3ms.skriptparser.types.changers.TypeSerializer; import org.bson.BsonDocument; @@ -23,6 +24,29 @@ public class TypesItem { static void register(SkriptRegistration reg) { // Please keep in alphabetical order + reg.newType(Inventory.class, "inventory", "inventor@y@ies") + .name("Inventory") + .description("Represents an inventory of an entity or block.") + .since("1.0.0") + .toStringFunction(Inventory::toString) + .serializer(new TypeSerializer<>() { + @Override + public JsonElement serialize(@NotNull Gson gson, @NotNull Inventory value) { + BsonDocument encode = Inventory.CODEC.encode(value, new ExtraInfo()); + return gson.fromJson(encode.toJson(), JsonElement.class); + } + + @Override + public Inventory deserialize(@NotNull Gson gson, @NotNull JsonElement element) { + return Inventory.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); + } + }) + .register(); + 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") + .register(); reg.newType(ItemComponent.class, "itemcomponent", "itemComponent@s") .name("Item Component") .description("Represents the component of a dropped item.") @@ -39,9 +63,12 @@ static void register(SkriptRegistration reg) { @Override public JsonElement serialize(@NotNull Gson gson, @NotNull ItemContainer value) { BsonDocument document; - if (value instanceof CombinedItemContainer cic) { - document = CombinedItemContainer.CODEC.encode(cic, new ExtraInfo()).asDocument(); - document.put("type", new BsonString("combined")); + if (value instanceof EmptyItemContainer eic) { + document = CombinedItemContainer.CODEC.encode(eic, new ExtraInfo()).asDocument(); + document.put("type", new BsonString("empty")); + } else if (value instanceof SimpleItemContainer sic) { + document = CombinedItemContainer.CODEC.encode(sic, new ExtraInfo()).asDocument(); + document.put("type", new BsonString("simple")); } else { document = ItemContainer.CODEC.encode(value, new ExtraInfo()).asDocument(); document.put("type", new BsonString("container")); @@ -56,12 +83,13 @@ public ItemContainer deserialize(@NotNull Gson gson, @NotNull JsonElement elemen String type = parse.getString("type").getValue(); if (type == null) return null; - if (type.equals("combined")) { - return CombinedItemContainer.CODEC.decode(parse, new ExtraInfo()); - } else if (type.equals("container")) { - return ItemContainer.CODEC.decode(parse, new ExtraInfo()); - } - return null; + return switch (type) { + // "combined" stays as it was an old thing we used (backwards compatability) + case "container", "combined" -> CombinedItemContainer.CODEC.decode(parse, new ExtraInfo()); + case "empty" -> EmptyItemContainer.CODEC.decode(parse, new ExtraInfo()); + case "simple" -> SimpleItemContainer.CODEC.decode(parse, new ExtraInfo()); + default -> null; + }; } }) .register(); @@ -93,34 +121,6 @@ public ItemStack deserialize(@NotNull Gson gson, @NotNull JsonElement element) { } }) .register(); - reg.newType(Inventory.class, "inventory", "inventor@y@ies") - .name("Inventory") - .description("Represents an inventory of an entity or block.") - .since("1.0.0") - .toStringFunction(Inventory::toString) - .serializer(new TypeSerializer<>() { - @Override - public JsonElement serialize(@NotNull Gson gson, @NotNull Inventory value) { - BsonDocument encode = Inventory.CODEC.encode(value, new ExtraInfo()); - return gson.fromJson(encode.toJson(), JsonElement.class); - } - - @Override - public Inventory deserialize(@NotNull Gson gson, @NotNull JsonElement element) { - return Inventory.CODEC.decode(BsonDocument.parse(element.toString()), new ExtraInfo()); - } - }) - .register(); - 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") - .register(); - reg.newEnumType(Page.class, "page", "page@s") - .name("Page") - .description("Represents a page type of an inventory.") - .since("INSERT VERSION") - .register(); } } From 8ec2a360dddbfe4d5a6ee9e6b178c3d2041087e3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 15 Feb 2026 16:31:47 -0800 Subject: [PATCH 022/106] Add current TPS/MSPT expressions --- .../expressions/ExpressionHandler.java | 4 ++ .../world/ExprWorldCurrentMSPT.java | 70 +++++++++++++++++++ .../world/ExprWorldCurrentTPS.java | 68 ++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentMSPT.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentTPS.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 806d4a0b..9e7f2cfb 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -58,6 +58,8 @@ import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprEnvironmentAtLocation; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprRelativePositionResolve; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorld; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldCurrentMSPT; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldCurrentTPS; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldOf; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldSpawn; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldDateTime; @@ -137,6 +139,8 @@ public static void register(SkriptRegistration registration) { ExprEnvironmentAtLocation.register(registration); ExprRelativePositionResolve.register(registration); ExprWorld.register(registration); + ExprWorldCurrentMSPT.register(registration); + ExprWorldCurrentTPS.register(registration); ExprWorldDateTime.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentMSPT.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentMSPT.java new file mode 100644 index 00000000..b805cb68 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentMSPT.java @@ -0,0 +1,70 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.metrics.metric.HistoricMetric; +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.TimeUnit; + +public class ExprWorldCurrentMSPT implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprWorldCurrentMSPT.class, Number.class, true, + "current mspt of %world%", + "current mspt of %world% for the last (10|ten) seconds", + "current mspt of %world% for the last [(1|one) ]minute", + "current mspt of %world% for the last (5|five) minutes") + .name("World Current MSPT") + .description("Represents the active MSPT (milliseconds per tick) of a world.", + "This would be how long it's taking each tick to process.") + .examples("if current mspt of world of player > 20:") + .since("INSERT VERSION") + .register(); + } + + private static final double NANOS_PER_MILLI = TimeUnit.MILLISECONDS.toNanos(1); + private int pattern; + private Expression worlds; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + this.worlds = (Expression) expressions[0]; + return true; + } + + @Override + public Number[] getValues(@NotNull TriggerContext ctx) { + int pattern = this.pattern > 0 ? this.pattern - 1 : this.pattern; + World world = this.worlds.getSingle(ctx).orElse(null); + if (world == null) return null; + + double total = 0; + + HistoricMetric metrics = world.getBufferedTickLengthMetricSet(); + double avgNanos = metrics.getAverage(pattern); + + if (avgNanos > 0) { + total = avgNanos / NANOS_PER_MILLI; + } + + return new Number[]{total}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = switch (this.pattern) { + case 1 -> " for the last 10 seconds"; + case 2 -> " for the last 1 minute"; + case 3 -> " for the last 5 minutes"; + default -> ""; + }; + return "current mspt of " + this.worlds.toString(ctx, debug) + type; + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentTPS.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentTPS.java new file mode 100644 index 00000000..5c039dfe --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldCurrentTPS.java @@ -0,0 +1,68 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.metrics.metric.HistoricMetric; +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class ExprWorldCurrentTPS implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprWorldCurrentTPS.class, Number.class, true, + "current tps of %world%", + "current tps of %world% for the last (10|ten) seconds", + "current tps of %world% for the last [(1|one) ]minute", + "current tps of %world% for the last (5|five) minutes") + .name("World Current TPS") + .description("Represents the active TPS of a world (how well the world is ticking).") + .examples("if current tps of world of player < 20:") + .since("INSERT VERSION") + .register(); + } + + private final int[] timeStates = new int[]{10, 60, (60 * 5)}; + private int pattern; + private Expression worlds; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + this.worlds = (Expression) expressions[0]; + return true; + } + + @Override + public Number[] getValues(@NotNull TriggerContext ctx) { + int pattern = this.pattern > 0 ? this.pattern - 1 : this.pattern; + World world = this.worlds.getSingle(ctx).orElse(null); + if (world == null) return null; + + HistoricMetric metrics = world.getBufferedTickLengthMetricSet(); + double length = metrics.getTimestamps(pattern).length; + + double tps; + if (length == 0) { + tps = world.getTps(); + } else { + tps = length / this.timeStates[pattern]; + } + return new Number[]{tps}; + } + + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = switch (this.pattern) { + case 1 -> " for the last 10 seconds"; + case 2 -> " for the last 1 minute"; + case 3 -> " for the last 5 minutes"; + default -> ""; + }; + return "current tps of " + this.worlds.toString(ctx, debug) + type; + } + +} From 2cae5e43a1088aebdc3a71f87e616e1695ad6c0c Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Sun, 15 Feb 2026 22:04:46 -0800 Subject: [PATCH 023/106] ExprBlockTypeOfBlock - note for later --- .../expressions/block/ExprBlockTypeOfBlock.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java index 04d5d615..0192eb77 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprBlockTypeOfBlock.java @@ -15,6 +15,22 @@ public class ExprBlockTypeOfBlock implements Expression { + // Finally found this... handle it later TODO + // com.hypixel.hytale.server.core.universe.world.SetBlockSettings + // public static final int NONE = 0; + // public static final int NO_NOTIFY = 1; + // public static final int NO_UPDATE_STATE = 2; + // public static final int NO_SEND_PARTICLES = 4; + // public static final int NO_SET_FILLER = 8; + // public static final int NO_BREAK_FILLER = 16; + // public static final int PHYSICS = 32; + // public static final int FORCE_CHANGED = 64; + // public static final int NO_UPDATE_NEIGHBOR_CONNECTIONS = 128; + // public static final int PERFORM_BLOCK_UPDATE = 256; + // public static final int NO_UPDATE_HEIGHTMAP = 512; + // public static final int NO_SEND_AUDIO = 1024; + // public static final int NO_DROP_ITEMS = 2048; + public static void register(SkriptRegistration reg) { reg.newExpression(ExprBlockTypeOfBlock.class, BlockType.class, false, "block[ ]type of %blocks% [with settings %number%]") From 7344e2b5cd7da83ee081571a03bf5e203e689da3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 05:45:45 -0800 Subject: [PATCH 024/106] EvtPlayerSwitchActiveSlot - add event --- .../plugin/elements/events/EventHandler.java | 2 + .../player/EvtPlayerSwitchActiveSlot.java | 158 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSwitchActiveSlot.java 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 c5dce8a2..1cbc3469 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 @@ -28,6 +28,7 @@ import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerPlaceBlock; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerSetupConnect; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerSetupDisconnect; +import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerSwitchActiveSlot; import com.github.skriptdev.skript.plugin.elements.events.player.EvtPlayerUseBlock; import com.github.skriptdev.skript.plugin.elements.events.server.EvtBoot; import com.github.skriptdev.skript.plugin.elements.events.server.EvtShutdown; @@ -64,6 +65,7 @@ public static void register(SkriptRegistration registration) { EvtPlayerPlaceBlock.register(registration); EvtPlayerSetupConnect.register(registration); EvtPlayerSetupDisconnect.register(registration); + EvtPlayerSwitchActiveSlot.register(registration); EvtPlayerUseBlock.register(registration); // SERVER diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSwitchActiveSlot.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSwitchActiveSlot.java new file mode 100644 index 00000000..787815aa --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/player/EvtPlayerSwitchActiveSlot.java @@ -0,0 +1,158 @@ +package com.github.skriptdev.skript.plugin.elements.events.player; + +import com.github.skriptdev.skript.api.skript.event.CancellableContext; +import com.github.skriptdev.skript.api.skript.event.PlayerContext; +import com.github.skriptdev.skript.api.skript.event.SystemEvent; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.ArchetypeChunk; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.component.query.Query; +import com.hypixel.hytale.component.system.EntityEventSystem; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.event.events.ecs.SwitchActiveSlotEvent; +import com.hypixel.hytale.server.core.inventory.container.ItemContainer; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.TriggerMap; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.registration.context.ContextValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class EvtPlayerSwitchActiveSlot extends SystemEvent> { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtPlayerSwitchActiveSlot.class, + "player switch active slot", + "player switch active hot[ ]bar slot", + "player switch active utility slot") + .setHandledContexts(SwitchSlotContext.class) + .name("Player Switch Active Slot") + .description("Called when a player switches their active slot.") + .experimental("This event doesn't appear to actually get called by the server for hotbar.") + .examples("") + .since("INSERT VERSION") + .register(); + + reg.newSingleContextValue(SwitchSlotContext.class, Number.class, + "slot-index", SwitchSlotContext::getPreviousSlot) + .description("The slot number before the change happened.") + .setState(ContextValue.State.PAST) + .register(); + + reg.newSingleContextValue(SwitchSlotContext.class, Number.class, + "slot-index", SwitchSlotContext::getNewSlot) + .description("The slot number after the change happened. Can be set.") + .setState(ContextValue.State.PRESENT) + .addSetter(SwitchSlotContext::setNewSlot) + .register(); + + reg.newSingleContextValue(SwitchSlotContext.class, ItemContainer.class, + "item-container", SwitchSlotContext::getContainer) + .description("The ItemContainer used in this event.") + .register(); + } + + private static SwitchSlotSystem SYSTEM; + private int pattern; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, ParseContext parseContext) { + if (SYSTEM == null) { + SYSTEM = new SwitchSlotSystem(); + applySystem(SYSTEM); + } + this.pattern = matchedPattern; + return true; + } + + @Override + public boolean check(TriggerContext ctx) { + if (ctx instanceof SwitchSlotContext ssc) { + if (this.pattern == 0) return true; + else if (this.pattern == 1) return ssc.isHotbar(); + else if (this.pattern == 2) return ssc.isUtility(); + } + return false; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "player switch active slot"; + } + + public record SwitchSlotContext(SwitchActiveSlotEvent event, Player player) + implements PlayerContext, CancellableContext { + + @Override + public Player getPlayer() { + return this.player; + } + + public int getPreviousSlot() { + return this.event.getPreviousSlot(); + } + + public int getNewSlot() { + return this.event.getNewSlot(); + } + + public void setNewSlot(Number number) { + this.event.setNewSlot(number.byteValue()); + } + + public ItemContainer getContainer() { + return this.player.getInventory().getSectionById(this.event.getInventorySectionId()); + } + + public boolean isHotbar() { + return this.event.getInventorySectionId() == -1; + } + + public boolean isUtility() { + return this.event.getInventorySectionId() == -5; + } + + @Override + public boolean isCancelled() { + return this.event.isCancelled(); + } + + @Override + public void setCancelled(boolean cancelled) { + this.event.setCancelled(cancelled); + } + + @Override + public String getName() { + return "switch active slot context"; + } + } + + public static class SwitchSlotSystem extends EntityEventSystem { + + protected SwitchSlotSystem() { + super(SwitchActiveSlotEvent.class); + } + + @Override + public void handle(int i, @NotNull ArchetypeChunk archetypeChunk, + @NotNull Store store, @NotNull CommandBuffer commandBuffer, + @NotNull SwitchActiveSlotEvent event) { + + Ref ref = archetypeChunk.getReferenceTo(i); + Player player = commandBuffer.getComponent(ref, Player.getComponentType()); + SwitchSlotContext context = new SwitchSlotContext(event, player); + TriggerMap.callTriggersByContext(context); + } + + @Override + public @Nullable Query getQuery() { + return Player.getComponentType(); + } + } + +} From 2704225826e8f98ccd0c8d8a474896e5b10b1b42 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 06:22:02 -0800 Subject: [PATCH 025/106] TypesItem - add Page type (you went missing somehow) --- .../skriptdev/skript/plugin/elements/types/TypesItem.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 c004ca3c..4dc54e7d 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 @@ -6,6 +6,7 @@ import com.google.gson.JsonElement; import com.hypixel.hytale.codec.ExtraInfo; import com.hypixel.hytale.protocol.InventoryActionType; +import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.server.core.inventory.Inventory; import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer; @@ -121,6 +122,11 @@ public ItemStack deserialize(@NotNull Gson gson, @NotNull JsonElement element) { } }) .register(); + reg.newEnumType(Page.class, "page", "page@s") + .name("Page") + .description("Represents a page type of an inventory window.") + .since("INSERT VERSION") + .register(); } } From aa737164d91d5a58b6db5ae6d0f8f58ca74a3b36 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 06:28:05 -0800 Subject: [PATCH 026/106] ElementRegistration - fix structure count --- .../plugin/elements/ElementRegistration.java | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) 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 8fb341be..622cb817 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 @@ -6,18 +6,16 @@ import com.github.skriptdev.skript.api.utils.Utils; import com.github.skriptdev.skript.plugin.elements.command.ScriptCommand; import com.github.skriptdev.skript.plugin.elements.command.ScriptSubCommand; -import com.github.skriptdev.skript.plugin.elements.types.DefaultComparators; -import com.github.skriptdev.skript.plugin.elements.types.DefaultConverters; import com.github.skriptdev.skript.plugin.elements.conditions.ConditionHandler; import com.github.skriptdev.skript.plugin.elements.effects.EffectHandler; import com.github.skriptdev.skript.plugin.elements.events.EventHandler; import com.github.skriptdev.skript.plugin.elements.expressions.ExpressionHandler; import com.github.skriptdev.skript.plugin.elements.functions.DefaultFunctions; import com.github.skriptdev.skript.plugin.elements.sections.SectionHandler; +import com.github.skriptdev.skript.plugin.elements.types.DefaultComparators; +import com.github.skriptdev.skript.plugin.elements.types.DefaultConverters; import com.github.skriptdev.skript.plugin.elements.types.Types; import io.github.syst3ms.skriptparser.Parser; -import io.github.syst3ms.skriptparser.lang.Structure; -import io.github.syst3ms.skriptparser.registration.SkriptEventInfo; import io.github.syst3ms.skriptparser.structures.functions.Functions; public class ElementRegistration { @@ -77,22 +75,8 @@ public void registerElements() { private void printSyntaxCount() { var mainRegistration = Parser.getMainRegistration(); - int structureSize = 0; - int eventSize = 0; - for (SkriptEventInfo event : this.registration.getEvents()) { - if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { - structureSize++; - } else { - eventSize++; - } - } - for (SkriptEventInfo event : mainRegistration.getEvents()) { - if (Structure.class.isAssignableFrom(event.getSyntaxClass())) { - structureSize++; - } else { - eventSize++; - } - } + int structureSize = this.registration.getStructures().size() + mainRegistration.getStructures().size(); + int eventSize = this.registration.getEvents().size() + mainRegistration.getEvents().size(); int effectSize = this.registration.getEffects().size() + mainRegistration.getEffects().size(); int expsSize = this.registration.getExpressions().size() + mainRegistration.getExpressions().size(); int secSize = this.registration.getSections().size() + mainRegistration.getSections().size(); From aa6fccd71ec07cb253fc298d33427234cee71a40 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 08:00:54 -0800 Subject: [PATCH 027/106] HySkriptAddon - add some quick link methods to Hytale plugin stuff --- .../api/skript/addon/HySkriptAddon.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) 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 fad80fae..bfc1d94a 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 @@ -1,10 +1,17 @@ package com.github.skriptdev.skript.api.skript.addon; import com.github.skriptdev.skript.api.utils.Utils; +import com.github.skriptdev.skript.plugin.HySk; +import com.hypixel.hytale.component.ComponentRegistryProxy; +import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.command.system.CommandRegistry; +import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import fi.sulku.hytale.TinyMsg; import io.github.syst3ms.skriptparser.registration.SkriptAddon; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -13,14 +20,17 @@ /** * Base class for addons for HySkript. */ +@SuppressWarnings("unused") public abstract class HySkriptAddon extends SkriptAddon { + private final HySk hySkInstance; private Manifest manifest; private final HytaleLogger hytaleLogger; public HySkriptAddon(String name) { super(name); this.hytaleLogger = Utils.getAddonLogger(name); + this.hySkInstance = HySk.getInstance(); } /** @@ -35,18 +45,79 @@ public HySkriptAddon(String name) { */ abstract public void shutdown(); + /** + * Get the {@link CommandRegistry Hytale CommandRegistry}. + * + * @return Hytale CommandRegistry + */ + public final CommandRegistry getHytaleCommandRegistry() { + return this.hySkInstance.getCommandRegistry(); + } + + /** + * Get the {@link ComponentRegistryProxy Hytale EntityStoreRegistry}. + * + * @return Hytale EntityStoreRegistry + */ + public final ComponentRegistryProxy getHytaleEntityStoreRegistry() { + return this.hySkInstance.getEntityStoreRegistry(); + } + + /** + * Get the {@link ComponentRegistryProxy Hytale ChunkStoreRegistry}. + * + * @return Hytale ChunkStoreRegistry + */ + public final ComponentRegistryProxy getHytaleChunkStoreRegistry() { + return this.hySkInstance.getChunkStoreRegistry(); + } + + /** + * Get the {@link EventRegistry Hytale EventRegistry}. + * + * @return Hytale EventRegistry + */ + public final EventRegistry getHytaleEventRegistry() { + return this.hySkInstance.getEventRegistry(); + } + + /** + * Get your addon instance of the {@link HytaleLogger}. + *
You can override this if you wish to create your own logger instance + * + * @return Addon instance of HytaleLogger + */ public HytaleLogger getHytaleLogger() { return this.hytaleLogger; } + /** + * Get the instance of HySkript. + * + * @return Instance of HySkript + */ + public final HySk getHySkriptInstance() { + return this.hySkInstance; + } + + @ApiStatus.Internal final void setManifest(Manifest manifest) { this.manifest = manifest; } + /** + * Get the addon Manifest + * + * @return Addon Manifest + */ public final Manifest getManifest() { return manifest; } + /** + * @hidden + */ + @ApiStatus.Internal public final Message[] getInfo() { List info = new ArrayList<>(); info.add(TinyMsg.parse("Version: " + this.manifest.getVersion())); From c46c9062e3abe03c52fe9a11e77c03589925ddab Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 08:19:40 -0800 Subject: [PATCH 028/106] More JavaDocs --- build.gradle.kts | 3 +- .../skript/api/hytale/utils/StoreUtils.java | 3 ++ .../skript/api/hytale/utils/package-info.java | 4 +++ .../skript/api/skript/addon/Manifest.java | 30 +++++++++++++++++++ .../skript/api/skript/addon/package-info.java | 4 +++ .../api/skript/command/package-info.java | 4 +++ .../api/skript/config/SkriptConfig.java | 2 +- .../api/skript/config/package-info.java | 4 +++ .../skript/api/skript/docs/package-info.java | 4 +++ .../skript/api/skript/event/WorldContext.java | 3 ++ .../skript/api/skript/event/package-info.java | 5 ++++ .../api/skript/registration/package-info.java | 4 +++ .../api/skript/testing/TestProperties.java | 3 ++ .../api/skript/testing/TestResults.java | 3 ++ .../skript/api/skript/testing/TestRunner.java | 3 ++ .../api/skript/testing/TestRunnerMain.java | 16 +++++++--- .../api/skript/testing/package-info.java | 4 +++ .../api/skript/variables/package-info.java | 4 +++ .../skript/api/utils/ReflectionUtils.java | 2 +- 19 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/github/skriptdev/skript/api/hytale/utils/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/addon/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/command/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/config/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/docs/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/event/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/registration/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/testing/package-info.java create mode 100644 src/main/java/com/github/skriptdev/skript/api/skript/variables/package-info.java diff --git a/build.gradle.kts b/build.gradle.kts index 06461f40..70612d25 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,7 +90,8 @@ tasks { options.encoding = Charsets.UTF_8.name() exclude( "com/github/skriptdev/skript/plugin/elements", - "com/github/skriptdev/skript/plugin/command" + "com/github/skriptdev/skript/plugin/command", + "com/github/skriptdev/skript/api/skript/testing/elements" ) (options as StandardJavadocDocletOptions).links( "https://javadoc.io/doc/org.jetbrains/annotations/latest/", diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/StoreUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/StoreUtils.java index d3a3a66d..cdeaba18 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/StoreUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/StoreUtils.java @@ -6,6 +6,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +/** + * Utilities for a {@link Store Hytale Store} + */ public class StoreUtils { @SuppressWarnings("unchecked") diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/package-info.java b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/package-info.java new file mode 100644 index 00000000..7695cdd8 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/package-info.java @@ -0,0 +1,4 @@ +/** + * Utility classes for Hytale. + */ +package com.github.skriptdev.skript.api.hytale.utils; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java b/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java index ec09a061..7ccd54a6 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/addon/Manifest.java @@ -3,9 +3,13 @@ import com.hypixel.hytale.codec.Codec; import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.builder.BuilderCodec; +import org.jetbrains.annotations.ApiStatus; import java.util.Arrays; +/** + * Represents a manifest of information about a {@link HySkriptAddon} + */ public class Manifest { private static final BuilderCodec.Builder BUILDER = BuilderCodec.builder(Manifest.class, Manifest::new); @@ -36,26 +40,52 @@ public class Manifest { private String[] authors; private String website; + @ApiStatus.Internal public String getMainClass() { return this.mainClass; } + /** + * Get the name of this addon. + * + * @return Name of addon + */ public String getName() { return this.name; } + /** + * Get the version of this addon. + * + * @return Version of addon + */ public String getVersion() { return this.version; } + /** + * Get the description of this addon. + * + * @return Description of addon + */ public String getDescription() { return this.description; } + /** + * Get the authors of this addon. + * + * @return Authors of addon + */ public String[] getAuthors() { return this.authors; } + /** + * Get the website of this addon. + * + * @return Website of addon + */ public String getWebsite() { return this.website; } diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/addon/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/addon/package-info.java new file mode 100644 index 00000000..eaac920a --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/addon/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package related to HySkript Addons + */ +package com.github.skriptdev.skript.api.skript.addon; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/command/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/command/package-info.java new file mode 100644 index 00000000..ac559776 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/command/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package regarding commands in HySkript. + */ +package com.github.skriptdev.skript.api.skript.command; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java b/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java index 54f788d0..ff27bd14 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/config/SkriptConfig.java @@ -27,7 +27,7 @@ import java.util.stream.Collectors; /** - * Config for Skript + * Config for HySkript */ public class SkriptConfig { diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/config/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/config/package-info.java new file mode 100644 index 00000000..7cdace87 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/config/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package for HySkript config. + */ +package com.github.skriptdev.skript.api.skript.config; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/docs/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/docs/package-info.java new file mode 100644 index 00000000..9d898ae4 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/docs/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package regarding {@link io.github.syst3ms.skriptparser.docs.Documentation} + */ +package com.github.skriptdev.skript.api.skript.docs; 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 index 13437904..9470d2b2 100644 --- 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 @@ -3,6 +3,9 @@ import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.lang.TriggerContext; +/** + * Represents a {@link TriggerContext} which includes a {@link World} + */ public interface WorldContext extends TriggerContext { World getWorld(); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/event/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/event/package-info.java new file mode 100644 index 00000000..2b4cf200 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/event/package-info.java @@ -0,0 +1,5 @@ +/** + * Main package regarding {@link io.github.syst3ms.skriptparser.lang.TriggerContext Contexts} in + * {@link io.github.syst3ms.skriptparser.lang.event.SkriptEvent Events} + */ +package com.github.skriptdev.skript.api.skript.event; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/registration/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/registration/package-info.java new file mode 100644 index 00000000..d446b1dd --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/registration/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package for registrations. + */ +package com.github.skriptdev.skript.api.skript.registration; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java index 34d4b6ab..4b9eacf2 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestProperties.java @@ -1,5 +1,8 @@ package com.github.skriptdev.skript.api.skript.testing; +/** + * Properties related to tests. + */ public class TestProperties { public static final boolean ENABLED = Boolean.parseBoolean(System.getProperty("skript.test.enabled", "false")); diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java index d44c0bb7..6389632d 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestResults.java @@ -15,6 +15,9 @@ import java.util.Map; import java.util.TreeMap; +/** + * Handles the results of tests. + */ public class TestResults { private boolean success = true; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java index 47413325..b4665b9c 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunner.java @@ -26,6 +26,9 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +/** + * Runner for HySkript tests. + */ public class TestRunner { private static final ScheduledExecutorService SCHEDULED = HytaleServer.SCHEDULED_EXECUTOR; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java index b8b99f9f..128381fb 100644 --- a/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/TestRunnerMain.java @@ -15,17 +15,25 @@ import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +/** + * Main runner for {@link TestRunner}. + */ public class TestRunnerMain { - public static final String GREEN = "\u001B[92m"; - public static final String LIGHT_GREY = "\u001B[37m"; - public static final String RED = "\u001B[91m"; - public static final String RESET = "\u001B[0m"; + private static final String GREEN = "\u001B[92m"; + private static final String LIGHT_GREY = "\u001B[37m"; + private static final String RED = "\u001B[91m"; + private static final String RESET = "\u001B[0m"; private static String serverVersion; private static String pluginVersion; private static String assetPath; + /** + * Run the {@link TestRunner} + * + * @param args serverVersion, pluginVersion, assetPath + */ static void main(String[] args) { serverVersion = args[0]; pluginVersion = args[1]; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/testing/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/testing/package-info.java new file mode 100644 index 00000000..2d0b9797 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/testing/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package for testing + */ +package com.github.skriptdev.skript.api.skript.testing; diff --git a/src/main/java/com/github/skriptdev/skript/api/skript/variables/package-info.java b/src/main/java/com/github/skriptdev/skript/api/skript/variables/package-info.java new file mode 100644 index 00000000..1aba3c3b --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/api/skript/variables/package-info.java @@ -0,0 +1,4 @@ +/** + * Main package for handling variables. + */ +package com.github.skriptdev.skript.api.skript.variables; diff --git a/src/main/java/com/github/skriptdev/skript/api/utils/ReflectionUtils.java b/src/main/java/com/github/skriptdev/skript/api/utils/ReflectionUtils.java index f3ccde70..20cae7be 100644 --- a/src/main/java/com/github/skriptdev/skript/api/utils/ReflectionUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/utils/ReflectionUtils.java @@ -29,7 +29,7 @@ public static void init() { /** * Get access to the HytaleBanProvider. - * This is currently private with no getter. + *
This is currently private with no getter. * * @return HytaleBanProvider */ From 61636b84668107cedd173641ba8380b4ac385d81 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 08:50:25 -0800 Subject: [PATCH 029/106] ExprWorldTimeDurations - add time durations --- .../expressions/ExpressionHandler.java | 2 + .../world/ExprWorldTimeDurations.java | 46 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTimeDurations.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 9e7f2cfb..c0d27f3f 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -64,6 +64,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldSpawn; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldDateTime; import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldTPS; +import com.github.skriptdev.skript.plugin.elements.expressions.world.ExprWorldTimeDurations; public class ExpressionHandler { @@ -144,6 +145,7 @@ public static void register(SkriptRegistration registration) { ExprWorldDateTime.register(registration); ExprWorldOf.register(registration); ExprWorldSpawn.register(registration); + ExprWorldTimeDurations.register(registration); ExprWorldTPS.register(registration); // TEST diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTimeDurations.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTimeDurations.java new file mode 100644 index 00000000..9a3f5de9 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/world/ExprWorldTimeDurations.java @@ -0,0 +1,46 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.world; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.universe.world.World; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; + +public class ExprWorldTimeDurations extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprWorldTimeDurations.class, Duration.class, + "world (daytime|1:nighttime|2:total[ ]time) duration", "worlds") + .name("World Time Durations") + .description("Represents the daytime/nighttime durations of a world.", + "These values are from a GamePlayConfig but can be overridden in your World config.", + "**Note**: Currently these cannot be set via code.") + .examples("set {_Day} to world daytime duration of world of player") + .since("INSERT VERSION") + .register(); + } + + private int pattern; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { + this.pattern = parseContext.getNumericMark(); + return super.init(expressions, matchedPattern, parseContext); + } + + @Override + public @Nullable Duration getProperty(@NotNull World world) { + int seconds = switch (this.pattern) { + case 0 -> world.getDaytimeDurationSeconds(); + case 1 -> world.getNighttimeDurationSeconds(); + case 2 -> world.getDaytimeDurationSeconds() + world.getNighttimeDurationSeconds(); + default -> 0; + }; + return Duration.ofSeconds(seconds); + } + +} From 6661c56527cc851697903722ff829fb4ce5cb7b5 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 09:27:26 -0800 Subject: [PATCH 030/106] ExprLocationOffset - add expression to offset a location --- .../expressions/ExpressionHandler.java | 2 + .../expressions/other/ExprLocationOffset.java | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOffset.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index c0d27f3f..bee75bf6 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -35,6 +35,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprDistance; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationDirection; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationOf; +import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationOffset; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprMessage; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprMessageColor; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprMessageLink; @@ -111,6 +112,7 @@ public static void register(SkriptRegistration registration) { ExprDistance.register(registration); ExprLocationDirection.register(registration); ExprLocationOf.register(registration); + ExprLocationOffset.register(registration); ExprMessage.register(registration); ExprMessageColor.register(registration); ExprMessageLink.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOffset.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOffset.java new file mode 100644 index 00000000..f7435d73 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationOffset.java @@ -0,0 +1,66 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.other; + +import com.github.skriptdev.skript.api.hytale.utils.LocationUtils; +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.Vector3i; +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 ExprLocationOffset implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprLocationOffset.class, Location.class, true, + "%location% offset by %vector3i/vector3d%", + "%location% ~ %vector3i/vector3d%") + .name("Location Offset") + .description("Returns a location offset by a vector.") + .examples("teleport player to (location of player offset by vector3i(0, 10, 0) # Teleports them 10 blocks up", + "set {_loc} to {_loc} ~ vector3d(0, -1.5, 0)") + .since("INSERT VERSION") + .register(); + } + + private Expression location; + private Expression vector; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.location = (Expression) expressions[0]; + this.vector = expressions[1]; + return true; + } + + @Override + public Location[] getValues(@NotNull TriggerContext ctx) { + + Location loc = this.location.getSingle(ctx).orElse(null); + if (loc == null) return null; + + Vector3d offset; + Object o = this.vector.getSingle(ctx).orElse(null); + if (o instanceof Vector3i vector3i) { + offset = vector3i.toVector3d(); + } else if (o instanceof Vector3d vector3d) { + offset = vector3d; + } else { + return null; + } + + Location location = LocationUtils.clone(loc); + location.getPosition().add(offset); + + return new Location[]{location}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return this.location.toString(ctx, debug) + " offset by " + + this.vector.toString(ctx, debug); + } + +} From 6cdc8b20daf69868783a5e95e88bf802ea021eac Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 09:30:35 -0800 Subject: [PATCH 031/106] ExprLocationOffset.sk - add test --- .../hyskript/expressions/other/ExprLocationOffset.sk | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOffset.sk diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOffset.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOffset.sk new file mode 100644 index 00000000..a507fdb5 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprLocationOffset.sk @@ -0,0 +1,5 @@ +test "ExprLocationOffset": + set {_loc} to location(0, 0, 0, event-world) + set {_newLoc} to {_loc} offset by vector3i(0, 10, 0) + set {_shouldBe} to location(0, 10, 0, event-world) + assert {_newLoc} = {_shouldBe} with "the locations should match after offsetting" From dfe2e563b8c14efd56dd711a34606aa87404157c Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 10:06:48 -0800 Subject: [PATCH 032/106] ExprCoordinates - add coord expr --- .../expressions/ExpressionHandler.java | 2 + .../expressions/other/ExprCoordinates.java | 174 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCoordinates.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index bee75bf6..5cd3655d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -31,6 +31,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemsInInventory; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCast; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprClassInfoOf; +import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCoordinates; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCurrentContext; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprDistance; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationDirection; @@ -109,6 +110,7 @@ public static void register(SkriptRegistration registration) { // OTHER ExprCast.register(registration); ExprClassInfoOf.register(registration); + ExprCoordinates.register(registration); ExprDistance.register(registration); ExprLocationDirection.register(registration); ExprLocationOf.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCoordinates.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCoordinates.java new file mode 100644 index 00000000..df2ada3a --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprCoordinates.java @@ -0,0 +1,174 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.other; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.math.vector.Vector3i; +import 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.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprCoordinates extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprCoordinates.class, Number.class, + "(0:x|1:y|2:z) coord[inate]", "locations/vector3is/vector3ds/vector3fs") + .name("Location/Vector Coordinates") + .description("Get the coordinates of a location/vector") + .examples("set {_y} to y coord of location of player", + "add 10 to y coord of {_loc}", + "subtract 15 from z coord of {_loc}", + "set x coord of {_vec} to 22") + .since("INSERT VERSION") + .register(); + } + + private int pattern; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { + this.pattern = parseContext.getNumericMark(); + return super.init(expressions, matchedPattern, parseContext); + } + + @Override + public @Nullable Number getProperty(@NotNull Object owner) { + switch (owner) { + case Location location -> { + Vector3d position = location.getPosition(); + return switch (this.pattern) { + case 1 -> position.getY(); + case 2 -> position.getZ(); + default -> position.getX(); + }; + } + case Vector3i position -> { + return switch (this.pattern) { + case 1 -> position.getY(); + case 2 -> position.getZ(); + default -> position.getX(); + }; + } + case Vector3d position -> { + return switch (this.pattern) { + case 1 -> position.getY(); + case 2 -> position.getZ(); + default -> position.getX(); + }; + } + case Vector3f position -> { + return switch (this.pattern) { + case 1 -> position.getY(); + case 2 -> position.getZ(); + default -> position.getX(); + }; + } + default -> { + } + } + return null; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Number number) || number == null) { + return; + } + + for (Object o : getOwner().getArray(ctx)) { + if (o instanceof Location location) { + Vector3d pos = location.getPosition(); + changeVec3d(pos, changeMode, number); + } else if (o instanceof Vector3d pos) { + changeVec3d(pos, changeMode, number); + } else if (o instanceof Vector3i pos) { + changeVec3i(pos, changeMode, number); + } else if (o instanceof Vector3f pos) { + changeVec3f(pos, changeMode, number); + } + } + } + + private void changeVec3d(Vector3d pos, ChangeMode mode, Number number) { + double oldValue = switch (this.pattern) { + case 1 -> pos.getY(); + case 2 -> pos.getZ(); + default -> pos.getX(); + }; + + double changeValue = number.doubleValue(); + double newValue = changeValue; + if (mode == ChangeMode.ADD) { + newValue = oldValue + changeValue; + } else if (mode == ChangeMode.REMOVE) { + newValue = oldValue - changeValue; + } + + switch (this.pattern) { + case 1 -> pos.setY(newValue); + case 2 -> pos.setZ(newValue); + default -> pos.setX(newValue); + } + } + + private void changeVec3f(Vector3f pos, ChangeMode mode, Number number) { + float oldValue = switch (this.pattern) { + case 1 -> pos.getY(); + case 2 -> pos.getZ(); + default -> pos.getX(); + }; + + float changeValue = number.floatValue(); + float newValue = changeValue; + if (mode == ChangeMode.ADD) { + newValue = oldValue + changeValue; + } else if (mode == ChangeMode.REMOVE) { + newValue = oldValue - changeValue; + } + + switch (this.pattern) { + case 1 -> pos.setY(newValue); + case 2 -> pos.setZ(newValue); + default -> pos.setX(newValue); + } + } + + private void changeVec3i(Vector3i pos, ChangeMode mode, Number number) { + int oldValue = switch (this.pattern) { + case 1 -> pos.getY(); + case 2 -> pos.getZ(); + default -> pos.getX(); + }; + + int changeValue = number.intValue(); + int newValue = changeValue; + if (mode == ChangeMode.ADD) { + newValue = oldValue + changeValue; + } else if (mode == ChangeMode.REMOVE) { + newValue = oldValue - changeValue; + } + + switch (this.pattern) { + case 1 -> pos.setY(newValue); + case 2 -> pos.setZ(newValue); + default -> pos.setX(newValue); + } + } + +} From 767c0aa2558ca5660901bccdb4f111f5de8fe6d7 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 10:19:04 -0800 Subject: [PATCH 033/106] ExprCoordinates.sk - add test --- .../hyskript/expressions/other/ExprCoordinates.sk | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/test/skript/tests/elements/hyskript/expressions/other/ExprCoordinates.sk diff --git a/src/test/skript/tests/elements/hyskript/expressions/other/ExprCoordinates.sk b/src/test/skript/tests/elements/hyskript/expressions/other/ExprCoordinates.sk new file mode 100644 index 00000000..f26132c2 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/expressions/other/ExprCoordinates.sk @@ -0,0 +1,12 @@ +test "ExprCoordinates": + set {_loc} to location(0, 0, 0, event-world) + set x coord of {_loc} to 10 + assert x coord of {_loc} = 10 with "The x coord should now be 10" + assert {_loc} = location(10, 0, 0, event-world) with "The locations should match after X change" + + add 5 to y coord of {_loc} + assert {_loc} = location(10, 5, 0, event-world) with "The locations should match after Y change" + + set {_vec} to vector3i(0, 0, 0) + add 5 to y coord of {_vec} + assert {_vec} = vector3i(0, 5, 0) with "The vectors should match after Y change" From e9d4f97db39380aeb4494905c0df90eac0938166 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 11:05:17 -0800 Subject: [PATCH 034/106] CondEntityIsAlive - add condition to check for life --- .../elements/conditions/ConditionHandler.java | 4 ++ .../conditions/entity/CondEntityIsAlive.java | 49 +++++++++++++++++++ .../conditions/entity/CondEntityIsAlive.sk | 7 +++ 3 files changed, 60 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsAlive.java create mode 100644 src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsAlive.sk diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index e1202e72..68e283ab 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -2,10 +2,14 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsAlive; public class ConditionHandler { public static void register(SkriptRegistration registration) { + // ENTITY + CondEntityIsAlive.register(registration); + CondEntityIsFrozen.register(registration); CondHasPermission.register(registration); CondInventoryCanHold.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsAlive.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsAlive.java new file mode 100644 index 00000000..c2e69587 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsAlive.java @@ -0,0 +1,49 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +public class CondEntityIsAlive extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondEntityIsAlive.class, + "livingentities", + ConditionalType.BE, + "(alive|:dead)") + .name("Entity is Alive/Dead") + .description("Check if a LivingEntity is alive/dead.") + .examples("if player is alive:", + "\tkill player") + .since("INSERT VERSION") + .register(); + } + + private boolean isDead; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { + this.isDead = parseContext.hasMark("dead"); + return super.init(expressions, matchedPattern, parseContext); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return getPerformer().check(ctx, entity -> { + DeathComponent component = EntityUtils.getComponent(entity, DeathComponent.getComponentType()); + if (this.isDead) { + return component != null; + } else { + return component == null; + } + }); + } + +} diff --git a/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsAlive.sk b/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsAlive.sk new file mode 100644 index 00000000..16d9bb48 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsAlive.sk @@ -0,0 +1,7 @@ +test "CondEntityIsAlive": + spawn a sheep at world spawn of event-world: + set {_e} to event-spawned-entity + + assert {_e} is alive with "The sheep should be alive" + kill {_e} + assert {_e} is dead with "The sheep should be dead now" From 27e084aeae42718f9be7d61dcddd603fd12f77ce Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 11:06:02 -0800 Subject: [PATCH 035/106] Conditions - repackage by type --- .../skript/plugin/elements/conditions/ConditionHandler.java | 4 ++++ .../elements/conditions/{ => entity}/CondEntityIsFrozen.java | 2 +- .../elements/conditions/{ => item}/CondInventoryCanHold.java | 2 +- .../elements/conditions/{ => player}/CondHasPermission.java | 2 +- .../conditions/{ => player}/CondPlayerIsCrouching.java | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) rename src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/{ => entity}/CondEntityIsFrozen.java (95%) rename src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/{ => item}/CondInventoryCanHold.java (97%) rename src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/{ => player}/CondHasPermission.java (97%) rename src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/{ => player}/CondPlayerIsCrouching.java (96%) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 68e283ab..093e0afc 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -3,6 +3,10 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsAlive; +import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsFrozen; +import com.github.skriptdev.skript.plugin.elements.conditions.item.CondInventoryCanHold; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondHasPermission; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerIsCrouching; public class ConditionHandler { diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsFrozen.java similarity index 95% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsFrozen.java index ab0252fe..133b28bc 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondEntityIsFrozen.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsFrozen.java @@ -1,4 +1,4 @@ -package com.github.skriptdev.skript.plugin.elements.conditions; +package com.github.skriptdev.skript.plugin.elements.conditions.entity; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.component.Ref; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondInventoryCanHold.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/item/CondInventoryCanHold.java similarity index 97% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondInventoryCanHold.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/item/CondInventoryCanHold.java index 866b82a7..cc2be19f 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondInventoryCanHold.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/item/CondInventoryCanHold.java @@ -1,4 +1,4 @@ -package com.github.skriptdev.skript.plugin.elements.conditions; +package com.github.skriptdev.skript.plugin.elements.conditions.item; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.hypixel.hytale.server.core.inventory.Inventory; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondHasPermission.java similarity index 97% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondHasPermission.java index 65473708..5abf9af0 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondHasPermission.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondHasPermission.java @@ -1,4 +1,4 @@ -package com.github.skriptdev.skript.plugin.elements.conditions; +package com.github.skriptdev.skript.plugin.elements.conditions.player; import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.permissions.PermissionsModule; diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondPlayerIsCrouching.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java similarity index 96% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondPlayerIsCrouching.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java index a512fa3c..6c98e9a6 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/CondPlayerIsCrouching.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java @@ -1,4 +1,4 @@ -package com.github.skriptdev.skript.plugin.elements.conditions; +package com.github.skriptdev.skript.plugin.elements.conditions.player; import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; From e423d15515d191af75e5cc8e052c4f57cc2d2025 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 11:07:36 -0800 Subject: [PATCH 036/106] Conditions - rename --- .../plugin/elements/conditions/ConditionHandler.java | 9 ++++++--- ...ndHasPermission.java => CondPlayerHasPermission.java} | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) rename src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/{CondHasPermission.java => CondPlayerHasPermission.java} (94%) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 093e0afc..8bd5eaf1 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -5,7 +5,7 @@ import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsAlive; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsFrozen; import com.github.skriptdev.skript.plugin.elements.conditions.item.CondInventoryCanHold; -import com.github.skriptdev.skript.plugin.elements.conditions.player.CondHasPermission; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerHasPermission; import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerIsCrouching; public class ConditionHandler { @@ -13,10 +13,13 @@ public class ConditionHandler { public static void register(SkriptRegistration registration) { // ENTITY CondEntityIsAlive.register(registration); - CondEntityIsFrozen.register(registration); - CondHasPermission.register(registration); + + // ITEM CondInventoryCanHold.register(registration); + + // PLAYER + CondPlayerHasPermission.register(registration); CondPlayerIsCrouching.register(registration); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondHasPermission.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerHasPermission.java similarity index 94% rename from src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondHasPermission.java rename to src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerHasPermission.java index 5abf9af0..f1b1c9e9 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondHasPermission.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerHasPermission.java @@ -12,10 +12,10 @@ import java.util.UUID; -public class CondHasPermission extends ConditionalExpression { +public class CondPlayerHasPermission extends ConditionalExpression { public static void register(SkriptRegistration registration) { - registration.newExpression(CondHasPermission.class, Boolean.class, true, + registration.newExpression(CondPlayerHasPermission.class, Boolean.class, true, "%players/playerrefs/uuid% (has|have) permission %strings%", "%players/playerrefs/uuid% (don't|do not|doesn't|does not) (has|have) permission %strings%") .name("Permission") From 2e45c887aa151967e6e9c0cf2bcae6af5dc26256 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 14:08:32 -0800 Subject: [PATCH 037/106] build.gradle.kts - switch to skript-parser dev branch --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 70612d25..a2422447 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:1.0.8") { + implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { From ce7ad94b0248c4879c5989ed3175010df66ffec9 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 14:34:06 -0800 Subject: [PATCH 038/106] DefaultFunctions - update to new function registration --- .../elements/functions/DefaultFunctions.java | 298 ++++++++---------- 1 file changed, 134 insertions(+), 164 deletions(-) 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 ae6e6064..85b27541 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 @@ -9,9 +9,6 @@ import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; -import io.github.syst3ms.skriptparser.structures.functions.FunctionParameter; -import io.github.syst3ms.skriptparser.structures.functions.Functions; -import io.github.syst3ms.skriptparser.structures.functions.JavaFunction; import io.github.syst3ms.skriptparser.util.SkriptDate; import io.github.syst3ms.skriptparser.util.Time; import org.bson.BsonDocument; @@ -24,30 +21,23 @@ public class DefaultFunctions { public static void register(SkriptRegistration reg) { dateTimeFunctions(reg); itemFunctions(reg); + mathFunctions(reg); positionFunctions(reg); } private static void dateTimeFunctions(SkriptRegistration reg) { - Functions.newJavaFunction(reg, new JavaFunction<>( - "date", - new FunctionParameter[]{ - new FunctionParameter<>("year", Number.class, true), - new FunctionParameter<>("month", Number.class, true), - new FunctionParameter<>("day", Number.class, true) - }, - SkriptDate.class, - true - ) { - @Override - public SkriptDate[] executeSimple(Object[][] params) { - Number year = (Number) params[0][0]; - Number month = (Number) params[1][0]; - Number day = (Number) params[2][0]; - LocalDateTime localDateTime = LocalDateTime.of(year.intValue(), month.intValue(), - day.intValue(), 0, 0); - long epochSecond = localDateTime.toEpochSecond(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); - return new SkriptDate[]{SkriptDate.of(epochSecond * 1000)}; - } + reg.newJavaFunction("date", SkriptDate.class, true) + .parameter("year", Number.class, true) + .parameter("month", Number.class, true) + .parameter("day", Number.class, true) + .executeSingle(params -> { + Number year = (Number) params[0][0]; + Number month = (Number) params[1][0]; + Number day = (Number) params[2][0]; + LocalDateTime localDateTime = LocalDateTime.of(year.intValue(), month.intValue(), + day.intValue(), 0, 0); + long epochSecond = localDateTime.toEpochSecond(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); + return SkriptDate.of(epochSecond * 1000); }) .name("Date") .description("Creates a new Date with the given parameters.") @@ -55,31 +45,24 @@ public SkriptDate[] executeSimple(Object[][] params) { .since("INSERT VERSION") .register(); - Functions.newJavaFunction(reg, new JavaFunction<>( - "dateTime", - new FunctionParameter[]{ - new FunctionParameter<>("year", Number.class, true), - new FunctionParameter<>("month", Number.class, true), - new FunctionParameter<>("day", Number.class, true), - new FunctionParameter<>("hour", Number.class, true), - new FunctionParameter<>("minute", Number.class, true), - new FunctionParameter<>("second", Number.class, true) - }, - SkriptDate.class, - true) { - @Override - public SkriptDate[] executeSimple(Object[][] params) { - Number year = (Number) params[0][0]; - Number month = (Number) params[1][0]; - Number day = (Number) params[2][0]; - Number hour = (Number) params[3][0]; - Number minute = (Number) params[4][0]; - Number second = (Number) params[5][0]; - LocalDateTime localDateTime = LocalDateTime.of(year.intValue(), month.intValue(), - day.intValue(), hour.intValue(), minute.intValue(), second.intValue()); - long epochSecond = localDateTime.toEpochSecond(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); - return new SkriptDate[]{SkriptDate.of(epochSecond * 1000)}; - } + reg.newJavaFunction("dateTime", SkriptDate.class, true) + .parameter("year", Number.class, true) + .parameter("month", Number.class, true) + .parameter("day", Number.class, true) + .parameter("hour", Number.class, true) + .parameter("minute", Number.class, true) + .parameter("second", Number.class, true) + .executeSingle(params -> { + Number year = (Number) params[0][0]; + Number month = (Number) params[1][0]; + Number day = (Number) params[2][0]; + Number hour = (Number) params[3][0]; + Number minute = (Number) params[4][0]; + Number second = (Number) params[5][0]; + LocalDateTime localDateTime = LocalDateTime.of(year.intValue(), month.intValue(), + day.intValue(), hour.intValue(), minute.intValue(), second.intValue()); + long epochSecond = localDateTime.toEpochSecond(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); + return SkriptDate.of(epochSecond * 1000); }) .name("DateTime") .description("Creates a new Date with a time with the given parameters.", @@ -88,22 +71,16 @@ public SkriptDate[] executeSimple(Object[][] params) { .since("INSERT VERSION") .register(); - Functions.newJavaFunction(reg, new JavaFunction<>( - "time", - new FunctionParameter[]{ - new FunctionParameter<>("hour", Number.class, true), - new FunctionParameter<>("minute", Number.class, true), - new FunctionParameter<>("second", Number.class, true) - }, - Time.class, - true) { - @Override - public Time[] executeSimple(Object[][] params) { - Number hour = (Number) params[0][0]; - Number minute = (Number) params[1][0]; - Number second = (Number) params[2][0]; - return new Time[]{Time.of(hour.intValue(), minute.intValue(), second.intValue(), 0)}; - } + reg.newJavaFunction("time", Time.class, true) + .parameter("hour", Number.class, true) + .parameter("minute", Number.class, true) + .parameter("second", Number.class, true) + .executeSingle(params -> { + Number hour = (Number) params[0][0]; + Number minute = (Number) params[1][0]; + Number second = (Number) params[2][0]; + return Time.of(hour.intValue(), minute.intValue(), second.intValue(), 0); + }) .name("Time") .description("Creates a new Time with the given parameters.", @@ -114,27 +91,19 @@ public Time[] executeSimple(Object[][] params) { } private static void itemFunctions(SkriptRegistration reg) { - Functions.newJavaFunction(reg, new JavaFunction<>( - "itemstack", - new FunctionParameter[]{ - new FunctionParameter<>("type", Item.class, true), - new FunctionParameter<>("quantity", Number.class, true), - new FunctionParameter<>("durability", Number.class, true), - new FunctionParameter<>("maxDurability", Number.class, true) - }, - ItemStack.class, - true) { - @Override - public ItemStack[] executeSimple(Object[][] params) { - Item type = (Item) params[0][0]; - Number quantity = (Number) params[1][0]; - Number durability = (Number) params[2][0]; - Number maxDurability = (Number) params[3][0]; - int max = maxDurability.intValue(); - ItemStack itemStack = new ItemStack(type.getId(), quantity.intValue(), - Math.clamp(durability.intValue(), 0, max), max, new BsonDocument()); - return new ItemStack[]{itemStack}; - } + reg.newJavaFunction("itemstack", ItemStack.class, true) + .parameter("type", Item.class, true) + .parameter("quantity", Number.class, true) + .parameter("durability", Number.class, true) + .parameter("maxDurability", Number.class, true) + .executeSingle(params -> { + Item type = (Item) params[0][0]; + Number quantity = (Number) params[1][0]; + Number durability = (Number) params[2][0]; + Number maxDurability = (Number) params[3][0]; + int max = maxDurability.intValue(); + return new ItemStack(type.getId(), quantity.intValue(), + Math.clamp(durability.intValue(), 0, max), max, new BsonDocument()); }) .name("ItemStack") .description("Creates a new ItemStack with the given parameters.") @@ -143,25 +112,57 @@ public ItemStack[] executeSimple(Object[][] params) { .register(); } + private static void mathFunctions(SkriptRegistration reg) { + reg.newJavaFunction("abs", Number.class, true) + .parameter("number", Number.class) + .executeSingle(params -> Math.abs(((Number) params[0][0]).doubleValue())) + .name("Math - Absolute") + .description("Returns the absolute (always positive) value of a number.") + .since("INSERT VERSION") + .register(); + reg.newJavaFunction("ceil", Number.class, true) + .parameter("number", Number.class) + .executeSingle(params -> Math.ceil(((Number) params[0][0]).doubleValue())) + .name("Math - Ceiling") + .description("Returns the ceiling (rounded up) value of a decimal point number.") + .since("INSERT VERSION") + .register(); + reg.newJavaFunction("cos", Number.class, true) + .parameter("number", Number.class) + .executeSingle(params -> Math.cos(((Number) params[0][0]).doubleValue())) + .name("Math - Cosine") + .description("Retuns the cosine of an angle.") + .since("INSERT VERSION") + .register(); + reg.newJavaFunction("floor", Number.class, true) + .parameter("number", Number.class) + .executeSingle(params -> Math.floor(((Number) params[0][0]).doubleValue())) + .name("Math - Floor") + .description("Returns the floor (rounded down) value of a decimal point number.") + .since("INSERT VERSION") + .register(); + reg.newJavaFunction("sin", Number.class, true) + .parameter("number", Number.class) + .executeSingle(params -> Math.sin(((Number) params[0][0]).doubleValue())) + .name("Math - Sine") + .description("Retuns the sine of an angle.") + .since("INSERT VERSION") + .register(); + } + private static void positionFunctions(SkriptRegistration reg) { - Functions.newJavaFunction(reg, new JavaFunction<>( - "location", - new FunctionParameter[]{ - new FunctionParameter<>("x", Number.class, true), - new FunctionParameter<>("y", Number.class, true), - new FunctionParameter<>("z", Number.class, true), - new FunctionParameter<>("world", World.class, true) - }, - Location.class, - true) { - @Override - public Location[] executeSimple(Object[][] params) { - Number x = (Number) params[0][0]; - Number y = (Number) params[1][0]; - Number z = (Number) params[2][0]; - World world = (World) params[3][0]; - return new Location[]{new Location(world.getName(), x.doubleValue(), y.doubleValue(), z.doubleValue())}; - } + reg.newJavaFunction("location", Location.class, true) + .parameter("x", Number.class) + .parameter("y", Number.class) + .parameter("z", Number.class) + .parameter("world", World.class) + .executeSingle(params -> { + Number x = (Number) params[0][0]; + Number y = (Number) params[1][0]; + Number z = (Number) params[2][0]; + World world = (World) params[3][0]; + return new Location(world.getName(), x.doubleValue(), y.doubleValue(), z.doubleValue()); + }) .name("Location") .description("Creates a location in a world.") @@ -169,22 +170,15 @@ public Location[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(reg, new JavaFunction<>( - "vector3i", - new FunctionParameter[]{ - new FunctionParameter<>("x", Number.class, true), - new FunctionParameter<>("y", Number.class, true), - new FunctionParameter<>("z", Number.class, true) - }, - Vector3i.class, - true) { - @Override - public Vector3i[] executeSimple(Object[][] params) { - Number x = (Number) params[0][0]; - Number y = (Number) params[1][0]; - Number z = (Number) params[2][0]; - return new Vector3i[]{new Vector3i(x.intValue(), y.intValue(), z.intValue())}; - } + reg.newJavaFunction("vector3i", Vector3i.class, true) + .parameter("x", Number.class) + .parameter("y", Number.class) + .parameter("z", Number.class) + .executeSingle(params -> { + Number x = (Number) params[0][0]; + Number y = (Number) params[1][0]; + Number z = (Number) params[2][0]; + return new Vector3i(x.intValue(), y.intValue(), z.intValue()); }) .name("Vector3i") .description("Creates a vector3i with integer coordinates.") @@ -192,22 +186,15 @@ public Vector3i[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(reg, new JavaFunction<>( - "vector3f", - new FunctionParameter[]{ - new FunctionParameter<>("x", Number.class, true), - new FunctionParameter<>("y", Number.class, true), - new FunctionParameter<>("z", Number.class, true) - }, - Vector3f.class, - true) { - @Override - public Vector3f[] executeSimple(Object[][] params) { - Number x = (Number) params[0][0]; - Number y = (Number) params[1][0]; - Number z = (Number) params[2][0]; - return new Vector3f[]{new Vector3f(x.floatValue(), y.floatValue(), z.floatValue())}; - } + reg.newJavaFunction("vector3f", Vector3f.class, true) + .parameter("x", Number.class) + .parameter("y", Number.class) + .parameter("z", Number.class) + .executeSingle(params -> { + Number x = (Number) params[0][0]; + Number y = (Number) params[1][0]; + Number z = (Number) params[2][0]; + return new Vector3f(x.floatValue(), y.floatValue(), z.floatValue()); }) .name("Vector3f") .description("Creates a vector3f with float coordinates.") @@ -215,22 +202,15 @@ public Vector3f[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(reg, new JavaFunction<>( - "vector3d", - new FunctionParameter[]{ - new FunctionParameter<>("x", Number.class, true), - new FunctionParameter<>("y", Number.class, true), - new FunctionParameter<>("z", Number.class, true) - }, - Vector3d.class, - true) { - @Override - public Vector3d[] executeSimple(Object[][] params) { - Number x = (Number) params[0][0]; - Number y = (Number) params[1][0]; - Number z = (Number) params[2][0]; - return new Vector3d[]{new Vector3d(x.doubleValue(), y.doubleValue(), z.doubleValue())}; - } + reg.newJavaFunction("vector3d", Vector3d.class, true) + .parameter("x", Number.class) + .parameter("y", Number.class) + .parameter("z", Number.class) + .executeSingle(params -> { + Number x = (Number) params[0][0]; + Number y = (Number) params[1][0]; + Number z = (Number) params[2][0]; + return new Vector3d(x.doubleValue(), y.doubleValue(), z.doubleValue()); }) .name("Vector3d") .description("Creates a vector3d with double coordinates.") @@ -238,19 +218,9 @@ public Vector3d[] executeSimple(Object[][] params) { .since("1.0.0") .register(); - Functions.newJavaFunction(reg, new JavaFunction<>( - "world", - new FunctionParameter[]{ - new FunctionParameter<>("name", String.class, true) - }, - World.class, - true) { - @Override - public World[] executeSimple(Object[][] params) { - String name = (String) params[0][0]; - return new World[]{Universe.get().getWorld(name)}; - } - }) + reg.newJavaFunction("world", World.class, true) + .parameter("name", String.class) + .executeSingle(params -> Universe.get().getWorld((String) params[0][0])) .name("World") .description("Returns the world with the given name.") .examples("set {_world} to world(\"default\")") From 092012209477540203cd935705ac252b3313ef79 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 14:59:53 -0800 Subject: [PATCH 039/106] ExprInventoryAmountOfItems - add expr to count items in inventory --- .../expressions/ExpressionHandler.java | 2 + .../item/ExprInventoryAmountOfItems.java | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 5cd3655d..26b99dee 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -23,6 +23,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprTargetEntityOfEntity; import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffectEffect; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventory; +import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventoryAmountOfItems; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlot; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlots; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemContainer; @@ -100,6 +101,7 @@ public static void register(SkriptRegistration registration) { // ITEM ExprInventory.register(registration); + ExprInventoryAmountOfItems.register(registration); ExprInventorySlot.register(registration); ExprInventorySlots.register(registration); ExprItemContainer.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java new file mode 100644 index 00000000..1e62f4de --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java @@ -0,0 +1,86 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.item; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +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 io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +public class ExprInventoryAmountOfItems implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprInventoryAmountOfItems.class, Number.class, true, + "item amount of %items/itemstacks% in %inventory/itemcontainer%") + .name("Inventory Item Count") + .description("Get the amount of a certain Item/ItemStack in an Inventory/ItemContainer.", + "If you pass in an Item, it will compare the Item types of ItemStacks in the inventory.", + "If you pass in an ItemStack, it will direct compare that the ItemStacks are the same " + + "(ie: same durability, max durability, metadata, etc), excluding stack size.") + .examples("set {_amount} to item amount of ingredient_stick in inventory of player") + .since("INSERT VERSION") + .register(); + } + + private Expression items; + private Expression container; + + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.items = expressions[0]; + this.container = expressions[1]; + return true; + } + + @Override + public Number[] getValues(@NotNull TriggerContext ctx) { + ItemContainer container; + + Object o = this.container.getSingle(ctx).orElse(null); + if (o instanceof ItemContainer c) { + container = c; + } else if (o instanceof Inventory inv) { + container = inv.getCombinedEverything(); + } else { + return null; + } + + List itemStacks = new ArrayList<>(); + for (Object object : this.items.getArray(ctx)) { + if (object instanceof ItemStack itemStack) { + itemStacks.add(itemStack); + } else if (object instanceof Item item) { + itemStacks.add(item); + } + } + if (itemStacks.isEmpty()) { + return new Number[]{0}; + } + + int count = container.countItemStacks(itemStack -> { + for (Object obj : itemStacks) { + if (obj instanceof ItemStack stack && stack.isStackableWith(itemStack)) { + return true; + } else if (obj instanceof Item item && itemStack.getItem().getId().equals(item.getId())) { + return true; + } + } + return false; + }); + + return new Number[]{count}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "item amount of " + this.items.toString(ctx, debug) + + " in " + this.container.toString(ctx, debug); + } + +} From c6b0e51418a88fbf0d2f454c980bc89aab29255e Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 15:20:50 -0800 Subject: [PATCH 040/106] ExprItemStackQuantity - add expr for itemstack quantity --- .../expressions/ExpressionHandler.java | 2 ++ .../item/ExprItemStackQuantity.java | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackQuantity.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 26b99dee..047d202d 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -28,6 +28,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlots; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemContainer; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStack; +import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStackQuantity; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemType; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemsInInventory; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCast; @@ -107,6 +108,7 @@ public static void register(SkriptRegistration registration) { ExprItemContainer.register(registration); ExprItemsInInventory.register(registration); ExprItemStack.register(registration); + ExprItemStackQuantity.register(registration); ExprItemType.register(registration); // OTHER diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackQuantity.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackQuantity.java new file mode 100644 index 00000000..266379b0 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackQuantity.java @@ -0,0 +1,28 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.item; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ExprItemStackQuantity extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprItemStackQuantity.class, Number.class, + "(item[stack] quantity|[item]stack size)", "itemstacks") + .name("ItemStack Quantity") + .description("Get the quanity/stack size of an ItemStack.", + "This cannot be set (Yes, Silly... I know!).") + .examples("set {_amount} to stack size of {_itmstack}", + "if item quanity of {_itme} > 1:") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(@NotNull ItemStack itemStack) { + return itemStack.getQuantity(); + } + +} From f93d8e7b9633fc660cbe5819fd9d772d61e756cb Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 15:28:54 -0800 Subject: [PATCH 041/106] ExprInventoryAmountOfItems - change pattern --- .../elements/expressions/item/ExprInventoryAmountOfItems.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java index 1e62f4de..a1625827 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprInventoryAmountOfItems.java @@ -17,13 +17,13 @@ public class ExprInventoryAmountOfItems implements Expression { public static void register(SkriptRegistration reg) { reg.newExpression(ExprInventoryAmountOfItems.class, Number.class, true, - "item amount of %items/itemstacks% in %inventory/itemcontainer%") + "(amount|number) of %items/itemstacks% in %inventory/itemcontainer%") .name("Inventory Item Count") .description("Get the amount of a certain Item/ItemStack in an Inventory/ItemContainer.", "If you pass in an Item, it will compare the Item types of ItemStacks in the inventory.", "If you pass in an ItemStack, it will direct compare that the ItemStacks are the same " + "(ie: same durability, max durability, metadata, etc), excluding stack size.") - .examples("set {_amount} to item amount of ingredient_stick in inventory of player") + .examples("set {_amount} to amount of ingredient_stick in inventory of player") .since("INSERT VERSION") .register(); } From 848ed5ddbba0433b1e5cc0a8c363c5ca0e3847cd Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 15:47:55 -0800 Subject: [PATCH 042/106] ExprItemStackWithQuantity - new expr to get itemstack copy with quantity --- .../expressions/ExpressionHandler.java | 2 + .../item/ExprItemStackWithQuantity.java | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackWithQuantity.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 047d202d..ff499d98 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -29,6 +29,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemContainer; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStack; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStackQuantity; +import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStackWithQuantity; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemType; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemsInInventory; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprCast; @@ -109,6 +110,7 @@ public static void register(SkriptRegistration registration) { ExprItemsInInventory.register(registration); ExprItemStack.register(registration); ExprItemStackQuantity.register(registration); + ExprItemStackWithQuantity.register(registration); ExprItemType.register(registration); // OTHER diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackWithQuantity.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackWithQuantity.java new file mode 100644 index 00000000..9005b309 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackWithQuantity.java @@ -0,0 +1,47 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.item; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.inventory.ItemStack; +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 ExprItemStackWithQuantity implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprItemStackWithQuantity.class, ItemStack.class, true, + "%itemstack% with quantity %number%") + .name("ItemStack with Quantity") + .description("Returns a copy ItemStack with the new quantity.") + .examples("set {_item} to held item of player with quanity 10") + .since("INSERT VERSION") + .register(); + } + + private Expression itemStack; + private Expression quantity; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.itemStack = (Expression) expressions[0]; + this.quantity = (Expression) expressions[1]; + return true; + } + + @Override + public ItemStack[] getValues(@NotNull TriggerContext ctx) { + ItemStack itemStack = this.itemStack.getSingle(ctx).orElse(null); + Number number = this.quantity.getSingle(ctx).orElse(null); + if (itemStack == null || number == null) return null; + + return new ItemStack[]{itemStack.withQuantity(number.intValue())}; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return this.itemStack.toString(ctx, debug) + " with quantity " + this.quantity.toString(ctx, debug); + } + +} From 1ec8f4303b5e349a8acb783f6c5903570a13b72c Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 16:37:06 -0800 Subject: [PATCH 043/106] DefaultFunctions - inventory functions --- .../elements/functions/DefaultFunctions.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) 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 85b27541..1254d335 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 @@ -6,7 +6,11 @@ import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.entity.LivingEntity; +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.inventory.container.SimpleItemContainer; import com.hypixel.hytale.server.core.universe.Universe; import com.hypixel.hytale.server.core.universe.world.World; import io.github.syst3ms.skriptparser.util.SkriptDate; @@ -110,6 +114,61 @@ private static void itemFunctions(SkriptRegistration reg) { .examples("set {_stack} to itemstack(Food_Fish_Grilled, 1, 50, 100)") .since("1.0.0") .register(); + + reg.newJavaFunction("inventory", Inventory.class, true) + .parameter("storageCapacity", Number.class) + .parameter("armorCapacity", Number.class) + .parameter("hotbarCapacity", Number.class) + .parameter("utilityCapacity", Number.class) + .executeSingle(params -> { + short storage = (short) Math.max(((Number) params[0][0]).shortValue(), 0); + short armor = (short) Math.max(((Number) params[1][0]).shortValue(), 0); + short hotbar = (short) Math.max(((Number) params[2][0]).shortValue(), 0); + short utility = (short) Math.max(((Number) params[3][0]).shortValue(), 0); + Inventory inv = new Inventory(storage, armor, hotbar, utility, (short) 0); + // Unregister click listeners since there's no owner + inv.unregister(); + return inv; + }) + .name("Inventory") + .description("Create a new inventory with the given ItemContainer sizes.", + "This inventory will not have click events due to no owner.", + "See the `inventoryWithOwner` function for that.") + .since("INSERT VERSION") + .register(); + + reg.newJavaFunction("inventoryWithOwner", Inventory.class, true) + .parameter("owner", LivingEntity.class) + .parameter("storageCapacity", Number.class) + .parameter("armorCapacity", Number.class) + .parameter("hotbarCapacity", Number.class) + .parameter("utilityCapacity", Number.class) + .executeSingle(params -> { + LivingEntity livingEntity = (LivingEntity) params[0][0]; + short storage = (short) Math.max(((Number) params[1][0]).shortValue(), 0); + short armor = (short) Math.max(((Number) params[2][0]).shortValue(), 0); + short hotbar = (short) Math.max(((Number) params[3][0]).shortValue(), 0); + short utility = (short) Math.max(((Number) params[4][0]).shortValue(), 0); + Inventory inv = new Inventory(storage, armor, hotbar, utility, (short) 0); + inv.setEntity(livingEntity); + return inv; + }) + .name("Inventory with Owner") + .description("Create a new inventory with the given ItemContainer capacities and an owner.", + "The owner is used for registered click events.") + .since("INSERT VERSION") + .register(); + + reg.newJavaFunction("itemContainer", ItemContainer.class, true) + .parameter("capacity", Number.class) + .executeSingle(params -> { + short capacity = (short) Math.max(((Number) params[0][0]).shortValue(), 0); + return new SimpleItemContainer(capacity); + }) + .name("ItemContainer") + .description("Create a new ItemContainer with the given capacity.") + .since("INSERT VERSRION") + .register(); } private static void mathFunctions(SkriptRegistration reg) { From aaf9a1fcb5951b029e1700a1d79f964f2f30d56b Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 17:11:33 -0800 Subject: [PATCH 044/106] build.gradle.kts - update to pre-release to make sure everything works --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index a2422447..1dda481f 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" -val hytaleVersion = "2026.02.06-aa1b071c2" +val hytaleVersion = "2026.02.11-255364b8e" // (pre-release) TODO switch back to release // 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 From a58c70e0f8d3c419a38b554a8d16a1627a7767ba Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 17:12:02 -0800 Subject: [PATCH 045/106] ExprItemContainer - removed last pattern as hytale removed the method --- .../plugin/elements/expressions/item/ExprItemContainer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java index 92ee97ff..3eb7082e 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemContainer.java @@ -23,8 +23,7 @@ public static void register(SkriptRegistration reg) { "combined everything item container of %inventory%", "combined hotbar first item container of %inventory%", "combined storage first item container of %inventory%", - "combined backpack storage hotbar item container of %inventory%", - "combined armor hotbar storage item container of %inventory%") + "combined backpack storage hotbar item container of %inventory%") .name("Item Container of Inventory") .description("Returns different item containers of an inventory.", "Theres also a few combined options (These are from Hytale).") @@ -60,7 +59,6 @@ public ItemContainer[] getValues(@NotNull TriggerContext ctx) { case 7 -> inventory.getCombinedHotbarFirst(); case 8 -> inventory.getCombinedStorageFirst(); case 9 -> inventory.getCombinedBackpackStorageHotbar(); - case 10 -> inventory.getCombinedArmorHotbarStorage(); default -> null; }; return new ItemContainer[]{container}; @@ -79,7 +77,6 @@ public String toString(@NotNull TriggerContext ctx, boolean debug) { case 7 -> "combined hotbar first"; case 8 -> "combined storage first"; case 9 -> "combined backpack storage hotbar"; - case 10 -> "combined armor hotbar storage"; default -> "unknown"; }; return type + " item container of " + this.inventory.toString(ctx, debug); From 11e20084ef166a6c33d25ca2ed984cc2a80f5d34 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 17:55:06 -0800 Subject: [PATCH 046/106] EffShoot - add broken shoot effect - will hopefully get this working one day --- .../elements/effects/EffectHandler.java | 2 + .../elements/effects/entity/EffShoot.java | 80 +++++++++++++++++++ .../elements/types/TypesAssetStore.java | 7 ++ 3 files changed, 89 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffShoot.java 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 f2fbe60d..8b8aa6c0 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 @@ -8,6 +8,7 @@ import com.github.skriptdev.skript.plugin.elements.effects.entity.EffEntityEffect; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffFreeze; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffKill; +import com.github.skriptdev.skript.plugin.elements.effects.entity.EffShoot; 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; @@ -39,6 +40,7 @@ public static void register(SkriptRegistration registration) { EffFreeze.register(registration); EffInteraction.register(registration); EffKill.register(registration); + EffShoot.register(registration); EffSpawnEntity.register(registration); EffTeleport.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffShoot.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffShoot.java new file mode 100644 index 00000000..24f6c480 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffShoot.java @@ -0,0 +1,80 @@ +package com.github.skriptdev.skript.plugin.elements.effects.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.hytale.utils.StoreUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.CommandBuffer; +import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.math.vector.Vector3d; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; +import com.hypixel.hytale.server.core.modules.projectile.ProjectileModule; +import com.hypixel.hytale.server.core.modules.projectile.config.ProjectileConfig; +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; + +public class EffShoot extends Effect { + + // TODO it doesn't actually work, hopefully it's just a Hytale Bug + public static void register(SkriptRegistration reg) { + reg.newEffect(EffShoot.class, + "make %livingentity% shoot %projectileconfig%", + "make %livingentity% shoot %projectileconfig% in direction %vector3d%") + .name("Shoot") + .description("Make a LivingEntity shoot a projectile.") + .since("INSERT VERSION"); + //.register(); + } + + private Expression livingEntity; + private Expression projectile; + private Expression direction; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.livingEntity = (Expression) expressions[0]; + this.projectile = (Expression) expressions[1]; + if (matchedPattern == 1) { + this.direction = (Expression) expressions[2]; + } + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + LivingEntity entity = this.livingEntity.getSingle(ctx).orElse(null); + ProjectileConfig config = this.projectile.getSingle(ctx).orElse(null); + if (entity == null || config == null) return; + Ref ref = entity.getReference(); + if (ref == null) return; + Store store = ref.getStore(); + CommandBuffer buffer = StoreUtils.getCommandBuffer(store); + + TransformComponent component = EntityUtils.getComponent(entity, TransformComponent.getComponentType()); + if (component == null) return; + + Vector3d pos = component.getTransform().getPosition().clone(); + Vector3d dir = component.getTransform().getDirection().clone(); + + if (this.direction != null) { + Vector3d vector3d = this.direction.getSingle(ctx).orElse(null); + if (vector3d != null) { + dir = vector3d; + } + } + + ProjectileModule.get().spawnProjectile(null, buffer, config, pos, dir); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "make " + this.livingEntity.toString(ctx, debug) + " shoot " + this.projectile.toString(ctx, debug); + } + +} 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 8e6b9459..66247447 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 @@ -123,6 +123,13 @@ public Item deserialize(@NotNull Gson gson, @NotNull JsonElement element) { .since("1.0.0") .toStringFunction(Projectile::getId) .register(); +// reg.newAssetStoreType(ProjectileConfig.class, ProjectileConfig.getAssetMap(), +// "projectileconfig", "projectileConfig@s") +// .name("Projectile Config") +// .description("") +// .since("INSERT VERSION") +// .toStringFunction(ProjectileConfig::getId) +// .register(); 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.") From 52c35b0b739663308198668ea9d6c0038b2ec710 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 22:14:00 -0800 Subject: [PATCH 047/106] Add some tameable stuff --- .../elements/conditions/ConditionHandler.java | 4 ++ .../entity/CondEntityIsTameable.java | 35 +++++++++++++ .../conditions/entity/CondEntityIsTamed.java | 35 +++++++++++++ .../elements/effects/EffectHandler.java | 2 + .../elements/effects/entity/EffTame.java | 52 +++++++++++++++++++ .../conditions/entity/CondEntityIsTameable.sk | 11 ++++ .../conditions/entity/CondEntityIsTamed.sk | 12 +++++ .../hyskript/effects/entity/EffTame.sk | 9 ++++ 8 files changed, 160 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTameable.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTamed.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffTame.java create mode 100644 src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTameable.sk create mode 100644 src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTamed.sk create mode 100644 src/test/skript/tests/elements/hyskript/effects/entity/EffTame.sk diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 8bd5eaf1..3f914a88 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -4,6 +4,8 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsAlive; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsFrozen; +import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsTameable; +import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsTamed; import com.github.skriptdev.skript.plugin.elements.conditions.item.CondInventoryCanHold; import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerHasPermission; import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerIsCrouching; @@ -14,6 +16,8 @@ public static void register(SkriptRegistration registration) { // ENTITY CondEntityIsAlive.register(registration); CondEntityIsFrozen.register(registration); + CondEntityIsTameable.register(registration); + CondEntityIsTamed.register(registration); // ITEM CondInventoryCanHold.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTameable.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTameable.java new file mode 100644 index 00000000..1fd33881 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTameable.java @@ -0,0 +1,35 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.npc.entities.NPCEntity; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import org.jetbrains.annotations.NotNull; + +public class CondEntityIsTameable extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondEntityIsTameable.class, "livingentities", + ConditionalType.BE, "tameable") + .name("Entity is Tameable") + .description("Check if an entity is tameable.") + .experimental("Hytale is just adding taming, so this may change in the future.") + .examples("if target entity of player is tameable:") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.getPerformer().check(ctx, entity -> { + if (entity instanceof NPCEntity npcEntity) { + return EntityUtils.isTameable(npcEntity); + } + return false; + }, isNegated()); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTamed.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTamed.java new file mode 100644 index 00000000..2f8c006a --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/entity/CondEntityIsTamed.java @@ -0,0 +1,35 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.npc.entities.NPCEntity; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import org.jetbrains.annotations.NotNull; + +public class CondEntityIsTamed extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondEntityIsTamed.class, "livingentities", + ConditionalType.BE, "tamed") + .name("Entity is Tamed") + .description("Check if an entity is tamed.") + .experimental("Hytale is just adding taming, so this may change in the future.") + .examples("if target entity of player is tameable:") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.getPerformer().check(ctx, entity -> { + if (entity instanceof NPCEntity npcEntity) { + return EntityUtils.isTamed(npcEntity); + } + return false; + }, isNegated()); + } + +} 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 8b8aa6c0..edaf91cb 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 @@ -10,6 +10,7 @@ import com.github.skriptdev.skript.plugin.elements.effects.entity.EffKill; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffShoot; import com.github.skriptdev.skript.plugin.elements.effects.entity.EffSpawnEntity; +import com.github.skriptdev.skript.plugin.elements.effects.entity.EffTame; 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; @@ -42,6 +43,7 @@ public static void register(SkriptRegistration registration) { EffKill.register(registration); EffShoot.register(registration); EffSpawnEntity.register(registration); + EffTame.register(registration); EffTeleport.register(registration); // OTHER diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffTame.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffTame.java new file mode 100644 index 00000000..3ba95abc --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/effects/entity/EffTame.java @@ -0,0 +1,52 @@ +package com.github.skriptdev.skript.plugin.elements.effects.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.LivingEntity; +import com.hypixel.hytale.server.npc.entities.NPCEntity; +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 EffTame extends Effect { + + public static void register(SkriptRegistration reg) { + reg.newEffect(EffTame.class, "tame %livingentities%", "untame %livingentities%") + .name("Entity Taming") + .description("Tame/untame a LivingEntity.", + "This will only work on tameable entities.") + .examples("tame target entity of player", + "untame all entities in radius 5 around player") + .since("INSERT VERSION") + .register(); + } + + private boolean tame; + private Expression entities; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.tame = matchedPattern == 0; + this.entities = (Expression) expressions[0]; + return true; + } + + @Override + protected void execute(@NotNull TriggerContext ctx) { + for (LivingEntity livingEntity : this.entities.getArray(ctx)) { + if (livingEntity instanceof NPCEntity npcEntity) { + EntityUtils.setTamed(npcEntity, this.tame); + } + } + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = this.tame ? "tame" : "untame"; + return type + " " + this.entities.toString(ctx, debug); + } + +} diff --git a/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTameable.sk b/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTameable.sk new file mode 100644 index 00000000..ecb84b5f --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTameable.sk @@ -0,0 +1,11 @@ +test "CondEntityIsTameable": + set {_loc} to world spawn of event-world + spawn a sheep at {_loc}: + set {_sheep} to event-spawned-entity + + assert {_sheep} is tameable with "A sheep should be tameable" + + spawn a Skeleton_Knight at {_loc}: + set {_skel} to event-spawned-entity + + assert {_skel} is not tameable with "A skeleton shouldn't be tameable" diff --git a/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTamed.sk b/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTamed.sk new file mode 100644 index 00000000..8adc0c32 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/conditions/entity/CondEntityIsTamed.sk @@ -0,0 +1,12 @@ +test "CondEntityIsTamed": + set {_loc} to world spawn of event-world + spawn a sheep at {_loc}: + set {_sheep} to event-spawned-entity + + assert {_sheep} is not tamed with "The sheep shouldn't be tamed" + + spawn a tamed_sheep at {_loc}: + set {_sheep} to event-spawned-entity + + assert {_sheep} is tamed with "The sheep should be tamed" + diff --git a/src/test/skript/tests/elements/hyskript/effects/entity/EffTame.sk b/src/test/skript/tests/elements/hyskript/effects/entity/EffTame.sk new file mode 100644 index 00000000..54101af7 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/effects/entity/EffTame.sk @@ -0,0 +1,9 @@ +test "EffTame": + set {_loc} to world spawn of event-world + spawn a sheep at {_loc}: + set {_e} to event-spawned-entity + tame {_e} + + wait 5 tick + # assert {_e} is tamed with "The sheep should be tamed now, but found '%npc role of {_e}%'" + # TODO broken, chunk loading issue From 7eb4891c3219e73707ecbc39b17ea686edfb2ce0 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 22:14:17 -0800 Subject: [PATCH 048/106] Add some tameable stuff - part 2 --- build.gradle.kts | 2 +- .../skript/api/hytale/utils/EntityUtils.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1dda481f..3761d38a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { + implementation("com.github.SkriptDev:skript-parser:1.0.8-test") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java index 3bf41373..56f02aa6 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java @@ -1,5 +1,7 @@ package com.github.skriptdev.skript.api.hytale.utils; +import com.github.skriptdev.skript.api.skript.registration.NPCRegistry; +import com.github.skriptdev.skript.api.utils.Utils; import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.Component; import com.hypixel.hytale.component.ComponentType; @@ -20,6 +22,9 @@ import com.hypixel.hytale.server.core.modules.entitystats.EntityStatsModule; 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.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -173,4 +178,54 @@ public static void setNameplateName(Entity entity, @Nullable String name) { return new Pair<>(com.hypixel.hytale.server.core.entity.EntityUtils.getEntity(itemEntityHolder), itemComponent); } + public static boolean isTameable(NPCEntity npcEntity) { + Role role = npcEntity.getRole(); + if (role == null) return false; + + String roleName = role.getRoleName(); + if (roleName.contains("Tamed_")) { + return true; + } + // I know this is hacky, but Hytale doesn't have any API for taming + // Maybe we'll get lucky and Hytale will create API for this + NPCRegistry.NPCRole parse = NPCRegistry.parse("tamed_" + roleName); + return parse != null; + } + + public static boolean isTamed(NPCEntity npcEntity) { + Role role = npcEntity.getRole(); + if (role == null) return false; + + // I know this is hacky, but Hytale doesn't have any API for taming + // Maybe we'll get lucky and Hytale will create API for this + String roleName = role.getRoleName(); + return roleName.startsWith("Tamed_"); + } + + public static void setTamed(NPCEntity npcEntity, boolean tamed) { + if (!isTameable(npcEntity)) { + return; + } + if ((tamed && isTamed(npcEntity)) || (!tamed && !isTamed(npcEntity))) { + return; + } + Ref reference = npcEntity.getReference(); + if (reference == null) return; + + Store store = reference.getStore(); + + // I know this is hacky, but Hytale doesn't have any API for taming + // Maybe we'll get lucky and Hytale will create API for this + Role currentRole = npcEntity.getRole(); + if (currentRole == null || currentRole.isRoleChangeRequested()) return; + + String roleName = currentRole.getRoleName(); + roleName = tamed ? "Tamed_" + roleName : roleName.replace("Tamed_", ""); + + NPCRegistry.NPCRole parse = NPCRegistry.parse(roleName); + + Utils.log("Changing role to %s", parse.name()); + RoleChangeSystem.requestRoleChange(reference, currentRole, parse.index(), true, store); + } + } From bf7e23387ca29030af6d6581a0afd131bea41a0b Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 22:14:49 -0800 Subject: [PATCH 049/106] build.gradle.kts - that wasn't supposed to get commited --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3761d38a..1dda481f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:1.0.8-test") { + implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { From df5abb8eb85a88e1c46105524b425a1c9aa7af35 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Mon, 16 Feb 2026 22:55:58 -0800 Subject: [PATCH 050/106] ExprItemStackName - expr for ittemstack name/description --- .../expressions/ExpressionHandler.java | 2 + .../expressions/item/ExprItemStackName.java | 52 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackName.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index ff499d98..df1b3ac0 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -28,6 +28,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprInventorySlots; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemContainer; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStack; +import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStackName; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStackQuantity; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemStackWithQuantity; import com.github.skriptdev.skript.plugin.elements.expressions.item.ExprItemType; @@ -109,6 +110,7 @@ public static void register(SkriptRegistration registration) { ExprItemContainer.register(registration); ExprItemsInInventory.register(registration); ExprItemStack.register(registration); + ExprItemStackName.register(registration); ExprItemStackQuantity.register(registration); ExprItemStackWithQuantity.register(registration); ExprItemType.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackName.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackName.java new file mode 100644 index 00000000..9ecae572 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/item/ExprItemStackName.java @@ -0,0 +1,52 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.item; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.Message; +import com.hypixel.hytale.server.core.asset.type.item.config.Item; +import com.hypixel.hytale.server.core.asset.type.item.config.ItemTranslationProperties; +import com.hypixel.hytale.server.core.inventory.ItemStack; +import com.hypixel.hytale.server.core.util.MessageUtil; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ExprItemStackName extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprItemStackName.class, String.class, + "item (:name|description)", "itemstacks/items") + .name("Item Name/Description") + .description("Get the name/description of an Item/ItemStack.", + "These currently cannot be set.") + .since("INSERT VERSION") + .register(); + } + + private boolean name; + + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, ParseContext parseContext) { + this.name = parseContext.hasMark("name"); + return super.init(expressions, matchedPattern, parseContext); + } + + @Override + public @Nullable String getProperty(@NotNull Object owner) { + Item item; + if (owner instanceof Item i) { + item = i; + } else if (owner instanceof ItemStack itemStack) { + item = itemStack.getItem(); + } else { + return null; + } + ItemTranslationProperties translation = item.getTranslationProperties(); + String prop = this.name ? translation.getName() : translation.getDescription(); + if (prop == null) return null; + + return MessageUtil.toAnsiString(Message.translation(prop)).toAnsi(); + } + +} From 63c8e2750c21aa9bc6a1a6b8dc944fb6c1d9ed70 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 08:03:17 -0800 Subject: [PATCH 051/106] build.gradle.kts - update Hytale release version --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1dda481f..69026bd8 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" -val hytaleVersion = "2026.02.11-255364b8e" // (pre-release) TODO switch back to release +val hytaleVersion = "2026.02.17-255364b8e" // 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 @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { + implementation("com.github.SkriptDev:skript-parser:1.0.8-test") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { From 33e665f32722a7f316354b89679a42cf792220f2 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 08:16:54 -0800 Subject: [PATCH 052/106] manifest.json - remove ">=" for server version - I guess Hytale doesn't actually support that --- src/main/resources/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/manifest.json b/src/main/resources/manifest.json index eafc5508..7334ca32 100644 --- a/src/main/resources/manifest.json +++ b/src/main/resources/manifest.json @@ -14,7 +14,7 @@ } ], "Website": "https://github.com/SkriptDev/HySkript", - "ServerVersion": ">=$hytaleVersion", + "ServerVersion": "$hytaleVersion", "Dependencies": {}, "OptionalDependencies": {}, "DisabledByDefault": false, From d6c568ed9f910900b2358b738f1fc34bed5a087c Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 08:22:36 -0800 Subject: [PATCH 053/106] build.gradle.kts - OMG Shane stop commiting your test version --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 69026bd8..ce4f9e9a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ repositories { dependencies { compileOnly("com.hypixel.hytale:Server:${hytaleVersion}") compileOnly("org.jetbrains:annotations:26.0.2") - implementation("com.github.SkriptDev:skript-parser:1.0.8-test") { + implementation("com.github.SkriptDev:skript-parser:dev~patch-SNAPSHOT") { isTransitive = false } implementation("com.github.Zoltus:TinyMessage:2.0.1") { From 63781e12672cb402ae539b82f369a7a0796675ab Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 09:35:13 -0800 Subject: [PATCH 054/106] ExprHighestBlock - add highest block expression --- .../expressions/ExpressionHandler.java | 2 + .../expressions/block/ExprHighestBlock.java | 84 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprHighestBlock.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index df1b3ac0..8c996826 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -9,6 +9,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockTypeAtLocation; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockTypeOfBlock; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprBlockFluidLevel; +import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprHighestBlock; import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprTargetBlockOfPlayer; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprActiveSlot; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntitiesInRadius; @@ -84,6 +85,7 @@ public static void register(SkriptRegistration registration) { ExprBlockSphere.register(registration); ExprBlockTypeAtLocation.register(registration); ExprBlockTypeOfBlock.register(registration); + ExprHighestBlock.register(registration); ExprTargetBlockOfPlayer.register(registration); // ENTITY diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprHighestBlock.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprHighestBlock.java new file mode 100644 index 00000000..395c5428 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/block/ExprHighestBlock.java @@ -0,0 +1,84 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.util.ChunkUtil; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3i; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; +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 ExprHighestBlock implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprHighestBlock.class, Object.class, true, + "highest block at %location%", + "highest block y at %location%", + "highest block location at %location%") + .name("Highest Block") + .description("Get the highest block/y coord/location at a location.", + "This represents the highest solid block generated at a location.") + .examples("teleport player to highest block location at location of player") + .since("INSERT VERSION") + .register(); + } + + private int pattern; + private Expression location; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.pattern = matchedPattern; + this.location = (Expression) expressions[0]; + return true; + } + + @Override + public Object[] getValues(@NotNull TriggerContext ctx) { + Location location = this.location.getSingle(ctx).orElse(null); + if (location == null) return null; + + String worldName = location.getWorld(); + World world = Universe.get().getWorld(worldName); + if (world == null) return null; + + Vector3i pos = location.getPosition().toVector3i().clone(); + int x = pos.getX(); + int z = pos.getZ(); + WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z)); + if (chunk == null) return null; + + short height = chunk.getHeight(x, z); + pos.setY(height); + if (this.pattern == 1) { + return new Number[]{height}; + } else if (this.pattern == 2) { + return new Location[]{new Location(worldName, pos)}; + } else { + return new Block[]{new Block(world, pos)}; + } + } + + @Override + public Class getReturnType() { + if (this.pattern == 0) return Block.class; + return Number.class; + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + String type = switch (this.pattern) { + case 1 -> "block y"; + case 2 -> "block location"; + default -> "block"; + }; + return "highest " + type + " at " + this.location.toString(ctx, debug); + } + +} From 33e6c1afb1f9e84a10a3597cc8b962f7f86c4fa3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 12:50:19 -0800 Subject: [PATCH 055/106] JsonDocPrinter - use new function registration --- .../api/skript/docs/JsonDocPrinter.java | 135 +++++++++--------- 1 file changed, 65 insertions(+), 70 deletions(-) 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 30a61618..f1b8e872 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 @@ -20,10 +20,7 @@ import io.github.syst3ms.skriptparser.registration.SkriptRegistration; import io.github.syst3ms.skriptparser.registration.SyntaxInfo; import io.github.syst3ms.skriptparser.registration.context.ContextValue; -import io.github.syst3ms.skriptparser.structures.functions.Function; import io.github.syst3ms.skriptparser.structures.functions.FunctionParameter; -import io.github.syst3ms.skriptparser.structures.functions.Functions; -import io.github.syst3ms.skriptparser.structures.functions.JavaFunction; import io.github.syst3ms.skriptparser.types.PatternType; import io.github.syst3ms.skriptparser.types.Type; import io.github.syst3ms.skriptparser.types.TypeManager; @@ -37,7 +34,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -276,84 +272,83 @@ private void printExpressions(BsonDocument mainDocs, SkriptRegistration registra @SuppressWarnings("unchecked") private void printFunctions(BsonDocument mainDocs, SkriptRegistration registration) { BsonArray functionsArray = mainDocs.getArray("functions", new BsonArray()); - Functions.getJavaFunctions(registration).stream().sorted(Comparator.comparing(Function::getName)).forEach(function -> { - if (function instanceof JavaFunction jf) { - Documentation documentation = jf.getDocumentation(); - if (documentation.isNoDoc()) return; + for (SkriptRegistration.FunctionRegistrar function : registration.getFunctions()) { + Documentation documentation = function.getDocumentation(); + if (documentation.isNoDoc()) return; - BsonDocument functionDoc = new BsonDocument(); - String name = documentation.getName(); - if (name == null) name = function.getName(); - - functionDoc.put("name", new BsonString(name)); - functionDoc.put("id", getId("function", name)); - - // Create params - FunctionParameter[] parameters = jf.getParameters(); - - List parameterNames = new ArrayList<>(); - for (FunctionParameter parameter : parameters) { - Optional> byClass = TypeManager.getByClass(parameter.getType()); - if (byClass.isPresent()) { - Type type = byClass.get(); - String typeName; - if (parameter.isSingle()) { - typeName = type.getBaseName(); - } else { - typeName = type.getPluralForm(); - } - String format = String.format("%s:%s", parameter.getName(), typeName); - parameterNames.add(format); + BsonDocument functionDoc = new BsonDocument(); + String name = documentation.getName(); + if (name == null) name = function.getFunctionName(); + + functionDoc.put("name", new BsonString(name)); + functionDoc.put("id", getId("function", name)); + + // Create params + List> parameters = function.getParams(); + + List parameterNames = new ArrayList<>(); + for (FunctionParameter parameter : parameters) { + Optional> byClass = TypeManager.getByClass(parameter.getType()); + if (byClass.isPresent()) { + Type type = byClass.get(); + String typeName; + if (parameter.isSingle()) { + typeName = type.getBaseName(); + } else { + typeName = type.getPluralForm(); } + String format = String.format("%s:%s", parameter.getName(), typeName); + parameterNames.add(format); } + } - // Create a pattern for a function - String pattern = String.format("%s(%s)", jf.getName(), String.join(", ", parameterNames)); - functionDoc.put("patterns", new BsonArray(List.of(new BsonString(pattern)))); - - // DESCRIPTION - BsonArray descriptionArray = new BsonArray(); - for (String s : documentation.getDescription()) { - descriptionArray.add(new BsonString(s)); - } - functionDoc.put("description", descriptionArray); + // Create a pattern for a function + String pattern = String.format("%s(%s)", function.getFunctionName(), String.join(", ", parameterNames)); + functionDoc.put("patterns", new BsonArray(List.of(new BsonString(pattern)))); - // USAGE - String usage = documentation.getUsage(); - if (usage != null) { - functionDoc.put("usage", new BsonString(usage)); - } + // DESCRIPTION + BsonArray descriptionArray = new BsonArray(); + for (String s : documentation.getDescription()) { + descriptionArray.add(new BsonString(s)); + } + functionDoc.put("description", descriptionArray); - // EXAMPLES - String[] examples = documentation.getExamples(); - if (examples != null) { - BsonArray exampleArray = new BsonArray(); - for (String s : examples) { - exampleArray.add(new BsonString(s)); - } - functionDoc.put("examples", exampleArray); - } + // USAGE + String usage = documentation.getUsage(); + if (usage != null) { + functionDoc.put("usage", new BsonString(usage)); + } - // SINCE - String since = documentation.getSince(); - if (since != null) { - functionDoc.put("since", new BsonArray(List.of(new BsonString(since)))); - } else { - Utils.warn(this.sender, "Function '%s' has no since tag!", name); + // EXAMPLES + String[] examples = documentation.getExamples(); + if (examples != null) { + BsonArray exampleArray = new BsonArray(); + for (String s : examples) { + exampleArray.add(new BsonString(s)); } + functionDoc.put("examples", exampleArray); + } - // RETURN TYPE - Optional> returnType = (Optional>) jf.getReturnType(); - if (returnType.isPresent()) { - Optional> byClass = TypeManager.getByClass(returnType.get()); - byClass.ifPresent(type -> - functionDoc.put("return type", new BsonString(type.getBaseName()))); - } - functionsArray.add(functionDoc); + // SINCE + String since = documentation.getSince(); + if (since != null) { + functionDoc.put("since", new BsonArray(List.of(new BsonString(since)))); + } else { + Utils.warn(this.sender, "Function '%s' has no since tag!", name); + } + // RETURN TYPE + Class returnType = function.getReturnType(); + if (returnType != null) { + Optional> byClass = TypeManager.getByClass(returnType); + byClass.ifPresent(type -> + functionDoc.put("return type", new BsonString(type.getBaseName()))); } - }); + functionsArray.add(functionDoc); + + + } mainDocs.put("functions", functionsArray); } From 2827bd30ba87dc42a5ae927ad7be17987d5dbab6 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 12:59:16 -0800 Subject: [PATCH 056/106] CondBlockIsSolid - add block is solid condition --- .../elements/conditions/ConditionHandler.java | 4 ++ .../conditions/block/CondBlockIsSolid.java | 39 +++++++++++++++++++ .../conditions/block/CondBlockIsSolid.sk | 9 +++++ 3 files changed, 52 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/block/CondBlockIsSolid.java create mode 100644 src/test/skript/tests/elements/hyskript/conditions/block/CondBlockIsSolid.sk diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 3f914a88..6060dea7 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -2,6 +2,7 @@ import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.github.skriptdev.skript.plugin.elements.conditions.block.CondBlockIsSolid; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsAlive; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsFrozen; import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsTameable; @@ -13,6 +14,9 @@ public class ConditionHandler { public static void register(SkriptRegistration registration) { + // BLOCK + CondBlockIsSolid.register(registration); + // ENTITY CondEntityIsAlive.register(registration); CondEntityIsFrozen.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/block/CondBlockIsSolid.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/block/CondBlockIsSolid.java new file mode 100644 index 00000000..4a6bcedd --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/block/CondBlockIsSolid.java @@ -0,0 +1,39 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.block; + +import com.github.skriptdev.skript.api.hytale.Block; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.protocol.BlockMaterial; +import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; + +public class CondBlockIsSolid extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondBlockIsSolid.class, "blocks/blocktypes", + ConditionalType.BE, "solid") + .name("Block is Solid") + .description("Check if a Block/BlockType is solid.") + .examples("if target block of player is solid:") + .since("INSERT VERSION") + .register(); + } + + @SuppressWarnings("NullableProblems") + @Override + public boolean check(TriggerContext ctx) { + return getPerformer().check(ctx, o -> { + BlockType blockType; + if (o instanceof BlockType bt) { + blockType = bt; + } else if (o instanceof Block block) { + blockType = block.getType(); + } else { + return false; + } + return blockType.getMaterial() == BlockMaterial.Solid; + }, isNegated()); + } + +} diff --git a/src/test/skript/tests/elements/hyskript/conditions/block/CondBlockIsSolid.sk b/src/test/skript/tests/elements/hyskript/conditions/block/CondBlockIsSolid.sk new file mode 100644 index 00000000..a01af5d8 --- /dev/null +++ b/src/test/skript/tests/elements/hyskript/conditions/block/CondBlockIsSolid.sk @@ -0,0 +1,9 @@ +test "CondBlockIsSolid": + set {_block} to block at world spawn of event-world + set blocktype of {_block} to rock_stone + assert {_block} is solid with "The stone block should be solid" + assert blocktype of {_block} is solid with "The type of block should be solid" + + set blocktype of {_block} to empty as BlockType + assert {_block} is not solid with "The empty block should not be solid" + assert blocktype of {_block} is not solid with "The type of block should not be solid" From fa6759dc28c4b2fbf86266035686868964573c54 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 16:46:55 -0800 Subject: [PATCH 057/106] ExprLocationRotation - add location rotation --- .../expressions/ExpressionHandler.java | 2 + .../other/ExprLocationRotation.java | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationRotation.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 8c996826..e6513a71 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -42,6 +42,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationDirection; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationOf; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationOffset; +import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprLocationRotation; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprMessage; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprMessageColor; import com.github.skriptdev.skript.plugin.elements.expressions.other.ExprMessageLink; @@ -125,6 +126,7 @@ public static void register(SkriptRegistration registration) { ExprLocationDirection.register(registration); ExprLocationOf.register(registration); ExprLocationOffset.register(registration); + ExprLocationRotation.register(registration); ExprMessage.register(registration); ExprMessageColor.register(registration); ExprMessageLink.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationRotation.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationRotation.java new file mode 100644 index 00000000..e7adcd46 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/other/ExprLocationRotation.java @@ -0,0 +1,68 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.other; + +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.vector.Location; +import com.hypixel.hytale.math.vector.Vector3f; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class ExprLocationRotation implements Expression { + + public static void register(SkriptRegistration reg) { + reg.newExpression(ExprLocationRotation.class, Vector3f.class, true, + "rotation of %location%") + .name("Location Rotation") + .description("Get/set the rotation of a location.", + "Do note that setting the rotation of a location will not rotate an entity it may be attached to.") + .examples("set rotation of {_loc} to vector3f(0, 10, 0)", + "set {_rot} to rotation of location of player") + .since("INSERT VERSION") + .register(); + } + + private Expression location; + + @SuppressWarnings("unchecked") + @Override + public boolean init(Expression @NotNull [] expressions, int matchedPattern, @NotNull ParseContext parseContext) { + this.location = (Expression) expressions[0]; + return true; + } + + @Override + public Vector3f[] getValues(@NotNull TriggerContext ctx) { + Location location = this.location.getSingle(ctx).orElse(null); + if (location == null) return null; + + return new Vector3f[]{location.getRotation()}; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET) { + return Optional.of(new Class[]{Vector3f.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Vector3f vector3f)) { + return; + } + + this.location.getSingle(ctx).ifPresent(loc -> loc.setRotation(vector3f)); + } + + @Override + public String toString(@NotNull TriggerContext ctx, boolean debug) { + return "rotation of " + this.location.toString(ctx, debug); + } + +} From 648b828190c39abb0fe0c5b212457576229a26fb Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 17:07:10 -0800 Subject: [PATCH 058/106] ExprEntityHeadRotation - add --- .../expressions/ExpressionHandler.java | 2 ++ .../entity/ExprEntityHeadRotation.java | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHeadRotation.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index e6513a71..6b8478d0 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -13,6 +13,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprTargetBlockOfPlayer; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprActiveSlot; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntitiesInRadius; +import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHeadRotation; import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffectDuration; import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffects; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHealth; @@ -92,6 +93,7 @@ public static void register(SkriptRegistration registration) { // ENTITY ExprActiveSlot.register(registration); ExprEntitiesInRadius.register(registration); + ExprEntityHeadRotation.register(registration); ExprEntityHealth.register(registration); ExprEntityStat.register(registration); ExprEntityVelocity.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHeadRotation.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHeadRotation.java new file mode 100644 index 00000000..05c30561 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityHeadRotation.java @@ -0,0 +1,32 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.math.vector.Vector3f; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ExprEntityHeadRotation extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprEntityHeadRotation.class, Vector3f.class, + "head rotation", "entities") + .name("Entity Head Rotation") + .description("Get the head rotation of an entity.") + .examples("set {_rot} to head rotation of player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Vector3f getProperty(@NotNull Entity entity) { + HeadRotation component = EntityUtils.getComponent(entity, HeadRotation.getComponentType()); + if (component == null) return null; + + return component.getRotation(); + } + +} From 1ae65fa098af6db7a3c93ba16aaac9c79b51181e Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 17:37:11 -0800 Subject: [PATCH 059/106] EntityUtils - add ensure --- .../skript/api/hytale/utils/EntityUtils.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java index 56f02aa6..22ecda48 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java @@ -104,7 +104,7 @@ public static void setNameplateName(Entity entity, @Nullable String name) { } /** - * Get a component from an Entity + * Get a component from an Entity. * * @param entity Entity to get component from * @param type Component type to get @@ -121,6 +121,26 @@ public static void setNameplateName(Entity entity, @Nullable String name) { return store.getComponent(reference, type); } + /** + * Get a component from an Entity or create it if not present. + * + * @param entity Entity to get component from + * @param type Component type to get + * @param EntityStore Type + * @param Type of returned component + * @return Component from entity if available otherwise will create/add a new one + */ + @SuppressWarnings("unchecked") + public static > @NotNull T ensureAndGetComponent(Entity entity, ComponentType type) { + Ref reference = (Ref) entity.getReference(); + if (reference == null) { + throw new IllegalStateException("Entity '" + entity + "' does not have a reference"); + } + + Store store = reference.getStore(); + return store.ensureAndGetComponent(reference, type); + } + /** * Get the EntityStatMap component of an entity. * From d93767b8064b26bf1968c86045a3ba23902da8f3 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 17:51:59 -0800 Subject: [PATCH 060/106] ExprEntityScale - add --- .../expressions/ExpressionHandler.java | 2 + .../expressions/entity/ExprEntityScale.java | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityScale.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 6b8478d0..10cedd02 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -14,6 +14,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprActiveSlot; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntitiesInRadius; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHeadRotation; +import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityScale; import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffectDuration; import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffects; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHealth; @@ -95,6 +96,7 @@ public static void register(SkriptRegistration registration) { ExprEntitiesInRadius.register(registration); ExprEntityHeadRotation.register(registration); ExprEntityHealth.register(registration); + ExprEntityScale.register(registration); ExprEntityStat.register(registration); ExprEntityVelocity.register(registration); ExprHeldItem.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityScale.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityScale.java new file mode 100644 index 00000000..f2fc34fd --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/entity/ExprEntityScale.java @@ -0,0 +1,71 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.entity; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.Entity; +import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprEntityScale extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprEntityScale.class, Number.class, + "scale", "entities") + .name("Entity Scale") + .description("Get/set the scale of an entity.", + "**Note**: Changing the scale of a player does not affect their camera.") + .examples("if scale of target entity of player > 1:", + "set scale of {_e} to 10", + "add 0.1 to scale of target entity of player", + "remove 0.5 from scale of {_entity}", + "reset scale of player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(@NotNull Entity entity) { + EntityScaleComponent component = EntityUtils.getComponent(entity, EntityScaleComponent.getComponentType()); + if (component == null) return 1.0f; + + return component.getScale(); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + if (changeWith == null || changeWith.length == 0 || !(changeWith[0] instanceof Number number)) { + return; + } + + for (Entity entity : getOwner().getArray(ctx)) { + EntityScaleComponent component = EntityUtils.ensureAndGetComponent(entity, EntityScaleComponent.getComponentType()); + + float oldValue = component.getScale(); + float changeValue = number.floatValue(); + float newValue = switch (changeMode) { + case ADD -> oldValue + changeValue; + case REMOVE -> oldValue - changeValue; + case RESET -> 1.0f; + default -> changeValue; + }; + + component.setScale(newValue); + } + } + +} From 29daa6ca2f26ecfc73111621ba6dc64d5403db93 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 20:52:27 -0800 Subject: [PATCH 061/106] EntityUtils - checkstyle --- .../skriptdev/skript/api/hytale/utils/EntityUtils.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java index 22ecda48..7ec55b51 100644 --- a/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java +++ b/src/main/java/com/github/skriptdev/skript/api/hytale/utils/EntityUtils.java @@ -126,18 +126,18 @@ public static void setNameplateName(Entity entity, @Nullable String name) { * * @param entity Entity to get component from * @param type Component type to get - * @param EntityStore Type + * @param EntityStore Type * @param Type of returned component * @return Component from entity if available otherwise will create/add a new one */ @SuppressWarnings("unchecked") - public static > @NotNull T ensureAndGetComponent(Entity entity, ComponentType type) { - Ref reference = (Ref) entity.getReference(); + public static > @NotNull T ensureAndGetComponent(Entity entity, ComponentType type) { + Ref reference = (Ref) entity.getReference(); if (reference == null) { throw new IllegalStateException("Entity '" + entity + "' does not have a reference"); } - Store store = reference.getStore(); + Store store = reference.getStore(); return store.ensureAndGetComponent(reference, type); } From e79ac766ba4097a71c1e89c0361f30bd5aa8ad32 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 21:58:08 -0800 Subject: [PATCH 062/106] PlayerMovement - add some expressions and conditions --- .../elements/conditions/ConditionHandler.java | 10 ++- .../player/CondPlayerIsCrouching.java | 54 ------------ .../player/CondPlayerMovementCanFly.java | 84 +++++++++++++++++++ .../player/CondPlayerMovementCrouching.java | 35 ++++++++ .../player/CondPlayerMovementJumping.java | 35 ++++++++ .../player/CondPlayerMovementOnGround.java | 33 ++++++++ .../expressions/ExpressionHandler.java | 8 ++ .../player/ExprPlayerMovementBaseSpeed.java | 77 +++++++++++++++++ .../player/ExprPlayerMovementJumpForce.java | 76 +++++++++++++++++ .../player/ExprPlayerMovementMass.java | 76 +++++++++++++++++ .../ExprPlayerMovementSwimJumpForce.java | 76 +++++++++++++++++ 11 files changed, 508 insertions(+), 56 deletions(-) delete mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCanFly.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCrouching.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementJumping.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementOnGround.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementBaseSpeed.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementJumpForce.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementMass.java create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementSwimJumpForce.java diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java index 6060dea7..eb2ba2d5 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/ConditionHandler.java @@ -9,7 +9,10 @@ import com.github.skriptdev.skript.plugin.elements.conditions.entity.CondEntityIsTamed; import com.github.skriptdev.skript.plugin.elements.conditions.item.CondInventoryCanHold; import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerHasPermission; -import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerIsCrouching; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerMovementCrouching; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerMovementJumping; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerMovementOnGround; +import com.github.skriptdev.skript.plugin.elements.conditions.player.CondPlayerMovementCanFly; public class ConditionHandler { @@ -28,7 +31,10 @@ public static void register(SkriptRegistration registration) { // PLAYER CondPlayerHasPermission.register(registration); - CondPlayerIsCrouching.register(registration); + CondPlayerMovementCanFly.register(registration); + CondPlayerMovementCrouching.register(registration); + CondPlayerMovementJumping.register(registration); + CondPlayerMovementOnGround.register(registration); } } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java deleted file mode 100644 index 6c98e9a6..00000000 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerIsCrouching.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.skriptdev.skript.plugin.elements.conditions.player; - -import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; -import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; -import com.hypixel.hytale.server.core.entity.entities.Player; -import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; -import io.github.syst3ms.skriptparser.lang.Expression; -import io.github.syst3ms.skriptparser.lang.TriggerContext; -import io.github.syst3ms.skriptparser.lang.base.ConditionalExpression; -import io.github.syst3ms.skriptparser.parsing.ParseContext; -import org.jetbrains.annotations.NotNull; - -public class CondPlayerIsCrouching extends ConditionalExpression { - - public static void register(SkriptRegistration reg) { - reg.newExpression(CondPlayerIsCrouching.class, Boolean.class, true, - "%players% (is|are) crouching", - "%players% (isn't|is not|aren't|are not) crouching") - .name("Player is Crouching") - .description("Checks if the player is crouching.") - .examples("if player is crouching:", - "\tmessage \"You are crouching!\"") - .since("1.0.0") - .register(); - } - - private Expression players; - - @SuppressWarnings("unchecked") - @Override - public boolean init(Expression[] expressions, int matchedPattern, @NotNull ParseContext parseContext) { - setNegated(matchedPattern == 1); - this.players = (Expression) expressions[0]; - return true; - } - - @Override - public boolean check(@NotNull TriggerContext ctx) { - return this.players.check(ctx, player -> { - MovementStatesComponent component = EntityUtils.getMovementStatesComponent(player); - if (component == null) return false; - - return component.getMovementStates().crouching; - }, isNegated()); - } - - @Override - public String toString(@NotNull TriggerContext ctx, boolean debug) { - boolean single = this.players.isSingle(); - String s = isNegated() ? (single ? "isn't" : "aren't") : (single ? "is" : "are"); - return this.players.toString(ctx, debug) + " " + s + " crouching"; - } - -} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCanFly.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCanFly.java new file mode 100644 index 00000000..dc1b8332 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCanFly.java @@ -0,0 +1,84 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.protocol.SavedMovementStates; +import com.hypixel.hytale.protocol.packets.player.SetMovementStates; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +public class CondPlayerMovementCanFly extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondPlayerMovementCanFly.class, + "players", ConditionalType.CAN, "fly") + .name("Player Movement - Can Fly") + .description("Check/set whether the player can fly.") + .examples("if player can fly:", + "set player can fly to false", + "reset player can fly") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return getPerformer().check(ctx, player -> { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + assert component != null; + return component.getSettings().canFly; + }, isNegated()); + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Boolean.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + Boolean changeValue = null; + if (changeWith != null && changeWith.length > 0 && changeWith[0] instanceof Boolean bool) { + changeValue = bool; + } + for (Player player : getPerformer().getArray(ctx)) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) continue; + + PlayerRef ref = EntityUtils.getComponent(player, PlayerRef.getComponentType()); + assert ref != null; + + if (changeMode == ChangeMode.RESET) { + component.getSettings().canFly = component.getDefaultSettings().canFly; + } else if (changeValue != null) { + component.getSettings().canFly = changeValue; + + } + + if (!component.getSettings().canFly) { + // Stop the player from actually flying + MovementStatesComponent movementStates = EntityUtils.getMovementStatesComponent(player); + assert movementStates != null; + movementStates.getMovementStates().flying = false; + ref.getPacketHandler().writeNoCache(new SetMovementStates(new SavedMovementStates(false))); + + } + + component.update(ref.getPacketHandler()); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCrouching.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCrouching.java new file mode 100644 index 00000000..b70ba591 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementCrouching.java @@ -0,0 +1,35 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import org.jetbrains.annotations.NotNull; + +public class CondPlayerMovementCrouching extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondPlayerMovementCrouching.class, + "players", ConditionalType.BE, "crouching") + .name("Player Movement - Crouching") + .description("Checks if the player is crouching.") + .examples("if player is crouching:", + "\tmessage \"You are crouching!\"") + .since("1.0.0") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.getPerformer().check(ctx, player -> { + MovementStatesComponent component = EntityUtils.getMovementStatesComponent(player); + + assert component != null; + return component.getMovementStates().crouching; + }, isNegated()); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementJumping.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementJumping.java new file mode 100644 index 00000000..2376b84f --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementJumping.java @@ -0,0 +1,35 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import org.jetbrains.annotations.NotNull; + +public class CondPlayerMovementJumping extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondPlayerMovementJumping.class, + "players", ConditionalType.BE, "jumping") + .name("Player Movement - Jumping") + .description("Checks if the player is jumping.") + .examples("if player is jumping:", + "\tmessage \"You are jumping!\"") + .since("1.0.0") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return this.getPerformer().check(ctx, player -> { + MovementStatesComponent component = EntityUtils.getMovementStatesComponent(player); + + assert component != null; + return component.getMovementStates().jumping; + }, isNegated()); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementOnGround.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementOnGround.java new file mode 100644 index 00000000..51b9e38e --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/conditions/player/CondPlayerMovementOnGround.java @@ -0,0 +1,33 @@ +package com.github.skriptdev.skript.plugin.elements.conditions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.movement.MovementStatesComponent; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.ConditionalType; +import io.github.syst3ms.skriptparser.lang.properties.PropertyConditional; +import org.jetbrains.annotations.NotNull; + +public class CondPlayerMovementOnGround extends PropertyConditional { + + public static void register(SkriptRegistration reg) { + reg.newPropertyConditional(CondPlayerMovementOnGround.class, + "players", ConditionalType.BE, "on ground") + .name("Player Movement - On Ground") + .description("Check if a player is on the ground.") + .description("if player is on ground:") + .since("INSERT VERSION") + .register(); + } + + @Override + public boolean check(@NotNull TriggerContext ctx) { + return getPerformer().check(ctx, player -> { + MovementStatesComponent component = EntityUtils.getMovementStatesComponent(player); + assert component != null; + return component.getMovementStates().onGround; + }, isNegated()); + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java index 10cedd02..5508807a 100644 --- a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/ExpressionHandler.java @@ -13,6 +13,7 @@ import com.github.skriptdev.skript.plugin.elements.expressions.block.ExprTargetBlockOfPlayer; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprActiveSlot; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntitiesInRadius; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerMovementBaseSpeed; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityHeadRotation; import com.github.skriptdev.skript.plugin.elements.expressions.entity.ExprEntityScale; import com.github.skriptdev.skript.plugin.elements.expressions.entityeffect.ExprActiveEntityEffectDuration; @@ -59,6 +60,9 @@ import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprChatMessage; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprGameMode; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerClientViewRadius; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerMovementJumpForce; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerMovementMass; +import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerMovementSwimJumpForce; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerPermissionGroup; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerPermissions; import com.github.skriptdev.skript.plugin.elements.expressions.player.ExprPlayerRespawnLocations; @@ -147,6 +151,10 @@ public static void register(SkriptRegistration registration) { ExprChatMessage.register(registration); ExprGameMode.register(registration); ExprPlayerClientViewRadius.register(registration); + ExprPlayerMovementBaseSpeed.register(registration); + ExprPlayerMovementJumpForce.register(registration); + ExprPlayerMovementMass.register(registration); + ExprPlayerMovementSwimJumpForce.register(registration); ExprPlayerPermissionGroup.register(registration); ExprPlayerPermissions.register(registration); ExprPlayerRespawnLocations.register(registration); diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementBaseSpeed.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementBaseSpeed.java new file mode 100644 index 00000000..3163806c --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementBaseSpeed.java @@ -0,0 +1,77 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprPlayerMovementBaseSpeed extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprPlayerMovementBaseSpeed.class, Number.class, + "base speed", "players") + .name("Player Movement - Base Speed") + .description("Get/change the base speed of a player.", + "This is not persistent. Default = 5.5") + .examples("set base speed of player to 10", + "reset base speed of player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(@NotNull Player player) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) return 0.0f; + + return component.getSettings().baseSpeed; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + Number number = null; + if (changeWith != null && changeWith.length > 0 && changeWith[0] instanceof Number num) { + number = num; + } + + for (Player player : getOwner().getArray(ctx)) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) continue; + + if (changeMode == ChangeMode.RESET) { + component.getSettings().baseSpeed = component.getDefaultSettings().baseSpeed; + } else if (number != null) { + + float previousValue = component.getSettings().baseSpeed; + float changeValue = number.floatValue(); + + component.getSettings().baseSpeed = switch (changeMode) { + case ADD -> previousValue + changeValue; + case REMOVE -> previousValue - changeValue; + default -> changeValue; + }; + } + PlayerRef ref = EntityUtils.getComponent(player, PlayerRef.getComponentType()); + assert ref != null; + component.update(ref.getPacketHandler()); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementJumpForce.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementJumpForce.java new file mode 100644 index 00000000..801cba16 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementJumpForce.java @@ -0,0 +1,76 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprPlayerMovementJumpForce extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprPlayerMovementJumpForce.class, Number.class, + "jump force", "players") + .name("Player Movement - Jump Force") + .description("Get/change the jump force of a player.", + "This is not persistent. Default = 11.8") + .examples("set jump force of player to 10", + "reset jump force of player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(@NotNull Player player) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) return 0.0f; + + return component.getSettings().jumpForce; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + Number number = null; + if (changeWith != null && changeWith.length > 0 && changeWith[0] instanceof Number num) { + number = num; + } + for (Player player : getOwner().getArray(ctx)) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) continue; + + if (changeMode == ChangeMode.RESET) { + component.getSettings().jumpForce = component.getDefaultSettings().jumpForce; + } else if (number != null) { + + float previousValue = component.getSettings().jumpForce; + float changeValue = number.floatValue(); + + component.getSettings().jumpForce = switch (changeMode) { + case ADD -> previousValue + changeValue; + case REMOVE -> previousValue - changeValue; + default -> changeValue; + }; + } + PlayerRef ref = EntityUtils.getComponent(player, PlayerRef.getComponentType()); + assert ref != null; + component.update(ref.getPacketHandler()); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementMass.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementMass.java new file mode 100644 index 00000000..241054ed --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementMass.java @@ -0,0 +1,76 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprPlayerMovementMass extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprPlayerMovementMass.class, Number.class, + "mass", "players") + .name("Player Movement - Mass") + .description("Get/change the mass of a player.", + "This is not persistent.") + .examples("set mass of player to 10", + "reset mass of player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(@NotNull Player player) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) return 0.0f; + + return component.getSettings().mass; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + Number number = null; + if (changeWith != null && changeWith.length > 0 && changeWith[0] instanceof Number num) { + number = num; + } + for (Player player : getOwner().getArray(ctx)) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) continue; + + if (changeMode == ChangeMode.RESET) { + component.getSettings().mass = component.getDefaultSettings().mass; + } else if (number != null) { + + float previousValue = component.getSettings().mass; + float changeValue = number.floatValue(); + + component.getSettings().mass = switch (changeMode) { + case ADD -> previousValue + changeValue; + case REMOVE -> previousValue - changeValue; + default -> changeValue; + }; + } + PlayerRef ref = EntityUtils.getComponent(player, PlayerRef.getComponentType()); + assert ref != null; + component.update(ref.getPacketHandler()); + } + } + +} diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementSwimJumpForce.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementSwimJumpForce.java new file mode 100644 index 00000000..cc9f9195 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/expressions/player/ExprPlayerMovementSwimJumpForce.java @@ -0,0 +1,76 @@ +package com.github.skriptdev.skript.plugin.elements.expressions.player; + +import com.github.skriptdev.skript.api.hytale.utils.EntityUtils; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.server.core.entity.entities.Player; +import com.hypixel.hytale.server.core.entity.entities.player.movement.MovementManager; +import com.hypixel.hytale.server.core.universe.PlayerRef; +import io.github.syst3ms.skriptparser.lang.TriggerContext; +import io.github.syst3ms.skriptparser.lang.properties.PropertyExpression; +import io.github.syst3ms.skriptparser.types.changers.ChangeMode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class ExprPlayerMovementSwimJumpForce extends PropertyExpression { + + public static void register(SkriptRegistration reg) { + reg.newPropertyExpression(ExprPlayerMovementSwimJumpForce.class, Number.class, + "swim jump force", "players") + .name("Player Movement - Swim Jump Force") + .description("Get/change the swim jump force of a player.", + "This is not persistent. Default = 10.0") + .examples("set swim jump force of player to 5", + "reset swim jump force of player") + .since("INSERT VERSION") + .register(); + } + + @Override + public @Nullable Number getProperty(@NotNull Player player) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) return 0.0f; + + return component.getSettings().jumpForce; + } + + @Override + public Optional[]> acceptsChange(@NotNull ChangeMode mode) { + if (mode == ChangeMode.SET || mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.RESET) { + return Optional.of(new Class[]{Number.class}); + } + return Optional.empty(); + } + + @SuppressWarnings("ConstantValue") + @Override + public void change(@NotNull TriggerContext ctx, @NotNull ChangeMode changeMode, Object @NotNull [] changeWith) { + Number number = null; + if (changeWith != null && changeWith.length > 0 && changeWith[0] instanceof Number num) { + number = num; + } + for (Player player : getOwner().getArray(ctx)) { + MovementManager component = EntityUtils.getComponent(player, MovementManager.getComponentType()); + if (component == null) continue; + + if (changeMode == ChangeMode.RESET) { + component.getSettings().swimJumpForce = component.getDefaultSettings().swimJumpForce; + } else if (number != null) { + + float previousValue = component.getSettings().swimJumpForce; + float changeValue = number.floatValue(); + + component.getSettings().swimJumpForce = switch (changeMode) { + case ADD -> previousValue + changeValue; + case REMOVE -> previousValue - changeValue; + default -> changeValue; + }; + } + PlayerRef ref = EntityUtils.getComponent(player, PlayerRef.getComponentType()); + assert ref != null; + component.update(ref.getPacketHandler()); + } + } + +} From c49a513c71ef64bda7f305250be8794fffeb65b0 Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 17 Feb 2026 23:17:23 -0800 Subject: [PATCH 063/106] EvtAtWorldTime - add new event --- .../plugin/elements/events/EventHandler.java | 4 + .../elements/events/world/EvtAtWorldTime.java | 122 ++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/main/java/com/github/skriptdev/skript/plugin/elements/events/world/EvtAtWorldTime.java 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 1cbc3469..8501ab24 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 @@ -33,6 +33,7 @@ import com.github.skriptdev.skript.plugin.elements.events.server.EvtBoot; import com.github.skriptdev.skript.plugin.elements.events.server.EvtShutdown; import com.github.skriptdev.skript.plugin.elements.events.skript.EvtLoad; +import com.github.skriptdev.skript.plugin.elements.events.world.EvtAtWorldTime; 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; @@ -75,6 +76,9 @@ public static void register(SkriptRegistration registration) { // SKRIPT EvtLoad.register(registration); + // WORLD + EvtAtWorldTime.register(registration); + // CONTEXT registerGlobalContexts(registration); } diff --git a/src/main/java/com/github/skriptdev/skript/plugin/elements/events/world/EvtAtWorldTime.java b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/world/EvtAtWorldTime.java new file mode 100644 index 00000000..0c0275a0 --- /dev/null +++ b/src/main/java/com/github/skriptdev/skript/plugin/elements/events/world/EvtAtWorldTime.java @@ -0,0 +1,122 @@ +package com.github.skriptdev.skript.plugin.elements.events.world; + +import com.github.skriptdev.skript.api.skript.event.WorldContext; +import com.github.skriptdev.skript.api.skript.registration.SkriptRegistration; +import com.hypixel.hytale.component.Store; +import com.hypixel.hytale.server.core.HytaleServer; +import com.hypixel.hytale.server.core.modules.time.WorldTimeResource; +import com.hypixel.hytale.server.core.universe.Universe; +import com.hypixel.hytale.server.core.universe.world.World; +import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; +import io.github.syst3ms.skriptparser.lang.Expression; +import io.github.syst3ms.skriptparser.lang.Literal; +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.VariableString; +import io.github.syst3ms.skriptparser.lang.event.SkriptEvent; +import io.github.syst3ms.skriptparser.lang.event.StartOnLoadEvent; +import io.github.syst3ms.skriptparser.log.ErrorType; +import io.github.syst3ms.skriptparser.parsing.ParseContext; +import io.github.syst3ms.skriptparser.util.Time; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class EvtAtWorldTime extends SkriptEvent implements StartOnLoadEvent { + + public static void register(SkriptRegistration reg) { + reg.newEvent(EvtAtWorldTime.class, + "*at %*time% [world time] in world %*string%") + .setHandledContexts(WorldTimeContext.class) + .name("At World Time") + .description("Triggered every ingame day at a given time.", + "Internally this is handled by the minute.") + .examples("at 10:00 pm in world \"default\":", + "\tkill all players in event-world") + .since("INSERT VERSION") + .register(); + } + + private Literal