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 java.util.Arrays;
import java.util.Collections;
import java.util.stream.Collectors;
public final class Computer {
@ -60,9 +61,11 @@ public final class Computer {
}
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
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) {
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)");
//TODO: If we have VirtualBox open, it won't close the server's port
//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 {
sendMessage(sender, "§cFailed to start computer: " + e.getMessage());
}
@ -108,7 +108,8 @@ public final class Computer {
handler.setProgress(progress);
handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way?
val fb = new MCFrameBuffer(console.getDisplay());
fb.start();
if (plugin.runEmbedded.get())
fb.start();
String fbid = console.getDisplay().attachFramebuffer(0L,
COMUtils.gimmeAFramebuffer(fb));
fb.setId(fbid);

View file

@ -224,6 +224,7 @@ public class ComputerCommand extends ICommand2MC {
switch (enableDisable) {
case "enable":
sender.sendMessage("§bEnabling plugin...");
PluginMain.Instance.reloadConfig();
PluginMain.Instance.pluginEnableInternal();
sender.sendMessage("§bPlugin enabled! More info on console.");
break;

View file

@ -48,9 +48,14 @@ public class PluginMain extends ButtonPlugin {
*/
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);
/**
* 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/.
*/

View file

@ -40,14 +40,15 @@ public class MachineEventHandler extends EventHandlerBase {
sender.sendMessage("§cFailed to start computer! See the console for more details.");
starting = false;
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
if (progress == null) return;
progress.waitForCompletion(-1);
if (progress != null && progress.getCompleted() && progress.getResultCode() != 0) {
if (progress.getCompleted() && progress.getResultCode() != 0) {
Logger l = PluginMain.Instance.getLogger();
l.warning("Result code: " + Integer.toHexString(progress.getResultCode()));
for (var info = progress.getErrorInfo(); info != null; info = info.getNext()) {
l.warning("----------------");
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 {
l.warning("VBox: " + info.getText());
l.warning("Component: " + info.getComponent());

View file

@ -22,7 +22,14 @@ public class MCFrameBuffer implements IMCFrameBuffer {
private final IDisplay display;
private final Holder<IDisplaySourceBitmap> holder = new Holder<>();
private BukkitTask tt;
/**
* Used when running embedded
*/
private Pointer pointer;
/**
* Used when not running embedded
*/
private byte[] screenImage; //TODO: Remove PluginMain.allpixels (and other PluginMain references)
private int width;
private int height;
@Getter
@ -38,7 +45,14 @@ public class MCFrameBuffer implements IMCFrameBuffer {
tt = Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
synchronized (this) { //If a change occurs twice, then wait for it
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);
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);
@ -46,9 +60,6 @@ public class MCFrameBuffer implements IMCFrameBuffer {
pointer = new Pointer(ptr[0]);
this.width = (int) w[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);
} else {
PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4);
@ -65,9 +76,7 @@ public class MCFrameBuffer implements IMCFrameBuffer {
e.printStackTrace();
} catch (Throwable t) {
t.printStackTrace();
} /*finally {
System.out.println("Change finished");
}*/
}
}
});
}
@ -87,6 +96,13 @@ public class MCFrameBuffer implements IMCFrameBuffer {
@Override
public void notifyUpdateImage(long x, long y, long width, long height, byte[] 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() {
@ -105,14 +121,8 @@ public class MCFrameBuffer implements IMCFrameBuffer {
continue;
}
if (!running) return;
//System.out.println("Update: " + x + " " + y + " - " + width + " " + height);
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");
updateScreen(pointer.getByteArray(0L, this.width * this.height * 4));
shouldUpdate.set(false);
/*else
System.out.println("Update finished");*/
}
}
} 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() {
synchronized (this) {
running = false;