Improve encapsulation and support of the rendering modes
This commit is contained in:
parent
61f3192760
commit
4a89e547a4
5 changed files with 133 additions and 76 deletions
|
@ -32,12 +32,14 @@ public final class Computer {
|
||||||
private IEventListener listener;
|
private IEventListener listener;
|
||||||
private final VirtualBoxManager manager;
|
private final VirtualBoxManager manager;
|
||||||
private MCFrameBuffer framebuffer;
|
private MCFrameBuffer framebuffer;
|
||||||
|
private final boolean direct;
|
||||||
|
|
||||||
public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox) {
|
public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox, boolean direct) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
session = manager.getSessionObject();
|
session = manager.getSessionObject();
|
||||||
this.vbox = vbox;
|
this.vbox = vbox;
|
||||||
|
this.direct = direct;
|
||||||
if (instance != null) throw new IllegalStateException("A computer already exists!");
|
if (instance != null) throw new IllegalStateException("A computer already exists!");
|
||||||
instance = this;
|
instance = this;
|
||||||
}
|
}
|
||||||
|
@ -107,7 +109,7 @@ public final class Computer {
|
||||||
IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2
|
IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2
|
||||||
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(), plugin, direct);
|
||||||
if (plugin.runEmbedded.get())
|
if (plugin.runEmbedded.get())
|
||||||
fb.start();
|
fb.start();
|
||||||
String fbid = console.getDisplay().attachFramebuffer(0L,
|
String fbid = console.getDisplay().attachFramebuffer(0L,
|
||||||
|
|
|
@ -14,13 +14,10 @@ import org.virtualbox_6_1.VirtualBoxManager;
|
||||||
import sznp.virtualcomputer.events.VBoxEventHandler;
|
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.util.Utils;
|
import sznp.virtualcomputer.util.Utils;
|
||||||
import sznp.virtualcomputer.util.VBoxLib;
|
import sznp.virtualcomputer.util.VBoxLib;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@ -32,13 +29,6 @@ public class PluginMain extends ButtonPlugin {
|
||||||
private VirtualBoxManager manager;
|
private VirtualBoxManager manager;
|
||||||
|
|
||||||
public static PluginMain Instance;
|
public static PluginMain Instance;
|
||||||
/**
|
|
||||||
* Only used if {@link #direct} is false.
|
|
||||||
*/
|
|
||||||
public static ByteBuffer allpixels; // It's set on each change
|
|
||||||
private static final ArrayList<IRenderer> renderers = new ArrayList<>();
|
|
||||||
public static boolean direct;
|
|
||||||
public static boolean sendAll;
|
|
||||||
@Getter
|
@Getter
|
||||||
private static boolean pluginEnabled; //The Bukkit plugin has to be enabled for the enable command to work
|
private static boolean pluginEnabled; //The Bukkit plugin has to be enabled for the enable command to work
|
||||||
|
|
||||||
|
@ -66,6 +56,14 @@ public class PluginMain extends ButtonPlugin {
|
||||||
* This can be useful to save resources as the plugin keeps the VirtualBox interface running while enabled.
|
* This can be useful to save resources as the plugin keeps the VirtualBox interface running while enabled.
|
||||||
*/
|
*/
|
||||||
private final ConfigData<Boolean> autoEnable = getIConfig().getData("autoEnable", true);
|
private final ConfigData<Boolean> autoEnable = getIConfig().getData("autoEnable", true);
|
||||||
|
/**
|
||||||
|
* The amount of rows to update if the slower, Bukkit renderer is being used.
|
||||||
|
*/
|
||||||
|
private final ConfigData<Integer> updateRows = getIConfig().getData("updateRows", 15, o -> (int) o, i -> BukkitRenderer.updatepixels = i);
|
||||||
|
/**
|
||||||
|
* Experimental. Whether all of the image data should be sent even on small changes. Defaults to true.
|
||||||
|
*/
|
||||||
|
private final ConfigData<Boolean> sendAll = getIConfig().getData("sendAll", true);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pluginEnable() {
|
public void pluginEnable() {
|
||||||
|
@ -83,7 +81,6 @@ public class PluginMain extends ButtonPlugin {
|
||||||
pluginEnabled = true;
|
pluginEnabled = true;
|
||||||
try {
|
try {
|
||||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||||
sendAll = getConfig().getBoolean("sendAll", true);
|
|
||||||
ccs.sendMessage("§bInitializing VirtualBox...");
|
ccs.sendMessage("§bInitializing VirtualBox...");
|
||||||
String osname = System.getProperty("os.name").toLowerCase();
|
String osname = System.getProperty("os.name").toLowerCase();
|
||||||
final boolean windows;
|
final boolean windows;
|
||||||
|
@ -102,7 +99,7 @@ public class PluginMain extends ButtonPlugin {
|
||||||
if (notGoodDir.test(new File(vbpath)))
|
if (notGoodDir.test(new File(vbpath)))
|
||||||
vbpath = "/usr/lib/virtualbox";
|
vbpath = "/usr/lib/virtualbox";
|
||||||
if (notGoodDir.test(new File(vbpath)))
|
if (notGoodDir.test(new File(vbpath)))
|
||||||
error("Could not find VirtualBox! Download from https://www.virtualbox.org/wiki/Downloads");
|
error("Could not find VirtualBox! Download from https://www.virtualbox.org/wiki/Downloads", null);
|
||||||
if (System.getProperty("vbox.home") == null || System.getProperty("vbox.home").isEmpty())
|
if (System.getProperty("vbox.home") == null || System.getProperty("vbox.home").isEmpty())
|
||||||
System.setProperty("vbox.home", vbpath);
|
System.setProperty("vbox.home", vbpath);
|
||||||
if (System.getProperty("sun.boot.library.path") == null
|
if (System.getProperty("sun.boot.library.path") == null
|
||||||
|
@ -118,49 +115,53 @@ public class PluginMain extends ButtonPlugin {
|
||||||
}
|
}
|
||||||
IVirtualBox vbox = manager.getVBox();
|
IVirtualBox vbox = manager.getVBox();
|
||||||
(listener = new VBoxEventHandler()).registerTo(vbox.getEventSource());
|
(listener = new VBoxEventHandler()).registerTo(vbox.getEventSource());
|
||||||
new Computer(this, manager, vbox); //Saves itself
|
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
ccs.sendMessage("§bLoading Screen...");
|
ccs.sendMessage("§bLoading Screen...");
|
||||||
|
boolean direct;
|
||||||
try {
|
try {
|
||||||
if (useGPU.get())
|
if (useGPU.get()) {
|
||||||
setupDirectRendering(ccs);
|
setupDirectRendering(ccs);
|
||||||
else
|
direct = true;
|
||||||
|
} else {
|
||||||
setupBukkitRendering(ccs);
|
setupBukkitRendering(ccs);
|
||||||
|
direct = false;
|
||||||
|
}
|
||||||
} catch (NoClassDefFoundError | Exception e) {
|
} catch (NoClassDefFoundError | Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
setupBukkitRendering(ccs);
|
setupBukkitRendering(ccs);
|
||||||
|
direct = false;
|
||||||
}
|
}
|
||||||
|
new Computer(this, manager, vbox, direct); //Saves itself
|
||||||
ccs.sendMessage("§bLoaded!");
|
ccs.sendMessage("§bLoaded!");
|
||||||
val mlpl = new MouseLockerPlayerListener();
|
val mlpl = new MouseLockerPlayerListener();
|
||||||
mousetask = getServer().getScheduler().runTaskTimer(this, mlpl, 0, 0);
|
mousetask = getServer().getScheduler().runTaskTimer(this, mlpl, 0, 0);
|
||||||
getServer().getPluginManager().registerEvents(mlpl, this);
|
getServer().getPluginManager().registerEvents(mlpl, this);
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
getLogger().severe("A fatal error occured, disabling plugin!");
|
error(null, e);
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupDirectRendering(CommandSender ccs) throws Exception {
|
private void setupDirectRendering(CommandSender ccs) throws Exception {
|
||||||
for (short i = 0; i < MCX; i++)
|
for (short i = 0; i < MCX; i++)
|
||||||
for (short j = 0; j < MCY; j++)
|
for (short j = 0; j < MCY; j++)
|
||||||
renderers.add(new GPURenderer((short) (startID.get() + j * MCX + i), Bukkit.getWorlds().get(0), i, j));
|
new GPURenderer((short) (startID.get() + j * MCX + i), Bukkit.getWorlds().get(0), sendAll.get(), i, j);
|
||||||
direct = true;
|
|
||||||
ccs.sendMessage("§bUsing Direct Renderer, all good");
|
ccs.sendMessage("§bUsing Direct Renderer, all good");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupBukkitRendering(CommandSender ccs) {
|
private void setupBukkitRendering(CommandSender ccs) {
|
||||||
for (short i = 0; i < MCX * MCY; i++)
|
for (short i = 0; i < MCX * MCY; i++)
|
||||||
renderers.add(new BukkitRenderer((short) (startID.get() + i), Bukkit.getWorlds().get(0), i * 128 * 128 * 4));
|
new BukkitRenderer((short) (startID.get() + i), Bukkit.getWorlds().get(0), i * 128 * 128 * 4, getLogger());
|
||||||
direct = false;
|
|
||||||
ccs.sendMessage("§6Using Bukkit renderer");
|
ccs.sendMessage("§6Using Bukkit renderer");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void error(String message) {
|
private void error(String message, Exception e) { //Message OR exception
|
||||||
getLogger().severe("A fatal error occured, disabling plugin!");
|
getLogger().severe("A fatal error occured, disabling plugin!");
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
throw new RuntimeException(message);
|
if (message != null)
|
||||||
|
throw new RuntimeException(message);
|
||||||
|
else
|
||||||
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -186,7 +187,6 @@ public class PluginMain extends ButtonPlugin {
|
||||||
Computer.getInstance().pluginDisable(ccs);
|
Computer.getInstance().pluginDisable(ccs);
|
||||||
ccs.sendMessage("§aHuh.");
|
ccs.sendMessage("§aHuh.");
|
||||||
saveConfig();
|
saveConfig();
|
||||||
renderers.clear();
|
|
||||||
manager.cleanup();
|
manager.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,30 +10,33 @@ import java.awt.image.BufferedImage;
|
||||||
import java.awt.image.DataBufferInt;
|
import java.awt.image.DataBufferInt;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class BukkitRenderer extends MapRenderer implements IRenderer {
|
public class BukkitRenderer extends MapRenderer implements IRenderer {
|
||||||
private ByteBuffer allpixels;
|
private static ByteBuffer allpixels;
|
||||||
private BufferedImage image;
|
private final BufferedImage image;
|
||||||
private int startindex;
|
private final int startindex;
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The raw pixel data from the machine in BGRA format
|
* Updates the screen.
|
||||||
|
*
|
||||||
|
* @param allpixels The raw pixel data from the machine in BGRA format
|
||||||
*/
|
*/
|
||||||
public void setAllPixels(ByteBuffer allpixels) {
|
public static void update(ByteBuffer allpixels, int x, int y, int width, int height) {
|
||||||
this.allpixels = allpixels;
|
BukkitRenderer.allpixels = allpixels; //TODO: Only update what actually changes
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic implementation, should work on most versions
|
* Generic implementation, should work on most versions
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id The ID of the current map
|
||||||
* The ID of the current map
|
* @param world The world to create new maps in
|
||||||
* @param world
|
* @param startindex The index
|
||||||
* The world to create new maps in
|
* @param logger The plugin's logger
|
||||||
* @param startindex
|
|
||||||
* The index to start from in allpixels
|
|
||||||
*/
|
*/
|
||||||
public BukkitRenderer(short id, World world, int startindex) {
|
public BukkitRenderer(short id, World world, int startindex, Logger logger) {
|
||||||
|
this.logger = logger;
|
||||||
MapView map = IRenderer.prepare(id, world);
|
MapView map = IRenderer.prepare(id, world);
|
||||||
map.addRenderer(this);
|
map.addRenderer(this);
|
||||||
this.startindex = startindex;
|
this.startindex = startindex;
|
||||||
|
@ -72,12 +75,12 @@ public class BukkitRenderer extends MapRenderer implements IRenderer {
|
||||||
|
|
||||||
long diff = System.nanoTime() - time;
|
long diff = System.nanoTime() - time;
|
||||||
if (TimeUnit.NANOSECONDS.toMillis(diff) > 40) {
|
if (TimeUnit.NANOSECONDS.toMillis(diff) > 40) {
|
||||||
System.out.println("Map rendering took " + TimeUnit.NANOSECONDS.toMillis(diff) + " ms");
|
logger.warning("Map rendering took " + TimeUnit.NANOSECONDS.toMillis(diff) + " ms");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.out.println("Progess: " + progress);
|
logger.warning("Progess: " + progress);
|
||||||
System.out.println("UpdatePixels: " + updatepixels);
|
logger.warning("UpdatePixels: " + updatepixels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.util.function.BiConsumer;
|
||||||
public class GPURenderer extends MapRenderer implements IRenderer {
|
public class GPURenderer extends MapRenderer implements IRenderer {
|
||||||
private byte[] buffer;
|
private byte[] buffer;
|
||||||
private final GPURendererInternal kernel;
|
private final GPURendererInternal kernel;
|
||||||
|
private final boolean sendAll;
|
||||||
private int mapx, mapy;
|
private int mapx, mapy;
|
||||||
//Store at central location after conversion
|
//Store at central location after conversion
|
||||||
private static int[] colors_;
|
private static int[] colors_;
|
||||||
|
@ -32,7 +33,8 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
||||||
private static boolean enabled = true;
|
private static boolean enabled = true;
|
||||||
private static boolean warned = false;
|
private static boolean warned = false;
|
||||||
|
|
||||||
public GPURenderer(short id, World world, int mapx, int mapy) throws Exception {
|
public GPURenderer(short id, World world, boolean sendAll, int mapx, int mapy) throws Exception {
|
||||||
|
this.sendAll = sendAll;
|
||||||
MapView map = IRenderer.prepare(id, world);
|
MapView map = IRenderer.prepare(id, world);
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
kernel = null;
|
kernel = null;
|
||||||
|
@ -90,7 +92,7 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
||||||
PluginMain.Instance.getLogger().warning("Server performance may be affected"); //TODO: Index 0 out of range 0
|
PluginMain.Instance.getLogger().warning("Server performance may be affected"); //TODO: Index 0 out of range 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!PluginMain.sendAll) {
|
if (!sendAll) {
|
||||||
synchronized (kernel) {
|
synchronized (kernel) {
|
||||||
if (changedX >= (mapx + 1) * 128 || changedY >= (mapy + 1) * 128
|
if (changedX >= (mapx + 1) * 128 || changedY >= (mapy + 1) * 128
|
||||||
|| changedX + changedWidth < mapx * 128 || changedY + changedHeight < mapy * 128) {
|
|| changedX + changedWidth < mapx * 128 || changedY + changedHeight < mapy * 128) {
|
||||||
|
@ -134,7 +136,7 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
||||||
public static void update(byte[] pixels, int width, int height, int changedX, int changedY, int changedWidth, int changedHeight) {
|
public static void update(byte[] pixels, int width, int height, int changedX, int changedY, int changedWidth, int changedHeight) {
|
||||||
for (GPURenderer r : renderers) {
|
for (GPURenderer r : renderers) {
|
||||||
synchronized (r.kernel) {
|
synchronized (r.kernel) {
|
||||||
if (!PluginMain.sendAll) {
|
if (!r.sendAll) {
|
||||||
if (changedX < r.changedX)
|
if (changedX < r.changedX)
|
||||||
r.changedX = changedX;
|
r.changedX = changedX;
|
||||||
if (changedY < r.changedY)
|
if (changedY < r.changedY)
|
||||||
|
|
|
@ -2,8 +2,8 @@ package sznp.virtualcomputer.renderer;
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import lombok.val;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.scheduler.BukkitTask;
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
import org.virtualbox_6_1.Holder;
|
import org.virtualbox_6_1.Holder;
|
||||||
|
@ -15,12 +15,18 @@ import sznp.virtualcomputer.util.COMUtils;
|
||||||
import sznp.virtualcomputer.util.IMCFrameBuffer;
|
import sznp.virtualcomputer.util.IMCFrameBuffer;
|
||||||
import sznp.virtualcomputer.util.Timing;
|
import sznp.virtualcomputer.util.Timing;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class MCFrameBuffer implements IMCFrameBuffer {
|
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 final Logger logger;
|
||||||
|
private final PluginMain plugin;
|
||||||
|
private final boolean embedded;
|
||||||
|
private final boolean direct;
|
||||||
private BukkitTask tt;
|
private BukkitTask tt;
|
||||||
/**
|
/**
|
||||||
* Used when running embedded
|
* Used when running embedded
|
||||||
|
@ -29,45 +35,59 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
/**
|
/**
|
||||||
* Used when not running embedded
|
* Used when not running embedded
|
||||||
*/
|
*/
|
||||||
private byte[] screenImage; //TODO: Remove PluginMain.allpixels (and other PluginMain references)
|
private byte[] screenImage;
|
||||||
|
/**
|
||||||
|
* Used when running in indirect mode, not embedded
|
||||||
|
*/
|
||||||
|
private ByteBuffer screenBuffer;
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private String id;
|
private String id;
|
||||||
private final AtomicBoolean shouldUpdate = new AtomicBoolean();
|
private final AtomicBoolean shouldUpdate = new AtomicBoolean();
|
||||||
|
private final AtomicIntegerArray updateParameters = new AtomicIntegerArray(4);
|
||||||
private boolean running;
|
private boolean running;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new framebuffer that receives images from the VM and sends the image data to Minecraft.
|
||||||
|
*
|
||||||
|
* @param display The VM display to use - TODO: Multiple monitors
|
||||||
|
* @param plugin The plugin
|
||||||
|
* @param direct Whether the GPU rendering is used
|
||||||
|
*/
|
||||||
|
public MCFrameBuffer(IDisplay display, PluginMain plugin, boolean direct) {
|
||||||
|
this.display = display;
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.logger = plugin.getLogger();
|
||||||
|
this.embedded = plugin.runEmbedded.get(); //Don't change even if the config got updated while running
|
||||||
|
this.direct = direct;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) {
|
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) {
|
||||||
if (tt != null)
|
if (tt != null)
|
||||||
tt.cancel();
|
tt.cancel();
|
||||||
tt = Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
tt = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
synchronized (this) { //If a change occurs twice, then wait for it
|
synchronized (this) { //If a change occurs twice, then wait for it
|
||||||
try {
|
try {
|
||||||
if (!PluginMain.Instance.runEmbedded.get()) { //Running separately
|
if (!embedded) { //Running separately
|
||||||
this.width = (int) width;
|
this.width = (int) width;
|
||||||
this.height = (int) height;
|
this.height = (int) height;
|
||||||
if (screenImage == null || screenImage.length != width * height * 4)
|
if (screenImage == null || screenImage.length != width * height * 4) {
|
||||||
screenImage = new byte[(int) (width * height * 4)];
|
screenImage = new byte[(int) (width * height * 4)];
|
||||||
updateScreen(screenImage);
|
screenBuffer = ByteBuffer.wrap(screenImage);
|
||||||
|
}
|
||||||
|
updateScreen((int) xOrigin, (int) yOrigin, (int) width, (int) height);
|
||||||
return;
|
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);
|
||||||
if (PluginMain.direct) {
|
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];
|
updateScreen(0, 0, (int) width, (int) 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 {
|
|
||||||
PluginMain.allpixels = new Pointer(ptr[0]).getByteBuffer(0L, width * height * 4);
|
|
||||||
if (width * height > 640 * 480)
|
|
||||||
PluginMain.allpixels.limit(640 * 480 * 4);
|
|
||||||
else
|
|
||||||
PluginMain.allpixels.limit((int) (width * height * 4));
|
|
||||||
}
|
|
||||||
} catch (VBoxException e) {
|
} catch (VBoxException e) {
|
||||||
if (e.getResultCode() == 0x80070005)
|
if (e.getResultCode() == 0x80070005)
|
||||||
return; // Machine is being powered down
|
return; // Machine is being powered down
|
||||||
|
@ -85,10 +105,14 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
public void notifyUpdate(long x, long y, long width, long height) {
|
public void notifyUpdate(long x, long y, long width, long height) {
|
||||||
/*if (this.width > 1024 || this.height > 768)
|
/*if (this.width > 1024 || this.height > 768)
|
||||||
return;*/
|
return;*/
|
||||||
if (!PluginMain.direct || shouldUpdate.get())
|
if (!direct || shouldUpdate.get())
|
||||||
return; //Don't wait for lock, ignore update since we're updating everything anyway - TODO: Not always
|
return; //Don't wait for lock, ignore update since we're updating everything anyway - TODO: Not always
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
shouldUpdate.set(true);
|
shouldUpdate.set(true);
|
||||||
|
updateParameters.set(0, (int) x);
|
||||||
|
updateParameters.set(1, (int) y);
|
||||||
|
updateParameters.set(2, (int) width);
|
||||||
|
updateParameters.set(3, (int) height);
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,19 +121,19 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
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) {
|
if (this.width == 0 || this.height == 0) {
|
||||||
PluginMain.Instance.getLogger().warning("Received screen image before resolution change!");
|
logger.warning("Received screen image before resolution change!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < height; i++) //Copy lines of the screen in a fast way
|
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);
|
System.arraycopy(image, (int) (i * width * 4), screenImage, (int) (x + y * this.width * 4), (int) width * 4);
|
||||||
updateScreen(image);
|
updateScreen((int) x, (int) y, (int) width, (int) height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
if (!PluginMain.direct)
|
if (!direct)
|
||||||
return;
|
return;
|
||||||
running = true;
|
running = true;
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||||
try {
|
try {
|
||||||
while (running) {
|
while (running) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
@ -121,7 +145,7 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
updateScreen(pointer.getByteArray(0L, this.width * this.height * 4));
|
updateScreen(updateParameters.get(0), updateParameters.get(1), updateParameters.get(2), updateParameters.get(3));
|
||||||
shouldUpdate.set(false);
|
shouldUpdate.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,11 +154,37 @@ public class MCFrameBuffer implements IMCFrameBuffer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateScreen(byte[] pixels) {
|
private void updateScreenDirectInternal(byte[] pixels, int x, int y, int width, int height) {
|
||||||
Timing t = new Timing(); //TODO: Add support for only sending changed fragments
|
Timing t = new Timing();
|
||||||
GPURenderer.update(pixels, this.width, this.height, (int) 0, (int) 0, (int) width, (int) height);
|
GPURenderer.update(pixels, this.width, this.height, x, y, width, height);
|
||||||
if (t.elapsedMS() > 60) //Typically 1ms max
|
if (t.elapsedMS() > 60) //Typically 1ms max
|
||||||
PluginMain.Instance.getLogger().warning("Update took " + t.elapsedMS() + "ms");
|
logger.warning("Update took " + t.elapsedMS() + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateScreenIndirectInternal(ByteBuffer buffer, int x, int y, int width, int height) {
|
||||||
|
if (this.width * this.height > 640 * 480)
|
||||||
|
buffer.limit(640 * 480 * 4);
|
||||||
|
else
|
||||||
|
buffer.limit(this.width * this.height * 4);
|
||||||
|
BukkitRenderer.update(buffer, x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the screen when the VM is embedded or when it isn't.
|
||||||
|
*
|
||||||
|
* @param x The x of change - passed along to the renderer to use
|
||||||
|
* @param y The y of change - passed along to the renderer to use
|
||||||
|
* @param width The width of change - passed along to the renderer to use
|
||||||
|
* @param height The height of change - passed along to the renderer to use
|
||||||
|
*/
|
||||||
|
private void updateScreen(int x, int y, int width, int height) {
|
||||||
|
if (direct) {
|
||||||
|
val arr = embedded ? pointer.getByteArray(0L, this.width * this.height * 4) : screenImage;
|
||||||
|
updateScreenDirectInternal(arr, x, y, width, height);
|
||||||
|
} else {
|
||||||
|
val bb = embedded ? pointer.getByteBuffer(0L, (long) this.width * this.height * 4) : screenBuffer;
|
||||||
|
updateScreenIndirectInternal(bb, x, y, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
|
Loading…
Reference in a new issue