Directly using VirtualBox from Java #5
5 changed files with 87 additions and 64 deletions
|
@ -1,10 +1,7 @@
|
||||||
package sznp.virtualcomputer;
|
package sznp.virtualcomputer;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import jnr.ffi.LibraryLoader;
|
import jnr.ffi.LibraryLoader;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.command.ConsoleCommandSender;
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
@ -13,18 +10,13 @@ import sznp.virtualcomputer.events.VBoxEventHandler;
|
||||||
import sznp.virtualcomputer.renderer.BukkitRenderer;
|
import sznp.virtualcomputer.renderer.BukkitRenderer;
|
||||||
import sznp.virtualcomputer.renderer.GPURenderer;
|
import sznp.virtualcomputer.renderer.GPURenderer;
|
||||||
import sznp.virtualcomputer.renderer.IRenderer;
|
import sznp.virtualcomputer.renderer.IRenderer;
|
||||||
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
import sznp.virtualcomputer.util.Utils;
|
||||||
import sznp.virtualcomputer.util.Scancode;
|
|
||||||
import sznp.virtualcomputer.util.VBoxLib;
|
import sznp.virtualcomputer.util.VBoxLib;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class PluginMain extends JavaPlugin {
|
public class PluginMain extends JavaPlugin {
|
||||||
|
@ -73,12 +65,12 @@ public class PluginMain extends JavaPlugin {
|
||||||
System.setProperty("sun.boot.library.path", vbpath);
|
System.setProperty("sun.boot.library.path", vbpath);
|
||||||
if (System.getProperty("java.library.path") == null || System.getProperty("java.library.path").isEmpty())
|
if (System.getProperty("java.library.path") == null || System.getProperty("java.library.path").isEmpty())
|
||||||
System.setProperty("java.library.path", vbpath);
|
System.setProperty("java.library.path", vbpath);
|
||||||
addLibraryPath(vbpath);
|
Utils.addLibraryPath(vbpath);
|
||||||
final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath());
|
final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath());
|
||||||
VBoxLib vbl = LibraryLoader.create(VBoxLib.class).load("vboxjxpcom");
|
VBoxLib vbl = LibraryLoader.create(VBoxLib.class).load("vboxjxpcom");
|
||||||
vbl.RTR3InitExe(0, "", 0);
|
vbl.RTR3InitExe(0, "", 0);
|
||||||
vbox = manager.getVBox();
|
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
|
session = manager.getSessionObject(); // TODO: Events
|
||||||
ccs.sendMessage("§bLoading Screen...");
|
ccs.sendMessage("§bLoading Screen...");
|
||||||
try {
|
try {
|
||||||
|
@ -127,30 +119,4 @@ public class PluginMain extends JavaPlugin {
|
||||||
saveConfig();
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package sznp.virtualcomputer.events;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
@ -9,10 +10,10 @@ import org.virtualbox_6_0.*;
|
||||||
import sznp.virtualcomputer.PluginMain;
|
import sznp.virtualcomputer.PluginMain;
|
||||||
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
||||||
import sznp.virtualcomputer.util.Scancode;
|
import sznp.virtualcomputer.util.Scancode;
|
||||||
|
import sznp.virtualcomputer.util.Utils;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public final class Computer {
|
public final class Computer {
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -46,25 +47,11 @@ public final class Computer {
|
||||||
session.setName("minecraft");
|
session.setName("minecraft");
|
||||||
// machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); - This creates a *process*, we don't want that anymore
|
// 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
|
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() {
|
VBoxEventHandler.getInstance().setup(machine.getId(), sender);
|
||||||
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);
|
|
||||||
} catch (VBoxException e) {
|
} catch (VBoxException e) {
|
||||||
if (e.getResultCode() == 0x80070005) { //lockMachine: "The object functionality is limited"
|
if (e.getResultCode() == 0x80070005) { //lockMachine: "The object functionality is limited"
|
||||||
sendMessage(sender, "§6Cannot start computer, the machine may be inaccessible");
|
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: 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?)
|
//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
|
//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.");
|
//sendMessage(sender, "§6Computer started with slower screen. Run as root to use a faster method.");
|
||||||
} else {
|
} else {
|
||||||
sendMessage(sender, "§cFailed to start computer: " + e.getMessage());
|
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) {
|
private void sendMessage(@Nullable CommandSender sender, String message) {
|
||||||
if(sender!=null)
|
if(sender!=null)
|
||||||
sender.sendMessage(message);
|
sender.sendMessage(message);
|
||||||
|
|
|
@ -5,8 +5,6 @@ import lombok.val;
|
||||||
import org.mozilla.interfaces.IEvent;
|
import org.mozilla.interfaces.IEvent;
|
||||||
import org.mozilla.interfaces.IEventListener;
|
import org.mozilla.interfaces.IEventListener;
|
||||||
import org.virtualbox_6_0.IStateChangedEvent;
|
import org.virtualbox_6_0.IStateChangedEvent;
|
||||||
import org.virtualbox_6_0.MachineState;
|
|
||||||
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
|
||||||
import sznp.virtualcomputer.util.COMObjectBase;
|
import sznp.virtualcomputer.util.COMObjectBase;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
|
|
@ -1,19 +1,36 @@
|
||||||
package sznp.virtualcomputer.events;
|
package sznp.virtualcomputer.events;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
import org.mozilla.interfaces.IEvent;
|
import org.mozilla.interfaces.IEvent;
|
||||||
import org.mozilla.interfaces.IEventListener;
|
import org.mozilla.interfaces.IEventListener;
|
||||||
import org.mozilla.interfaces.nsISupports;
|
import org.virtualbox_6_0.ISessionStateChangedEvent;
|
||||||
import org.mozilla.xpcom.Mozilla;
|
import org.virtualbox_6_0.SessionState;
|
||||||
import org.virtualbox_6_0.IMachineStateChangedEvent;
|
|
||||||
import org.virtualbox_6_0.VBoxEventType;
|
|
||||||
import sznp.virtualcomputer.util.COMObjectBase;
|
import sznp.virtualcomputer.util.COMObjectBase;
|
||||||
|
|
||||||
public class VBoxEventHandler extends COMObjectBase implements IEventListener {
|
public class VBoxEventHandler extends COMObjectBase implements IEventListener {
|
||||||
|
public VBoxEventHandler() {
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private static VBoxEventHandler instance;
|
||||||
|
private String machineID;
|
||||||
|
private CommandSender sender;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleEvent(IEvent iEvent) {
|
public void handleEvent(IEvent iEvent) {
|
||||||
if(iEvent.getType()== VBoxEventType.OnMachineStateChanged.value()) {
|
if (iEvent instanceof ISessionStateChangedEvent) {
|
||||||
val event=(IMachineStateChangedEvent) iEvent;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue