Compare commits
26 commits
Author | SHA1 | Date | |
---|---|---|---|
f4871574d3 | |||
2d08ef55fa | |||
e23e062be9 | |||
4a89e547a4 | |||
61f3192760 | |||
fd9803c6f1 | |||
2c6eb3a610 | |||
6058842b61 | |||
86b8e363e2 | |||
a32f80fb79 | |||
559b2989c6 | |||
26d8cc1ff0 | |||
580d48d7d0 | |||
586ab05033 | |||
32cea5c384 | |||
3ce2da7f7f | |||
de13d9876d | |||
cd0e1d01b6 | |||
c98fcdb1c0 | |||
6de24a2742 | |||
751cc5eb92 | |||
9ba0c3e820 | |||
515fb1f4d1 | |||
b0ad790f72 | |||
b7d0b49ac6 | |||
ea6c248534 |
74 changed files with 2010 additions and 1105 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
libpxc.so
|
||||
dependency-reduced-pom.xml
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
|
|
@ -3,12 +3,12 @@ A working modern computer in Minecraft made using VirtualBox and Java. No client
|
|||
|
||||
## Video
|
||||
|
||||
[![Video](https://img.youtube.com/vi/K4t98YUIm4M/0.jpg)](https://www.youtube.com/watch?v=K4t98YUIm4M)
|
||||
[![Original video](https://img.youtube.com/vi/VxSyDfxPd3s/0.jpg)](https://www.youtube.com/watch?v=VxSyDfxPd3s)
|
||||
|
||||
## Installation requirements
|
||||
*Note: only the server host has to meet these requirements.*
|
||||
|
||||
* A non-Windows OS supported by VirtualBox
|
||||
* A non-Windows OS
|
||||
* Root (sudo) privileges
|
||||
* Enough resources to run another OS
|
||||
|
||||
|
@ -44,7 +44,7 @@ A working modern computer in Minecraft made using VirtualBox and Java. No client
|
|||
*Please give appropriate credit and link to this page if you use this machine in one of your videos/creations/etc.*
|
||||
|
||||
### Display
|
||||
*Note: You can create as many displays as you want, but all of them will show the same currently.*
|
||||
*Note:* You can create as many displays as you want, but all of them will show the same currently.
|
||||
|
||||
To create a display, make a 5 wide 4 high wall of item frames, then put the maps 0-19 on them starting from the top left and going downwards. You can give the maps with this command:
|
||||
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<artifactId>VirtualComputer-Core</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<build>
|
||||
<finalName>VirtualComputer</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
|
@ -20,7 +25,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
@ -32,68 +37,34 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>repo</id>
|
||||
<url>file://${basedir}/repo</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.12-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency> <!-- This has the least additional (platform-dependent) code -->
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<version>6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<version>6.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.jnr/jnr-ffi -->
|
||||
<dependency>
|
||||
<groupId>com.github.jnr</groupId>
|
||||
<artifactId>jnr-ffi</artifactId>
|
||||
<version>2.1.7</version>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<version>6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.aparapi</groupId>
|
||||
<artifactId>aparapi</artifactId>
|
||||
<version>1.10.0</version>
|
||||
<version>2.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.16.16</version>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer-XPCOM</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency> <!-- javax.annotations.Nullable -->
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<dependency>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer-MSCOM</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>me.lucko</groupId>
|
||||
<artifactId>commodore</artifactId>
|
||||
<version>1.8</version>
|
||||
</dependency> -->
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
|
@ -112,8 +83,26 @@
|
|||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.virtualbox:VirtualBox-MSCOM</exclude>
|
||||
<exclude>io.github.NorbiPeti:VirtualComputer-MSCOM</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<!-- <minimizeJar>true</minimizeJar> -->
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>io.github.NorbiPeti:VirtualComputer-XPCOM</artifact>
|
||||
<includes>
|
||||
<include>**
|
||||
</include> <!-- Don't delete org.mozilla.interfaces.internal when minimizing -->
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>com.github.jnr:jnr-ffi</artifact>
|
||||
<includes>
|
||||
<include>**
|
||||
</include> <!-- Don't delete org.mozilla.interfaces.internal when minimizing -->
|
||||
</includes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -137,6 +126,7 @@
|
|||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.virtualbox:VirtualBox</exclude>
|
||||
<exclude>io.github.NorbiPeti:VirtualComputer-XPCOM</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
|
@ -0,0 +1,393 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import lombok.var;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.virtualbox_6_1.*;
|
||||
import sznp.virtualcomputer.events.MachineEventHandler;
|
||||
import sznp.virtualcomputer.events.VBoxEventHandler;
|
||||
import sznp.virtualcomputer.renderer.GPURenderer;
|
||||
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
||||
import sznp.virtualcomputer.util.COMUtils;
|
||||
import sznp.virtualcomputer.util.Scancode;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class Computer {
|
||||
@Getter
|
||||
private static Computer instance;
|
||||
|
||||
private final PluginMain plugin;
|
||||
private ISession session;
|
||||
private final IVirtualBox vbox;
|
||||
private IMachine machine;
|
||||
private MachineEventHandler handler;
|
||||
private IEventListener listener;
|
||||
private final VirtualBoxManager manager;
|
||||
private MCFrameBuffer framebuffer;
|
||||
private final boolean direct;
|
||||
private final boolean embedded;
|
||||
|
||||
public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox, boolean direct, boolean embedded) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
session = manager.getSessionObject();
|
||||
this.vbox = vbox;
|
||||
this.direct = direct;
|
||||
this.embedded = embedded;
|
||||
if (instance != null) throw new IllegalStateException("A computer already exists!");
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public void Start(CommandSender sender, int index) {// TODO: Add touchscreen support (#2)
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
sender.sendMessage("§cThe machine is already running!");
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (index < 0 || vbox.getMachines().size() <= index) {
|
||||
sendMessage(sender, "§cMachine not found!");
|
||||
return;
|
||||
}
|
||||
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
|
||||
synchronized (this) {
|
||||
if (embedded)
|
||||
machine.lockMachine(session, LockType.VM); //Run in our process <-- Need the VM type to have console access
|
||||
else {
|
||||
val progress = machine.launchVMProcess(session, "headless", Collections.emptyList()); //Run in a separate process
|
||||
onStartSetProgress(progress, sender);
|
||||
}
|
||||
}
|
||||
} 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: "The object in question already exists." on second start
|
||||
} else {
|
||||
sendMessage(sender, "§cFailed to start computer: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when the machine is locked after {@link #Start(CommandSender, int)}
|
||||
*
|
||||
* @param sender The sender which started the machine
|
||||
*/
|
||||
public void onLock(CommandSender sender) {
|
||||
System.out.println("A");
|
||||
if (session.getState() != SessionState.Locked) {
|
||||
sendMessage(sender, "§cFailed to start computer! Failed to lock session");
|
||||
return;
|
||||
}
|
||||
machine = session.getMachine(); // This is the Machine object we can work with
|
||||
final IConsole console = session.getConsole();
|
||||
if (embedded) { //Otherwise it's set while starting the VM
|
||||
IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2
|
||||
onStartSetProgress(progress, sender);
|
||||
}
|
||||
System.out.println("B");
|
||||
System.out.println("State: " + console.getState());
|
||||
listener = handler.registerTo(console.getEventSource());
|
||||
System.out.println("State: " + console.getState());
|
||||
val fb = new MCFrameBuffer(console.getDisplay(), plugin, embedded, direct);
|
||||
System.out.println("C");
|
||||
if (embedded)
|
||||
fb.startEmbedded();
|
||||
String fbid = console.getDisplay().attachFramebuffer(0L,
|
||||
COMUtils.gimmeAFramebuffer(fb));
|
||||
System.out.println("State: " + console.getState());
|
||||
System.out.println("D"); //TODO: No UpdateImage
|
||||
fb.setId(fbid);
|
||||
framebuffer = fb;
|
||||
}
|
||||
|
||||
private void onStartSetProgress(IProgress progress, CommandSender sender) {
|
||||
if (handler != null)
|
||||
handler.disable();
|
||||
handler = new MachineEventHandler(Computer.this, sender);
|
||||
handler.setProgress(progress);
|
||||
handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way?
|
||||
}
|
||||
|
||||
private void sendMessage(@Nullable CommandSender sender, String message) {
|
||||
if (sender != null)
|
||||
sender.sendMessage(message);
|
||||
plugin.getLogger().warning((sender == null ? "" : sender.getName() + ": ") + ChatColor.stripColor(message));
|
||||
}
|
||||
|
||||
public void Stop(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender)) {
|
||||
if (session.getState().equals(SessionState.Locked)) {
|
||||
onMachineStop(sender); //Needed for session reset
|
||||
sendMessage(sender, "§eComputer was already off, released it.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
sendMessage(sender, "§eStopping computer...");
|
||||
synchronized (this) {
|
||||
session.getConsole().powerDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void PowerButton(CommandSender sender, int index) {
|
||||
sendMessage(sender, "§ePressing powerbutton...");
|
||||
Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (session.getState() != SessionState.Locked || session.getMachine() == null) {
|
||||
Start(sender, index);
|
||||
} else {
|
||||
synchronized (this) {
|
||||
session.getConsole().powerButton();
|
||||
}
|
||||
sendMessage(sender, "§ePowerbutton pressed.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Reset(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
sendMessage(sender, "§eResetting computer...");
|
||||
synchronized (this) {
|
||||
session.getConsole().reset();
|
||||
}
|
||||
sendMessage(sender, "§eComputer reset.");
|
||||
}
|
||||
|
||||
public void SaveState(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
sendMessage(sender, "§eSaving computer state...");
|
||||
synchronized (this) {
|
||||
session.getMachine().saveState();
|
||||
}
|
||||
}
|
||||
|
||||
public void FixScreen(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
if (framebuffer == null) {
|
||||
sender.sendMessage("§cFramebuffer is null...");
|
||||
return;
|
||||
}
|
||||
sendMessage(sender, "§eFixing screen...");
|
||||
try {
|
||||
synchronized (this) {
|
||||
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 (this) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkMachineNotRunning(@Nullable CommandSender sender) {
|
||||
if (session.getState() != SessionState.Locked || machine.getState() != MachineState.Running) {
|
||||
if (sender != null)
|
||||
sender.sendMessage("§cMachine isn't running.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void PressKey(CommandSender sender, String key, String stateorduration) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
int durationorstate;
|
||||
if (stateorduration.length() == 0)
|
||||
durationorstate = 0;
|
||||
else if (stateorduration.equalsIgnoreCase("down"))
|
||||
durationorstate = -1;
|
||||
else if (stateorduration.equalsIgnoreCase("up"))
|
||||
durationorstate = -2;
|
||||
else
|
||||
durationorstate = Short.parseShort(stateorduration);
|
||||
int code = Scancode.getCode("sc_" + key.toLowerCase());
|
||||
if (code == -1) {
|
||||
sender.sendMessage("§cUnknown key: " + key);
|
||||
return;
|
||||
}
|
||||
// Release key scan code concept taken from VirtualBox source code (KeyboardImpl.cpp:putCAD())
|
||||
// +128
|
||||
synchronized (this) {
|
||||
if (durationorstate != -2)
|
||||
session.getConsole().getKeyboard().putScancode(code);
|
||||
Runnable sendrelease = () -> session.getConsole().getKeyboard().putScancodes(Lists.newArrayList(code + 128,
|
||||
Scancode.sc_controlLeft.Code + 128, Scancode.sc_shiftLeft.Code + 128, Scancode.sc_altLeft.Code + 128));
|
||||
if (durationorstate == 0 || durationorstate == -2)
|
||||
sendrelease.run();
|
||||
if (durationorstate > 0) {
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, sendrelease, durationorstate * 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs, boolean down) throws Exception {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
int state = 0;
|
||||
if (mbs.length() > 0 && down)
|
||||
state = Arrays.stream(MouseButtonState.values()).filter(mousebs -> mousebs.name().equalsIgnoreCase(mbs))
|
||||
.findAny().orElseThrow(() -> new Exception("Unknown mouse button")).value();
|
||||
synchronized (this) {
|
||||
session.getConsole().getMouse().putMouseEvent(x, y, z, w, state);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs) throws Exception {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
UpdateMouse(sender, x, y, z, w, mbs, true);
|
||||
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) {
|
||||
if (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(", ")));
|
||||
} else if (machine.getState().value() < MachineState.FirstOnline.value()
|
||||
|| machine.getState().value() > MachineState.LastOnline.value())
|
||||
sender.sendMessage("§bUse /c stop to fix the computer.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onMachineStart(CommandSender sender) {
|
||||
sendMessage(sender, "§eComputer started.");
|
||||
}
|
||||
|
||||
public void onMachineStop(CommandSender sender) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
synchronized (this) {
|
||||
session.unlockMachine(); //Needs to be outside of the event handler
|
||||
}
|
||||
handler = null;
|
||||
machine = null;
|
||||
session = manager.getSessionObject();
|
||||
sendMessage(sender, "§eComputer powered off."); //This block runs later
|
||||
}
|
||||
});
|
||||
GPURenderer.update(new byte[1], 0, 0, 0, 0, 640, 480); //Black screen
|
||||
stopEvents();
|
||||
MouseLockerPlayerListener.computerStop();
|
||||
if (framebuffer != null)
|
||||
framebuffer.stop();
|
||||
}
|
||||
|
||||
private void stopEvents() {
|
||||
/*if(session!=null && listener!=null)
|
||||
session.getConsole().getEventSource().unregisterListener(listener);*/
|
||||
if (listener != null)
|
||||
handler.disable();
|
||||
listener = null;
|
||||
}
|
||||
|
||||
public void pluginDisable(CommandSender ccs) {
|
||||
stopEvents();
|
||||
if (framebuffer != null)
|
||||
framebuffer.stop();
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
if (session.getMachine().getState().equals(MachineState.Running)) {
|
||||
ccs.sendMessage("§aSaving machine state...");
|
||||
session.getMachine().saveState().waitForCompletion(10000);
|
||||
}
|
||||
session.unlockMachine();
|
||||
}
|
||||
instance = null; //Allow setting it again
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import buttondevteam.lib.chat.Command2;
|
||||
import buttondevteam.lib.chat.CommandClass;
|
||||
import buttondevteam.lib.chat.CustomTabComplete;
|
||||
import buttondevteam.lib.chat.ICommand2MC;
|
||||
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.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.MapMeta;
|
||||
import org.virtualbox_6_1.VBoxException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
|
||||
@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) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().Status(sender);
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().Start(sender, index);
|
||||
}
|
||||
|
||||
@Command2.Subcommand
|
||||
public void list(CommandSender sender) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().List(sender);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(aliases = {"poweroff", "off", "kill"})
|
||||
public void stop(CommandSender sender) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().Stop(sender);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(aliases = {"powerbtn", "pwrbtn"})
|
||||
public void powerbutton(CommandSender sender, @Command2.OptionalArg int index) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().PowerButton(sender, index);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(aliases = "restart")
|
||||
public void reset(CommandSender sender) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().Reset(sender);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(aliases = "save state")
|
||||
public void save(CommandSender sender) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().SaveState(sender);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(aliases = "fix screen")
|
||||
public void fix(CommandSender sender) {
|
||||
if (checkDisabled(sender)) return;
|
||||
Computer.getInstance().FixScreen(sender);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(aliases = {"press", "presskey", "keypress"}, helpText = {
|
||||
"Press key",
|
||||
"Presses the specified key. Valid values for the last param are 'down', 'up' and amount of ticks to hold."
|
||||
})
|
||||
public void key(CommandSender sender, String key, @Command2.OptionalArg String stateorduration) {
|
||||
if (checkDisabled(sender)) return;
|
||||
if (stateorduration == null) stateorduration = "";
|
||||
Computer.getInstance().PressKey(sender, key, stateorduration);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(helpText = {
|
||||
"Mouse event",
|
||||
"Move the mouse by the specified offset or press the given button."
|
||||
})
|
||||
public void mouse(CommandSender sender, String keyOrX, @Command2.OptionalArg int y, @Command2.OptionalArg int z, @Command2.OptionalArg int w) {
|
||||
if (checkDisabled(sender)) return;
|
||||
try {
|
||||
if (y != 0 || z != 0 || w != 0) {
|
||||
int x = Integer.parseInt(keyOrX);
|
||||
Computer.getInstance().UpdateMouse(sender, x, y, z, w, "");
|
||||
} else
|
||||
Computer.getInstance().UpdateMouse(sender, 0, 0, 0, 0, keyOrX);
|
||||
} catch (VBoxException e) {
|
||||
throw e;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Command2.Subcommand(helpText = {
|
||||
"Show keyboard",
|
||||
"Displays a keyboard in chat that you can click on.",
|
||||
"Each keypress is actually a command so they will be logged by the server."
|
||||
})
|
||||
public void show_keyboard(CommandSender sender) {
|
||||
sender.sendMessage("§6Avoid entering sensitive info here unless you own the server");
|
||||
var cc = new TextComponent();
|
||||
for (int i = 0; i < 10; i++)
|
||||
cc.addExtra(keyComp(i + "", null));
|
||||
var row = new String[]{"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"};
|
||||
for (String s : row) cc.addExtra(keyComp(s, null));
|
||||
cc.addExtra(keyComp("-", "minus"));
|
||||
cc.addExtra(keyComp("=", "equals"));
|
||||
cc.addExtra(keyComp("Backspace", "backspace"));
|
||||
cc.addExtra(keyComp("Tab", "tab"));
|
||||
sender.spigot().sendMessage(cc);
|
||||
|
||||
cc = new TextComponent();
|
||||
row = new String[]{"A", "S", "D", "F", "G", "H", "J", "K", "L"};
|
||||
for (String s : row) cc.addExtra(keyComp(s, null)); //TODO: File format for layouts
|
||||
|
||||
cc.addExtra(keyComp("{", "bracketLeft"));
|
||||
cc.addExtra(keyComp("}", "bracketRight"));
|
||||
|
||||
sender.spigot().sendMessage(cc);
|
||||
}
|
||||
|
||||
private TextComponent keyComp(String name, @Nullable String code) {
|
||||
if (code == null) code = name;
|
||||
var tc = new TextComponent("[" + name + "] ");
|
||||
tc.setColor(ChatColor.AQUA);
|
||||
tc.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/c key " + code));
|
||||
return tc;
|
||||
}
|
||||
|
||||
@Command2.Subcommand(helpText = {
|
||||
"Lock mouse",
|
||||
"Locks the mouse to where you're looking. If you move your mouse, the computer's mouse will be moved."
|
||||
})
|
||||
public void lock_mouse(Player player) {
|
||||
if (checkDisabled(player)) return;
|
||||
MouseLockerPlayerListener.toggleLock(player);
|
||||
}
|
||||
|
||||
@Command2.Subcommand(helpText = {
|
||||
"Set mouse speed",
|
||||
"Sets the mouse speed when locked. The default is 5."
|
||||
})
|
||||
public void lock_speed(CommandSender sender, float speed) {
|
||||
if (checkDisabled(sender)) return;
|
||||
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) {
|
||||
if (checkDisabled(player)) return;
|
||||
var loc = player.getLocation();
|
||||
var world = Objects.requireNonNull(loc.getWorld());
|
||||
short id = PluginMain.Instance.startID.get();
|
||||
for (int j = PluginMain.MCY - 1; j >= 0; j--) {
|
||||
for (int i = 0; i < PluginMain.MCX; i++) {
|
||||
var block = world.getBlockAt(loc.getBlockX() + i, loc.getBlockY() + j, loc.getBlockZ());
|
||||
block.setType(Material.BLACK_WOOL);
|
||||
var frameLoc = block.getLocation().add(0, 0, 1);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Command2.Subcommand(helpText = {
|
||||
"Set layout",
|
||||
"This command sets the keyboard layout used for /c show keyboard.",
|
||||
"Valid options are files in the layouts folder in the plugin's directory."
|
||||
})
|
||||
public void set_layout(CommandSender sender, String layout) {
|
||||
var lf = PluginMain.Instance.layoutFolder;
|
||||
if (!lf.mkdirs()) {
|
||||
sender.sendMessage("§cFailed to create layouts folder!");
|
||||
return;
|
||||
}
|
||||
var l = new File(lf, layout + ".yml");
|
||||
if (!l.exists()) {
|
||||
sender.sendMessage("§cThe file " + l + " does not exist.");
|
||||
return;
|
||||
}
|
||||
var yc = YamlConfiguration.loadConfiguration(l);
|
||||
var list = yc.getList("keyboard");
|
||||
if (list == null) {
|
||||
sender.sendMessage("§cThe 'keyboard' key is missing.");
|
||||
return;
|
||||
}
|
||||
for (var item : list) {
|
||||
System.out.println("item: " + item);
|
||||
}
|
||||
}
|
||||
|
||||
@Command2.Subcommand(helpText = {
|
||||
"Plugin enable/disable",
|
||||
"Use this command to enable or disable the plugin.",
|
||||
"This can be useful to save resources if it is unused."
|
||||
})
|
||||
public boolean plugin(CommandSender sender, @CustomTabComplete({"enable", "disable"}) String enableDisable) {
|
||||
switch (enableDisable) {
|
||||
case "enable":
|
||||
sender.sendMessage("§bEnabling plugin...");
|
||||
PluginMain.Instance.reloadConfig();
|
||||
PluginMain.Instance.pluginEnableInternal();
|
||||
sender.sendMessage("§bPlugin enabled! More info on console.");
|
||||
break;
|
||||
case "disable":
|
||||
sender.sendMessage("§aDisabling plugin...");
|
||||
PluginMain.Instance.pluginDisableInternal();
|
||||
sender.sendMessage("§aPlugin disabled! More info on console.");
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkDisabled(CommandSender sender) {
|
||||
boolean disabled = !PluginMain.isPluginEnabled();
|
||||
if (disabled)
|
||||
sender.sendMessage("The plugin is currently disabled. Do /c plugin enable to enable it.");
|
||||
return disabled;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import buttondevteam.lib.architecture.ButtonPlugin;
|
||||
import buttondevteam.lib.architecture.ConfigData;
|
||||
import jnr.ffi.LibraryLoader;
|
||||
import lombok.Getter;
|
||||
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;
|
||||
import org.virtualbox_6_1.VirtualBoxManager;
|
||||
import sznp.virtualcomputer.events.VBoxEventHandler;
|
||||
import sznp.virtualcomputer.renderer.BukkitRenderer;
|
||||
import sznp.virtualcomputer.renderer.GPURenderer;
|
||||
import sznp.virtualcomputer.util.Utils;
|
||||
import sznp.virtualcomputer.util.VBoxLib;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PluginMain extends ButtonPlugin {
|
||||
public static final int MCX = 5;
|
||||
public static final int MCY = 4;
|
||||
private BukkitTask mousetask;
|
||||
private VBoxEventHandler listener;
|
||||
private VirtualBoxManager manager;
|
||||
|
||||
public static PluginMain Instance;
|
||||
@Getter
|
||||
private static boolean pluginEnabled; //The Bukkit plugin has to be enabled for the enable command to work
|
||||
|
||||
/**
|
||||
* 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. May require root on Linux.
|
||||
*/
|
||||
private final ConfigData<Boolean> useGPU = getIConfig().getData("useGPU", true);
|
||||
/**
|
||||
* The virtual machine will be hosted inside the server process if this option is enabled.
|
||||
* This can improve performance but may cause stability and other issues.
|
||||
*/
|
||||
public final ConfigData<Boolean> runEmbedded = getIConfig().getData("runEmbedded", true);
|
||||
/**
|
||||
* Determines the keyboard layout to use for /c show keyboard. Layouts can be defined in VirtualComputer/layouts/.
|
||||
*/
|
||||
private final ConfigData<String> kbLayout = getIConfig().getData("kbLayout", "en");
|
||||
public File layoutFolder = new File(getDataFolder(), "layouts");
|
||||
/**
|
||||
* When set to false, the plugin will not initialize on server startup and its only valid command will be /c plugin enable.
|
||||
* 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);
|
||||
/**
|
||||
* 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
|
||||
public void pluginEnable() {
|
||||
registerCommand(new ComputerCommand());
|
||||
Instance = this;
|
||||
if (autoEnable.get())
|
||||
pluginEnableInternal();
|
||||
else
|
||||
getLogger().info("Auto-enable is disabled. Enable with /c plugin enable.");
|
||||
}
|
||||
|
||||
void pluginEnableInternal() {
|
||||
if (pluginEnabled)
|
||||
return;
|
||||
pluginEnabled = true;
|
||||
try {
|
||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||
ccs.sendMessage("§bInitializing VirtualBox...");
|
||||
String osname = System.getProperty("os.name").toLowerCase();
|
||||
final boolean windows;
|
||||
String vbpath;
|
||||
{
|
||||
boolean win = false;
|
||||
vbpath = osname.contains("mac")
|
||||
? "/Applications/VirtualBox.app/Contents/MacOS"
|
||||
: (win = osname.contains("windows"))
|
||||
? "C:\\Program Files\\Oracle\\VirtualBox"
|
||||
: "/opt/virtualbox";
|
||||
windows = win;
|
||||
}
|
||||
//noinspection ConstantConditions
|
||||
Predicate<File> notGoodDir = ff -> !ff.isDirectory() || (!windows && Arrays.stream(ff.list()).noneMatch(s -> s.contains("xpcom")));
|
||||
if (notGoodDir.test(new File(vbpath)))
|
||||
vbpath = "/usr/lib/virtualbox";
|
||||
if (notGoodDir.test(new File(vbpath)))
|
||||
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())
|
||||
System.setProperty("vbox.home", vbpath);
|
||||
if (System.getProperty("sun.boot.library.path") == null
|
||||
|| System.getProperty("sun.boot.library.path").isEmpty())
|
||||
System.setProperty("sun.boot.library.path", vbpath);
|
||||
if (System.getProperty("java.library.path") == null || System.getProperty("java.library.path").isEmpty())
|
||||
System.setProperty("java.library.path", vbpath);
|
||||
Utils.addLibraryPath(vbpath); //TODO: Jacob DLL must be in the server folder
|
||||
final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath());
|
||||
if (!windows && runEmbedded.get()) {
|
||||
VBoxLib vbl = LibraryLoader.create(VBoxLib.class).load("vboxjxpcom");
|
||||
vbl.RTR3InitExe(0, "", 0);
|
||||
}
|
||||
IVirtualBox vbox = manager.getVBox();
|
||||
(listener = new VBoxEventHandler()).registerTo(vbox.getEventSource());
|
||||
this.manager = manager;
|
||||
ccs.sendMessage("§bLoading Screen...");
|
||||
boolean direct;
|
||||
try {
|
||||
if (useGPU.get()) {
|
||||
setupDirectRendering(ccs);
|
||||
direct = true;
|
||||
} else {
|
||||
setupBukkitRendering(ccs);
|
||||
direct = false;
|
||||
}
|
||||
} catch (NoClassDefFoundError | Exception e) {
|
||||
e.printStackTrace();
|
||||
setupBukkitRendering(ccs);
|
||||
direct = false;
|
||||
}
|
||||
new Computer(this, manager, vbox, direct, runEmbedded.get()); //Saves itself
|
||||
ccs.sendMessage("§bLoaded!");
|
||||
val mlpl = new MouseLockerPlayerListener();
|
||||
mousetask = getServer().getScheduler().runTaskTimer(this, mlpl, 0, 0);
|
||||
getServer().getPluginManager().registerEvents(mlpl, this);
|
||||
|
||||
} catch (final Exception e) {
|
||||
error(null, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupDirectRendering(CommandSender ccs) throws Exception {
|
||||
for (short i = 0; i < MCX; i++)
|
||||
for (short j = 0; j < MCY; j++)
|
||||
new GPURenderer((short) (startID.get() + j * MCX + i), Bukkit.getWorlds().get(0), sendAll.get(), i, j);
|
||||
ccs.sendMessage("§bUsing Direct Renderer, all good");
|
||||
}
|
||||
|
||||
private void setupBukkitRendering(CommandSender ccs) {
|
||||
for (short i = 0; i < MCX * MCY; i++)
|
||||
new BukkitRenderer((short) (startID.get() + i), Bukkit.getWorlds().get(0), i * 128 * 128 * 4, getLogger());
|
||||
ccs.sendMessage("§6Using Bukkit renderer");
|
||||
}
|
||||
|
||||
private void error(String message, Exception e) { //Message OR exception
|
||||
getLogger().severe("A fatal error occured, disabling plugin!");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
if (message != null)
|
||||
throw new RuntimeException(message);
|
||||
else
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pluginDisable() {
|
||||
pluginDisableInternal();
|
||||
}
|
||||
|
||||
void pluginDisableInternal() {
|
||||
if (!pluginEnabled)
|
||||
return;
|
||||
pluginEnabled = false;
|
||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||
if (mousetask != null)
|
||||
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)
|
||||
}*/
|
||||
if (listener != null)
|
||||
listener.disable(); //The save progress wait locks with the event
|
||||
if (Computer.getInstance() != null)
|
||||
Computer.getInstance().pluginDisable(ccs);
|
||||
ccs.sendMessage("§aHuh.");
|
||||
saveConfig();
|
||||
manager.cleanup();
|
||||
}
|
||||
|
||||
}
|
|
@ -2,12 +2,11 @@ package sznp.virtualcomputer.events;
|
|||
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.mozilla.interfaces.IEvent;
|
||||
import org.mozilla.interfaces.IEventListener;
|
||||
import org.virtualbox_6_0.IEventSource;
|
||||
import org.virtualbox_6_0.VBoxEventType;
|
||||
import sznp.virtualcomputer.util.COMObjectBase;
|
||||
import sznp.virtualcomputer.util.Utils;
|
||||
import org.virtualbox_6_1.IEvent;
|
||||
import org.virtualbox_6_1.IEventSource;
|
||||
import org.virtualbox_6_1.VBoxEventType;
|
||||
import sznp.virtualcomputer.util.COMUtils;
|
||||
import sznp.virtualcomputer.util.IEventHandler;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -17,31 +16,29 @@ import java.util.Map;
|
|||
/**
|
||||
* A Bukkit-like event system which calls the appropriate methods on an event.
|
||||
*/
|
||||
public abstract class EventHandlerBase extends COMObjectBase implements IEventListener {
|
||||
public abstract class EventHandlerBase implements IEventHandler {
|
||||
/**
|
||||
* 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 final Map<VBoxEventType, Class<? extends org.virtualbox_6_1.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));
|
||||
protected EventHandlerBase(Map<VBoxEventType, Class<? extends org.virtualbox_6_1.IEvent>> eventMap) {
|
||||
this.eventMap = eventMap;
|
||||
}
|
||||
|
||||
@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();
|
||||
val kv = eventMap.entrySet().stream().filter(e -> e.getKey().value() == iEvent.getType().value()).findAny();
|
||||
if (!kv.isPresent()) return; //Event not supported
|
||||
val cl = kv.get().getValue();
|
||||
for (Method method : getClass().getMethods()) {
|
||||
if (method.isAnnotationPresent(org.bukkit.event.EventHandler.class)
|
||||
&& method.getParameterCount() == 1 && method.getParameterTypes()[0] == cl) {
|
||||
try {
|
||||
method.invoke(this, Utils.getEvent(iEvent, cl));
|
||||
method.invoke(this, COMUtils.getEvent(iEvent, cl));
|
||||
return;
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
|
@ -53,8 +50,8 @@ public abstract class EventHandlerBase extends COMObjectBase implements IEventLi
|
|||
}
|
||||
}
|
||||
|
||||
public <T extends EventHandlerBase> org.virtualbox_6_0.IEventListener registerTo(IEventSource source) {
|
||||
return Utils.registerListener(source, this, new ArrayList<>(eventMap.keySet()));
|
||||
public <T extends EventHandlerBase> org.virtualbox_6_1.IEventListener registerTo(IEventSource source) {
|
||||
return COMUtils.registerListener(source, this, new ArrayList<>(eventMap.keySet()));
|
||||
}
|
||||
|
||||
public void disable() {
|
|
@ -6,9 +6,9 @@ import lombok.experimental.var;
|
|||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.virtualbox_6_0.IProgress;
|
||||
import org.virtualbox_6_0.IStateChangedEvent;
|
||||
import org.virtualbox_6_0.VBoxEventType;
|
||||
import org.virtualbox_6_1.IProgress;
|
||||
import org.virtualbox_6_1.IStateChangedEvent;
|
||||
import org.virtualbox_6_1.VBoxEventType;
|
||||
import sznp.virtualcomputer.Computer;
|
||||
import sznp.virtualcomputer.PluginMain;
|
||||
|
||||
|
@ -38,17 +38,22 @@ 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, () -> {
|
||||
if (progress == null) return;
|
||||
progress.waitForCompletion(-1);
|
||||
if (progress != null && progress.getCompleted() && progress.getResultCode() != 0) {
|
||||
if (progress.getCompleted() && progress.getResultCode() != 0) {
|
||||
Logger l = PluginMain.Instance.getLogger();
|
||||
l.warning("Result code: " + Integer.toHexString(progress.getResultCode()));
|
||||
for (var info = progress.getErrorInfo(); info != null; info = info.getNext()) {
|
||||
l.warning("----------------");
|
||||
l.warning("VBox: " + info.getText());
|
||||
l.warning("Component: " + info.getComponent());
|
||||
if (info.getResultCode() == 0x80004005 && info.getResultDetail() == 0xFFFFF88B)
|
||||
l.warning("The server cannot access the VirtualBox driver. Either run it as root or disable the 'runEmbedded' config option. Make sure to only run plugins you trust as root.");
|
||||
else {
|
||||
l.warning("VBox: " + info.getText());
|
||||
l.warning("Component: " + info.getComponent());
|
||||
l.warning("Error codes: " + Integer.toHexString(info.getResultCode()) + " " + Integer.toHexString(info.getResultDetail()));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
package sznp.virtualcomputer.events;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.virtualbox_6_1.ISessionStateChangedEvent;
|
||||
import org.virtualbox_6_1.SessionState;
|
||||
import org.virtualbox_6_1.VBoxEventType;
|
||||
import sznp.virtualcomputer.Computer;
|
||||
|
||||
public class VBoxEventHandler extends EventHandlerBase {
|
||||
public VBoxEventHandler() {
|
||||
super(ImmutableMap.of(VBoxEventType.OnSessionStateChanged, ISessionStateChangedEvent.class));
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static VBoxEventHandler instance;
|
||||
private String machineID;
|
||||
private CommandSender sender;
|
||||
|
||||
@EventHandler
|
||||
public void onSessionStateChange(ISessionStateChangedEvent event) {
|
||||
if (!event.getMachineId().equals(machineID)) return;
|
||||
try {
|
||||
if (event.getState() == SessionState.Locked) //Need to check here, because we can't access the console yet
|
||||
Computer.getInstance().onLock(sender);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage("§cFailed to start computer! See the console for more details.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void setup(String machineID, CommandSender sender) {
|
||||
this.machineID = machineID;
|
||||
this.sender = sender;
|
||||
}
|
||||
}
|
|
@ -10,30 +10,33 @@ import java.awt.image.BufferedImage;
|
|||
import java.awt.image.DataBufferInt;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BukkitRenderer extends MapRenderer implements IRenderer {
|
||||
private ByteBuffer allpixels;
|
||||
private BufferedImage image;
|
||||
private int startindex;
|
||||
private static ByteBuffer allpixels;
|
||||
private final BufferedImage image;
|
||||
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) {
|
||||
this.allpixels = allpixels;
|
||||
public static void update(ByteBuffer allpixels, int x, int y, int width, int height) {
|
||||
BukkitRenderer.allpixels = allpixels; //TODO: Only update what actually changes
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic implementation, should work on most versions
|
||||
*
|
||||
* @param id
|
||||
* The ID of the current map
|
||||
* @param world
|
||||
* The world to create new maps in
|
||||
* @param startindex
|
||||
* The index to start from in allpixels
|
||||
* @param id The ID of the current map
|
||||
* @param world The world to create new maps in
|
||||
* @param startindex The index
|
||||
* @param logger The plugin's logger
|
||||
*/
|
||||
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);
|
||||
map.addRenderer(this);
|
||||
this.startindex = startindex;
|
||||
|
@ -72,12 +75,12 @@ public class BukkitRenderer extends MapRenderer implements IRenderer {
|
|||
|
||||
long diff = System.nanoTime() - time;
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Progess: " + progress);
|
||||
System.out.println("UpdatePixels: " + updatepixels);
|
||||
logger.warning("Progess: " + progress);
|
||||
logger.warning("UpdatePixels: " + updatepixels);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package sznp.virtualcomputer.renderer;
|
||||
|
||||
import net.minecraft.server.v1_12_R1.WorldMap;
|
||||
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;
|
||||
|
@ -12,19 +15,26 @@ import sznp.virtualcomputer.util.Timing;
|
|||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class GPURenderer extends MapRenderer implements IRenderer {
|
||||
private byte[] buffer;
|
||||
private final GPURendererInternal kernel;
|
||||
private WorldMap wmap;
|
||||
private final boolean sendAll;
|
||||
private int mapx, mapy;
|
||||
//Store at central location after conversion
|
||||
private static int[] colors_;
|
||||
private int changedX = 0, changedY = 0, changedWidth = 640, changedHeight = 480;
|
||||
private static ArrayList<GPURenderer> renderers = new ArrayList<>();
|
||||
private BiConsumer<Integer, Integer> flagDirty; //This way it's version independent, as long as it's named the same
|
||||
private static final ArrayList<GPURenderer> renderers = new ArrayList<>();
|
||||
private static Method flagDirtyMethod;
|
||||
private static boolean enabled = true;
|
||||
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);
|
||||
if (map == null) {
|
||||
kernel = null;
|
||||
|
@ -44,15 +54,29 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
|||
this.mapy = mapy;
|
||||
Field field = map.getClass().getDeclaredField("worldMap");
|
||||
field.setAccessible(true);
|
||||
wmap = (WorldMap) field.get(map);
|
||||
val wmap = field.get(map);
|
||||
if (flagDirtyMethod == null)
|
||||
flagDirtyMethod = wmap.getClass().getMethod("flagDirty", int.class, int.class);
|
||||
flagDirty = (x, y) -> {
|
||||
try {
|
||||
flagDirtyMethod.invoke(wmap, x, y);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
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);
|
||||
enabled = true; //Enable at each plugin (re)enable
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MapView map, MapCanvas canvas, Player player) {
|
||||
if (!enabled) return;
|
||||
Timing t = new Timing();
|
||||
try {
|
||||
if (kernel.isRendered()) return;
|
||||
|
@ -61,7 +85,15 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
|||
field.setAccessible(true);
|
||||
buffer = (byte[]) field.get(canvas);
|
||||
}
|
||||
if (!PluginMain.sendAll) {
|
||||
if (!warned) { //Only print once
|
||||
warned = true;
|
||||
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 (!sendAll) {
|
||||
synchronized (kernel) {
|
||||
if (changedX >= (mapx + 1) * 128 || changedY >= (mapy + 1) * 128
|
||||
|| changedX + changedWidth < mapx * 128 || changedY + changedHeight < mapy * 128) {
|
||||
|
@ -79,28 +111,33 @@ public class GPURenderer extends MapRenderer implements IRenderer {
|
|||
int yy = y + changedHeight >= 128 ? 127 : y + changedHeight;
|
||||
//System.out.println("local: ("+x+", "+y+") "+w+"x"+h);
|
||||
kernel.render(buffer);
|
||||
wmap.flagDirty(x, y);
|
||||
wmap.flagDirty(xx, yy); // Send the changes only
|
||||
flagDirty.accept(x, y);
|
||||
flagDirty.accept(xx, yy); // Send the changes only
|
||||
changedX = Integer.MAX_VALUE; //Finished rendering
|
||||
changedY = Integer.MAX_VALUE; //TODO: Render as soon as we receive new image
|
||||
changedWidth = -1; //Finished rendering
|
||||
changedHeight = -1;
|
||||
} else {
|
||||
kernel.render(buffer);
|
||||
wmap.flagDirty(0, 0);
|
||||
wmap.flagDirty(127, 127); // Send everything
|
||||
flagDirty.accept(0, 0);
|
||||
flagDirty.accept(127, 127); // Send everything
|
||||
}
|
||||
} catch (Exception e) {
|
||||
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) {
|
||||
for (GPURenderer r : renderers) {
|
||||
synchronized (r.kernel) {
|
||||
if (!PluginMain.sendAll) {
|
||||
if (!r.sendAll) {
|
||||
if (changedX < r.changedX)
|
||||
r.changedX = changedX;
|
||||
if (changedY < r.changedY)
|
|
@ -0,0 +1,209 @@
|
|||
package sznp.virtualcomputer.renderer;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.virtualbox_6_1.Holder;
|
||||
import org.virtualbox_6_1.IDisplay;
|
||||
import org.virtualbox_6_1.IDisplaySourceBitmap;
|
||||
import org.virtualbox_6_1.VBoxException;
|
||||
import sznp.virtualcomputer.PluginMain;
|
||||
import sznp.virtualcomputer.util.COMUtils;
|
||||
import sznp.virtualcomputer.util.IMCFrameBuffer;
|
||||
import sznp.virtualcomputer.util.Timing;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MCFrameBuffer implements IMCFrameBuffer {
|
||||
private final IDisplay display;
|
||||
private final Holder<IDisplaySourceBitmap> holder = new Holder<>();
|
||||
private final Logger logger;
|
||||
private final PluginMain plugin;
|
||||
/**
|
||||
* Whether the VM is running inside the server
|
||||
*/
|
||||
private final boolean embedded;
|
||||
/**
|
||||
* Whether the GPU is being used to render
|
||||
*/
|
||||
private final boolean direct;
|
||||
private BukkitTask tt;
|
||||
/**
|
||||
* Used when running embedded
|
||||
*/
|
||||
private Pointer pointer;
|
||||
/**
|
||||
* Used when not running embedded
|
||||
*/
|
||||
private byte[] screenImage;
|
||||
/**
|
||||
* Used when running in indirect mode, not embedded
|
||||
*/
|
||||
private ByteBuffer screenBuffer;
|
||||
private int width;
|
||||
private int height;
|
||||
@Getter
|
||||
@Setter
|
||||
private String id;
|
||||
private final AtomicBoolean shouldUpdate = new AtomicBoolean();
|
||||
private final AtomicIntegerArray updateParameters = new AtomicIntegerArray(4);
|
||||
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 embedded, boolean direct) {
|
||||
this.display = display;
|
||||
this.plugin = plugin;
|
||||
this.logger = plugin.getLogger();
|
||||
this.embedded = embedded; //Don't change even if the config got updated while running
|
||||
this.direct = direct;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) {
|
||||
if (tt != null)
|
||||
tt.cancel();
|
||||
tt = Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
synchronized (this) { //If a change occurs twice, then wait for it
|
||||
try {
|
||||
if (!embedded) { //Running separately
|
||||
this.width = (int) width;
|
||||
this.height = (int) height;
|
||||
if (screenImage == null || screenImage.length != width * height * 4) {
|
||||
screenImage = new byte[(int) (width * height * 4)];
|
||||
screenBuffer = ByteBuffer.wrap(screenImage);
|
||||
}
|
||||
updateScreen((int) xOrigin, (int) yOrigin, (int) width, (int) height);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
pointer = new Pointer(ptr[0]);
|
||||
this.width = (int) w[0];
|
||||
this.height = (int) h[0];
|
||||
updateScreen(0, 0, (int) width, (int) height);
|
||||
} catch (VBoxException e) {
|
||||
if (e.getResultCode() == 0x80070005)
|
||||
return; // Machine is being powered down
|
||||
if (e.getResultCode() == 0x80004005) //The function "querySourceBitmap" returned an error condition: "Operation failed (NS_ERROR_FAILURE)"
|
||||
System.out.println("I don't know why this happens, but stopping the computer helps.");
|
||||
e.printStackTrace();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdate(long x, long y, long width, long height) {
|
||||
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);
|
||||
updateParameters.set(0, (int) x);
|
||||
updateParameters.set(1, (int) y);
|
||||
updateParameters.set(2, (int) width);
|
||||
updateParameters.set(3, (int) height);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdateImage(long x, long y, long width, long height, byte[] image) {
|
||||
System.out.println("Update image!");
|
||||
if (this.width == 0 || this.height == 0) {
|
||||
logger.warning("Received screen image before resolution change!");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
updateScreen((int) x, (int) y, (int) width, (int) height);
|
||||
}
|
||||
|
||||
public void startEmbedded() {
|
||||
running = true;
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
try {
|
||||
while (running) {
|
||||
synchronized (this) {
|
||||
while (!shouldUpdate.get())
|
||||
wait(1000);
|
||||
if (pointer == null) {
|
||||
logger.warning("Embedded screen data pointer is null");
|
||||
shouldUpdate.set(false);
|
||||
continue;
|
||||
}
|
||||
if (!running) return;
|
||||
updateScreen(updateParameters.get(0), updateParameters.get(1), updateParameters.get(2), updateParameters.get(3));
|
||||
shouldUpdate.set(false);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateScreenDirectInternal(byte[] pixels, int x, int y, int width, int height) {
|
||||
if (pixels == null) {
|
||||
logger.warning("Direct pixel data is null");
|
||||
return;
|
||||
}
|
||||
Timing t = new Timing();
|
||||
GPURenderer.update(pixels, this.width, this.height, x, y, width, height);
|
||||
if (t.elapsedMS() > 60) //Typically 1ms max
|
||||
logger.warning("Direct update took " + t.elapsedMS() + "ms");
|
||||
}
|
||||
|
||||
private void updateScreenIndirectInternal(ByteBuffer buffer, int x, int y, int width, int height) {
|
||||
if (buffer == null) {
|
||||
logger.warning("Indirect pixel buffer is null");
|
||||
return;
|
||||
}
|
||||
if (this.width * this.height > 640 * 480)
|
||||
buffer.limit(640 * 480 * 4);
|
||||
else
|
||||
buffer.limit(this.width * this.height * 4);
|
||||
Timing t = new Timing();
|
||||
BukkitRenderer.update(buffer, x, y, width, height);
|
||||
if (t.elapsedMS() > 60)
|
||||
logger.warning("Indirect update took " + t.elapsedMS() + "ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
synchronized (this) {
|
||||
running = false;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Utils {
|
||||
/**
|
||||
* Adds the specified path to the java library path
|
||||
*
|
||||
* @param pathToAdd the path to add
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addLibraryPath(String pathToAdd) throws Exception {
|
||||
try {
|
||||
addLibraryPathOld(pathToAdd);
|
||||
} catch (Throwable t) {
|
||||
addLibraryPathNew(pathToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addLibraryPathOld(String pathToAdd) throws Exception {
|
||||
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
|
||||
usrPathsField.setAccessible(true);
|
||||
|
||||
// get array of paths
|
||||
final String[] paths = (String[]) usrPathsField.get(null);
|
||||
|
||||
// check if the path to add is already present
|
||||
for (String path : paths) {
|
||||
if (path.equals(pathToAdd)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add the new path
|
||||
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
|
||||
newPaths[newPaths.length - 1] = pathToAdd;
|
||||
usrPathsField.set(null, newPaths);
|
||||
}
|
||||
|
||||
private static void addLibraryPathNew(String pathToAdd) throws Exception {
|
||||
MethodHandles.Lookup cl = MethodHandles.privateLookupIn(ClassLoader.class, MethodHandles.lookup());
|
||||
VarHandle sys_paths = cl.findStaticVarHandle(ClassLoader.class, "sys_paths", String[].class);
|
||||
final String[] paths = (String[]) sys_paths.get();
|
||||
|
||||
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
|
||||
newPaths[newPaths.length - 1] = pathToAdd;
|
||||
sys_paths.set((Object) newPaths);
|
||||
}
|
||||
}
|
35
VirtualComputer-Core/src/main/resources/computer.commodore
Normal file
35
VirtualComputer-Core/src/main/resources/computer.commodore
Normal file
|
@ -0,0 +1,35 @@
|
|||
computer {
|
||||
start {
|
||||
index brigadier:integer;
|
||||
}
|
||||
on {
|
||||
index brigadier:integer;
|
||||
}
|
||||
list;
|
||||
stop;
|
||||
off;
|
||||
shutdown;
|
||||
kill;
|
||||
powerbutton;
|
||||
reset;
|
||||
restart;
|
||||
fix;
|
||||
fixscreen;
|
||||
key {
|
||||
key brigadier:string single_word;
|
||||
}
|
||||
press {
|
||||
key brigadier:string single_word;
|
||||
}
|
||||
mouse {
|
||||
button brigadier:string single_word;
|
||||
}
|
||||
input {
|
||||
key;
|
||||
mouse;
|
||||
}
|
||||
show {
|
||||
key;
|
||||
mouse;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
name: VirtualComputer
|
||||
main: sznp.virtualcomputer.PluginMain
|
||||
version: 2.0
|
||||
version: '3.0'
|
||||
author: 'NorbiPeti'
|
||||
commands:
|
||||
computer:
|
||||
usage: Use /computer start|stop|reset|key|mouse|input|fix
|
||||
aliases: c
|
||||
api-version: '1.13'
|
||||
depend:
|
||||
- Chroma-Core
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>VirtualComputer</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -1,13 +0,0 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
|
|
@ -1,144 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<compilerArgs>
|
||||
<arg>-g</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration />
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>XPCOM</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.virtualbox:VirtualBox-MSCOM</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>MSCOM</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.virtualbox:VirtualBox</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>repo</id>
|
||||
<url>file://${basedir}/repo</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.12-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<groupId>commons-lang</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>guava</artifactId>
|
||||
<groupId>com.google.guava</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>gson</artifactId>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<groupId>org.yaml</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
<groupId>net.md-5</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<version>6.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>craftbukkit</artifactId>
|
||||
<version>1.12.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<versioning>
|
||||
<release>6.0</release>
|
||||
<versions>
|
||||
<version>6.0</version>
|
||||
</versions>
|
||||
<lastUpdated>20190405225845</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
Binary file not shown.
|
@ -1,162 +0,0 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.virtualbox_6_0.VBoxException;
|
||||
|
||||
public class Commands implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
|
||||
if (args.length == 0)
|
||||
return false;
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "start":
|
||||
case "poweron":
|
||||
case "on":
|
||||
case "startup":
|
||||
int c = getMachineIndex(args);
|
||||
if (c == -1) {
|
||||
sender.sendMessage("§cUsage: /" + label + " start [index]");
|
||||
return true;
|
||||
}
|
||||
Computer.getInstance().Start(sender, c);
|
||||
break;
|
||||
case "stop":
|
||||
case "poweroff":
|
||||
case "off":
|
||||
case "shutdown":
|
||||
case "kill":
|
||||
Computer.getInstance().Stop(sender);
|
||||
break;
|
||||
case "powerbutton":
|
||||
case "pwrbtn":
|
||||
case "powerbtn":
|
||||
c = getMachineIndex(args);
|
||||
if (c == -1) {
|
||||
sender.sendMessage("§cUsage: /" + label + " powerbutton [index]");
|
||||
return true;
|
||||
}
|
||||
Computer.getInstance().PowerButton(sender, c);
|
||||
break;
|
||||
case "reset":
|
||||
case "restart":
|
||||
Computer.getInstance().Reset(sender);
|
||||
break;
|
||||
case "fix":
|
||||
case "fixscreen":
|
||||
Computer.getInstance().FixScreen(sender);
|
||||
break;
|
||||
case "key":
|
||||
case "press":
|
||||
case "presskey":
|
||||
case "keypress":
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("§cUsage: /computer key <key> [down/up|duration(ticks)]");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 3)
|
||||
Computer.getInstance().PressKey(sender, args[1], "");
|
||||
else
|
||||
Computer.getInstance().PressKey(sender, args[1], args[2]);
|
||||
break;
|
||||
case "mouse":
|
||||
boolean showusage = true;
|
||||
if (args.length < 6) {
|
||||
try {
|
||||
// Command overloading, because I can :P
|
||||
if (args.length > 4) // 4<x<6
|
||||
{
|
||||
Computer.getInstance().UpdateMouse(sender, Integer.parseInt(args[1]), Integer.parseInt(args[2]),
|
||||
Integer.parseInt(args[3]), Integer.parseInt(args[4]), "", false);
|
||||
showusage = false;
|
||||
} else {
|
||||
if (args.length == 3) {
|
||||
Computer.getInstance().UpdateMouse(sender, 0, 0, 0, 0, args[1], args[2].equals("down"));
|
||||
showusage = false;
|
||||
} else if (args.length == 2) {
|
||||
Computer.getInstance().UpdateMouse(sender, 0, 0, 0, 0, args[1]);
|
||||
showusage = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (VBoxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (Exception ignored) { //It will show the usage here
|
||||
}
|
||||
}
|
||||
if (showusage) {
|
||||
sender.sendMessage("§cUsage: /computer mouse <relx> <rely> <relz> <relw>");
|
||||
sender.sendMessage("§cOr: /computer mouse <button> [up/down]");
|
||||
}
|
||||
break;
|
||||
case "input":
|
||||
case "show":
|
||||
case "showinput":
|
||||
case "shinput": {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage("§cError: Only players can use this command.");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage("§cUsage: /computer input <key|mouse>");
|
||||
return true;
|
||||
}
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "key":
|
||||
case "keyboard":
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName()
|
||||
+ " [\"\",{\"text\":\" [0]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D0\"}},{\"text\":\" [1]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D1\"}},{\"text\":\" [2]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D2\"}},{\"text\":\" [3]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D3\"}},{\"text\":\" [4]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D4\"}},{\"text\":\" [5]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D5\"}},{\"text\":\" [6]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D6\"}},{\"text\":\" [7]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D7\"}},{\"text\":\" [8]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D8\"}},{\"text\":\" [9]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D9\"}}]");
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName()
|
||||
+ " [\"\",{\"text\":\" [Tab]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key Tab\"}},{\"text\":\" [Q]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key Q\"}},{\"text\":\" [W]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key W\"}},{\"text\":\" [E]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key E\"}},{\"text\":\" [R]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key R\"}},{\"text\":\" [T]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key T\"}},{\"text\":\" [Y]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key Y\"}},{\"text\":\" [U]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key U\"}},{\"text\":\" [I]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key I\"}},{\"text\":\" [O]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key O\"}},{\"text\":\" [P]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key P\"}}]");
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName()
|
||||
+ " [\"\",{\"text\":\" [CapsLock]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key CapsLock\"}},{\"text\":\" [A]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key A\"}},{\"text\":\" [S]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key S\"}},{\"text\":\" [D]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key D\"}},{\"text\":\" [F]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key F\"}},{\"text\":\" [G]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key G\"}},{\"text\":\" [H]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key H\"}},{\"text\":\" [J]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key J\"}},{\"text\":\" [K]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key K\"}},{\"text\":\" [L]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key L\"}},{\"text\":\" [Enter]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key Enter\"}}]");
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName()
|
||||
+ " [\"\",{\"text\":\" [Z]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key Z\"}},{\"text\":\" [X]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key X\"}},{\"text\":\" [C]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key C\"}},{\"text\":\" [V]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key V\"}},{\"text\":\" [B]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key B\"}},{\"text\":\" [N]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key N\"}},{\"text\":\" [M]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key M\"}}]");
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName()
|
||||
+ " [\"\",{\"text\":\" [Ctrl]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key ControlLeft\"}},{\"text\":\" [Alt]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key AltLeft\"}},{\"text\":\" [Space]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key Space\"}},{\"text\":\" [AltGr]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key AltRight\"}},{\"text\":\" [Ctrl]\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/computer key ControlRight\"}}]");
|
||||
break;
|
||||
case "mouse":
|
||||
MouseLockerPlayerListener.toggleLock((Player) sender);
|
||||
break;
|
||||
case "mspeed":
|
||||
case "mousespeed":
|
||||
if (args.length < 3) {
|
||||
sender.sendMessage("§cUsage: /computer input mspeed <number>");
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
MouseLockerPlayerListener.LockedSpeed = Float.parseFloat(args[2]);
|
||||
} catch(NumberFormatException e) {
|
||||
sender.sendMessage("§cThe speed must be a number.");
|
||||
break;
|
||||
}
|
||||
sender.sendMessage("§aMouse speed set to " + MouseLockerPlayerListener.LockedSpeed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the 2nd parameter
|
||||
*
|
||||
* @return The index of the machine or -1 on usage error
|
||||
*/
|
||||
private int getMachineIndex(String[] args) {
|
||||
int c = 0;
|
||||
if (args.length >= 2) {
|
||||
try {
|
||||
c = Integer.parseInt(args[1]);
|
||||
} catch (Exception ignored) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
|
@ -1,235 +0,0 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import lombok.Getter;
|
||||
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.GPURenderer;
|
||||
import sznp.virtualcomputer.renderer.MCFrameBuffer;
|
||||
import sznp.virtualcomputer.util.Scancode;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class Computer {
|
||||
@Getter
|
||||
private static Computer instance;
|
||||
|
||||
private final PluginMain plugin;
|
||||
private ISession session;
|
||||
private IVirtualBox vbox;
|
||||
private IMachine machine;
|
||||
private MachineEventHandler handler;
|
||||
private IEventListener listener;
|
||||
private VirtualBoxManager manager;
|
||||
|
||||
@java.beans.ConstructorProperties({"plugin"})
|
||||
public Computer(PluginMain plugin, VirtualBoxManager manager, IVirtualBox vbox) {
|
||||
this.plugin = plugin;
|
||||
this.manager = manager;
|
||||
session = manager.getSessionObject();
|
||||
this.vbox = vbox;
|
||||
if (instance != null) throw new IllegalStateException("A computer already exists!");
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public void Start(CommandSender sender, int index) {// TODO: Add touchscreen support (#2)
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
sender.sendMessage("§cThe machine is already running!");
|
||||
return;
|
||||
}
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (vbox.getMachines().size() <= index) {
|
||||
sendMessage(sender, "§cMachine not found!");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
sendMessage(sender, "§eStarting computer...");
|
||||
machine = vbox.getMachines().get(index);
|
||||
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); //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: "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.");
|
||||
} else {
|
||||
sendMessage(sender, "§cFailed to start computer: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when the machine is locked after {@link #Start(CommandSender, int)}
|
||||
*
|
||||
* @param sender The sender which started the machine
|
||||
*/
|
||||
public void onLock(CommandSender sender) {
|
||||
machine = session.getMachine(); // This is the Machine object we can work with
|
||||
final IConsole console = session.getConsole();
|
||||
if (handler != null)
|
||||
handler.disable();
|
||||
handler = new MachineEventHandler(Computer.this, sender);
|
||||
listener = handler.registerTo(console.getEventSource());
|
||||
IProgress progress = console.powerUp(); // https://marc.info/?l=vbox-dev&m=142780789819967&w=2
|
||||
handler.setProgress(progress);
|
||||
handler.registerTo(progress.getEventSource()); //TODO: Show progress bar some way?
|
||||
console.getDisplay().attachFramebuffer(0L,
|
||||
new IFramebuffer(new MCFrameBuffer(console.getDisplay(), true)));
|
||||
}
|
||||
|
||||
private void sendMessage(@Nullable CommandSender sender, String message) {
|
||||
if (sender != null)
|
||||
sender.sendMessage(message);
|
||||
plugin.getLogger().warning((sender == null ? "" : sender.getName() + ": ") + ChatColor.stripColor(message));
|
||||
}
|
||||
|
||||
public void Stop(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender)) {
|
||||
if (session.getState().equals(SessionState.Locked)) {
|
||||
onMachineStop(sender); //Needed for session reset
|
||||
sendMessage(sender, "§eComputer was already off, released it.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
sendMessage(sender, "§eStopping computer...");
|
||||
session.getConsole().powerDown();
|
||||
}
|
||||
|
||||
public void PowerButton(CommandSender sender, int index) {
|
||||
sendMessage(sender, "§ePressing powerbutton...");
|
||||
Bukkit.getServer().getScheduler().runTaskAsynchronously(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (session.getState() != SessionState.Locked || session.getMachine() == null) {
|
||||
Start(sender, index);
|
||||
} else {
|
||||
session.getConsole().powerButton();
|
||||
sendMessage(sender, "§ePowerbutton pressed.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Reset(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
sendMessage(sender, "§eResetting computer...");
|
||||
session.getConsole().reset();
|
||||
sendMessage(sender, "§eComputer reset.");
|
||||
}
|
||||
|
||||
public void FixScreen(CommandSender sender) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
sendMessage(sender, "§eFixing screen...");
|
||||
session.getConsole().getDisplay().setSeamlessMode(false);
|
||||
session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L);
|
||||
sendMessage(sender, "§eScreen fixed.");
|
||||
}
|
||||
|
||||
public boolean checkMachineNotRunning(@Nullable CommandSender sender) {
|
||||
if (session.getState() != SessionState.Locked || machine.getState() != MachineState.Running) {
|
||||
if (sender != null)
|
||||
sender.sendMessage("§cMachine isn't running.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void PressKey(CommandSender sender, String key, String stateorduration) {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
int durationorstate;
|
||||
if (stateorduration.length() == 0)
|
||||
durationorstate = 0;
|
||||
else if (stateorduration.equalsIgnoreCase("down"))
|
||||
durationorstate = -1;
|
||||
else if (stateorduration.equalsIgnoreCase("up"))
|
||||
durationorstate = -2;
|
||||
else
|
||||
durationorstate = Short.parseShort(stateorduration);
|
||||
int code = Scancode.getCode("sc_" + key.toLowerCase());
|
||||
if (code == -1) {
|
||||
sender.sendMessage("§cUnknown key: " + key);
|
||||
return;
|
||||
}
|
||||
// Release key scan code concept taken from VirtualBox source code (KeyboardImpl.cpp:putCAD())
|
||||
// +128
|
||||
if (durationorstate != -2)
|
||||
session.getConsole().getKeyboard().putScancode(code);
|
||||
Runnable sendrelease = () -> session.getConsole().getKeyboard().putScancodes(Lists.newArrayList(code + 128,
|
||||
Scancode.sc_controlLeft.Code + 128, Scancode.sc_shiftLeft.Code + 128, Scancode.sc_altLeft.Code + 128));
|
||||
if (durationorstate == 0 || durationorstate == -2)
|
||||
sendrelease.run();
|
||||
if (durationorstate > 0) {
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, sendrelease, durationorstate);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs, boolean down) throws Exception {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
int state = 0;
|
||||
if (mbs.length() > 0 && down)
|
||||
state = Arrays.stream(MouseButtonState.values()).filter(mousebs -> mousebs.name().equalsIgnoreCase(mbs))
|
||||
.findAny().orElseThrow(() -> new Exception("Unknown mouse button")).value();
|
||||
session.getConsole().getMouse().putMouseEvent(x, y, z, w, state);
|
||||
}
|
||||
|
||||
public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs) throws Exception {
|
||||
if (checkMachineNotRunning(sender))
|
||||
return;
|
||||
UpdateMouse(sender, x, y, z, w, mbs, true);
|
||||
UpdateMouse(sender, x, y, z, w, mbs, false);
|
||||
}
|
||||
|
||||
public void onMachineStart(CommandSender sender) {
|
||||
sendMessage(sender, "§eComputer started.");
|
||||
}
|
||||
|
||||
public void onMachineStop(CommandSender sender) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
session.unlockMachine(); //Needs to be outside of the event handler
|
||||
handler = null;
|
||||
machine = null;
|
||||
session = manager.getSessionObject();
|
||||
sendMessage(sender, "§eComputer powered off."); //This block runs later
|
||||
}
|
||||
});
|
||||
GPURenderer.update(new byte[1], 0, 0, 0, 0, 640, 480); //Black screen
|
||||
stopEvents();
|
||||
MouseLockerPlayerListener.computerStop();
|
||||
}
|
||||
|
||||
public void stopEvents() {
|
||||
/*if(session!=null && listener!=null)
|
||||
session.getConsole().getEventSource().unregisterListener(listener);*/
|
||||
if (listener != null)
|
||||
handler.disable();
|
||||
listener = null;
|
||||
}
|
||||
|
||||
public void pluginDisable(CommandSender ccs) {
|
||||
stopEvents();
|
||||
if (session.getState() == SessionState.Locked) {
|
||||
if (session.getMachine().getState().equals(MachineState.Running)) {
|
||||
ccs.sendMessage("§aSaving machine state...");
|
||||
session.getMachine().saveState().waitForCompletion(10000);
|
||||
}
|
||||
session.unlockMachine();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import jnr.ffi.LibraryLoader;
|
||||
import lombok.val;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.virtualbox_6_0.IEventListener;
|
||||
import org.virtualbox_6_0.IVirtualBox;
|
||||
import org.virtualbox_6_0.VirtualBoxManager;
|
||||
import sznp.virtualcomputer.events.VBoxEventHandler;
|
||||
import sznp.virtualcomputer.renderer.BukkitRenderer;
|
||||
import sznp.virtualcomputer.renderer.GPURenderer;
|
||||
import sznp.virtualcomputer.renderer.IRenderer;
|
||||
import sznp.virtualcomputer.util.Utils;
|
||||
import sznp.virtualcomputer.util.VBoxLib;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PluginMain extends JavaPlugin {
|
||||
private static final int MCX = 5;
|
||||
private static final int MCY = 4;
|
||||
private BukkitTask mousetask;
|
||||
private IEventListener listener;
|
||||
|
||||
public static PluginMain Instance;
|
||||
//public static ByteBuffer allpixels = ByteBuffer.allocate(640 * 480 * 4); // It's set on each change
|
||||
/**
|
||||
* Only used if {@link #direct} is false.
|
||||
*/
|
||||
public static ByteBuffer allpixels; // It's set on each change
|
||||
private static ArrayList<IRenderer> renderers = new ArrayList<>();
|
||||
/*
|
||||
* Only used if {@link #direct} is true.
|
||||
*/
|
||||
//public static PXCLib pxc;
|
||||
public static boolean direct;
|
||||
public static boolean sendAll;
|
||||
|
||||
// Fired when plugin is first enabled
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Instance = this;
|
||||
try {
|
||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||
this.getCommand("computer").setExecutor(new Commands());
|
||||
sendAll = getConfig().getBoolean("sendAll", true);
|
||||
ccs.sendMessage("§bInitializing VirtualBox...");
|
||||
String vbpath = System.getProperty("os.name").toLowerCase().contains("mac")
|
||||
? "/Applications/VirtualBox.app/Contents/MacOS"
|
||||
: "/opt/virtualbox";
|
||||
//noinspection ConstantConditions
|
||||
Predicate<File> notGoodDir= ff->!ff.isDirectory() || Arrays.stream(ff.list()).noneMatch(s -> s.contains("xpcom"));
|
||||
if (notGoodDir.test(new File(vbpath)))
|
||||
vbpath = "/usr/lib/virtualbox";
|
||||
if(notGoodDir.test(new File(vbpath)))
|
||||
error("Could not find VirtualBox! Download from https://www.virtualbox.org/wiki/Downloads");
|
||||
if (System.getProperty("vbox.home") == null || System.getProperty("vbox.home").isEmpty())
|
||||
System.setProperty("vbox.home", vbpath);
|
||||
if (System.getProperty("sun.boot.library.path") == null
|
||||
|| System.getProperty("sun.boot.library.path").isEmpty())
|
||||
System.setProperty("sun.boot.library.path", vbpath);
|
||||
if (System.getProperty("java.library.path") == null || System.getProperty("java.library.path").isEmpty())
|
||||
System.setProperty("java.library.path", vbpath);
|
||||
Utils.addLibraryPath(vbpath);
|
||||
final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath());
|
||||
VBoxLib vbl = LibraryLoader.create(VBoxLib.class).load("vboxjxpcom"); //TODO: Test for MSCOM
|
||||
vbl.RTR3InitExe(0, "", 0);
|
||||
IVirtualBox vbox = manager.getVBox();
|
||||
listener = new VBoxEventHandler().registerTo(vbox.getEventSource());
|
||||
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");
|
||||
} 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");
|
||||
}
|
||||
ccs.sendMessage("§bLoaded!");
|
||||
val mlpl = new MouseLockerPlayerListener();
|
||||
mousetask = getServer().getScheduler().runTaskTimer(this, mlpl, 0, 0);
|
||||
getServer().getPluginManager().registerEvents(mlpl, this);
|
||||
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void error(String message) {
|
||||
getLogger().severe("A fatal error occured, disabling plugin!");
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
throw new RuntimeException(message);
|
||||
}
|
||||
|
||||
// Fired when plugin is disabled
|
||||
@Override
|
||||
public void onDisable() {
|
||||
ConsoleCommandSender ccs = getServer().getConsoleSender();
|
||||
if (mousetask != null)
|
||||
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)
|
||||
}*/
|
||||
if (listener != null)
|
||||
((VBoxEventHandler) listener.getTypedWrapped()).disable(); //The save progress wait locks with the event
|
||||
if (Computer.getInstance() != null)
|
||||
Computer.getInstance().pluginDisable(ccs);
|
||||
ccs.sendMessage("§aHuh.");
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package sznp.virtualcomputer.events;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.virtualbox_6_0.ISessionStateChangedEvent;
|
||||
import org.virtualbox_6_0.SessionState;
|
||||
import org.virtualbox_6_0.VBoxEventType;
|
||||
import sznp.virtualcomputer.Computer;
|
||||
|
||||
public class VBoxEventHandler extends EventHandlerBase {
|
||||
public VBoxEventHandler() {
|
||||
super(ImmutableMap.of(VBoxEventType.OnSessionStateChanged, ISessionStateChangedEvent.class));
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static VBoxEventHandler instance;
|
||||
private String machineID;
|
||||
private CommandSender sender;
|
||||
|
||||
@EventHandler
|
||||
public void onSessionStateChange(ISessionStateChangedEvent event) {
|
||||
if (!event.getMachineId().equals(machineID)) return;
|
||||
if (event.getState() == SessionState.Locked) //Need to check here, because we can't access the console yet
|
||||
Computer.getInstance().onLock(sender);
|
||||
}
|
||||
|
||||
public void setup(String machineID, CommandSender sender) {
|
||||
this.machineID = machineID;
|
||||
this.sender = sender;
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package sznp.virtualcomputer.renderer;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.mozilla.interfaces.IFramebuffer;
|
||||
import org.mozilla.interfaces.IFramebufferOverlay;
|
||||
import org.mozilla.interfaces.nsISupports;
|
||||
import org.mozilla.xpcom.Mozilla;
|
||||
import org.virtualbox_6_0.*;
|
||||
import sznp.virtualcomputer.PluginMain;
|
||||
import sznp.virtualcomputer.util.Timing;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MCFrameBuffer implements IFramebuffer {
|
||||
private IDisplay display;
|
||||
private Holder<IDisplaySourceBitmap> holder = new Holder<>();
|
||||
|
||||
public MCFrameBuffer(IDisplay display, boolean VBoxDirect) { //TODO: Implement param
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public nsISupports queryInterface(String id) {
|
||||
return Mozilla.queryInterface(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBitsPerPixel() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBytesPerLine() {
|
||||
return 640L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] getCapabilities(long[] arg0) {
|
||||
try {
|
||||
System.out.println("Capabilities queried");
|
||||
System.out.println("Capabilities: " + Arrays.toString(arg0));
|
||||
return new long[]{FramebufferCapabilities.UpdateImage.value()};
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return new long[]{};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHeight() {
|
||||
return 480;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHeightReduction() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFramebufferOverlay getOverlay() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPixelFormat() {
|
||||
return BitmapFormat.BGRA.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVisibleRegion(byte arg0, long arg1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWidth() {
|
||||
return 640;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWinId() {
|
||||
return 0; // Zero means no win id
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify3DEvent(long arg0, byte[] arg1) {
|
||||
}
|
||||
|
||||
private BukkitTask tt;
|
||||
private Pointer pointer;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
@Override
|
||||
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) {
|
||||
if (tt != null)
|
||||
tt.cancel();
|
||||
tt = Bukkit.getScheduler().runTaskAsynchronously(PluginMain.Instance, () -> {
|
||||
try {
|
||||
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];
|
||||
holder.value.getTypedWrapped().queryBitmapInfo(ptr, w, h, bpp, bpl, pf);
|
||||
if (PluginMain.direct) {
|
||||
pointer = new Pointer(ptr[0]);
|
||||
this.width = (int) w[0];
|
||||
this.height = (int) h[0];
|
||||
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) {
|
||||
if (e.getResultCode() == 0x80070005)
|
||||
return; // Machine is being powered down
|
||||
if (e.getResultCode() == 0x80004005) //The function "querySourceBitmap" returned an error condition: "Operation failed (NS_ERROR_FAILURE)"
|
||||
System.out.println("I don't know why this happens, but stopping the computer helps.");
|
||||
e.printStackTrace();
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdate(long x, long y, long width, long 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");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdateImage(long arg0, long arg1, long arg2, long arg3, byte[] arg4) {
|
||||
System.out.println("Update image!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibleRegion(byte arg0, long arg1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processVHWACommand(byte b, int i, boolean b1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean videoModeSupported(long arg0, long arg1, long arg2) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import lombok.val;
|
||||
import org.mozilla.interfaces.IEventListener;
|
||||
import org.virtualbox_6_0.IEvent;
|
||||
import org.virtualbox_6_0.IEventSource;
|
||||
import org.virtualbox_6_0.VBoxEventType;
|
||||
import org.virtualbox_6_0.xpcom.IUnknown;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Utils {
|
||||
/**
|
||||
* Adds the specified path to the java library path
|
||||
*
|
||||
* @param pathToAdd the path to add
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addLibraryPath(String pathToAdd) throws Exception {
|
||||
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
|
||||
usrPathsField.setAccessible(true);
|
||||
|
||||
// get array of paths
|
||||
final String[] paths = (String[]) usrPathsField.get(null);
|
||||
|
||||
// check if the path to add is already present
|
||||
for (String path : paths) {
|
||||
if (path.equals(pathToAdd)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add the new path
|
||||
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
|
||||
newPaths[newPaths.length - 1] = pathToAdd;
|
||||
usrPathsField.set(null, newPaths);
|
||||
}
|
||||
|
||||
//public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) {
|
||||
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")
|
||||
public static <T extends IEvent> T getEvent(org.mozilla.interfaces.IEvent event, Class<T> cl) {
|
||||
//if (event.getType() != type.value()) return null;
|
||||
//return (T) T.queryInterface(new IEvent(event)); - Probably won't work
|
||||
try {
|
||||
val method = cl.getMethod("queryInterface", IUnknown.class);
|
||||
return (T) method.invoke(null, new IEvent(event));
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
31
VirtualComputerMSCOM/pom.xml
Normal file
31
VirtualComputerMSCOM/pom.xml
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>VirtualComputer-MSCOM</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<version>6.1</version>
|
||||
<optional>true
|
||||
</optional> <!-- https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jacob-project</groupId>
|
||||
<artifactId>jacob</artifactId>
|
||||
<version>1.19</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,97 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.virtualbox_6_1.BitmapFormat;
|
||||
import org.virtualbox_6_1.FramebufferCapabilities;
|
||||
import org.virtualbox_6_1.IFramebufferOverlay;
|
||||
import sznp.virtualcomputer.util.IMCFrameBuffer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class COMFrameBuffer {
|
||||
private final IMCFrameBuffer frameBuffer;
|
||||
|
||||
public long getBitsPerPixel() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
public long getBytesPerLine() {
|
||||
return 640L;
|
||||
}
|
||||
|
||||
public long[] getCapabilities(long[] arg0) {
|
||||
try {
|
||||
System.out.println("Capabilities queried");
|
||||
System.out.println("Capabilities: " + Arrays.toString(arg0));
|
||||
return new long[]{FramebufferCapabilities.UpdateImage.value()};
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new long[]{};
|
||||
}
|
||||
}
|
||||
|
||||
public long getHeight() {
|
||||
return 480;
|
||||
}
|
||||
|
||||
public long getHeightReduction() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public IFramebufferOverlay getOverlay() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getPixelFormat() {
|
||||
return BitmapFormat.BGRA.value();
|
||||
}
|
||||
|
||||
public long getVisibleRegion(byte arg0, long arg1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getWidth() {
|
||||
return 640;
|
||||
}
|
||||
|
||||
public long getWinId() {
|
||||
return 0; // Zero means no win id
|
||||
}
|
||||
|
||||
public void notify3DEvent(long type, byte[] data) {
|
||||
System.out.println("3D event! " + type + " - " + Arrays.toString(data));
|
||||
}
|
||||
|
||||
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) {
|
||||
frameBuffer.notifyChange(screenId, xOrigin, yOrigin, width, height);
|
||||
}
|
||||
|
||||
public void notifyUpdate(long x, long y, long width, long height) {
|
||||
frameBuffer.notifyUpdate(x, y, width, height);
|
||||
}
|
||||
|
||||
public void notifyUpdateImage(long arg0, long arg1, long arg2, long arg3, byte[] arg4) {
|
||||
frameBuffer.notifyUpdateImage(arg0, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
public void setVisibleRegion(byte arg0, long arg1) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts a Video HW Acceleration Command to the frame buffer for processing.<br />
|
||||
* <br />
|
||||
* The commands used for 2D video acceleration (DDraw surface creation/destroying, blitting, scaling, color conversion, overlaying, etc.) are posted from quest to the host to be processed by the host hardware.
|
||||
*
|
||||
* @param command Pointer to VBOXVHWACMD containing the command to execute.
|
||||
* @param enmCmd The validated VBOXVHWACMD::enmCmd value from the command.
|
||||
* @param fromGuest Set when the command origins from the guest, clear if host.
|
||||
*/ //https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Frontends/VirtualBox/src/VBoxFBOverlay.cpp#L4645
|
||||
public void processVHWACommand(byte command, int enmCmd, boolean fromGuest) {
|
||||
|
||||
}
|
||||
|
||||
public boolean videoModeSupported(long arg0, long arg1, long arg2) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import org.virtualbox_6_1.IEvent;
|
||||
import sznp.virtualcomputer.util.COMObjectBase;
|
||||
import sznp.virtualcomputer.util.IEventHandler;
|
||||
|
||||
/**
|
||||
* A Bukkit-like event system which calls the appropriate methods on an event.
|
||||
*/
|
||||
public final class EventHandler extends COMObjectBase {
|
||||
private final IEventHandler handler;
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* New MSCOM event handler.
|
||||
*
|
||||
* @param handler The handle method that handles what needs to be handled
|
||||
*/
|
||||
public EventHandler(IEventHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public final void handleEvent(IEvent iEvent) {
|
||||
if (!enabled)
|
||||
return;
|
||||
handler.handleEvent(iEvent);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import com.jacob.com.Dispatch;
|
||||
import com.jacob.com.Variant;
|
||||
|
||||
public abstract class COMObjectBase extends Dispatch {
|
||||
public COMObjectBase() {
|
||||
super("clsid:{67099191-32E7-4F6C-85EE-422304C71B90}");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import com.jacob.activeX.ActiveXComponent;
|
||||
import com.jacob.com.*;
|
||||
import lombok.val;
|
||||
import org.virtualbox_6_1.*;
|
||||
import org.virtualbox_6_1.mscom.Helper;
|
||||
import org.virtualbox_6_1.mscom.IUnknown;
|
||||
import sznp.virtualcomputer.COMFrameBuffer;
|
||||
import sznp.virtualcomputer.EventHandler;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public final class COMUtils {
|
||||
private COMUtils() {
|
||||
}
|
||||
|
||||
//public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) {
|
||||
public static org.virtualbox_6_1.IEventListener registerListener(IEventSource source, IEventHandler listener, List<VBoxEventType> types) {
|
||||
//new DispatchEvents(source.getTypedWrapped(), listener);
|
||||
val ret = new org.virtualbox_6_1.IEventListener(new EventHandler(listener));
|
||||
/*com.jacob.activeX.ActiveXComponent.createNewInstance("IEventListener");
|
||||
new ActiveXComponent("");
|
||||
source.registerListener(ret, types, true);*/
|
||||
//registerListener(source, new EventHandler(listener), types, true);
|
||||
System.out.println("Testing listener...");
|
||||
ret.handleEvent(null);
|
||||
System.out.println("Tested");
|
||||
return ret;
|
||||
//return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends IEvent> T getEvent(IEvent event, Class<T> cl) {
|
||||
try {
|
||||
val method = cl.getMethod("queryInterface", IUnknown.class);
|
||||
return (T) method.invoke(null, event);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static IFramebuffer gimmeAFramebuffer(IMCFrameBuffer frameBuffer) {
|
||||
return new IFramebuffer(new Variant(new COMFrameBuffer(frameBuffer)).getDispatch());
|
||||
}
|
||||
|
||||
public static void queryBitmapInfo(IDisplaySourceBitmap bitmap, long[] ptr, long[] w, long[] h, long[] bpp, long[] bpl, long[] pf) {
|
||||
Dispatch.call(bitmap.getTypedWrapped(), "queryBitmapInfo", ptr, w, h, bpp, bpl, pf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import org.virtualbox_6_1.IEvent;
|
||||
|
||||
public interface IEventHandler {
|
||||
void handleEvent(IEvent iEvent);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
public interface IMCFrameBuffer {
|
||||
void notifyUpdate(long x, long y, long width, long height);
|
||||
|
||||
void notifyUpdateImage(long x, long y, long width, long height, byte[] image);
|
||||
|
||||
void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height);
|
||||
}
|
45
VirtualComputerXPCOM/pom.xml
Normal file
45
VirtualComputerXPCOM/pom.xml
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>VirtualComputer</artifactId>
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>VirtualComputer-XPCOM</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<version>6.1</version>
|
||||
<optional>true
|
||||
</optional> <!-- https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.virtualbox:VirtualBox</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,122 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.mozilla.interfaces.IFramebuffer;
|
||||
import org.mozilla.interfaces.IFramebufferOverlay;
|
||||
import org.mozilla.interfaces.nsISupports;
|
||||
import org.mozilla.xpcom.Mozilla;
|
||||
import org.virtualbox_6_1.BitmapFormat;
|
||||
import org.virtualbox_6_1.FramebufferCapabilities;
|
||||
import sznp.virtualcomputer.util.IMCFrameBuffer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class COMFrameBuffer implements IFramebuffer {
|
||||
private final IMCFrameBuffer frameBuffer;
|
||||
|
||||
@Override
|
||||
public nsISupports queryInterface(String id) {
|
||||
return Mozilla.queryInterface(this, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBitsPerPixel() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBytesPerLine() {
|
||||
return 640L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] getCapabilities(long[] arg0) {
|
||||
try {
|
||||
System.out.println("Capabilities queried");
|
||||
System.out.println("Capabilities: " + Arrays.toString(arg0));
|
||||
return new long[]{FramebufferCapabilities.UpdateImage.value()};
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new long[]{};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHeight() {
|
||||
return 480;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHeightReduction() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IFramebufferOverlay getOverlay() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPixelFormat() {
|
||||
return BitmapFormat.BGRA.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVisibleRegion(byte arg0, long arg1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWidth() {
|
||||
return 640;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getWinId() {
|
||||
return 0; // Zero means no win id
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify3DEvent(long type, byte[] data) {
|
||||
System.out.println("3D event! " + type + " - " + Arrays.toString(data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height) {
|
||||
frameBuffer.notifyChange(screenId, xOrigin, yOrigin, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdate(long x, long y, long width, long height) {
|
||||
frameBuffer.notifyUpdate(x, y, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyUpdateImage(long arg0, long arg1, long arg2, long arg3, byte[] arg4) {
|
||||
frameBuffer.notifyUpdateImage(arg0, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibleRegion(byte arg0, long arg1) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Posts a Video HW Acceleration Command to the frame buffer for processing.<br />
|
||||
* <br />
|
||||
* The commands used for 2D video acceleration (DDraw surface creation/destroying, blitting, scaling, color conversion, overlaying, etc.) are posted from quest to the host to be processed by the host hardware.
|
||||
*
|
||||
* @param command Pointer to VBOXVHWACMD containing the command to execute.
|
||||
* @param enmCmd The validated VBOXVHWACMD::enmCmd value from the command.
|
||||
* @param fromGuest Set when the command origins from the guest, clear if host.
|
||||
*/ //https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Frontends/VirtualBox/src/VBoxFBOverlay.cpp#L4645
|
||||
@Override
|
||||
public void processVHWACommand(byte command, int enmCmd, boolean fromGuest) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean videoModeSupported(long arg0, long arg1, long arg2) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package sznp.virtualcomputer;
|
||||
|
||||
import org.mozilla.interfaces.IEvent;
|
||||
import org.mozilla.interfaces.IEventListener;
|
||||
import sznp.virtualcomputer.util.COMObjectBase;
|
||||
import sznp.virtualcomputer.util.IEventHandler;
|
||||
|
||||
/**
|
||||
* A Bukkit-like event system which calls the appropriate methods on an event.
|
||||
*/
|
||||
public final class EventHandler extends COMObjectBase implements IEventListener {
|
||||
private final IEventHandler handler;
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* New XPCOM event handler.
|
||||
*
|
||||
* @param handler The handle method that handles what needs to be handled
|
||||
*/
|
||||
public EventHandler(IEventHandler handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void handleEvent(IEvent iEvent) {
|
||||
if (!enabled)
|
||||
return;
|
||||
handler.handleEvent(new org.virtualbox_6_1.IEvent(iEvent));
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import lombok.val;
|
||||
import org.virtualbox_6_1.*;
|
||||
import org.virtualbox_6_1.xpcom.IUnknown;
|
||||
import sznp.virtualcomputer.COMFrameBuffer;
|
||||
import sznp.virtualcomputer.EventHandler;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public final class COMUtils {
|
||||
private COMUtils() {
|
||||
}
|
||||
|
||||
//public static void registerListener(IEventSource source, IEventListener listener, VBoxEventType... types) {
|
||||
public static org.virtualbox_6_1.IEventListener registerListener(IEventSource source, IEventHandler listener, List<VBoxEventType> types) {
|
||||
val ret = new org.virtualbox_6_1.IEventListener(new EventHandler(listener));
|
||||
source.registerListener(ret, types, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends IEvent> T getEvent(IEvent event, Class<T> cl) {
|
||||
try {
|
||||
val method = cl.getMethod("queryInterface", IUnknown.class);
|
||||
return (T) method.invoke(null, event);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static IFramebuffer gimmeAFramebuffer(IMCFrameBuffer frameBuffer) {
|
||||
return new IFramebuffer(new COMFrameBuffer(frameBuffer));
|
||||
}
|
||||
|
||||
public static void queryBitmapInfo(IDisplaySourceBitmap bitmap, long[] ptr, long[] w, long[] h, long[] bpp, long[] bpl, long[] pf) {
|
||||
bitmap.getTypedWrapped().queryBitmapInfo(ptr, w, h, bpp, bpl, pf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
import org.virtualbox_6_1.IEvent;
|
||||
|
||||
public interface IEventHandler {
|
||||
void handleEvent(IEvent iEvent);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package sznp.virtualcomputer.util;
|
||||
|
||||
public interface IMCFrameBuffer {
|
||||
void notifyUpdate(long x, long y, long width, long height);
|
||||
|
||||
void notifyUpdateImage(long x, long y, long width, long height, byte[] image);
|
||||
|
||||
void notifyChange(long screenId, long xOrigin, long yOrigin, long width, long height);
|
||||
}
|
135
pom.xml
Normal file
135
pom.xml
Normal file
|
@ -0,0 +1,135 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>io.github.NorbiPeti</groupId>
|
||||
<artifactId>VirtualComputer</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
<modules>
|
||||
<module>VirtualComputerXPCOM</module>
|
||||
<module>VirtualComputer-Core</module>
|
||||
<module>VirtualComputerMSCOM</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>VirtualComputer</name>
|
||||
<url>https://github.com/NorbiPeti/VirtualComputer</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>repo</id>
|
||||
<url>file://${basedir}/../repo</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack</id>
|
||||
<url>https://jitpack.io/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.12</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/com.github.jnr/jnr-ffi -->
|
||||
<dependency>
|
||||
<groupId>com.github.jnr</groupId>
|
||||
<artifactId>jnr-ffi</artifactId>
|
||||
<version>2.1.16</version>
|
||||
</dependency>
|
||||
<dependency> <!-- javax.annotations.Nullable -->
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<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 -
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.16.1-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency> -->
|
||||
<dependency>
|
||||
<groupId>com.github.TBMCPlugins.ChromaCore</groupId>
|
||||
<artifactId>Chroma-Core</artifactId>
|
||||
<version>v1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
BIN
repo/net/sf/jacob-project/jacob/1.19/jacob-1.19.jar
Normal file
BIN
repo/net/sf/jacob-project/jacob/1.19/jacob-1.19.jar
Normal file
Binary file not shown.
9
repo/net/sf/jacob-project/jacob/1.19/jacob-1.19.pom
Normal file
9
repo/net/sf/jacob-project/jacob/1.19/jacob-1.19.pom
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>net.sf.jacob-project</groupId>
|
||||
<artifactId>jacob</artifactId>
|
||||
<version>1.19</version>
|
||||
<description>POM was created from install:install-file</description>
|
||||
</project>
|
12
repo/net/sf/jacob-project/jacob/maven-metadata-local.xml
Normal file
12
repo/net/sf/jacob-project/jacob/maven-metadata-local.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>net.sf.jacob-project</groupId>
|
||||
<artifactId>jacob</artifactId>
|
||||
<versioning>
|
||||
<release>1.19</release>
|
||||
<versions>
|
||||
<version>1.19</version>
|
||||
</versions>
|
||||
<lastUpdated>20200811112851</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
Binary file not shown.
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<version>5.1</version>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<version>6.1</version>
|
||||
<description>POM was created from install:install-file</description>
|
||||
</project>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox-MSCOM</artifactId>
|
||||
<versioning>
|
||||
<release>6.1</release>
|
||||
<versions>
|
||||
<version>6.0</version>
|
||||
<version>6.1</version>
|
||||
</versions>
|
||||
<lastUpdated>20200731215317</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
BIN
repo/org/virtualbox/VirtualBox/5.1/VirtualBox-5.1-javadoc.jar
Normal file
BIN
repo/org/virtualbox/VirtualBox/5.1/VirtualBox-5.1-javadoc.jar
Normal file
Binary file not shown.
Binary file not shown.
10
repo/org/virtualbox/VirtualBox/5.1/VirtualBox-5.1.pom
Normal file
10
repo/org/virtualbox/VirtualBox/5.1/VirtualBox-5.1.pom
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<version>5.1</version>
|
||||
<description>POM was created from install:install-file</description>
|
||||
</project>
|
10
repo/org/virtualbox/VirtualBox/5.2/VirtualBox-5.2.pom
Executable file
10
repo/org/virtualbox/VirtualBox/5.2/VirtualBox-5.2.pom
Executable file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<version>5.2</version>
|
||||
<description>POM was created from install:install-file</description>
|
||||
</project>
|
BIN
repo/org/virtualbox/VirtualBox/6.1/VirtualBox-6.1-sources.jar
Normal file
BIN
repo/org/virtualbox/VirtualBox/6.1/VirtualBox-6.1-sources.jar
Normal file
Binary file not shown.
BIN
repo/org/virtualbox/VirtualBox/6.1/VirtualBox-6.1.jar
Normal file
BIN
repo/org/virtualbox/VirtualBox/6.1/VirtualBox-6.1.jar
Normal file
Binary file not shown.
4
VirtualComputer/repo/org/virtualbox/VirtualBox/5.2/VirtualBox-5.2.pom → repo/org/virtualbox/VirtualBox/6.1/VirtualBox-6.1.pom
Executable file → Normal file
4
VirtualComputer/repo/org/virtualbox/VirtualBox/5.2/VirtualBox-5.2.pom → repo/org/virtualbox/VirtualBox/6.1/VirtualBox-6.1.pom
Executable file → Normal file
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<version>5.2</version>
|
||||
<version>6.1</version>
|
||||
<description>POM was created from install:install-file</description>
|
||||
</project>
|
|
@ -3,12 +3,13 @@
|
|||
<groupId>org.virtualbox</groupId>
|
||||
<artifactId>VirtualBox</artifactId>
|
||||
<versioning>
|
||||
<release>6.0</release>
|
||||
<release>6.1</release>
|
||||
<versions>
|
||||
<version>5.1</version>
|
||||
<version>5.2</version>
|
||||
<version>6.0</version>
|
||||
<version>6.0</version>
|
||||
<version>6.1</version>
|
||||
</versions>
|
||||
<lastUpdated>20181221211412</lastUpdated>
|
||||
<lastUpdated>20200811171440</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
Loading…
Reference in a new issue