Info, status cmd, spawn cmd, config, render crash check

The spawn command is 1.13+
This commit is contained in:
Norbi Peti 2020-10-14 01:15:00 +02:00
parent a32f80fb79
commit 86b8e363e2
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
8 changed files with 198 additions and 27 deletions

View file

@ -17,6 +17,7 @@ import sznp.virtualcomputer.util.Scancode;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.stream.Collectors;
public final class Computer {
@Getter
@ -24,14 +25,13 @@ public final class Computer {
private final PluginMain plugin;
private ISession session;
private IVirtualBox vbox;
private final IVirtualBox vbox;
private IMachine machine;
private MachineEventHandler handler;
private IEventListener listener;
private VirtualBoxManager manager;
private final VirtualBoxManager manager;
private MCFrameBuffer framebuffer;
@java.beans.ConstructorProperties({"plugin"})
public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox) {
this.plugin = plugin;
this.manager = manager;
@ -54,12 +54,16 @@ public final class Computer {
try {
sendMessage(sender, "§eStarting computer...");
machine = vbox.getMachines().get(index);
if (!machine.getAccessible()) {
sendMessage(sender, "§cMachine is not accessible! " + machine.getAccessError().getText());
return;
}
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
}
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");
@ -77,10 +81,14 @@ public final class Computer {
}
public void List(CommandSender sender) {
sender.sendMessage("§bAvailable machines:");
val machines = vbox.getMachines();
for (int i = 0; i < machines.size(); i++) {
val m = machines.get(i);
sender.sendMessage("[" + i + "] " + m.getName() + " - " + m.getState());
if (m.getAccessible())
sender.sendMessage("[" + i + "] " + m.getName() + " - " + m.getState());
else
sender.sendMessage("[" + i + "] <Inaccessible, check VirtualBox>");
}
}
@ -247,6 +255,72 @@ public final class Computer {
UpdateMouse(sender, x, y, z, w, mbs, false);
}
public void Status(CommandSender sender) {
switch (session.getState()) {
case Spawning:
sender.sendMessage("§bThe computer session is currently starting.");
break;
case Unlocking:
sender.sendMessage("§bThe computer session is currently stopping.");
break;
case Unlocked:
sender.sendMessage("§bThe computer is currently powered off. Use /c start to start it.");
break;
case Null:
sender.sendMessage("§bUnknown state! Try /c stop if the machine isn't running.");
break;
case Locked:
sender.sendMessage("§bThe computer session is active.");
break;
}
if (machine == null)
return;
switch (machine.getState()) {
case Aborted:
sender.sendMessage("§bThe computer is powered off. It was unexpectedly shut down last time.");
return;
case Paused:
sender.sendMessage("§bThe machine is currently paused.");
return;
case PoweredOff:
sender.sendMessage("§bThe computer is currently powered off.");
return;
case Restoring:
sender.sendMessage("§bThe computer is restoring a saved state. This can take a while...");
return;
case Running:
sender.sendMessage("§bThe computer is currently running.");
return;
case Saving:
sender.sendMessage("§bThe computer is saving the current state. This can take a while...");
return;
case Saved:
sender.sendMessage("§bThe computer is powered off. It has a saved state it will load on start.");
return;
case Starting:
sender.sendMessage("§bThe computer is currently starting...");
return;
case Stopping:
sender.sendMessage("§bThe computer is currently stopping...");
return;
case SettingUp:
sender.sendMessage("§bThe computer is setting up...");
break;
case Stuck:
sender.sendMessage("§bThe computer is stuck. Use /c stop.");
break;
}
if (session.getState() == SessionState.Locked && machine.getState() == MachineState.Running) {
var con = session.getConsole();
Holder<Long> w = new Holder<>(), h = new Holder<>(), bpp = new Holder<>();
Holder<Integer> xo = new Holder<>(), yo = new Holder<>();
var gms = new Holder<GuestMonitorStatus>();
con.getDisplay().getScreenResolution(0L, w, h, bpp, xo, yo, gms);
sender.sendMessage("§bScreen info: " + w.value + "x" + h.value + " (" + bpp.value + ") at " + xo.value + " " + yo.value + " - " + gms.value);
sender.sendMessage("§bKeyboard LEDs: " + con.getKeyboard().getKeyboardLEDs().stream().map(Enum::toString).collect(Collectors.joining(", ")));
}
}
public void onMachineStart(CommandSender sender) {
sendMessage(sender, "§eComputer started.");
}

View file

@ -7,21 +7,38 @@ import lombok.var;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.material.Wool;
import org.javatuples.Tuple;
import org.virtualbox_6_1.VBoxException;
import javax.annotation.Nullable;
import java.util.Objects;
@CommandClass
@CommandClass(helpText = {
"Computer plugin",
"§bTo see the computer's status do /c status",
"§bTo see the available machines do /c list",
"§bTo start one of them do /c start <index>",
"§bYou can only have one machine running at a time."
}, permGroup = "computer")
public class ComputerCommand extends ICommand2MC {
@Command2.Subcommand
public void status(CommandSender sender) {
//TODO
Computer.getInstance().Status(sender);
}
@Command2.Subcommand(aliases = {"poweron", "on"})
@Command2.Subcommand(aliases = {"poweron", "on"}, helpText = {
"Start",
"Starts the given virtual machine or the first one by default.",
"Use /c list to see the index of the machines."
})
public void start(CommandSender sender, @Command2.OptionalArg int index) {
Computer.getInstance().Start(sender, index);
}
@ -46,12 +63,12 @@ public class ComputerCommand extends ICommand2MC {
Computer.getInstance().Reset(sender);
}
@Command2.Subcommand(aliases = "savestate")
@Command2.Subcommand(aliases = "save state")
public void save(CommandSender sender) {
Computer.getInstance().SaveState(sender);
}
@Command2.Subcommand(aliases = "fixscreen")
@Command2.Subcommand(aliases = "fix screen")
public void fix(CommandSender sender) {
Computer.getInstance().FixScreen(sender);
}
@ -134,4 +151,34 @@ public class ComputerCommand extends ICommand2MC {
MouseLockerPlayerListener.LockedSpeed = speed;
sender.sendMessage("§aMouse speed set to " + MouseLockerPlayerListener.LockedSpeed);
}
@Command2.Subcommand(helpText = {
"Spawn screen",
"Spawns a computer screen near you. All of them show the same thing."
})
public void spawn(Player player) {
var loc = player.getLocation();
System.out.println("Player location: " + loc);
var world = Objects.requireNonNull(loc.getWorld());
short id = PluginMain.Instance.startID.get();
for (int i = 0; i < PluginMain.MCX; i++) {
for (int j = PluginMain.MCY - 1; j >= 0; j--) {
var block = world.getBlockAt(loc.getBlockX() + i, loc.getBlockY() + j, loc.getBlockZ());
block.setType(Material.BLACK_WOOL);
/*var ws = block.getState();
var wool = (Wool) ws.getData();
wool.setColor(DyeColor.BLACK);
ws.setData(wool);
ws.update();*/
var frameLoc = block.getLocation().add(0, 0, 1);
System.out.println("Setting " + frameLoc + " to " + id);
var map = new ItemStack(Material.FILLED_MAP, 1);
var meta = ((MapMeta) map.getItemMeta());
if (meta == null) throw new NullPointerException("Map meta is null for " + frameLoc);
meta.setMapId(id++);
map.setItemMeta(meta);
world.spawn(frameLoc, ItemFrame.class).setItem(map);
}
}
}
}

View file

@ -1,9 +1,11 @@
package sznp.virtualcomputer;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.ConfigData;
import jnr.ffi.LibraryLoader;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.scheduler.BukkitTask;
import org.virtualbox_6_1.IVirtualBox;
@ -22,8 +24,8 @@ import java.util.Arrays;
import java.util.function.Predicate;
public class PluginMain extends ButtonPlugin {
private static final int MCX = 5;
private static final int MCY = 4;
public static final int MCX = 5;
public static final int MCY = 4;
private BukkitTask mousetask;
private VBoxEventHandler listener;
@ -36,12 +38,22 @@ public class PluginMain extends ButtonPlugin {
public static boolean direct;
public static boolean sendAll;
/**
* The first map ID to use for the screen.
* The maps with IDs in the range startID -> startID+19 will be temporarily replaced with the screen.
*/
public final ConfigData<Short> startID = getIConfig().getData("startID", (short) 0);
/**
* If true, uses the GPU to accelerate screen rendering. Requires root on Linux.
*/
private final ConfigData<Boolean> useGPU = getIConfig().getData("useGPU", true);
@Override
public void pluginEnable() {
Instance = this;
try {
ConsoleCommandSender ccs = getServer().getConsoleSender();
getCommand2MC().registerCommand(new ComputerCommand());
registerCommand(new ComputerCommand());
sendAll = getConfig().getBoolean("sendAll", true);
ccs.sendMessage("§bInitializing VirtualBox...");
String osname = System.getProperty("os.name").toLowerCase();
@ -80,19 +92,13 @@ public class PluginMain extends ButtonPlugin {
new Computer(this, manager, vbox); //Saves itself
ccs.sendMessage("§bLoading Screen...");
try {
//throw new NoClassDefFoundError("Test error pls ignore");
for (short i = 0; i < MCX; i++)
for (short j = 0; j < MCY; j++)
renderers.add(new GPURenderer((short) (j * 5 + i), Bukkit.getWorlds().get(0), i, j));
//pxc = LibraryLoader.create(PXCLib.class).search(getDataFolder().getAbsolutePath()).load("pxc");
direct = true;
ccs.sendMessage("§bUsing Direct Renderer, all good");
if (useGPU.get())
setupDirectRendering(ccs);
else
setupBukkitRendering(ccs);
} catch (NoClassDefFoundError | Exception e) {
for (short i = 0; i < 20; i++)
renderers.add(new BukkitRenderer(i, Bukkit.getWorlds().get(0), i * 128 * 128 * 4));
direct = false;
e.printStackTrace();
ccs.sendMessage("§6Compatibility error, using slower renderer");
setupBukkitRendering(ccs);
}
ccs.sendMessage("§bLoaded!");
val mlpl = new MouseLockerPlayerListener();
@ -106,6 +112,21 @@ public class PluginMain extends ButtonPlugin {
}
}
private void setupDirectRendering(CommandSender ccs) throws Exception {
for (short i = 0; i < MCX; i++)
for (short j = 0; j < MCY; j++)
renderers.add(new GPURenderer((short) (startID.get() + j * MCX + i), Bukkit.getWorlds().get(0), i, j));
direct = true;
ccs.sendMessage("§bUsing Direct Renderer, all good");
}
private void setupBukkitRendering(CommandSender ccs) {
for (short i = 0; i < MCX * MCY; i++)
renderers.add(new BukkitRenderer((short) (startID.get() + i), Bukkit.getWorlds().get(0), i * 128 * 128 * 4));
direct = false;
ccs.sendMessage("§6Compatibility error, using slower renderer");
}
private void error(String message) {
getLogger().severe("A fatal error occured, disabling plugin!");
Bukkit.getPluginManager().disablePlugin(this);

View file

@ -38,7 +38,6 @@ public class MachineEventHandler extends EventHandlerBase {
case Saved:
if (starting) {
sender.sendMessage("§cFailed to start computer! See the console for more details.");
sender.sendMessage("§cMake sure that 2D and 3D acceleration is disabled.");
starting = false;
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
progress.waitForCompletion(-1);

View file

@ -1,6 +1,9 @@
package sznp.virtualcomputer.renderer;
import com.aparapi.device.Device;
import com.aparapi.internal.kernel.KernelManager;
import lombok.val;
import lombok.var;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.map.MapCanvas;
@ -26,6 +29,7 @@ public class GPURenderer extends MapRenderer implements IRenderer {
private BiConsumer<Integer, Integer> flagDirty; //This way it's version independent, as long as it's named the same
private static ArrayList<GPURenderer> renderers = new ArrayList<>();
private static Method flagDirtyMethod;
private static boolean enabled = true;
public GPURenderer(short id, World world, int mapx, int mapy) throws Exception {
MapView map = IRenderer.prepare(id, world);
@ -58,6 +62,9 @@ public class GPURenderer extends MapRenderer implements IRenderer {
}
};
kernel = new GPURendererInternal(mapx, mapy, colors_);
var dev = kernel.getTargetDevice();
if (mapx == mapy && mapx == 0)
PluginMain.Instance.getLogger().info("Using device: " + dev.getShortDescription());
renderers.add(this);
map.addRenderer(this);
@ -65,6 +72,7 @@ public class GPURenderer extends MapRenderer implements IRenderer {
@Override
public void render(MapView map, MapCanvas canvas, Player player) {
if (!enabled) return;
Timing t = new Timing();
try {
if (kernel.isRendered()) return;
@ -73,6 +81,13 @@ public class GPURenderer extends MapRenderer implements IRenderer {
field.setAccessible(true);
buffer = (byte[]) field.get(canvas);
}
if (mapx == 0 && mapy == 0) { //Only print once
if (kernel.getTargetDevice().getType() != Device.TYPE.GPU) {
PluginMain.Instance.getLogger().warning("Cannot use GPU! Target device: " + kernel.getTargetDevice().getShortDescription()
+ " - Best device: " + KernelManager.instance().bestDevice().getShortDescription());
PluginMain.Instance.getLogger().warning("Server performance may be affected"); //TODO: Index 0 out of range 0
}
}
if (!PluginMain.sendAll) {
synchronized (kernel) {
if (changedX >= (mapx + 1) * 128 || changedY >= (mapy + 1) * 128
@ -106,7 +121,12 @@ public class GPURenderer extends MapRenderer implements IRenderer {
e.printStackTrace();
}
if (t.elapsedMS() > 60)
System.out.println("Map rendering took " + t.elapsedMS() + "ms");
PluginMain.Instance.getLogger().warning("Map rendering took " + t.elapsedMS() + "ms");
if (t.elapsedMS() > 2000) {
PluginMain.Instance.getLogger().severe("Map rendering is taking too long! Disabling rendering to prevent the server from crashing.");
PluginMain.Instance.getLogger().severe("Make sure the server has root privileges or disable GPU rendering.");
enabled = false;
}
}
public static void update(byte[] pixels, int width, int height, int changedX, int changedY, int changedWidth, int changedHeight) {

View file

@ -2,7 +2,11 @@ package sznp.virtualcomputer.renderer;
import com.aparapi.Kernel;
import com.aparapi.Range;
import com.aparapi.device.Device;
import com.aparapi.internal.kernel.KernelManager;
import lombok.Getter;
import lombok.var;
import sznp.virtualcomputer.PluginMain;
//Accessing the GPURenderer results in ArrayIndexOutOfBoundsExceptions - IT'S THE LAMBDAS
public class GPURendererInternal extends Kernel {
@ -24,7 +28,12 @@ public class GPURendererInternal extends Kernel {
this.mapx = mapx;
this.mapy = mapy;
this.colors = colors;
//range = Range.create2D(128, 128);
//var dev = KernelManager.instance().bestDevice();
range = Range.create2D(128, 128);
/*var dev = getTargetDevice();
if (mapx == mapy && mapx == 0)
PluginMain.Instance.getLogger().info("Using device: " + dev.getShortDescription());*/
//Do an intial draw of a black screen with Aparapi so it doesn't lag at start
pixels = new byte[1];

View file

@ -1,6 +1,7 @@
name: VirtualComputer
main: sznp.virtualcomputer.PluginMain
version: '3.0'
author: 'NorbiPeti'
commands:
computer:
usage: Use /computer start|stop|reset|key|mouse|input|fix

View file

@ -71,7 +71,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.12-R0.1-SNAPSHOT</version>
<version>1.14.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- <dependency> !- Not using anything from here directly so we're not that dependent on versions -