From 9ef0b00caa2b0a99299f73a6ee9bcac9a7ad5604 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 24 Jun 2017 19:20:20 +0200 Subject: [PATCH] Implemented computer methods, fixes --- VirtualComputer/dependency-reduced-pom.xml | 12 +- .../sznp/virtualcomputer/BukkitRenderer.java | 37 ++++- .../src/sznp/virtualcomputer/Commands.java | 5 +- .../sznp/virtualcomputer/DirectRenderer.java | 6 +- .../src/sznp/virtualcomputer/IRenderer.java | 12 ++ .../src/sznp/virtualcomputer/PluginMain.java | 147 ++++++++++++------ .../src/sznp/virtualcomputer/Scancode.java | 114 ++++++++++++++ 7 files changed, 270 insertions(+), 63 deletions(-) create mode 100644 VirtualComputer/src/sznp/virtualcomputer/Scancode.java diff --git a/VirtualComputer/dependency-reduced-pom.xml b/VirtualComputer/dependency-reduced-pom.xml index 4a5d768..21feae7 100644 --- a/VirtualComputer/dependency-reduced-pom.xml +++ b/VirtualComputer/dependency-reduced-pom.xml @@ -70,7 +70,7 @@ org.spigotmc spigot-api - 1.9.2-R0.1-SNAPSHOT + 1.12-R0.1-SNAPSHOT provided @@ -89,10 +89,6 @@ gson com.google.code.gson - - ebean - org.avaje - snakeyaml org.yaml @@ -109,6 +105,12 @@ 3.0.0 provided + + org.bukkit + craftbukkit + 1.12-R0.1-SNAPSHOT + provided + diff --git a/VirtualComputer/src/sznp/virtualcomputer/BukkitRenderer.java b/VirtualComputer/src/sznp/virtualcomputer/BukkitRenderer.java index 48ae404..c832e52 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/BukkitRenderer.java +++ b/VirtualComputer/src/sznp/virtualcomputer/BukkitRenderer.java @@ -1,18 +1,38 @@ package sznp.virtualcomputer; import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; import java.util.concurrent.TimeUnit; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; import org.bukkit.map.MapRenderer; import org.bukkit.map.MapView; public class BukkitRenderer extends MapRenderer implements IRenderer { + private byte[] allpixels; private BufferedImage image; + private int startindex; - public BukkitRenderer(BufferedImage image) { - this.image = image; + /** + * 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 allpixels + * The raw pixel data from the machine in BGRA format + * @param startindex + * The index to start from in allpixels + */ + public BukkitRenderer(short id, World world, byte[] allpixels, int startindex) { + MapView map = IRenderer.prepare(id, world); + map.addRenderer(this); + this.allpixels = allpixels; + this.startindex = startindex; + image = new BufferedImage(640, 480, BufferedImage.TYPE_INT_RGB); } private int progress = 0; @@ -22,6 +42,19 @@ public class BukkitRenderer extends MapRenderer implements IRenderer { public void render(MapView view, MapCanvas canvas, Player player) { long time = System.nanoTime(); + final int[] a = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); // Directly update the bytes of the image + + // (byte) bgra to rgb (int) + for (int i = startindex, j = 0; i < startindex + 128 * 128; i = i + 4, j++) { + int b, g, r; + + b = allpixels[i] & 0xFF; + g = allpixels[i + 1] & 0xFF; + r = allpixels[i + 2] & 0xFF; + + a[j] = (r << 16) | (g << 8) | b; + } + try { canvas.drawImage(0, progress * updatepixels, image.getSubimage(0, progress * updatepixels, 128, (progress * updatepixels + updatepixels >= 128 ? 128 - progress * updatepixels : updatepixels))); diff --git a/VirtualComputer/src/sznp/virtualcomputer/Commands.java b/VirtualComputer/src/sznp/virtualcomputer/Commands.java index 5889c26..2fd4272 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/Commands.java +++ b/VirtualComputer/src/sznp/virtualcomputer/Commands.java @@ -62,7 +62,7 @@ public class Commands implements CommandExecutor case "key": if (args.length < 2) { - sender.sendMessage("§cUsage: /computer key [down/up|interval]"); + sender.sendMessage("§cUsage: /computer key [down/up|duration(ticks)]"); return true; } if (args.length < 3) @@ -187,8 +187,7 @@ public class Commands implements CommandExecutor } MouseLockerPlayerListener.LockedSpeed = Float .parseFloat(args[2]); - sender.sendMessage("§aMouse speed set to " - + MouseLockerPlayerListener.LockedSpeed); + sender.sendMessage("§aMouse speed set to " + MouseLockerPlayerListener.LockedSpeed); } break; } diff --git a/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java b/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java index 5838d99..73d9095 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java +++ b/VirtualComputer/src/sznp/virtualcomputer/DirectRenderer.java @@ -33,13 +33,9 @@ public class DirectRenderer implements IRenderer { * @throws Exception * Usually happens on incompatibility */ - @SuppressWarnings("deprecation") public DirectRenderer(short id, World world, byte[] allpixels, int startindex) throws Exception, Exception, Exception, Exception { - map = Bukkit.getMap(id); - if (map == null) - map = Bukkit.createMap(world); - map.getRenderers().clear(); + map = IRenderer.prepare(id, world); final Field field = map.getClass().getDeclaredField("renderCache"); field.setAccessible(true); @SuppressWarnings("unchecked") diff --git a/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java b/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java index 815ca2e..6d31d53 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java +++ b/VirtualComputer/src/sznp/virtualcomputer/IRenderer.java @@ -1,4 +1,16 @@ package sznp.virtualcomputer; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.map.MapView; + public interface IRenderer { + static MapView prepare(short id, World world) { + @SuppressWarnings("deprecation") + MapView map = Bukkit.getMap(id); + if (map == null) + map = Bukkit.createMap(world); + map.getRenderers().clear(); + return map; + } } diff --git a/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java b/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java index 7992b52..d928c32 100644 --- a/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java +++ b/VirtualComputer/src/sznp/virtualcomputer/PluginMain.java @@ -1,8 +1,8 @@ package sznp.virtualcomputer; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; -import java.util.HashMap; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.CraftManager; @@ -12,21 +12,18 @@ import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitTask; -import org.virtualbox_5_1.IFramebuffer; -import org.virtualbox_5_1.ISession; -import org.virtualbox_5_1.IVirtualBox; -import org.virtualbox_5_1.VirtualBoxManager; +import org.virtualbox_5_1.*; -import com.mcplugindev.slipswhitley.sketchmap.map.RelativeLocation; -import com.mcplugindev.slipswhitley.sketchmap.map.SketchMap; +import com.google.common.collect.Lists; public class PluginMain extends JavaPlugin { private IVirtualBox vbox; private ISession session; - private SketchMap smap; + private ArrayList renderers = new ArrayList<>(); + private IMachine machine; public static PluginMain Instance; + public static byte[] allpixels = new byte[640 * 480]; // Fired when plugin is first enabled @Override @@ -36,21 +33,27 @@ public class PluginMain extends JavaPlugin { ConsoleCommandSender ccs = getServer().getConsoleSender(); this.getCommand("computer").setExecutor(new Commands()); ccs.sendMessage("§bInitializing VirtualBox..."); + final String vbpath = System.getProperty("os.name").toLowerCase().contains("mac") + ? "/Applications/VirtualBox.app/Contents/MacOS" : "/opt/virtualbox"; if (System.getProperty("vbox.home") == null || System.getProperty("vbox.home").isEmpty()) - System.setProperty("vbox.home", "/opt/virtualbox"); + 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); + addLibraryPath(vbpath); final VirtualBoxManager manager = VirtualBoxManager.createInstance(getDataFolder().getAbsolutePath()); vbox = manager.getVBox(); session = manager.getSessionObject(); - ccs.sendMessage("§bStarting VM for testing..."); - vbox.getMachines().get(0).launchVMProcess(session, "headless", "").waitForCompletion(2000); - session.getConsole().getDisplay().attachFramebuffer(0L, new IFramebuffer(new MCFrameBuffer())); - ccs.sendMessage("§bLoading SketchMap..."); - img = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB); - HashMap map = new HashMap<>(); - for (int i = 0; i < 5; i++) - for (int j = 0; j < 4; j++) - map.put((short) (i * 4 + j), new RelativeLocation(i, j)); - smap = new SketchMap(img, "Screen", 5, 4, false, map); + ccs.sendMessage("§bLoading Screen..."); + try { + for (short i = 0; i < 20; i++) + renderers.add(new DirectRenderer(i, Bukkit.getWorlds().get(0), allpixels, i * 128 * 128 * 4)); // TODO: The pixels are selected in a horribly wrong way probably + ccs.sendMessage("§bUsing Direct Renderer"); + } catch (NoClassDefFoundError e) { + for (short i = 0; i < 20; i++) + renderers.add(new BukkitRenderer(i, Bukkit.getWorlds().get(0), allpixels, i * 128 * 128 * 4)); + ccs.sendMessage("§6Compability error, using slower renderer"); + } ccs.sendMessage("§bLoaded!"); getServer().getPluginManager().registerEvents(new MouseLockerPlayerListener(), this); DoStart(); @@ -67,12 +70,12 @@ public class PluginMain extends JavaPlugin { saveConfig(); } - private volatile BufferedImage img; - private volatile BukkitTask task = null; - public void Start(CommandSender sender) { sender.sendMessage("§eStarting computer..."); - // computer.Start(); + if (machine == null) + machine = vbox.getMachines().get(0); + machine.launchVMProcess(session, "headless", "").waitForCompletion(10000); + session.getConsole().getDisplay().attachFramebuffer(0L, new IFramebuffer(new MCFrameBuffer())); sender.sendMessage("§eComputer started."); DoStart(); } @@ -80,14 +83,6 @@ public class PluginMain extends JavaPlugin { public static int MouseSpeed = 1; private void DoStart() { - if (task == null) - task = this.getServer().getScheduler().runTaskTimerAsynchronously(this, new Runnable() { - public void run() { - final int[] a = ((DataBufferInt) smap.image.getRaster().getDataBuffer()).getData(); - // final int[] data = computer.GetScreenPixelColors(); - // System.arraycopy(data, 0, a, 0, data.length); - } - }, 1, 10); if (getServer().getPluginManager().isPluginEnabled("Movecraft")) { this.getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() { public void run() { @@ -113,50 +108,106 @@ public class PluginMain extends JavaPlugin { public void Stop(CommandSender sender) { sender.sendMessage("§eStopping computer..."); - // computer.PowerOff(); + session.getConsole().powerDown().waitForCompletion(2000); sender.sendMessage("§eComputer stopped."); } public void PowerButton(CommandSender sender) { - sender.sendMessage("§eStarting/stoppping computer..."); + sender.sendMessage("§ePressing powerbutton..."); final CommandSender s = sender; getServer().getScheduler().runTaskAsynchronously(this, new Runnable() { @Override public void run() { - /* - * if (computer.PowerButton()) { DoStart(); s.sendMessage("§eComputer started."); } else s.sendMessage("§ePowerbutton pressed."); - */ + if (session.getState() != SessionState.Locked || session.getMachine() == null) { + Start(sender); + } else { + session.getConsole().powerButton(); + s.sendMessage("§ePowerbutton pressed."); + } } }); } public void Reset(CommandSender sender) { sender.sendMessage("§eResetting computer..."); - // computer.Reset(); + if (session.getState() == SessionState.Locked) + session.getConsole().powerDown().waitForCompletion(10000); sender.sendMessage("§eComputer reset."); } public void FixScreen(CommandSender sender) { sender.sendMessage("§eFixing screen..."); - // computer.FixScreen(); + session.getConsole().getDisplay().setSeamlessMode(false); + session.getConsole().getDisplay().setVideoModeHint(0L, true, false, 0, 0, 640L, 480L, 32L); sender.sendMessage("§eScreen fixed."); } public void PressKey(CommandSender sender, String key, String stateorduration) { - /* - * if (stateorduration.length() == 0) computer.PressKey(key, (short) 0); else if (stateorduration.equalsIgnoreCase("down")) computer.PressKey(key, (short) -1); else if - * (stateorduration.equalsIgnoreCase("up")) computer.PressKey(key, (short) -2); else computer.PressKey(key, Short.parseShort(stateorduration)); - */ + if (session.getState() == SessionState.Locked) { + 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 = 0; + // 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(this, sendrelease, durationorstate); + } + } } public void UpdateMouse(CommandSender sender, int x, int y, int z, int w, String mbs, boolean down) { - /* - * if (down) computer.UpdateMouse(x, y, z, w, mbs); else computer.UpdateMouse(x, y, z, w, ""); - */ + if (session.getState() != SessionState.Locked) + return; + int state = 0; + if (mbs.length() > 0 && down) + state = Arrays.stream(MouseButtonState.values()).filter(mousebs -> mousebs.name().equalsIgnoreCase(mbs)) + .findAny().orElseThrow(() -> new RuntimeException("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) { UpdateMouse(sender, x, y, z, w, mbs, true); UpdateMouse(sender, x, y, z, w, mbs, false); } + + /** + * 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); + } } diff --git a/VirtualComputer/src/sznp/virtualcomputer/Scancode.java b/VirtualComputer/src/sznp/virtualcomputer/Scancode.java new file mode 100644 index 0000000..2a4a623 --- /dev/null +++ b/VirtualComputer/src/sznp/virtualcomputer/Scancode.java @@ -0,0 +1,114 @@ +package sznp.virtualcomputer; + +/* +The scancode values come from: +- http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc (March 16), 2000). +- http://www.computer-engineering.org/ps2keyboard/scancodes1.html +- using MapVirtualKeyEx( VK_*), MAPVK_VK_TO_VSC_EX), 0 ) with the english us keyboard layout +- reading win32 WM_INPUT keyboard messages. +*/ + +enum Scancode { // https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names + + sc_escape(0x01), sc_1(0x02), sc_2(0x03), sc_3(0x04), sc_4(0x05), sc_5(0x06), sc_6(0x07), sc_7(0x08), sc_8( + 0x09), sc_9(0x0A), sc_0(0x0B), sc_minus(0x0C), sc_equals(0x0D), sc_backspace(0x0E), sc_tab(0x0F), sc_q( + 0x10), sc_w(0x11), sc_e(0x12), sc_r(0x13), sc_t(0x14), sc_y(0x15), sc_u(0x16), sc_i(0x17), sc_o( + 0x18), sc_p(0x19), sc_bracketLeft(0x1A), sc_bracketRight(0x1B), sc_enter( + 0x1C), sc_controlLeft(0x1D), sc_a(0x1E), sc_s(0x1F), sc_d( + 0x20), sc_f(0x21), sc_g(0x22), sc_h(0x23), sc_j(0x24), sc_k(0x25), sc_l( + 0x26), sc_semicolon(0x27), sc_apostrophe(0x28), sc_grave( + 0x29), sc_shiftLeft(0x2A), sc_backslash(0x2B), sc_z( + 0x2C), sc_x(0x2D), sc_c(0x2E), sc_v(0x2F), sc_b( + 0x30), sc_n(0x31), sc_m(0x32), sc_comma( + 0x33), sc_preiod(0x34), sc_slash( + 0x35), sc_shiftRight( + 0x36), sc_numpad_multiply( + 0x37), sc_altLeft( + 0x38), sc_space( + 0x39), sc_capsLock( + 0x3A), sc_f1( + 0x3B), sc_f2( + 0x3C), sc_f3( + 0x3D), sc_f4( + 0x3E), sc_f5( + 0x3F), sc_f6( + 0x40), sc_f7( + 0x41), sc_f8( + 0x42), sc_f9( + 0x43), sc_f10( + 0x44), sc_numLock( + 0x45), sc_scrollLock( + 0x46), sc_numpad_7( + 0x47), sc_numpad_8( + 0x48), sc_numpad_9( + 0x49), sc_numpad_minus( + 0x4A), sc_numpad_4( + 0x4B), sc_numpad_5( + 0x4C), sc_numpad_6( + 0x4D), sc_numpad_plus( + 0x4E), sc_numpad_1( + 0x4F), sc_numpad_2( + 0x50), sc_numpad_3( + 0x51), sc_numpad_0( + 0x52), sc_numpad_period( + 0x53), sc_alt_printScreen( + 0x54), /* + * Alt + * + + * print + * screen. + * MapVirtualKeyEx( + * VK_SNAPSHOT + * ) + * , + * MAPVK_VK_TO_VSC_EX + * ) + * , + * 0 + * ) + * returns + * scancode + * 0x54. + */ + sc_bracketAngle(0x56), /* Key between the left shift and Z. */ + sc_f11(0x57), sc_f12(0x58), sc_oem_1(0x5a), /* VK_OEM_WSCTRL */ + sc_oem_2(0x5b), /* VK_OEM_FINISH */ + sc_oem_3(0x5c), /* VK_OEM_JUMP */ + sc_eraseEOF(0x5d), sc_oem_4(0x5e), /* VK_OEM_BACKTAB */ + sc_oem_5(0x5f), /* VK_OEM_AUTO */ + sc_zoom(0x62), sc_help(0x63), sc_f13(0x64), sc_f14(0x65), sc_f15(0x66), sc_f16(0x67), sc_f17(0x68), sc_f18( + 0x69), sc_f19( + 0x6a), sc_f20(0x6b), sc_f21(0x6c), sc_f22(0x6d), sc_f23(0x6e), sc_oem_6(0x6f), /* VK_OEM_PA3 */ + sc_katakana(0x70), sc_oem_7(0x71), /* VK_OEM_RESET */ + sc_f24(0x76), sc_sbcschar(0x77), sc_convert(0x79), sc_nonconvert(0x7B), /* VK_OEM_PA1 */ + + sc_media_previous(0xE010), sc_media_next(0xE019), sc_numpad_enter(0xE01C), sc_controlRight(0xE01D), sc_volume_mute( + 0xE020), sc_launch_app2(0xE021), sc_media_play(0xE022), sc_media_stop( + 0xE024), sc_volume_down(0xE02E), sc_volume_up( + 0xE030), sc_browser_home(0xE032), sc_numpad_divide(0xE035), sc_printScreen(0xE037), + /* + * sc_printScreen: - make: 0xE02A 0xE037 - break: 0xE0B7 0xE0AA - MapVirtualKeyEx( VK_SNAPSHOT), MAPVK_VK_TO_VSC_EX), 0 ) returns scancode 0x54; - There is no VK_KEYDOWN with VK_SNAPSHOT. + */ + sc_altRight(0xE038), sc_cancel(0xE046), /* CTRL + Pause */ + sc_home(0xE047), sc_arrowUp(0xE048), sc_pageUp(0xE049), sc_arrowLeft(0xE04B), sc_arrowRight(0xE04D), sc_end( + 0xE04F), sc_arrowDown(0xE050), sc_pageDown(0xE051), sc_insert(0xE052), sc_delete(0xE053), sc_metaLeft( + 0xE05B), sc_metaRight(0xE05C), sc_application(0xE05D), sc_power(0xE05E), sc_sleep(0xE05F), sc_wake( + 0xE063), sc_browser_search(0xE065), sc_browser_favorites(0xE066), sc_browser_refresh( + 0xE067), sc_browser_stop(0xE068), sc_browser_forward(0xE069), sc_browser_back( + 0xE06A), sc_launch_app1( + 0xE06B), sc_launch_email(0xE06C), sc_launch_media(0xE06D), + + sc_pause(0xE11D45); + /* + * sc_pause: - make: 0xE11D 45 0xE19D C5 - make in raw input: 0xE11D 0x45 - break: none - No repeat when you hold the key down - There are no break so I don't know how the key down/up is expected + * to work. Raw input sends "keydown" and "keyup" messages), and it appears that the keyup message is sent directly after the keydown message (you can't hold the key down) so depending on when + * GetMessage or PeekMessage will return messages), you may get both a keydown and keyup message "at the same time". If you use VK messages most of the time you only get keydown messages), but + * some times you get keyup messages too. - when pressed at the same time as one or both control keys, generates a 0xE046 (sc_cancel) and the string for that scancode is "break". + */ + + public int Code; + + Scancode(int code) { + Code = code; + } +}; \ No newline at end of file