Imitial support for running in a separate process

(Again)
This commit is contained in:
Norbi Peti 2021-02-20 03:36:03 +01:00
parent fd9803c6f1
commit 61f3192760
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
5 changed files with 48 additions and 23 deletions

View file

@ -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,7 +108,8 @@ 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());
fb.start(); if (plugin.runEmbedded.get())
fb.start();
String fbid = console.getDisplay().attachFramebuffer(0L, String fbid = console.getDisplay().attachFramebuffer(0L,
COMUtils.gimmeAFramebuffer(fb)); COMUtils.gimmeAFramebuffer(fb));
fb.setId(fbid); fb.setId(fbid);

View file

@ -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;

View file

@ -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/.
*/ */

View file

@ -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());

View file

@ -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;