From 86b8e363e280978ac9226e48ceddd5d3b54c8c51 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 14 Oct 2020 01:15:00 +0200 Subject: [PATCH] Info, status cmd, spawn cmd, config, render crash check The spawn command is 1.13+ --- .../java/sznp/virtualcomputer/Computer.java | 84 +++++++++++++++++-- .../sznp/virtualcomputer/ComputerCommand.java | 57 +++++++++++-- .../java/sznp/virtualcomputer/PluginMain.java | 49 +++++++---- .../events/MachineEventHandler.java | 1 - .../virtualcomputer/renderer/GPURenderer.java | 22 ++++- .../renderer/GPURendererInternal.java | 9 ++ .../src/main/resources/plugin.yml | 1 + pom.xml | 2 +- 8 files changed, 198 insertions(+), 27 deletions(-) diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java index 598ac18..3a14e55 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java @@ -17,6 +17,7 @@ import sznp.virtualcomputer.util.Scancode; import javax.annotation.Nullable; import java.util.Arrays; +import java.util.stream.Collectors; public final class Computer { @Getter @@ -24,14 +25,13 @@ public final class Computer { private final PluginMain plugin; private ISession session; - private IVirtualBox vbox; + private final IVirtualBox vbox; private IMachine machine; private MachineEventHandler handler; private IEventListener listener; - private VirtualBoxManager manager; + private final VirtualBoxManager manager; private MCFrameBuffer framebuffer; - @java.beans.ConstructorProperties({"plugin"}) public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox) { this.plugin = plugin; this.manager = manager; @@ -54,12 +54,16 @@ public final class Computer { try { sendMessage(sender, "§eStarting computer..."); machine = vbox.getMachines().get(index); + if (!machine.getAccessible()) { + sendMessage(sender, "§cMachine is not accessible! " + machine.getAccessError().getText()); + return; + } session.setName("minecraft"); + VBoxEventHandler.getInstance().setup(machine.getId(), sender); //TODO: Sometimes null // machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); - This creates a *process*, we don't want that anymore synchronized (session) { machine.lockMachine(session, LockType.VM); // We want the machine inside *our* process <-- Need the VM type to have console access } - VBoxEventHandler.getInstance().setup(machine.getId(), sender); //TODO: Sometimes null } catch (VBoxException e) { if (e.getResultCode() == 0x80070005) { //lockMachine: "The object functionality is limited" sendMessage(sender, "§6Cannot start computer, the machine may be inaccessible"); @@ -77,10 +81,14 @@ public final class Computer { } public void List(CommandSender sender) { + sender.sendMessage("§bAvailable machines:"); val machines = vbox.getMachines(); for (int i = 0; i < machines.size(); i++) { val m = machines.get(i); - sender.sendMessage("[" + i + "] " + m.getName() + " - " + m.getState()); + if (m.getAccessible()) + sender.sendMessage("[" + i + "] " + m.getName() + " - " + m.getState()); + else + sender.sendMessage("[" + i + "] "); } } @@ -247,6 +255,72 @@ public final class Computer { UpdateMouse(sender, x, y, z, w, mbs, false); } + public void Status(CommandSender sender) { + switch (session.getState()) { + case Spawning: + sender.sendMessage("§bThe computer session is currently starting."); + break; + case Unlocking: + sender.sendMessage("§bThe computer session is currently stopping."); + break; + case Unlocked: + sender.sendMessage("§bThe computer is currently powered off. Use /c start to start it."); + break; + case Null: + sender.sendMessage("§bUnknown state! Try /c stop if the machine isn't running."); + break; + case Locked: + sender.sendMessage("§bThe computer session is active."); + break; + } + if (machine == null) + return; + switch (machine.getState()) { + case Aborted: + sender.sendMessage("§bThe computer is powered off. It was unexpectedly shut down last time."); + return; + case Paused: + sender.sendMessage("§bThe machine is currently paused."); + return; + case PoweredOff: + sender.sendMessage("§bThe computer is currently powered off."); + return; + case Restoring: + sender.sendMessage("§bThe computer is restoring a saved state. This can take a while..."); + return; + case Running: + sender.sendMessage("§bThe computer is currently running."); + return; + case Saving: + sender.sendMessage("§bThe computer is saving the current state. This can take a while..."); + return; + case Saved: + sender.sendMessage("§bThe computer is powered off. It has a saved state it will load on start."); + return; + case Starting: + sender.sendMessage("§bThe computer is currently starting..."); + return; + case Stopping: + sender.sendMessage("§bThe computer is currently stopping..."); + return; + case SettingUp: + sender.sendMessage("§bThe computer is setting up..."); + break; + case Stuck: + sender.sendMessage("§bThe computer is stuck. Use /c stop."); + break; + } + if (session.getState() == SessionState.Locked && machine.getState() == MachineState.Running) { + var con = session.getConsole(); + Holder w = new Holder<>(), h = new Holder<>(), bpp = new Holder<>(); + Holder xo = new Holder<>(), yo = new Holder<>(); + var gms = new Holder(); + con.getDisplay().getScreenResolution(0L, w, h, bpp, xo, yo, gms); + sender.sendMessage("§bScreen info: " + w.value + "x" + h.value + " (" + bpp.value + ") at " + xo.value + " " + yo.value + " - " + gms.value); + sender.sendMessage("§bKeyboard LEDs: " + con.getKeyboard().getKeyboardLEDs().stream().map(Enum::toString).collect(Collectors.joining(", "))); + } + } + public void onMachineStart(CommandSender sender) { sendMessage(sender, "§eComputer started."); } diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/ComputerCommand.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/ComputerCommand.java index 8515bc5..3259bc8 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/ComputerCommand.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/ComputerCommand.java @@ -7,21 +7,38 @@ import lombok.var; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.DyeColor; +import org.bukkit.Material; import org.bukkit.command.CommandSender; +import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.MapMeta; +import org.bukkit.material.Wool; import org.javatuples.Tuple; import org.virtualbox_6_1.VBoxException; import javax.annotation.Nullable; +import java.util.Objects; -@CommandClass +@CommandClass(helpText = { + "Computer plugin", + "§bTo see the computer's status do /c status", + "§bTo see the available machines do /c list", + "§bTo start one of them do /c start ", + "§bYou can only have one machine running at a time." +}, permGroup = "computer") public class ComputerCommand extends ICommand2MC { @Command2.Subcommand public void status(CommandSender sender) { - //TODO + Computer.getInstance().Status(sender); } - @Command2.Subcommand(aliases = {"poweron", "on"}) + @Command2.Subcommand(aliases = {"poweron", "on"}, helpText = { + "Start", + "Starts the given virtual machine or the first one by default.", + "Use /c list to see the index of the machines." + }) public void start(CommandSender sender, @Command2.OptionalArg int index) { Computer.getInstance().Start(sender, index); } @@ -46,12 +63,12 @@ public class ComputerCommand extends ICommand2MC { Computer.getInstance().Reset(sender); } - @Command2.Subcommand(aliases = "savestate") + @Command2.Subcommand(aliases = "save state") public void save(CommandSender sender) { Computer.getInstance().SaveState(sender); } - @Command2.Subcommand(aliases = "fixscreen") + @Command2.Subcommand(aliases = "fix screen") public void fix(CommandSender sender) { Computer.getInstance().FixScreen(sender); } @@ -134,4 +151,34 @@ public class ComputerCommand extends ICommand2MC { MouseLockerPlayerListener.LockedSpeed = speed; sender.sendMessage("§aMouse speed set to " + MouseLockerPlayerListener.LockedSpeed); } + + @Command2.Subcommand(helpText = { + "Spawn screen", + "Spawns a computer screen near you. All of them show the same thing." + }) + public void spawn(Player player) { + var loc = player.getLocation(); + System.out.println("Player location: " + loc); + var world = Objects.requireNonNull(loc.getWorld()); + short id = PluginMain.Instance.startID.get(); + for (int i = 0; i < PluginMain.MCX; i++) { + for (int j = PluginMain.MCY - 1; j >= 0; j--) { + var block = world.getBlockAt(loc.getBlockX() + i, loc.getBlockY() + j, loc.getBlockZ()); + block.setType(Material.BLACK_WOOL); + /*var ws = block.getState(); + var wool = (Wool) ws.getData(); + wool.setColor(DyeColor.BLACK); + ws.setData(wool); + ws.update();*/ + var frameLoc = block.getLocation().add(0, 0, 1); + System.out.println("Setting " + frameLoc + " to " + id); + var map = new ItemStack(Material.FILLED_MAP, 1); + var meta = ((MapMeta) map.getItemMeta()); + if (meta == null) throw new NullPointerException("Map meta is null for " + frameLoc); + meta.setMapId(id++); + map.setItemMeta(meta); + world.spawn(frameLoc, ItemFrame.class).setItem(map); + } + } + } } diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/PluginMain.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/PluginMain.java index 0f7af8a..e69e2d2 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/PluginMain.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/PluginMain.java @@ -1,9 +1,11 @@ package sznp.virtualcomputer; import buttondevteam.lib.architecture.ButtonPlugin; +import buttondevteam.lib.architecture.ConfigData; import jnr.ffi.LibraryLoader; import lombok.val; import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.scheduler.BukkitTask; import org.virtualbox_6_1.IVirtualBox; @@ -22,8 +24,8 @@ import java.util.Arrays; import java.util.function.Predicate; public class PluginMain extends ButtonPlugin { - private static final int MCX = 5; - private static final int MCY = 4; + public static final int MCX = 5; + public static final int MCY = 4; private BukkitTask mousetask; private VBoxEventHandler listener; @@ -36,12 +38,22 @@ public class PluginMain extends ButtonPlugin { public static boolean direct; public static boolean sendAll; + /** + * The first map ID to use for the screen. + * The maps with IDs in the range startID -> startID+19 will be temporarily replaced with the screen. + */ + public final ConfigData startID = getIConfig().getData("startID", (short) 0); + /** + * If true, uses the GPU to accelerate screen rendering. Requires root on Linux. + */ + private final ConfigData useGPU = getIConfig().getData("useGPU", true); + @Override public void pluginEnable() { Instance = this; try { ConsoleCommandSender ccs = getServer().getConsoleSender(); - getCommand2MC().registerCommand(new ComputerCommand()); + registerCommand(new ComputerCommand()); sendAll = getConfig().getBoolean("sendAll", true); ccs.sendMessage("§bInitializing VirtualBox..."); String osname = System.getProperty("os.name").toLowerCase(); @@ -80,19 +92,13 @@ public class PluginMain extends ButtonPlugin { new Computer(this, manager, vbox); //Saves itself ccs.sendMessage("§bLoading Screen..."); try { - //throw new NoClassDefFoundError("Test error pls ignore"); - for (short i = 0; i < MCX; i++) - for (short j = 0; j < MCY; j++) - renderers.add(new GPURenderer((short) (j * 5 + i), Bukkit.getWorlds().get(0), i, j)); - //pxc = LibraryLoader.create(PXCLib.class).search(getDataFolder().getAbsolutePath()).load("pxc"); - direct = true; - ccs.sendMessage("§bUsing Direct Renderer, all good"); + if (useGPU.get()) + setupDirectRendering(ccs); + else + setupBukkitRendering(ccs); } catch (NoClassDefFoundError | Exception e) { - for (short i = 0; i < 20; i++) - renderers.add(new BukkitRenderer(i, Bukkit.getWorlds().get(0), i * 128 * 128 * 4)); - direct = false; e.printStackTrace(); - ccs.sendMessage("§6Compatibility error, using slower renderer"); + setupBukkitRendering(ccs); } ccs.sendMessage("§bLoaded!"); val mlpl = new MouseLockerPlayerListener(); @@ -106,6 +112,21 @@ public class PluginMain extends ButtonPlugin { } } + private void setupDirectRendering(CommandSender ccs) throws Exception { + for (short i = 0; i < MCX; i++) + for (short j = 0; j < MCY; j++) + renderers.add(new GPURenderer((short) (startID.get() + j * MCX + i), Bukkit.getWorlds().get(0), i, j)); + direct = true; + ccs.sendMessage("§bUsing Direct Renderer, all good"); + } + + private void setupBukkitRendering(CommandSender ccs) { + for (short i = 0; i < MCX * MCY; i++) + renderers.add(new BukkitRenderer((short) (startID.get() + i), Bukkit.getWorlds().get(0), i * 128 * 128 * 4)); + direct = false; + ccs.sendMessage("§6Compatibility error, using slower renderer"); + } + private void error(String message) { getLogger().severe("A fatal error occured, disabling plugin!"); Bukkit.getPluginManager().disablePlugin(this); diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java index fb4e893..8115710 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java @@ -38,7 +38,6 @@ public class MachineEventHandler extends EventHandlerBase { case Saved: if (starting) { sender.sendMessage("§cFailed to start computer! See the console for more details."); - sender.sendMessage("§cMake sure that 2D and 3D acceleration is disabled."); starting = false; Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> { progress.waitForCompletion(-1); diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURenderer.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURenderer.java index a45b96e..43a410a 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURenderer.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURenderer.java @@ -1,6 +1,9 @@ package sznp.virtualcomputer.renderer; +import com.aparapi.device.Device; +import com.aparapi.internal.kernel.KernelManager; import lombok.val; +import lombok.var; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; @@ -26,6 +29,7 @@ public class GPURenderer extends MapRenderer implements IRenderer { private BiConsumer flagDirty; //This way it's version independent, as long as it's named the same private static ArrayList renderers = new ArrayList<>(); private static Method flagDirtyMethod; + private static boolean enabled = true; public GPURenderer(short id, World world, int mapx, int mapy) throws Exception { MapView map = IRenderer.prepare(id, world); @@ -58,6 +62,9 @@ public class GPURenderer extends MapRenderer implements IRenderer { } }; kernel = new GPURendererInternal(mapx, mapy, colors_); + var dev = kernel.getTargetDevice(); + if (mapx == mapy && mapx == 0) + PluginMain.Instance.getLogger().info("Using device: " + dev.getShortDescription()); renderers.add(this); map.addRenderer(this); @@ -65,6 +72,7 @@ public class GPURenderer extends MapRenderer implements IRenderer { @Override public void render(MapView map, MapCanvas canvas, Player player) { + if (!enabled) return; Timing t = new Timing(); try { if (kernel.isRendered()) return; @@ -73,6 +81,13 @@ public class GPURenderer extends MapRenderer implements IRenderer { field.setAccessible(true); buffer = (byte[]) field.get(canvas); } + if (mapx == 0 && mapy == 0) { //Only print once + if (kernel.getTargetDevice().getType() != Device.TYPE.GPU) { + PluginMain.Instance.getLogger().warning("Cannot use GPU! Target device: " + kernel.getTargetDevice().getShortDescription() + + " - Best device: " + KernelManager.instance().bestDevice().getShortDescription()); + PluginMain.Instance.getLogger().warning("Server performance may be affected"); //TODO: Index 0 out of range 0 + } + } if (!PluginMain.sendAll) { synchronized (kernel) { if (changedX >= (mapx + 1) * 128 || changedY >= (mapy + 1) * 128 @@ -106,7 +121,12 @@ public class GPURenderer extends MapRenderer implements IRenderer { e.printStackTrace(); } if (t.elapsedMS() > 60) - System.out.println("Map rendering took " + t.elapsedMS() + "ms"); + PluginMain.Instance.getLogger().warning("Map rendering took " + t.elapsedMS() + "ms"); + if (t.elapsedMS() > 2000) { + PluginMain.Instance.getLogger().severe("Map rendering is taking too long! Disabling rendering to prevent the server from crashing."); + PluginMain.Instance.getLogger().severe("Make sure the server has root privileges or disable GPU rendering."); + enabled = false; + } } public static void update(byte[] pixels, int width, int height, int changedX, int changedY, int changedWidth, int changedHeight) { diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURendererInternal.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURendererInternal.java index 67896e3..8920343 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURendererInternal.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/GPURendererInternal.java @@ -2,7 +2,11 @@ package sznp.virtualcomputer.renderer; import com.aparapi.Kernel; import com.aparapi.Range; +import com.aparapi.device.Device; +import com.aparapi.internal.kernel.KernelManager; import lombok.Getter; +import lombok.var; +import sznp.virtualcomputer.PluginMain; //Accessing the GPURenderer results in ArrayIndexOutOfBoundsExceptions - IT'S THE LAMBDAS public class GPURendererInternal extends Kernel { @@ -24,7 +28,12 @@ public class GPURendererInternal extends Kernel { this.mapx = mapx; this.mapy = mapy; this.colors = colors; + //range = Range.create2D(128, 128); + //var dev = KernelManager.instance().bestDevice(); range = Range.create2D(128, 128); + /*var dev = getTargetDevice(); + if (mapx == mapy && mapx == 0) + PluginMain.Instance.getLogger().info("Using device: " + dev.getShortDescription());*/ //Do an intial draw of a black screen with Aparapi so it doesn't lag at start pixels = new byte[1]; diff --git a/VirtualComputer-Core/src/main/resources/plugin.yml b/VirtualComputer-Core/src/main/resources/plugin.yml index 4f69ce9..25aade5 100644 --- a/VirtualComputer-Core/src/main/resources/plugin.yml +++ b/VirtualComputer-Core/src/main/resources/plugin.yml @@ -1,6 +1,7 @@ name: VirtualComputer main: sznp.virtualcomputer.PluginMain version: '3.0' +author: 'NorbiPeti' commands: computer: usage: Use /computer start|stop|reset|key|mouse|input|fix diff --git a/pom.xml b/pom.xml index baf53f0..597d6a7 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ org.spigotmc spigot-api - 1.12-R0.1-SNAPSHOT + 1.14.4-R0.1-SNAPSHOT provided