Black screen, event fixes, error detection
Only getting map fields once to speed things up Rendering black screen on first render, which also helps initialize Aparapi Disabling event handlers on stop (can't unregister) Removed and moved unlockMachine() calls from event handlers Detecting failed machine start Added note about sudo
This commit is contained in:
parent
c8ea718787
commit
e26489d864
8 changed files with 85 additions and 40 deletions
|
@ -2,13 +2,13 @@ package sznp.virtualcomputer;
|
|||
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.virtualbox_6_0.*;
|
||||
import sznp.virtualcomputer.events.MachineEventHandler;
|
||||
import sznp.virtualcomputer.events.VBoxEventHandler;
|
||||
import sznp.virtualcomputer.renderer.GPURendererInternal;
|
||||
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
||||
import sznp.virtualcomputer.util.Scancode;
|
||||
|
||||
|
@ -23,6 +23,8 @@ public final class Computer {
|
|||
private ISession session;
|
||||
private IVirtualBox vbox;
|
||||
private IMachine machine;
|
||||
private MachineEventHandler handler;
|
||||
private IEventListener listener;
|
||||
|
||||
@java.beans.ConstructorProperties({"plugin"})
|
||||
public Computer(PluginMain plugin, ISession session, IVirtualBox vbox) {
|
||||
|
@ -49,13 +51,13 @@ public final class Computer {
|
|||
session.setName("minecraft");
|
||||
// machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); - This creates a *process*, we don't want that anymore
|
||||
machine.lockMachine(session, LockType.VM); // We want the machine inside *our* process <-- Need the VM type to have console access
|
||||
VBoxEventHandler.getInstance().setup(machine.getId(), sender);
|
||||
VBoxEventHandler.getInstance().setup(machine.getId(), sender); //TODO: Sometimes null
|
||||
} catch (VBoxException e) {
|
||||
if (e.getResultCode() == 0x80070005) { //lockMachine: "The object functionality is limited"
|
||||
sendMessage(sender, "§6Cannot start computer, the machine may be inaccessible");
|
||||
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: Can't detect if machine fails to start because of hardening issues
|
||||
//TODO: This error also occurs if the machine has failed to start at least once (always reassign the machine?)
|
||||
//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 MCFrameBuffer(session.getConsole().getDisplay(), false)));
|
||||
//sendMessage(sender, "§6Computer started with slower screen. Run as root to use a faster method.");
|
||||
|
@ -74,8 +76,8 @@ public final class Computer {
|
|||
public void onLock(CommandSender sender) {
|
||||
machine = session.getMachine(); // This is the Machine object we can work with
|
||||
final IConsole console = session.getConsole();
|
||||
val handler = new MachineEventHandler(Computer.this);
|
||||
handler.registerTo(console.getEventSource());
|
||||
handler = new MachineEventHandler(Computer.this);
|
||||
listener = handler.registerTo(console.getEventSource());
|
||||
IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2
|
||||
handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way?
|
||||
console.getDisplay().attachFramebuffer(0L,
|
||||
|
@ -99,7 +101,6 @@ public final class Computer {
|
|||
}
|
||||
sendMessage(sender, "§eStopping computer...");
|
||||
session.getConsole().powerDown().waitForCompletion(2000);
|
||||
session.unlockMachine();
|
||||
sendMessage(sender, "§eComputer stopped.");
|
||||
}
|
||||
|
||||
|
@ -192,7 +193,26 @@ public final class Computer {
|
|||
}
|
||||
|
||||
public void onMachineStop() {
|
||||
session.unlockMachine();
|
||||
plugin.getLogger().info("Computer powered off.");
|
||||
System.out.println("Unlocking machine...");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
System.out.println("Unlocking...");
|
||||
session.unlockMachine(); //Needs to be outside of the event handler
|
||||
System.out.println("Machine unlocked.");
|
||||
}
|
||||
});
|
||||
System.out.println("Setting pixels...");
|
||||
GPURendererInternal.setPixels(new byte[1], 0, 0); //Black screen
|
||||
System.out.println("Stopping events...");
|
||||
stopEvents();
|
||||
plugin.getLogger().info("Computer powered off.");
|
||||
}
|
||||
|
||||
public void stopEvents() {
|
||||
/*if(session!=null && listener!=null)
|
||||
session.getConsole().getEventSource().unregisterListener(listener);*/
|
||||
if (listener != null)
|
||||
handler.disable();
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ public class PluginMain extends JavaPlugin {
|
|||
private IMachine machine;
|
||||
private BukkitTask screenupdatetask;
|
||||
private BukkitTask mousetask;
|
||||
private IEventListener listener;
|
||||
private IEventSource source;
|
||||
|
||||
public static PluginMain Instance;
|
||||
//public static ByteBuffer allpixels = ByteBuffer.allocate(640 * 480 * 4); // It's set on each change
|
||||
|
@ -70,7 +72,7 @@ public class PluginMain extends JavaPlugin {
|
|||
VBoxLib vbl = LibraryLoader.create(VBoxLib.class).load("vboxjxpcom");
|
||||
vbl.RTR3InitExe(0, "", 0);
|
||||
vbox = manager.getVBox();
|
||||
new VBoxEventHandler().registerTo(vbox.getEventSource());
|
||||
listener = new VBoxEventHandler().registerTo(source = vbox.getEventSource());
|
||||
session = manager.getSessionObject();
|
||||
new Computer(this, session, vbox); //Saves itself
|
||||
ccs.sendMessage("§bLoading Screen...");
|
||||
|
@ -109,6 +111,14 @@ public class PluginMain extends JavaPlugin {
|
|||
public void onDisable() {
|
||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||
mousetask.cancel();
|
||||
/*try {
|
||||
source.unregisterListener(listener);
|
||||
} catch (VBoxException e) { //"Listener was never registered"
|
||||
e.printStackTrace(); - VBox claims the listener was never registered (can double register as well)
|
||||
}*/
|
||||
((VBoxEventHandler) listener.getTypedWrapped()).disable(); //The save progress wait locks with the event
|
||||
if (Computer.getInstance() != null)
|
||||
Computer.getInstance().stopEvents();
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
if (session.getMachine().getState().equals(MachineState.Running)) {
|
||||
ccs.sendMessage("§aSaving machine state...");
|
||||
|
|
|
@ -22,6 +22,7 @@ public abstract class EventHandlerBase extends COMObjectBase implements IEventLi
|
|||
* The events to listen for. It will only look for these handlers.
|
||||
*/
|
||||
private final Map<VBoxEventType, Class<? extends org.virtualbox_6_0.IEvent>> eventMap;
|
||||
private boolean enabled = true;
|
||||
|
||||
protected EventHandlerBase(Map<VBoxEventType, Class<? extends org.virtualbox_6_0.IEvent>> eventMap) {
|
||||
//this.eventMap = eventMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().value(), Map.Entry::getValue));
|
||||
|
@ -31,6 +32,8 @@ public abstract class EventHandlerBase extends COMObjectBase implements IEventLi
|
|||
@Override
|
||||
public final void handleEvent(IEvent iEvent) {
|
||||
//val cl=eventMap.get((int)iEvent.getType()); - We can afford to search through the events for this handler
|
||||
if (!enabled)
|
||||
return;
|
||||
val kv = eventMap.entrySet().stream().filter(e -> e.getKey().value() == iEvent.getType()).findAny();
|
||||
if (!kv.isPresent()) return; //Event not supported
|
||||
val cl = kv.get().getValue();
|
||||
|
@ -50,7 +53,11 @@ public abstract class EventHandlerBase extends COMObjectBase implements IEventLi
|
|||
}
|
||||
}
|
||||
|
||||
public <T extends EventHandlerBase> void registerTo(IEventSource source) {
|
||||
Utils.registerListener(source, this, new ArrayList<>(eventMap.keySet()));
|
||||
public <T extends EventHandlerBase> org.virtualbox_6_0.IEventListener registerTo(IEventSource source) {
|
||||
return Utils.registerListener(source, this, new ArrayList<>(eventMap.keySet()));
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import sznp.virtualcomputer.Computer;
|
|||
|
||||
public class MachineEventHandler extends EventHandlerBase {
|
||||
private final Computer computer;
|
||||
private boolean starting = false;
|
||||
|
||||
public MachineEventHandler(Computer computer) {
|
||||
super(ImmutableMap.of(VBoxEventType.OnStateChanged, IStateChangedEvent.class,
|
||||
|
@ -18,13 +19,26 @@ public class MachineEventHandler extends EventHandlerBase {
|
|||
|
||||
@EventHandler
|
||||
public void handleStateChange(IStateChangedEvent event) { //https://www.virtualbox.org/sdkref/_virtual_box_8idl.html#a80b08f71210afe16038e904a656ed9eb
|
||||
System.out.println("State event: " + event.getState());
|
||||
switch (event.getState()) {
|
||||
case Stuck:
|
||||
computer.Stop(null);
|
||||
break;
|
||||
case PoweredOff:
|
||||
case Saved:
|
||||
if (starting) {
|
||||
System.out.println("Failed to start computer! See the VM's log for more details.");
|
||||
starting = false; //TODO: Sender
|
||||
}
|
||||
computer.onMachineStop();
|
||||
}
|
||||
break;
|
||||
case Starting:
|
||||
starting = true;
|
||||
break;
|
||||
case Running:
|
||||
System.out.println("Computer is running.");
|
||||
starting = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,10 +22,7 @@ public class VBoxEventHandler extends EventHandlerBase {
|
|||
|
||||
@EventHandler
|
||||
public void onSessionStateChange(ISessionStateChangedEvent event) {
|
||||
System.out.println("Session change event: " + event);
|
||||
System.out.println("ID1: " + event.getMachineId() + " - ID2: " + machineID);
|
||||
if (!event.getMachineId().equals(machineID)) return;
|
||||
System.out.println("State: " + event.getState());
|
||||
if (event.getState() == SessionState.Locked) //Need to check here, because we can't access the console yet
|
||||
Computer.getInstance().onLock(sender);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package sznp.virtualcomputer.renderer;
|
||||
|
||||
import lombok.val;
|
||||
import net.minecraft.server.v1_12_R1.WorldMap;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_12_R1.map.RenderData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.map.MapCanvas;
|
||||
import org.bukkit.map.MapPalette;
|
||||
|
@ -14,23 +11,20 @@ import sznp.virtualcomputer.util.Timing;
|
|||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
|
||||
public class GPURenderer extends MapRenderer implements IRenderer {
|
||||
private byte[] buffer;
|
||||
private GPURendererInternal kernel;
|
||||
private WorldMap wmap;
|
||||
//Store at central location after conversion
|
||||
private static int[] colors_;
|
||||
|
||||
public GPURenderer(short id, World world, int mapx, int mapy) throws Exception {
|
||||
MapView map = IRenderer.prepare(id, world);
|
||||
if (map == null) return; //Testing
|
||||
Field field = map.getClass().getDeclaredField("renderCache");
|
||||
field.setAccessible(true);
|
||||
@SuppressWarnings("unchecked") val renderCache = (Map<CraftPlayer, RenderData>) field.get(map);
|
||||
|
||||
if (colors_ == null) {
|
||||
field = MapPalette.class.getDeclaredField("colors");
|
||||
Field field = MapPalette.class.getDeclaredField("colors");
|
||||
field.setAccessible(true);
|
||||
Color[] cs = (Color[]) field.get(null);
|
||||
colors_ = new int[cs.length];
|
||||
|
@ -38,15 +32,11 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
|||
colors_[i] = cs[i].getRGB();
|
||||
}
|
||||
}
|
||||
Field field = map.getClass().getDeclaredField("worldMap");
|
||||
field.setAccessible(true);
|
||||
wmap = (WorldMap) field.get(map);
|
||||
kernel = new GPURendererInternal(mapx, mapy, colors_);
|
||||
|
||||
RenderData render = renderCache.get(null);
|
||||
|
||||
if (render == null)
|
||||
renderCache.put(null, render = new RenderData());
|
||||
|
||||
this.buffer = render.buffer;
|
||||
|
||||
//System.setProperty("com.codegen.config.enable.NEW", "true");
|
||||
|
||||
map.addRenderer(this);
|
||||
|
@ -57,13 +47,12 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
|||
Timing t = new Timing();
|
||||
try {
|
||||
if (kernel.isRendered()) return; //TODO: Stop rendering after computer is stopped
|
||||
Field field = canvas.getClass().getDeclaredField("buffer");
|
||||
field.setAccessible(true);
|
||||
buffer = (byte[]) field.get(canvas);
|
||||
if (buffer == null) { //The buffer remains the same, as the canvas remains the same
|
||||
Field field = canvas.getClass().getDeclaredField("buffer");
|
||||
field.setAccessible(true);
|
||||
buffer = (byte[]) field.get(canvas);
|
||||
}
|
||||
kernel.render(buffer);
|
||||
field = map.getClass().getDeclaredField("worldMap");
|
||||
field.setAccessible(true);
|
||||
WorldMap wmap = (WorldMap) field.get(map);
|
||||
wmap.flagDirty(0, 0);
|
||||
wmap.flagDirty(127, 127); // Send the whole image - TODO: Only send changes
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -20,7 +20,7 @@ public class GPURendererInternal extends Kernel {
|
|||
private byte[] buffer; //References the map buffer
|
||||
private Range range;
|
||||
@Getter
|
||||
private boolean rendered = true;
|
||||
private boolean rendered;
|
||||
private static ArrayList<GPURendererInternal> renderers = new ArrayList<>();
|
||||
|
||||
//public static byte[] test=new byte[1]; - LAMBDAS
|
||||
|
@ -29,6 +29,12 @@ public class GPURendererInternal extends Kernel {
|
|||
this.mapy = mapy;
|
||||
this.colors = colors;
|
||||
range = Range.create2D(128, 128);
|
||||
|
||||
//Do an intial draw of a black screen with Aparapi so it doesn't lag at start
|
||||
pixels = new byte[1];
|
||||
width = height = 0;
|
||||
rendered = false;
|
||||
|
||||
renderers.add(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,10 @@ public class Utils {
|
|||
}
|
||||
|
||||
//public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) {
|
||||
public static void registerListener(IEventSource source, IEventListener listener, List<VBoxEventType> types) {
|
||||
source.registerListener(new org.virtualbox_6_0.IEventListener(listener), types, true);
|
||||
public static org.virtualbox_6_0.IEventListener registerListener(IEventSource source, IEventListener listener, List<VBoxEventType> types) {
|
||||
val ret = new org.virtualbox_6_0.IEventListener(listener);
|
||||
source.registerListener(ret, types, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
Loading…
Reference in a new issue