// This routine with be run synchronously with UDPWii.Server public void SendDataPacket(UDPWii.DataDSU packet) { // If the server is not running, just drop the packet if (tokenSource == null) { return; } lock (clients) { // If there's no client, skip the packet processing step and drop the packet if (clients.Count == 0) { return; } List <IPEndPoint> toRemove = new List <IPEndPoint>(); DSU.DataRsp dsuPacket = packet.ToDSU(id); byte[] dsuPacketData = dsuPacket.Finish(); //Console.WriteLine("[DSU.Sender] ({0}, {1})", dsuPacket.touch1.x, dsuPacket.touch1.y); foreach (var keyValuePair in clients) { Client client = keyValuePair.Value; if (!Util.IsTimeout(client.lastSeenAll) || !Util.IsTimeout(client.lastSeenSlot[packet.client.slot])) { udpSend.Send(dsuPacketData, dsuPacketData.Length, keyValuePair.Key); } else { // Is this client completely dead? If so, mark it for removal // Reference: https://github.com/Davidobot/BetterJoy/blob/master/BetterJoyForCemu/UpdServer.cs // We're here so lastSeenAll must have timed out if (client.lastSeenSlot.All(Util.IsTimeout)) { toRemove.Add(keyValuePair.Key); } } //Console.WriteLine("[DSU.Sender] Packet sent to " + keyValuePair.Key.ToString()); } // Clear all dead clients now to prevent concurrent modification foreach (IPEndPoint key in toRemove) { Console.WriteLine("[DSU.Server] Removing dead client {0}", key.ToString()); clients.Remove(key); } } }
public DSU.DataRsp ToDSU(UInt32 id) { DSU.DataRsp packet = new DSU.DataRsp(id); // Buttons (& Analog) // XXX: Packet w/o button info == No button pressed if ((flags & Flags.Buttons) != 0) { /* * if ((buttons & Buttons.B1) != 0) packet.buttons |= DSU.DataRsp.Buttons.X; * if ((buttons & Buttons.B2) != 0) packet.buttons |= DSU.DataRsp.Buttons.Y; * if ((buttons & Buttons.BA) != 0) packet.buttons |= DSU.DataRsp.Buttons.A; * if ((buttons & Buttons.BB) != 0) packet.buttons |= DSU.DataRsp.Buttons.B; * if ((buttons & Buttons.BP) != 0) packet.buttons |= DSU.DataRsp.Buttons.L1; * if ((buttons & Buttons.BM) != 0) packet.buttons |= DSU.DataRsp.Buttons.R1; * if ((buttons & Buttons.BH) != 0) packet.buttons |= DSU.DataRsp.Buttons.Options; * if ((buttons & Buttons.BU) != 0) packet.buttons |= DSU.DataRsp.Buttons.DPadUp; * if ((buttons & Buttons.BD) != 0) packet.buttons |= DSU.DataRsp.Buttons.DPadDown; * if ((buttons & Buttons.BL) != 0) packet.buttons |= DSU.DataRsp.Buttons.DPadLeft; * if ((buttons & Buttons.BR) != 0) packet.buttons |= DSU.DataRsp.Buttons.DPadRight; * if ((buttons & Buttons.SK) != 0) throw new NotImplementedException("UDPWii.DataDSU.ToDSU(): WTH is button SK?"); */ /* * New button mappings: * L, D, R, U => Analog.{L, D, R, U} * A, B, 1, 2 => Analog.{A, B, X, Y} * P, M, H => Buttons.{R3, L3, Options} * SK => Buttons.Share */ if ((buttons & Buttons.BL) != 0) { packet.analog.DPadLeft = 0xff; } if ((buttons & Buttons.BD) != 0) { packet.analog.DPadDown = 0xff; } if ((buttons & Buttons.BR) != 0) { packet.analog.DPadRight = 0xff; } if ((buttons & Buttons.BU) != 0) { packet.analog.DPadUp = 0xff; } if ((buttons & Buttons.BA) != 0) { packet.analog.A = 0xff; } if ((buttons & Buttons.BB) != 0) { packet.analog.B = 0xff; } if ((buttons & Buttons.B1) != 0) { packet.analog.X = 0xff; } if ((buttons & Buttons.B2) != 0) { packet.analog.Y = 0xff; } if ((buttons & Buttons.BP) != 0) { packet.buttons |= DSU.DataRsp.Buttons.R3; } if ((buttons & Buttons.BM) != 0) { packet.buttons |= DSU.DataRsp.Buttons.L3; } if ((buttons & Buttons.BH) != 0) { packet.buttons |= DSU.DataRsp.Buttons.Options; } if ((buttons & Buttons.SK) != 0) { packet.buttons |= DSU.DataRsp.Buttons.Share; } } // Stick (ignored) // Set sticks in the middle packet.stick.leftX = packet.stick.leftY = 0x80; packet.stick.rightX = packet.stick.rightY = 0x80; // Touch1 (using IR data) if ((flags & Flags.IR) != 0) { client.touching = true; packet.touch1.active = 1; packet.touch1.id = client.touchId; // UDPWii: x, y in [0, 1], (0, 0) at bottom left, absloute // DSU: x, y in [-1000, 1000]*[-500, 500] (TOUCH_(X|Y)_AXIS_MAX), (0, 0) at center, absloute packet.touch1.x = (Int16)(((float)ir.x / 1024 / 1024 - 0.5f) * 1000 * 2); packet.touch1.y = (Int16)(((float)ir.y / 1024 / 1024 - 0.5f) * 500 * 2); } else { // Increment touch ID on falling edge if (client.touching) { client.touching = false; client.touchId++; } } // Touch2 (ignored) // Accel if ((flags & Flags.Accel) != 0) { // UDPWii: In Gs // DSU: In Gs /* * // Don't know why but let emulation makes sense * packet.accel.x = (float)accel.x / 1024 / 1024; * packet.accel.y = (float)accel.y / 1024 / 1024; * packet.accel.z = -(float)accel.z / 1024 / 1024; */ // STILL don't know why but let Mario Kart Wii & Wii Sports Resort playable // XXX: Why the axises work in this way & how it reacts with JDFix tweak? packet.accel.x = (float)accel.x / 1024 / 1024; packet.accel.y = -(float)accel.z / 1024 / 1024; packet.accel.z = -(float)accel.y / 1024 / 1024; } // Accel timestamp if ((flags & Flags.AccelTS) != 0) { // UDPWii: In nanoseconds // DSU: In nanoseconds packet.accel.timestamp = accelTimestamp; } // Gyro if ((flags & Flags.Gyro) != 0) { client.gyroSeen = true; // UDPWii: In rad/s // DSU: In deg/s // Set after 3 hrs of desperate trial-and-error, tested with Wii Play: Motion & Wii Sports Resort // NOTE: Use a phone with hardware gyroscope! Pseudo (virtual) ones are SHITTY and renders emulation unusable // XXX: Why the axises work in this way & how it should react with JDFix tweak? packet.gyro.pitch = (float)gyro.pitch / 1024 / 1024 * (float)(180 / Math.PI); packet.gyro.yaw = -(float)gyro.roll / 1024 / 1024 * (float)(180 / Math.PI); packet.gyro.roll = (float)gyro.yaw / 1024 / 1024 * (float)(180 / Math.PI); } // Slot packet.slot = SlotDSU(client); // Misc things //packet.connected = (byte)((packet.slot.state == DSU.Slot.State.Connected) ? 1 : 0); packet.connected = 1; // Always connected (since we're processing a fresh packet) packet.packetNumber = client.packetNumber++; return(packet); }