Example #1
0
        // 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);
                }
            }
        }
Example #2
0
        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);
        }