├── .config
└── dotnet-tools.json
├── .editorconfig
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── ci.yml
│ └── pack.yml
├── .gitignore
├── Directory.Build.props
├── LICENCE
├── README.md
├── UseLocalOsu.ps1
├── UseLocalOsu.sh
├── global.json
├── osu-difficulty-calculator.licenseheader
├── osu.Server.DifficultyCalculator.sln
├── osu.Server.DifficultyCalculator.sln.DotSettings
├── osu.Server.DifficultyCalculator
├── AppSettings.cs
├── BeatmapLoader.cs
├── Commands
│ ├── AllCommand.cs
│ ├── BeatmapsCommand.cs
│ ├── BeatmapsStringCommand.cs
│ ├── CalculatorCommand.cs
│ ├── CommandBase.cs
│ ├── FilesCommand.cs
│ └── ProcessingMode.cs
├── Dockerfile
├── Program.cs
├── Reporter.cs
├── ServerDifficultyCalculator.cs
└── osu.Server.DifficultyCalculator.csproj
└── osu.Server.Queues.BeatmapProcessor
├── .dockerignore
├── BeatmapItem.cs
├── BeatmapProcessor.cs
├── Dockerfile
├── Program.cs
└── osu.Server.Queues.BeatmapProcessor.csproj
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "CodeFileSanity": {
6 | "version": "0.0.37",
7 | "commands": [
8 | "CodeFileSanity"
9 | ]
10 | },
11 | "jetbrains.resharper.globaltools": {
12 | "version": "2023.3.3",
13 | "commands": [
14 | "jb"
15 | ]
16 | },
17 | "nvika": {
18 | "version": "4.0.0",
19 | "commands": [
20 | "nvika"
21 | ]
22 | }
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://editorconfig.org
2 | root = true
3 |
4 | [*.cs]
5 | end_of_line = crlf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | #Roslyn naming styles
12 |
13 | #PascalCase for public and protected members
14 | dotnet_naming_style.pascalcase.capitalization = pascal_case
15 | dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
16 | dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event
17 | dotnet_naming_rule.public_members_pascalcase.severity = error
18 | dotnet_naming_rule.public_members_pascalcase.symbols = public_members
19 | dotnet_naming_rule.public_members_pascalcase.style = pascalcase
20 |
21 | #camelCase for private members
22 | dotnet_naming_style.camelcase.capitalization = camel_case
23 |
24 | dotnet_naming_symbols.private_members.applicable_accessibilities = private
25 | dotnet_naming_symbols.private_members.applicable_kinds = property,method,field,event
26 | dotnet_naming_rule.private_members_camelcase.severity = warning
27 | dotnet_naming_rule.private_members_camelcase.symbols = private_members
28 | dotnet_naming_rule.private_members_camelcase.style = camelcase
29 |
30 | dotnet_naming_symbols.local_function.applicable_kinds = local_function
31 | dotnet_naming_rule.local_function_camelcase.severity = warning
32 | dotnet_naming_rule.local_function_camelcase.symbols = local_function
33 | dotnet_naming_rule.local_function_camelcase.style = camelcase
34 |
35 | #all_lower for private and local constants/static readonlys
36 | dotnet_naming_style.all_lower.capitalization = all_lower
37 | dotnet_naming_style.all_lower.word_separator = _
38 |
39 | dotnet_naming_symbols.private_constants.applicable_accessibilities = private
40 | dotnet_naming_symbols.private_constants.required_modifiers = const
41 | dotnet_naming_symbols.private_constants.applicable_kinds = field
42 | dotnet_naming_rule.private_const_all_lower.severity = warning
43 | dotnet_naming_rule.private_const_all_lower.symbols = private_constants
44 | dotnet_naming_rule.private_const_all_lower.style = all_lower
45 |
46 | dotnet_naming_symbols.private_static_readonly.applicable_accessibilities = private
47 | dotnet_naming_symbols.private_static_readonly.required_modifiers = static,readonly
48 | dotnet_naming_symbols.private_static_readonly.applicable_kinds = field
49 | dotnet_naming_rule.private_static_readonly_all_lower.severity = warning
50 | dotnet_naming_rule.private_static_readonly_all_lower.symbols = private_static_readonly
51 | dotnet_naming_rule.private_static_readonly_all_lower.style = all_lower
52 |
53 | dotnet_naming_symbols.local_constants.applicable_kinds = local
54 | dotnet_naming_symbols.local_constants.required_modifiers = const
55 | dotnet_naming_rule.local_const_all_lower.severity = warning
56 | dotnet_naming_rule.local_const_all_lower.symbols = local_constants
57 | dotnet_naming_rule.local_const_all_lower.style = all_lower
58 |
59 | #ALL_UPPER for non private constants/static readonlys
60 | dotnet_naming_style.all_upper.capitalization = all_upper
61 | dotnet_naming_style.all_upper.word_separator = _
62 |
63 | dotnet_naming_symbols.public_constants.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
64 | dotnet_naming_symbols.public_constants.required_modifiers = const
65 | dotnet_naming_symbols.public_constants.applicable_kinds = field
66 | dotnet_naming_rule.public_const_all_upper.severity = warning
67 | dotnet_naming_rule.public_const_all_upper.symbols = public_constants
68 | dotnet_naming_rule.public_const_all_upper.style = all_upper
69 |
70 | dotnet_naming_symbols.public_static_readonly.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
71 | dotnet_naming_symbols.public_static_readonly.required_modifiers = static,readonly
72 | dotnet_naming_symbols.public_static_readonly.applicable_kinds = field
73 | dotnet_naming_rule.public_static_readonly_all_upper.severity = warning
74 | dotnet_naming_rule.public_static_readonly_all_upper.symbols = public_static_readonly
75 | dotnet_naming_rule.public_static_readonly_all_upper.style = all_upper
76 |
77 | #Roslyn formating options
78 |
79 | #Formatting - indentation options
80 | csharp_indent_case_contents = true
81 | csharp_indent_case_contents_when_block = false
82 | csharp_indent_labels = one_less_than_current
83 | csharp_indent_switch_labels = true
84 |
85 | #Formatting - new line options
86 | csharp_new_line_before_catch = true
87 | csharp_new_line_before_else = true
88 | csharp_new_line_before_finally = true
89 | csharp_new_line_before_open_brace = all
90 | #csharp_new_line_before_members_in_anonymous_types = true
91 | #csharp_new_line_before_members_in_object_initializers = true # Currently no effect in VS/dotnet format (16.4), and makes Rider confusing
92 | csharp_new_line_between_query_expression_clauses = true
93 |
94 | #Formatting - organize using options
95 | dotnet_sort_system_directives_first = true
96 |
97 | #Formatting - spacing options
98 | csharp_space_after_cast = false
99 | csharp_space_after_colon_in_inheritance_clause = true
100 | csharp_space_after_keywords_in_control_flow_statements = true
101 | csharp_space_before_colon_in_inheritance_clause = true
102 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
103 | csharp_space_between_method_call_name_and_opening_parenthesis = false
104 | csharp_space_between_method_call_parameter_list_parentheses = false
105 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
106 | csharp_space_between_method_declaration_parameter_list_parentheses = false
107 |
108 | #Formatting - wrapping options
109 | csharp_preserve_single_line_blocks = true
110 | csharp_preserve_single_line_statements = true
111 |
112 | #Roslyn language styles
113 |
114 | #Style - this. qualification
115 | dotnet_style_qualification_for_field = false:warning
116 | dotnet_style_qualification_for_property = false:warning
117 | dotnet_style_qualification_for_method = false:warning
118 | dotnet_style_qualification_for_event = false:warning
119 |
120 | #Style - type names
121 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning
122 | dotnet_style_predefined_type_for_member_access = true:warning
123 | csharp_style_var_when_type_is_apparent = true:none
124 | csharp_style_var_for_built_in_types = true:none
125 | csharp_style_var_elsewhere = true:silent
126 |
127 | #Style - modifiers
128 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
129 | csharp_preferred_modifier_order = public,private,protected,internal,new,abstract,virtual,sealed,override,static,readonly,extern,unsafe,volatile,async:warning
130 |
131 | #Style - parentheses
132 | # Skipped because roslyn cannot separate +-*/ with << >>
133 |
134 | #Style - expression bodies
135 | csharp_style_expression_bodied_accessors = true:warning
136 | csharp_style_expression_bodied_constructors = false:none
137 | csharp_style_expression_bodied_indexers = true:warning
138 | csharp_style_expression_bodied_methods = false:silent
139 | csharp_style_expression_bodied_operators = true:warning
140 | csharp_style_expression_bodied_properties = true:warning
141 | csharp_style_expression_bodied_local_functions = true:silent
142 |
143 | #Style - expression preferences
144 | dotnet_style_object_initializer = true:warning
145 | dotnet_style_collection_initializer = true:warning
146 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
147 | dotnet_style_prefer_auto_properties = true:warning
148 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
149 | dotnet_style_prefer_conditional_expression_over_return = true:silent
150 | dotnet_style_prefer_compound_assignment = true:warning
151 |
152 | #Style - null/type checks
153 | dotnet_style_coalesce_expression = true:warning
154 | dotnet_style_null_propagation = true:warning
155 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning
156 | csharp_style_pattern_matching_over_as_with_null_check = true:warning
157 | csharp_style_throw_expression = true:silent
158 | csharp_style_conditional_delegate_call = true:warning
159 |
160 | #Style - unused
161 | dotnet_style_readonly_field = true:silent
162 | dotnet_code_quality_unused_parameters = non_public:silent
163 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent
164 | csharp_style_unused_value_assignment_preference = discard_variable:warning
165 |
166 | #Style - variable declaration
167 | csharp_style_inlined_variable_declaration = true:warning
168 | csharp_style_deconstructed_variable_declaration = true:warning
169 |
170 | #Style - other C# 7.x features
171 | dotnet_style_prefer_inferred_tuple_names = true:warning
172 | csharp_prefer_simple_default_expression = true:warning
173 | csharp_style_pattern_local_over_anonymous_function = true:warning
174 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
175 |
176 | #Style - C# 8 features
177 | csharp_prefer_static_local_function = true:warning
178 | csharp_prefer_simple_using_statement = true:silent
179 | csharp_style_prefer_index_operator = true:warning
180 | csharp_style_prefer_range_operator = true:warning
181 | csharp_style_prefer_switch_expression = false:none
182 |
183 | #Supressing roslyn built-in analyzers
184 | # Suppress: EC112
185 |
186 | #Private method is unused
187 | dotnet_diagnostic.IDE0051.severity = silent
188 | #Private member is unused
189 | dotnet_diagnostic.IDE0052.severity = silent
190 |
191 | #Rules for disposable
192 | dotnet_diagnostic.IDE0067.severity = none
193 | dotnet_diagnostic.IDE0068.severity = none
194 | dotnet_diagnostic.IDE0069.severity = none
195 |
196 | #Disable operator overloads requiring alternate named methods
197 | dotnet_diagnostic.CA2225.severity = none
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Autodetect text files and ensure that we normalise their
2 | # line endings to lf internally. When checked out they may
3 | # use different line endings.
4 | * text=auto
5 |
6 | # Check out with crlf (Windows) line endings
7 | *.sln text eol=crlf
8 | *.csproj text eol=crlf
9 | *.cs text diff=csharp eol=crlf
10 | *.resx text eol=crlf
11 | *.vsixmanifest text eol=crlf
12 | packages.config text eol=crlf
13 | App.config text eol=crlf
14 | *.bat text eol=crlf
15 | *.cmd text eol=crlf
16 | *.snippet text eol=crlf
17 | *.manifest text eol=crlf
18 |
19 | # Check out with lf (UNIX) line endings
20 | *.sh text eol=lf
21 | .gitignore text eol=lf
22 | .gitattributes text eol=lf
23 | *.md text eol=lf
24 | .travis.yml text eol=lf
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: nuget
4 | directory: "/"
5 | schedule:
6 | interval: monthly
7 | time: "17:00"
8 | open-pull-requests-limit: 0 # disabled until https://github.com/dependabot/dependabot-core/issues/369 is resolved.
9 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 | name: Continuous Integration
3 | concurrency:
4 | group: ${{ github.workflow }}-${{ github.ref }}
5 | cancel-in-progress: true
6 |
7 | jobs:
8 | inspect-code:
9 | name: Code Quality
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 |
15 | - name: Install .NET 8.0.x
16 | uses: actions/setup-dotnet@v4
17 | with:
18 | dotnet-version: "8.0.x"
19 |
20 | - name: Restore Tools
21 | run: dotnet tool restore
22 |
23 | - name: Restore Packages
24 | run: dotnet restore
25 |
26 | - name: CodeFileSanity
27 | run: |
28 | # TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
29 | # FIXME: Suppress warnings from templates project
30 | exit_code=0
31 | while read -r line; do
32 | if [[ ! -z "$line" ]]; then
33 | echo "::error::$line"
34 | exit_code=1
35 | fi
36 | done <<< $(dotnet codefilesanity)
37 | exit $exit_code
38 |
39 | - name: InspectCode
40 | run: dotnet jb inspectcode $(pwd)/osu.Server.DifficultyCalculator.sln --build --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
41 |
42 | - name: NVika
43 | run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors
44 |
--------------------------------------------------------------------------------
/.github/workflows/pack.yml:
--------------------------------------------------------------------------------
1 | name: Build and publish
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-diffcalc
8 | tags:
9 | - '*'
10 |
11 | jobs:
12 | push_to_registry:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | include:
17 | - image: pppy/osu-difficulty-calculator
18 | context: ./
19 | file: ./osu.Server.DifficultyCalculator/Dockerfile
20 | - image: pppy/osu-queue-beatmap-processor
21 | context: ./
22 | file: ./osu.Server.Queues.BeatmapProcessor/Dockerfile
23 | steps:
24 | -
25 | name: Checkout
26 | uses: actions/checkout@v2
27 | -
28 | name: Docker meta
29 | id: meta
30 | uses: docker/metadata-action@v3
31 | with:
32 | # list of Docker images to use as base name for tags
33 | images: |
34 | ${{ matrix.image }}
35 | # generate Docker tags based on the following events/attributes
36 | # on tag event: tag using git tag, and as latest if the tag doesn't contain hyphens (pre-releases)
37 | # on push event: tag using git sha, branch name and as latest-dev
38 | tags: |
39 | type=raw,value=latest,enable=${{ github.ref_type == 'tag' && !contains(github.ref_name, '-') }}
40 | type=raw,value=latest-dev,enable=${{ github.ref_type == 'branch' && github.ref_name == 'master' }}
41 | type=raw,value=${{ github.ref_name }}
42 | type=raw,value=${{ github.sha }},enable=${{ github.ref_type == 'branch' }}
43 | flavor: |
44 | latest=false
45 | -
46 | name: Set up Docker Buildx
47 | uses: docker/setup-buildx-action@v1
48 | -
49 | name: Login to DockerHub
50 | uses: docker/login-action@v1
51 | with:
52 | username: ${{ secrets.DOCKER_USERNAME }}
53 | password: ${{ secrets.DOCKER_PASSWORD }}
54 | -
55 | name: Build and push
56 | uses: docker/build-push-action@v2
57 | with:
58 | context: ${{ matrix.context }}
59 | file: ${{ matrix.file }}
60 | platforms: linux/amd64
61 | push: true
62 | tags: ${{ steps.meta.outputs.tags }}
63 | labels: ${{ steps.meta.outputs.labels }}
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | ### Cake ###
14 | tools/**
15 | build/tools/**
16 |
17 | # Build results
18 | bin/[Dd]ebug/
19 | [Dd]ebugPublic/
20 | [Rr]elease/
21 | [Rr]eleases/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # DNX
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 |
50 | *_i.c
51 | *_p.c
52 | *_i.h
53 | *.ilk
54 | *.meta
55 | *.obj
56 | *.pch
57 | *.pdb
58 | *.pgc
59 | *.pgd
60 | *.rsp
61 | *.sbr
62 | *.tlb
63 | *.tli
64 | *.tlh
65 | *.tmp
66 | *.tmp_proj
67 | *.log
68 | *.vspscc
69 | *.vssscc
70 | .builds
71 | *.pidb
72 | *.svclog
73 | *.scc
74 |
75 | # Chutzpah Test files
76 | _Chutzpah*
77 |
78 | # Visual C++ cache files
79 | ipch/
80 | *.aps
81 | *.ncb
82 | *.opendb
83 | *.opensdf
84 | *.sdf
85 | *.cachefile
86 | *.VC.db
87 | *.VC.VC.opendb
88 |
89 | # Visual Studio profiler
90 | *.psess
91 | *.vsp
92 | *.vspx
93 | *.sap
94 |
95 | # TFS 2012 Local Workspace
96 | $tf/
97 |
98 | # Guidance Automation Toolkit
99 | *.gpState
100 |
101 | # ReSharper is a .NET coding add-in
102 | _ReSharper*/
103 | *.[Rr]e[Ss]harper
104 | *.DotSettings.user
105 | inspectcode
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # NCrunch
117 | _NCrunch_*
118 | .*crunch*.local.xml
119 | nCrunchTemp_*
120 |
121 | # MightyMoose
122 | *.mm.*
123 | AutoTest.Net/
124 |
125 | # Web workbench (sass)
126 | .sass-cache/
127 |
128 | # Installshield output folder
129 | [Ee]xpress/
130 |
131 | # DocProject is a documentation generator add-in
132 | DocProject/buildhelp/
133 | DocProject/Help/*.HxT
134 | DocProject/Help/*.HxC
135 | DocProject/Help/*.hhc
136 | DocProject/Help/*.hhk
137 | DocProject/Help/*.hhp
138 | DocProject/Help/Html2
139 | DocProject/Help/html
140 |
141 | # Click-Once directory
142 | publish/
143 |
144 | # Publish Web Output
145 | *.[Pp]ublish.xml
146 | *.azurePubxml
147 | # TODO: Comment the next line if you want to checkin your web deploy settings
148 | # but database connection strings (with potential passwords) will be unencrypted
149 | *.pubxml
150 | *.publishproj
151 |
152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
153 | # checkin your Azure Web App publish settings, but sensitive information contained
154 | # in these scripts will be unencrypted
155 | PublishScripts/
156 |
157 | # NuGet Packages
158 | *.nupkg
159 | # The packages folder can be ignored because of Package Restore
160 | **/packages/*
161 | # except build/, which is used as an MSBuild target.
162 | !**/packages/build/
163 | # Uncomment if necessary however generally it will be regenerated when needed
164 | #!**/packages/repositories.config
165 | # NuGet v3's project.json files produces more ignoreable files
166 | *.nuget.props
167 | *.nuget.targets
168 |
169 | # Microsoft Azure Build Output
170 | csx/
171 | *.build.csdef
172 |
173 | # Microsoft Azure Emulator
174 | ecf/
175 | rcf/
176 |
177 | # Windows Store app package directories and files
178 | AppPackages/
179 | BundleArtifacts/
180 | Package.StoreAssociation.xml
181 | _pkginfo.txt
182 |
183 | # Visual Studio cache files
184 | # files ending in .cache can be ignored
185 | *.[Cc]ache
186 | # but keep track of directories ending in .cache
187 | !*.[Cc]ache/
188 |
189 | # Others
190 | ClientBin/
191 | ~$*
192 | *~
193 | *.dbmdl
194 | *.dbproj.schemaview
195 | *.pfx
196 | *.publishsettings
197 | node_modules/
198 | orleans.codegen.cs
199 |
200 | # Since there are multiple workflows, uncomment next line to ignore bower_components
201 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
202 | #bower_components/
203 |
204 | # RIA/Silverlight projects
205 | Generated_Code/
206 |
207 | # Backup & report files from converting an old project file
208 | # to a newer Visual Studio version. Backup files are not needed,
209 | # because we have git ;-)
210 | _UpgradeReport_Files/
211 | Backup*/
212 | UpgradeLog*.XML
213 | UpgradeLog*.htm
214 |
215 | # SQL Server files
216 | *.mdf
217 | *.ldf
218 |
219 | # Business Intelligence projects
220 | *.rdl.data
221 | *.bim.layout
222 | *.bim_*.settings
223 |
224 | # Microsoft Fakes
225 | FakesAssemblies/
226 |
227 | # GhostDoc plugin setting file
228 | *.GhostDoc.xml
229 |
230 | # Node.js Tools for Visual Studio
231 | .ntvs_analysis.dat
232 |
233 | # Visual Studio 6 build log
234 | *.plg
235 |
236 | # Visual Studio 6 workspace options file
237 | *.opt
238 |
239 | # Visual Studio LightSwitch build output
240 | **/*.HTMLClient/GeneratedArtifacts
241 | **/*.DesktopClient/GeneratedArtifacts
242 | **/*.DesktopClient/ModelManifest.xml
243 | **/*.Server/GeneratedArtifacts
244 | **/*.Server/ModelManifest.xml
245 | _Pvt_Extensions
246 |
247 | # Paket dependency manager
248 | .paket/paket.exe
249 | paket-files/
250 |
251 | # FAKE - F# Make
252 | .fake/
253 |
254 | # JetBrains Rider
255 | .idea/
256 | *.sln.iml
257 |
258 | # CodeRush
259 | .cr/
260 |
261 | # Python Tools for Visual Studio (PTVS)
262 | __pycache__/
263 | *.pyc
264 | Staging/
265 |
266 | inspectcodereport.xml
267 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 7
4 |
5 |
6 |
7 | osu-difficulty-calculator.licenseheader
8 |
9 |
10 |
11 | ppy Pty Ltd
12 | Copyright (c) 2022 ppy Pty Ltd
13 |
14 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 ppy Pty Ltd .
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # osu-difficulty-calculator [](https://discord.gg/ppy)
2 |
3 | Difficulty calculation server for [osu!](https://osu.ppy.sh).
4 |
5 | For more detailed information see per-component READMEs.
6 |
7 | # Current Versions
8 |
9 | This is part of a group of projects which are used in live deployments where the deployed version is critical to producing correct results. The `master` branch tracks ongoing developments. If looking to use the correct version for matching live values, please [consult this wiki page](https://github.com/ppy/osu-infrastructure/wiki/Star-Rating-and-Performance-Points) for the latest information.
10 |
11 | # Requirements
12 |
13 | - A desktop platform that can compile .NET 6.0. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
14 |
15 | # Getting Started
16 | - Clone the repository (`git clone https://github.com/ppy/osu-difficulty-calculator`)
17 | - Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`)
18 |
19 | # Contributing
20 |
21 | Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-osu-difficulty-calculator/issues).
22 |
23 | Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
24 |
25 | # Licence
26 |
27 | The osu! client code, framework, and server-side components are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
28 |
29 | Please note that this *does not cover* the usage of the "osu!" or "ppy" branding in any software, resources, advertising or promotion, as this is protected by trademark law.
30 |
31 | Please also note that game resources are covered by a separate licence. Please see the [ppy/osu-resources](https://github.com/ppy/osu-resources) repository for clarifications.
32 |
33 |
--------------------------------------------------------------------------------
/UseLocalOsu.ps1:
--------------------------------------------------------------------------------
1 | # Run this script to use a local copy of osu rather than fetching it from nuget.
2 | # It expects the osu directory to be at the same level as the osu-tools directory
3 |
4 |
5 | $CSPROJ="osu.Server.DifficultyCalculator/osu.Server.DifficultyCalculator.csproj"
6 | $SLN="osu.Server.DifficultyCalculator.sln"
7 |
8 | $DEPENDENCIES=@(
9 | "..\osu\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj"
10 | "..\osu\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj"
11 | "..\osu\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj"
12 | "..\osu\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj"
13 | "..\osu\osu.Game\osu.Game.csproj"
14 | )
15 |
16 |
17 | dotnet remove $CSPROJ package ppy.osu.Game
18 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Osu
19 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Taiko
20 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Catch
21 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Mania
22 |
23 | dotnet sln $SLN add $DEPENDENCIES
24 | dotnet add $CSPROJ reference $DEPENDENCIES
25 |
--------------------------------------------------------------------------------
/UseLocalOsu.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Run this script to use a local copy of osu rather than fetching it from nuget.
4 | # It expects the osu directory to be at the same level as the osu-tools directory
5 |
6 |
7 | CSPROJ="osu.Server.DifficultyCalculator/osu.Server.DifficultyCalculator.csproj"
8 | SLN="osu.Server.DifficultyCalculator.sln"
9 |
10 | DEPENDENCIES="../osu/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
11 | ../osu/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
12 | ../osu/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
13 | ../osu/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
14 | ../osu/osu.Game/osu.Game.csproj"
15 |
16 |
17 | dotnet remove $CSPROJ package ppy.osu.Game
18 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Osu
19 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Taiko
20 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Catch
21 | dotnet remove $CSPROJ package ppy.osu.Game.Rulesets.Mania
22 |
23 | dotnet sln $SLN add $DEPENDENCIES
24 | dotnet add $CSPROJ reference $DEPENDENCIES
25 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.100",
4 | "rollForward": "latestFeature",
5 | "allowPrerelease": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/osu-difficulty-calculator.licenseheader:
--------------------------------------------------------------------------------
1 | extensions: .cs
2 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
3 | // See the LICENCE file in the repository root for full licence text.
4 |
5 | extensions: .xml .config .xsd
6 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Server.DifficultyCalculator", "osu.Server.DifficultyCalculator\osu.Server.DifficultyCalculator.csproj", "{B86E5C04-C9B1-4276-AB15-4F6C7F4A1343}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Server.Queues.BeatmapProcessor", "osu.Server.Queues.BeatmapProcessor\osu.Server.Queues.BeatmapProcessor.csproj", "{EEB73785-2BD4-4AD9-87B4-B7835842C453}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {B86E5C04-C9B1-4276-AB15-4F6C7F4A1343}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {B86E5C04-C9B1-4276-AB15-4F6C7F4A1343}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {B86E5C04-C9B1-4276-AB15-4F6C7F4A1343}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {B86E5C04-C9B1-4276-AB15-4F6C7F4A1343}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {EEB73785-2BD4-4AD9-87B4-B7835842C453}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {EEB73785-2BD4-4AD9-87B4-B7835842C453}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {EEB73785-2BD4-4AD9-87B4-B7835842C453}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {EEB73785-2BD4-4AD9-87B4-B7835842C453}.Release|Any CPU.Build.0 = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | True
5 | True
6 |
7 | SOLUTION
8 | WARNING
9 | WARNING
10 | WARNING
11 | HINT
12 | HINT
13 | WARNING
14 | WARNING
15 | True
16 | WARNING
17 | WARNING
18 | HINT
19 | DO_NOT_SHOW
20 | HINT
21 | DO_NOT_SHOW
22 | DO_NOT_SHOW
23 | WARNING
24 | WARNING
25 | WARNING
26 | WARNING
27 | WARNING
28 | WARNING
29 | WARNING
30 | WARNING
31 | WARNING
32 | WARNING
33 | WARNING
34 | WARNING
35 | WARNING
36 | WARNING
37 | WARNING
38 | WARNING
39 | WARNING
40 | WARNING
41 | WARNING
42 | WARNING
43 | WARNING
44 | WARNING
45 | WARNING
46 | WARNING
47 | WARNING
48 | WARNING
49 | WARNING
50 | WARNING
51 | WARNING
52 | HINT
53 | WARNING
54 | HINT
55 | SUGGESTION
56 | HINT
57 | HINT
58 | HINT
59 | WARNING
60 | WARNING
61 | WARNING
62 | WARNING
63 | WARNING
64 | WARNING
65 | WARNING
66 | WARNING
67 | WARNING
68 | WARNING
69 | WARNING
70 | WARNING
71 | WARNING
72 | HINT
73 | WARNING
74 | HINT
75 | WARNING
76 | DO_NOT_SHOW
77 | WARNING
78 | WARNING
79 | WARNING
80 | WARNING
81 | WARNING
82 | WARNING
83 | WARNING
84 | WARNING
85 | HINT
86 | WARNING
87 | DO_NOT_SHOW
88 | WARNING
89 | HINT
90 | DO_NOT_SHOW
91 | HINT
92 | HINT
93 | ERROR
94 | WARNING
95 | HINT
96 | HINT
97 | HINT
98 | WARNING
99 | WARNING
100 | HINT
101 | DO_NOT_SHOW
102 | HINT
103 | HINT
104 | HINT
105 | HINT
106 | WARNING
107 | WARNING
108 | DO_NOT_SHOW
109 | WARNING
110 | WARNING
111 | WARNING
112 | WARNING
113 | WARNING
114 | WARNING
115 | WARNING
116 | WARNING
117 | WARNING
118 | WARNING
119 | WARNING
120 | WARNING
121 | HINT
122 | WARNING
123 | HINT
124 | HINT
125 | HINT
126 | DO_NOT_SHOW
127 | HINT
128 | HINT
129 | WARNING
130 | WARNING
131 | HINT
132 | HINT
133 | WARNING
134 | WARNING
135 | WARNING
136 | WARNING
137 | HINT
138 | DO_NOT_SHOW
139 | DO_NOT_SHOW
140 | DO_NOT_SHOW
141 | WARNING
142 | WARNING
143 | WARNING
144 | WARNING
145 | WARNING
146 | SUGGESTION
147 | WARNING
148 | WARNING
149 | WARNING
150 | ERROR
151 | WARNING
152 | WARNING
153 | HINT
154 | WARNING
155 | WARNING
156 | WARNING
157 | WARNING
158 | WARNING
159 | WARNING
160 | WARNING
161 | WARNING
162 | WARNING
163 | WARNING
164 | WARNING
165 | WARNING
166 | WARNING
167 | WARNING
168 | WARNING
169 | WARNING
170 | WARNING
171 | WARNING
172 | WARNING
173 | WARNING
174 | WARNING
175 | WARNING
176 | WARNING
177 | WARNING
178 | WARNING
179 | WARNING
180 | WARNING
181 | WARNING
182 | WARNING
183 | WARNING
184 | WARNING
185 | WARNING
186 | WARNING
187 | WARNING
188 | WARNING
189 | WARNING
190 | WARNING
191 | WARNING
192 | WARNING
193 | WARNING
194 | WARNING
195 | WARNING
196 | WARNING
197 | WARNING
198 | WARNING
199 | WARNING
200 | WARNING
201 | WARNING
202 | WARNING
203 | WARNING
204 | WARNING
205 | HINT
206 | WARNING
207 | WARNING
208 | DO_NOT_SHOW
209 | DO_NOT_SHOW
210 | DO_NOT_SHOW
211 | WARNING
212 | WARNING
213 | WARNING
214 | WARNING
215 | WARNING
216 | HINT
217 | WARNING
218 | HINT
219 | HINT
220 | HINT
221 | HINT
222 | HINT
223 | HINT
224 | HINT
225 | HINT
226 | HINT
227 | HINT
228 | DO_NOT_SHOW
229 | WARNING
230 | WARNING
231 | WARNING
232 | WARNING
233 | WARNING
234 | WARNING
235 |
236 | True
237 | WARNING
238 | WARNING
239 | WARNING
240 | WARNING
241 | WARNING
242 | HINT
243 | HINT
244 | WARNING
245 | WARNING
246 | <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile>
247 | Code Cleanup (peppy)
248 | RequiredForMultiline
249 | RequiredForMultiline
250 | RequiredForMultiline
251 | RequiredForMultiline
252 | RequiredForMultiline
253 | RequiredForMultiline
254 | RequiredForMultiline
255 | RequiredForMultiline
256 | Explicit
257 | ExpressionBody
258 | BlockBody
259 | True
260 | NEXT_LINE
261 | True
262 | True
263 | True
264 | True
265 | True
266 | True
267 | True
268 | True
269 | NEXT_LINE
270 | 1
271 | 1
272 | NEXT_LINE
273 | MULTILINE
274 | True
275 | True
276 | True
277 | True
278 | NEXT_LINE
279 | 1
280 | 1
281 | True
282 | NEXT_LINE
283 | NEVER
284 | NEVER
285 | True
286 | False
287 | True
288 | NEVER
289 | False
290 | False
291 | True
292 | False
293 | False
294 | True
295 | True
296 | False
297 | False
298 | CHOP_IF_LONG
299 | True
300 | 200
301 | CHOP_IF_LONG
302 | False
303 | False
304 | AABB
305 | API
306 | BPM
307 | GC
308 | GL
309 | GLSL
310 | HID
311 | HTML
312 | HUD
313 | ID
314 | IL
315 | IOS
316 | IP
317 | IPC
318 | JIT
319 | LTRB
320 | MD5
321 | NS
322 | OS
323 | PM
324 | RGB
325 | RNG
326 | SHA
327 | SRGB
328 | TK
329 | SS
330 | PP
331 | GMT
332 | QAT
333 | BNG
334 | UI
335 | False
336 | HINT
337 | <?xml version="1.0" encoding="utf-16"?>
338 | <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
339 | <TypePattern DisplayName="COM interfaces or structs">
340 | <TypePattern.Match>
341 | <Or>
342 | <And>
343 | <Kind Is="Interface" />
344 | <Or>
345 | <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
346 | <HasAttribute Name="System.Runtime.InteropServices.ComImport" />
347 | </Or>
348 | </And>
349 | <Kind Is="Struct" />
350 | </Or>
351 | </TypePattern.Match>
352 | </TypePattern>
353 | <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
354 | <TypePattern.Match>
355 | <And>
356 | <Kind Is="Class" />
357 | <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
358 | </And>
359 | </TypePattern.Match>
360 | <Entry DisplayName="Setup/Teardown Methods">
361 | <Entry.Match>
362 | <And>
363 | <Kind Is="Method" />
364 | <Or>
365 | <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
366 | <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
367 | <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" />
368 | <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" />
369 | </Or>
370 | </And>
371 | </Entry.Match>
372 | </Entry>
373 | <Entry DisplayName="All other members" />
374 | <Entry Priority="100" DisplayName="Test Methods">
375 | <Entry.Match>
376 | <And>
377 | <Kind Is="Method" />
378 | <HasAttribute Name="NUnit.Framework.TestAttribute" />
379 | </And>
380 | </Entry.Match>
381 | <Entry.SortBy>
382 | <Name />
383 | </Entry.SortBy>
384 | </Entry>
385 | </TypePattern>
386 | <TypePattern DisplayName="Default Pattern">
387 | <Group DisplayName="Fields/Properties">
388 | <Group DisplayName="Public Fields">
389 | <Entry DisplayName="Constant Fields">
390 | <Entry.Match>
391 | <And>
392 | <Access Is="Public" />
393 | <Or>
394 | <Kind Is="Constant" />
395 | <Readonly />
396 | <And>
397 | <Static />
398 | <Readonly />
399 | </And>
400 | </Or>
401 | </And>
402 | </Entry.Match>
403 | </Entry>
404 | <Entry DisplayName="Static Fields">
405 | <Entry.Match>
406 | <And>
407 | <Access Is="Public" />
408 | <Static />
409 | <Not>
410 | <Readonly />
411 | </Not>
412 | <Kind Is="Field" />
413 | </And>
414 | </Entry.Match>
415 | </Entry>
416 | <Entry DisplayName="Normal Fields">
417 | <Entry.Match>
418 | <And>
419 | <Access Is="Public" />
420 | <Not>
421 | <Or>
422 | <Static />
423 | <Readonly />
424 | </Or>
425 | </Not>
426 | <Kind Is="Field" />
427 | </And>
428 | </Entry.Match>
429 | </Entry>
430 | </Group>
431 | <Entry DisplayName="Public Properties">
432 | <Entry.Match>
433 | <And>
434 | <Access Is="Public" />
435 | <Kind Is="Property" />
436 | </And>
437 | </Entry.Match>
438 | </Entry>
439 | <Group DisplayName="Internal Fields">
440 | <Entry DisplayName="Constant Fields">
441 | <Entry.Match>
442 | <And>
443 | <Access Is="Internal" />
444 | <Or>
445 | <Kind Is="Constant" />
446 | <Readonly />
447 | <And>
448 | <Static />
449 | <Readonly />
450 | </And>
451 | </Or>
452 | </And>
453 | </Entry.Match>
454 | </Entry>
455 | <Entry DisplayName="Static Fields">
456 | <Entry.Match>
457 | <And>
458 | <Access Is="Internal" />
459 | <Static />
460 | <Not>
461 | <Readonly />
462 | </Not>
463 | <Kind Is="Field" />
464 | </And>
465 | </Entry.Match>
466 | </Entry>
467 | <Entry DisplayName="Normal Fields">
468 | <Entry.Match>
469 | <And>
470 | <Access Is="Internal" />
471 | <Not>
472 | <Or>
473 | <Static />
474 | <Readonly />
475 | </Or>
476 | </Not>
477 | <Kind Is="Field" />
478 | </And>
479 | </Entry.Match>
480 | </Entry>
481 | </Group>
482 | <Entry DisplayName="Internal Properties">
483 | <Entry.Match>
484 | <And>
485 | <Access Is="Internal" />
486 | <Kind Is="Property" />
487 | </And>
488 | </Entry.Match>
489 | </Entry>
490 | <Group DisplayName="Protected Fields">
491 | <Entry DisplayName="Constant Fields">
492 | <Entry.Match>
493 | <And>
494 | <Access Is="Protected" />
495 | <Or>
496 | <Kind Is="Constant" />
497 | <Readonly />
498 | <And>
499 | <Static />
500 | <Readonly />
501 | </And>
502 | </Or>
503 | </And>
504 | </Entry.Match>
505 | </Entry>
506 | <Entry DisplayName="Static Fields">
507 | <Entry.Match>
508 | <And>
509 | <Access Is="Protected" />
510 | <Static />
511 | <Not>
512 | <Readonly />
513 | </Not>
514 | <Kind Is="Field" />
515 | </And>
516 | </Entry.Match>
517 | </Entry>
518 | <Entry DisplayName="Normal Fields">
519 | <Entry.Match>
520 | <And>
521 | <Access Is="Protected" />
522 | <Not>
523 | <Or>
524 | <Static />
525 | <Readonly />
526 | </Or>
527 | </Not>
528 | <Kind Is="Field" />
529 | </And>
530 | </Entry.Match>
531 | </Entry>
532 | </Group>
533 | <Entry DisplayName="Protected Properties">
534 | <Entry.Match>
535 | <And>
536 | <Access Is="Protected" />
537 | <Kind Is="Property" />
538 | </And>
539 | </Entry.Match>
540 | </Entry>
541 | <Group DisplayName="Private Fields">
542 | <Entry DisplayName="Constant Fields">
543 | <Entry.Match>
544 | <And>
545 | <Access Is="Private" />
546 | <Or>
547 | <Kind Is="Constant" />
548 | <Readonly />
549 | <And>
550 | <Static />
551 | <Readonly />
552 | </And>
553 | </Or>
554 | </And>
555 | </Entry.Match>
556 | </Entry>
557 | <Entry DisplayName="Static Fields">
558 | <Entry.Match>
559 | <And>
560 | <Access Is="Private" />
561 | <Static />
562 | <Not>
563 | <Readonly />
564 | </Not>
565 | <Kind Is="Field" />
566 | </And>
567 | </Entry.Match>
568 | </Entry>
569 | <Entry DisplayName="Normal Fields">
570 | <Entry.Match>
571 | <And>
572 | <Access Is="Private" />
573 | <Not>
574 | <Or>
575 | <Static />
576 | <Readonly />
577 | </Or>
578 | </Not>
579 | <Kind Is="Field" />
580 | </And>
581 | </Entry.Match>
582 | </Entry>
583 | </Group>
584 | <Entry DisplayName="Private Properties">
585 | <Entry.Match>
586 | <And>
587 | <Access Is="Private" />
588 | <Kind Is="Property" />
589 | </And>
590 | </Entry.Match>
591 | </Entry>
592 | </Group>
593 | <Group DisplayName="Constructor/Destructor">
594 | <Entry DisplayName="Ctor">
595 | <Entry.Match>
596 | <Kind Is="Constructor" />
597 | </Entry.Match>
598 | </Entry>
599 | <Region Name="Disposal">
600 | <Entry DisplayName="Dtor">
601 | <Entry.Match>
602 | <Kind Is="Destructor" />
603 | </Entry.Match>
604 | </Entry>
605 | <Entry DisplayName="Dispose()">
606 | <Entry.Match>
607 | <And>
608 | <Access Is="Public" />
609 | <Kind Is="Method" />
610 | <Name Is="Dispose" />
611 | </And>
612 | </Entry.Match>
613 | </Entry>
614 | <Entry DisplayName="Dispose(true)">
615 | <Entry.Match>
616 | <And>
617 | <Access Is="Protected" />
618 | <Or>
619 | <Virtual />
620 | <Override />
621 | </Or>
622 | <Kind Is="Method" />
623 | <Name Is="Dispose" />
624 | </And>
625 | </Entry.Match>
626 | </Entry>
627 | </Region>
628 | </Group>
629 | <Group DisplayName="Methods">
630 | <Group DisplayName="Public">
631 | <Entry DisplayName="Static Methods">
632 | <Entry.Match>
633 | <And>
634 | <Access Is="Public" />
635 | <Static />
636 | <Kind Is="Method" />
637 | </And>
638 | </Entry.Match>
639 | </Entry>
640 | <Entry DisplayName="Methods">
641 | <Entry.Match>
642 | <And>
643 | <Access Is="Public" />
644 | <Not>
645 | <Static />
646 | </Not>
647 | <Kind Is="Method" />
648 | </And>
649 | </Entry.Match>
650 | </Entry>
651 | </Group>
652 | <Group DisplayName="Internal">
653 | <Entry DisplayName="Static Methods">
654 | <Entry.Match>
655 | <And>
656 | <Access Is="Internal" />
657 | <Static />
658 | <Kind Is="Method" />
659 | </And>
660 | </Entry.Match>
661 | </Entry>
662 | <Entry DisplayName="Methods">
663 | <Entry.Match>
664 | <And>
665 | <Access Is="Internal" />
666 | <Not>
667 | <Static />
668 | </Not>
669 | <Kind Is="Method" />
670 | </And>
671 | </Entry.Match>
672 | </Entry>
673 | </Group>
674 | <Group DisplayName="Protected">
675 | <Entry DisplayName="Static Methods">
676 | <Entry.Match>
677 | <And>
678 | <Access Is="Protected" />
679 | <Static />
680 | <Kind Is="Method" />
681 | </And>
682 | </Entry.Match>
683 | </Entry>
684 | <Entry DisplayName="Methods">
685 | <Entry.Match>
686 | <And>
687 | <Access Is="Protected" />
688 | <Not>
689 | <Static />
690 | </Not>
691 | <Kind Is="Method" />
692 | </And>
693 | </Entry.Match>
694 | </Entry>
695 | </Group>
696 | <Group DisplayName="Private">
697 | <Entry DisplayName="Static Methods">
698 | <Entry.Match>
699 | <And>
700 | <Access Is="Private" />
701 | <Static />
702 | <Kind Is="Method" />
703 | </And>
704 | </Entry.Match>
705 | </Entry>
706 | <Entry DisplayName="Methods">
707 | <Entry.Match>
708 | <And>
709 | <Access Is="Private" />
710 | <Not>
711 | <Static />
712 | </Not>
713 | <Kind Is="Method" />
714 | </And>
715 | </Entry.Match>
716 | </Entry>
717 | </Group>
718 | </Group>
719 | </TypePattern>
720 | </Patterns>
721 | Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
722 | See the LICENCE file in the repository root for full licence text.
723 |
724 | <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
725 | <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" />
726 | <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
727 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
728 | <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
729 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy>
730 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
731 | <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
732 | <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
733 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
734 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy>
735 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy>
736 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"><ElementKinds><Kind Name="TYPE_PARAMETER" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
737 | <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></Policy>
738 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy>
739 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local functions"><ElementKinds><Kind Name="LOCAL_FUNCTION" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
740 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Enum members"><ElementKinds><Kind Name="ENUM_MEMBER" /></ElementKinds></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></Policy>
741 | <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
742 | <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
743 | <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
744 | <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"><ElementKinds><Kind Name="LOCAL_CONSTANT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></Policy>
745 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></Policy>
746 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
747 | <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
748 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
749 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
750 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
751 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
752 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
753 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
754 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
755 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
756 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
757 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
758 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
759 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
760 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
761 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
762 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
763 | <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
764 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
765 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
766 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
767 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
768 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
769 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
770 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
771 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
772 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
773 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
774 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
775 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
776 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
777 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
778 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
779 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
780 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
781 | <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
782 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
783 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
784 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
785 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
786 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
787 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
788 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
789 |
790 | True
791 | True
792 | True
793 | True
794 | True
795 | True
796 | True
797 | True
798 | True
799 | True
800 | True
801 | True
802 | True
803 | True
804 | TestFolder
805 | True
806 | True
807 | o!f – Object Initializer: Anchor&Origin
808 | True
809 | constant("Centre")
810 | 0
811 | True
812 | True
813 | 2.0
814 | InCSharpFile
815 | ofao
816 | True
817 | Anchor = Anchor.$anchor$,
818 | Origin = Anchor.$anchor$,
819 | True
820 | True
821 | o!f – InternalChildren = []
822 | True
823 | True
824 | 2.0
825 | InCSharpFile
826 | ofic
827 | True
828 | InternalChildren = new Drawable[]
829 | {
830 | $END$
831 | };
832 | True
833 | True
834 | o!f – new GridContainer { .. }
835 | True
836 | True
837 | 2.0
838 | InCSharpFile
839 | ofgc
840 | True
841 | new GridContainer
842 | {
843 | RelativeSizeAxes = Axes.Both,
844 | Content = new[]
845 | {
846 | new Drawable[] { $END$ },
847 | new Drawable[] { }
848 | }
849 | };
850 | True
851 | True
852 | o!f – new FillFlowContainer { .. }
853 | True
854 | True
855 | 2.0
856 | InCSharpFile
857 | offf
858 | True
859 | new FillFlowContainer
860 | {
861 | RelativeSizeAxes = Axes.Both,
862 | Direction = FillDirection.Vertical,
863 | Children = new Drawable[]
864 | {
865 | $END$
866 | }
867 | },
868 | True
869 | True
870 | o!f – new Container { .. }
871 | True
872 | True
873 | 2.0
874 | InCSharpFile
875 | ofcont
876 | True
877 | new Container
878 | {
879 | RelativeSizeAxes = Axes.Both,
880 | Children = new Drawable[]
881 | {
882 | $END$
883 | }
884 | },
885 | True
886 | True
887 | o!f – BackgroundDependencyLoader load()
888 | True
889 | True
890 | 2.0
891 | InCSharpFile
892 | ofbdl
893 | True
894 | [BackgroundDependencyLoader]
895 | private void load()
896 | {
897 | $END$
898 | }
899 | True
900 | True
901 | o!f – new Box { .. }
902 | True
903 | True
904 | 2.0
905 | InCSharpFile
906 | ofbox
907 | True
908 | new Box
909 | {
910 | Colour = Color4.Black,
911 | RelativeSizeAxes = Axes.Both,
912 | },
913 | True
914 | True
915 | o!f – Children = []
916 | True
917 | True
918 | 2.0
919 | InCSharpFile
920 | ofc
921 | True
922 | Children = new Drawable[]
923 | {
924 | $END$
925 | };
926 | True
927 | True
928 | True
929 | True
930 | True
931 | True
932 | True
933 | True
934 | True
935 | True
936 | True
937 | True
938 | True
939 | True
940 | True
941 | True
942 | True
943 | True
944 | True
945 | True
946 | True
947 | True
948 | True
949 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/AppSettings.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System;
5 |
6 | namespace osu.Server.DifficultyCalculator
7 | {
8 | public static class AppSettings
9 | {
10 | ///
11 | /// Whether to insert entries into the beatmaps table should they not exist. Should be false for production (beatmaps should already exist).
12 | ///
13 | public static readonly bool INSERT_BEATMAPS;
14 |
15 | ///
16 | /// Whether to insert entries to `osu_difficulty_attributes`. This is quite an intensive operation, and may be skipped when not required (ie. for sandbox runs).
17 | ///
18 | public static readonly bool SKIP_INSERT_ATTRIBUTES;
19 |
20 | ///
21 | /// A full or relative path used to store beatmaps.
22 | ///
23 | public static readonly string BEATMAPS_PATH;
24 |
25 | ///
26 | /// Whether beatmaps should be downloaded if they don't exist in .
27 | ///
28 | public static readonly bool ALLOW_DOWNLOAD;
29 |
30 | ///
31 | /// A URL used to download beatmaps with {0} being replaced with the beatmap_id.
32 | /// ie. "https://osu.ppy.sh/osu/{0}"
33 | ///
34 | public static readonly string DOWNLOAD_PATH;
35 |
36 | ///
37 | /// Whether downloaded files should be cached to .
38 | ///
39 | public static readonly bool SAVE_DOWNLOADED;
40 |
41 | static AppSettings()
42 | {
43 | INSERT_BEATMAPS = Environment.GetEnvironmentVariable("INSERT_BEATMAPS") == "1";
44 | SKIP_INSERT_ATTRIBUTES = Environment.GetEnvironmentVariable("SKIP_INSERT_ATTRIBUTES") == "1";
45 | ALLOW_DOWNLOAD = Environment.GetEnvironmentVariable("ALLOW_DOWNLOAD") == "1";
46 | SAVE_DOWNLOADED = Environment.GetEnvironmentVariable("SAVE_DOWNLOADED") == "1";
47 |
48 | BEATMAPS_PATH = Environment.GetEnvironmentVariable("BEATMAPS_PATH") ?? "osu";
49 | DOWNLOAD_PATH = Environment.GetEnvironmentVariable("BEATMAP_DOWNLOAD_PATH") ?? "https://osu.ppy.sh/osu/{0}";
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/BeatmapLoader.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System;
5 | using System.IO;
6 | using McMaster.Extensions.CommandLineUtils;
7 | using osu.Framework.Audio.Track;
8 | using osu.Framework.Graphics.Textures;
9 | using osu.Framework.IO.Network;
10 | using osu.Game.Beatmaps;
11 | using osu.Game.Beatmaps.Formats;
12 | using osu.Game.IO;
13 | using osu.Game.Rulesets.Catch;
14 | using osu.Game.Rulesets.Mania;
15 | using osu.Game.Rulesets.Osu;
16 | using osu.Game.Rulesets.Taiko;
17 | using osu.Game.Skinning;
18 |
19 | namespace osu.Server.DifficultyCalculator
20 | {
21 | public static class BeatmapLoader
22 | {
23 | public static WorkingBeatmap GetBeatmap(int beatmapId, bool verbose = false, bool forceDownload = true, IReporter? reporter = null)
24 | {
25 | string fileLocation = Path.Combine(AppSettings.BEATMAPS_PATH, beatmapId.ToString()) + ".osu";
26 |
27 | if ((forceDownload || !File.Exists(fileLocation)) && AppSettings.ALLOW_DOWNLOAD)
28 | {
29 | if (verbose)
30 | reporter?.Verbose($"Downloading {beatmapId}.");
31 |
32 | var req = new WebRequest(string.Format(AppSettings.DOWNLOAD_PATH, beatmapId))
33 | {
34 | AllowInsecureRequests = true
35 | };
36 |
37 | req.Failed += _ =>
38 | {
39 | if (verbose)
40 | reporter?.Error($"Failed to download {beatmapId}.");
41 | };
42 |
43 | req.Finished += () =>
44 | {
45 | if (verbose)
46 | reporter?.Verbose($"{beatmapId} successfully downloaded.");
47 | };
48 |
49 | req.Perform();
50 |
51 | var stream = req.ResponseStream;
52 |
53 | if (stream.Length == 0)
54 | throw new Exception("Beatmap download failed.");
55 |
56 | if (AppSettings.SAVE_DOWNLOADED)
57 | {
58 | using (var fileStream = File.Create(fileLocation))
59 | {
60 | stream.CopyTo(fileStream);
61 | stream.Seek(0, SeekOrigin.Begin);
62 | }
63 | }
64 |
65 | return new LoaderWorkingBeatmap(stream);
66 | }
67 |
68 | if (!File.Exists(fileLocation))
69 | throw new Exception("Beatmap file does not exist and was not downloaded.");
70 |
71 | return new LoaderWorkingBeatmap(fileLocation);
72 | }
73 |
74 | private class LoaderWorkingBeatmap : WorkingBeatmap
75 | {
76 | private readonly Beatmap beatmap;
77 |
78 | ///
79 | /// Constructs a new from a .osu file.
80 | ///
81 | /// The .osu file.
82 | public LoaderWorkingBeatmap(string file)
83 | : this(File.OpenRead(file))
84 | {
85 | }
86 |
87 | public LoaderWorkingBeatmap(Stream stream)
88 | : this(new LineBufferedReader(stream))
89 | {
90 | stream.Dispose();
91 | }
92 |
93 | private LoaderWorkingBeatmap(LineBufferedReader reader)
94 | : this(Decoder.GetDecoder(reader).Decode(reader))
95 | {
96 | }
97 |
98 | private LoaderWorkingBeatmap(Beatmap beatmap)
99 | : base(beatmap.BeatmapInfo, null)
100 | {
101 | this.beatmap = beatmap;
102 |
103 | switch (beatmap.BeatmapInfo.Ruleset.OnlineID)
104 | {
105 | case 0:
106 | beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo;
107 | break;
108 |
109 | case 1:
110 | beatmap.BeatmapInfo.Ruleset = new TaikoRuleset().RulesetInfo;
111 | break;
112 |
113 | case 2:
114 | beatmap.BeatmapInfo.Ruleset = new CatchRuleset().RulesetInfo;
115 | break;
116 |
117 | case 3:
118 | beatmap.BeatmapInfo.Ruleset = new ManiaRuleset().RulesetInfo;
119 | break;
120 | }
121 | }
122 |
123 | protected override IBeatmap GetBeatmap() => beatmap;
124 | public override Texture? GetBackground() => null;
125 | protected override Track? GetBeatmapTrack() => null;
126 | protected override ISkin? GetSkin() => null;
127 | public override Stream? GetStream(string storagePath) => null;
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/AllCommand.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System.Collections.Generic;
5 | using Dapper;
6 | using McMaster.Extensions.CommandLineUtils;
7 | using osu.Server.QueueProcessor;
8 |
9 | namespace osu.Server.DifficultyCalculator.Commands
10 | {
11 | [Command(Name = "all", Description = "Calculates the difficulty of all beatmaps in the database.")]
12 | public class AllCommand : CalculatorCommand
13 | {
14 | [Option(CommandOptionType.NoValue, Template = "-r|--ranked", Description = "Only calculate difficulty for ranked/approved/qualified/loved maps.")]
15 | public bool RankedOnly { get; set; }
16 |
17 | [Option("--sql", Description = "Specify a custom query to limit the scope of beatmaps")]
18 | public string? CustomQuery { get; set; }
19 |
20 | [Option("--from", Description = "The minimum beatmap id to calculate the difficulty for.")]
21 | public int StartId { get; set; }
22 |
23 | protected override IEnumerable GetBeatmaps()
24 | {
25 | using (var conn = DatabaseAccess.GetConnection())
26 | {
27 | var condition = CombineSqlConditions(
28 | RankedOnly ? "`approved` >= 1" : null,
29 | $"`beatmap_id` >= {StartId}",
30 | "`deleted_at` IS NULL",
31 | CustomQuery
32 | );
33 |
34 | return conn.Query($"SELECT `beatmap_id` FROM `osu_beatmaps` {condition}");
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/BeatmapsCommand.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using McMaster.Extensions.CommandLineUtils;
7 |
8 | namespace osu.Server.DifficultyCalculator.Commands
9 | {
10 | [Command(Name = "beatmaps", Description = "Calculates the difficulty of specific beatmaps.")]
11 | public class BeatmapsCommand : CalculatorCommand
12 | {
13 | [Argument(0, "beatmap", Description = "One or more beatmap ids to calculate the difficulty for.")]
14 | public int[] BeatmapIds { get; set; } = Array.Empty();
15 |
16 | protected override IEnumerable GetBeatmaps() => BeatmapIds;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/BeatmapsStringCommand.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using McMaster.Extensions.CommandLineUtils;
7 |
8 | namespace osu.Server.DifficultyCalculator.Commands
9 | {
10 | [Command("beatmapsstring", Description = "A compatibility mode which accepts a comma-separated list of beatmap ids.")]
11 | public class BeatmapsStringCommand : CalculatorCommand
12 | {
13 | [Argument(0, "beatmaps", Description = "A comma-separated list of beatmap ids.")]
14 | public string Beatmaps { get; set; } = string.Empty;
15 |
16 | protected override IEnumerable GetBeatmaps() => Beatmaps.Split(',').Select(int.Parse);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/CalculatorCommand.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System;
5 | using System.Collections.Concurrent;
6 | using System.Collections.Generic;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using McMaster.Extensions.CommandLineUtils;
11 |
12 | namespace osu.Server.DifficultyCalculator.Commands
13 | {
14 | public abstract class CalculatorCommand : CommandBase
15 | {
16 | [Option(CommandOptionType.MultipleValue, Template = "-m|--mode ", Description = "Ruleset(s) to compute difficulty for.\n"
17 | + "0 - osu!\n"
18 | + "1 - osu!taiko\n"
19 | + "2 - osu!catch\n"
20 | + "3 - osu!mania")]
21 | public int[]? Rulesets { get; set; }
22 |
23 | [Option(CommandOptionType.NoValue, Template = "-ac|--allow-converts", Description = "Attempt to convert beatmaps to other rulesets to calculate difficulty.")]
24 | public bool Converts { get; set; } = false;
25 |
26 | [Option(CommandOptionType.SingleValue, Template = "-c|--concurrency", Description = "Number of threads to use. Default 1.")]
27 | public int Concurrency { get; set; } = 1;
28 |
29 | [Option(CommandOptionType.NoValue, Template = "-d|--force-download", Description = "Force download of all beatmaps.")]
30 | public bool ForceDownload { get; set; }
31 |
32 | [Option(CommandOptionType.NoValue, Template = "-v|--verbose", Description = "Provide verbose console output.")]
33 | public bool Verbose { get; set; }
34 |
35 | [Option(CommandOptionType.NoValue, Template = "-q|--quiet", Description = "Disable all console output.")]
36 | public bool Quiet { get; set; }
37 |
38 | [Option(CommandOptionType.SingleValue, Template = "-l|--log-file", Description = "The file to log output to.")]
39 | public string? LogFile { get; set; }
40 |
41 | [Option(CommandOptionType.NoValue, Template = "-dry|--dry-run", Description = "Whether to run the process without writing to the database.")]
42 | public bool DryRun { get; set; }
43 |
44 | [Option(CommandOptionType.SingleValue, Template = "--processing-mode", Description = "The mode in which to process beatmaps.")]
45 | public ProcessingMode ProcessingMode { get; set; } = ProcessingMode.All;
46 |
47 | private int[] threadBeatmapIds = null!;
48 | private IReporter reporter = null!;
49 |
50 | private int totalBeatmaps;
51 | private int processedBeatmaps;
52 |
53 | public void OnExecute(CommandLineApplication app, IConsole console)
54 | {
55 | reporter = new Reporter(console, LogFile)
56 | {
57 | IsQuiet = Quiet,
58 | IsVerbose = Verbose
59 | };
60 |
61 | if (Concurrency < 1)
62 | {
63 | reporter.Error("Concurrency level must be above 1.");
64 | return;
65 | }
66 |
67 | threadBeatmapIds = new int[Concurrency];
68 |
69 | var beatmaps = new ConcurrentQueue(GetBeatmaps());
70 |
71 | totalBeatmaps = beatmaps.Count;
72 |
73 | var tasks = new Task[Concurrency];
74 |
75 | for (int i = 0; i < Concurrency; i++)
76 | {
77 | int tmp = i;
78 |
79 | tasks[i] = Task.Factory.StartNew(() =>
80 | {
81 | var calc = new ServerDifficultyCalculator(Rulesets, Converts, DryRun);
82 |
83 | while (beatmaps.TryDequeue(out int beatmapId))
84 | {
85 | threadBeatmapIds[tmp] = beatmapId;
86 | reporter.Verbose($"Processing difficulty for beatmap {beatmapId}.");
87 |
88 | try
89 | {
90 | var beatmap = BeatmapLoader.GetBeatmap(beatmapId, Verbose, ForceDownload, reporter);
91 |
92 | // ensure the correct online id is set
93 | beatmap.BeatmapInfo.OnlineID = beatmapId;
94 |
95 | calc.Process(beatmap, ProcessingMode);
96 |
97 | reporter.Verbose($"Difficulty updated for beatmap {beatmapId}.");
98 | }
99 | catch (Exception e)
100 | {
101 | reporter.Error($"{beatmapId} failed with: {e.Message}");
102 | }
103 |
104 | Interlocked.Increment(ref processedBeatmaps);
105 | }
106 | });
107 | }
108 |
109 | reporter.Output($"Processing {totalBeatmaps} beatmaps.");
110 |
111 | using (new Timer(_ => outputProgress(), null, 1000, 1000))
112 | using (new Timer(_ => outputHealth(), null, 5000, 5000))
113 | Task.WaitAll(tasks);
114 |
115 | outputProgress();
116 |
117 | reporter.Output("Done.");
118 | }
119 |
120 | private int lastProgress;
121 |
122 | private void outputProgress()
123 | {
124 | int processed = processedBeatmaps;
125 | reporter.Output($"Processed {processed} / {totalBeatmaps} ({processed - lastProgress}/sec)");
126 | lastProgress = processed;
127 | }
128 |
129 | private void outputHealth()
130 | {
131 | // var process = Process.GetCurrentProcess();
132 | //reporter.Output($"Health p:{process.PrivateMemorySize64.Bytes()} v:{process.VirtualMemorySize64.Bytes()} w:{process.WorkingSet64.Bytes()}");
133 |
134 | string threadsString = string.Empty;
135 | for (int i = 0; i < threadBeatmapIds.Length; i++)
136 | threadsString += $"{i}:{threadBeatmapIds[i]} ";
137 |
138 | reporter.Output($"Threads {threadsString}");
139 | }
140 |
141 | protected string CombineSqlConditions(params string?[] conditions)
142 | {
143 | var builder = new StringBuilder();
144 |
145 | foreach (var c in conditions)
146 | {
147 | if (string.IsNullOrEmpty(c))
148 | continue;
149 |
150 | builder.Append(builder.Length > 0 ? " AND " : " WHERE ");
151 | builder.Append(c);
152 | }
153 |
154 | return builder.ToString();
155 | }
156 |
157 | protected abstract IEnumerable GetBeatmaps();
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/CommandBase.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using McMaster.Extensions.CommandLineUtils;
5 |
6 | namespace osu.Server.DifficultyCalculator.Commands
7 | {
8 | [HelpOption("-?|-h|--help")]
9 | public abstract class CommandBase
10 | {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/FilesCommand.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using McMaster.Extensions.CommandLineUtils;
7 |
8 | namespace osu.Server.DifficultyCalculator.Commands
9 | {
10 | [Command(Name = "files", Description = "Computes the difficulty of all files in the beatmaps path.")]
11 | public class FilesCommand : CalculatorCommand
12 | {
13 | protected override IEnumerable GetBeatmaps()
14 | {
15 | var ids = new List();
16 |
17 | foreach (var f in Directory.GetFiles(AppSettings.BEATMAPS_PATH))
18 | {
19 | var filename = Path.GetFileNameWithoutExtension(f);
20 |
21 | if (int.TryParse(filename.Split('.')[0], out var id))
22 | ids.Add(id);
23 | }
24 |
25 | return ids;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Commands/ProcessingMode.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | namespace osu.Server.DifficultyCalculator.Commands
5 | {
6 | public enum ProcessingMode
7 | {
8 | All,
9 | Difficulty,
10 | ScoreAttributes
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
2 | WORKDIR /app
3 |
4 | # Handle project files and dependencies first for caching benefits
5 | COPY *.sln ./
6 | COPY osu.Server.DifficultyCalculator/osu.Server.DifficultyCalculator.csproj ./osu.Server.DifficultyCalculator/
7 | COPY osu.Server.Queues.BeatmapProcessor/osu.Server.Queues.BeatmapProcessor.csproj ./osu.Server.Queues.BeatmapProcessor/
8 |
9 | RUN dotnet restore
10 |
11 | # Copy everything else and build
12 | COPY . ./
13 | WORKDIR /app/osu.Server.DifficultyCalculator
14 | RUN dotnet publish -c Release -o out
15 | # get rid of bloat
16 | RUN rm -rf ./out/runtimes ./out/osu.Game.Resources.dll
17 |
18 | # Build runtime image
19 | FROM mcr.microsoft.com/dotnet/runtime:8.0
20 | WORKDIR /app
21 | COPY --from=build-env /app/osu.Server.DifficultyCalculator/out .
22 | ENTRYPOINT ["dotnet", "osu.Server.DifficultyCalculator.dll"]
23 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Program.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System.Net;
5 | using McMaster.Extensions.CommandLineUtils;
6 | using osu.Game.Beatmaps.Formats;
7 | using osu.Server.DifficultyCalculator.Commands;
8 |
9 | namespace osu.Server.DifficultyCalculator
10 | {
11 | [Command]
12 | [Subcommand(typeof(AllCommand))]
13 | [Subcommand(typeof(FilesCommand))]
14 | [Subcommand(typeof(BeatmapsCommand))]
15 | [Subcommand(typeof(BeatmapsStringCommand))]
16 | public class Program
17 | {
18 | public static int Main(string[] args)
19 | {
20 | LegacyDifficultyCalculatorBeatmapDecoder.Register();
21 | ServicePointManager.DefaultConnectionLimit = 128;
22 |
23 | return CommandLineApplication.Execute(args);
24 | }
25 |
26 | public int OnExecute(CommandLineApplication app)
27 | {
28 | app.ShowHelp();
29 | return 1;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/Reporter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System;
5 | using System.IO;
6 | using McMaster.Extensions.CommandLineUtils;
7 |
8 | namespace osu.Server.DifficultyCalculator
9 | {
10 | public class Reporter : IReporter
11 | {
12 | ///
13 | /// Whether verbose output should be displayed.
14 | ///
15 | public bool IsVerbose { get; set; }
16 |
17 | ///
18 | /// Whether quiet output should be displayed.
19 | ///
20 | public bool IsQuiet { get; set; }
21 |
22 | private readonly object writeLock = new object();
23 | private readonly IConsole console;
24 | private readonly StreamWriter? fileWriter;
25 | private readonly StreamWriter errorFileWriter;
26 |
27 | public Reporter(IConsole console, string? file = null)
28 | {
29 | this.console = console;
30 |
31 | if (file != null)
32 | {
33 | try
34 | {
35 | fileWriter = new StreamWriter(file);
36 | }
37 | catch (Exception e)
38 | {
39 | Warn($"Failed to initialise log file ({file}): {e}");
40 | Warn("Continuing without log file.");
41 | }
42 | }
43 |
44 | errorFileWriter = new StreamWriter("error.log", true) { AutoFlush = true };
45 | }
46 |
47 | private void writeLine(TextWriter consoleWriter, string message, ConsoleColor? foregroundColour = null)
48 | {
49 | lock (writeLock)
50 | {
51 | if (foregroundColour.HasValue)
52 | Console.ForegroundColor = foregroundColour.Value;
53 |
54 | string line = $"[{DateTime.UtcNow}]: {message}";
55 |
56 | if (!IsQuiet)
57 | consoleWriter.WriteLine(line);
58 | fileWriter?.WriteLine(line);
59 |
60 | if (consoleWriter == console.Error)
61 | errorFileWriter.WriteLine(line);
62 |
63 | if (foregroundColour.HasValue)
64 | Console.ResetColor();
65 | }
66 | }
67 |
68 | ///
69 | /// Writes a message in to the console/file outputs.
70 | ///
71 | /// The message to write.
72 | public void Verbose(string message)
73 | {
74 | if (!IsVerbose)
75 | return;
76 |
77 | writeLine(console.Out, message, ConsoleColor.DarkGray);
78 | }
79 |
80 | public void Output(string message) => writeLine(console.Out, message);
81 |
82 | public void Warn(string message) => writeLine(console.Out, message, ConsoleColor.Yellow);
83 |
84 | public void Error(string message) => writeLine(console.Error, message, ConsoleColor.Red);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/osu.Server.DifficultyCalculator/ServerDifficultyCalculator.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
2 | // See the LICENCE file in the repository root for full licence text.
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Reflection;
9 | using Dapper;
10 | using MySqlConnector;
11 | using osu.Game.Beatmaps;
12 | using osu.Game.Beatmaps.Legacy;
13 | using osu.Game.Rulesets;
14 | using osu.Game.Rulesets.Mods;
15 | using osu.Game.Rulesets.Objects.Legacy;
16 | using osu.Game.Rulesets.Scoring.Legacy;
17 | using osu.Server.DifficultyCalculator.Commands;
18 | using osu.Server.QueueProcessor;
19 |
20 | namespace osu.Server.DifficultyCalculator
21 | {
22 | public class ServerDifficultyCalculator
23 | {
24 | private static readonly List available_rulesets = getRulesets();
25 |
26 | private readonly bool processConverts;
27 | private readonly bool dryRun;
28 | private readonly List processableRulesets = new List();
29 |
30 | public ServerDifficultyCalculator(int[]? rulesetIds = null, bool processConverts = true, bool dryRun = false)
31 | {
32 | this.processConverts = processConverts;
33 | this.dryRun = dryRun;
34 |
35 | if (rulesetIds != null)
36 | {
37 | foreach (int id in rulesetIds)
38 | processableRulesets.Add(available_rulesets.Single(r => r.RulesetInfo.OnlineID == id));
39 | }
40 | else
41 | {
42 | processableRulesets.AddRange(available_rulesets);
43 | }
44 | }
45 |
46 | public void Process(WorkingBeatmap beatmap, ProcessingMode mode)
47 | {
48 | switch (mode)
49 | {
50 | case ProcessingMode.All:
51 | ProcessDifficulty(beatmap);
52 | ProcessLegacyAttributes(beatmap);
53 | break;
54 |
55 | case ProcessingMode.Difficulty:
56 | ProcessDifficulty(beatmap);
57 | break;
58 |
59 | case ProcessingMode.ScoreAttributes:
60 | ProcessLegacyAttributes(beatmap);
61 | break;
62 |
63 | default:
64 | throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unsupported processing mode supplied");
65 | }
66 | }
67 |
68 | public void ProcessDifficulty(WorkingBeatmap beatmap) => run(beatmap, processDifficulty);
69 |
70 | public void ProcessLegacyAttributes(WorkingBeatmap beatmap) => run(beatmap, processLegacyAttributes);
71 |
72 | private void run(WorkingBeatmap beatmap, Action callback)
73 | {
74 | try
75 | {
76 | bool ranked;
77 |
78 | using (var conn = DatabaseAccess.GetConnection())
79 | {
80 | ranked = conn.QuerySingleOrDefault("SELECT `approved` FROM `osu_beatmaps` WHERE `beatmap_id` = @BeatmapId", new
81 | {
82 | BeatmapId = beatmap.BeatmapInfo.OnlineID
83 | }) > 0;
84 |
85 | if (ranked && beatmap.Beatmap.HitObjects.Count == 0)
86 | throw new ArgumentException($"Ranked beatmap {beatmap.BeatmapInfo.OnlineInfo} has 0 hitobjects!");
87 | }
88 |
89 | using (var conn = DatabaseAccess.GetConnection())
90 | {
91 | if (processConverts && beatmap.BeatmapInfo.Ruleset.OnlineID == 0)
92 | {
93 | foreach (var ruleset in processableRulesets)
94 | callback(new ProcessableItem(beatmap, ruleset, ranked), conn);
95 | }
96 | else if (processableRulesets.Any(r => r.RulesetInfo.OnlineID == beatmap.BeatmapInfo.Ruleset.OnlineID))
97 | callback(new ProcessableItem(beatmap, beatmap.BeatmapInfo.Ruleset.CreateInstance(), ranked), conn);
98 | }
99 | }
100 | catch (Exception e)
101 | {
102 | throw new Exception($"{beatmap.BeatmapInfo.OnlineID} failed with: {e.Message}");
103 | }
104 | }
105 |
106 | private void processDifficulty(ProcessableItem item, MySqlConnection conn)
107 | {
108 | foreach (var attribute in item.Ruleset.CreateDifficultyCalculator(item.WorkingBeatmap).CalculateAllLegacyCombinations())
109 | {
110 | if (dryRun)
111 | continue;
112 |
113 | LegacyMods legacyMods = item.Ruleset.ConvertToLegacyMods(attribute.Mods);
114 |
115 | conn.Execute(
116 | "INSERT INTO `osu_beatmap_difficulty` (`beatmap_id`, `mode`, `mods`, `diff_unified`) "
117 | + "VALUES (@BeatmapId, @Mode, @Mods, @Diff) "
118 | + "ON DUPLICATE KEY UPDATE `diff_unified` = @Diff",
119 | new
120 | {
121 | BeatmapId = item.BeatmapID,
122 | Mode = item.RulesetID,
123 | Mods = (int)legacyMods,
124 | Diff = attribute.StarRating
125 | });
126 |
127 | if (item.Ranked && !AppSettings.SKIP_INSERT_ATTRIBUTES)
128 | {
129 | var parameters = new List