From 18f0a9b46b2c67f2684a94ebd62e3fd37d7f09e7 Mon Sep 17 00:00:00 2001 From: Trong Date: Sun, 16 Jun 2024 11:17:54 -0700 Subject: [PATCH] clean README --- README.md | 287 +----------------------------------------------------- 1 file changed, 2 insertions(+), 285 deletions(-) diff --git a/README.md b/README.md index b16a21fc..9a2f7cab 100644 --- a/README.md +++ b/README.md @@ -104,20 +104,6 @@ After that: - **For Pico-based controllers**: hold the bootsel button while plugging it in (or your Start button if you already have HayBox installed) and then drag and drop the file `HayBox/.pio/build//firmware.uf2` onto the RPI-RP2 drive that comes up. - **For Arduino-based controllers**: Plug in your controller via USB and click **Upload** (next to the Build button) -#### Building using GitHub Codespaces - -This is probably the most convenient way to modify and rebuild HayBox, but bear in mind that GitHub's free tier places [some limitations](https://docs.github.com/en/billing/managing-billing-for-github-codespaces/about-billing-for-github-codespaces#monthly-included-storage-and-core-hours-for-personal-accounts) on how much you can use Codespaces each month. Because of this, you will want to make sure you shut down your Codespaces when you aren't using them, in order to maximise what you can get from your quota. - -First, create a GitHub account or just log in if you already have one, then [fork this repository](https://github.com/JonnyHaystack/HayBox/fork) and open your fork in a new Codespace by clicking the green Code button -> Codespaces -> Create codespace on master. This should open VS Code in your browser with all of the necessary extensions and dependencies pre-installed. From here, the process is much the same as [building locally](#building-locally), except you can't use the Upload button to flash the firmware. You will instead have to download the compiled binary from `HayBox/.pio/build//` and flash it manually (see [here](#pre-built-binaries) for more on that). - -#### Building using GitHub Actions - -This repository contains a GitHub Actions workflow definition that builds each PlatformIO environment [specified in the matrix](https://github.com/JonnyHaystack/HayBox/blob/master/.github/workflows/build.yml#L13) on every push, and uploads firmware binaries as artifacts which you can download by clicking a specific workflow run from the [history](https://github.com/JonnyHaystack/HayBox/actions). You can create a fork of this repository and enable Actions by clicking Settings -> Actions -> General -> Select "Allow all actions and reusable workflows" -> Save. - -The fastest way to make changes if you only want to build via GitHub Actions is to use [github.dev](https://github.dev). You can do so by simply pressing `.` on your keyboard while you have your fork of this repository open, and it will open a VS Code editor in your browser. This does not give you the same development capabilities that you'd get in a Codespace, but it does allow you to make changes and commit them directly from your browser. Change whatever you'd like, then use the Source Control tab on the left to add, commit, and push your changes. Finally, go back to the repository and click on the Actions tab, click on your workflow run, and wait for it to build the artifact. - -If you are adding a new device config/PlatformIO environment, you will have to add the environment to the [matrix](https://github.com/JonnyHaystack/HayBox/blob/master/.github/workflows/build.yml#L13) in order for it to be built by the GitHub Actions workflow. You can also remove any environments from the matrix that you don't care about in order to reduce resource usage and potentially speed up your builds. - ## Usage ### Default button holds @@ -160,9 +146,10 @@ you have to hold with one hand while plugging in. The default controller mode button combinations are: - Mod X + Start + L - Melee mode (default) - Mod X + Start + Left - Project M/Project+ mode -- Mod X + Start + Down - Ultimate mode +- Mod X + Start + Down - Ultimate mode (default on Nintendo Switch) - Mod X + Start + Right - FGC mode (Hitbox style fighting game layout) - Mod X + Start + B - Rivals of Aether mode +- Mod Y + Start + Down - Mario Kart 8 Deluxe mode Default keyboard mode button combinations (only available when using DInput backend, **not** with XInput): - Mod Y + Start + L - Default keyboard mode @@ -199,276 +186,6 @@ To install the profile: \* macOS only supports DInput (and not very well), so if using a Pico/RP2040-based controller you will have to force DInput mode by holding Z on plugin, and even then it may not work. I can't really do anything about Apple's poor controller support (which they seem to break with every other update) and I don't own any Apple devices, so this will also be considered unsupported usage of HayBox. -## Customisation - -### Console/gamemode selection bindings - -#### Communication backends (console selection) - -The communication backend (e.g. DInput, GameCube, or N64) is selected partly -through auto detection and partly based on the buttons held on plugin. This is -handled in `config//config.cpp`, in the `setup()` function. -The logic is fairly simple, and even if you don't have programming experience it -shouldn't be too hard to see what's going on and change things if you wish. - -The config folders corresponding to the Arduino environments are: -- `config/arduino_nativeusb/` for Arduino with native USB support (e.g. Leonardo, Micro) -- `config/arduino/` for Arduino without native USB support (e.g. Uno, Nano, Mega 2560) - -For Arduino device configs you may notice that the number 125 is passed into -`GamecubeBackend()`. This lets you change the polling rate e.g. if your DIY -doesn't support native USB and you want to use it with an overclocked GameCube -controller adapter. In that example, you could pass in 1000 to sync up to the -1000Hz polling rate, or 0 to disable this lag fix completely. -Polling rate can be passed into the N64Backend constructor in the same way as this. - -You may notice that 1000Hz polling rate works on console as well. Be aware -that while this works, it will result in more input lag. The point of setting -the polling rate here is so that the GameCube backend can delay until right -before the next poll before reading the inputs, so that the inputs are fresh and -not outdated. - -For Pico/RP2040, it is not necessary to pass in a console polling rate, because -the Pico has enough processing power to read/process inputs after receiving the -console poll, so there is no need to predict when the poll will arrive and -prepare things in advance. - -#### Input modes - -To configure the button holds for input modes (controller/keyboard modes), edit -the `select_mode()` function in `config/mode_selection.hpp`. Each `if` -statement is a button combination to select an input mode. - -Most input modes support passing in an SOCD cleaning mode, e.g. -`socd::2IP_NO_REAC`. See [here](#socd) for the other available modes. - -### Creating custom input modes - -For creating new input modes, it helps if you know some C++, or at least have -some programming experience. That said, you should be able to get by even -without prior experience if you just base your new mode off the existing ones -and refer to them as examples. - -There are two types of input modes: ControllerMode and KeyboardMode - -#### Keyboard modes - -Keyboard modes are a little bit simpler so let's start there. - -A KeyboardMode behaves as a standard keyboard and should work with any device -that supports keyboards. - -You are free to use whatever logic and programming tricks you like in the -`UpdateKeys()` function to decide the outputs based on the input state. You could -create input layers (like the D-Pad layer in Melee mode that is activated when -holding Mod X and Mod Y), or other types of conditional inputs. - -The list of available keycodes can be found [here](https://github.com/JonnyHaystack/ArduinoKeyboard/blob/master/include/keycodes.h). - -Remember that keyboard modes can only be activated when using the **DInput** communication backend (**not** XInput). - -#### Controller modes - -A ControllerMode takes a digital button input state and transforms it into an -output state corresponding to a standard gamepad. Any ControllerMode will work -with any CommunicationBackend. A CommunicationBackend simply reads inputs from one or more input sources, uses the current ControllerMode to update the outputs based on those inputs, and handles the sending of the outputs to the console or PC. - -To create a ControllerMode, you just need to implement the functions -`UpdateDigitalOutputs()` and `UpdateAnalogOutputs()`. - -`UpdateDigitalOutputs()` is very similar to the `UpdateKeys()` function in keyboard -modes, with the difference that rather than calling a `Press()` function to -immediately send inputs, we are simply setting the output state for this -iteration. As the name indicates, we will only deal with the digital outputs in -this function. - -`UpdateAnalogOutputs()` is a bit more complicated. Firstly, it has to call -`UpdateDirections()` before doing anything else. This function takes in values -indicating whether your left and right sticks are pointing left/right/up/down. -You also pass in the minimum, neutral (centre), and maximum stick analog values, -so you can configure these on a per-mode basis. All this information is used to -automatically set the stick analog values based on the inputs you passed in. This -is all you need to do unless you want to implement modifiers. - -`UpdateDirections()` also populates the variable `directions` with values -indicating current stick direction, which can be 1, 0, or -1 for the X and Y -axes for both sticks. These values make it much easier to write modifier logic. - -After calling `UpdateDirections()`, add any modifier handling logic that you want. -Remember that `UpdateDirections()` already set the default analog stick values, -so when handling modifiers you only need to manually set the values for the axes -that are actually being modified. Other than this, I can't teach how to write -your modifier logic, so just look at the examples and play around. - -Finally, set any analog trigger values that you need. - -Note: Analog trigger outputs could just as well be handled in -`UpdateDigitalOutputs()`, but I think it usually looks cleaner to keep them -along with the other analog outputs. - -Also note: You don't need to worry about resetting the output state or clearing -anything from it. This is done automatically at the start of each iteration. - -### SOCD - -In the constructor of each mode (for controller modes *and* keyboard modes), you -can configure pairs of opposing direction inputs to apply SOCD cleaning to. - -For example, in `src/modes/Melee20Button.cpp`: -``` -_socd_pair_count = 4; -_socd_pairs = new socd::SocdPair[_socd_pair_count]{ - socd::SocdPair{&InputState::left, &InputState::right, socd_type}, - socd::SocdPair{ &InputState::down, &InputState::up, socd_type}, - socd::SocdPair{ &InputState::c_left, &InputState::c_right, socd_type}, - socd::SocdPair{ &InputState::c_down, &InputState::c_up, socd_type}, -}; -``` - -This sets up left/right, down/up, C-Left/C-Right, and C-Down/C-Up as pairs of -opposing cardinal directions for which SOCD cleaning will be applied. The SOCD -cleaning is automatically done before `UpdateDigitalOutputs()` and -`UpdateAnalogOutputs()`, and you do not need to worry about it any further than -that. - -For each `SocdPair` you can pass in an `SocdType` of your choosing. By default -for most modes this is passed in as a single constructor parameter, but it is -possible to pass in multiple parameters, or simply use a hardcoded value. Both -of these approaches are exemplified in `src/modes/FgcMode.cpp`. - -| `SocdType` | Description | -| ---------- | ----------- | -| `SOCD_NEUTRAL` | Left + right = neutral - the default if no `SocdType` specified in the `SocdPair` | -| `SOCD_2IP` | Second input priority - left -> left + right = right, and vice versa. Releasing the second direction gives the original direction | -| `SOCD_2IP_NO_REAC` | Second input priority without reactivation - same as above, except releasing the second direction results in neutral. The original direction must be physically reactuated. | -| `SOCD_DIR1_PRIORITY` | The first button in the `SocdPair` always takes priority over the second | -| `SOCD_DIR2_PRIORITY` | The second button in the `SocdPair` always takes priority over the first | -| `SOCD_NONE` | No SOCD resolution - the game decides | - -Note that you do not have to implement a `HandleSocd()` function like in the -Melee20Button and Melee18Button modes. It is only overridden in these modes -so that we can check if left and right are both held *before* SOCD cleaning, -because when they are both held (without a vertical direction being held) we -need to override all modifiers. - -### Mod X lightshield and R shield tilt - -If your controller has no lightshield buttons, you may want to use Mod X for -lightshield and put shield tilt on R instead. You can do this by using the -Melee18Button mode instead of Melee20Button. - -### Mode-specific optional features - -#### Melee modes - -The Melee20Button and Melee18Button modes provide a choice of which coordinates to use -when pressing down + right. By default, holding down + back will allow you to do automatic -jab-cancels, which is a useful technique for some characters. - -Another popular technique that uses the down + right diagonal is the so-called crouch/walk -option-select. This technique involves holding down + forward at a certain angle while -crouching, such that after crouch-cancelling an attack you will automatically start -walking towards your opponent instead of going back into crouch. This can be very useful -for tech-chasing, but the coordinates used for this technique do not allow you to auto -jab-cancel. - -This can be configured as seen in `config/mode_selection.hpp` by setting the `crouch_walk_os` option to true: - -``` -new Melee20Button(socd::SOCD_2IP_NO_REAC, { .crouch_walk_os = false }) -``` - -You will also have to change this in your `config//config.cpp` in order for it to be applied on plugin, as `mode_selection.hpp` only controls what happens when you *switch* mode. - -#### Project M/Project+ mode - -The ProjectM mode has some extra options to configure certain behaviours. As seen -in `config/mode_selection.hpp`: - -``` -new ProjectM( - socd::SOCD_2IP_NO_REAC, - { .true_z_press = false, .ledgedash_max_jump_traj = true } -) -``` - -Firstly, the `ledgedash_max_jump_traj` option allows you to enable or disable the behaviour -borrowed from Melee mode where holding left and right (and no vertical directions) -will give a 1.0 cardinal regardless of modifiers being held. - -If you change the SOCD mode to 2IP (with reactivation), you should also change -this option to false if you want a smooth gameplay experience. - -Secondly, the `true_z_press` option exists because Project M/Project+ do not handle -Z presses the same way Melee does. -Melee interprets a Z press as lightshield + A, and thus it can be used for L -cancelling without locking you out of techs. In PM/P+, a Z press will trigger a -tech and thus cause unwanted tech lockouts if used to L cancel. -By default in HayBox, the ProjectM mode is set to use a macro of lightshield + A -in order to preserve expected behaviour from Melee. However, this macro does not -enable you to use tether/grapple attacks or grab items. To workaround this, you -can press Mod X + Z to send a true Z input. - -If this bothers you, and you just want to send a true Z input by default when -pressing Z, you can set the `true_z_press` option to true. - -### Input sources - -HayBox supports several input sources that can be read from to update the input -state: -- `GpioButtonInput` - The most commonly used, for reading switches/buttons connected directly to GPIO pins. The input mappings are defined by an array of `GpioButtonMapping` as can be seen in almost all existing configs. -- `SwitchMatrixInput` - Similar to the above, but scans a keyboard style switch matrix instead of individual switches. A config for Crane's Model C<=53 is included at `config/c53/config.cpp` which serves as an example of how to define and use a switch matrix input source. -- `NunchukInput` - Reads inputs from a Wii Nunchuk using i2c. This can be used for mixed input controllers (e.g. left hand uses a Nunchuk for movement, and right hand uses buttons for other controls) -- `GamecubeControllerInput` - Similar to the above, but reads from a GameCube controller. Can be instantiated similarly to GamecubeBackend. Currently only implemented for Pico, and you must either run it on a different pio instance (pio0 or pio1) than any instances of GamecubeBackend, or make sure that both use the same PIO instruction memory offset. - -Each input source has a "scan speed" value which indicates roughly how long it takes for it to read inputs. Fast input sources are always read at the last possible moment (at least on Pico), resulting in very low latency. Conversely, slow input sources are typically read quite long before they are needed, as they are too slow to be read in response to poll. Because of this, it is more ideal to be constantly reading those inputs on a separate core. This is not possible on AVR MCUs as they are all single core, but it is possible (and easy) on the Pico/RP2040. The bottom of the default Pico config `config/pico/config.cpp` illustrates this by using core1 to read Nunchuk inputs while core0 handles everything else. See [the next section](#using-the-picos-second-core) for more information about using core1. - - -In each config's `setup()` function, we build up an array of input sources, and then pass it into a communication backend. The communication backend decides when to read which input sources, because inputs need to be read at different points in time for different backends. We also build an array of communication backends, allowing more than one backend to be used at once. For example, in most configs, the B0XX input viewer backend is used as a secondary backend whenever the DInput backend is used. In each iteration, the main loop tells each of the backends to send their respective reports. In future, there could be more backends for things like writing information to an OLED display. - -### Using the Pico's second core - -In each config, there are the functions `setup()` and `loop()`, where `setup()` runs first, and then `loop()` runs repeatedly until the device is powered off. - -On Pico/RP2040, the `setup()` and `loop()` functions execute on core0, and you can add the functions `setup1()` and `loop1()` in order to run tasks on core1. - -For example, to read GameCube controller inputs on core1: -``` -GamecubeControllerInput *gcc = nullptr; - -void setup1() { - while (backends == nullptr) { - tight_loop_contents(); - } - - gcc = new GamecubeControllerInput(gcc_pin, 2500, pio1); -} - -void loop1() { - if (backends != nullptr) { - gcc->UpdateInputs(backends[0]->GetInputs()); - } -} -``` - -The `while` loop makes sure we wait until `setup()` on core0 has finished setting up the communication backends. We then create a GameCube controller input source with a polling rate of 2500Hz. We also run it on `pio1` as an easy way to avoid interfering with any GameCube/N64 backends, which use `pio0` unless otherwise specified. In `loop1()` we make the assumption that the primary backend is the first element of the `backends` array (which is configured in the same file anyway, so we aren't truly assuming anything we don't know) and directly scan the GameCube controller inputs into the backend's input state. - -As a slightly crazier hypothetical example, one could even power all the controls for a two person arcade cabinet using a single Pico by creating two switch matrix input sources using say 10 pins each, and two GameCube backends, both on separate cores. The possibilities are endless. - -## Troubleshooting - -### Controller not working with console or GameCube adapter - -If you are using an official adapter with an Arduino-based controller you will likely have to hold A on plugin which disables the polling latency optimisation by passing in a polling rate of 0 to the GamecubeBackend constructor. - -If you are using an Arduino-based controller without a boost circuit, you will need 5V power so for Mayflash adapter you need both USB cables plugged in, and on console the rumble line needs to be intact. Pico works natively with 3.3V power so this isn't an issue. - -## Contributing - -I welcome contributions and if you make an input mode that you want to share, -feel free to make a pull request. Please install the clang-format plugin for -VS Code and use it to format any code you want added. - ### Versioning We use [SemVer](http://semver.org/) for versioning. For the versions available,