diff --git a/build.gradle.kts b/build.gradle.kts index 29c3f74f..74f434e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,7 @@ +import groovy.json.JsonBuilder +import groovy.json.JsonSlurper +import java.util.Locale + plugins { java id("org.leavesmc.leavesweight.patcher") version "2.0.0-SNAPSHOT" @@ -73,4 +77,189 @@ paperweight { outputDir = file("paper-api") } } +} + +val patchTasks = listOf( + "::applyPaperApiPatches" to "::rebuildPaperApiPatches", + "::applyPaperSingleFilePatches" to "::rebuildPaperSingleFilePatches", + ":leaves-server:applyMinecraftPatches" to ":leaves-server:rebuildMinecraftPatches", + ":leaves-server:applyPaperServerPatches" to ":leaves-server:rebuildPaperServerPatches" +) + +val statusFile = layout.buildDirectory.file("patchTaskStatus.json").get().asFile +fun readTaskStatus(): Map { + if (!statusFile.exists()) { + return emptyMap() + } + try { + @Suppress("UNCHECKED_CAST") + return JsonSlurper().parse(statusFile) as Map + } catch (_: Exception) { + return emptyMap() + } +} + +fun writeTaskStatus(status: Map) { + statusFile.writeText(JsonBuilder(status).toPrettyString()) +} + +fun executeTask(taskPath: String): Boolean { + val parts = taskPath.split(":") + val projectPath = if (parts.size > 2) parts.subList(0, parts.size - 1).joinToString(":") else ":" + val taskName = parts.last() + val fullTaskPath = if (projectPath == ":" || projectPath.isEmpty()) taskName else "$projectPath:$taskName" + + try { + val gradlew = if (System.getProperty("os.name").lowercase(Locale.getDefault()).contains("windows")) + "${project.rootDir}\\gradlew.bat" + else + "${project.rootDir}/gradlew" + + val processBuilder = ProcessBuilder( + gradlew, + fullTaskPath, + // "--console=verbose", + // "--info" + ) + processBuilder.directory(project.rootDir) + + val env = processBuilder.environment() + env["TERM"] = "xterm-256color" + + val process = processBuilder.start() + + val outputThread = Thread { + process.inputStream.bufferedReader().use { reader -> + var line: String? + while (reader.readLine().also { line = it } != null) { + println(line) + } + } + } + + val errorThread = Thread { + process.errorStream.bufferedReader().use { reader -> + var line: String? + while (reader.readLine().also { line = it } != null) { + System.err.println(line) + } + } + } + + outputThread.start() + errorThread.start() + + val exitCode = process.waitFor() + + outputThread.join() + errorThread.join() + + if (exitCode != 0) { + throw GradleException("Task $fullTaskPath FAILED,$exitCode") + } + + return true + } catch (e: Exception) { + println("⚠️ Task FAILED: $taskPath - ${e.message}") + throw e + } +} + +tasks.register("applyAllPatchesSequentially") { + group = "leaves" + description = "Apply all patches sequentially, run rebuild after success, wait for manual fix if failed" + + doFirst { + println("πŸš€ Starting sequential patch application...") + } + + doLast { + val taskStatus = readTaskStatus().toMutableMap() + var currentIndex = 0 + + while (currentIndex < patchTasks.size) { + val (applyTaskPath, rebuildTaskPath) = patchTasks[currentIndex] + + if (taskStatus[applyTaskPath] == "COMPLETED") { + println("⏩ Skipping completed task: $applyTaskPath") + currentIndex++ + continue + } + + if (taskStatus[applyTaskPath] == "FAILED") { + println("⚠️ Detected previous failure of $applyTaskPath, running $rebuildTaskPath first") + try { + executeTask(rebuildTaskPath) + println("βœ… $rebuildTaskPath completed successfully") + } catch (e: Exception) { + taskStatus[applyTaskPath] = "FAILED" + writeTaskStatus(taskStatus) + throw GradleException("$rebuildTaskPath failed: ${e.message}", e) + } + } + + println("πŸ”„ Running $applyTaskPath...") + try { + executeTask(applyTaskPath) + println("βœ… $applyTaskPath completed successfully") + + println("πŸ”„ Running $rebuildTaskPath after successful apply...") + try { + executeTask(rebuildTaskPath) + println("βœ… $rebuildTaskPath completed successfully") + } catch (e: Exception) { + println("⚠️ $rebuildTaskPath failed, but continuing with next tasks: ${e.message}") + } + + taskStatus[applyTaskPath] = "COMPLETED" + writeTaskStatus(taskStatus) + currentIndex++ + } catch (e: Exception) { + taskStatus[applyTaskPath] = "FAILED" + writeTaskStatus(taskStatus) + throw GradleException("$applyTaskPath failed, please fix the issues and run this task again", e) + } + } + + if (currentIndex >= patchTasks.size) { + tasks.named("resetPatchTaskStatus").get().actions.forEach { it.execute(tasks.named("resetPatchTaskStatus").get()) } + println("✨ All patch tasks completed successfully!") + } + } +} + +tasks.register("resetPatchTaskStatus") { + group = "leaves" + description = "Reset the status of all patch tasks" + + doLast { + if (statusFile.exists()) { + statusFile.delete() + println("🧹 All patch task statuses have been reset") + } + } +} + +tasks.register("showPatchTaskStatus") { + group = "leaves" + description = "Show the current status of all patch tasks" + + doLast { + val status = readTaskStatus() + println("πŸ“Š Patch Task Status:") + + if (status.isEmpty()) { + println(" No tasks executed yet or status has been reset") + } else { + patchTasks.forEach { (applyTask, _) -> + val taskStatus = status[applyTask] ?: "PENDING" + val statusIcon = when (taskStatus) { + "COMPLETED" -> "βœ…" + "FAILED" -> "❌" + else -> "⏳" + } + println(" $statusIcon $applyTask: $taskStatus") + } + } + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c0c14147..a6c1cb3f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ group=org.leavesmc.leaves version=1.21.4-R0.1-SNAPSHOT mcVersion=1.21.4 -paperRef=6ea42025a49f232f47861c6ca943b0fc66b7effe +paperRef=bb1beda67bfd94632815acc8ac5d68f5a8e3e410 preVersion=true org.gradle.jvmargs=-Xmx2G org.gradle.caching=true diff --git a/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch b/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch index f9af21aa..1e8509b0 100644 --- a/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch +++ b/leaves-api/paper-patches/features/0003-Add-fakeplayer-api.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add fakeplayer api diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index a371da9d1d9d1b6cb266db58b9f74a09dc858d2c..a5b316601b433544b604455dc1c8079bf478b43e 100644 +index db47fe6560e1b0c737889698cf52c5c3fb06185f..699c71f6749352aaf8cb82a51a82f86c3a8d3abc 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2995,4 +2995,15 @@ public final class Bukkit { +@@ -2998,4 +2998,15 @@ public final class Bukkit { public static void restart() { server.restart(); } @@ -25,7 +25,7 @@ index a371da9d1d9d1b6cb266db58b9f74a09dc858d2c..a5b316601b433544b604455dc1c8079b + // Leaves end - Bot API } diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index d7ebfcfc6a41b7e94a041f7faf3c2d8090fab3c4..cc7c894572652b86b0069325e28f8e73f7a66f01 100644 +index 2eb72a31cc36a41694654da15be5a1f4983aa1bd..68c45204d1d6b8c33ab47a2414158fc273873414 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -66,6 +66,7 @@ import org.jetbrains.annotations.ApiStatus; @@ -36,7 +36,7 @@ index d7ebfcfc6a41b7e94a041f7faf3c2d8090fab3c4..cc7c894572652b86b0069325e28f8e73 /** * Represents a server implementation. -@@ -2698,4 +2699,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi +@@ -2696,4 +2697,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi */ void allowPausing(@NotNull org.bukkit.plugin.Plugin plugin, boolean value); // Paper end - API to check if the server is sleeping diff --git a/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch b/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch index d7a3080a..57d0f1b6 100644 --- a/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch +++ b/leaves-api/paper-patches/features/0005-Replay-Mod-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Replay Mod API diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index a5b316601b433544b604455dc1c8079bf478b43e..b73c009ece3af3daf8251adb7502ee9c8ad103c4 100644 +index 699c71f6749352aaf8cb82a51a82f86c3a8d3abc..8018a4e88c9392fd68c3bd03f13e7443e6a3966d 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -3006,4 +3006,10 @@ public final class Bukkit { +@@ -3009,4 +3009,10 @@ public final class Bukkit { return server.getBotManager(); } // Leaves end - Bot API @@ -20,7 +20,7 @@ index a5b316601b433544b604455dc1c8079bf478b43e..b73c009ece3af3daf8251adb7502ee9c + // Leaves end - Photographer API } diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index cc7c894572652b86b0069325e28f8e73f7a66f01..632d510f4ae42c5bbb00320b517659c857ccded7 100644 +index 68c45204d1d6b8c33ab47a2414158fc273873414..f1519b243b676017185308145fe972702073c222 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -67,6 +67,7 @@ import org.jetbrains.annotations.Contract; @@ -31,7 +31,7 @@ index cc7c894572652b86b0069325e28f8e73f7a66f01..632d510f4ae42c5bbb00320b517659c8 /** * Represents a server implementation. -@@ -2708,4 +2709,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi +@@ -2706,4 +2707,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi */ @NotNull BotManager getBotManager(); // Leaves end - Bot API diff --git a/leaves-api/paper-patches/features/0006-Bytebuf-API.patch b/leaves-api/paper-patches/features/0006-Bytebuf-API.patch index 7800a561..c62401ad 100644 --- a/leaves-api/paper-patches/features/0006-Bytebuf-API.patch +++ b/leaves-api/paper-patches/features/0006-Bytebuf-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Bytebuf API diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index b73c009ece3af3daf8251adb7502ee9c8ad103c4..dfc3da5eef1b8c11c5deef3853e47a7f685f0d46 100644 +index 8018a4e88c9392fd68c3bd03f13e7443e6a3966d..1d347aefb56dcd5c9bcc21f2d8bc4a884ba488fa 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -3012,4 +3012,10 @@ public final class Bukkit { +@@ -3015,4 +3015,10 @@ public final class Bukkit { return server.getPhotographerManager(); } // Leaves end - Photographer API @@ -20,10 +20,10 @@ index b73c009ece3af3daf8251adb7502ee9c8ad103c4..dfc3da5eef1b8c11c5deef3853e47a7f + // Leaves end - Bytebuf API } diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index 632d510f4ae42c5bbb00320b517659c857ccded7..ef91d117defb23b27a8ca01551753716dc24d184 100644 +index f1519b243b676017185308145fe972702073c222..a3f895426062c97cba925c845404dbfe732e6cc8 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java -@@ -2713,4 +2713,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi +@@ -2711,4 +2711,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi // Leaves start - Photographer API @NotNull PhotographerManager getPhotographerManager(); // Leaves end - Photographer API @@ -33,7 +33,7 @@ index 632d510f4ae42c5bbb00320b517659c857ccded7..ef91d117defb23b27a8ca01551753716 + // Leaves end - Bytebuf API } diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java -index 69f982d1dbffa256b65c32292805320452a9842f..9041dbf1aaa0a8f548b3122817cf6ef3b9c56ba2 100644 +index 494dca2ee48a03953d47050b178496df12bc48c5..b429eb9a0326460a8841a1cfdfd6cd13c02ec2b7 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -3853,6 +3853,12 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/ServerBot.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/ServerBot.java index 1f72bb39..c73f9032 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/ServerBot.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/ServerBot.java @@ -78,7 +78,7 @@ public class ServerBot extends ServerPlayer { public UUID createPlayer; private final int tracingRange; - private final ServerStatsCounter stats; + private final BotStatsCounter stats; private final BotInventoryContainer container; public int notSleepTicks; diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/configs/TickTypeConfig.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/configs/TickTypeConfig.java index 94c8ddde..5ed4cfb5 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/configs/TickTypeConfig.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/configs/TickTypeConfig.java @@ -13,7 +13,7 @@ import java.util.List; public class TickTypeConfig extends AbstractBotConfig { private static final String NAME = "tick_type"; - private static final CommandArgumentType TICK_TYPE_ARGUMENT = CommandArgumentType.of(ServerBot.TickType.class, (string -> ServerBot.TickType.valueOf(string.toUpperCase()))); + private static final CommandArgumentType TICK_TYPE_ARGUMENT = CommandArgumentType.ofEnum(ServerBot.TickType.class); private ServerBot.TickType value; diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java b/leaves-server/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java index 60541ada..b39f6f46 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/command/CommandArgumentType.java @@ -7,10 +7,10 @@ import java.util.function.Function; public abstract class CommandArgumentType { + public static final CommandArgumentType STRING = CommandArgumentType.string(); public static final CommandArgumentType INTEGER = CommandArgumentType.of(Integer.class, Integer::parseInt); public static final CommandArgumentType DOUBLE = CommandArgumentType.of(Double.class, Double::parseDouble); public static final CommandArgumentType FLOAT = CommandArgumentType.of(Float.class, Float::parseFloat); - public static final CommandArgumentType STRING = CommandArgumentType.of(String.class, (arg) -> arg); public static final CommandArgumentType BOOLEAN = CommandArgumentType.of(Boolean.class, Boolean::parseBoolean); private final Class type; @@ -34,6 +34,23 @@ public abstract class CommandArgumentType { }; } + @NotNull + @Contract(value = "_ -> new", pure = true) + public static > CommandArgumentType ofEnum(Class type) { + return of(type, (string -> Enum.valueOf(type, string.toUpperCase()))); + } + + @NotNull + @Contract(value = " -> new", pure = true) + private static CommandArgumentType string() { + return new CommandArgumentType<>(String.class) { + @Override + public String parse(@NotNull String arg) { + return arg; + } + }; + } + public Class getType() { return type; }