diff --git a/.classpath b/.classpath index a1e0553..a1aa4e3 100644 --- a/.classpath +++ b/.classpath @@ -8,6 +8,7 @@ + diff --git a/build.xml b/build.xml index d65f456..0d71ec9 100644 --- a/build.xml +++ b/build.xml @@ -7,6 +7,7 @@ + @@ -104,6 +105,7 @@ + diff --git a/lib/coreapi-2.0.9.jar b/lib/coreapi-2.0.9.jar new file mode 100644 index 0000000..fce99de Binary files /dev/null and b/lib/coreapi-2.0.9.jar differ diff --git a/lib/jinput-2.0.9.jar b/lib/jinput-2.0.9.jar new file mode 100644 index 0000000..e46a78f Binary files /dev/null and b/lib/jinput-2.0.9.jar differ diff --git a/lib/jinput-natives/jinput-dx8_64.dll b/lib/jinput-natives/jinput-dx8_64.dll new file mode 100644 index 0000000..732718c Binary files /dev/null and b/lib/jinput-natives/jinput-dx8_64.dll differ diff --git a/lib/jinput-natives/jinput-raw_64.dll b/lib/jinput-natives/jinput-raw_64.dll new file mode 100644 index 0000000..644d106 Binary files /dev/null and b/lib/jinput-natives/jinput-raw_64.dll differ diff --git a/lib/jinput-natives/jinput-wintab.dll b/lib/jinput-natives/jinput-wintab.dll new file mode 100644 index 0000000..3ac18eb Binary files /dev/null and b/lib/jinput-natives/jinput-wintab.dll differ diff --git a/lib/jinput-natives/libjinput-linux64.so b/lib/jinput-natives/libjinput-linux64.so new file mode 100644 index 0000000..0512c92 Binary files /dev/null and b/lib/jinput-natives/libjinput-linux64.so differ diff --git a/lib/jinput-natives/libjinput-osx.jnilib b/lib/jinput-natives/libjinput-osx.jnilib new file mode 100644 index 0000000..4d90199 Binary files /dev/null and b/lib/jinput-natives/libjinput-osx.jnilib differ diff --git a/src/Client/ConfigWindow.java b/src/Client/ConfigWindow.java index 7938ac9..0abfbf7 100644 --- a/src/Client/ConfigWindow.java +++ b/src/Client/ConfigWindow.java @@ -27,6 +27,7 @@ import Game.Camera; import Game.Client; import Game.Game; +import Game.JoystickHandler; import Game.KeyboardHandler; import Game.Renderer; import com.formdev.flatlaf.ui.FlatRoundBorder; @@ -67,6 +68,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Hashtable; +import java.util.LinkedHashMap; import javax.imageio.ImageIO; import javax.swing.BorderFactory; import javax.swing.Box; @@ -284,6 +286,12 @@ public class ConfigWindow { private HashMap worldListSpacingLabels = new HashMap(); private JPanel worldListPanel = new JPanel(); + //// Joystick tab + private JCheckBox joystickPanelJoystickEnabledCheckbox; + private HashMap joystickInputJlabels = new LinkedHashMap(); + private HashMap joystickInputValueJlabels = new HashMap(); + private JPanel joystickPanel = new JPanel(); + public ConfigWindow() { Util.setUITheme(); eventQueueListener = createConfigWindowEventQueueListener(); @@ -392,6 +400,7 @@ public void mouseClicked(MouseEvent e) { JScrollPane streamingScrollPane = new JScrollPane(); JScrollPane keybindScrollPane = new JScrollPane(); JScrollPane worldListScrollPane = new JScrollPane(); + JScrollPane joystickScrollPane = new JScrollPane(); JScrollPane authorsScrollPane = new JScrollPane(); if (isUsingFlatLAFTheme()) { @@ -424,6 +433,7 @@ public void mouseClicked(MouseEvent e) { streamingScrollPane.setBorder(scrollPaneBorder); keybindScrollPane.setBorder(scrollPaneBorder); worldListScrollPane.setBorder(scrollPaneBorder); + joystickScrollPane.setBorder(scrollPaneBorder); authorsScrollPane.setBorder(scrollPaneBorder); } @@ -441,6 +451,8 @@ public void mouseClicked(MouseEvent e) { keybindPanel.setName("keybinds"); worldListPanel = new JPanel(); worldListPanel.setName("world_list"); + joystickPanel = new JPanel(); + joystickPanel.setName("Joystick"); JPanel authorsPanel = new JPanel(); authorsPanel.setName("authors"); @@ -459,6 +471,7 @@ public void mouseClicked(MouseEvent e) { tabbedPane.addTab("Streaming & Privacy", null, streamingScrollPane, null); tabbedPane.addTab("Keybinds", null, keybindScrollPane, null); tabbedPane.addTab("World List", null, worldListScrollPane, null); + tabbedPane.addTab("Joystick", null, joystickScrollPane, null); tabbedPane.addTab("Authors", null, authorsScrollPane, null); presetsScrollPane.setViewportView(presetsPanel); @@ -468,6 +481,7 @@ public void mouseClicked(MouseEvent e) { streamingScrollPane.setViewportView(streamingPanel); keybindScrollPane.setViewportView(keybindPanel); worldListScrollPane.setViewportView(worldListPanel); + joystickScrollPane.setViewportView(joystickPanel); authorsScrollPane.setViewportView(authorsPanel); // Adding padding for aesthetics @@ -495,6 +509,8 @@ public void mouseClicked(MouseEvent e) { keybindPanel.setBorder(BorderFactory.createEmptyBorder(border10, border10, border10, border10)); worldListPanel.setBorder( BorderFactory.createEmptyBorder(border10, border10, border10, border10)); + joystickPanel.setBorder( + BorderFactory.createEmptyBorder(border10, border10, border10, border10)); authorsPanel.setBorder(BorderFactory.createEmptyBorder(border10, border10, border10, border10)); int verticalSpeed = osScaleMul(20); @@ -507,6 +523,7 @@ public void mouseClicked(MouseEvent e) { setScrollSpeed(streamingScrollPane, verticalSpeed, horizontalSpeed); setScrollSpeed(keybindScrollPane, verticalSpeed, horizontalSpeed); setScrollSpeed(worldListScrollPane, verticalSpeed, horizontalSpeed); + setScrollSpeed(joystickScrollPane, verticalSpeed, horizontalSpeed); setScrollSpeed(authorsScrollPane, verticalSpeed, horizontalSpeed); /* @@ -2396,6 +2413,62 @@ public void actionPerformed(ActionEvent e) { addPanelBottomGlue(authorsPanel); + // Joystick Tab + + joystickPanel.setAlignmentY(Component.TOP_ALIGNMENT); + joystickPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + joystickPanel.setLayout(new BoxLayout(joystickPanel, BoxLayout.Y_AXIS)); + + addSettingsHeader(joystickPanel, "Explanation"); + + JLabel joystickExplanation = + new JLabel( + String.format( + "

" + + "Currently, RSCx is compatible with only the 3DConnexion Space Navigator 3D Mouse.
" + + "It is used to enable a 5 degree of freedom camera (roll left/right is omitted).

" + + "This setting does not allow you to move the player with a joystick or perform in-game actions.

" + + "If you do not have a 3DConnexion Space Navigator 3D Mouse, you should not enable this setting." + + "


", + osScaleMul(10))); + joystickPanel.add(joystickExplanation); + + addSettingsHeader(joystickPanel, "Joystick"); + joystickPanelJoystickEnabledCheckbox = + addCheckbox( + "Enable Joystick polling (Performance decreased if not using joystick)", joystickPanel); + joystickPanelJoystickEnabledCheckbox.setToolTipText("Enable Joystick polling once every frame"); + joystickPanelJoystickEnabledCheckbox.setBorder( + BorderFactory.createEmptyBorder(0, 0, osScaleMul(7), 0)); + + joystickInputJlabels.put("X Axis", new JLabel("X Axis")); + joystickInputJlabels.put("Y Axis", new JLabel("Y Axis")); + joystickInputJlabels.put("Z Axis", new JLabel("Z Axis")); + joystickInputJlabels.put("X Rotation", new JLabel("X Rotate")); + joystickInputJlabels.put("Y Rotation", new JLabel("Y Rotate")); + joystickInputJlabels.put("Z Rotation", new JLabel("Z Rotate")); + joystickInputJlabels.put("Button 0", new JLabel("Button 0")); + joystickInputJlabels.put("Button 1", new JLabel("Button 1")); + + joystickInputValueJlabels.put("X Axis", new JLabel("No input")); + joystickInputValueJlabels.put("Y Axis", new JLabel("No input")); + joystickInputValueJlabels.put("Z Axis", new JLabel("No input")); + joystickInputValueJlabels.put("X Rotation", new JLabel("No input")); + joystickInputValueJlabels.put("Y Rotation", new JLabel("No input")); + joystickInputValueJlabels.put("Z Rotation", new JLabel("No input")); + joystickInputValueJlabels.put("Button 0", new JLabel("No input")); + joystickInputValueJlabels.put("Button 1", new JLabel("No input")); + + joystickInputJlabels.forEach( + (key, value) -> { + joystickPanel.add(value); + joystickPanel.add(joystickInputValueJlabels.get(key)); + JLabel joystickInputspacerLabel = new JLabel("
"); + joystickPanel.add(joystickInputspacerLabel); + }); + + addPanelBottomGlue(joystickPanel); + //// End component creation //// } @@ -2928,6 +3001,10 @@ public void synchronizeGuiValues() { // World List tab synchronizeWorldTab(); + // Joystick tab + joystickPanelJoystickEnabledCheckbox.setSelected( + Settings.JOYSTICK_ENABLED.get(Settings.currentProfile)); + for (KeybindSet kbs : KeyboardHandler.keybindSetList) { setKeybindButtonText(kbs); } @@ -3208,6 +3285,10 @@ public void saveSettings() { if (Client.state == Client.STATE_GAME) Client.sortFriends(); + //// joystick + Settings.JOYSTICK_ENABLED.put( + Settings.currentProfile, joystickPanelJoystickEnabledCheckbox.isSelected()); + // Save Settings Settings.save(); } @@ -3542,6 +3623,16 @@ public void synchronizeWorldTab() { } } } + + public void updateJoystickInput(String compName) { + joystickInputValueJlabels + .get(compName) + .setText( + String.format( + "%d", (int) Math.floor(JoystickHandler.joystickInputReports.get(compName)))); + joystickPanel.revalidate(); + joystickPanel.repaint(); + } } /** Implements ActionListener; to be used for the buttons in the keybinds tab. */ diff --git a/src/Client/JClassPatcher.java b/src/Client/JClassPatcher.java index c9e251f..2a7e20d 100644 --- a/src/Client/JClassPatcher.java +++ b/src/Client/JClassPatcher.java @@ -140,6 +140,17 @@ private void patchGeneric(ClassNode node) { hookClassVariable( methodNode, "mudclient", "bz", "I", "Game/Client", "login_screen", "I", true, true); + hookClassVariable( + methodNode, + "jagex/client/j", + "dn", + "I", + "Game/Camera", + "pitch_internal", + "I", + true, + true); + hookConditionalClassVariable( methodNode, "jagex/client/j", @@ -2261,6 +2272,35 @@ private void patchScene(ClassNode node) { } } } + + // Set camera routine + if (methodNode.name.equals("uh") && methodNode.desc.equals("(IIIIIII)V")) { + Logger.Info("patching setCamera()"); + + // offset_height hook + AbstractInsnNode start = methodNode.instructions.getFirst(); + while (start != null) { + if (start.getOpcode() == Opcodes.PUTFIELD + && start.getPrevious().getOpcode() == Opcodes.ISUB) { + FieldInsnNode insnNode = (FieldInsnNode) start; + if (insnNode.name.equals("bn")) { + methodNode.instructions.insertBefore( + start, new FieldInsnNode(Opcodes.GETSTATIC, "Game/Camera", "offset_height", "I")); + methodNode.instructions.insertBefore(start, new InsnNode(Opcodes.ISUB)); + + break; + } + } + + start = start.getNext(); + } + + // post-hook + AbstractInsnNode findNode = methodNode.instructions.getLast(); + methodNode.instructions.insertBefore( + findNode, + new MethodInsnNode(Opcodes.INVOKESTATIC, "Game/Camera", "postSetCamera", "()V", false)); + } } } diff --git a/src/Client/Launcher.java b/src/Client/Launcher.java index 09b31a0..c10093b 100644 --- a/src/Client/Launcher.java +++ b/src/Client/Launcher.java @@ -34,6 +34,8 @@ import java.awt.event.AWTEventListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -118,6 +120,9 @@ public void init() { } } + // Extract libraries that only work outside the jar + extractJInputNatives(); + // Set size getContentPane().setPreferredSize(osScaleMul(new Dimension(280, 32))); setTitle("RSCTimes Launcher"); @@ -587,6 +592,42 @@ public static void finishedLoading() { Game.getInstance().getJConfig().changeWorld(1); } + public static void extractJInputNatives() { + extractResource( + "/lib/jinput-natives/jinput-dx8_64.dll", + new File(Settings.Dir.JINPUTNATIVELIB + "/jinput-dx8_64.dll")); + extractResource( + "/lib/jinput-natives/jinput-raw_64.dll", + new File(Settings.Dir.JINPUTNATIVELIB + "/jinput-raw_64.dll")); + extractResource( + "/lib/jinput-natives/jinput-wintab.dll", + new File(Settings.Dir.JINPUTNATIVELIB + "/jinput-wintab.dll")); + extractResource( + "/lib/jinput-natives/libjinput-linux64.so", + new File(Settings.Dir.JINPUTNATIVELIB + "/libjinput-linux64.so")); + extractResource( + "/lib/jinput-natives/libjinput-osx.jnilib", + new File(Settings.Dir.JINPUTNATIVELIB + "/libjinput-osx.jnilib")); + } + + public static void extractResource(String pathInJar, File destinationPath) { + try { + BufferedInputStream source = new BufferedInputStream(Launcher.getResourceAsStream(pathInJar)); + BufferedOutputStream target = new BufferedOutputStream(new FileOutputStream(destinationPath)); + byte[] buf = new byte[8192]; + int length; + while ((length = source.read(buf)) > 0) { + target.write(buf, 0, length); + } + source.close(); + target.close(); + Logger.Info("Successfully extracted " + pathInJar); + } catch (Exception e) { + Logger.Error("Could not extract " + pathInJar); + e.printStackTrace(); + } + } + /** @return the window */ public static ConfigWindow getConfigWindow() { return configWindow; diff --git a/src/Client/Settings.java b/src/Client/Settings.java index ad9584e..bb1112f 100644 --- a/src/Client/Settings.java +++ b/src/Client/Settings.java @@ -181,7 +181,10 @@ public class Settings { public static int WORLDS_TO_DISPLAY = 5; public static boolean noWorldsConfigured = true; - //// nogui + //// joystick + public static HashMap JOYSTICK_ENABLED = new HashMap(); + + //// no gui public static HashMap COMBAT_STYLE = new HashMap(); public static HashMap WORLD = new HashMap(); public static HashMap FIRST_TIME = new HashMap(); @@ -1159,6 +1162,16 @@ public static void definePresets(Properties props) { //// world list initWorlds(); + //// joystick + JOYSTICK_ENABLED.put("vanilla", false); + JOYSTICK_ENABLED.put("vanilla_resizable", false); + JOYSTICK_ENABLED.put("lite", false); + JOYSTICK_ENABLED.put("default", false); + JOYSTICK_ENABLED.put("heavy", false); + JOYSTICK_ENABLED.put("all", true); + JOYSTICK_ENABLED.put( + "custom", getPropBoolean(props, "joystick_enabled", JOYSTICK_ENABLED.get("default"))); + COMBAT_STYLE.put("vanilla", Client.COMBAT_AGGRESSIVE); COMBAT_STYLE.put("vanilla_resizable", Client.COMBAT_AGGRESSIVE); COMBAT_STYLE.put("lite", Client.COMBAT_AGGRESSIVE); @@ -1280,6 +1293,10 @@ public static void initDir() { // TODO: Consider moving to a more relevant place Util.makeDirectory(Dir.REPLAY); Dir.WORLDS = Dir.JAR + "/worlds"; Util.makeDirectory(Dir.WORLDS); + Dir.LIB = Dir.JAR + "/lib"; + Util.makeDirectory(Dir.LIB); + Dir.JINPUTNATIVELIB = Dir.LIB + "/jinput-natives"; + Util.makeDirectory(Dir.JINPUTNATIVELIB); } /** @@ -2262,6 +2279,9 @@ public static void save(String preset) { //// world urls saveWorlds(); + //// joystick + props.setProperty("joystick_enabled", Boolean.toString(JOYSTICK_ENABLED.get(preset))); + //// presets props.setProperty("current_profile", currentProfile); @@ -2468,6 +2488,8 @@ public static class Dir { public static String SCREENSHOT; public static String REPLAY; public static String WORLDS; + public static String LIB; + public static String JINPUTNATIVELIB; } public static void updateInjectedVariables() { diff --git a/src/Game/Camera.java b/src/Game/Camera.java index 94c65a2..c9179d6 100644 --- a/src/Game/Camera.java +++ b/src/Game/Camera.java @@ -42,6 +42,10 @@ public class Camera { public static int distance3; // This one is divided onto something to do with fog (it's usually 1) public static int distance4; // This one seems to be fog distance + // This will offset the camera height + public static int offset_height = 0; + + public static int pitch_internal; public static float add_lookat_x; public static float add_lookat_y; public static int new_lookat_x; @@ -51,6 +55,9 @@ public class Camera { public static float delta_zoom = 0.0f; public static float delta_rotation = 0.0f; + public static int pitch_rsctimes = 112; + public static boolean isUsing3DMouseControls = false; + private Camera() { // Empty private constructor to prevent instantiation. } @@ -97,7 +104,7 @@ public static void update(float delta_time) { else addZoom(8 * 50 * delta_time); } - if (!KeyboardHandler.keyShift) { + if (!KeyboardHandler.keyShift && !isUsing3DMouseControls) { int tileX = ((int) add_lookat_x / 128) * 128; int tileY = ((int) add_lookat_y / 128) * 128; add_lookat_x = Util.lerp(add_lookat_x, tileX, 8.0f * delta_time); @@ -116,9 +123,7 @@ public static void update(float delta_time) { // If the user changes modes, reset if (relative != Settings.CAMERA_MOVABLE_RELATIVE.get(Settings.currentProfile)) { - add_lookat_x = 0; - add_lookat_y = 0; - relative = Settings.CAMERA_MOVABLE_RELATIVE.get(Settings.currentProfile); + resetCamera(); } delta_lookat_x = new_lookat_x; // Util.lerp(delta_lookat_x, new_lookat_x, 8.0f * delta_time); @@ -157,6 +162,14 @@ public static void resize() { } } + private static void resetCamera() { + add_lookat_x = 0; + add_lookat_y = 0; + relative = Settings.CAMERA_MOVABLE_RELATIVE.get(Settings.currentProfile); + resetPitch(); + offset_height = 0; + } + public static void strafe(float speed) { if (!Settings.CAMERA_MOVABLE.get(Settings.currentProfile) || Settings.SPEEDRUNNER_MODE_ACTIVE.get(Settings.currentProfile)) { @@ -179,11 +192,32 @@ public static void move(float speed) { add_movement(xDiff * speed, yDiff * speed); } + // up or down + private static void elevate(float speed) { + if (!Settings.CAMERA_MOVABLE.get(Settings.currentProfile) + || Settings.SPEEDRUNNER_MODE_ACTIVE.get(Settings.currentProfile)) { + return; + } + offset_height -= speed; + } + + // pitch forward or back + public static void pitch(float speed) { + if (!Settings.CAMERA_MOVABLE.get(Settings.currentProfile) + || Settings.SPEEDRUNNER_MODE_ACTIVE.get(Settings.currentProfile)) { + return; + } + pitch_rsctimes -= speed; + if (pitch_rsctimes < 0) pitch_rsctimes = 1023; + if (pitch_rsctimes > 1023) pitch_rsctimes = 0; + } + public static void add_movement(float x, float y) { if (!Settings.CAMERA_MOVABLE_RELATIVE.get(Settings.currentProfile) && ((add_lookat_x == 0.0f && x != 0.0f) || (add_lookat_y == 0.0f && y != 0.0f))) { add_lookat_x = lookat_x; add_lookat_y = lookat_y; + if (isUsing3DMouseControls) offset_height = 200; Client.displayMessage("The camera is no longer following the player", Client.CHAT_NONE); } @@ -233,6 +267,10 @@ public static void addRotation(float amount) { public static void addZoom(float amount) { if (amount == 0 || !Settings.CAMERA_ZOOMABLE.get(Settings.currentProfile)) return; + if (isUsing3DMouseControls) { + leave3DMouseControls(); + } + delta_zoom += amount; if (delta_zoom > 1238.0f) delta_zoom = 1238.0f; else if (delta_zoom < 262.0f) delta_zoom = 262.0f; @@ -263,4 +301,59 @@ public static void resetRotation() { rotation = 126; delta_rotation = (float) rotation; } + + public static void postSetCamera() { + Camera.pitch_internal = pitch_rsctimes; + } + + public static void resetPitch() { + Camera.pitch_rsctimes = 112; + } + + public static void handleJoystick(String inputName) { + float amount = JoystickHandler.joystickInputReports.get(inputName) / 50; + zoom = 0; // this is so that Z Rotation isn't displaced from the camera + isUsing3DMouseControls = true; + switch (inputName) { + case "X Axis": + if (KeyboardHandler.keyShift) { + move(amount / 2); + } else { + move(amount / 5); + } + break; + case "Y Axis": + if (KeyboardHandler.keyShift) { + strafe(amount / 2); + } else { + strafe(amount / 5); + } + break; + case "Z Axis": + elevate(JoystickHandler.joystickInputReports.get(inputName) / 10); + break; + case "X Rotation": + pitch(JoystickHandler.joystickInputReports.get(inputName) / 30); + break; + case "Y Rotation": + break; + case "Z Rotation": + addRotation(amount); + break; + case "Button 0": + if (JoystickHandler.joystickInputReports.get(inputName) == 1) break; + case "Button 1": + if (JoystickHandler.joystickInputReports.get(inputName) == 1) { + leave3DMouseControls(); + } + break; + } + } + + private static void leave3DMouseControls() { + isUsing3DMouseControls = false; + init(); // resets zoom as well + resetCamera(); + Client.displayMessage("The camera is now following the player", Client.CHAT_NONE); + } } diff --git a/src/Game/Client.java b/src/Game/Client.java index 6f6db74..b0cc290 100644 --- a/src/Game/Client.java +++ b/src/Game/Client.java @@ -265,6 +265,10 @@ public static void update() { Camera.setLookatTile(getPlayerWaypointX(), getPlayerWaypointY()); Camera.update(delta_time); + if (Settings.JOYSTICK_ENABLED.get(Settings.currentProfile)) { + JoystickHandler.poll(); + } + // Replay.update(); /*if (Settings.RECORD_AUTOMATICALLY_FIRST_TIME.get(Settings.currentProfile) diff --git a/src/Game/Game.java b/src/Game/Game.java index 098f737..8e3a978 100644 --- a/src/Game/Game.java +++ b/src/Game/Game.java @@ -101,6 +101,7 @@ public void start() { Reflection.Load(); Renderer.init(); + JoystickHandler.init(); } public JConfig getJConfig() { diff --git a/src/Game/JoystickHandler.java b/src/Game/JoystickHandler.java new file mode 100644 index 0000000..e69670b --- /dev/null +++ b/src/Game/JoystickHandler.java @@ -0,0 +1,79 @@ +package Game; + +import Client.Launcher; +import Client.Settings; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import net.java.games.input.Component; +import net.java.games.input.Controller; +import net.java.games.input.ControllerEnvironment; +import net.java.games.input.Event; +import net.java.games.input.EventQueue; + +public class JoystickHandler { + public static List knownAxises = new ArrayList<>(); + public static HashMap joystickInputReports = new HashMap(); + public static HashMap joystickInputReportTimestamps = new HashMap(); + + public static void init() { + System.setProperty( + "net.java.games.input.librarypath", + new File(Settings.Dir.JAR + "/lib/jinput-natives").getAbsolutePath()); + } + + public static void poll() { + /* Get the available controllers */ + Controller[] controllers = ControllerEnvironment.getDefaultEnvironment().getControllers(); + if (controllers.length == 0) { + return; + } + + for (int i = 0; i < controllers.length; i++) { + if (!isSupported3DMouse(controllers[i].getName())) continue; + + // process the controller's eventqueue + controllers[i].poll(); + EventQueue queue = controllers[i].getEventQueue(); + Event event = new Event(); + while (queue.getNextEvent(event)) { + Component comp = event.getComponent(); + if (!knownAxises.contains(comp.getName())) { + knownAxises.add(comp.getName()); + } + + float value = event.getValue(); + joystickInputReports.put(comp.getName(), value); + joystickInputReportTimestamps.put(comp.getName(), System.currentTimeMillis()); + + doJoystickAction(comp.getName()); + } + + // Update inputs that are stale + // returning to Zero position does not consistently generate an event, so we do this. + joystickInputReportTimestamps.forEach( + (key, value) -> { + if (value != 0) { + if (System.currentTimeMillis() - value > 100) { + joystickInputReports.put(key, 0f); + doJoystickAction(key); + } + } + }); + } + } + + private static boolean isSupported3DMouse(String name) { + return name.contains("SpaceNavigator") || name.contains("SpaceMouse"); + } + + public static void doJoystickAction(String inputName) { + if (joystickInputReports.get(inputName) != 0) { + Camera.handleJoystick(inputName); + } + if (Launcher.getConfigWindow().isShown()) { + Launcher.getConfigWindow().updateJoystickInput(inputName); + } + } +} diff --git a/src/Game/Menu.java b/src/Game/Menu.java index dad33c1..3e4abe7 100644 --- a/src/Game/Menu.java +++ b/src/Game/Menu.java @@ -18,7 +18,6 @@ */ package Game; - /** Handles adjusting the position and behavior of the in-game menu */ public class Menu { diff --git a/src/Game/Renderer.java b/src/Game/Renderer.java index a20d04d..79535a5 100644 --- a/src/Game/Renderer.java +++ b/src/Game/Renderer.java @@ -1030,6 +1030,8 @@ public static void present(Image image) { y += 16; drawShadowText(g2, "Camera Auto Speed: " + Camera.auto_speed, x, y, color_text, false); y += 16; + drawShadowText(g2, "Camera Pitch: " + Camera.pitch_internal, x, y, color_text, false); + y += 16; drawShadowText(g2, "Camera Rotation Y: " + Camera.rotation_y, x, y, color_text, false); y += 16; drawShadowText(g2, "Camera Lookat X: " + Camera.lookat_x, x, y, color_text, false);