Merge branch 'master' into refactor-metadata-lookup-sources
@ -3,28 +3,28 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"jetbrains.resharper.globaltools": {
|
"jetbrains.resharper.globaltools": {
|
||||||
"version": "2022.2.3",
|
"version": "2023.3.3",
|
||||||
"commands": [
|
"commands": [
|
||||||
"jb"
|
"jb"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"nvika": {
|
"nvika": {
|
||||||
"version": "2.2.0",
|
"version": "3.0.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"nvika"
|
"nvika"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"codefilesanity": {
|
"codefilesanity": {
|
||||||
"version": "0.0.36",
|
"version": "0.0.37",
|
||||||
"commands": [
|
"commands": [
|
||||||
"CodeFileSanity"
|
"CodeFileSanity"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ppy.localisationanalyser.tools": {
|
"ppy.localisationanalyser.tools": {
|
||||||
"version": "2022.809.0",
|
"version": "2023.1117.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"localisation"
|
"localisation"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,9 @@ indent_style = space
|
|||||||
indent_size = 2
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[g_*.cs]
|
||||||
|
generated_code = true
|
||||||
|
|
||||||
[*.cs]
|
[*.cs]
|
||||||
end_of_line = crlf
|
end_of_line = crlf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
@ -6,3 +6,5 @@
|
|||||||
212d78865a6b5f091173a347bad5686834d1d5fe
|
212d78865a6b5f091173a347bad5686834d1d5fe
|
||||||
# Add partial specs in mobile projects too
|
# Add partial specs in mobile projects too
|
||||||
00c11b2b4e389e48f3995d63484a6bc66a7afbdb
|
00c11b2b4e389e48f3995d63484a6bc66a7afbdb
|
||||||
|
# Mass NRT enabling
|
||||||
|
0ab0c52ad577b3e7b406d09fa6056a56ff997c3e
|
||||||
|
26
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@ -11,6 +11,10 @@ body:
|
|||||||
- Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0).
|
- Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0).
|
||||||
- And most importantly, search for your issue both in the [issue listing](https://github.com/ppy/osu/issues) and the [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a). If you find that it already exists, respond with a reaction or add any further information that may be helpful.
|
- And most importantly, search for your issue both in the [issue listing](https://github.com/ppy/osu/issues) and the [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a). If you find that it already exists, respond with a reaction or add any further information that may be helpful.
|
||||||
|
|
||||||
|
# ATTENTION LINUX USERS
|
||||||
|
|
||||||
|
If you are having an issue and it is hardware related, **please open a [q&a discussion](https://github.com/ppy/osu/discussions/categories/q-a)** instead of an issue. There's a high chance your issue is due to your system configuration, and not our software.
|
||||||
|
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: Type
|
label: Type
|
||||||
@ -38,7 +42,7 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: The version you encountered this bug on. This is shown at the bottom of the main menu and also at the end of the settings screen.
|
description: The version you encountered this bug on. This is shown at the end of the settings overlay.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: markdown
|
- type: markdown
|
||||||
@ -46,22 +50,16 @@ body:
|
|||||||
value: |
|
value: |
|
||||||
## Logs
|
## Logs
|
||||||
|
|
||||||
Attaching log files is required for every reported bug. See instructions below on how to find them.
|
Attaching log files is required for **every** issue, regardless of whether you deem them required or not. See instructions below on how to find them.
|
||||||
|
|
||||||
**Logs are reset when you reopen the game.** If the game crashed or has been closed since you found the bug, retrieve the logs using the file explorer instead.
|
|
||||||
|
|
||||||
### Desktop platforms
|
### Desktop platforms
|
||||||
|
|
||||||
If the game has not yet been closed since you found the bug:
|
If the game has not yet been closed since you found the bug:
|
||||||
1. Head on to game settings and click on "Open osu! folder"
|
1. Head on to game settings and click on "Export logs"
|
||||||
2. Then open the `logs` folder located there
|
2. Click the notification to locate the file
|
||||||
|
3. Drag the generated `.zip` files into the github issue window
|
||||||
|
|
||||||
The default places to find the logs on desktop platforms are as follows:
|
![export logs button](https://github.com/ppy/osu/assets/191335/cbfa5550-b7ed-4c5c-8dd0-8b87cc90ad9b)
|
||||||
- `%AppData%/osu/logs` *on Windows*
|
|
||||||
- `~/.local/share/osu/logs` *on Linux*
|
|
||||||
- `~/Library/Application Support/osu/logs` *on macOS*
|
|
||||||
|
|
||||||
If you have selected a custom location for the game files, you can find the `logs` folder there.
|
|
||||||
|
|
||||||
### Mobile platforms
|
### Mobile platforms
|
||||||
|
|
||||||
@ -69,10 +67,6 @@ body:
|
|||||||
- *On Android*, navigate to `Android/data/sh.ppy.osulazer/files/logs` using a file browser app.
|
- *On Android*, navigate to `Android/data/sh.ppy.osulazer/files/logs` using a file browser app.
|
||||||
- *On iOS*, connect your device to a PC and copy the `logs` directory from the app's document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer)
|
- *On iOS*, connect your device to a PC and copy the `logs` directory from the app's document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer)
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
After locating the `logs` folder, select all log files inside and drag them into the "Logs" box below.
|
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Logs
|
label: Logs
|
||||||
|
46
.github/workflows/ci.yml
vendored
@ -15,17 +15,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
|
- name: Install .NET 8.0.x
|
||||||
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
|
|
||||||
- name: Install .NET 3.1.x LTS
|
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "3.1.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: "6.0.x"
|
|
||||||
|
|
||||||
- name: Restore Tools
|
- name: Restore Tools
|
||||||
run: dotnet tool restore
|
run: dotnet tool restore
|
||||||
@ -79,10 +72,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
|
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
|
||||||
@ -108,10 +101,16 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Setup JDK 11
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: microsoft
|
||||||
|
java-version: 11
|
||||||
|
|
||||||
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET workloads
|
- name: Install .NET workloads
|
||||||
run: dotnet workload install maui-android
|
run: dotnet workload install maui-android
|
||||||
@ -121,31 +120,24 @@ jobs:
|
|||||||
|
|
||||||
build-only-ios:
|
build-only-ios:
|
||||||
name: Build only (iOS)
|
name: Build only (iOS)
|
||||||
# `macos-13` is required, because Xcode 14.3 is required (see below).
|
# `macos-13` is required, because the newest Microsoft.iOS.Sdk versions require Xcode 14.3.
|
||||||
# TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta)
|
# TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta: https://github.com/actions/runner-images/tree/main#available-images)
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# newest Microsoft.iOS.Sdk versions require Xcode 14.3.
|
- name: Install .NET 8.0.x
|
||||||
# 14.3 is currently not the default Xcode version (https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode),
|
|
||||||
# so set it manually.
|
|
||||||
# TODO: remove when 14.3 becomes the default Xcode version.
|
|
||||||
- name: Set Xcode version
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
sudo xcode-select -s "/Applications/Xcode_14.3.app"
|
|
||||||
echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.3.app" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET Workloads
|
- name: Install .NET Workloads
|
||||||
run: dotnet workload install maui-ios
|
run: dotnet workload install maui-ios
|
||||||
|
|
||||||
|
- name: Select Xcode 15.2
|
||||||
|
run: sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: dotnet build -c Debug osu.iOS
|
run: dotnet build -c Debug osu.iOS
|
||||||
|
530
.github/workflows/diffcalc.yml
vendored
@ -1,206 +1,382 @@
|
|||||||
# Listens for new PR comments containing !pp check [id], and runs a diffcalc comparison against master.
|
# ## Description
|
||||||
# Usage:
|
|
||||||
# !pp check 0 | Runs only the osu! ruleset.
|
|
||||||
# !pp check 0 2 | Runs only the osu! and catch rulesets.
|
|
||||||
#
|
#
|
||||||
|
# Uses [diffcalc-sheet-generator](https://github.com/smoogipoo/diffcalc-sheet-generator) to run two builds of osu and generate an SR/PP/Score comparison spreadsheet.
|
||||||
|
#
|
||||||
|
# ## Requirements
|
||||||
|
#
|
||||||
|
# Self-hosted runner with installed:
|
||||||
|
# - `docker >= 20.10.16`
|
||||||
|
# - `docker-compose >= 2.5.1`
|
||||||
|
# - `lbzip2`
|
||||||
|
# - `jq`
|
||||||
|
#
|
||||||
|
# ## Usage
|
||||||
|
#
|
||||||
|
# The workflow can be run in two ways:
|
||||||
|
# 1. Via workflow dispatch.
|
||||||
|
# 2. By an owner of the repository posting a pull request or issue comment containing `!diffcalc`.
|
||||||
|
# For pull requests, the workflow will assume the pull request as the target to compare against (i.e. the `OSU_B` variable).
|
||||||
|
# Any lines in the comment of the form `KEY=VALUE` are treated as variables for the generator.
|
||||||
|
#
|
||||||
|
# ## Google Service Account
|
||||||
|
#
|
||||||
|
# Spreadsheets are uploaded to a Google Service Account, and exposed with read-only permissions to the wider audience.
|
||||||
|
#
|
||||||
|
# 1. Create a project at https://console.cloud.google.com
|
||||||
|
# 2. Enable the `Google Sheets` and `Google Drive` APIs.
|
||||||
|
# 3. Create a Service Account
|
||||||
|
# 4. Generate a key in the JSON format.
|
||||||
|
# 5. Encode the key as base64 and store as an **actions secret** with name **`DIFFCALC_GOOGLE_CREDENTIALS`**
|
||||||
|
#
|
||||||
|
# ## Environment variables
|
||||||
|
#
|
||||||
|
# The default environment may be configured via **actions variables**.
|
||||||
|
#
|
||||||
|
# Refer to [the sample environment](https://github.com/smoogipoo/diffcalc-sheet-generator/blob/master/.env.sample), and prefix each variable with `DIFFCALC_` (e.g. `DIFFCALC_THREADS`, `DIFFCALC_INNODB_BUFFER_SIZE`, etc...).
|
||||||
|
|
||||||
|
name: Run difficulty calculation comparison
|
||||||
|
|
||||||
|
run-name: "${{ github.event_name == 'workflow_dispatch' && format('Manual run: {0}', inputs.osu-b) || 'Automatic comment trigger' }}"
|
||||||
|
|
||||||
name: Difficulty Calculation
|
|
||||||
on:
|
on:
|
||||||
issue_comment:
|
issue_comment:
|
||||||
types: [ created ]
|
types: [ created ]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
osu-b:
|
||||||
|
description: "The target build of ppy/osu"
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
ruleset:
|
||||||
|
description: "The ruleset to process"
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
options:
|
||||||
|
- osu
|
||||||
|
- taiko
|
||||||
|
- catch
|
||||||
|
- mania
|
||||||
|
converts:
|
||||||
|
description: "Include converted beatmaps"
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
ranked-only:
|
||||||
|
description: "Only ranked beatmaps"
|
||||||
|
type: boolean
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
generators:
|
||||||
|
description: "Comma-separated list of generators (available: [sr, pp, score])"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: 'pp,sr'
|
||||||
|
osu-a:
|
||||||
|
description: "The source build of ppy/osu"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
difficulty-calculator-a:
|
||||||
|
description: "The source build of ppy/osu-difficulty-calculator"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
difficulty-calculator-b:
|
||||||
|
description: "The target build of ppy/osu-difficulty-calculator"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
score-processor-a:
|
||||||
|
description: "The source build of ppy/osu-queue-score-statistics"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
score-processor-b:
|
||||||
|
description: "The target build of ppy/osu-queue-score-statistics"
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
default: 'latest'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CONCURRENCY: 4
|
EXECUTION_ID: execution-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}
|
||||||
ALLOW_DOWNLOAD: 1
|
|
||||||
SAVE_DOWNLOADED: 1
|
|
||||||
SKIP_INSERT_ATTRIBUTES: 1
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
metadata:
|
check-permissions:
|
||||||
name: Check for requests
|
name: Check permissions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.comment.body, '!diffcalc') }}
|
||||||
|
steps:
|
||||||
|
- name: Check permissions
|
||||||
|
if: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
|
uses: actions-cool/check-user-permission@a0668c9aec87f3875fc56170b6452a453e9dd819 # v2.2.0
|
||||||
|
with:
|
||||||
|
require: 'write'
|
||||||
|
|
||||||
|
create-comment:
|
||||||
|
name: Create PR comment
|
||||||
|
needs: check-permissions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request }}
|
||||||
|
steps:
|
||||||
|
- name: Create comment
|
||||||
|
uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2
|
||||||
|
with:
|
||||||
|
comment_tag: ${{ env.EXECUTION_ID }}
|
||||||
|
message: |
|
||||||
|
Difficulty calculation queued -- please wait! (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||||
|
|
||||||
|
*This comment will update on completion*
|
||||||
|
|
||||||
|
directory:
|
||||||
|
name: Prepare directory
|
||||||
|
needs: check-permissions
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER')
|
|
||||||
outputs:
|
outputs:
|
||||||
matrix: ${{ steps.generate-matrix.outputs.matrix }}
|
GENERATOR_DIR: ${{ steps.set-outputs.outputs.GENERATOR_DIR }}
|
||||||
continue: ${{ steps.generate-matrix.outputs.continue }}
|
GENERATOR_ENV: ${{ steps.set-outputs.outputs.GENERATOR_ENV }}
|
||||||
|
GOOGLE_CREDS_FILE: ${{ steps.set-outputs.outputs.GOOGLE_CREDS_FILE }}
|
||||||
steps:
|
steps:
|
||||||
- name: Construct build matrix
|
- name: Checkout diffcalc-sheet-generator
|
||||||
id: generate-matrix
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: ${{ env.EXECUTION_ID }}
|
||||||
|
repository: 'smoogipoo/diffcalc-sheet-generator'
|
||||||
|
|
||||||
|
- name: Set outputs
|
||||||
|
id: set-outputs
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ github.event.comment.body }}" =~ "osu" ]] ; then
|
echo "GENERATOR_DIR=${{ github.workspace }}/${{ env.EXECUTION_ID }}" >> "${GITHUB_OUTPUT}"
|
||||||
MATRIX_PROJECTS_JSON+='{ "name": "osu", "id": 0 },'
|
echo "GENERATOR_ENV=${{ github.workspace }}/${{ env.EXECUTION_ID }}/.env" >> "${GITHUB_OUTPUT}"
|
||||||
fi
|
echo "GOOGLE_CREDS_FILE=${{ github.workspace }}/${{ env.EXECUTION_ID }}/google-credentials.json" >> "${GITHUB_OUTPUT}"
|
||||||
if [[ "${{ github.event.comment.body }}" =~ "taiko" ]] ; then
|
|
||||||
MATRIX_PROJECTS_JSON+='{ "name": "taiko", "id": 1 },'
|
|
||||||
fi
|
|
||||||
if [[ "${{ github.event.comment.body }}" =~ "catch" ]] ; then
|
|
||||||
MATRIX_PROJECTS_JSON+='{ "name": "catch", "id": 2 },'
|
|
||||||
fi
|
|
||||||
if [[ "${{ github.event.comment.body }}" =~ "mania" ]] ; then
|
|
||||||
MATRIX_PROJECTS_JSON+='{ "name": "mania", "id": 3 },'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${MATRIX_PROJECTS_JSON}" != "" ]]; then
|
environment:
|
||||||
MATRIX_JSON="{ \"ruleset\": [ ${MATRIX_PROJECTS_JSON} ] }"
|
name: Setup environment
|
||||||
echo "${MATRIX_JSON}"
|
needs: directory
|
||||||
CONTINUE="yes"
|
|
||||||
else
|
|
||||||
CONTINUE="no"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "continue=${CONTINUE}" >> $GITHUB_OUTPUT
|
|
||||||
echo "matrix=${MATRIX_JSON}" >> $GITHUB_OUTPUT
|
|
||||||
diffcalc:
|
|
||||||
name: Run
|
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
timeout-minutes: 1440
|
env:
|
||||||
if: needs.metadata.outputs.continue == 'yes'
|
VARS_JSON: ${{ toJSON(vars) }}
|
||||||
needs: metadata
|
|
||||||
strategy:
|
|
||||||
matrix: ${{ fromJson(needs.metadata.outputs.matrix) }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Verify MySQL connection from host
|
- name: Add base environment
|
||||||
run: |
|
run: |
|
||||||
mysql -e "SHOW DATABASES"
|
# Required by diffcalc-sheet-generator
|
||||||
|
cp '${{ needs.directory.outputs.GENERATOR_DIR }}/.env.sample' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
|
||||||
- name: Drop previous databases
|
# Add Google credentials
|
||||||
run: |
|
echo '${{ secrets.DIFFCALC_GOOGLE_CREDENTIALS }}' | base64 -d > "${{ needs.directory.outputs.GOOGLE_CREDS_FILE }}"
|
||||||
for db in osu_master osu_pr
|
|
||||||
do
|
# Add repository variables
|
||||||
mysql -e "DROP DATABASE IF EXISTS $db"
|
echo "${VARS_JSON}" | jq -c '. | to_entries | .[]' | while read -r line; do
|
||||||
|
opt=$(jq -r '.key' <<< ${line})
|
||||||
|
val=$(jq -r '.value' <<< ${line})
|
||||||
|
|
||||||
|
if [[ "${opt}" =~ ^DIFFCALC_ ]]; then
|
||||||
|
optNoPrefix=$(echo "${opt}" | cut -d '_' -f2-)
|
||||||
|
sed -i "s;^${optNoPrefix}=.*$;${optNoPrefix}=${val};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Create directory structure
|
- name: Add pull-request environment
|
||||||
|
if: ${{ github.event_name == 'issue_comment' && github.event.issue.pull_request }}
|
||||||
run: |
|
run: |
|
||||||
mkdir -p $GITHUB_WORKSPACE/master/
|
sed -i "s;^OSU_B=.*$;OSU_B=${{ github.event.issue.pull_request.html_url }};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
mkdir -p $GITHUB_WORKSPACE/pr/
|
|
||||||
|
|
||||||
- name: Get upstream branch # https://akaimo.hatenablog.jp/entry/2020/05/16/101251
|
- name: Add comment environment
|
||||||
id: upstreambranch
|
if: ${{ github.event_name == 'issue_comment' }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||||
run: |
|
run: |
|
||||||
echo "branchname=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" >> $GITHUB_OUTPUT
|
# Add comment environment
|
||||||
echo "repo=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" >> $GITHUB_OUTPUT
|
echo "$COMMENT_BODY" | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do
|
||||||
|
opt=$(echo "${line}" | cut -d '=' -f1)
|
||||||
# Checkout osu
|
sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
- name: Checkout osu (master)
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
path: 'master/osu'
|
|
||||||
- name: Checkout osu (pr)
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
path: 'pr/osu'
|
|
||||||
repository: ${{ steps.upstreambranch.outputs.repo }}
|
|
||||||
ref: ${{ steps.upstreambranch.outputs.branchname }}
|
|
||||||
|
|
||||||
- name: Checkout osu-difficulty-calculator (master)
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: ppy/osu-difficulty-calculator
|
|
||||||
path: 'master/osu-difficulty-calculator'
|
|
||||||
- name: Checkout osu-difficulty-calculator (pr)
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
repository: ppy/osu-difficulty-calculator
|
|
||||||
path: 'pr/osu-difficulty-calculator'
|
|
||||||
|
|
||||||
- name: Install .NET 5.0.x
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: "5.0.x"
|
|
||||||
|
|
||||||
# Sanity checks to make sure diffcalc is not run when incompatible.
|
|
||||||
- name: Build diffcalc (master)
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator
|
|
||||||
./UseLocalOsu.sh
|
|
||||||
dotnet build
|
|
||||||
- name: Build diffcalc (pr)
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator
|
|
||||||
./UseLocalOsu.sh
|
|
||||||
dotnet build
|
|
||||||
|
|
||||||
- name: Download + import data
|
|
||||||
run: |
|
|
||||||
PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top_1000 | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g')
|
|
||||||
BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g')
|
|
||||||
|
|
||||||
# Set env variable for further steps.
|
|
||||||
echo "BEATMAPS_PATH=$GITHUB_WORKSPACE/$BEATMAPS_DATA_NAME" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
|
|
||||||
echo "Downloading database dump $PERFORMANCE_DATA_NAME.."
|
|
||||||
wget -q -nc https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2
|
|
||||||
echo "Extracting.."
|
|
||||||
tar -xf $PERFORMANCE_DATA_NAME.tar.bz2
|
|
||||||
|
|
||||||
echo "Downloading beatmap dump $BEATMAPS_DATA_NAME.."
|
|
||||||
wget -q -nc https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2
|
|
||||||
echo "Extracting.."
|
|
||||||
tar -xf $BEATMAPS_DATA_NAME.tar.bz2
|
|
||||||
|
|
||||||
cd $PERFORMANCE_DATA_NAME
|
|
||||||
|
|
||||||
for db in osu_master osu_pr
|
|
||||||
do
|
|
||||||
echo "Setting up database $db.."
|
|
||||||
|
|
||||||
mysql -e "CREATE DATABASE $db"
|
|
||||||
|
|
||||||
echo "Importing beatmaps.."
|
|
||||||
cat osu_beatmaps.sql | mysql $db
|
|
||||||
echo "Importing beatmapsets.."
|
|
||||||
cat osu_beatmapsets.sql | mysql $db
|
|
||||||
|
|
||||||
echo "Creating table structure.."
|
|
||||||
mysql $db -e 'CREATE TABLE `osu_beatmap_difficulty` (
|
|
||||||
`beatmap_id` int unsigned NOT NULL,
|
|
||||||
`mode` tinyint NOT NULL DEFAULT 0,
|
|
||||||
`mods` int unsigned NOT NULL,
|
|
||||||
`diff_unified` float NOT NULL,
|
|
||||||
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`beatmap_id`,`mode`,`mods`),
|
|
||||||
KEY `diff_sort` (`mode`,`mods`,`diff_unified`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;'
|
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Run diffcalc (master)
|
- name: Add dispatch environment
|
||||||
env:
|
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||||
DB_NAME: osu_master
|
|
||||||
run: |
|
run: |
|
||||||
cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator/osu.Server.DifficultyCalculator
|
sed -i 's;^OSU_B=.*$;OSU_B=${{ inputs.osu-b }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }}
|
sed -i 's/^RULESET=.*$/RULESET=${{ inputs.ruleset }}/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
- name: Run diffcalc (pr)
|
sed -i 's/^GENERATORS=.*$/GENERATORS=${{ inputs.generators }}/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
env:
|
|
||||||
DB_NAME: osu_pr
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator/osu.Server.DifficultyCalculator
|
|
||||||
dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }}
|
|
||||||
|
|
||||||
- name: Print diffs
|
if [[ '${{ inputs.osu-a }}' != 'latest' ]]; then
|
||||||
run: |
|
sed -i 's;^OSU_A=.*$;OSU_A=${{ inputs.osu-a }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
mysql -e "
|
fi
|
||||||
SELECT
|
|
||||||
m.beatmap_id,
|
|
||||||
m.mods,
|
|
||||||
b.filename,
|
|
||||||
m.diff_unified as 'sr_master',
|
|
||||||
p.diff_unified as 'sr_pr',
|
|
||||||
(p.diff_unified - m.diff_unified) as 'diff'
|
|
||||||
FROM osu_master.osu_beatmap_difficulty m
|
|
||||||
JOIN osu_pr.osu_beatmap_difficulty p
|
|
||||||
ON m.beatmap_id = p.beatmap_id
|
|
||||||
AND m.mode = p.mode
|
|
||||||
AND m.mods = p.mods
|
|
||||||
JOIN osu_pr.osu_beatmaps b
|
|
||||||
ON b.beatmap_id = p.beatmap_id
|
|
||||||
WHERE abs(m.diff_unified - p.diff_unified) > 0.1
|
|
||||||
ORDER BY abs(m.diff_unified - p.diff_unified)
|
|
||||||
DESC
|
|
||||||
LIMIT 10000;"
|
|
||||||
|
|
||||||
# Todo: Run ppcalc
|
if [[ '${{ inputs.difficulty-calculator-a }}' != 'latest' ]]; then
|
||||||
|
sed -i 's;^DIFFICULTY_CALCULATOR_A=.*$;DIFFICULTY_CALCULATOR_A=${{ inputs.difficulty-calculator-a }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.difficulty-calculator-b }}' != 'latest' ]]; then
|
||||||
|
sed -i 's;^DIFFICULTY_CALCULATOR_B=.*$;DIFFICULTY_CALCULATOR_B=${{ inputs.difficulty-calculator-b }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.score-processor-a }}' != 'latest' ]]; then
|
||||||
|
sed -i 's;^SCORE_PROCESSOR_A=.*$;SCORE_PROCESSOR_A=${{ inputs.score-processor-a }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.score-processor-b }}' != 'latest' ]]; then
|
||||||
|
sed -i 's;^SCORE_PROCESSOR_B=.*$;SCORE_PROCESSOR_B=${{ inputs.score-processor-b }};' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.converts }}' == 'true' ]]; then
|
||||||
|
sed -i 's/^NO_CONVERTS=.*$/NO_CONVERTS=0/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
else
|
||||||
|
sed -i 's/^NO_CONVERTS=.*$/NO_CONVERTS=1/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ '${{ inputs.ranked-only }}' == 'true' ]]; then
|
||||||
|
sed -i 's/^RANKED_ONLY=.*$/RANKED_ONLY=1/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
else
|
||||||
|
sed -i 's/^RANKED_ONLY=.*$/RANKED_ONLY=0/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
scores:
|
||||||
|
name: Setup scores
|
||||||
|
needs: [ directory, environment ]
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Query latest data
|
||||||
|
id: query
|
||||||
|
run: |
|
||||||
|
ruleset=$(cat ${{ needs.directory.outputs.GENERATOR_ENV }} | grep -E '^RULESET=' | cut -d '=' -f2-)
|
||||||
|
performance_data_name=$(curl -s "https://data.ppy.sh/" | grep "performance_${ruleset}_top_1000\b" | tail -1 | awk -F "'" '{print $2}' | sed 's/\.tar\.bz2//g')
|
||||||
|
|
||||||
|
echo "TARGET_DIR=${{ needs.directory.outputs.GENERATOR_DIR }}/sql/${ruleset}" >> "${GITHUB_OUTPUT}"
|
||||||
|
echo "DATA_NAME=${performance_data_name}" >> "${GITHUB_OUTPUT}"
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
id: restore-cache
|
||||||
|
uses: maxnowack/local-cache@038cc090b52e4f205fbc468bf5b0756df6f68775 # v1
|
||||||
|
with:
|
||||||
|
path: ${{ steps.query.outputs.DATA_NAME }}.tar.bz2
|
||||||
|
key: ${{ steps.query.outputs.DATA_NAME }}
|
||||||
|
|
||||||
|
- name: Download
|
||||||
|
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
wget -q -nc "https://data.ppy.sh/${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
|
||||||
|
|
||||||
|
- name: Extract
|
||||||
|
run: |
|
||||||
|
tar -I lbzip2 -xf "${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
|
||||||
|
rm -r "${{ steps.query.outputs.TARGET_DIR }}"
|
||||||
|
mv "${{ steps.query.outputs.DATA_NAME }}" "${{ steps.query.outputs.TARGET_DIR }}"
|
||||||
|
|
||||||
|
beatmaps:
|
||||||
|
name: Setup beatmaps
|
||||||
|
needs: directory
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Query latest data
|
||||||
|
id: query
|
||||||
|
run: |
|
||||||
|
beatmaps_data_name=$(curl -s "https://data.ppy.sh/" | grep "osu_files" | tail -1 | awk -F "'" '{print $2}' | sed 's/\.tar\.bz2//g')
|
||||||
|
|
||||||
|
echo "TARGET_DIR=${{ needs.directory.outputs.GENERATOR_DIR }}/beatmaps" >> "${GITHUB_OUTPUT}"
|
||||||
|
echo "DATA_NAME=${beatmaps_data_name}" >> "${GITHUB_OUTPUT}"
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
id: restore-cache
|
||||||
|
uses: maxnowack/local-cache@038cc090b52e4f205fbc468bf5b0756df6f68775 # v1
|
||||||
|
with:
|
||||||
|
path: ${{ steps.query.outputs.DATA_NAME }}.tar.bz2
|
||||||
|
key: ${{ steps.query.outputs.DATA_NAME }}
|
||||||
|
|
||||||
|
- name: Download
|
||||||
|
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
wget -q -nc "https://data.ppy.sh/${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
|
||||||
|
|
||||||
|
- name: Extract
|
||||||
|
run: |
|
||||||
|
tar -I lbzip2 -xf "${{ steps.query.outputs.DATA_NAME }}.tar.bz2"
|
||||||
|
rm -r "${{ steps.query.outputs.TARGET_DIR }}"
|
||||||
|
mv "${{ steps.query.outputs.DATA_NAME }}" "${{ steps.query.outputs.TARGET_DIR }}"
|
||||||
|
|
||||||
|
generator:
|
||||||
|
name: Run generator
|
||||||
|
needs: [ directory, environment, scores, beatmaps ]
|
||||||
|
runs-on: self-hosted
|
||||||
|
timeout-minutes: 720
|
||||||
|
outputs:
|
||||||
|
TARGET: ${{ steps.run.outputs.TARGET }}
|
||||||
|
SPREADSHEET_LINK: ${{ steps.run.outputs.SPREADSHEET_LINK }}
|
||||||
|
steps:
|
||||||
|
- name: Run
|
||||||
|
id: run
|
||||||
|
run: |
|
||||||
|
# Add the GitHub token. This needs to be done here because it's unique per-job.
|
||||||
|
sed -i 's/^GH_TOKEN=.*$/GH_TOKEN=${{ github.token }}/' "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
|
|
||||||
|
cd "${{ needs.directory.outputs.GENERATOR_DIR }}"
|
||||||
|
docker-compose up --build generator
|
||||||
|
|
||||||
|
link=$(docker-compose logs generator -n 10 | grep 'http' | sed -E 's/^.*(http.*)$/\1/')
|
||||||
|
target=$(cat "${{ needs.directory.outputs.GENERATOR_ENV }}" | grep -E '^OSU_B=' | cut -d '=' -f2-)
|
||||||
|
|
||||||
|
echo "TARGET=${target}" >> "${GITHUB_OUTPUT}"
|
||||||
|
echo "SPREADSHEET_LINK=${link}" >> "${GITHUB_OUTPUT}"
|
||||||
|
|
||||||
|
- name: Shutdown
|
||||||
|
if: ${{ always() }}
|
||||||
|
run: |
|
||||||
|
cd "${{ needs.directory.outputs.GENERATOR_DIR }}"
|
||||||
|
docker-compose down -v
|
||||||
|
|
||||||
|
output-cli:
|
||||||
|
name: Output info
|
||||||
|
needs: generator
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Output info
|
||||||
|
run: |
|
||||||
|
echo "Target: ${{ needs.generator.outputs.TARGET }}"
|
||||||
|
echo "Spreadsheet: ${{ needs.generator.outputs.SPREADSHEET_LINK }}"
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
name: Cleanup
|
||||||
|
needs: [ directory, generator ]
|
||||||
|
if: ${{ always() && needs.directory.result == 'success' }}
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: Cleanup
|
||||||
|
run: |
|
||||||
|
rm -rf "${{ needs.directory.outputs.GENERATOR_DIR }}"
|
||||||
|
|
||||||
|
update-comment:
|
||||||
|
name: Update PR comment
|
||||||
|
needs: [ create-comment, generator ]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ always() && needs.create-comment.result == 'success' }}
|
||||||
|
steps:
|
||||||
|
- name: Update comment on success
|
||||||
|
if: ${{ needs.generator.result == 'success' }}
|
||||||
|
uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2
|
||||||
|
with:
|
||||||
|
comment_tag: ${{ env.EXECUTION_ID }}
|
||||||
|
mode: upsert
|
||||||
|
create_if_not_exists: false
|
||||||
|
message: |
|
||||||
|
Target: ${{ needs.generator.outputs.TARGET }}
|
||||||
|
Spreadsheet: ${{ needs.generator.outputs.SPREADSHEET_LINK }}
|
||||||
|
|
||||||
|
- name: Update comment on failure
|
||||||
|
if: ${{ needs.generator.result == 'failure' }}
|
||||||
|
uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2
|
||||||
|
with:
|
||||||
|
comment_tag: ${{ env.EXECUTION_ID }}
|
||||||
|
mode: upsert
|
||||||
|
create_if_not_exists: false
|
||||||
|
message: |
|
||||||
|
Difficulty calculation failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
|
- name: Update comment on cancellation
|
||||||
|
if: ${{ needs.generator.result == 'cancelled' }}
|
||||||
|
uses: thollander/actions-comment-pull-request@363c6f6eae92cc5c3a66e95ba016fc771bb38943 # v2.4.2
|
||||||
|
with:
|
||||||
|
comment_tag: ${{ env.EXECUTION_ID }}
|
||||||
|
mode: delete
|
||||||
|
message: '.' # Appears to be required by this action for non-error status code.
|
||||||
|
@ -12,10 +12,10 @@ jobs:
|
|||||||
name: Update osu-web mod definitions
|
name: Update osu-web mod definitions
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Checkout ppy/osu
|
- name: Checkout ppy/osu
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
1
.gitignore
vendored
@ -339,6 +339,5 @@ inspectcode
|
|||||||
|
|
||||||
# Fody (pulled in by Realm) - schema file
|
# Fody (pulled in by Realm) - schema file
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
**/FodyWeavers.xml
|
|
||||||
|
|
||||||
.idea/.idea.osu.Desktop/.idea/misc.xml
|
.idea/.idea.osu.Desktop/.idea/misc.xml
|
@ -1,5 +1,3 @@
|
|||||||
is_global = true
|
|
||||||
|
|
||||||
# .NET Code Style
|
# .NET Code Style
|
||||||
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
||||||
|
|
||||||
@ -56,4 +54,4 @@ dotnet_diagnostic.RS0030.severity = error
|
|||||||
|
|
||||||
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
|
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
|
||||||
# See: https://github.com/ppy/osu/pull/19677
|
# See: https://github.com/ppy/osu/pull/19677
|
||||||
dotnet_diagnostic.OSUF001.severity = none
|
dotnet_diagnostic.OSUF001.severity = none
|
||||||
|
@ -59,7 +59,7 @@ The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of
|
|||||||
|
|
||||||
In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive.
|
In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive.
|
||||||
|
|
||||||
If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! public Figma library](https://www.figma.com/file/6m10GiGEncVFWmgOoSyakH/osu!-Figma-Library).
|
If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library).
|
||||||
|
|
||||||
Aside from the above, below is a brief checklist of things to watch out when you're preparing your code changes:
|
Aside from the above, below is a brief checklist of things to watch out when you're preparing your code changes:
|
||||||
|
|
||||||
@ -85,4 +85,4 @@ If you're uncertain about some part of the codebase or some inner workings of th
|
|||||||
- [Development roadmap](https://github.com/orgs/ppy/projects/7/views/6): What the core team is currently working on
|
- [Development roadmap](https://github.com/orgs/ppy/projects/7/views/6): What the core team is currently working on
|
||||||
- [`ppy/osu-framework` wiki](https://github.com/ppy/osu-framework/wiki): Contains introductory information about osu!framework, the bespoke 2D game framework we use for the game
|
- [`ppy/osu-framework` wiki](https://github.com/ppy/osu-framework/wiki): Contains introductory information about osu!framework, the bespoke 2D game framework we use for the game
|
||||||
- [`ppy/osu` wiki](https://github.com/ppy/osu/wiki): Contains articles about various technical aspects of the game
|
- [`ppy/osu` wiki](https://github.com/ppy/osu/wiki): Contains articles about various technical aspects of the game
|
||||||
- [Public Figma library](https://www.figma.com/file/6m10GiGEncVFWmgOoSyakH/osu!-Figma-Library): Contains finished and draft designs for osu!
|
- [Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library): Contains finished and draft designs for osu!
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!-- Contains required properties for osu!framework projects. -->
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Label="C#">
|
<PropertyGroup Label="C#">
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>12.0</LangVersion>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
|
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
|
||||||
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
|
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
|
||||||
<Company>ppy Pty Ltd</Company>
|
<Company>ppy Pty Ltd</Company>
|
||||||
<Copyright>Copyright (c) 2022 ppy Pty Ltd</Copyright>
|
<Copyright>Copyright (c) 2024 ppy Pty Ltd</Copyright>
|
||||||
<PackageTags>osu game</PackageTags>
|
<PackageTags>osu game</PackageTags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
2
LICENCE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2022 ppy Pty Ltd <contact@ppy.sh>.
|
Copyright (c) 2024 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
45
README.md
@ -12,45 +12,48 @@
|
|||||||
|
|
||||||
A free-to-win rhythm game. Rhythm is just a *click* away!
|
A free-to-win rhythm game. Rhythm is just a *click* away!
|
||||||
|
|
||||||
The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Currently known by and released under the release codename "*lazer*". As in sharper than cutting-edge.
|
This is the future – and final – iteration of the [osu!](https://osu.ppy.sh) game client which marks the beginning of an open era! Currently known by and released under the release codename "*lazer*". As in sharper than cutting-edge.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
This project is under constant development, but we aim to keep things in a stable state. Users are encouraged to try it out and keep it installed alongside the stable *osu!* client. It will continue to evolve to the point of eventually replacing the existing stable client as an update.
|
This project is under constant development, but we do our best to keep things in a stable state. Players are encouraged to install from a release alongside their stable *osu!* client. This project will continue to evolve until we eventually reach the point where most users prefer it over the previous "osu!stable" release.
|
||||||
|
|
||||||
**IMPORTANT:** Gameplay mechanics (and other features which you may have come to know and love) are in a constant state of flux. Game balance and final quality-of-life passes come at the end of development, preceded by experimentation and changes which may potentially **reduce playability or usability**. This is done in order to allow us to move forward as developers and designers more efficiently. If this offends you, please consider sticking to a [stable release](https://osu.ppy.sh/home/download) of osu!. We are not yet open to heated discussion over game mechanics and will not be using github as a forum for such discussions just yet.
|
A few resources are available as starting points to getting involved and understanding the project:
|
||||||
|
|
||||||
We are accepting bug reports (please report with as much detail as possible and follow the existing issue templates). Feature requests are also welcome, but understand that our focus is on completing the game to feature parity before adding new features. A few resources are available as starting points to getting involved and understanding the project:
|
|
||||||
|
|
||||||
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
- Detailed release changelogs are available on the [official osu! site](https://osu.ppy.sh/home/changelog/lazer).
|
||||||
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
|
- You can learn more about our approach to [project management](https://github.com/ppy/osu/wiki/Project-management).
|
||||||
|
- Track our current efforts [towards improving the game](https://github.com/orgs/ppy/projects/7/views/6).
|
||||||
|
|
||||||
## Running osu!
|
## Running osu!
|
||||||
|
|
||||||
If you are looking to install or test osu! without setting up a development environment, you can consume our [releases](https://github.com/ppy/osu/releases). You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download). Failing that, you may use the links below to download the latest version for your operating system of choice:
|
If you are just looking to give the game a whirl, you can grab the latest release for your platform:
|
||||||
|
|
||||||
**Latest release:**
|
### Latest release:
|
||||||
|
|
||||||
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
|
| [Windows 10+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 12+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
|
||||||
| ------------- | ------------- | ------------- | ------------- | ------------- |
|
|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------------- | ------------- | ------------- |
|
||||||
|
|
||||||
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
|
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
|
||||||
|
|
||||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||||
|
|
||||||
|
**For iOS/iPadOS users**: The iOS testflight link fills up very fast (Apple has a hard limit of 10,000 users). We reset it occasionally. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements. Our goal is to get the game on mobile app stores in early 2024.
|
||||||
|
|
||||||
## Developing a custom ruleset
|
## Developing a custom ruleset
|
||||||
|
|
||||||
osu! is designed to have extensible modular gameplay modes, called "rulesets". Building one of these allows a developer to harness the power of osu! for their own game style. To get started working on a ruleset, we have some templates available [here](https://github.com/ppy/osu/tree/master/Templates).
|
osu! is designed to allow user-created gameplay variations, called "rulesets". Building one of these allows a developer to harness the power of the osu! beatmap library, game engine, and general UX for a new style of gameplay. To get started working on a ruleset, we have some templates available [here](https://github.com/ppy/osu/tree/master/Templates).
|
||||||
|
|
||||||
You can see some examples of custom rulesets by visiting the [custom ruleset directory](https://github.com/ppy/osu/discussions/13096).
|
You can see some examples of custom rulesets by visiting the [custom ruleset directory](https://github.com/ppy/osu/discussions/13096).
|
||||||
|
|
||||||
## Developing osu!
|
## Developing osu!
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
Please make sure you have the following prerequisites:
|
Please make sure you have the following prerequisites:
|
||||||
|
|
||||||
- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
|
- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
|
||||||
|
|
||||||
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) plugin installed.
|
||||||
|
|
||||||
### Downloading the source code
|
### Downloading the source code
|
||||||
|
|
||||||
@ -69,9 +72,19 @@ git pull
|
|||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this is provided [below](#contributing).
|
#### From an IDE
|
||||||
|
|
||||||
- Visual Studio / Rider users should load the project via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will allow access to template run configurations.
|
You should load the solution via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will reduce dependencies and hide platforms that you don't care about. Valid `.slnf` files are:
|
||||||
|
|
||||||
|
- `osu.Desktop.slnf` (most common)
|
||||||
|
- `osu.Android.slnf`
|
||||||
|
- `osu.iOS.slnf`
|
||||||
|
|
||||||
|
Run configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `osu! (Tests)` project/configuration. More information on this is provided [below](#contributing).
|
||||||
|
|
||||||
|
To build for mobile platforms, you will likely need to run `sudo dotnet workload restore` if you haven't done so previously. This will install Android/iOS tooling required to complete the build.
|
||||||
|
|
||||||
|
#### From CLI
|
||||||
|
|
||||||
You can also build and run *osu!* from the command-line with a single command:
|
You can also build and run *osu!* from the command-line with a single command:
|
||||||
|
|
||||||
@ -79,12 +92,10 @@ You can also build and run *osu!* from the command-line with a single command:
|
|||||||
dotnet run --project osu.Desktop
|
dotnet run --project osu.Desktop
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are not interested in debugging *osu!*, you can add `-c Release` to gain performance. In this case, you must replace `Debug` with `Release` in any commands mentioned in this document.
|
When running locally to do any kind of performance testing, make sure to add `-c Release` to the build command, as the overhead of running with the default `Debug` configuration can be large (especially when testing with local framework modifications as below).
|
||||||
|
|
||||||
If the build fails, try to restore NuGet packages with `dotnet restore`.
|
If the build fails, try to restore NuGet packages with `dotnet restore`.
|
||||||
|
|
||||||
_Due to a historical feature gap between .NET Core and Xamarin, running `dotnet` CLI from the root directory will not work for most commands. This can be resolved by specifying a target `.csproj` or the helper project at `build/Desktop.proj`. Configurations have been provided to work around this issue for all supported IDEs mentioned above._
|
|
||||||
|
|
||||||
### Testing with resource/framework modifications
|
### Testing with resource/framework modifications
|
||||||
|
|
||||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be quickly achieved using included commands:
|
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be quickly achieved using included commands:
|
||||||
|
@ -7,7 +7,7 @@ Templates for use when creating osu! dependent projects. Create a fully-testable
|
|||||||
```bash
|
```bash
|
||||||
# install (or update) templates package.
|
# install (or update) templates package.
|
||||||
# this only needs to be done once
|
# this only needs to be done once
|
||||||
dotnet new -i ppy.osu.Game.Templates
|
dotnet new install ppy.osu.Game.Templates
|
||||||
|
|
||||||
# create an empty freeform ruleset
|
# create an empty freeform ruleset
|
||||||
dotnet new ruleset -n MyCoolRuleset
|
dotnet new ruleset -n MyCoolRuleset
|
||||||
|
10
Templates/Rulesets/ruleset-empty/Directory.Build.props
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Documentation">
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
<assemblyIdentity version="1.0.0.0" name="osu!" />
|
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
|
||||||
<SquirrelAwareVersion xmlns="urn:schema-squirrel-com:asm.v1">1</SquirrelAwareVersion>
|
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
@ -13,9 +12,35 @@
|
|||||||
</applicationRequestMinimum>
|
</applicationRequestMinimum>
|
||||||
</security>
|
</security>
|
||||||
</trustInfo>
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
<asmv3:application>
|
<asmv3:application>
|
||||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
<dpiAware>true</dpiAware>
|
<dpiAware>true</dpiAware>
|
||||||
</asmv3:windowsSettings>
|
</asmv3:windowsSettings>
|
||||||
</asmv3:application>
|
</asmv3:application>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
</asmv1:assembly>
|
</asmv1:assembly>
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||||
{
|
{
|
||||||
host.Run(new OsuTestBrowser());
|
host.Run(new OsuTestBrowser());
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -9,16 +9,16 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyFreeform\osu.Game.Rulesets.EmptyFreeform.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.EmptyFreeform.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.EmptyFreeform.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
// todo: implement judgement logic
|
// todo: implement judgement logic
|
||||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
ApplyResult(HitResult.Perfect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle>
|
<AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
10
Templates/Rulesets/ruleset-example/Directory.Build.props
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Documentation">
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
46
Templates/Rulesets/ruleset-example/app.manifest
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
<applicationRequestMinimum>
|
||||||
|
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||||
|
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||||
|
</applicationRequestMinimum>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</asmv1:assembly>
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||||
{
|
{
|
||||||
host.Run(new OsuTestBrowser());
|
host.Run(new OsuTestBrowser());
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -9,16 +9,16 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
|||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
ApplyResult(r => r.Type = IsHovered ? HitResult.Perfect : HitResult.Miss);
|
{
|
||||||
|
if (IsHovered)
|
||||||
|
ApplyMaxResult();
|
||||||
|
else
|
||||||
|
ApplyMinResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double InitialLifetimeOffset => time_preempt;
|
protected override double InitialLifetimeOffset => time_preempt;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
|
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Documentation">
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
46
Templates/Rulesets/ruleset-scrolling-empty/app.manifest
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
<applicationRequestMinimum>
|
||||||
|
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||||
|
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||||
|
</applicationRequestMinimum>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</asmv1:assembly>
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||||
{
|
{
|
||||||
host.Run(new OsuTestBrowser());
|
host.Run(new OsuTestBrowser());
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -9,16 +9,16 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.EmptyScrolling\osu.Game.Rulesets.EmptyScrolling.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.EmptyScrolling.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.EmptyScrolling.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
// todo: implement judgement logic
|
// todo: implement judgement logic
|
||||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
ApplyMaxResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle>
|
<AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Documentation">
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
46
Templates/Rulesets/ruleset-scrolling-example/app.manifest
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
<applicationRequestMinimum>
|
||||||
|
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||||
|
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||||
|
</applicationRequestMinimum>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</asmv1:assembly>
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu"))
|
||||||
{
|
{
|
||||||
host.Run(new OsuTestBrowser());
|
host.Run(new OsuTestBrowser());
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -9,16 +9,16 @@
|
|||||||
<GenerateProgramFile>false</GenerateProgramFile>
|
<GenerateProgramFile>false</GenerateProgramFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Pippidon\osu.Game.Rulesets.Pippidon.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Pippidon.UI;
|
using osu.Game.Rulesets.Pippidon.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
|||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
ApplyResult(r => r.Type = currentLane.Value == HitObject.Lane ? HitResult.Perfect : HitResult.Miss);
|
{
|
||||||
|
if (currentLane.Value == HitObject.Lane)
|
||||||
|
ApplyMaxResult();
|
||||||
|
else
|
||||||
|
ApplyMinResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
|
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageType>Template</PackageType>
|
<PackageType>Template</PackageType>
|
||||||
<PackageId>ppy.osu.Game.Templates</PackageId>
|
<PackageId>ppy.osu.Game.Templates</PackageId>
|
||||||
@ -8,7 +8,7 @@
|
|||||||
<PackageProjectUrl>https://github.com/ppy/osu/blob/master/Templates</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/ppy/osu/blob/master/Templates</PackageProjectUrl>
|
||||||
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
|
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
|
||||||
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
|
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
|
||||||
<copyright>Copyright (c) 2022 ppy Pty Ltd</copyright>
|
<copyright>Copyright (c) 2024 ppy Pty Ltd</copyright>
|
||||||
<Description>Templates to use when creating a ruleset for consumption in osu!.</Description>
|
<Description>Templates to use when creating a ruleset for consumption in osu!.</Description>
|
||||||
<PackageTags>dotnet-new;templates;osu</PackageTags>
|
<PackageTags>dotnet-new;templates;osu</PackageTags>
|
||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
26
app.manifest
@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
<assemblyIdentity version="1.0.0.0" name="osu!" />
|
<assemblyIdentity version="1.0.0.0" name="osu!" />
|
||||||
|
<SquirrelAwareVersion xmlns="urn:schema-squirrel-com:asm.v1">1</SquirrelAwareVersion>
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
<security>
|
<security>
|
||||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
@ -14,33 +15,10 @@
|
|||||||
</trustInfo>
|
</trustInfo>
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
<application>
|
<application>
|
||||||
<!-- Windows Vista -->
|
|
||||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
|
||||||
<!-- Windows 7 -->
|
|
||||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
|
||||||
<!-- Windows 8 -->
|
|
||||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
|
||||||
<!-- Windows 8.1 -->
|
<!-- Windows 8.1 -->
|
||||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
<!-- Windows 10 -->
|
<!-- Windows 10 and Windows 11 -->
|
||||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
</application>
|
</application>
|
||||||
</compatibility>
|
</compatibility>
|
||||||
<asmv3:application>
|
|
||||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
|
||||||
<dpiAware>true</dpiAware>
|
|
||||||
</asmv3:windowsSettings>
|
|
||||||
</asmv3:application>
|
|
||||||
<dependency>
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity
|
|
||||||
type="win32"
|
|
||||||
name="Microsoft.Windows.Common-Controls"
|
|
||||||
version="6.0.0.0"
|
|
||||||
processorArchitecture="*"
|
|
||||||
publicKeyToken="6595b64144ccf1df"
|
|
||||||
language="*"
|
|
||||||
/>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
</asmv1:assembly>
|
</asmv1:assembly>
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
assets/lazer.png
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 321 KiB |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "6.0.100",
|
"version": "8.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature",
|
||||||
|
"allowPrerelease": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,13 +8,9 @@
|
|||||||
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
|
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
|
||||||
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.608.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.205.0" />
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -1,5 +1,20 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
<application android:allowBackup="true"
|
||||||
</manifest>
|
android:supportsRtl="true"
|
||||||
|
android:label="osu!"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher" />
|
||||||
|
<!-- for editor usage -->
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
|
<!--
|
||||||
|
READ_MEDIA_* permissions are available only on API 33 or greater. Devices with older android versions
|
||||||
|
don't understand the new permissions, so request the old READ_EXTERNAL_STORAGE permission to get storage access.
|
||||||
|
Since the old permission has no effect on >= API 33, don't request it.
|
||||||
|
|
||||||
|
Care needs to be taken to ensure runtime permission checks target the correct permission for the API level.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
|
||||||
|
</manifest>
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Android
|
|||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = MouseSettingsStrings.DisableMouseButtons,
|
LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
|
||||||
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
|
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Android.Content.PM;
|
using Android.Content.PM;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -13,10 +11,10 @@ namespace osu.Android
|
|||||||
{
|
{
|
||||||
public partial class GameplayScreenRotationLocker : Component
|
public partial class GameplayScreenRotationLocker : Component
|
||||||
{
|
{
|
||||||
private Bindable<bool> localUserPlaying;
|
private Bindable<bool> localUserPlaying = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuGameActivity gameActivity { get; set; }
|
private OsuGameActivity gameActivity { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuGame game)
|
private void load(OsuGame game)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -15,6 +13,7 @@ using Android.Graphics;
|
|||||||
using Android.OS;
|
using Android.OS;
|
||||||
using Android.Views;
|
using Android.Views;
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using Debug = System.Diagnostics.Debug;
|
using Debug = System.Diagnostics.Debug;
|
||||||
using Uri = Android.Net.Uri;
|
using Uri = Android.Net.Uri;
|
||||||
@ -51,11 +50,11 @@ namespace osu.Android
|
|||||||
/// <remarks>Adjusted on startup to match expected UX for the current device type (phone/tablet).</remarks>
|
/// <remarks>Adjusted on startup to match expected UX for the current device type (phone/tablet).</remarks>
|
||||||
public ScreenOrientation DefaultOrientation = ScreenOrientation.Unspecified;
|
public ScreenOrientation DefaultOrientation = ScreenOrientation.Unspecified;
|
||||||
|
|
||||||
private OsuGameAndroid game;
|
private OsuGameAndroid game = null!;
|
||||||
|
|
||||||
protected override Framework.Game CreateGame() => game = new OsuGameAndroid(this);
|
protected override Framework.Game CreateGame() => game = new OsuGameAndroid(this);
|
||||||
|
|
||||||
protected override void OnCreate(Bundle savedInstanceState)
|
protected override void OnCreate(Bundle? savedInstanceState)
|
||||||
{
|
{
|
||||||
base.OnCreate(savedInstanceState);
|
base.OnCreate(savedInstanceState);
|
||||||
|
|
||||||
@ -73,9 +72,9 @@ namespace osu.Android
|
|||||||
Debug.Assert(Resources?.DisplayMetrics != null);
|
Debug.Assert(Resources?.DisplayMetrics != null);
|
||||||
|
|
||||||
Point displaySize = new Point();
|
Point displaySize = new Point();
|
||||||
#pragma warning disable 618 // GetSize is deprecated
|
#pragma warning disable CA1422 // GetSize is deprecated
|
||||||
WindowManager.DefaultDisplay.GetSize(displaySize);
|
WindowManager.DefaultDisplay.GetSize(displaySize);
|
||||||
#pragma warning restore 618
|
#pragma warning restore CA1422
|
||||||
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
|
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
|
||||||
bool isTablet = smallestWidthDp >= 600f;
|
bool isTablet = smallestWidthDp >= 600f;
|
||||||
|
|
||||||
@ -92,15 +91,15 @@ namespace osu.Android
|
|||||||
Assembly.Load("osu.Game.Rulesets.Mania");
|
Assembly.Load("osu.Game.Rulesets.Mania");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnNewIntent(Intent intent) => handleIntent(intent);
|
protected override void OnNewIntent(Intent? intent) => handleIntent(intent);
|
||||||
|
|
||||||
private void handleIntent(Intent intent)
|
private void handleIntent(Intent? intent)
|
||||||
{
|
{
|
||||||
switch (intent.Action)
|
switch (intent?.Action)
|
||||||
{
|
{
|
||||||
case Intent.ActionDefault:
|
case Intent.ActionDefault:
|
||||||
if (intent.Scheme == ContentResolver.SchemeContent)
|
if (intent.Scheme == ContentResolver.SchemeContent)
|
||||||
handleImportFromUris(intent.Data);
|
handleImportFromUris(intent.Data.AsNonNull());
|
||||||
else if (osu_url_schemes.Contains(intent.Scheme))
|
else if (osu_url_schemes.Contains(intent.Scheme))
|
||||||
game.HandleLink(intent.DataString);
|
game.HandleLink(intent.DataString);
|
||||||
break;
|
break;
|
||||||
@ -114,7 +113,7 @@ namespace osu.Android
|
|||||||
{
|
{
|
||||||
var content = intent.ClipData?.GetItemAt(i);
|
var content = intent.ClipData?.GetItemAt(i);
|
||||||
if (content != null)
|
if (content != null)
|
||||||
uris.Add(content.Uri);
|
uris.Add(content.Uri.AsNonNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
handleImportFromUris(uris.ToArray());
|
handleImportFromUris(uris.ToArray());
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using Microsoft.Maui.Devices;
|
using Microsoft.Maui.Devices;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Android.Input;
|
using osu.Framework.Android.Input;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Input.Handlers;
|
using osu.Framework.Input.Handlers;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Overlays.Settings.Sections.Input;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ namespace osu.Android
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var packageInfo = Application.Context.ApplicationContext.PackageManager.GetPackageInfo(Application.Context.ApplicationContext.PackageName, 0);
|
var packageInfo = Application.Context.ApplicationContext!.PackageManager!.GetPackageInfo(Application.Context.ApplicationContext.PackageName!, 0).AsNonNull();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -45,7 +45,7 @@ namespace osu.Android
|
|||||||
// Basic conversion format (as done in Fastfile): 2020.606.0 -> 202006060
|
// Basic conversion format (as done in Fastfile): 2020.606.0 -> 202006060
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/52977079/android-sdk-28-versioncode-in-packageinfo-has-been-deprecated
|
// https://stackoverflow.com/questions/52977079/android-sdk-28-versioncode-in-packageinfo-has-been-deprecated
|
||||||
string versionName = string.Empty;
|
string versionName;
|
||||||
|
|
||||||
if (OperatingSystem.IsAndroidVersionAtLeast(28))
|
if (OperatingSystem.IsAndroidVersionAtLeast(28))
|
||||||
{
|
{
|
||||||
@ -68,7 +68,7 @@ namespace osu.Android
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Version(packageInfo.VersionName);
|
return new Version(packageInfo.VersionName.AsNonNull());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,6 +98,9 @@ namespace osu.Android
|
|||||||
case AndroidJoystickHandler jh:
|
case AndroidJoystickHandler jh:
|
||||||
return new AndroidJoystickSettings(jh);
|
return new AndroidJoystickSettings(jh);
|
||||||
|
|
||||||
|
case AndroidTouchHandler th:
|
||||||
|
return new TouchSettings(th);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return base.CreateSettingsSubsectionFor(handler);
|
return base.CreateSettingsSubsectionFor(handler);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<queries>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="https" />
|
|
||||||
</intent>
|
|
||||||
<intent>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="mailto" />
|
|
||||||
</intent>
|
|
||||||
</queries>
|
|
||||||
</manifest>
|
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Android;
|
using Android;
|
||||||
using Android.App;
|
using Android.App;
|
||||||
|
|
||||||
|
618
osu.Android/Resources/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,618 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.67"
|
||||||
|
android:scaleY="0.67"
|
||||||
|
android:translateX="17.82"
|
||||||
|
android:translateY="17.82">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M109.48,-1.48H-1.48V109.48H109.48V-1.48Z"
|
||||||
|
android:fillColor="#404041"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,-0.31H108V107.69H0V-0.31Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M215.01,-78.1H-78.39V215.3H215.01V-78.1Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M149.56,96.97H96.68V149.85H149.56V96.97Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M149.56,96.97H96.68V149.85H149.56V96.97Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M100.57,98.05C100.34,98.05 100.15,98.24 100.15,98.47V102.61C100.15,102.84 100.34,103.03 100.57,103.03C100.79,103.03 100.98,102.84 100.98,102.61V98.47C100.98,98.24 100.79,98.05 100.57,98.05Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M101.86,99.22C101.63,99.22 101.44,99.41 101.44,99.63V101.45C101.44,101.67 101.63,101.86 101.86,101.86C102.08,101.86 102.27,101.67 102.27,101.45V99.63C102.27,99.41 102.08,99.22 101.86,99.22Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M104.44,100.54C104.44,101.06 104.33,101.57 104.13,102.05C103.94,102.51 103.65,102.92 103.3,103.28C102.95,103.63 102.53,103.91 102.07,104.11C101.59,104.31 101.09,104.41 100.57,104.41C100.04,104.41 99.54,104.31 99.06,104.11C98.6,103.91 98.19,103.63 97.83,103.28C97.48,102.92 97.2,102.51 97,102.05C96.8,101.57 96.7,101.06 96.7,100.54C96.7,100.02 96.8,99.51 97,99.04C97.2,98.58 97.48,98.16 97.83,97.81C98.19,97.45 98.6,97.18 99.06,96.98C99.54,96.78 100.04,96.67 100.57,96.67C101.09,96.67 101.59,96.77 102.07,96.98C102.53,97.17 102.95,97.45 103.3,97.81C103.65,98.16 103.93,98.58 104.13,99.04C104.33,99.51 104.44,100.02 104.44,100.54ZM103.78,100.54C103.78,98.77 102.34,97.33 100.57,97.33C98.8,97.33 97.36,98.77 97.36,100.54C97.36,102.31 98.8,103.75 100.57,103.75C102.34,103.75 103.78,102.31 103.78,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M99.28,101.86C99.51,101.86 99.69,101.67 99.69,101.45V99.64C99.69,99.41 99.51,99.23 99.28,99.23C99.05,99.23 98.87,99.41 98.87,99.64V101.45C98.87,101.68 99.05,101.86 99.28,101.86Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M149.56,44.09H96.68V96.97H149.56V44.09Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M149.56,44.09H96.68V96.97H149.56V44.09Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M104.44,100.54C104.44,101.06 104.33,101.57 104.13,102.05C103.94,102.51 103.65,102.92 103.3,103.28C102.95,103.63 102.53,103.91 102.07,104.11C101.59,104.31 101.09,104.41 100.57,104.41C100.04,104.41 99.54,104.31 99.06,104.11C98.6,103.91 98.19,103.63 97.83,103.28C97.48,102.92 97.2,102.51 97,102.05C96.8,101.57 96.7,101.06 96.7,100.54C96.7,100.02 96.8,99.51 97,99.04C97.2,98.58 97.48,98.16 97.83,97.81C98.19,97.45 98.6,97.18 99.06,96.98C99.54,96.78 100.04,96.67 100.57,96.67C101.09,96.67 101.59,96.77 102.07,96.98C102.53,97.17 102.95,97.45 103.3,97.81C103.65,98.16 103.93,98.58 104.13,99.04C104.33,99.51 104.44,100.02 104.44,100.54ZM103.78,100.54C103.78,98.77 102.34,97.33 100.57,97.33C98.8,97.33 97.36,98.77 97.36,100.54C97.36,102.31 98.8,103.75 100.57,103.75C102.34,103.75 103.78,102.31 103.78,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M100.57,70.97C100.34,70.97 100.15,71.16 100.15,71.38V75.53C100.15,75.76 100.34,75.94 100.57,75.94C100.79,75.94 100.98,75.76 100.98,75.53V71.38C100.98,71.15 100.79,70.97 100.57,70.97Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M101.86,72.14C101.63,72.14 101.44,72.33 101.44,72.55V74.36C101.44,74.59 101.63,74.77 101.86,74.77C102.08,74.77 102.27,74.59 102.27,74.36V72.55C102.27,72.32 102.08,72.14 101.86,72.14Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M99.28,74.78C99.51,74.78 99.69,74.59 99.69,74.37V72.55C99.69,72.33 99.51,72.14 99.28,72.14C99.05,72.14 98.87,72.33 98.87,72.55V74.37C98.87,74.59 99.05,74.78 99.28,74.78Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M100.57,45.18C100.34,45.18 100.15,45.37 100.15,45.59V49.74C100.15,49.97 100.34,50.15 100.57,50.15C100.79,50.15 100.98,49.97 100.98,49.74V45.59C100.98,45.36 100.79,45.18 100.57,45.18Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M101.86,46.35C101.63,46.35 101.44,46.53 101.44,46.76V48.57C101.44,48.8 101.63,48.98 101.86,48.98C102.08,48.98 102.27,48.79 102.27,48.57V46.76C102.27,46.53 102.08,46.35 101.86,46.35Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M104.44,47.67C104.44,48.19 104.33,48.69 104.13,49.17C103.94,49.63 103.65,50.04 103.3,50.4C102.95,50.75 102.53,51.03 102.07,51.23C101.59,51.43 101.09,51.53 100.57,51.53C100.04,51.53 99.54,51.43 99.06,51.23C98.6,51.03 98.19,50.75 97.83,50.4C97.48,50.04 97.2,49.63 97,49.17C96.8,48.69 96.7,48.19 96.7,47.67C96.7,47.14 96.8,46.64 97,46.16C97.2,45.7 97.48,45.29 97.83,44.93C98.19,44.58 98.6,44.3 99.06,44.1C99.54,43.9 100.04,43.8 100.57,43.8C101.09,43.8 101.59,43.9 102.07,44.1C102.53,44.3 102.95,44.58 103.3,44.93C103.65,45.29 103.93,45.7 104.13,46.16C104.33,46.64 104.44,47.14 104.44,47.67ZM103.78,47.67C103.78,45.89 102.34,44.46 100.57,44.46C98.8,44.46 97.36,45.89 97.36,47.67C97.36,49.44 98.8,50.87 100.57,50.87C102.34,50.87 103.78,49.44 103.78,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M104.44,73.46C104.44,73.98 104.33,74.49 104.13,74.96C103.94,75.42 103.65,75.84 103.3,76.19C102.95,76.55 102.53,76.83 102.07,77.02C101.59,77.22 101.09,77.33 100.57,77.33C100.04,77.33 99.54,77.23 99.06,77.02C98.6,76.83 98.19,76.55 97.83,76.19C97.48,75.84 97.2,75.42 97,74.96C96.8,74.49 96.7,73.98 96.7,73.46C96.7,72.94 96.8,72.43 97,71.95C97.2,71.49 97.48,71.08 97.83,70.72C98.19,70.37 98.6,70.09 99.06,69.89C99.54,69.69 100.04,69.59 100.57,69.59C101.09,69.59 101.59,69.69 102.07,69.89C102.53,70.09 102.95,70.37 103.3,70.72C103.65,71.08 103.93,71.49 104.13,71.95C104.33,72.43 104.44,72.94 104.44,73.46ZM103.78,73.46C103.78,71.68 102.34,70.25 100.57,70.25C98.8,70.25 97.36,71.69 97.36,73.46C97.36,75.23 98.8,76.67 100.57,76.67C102.34,76.67 103.78,75.23 103.78,73.46Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M99.28,48.98C99.51,48.98 99.69,48.8 99.69,48.57V46.76C99.69,46.53 99.51,46.35 99.28,46.35C99.05,46.35 98.87,46.54 98.87,46.76V48.57C98.87,48.8 99.05,48.98 99.28,48.98Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M149.56,-8.78H96.68V44.09H149.56V-8.78Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M149.56,-8.78H96.68V44.09H149.56V-8.78Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M104.44,47.67C104.44,48.19 104.33,48.69 104.13,49.17C103.94,49.63 103.65,50.04 103.3,50.4C102.95,50.75 102.53,51.03 102.07,51.23C101.59,51.43 101.09,51.53 100.57,51.53C100.04,51.53 99.54,51.43 99.06,51.23C98.6,51.03 98.19,50.75 97.83,50.4C97.48,50.04 97.2,49.63 97,49.17C96.8,48.69 96.7,48.19 96.7,47.67C96.7,47.14 96.8,46.64 97,46.16C97.2,45.7 97.48,45.29 97.83,44.93C98.19,44.58 98.6,44.3 99.06,44.1C99.54,43.9 100.04,43.8 100.57,43.8C101.09,43.8 101.59,43.9 102.07,44.1C102.53,44.3 102.95,44.58 103.3,44.93C103.65,45.29 103.93,45.7 104.13,46.16C104.33,46.64 104.44,47.14 104.44,47.67ZM103.78,47.67C103.78,45.89 102.34,44.46 100.57,44.46C98.8,44.46 97.36,45.89 97.36,47.67C97.36,49.44 98.8,50.87 100.57,50.87C102.34,50.87 103.78,49.44 103.78,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M100.57,18.1C100.34,18.1 100.15,18.28 100.15,18.51V22.66C100.15,22.89 100.34,23.07 100.57,23.07C100.79,23.07 100.98,22.88 100.98,22.66V18.51C100.98,18.28 100.79,18.1 100.57,18.1Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M101.86,19.27C101.63,19.27 101.44,19.45 101.44,19.68V21.49C101.44,21.72 101.63,21.9 101.86,21.9C102.08,21.9 102.27,21.71 102.27,21.49V19.68C102.27,19.45 102.08,19.27 101.86,19.27Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M99.28,21.9C99.51,21.9 99.69,21.71 99.69,21.49V19.68C99.69,19.45 99.51,19.27 99.28,19.27C99.05,19.27 98.87,19.45 98.87,19.68V21.49C98.87,21.72 99.05,21.9 99.28,21.9Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M104.44,20.58C104.44,21.1 104.33,21.61 104.13,22.09C103.94,22.55 103.65,22.96 103.3,23.32C102.95,23.67 102.53,23.95 102.07,24.15C101.59,24.35 101.09,24.45 100.57,24.45C100.04,24.45 99.54,24.35 99.06,24.15C98.6,23.95 98.19,23.67 97.83,23.32C97.48,22.96 97.2,22.55 97,22.09C96.8,21.61 96.7,21.1 96.7,20.58C96.7,20.06 96.8,19.55 97,19.08C97.2,18.62 97.48,18.2 97.83,17.85C98.19,17.49 98.6,17.22 99.06,17.02C99.54,16.82 100.04,16.71 100.57,16.71C101.09,16.71 101.59,16.81 102.07,17.02C102.53,17.21 102.95,17.49 103.3,17.85C103.65,18.2 103.93,18.62 104.13,19.08C104.33,19.55 104.44,20.06 104.44,20.58ZM103.78,20.58C103.78,18.81 102.34,17.37 100.57,17.37C98.8,17.37 97.36,18.81 97.36,20.58C97.36,22.35 98.8,23.79 100.57,23.79C102.34,23.79 103.78,22.35 103.78,20.58Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M96.68,96.97H43.8V149.85H96.68V96.97Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M96.68,96.97H43.8V149.85H96.68V96.97Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M73.51,98.07C72.15,98.07 71.04,99.18 71.04,100.54C71.04,101.91 72.15,103.01 73.51,103.01C74.87,103.01 75.98,101.91 75.98,100.54C75.98,99.18 74.87,98.07 73.51,98.07Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.38,100.54C77.38,101.06 77.28,101.57 77.07,102.05C76.88,102.51 76.6,102.92 76.24,103.28C75.89,103.63 75.48,103.91 75.02,104.11C74.54,104.31 74.03,104.41 73.51,104.41C72.99,104.41 72.48,104.31 72.01,104.11C71.55,103.91 71.13,103.63 70.78,103.28C70.42,102.92 70.14,102.51 69.95,102.05C69.75,101.57 69.64,101.06 69.64,100.54C69.64,100.02 69.74,99.51 69.95,99.04C70.14,98.58 70.42,98.16 70.78,97.81C71.13,97.45 71.55,97.18 72.01,96.98C72.48,96.78 72.99,96.67 73.51,96.67C74.03,96.67 74.54,96.77 75.02,96.98C75.48,97.17 75.89,97.45 76.24,97.81C76.6,98.16 76.88,98.58 77.07,99.04C77.28,99.51 77.38,100.02 77.38,100.54ZM76.72,100.54C76.72,98.77 75.28,97.33 73.51,97.33C71.74,97.33 70.3,98.77 70.3,100.54C70.3,102.31 71.74,103.75 73.51,103.75C75.28,103.75 76.72,102.31 76.72,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M47.69,98.05C47.46,98.05 47.28,98.24 47.28,98.47V102.61C47.28,102.84 47.46,103.03 47.69,103.03C47.92,103.03 48.11,102.84 48.11,102.61V98.47C48.11,98.24 47.92,98.05 47.69,98.05Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M48.98,99.22C48.75,99.22 48.57,99.41 48.57,99.63V101.45C48.57,101.67 48.75,101.86 48.98,101.86C49.21,101.86 49.4,101.67 49.4,101.45V99.63C49.4,99.41 49.21,99.22 48.98,99.22Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M51.56,100.54C51.56,101.06 51.46,101.57 51.26,102.05C51.06,102.51 50.78,102.92 50.43,103.28C50.07,103.63 49.66,103.91 49.2,104.11C48.72,104.31 48.21,104.41 47.69,104.41C47.17,104.41 46.67,104.31 46.19,104.11C45.73,103.91 45.31,103.63 44.96,103.28C44.6,102.92 44.33,102.51 44.13,102.05C43.93,101.57 43.82,101.06 43.82,100.54C43.82,100.02 43.93,99.51 44.13,99.04C44.32,98.58 44.6,98.16 44.96,97.81C45.31,97.45 45.73,97.18 46.19,96.98C46.67,96.78 47.17,96.67 47.69,96.67C48.21,96.67 48.72,96.77 49.2,96.98C49.66,97.17 50.07,97.45 50.43,97.81C50.78,98.16 51.06,98.58 51.26,99.04C51.46,99.51 51.56,100.02 51.56,100.54ZM50.9,100.54C50.9,98.77 49.46,97.33 47.69,97.33C45.92,97.33 44.48,98.77 44.48,100.54C44.48,102.31 45.92,103.75 47.69,103.75C49.46,103.75 50.9,102.31 50.9,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M46.4,101.86C46.63,101.86 46.82,101.67 46.82,101.45V99.64C46.82,99.41 46.63,99.23 46.4,99.23C46.17,99.23 45.99,99.41 45.99,99.64V101.45C45.99,101.68 46.17,101.86 46.4,101.86Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M96.68,44.09H43.8V96.97H96.68V44.09Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M96.68,44.09H43.8V96.97H96.68V44.09Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.38,100.54C77.38,101.06 77.28,101.57 77.07,102.05C76.88,102.51 76.6,102.92 76.24,103.28C75.89,103.63 75.48,103.91 75.02,104.11C74.54,104.31 74.03,104.41 73.51,104.41C72.99,104.41 72.48,104.31 72.01,104.11C71.55,103.91 71.13,103.63 70.78,103.28C70.42,102.92 70.14,102.51 69.95,102.05C69.75,101.57 69.64,101.06 69.64,100.54C69.64,100.02 69.74,99.51 69.95,99.04C70.14,98.58 70.42,98.16 70.78,97.81C71.13,97.45 71.55,97.18 72.01,96.98C72.48,96.78 72.99,96.67 73.51,96.67C74.03,96.67 74.54,96.77 75.02,96.98C75.48,97.17 75.89,97.45 76.24,97.81C76.6,98.16 76.88,98.58 77.07,99.04C77.28,99.51 77.38,100.02 77.38,100.54ZM76.72,100.54C76.72,98.77 75.28,97.33 73.51,97.33C71.74,97.33 70.3,98.77 70.3,100.54C70.3,102.31 71.74,103.75 73.51,103.75C75.28,103.75 76.72,102.31 76.72,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M51.56,100.54C51.56,101.06 51.46,101.57 51.26,102.05C51.06,102.51 50.78,102.92 50.43,103.28C50.07,103.63 49.66,103.91 49.2,104.11C48.72,104.31 48.21,104.41 47.69,104.41C47.17,104.41 46.67,104.31 46.19,104.11C45.73,103.91 45.31,103.63 44.96,103.28C44.6,102.92 44.33,102.51 44.13,102.05C43.93,101.57 43.82,101.06 43.82,100.54C43.82,100.02 43.93,99.51 44.13,99.04C44.32,98.58 44.6,98.16 44.96,97.81C45.31,97.45 45.73,97.18 46.19,96.98C46.67,96.78 47.17,96.67 47.69,96.67C48.21,96.67 48.72,96.77 49.2,96.98C49.66,97.17 50.07,97.45 50.43,97.81C50.78,98.16 51.06,98.58 51.26,99.04C51.46,99.51 51.56,100.02 51.56,100.54ZM50.9,100.54C50.9,98.77 49.46,97.33 47.69,97.33C45.92,97.33 44.48,98.77 44.48,100.54C44.48,102.31 45.92,103.75 47.69,103.75C49.46,103.75 50.9,102.31 50.9,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M47.69,70.97C47.46,70.97 47.28,71.16 47.28,71.38V75.53C47.28,75.76 47.46,75.94 47.69,75.94C47.92,75.94 48.11,75.76 48.11,75.53V71.38C48.11,71.15 47.92,70.97 47.69,70.97Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M48.98,72.14C48.75,72.14 48.57,72.33 48.57,72.55V74.36C48.57,74.59 48.75,74.77 48.98,74.77C49.21,74.77 49.4,74.59 49.4,74.36V72.55C49.4,72.32 49.21,72.14 48.98,72.14Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M46.4,74.78C46.63,74.78 46.82,74.59 46.82,74.37V72.55C46.82,72.33 46.63,72.14 46.4,72.14C46.17,72.14 45.99,72.33 45.99,72.55V74.37C45.99,74.59 46.17,74.78 46.4,74.78Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M73.51,45.2C72.15,45.2 71.04,46.3 71.04,47.67C71.04,49.03 72.15,50.13 73.51,50.13C74.87,50.13 75.98,49.03 75.98,47.67C75.98,46.3 74.87,45.2 73.51,45.2Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.38,47.67C77.38,48.19 77.28,48.69 77.07,49.17C76.88,49.63 76.6,50.04 76.24,50.4C75.89,50.75 75.48,51.03 75.02,51.23C74.54,51.43 74.03,51.53 73.51,51.53C72.99,51.53 72.48,51.43 72.01,51.23C71.55,51.03 71.13,50.75 70.78,50.4C70.42,50.04 70.14,49.63 69.95,49.17C69.75,48.69 69.64,48.19 69.64,47.67C69.64,47.14 69.74,46.64 69.95,46.16C70.14,45.7 70.42,45.29 70.78,44.93C71.13,44.58 71.55,44.3 72.01,44.1C72.48,43.9 72.99,43.8 73.51,43.8C74.03,43.8 74.54,43.9 75.02,44.1C75.48,44.3 75.89,44.58 76.24,44.93C76.6,45.29 76.88,45.7 77.07,46.16C77.28,46.64 77.38,47.14 77.38,47.67ZM76.72,47.67C76.72,45.89 75.28,44.46 73.51,44.46C71.74,44.46 70.3,45.89 70.3,47.67C70.3,49.44 71.74,50.87 73.51,50.87C75.28,50.87 76.72,49.44 76.72,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M90.28,60.56C90.28,61.08 90.17,61.59 89.97,62.07C89.78,62.53 89.5,62.94 89.14,63.3C88.79,63.65 88.37,63.93 87.91,64.13C87.43,64.33 86.93,64.43 86.41,64.43C85.88,64.43 85.38,64.33 84.9,64.13C84.44,63.93 84.03,63.65 83.67,63.3C83.32,62.94 83.04,62.53 82.84,62.07C82.64,61.59 82.54,61.08 82.54,60.56C82.54,60.04 82.64,59.54 82.84,59.06C83.04,58.6 83.32,58.18 83.67,57.83C84.03,57.47 84.44,57.2 84.9,57C85.38,56.8 85.88,56.69 86.41,56.69C86.93,56.69 87.43,56.8 87.91,57C88.37,57.19 88.79,57.47 89.14,57.83C89.5,58.18 89.77,58.6 89.97,59.06C90.17,59.54 90.28,60.04 90.28,60.56ZM89.62,60.56C89.62,58.79 88.18,57.35 86.41,57.35C84.64,57.35 83.2,58.79 83.2,60.56C83.2,62.33 84.64,63.77 86.41,63.77C88.18,63.77 89.62,62.33 89.62,60.56Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M87.38,59.95C87.03,59.95 86.76,60.22 86.76,60.56C86.76,60.9 87.04,61.18 87.38,61.18C87.71,61.18 87.99,60.9 87.99,60.56C87.99,60.22 87.71,59.95 87.38,59.95Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,58.69C85.59,58.69 85.32,58.97 85.32,59.31C85.32,59.65 85.59,59.92 85.93,59.92C86.27,59.92 86.55,59.65 86.55,59.31C86.55,58.97 86.27,58.69 85.93,58.69Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,61.2C85.59,61.2 85.32,61.48 85.32,61.82C85.32,62.16 85.59,62.43 85.93,62.43C86.27,62.43 86.55,62.16 86.55,61.82C86.55,61.48 86.27,61.2 85.93,61.2Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M73.51,70.99C72.15,70.99 71.04,72.09 71.04,73.46C71.04,74.82 72.15,75.93 73.51,75.93C74.87,75.93 75.98,74.82 75.98,73.46C75.98,72.09 74.87,70.99 73.51,70.99Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.38,73.46C77.38,73.98 77.28,74.49 77.07,74.96C76.88,75.42 76.6,75.84 76.24,76.19C75.89,76.55 75.48,76.83 75.02,77.02C74.54,77.22 74.03,77.33 73.51,77.33C72.99,77.33 72.48,77.23 72.01,77.02C71.55,76.83 71.13,76.55 70.78,76.19C70.42,75.84 70.14,75.42 69.95,74.96C69.75,74.49 69.64,73.98 69.64,73.46C69.64,72.94 69.74,72.43 69.95,71.95C70.14,71.49 70.42,71.08 70.78,70.72C71.13,70.37 71.55,70.09 72.01,69.89C72.48,69.69 72.99,69.59 73.51,69.59C74.03,69.59 74.54,69.69 75.02,69.89C75.48,70.09 75.89,70.37 76.24,70.72C76.6,71.08 76.88,71.49 77.07,71.95C77.28,72.43 77.38,72.94 77.38,73.46ZM76.72,73.46C76.72,71.68 75.28,70.25 73.51,70.25C71.74,70.25 70.3,71.69 70.3,73.46C70.3,75.23 71.74,76.67 73.51,76.67C75.28,76.67 76.72,75.23 76.72,73.46Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M90.28,86.36C90.28,86.88 90.17,87.38 89.97,87.86C89.78,88.32 89.5,88.74 89.14,89.09C88.79,89.45 88.37,89.72 87.91,89.92C87.43,90.12 86.93,90.23 86.41,90.23C85.88,90.23 85.38,90.12 84.9,89.92C84.44,89.73 84.03,89.45 83.67,89.09C83.32,88.74 83.04,88.32 82.84,87.86C82.64,87.38 82.54,86.88 82.54,86.36C82.54,85.84 82.64,85.33 82.84,84.85C83.04,84.39 83.32,83.98 83.67,83.62C84.03,83.27 84.44,82.99 84.9,82.79C85.38,82.59 85.88,82.49 86.41,82.49C86.93,82.49 87.43,82.59 87.91,82.79C88.37,82.99 88.79,83.27 89.14,83.62C89.5,83.98 89.77,84.39 89.97,84.85C90.17,85.33 90.28,85.84 90.28,86.36ZM89.62,86.36C89.62,84.58 88.18,83.15 86.41,83.15C84.64,83.15 83.2,84.59 83.2,86.36C83.2,88.13 84.64,89.57 86.41,89.57C88.18,89.57 89.62,88.13 89.62,86.36Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M87.38,85.74C87.03,85.74 86.76,86.01 86.76,86.35C86.76,86.69 87.04,86.97 87.38,86.97C87.71,86.97 87.99,86.69 87.99,86.35C87.99,86.01 87.71,85.74 87.38,85.74Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,84.48C85.59,84.48 85.32,84.76 85.32,85.1C85.32,85.44 85.59,85.72 85.93,85.72C86.27,85.72 86.55,85.44 86.55,85.1C86.55,84.76 86.27,84.48 85.93,84.48Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,86.99C85.59,86.99 85.32,87.27 85.32,87.61C85.32,87.95 85.59,88.23 85.93,88.23C86.27,88.23 86.55,87.95 86.55,87.61C86.55,87.27 86.27,86.99 85.93,86.99Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M47.69,45.18C47.46,45.18 47.28,45.37 47.28,45.59V49.74C47.28,49.97 47.46,50.15 47.69,50.15C47.92,50.15 48.11,49.97 48.11,49.74V45.59C48.11,45.36 47.92,45.18 47.69,45.18Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M48.98,46.35C48.75,46.35 48.57,46.53 48.57,46.76V48.57C48.57,48.8 48.75,48.98 48.98,48.98C49.21,48.98 49.4,48.79 49.4,48.57V46.76C49.4,46.53 49.21,46.35 48.98,46.35Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M51.56,47.67C51.56,48.19 51.46,48.69 51.26,49.17C51.06,49.63 50.78,50.04 50.43,50.4C50.07,50.75 49.66,51.03 49.2,51.23C48.72,51.43 48.21,51.53 47.69,51.53C47.17,51.53 46.67,51.43 46.19,51.23C45.73,51.03 45.31,50.75 44.96,50.4C44.6,50.04 44.33,49.63 44.13,49.17C43.93,48.69 43.82,48.19 43.82,47.67C43.82,47.14 43.93,46.64 44.13,46.16C44.32,45.7 44.6,45.29 44.96,44.93C45.31,44.58 45.73,44.3 46.19,44.1C46.67,43.9 47.17,43.8 47.69,43.8C48.21,43.8 48.72,43.9 49.2,44.1C49.66,44.3 50.07,44.58 50.43,44.93C50.78,45.29 51.06,45.7 51.26,46.16C51.46,46.64 51.56,47.14 51.56,47.67ZM50.9,47.67C50.9,45.89 49.46,44.46 47.69,44.46C45.92,44.46 44.48,45.89 44.48,47.67C44.48,49.44 45.92,50.87 47.69,50.87C49.46,50.87 50.9,49.44 50.9,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M63.06,60.56C63.06,61.93 61.96,63.03 60.59,63.03C59.23,63.03 58.12,61.93 58.12,60.56C58.12,59.2 59.23,58.09 60.59,58.09C61.96,58.09 63.06,59.2 63.06,60.56ZM60.18,62.16V58.97C59.48,59.15 58.94,59.8 58.94,60.56C58.94,61.33 59.48,61.97 60.18,62.16M62.24,60.56C62.24,59.8 61.7,59.15 61,58.97V62.16C61.7,61.97 62.24,61.33 62.24,60.56"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M64.46,60.56C64.46,61.08 64.36,61.59 64.15,62.07C63.96,62.53 63.68,62.94 63.32,63.3C62.97,63.65 62.55,63.93 62.09,64.13C61.62,64.33 61.11,64.43 60.59,64.43C60.07,64.43 59.56,64.33 59.08,64.13C58.62,63.93 58.21,63.65 57.85,63.3C57.5,62.94 57.22,62.53 57.02,62.07C56.82,61.59 56.72,61.08 56.72,60.56C56.72,60.04 56.82,59.54 57.02,59.06C57.22,58.6 57.5,58.18 57.85,57.83C58.21,57.47 58.62,57.2 59.08,57C59.56,56.8 60.07,56.69 60.59,56.69C61.11,56.69 61.62,56.8 62.09,57C62.55,57.19 62.97,57.47 63.32,57.83C63.68,58.18 63.95,58.6 64.15,59.06C64.35,59.54 64.46,60.04 64.46,60.56ZM63.8,60.56C63.8,58.79 62.36,57.35 60.59,57.35C58.82,57.35 57.38,58.79 57.38,60.56C57.38,62.33 58.82,63.77 60.59,63.77C62.36,63.77 63.8,62.33 63.8,60.56Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M51.56,73.46C51.56,73.98 51.46,74.49 51.26,74.96C51.06,75.42 50.78,75.84 50.43,76.19C50.07,76.55 49.66,76.83 49.2,77.02C48.72,77.22 48.21,77.33 47.69,77.33C47.17,77.33 46.67,77.23 46.19,77.02C45.73,76.83 45.31,76.55 44.96,76.19C44.6,75.84 44.33,75.42 44.13,74.96C43.93,74.49 43.82,73.98 43.82,73.46C43.82,72.94 43.93,72.43 44.13,71.95C44.32,71.49 44.6,71.08 44.96,70.72C45.31,70.37 45.73,70.09 46.19,69.89C46.67,69.69 47.17,69.59 47.69,69.59C48.21,69.59 48.72,69.69 49.2,69.89C49.66,70.09 50.07,70.37 50.43,70.72C50.78,71.08 51.06,71.49 51.26,71.95C51.46,72.43 51.56,72.94 51.56,73.46ZM50.9,73.46C50.9,71.68 49.46,70.25 47.69,70.25C45.92,70.25 44.48,71.69 44.48,73.46C44.48,75.23 45.92,76.67 47.69,76.67C49.46,76.67 50.9,75.23 50.9,73.46Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M63.06,86.36C63.06,87.72 61.96,88.83 60.59,88.83C59.23,88.83 58.12,87.72 58.12,86.36C58.12,84.99 59.23,83.89 60.59,83.89C61.96,83.89 63.06,84.99 63.06,86.36ZM60.18,87.95V84.76C59.48,84.94 58.94,85.59 58.94,86.36C58.94,87.12 59.48,87.77 60.18,87.95M62.24,86.36C62.24,85.59 61.7,84.95 61,84.76V87.95C61.7,87.77 62.24,87.12 62.24,86.35"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M64.46,86.36C64.46,86.88 64.36,87.38 64.15,87.86C63.96,88.32 63.68,88.74 63.32,89.09C62.97,89.45 62.55,89.72 62.09,89.92C61.62,90.12 61.11,90.23 60.59,90.23C60.07,90.23 59.56,90.12 59.08,89.92C58.62,89.73 58.21,89.45 57.85,89.09C57.5,88.74 57.22,88.32 57.02,87.86C56.82,87.38 56.72,86.88 56.72,86.36C56.72,85.84 56.82,85.33 57.02,84.85C57.22,84.39 57.5,83.98 57.85,83.62C58.21,83.27 58.62,82.99 59.08,82.79C59.56,82.59 60.07,82.49 60.59,82.49C61.11,82.49 61.62,82.59 62.09,82.79C62.55,82.99 62.97,83.27 63.32,83.62C63.68,83.98 63.95,84.39 64.15,84.85C64.35,85.33 64.46,85.84 64.46,86.36ZM63.8,86.36C63.8,84.58 62.36,83.15 60.59,83.15C58.82,83.15 57.38,84.59 57.38,86.36C57.38,88.13 58.82,89.57 60.59,89.57C62.36,89.57 63.8,88.13 63.8,86.36Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M46.4,48.98C46.63,48.98 46.82,48.8 46.82,48.57V46.76C46.82,46.53 46.63,46.35 46.4,46.35C46.17,46.35 45.99,46.54 45.99,46.76V48.57C45.99,48.8 46.17,48.98 46.4,48.98Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M96.68,-8.78H43.8V44.09H96.68V-8.78Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M96.68,-8.78H43.8V44.09H96.68V-8.78Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.38,47.67C77.38,48.19 77.28,48.69 77.07,49.17C76.88,49.63 76.6,50.04 76.24,50.4C75.89,50.75 75.48,51.03 75.02,51.23C74.54,51.43 74.03,51.53 73.51,51.53C72.99,51.53 72.48,51.43 72.01,51.23C71.55,51.03 71.13,50.75 70.78,50.4C70.42,50.04 70.14,49.63 69.95,49.17C69.75,48.69 69.64,48.19 69.64,47.67C69.64,47.14 69.74,46.64 69.95,46.16C70.14,45.7 70.42,45.29 70.78,44.93C71.13,44.58 71.55,44.3 72.01,44.1C72.48,43.9 72.99,43.8 73.51,43.8C74.03,43.8 74.54,43.9 75.02,44.1C75.48,44.3 75.89,44.58 76.24,44.93C76.6,45.29 76.88,45.7 77.07,46.16C77.28,46.64 77.38,47.14 77.38,47.67ZM76.72,47.67C76.72,45.89 75.28,44.46 73.51,44.46C71.74,44.46 70.3,45.89 70.3,47.67C70.3,49.44 71.74,50.87 73.51,50.87C75.28,50.87 76.72,49.44 76.72,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M51.56,47.67C51.56,48.19 51.46,48.69 51.26,49.17C51.06,49.63 50.78,50.04 50.43,50.4C50.07,50.75 49.66,51.03 49.2,51.23C48.72,51.43 48.21,51.53 47.69,51.53C47.17,51.53 46.67,51.43 46.19,51.23C45.73,51.03 45.31,50.75 44.96,50.4C44.6,50.04 44.33,49.63 44.13,49.17C43.93,48.69 43.82,48.19 43.82,47.67C43.82,47.14 43.93,46.64 44.13,46.16C44.32,45.7 44.6,45.29 44.96,44.93C45.31,44.58 45.73,44.3 46.19,44.1C46.67,43.9 47.17,43.8 47.69,43.8C48.21,43.8 48.72,43.9 49.2,44.1C49.66,44.3 50.07,44.58 50.43,44.93C50.78,45.29 51.06,45.7 51.26,46.16C51.46,46.64 51.56,47.14 51.56,47.67ZM50.9,47.67C50.9,45.89 49.46,44.46 47.69,44.46C45.92,44.46 44.48,45.89 44.48,47.67C44.48,49.44 45.92,50.87 47.69,50.87C49.46,50.87 50.9,49.44 50.9,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M47.69,18.1C47.46,18.1 47.28,18.28 47.28,18.51V22.66C47.28,22.89 47.46,23.07 47.69,23.07C47.92,23.07 48.11,22.88 48.11,22.66V18.51C48.11,18.28 47.92,18.1 47.69,18.1Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M48.98,19.27C48.75,19.27 48.57,19.45 48.57,19.68V21.49C48.57,21.72 48.75,21.9 48.98,21.9C49.21,21.9 49.4,21.71 49.4,21.49V19.68C49.4,19.45 49.21,19.27 48.98,19.27Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M46.4,21.9C46.63,21.9 46.82,21.71 46.82,21.49V19.68C46.82,19.45 46.63,19.27 46.4,19.27C46.17,19.27 45.99,19.45 45.99,19.68V21.49C45.99,21.72 46.17,21.9 46.4,21.9Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M90.28,7.69C90.28,8.21 90.17,8.71 89.97,9.19C89.78,9.65 89.5,10.07 89.14,10.42C88.79,10.78 88.37,11.05 87.91,11.25C87.43,11.45 86.93,11.56 86.41,11.56C85.88,11.56 85.38,11.45 84.9,11.25C84.44,11.06 84.03,10.78 83.67,10.42C83.32,10.07 83.04,9.65 82.84,9.19C82.64,8.71 82.54,8.21 82.54,7.69C82.54,7.17 82.64,6.66 82.84,6.18C83.04,5.72 83.32,5.31 83.67,4.95C84.03,4.6 84.44,4.32 84.9,4.12C85.38,3.92 85.88,3.82 86.41,3.82C86.93,3.82 87.43,3.92 87.91,4.12C88.37,4.32 88.79,4.6 89.14,4.95C89.5,5.31 89.77,5.72 89.97,6.18C90.17,6.66 90.28,7.17 90.28,7.69ZM89.62,7.69C89.62,5.91 88.18,4.48 86.41,4.48C84.64,4.48 83.2,5.92 83.2,7.69C83.2,9.46 84.64,10.9 86.41,10.9C88.18,10.9 89.62,9.46 89.62,7.69Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M87.38,7.07C87.03,7.07 86.76,7.35 86.76,7.69C86.76,8.03 87.04,8.3 87.38,8.3C87.71,8.3 87.99,8.03 87.99,7.69C87.99,7.35 87.71,7.07 87.38,7.07Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,5.81C85.59,5.81 85.32,6.09 85.32,6.43C85.32,6.77 85.59,7.05 85.93,7.05C86.27,7.05 86.55,6.77 86.55,6.43C86.55,6.09 86.27,5.81 85.93,5.81Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,8.33C85.59,8.33 85.32,8.6 85.32,8.94C85.32,9.28 85.59,9.56 85.93,9.56C86.27,9.56 86.55,9.28 86.55,8.94C86.55,8.6 86.27,8.33 85.93,8.33Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M73.51,18.11C72.15,18.11 71.04,19.22 71.04,20.58C71.04,21.95 72.15,23.05 73.51,23.05C74.87,23.05 75.98,21.95 75.98,20.58C75.98,19.22 74.87,18.11 73.51,18.11Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M77.38,20.58C77.38,21.1 77.28,21.61 77.07,22.09C76.88,22.55 76.6,22.96 76.24,23.32C75.89,23.67 75.48,23.95 75.02,24.15C74.54,24.35 74.03,24.45 73.51,24.45C72.99,24.45 72.48,24.35 72.01,24.15C71.55,23.95 71.13,23.67 70.78,23.32C70.42,22.96 70.14,22.55 69.95,22.09C69.75,21.61 69.64,21.1 69.64,20.58C69.64,20.06 69.74,19.55 69.95,19.08C70.14,18.62 70.42,18.2 70.78,17.85C71.13,17.49 71.55,17.22 72.01,17.02C72.48,16.82 72.99,16.71 73.51,16.71C74.03,16.71 74.54,16.81 75.02,17.02C75.48,17.21 75.89,17.49 76.24,17.85C76.6,18.2 76.88,18.62 77.07,19.08C77.28,19.55 77.38,20.06 77.38,20.58ZM76.72,20.58C76.72,18.81 75.28,17.37 73.51,17.37C71.74,17.37 70.3,18.81 70.3,20.58C70.3,22.35 71.74,23.79 73.51,23.79C75.28,23.79 76.72,22.35 76.72,20.58Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M90.28,33.48C90.28,34 90.17,34.51 89.97,34.99C89.78,35.45 89.5,35.86 89.14,36.21C88.79,36.57 88.37,36.85 87.91,37.04C87.43,37.24 86.93,37.35 86.41,37.35C85.88,37.35 85.38,37.25 84.9,37.04C84.44,36.85 84.03,36.57 83.67,36.21C83.32,35.86 83.04,35.45 82.84,34.99C82.64,34.51 82.54,34 82.54,33.48C82.54,32.96 82.64,32.45 82.84,31.97C83.04,31.51 83.32,31.1 83.67,30.75C84.03,30.39 84.44,30.11 84.9,29.92C85.38,29.72 85.88,29.61 86.41,29.61C86.93,29.61 87.43,29.71 87.91,29.92C88.37,30.11 88.79,30.39 89.14,30.75C89.5,31.1 89.77,31.51 89.97,31.97C90.17,32.45 90.28,32.96 90.28,33.48ZM89.62,33.48C89.62,31.71 88.18,30.27 86.41,30.27C84.64,30.27 83.2,31.71 83.2,33.48C83.2,35.25 84.64,36.69 86.41,36.69C88.18,36.69 89.62,35.25 89.62,33.48Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M87.38,32.86C87.03,32.86 86.76,33.14 86.76,33.48C86.76,33.82 87.04,34.1 87.38,34.1C87.71,34.1 87.99,33.82 87.99,33.48C87.99,33.14 87.71,32.86 87.38,32.86Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,31.61C85.59,31.61 85.32,31.88 85.32,32.22C85.32,32.56 85.59,32.84 85.93,32.84C86.27,32.84 86.55,32.56 86.55,32.22C86.55,31.88 86.27,31.61 85.93,31.61Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M85.93,34.12C85.59,34.12 85.32,34.4 85.32,34.74C85.32,35.08 85.59,35.35 85.93,35.35C86.27,35.35 86.55,35.08 86.55,34.74C86.55,34.4 86.27,34.12 85.93,34.12Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M63.06,7.69C63.06,9.05 61.96,10.16 60.59,10.16C59.23,10.16 58.12,9.05 58.12,7.69C58.12,6.32 59.23,5.22 60.59,5.22C61.96,5.22 63.06,6.32 63.06,7.69ZM60.18,9.28V6.09C59.48,6.28 58.94,6.92 58.94,7.69C58.94,8.45 59.48,9.1 60.18,9.28M62.24,7.69C62.24,6.92 61.7,6.28 61,6.09V9.28C61.7,9.1 62.24,8.45 62.24,7.68"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M64.46,7.69C64.46,8.21 64.36,8.71 64.15,9.19C63.96,9.65 63.68,10.07 63.32,10.42C62.97,10.78 62.55,11.05 62.09,11.25C61.62,11.45 61.11,11.56 60.59,11.56C60.07,11.56 59.56,11.45 59.08,11.25C58.62,11.06 58.21,10.78 57.85,10.42C57.5,10.07 57.22,9.65 57.02,9.19C56.82,8.71 56.72,8.21 56.72,7.69C56.72,7.17 56.82,6.66 57.02,6.18C57.22,5.72 57.5,5.31 57.85,4.95C58.21,4.6 58.62,4.32 59.08,4.12C59.56,3.92 60.07,3.82 60.59,3.82C61.11,3.82 61.62,3.92 62.09,4.12C62.55,4.32 62.97,4.6 63.32,4.95C63.68,5.31 63.95,5.72 64.15,6.18C64.35,6.66 64.46,7.17 64.46,7.69ZM63.8,7.69C63.8,5.91 62.36,4.48 60.59,4.48C58.82,4.48 57.38,5.92 57.38,7.69C57.38,9.46 58.82,10.9 60.59,10.9C62.36,10.9 63.8,9.46 63.8,7.69Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M51.56,20.58C51.56,21.1 51.46,21.61 51.26,22.09C51.06,22.55 50.78,22.96 50.43,23.32C50.07,23.67 49.66,23.95 49.2,24.15C48.72,24.35 48.21,24.45 47.69,24.45C47.17,24.45 46.67,24.35 46.19,24.15C45.73,23.95 45.31,23.67 44.96,23.32C44.6,22.96 44.33,22.55 44.13,22.09C43.93,21.61 43.82,21.1 43.82,20.58C43.82,20.06 43.93,19.55 44.13,19.08C44.32,18.62 44.6,18.2 44.96,17.85C45.31,17.49 45.73,17.22 46.19,17.02C46.67,16.82 47.17,16.71 47.69,16.71C48.21,16.71 48.72,16.81 49.2,17.02C49.66,17.21 50.07,17.49 50.43,17.85C50.78,18.2 51.06,18.62 51.26,19.08C51.46,19.55 51.56,20.06 51.56,20.58ZM50.9,20.58C50.9,18.81 49.46,17.37 47.69,17.37C45.92,17.37 44.48,18.81 44.48,20.58C44.48,22.35 45.92,23.79 47.69,23.79C49.46,23.79 50.9,22.35 50.9,20.58Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M63.06,33.48C63.06,34.84 61.96,35.95 60.59,35.95C59.23,35.95 58.12,34.84 58.12,33.48C58.12,32.12 59.23,31.01 60.59,31.01C61.96,31.01 63.06,32.12 63.06,33.48ZM60.18,35.08V31.89C59.48,32.07 58.94,32.72 58.94,33.48C58.94,34.25 59.48,34.89 60.18,35.08M62.24,33.48C62.24,32.71 61.7,32.07 61,31.88V35.07C61.7,34.89 62.24,34.24 62.24,33.48"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M64.46,33.48C64.46,34 64.36,34.51 64.15,34.99C63.96,35.45 63.68,35.86 63.32,36.21C62.97,36.57 62.55,36.85 62.09,37.04C61.62,37.24 61.11,37.35 60.59,37.35C60.07,37.35 59.56,37.25 59.08,37.04C58.62,36.85 58.21,36.57 57.85,36.21C57.5,35.86 57.22,35.45 57.02,34.99C56.82,34.51 56.72,34 56.72,33.48C56.72,32.96 56.82,32.45 57.02,31.97C57.22,31.51 57.5,31.1 57.85,30.75C58.21,30.39 58.62,30.11 59.08,29.92C59.56,29.72 60.07,29.61 60.59,29.61C61.11,29.61 61.62,29.71 62.09,29.92C62.55,30.11 62.97,30.39 63.32,30.75C63.68,31.1 63.95,31.51 64.15,31.97C64.35,32.45 64.46,32.96 64.46,33.48ZM63.8,33.48C63.8,31.71 62.36,30.27 60.59,30.27C58.82,30.27 57.38,31.71 57.38,33.48C57.38,35.25 58.82,36.69 60.59,36.69C62.36,36.69 63.8,35.25 63.8,33.48Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M43.81,96.97H-9.07V149.85H43.81V96.97Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M43.81,96.97H-9.07V149.85H43.81V96.97Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.63,98.07C19.27,98.07 18.17,99.18 18.17,100.54C18.17,101.91 19.27,103.01 20.63,103.01C22,103.01 23.1,101.91 23.1,100.54C23.1,99.18 22,98.07 20.63,98.07Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.5,100.54C24.5,101.06 24.4,101.57 24.2,102.05C24,102.51 23.72,102.92 23.37,103.28C23.01,103.63 22.6,103.91 22.14,104.11C21.66,104.31 21.16,104.41 20.63,104.41C20.11,104.41 19.61,104.31 19.13,104.11C18.67,103.91 18.25,103.63 17.9,103.28C17.55,102.92 17.27,102.51 17.07,102.05C16.87,101.57 16.76,101.06 16.76,100.54C16.76,100.02 16.87,99.51 17.07,99.04C17.26,98.58 17.55,98.16 17.9,97.81C18.25,97.45 18.67,97.18 19.13,96.98C19.61,96.78 20.11,96.67 20.63,96.67C21.16,96.67 21.66,96.77 22.14,96.98C22.6,97.17 23.01,97.45 23.37,97.81C23.72,98.16 24,98.58 24.2,99.04C24.4,99.51 24.5,100.02 24.5,100.54ZM23.85,100.54C23.85,98.77 22.41,97.33 20.64,97.33C18.87,97.33 17.43,98.77 17.43,100.54C17.43,102.31 18.87,103.75 20.64,103.75C22.41,103.75 23.85,102.31 23.85,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M43.81,44.09H-9.07V96.97H43.81V44.09Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M43.81,44.09H-9.07V96.97H43.81V44.09Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.5,100.54C24.5,101.06 24.4,101.57 24.2,102.05C24,102.51 23.72,102.92 23.37,103.28C23.01,103.63 22.6,103.91 22.14,104.11C21.66,104.31 21.16,104.41 20.63,104.41C20.11,104.41 19.61,104.31 19.13,104.11C18.67,103.91 18.25,103.63 17.9,103.28C17.55,102.92 17.27,102.51 17.07,102.05C16.87,101.57 16.76,101.06 16.76,100.54C16.76,100.02 16.87,99.51 17.07,99.04C17.26,98.58 17.55,98.16 17.9,97.81C18.25,97.45 18.67,97.18 19.13,96.98C19.61,96.78 20.11,96.67 20.63,96.67C21.16,96.67 21.66,96.77 22.14,96.98C22.6,97.17 23.01,97.45 23.37,97.81C23.72,98.16 24,98.58 24.2,99.04C24.4,99.51 24.5,100.02 24.5,100.54ZM23.85,100.54C23.85,98.77 22.41,97.33 20.64,97.33C18.87,97.33 17.43,98.77 17.43,100.54C17.43,102.31 18.87,103.75 20.64,103.75C22.41,103.75 23.85,102.31 23.85,100.54Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.63,45.2C19.27,45.2 18.17,46.3 18.17,47.67C18.17,49.03 19.27,50.13 20.63,50.13C22,50.13 23.1,49.03 23.1,47.67C23.1,46.3 22,45.2 20.63,45.2Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.5,47.67C24.5,48.19 24.4,48.69 24.2,49.17C24,49.63 23.72,50.04 23.37,50.4C23.01,50.75 22.6,51.03 22.14,51.23C21.66,51.43 21.16,51.53 20.63,51.53C20.11,51.53 19.61,51.43 19.13,51.23C18.67,51.03 18.25,50.75 17.9,50.4C17.55,50.04 17.27,49.63 17.07,49.17C16.87,48.69 16.76,48.19 16.76,47.67C16.76,47.14 16.87,46.64 17.07,46.16C17.26,45.7 17.55,45.29 17.9,44.93C18.25,44.58 18.67,44.3 19.13,44.1C19.61,43.9 20.11,43.8 20.63,43.8C21.16,43.8 21.66,43.9 22.14,44.1C22.6,44.3 23.01,44.58 23.37,44.93C23.72,45.29 24,45.7 24.2,46.16C24.4,46.64 24.5,47.14 24.5,47.67ZM23.85,47.67C23.85,45.89 22.41,44.46 20.64,44.46C18.87,44.46 17.43,45.89 17.43,47.67C17.43,49.44 18.87,50.87 20.64,50.87C22.41,50.87 23.85,49.44 23.85,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M37.4,60.56C37.4,61.08 37.3,61.59 37.1,62.07C36.9,62.53 36.62,62.94 36.27,63.3C35.91,63.65 35.5,63.93 35.04,64.13C34.56,64.33 34.05,64.43 33.53,64.43C33.01,64.43 32.5,64.33 32.03,64.13C31.57,63.93 31.15,63.65 30.8,63.3C30.44,62.94 30.17,62.53 29.97,62.07C29.77,61.59 29.66,61.08 29.66,60.56C29.66,60.04 29.76,59.54 29.97,59.06C30.16,58.6 30.44,58.18 30.8,57.83C31.15,57.47 31.57,57.2 32.03,57C32.5,56.8 33.01,56.69 33.53,56.69C34.05,56.69 34.56,56.8 35.04,57C35.5,57.19 35.91,57.47 36.27,57.83C36.62,58.18 36.9,58.6 37.1,59.06C37.3,59.54 37.4,60.04 37.4,60.56ZM36.74,60.56C36.74,58.79 35.3,57.35 33.53,57.35C31.76,57.35 30.32,58.79 30.32,60.56C30.32,62.33 31.76,63.77 33.53,63.77C35.3,63.77 36.74,62.33 36.74,60.56Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M34.5,59.95C34.16,59.95 33.88,60.22 33.88,60.56C33.88,60.9 34.16,61.18 34.5,61.18C34.84,61.18 35.12,60.9 35.12,60.56C35.12,60.22 34.84,59.95 34.5,59.95Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,58.69C32.71,58.69 32.44,58.97 32.44,59.31C32.44,59.65 32.72,59.92 33.06,59.92C33.4,59.92 33.67,59.65 33.67,59.31C33.67,58.97 33.4,58.69 33.06,58.69Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,61.2C32.71,61.2 32.44,61.48 32.44,61.82C32.44,62.16 32.72,62.43 33.06,62.43C33.4,62.43 33.67,62.16 33.67,61.82C33.67,61.48 33.4,61.2 33.06,61.2Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.63,70.99C19.27,70.99 18.17,72.09 18.17,73.46C18.17,74.82 19.27,75.93 20.63,75.93C22,75.93 23.1,74.82 23.1,73.46C23.1,72.09 22,70.99 20.63,70.99Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.5,73.46C24.5,73.98 24.4,74.49 24.2,74.96C24,75.42 23.72,75.84 23.37,76.19C23.01,76.55 22.6,76.83 22.14,77.02C21.66,77.22 21.16,77.33 20.63,77.33C20.11,77.33 19.61,77.23 19.13,77.02C18.67,76.83 18.25,76.55 17.9,76.19C17.55,75.84 17.27,75.42 17.07,74.96C16.87,74.49 16.76,73.98 16.76,73.46C16.76,72.94 16.87,72.43 17.07,71.95C17.26,71.49 17.55,71.08 17.9,70.72C18.25,70.37 18.67,70.09 19.13,69.89C19.61,69.69 20.11,69.59 20.63,69.59C21.16,69.59 21.66,69.69 22.14,69.89C22.6,70.09 23.01,70.37 23.37,70.72C23.72,71.08 24,71.49 24.2,71.95C24.4,72.43 24.5,72.94 24.5,73.46ZM23.85,73.46C23.85,71.68 22.41,70.25 20.64,70.25C18.87,70.25 17.43,71.69 17.43,73.46C17.43,75.23 18.87,76.67 20.64,76.67C22.41,76.67 23.85,75.23 23.85,73.46Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M37.4,86.36C37.4,86.88 37.3,87.38 37.1,87.86C36.9,88.32 36.62,88.74 36.27,89.09C35.91,89.45 35.5,89.72 35.04,89.92C34.56,90.12 34.05,90.23 33.53,90.23C33.01,90.23 32.5,90.12 32.03,89.92C31.57,89.73 31.15,89.45 30.8,89.09C30.44,88.74 30.17,88.32 29.97,87.86C29.77,87.38 29.66,86.88 29.66,86.36C29.66,85.84 29.76,85.33 29.97,84.85C30.16,84.39 30.44,83.98 30.8,83.62C31.15,83.27 31.57,82.99 32.03,82.79C32.5,82.59 33.01,82.49 33.53,82.49C34.05,82.49 34.56,82.59 35.04,82.79C35.5,82.99 35.91,83.27 36.27,83.62C36.62,83.98 36.9,84.39 37.1,84.85C37.3,85.33 37.4,85.84 37.4,86.36ZM36.74,86.36C36.74,84.58 35.3,83.15 33.53,83.15C31.76,83.15 30.32,84.59 30.32,86.36C30.32,88.13 31.76,89.57 33.53,89.57C35.3,89.57 36.74,88.13 36.74,86.36Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M34.5,85.74C34.16,85.74 33.88,86.01 33.88,86.35C33.88,86.69 34.16,86.97 34.5,86.97C34.84,86.97 35.12,86.69 35.12,86.35C35.12,86.01 34.84,85.74 34.5,85.74Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,84.48C32.71,84.48 32.44,84.76 32.44,85.1C32.44,85.44 32.72,85.72 33.06,85.72C33.4,85.72 33.67,85.44 33.67,85.1C33.67,84.76 33.4,84.48 33.06,84.48Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,86.99C32.71,86.99 32.44,87.27 32.44,87.61C32.44,87.95 32.72,88.23 33.06,88.23C33.4,88.23 33.67,87.95 33.67,87.61C33.67,87.27 33.4,86.99 33.06,86.99Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.18,60.56C10.18,61.93 9.08,63.03 7.71,63.03C6.35,63.03 5.25,61.93 5.25,60.56C5.25,59.2 6.35,58.09 7.71,58.09C9.08,58.09 10.18,59.2 10.18,60.56ZM7.3,62.16V58.97C6.6,59.15 6.07,59.8 6.07,60.56C6.07,61.33 6.6,61.97 7.3,62.16M9.36,60.56C9.36,59.8 8.83,59.15 8.13,58.97V62.16C8.83,61.97 9.36,61.33 9.36,60.56"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.58,60.56C11.58,61.08 11.48,61.59 11.28,62.07C11.08,62.53 10.8,62.94 10.45,63.3C10.09,63.65 9.68,63.93 9.22,64.13C8.74,64.33 8.23,64.43 7.71,64.43C7.19,64.43 6.68,64.33 6.21,64.13C5.75,63.93 5.33,63.65 4.98,63.3C4.62,62.94 4.34,62.53 4.15,62.07C3.95,61.59 3.84,61.08 3.84,60.56C3.84,60.04 3.94,59.54 4.15,59.06C4.34,58.6 4.62,58.18 4.98,57.83C5.33,57.47 5.75,57.2 6.21,57C6.68,56.8 7.19,56.69 7.71,56.69C8.23,56.69 8.74,56.8 9.22,57C9.68,57.19 10.09,57.47 10.45,57.83C10.8,58.18 11.08,58.6 11.28,59.06C11.48,59.54 11.58,60.04 11.58,60.56ZM10.92,60.56C10.92,58.79 9.49,57.35 7.71,57.35C5.94,57.35 4.51,58.79 4.51,60.56C4.51,62.33 5.94,63.77 7.71,63.77C9.49,63.77 10.92,62.33 10.92,60.56Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.18,86.36C10.18,87.72 9.08,88.83 7.71,88.83C6.35,88.83 5.25,87.72 5.25,86.36C5.25,84.99 6.35,83.89 7.71,83.89C9.08,83.89 10.18,84.99 10.18,86.36ZM7.3,87.95V84.76C6.6,84.94 6.07,85.59 6.07,86.36C6.07,87.12 6.6,87.77 7.3,87.95M9.36,86.36C9.36,85.59 8.83,84.95 8.13,84.76V87.95C8.83,87.77 9.36,87.12 9.36,86.35"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.58,86.36C11.58,86.88 11.48,87.38 11.28,87.86C11.08,88.32 10.8,88.74 10.45,89.09C10.09,89.45 9.68,89.72 9.22,89.92C8.74,90.12 8.23,90.23 7.71,90.23C7.19,90.23 6.68,90.12 6.21,89.92C5.75,89.73 5.33,89.45 4.98,89.09C4.62,88.74 4.34,88.32 4.15,87.86C3.95,87.38 3.84,86.88 3.84,86.36C3.84,85.84 3.94,85.33 4.15,84.85C4.34,84.39 4.62,83.98 4.98,83.62C5.33,83.27 5.75,82.99 6.21,82.79C6.68,82.59 7.19,82.49 7.71,82.49C8.23,82.49 8.74,82.59 9.22,82.79C9.68,82.99 10.09,83.27 10.45,83.62C10.8,83.98 11.08,84.39 11.28,84.85C11.48,85.33 11.58,85.84 11.58,86.36ZM10.92,86.36C10.92,84.58 9.49,83.15 7.71,83.15C5.94,83.15 4.51,84.59 4.51,86.36C4.51,88.13 5.94,89.57 7.71,89.57C9.49,89.57 10.92,88.13 10.92,86.36Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M43.81,-8.78H-9.07V44.09H43.81V-8.78Z"/>
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M43.81,-8.78H-9.07V44.09H43.81V-8.78Z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.5,47.67C24.5,48.19 24.4,48.69 24.2,49.17C24,49.63 23.72,50.04 23.37,50.4C23.01,50.75 22.6,51.03 22.14,51.23C21.66,51.43 21.16,51.53 20.63,51.53C20.11,51.53 19.61,51.43 19.13,51.23C18.67,51.03 18.25,50.75 17.9,50.4C17.55,50.04 17.27,49.63 17.07,49.17C16.87,48.69 16.76,48.19 16.76,47.67C16.76,47.14 16.87,46.64 17.07,46.16C17.26,45.7 17.55,45.29 17.9,44.93C18.25,44.58 18.67,44.3 19.13,44.1C19.61,43.9 20.11,43.8 20.63,43.8C21.16,43.8 21.66,43.9 22.14,44.1C22.6,44.3 23.01,44.58 23.37,44.93C23.72,45.29 24,45.7 24.2,46.16C24.4,46.64 24.5,47.14 24.5,47.67ZM23.85,47.67C23.85,45.89 22.41,44.46 20.64,44.46C18.87,44.46 17.43,45.89 17.43,47.67C17.43,49.44 18.87,50.87 20.64,50.87C22.41,50.87 23.85,49.44 23.85,47.67Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M37.4,7.69C37.4,8.21 37.3,8.71 37.1,9.19C36.9,9.65 36.62,10.07 36.27,10.42C35.91,10.78 35.5,11.05 35.04,11.25C34.56,11.45 34.05,11.56 33.53,11.56C33.01,11.56 32.5,11.45 32.03,11.25C31.57,11.06 31.15,10.78 30.8,10.42C30.44,10.07 30.17,9.65 29.97,9.19C29.77,8.71 29.66,8.21 29.66,7.69C29.66,7.17 29.76,6.66 29.97,6.18C30.16,5.72 30.44,5.31 30.8,4.95C31.15,4.6 31.57,4.32 32.03,4.12C32.5,3.92 33.01,3.82 33.53,3.82C34.05,3.82 34.56,3.92 35.04,4.12C35.5,4.32 35.91,4.6 36.27,4.95C36.62,5.31 36.9,5.72 37.1,6.18C37.3,6.66 37.4,7.17 37.4,7.69ZM36.74,7.69C36.74,5.91 35.3,4.48 33.53,4.48C31.76,4.48 30.32,5.92 30.32,7.69C30.32,9.46 31.76,10.9 33.53,10.9C35.3,10.9 36.74,9.46 36.74,7.69Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M34.5,7.07C34.16,7.07 33.88,7.35 33.88,7.69C33.88,8.03 34.16,8.3 34.5,8.3C34.84,8.3 35.12,8.03 35.12,7.69C35.12,7.35 34.84,7.07 34.5,7.07Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,5.81C32.71,5.81 32.44,6.09 32.44,6.43C32.44,6.77 32.72,7.05 33.06,7.05C33.4,7.05 33.67,6.77 33.67,6.43C33.67,6.09 33.4,5.81 33.06,5.81Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,8.33C32.71,8.33 32.44,8.6 32.44,8.94C32.44,9.28 32.72,9.56 33.06,9.56C33.4,9.56 33.67,9.28 33.67,8.94C33.67,8.6 33.4,8.33 33.06,8.33Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.63,18.11C19.27,18.11 18.17,19.22 18.17,20.58C18.17,21.95 19.27,23.05 20.63,23.05C22,23.05 23.1,21.95 23.1,20.58C23.1,19.22 22,18.11 20.63,18.11Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M24.5,20.58C24.5,21.1 24.4,21.61 24.2,22.09C24,22.55 23.72,22.96 23.37,23.32C23.01,23.67 22.6,23.95 22.14,24.15C21.66,24.35 21.16,24.45 20.63,24.45C20.11,24.45 19.61,24.35 19.13,24.15C18.67,23.95 18.25,23.67 17.9,23.32C17.55,22.96 17.27,22.55 17.07,22.09C16.87,21.61 16.76,21.1 16.76,20.58C16.76,20.06 16.87,19.55 17.07,19.08C17.26,18.62 17.55,18.2 17.9,17.85C18.25,17.49 18.67,17.22 19.13,17.02C19.61,16.82 20.11,16.71 20.63,16.71C21.16,16.71 21.66,16.81 22.14,17.02C22.6,17.21 23.01,17.49 23.37,17.85C23.72,18.2 24,18.62 24.2,19.08C24.4,19.55 24.5,20.06 24.5,20.58ZM23.85,20.58C23.85,18.81 22.41,17.37 20.64,17.37C18.87,17.37 17.43,18.81 17.43,20.58C17.43,22.35 18.87,23.79 20.64,23.79C22.41,23.79 23.85,22.35 23.85,20.58Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M37.4,33.48C37.4,34 37.3,34.51 37.1,34.99C36.9,35.45 36.62,35.86 36.27,36.21C35.91,36.57 35.5,36.85 35.04,37.04C34.56,37.24 34.05,37.35 33.53,37.35C33.01,37.35 32.5,37.25 32.03,37.04C31.57,36.85 31.15,36.57 30.8,36.21C30.44,35.86 30.17,35.45 29.97,34.99C29.77,34.51 29.66,34 29.66,33.48C29.66,32.96 29.76,32.45 29.97,31.97C30.16,31.51 30.44,31.1 30.8,30.75C31.15,30.39 31.57,30.11 32.03,29.92C32.5,29.72 33.01,29.61 33.53,29.61C34.05,29.61 34.56,29.71 35.04,29.92C35.5,30.11 35.91,30.39 36.27,30.75C36.62,31.1 36.9,31.51 37.1,31.97C37.3,32.45 37.4,32.96 37.4,33.48ZM36.74,33.48C36.74,31.71 35.3,30.27 33.53,30.27C31.76,30.27 30.32,31.71 30.32,33.48C30.32,35.25 31.76,36.69 33.53,36.69C35.3,36.69 36.74,35.25 36.74,33.48Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M34.5,32.86C34.16,32.86 33.88,33.14 33.88,33.48C33.88,33.82 34.16,34.1 34.5,34.1C34.84,34.1 35.12,33.82 35.12,33.48C35.12,33.14 34.84,32.86 34.5,32.86Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,31.61C32.71,31.61 32.44,31.88 32.44,32.22C32.44,32.56 32.72,32.84 33.06,32.84C33.4,32.84 33.67,32.56 33.67,32.22C33.67,31.88 33.4,31.61 33.06,31.61Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M33.06,34.12C32.71,34.12 32.44,34.4 32.44,34.74C32.44,35.08 32.72,35.35 33.06,35.35C33.4,35.35 33.67,35.08 33.67,34.74C33.67,34.4 33.4,34.12 33.06,34.12Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.18,7.69C10.18,9.05 9.08,10.16 7.71,10.16C6.35,10.16 5.25,9.05 5.25,7.69C5.25,6.32 6.35,5.22 7.71,5.22C9.08,5.22 10.18,6.32 10.18,7.69ZM7.3,9.28V6.09C6.6,6.28 6.07,6.92 6.07,7.69C6.07,8.45 6.6,9.1 7.3,9.28M9.36,7.69C9.36,6.92 8.83,6.28 8.13,6.09V9.28C8.83,9.1 9.36,8.45 9.36,7.68"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.58,7.69C11.58,8.21 11.48,8.71 11.28,9.19C11.08,9.65 10.8,10.07 10.45,10.42C10.09,10.78 9.68,11.05 9.22,11.25C8.74,11.45 8.23,11.56 7.71,11.56C7.19,11.56 6.68,11.45 6.21,11.25C5.75,11.06 5.33,10.78 4.98,10.42C4.62,10.07 4.34,9.65 4.15,9.19C3.95,8.71 3.84,8.21 3.84,7.69C3.84,7.17 3.94,6.66 4.15,6.18C4.34,5.72 4.62,5.31 4.98,4.95C5.33,4.6 5.75,4.32 6.21,4.12C6.68,3.92 7.19,3.82 7.71,3.82C8.23,3.82 8.74,3.92 9.22,4.12C9.68,4.32 10.09,4.6 10.45,4.95C10.8,5.31 11.08,5.72 11.28,6.18C11.48,6.66 11.58,7.17 11.58,7.69ZM10.92,7.69C10.92,5.91 9.49,4.48 7.71,4.48C5.94,4.48 4.51,5.92 4.51,7.69C4.51,9.46 5.94,10.9 7.71,10.9C9.49,10.9 10.92,9.46 10.92,7.69Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10.18,33.48C10.18,34.84 9.08,35.95 7.71,35.95C6.35,35.95 5.25,34.84 5.25,33.48C5.25,32.12 6.35,31.01 7.71,31.01C9.08,31.01 10.18,32.12 10.18,33.48ZM7.3,35.08V31.89C6.6,32.07 6.07,32.72 6.07,33.48C6.07,34.25 6.6,34.89 7.3,35.08M9.36,33.48C9.36,32.71 8.83,32.07 8.13,31.88V35.07C8.83,34.89 9.36,34.24 9.36,33.48"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.58,33.48C11.58,34 11.48,34.51 11.28,34.99C11.08,35.45 10.8,35.86 10.45,36.21C10.09,36.57 9.68,36.85 9.22,37.04C8.74,37.24 8.23,37.35 7.71,37.35C7.19,37.35 6.68,37.25 6.21,37.04C5.75,36.85 5.33,36.57 4.98,36.21C4.62,35.86 4.34,35.45 4.15,34.99C3.95,34.51 3.84,34 3.84,33.48C3.84,32.96 3.94,32.45 4.15,31.97C4.34,31.51 4.62,31.1 4.98,30.75C5.33,30.39 5.75,30.11 6.21,29.92C6.68,29.72 7.19,29.61 7.71,29.61C8.23,29.61 8.74,29.71 9.22,29.92C9.68,30.11 10.09,30.39 10.45,30.75C10.8,31.1 11.08,31.51 11.28,31.97C11.48,32.45 11.58,32.96 11.58,33.48ZM10.92,33.48C10.92,31.71 9.49,30.27 7.71,30.27C5.94,30.27 4.51,31.71 4.51,33.48C4.51,35.25 5.94,36.69 7.71,36.69C9.49,36.69 10.92,35.25 10.92,33.48Z"
|
||||||
|
android:strokeAlpha="0.2"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:fillAlpha="0.2"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</vector>
|
Before Width: | Height: | Size: 38 KiB |
24
osu.Android/Resources/drawable/monochrome.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:pathData="M73.92,44.43C74.82,44.43 75.43,45.1 75.43,46.02V54.54C75.43,55.46 74.82,56.13 73.92,56.13C73,56.13 72.41,55.46 72.41,54.54V46.02C72.41,45.1 73,44.43 73.92,44.43ZM73.92,61.55C72.82,61.55 71.95,60.68 71.95,59.58C71.95,58.51 72.82,57.64 73.92,57.64C75.02,57.64 75.89,58.51 75.89,59.58C75.89,60.68 75.02,61.55 73.92,61.55Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M68.41,48.55C69.33,48.55 69.92,49.22 69.92,50.11V55.77C69.92,59.94 67.35,61.55 64.22,61.55C61.08,61.55 58.5,59.94 58.5,55.77V50.11C58.5,49.22 59.09,48.55 60.01,48.55C60.91,48.55 61.52,49.22 61.52,50.11V55.56C61.52,57.84 62.48,58.74 64.22,58.74C65.94,58.74 66.9,57.84 66.9,55.56V50.11C66.9,49.22 67.51,48.55 68.41,48.55Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M49.94,52.01C49.94,52.85 50.81,53.16 52.47,53.54C54.78,54.1 56.98,54.69 56.98,57.53C56.98,60.3 54.93,61.55 51.99,61.55C49.56,61.55 47.79,60.71 46.97,59.73C46.33,58.97 46.41,58.3 47.02,57.71C47.79,56.97 48.46,57.28 48.89,57.66C49.58,58.3 50.43,58.97 52.07,58.97C53.29,58.97 54.06,58.56 54.06,57.74C54.06,56.92 53.24,56.64 51.09,56.05C48.97,55.46 47.08,54.9 47.08,52.36C47.08,49.52 49.38,48.35 51.94,48.35C53.4,48.35 55.06,48.73 56.08,49.83C56.52,50.27 56.85,50.88 56.08,51.72C55.32,52.52 54.75,52.29 54.22,51.88C53.73,51.52 52.88,50.91 51.6,50.91C50.73,50.91 49.94,51.19 49.94,52.01Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M38.79,61.55C34.9,61.55 32.11,58.74 32.11,54.95C32.11,51.14 34.9,48.35 38.79,48.35C42.68,48.35 45.47,51.14 45.47,54.95C45.47,58.74 42.68,61.55 38.79,61.55ZM38.79,58.74C41.04,58.74 42.45,57.1 42.45,54.95C42.45,52.8 41.04,51.14 38.79,51.14C36.54,51.14 35.13,52.8 35.13,54.95C35.13,57.1 36.54,58.74 38.79,58.74Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M86,54C86,71.67 71.67,86 54,86C36.33,86 22,71.67 22,54C22,36.33 36.33,22 54,22C71.67,22 86,36.33 86,54ZM25.2,54C25.2,69.91 38.09,82.8 54,82.8C69.91,82.8 82.8,69.91 82.8,54C82.8,38.09 69.91,25.2 54,25.2C38.09,25.2 25.2,38.09 25.2,54Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M36.78,54.99C36.78,56.09 37.65,56.96 38.75,56.96C39.85,56.96 40.72,56.09 40.72,54.99C40.72,53.91 39.85,53.04 38.75,53.04C37.65,53.04 36.78,53.91 36.78,54.99Z"
|
||||||
|
android:fillColor="#000000"/>
|
||||||
|
</vector>
|
6
osu.Android/Resources/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@drawable/monochrome"/>
|
||||||
|
</adaptive-icon>
|
BIN
osu.Android/Resources/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
osu.Android/Resources/mipmap-hdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
osu.Android/Resources/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
osu.Android/Resources/mipmap-mdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
osu.Android/Resources/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
osu.Android/Resources/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
osu.Android/Resources/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
osu.Android/Resources/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
osu.Android/Resources/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
osu.Android/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 35 KiB |
@ -1,13 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Android.props" />
|
<Import Project="..\osu.Android.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-android</TargetFramework>
|
<TargetFramework>net8.0-android</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>osu.Android</RootNamespace>
|
<RootNamespace>osu.Android</RootNamespace>
|
||||||
<AssemblyName>osu.Android</AssemblyName>
|
<AssemblyName>osu.Android</AssemblyName>
|
||||||
<UseMauiEssentials>true</UseMauiEssentials>
|
|
||||||
<!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 -->
|
|
||||||
<EnableLLVM>false</EnableLLVM>
|
|
||||||
<Version>0.0.0</Version>
|
<Version>0.0.0</Version>
|
||||||
<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
|
<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
|
||||||
<ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
|
||||||
@ -19,4 +16,7 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -16,15 +16,14 @@
|
|||||||
"osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj",
|
"osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj",
|
||||||
"osu.Game.Tournament\\osu.Game.Tournament.csproj",
|
"osu.Game.Tournament\\osu.Game.Tournament.csproj",
|
||||||
"osu.Game\\osu.Game.csproj",
|
"osu.Game\\osu.Game.csproj",
|
||||||
|
|
||||||
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform\\osu.Game.Rulesets.EmptyFreeform.csproj",
|
|
||||||
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform.Tests\\osu.Game.Rulesets.EmptyFreeform.Tests.csproj",
|
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform.Tests\\osu.Game.Rulesets.EmptyFreeform.Tests.csproj",
|
||||||
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj",
|
"Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform\\osu.Game.Rulesets.EmptyFreeform.csproj",
|
||||||
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
|
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
|
||||||
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling\\osu.Game.Rulesets.EmptyScrolling.csproj",
|
"Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj",
|
||||||
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling.Tests\\osu.Game.Rulesets.EmptyScrolling.Tests.csproj",
|
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling.Tests\\osu.Game.Rulesets.EmptyScrolling.Tests.csproj",
|
||||||
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj",
|
"Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling\\osu.Game.Rulesets.EmptyScrolling.csproj",
|
||||||
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj"
|
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj",
|
||||||
|
"Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -34,7 +33,7 @@ namespace osu.Desktop
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; } = null!;
|
private IAPIProvider api { get; set; } = null!;
|
||||||
|
|
||||||
private readonly IBindable<UserStatus> status = new Bindable<UserStatus>();
|
private readonly IBindable<UserStatus?> status = new Bindable<UserStatus?>();
|
||||||
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
|
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
|
||||||
|
|
||||||
private readonly Bindable<DiscordRichPresenceMode> privacyMode = new Bindable<DiscordRichPresenceMode>();
|
private readonly Bindable<DiscordRichPresenceMode> privacyMode = new Bindable<DiscordRichPresenceMode>();
|
||||||
@ -54,9 +53,6 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
client.OnReady += onReady;
|
client.OnReady += onReady;
|
||||||
|
|
||||||
// safety measure for now, until we performance test / improve backoff for failed connections.
|
|
||||||
client.OnConnectionFailed += (_, _) => client.Deinitialize();
|
|
||||||
|
|
||||||
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
|
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
|
||||||
|
|
||||||
config.BindWith(OsuSetting.DiscordRichPresence, privacyMode);
|
config.BindWith(OsuSetting.DiscordRichPresence, privacyMode);
|
||||||
@ -90,25 +86,26 @@ namespace osu.Desktop
|
|||||||
if (!client.IsInitialized)
|
if (!client.IsInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (status.Value is UserStatusOffline || privacyMode.Value == DiscordRichPresenceMode.Off)
|
if (status.Value == UserStatus.Offline || privacyMode.Value == DiscordRichPresenceMode.Off)
|
||||||
{
|
{
|
||||||
client.ClearPresence();
|
client.ClearPresence();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.Value is UserStatusOnline && activity.Value != null)
|
if (status.Value == UserStatus.Online && activity.Value != null)
|
||||||
{
|
{
|
||||||
presence.State = truncate(activity.Value.GetStatus(privacyMode.Value == DiscordRichPresenceMode.Limited));
|
bool hideIdentifiableInformation = privacyMode.Value == DiscordRichPresenceMode.Limited;
|
||||||
presence.Details = truncate(getDetails(activity.Value));
|
presence.State = truncate(activity.Value.GetStatus(hideIdentifiableInformation));
|
||||||
|
presence.Details = truncate(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty);
|
||||||
|
|
||||||
if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0)
|
if (getBeatmapID(activity.Value) is int beatmapId && beatmapId > 0)
|
||||||
{
|
{
|
||||||
presence.Buttons = new[]
|
presence.Buttons = new[]
|
||||||
{
|
{
|
||||||
new Button
|
new Button
|
||||||
{
|
{
|
||||||
Label = "View beatmap",
|
Label = "View beatmap",
|
||||||
Url = $@"{api.WebsiteRootUrl}/beatmapsets/{beatmap.BeatmapSet?.OnlineID}#{ruleset.Value.ShortName}/{beatmap.OnlineID}"
|
Url = $@"{api.WebsiteRootUrl}/beatmaps/{beatmapId}?mode={ruleset.Value.ShortName}"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -162,40 +159,20 @@ namespace osu.Desktop
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBeatmapInfo? getBeatmap(UserActivity activity)
|
private int? getBeatmapID(UserActivity activity)
|
||||||
{
|
{
|
||||||
switch (activity)
|
switch (activity)
|
||||||
{
|
{
|
||||||
case UserActivity.InGame game:
|
case UserActivity.InGame game:
|
||||||
return game.BeatmapInfo;
|
return game.BeatmapID;
|
||||||
|
|
||||||
case UserActivity.EditingBeatmap edit:
|
case UserActivity.EditingBeatmap edit:
|
||||||
return edit.BeatmapInfo;
|
return edit.BeatmapID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string getDetails(UserActivity activity)
|
|
||||||
{
|
|
||||||
switch (activity)
|
|
||||||
{
|
|
||||||
case UserActivity.InGame game:
|
|
||||||
return game.BeatmapInfo.ToString() ?? string.Empty;
|
|
||||||
|
|
||||||
case UserActivity.EditingBeatmap edit:
|
|
||||||
return edit.BeatmapInfo.ToString() ?? string.Empty;
|
|
||||||
|
|
||||||
case UserActivity.WatchingReplay watching:
|
|
||||||
return watching.BeatmapInfo.ToString();
|
|
||||||
|
|
||||||
case UserActivity.InLobby lobby:
|
|
||||||
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
client.Dispose();
|
client.Dispose();
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Desktop.LegacyIpc
|
|||||||
case LegacyIpcDifficultyCalculationRequest req:
|
case LegacyIpcDifficultyCalculationRequest req:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile);
|
WorkingBeatmap beatmap = new FlatWorkingBeatmap(req.BeatmapFile);
|
||||||
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||||
Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();
|
Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();
|
||||||
|
|
||||||
|
739
osu.Desktop/NVAPI.cs
Normal file
@ -0,0 +1,739 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
#pragma warning disable IDE1006 // Naming rule violation
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
|
||||||
|
namespace osu.Desktop
|
||||||
|
{
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
internal static class NVAPI
|
||||||
|
{
|
||||||
|
private const string osu_filename = "osu!.exe";
|
||||||
|
|
||||||
|
// This is a good reference:
|
||||||
|
// https://github.com/errollw/Warp-and-Blend-Quadros/blob/master/WarpBlend-Quadros/UnwarpAll-Quadros/include/nvapi.h
|
||||||
|
// Note our Stride == their VERSION (e.g. NVDRS_SETTING_VER)
|
||||||
|
|
||||||
|
public const int MAX_PHYSICAL_GPUS = 64;
|
||||||
|
public const int UNICODE_STRING_MAX = 2048;
|
||||||
|
|
||||||
|
public const string APPLICATION_NAME = @"osu!";
|
||||||
|
public const string PROFILE_NAME = @"osu!";
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus EnumPhysicalGPUsDelegate([Out] IntPtr[] gpuHandles, out int gpuCount);
|
||||||
|
|
||||||
|
public static readonly EnumPhysicalGPUsDelegate EnumPhysicalGPUs;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus EnumLogicalGPUsDelegate([Out] IntPtr[] gpuHandles, out int gpuCount);
|
||||||
|
|
||||||
|
public static readonly EnumLogicalGPUsDelegate EnumLogicalGPUs;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus GetSystemTypeDelegate(IntPtr gpuHandle, out NvSystemType systemType);
|
||||||
|
|
||||||
|
public static readonly GetSystemTypeDelegate GetSystemType;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus GetGPUTypeDelegate(IntPtr gpuHandle, out NvGpuType gpuType);
|
||||||
|
|
||||||
|
public static readonly GetGPUTypeDelegate GetGPUType;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus CreateSessionDelegate(out IntPtr sessionHandle);
|
||||||
|
|
||||||
|
public static CreateSessionDelegate CreateSession;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus LoadSettingsDelegate(IntPtr sessionHandle);
|
||||||
|
|
||||||
|
public static LoadSettingsDelegate LoadSettings;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus FindApplicationByNameDelegate(IntPtr sessionHandle, [MarshalAs(UnmanagedType.BStr)] string appName, out IntPtr profileHandle, ref NvApplication application);
|
||||||
|
|
||||||
|
public static FindApplicationByNameDelegate FindApplicationByName;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus GetCurrentGlobalProfileDelegate(IntPtr sessionHandle, out IntPtr profileHandle);
|
||||||
|
|
||||||
|
public static GetCurrentGlobalProfileDelegate GetCurrentGlobalProfile;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus GetProfileInfoDelegate(IntPtr sessionHandle, IntPtr profileHandle, ref NvProfile profile);
|
||||||
|
|
||||||
|
public static GetProfileInfoDelegate GetProfileInfo;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public delegate NvStatus GetSettingDelegate(IntPtr sessionHandle, IntPtr profileHandle, NvSettingID settingID, ref NvSetting setting);
|
||||||
|
|
||||||
|
public static GetSettingDelegate GetSetting;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate NvStatus CreateProfileDelegate(IntPtr sessionHandle, ref NvProfile profile, out IntPtr profileHandle);
|
||||||
|
|
||||||
|
private static readonly CreateProfileDelegate CreateProfile;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate NvStatus SetSettingDelegate(IntPtr sessionHandle, IntPtr profileHandle, ref NvSetting setting);
|
||||||
|
|
||||||
|
private static readonly SetSettingDelegate SetSetting;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate NvStatus EnumApplicationsDelegate(IntPtr sessionHandle, IntPtr profileHandle, uint startIndex, ref uint appCount, [In, Out, MarshalAs(UnmanagedType.LPArray)] NvApplication[] applications);
|
||||||
|
|
||||||
|
private static readonly EnumApplicationsDelegate EnumApplications;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate NvStatus CreateApplicationDelegate(IntPtr sessionHandle, IntPtr profileHandle, ref NvApplication application);
|
||||||
|
|
||||||
|
private static readonly CreateApplicationDelegate CreateApplication;
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private delegate NvStatus SaveSettingsDelegate(IntPtr sessionHandle);
|
||||||
|
|
||||||
|
private static readonly SaveSettingsDelegate SaveSettings;
|
||||||
|
|
||||||
|
public static NvStatus Status { get; private set; } = NvStatus.OK;
|
||||||
|
public static bool Available { get; private set; }
|
||||||
|
|
||||||
|
private static IntPtr sessionHandle;
|
||||||
|
|
||||||
|
public static bool IsUsingOptimusDedicatedGpu
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Available)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsLaptop)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IntPtr profileHandle;
|
||||||
|
if (!getProfile(out profileHandle, out _, out bool _))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get the optimus setting
|
||||||
|
NvSetting setting;
|
||||||
|
if (!getSetting(NvSettingID.SHIM_RENDERING_MODE_ID, profileHandle, out setting))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (setting.U32CurrentValue & (uint)NvShimSetting.SHIM_RENDERING_MODE_ENABLE) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsLaptop
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Available)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Make sure that this is a laptop.
|
||||||
|
IntPtr[] gpus = new IntPtr[64];
|
||||||
|
if (checkError(EnumPhysicalGPUs(gpus, out int gpuCount)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < gpuCount; i++)
|
||||||
|
{
|
||||||
|
if (checkError(GetSystemType(gpus[i], out var type)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (type == NvSystemType.LAPTOP)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NvThreadControlSetting ThreadedOptimisations
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!Available)
|
||||||
|
return NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT;
|
||||||
|
|
||||||
|
IntPtr profileHandle;
|
||||||
|
if (!getProfile(out profileHandle, out _, out bool _))
|
||||||
|
return NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT;
|
||||||
|
|
||||||
|
// Get the threaded optimisations setting
|
||||||
|
NvSetting setting;
|
||||||
|
if (!getSetting(NvSettingID.OGL_THREAD_CONTROL_ID, profileHandle, out setting))
|
||||||
|
return NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT;
|
||||||
|
|
||||||
|
return (NvThreadControlSetting)setting.U32CurrentValue;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!Available)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool success = setSetting(NvSettingID.OGL_THREAD_CONTROL_ID, (uint)value);
|
||||||
|
|
||||||
|
Logger.Log(success ? $"Threaded optimizations set to \"{value}\"!" : "Threaded optimizations set failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the profile contains the current application.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>If the profile contains the current application.</returns>
|
||||||
|
private static bool containsApplication(IntPtr profileHandle, NvProfile profile, out NvApplication application)
|
||||||
|
{
|
||||||
|
application = new NvApplication
|
||||||
|
{
|
||||||
|
Version = NvApplication.Stride
|
||||||
|
};
|
||||||
|
|
||||||
|
if (profile.NumOfApps == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NvApplication[] applications = new NvApplication[profile.NumOfApps];
|
||||||
|
applications[0].Version = NvApplication.Stride;
|
||||||
|
|
||||||
|
uint numApps = profile.NumOfApps;
|
||||||
|
|
||||||
|
if (checkError(EnumApplications(sessionHandle, profileHandle, 0, ref numApps, applications)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint i = 0; i < numApps; i++)
|
||||||
|
{
|
||||||
|
if (applications[i].AppName == osu_filename)
|
||||||
|
{
|
||||||
|
application = applications[i];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the profile of the current application.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileHandle">The profile handle.</param>
|
||||||
|
/// <param name="application">The current application description.</param>
|
||||||
|
/// <param name="isApplicationSpecific">If this profile is not a global (default) profile.</param>
|
||||||
|
/// <returns>If the operation succeeded.</returns>
|
||||||
|
private static bool getProfile(out IntPtr profileHandle, out NvApplication application, out bool isApplicationSpecific)
|
||||||
|
{
|
||||||
|
application = new NvApplication
|
||||||
|
{
|
||||||
|
Version = NvApplication.Stride
|
||||||
|
};
|
||||||
|
|
||||||
|
isApplicationSpecific = true;
|
||||||
|
|
||||||
|
if (checkError(FindApplicationByName(sessionHandle, osu_filename, out profileHandle, ref application)))
|
||||||
|
{
|
||||||
|
isApplicationSpecific = false;
|
||||||
|
if (checkError(GetCurrentGlobalProfile(sessionHandle, out profileHandle)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a profile.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="profileHandle">The profile handle.</param>
|
||||||
|
/// <returns>If the operation succeeded.</returns>
|
||||||
|
private static bool createProfile(out IntPtr profileHandle)
|
||||||
|
{
|
||||||
|
NvProfile newProfile = new NvProfile
|
||||||
|
{
|
||||||
|
Version = NvProfile.Stride,
|
||||||
|
IsPredefined = 0,
|
||||||
|
ProfileName = PROFILE_NAME,
|
||||||
|
GPUSupport = new uint[32]
|
||||||
|
};
|
||||||
|
|
||||||
|
newProfile.GPUSupport[0] = 1;
|
||||||
|
|
||||||
|
if (checkError(CreateProfile(sessionHandle, ref newProfile, out profileHandle)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a setting from the profile.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="settingId">The setting to retrieve.</param>
|
||||||
|
/// <param name="profileHandle">The profile handle to retrieve the setting from.</param>
|
||||||
|
/// <param name="setting">The setting.</param>
|
||||||
|
/// <returns>If the operation succeeded.</returns>
|
||||||
|
private static bool getSetting(NvSettingID settingId, IntPtr profileHandle, out NvSetting setting)
|
||||||
|
{
|
||||||
|
setting = new NvSetting
|
||||||
|
{
|
||||||
|
Version = NvSetting.Stride,
|
||||||
|
SettingID = settingId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (checkError(GetSetting(sessionHandle, profileHandle, settingId, ref setting)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool setSetting(NvSettingID settingId, uint settingValue)
|
||||||
|
{
|
||||||
|
NvApplication application;
|
||||||
|
IntPtr profileHandle;
|
||||||
|
bool isApplicationSpecific;
|
||||||
|
if (!getProfile(out profileHandle, out application, out isApplicationSpecific))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!isApplicationSpecific)
|
||||||
|
{
|
||||||
|
// We don't want to interfere with the user's other settings, so let's create a separate config for osu!
|
||||||
|
if (!createProfile(out profileHandle))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NvSetting newSetting = new NvSetting
|
||||||
|
{
|
||||||
|
Version = NvSetting.Stride,
|
||||||
|
SettingID = settingId,
|
||||||
|
U32CurrentValue = settingValue
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the thread state
|
||||||
|
if (checkError(SetSetting(sessionHandle, profileHandle, ref newSetting)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get the profile (needed to check app count)
|
||||||
|
NvProfile profile = new NvProfile
|
||||||
|
{
|
||||||
|
Version = NvProfile.Stride
|
||||||
|
};
|
||||||
|
if (checkError(GetProfileInfo(sessionHandle, profileHandle, ref profile)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!containsApplication(profileHandle, profile, out application))
|
||||||
|
{
|
||||||
|
// Need to add the current application to the profile
|
||||||
|
application.IsPredefined = 0;
|
||||||
|
|
||||||
|
application.AppName = osu_filename;
|
||||||
|
application.UserFriendlyName = APPLICATION_NAME;
|
||||||
|
|
||||||
|
if (checkError(CreateApplication(sessionHandle, profileHandle, ref application)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save!
|
||||||
|
return !checkError(SaveSettings(sessionHandle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a session to access the driver configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>If the operation succeeded.</returns>
|
||||||
|
private static bool createSession()
|
||||||
|
{
|
||||||
|
if (checkError(CreateSession(out sessionHandle)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load settings into session
|
||||||
|
if (checkError(LoadSettings(sessionHandle)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool checkError(NvStatus status)
|
||||||
|
{
|
||||||
|
Status = status;
|
||||||
|
return status != NvStatus.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NVAPI()
|
||||||
|
{
|
||||||
|
// TODO: check whether gpu vendor contains NVIDIA before attempting load?
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Try to load NVAPI
|
||||||
|
if ((IntPtr.Size == 4 && loadLibrary(@"nvapi.dll") == IntPtr.Zero)
|
||||||
|
|| (IntPtr.Size == 8 && loadLibrary(@"nvapi64.dll") == IntPtr.Zero))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeDelegate initialize;
|
||||||
|
getDelegate(0x0150E828, out initialize);
|
||||||
|
|
||||||
|
if (initialize?.Invoke() == NvStatus.OK)
|
||||||
|
{
|
||||||
|
// IDs can be found here: https://github.com/jNizM/AHK_NVIDIA_NvAPI/blob/master/info/NvAPI_IDs.txt
|
||||||
|
|
||||||
|
getDelegate(0xE5AC921F, out EnumPhysicalGPUs);
|
||||||
|
getDelegate(0x48B3EA59, out EnumLogicalGPUs);
|
||||||
|
getDelegate(0xBAAABFCC, out GetSystemType);
|
||||||
|
getDelegate(0xC33BAEB1, out GetGPUType);
|
||||||
|
getDelegate(0x0694D52E, out CreateSession);
|
||||||
|
getDelegate(0x375DBD6B, out LoadSettings);
|
||||||
|
getDelegate(0xEEE566B2, out FindApplicationByName);
|
||||||
|
getDelegate(0x617BFF9F, out GetCurrentGlobalProfile);
|
||||||
|
getDelegate(0x577DD202, out SetSetting);
|
||||||
|
getDelegate(0x61CD6FD6, out GetProfileInfo);
|
||||||
|
getDelegate(0x73BF8338, out GetSetting);
|
||||||
|
getDelegate(0xCC176068, out CreateProfile);
|
||||||
|
getDelegate(0x7FA2173A, out EnumApplications);
|
||||||
|
getDelegate(0x4347A9DE, out CreateApplication);
|
||||||
|
getDelegate(0xFCBC7E14, out SaveSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createSession())
|
||||||
|
Available = true;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void getDelegate<T>(uint id, out T newDelegate) where T : class
|
||||||
|
{
|
||||||
|
IntPtr ptr = IntPtr.Size == 4 ? queryInterface32(id) : queryInterface64(id);
|
||||||
|
newDelegate = ptr == IntPtr.Zero ? null : Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
|
||||||
|
private static extern IntPtr loadLibrary(string dllToLoad);
|
||||||
|
|
||||||
|
[DllImport(@"nvapi.dll", EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr queryInterface32(uint id);
|
||||||
|
|
||||||
|
[DllImport(@"nvapi64.dll", EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr queryInterface64(uint id);
|
||||||
|
|
||||||
|
private delegate NvStatus InitializeDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct NvSetting
|
||||||
|
{
|
||||||
|
public uint Version;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string SettingName;
|
||||||
|
|
||||||
|
public NvSettingID SettingID;
|
||||||
|
public uint SettingType;
|
||||||
|
public uint SettingLocation;
|
||||||
|
public uint IsCurrentPredefined;
|
||||||
|
public uint IsPredefinedValid;
|
||||||
|
|
||||||
|
public uint U32PredefinedValue;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string StringPredefinedValue;
|
||||||
|
|
||||||
|
public uint U32CurrentValue;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string StringCurrentValue;
|
||||||
|
|
||||||
|
public static uint Stride => (uint)Marshal.SizeOf(typeof(NvSetting)) | (1 << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct NvProfile
|
||||||
|
{
|
||||||
|
public uint Version;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string ProfileName;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||||
|
public uint[] GPUSupport;
|
||||||
|
|
||||||
|
public uint IsPredefined;
|
||||||
|
public uint NumOfApps;
|
||||||
|
public uint NumOfSettings;
|
||||||
|
|
||||||
|
public static uint Stride => (uint)Marshal.SizeOf(typeof(NvProfile)) | (1 << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct NvApplication
|
||||||
|
{
|
||||||
|
public uint Version;
|
||||||
|
public uint IsPredefined;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string AppName;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string UserFriendlyName;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string Launcher;
|
||||||
|
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
|
public string FileInFolder;
|
||||||
|
|
||||||
|
public static uint Stride => (uint)Marshal.SizeOf(typeof(NvApplication)) | (2 << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum NvStatus
|
||||||
|
{
|
||||||
|
OK = 0, // Success. Request is completed.
|
||||||
|
ERROR = -1, // Generic error
|
||||||
|
LIBRARY_NOT_FOUND = -2, // NVAPI support library cannot be loaded.
|
||||||
|
NO_IMPLEMENTATION = -3, // not implemented in current driver installation
|
||||||
|
API_NOT_INITIALIZED = -4, // Initialize has not been called (successfully)
|
||||||
|
INVALID_ARGUMENT = -5, // The argument/parameter value is not valid or NULL.
|
||||||
|
NVIDIA_DEVICE_NOT_FOUND = -6, // No NVIDIA display driver, or NVIDIA GPU driving a display, was found.
|
||||||
|
END_ENUMERATION = -7, // No more items to enumerate
|
||||||
|
INVALID_HANDLE = -8, // Invalid handle
|
||||||
|
INCOMPATIBLE_STRUCT_VERSION = -9, // An argument's structure version is not supported
|
||||||
|
HANDLE_INVALIDATED = -10, // The handle is no longer valid (likely due to GPU or display re-configuration)
|
||||||
|
OPENGL_CONTEXT_NOT_CURRENT = -11, // No NVIDIA OpenGL context is current (but needs to be)
|
||||||
|
INVALID_POINTER = -14, // An invalid pointer, usually NULL, was passed as a parameter
|
||||||
|
NO_GL_EXPERT = -12, // OpenGL Expert is not supported by the current drivers
|
||||||
|
INSTRUMENTATION_DISABLED = -13, // OpenGL Expert is supported, but driver instrumentation is currently disabled
|
||||||
|
NO_GL_NSIGHT = -15, // OpenGL does not support Nsight
|
||||||
|
|
||||||
|
EXPECTED_LOGICAL_GPU_HANDLE = -100, // Expected a logical GPU handle for one or more parameters
|
||||||
|
EXPECTED_PHYSICAL_GPU_HANDLE = -101, // Expected a physical GPU handle for one or more parameters
|
||||||
|
EXPECTED_DISPLAY_HANDLE = -102, // Expected an NV display handle for one or more parameters
|
||||||
|
INVALID_COMBINATION = -103, // The combination of parameters is not valid.
|
||||||
|
NOT_SUPPORTED = -104, // Requested feature is not supported in the selected GPU
|
||||||
|
PORTID_NOT_FOUND = -105, // No port ID was found for the I2C transaction
|
||||||
|
EXPECTED_UNATTACHED_DISPLAY_HANDLE = -106, // Expected an unattached display handle as one of the input parameters.
|
||||||
|
INVALID_PERF_LEVEL = -107, // Invalid perf level
|
||||||
|
DEVICE_BUSY = -108, // Device is busy; request not fulfilled
|
||||||
|
NV_PERSIST_FILE_NOT_FOUND = -109, // NV persist file is not found
|
||||||
|
PERSIST_DATA_NOT_FOUND = -110, // NV persist data is not found
|
||||||
|
EXPECTED_TV_DISPLAY = -111, // Expected a TV output display
|
||||||
|
EXPECTED_TV_DISPLAY_ON_DCONNECTOR = -112, // Expected a TV output on the D Connector - HDTV_EIAJ4120.
|
||||||
|
NO_ACTIVE_SLI_TOPOLOGY = -113, // SLI is not active on this device.
|
||||||
|
SLI_RENDERING_MODE_NOTALLOWED = -114, // Setup of SLI rendering mode is not possible right now.
|
||||||
|
EXPECTED_DIGITAL_FLAT_PANEL = -115, // Expected a digital flat panel.
|
||||||
|
ARGUMENT_EXCEED_MAX_SIZE = -116, // Argument exceeds the expected size.
|
||||||
|
DEVICE_SWITCHING_NOT_ALLOWED = -117, // Inhibit is ON due to one of the flags in NV_GPU_DISPLAY_CHANGE_INHIBIT or SLI active.
|
||||||
|
TESTING_CLOCKS_NOT_SUPPORTED = -118, // Testing of clocks is not supported.
|
||||||
|
UNKNOWN_UNDERSCAN_CONFIG = -119, // The specified underscan config is from an unknown source (e.g. INF)
|
||||||
|
TIMEOUT_RECONFIGURING_GPU_TOPO = -120, // Timeout while reconfiguring GPUs
|
||||||
|
DATA_NOT_FOUND = -121, // Requested data was not found
|
||||||
|
EXPECTED_ANALOG_DISPLAY = -122, // Expected an analog display
|
||||||
|
NO_VIDLINK = -123, // No SLI video bridge is present
|
||||||
|
REQUIRES_REBOOT = -124, // NVAPI requires a reboot for the settings to take effect
|
||||||
|
INVALID_HYBRID_MODE = -125, // The function is not supported with the current Hybrid mode.
|
||||||
|
MIXED_TARGET_TYPES = -126, // The target types are not all the same
|
||||||
|
SYSWOW64_NOT_SUPPORTED = -127, // The function is not supported from 32-bit on a 64-bit system.
|
||||||
|
IMPLICIT_SET_GPU_TOPOLOGY_CHANGE_NOT_ALLOWED = -128, // There is no implicit GPU topology active. Use SetHybridMode to change topology.
|
||||||
|
REQUEST_USER_TO_CLOSE_NON_MIGRATABLE_APPS = -129, // Prompt the user to close all non-migratable applications.
|
||||||
|
OUT_OF_MEMORY = -130, // Could not allocate sufficient memory to complete the call.
|
||||||
|
WAS_STILL_DRAWING = -131, // The previous operation that is transferring information to or from this surface is incomplete.
|
||||||
|
FILE_NOT_FOUND = -132, // The file was not found.
|
||||||
|
TOO_MANY_UNIQUE_STATE_OBJECTS = -133, // There are too many unique instances of a particular type of state object.
|
||||||
|
INVALID_CALL = -134, // The method call is invalid. For example, a method's parameter may not be a valid pointer.
|
||||||
|
D3D10_1_LIBRARY_NOT_FOUND = -135, // d3d10_1.dll cannot be loaded.
|
||||||
|
FUNCTION_NOT_FOUND = -136, // Couldn't find the function in the loaded DLL.
|
||||||
|
INVALID_USER_PRIVILEGE = -137, // Current User is not Admin.
|
||||||
|
EXPECTED_NON_PRIMARY_DISPLAY_HANDLE = -138, // The handle corresponds to GDIPrimary.
|
||||||
|
EXPECTED_COMPUTE_GPU_HANDLE = -139, // Setting Physx GPU requires that the GPU is compute-capable.
|
||||||
|
STEREO_NOT_INITIALIZED = -140, // The Stereo part of NVAPI failed to initialize completely. Check if the stereo driver is installed.
|
||||||
|
STEREO_REGISTRY_ACCESS_FAILED = -141, // Access to stereo-related registry keys or values has failed.
|
||||||
|
STEREO_REGISTRY_PROFILE_TYPE_NOT_SUPPORTED = -142, // The given registry profile type is not supported.
|
||||||
|
STEREO_REGISTRY_VALUE_NOT_SUPPORTED = -143, // The given registry value is not supported.
|
||||||
|
STEREO_NOT_ENABLED = -144, // Stereo is not enabled and the function needed it to execute completely.
|
||||||
|
STEREO_NOT_TURNED_ON = -145, // Stereo is not turned on and the function needed it to execute completely.
|
||||||
|
STEREO_INVALID_DEVICE_INTERFACE = -146, // Invalid device interface.
|
||||||
|
STEREO_PARAMETER_OUT_OF_RANGE = -147, // Separation percentage or JPEG image capture quality is out of [0-100] range.
|
||||||
|
STEREO_FRUSTUM_ADJUST_MODE_NOT_SUPPORTED = -148, // The given frustum adjust mode is not supported.
|
||||||
|
TOPO_NOT_POSSIBLE = -149, // The mosaic topology is not possible given the current state of the hardware.
|
||||||
|
MODE_CHANGE_FAILED = -150, // An attempt to do a display resolution mode change has failed.
|
||||||
|
D3D11_LIBRARY_NOT_FOUND = -151, // d3d11.dll/d3d11_beta.dll cannot be loaded.
|
||||||
|
INVALID_ADDRESS = -152, // Address is outside of valid range.
|
||||||
|
STRING_TOO_SMALL = -153, // The pre-allocated string is too small to hold the result.
|
||||||
|
MATCHING_DEVICE_NOT_FOUND = -154, // The input does not match any of the available devices.
|
||||||
|
DRIVER_RUNNING = -155, // Driver is running.
|
||||||
|
DRIVER_NOTRUNNING = -156, // Driver is not running.
|
||||||
|
ERROR_DRIVER_RELOAD_REQUIRED = -157, // A driver reload is required to apply these settings.
|
||||||
|
SET_NOT_ALLOWED = -158, // Intended setting is not allowed.
|
||||||
|
ADVANCED_DISPLAY_TOPOLOGY_REQUIRED = -159, // Information can't be returned due to "advanced display topology".
|
||||||
|
SETTING_NOT_FOUND = -160, // Setting is not found.
|
||||||
|
SETTING_SIZE_TOO_LARGE = -161, // Setting size is too large.
|
||||||
|
TOO_MANY_SETTINGS_IN_PROFILE = -162, // There are too many settings for a profile.
|
||||||
|
PROFILE_NOT_FOUND = -163, // Profile is not found.
|
||||||
|
PROFILE_NAME_IN_USE = -164, // Profile name is duplicated.
|
||||||
|
PROFILE_NAME_EMPTY = -165, // Profile name is empty.
|
||||||
|
EXECUTABLE_NOT_FOUND = -166, // Application not found in the Profile.
|
||||||
|
EXECUTABLE_ALREADY_IN_USE = -167, // Application already exists in the other profile.
|
||||||
|
DATATYPE_MISMATCH = -168, // Data Type mismatch
|
||||||
|
PROFILE_REMOVED = -169, // The profile passed as parameter has been removed and is no longer valid.
|
||||||
|
UNREGISTERED_RESOURCE = -170, // An unregistered resource was passed as a parameter.
|
||||||
|
ID_OUT_OF_RANGE = -171, // The DisplayId corresponds to a display which is not within the normal outputId range.
|
||||||
|
DISPLAYCONFIG_VALIDATION_FAILED = -172, // Display topology is not valid so the driver cannot do a mode set on this configuration.
|
||||||
|
DPMST_CHANGED = -173, // Display Port Multi-Stream topology has been changed.
|
||||||
|
INSUFFICIENT_BUFFER = -174, // Input buffer is insufficient to hold the contents.
|
||||||
|
ACCESS_DENIED = -175, // No access to the caller.
|
||||||
|
MOSAIC_NOT_ACTIVE = -176, // The requested action cannot be performed without Mosaic being enabled.
|
||||||
|
SHARE_RESOURCE_RELOCATED = -177, // The surface is relocated away from video memory.
|
||||||
|
REQUEST_USER_TO_DISABLE_DWM = -178, // The user should disable DWM before calling NvAPI.
|
||||||
|
D3D_DEVICE_LOST = -179, // D3D device status is D3DERR_DEVICELOST or D3DERR_DEVICENOTRESET - the user has to reset the device.
|
||||||
|
INVALID_CONFIGURATION = -180, // The requested action cannot be performed in the current state.
|
||||||
|
STEREO_HANDSHAKE_NOT_DONE = -181, // Call failed as stereo handshake not completed.
|
||||||
|
EXECUTABLE_PATH_IS_AMBIGUOUS = -182, // The path provided was too short to determine the correct NVDRS_APPLICATION
|
||||||
|
DEFAULT_STEREO_PROFILE_IS_NOT_DEFINED = -183, // Default stereo profile is not currently defined
|
||||||
|
DEFAULT_STEREO_PROFILE_DOES_NOT_EXIST = -184, // Default stereo profile does not exist
|
||||||
|
CLUSTER_ALREADY_EXISTS = -185, // A cluster is already defined with the given configuration.
|
||||||
|
DPMST_DISPLAY_ID_EXPECTED = -186, // The input display id is not that of a multi stream enabled connector or a display device in a multi stream topology
|
||||||
|
INVALID_DISPLAY_ID = -187, // The input display id is not valid or the monitor associated to it does not support the current operation
|
||||||
|
STREAM_IS_OUT_OF_SYNC = -188, // While playing secure audio stream, stream goes out of sync
|
||||||
|
INCOMPATIBLE_AUDIO_DRIVER = -189, // Older audio driver version than required
|
||||||
|
VALUE_ALREADY_SET = -190, // Value already set, setting again not allowed.
|
||||||
|
TIMEOUT = -191, // Requested operation timed out
|
||||||
|
GPU_WORKSTATION_FEATURE_INCOMPLETE = -192, // The requested workstation feature set has incomplete driver internal allocation resources
|
||||||
|
STEREO_INIT_ACTIVATION_NOT_DONE = -193, // Call failed because InitActivation was not called.
|
||||||
|
SYNC_NOT_ACTIVE = -194, // The requested action cannot be performed without Sync being enabled.
|
||||||
|
SYNC_MASTER_NOT_FOUND = -195, // The requested action cannot be performed without Sync Master being enabled.
|
||||||
|
INVALID_SYNC_TOPOLOGY = -196, // Invalid displays passed in the NV_GSYNC_DISPLAY pointer.
|
||||||
|
ECID_SIGN_ALGO_UNSUPPORTED = -197, // The specified signing algorithm is not supported. Either an incorrect value was entered or the current installed driver/hardware does not support the input value.
|
||||||
|
ECID_KEY_VERIFICATION_FAILED = -198, // The encrypted public key verification has failed.
|
||||||
|
FIRMWARE_OUT_OF_DATE = -199, // The device's firmware is out of date.
|
||||||
|
FIRMWARE_REVISION_NOT_SUPPORTED = -200, // The device's firmware is not supported.
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum NvSystemType
|
||||||
|
{
|
||||||
|
UNKNOWN = 0,
|
||||||
|
LAPTOP = 1,
|
||||||
|
DESKTOP = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum NvGpuType
|
||||||
|
{
|
||||||
|
UNKNOWN = 0,
|
||||||
|
IGPU = 1, // Integrated
|
||||||
|
DGPU = 2, // Discrete
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum NvSettingID : uint
|
||||||
|
{
|
||||||
|
OGL_AA_LINE_GAMMA_ID = 0x2089BF6C,
|
||||||
|
OGL_DEEP_COLOR_SCANOUT_ID = 0x2097C2F6,
|
||||||
|
OGL_DEFAULT_SWAP_INTERVAL_ID = 0x206A6582,
|
||||||
|
OGL_DEFAULT_SWAP_INTERVAL_FRACTIONAL_ID = 0x206C4581,
|
||||||
|
OGL_DEFAULT_SWAP_INTERVAL_SIGN_ID = 0x20655CFA,
|
||||||
|
OGL_EVENT_LOG_SEVERITY_THRESHOLD_ID = 0x209DF23E,
|
||||||
|
OGL_EXTENSION_STRING_VERSION_ID = 0x20FF7493,
|
||||||
|
OGL_FORCE_BLIT_ID = 0x201F619F,
|
||||||
|
OGL_FORCE_STEREO_ID = 0x204D9A0C,
|
||||||
|
OGL_IMPLICIT_GPU_AFFINITY_ID = 0x20D0F3E6,
|
||||||
|
OGL_MAX_FRAMES_ALLOWED_ID = 0x208E55E3,
|
||||||
|
OGL_MULTIMON_ID = 0x200AEBFC,
|
||||||
|
OGL_OVERLAY_PIXEL_TYPE_ID = 0x209AE66F,
|
||||||
|
OGL_OVERLAY_SUPPORT_ID = 0x206C28C4,
|
||||||
|
OGL_QUALITY_ENHANCEMENTS_ID = 0x20797D6C,
|
||||||
|
OGL_SINGLE_BACKDEPTH_BUFFER_ID = 0x20A29055,
|
||||||
|
OGL_THREAD_CONTROL_ID = 0x20C1221E,
|
||||||
|
OGL_TRIPLE_BUFFER_ID = 0x20FDD1F9,
|
||||||
|
OGL_VIDEO_EDITING_MODE_ID = 0x20EE02B4,
|
||||||
|
AA_BEHAVIOR_FLAGS_ID = 0x10ECDB82,
|
||||||
|
AA_MODE_ALPHATOCOVERAGE_ID = 0x10FC2D9C,
|
||||||
|
AA_MODE_GAMMACORRECTION_ID = 0x107D639D,
|
||||||
|
AA_MODE_METHOD_ID = 0x10D773D2,
|
||||||
|
AA_MODE_REPLAY_ID = 0x10D48A85,
|
||||||
|
AA_MODE_SELECTOR_ID = 0x107EFC5B,
|
||||||
|
AA_MODE_SELECTOR_SLIAA_ID = 0x107AFC5B,
|
||||||
|
ANISO_MODE_LEVEL_ID = 0x101E61A9,
|
||||||
|
ANISO_MODE_SELECTOR_ID = 0x10D2BB16,
|
||||||
|
APPLICATION_PROFILE_NOTIFICATION_TIMEOUT_ID = 0x104554B6,
|
||||||
|
APPLICATION_STEAM_ID_ID = 0x107CDDBC,
|
||||||
|
CPL_HIDDEN_PROFILE_ID = 0x106D5CFF,
|
||||||
|
CUDA_EXCLUDED_GPUS_ID = 0x10354FF8,
|
||||||
|
D3DOGL_GPU_MAX_POWER_ID = 0x10D1EF29,
|
||||||
|
EXPORT_PERF_COUNTERS_ID = 0x108F0841,
|
||||||
|
FXAA_ALLOW_ID = 0x1034CB89,
|
||||||
|
FXAA_ENABLE_ID = 0x1074C972,
|
||||||
|
FXAA_INDICATOR_ENABLE_ID = 0x1068FB9C,
|
||||||
|
MCSFRSHOWSPLIT_ID = 0x10287051,
|
||||||
|
OPTIMUS_MAXAA_ID = 0x10F9DC83,
|
||||||
|
PHYSXINDICATOR_ID = 0x1094F16F,
|
||||||
|
PREFERRED_PSTATE_ID = 0x1057EB71,
|
||||||
|
PREVENT_UI_AF_OVERRIDE_ID = 0x103BCCB5,
|
||||||
|
PS_FRAMERATE_LIMITER_ID = 0x10834FEE,
|
||||||
|
PS_FRAMERATE_LIMITER_GPS_CTRL_ID = 0x10834F01,
|
||||||
|
SHIM_MAXRES_ID = 0x10F9DC82,
|
||||||
|
SHIM_MCCOMPAT_ID = 0x10F9DC80,
|
||||||
|
SHIM_RENDERING_MODE_ID = 0x10F9DC81,
|
||||||
|
SHIM_RENDERING_OPTIONS_ID = 0x10F9DC84,
|
||||||
|
SLI_GPU_COUNT_ID = 0x1033DCD1,
|
||||||
|
SLI_PREDEFINED_GPU_COUNT_ID = 0x1033DCD2,
|
||||||
|
SLI_PREDEFINED_GPU_COUNT_DX10_ID = 0x1033DCD3,
|
||||||
|
SLI_PREDEFINED_MODE_ID = 0x1033CEC1,
|
||||||
|
SLI_PREDEFINED_MODE_DX10_ID = 0x1033CEC2,
|
||||||
|
SLI_RENDERING_MODE_ID = 0x1033CED1,
|
||||||
|
VRRFEATUREINDICATOR_ID = 0x1094F157,
|
||||||
|
VRROVERLAYINDICATOR_ID = 0x1095F16F,
|
||||||
|
VRRREQUESTSTATE_ID = 0x1094F1F7,
|
||||||
|
VSYNCSMOOTHAFR_ID = 0x101AE763,
|
||||||
|
VSYNCVRRCONTROL_ID = 0x10A879CE,
|
||||||
|
VSYNC_BEHAVIOR_FLAGS_ID = 0x10FDEC23,
|
||||||
|
WKS_API_STEREO_EYES_EXCHANGE_ID = 0x11AE435C,
|
||||||
|
WKS_API_STEREO_MODE_ID = 0x11E91A61,
|
||||||
|
WKS_MEMORY_ALLOCATION_POLICY_ID = 0x11112233,
|
||||||
|
WKS_STEREO_DONGLE_SUPPORT_ID = 0x112493BD,
|
||||||
|
WKS_STEREO_SUPPORT_ID = 0x11AA9E99,
|
||||||
|
WKS_STEREO_SWAP_MODE_ID = 0x11333333,
|
||||||
|
AO_MODE_ID = 0x00667329,
|
||||||
|
AO_MODE_ACTIVE_ID = 0x00664339,
|
||||||
|
AUTO_LODBIASADJUST_ID = 0x00638E8F,
|
||||||
|
ICAFE_LOGO_CONFIG_ID = 0x00DB1337,
|
||||||
|
LODBIASADJUST_ID = 0x00738E8F,
|
||||||
|
PRERENDERLIMIT_ID = 0x007BA09E,
|
||||||
|
PS_DYNAMIC_TILING_ID = 0x00E5C6C0,
|
||||||
|
PS_SHADERDISKCACHE_ID = 0x00198FFF,
|
||||||
|
PS_TEXFILTER_ANISO_OPTS2_ID = 0x00E73211,
|
||||||
|
PS_TEXFILTER_BILINEAR_IN_ANISO_ID = 0x0084CD70,
|
||||||
|
PS_TEXFILTER_DISABLE_TRILIN_SLOPE_ID = 0x002ECAF2,
|
||||||
|
PS_TEXFILTER_NO_NEG_LODBIAS_ID = 0x0019BB68,
|
||||||
|
QUALITY_ENHANCEMENTS_ID = 0x00CE2691,
|
||||||
|
REFRESH_RATE_OVERRIDE_ID = 0x0064B541,
|
||||||
|
SET_POWER_THROTTLE_FOR_PCIe_COMPLIANCE_ID = 0x00AE785C,
|
||||||
|
SET_VAB_DATA_ID = 0x00AB8687,
|
||||||
|
VSYNCMODE_ID = 0x00A879CF,
|
||||||
|
VSYNCTEARCONTROL_ID = 0x005A375C,
|
||||||
|
TOTAL_DWORD_SETTING_NUM = 80,
|
||||||
|
TOTAL_WSTRING_SETTING_NUM = 4,
|
||||||
|
TOTAL_SETTING_NUM = 84,
|
||||||
|
INVALID_SETTING_ID = 0xFFFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum NvShimSetting : uint
|
||||||
|
{
|
||||||
|
SHIM_RENDERING_MODE_INTEGRATED = 0x00000000,
|
||||||
|
SHIM_RENDERING_MODE_ENABLE = 0x00000001,
|
||||||
|
SHIM_RENDERING_MODE_USER_EDITABLE = 0x00000002,
|
||||||
|
SHIM_RENDERING_MODE_MASK = 0x00000003,
|
||||||
|
SHIM_RENDERING_MODE_VIDEO_MASK = 0x00000004,
|
||||||
|
SHIM_RENDERING_MODE_VARYING_BIT = 0x00000008,
|
||||||
|
SHIM_RENDERING_MODE_AUTO_SELECT = 0x00000010,
|
||||||
|
SHIM_RENDERING_MODE_OVERRIDE_BIT = 0x80000000,
|
||||||
|
SHIM_RENDERING_MODE_NUM_VALUES = 8,
|
||||||
|
SHIM_RENDERING_MODE_DEFAULT = SHIM_RENDERING_MODE_AUTO_SELECT
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum NvThreadControlSetting : uint
|
||||||
|
{
|
||||||
|
OGL_THREAD_CONTROL_ENABLE = 0x00000001,
|
||||||
|
OGL_THREAD_CONTROL_DISABLE = 0x00000002,
|
||||||
|
OGL_THREAD_CONTROL_NUM_VALUES = 2,
|
||||||
|
OGL_THREAD_CONTROL_DEFAULT = 0
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,10 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using osu.Desktop.Security;
|
using osu.Desktop.Security;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -17,9 +15,9 @@ using osu.Framework;
|
|||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Desktop.Windows;
|
using osu.Desktop.Windows;
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using SDL2;
|
using SDL2;
|
||||||
|
|
||||||
@ -111,6 +109,25 @@ namespace osu.Desktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool RestartAppWhenExited()
|
||||||
|
{
|
||||||
|
switch (RuntimeInfo.OS)
|
||||||
|
{
|
||||||
|
case RuntimeInfo.Platform.Windows:
|
||||||
|
Debug.Assert(OperatingSystem.IsWindows());
|
||||||
|
|
||||||
|
// Of note, this is an async method in squirrel that adds an arbitrary delay before returning
|
||||||
|
// likely to ensure the external process is in a good state.
|
||||||
|
//
|
||||||
|
// We're not waiting on that here, but the outro playing before the actual exit should be enough
|
||||||
|
// to cover this.
|
||||||
|
Squirrel.UpdateManager.RestartAppWhenExited().FireAndForget();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.RestartAppWhenExited();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -130,60 +147,16 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
base.SetHost(host);
|
base.SetHost(host);
|
||||||
|
|
||||||
var desktopWindow = (SDL2DesktopWindow)host.Window;
|
|
||||||
|
|
||||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||||
if (iconStream != null)
|
if (iconStream != null)
|
||||||
desktopWindow.SetIconFromStream(iconStream);
|
host.Window.SetIconFromStream(iconStream);
|
||||||
|
|
||||||
desktopWindow.CursorState |= CursorState.Hidden;
|
host.Window.CursorState |= CursorState.Hidden;
|
||||||
desktopWindow.Title = Name;
|
host.Window.Title = Name;
|
||||||
desktopWindow.DragDrop += f =>
|
|
||||||
{
|
|
||||||
// on macOS, URL associations are handled via SDL_DROPFILE events.
|
|
||||||
if (f.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
HandleLink(f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileDrop(new[] { f });
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
|
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
|
||||||
|
|
||||||
private readonly List<string> importableFiles = new List<string>();
|
|
||||||
private ScheduledDelegate? importSchedule;
|
|
||||||
|
|
||||||
private void fileDrop(string[] filePaths)
|
|
||||||
{
|
|
||||||
lock (importableFiles)
|
|
||||||
{
|
|
||||||
importableFiles.AddRange(filePaths);
|
|
||||||
|
|
||||||
Logger.Log($"Adding {filePaths.Length} files for import");
|
|
||||||
|
|
||||||
// File drag drop operations can potentially trigger hundreds or thousands of these calls on some platforms.
|
|
||||||
// In order to avoid spawning multiple import tasks for a single drop operation, debounce a touch.
|
|
||||||
importSchedule?.Cancel();
|
|
||||||
importSchedule = Scheduler.AddDelayed(handlePendingImports, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePendingImports()
|
|
||||||
{
|
|
||||||
lock (importableFiles)
|
|
||||||
{
|
|
||||||
Logger.Log($"Handling batch import of {importableFiles.Count} files");
|
|
||||||
|
|
||||||
string[] paths = importableFiles.ToArray();
|
|
||||||
importableFiles.Clear();
|
|
||||||
|
|
||||||
Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
@ -30,7 +30,19 @@ namespace osu.Desktop
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// run Squirrel first, as the app may exit after these run
|
/*
|
||||||
|
* WARNING: DO NOT PLACE **ANY** CODE ABOVE THE FOLLOWING BLOCK!
|
||||||
|
*
|
||||||
|
* Logic handling Squirrel MUST run before EVERYTHING if you do not want to break it.
|
||||||
|
* To be more precise: Squirrel is internally using a rather... crude method to determine whether it is running under NUnit,
|
||||||
|
* namely by checking loaded assemblies:
|
||||||
|
* https://github.com/clowd/Clowd.Squirrel/blob/24427217482deeeb9f2cacac555525edfc7bd9ac/src/Squirrel/SimpleSplat/PlatformModeDetector.cs#L17-L32
|
||||||
|
*
|
||||||
|
* If it finds ANY assembly from the ones listed above - REGARDLESS of the reason why it is loaded -
|
||||||
|
* the app will then do completely broken things like:
|
||||||
|
* - not creating system shortcuts (as the logic is if'd out if "running tests")
|
||||||
|
* - not exiting after the install / first-update / uninstall hooks are ran (as the `Environment.Exit()` calls are if'd out if "running tests")
|
||||||
|
*/
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
var windowsVersion = Environment.OSVersion.Version;
|
var windowsVersion = Environment.OSVersion.Version;
|
||||||
@ -54,6 +66,11 @@ namespace osu.Desktop
|
|||||||
setupSquirrel();
|
setupSquirrel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NVIDIA profiles are based on the executable name of a process.
|
||||||
|
// Lazer and stable share the same executable name.
|
||||||
|
// Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup.
|
||||||
|
NVAPI.ThreadedOptimisations = NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT;
|
||||||
|
|
||||||
// Back up the cwd before DesktopGameHost changes it
|
// Back up the cwd before DesktopGameHost changes it
|
||||||
string cwd = Environment.CurrentDirectory;
|
string cwd = Environment.CurrentDirectory;
|
||||||
|
|
||||||
@ -85,7 +102,7 @@ namespace osu.Desktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = true }))
|
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null }))
|
||||||
{
|
{
|
||||||
if (!host.IsPrimaryInstance)
|
if (!host.IsPrimaryInstance)
|
||||||
{
|
{
|
||||||
|
@ -10,8 +10,8 @@ using osu.Game;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using Squirrel;
|
|
||||||
using Squirrel.SimpleSplat;
|
using Squirrel.SimpleSplat;
|
||||||
|
using Squirrel.Sources;
|
||||||
using LogLevel = Squirrel.SimpleSplat.LogLevel;
|
using LogLevel = Squirrel.SimpleSplat.LogLevel;
|
||||||
using UpdateManager = osu.Game.Updater.UpdateManager;
|
using UpdateManager = osu.Game.Updater.UpdateManager;
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ namespace osu.Desktop.Updater
|
|||||||
if (localUserInfo?.IsPlaying.Value == true)
|
if (localUserInfo?.IsPlaying.Value == true)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
updateManager ??= new GithubUpdateManager(@"https://github.com/ppy/osu", false, github_token, @"osulazer");
|
updateManager ??= new Squirrel.UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), @"osulazer");
|
||||||
|
|
||||||
var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false);
|
var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false);
|
||||||
|
|
||||||
|
BIN
osu.Desktop/lazer.ico
Executable file → Normal file
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 66 KiB |
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
||||||
@ -8,7 +8,6 @@
|
|||||||
<Title>osu!</Title>
|
<Title>osu!</Title>
|
||||||
<Product>osu!(lazer)</Product>
|
<Product>osu!(lazer)</Product>
|
||||||
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
||||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
|
||||||
<Version>0.0.0</Version>
|
<Version>0.0.0</Version>
|
||||||
<FileVersion>0.0.0</FileVersion>
|
<FileVersion>0.0.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -24,10 +23,10 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
<PackageReference Include="Clowd.Squirrel" Version="2.11.1" />
|
||||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
<PackageReference Include="System.IO.Packaging" Version="8.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -7,11 +7,12 @@
|
|||||||
<authors>ppy Pty Ltd</authors>
|
<authors>ppy Pty Ltd</authors>
|
||||||
<owners>Dean Herbert</owners>
|
<owners>Dean Herbert</owners>
|
||||||
<projectUrl>https://osu.ppy.sh/</projectUrl>
|
<projectUrl>https://osu.ppy.sh/</projectUrl>
|
||||||
<iconUrl>https://puu.sh/tYyXZ/9a01a5d1b0.ico</iconUrl>
|
<iconUrl>https://github.com/ppy/osu/blob/master/assets/lazer-nuget.png?raw=true</iconUrl>
|
||||||
|
<icon>icon.png</icon>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>A free-to-win rhythm game. Rhythm is just a *click* away!</description>
|
<description>A free-to-win rhythm game. Rhythm is just a *click* away!</description>
|
||||||
<releaseNotes>testing</releaseNotes>
|
<releaseNotes>testing</releaseNotes>
|
||||||
<copyright>Copyright (c) 2022 ppy Pty Ltd</copyright>
|
<copyright>Copyright (c) 2024 ppy Pty Ltd</copyright>
|
||||||
<language>en-AU</language>
|
<language>en-AU</language>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
@ -19,5 +20,6 @@
|
|||||||
<file src="**.dll" target="lib\net45\"/>
|
<file src="**.dll" target="lib\net45\"/>
|
||||||
<file src="**.config" target="lib\net45\"/>
|
<file src="**.config" target="lib\net45\"/>
|
||||||
<file src="**.json" target="lib\net45\"/>
|
<file src="**.json" target="lib\net45\"/>
|
||||||
|
<file src="icon.png" target=""/>
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
31
osu.Game.Benchmarks/BenchmarkUnstableRate.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Benchmarks
|
||||||
|
{
|
||||||
|
public class BenchmarkUnstableRate : BenchmarkTest
|
||||||
|
{
|
||||||
|
private List<HitEvent> events = null!;
|
||||||
|
|
||||||
|
public override void SetUp()
|
||||||
|
{
|
||||||
|
base.SetUp();
|
||||||
|
events = new List<HitEvent>();
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; i++)
|
||||||
|
events.Add(new HitEvent(RNG.NextDouble(-200.0, 200.0), RNG.NextDouble(1.0, 2.0), HitResult.Great, new HitObject(), null, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CalculateUnstableRate()
|
||||||
|
{
|
||||||
|
_ = events.CalculateUnstableRate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.4" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
|
||||||
<PackageReference Include="nunit" Version="3.13.3" />
|
<PackageReference Include="nunit" Version="3.14.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- using a different name because package name cannot contain 'catch' -->
|
<!-- using a different name because package name cannot contain 'catch' -->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Android.App;
|
using Android.App;
|
||||||
using osu.Framework.Android;
|
using osu.Framework.Android;
|
||||||
using osu.Game.Tests;
|
using osu.Game.Tests;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Android.props" />
|
<Import Project="..\osu.Android.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-android</TargetFramework>
|
<TargetFramework>net8.0-android</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
|
||||||
<AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName>
|
<AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName>
|
||||||
@ -21,4 +21,4 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>osu.Game.Rulesets.Catch.Tests.iOS</string>
|
<string>osu.Game.Rulesets.Catch.Tests.iOS</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>ppy.osu-Game-Rulesets-Catch-Tests-iOS</string>
|
<string>sh.ppy.catch-ruleset-tests</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
@ -42,4 +42,4 @@
|
|||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0-ios</TargetFramework>
|
<TargetFramework>net8.0-ios</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
|
||||||
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
|
||||||
<AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName>
|
<AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -18,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch.Tests";
|
||||||
|
|
||||||
[TestCase("basic")]
|
[TestCase("basic")]
|
||||||
[TestCase("spinner")]
|
[TestCase("spinner")]
|
||||||
@ -29,6 +27,32 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })]
|
[TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })]
|
||||||
[TestCase("right-bound-hr-offset", new[] { typeof(CatchModHardRock) })]
|
[TestCase("right-bound-hr-offset", new[] { typeof(CatchModHardRock) })]
|
||||||
[TestCase("basic-hyperdash")]
|
[TestCase("basic-hyperdash")]
|
||||||
|
[TestCase("pixel-jump")]
|
||||||
|
[TestCase("tiny-ticks")]
|
||||||
|
[TestCase("v8-tick-distance")]
|
||||||
|
[TestCase("spinner-precision")]
|
||||||
|
[TestCase("37902", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("39206", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("42587")]
|
||||||
|
[TestCase("50859", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("75858", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("103019", new[] { typeof(CatchModHidden) })]
|
||||||
|
[TestCase("104973", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("871815", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("1284935", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock) })]
|
||||||
|
[TestCase("1431386", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("1597806", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("2190499", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("2571731", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("2768615", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock) })]
|
||||||
|
[TestCase("2781126", new[] { typeof(CatchModHidden) })]
|
||||||
|
[TestCase("3152510", new[] { typeof(CatchModDoubleTime) })]
|
||||||
|
[TestCase("3227428", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })]
|
||||||
|
[TestCase("3524302", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
||||||
|
[TestCase("3644427", new[] { typeof(CatchModEasy), typeof(CatchModFlashlight) })]
|
||||||
|
[TestCase("3689906", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
||||||
|
[TestCase("3949367", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
||||||
|
[TestCase("112643")]
|
||||||
public new void Test(string name, params Type[] mods) => base.Test(name, mods);
|
public new void Test(string name, params Type[] mods) => base.Test(name, mods);
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
@ -62,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float conversion_lenience = 2;
|
private const float conversion_lenience = 3;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public readonly CatchHitObject HitObject;
|
public readonly CatchHitObject HitObject;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Difficulty;
|
using osu.Game.Rulesets.Catch.Difficulty;
|
||||||
@ -14,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest
|
public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch.Tests";
|
||||||
|
|
||||||
[TestCase(4.0505463516206195d, 127, "diffcalc-test")]
|
[TestCase(4.0505463516206195d, 127, "diffcalc-test")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Catch.Mods;
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
@ -26,7 +25,8 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
new object[] { LegacyMods.HalfTime, new[] { typeof(CatchModHalfTime) } },
|
new object[] { LegacyMods.HalfTime, new[] { typeof(CatchModHalfTime) } },
|
||||||
new object[] { LegacyMods.Flashlight, new[] { typeof(CatchModFlashlight) } },
|
new object[] { LegacyMods.Flashlight, new[] { typeof(CatchModFlashlight) } },
|
||||||
new object[] { LegacyMods.Autoplay, new[] { typeof(CatchModAutoplay) } },
|
new object[] { LegacyMods.Autoplay, new[] { typeof(CatchModAutoplay) } },
|
||||||
new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(CatchModHardRock), typeof(CatchModDoubleTime) } }
|
new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(CatchModHardRock), typeof(CatchModDoubleTime) } },
|
||||||
|
new object[] { LegacyMods.ScoreV2, new[] { typeof(ModScoreV2) } },
|
||||||
};
|
};
|
||||||
|
|
||||||
[TestCaseSource(nameof(catch_mod_mapping))]
|
[TestCaseSource(nameof(catch_mod_mapping))]
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class CatchRateAdjustedDisplayDifficultyTest
|
||||||
|
{
|
||||||
|
private static IEnumerable<float> difficultyValuesToTest()
|
||||||
|
{
|
||||||
|
for (float i = 0; i <= 10; i += 0.5f)
|
||||||
|
yield return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(difficultyValuesToTest))]
|
||||||
|
public void TestApproachRateIsUnchangedWithRateEqualToOne(float originalApproachRate)
|
||||||
|
{
|
||||||
|
var ruleset = new CatchRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate };
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateBelowOne()
|
||||||
|
{
|
||||||
|
var ruleset = new CatchRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateAboveOne()
|
||||||
|
{
|
||||||
|
var ruleset = new CatchRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Game.Rulesets.Catch.Skinning;
|
using osu.Game.Rulesets.Catch.Skinning;
|
||||||
@ -30,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private class TestLegacySkin : LegacySkin
|
private class TestLegacySkin : LegacySkin
|
||||||
{
|
{
|
||||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage)
|
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> fallbackStore)
|
||||||
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
||||||
: base(skin, null, storage)
|
: base(skin, null, fallbackStore)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1]));
|
AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1]));
|
||||||
AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0]));
|
AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0]));
|
||||||
AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1]));
|
AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1]));
|
||||||
AddAssert("default slider velocity", () => lastObject.SliderVelocityBindable.IsDefault);
|
AddAssert("default slider velocity", () => lastObject.SliderVelocityMultiplierBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
addPlacementSteps(times, positions);
|
addPlacementSteps(times, positions);
|
||||||
addPathCheckStep(times, positions);
|
addPathCheckStep(times, positions);
|
||||||
|
|
||||||
AddAssert("slider velocity changed", () => !lastObject.SliderVelocityBindable.IsDefault);
|
AddAssert("slider velocity changed", () => !lastObject.SliderVelocityMultiplierBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -108,11 +108,11 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
double[] times = { 100, 300 };
|
double[] times = { 100, 300 };
|
||||||
float[] positions = { 200, 300 };
|
float[] positions = { 200, 300 };
|
||||||
addBlueprintStep(times, positions);
|
addBlueprintStep(times, positions);
|
||||||
AddAssert("default slider velocity", () => hitObject.SliderVelocityBindable.IsDefault);
|
AddAssert("default slider velocity", () => hitObject.SliderVelocityMultiplierBindable.IsDefault);
|
||||||
|
|
||||||
addDragStartStep(times[1], positions[1]);
|
addDragStartStep(times[1], positions[1]);
|
||||||
AddMouseMoveStep(times[1], 400);
|
AddMouseMoveStep(times[1], 400);
|
||||||
AddAssert("slider velocity changed", () => !hitObject.SliderVelocityBindable.IsDefault);
|
AddAssert("slider velocity changed", () => !hitObject.SliderVelocityMultiplierBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
|
|
||||||
AddStep("update hit object path", () =>
|
AddStep("update hit object path", () =>
|
||||||
{
|
{
|
||||||
hitObject.Path = new SliderPath(PathType.PerfectCurve, new[]
|
hitObject.Path = new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(100, 100),
|
new Vector2(100, 100),
|
||||||
@ -190,16 +190,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestVertexResampling()
|
public void TestVertexResampling()
|
||||||
{
|
{
|
||||||
addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[]
|
addBlueprintStep(100, 100, new SliderPath(PathType.PERFECT_CURVE, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(100, 100),
|
new Vector2(100, 100),
|
||||||
new Vector2(50, 200),
|
new Vector2(50, 200),
|
||||||
}), 0.5);
|
}), 0.5);
|
||||||
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
|
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
|
||||||
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE);
|
||||||
addAddVertexSteps(150, 150);
|
addAddVertexSteps(150, 150);
|
||||||
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear);
|
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.LINEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () =>
|
private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () =>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -156,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
} while (rng.Next(2) != 0);
|
} while (rng.Next(2) != 0);
|
||||||
|
|
||||||
int length = sliderPath.ControlPoints.Count - start + 1;
|
int length = sliderPath.ControlPoints.Count - start + 1;
|
||||||
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
|
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECT_CURVE : PathType.BEZIER;
|
||||||
} while (rng.Next(3) != 0);
|
} while (rng.Next(3) != 0);
|
||||||
|
|
||||||
if (rng.Next(5) == 0)
|
if (rng.Next(5) == 0)
|
||||||
@ -217,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
foreach (var point in sliderPath.ControlPoints)
|
foreach (var point in sliderPath.ControlPoints)
|
||||||
{
|
{
|
||||||
Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null);
|
Assert.That(point.Type, Is.EqualTo(PathType.LINEAR).Or.Null);
|
||||||
Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests.Mods
|
||||||
|
{
|
||||||
|
public partial class TestSceneCatchModFloatingFruits : ModTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFloating() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new CatchModFloatingFruits(),
|
||||||
|
PassCondition = () => true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests.Mods
|
namespace osu.Game.Rulesets.Catch.Tests.Mods
|
||||||
{
|
{
|
||||||
public partial class TestSceneCatchModPerfect : ModPerfectTestScene
|
public partial class TestSceneCatchModPerfect : ModFailConditionTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
|
|||||||
var stream = new JuiceStream
|
var stream = new JuiceStream
|
||||||
{
|
{
|
||||||
StartTime = 1000,
|
StartTime = 1000,
|
||||||
Path = new SliderPath(PathType.Linear, new[]
|
Path = new SliderPath(PathType.LINEAR, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(100, 0),
|
new Vector2(100, 0),
|
||||||
|