Imitial support for running in a separate process
(Again)
This commit is contained in:
parent
fd9803c6f1
commit
61f3192760
5 changed files with 48 additions and 23 deletions
|
@ -17,6 +17,7 @@ import sznp.virtualcomputer.util.Scancode;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public final class Computer {
|
public final class Computer {
|
||||||
|
@ -60,9 +61,11 @@ public final class Computer {
|
||||||
}
|
}
|
||||||
session.setName("minecraft");
|
session.setName("minecraft");
|
||||||
VBoxEventHandler.getInstance().setup(machine.getId(), sender); //TODO: Sometimes null
|
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) {
|
synchronized (session) {
|
||||||
machine.lockMachine(session, LockType.VM); // We want the machine inside *our* process <-- Need the VM type to have console access
|
if (plugin.runEmbedded.get())
|
||||||
|
machine.lockMachine(session, LockType.VM); //Run in our process <-- Need the VM type to have console access
|
||||||
|
else
|
||||||
|
machine.launchVMProcess(session, "headless", Collections.emptyList()); //Run in a separate process
|
||||||
}
|
}
|
||||||
} 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"
|
||||||
|
@ -70,9 +73,6 @@ public final class Computer {
|
||||||
sendMessage(sender, "§6Make sure that the server is running as root (sudo)");
|
sendMessage(sender, "§6Make sure that the server is running as root (sudo)");
|
||||||
//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: "The object in question already exists." on second start
|
//TODO: "The object in question already exists." on second start
|
||||||
//machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); //No privileges, start the 'old' way
|
|
||||||
//session.getConsole().getDisplay().attachFramebuffer(0L, new IFramebuffer(new COMFrameBuffer(session.getConsole().getDisplay(), false)));
|
|
||||||
//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());
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,7 @@ public final class Computer {
|
||||||
handler.setProgress(progress);
|
handler.setProgress(progress);
|
||||||
handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way?
|
handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way?
|
||||||
val fb = new MCFrameBuffer(console.getDisplay());
|
val fb = new MCFrameBuffer(console.getDisplay());
|
||||||
|
if (plugin.runEmbedded.get())
|
||||||
fb.start();
|
fb.start();
|
||||||
String fbid = console.getDisplay().attachFramebuffer(0L,
|
String fbid = console.getDisplay().attachFramebuffer(0L,
|
||||||
COMUtils.gimmeAFramebuffer(fb));
|
COMUtils.gimmeAFramebuffer(fb));
|
||||||
|
|
|
@ -224,6 +224,7 @@ public class ComputerCommand extends ICommand2MC {
|
||||||
switch (enableDisable) {
|
switch (enableDisable) {
|
||||||
case "enable":
|
case "enable":
|
||||||
sender.sendMessage("§bEnabling plugin...");
|
sender.sendMessage("§bEnabling plugin...");
|
||||||
|
PluginMain.Instance.reloadConfig();
|
||||||
PluginMain.Instance.pluginEnableInternal();
|
PluginMain.Instance.pluginEnableInternal();
|
||||||
sender.sendMessage("§bPlugin enabled! More info on console.");
|
sender.sendMessage("§bPlugin enabled! More info on console.");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -48,9 +48,14 @@ public class PluginMain extends ButtonPlugin {
|
||||||
*/
|
*/
|
||||||
public final ConfigData<Short> startID = getIConfig().getData("startID", (short) 0);
|
public final ConfigData<Short> startID = getIConfig().getData("startID", (short) 0);
|
||||||
/**
|
/**
|
||||||
* If true, uses the GPU to accelerate screen rendering. Requires root on Linux.
|
* If true, uses the GPU to accelerate screen rendering. May require root on Linux.
|
||||||
*/
|
*/
|
||||||
private final ConfigData<Boolean> useGPU = getIConfig().getData("useGPU", true);
|
private final ConfigData<Boolean> useGPU = getIConfig().getData("useGPU", true);
|
||||||
|
/**
|
||||||
|
* The virtual machine will be hosted inside the server process if this option is enabled.
|
||||||
|
* This can improve performance but may cause stability and other issues.
|
||||||
|
*/
|
||||||
|
public final ConfigData<Boolean> runEmbedded = getIConfig().getData("runEmbedded", true);
|
||||||
/**
|
/**
|
||||||
* Determines the keyboard layout to use for /c show keyboard. Layouts can be defined in VirtualComputer/layouts/.
|
* Determines the keyboard layout to use for /c show keyboard. Layouts can be defined in VirtualComputer/layouts/.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -40,14 +40,15 @@ public class MachineEventHandler extends EventHandlerBase {
|
||||||
sender.sendMessage("§cFailed to start computer! See the console for more details.");
|
sender.sendMessage("§cFailed to start computer! See the console for more details.");
|
||||||
starting = false;
|
starting = false;
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
||||||
|
if (progress == null) return;
|
||||||
progress.waitForCompletion(-1);
|
progress.waitForCompletion(-1);
|
||||||
if (progress != null && progress.getCompleted() && progress.getResultCode() != 0) {
|
if (progress.getCompleted() && progress.getResultCode() != 0) {
|
||||||
Logger l = PluginMain.Instance.getLogger();
|
Logger l = PluginMain.Instance.getLogger();
|
||||||
l.warning("Result code: " + Integer.toHexString(progress.getResultCode()));
|
l.warning("Result code: " + Integer.toHexString(progress.getResultCode()));
|
||||||
for (var info = progress.getErrorInfo(); info != null; info = info.getNext()) {
|
for (var info = progress.getErrorInfo(); info != null; info = info.getNext()) {
|
||||||
l.warning("----------------");
|
l.warning("----------------");
|
||||||
if (info.getResultCode() == 0x80004005 && info.getResultDetail() == 0xFFFFF88B)
|
if (info.getResultCode() == 0x80004005 && info.getResultDetail() == 0xFFFFF88B)
|
||||||
l.warning("The server cannot access the VirtualBox driver, run it with sudo. Make sure to only run plugins you trust.");
|
l.warning("The server cannot access the VirtualBox driver. Either run it as root or disable the 'runEmbedded' config option. Make sure to only run plugins you trust as root.");
|
||||||
else {
|
else {
|
||||||
l.warning("VBox: " + info.getText());
|
l.warning("VBox: " + info.getText());
|
||||||
l.warning("Component: " + info.getComponent());
|
l.warning("Component: " + info.getComponent());
|
||||||
|
|
|
@ -22,7 +22,14 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
private final IDisplay display;
|
private final IDisplay display;
|
||||||
private final Holder<IDisplaySourceBitmap> holder = new Holder<>();
|
private final Holder<IDisplaySourceBitmap> holder = new Holder<>();
|
||||||
private BukkitTask tt;
|
private BukkitTask tt;
|
||||||
|
/**
|
||||||
|
* Used when running embedded
|
||||||
|
*/
|
||||||
private Pointer pointer;
|
private Pointer pointer;
|
||||||
|
/**
|
||||||
|
* Used when not running embedded
|
||||||
|
*/
|
||||||
|
private byte[] screenImage; //TODO: Remove PluginMain.allpixels (and other PluginMain references)
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -38,7 +45,14 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
tt = Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
tt = Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
||||||
synchronized (this) { //If a change occurs twice, then wait for it
|
synchronized (this) { //If a change occurs twice, then wait for it
|
||||||
try {
|
try {
|
||||||
//System.out.println("Change: " + xOrigin + " " + yOrigin + " - " + width + " " + height);
|
if (!PluginMain.Instance.runEmbedded.get()) { //Running separately
|
||||||
|
this.width = (int) width;
|
||||||
|
this.height = (int) height;
|
||||||
|
if (screenImage == null || screenImage.length != width * height * 4)
|
||||||
|
screenImage = new byte[(int) (width * height * 4)];
|
||||||
|
updateScreen(screenImage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
display.querySourceBitmap(0L, holder);
|
display.querySourceBitmap(0L, holder);
|
||||||
long[] ptr = new long[1], w = new long[1], h = new long[1], bpp = new long[1], bpl = new long[1], pf = new long[1];
|
long[] ptr = new long[1], w = new long[1], h = new long[1], bpp = new long[1], bpl = new long[1], pf = new long[1];
|
||||||
COMUtils.queryBitmapInfo(holder.value, ptr, w, h, bpp, bpl, pf);
|
COMUtils.queryBitmapInfo(holder.value, ptr, w, h, bpp, bpl, pf);
|
||||||
|
@ -46,9 +60,6 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
pointer = new Pointer(ptr[0]);
|
pointer = new Pointer(ptr[0]);
|
||||||
this.width = (int) w[0];
|
this.width = (int) w[0];
|
||||||
this.height = (int) h[0];
|
this.height = (int) h[0];
|
||||||
//System.out.println("Actual sizes: " + this.width + " " + this.height);
|
|
||||||
/*if (this.width > 1024 || this.height > 768)
|
|
||||||
return;*/
|
|
||||||
GPURenderer.update(pointer.getByteArray(0L, (int) (w[0] * h[0] * 4)), (int) w[0], (int) h[0], 0, 0, this.width, this.height);
|
GPURenderer.update(pointer.getByteArray(0L, (int) (w[0] * h[0] * 4)), (int) w[0], (int) h[0], 0, 0, this.width, this.height);
|
||||||
} else {
|
} else {
|
||||||
PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4);
|
PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4);
|
||||||
|
@ -65,9 +76,7 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
} /*finally {
|
}
|
||||||
System.out.println("Change finished");
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -87,6 +96,13 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
@Override
|
@Override
|
||||||
public void notifyUpdateImage(long x, long y, long width, long height, byte[] image) {
|
public void notifyUpdateImage(long x, long y, long width, long height, byte[] image) {
|
||||||
System.out.println("Update image!");
|
System.out.println("Update image!");
|
||||||
|
if (this.width == 0 || this.height == 0) {
|
||||||
|
PluginMain.Instance.getLogger().warning("Received screen image before resolution change!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < height; i++) //Copy lines of the screen in a fast way
|
||||||
|
System.arraycopy(image, (int) (i * width * 4), screenImage, (int) (x + y * this.width * 4), (int) width * 4);
|
||||||
|
updateScreen(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
@ -105,14 +121,8 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
//System.out.println("Update: " + x + " " + y + " - " + width + " " + height);
|
updateScreen(pointer.getByteArray(0L, this.width * this.height * 4));
|
||||||
Timing t = new Timing(); //TODO: Add support for only sending changed fragments
|
|
||||||
GPURenderer.update(pointer.getByteArray(0L, this.width * this.height * 4), this.width, this.height, (int) 0, (int) 0, (int) width, (int) height);
|
|
||||||
if (t.elapsedMS() > 60) //Typically 1ms max
|
|
||||||
System.out.println("Update took " + t.elapsedMS() + "ms");
|
|
||||||
shouldUpdate.set(false);
|
shouldUpdate.set(false);
|
||||||
/*else
|
|
||||||
System.out.println("Update finished");*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ignored) {
|
} catch (InterruptedException ignored) {
|
||||||
|
@ -120,6 +130,13 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateScreen(byte[] pixels) {
|
||||||
|
Timing t = new Timing(); //TODO: Add support for only sending changed fragments
|
||||||
|
GPURenderer.update(pixels, this.width, this.height, (int) 0, (int) 0, (int) width, (int) height);
|
||||||
|
if (t.elapsedMS() > 60) //Typically 1ms max
|
||||||
|
PluginMain.Instance.getLogger().warning("Update took " + t.elapsedMS() + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
running = false;
|
running = false;
|
||||||
|
|
Loading…
Reference in a new issue