diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Commands.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Commands.java index af414ae..ba5ae48 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Commands.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Commands.java @@ -67,9 +67,16 @@ public class Commands implements CommandExecutor, TabCompleter { case "restart": Computer.getInstance().Reset(sender); break; + case "save": + case "savestate": + Computer.getInstance().SaveState(sender); + break; case "fix": case "fixscreen": - Computer.getInstance().FixScreen(sender); + Boolean seamless; + if (args.length < 2) seamless = null; + else seamless = args[1].equalsIgnoreCase("true"); + Computer.getInstance().FixScreen(sender, seamless); break; case "key": case "press": diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java index 53636c2..7aff0cf 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/Computer.java @@ -100,6 +100,7 @@ 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(); String fbid = console.getDisplay().attachFramebuffer(0L, COMUtils.gimmeAFramebuffer(fb)); fb.setId(fbid); @@ -153,7 +154,16 @@ public final class Computer { sendMessage(sender, "§eComputer reset."); } - public void FixScreen(CommandSender sender) { + public void SaveState(CommandSender sender) { + if (checkMachineNotRunning(sender)) + return; + sendMessage(sender, "§eSaving computer state..."); + synchronized (session) { + session.getMachine().saveState(); + } + } + + public void FixScreen(CommandSender sender, Boolean seamless) { if (checkMachineNotRunning(sender)) return; if (framebuffer == null) { @@ -166,15 +176,20 @@ public final class Computer { sendMessage(sender, "§eFixing screen..."); try { synchronized (session) { - session.getConsole().getDisplay().setSeamlessMode(false); - session.getConsole().getDisplay().detachFramebuffer(0L, framebuffer.getId()); - session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L, true); - framebuffer.setId(session.getConsole().getDisplay().attachFramebuffer(0L, COMUtils.gimmeAFramebuffer(framebuffer))); - } + if (seamless == null) + session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L, false); + } //Last param: notify - send PnP notification - stops updates but not changes for some reason + Bukkit.getScheduler().runTaskLaterAsynchronously(PluginMain.Instance, () -> { + synchronized (session) { + sendMessage(sender, "Needs host cursor: " + session.getConsole().getMouse().getNeedsHostCursor()); + session.getConsole().getMouse().putMouseEventAbsolute(-1, -1, 0, 0, 0); + session.getConsole().getMouse().putMouseEvent(0, 0, 0, 0, 0); //Switch to relative mode + sendMessage(sender, "§eScreen fixed."); + } + }, 20); } catch (Exception e) { e.printStackTrace(); } - sendMessage(sender, "§eScreen fixed."); status = session.getConsole().getGuest().getFacilityStatus(AdditionsFacilityType.Seamless, lastUpdated); sendMessage(sender, "Seamless status: " + status); } @@ -258,6 +273,7 @@ public final class Computer { GPURenderer.update(new byte[1], 0, 0, 0, 0, 640, 480); //Black screen stopEvents(); MouseLockerPlayerListener.computerStop(); + framebuffer.stop(); } public void stopEvents() { @@ -270,6 +286,7 @@ public final class Computer { public void pluginDisable(CommandSender ccs) { stopEvents(); + framebuffer.stop(); if (session.getState() == SessionState.Locked) { if (session.getMachine().getState().equals(MachineState.Running)) { ccs.sendMessage("§aSaving machine state..."); diff --git a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java index f1548f3..61d8a40 100644 --- a/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java +++ b/VirtualComputer-Core/src/main/java/sznp/virtualcomputer/renderer/MCFrameBuffer.java @@ -15,6 +15,8 @@ import sznp.virtualcomputer.util.COMUtils; import sznp.virtualcomputer.util.IMCFrameBuffer; import sznp.virtualcomputer.util.Timing; +import java.util.concurrent.atomic.AtomicBoolean; + @RequiredArgsConstructor public class MCFrameBuffer implements IMCFrameBuffer { private final IDisplay display; @@ -23,10 +25,11 @@ public class MCFrameBuffer implements IMCFrameBuffer { private Pointer pointer; private int width; private int height; - private boolean updating = false; @Getter @Setter private String id; + private final AtomicBoolean shouldUpdate = new AtomicBoolean(); + private boolean running; @Override public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) { @@ -35,7 +38,7 @@ 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); + //System.out.println("Change: " + xOrigin + " " + yOrigin + " - " + width + " " + height); 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); @@ -43,8 +46,9 @@ public class MCFrameBuffer implements IMCFrameBuffer { pointer = new Pointer(ptr[0]); this.width = (int) w[0]; this.height = (int) h[0]; - if (this.width > 1024 || this.height > 768) - return; + //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); @@ -61,43 +65,63 @@ public class MCFrameBuffer implements IMCFrameBuffer { e.printStackTrace(); } catch (Throwable t) { t.printStackTrace(); - } finally { + } /*finally { System.out.println("Change finished"); - } + }*/ } }); } @Override public void notifyUpdate(long x, long y, long width, long height) { - if (this.width > 1024 || this.height > 768) - return; - Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> { - synchronized (this) { - if (updating) { - System.out.println("Ignoring update"); - return; - } - updating = true; - if (pointer == null) { - System.out.println("Screen pointer is null"); - updating = false; - return; - } - System.out.println("Update: " + x + " " + y + " - " + width + " " + height); - Timing t = new Timing(); - GPURenderer.update(pointer.getByteArray(0L, this.width * this.height * 4), this.width, this.height, (int) x, (int) y, (int) width, (int) height); - if (t.elapsedMS() > 60) //Typically 1ms max - System.out.println("Update took " + t.elapsedMS() + "ms"); - else - System.out.println("Update finished"); - updating = false; - } - }); + /*if (this.width > 1024 || this.height > 768) + return;*/ + if(shouldUpdate.get()) + return; //Don't wait for lock, ignore update since we're updating everything anyway - TODO: Not always + synchronized (this) { + shouldUpdate.set(true); + notifyAll(); + } } @Override public void notifyUpdateImage(long x, long y, long width, long height, byte[] image) { System.out.println("Update image!"); } + + public void start() { + running = true; + Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> { + try { + while (running) { + synchronized (this) { + while (!shouldUpdate.get()) + wait(1000); + if (pointer == null) { + System.out.println("Screen pointer is null"); + shouldUpdate.set(false); + 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"); + shouldUpdate.set(false); + /*else + System.out.println("Update finished");*/ + } + } + } catch (InterruptedException ignored) { + } + }); + } + + public void stop() { + synchronized (this) { + running = false; + notifyAll(); + } + } }