From e75b841d337b839885334c0e46f2ed64f328e2fc Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 20 Mar 2019 15:47:27 +0100 Subject: [PATCH] Using events instead of polling Using events for locking, startup progress and stopping --- .../java/sznp/virtualcomputer/PluginMain.java | 40 ++----------------- .../sznp/virtualcomputer/events/Computer.java | 40 ++++++++++--------- .../events/MachineEventHandler.java | 2 - .../events/VBoxEventHandler.java | 29 +++++++++++--- .../java/sznp/virtualcomputer/util/Utils.java | 40 +++++++++++++++++++ 5 files changed, 87 insertions(+), 64 deletions(-) create mode 100644 VirtualComputer/src/main/java/sznp/virtualcomputer/util/Utils.java diff --git a/VirtualComputer/src/main/java/sznp/virtualcomputer/PluginMain.java b/VirtualComputer/src/main/java/sznp/virtualcomputer/PluginMain.java index 5e7a2f8..b04db1d 100644 --- a/VirtualComputer/src/main/java/sznp/virtualcomputer/PluginMain.java +++ b/VirtualComputer/src/main/java/sznp/virtualcomputer/PluginMain.java @@ -1,10 +1,7 @@ package sznp.virtualcomputer; -import com.google.common.collect.Lists; import jnr.ffi.LibraryLoader; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; @@ -13,18 +10,13 @@ import sznp.virtualcomputer.events.VBoxEventHandler; import sznp.virtualcomputer.renderer.BukkitRenderer; import sznp.virtualcomputer.renderer.GPURenderer; import sznp.virtualcomputer.renderer.IRenderer; -import sznp.virtualcomputer.renderer.MCFrameBuffer; -import sznp.virtualcomputer.util.Scancode; +import sznp.virtualcomputer.util.Utils; import sznp.virtualcomputer.util.VBoxLib; -import javax.annotation.Nullable; import java.io.File; -import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.function.Predicate; public class PluginMain extends JavaPlugin { @@ -73,12 +65,12 @@ public class PluginMain extends JavaPlugin { System.setProperty("sun.boot.library.path", vbpath); if (System.getProperty("java.library.path") == null || System.getProperty("java.library.path").isEmpty()) System.setProperty("java.library.path", vbpath); - addLibraryPath(vbpath); + Utils.addLibraryPath(vbpath); final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath()); VBoxLib vbl = LibraryLoader.create(VBoxLib.class).load("vboxjxpcom"); vbl.RTR3InitExe(0, "", 0); vbox = manager.getVBox(); - vbox.getEventSource().registerListener(new IEventListener(new VBoxEventHandler()), Arrays.asList(VBoxEventType.OnMachineStateChanged), true); + Utils.registerListener(vbox.getEventSource(), new VBoxEventHandler(), VBoxEventType.OnMachineStateChanged); session = manager.getSessionObject(); // TODO: Events ccs.sendMessage("§bLoading Screen..."); try { @@ -127,30 +119,4 @@ public class PluginMain extends JavaPlugin { saveConfig(); } - /** - * Adds the specified path to the java library path - * - * @param pathToAdd - * the path to add - * @throws Exception - */ - public static void addLibraryPath(String pathToAdd) throws Exception { - final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths"); - usrPathsField.setAccessible(true); - - // get array of paths - final String[] paths = (String[]) usrPathsField.get(null); - - // check if the path to add is already present - for (String path : paths) { - if (path.equals(pathToAdd)) { - return; - } - } - - // add the new path - final String[] newPaths = Arrays.copyOf(paths, paths.length + 1); - newPaths[newPaths.length - 1] = pathToAdd; - usrPathsField.set(null, newPaths); - } } diff --git a/VirtualComputer/src/main/java/sznp/virtualcomputer/events/Computer.java b/VirtualComputer/src/main/java/sznp/virtualcomputer/events/Computer.java index 24d4d41..8e57b4a 100644 --- a/VirtualComputer/src/main/java/sznp/virtualcomputer/events/Computer.java +++ b/VirtualComputer/src/main/java/sznp/virtualcomputer/events/Computer.java @@ -2,6 +2,7 @@ package sznp.virtualcomputer.events; import com.google.common.collect.Lists; import lombok.Getter; +import lombok.val; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -9,10 +10,10 @@ import org.virtualbox_6_0.*; import sznp.virtualcomputer.PluginMain; import sznp.virtualcomputer.renderer.MCFrameBuffer; import sznp.virtualcomputer.util.Scancode; +import sznp.virtualcomputer.util.Utils; import javax.annotation.Nullable; import java.util.Arrays; -import java.util.Collections; public final class Computer { @Getter @@ -46,25 +47,11 @@ public final class Computer { session.setName("minecraft"); // machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); - This creates a *process*, we don't want that anymore machine.lockMachine(session, LockType.VM); // We want the machine inside *our* process <-- Need the VM type to have console access - final Runnable tr = new Runnable() { - public void run() { - if (session.getState() != SessionState.Locked) { // https://www.virtualbox.org/sdkref/_virtual_box_8idl.html#ac82c179a797c0d7c249d1b98a8e3aa8f - Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, this, 5); - return; // "This state also occurs as a short transient state during an IMachine::lockMachine call." - } - machine = session.getMachine(); // This is the Machine object we can work with - final IConsole console = session.getConsole(); - console.getEventSource().registerListener(new IEventListener(new MachineEventHandler(Computer.this)), Collections.singletonList(VBoxEventType.MachineEvent), true); - console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2 - console.getDisplay().attachFramebuffer(0L, - new IFramebuffer(new MCFrameBuffer(console.getDisplay(), true))); - } - }; - Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, tr, 5); + VBoxEventHandler.getInstance().setup(machine.getId(), sender); } catch (VBoxException e) { if (e.getResultCode() == 0x80070005) { //lockMachine: "The object functionality is limited" sendMessage(sender, "§6Cannot start computer, the machine may be inaccessible"); - return; //TODO: If we have VirtualBox open, it won't close the server's port + //TODO: If we have VirtualBox open, it won't close the server's port //TODO: Can't detect if machine fails to start because of hardening issues //TODO: This error also occurs if the machine has failed to start at least once (always reassign the machine?) //machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); //No privileges, start the 'old' way @@ -72,13 +59,28 @@ public final class Computer { //sendMessage(sender, "§6Computer started with slower screen. Run as root to use a faster method."); } else { sendMessage(sender, "§cFailed to start computer: " + e.getMessage()); - return; } } - sendMessage(sender, "§eComputer started."); }); } + /** + * Gets called when the machine is locked after {@link #Start(CommandSender, int)} + * + * @param sender The sender which started the machine + */ + public void onLock(CommandSender sender) { + machine = session.getMachine(); // This is the Machine object we can work with + final IConsole console = session.getConsole(); + val handler = new MachineEventHandler(Computer.this); + Utils.registerListener(console.getEventSource(), handler, VBoxEventType.MachineEvent); + IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2 + Utils.registerListener(progress.getEventSource(), handler, VBoxEventType.OnProgressTaskCompleted); //TODO: Show progress bar some way? + console.getDisplay().attachFramebuffer(0L, + new IFramebuffer(new MCFrameBuffer(console.getDisplay(), true))); + sendMessage(sender, "§eComputer started."); + } + private void sendMessage(@Nullable CommandSender sender, String message) { if(sender!=null) sender.sendMessage(message); diff --git a/VirtualComputer/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java b/VirtualComputer/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java index 2865227..4b90bfb 100644 --- a/VirtualComputer/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java +++ b/VirtualComputer/src/main/java/sznp/virtualcomputer/events/MachineEventHandler.java @@ -5,8 +5,6 @@ import lombok.val; import org.mozilla.interfaces.IEvent; import org.mozilla.interfaces.IEventListener; import org.virtualbox_6_0.IStateChangedEvent; -import org.virtualbox_6_0.MachineState; -import sznp.virtualcomputer.renderer.MCFrameBuffer; import sznp.virtualcomputer.util.COMObjectBase; @RequiredArgsConstructor diff --git a/VirtualComputer/src/main/java/sznp/virtualcomputer/events/VBoxEventHandler.java b/VirtualComputer/src/main/java/sznp/virtualcomputer/events/VBoxEventHandler.java index d33252d..5a7ce86 100644 --- a/VirtualComputer/src/main/java/sznp/virtualcomputer/events/VBoxEventHandler.java +++ b/VirtualComputer/src/main/java/sznp/virtualcomputer/events/VBoxEventHandler.java @@ -1,19 +1,36 @@ package sznp.virtualcomputer.events; +import lombok.Getter; import lombok.val; +import org.bukkit.command.CommandSender; import org.mozilla.interfaces.IEvent; import org.mozilla.interfaces.IEventListener; -import org.mozilla.interfaces.nsISupports; -import org.mozilla.xpcom.Mozilla; -import org.virtualbox_6_0.IMachineStateChangedEvent; -import org.virtualbox_6_0.VBoxEventType; +import org.virtualbox_6_0.ISessionStateChangedEvent; +import org.virtualbox_6_0.SessionState; import sznp.virtualcomputer.util.COMObjectBase; public class VBoxEventHandler extends COMObjectBase implements IEventListener { + public VBoxEventHandler() { + instance = this; + } + + @Getter + private static VBoxEventHandler instance; + private String machineID; + private CommandSender sender; + @Override public void handleEvent(IEvent iEvent) { - if(iEvent.getType()== VBoxEventType.OnMachineStateChanged.value()) { - val event=(IMachineStateChangedEvent) iEvent; + if (iEvent instanceof ISessionStateChangedEvent) { + val event = ((ISessionStateChangedEvent) iEvent); + if (!event.getMachineId().equals(machineID)) return; + if (event.getState() == SessionState.Locked) //Need to check here, because we can't access the console yet + Computer.getInstance().onLock(sender); } } + + public void setup(String machineID, CommandSender sender) { + this.machineID = machineID; + this.sender = sender; + } } diff --git a/VirtualComputer/src/main/java/sznp/virtualcomputer/util/Utils.java b/VirtualComputer/src/main/java/sznp/virtualcomputer/util/Utils.java new file mode 100644 index 0000000..4b046e6 --- /dev/null +++ b/VirtualComputer/src/main/java/sznp/virtualcomputer/util/Utils.java @@ -0,0 +1,40 @@ +package sznp.virtualcomputer.util; + +import org.mozilla.interfaces.IEventListener; +import org.virtualbox_6_0.IEventSource; +import org.virtualbox_6_0.VBoxEventType; + +import java.lang.reflect.Field; +import java.util.Arrays; + +public class Utils { + /** + * Adds the specified path to the java library path + * + * @param pathToAdd the path to add + * @throws Exception + */ + public static void addLibraryPath(String pathToAdd) throws Exception { + final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths"); + usrPathsField.setAccessible(true); + + // get array of paths + final String[] paths = (String[]) usrPathsField.get(null); + + // check if the path to add is already present + for (String path : paths) { + if (path.equals(pathToAdd)) { + return; + } + } + + // add the new path + final String[] newPaths = Arrays.copyOf(paths, paths.length + 1); + newPaths[newPaths.length - 1] = pathToAdd; + usrPathsField.set(null, newPaths); + } + + public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) { + source.registerListener(new org.virtualbox_6_0.IEventListener(listener), Arrays.asList(types), true); + } +}