From 47abdd558552468f4f23be6d72c9acf2c525e586 Mon Sep 17 00:00:00 2001 From: kiip1 <25848425+kiipy@users.noreply.github.com> Date: Fri, 22 Apr 2022 15:22:03 +0200 Subject: [PATCH 1/2] Continue to work on ui --- .gitignore | 44 ++++ src/main/java/net/minestom/ui/MinestomUI.java | 14 +- src/main/java/net/minestom/ui/ServerUI.java | 249 +++++++++++++----- .../java/net/minestom/ui/IntegrationTest.java | 7 + 4 files changed, 240 insertions(+), 74 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83f468 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +/imgui.ini +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store +/.idea/ \ No newline at end of file diff --git a/src/main/java/net/minestom/ui/MinestomUI.java b/src/main/java/net/minestom/ui/MinestomUI.java index a8c0ab7..a4a80f0 100644 --- a/src/main/java/net/minestom/ui/MinestomUI.java +++ b/src/main/java/net/minestom/ui/MinestomUI.java @@ -2,8 +2,7 @@ import imgui.app.Application; import net.minestom.server.MinecraftServer; -import net.minestom.server.snapshot.ServerSnapshot; -import net.minestom.server.timer.TaskSchedule; +import net.minestom.server.ServerProcess; import org.lwjgl.glfw.GLFW; import java.util.concurrent.atomic.AtomicBoolean; @@ -15,19 +14,14 @@ public static void launch() { if (!initialized.compareAndSet(false, true)) { throw new IllegalStateException("MinestomUI is already initialized"); } - var process = MinecraftServer.process(); + ServerProcess process = MinecraftServer.process(); if (process == null) { throw new IllegalStateException("Minestom server must be initialized before launching the UI"); } ServerUI ui = new ServerUI(); new Thread(() -> Application.launch(ui)).start(); - - var scheduler = process.scheduler(); - // Retrieve up-to-date server info every tick - scheduler.scheduleTask(() -> { - ui.snapshotReference = ServerSnapshot.update(); - }, TaskSchedule.nextTick(), TaskSchedule.nextTick()); + // Close the ui when the server is closed - scheduler.buildShutdownTask(() -> GLFW.glfwSetWindowShouldClose(ui.getHandle(), true)); + process.scheduler().buildShutdownTask(() -> GLFW.glfwSetWindowShouldClose(ui.getHandle(), true)); } } diff --git a/src/main/java/net/minestom/ui/ServerUI.java b/src/main/java/net/minestom/ui/ServerUI.java index 8b1ff84..2cd5a13 100644 --- a/src/main/java/net/minestom/ui/ServerUI.java +++ b/src/main/java/net/minestom/ui/ServerUI.java @@ -1,100 +1,221 @@ package net.minestom.ui; -import imgui.ImGui; import imgui.app.Application; +import imgui.app.Configuration; import imgui.type.ImDouble; import imgui.type.ImFloat; import imgui.type.ImInt; import imgui.type.ImString; -import net.minestom.server.snapshot.EntitySnapshot; -import net.minestom.server.snapshot.InstanceSnapshot; -import net.minestom.server.snapshot.ServerSnapshot; +import net.minestom.server.MinecraftServer; +import net.minestom.server.ServerProcess; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.Entity; +import net.minestom.server.instance.Chunk; +import net.minestom.server.instance.Instance; import net.minestom.server.world.DimensionType; +import org.jetbrains.annotations.Nullable; -/* TODO: Input reading and relay - * This class needs to be able to take input data and relay changes back to the server. - */ -final class ServerUI extends Application { +import java.util.Collection; +import java.util.UUID; - volatile ServerSnapshot snapshotReference; +import static imgui.ImGui.*; +import static imgui.flag.ImGuiInputTextFlags.EnterReturnsTrue; +import static imgui.flag.ImGuiInputTextFlags.ReadOnly; +final class ServerUI extends Application { + private final ServerProcess process = MinecraftServer.process(); + + @Override + protected void configure(Configuration config) { + config.setTitle("MinestomUI"); + } + @Override public void process() { - var snapshot = this.snapshotReference; - if (snapshot == null) return; - - ImGui.begin("Instances"); - snapshot.instances().forEach(instance -> { - DimensionType dimension = instance.dimensionType(); - if (ImGui.treeNode(dimension.toString())) { - instance(instance, snapshot); - ImGui.treePop(); + begin("Instances"); + for (Instance instance : process.instance().getInstances()) { + if (treeNode(instance.toString())) { + instance(instance); + treePop(); } - }); - ImGui.end(); + } + end(); + + begin("Console"); + console(); + end(); } - private void instance(InstanceSnapshot instance, ServerSnapshot snapshot) { + private void instance(@Nullable Instance instance) { + if (instance == null) return; + // World age - ImGui.inputInt("World age", new ImInt(Math.toIntExact(instance.worldAge()))); + inputInt("World age", new ImInt(Math.toIntExact(instance.getWorldAge())), 0, 0, ReadOnly); + + // Time + ImInt time = new ImInt(Math.toIntExact(instance.getTime())); + if (inputInt("Time", time, 100, 1000, EnterReturnsTrue)) { + instance.setTime(time.get()); + } // Dimension type - dimensionType(instance.dimensionType()); - + dimensionType(instance.getDimensionType()); + + // Chunks + if (instance.getChunks().size() > 0 && treeNode("Chunks")) { + instance.getChunks().forEach(this::chunk); + treePop(); + } + // Entities - instance.entities().forEach(entity -> entity(entity, snapshot)); + if (instance.getEntities().size() > 0 && treeNode("Entities")) { + instance.getEntities().forEach(this::entity); + treePop(); + } + } + + private void chunk(@Nullable Chunk chunk) { + if (chunk == null) return; + + if (treeNode(chunk.toString())) { + // Location + inputInt("chunkX", new ImInt(chunk.getChunkX()), 0, 0, ReadOnly); + inputInt("chunkZ", new ImInt(chunk.getChunkZ()), 0, 0, ReadOnly); + + // Entities + Collection entities = chunk.getInstance().getChunkEntities(chunk); + if (entities.size() > 0 && treeNode("Entities")) { + entities.forEach(this::entity); + treePop(); + } + + // Instance + if (treeNode("Instance")) { + instance(chunk.getInstance()); + treePop(); + } + + treePop(); + } } - private void entity(EntitySnapshot entity, ServerSnapshot snapshot) { - if (ImGui.treeNode(entity.id() + " " + entity.type().name())) { - + private void entity(@Nullable Entity entity) { + if (entity == null) return; + + if (treeNode(entity.getEntityId() + " " + entity.getEntityType().name())) { + // UUID + ImString uuid = new ImString(entity.getUuid().toString()); + if (inputText("Uuid", uuid, EnterReturnsTrue)) { + try { + entity.setUuid(UUID.fromString(uuid.get())); + } catch (Exception ignored) {} + } + // Position - if (ImGui.treeNode("Position")) { - ImGui.inputDouble("x", new ImDouble(entity.position().x())); - ImGui.inputDouble("y", new ImDouble(entity.position().y())); - ImGui.inputDouble("z", new ImDouble(entity.position().z())); - ImGui.inputDouble("pitch", new ImDouble(entity.position().pitch())); - ImGui.inputDouble("yaw", new ImDouble(entity.position().yaw())); - ImGui.treePop(); + { + ImDouble x = new ImDouble(entity.getPosition().x()); + ImDouble y = new ImDouble(entity.getPosition().y()); + ImDouble z = new ImDouble(entity.getPosition().z()); + ImFloat yaw = new ImFloat(entity.getPosition().yaw()); + ImFloat pitch = new ImFloat(entity.getPosition().pitch()); + if (treeNode("Position")) { + int changed = inputDouble("x", x, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("y", y, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("z", z, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputFloat("yaw", yaw, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputFloat("pitch", pitch, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + + if (changed > 0) + entity.teleport(new Pos(x.get(), y.get(), z.get(), yaw.get(), pitch.get())); + + treePop(); + } + } + + // Velocity + { + ImDouble x = new ImDouble(entity.getVelocity().x()); + ImDouble y = new ImDouble(entity.getVelocity().y()); + ImDouble z = new ImDouble(entity.getVelocity().z()); + if (treeNode("Velocity")) { + int changed = inputDouble("x", x, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("y", y, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("z", z, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + + if (changed > 0) + entity.setVelocity(new Vec(x.get(), y.get(), z.get())); + + treePop(); + } } // Instance - if (ImGui.treeNode("Instance")) { - instance(entity.instance(), snapshot); - ImGui.treePop(); + if (treeNode("Instance")) { + instance(entity.getInstance()); + treePop(); + } + + // Chunk + chunk(entity.getChunk()); + + // Viewers + if (entity.getViewers().size() > 0 && treeNode("Viewers")) { + entity.getViewers().forEach(this::entity); + treePop(); } // Passengers - if (entity.passengers().size() > 0 && ImGui.treeNode("Passengers")) { - entity.passengers().forEach(passenger -> entity(passenger, snapshot)); + if (entity.getPassengers().size() > 0 && treeNode("Passengers")) { + entity.getPassengers().forEach(this::entity); + treePop(); } - - ImGui.treePop(); + + // Vehicle + Entity vehicle = entity.getVehicle(); + if (vehicle != null) { + entity(vehicle); + } + + treePop(); } } - private void dimensionType(DimensionType dimension) { - if (ImGui.treeNode("Dimension type")) { - ImGui.checkbox("ultrawarm", dimension.isUltrawarm()); - ImGui.checkbox("natural", dimension.isNatural()); - ImGui.checkbox("piglin safe", dimension.isPiglinSafe()); - ImGui.checkbox("respawn anchor safe", dimension.isRespawnAnchorSafe()); - ImGui.checkbox("bed safe", dimension.isBedSafe()); - ImGui.checkbox("raid capable", dimension.isRaidCapable()); - ImGui.checkbox("skylight", dimension.isSkylightEnabled()); - ImGui.checkbox("ceiling", dimension.isCeilingEnabled()); - if (ImGui.checkbox("fixed time", dimension.getFixedTime() != null)) { - Long boxedFixedTime = dimension.getFixedTime(); - // TODO: Properly support longs - ImInt fixedTime = new ImInt(Math.toIntExact(boxedFixedTime == null ? 0 : boxedFixedTime)); - ImGui.inputInt("fixed time", fixedTime); - } - ImGui.inputFloat("ambient light", new ImFloat(dimension.getAmbientLight())); - ImGui.inputInt("height", new ImInt(dimension.getHeight())); - ImGui.inputInt("min y", new ImInt(dimension.getMinY())); - ImGui.inputInt("logical height", new ImInt(dimension.getLogicalHeight())); - ImGui.inputText("infiniburn", new ImString(dimension.getInfiniburn().toString())); - ImGui.treePop(); + private void dimensionType(@Nullable DimensionType dimension) { + if (dimension == null) return; + + if (treeNode("Dimension type")) { + checkboxFlags("ultrawarm", bool(dimension.isUltrawarm()), ReadOnly); + checkboxFlags("natural", bool(dimension.isNatural()), ReadOnly); + checkboxFlags("piglin safe", bool(dimension.isPiglinSafe()), ReadOnly); + checkboxFlags("respawn anchor safe", bool(dimension.isRespawnAnchorSafe()), ReadOnly); + checkboxFlags("bed safe", bool(dimension.isBedSafe()), ReadOnly); + checkboxFlags("raid capable", bool(dimension.isRaidCapable()), ReadOnly); + checkboxFlags("skylight", bool(dimension.isSkylightEnabled()), ReadOnly); + checkboxFlags("ceiling", bool(dimension.isCeilingEnabled()), ReadOnly); + checkboxFlags("fixed time", bool(dimension.getFixedTime() != null), ReadOnly); + inputFloat("ambient light", new ImFloat(dimension.getAmbientLight()), 0, 0, "%.6f", ReadOnly); + inputInt("height", new ImInt(dimension.getHeight()), 0, 0, ReadOnly); + inputInt("min y", new ImInt(dimension.getMinY()), 0, 0, ReadOnly); + inputInt("logical height", new ImInt(dimension.getLogicalHeight()), 0, 0, ReadOnly); + inputText("infiniburn", new ImString(dimension.getInfiniburn().toString()), ReadOnly); + treePop(); } } + + private void console() { + ImString command = new ImString(); + if (inputText("Command", command, EnterReturnsTrue)) { + process.command() + .getDispatcher() + .execute( + process.command().getConsoleSender(), + command.get() + ); + } + } + + private static ImInt bool(boolean value) { + return new ImInt(value ? 1 : 0); + } } diff --git a/src/test/java/net/minestom/ui/IntegrationTest.java b/src/test/java/net/minestom/ui/IntegrationTest.java index a43a02b..3264bb9 100644 --- a/src/test/java/net/minestom/ui/IntegrationTest.java +++ b/src/test/java/net/minestom/ui/IntegrationTest.java @@ -1,6 +1,8 @@ package net.minestom.ui; import net.minestom.server.MinecraftServer; +import net.minestom.server.command.CommandManager; +import net.minestom.server.command.builder.Command; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Entity; import net.minestom.server.entity.EntityType; @@ -32,6 +34,11 @@ public static void main(String[] args) { event.setSpawningInstance(instanceContainer); player.setRespawnPoint(new Pos(0, 42, 0)); }); + + CommandManager commandManager = MinecraftServer.getCommandManager(); + commandManager.register(new Command("test") {{ + setDefaultExecutor((sender, context) -> sender.sendMessage("TEST")); + }}); globalEventHandler.addListener(PlayerChatEvent.class, event -> { final Player player = event.getPlayer(); From e49c0750d03d1c1cd5370bc2e1075a1fae8067ba Mon Sep 17 00:00:00 2001 From: kiip1 <25848425+kiip1@users.noreply.github.com> Date: Sun, 8 May 2022 15:31:07 +0200 Subject: [PATCH 2/2] Continue to work on ui --- src/main/java/net/minestom/ui/ServerUI.java | 96 +++++++++++++++++---- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/minestom/ui/ServerUI.java b/src/main/java/net/minestom/ui/ServerUI.java index 2cd5a13..0398e84 100644 --- a/src/main/java/net/minestom/ui/ServerUI.java +++ b/src/main/java/net/minestom/ui/ServerUI.java @@ -13,11 +13,18 @@ import net.minestom.server.entity.Entity; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; +import net.minestom.server.instance.Section; +import net.minestom.server.instance.palette.Palette; +import net.minestom.server.thread.Acquirable; import net.minestom.server.world.DimensionType; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import static imgui.ImGui.*; import static imgui.flag.ImGuiInputTextFlags.EnterReturnsTrue; @@ -25,6 +32,7 @@ final class ServerUI extends Application { private final ServerProcess process = MinecraftServer.process(); + private final BlockingQueue queue = new LinkedBlockingQueue<>(); @Override protected void configure(Configuration config) { @@ -45,6 +53,17 @@ public void process() { begin("Console"); console(); end(); + + tick(); + } + + public void tick() { + List tasks = new ArrayList<>(); + queue.drainTo(tasks); + + for (Runnable task : tasks) { + Acquirable.of(task).sync(Runnable::run); + } } private void instance(@Nullable Instance instance) { @@ -55,9 +74,8 @@ private void instance(@Nullable Instance instance) { // Time ImInt time = new ImInt(Math.toIntExact(instance.getTime())); - if (inputInt("Time", time, 100, 1000, EnterReturnsTrue)) { - instance.setTime(time.get()); - } + if (inputInt("Time", time, 100, 1000, EnterReturnsTrue)) + queue.add(() -> instance.setTime(time.get())); // Dimension type dimensionType(instance.getDimensionType()); @@ -83,6 +101,12 @@ private void chunk(@Nullable Chunk chunk) { inputInt("chunkX", new ImInt(chunk.getChunkX()), 0, 0, ReadOnly); inputInt("chunkZ", new ImInt(chunk.getChunkZ()), 0, 0, ReadOnly); + // Sections + if (chunk.getSections().size() > 0 && treeNode("Sections")) { + chunk.getSections().forEach(this::section); + treePop(); + } + // Entities Collection entities = chunk.getInstance().getChunkEntities(chunk); if (entities.size() > 0 && treeNode("Entities")) { @@ -99,6 +123,38 @@ private void chunk(@Nullable Chunk chunk) { treePop(); } } + + private void section(@Nullable Section section) { + if (section == null) return; + + if (treeNode(section.toString())) { + // Block palette + if (treeNode("Block Palette")) { + final Palette palette = section.blockPalette(); + + inputInt("Count", new ImInt(palette.count()), ReadOnly); + inputInt("Max size", new ImInt(palette.maxSize()), ReadOnly); + inputInt("Dimension", new ImInt(palette.dimension()), ReadOnly); + + StringBuilder builder = new StringBuilder(); + palette.getAllPresent((x, y, z, value) -> { + builder.append(x) + .append(",") + .append(y) + .append(",") + .append(z) + .append(" = ") + .append(value) + .append("\n"); + }); + inputTextMultiline("Entries", new ImString(builder.toString()), ReadOnly); + + treePop(); + } + + treePop(); + } + } private void entity(@Nullable Entity entity) { if (entity == null) return; @@ -107,9 +163,11 @@ private void entity(@Nullable Entity entity) { // UUID ImString uuid = new ImString(entity.getUuid().toString()); if (inputText("Uuid", uuid, EnterReturnsTrue)) { - try { - entity.setUuid(UUID.fromString(uuid.get())); - } catch (Exception ignored) {} + queue.add(() -> { + try { + entity.setUuid(UUID.fromString(uuid.get())); + } catch (Exception ignored) {} + }); } // Position @@ -120,15 +178,15 @@ private void entity(@Nullable Entity entity) { ImFloat yaw = new ImFloat(entity.getPosition().yaw()); ImFloat pitch = new ImFloat(entity.getPosition().pitch()); if (treeNode("Position")) { - int changed = inputDouble("x", x, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; - changed += inputDouble("y", y, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; - changed += inputDouble("z", z, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; - changed += inputFloat("yaw", yaw, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; - changed += inputFloat("pitch", pitch, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + int changed = inputDouble("x", x, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("y", y, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("z", z, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputFloat("yaw", yaw, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputFloat("pitch", pitch, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; if (changed > 0) - entity.teleport(new Pos(x.get(), y.get(), z.get(), yaw.get(), pitch.get())); - + queue.add(() -> entity.teleport(new Pos(x.get(), y.get(), z.get(), yaw.get(), pitch.get()))); + treePop(); } } @@ -139,12 +197,12 @@ private void entity(@Nullable Entity entity) { ImDouble y = new ImDouble(entity.getVelocity().y()); ImDouble z = new ImDouble(entity.getVelocity().z()); if (treeNode("Velocity")) { - int changed = inputDouble("x", x, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; - changed += inputDouble("y", y, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; - changed += inputDouble("z", z, 0, 0, "%.6f", EnterReturnsTrue) ? 1 : 0; + int changed = inputDouble("x", x, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("y", y, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; + changed += inputDouble("z", z, 1, 10, "%.6f", EnterReturnsTrue) ? 1 : 0; if (changed > 0) - entity.setVelocity(new Vec(x.get(), y.get(), z.get())); + queue.add(() -> entity.setVelocity(new Vec(x.get(), y.get(), z.get()))); treePop(); } @@ -206,12 +264,12 @@ private void dimensionType(@Nullable DimensionType dimension) { private void console() { ImString command = new ImString(); if (inputText("Command", command, EnterReturnsTrue)) { - process.command() + queue.add(() -> process.command() .getDispatcher() .execute( process.command().getConsoleSender(), command.get() - ); + )); } }