diff --git a/moonlight-common/src/com/limelight/nvstream/NvConnection.java b/moonlight-common/src/com/limelight/nvstream/NvConnection.java index 963e07b5..416f1f27 100644 --- a/moonlight-common/src/com/limelight/nvstream/NvConnection.java +++ b/moonlight-common/src/com/limelight/nvstream/NvConnection.java @@ -348,6 +348,20 @@ public class NvConnection { inputStream.sendMouseButtonUp(mouseButton); } + public void sendControllerInput(final short controllerNumber, + final short buttonFlags, + final byte leftTrigger, final byte rightTrigger, + final short leftStickX, final short leftStickY, + final short rightStickX, final short rightStickY) + { + if (inputStream == null) + return; + + inputStream.sendControllerInput(controllerNumber, buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY); + } + public void sendControllerInput(final short buttonFlags, final byte leftTrigger, final byte rightTrigger, final short leftStickX, final short leftStickY, diff --git a/moonlight-common/src/com/limelight/nvstream/input/ControllerBatchingBlock.java b/moonlight-common/src/com/limelight/nvstream/input/ControllerBatchingBlock.java index dab50ce1..25ecb985 100644 --- a/moonlight-common/src/com/limelight/nvstream/input/ControllerBatchingBlock.java +++ b/moonlight-common/src/com/limelight/nvstream/input/ControllerBatchingBlock.java @@ -11,8 +11,9 @@ public class ControllerBatchingBlock { private short leftStickY; private short rightStickX; private short rightStickY; + private short controllerNumber; - public ControllerBatchingBlock(ControllerPacket initialPacket) { + public ControllerBatchingBlock(MultiControllerPacket initialPacket) { this.buttonFlags = initialPacket.buttonFlags; this.leftTrigger = initialPacket.leftTrigger; this.rightTrigger = initialPacket.rightTrigger; @@ -54,8 +55,9 @@ public class ControllerBatchingBlock { // We have several restrictions that will cause batching to break up the controller packets. // 1) Button flags must be the same for all packets in the batch // 2) The movement direction of all axes must remain the same or be neutral - public boolean submitNewPacket(ControllerPacket packet) { + public boolean submitNewPacket(MultiControllerPacket packet) { if (buttonFlags != packet.buttonFlags || + controllerNumber != packet.controllerNumber || !checkDirs(leftTrigger, packet.leftTrigger, 0) || !checkDirs(rightTrigger, packet.rightTrigger, 1) || !checkDirs(leftStickX, packet.leftStickX, 2) || @@ -66,6 +68,7 @@ public class ControllerBatchingBlock { return false; } + this.controllerNumber = packet.controllerNumber; this.leftTrigger = packet.leftTrigger; this.rightTrigger = packet.rightTrigger; this.leftStickX = packet.leftStickX; @@ -75,7 +78,8 @@ public class ControllerBatchingBlock { return true; } - public void reinitializePacket(ControllerPacket packet) { + public void reinitializePacket(MultiControllerPacket packet) { + packet.controllerNumber = controllerNumber; packet.buttonFlags = buttonFlags; packet.leftTrigger = leftTrigger; packet.rightTrigger = rightTrigger; diff --git a/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java b/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java index 73e25ebb..315fdbf5 100644 --- a/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java +++ b/moonlight-common/src/com/limelight/nvstream/input/ControllerPacket.java @@ -3,7 +3,7 @@ package com.limelight.nvstream.input; import java.nio.ByteBuffer; import java.nio.ByteOrder; -public class ControllerPacket extends InputPacket { +public class ControllerPacket extends MultiControllerPacket { public static final byte[] HEADER = { 0x0A, @@ -46,26 +46,12 @@ public class ControllerPacket extends InputPacket { public static final short PACKET_LENGTH = PAYLOAD_LENGTH + InputPacket.HEADER_LENGTH; - // Set this flag if you want ControllerPacket to handle scaling for you - // You MUST properly handle deadzones to use this flag - // - // Note: This flag does nothing right now. It causes some controllers - // to behave badly. - public static boolean enableAxisScaling = false; - - short buttonFlags; - byte leftTrigger; - byte rightTrigger; - short leftStickX; - short leftStickY; - short rightStickX; - short rightStickY; - public ControllerPacket(short buttonFlags, byte leftTrigger, byte rightTrigger, short leftStickX, short leftStickY, short rightStickX, short rightStickY) { - super(PACKET_TYPE); + super((short) 0, buttonFlags, leftTrigger, rightTrigger, leftStickX, + leftStickY, rightStickX, rightStickY); this.buttonFlags = buttonFlags; this.leftTrigger = leftTrigger; diff --git a/moonlight-common/src/com/limelight/nvstream/input/ControllerStream.java b/moonlight-common/src/com/limelight/nvstream/input/ControllerStream.java index 1053a3cb..506a30bf 100644 --- a/moonlight-common/src/com/limelight/nvstream/input/ControllerStream.java +++ b/moonlight-common/src/com/limelight/nvstream/input/ControllerStream.java @@ -129,8 +129,8 @@ public class ControllerStream { } while (totalDeltaX != 0 && totalDeltaY != 0); } // Try to batch axis changes on controller packets too - else if (!inputQueue.isEmpty() && packet instanceof ControllerPacket) { - ControllerPacket initialControllerPacket = (ControllerPacket) packet; + else if (!inputQueue.isEmpty() && packet instanceof MultiControllerPacket) { + MultiControllerPacket initialControllerPacket = (MultiControllerPacket) packet; ControllerBatchingBlock batchingBlock = null; synchronized (inputQueue) { @@ -138,13 +138,13 @@ public class ControllerStream { while (i.hasNext()) { InputPacket queuedPacket = i.next(); - if (queuedPacket instanceof ControllerPacket) { + if (queuedPacket instanceof MultiControllerPacket) { // Only initialize the batching block if we got here if (batchingBlock == null) { batchingBlock = new ControllerBatchingBlock(initialControllerPacket); } - if (batchingBlock.submitNewPacket((ControllerPacket) queuedPacket)) + if (batchingBlock.submitNewPacket((MultiControllerPacket) queuedPacket)) { // Batching was successful, so remove this packet i.remove(); @@ -263,9 +263,35 @@ public class ControllerStream { public void sendControllerInput(short buttonFlags, byte leftTrigger, byte rightTrigger, short leftStickX, short leftStickY, short rightStickX, short rightStickY) { - queuePacket(new ControllerPacket(buttonFlags, leftTrigger, - rightTrigger, leftStickX, leftStickY, - rightStickX, rightStickY)); + if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) { + // Use legacy controller packets for generation 3 + queuePacket(new ControllerPacket(buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY)); + } + else { + // Use multi-controller packets for generation 4 and above + queuePacket(new MultiControllerPacket((short) 0, buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY)); + } + } + + public void sendControllerInput(short controllerNumber, short buttonFlags, byte leftTrigger, byte rightTrigger, + short leftStickX, short leftStickY, short rightStickX, short rightStickY) + { + if (context.serverGeneration == ConnectionContext.SERVER_GENERATION_3) { + // Use legacy controller packets for generation 3 + queuePacket(new ControllerPacket(buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY)); + } + else { + // Use multi-controller packets for generation 4 and above + queuePacket(new MultiControllerPacket(controllerNumber, buttonFlags, leftTrigger, + rightTrigger, leftStickX, leftStickY, + rightStickX, rightStickY)); + } } public void sendMouseButtonDown(byte mouseButton) diff --git a/moonlight-common/src/com/limelight/nvstream/input/MultiControllerPacket.java b/moonlight-common/src/com/limelight/nvstream/input/MultiControllerPacket.java new file mode 100644 index 00000000..5487598d --- /dev/null +++ b/moonlight-common/src/com/limelight/nvstream/input/MultiControllerPacket.java @@ -0,0 +1,73 @@ +package com.limelight.nvstream.input; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class MultiControllerPacket extends InputPacket { + public static final byte[] TAIL = + { + (byte)0x9C, + 0x00, + 0x00, + 0x00, + 0x55, + 0x00 + }; + + public static final int PACKET_TYPE = 0x1e; + + public static final short PAYLOAD_LENGTH = 30; + public static final short PACKET_LENGTH = PAYLOAD_LENGTH + + InputPacket.HEADER_LENGTH; + + short controllerNumber; + short buttonFlags; + byte leftTrigger; + byte rightTrigger; + short leftStickX; + short leftStickY; + short rightStickX; + short rightStickY; + + public MultiControllerPacket(short controllerNumber, short buttonFlags, byte leftTrigger, byte rightTrigger, + short leftStickX, short leftStickY, + short rightStickX, short rightStickY) + { + super(PACKET_TYPE); + + this.controllerNumber = controllerNumber; + + this.buttonFlags = buttonFlags; + this.leftTrigger = leftTrigger; + this.rightTrigger = rightTrigger; + + this.leftStickX = leftStickX; + this.leftStickY = leftStickY; + + this.rightStickX = rightStickX; + this.rightStickY = rightStickY; + } + + @Override + public void toWirePayload(ByteBuffer bb) { + bb.order(ByteOrder.LITTLE_ENDIAN); + bb.putInt(0xd); + bb.putShort((short) 0x1a); + bb.putShort(controllerNumber); + bb.putShort((short) 0x07); + bb.putShort((short) 0x14); + bb.putShort(buttonFlags); + bb.put(leftTrigger); + bb.put(rightTrigger); + bb.putShort(leftStickX); + bb.putShort(leftStickY); + bb.putShort(rightStickX); + bb.putShort(rightStickY); + bb.put(TAIL); + } + + @Override + public int getPacketLength() { + return PACKET_LENGTH; + } + } \ No newline at end of file