Info, status cmd, spawn cmd, config, render crash check
The spawn command is 1.13+
This commit is contained in:
parent
a32f80fb79
commit
86b8e363e2
8 changed files with 198 additions and 27 deletions
|
@ -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);
|
||||
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.");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -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 -
|
||||
|
|
Loading…
Reference in a new issue