├── .editorconfig
├── .gitattributes
├── .gitignore
├── HttpStatusExtention.sln
├── HttpStatusExtention
├── Converters
│ ├── BeatMapCoreConverter.cs
│ └── SongDetailsConveter.cs
├── Directory.Build.props
├── Directory.Build.targets
├── Extentions
│ └── BeatmapLevelExtention.cs
├── HarmonyPathces
│ └── SRMConigPatch.cs
├── HttpStatusExtention.csproj
├── HttpStatusExtentionController.cs
├── Installer
│ ├── HttpStatusExtentionAppInstaller.cs
│ ├── HttpStatusExtentionInstaller.cs
│ └── HttpStatusExtentionMenuAndGameInstaller.cs
├── Interfaces
│ └── ISongDataUtil.cs
├── Models
│ ├── BeatSongData.cs
│ ├── BeatSongDataDifficultyStats.cs
│ ├── CoroutineManager.cs
│ └── Enums.cs
├── Networks
│ └── WebClient.cs
├── PPCounters
│ ├── Calculators
│ │ ├── AccSaberCalculator.cs
│ │ ├── BeatLeaderCalculator.cs
│ │ └── ScoreSaberCalculator.cs
│ ├── CurveUtils.cs
│ ├── Data
│ │ ├── AccSaberData.cs
│ │ ├── BeatLeaderData.cs
│ │ ├── PPData.cs
│ │ └── SSData.cs
│ ├── GameplayModifierUtils.cs
│ ├── OSUtils.cs
│ ├── PPDownloader.cs
│ ├── SongDataUtils.cs
│ └── Structs
│ │ └── Struct.cs
├── Plugin.cs
├── Properties
│ └── AssemblyInfo.cs
├── SRMQueueStatus.cs
├── SongDetailsCaches
│ └── SongDetailsCacheUtility.cs
└── manifest.json
├── LICENSE
└── README.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | # 上位ディレクトリから .editorconfig 設定を継承する場合は、以下の行を削除します
2 | root = true
3 |
4 | # C# ファイル
5 | [*.cs]
6 |
7 | #### コア EditorConfig オプション ####
8 |
9 | # インデントと間隔
10 | indent_size = 4
11 | indent_style = space
12 | tab_width = 4
13 |
14 | # 改行設定
15 | end_of_line = crlf
16 | insert_final_newline = false
17 |
18 | #### .NET コーディング規則 ####
19 |
20 | # using の整理
21 | dotnet_separate_import_directive_groups = false
22 | dotnet_sort_system_directives_first = false
23 | file_header_template = unset
24 |
25 | # this. と Me. の設定
26 | dotnet_style_qualification_for_event = true:suggestion
27 | dotnet_style_qualification_for_field = true
28 | dotnet_style_qualification_for_method = true:suggestion
29 | dotnet_style_qualification_for_property = true:suggestion
30 |
31 | # 言語キーワードと BCL の種類の設定
32 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
33 | dotnet_style_predefined_type_for_member_access = true:suggestion
34 |
35 | # かっこの設定
36 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
37 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
38 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary
39 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
40 |
41 | # 修飾子設定
42 | dotnet_style_require_accessibility_modifiers = for_non_interface_members
43 |
44 | # 式レベルの設定
45 | dotnet_style_coalesce_expression = true
46 | dotnet_style_collection_initializer = true
47 | dotnet_style_explicit_tuple_names = true
48 | dotnet_style_namespace_match_folder = true
49 | dotnet_style_null_propagation = true
50 | dotnet_style_object_initializer = true
51 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
52 | dotnet_style_prefer_auto_properties = true
53 | dotnet_style_prefer_compound_assignment = true
54 | dotnet_style_prefer_conditional_expression_over_assignment = true
55 | dotnet_style_prefer_conditional_expression_over_return = true
56 | dotnet_style_prefer_inferred_anonymous_type_member_names = true
57 | dotnet_style_prefer_inferred_tuple_names = true
58 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true
59 | dotnet_style_prefer_simplified_boolean_expressions = true:warning
60 | dotnet_style_prefer_simplified_interpolation = true
61 |
62 | # フィールド設定
63 | dotnet_style_readonly_field = true
64 |
65 | # パラメーターの設定
66 | dotnet_code_quality_unused_parameters = non_public:silent
67 |
68 | # 抑制の設定
69 | dotnet_remove_unnecessary_suppression_exclusions = none
70 |
71 | # 改行設定
72 | dotnet_style_allow_multiple_blank_lines_experimental = false
73 | dotnet_style_allow_statement_immediately_after_block_experimental = true
74 |
75 | #### C# コーディング規則 ####
76 |
77 | # var を優先
78 | csharp_style_var_elsewhere = true:suggestion
79 | csharp_style_var_for_built_in_types = true:suggestion
80 | csharp_style_var_when_type_is_apparent = true:suggestion
81 |
82 | # 式のようなメンバー
83 | csharp_style_expression_bodied_accessors = true
84 | csharp_style_expression_bodied_constructors = false
85 | csharp_style_expression_bodied_indexers = true
86 | csharp_style_expression_bodied_lambdas = true
87 | csharp_style_expression_bodied_local_functions = false
88 | csharp_style_expression_bodied_methods = false
89 | csharp_style_expression_bodied_operators = false
90 | csharp_style_expression_bodied_properties = true
91 |
92 | # パターン マッチング設定
93 | csharp_style_pattern_matching_over_as_with_null_check = true
94 | csharp_style_pattern_matching_over_is_with_cast_check = true
95 | csharp_style_prefer_extended_property_pattern = true
96 | csharp_style_prefer_not_pattern = true:silent
97 | csharp_style_prefer_pattern_matching = false
98 | csharp_style_prefer_switch_expression = false
99 |
100 | # Null チェック設定
101 | csharp_style_conditional_delegate_call = true
102 |
103 | # 修飾子設定
104 | csharp_prefer_static_local_function = true
105 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
106 |
107 | # コード ブロックの設定
108 | csharp_prefer_braces = true
109 | csharp_prefer_simple_using_statement = false
110 | csharp_style_namespace_declarations = block_scoped
111 |
112 | # 式レベルの設定
113 | csharp_prefer_simple_default_expression = true:silent
114 | csharp_style_deconstructed_variable_declaration = true
115 | csharp_style_implicit_object_creation_when_type_is_apparent = false:silent
116 | csharp_style_inlined_variable_declaration = true
117 | csharp_style_prefer_index_operator = false:warning
118 | csharp_style_prefer_local_over_anonymous_function = true
119 | csharp_style_prefer_null_check_over_type_check = true
120 | csharp_style_prefer_range_operator = false
121 | csharp_style_prefer_tuple_swap = true
122 | csharp_style_throw_expression = true
123 | csharp_style_unused_value_assignment_preference = unused_local_variable:warning
124 | csharp_style_unused_value_expression_statement_preference = discard_variable
125 |
126 | # 'using' ディレクティブの基本設定
127 | csharp_using_directive_placement = outside_namespace
128 |
129 | # 改行設定
130 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
131 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion
132 | csharp_style_allow_embedded_statements_on_same_line_experimental = false
133 |
134 | #### C# 書式ルール ####
135 |
136 | # 改行設定
137 | csharp_new_line_before_catch = true
138 | csharp_new_line_before_else = true
139 | csharp_new_line_before_finally = true
140 | csharp_new_line_before_members_in_anonymous_types = true
141 | csharp_new_line_before_members_in_object_initializers = true
142 | csharp_new_line_before_open_brace = accessors,anonymous_methods,anonymous_types,lambdas,methods,object_collection_array_initializers,properties,types
143 | csharp_new_line_between_query_expression_clauses = true
144 |
145 | # インデント設定
146 | csharp_indent_block_contents = true
147 | csharp_indent_braces = false
148 | csharp_indent_case_contents = true
149 | csharp_indent_case_contents_when_block = true
150 | csharp_indent_labels = one_less_than_current
151 | csharp_indent_switch_labels = true
152 |
153 | # スペース設定
154 | csharp_space_after_cast = false
155 | csharp_space_after_colon_in_inheritance_clause = true
156 | csharp_space_after_comma = true
157 | csharp_space_after_dot = false
158 | csharp_space_after_keywords_in_control_flow_statements = true
159 | csharp_space_after_semicolon_in_for_statement = true
160 | csharp_space_around_binary_operators = before_and_after
161 | csharp_space_around_declaration_statements = false
162 | csharp_space_before_colon_in_inheritance_clause = true
163 | csharp_space_before_comma = false
164 | csharp_space_before_dot = false
165 | csharp_space_before_open_square_brackets = false
166 | csharp_space_before_semicolon_in_for_statement = false
167 | csharp_space_between_empty_square_brackets = false
168 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
169 | csharp_space_between_method_call_name_and_opening_parenthesis = false
170 | csharp_space_between_method_call_parameter_list_parentheses = false
171 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
172 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
173 | csharp_space_between_method_declaration_parameter_list_parentheses = false
174 | csharp_space_between_parentheses = false
175 | csharp_space_between_square_brackets = false
176 |
177 | # 折り返しの設定
178 | csharp_preserve_single_line_blocks = true
179 | csharp_preserve_single_line_statements = true
180 |
181 | #### 命名スタイル ####
182 |
183 | # 名前付けルール
184 |
185 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
186 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
187 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
188 |
189 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
190 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
191 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
192 |
193 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
194 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
195 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
196 |
197 | dotnet_naming_rule.static_field_should_be_began_with_s_.severity = suggestion
198 | dotnet_naming_rule.static_field_should_be_began_with_s_.symbols = static_field
199 | dotnet_naming_rule.static_field_should_be_began_with_s_.style = began_with_s_
200 |
201 | dotnet_naming_rule.private_or_internal_field_should_be_began_with__.severity = suggestion
202 | dotnet_naming_rule.private_or_internal_field_should_be_began_with__.symbols = private_or_internal_field
203 | dotnet_naming_rule.private_or_internal_field_should_be_began_with__.style = began_with__
204 |
205 | # 記号の仕様
206 |
207 | dotnet_naming_symbols.interface.applicable_kinds = interface
208 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
209 | dotnet_naming_symbols.interface.required_modifiers =
210 |
211 | dotnet_naming_symbols.static_field.applicable_kinds = field
212 | dotnet_naming_symbols.static_field.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
213 | dotnet_naming_symbols.static_field.required_modifiers = static
214 |
215 | dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
216 | dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
217 | dotnet_naming_symbols.private_or_internal_field.required_modifiers =
218 |
219 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
220 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
221 | dotnet_naming_symbols.types.required_modifiers =
222 |
223 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
224 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
225 | dotnet_naming_symbols.non_field_members.required_modifiers =
226 |
227 | # 命名スタイル
228 |
229 | dotnet_naming_style.pascal_case.required_prefix =
230 | dotnet_naming_style.pascal_case.required_suffix =
231 | dotnet_naming_style.pascal_case.word_separator =
232 | dotnet_naming_style.pascal_case.capitalization = pascal_case
233 |
234 | dotnet_naming_style.begins_with_i.required_prefix = I
235 | dotnet_naming_style.begins_with_i.required_suffix =
236 | dotnet_naming_style.begins_with_i.word_separator =
237 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
238 |
239 | dotnet_naming_style.began_with__.required_prefix = _
240 | dotnet_naming_style.began_with__.required_suffix =
241 | dotnet_naming_style.began_with__.word_separator =
242 | dotnet_naming_style.began_with__.capitalization = camel_case
243 |
244 | dotnet_naming_style.began_with_s_.required_prefix = s_
245 | dotnet_naming_style.began_with_s_.required_suffix =
246 | dotnet_naming_style.began_with_s_.word_separator =
247 | dotnet_naming_style.began_with_s_.capitalization = camel_case
248 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/HttpStatusExtention.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32210.238
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpStatusExtention", "HttpStatusExtention\HttpStatusExtention.csproj", "{56DA414F-1B2A-44FC-BF87-1FAB0BA34C96}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {56DA414F-1B2A-44FC-BF87-1FAB0BA34C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {56DA414F-1B2A-44FC-BF87-1FAB0BA34C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {56DA414F-1B2A-44FC-BF87-1FAB0BA34C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {56DA414F-1B2A-44FC-BF87-1FAB0BA34C96}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {810C3134-B540-4E34-9863-9BEB2D3B8B99}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Converters/BeatMapCoreConverter.cs:
--------------------------------------------------------------------------------
1 | using HttpStatusExtention.Models;
2 |
3 | namespace HttpStatusExtention.Converters
4 | {
5 | public static class BeatMapCoreConverter
6 | {
7 | public static BeatMapDifficulty ConvertToBeatMapDifficulity(BeatmapDifficulty difficulty)
8 | {
9 | var result = BeatMapDifficulty.Easy;
10 | switch (difficulty) {
11 | case BeatmapDifficulty.Easy:
12 | result = BeatMapDifficulty.Easy;
13 | break;
14 | case BeatmapDifficulty.Normal:
15 | result = BeatMapDifficulty.Normal;
16 | break;
17 | case BeatmapDifficulty.Hard:
18 | result = BeatMapDifficulty.Hard;
19 | break;
20 | case BeatmapDifficulty.Expert:
21 | result = BeatMapDifficulty.Expert;
22 | break;
23 | case BeatmapDifficulty.ExpertPlus:
24 | result = BeatMapDifficulty.ExpertPlus;
25 | break;
26 | default:
27 | break;
28 | }
29 | return result;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Converters/SongDetailsConveter.cs:
--------------------------------------------------------------------------------
1 | using HttpStatusExtention.Models;
2 | using SongDetailsCache.Structs;
3 |
4 | namespace HttpStatusExtention.Converters
5 | {
6 | public static class SongDetailsConveter
7 | {
8 | public static BeatDataCharacteristics ConvertToBeatDataCharacteristics(MapCharacteristic mapCharacteristic)
9 | {
10 | var result = BeatDataCharacteristics.Unkown;
11 | switch (mapCharacteristic) {
12 | case MapCharacteristic.Custom:
13 | result = BeatDataCharacteristics.Unkown;
14 | break;
15 | case MapCharacteristic.Standard:
16 | result = BeatDataCharacteristics.Standard;
17 | break;
18 | case MapCharacteristic.OneSaber:
19 | result = BeatDataCharacteristics.OneSaber;
20 | break;
21 | case MapCharacteristic.NoArrows:
22 | result = BeatDataCharacteristics.NoArrows;
23 | break;
24 | case MapCharacteristic.NinetyDegree:
25 | result = BeatDataCharacteristics.Degree90;
26 | break;
27 | case MapCharacteristic.ThreeSixtyDegree:
28 | result = BeatDataCharacteristics.Degree360;
29 | break;
30 | case MapCharacteristic.Lightshow:
31 | result = BeatDataCharacteristics.Lightshow;
32 | break;
33 | case MapCharacteristic.Lawless:
34 | result = BeatDataCharacteristics.Lawless;
35 | break;
36 | default:
37 | break;
38 | }
39 | return result;
40 | }
41 |
42 | public static BeatMapDifficulty ConvertToBeatMapDifficulty(MapDifficulty mapDifficulty)
43 | {
44 | var result = BeatMapDifficulty.Easy;
45 | switch (mapDifficulty) {
46 | case MapDifficulty.Easy:
47 | result = BeatMapDifficulty.Easy;
48 | break;
49 | case MapDifficulty.Normal:
50 | result = BeatMapDifficulty.Normal;
51 | break;
52 | case MapDifficulty.Hard:
53 | result = BeatMapDifficulty.Hard;
54 | break;
55 | case MapDifficulty.Expert:
56 | result = BeatMapDifficulty.Expert;
57 | break;
58 | case MapDifficulty.ExpertPlus:
59 | result = BeatMapDifficulty.ExpertPlus;
60 | break;
61 | default:
62 | break;
63 | }
64 | return result;
65 | }
66 |
67 | public static RecomendMod ConvertToRecomendMod(MapMods mapMods)
68 | {
69 | RecomendMod result = 0;
70 | if ((mapMods & MapMods.NoodleExtensions) != 0) {
71 | result |= RecomendMod.NoodleExtensions;
72 | }
73 | if ((mapMods & MapMods.MappingExtensions) != 0) {
74 | result |= RecomendMod.MappingExtensions;
75 | }
76 | if ((mapMods & MapMods.Chroma) != 0) {
77 | result |= RecomendMod.Chroma;
78 | }
79 | if ((mapMods & MapMods.Cinema) != 0) {
80 | result |= RecomendMod.Cinema;
81 | }
82 | return result;
83 | }
84 |
85 | public static RankStatus ConvertToTRankStatus(RankedStates rankedStatus)
86 | {
87 | RankStatus result;
88 | switch (rankedStatus) {
89 |
90 | case RankedStates.ScoresaberRanked:
91 | result = RankStatus.Ranked;
92 | break;
93 | case RankedStates.ScoresaberQualified:
94 | result = RankStatus.Queued;
95 | break;
96 | case RankedStates.BeatleaderQualified:
97 | case RankedStates.BeatleaderRanked:
98 | case RankedStates.Unranked:
99 | default:
100 | result = RankStatus.Unranked;
101 | break;
102 | }
103 | return result;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | true
7 | true
8 |
9 |
10 | false
11 | true
12 | true
13 |
14 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | True
6 | BSIPA
7 |
8 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Extentions/BeatmapLevelExtention.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace HttpStatusExtention.Extentions
4 | {
5 | public static class BeatmapLevelExtention
6 | {
7 | public static string GetHashOrLevelID(this BeatmapLevel level)
8 | {
9 | var strings = level.levelID.Split('_');
10 | return strings.Length != 3 || strings.ElementAt(2).Length < 40 ? level.levelID : strings.ElementAt(2).Substring(0, 40);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/HttpStatusExtention/HarmonyPathces/SRMConigPatch.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using IPA.Loader;
3 | using System;
4 |
5 | namespace HttpStatusExtention.HarmonyPathces
6 | {
7 | [HarmonyPatch("SongRequestManagerV2.Configuration.RequestBotConfig, SongRequestManagerV2", "RequestQueueOpen", MethodType.Setter)]
8 | public class SRMConigPatch
9 | {
10 | [HarmonyPrepare]
11 | public static bool Prepare()
12 | {
13 | return PluginManager.GetPlugin("Song Request Manager V2") != null;
14 | }
15 |
16 | public static void Postfix(ref object __instance)
17 | {
18 | var isOpen = (bool)__instance.GetType().GetProperty("RequestQueueOpen").GetValue(__instance);
19 | OnQueueStatusChanged?.Invoke(isOpen);
20 | }
21 |
22 | public static event Action OnQueueStatusChanged;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/HttpStatusExtention/HttpStatusExtention.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {56DA414F-1B2A-44FC-BF87-1FAB0BA34C96}
9 | Library
10 | Properties
11 | HttpStatusExtention
12 | HttpStatusExtention
13 | v4.8
14 | 512
15 | true
16 | portable
17 | ..\Refs
18 | $(LocalRefsDir)
19 | $(MSBuildProjectDirectory)\
20 |
21 | prompt
22 | 4
23 |
24 |
25 | false
26 | bin\Debug\
27 | DEBUG;TRACE
28 |
29 |
30 | true
31 | bin\Release\
32 | prompt
33 | 4
34 |
35 |
36 | True
37 |
38 |
39 | True
40 | True
41 |
42 |
43 |
44 | False
45 | $(BeatSaberDir)\Libs\0Harmony.dll
46 | False
47 |
48 |
49 | False
50 | $(BeatSaberDir)\Beat Saber_Data\Managed\BeatmapCore.dll
51 | False
52 |
53 |
54 | False
55 | $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.AppFlow.dll
56 | False
57 |
58 |
59 | False
60 | $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.DotnetExtension.dll
61 | False
62 |
63 |
64 | False
65 | $(BeatSaberDir)\Beat Saber_Data\Managed\BGLib.UnityExtension.dll
66 | False
67 |
68 |
69 | False
70 | $(BeatSaberDir)\Plugins\BS_Utils.dll
71 | False
72 |
73 |
74 | False
75 | $(BeatSaberDir)\Beat Saber_Data\Managed\DataModels.dll
76 | False
77 |
78 |
79 | False
80 | $(BeatSaberDir)\Beat Saber_Data\Managed\GameplayCore.dll
81 | False
82 |
83 |
84 | False
85 | $(BeatSaberDir)\Plugins\HttpSiraStatus.dll
86 | False
87 |
88 |
89 | $(BeatSaberDir)\Beat Saber_Data\Managed\netstandard.dll
90 | False
91 | False
92 |
93 |
94 | False
95 | $(BeatSaberDir)\Beat Saber_Data\Managed\Newtonsoft.Json.dll
96 | False
97 |
98 |
99 | False
100 | $(BeatSaberDir)\Plugins\SiraUtil.dll
101 | False
102 |
103 |
104 | False
105 | $(BeatSaberDir)\Plugins\SongCore.dll
106 | False
107 |
108 |
109 | False
110 | $(BeatSaberDir)\Libs\SongDetailsCache.dll
111 | False
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | $(BeatSaberDir)\Beat Saber_Data\Managed\Main.dll
121 | False
122 | True
123 |
124 |
125 | $(BeatSaberDir)\Beat Saber_Data\Managed\HMLib.dll
126 | False
127 |
128 |
129 | $(BeatSaberDir)\Beat Saber_Data\Managed\HMUI.dll
130 | False
131 |
132 |
133 | $(BeatSaberDir)\Beat Saber_Data\Managed\IPA.Loader.dll
134 | False
135 |
136 |
137 | False
138 | $(BeatSaberDir)\Beat Saber_Data\Managed\Unity.ResourceManager.dll
139 | False
140 |
141 |
142 | $(BeatSaberDir)\Beat Saber_Data\Managed\Unity.TextMeshPro.dll
143 | False
144 |
145 |
146 | $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.dll
147 | False
148 |
149 |
150 | $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll
151 | False
152 |
153 |
154 | $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UI.dll
155 | False
156 |
157 |
158 | $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIElementsModule.dll
159 | False
160 |
161 |
162 | $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.UIModule.dll
163 | False
164 |
165 |
166 | $(BeatSaberDir)\Beat Saber_Data\Managed\UnityEngine.VRModule.dll
167 | False
168 |
169 |
170 | False
171 | $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject.dll
172 | False
173 |
174 |
175 | False
176 | $(BeatSaberDir)\Beat Saber_Data\Managed\Zenject-usage.dll
177 | False
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 | 2.0.0-beta7
224 | runtime; build; native; contentfiles; analyzers; buildtransitive
225 | all
226 |
227 |
228 |
229 |
230 |
231 |
--------------------------------------------------------------------------------
/HttpStatusExtention/HttpStatusExtentionController.cs:
--------------------------------------------------------------------------------
1 | using HttpSiraStatus.Enums;
2 | using HttpSiraStatus.Interfaces;
3 | using HttpSiraStatus.Util;
4 | using HttpStatusExtention.Interfaces;
5 | using HttpStatusExtention.Models;
6 | using HttpStatusExtention.PPCounters;
7 | using SiraUtil.Zenject;
8 | using SongCore;
9 | using System;
10 | using System.Collections;
11 | using System.Linq;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 | using UnityEngine;
15 | using Zenject;
16 |
17 | namespace HttpStatusExtention
18 | {
19 | public class HttpStatusExtentionController : IAsyncInitializable, IDisposable
20 | {
21 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
22 | #region // パブリックメソッド
23 | public Task InitializeAsync(CancellationToken token)
24 | {
25 | return this.Setup();
26 | }
27 | #endregion
28 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
29 | #region // プライベートメソッド
30 | private void OnGameResume()
31 | {
32 | _ = CoroutineManager.Instance.StartCoroutine(this.SongStartWait(false));
33 | }
34 | private void SendPP()
35 | {
36 | this.SendScoreSaberPP(this._relativeScoreAndImmediateRankCounter.relativeScore);
37 | this.SendBeatLeaderPP(this._relativeScoreAndImmediateRankCounter.relativeScore);
38 | this.SendAccSaberAP(this._relativeScoreAndImmediateRankCounter.relativeScore);
39 | }
40 |
41 | private void SendScoreSaberPP(float relativeScore)
42 | {
43 | if (this._scoreSaberRowPP == 0) {
44 | return;
45 | }
46 | if (this._statusManager.StatusJSON["performance"] == null) {
47 | this._statusManager.StatusJSON["performance"] = new JSONObject();
48 | }
49 | var jsonObject = this._statusManager.StatusJSON["performance"].AsObject;
50 | jsonObject["current_pp"].AsFloat = this._scoreSaberCalculator.CalculatePP(this._scoreSaberRowPP, relativeScore, this._failed);
51 | this._statusManager.EmitStatusUpdate(ChangedProperty.Performance, BeatSaberEvent.ScoreChanged);
52 | }
53 |
54 | private void SendBeatLeaderPP(float accracy)
55 | {
56 | if (!this._isBeatLeaderRank) {
57 | return;
58 | }
59 | if (this._statusManager.StatusJSON["performance"] == null) {
60 | this._statusManager.StatusJSON["performance"] = new JSONObject();
61 | }
62 | var jsonObject = this._statusManager.StatusJSON["performance"].AsObject;
63 | jsonObject["current_bl_pp"].AsFloat = this._beatLeaderCalculator.CalculatePP(this._songID, accracy, this._failed);
64 | this._statusManager.EmitStatusUpdate(ChangedProperty.Performance, BeatSaberEvent.ScoreChanged);
65 | }
66 |
67 | private void SendAccSaberAP(float accracy)
68 | {
69 | if (!this._isAccSaberRank) {
70 | return;
71 | }
72 | if (this._statusManager.StatusJSON["performance"] == null) {
73 | this._statusManager.StatusJSON["performance"] = new JSONObject();
74 | }
75 | var jsonObject = this._statusManager.StatusJSON["performance"].AsObject;
76 | jsonObject["current_acc_saber_ap"].AsFloat = this._accSaberCalculator.CalculateAP(this._scoreSaberRowPP, accracy);
77 | this._statusManager.EmitStatusUpdate(ChangedProperty.Performance, BeatSaberEvent.ScoreChanged);
78 | }
79 |
80 | private async Task Setup()
81 | {
82 | Plugin.Log.Debug($"Setup start.");
83 | this._gamePause.didResumeEvent += this.OnGameResume;
84 | this._relativeScoreAndImmediateRankCounter.relativeScoreOrImmediateRankDidChangeEvent += this.RelativeScoreAndImmediateRankCounter_relativeScoreOrImmediateRankDidChangeEvent;
85 | var beatmapLevel = this._gameplayCoreSceneSetupData.beatmapLevel;
86 | var beatmapKey = this._gameplayCoreSceneSetupData.beatmapKey;
87 | var key = beatmapKey.beatmapCharacteristic.serializedName;
88 | //this._gameplayCoreSceneSetupData.difficultyBeatmap.parentDifficultyBeatmapSet.beatmapCharacteristic.serializedName;
89 | this._currentBeatmapCharacteristics = Enum.GetValues(typeof(BeatDataCharacteristics)).OfType().FirstOrDefault(x => x.GetDescription() == key);
90 | this._currentBeatmapDifficulty = beatmapKey.difficulty;
91 | //this._gameplayCoreSceneSetupData.difficultyBeatmap.difficulty;
92 | this._levelID = beatmapLevel.levelID;
93 |
94 | var previewBeatmap = Loader.GetLevelById(beatmapLevel.levelID);
95 | this._currentCustomBeatmapLevel = previewBeatmap;
96 | if (this._currentCustomBeatmapLevel != null) {
97 | await this.SetStarInfo(this._levelID);
98 | }
99 | _ = CoroutineManager.Instance.StartCoroutine(this.SongStartWait());
100 | }
101 |
102 | private async Task SetStarInfo(string levelID)
103 | {
104 | var multiplier = this._gameStatus.modifierMultiplier;
105 | this._scoreSaberRowPP = this._ssData.GetPP(this._songID);
106 | this._isBeatLeaderRank = this._beatLeaderData.IsRanked(this._songID);
107 | this._isAccSaberRank = this._accSaberData.IsRanked(this._songID);
108 | this.SetCustomLabel(this._currentCustomBeatmapLevel, this._currentBeatmapDifficulty, this._currentBeatmapCharacteristics);
109 | this._currentStarSong = this._songDataUtil.GetBeatStarSong(this._currentCustomBeatmapLevel);
110 | this._currentStarSongDiff = this._songDataUtil.GetBeatStarSongDiffculityStats(this._currentCustomBeatmapLevel, this._currentBeatmapDifficulty, this._currentBeatmapCharacteristics);
111 | if (this._statusManager.StatusJSON["beatmap"] == null) {
112 | this._statusManager.StatusJSON["beatmap"] = new JSONObject();
113 | }
114 | var beatmapJson = this._statusManager.StatusJSON["beatmap"].AsObject;
115 |
116 | if (this._currentStarSong != null && this._currentStarSongDiff != null) {
117 | while (this._PPDownloader?.Init != true) {
118 | await Task.Delay(1);
119 | }
120 | beatmapJson["pp"] = new JSONNumber(this._scoreSaberCalculator.CalculatePP(this._scoreSaberRowPP, 0.95f));
121 | beatmapJson["bl_pp"] = new JSONNumber(this._beatLeaderCalculator.CalculatePP(this._songID, 0.95f));
122 | beatmapJson["acc_saber_ap"] = new JSONNumber(this._accSaberCalculator.CalculateAP(this._songID, 0.95f));
123 | beatmapJson["star"] = new JSONNumber(this._currentStarSongDiff.Star);
124 | beatmapJson["downloadCount"] = new JSONNumber(this._currentStarSong.DownloadCount);
125 | beatmapJson["upVotes"] = new JSONNumber(this._currentStarSong.Upvotes);
126 | beatmapJson["downVotes"] = new JSONNumber(this._currentStarSong.Downvotes);
127 | beatmapJson["rating"] = new JSONNumber(this._currentStarSong.Rating);
128 | }
129 | }
130 |
131 | private void RelativeScoreAndImmediateRankCounter_relativeScoreOrImmediateRankDidChangeEvent()
132 | {
133 | this.SendPP();
134 | }
135 |
136 | private void SetCustomLabel(BeatmapLevel beatmap, BeatmapDifficulty diff, BeatDataCharacteristics beatDataCharacteristics)
137 | {
138 | if (beatmap == null) {
139 | return;
140 | }
141 | var songData = Collections.RetrieveExtraSongData(SongCore.Utilities.Hashing.GetCustomLevelHash(beatmap));
142 | var diffData = songData._difficulties?.FirstOrDefault(x => x._beatmapCharacteristicName == beatDataCharacteristics.GetDescription() && x._difficulty == diff);
143 | var currentDiffLabel = diffData?._difficultyLabel;
144 | if (string.IsNullOrEmpty(currentDiffLabel)) {
145 | return;
146 | }
147 | if (this._statusManager.StatusJSON["beatmap"] == null) {
148 | this._statusManager.StatusJSON["beatmap"] = new JSONObject();
149 | }
150 | var beatmapJson = this._statusManager.StatusJSON["beatmap"].AsObject;
151 | beatmapJson["customLabel"] = currentDiffLabel;
152 | }
153 |
154 | private IEnumerator SongStartWait(bool songStart = true)
155 | {
156 | if (this._audioTimeSource == null) {
157 | yield break;
158 | }
159 | var songTime = this._audioTimeSource.songTime;
160 | yield return new WaitWhile(() => this._audioTimeSource.songTime <= songTime);
161 | var practiceSettings = this._gameplayCoreSceneSetupData.practiceSettings;
162 | var songSpeedMul = this._gameplayCoreSceneSetupData.gameplayModifiers.songSpeedMul;
163 | if (practiceSettings != null) {
164 | songSpeedMul = practiceSettings.songSpeedMul;
165 | }
166 |
167 | if (songStart) {
168 | this._statusManager.EmitStatusUpdate(ChangedProperty.AllButNoteCut, BeatSaberEvent.SongStart);
169 | }
170 | else {
171 | this._statusManager.EmitStatusUpdate(ChangedProperty.Beatmap, BeatSaberEvent.Resume);
172 | }
173 | }
174 |
175 | private void OnGameEnergyCounter_gameEnergyDidReach0Event()
176 | {
177 | this._failed = true;
178 | if (this._gameEnergyCounter != null) {
179 | this._gameEnergyCounter.gameEnergyDidReach0Event -= this.OnGameEnergyCounter_gameEnergyDidReach0Event;
180 | }
181 | }
182 | #endregion
183 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
184 | #region // メンバ変数
185 | private IGamePause _gamePause;
186 | private IGameStatus _gameStatus;
187 | private bool _disposedValue;
188 | private IStatusManager _statusManager;
189 | private RelativeScoreAndImmediateRankCounter _relativeScoreAndImmediateRankCounter;
190 | private IAudioTimeSource _audioTimeSource;
191 | private GameplayCoreSceneSetupData _gameplayCoreSceneSetupData;
192 | private ISongDataUtil _songDataUtil;
193 | private BeatmapLevel _currentCustomBeatmapLevel;
194 | private BeatmapDifficulty _currentBeatmapDifficulty;
195 | private BeatDataCharacteristics _currentBeatmapCharacteristics;
196 | private BeatSongData _currentStarSong;
197 | private BeatSongDataDifficultyStats _currentStarSongDiff;
198 | private float _scoreSaberRowPP;
199 | private bool _isBeatLeaderRank;
200 | private bool _isAccSaberRank;
201 | private string _levelID;
202 | private bool _failed = false;
203 | private ScoreSaberCalculator _scoreSaberCalculator;
204 | private SSData _ssData;
205 | private BeatLeaderData _beatLeaderData;
206 | private AccSaberData _accSaberData;
207 | private BeatLeaderCalculator _beatLeaderCalculator;
208 | private AccSaberCalculator _accSaberCalculator;
209 | private PPDownloader _PPDownloader;
210 | private SongID _songID;
211 | private IGameEnergyCounter _gameEnergyCounter;
212 | #endregion
213 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
214 | #region // 構築・破棄
215 | [Inject]
216 | protected void Constractor(
217 | IStatusManager statusManager,
218 | IGameStatus gameStatus,
219 | RelativeScoreAndImmediateRankCounter relativeScoreAndImmediateRankCounter,
220 | IAudioTimeSource audioTimeSource,
221 | GameplayCoreSceneSetupData gameplayCoreSceneSetupData,
222 | ISongDataUtil songDataUtil,
223 | IGamePause gamePause,
224 | PPDownloader pPDownloader,
225 | ScoreSaberCalculator scoreSaberCalculator,
226 | BeatLeaderCalculator beatLeaderCalculator,
227 | AccSaberCalculator accSaberCalculator,
228 | SSData ssData,
229 | BeatLeaderData beatLeaderData,
230 | AccSaberData accSaberData,
231 | IGameEnergyCounter gameEnergyCounter)
232 | {
233 | this._statusManager = statusManager;
234 | this._relativeScoreAndImmediateRankCounter = relativeScoreAndImmediateRankCounter;
235 | this._audioTimeSource = audioTimeSource;
236 | this._gameplayCoreSceneSetupData = gameplayCoreSceneSetupData;
237 | this._songDataUtil = songDataUtil;
238 | this._gamePause = gamePause;
239 | this._gameStatus = gameStatus;
240 | this._scoreSaberCalculator = scoreSaberCalculator;
241 | this._beatLeaderCalculator = beatLeaderCalculator;
242 | this._accSaberCalculator = accSaberCalculator;
243 | this._PPDownloader = pPDownloader;
244 | this._ssData = ssData;
245 | this._beatLeaderData = beatLeaderData;
246 | this._accSaberData = accSaberData;
247 | this._gameEnergyCounter = gameEnergyCounter;
248 | this._gameEnergyCounter.gameEnergyDidReach0Event += this.OnGameEnergyCounter_gameEnergyDidReach0Event;
249 | var level = gameplayCoreSceneSetupData.beatmapLevel;
250 | var key = gameplayCoreSceneSetupData.beatmapKey;
251 | var id = SongDataUtils.GetHash(level.levelID);
252 | this._songID = new SongID(id, key.difficulty);
253 | }
254 |
255 | protected virtual void Dispose(bool disposing)
256 | {
257 | if (!this._disposedValue) {
258 | if (disposing) {
259 | // TODO: マネージド状態を破棄します (マネージド オブジェクト)
260 | Plugin.Log.Debug($"Dispose call");
261 | this._gamePause.didResumeEvent -= this.OnGameResume;
262 | this._relativeScoreAndImmediateRankCounter.relativeScoreOrImmediateRankDidChangeEvent -= this.RelativeScoreAndImmediateRankCounter_relativeScoreOrImmediateRankDidChangeEvent;
263 | }
264 | this._disposedValue = true;
265 | }
266 | }
267 | public void Dispose()
268 | {
269 | // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
270 | this.Dispose(disposing: true);
271 | GC.SuppressFinalize(this);
272 | }
273 |
274 | #endregion
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Installer/HttpStatusExtentionAppInstaller.cs:
--------------------------------------------------------------------------------
1 | using HttpStatusExtention.PPCounters;
2 | using HttpStatusExtention.SongDetailsCaches;
3 | using Zenject;
4 |
5 | namespace HttpStatusExtention.Installers
6 | {
7 | public class HttpStatusExtentionAppInstaller : Installer
8 | {
9 | public override void InstallBindings()
10 | {
11 | _ = this.Container.BindInterfacesAndSelfTo().AsSingle().NonLazy();
12 | _ = this.Container.BindInterfacesAndSelfTo().AsSingle().NonLazy();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Installer/HttpStatusExtentionInstaller.cs:
--------------------------------------------------------------------------------
1 | using HttpStatusExtention.PPCounters;
2 |
3 | namespace HttpStatusExtention.Installers
4 | {
5 | public class HttpStatusExtentionInstaller : Zenject.Installer
6 | {
7 | public override void InstallBindings()
8 | {
9 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
10 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
11 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
12 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
13 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
14 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
15 | _ = this.Container.BindInterfacesAndSelfTo().AsCached();
16 | _ = this.Container.BindInterfacesAndSelfTo().AsCached().NonLazy();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Installer/HttpStatusExtentionMenuAndGameInstaller.cs:
--------------------------------------------------------------------------------
1 | using Zenject;
2 |
3 | namespace HttpStatusExtention.Installers
4 | {
5 | public class HttpStatusExtentionMenuAndGameInstaller : MonoInstaller
6 | {
7 | public override void InstallBindings()
8 | {
9 | _ = this.Container.BindInterfacesAndSelfTo().AsCached().NonLazy();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Interfaces/ISongDataUtil.cs:
--------------------------------------------------------------------------------
1 | using HttpStatusExtention.Models;
2 |
3 | namespace HttpStatusExtention.Interfaces
4 | {
5 | public interface ISongDataUtil
6 | {
7 | BeatSongData GetBeatStarSong(BeatmapLevel beatmapLevel);
8 | BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatSongData song, BeatmapDifficulty difficulty);
9 | BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatSongData song, BeatmapDifficulty difficulty, BeatDataCharacteristics beatDataCharacteristics);
10 | BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatmapLevel beatmapLevel, BeatmapDifficulty difficulty);
11 | BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatmapLevel beatmapLevel, BeatmapDifficulty difficulty, BeatDataCharacteristics beatDataCharacteristics);
12 | double GetPP(BeatmapLevel beatmapLevel, BeatmapDifficulty difficulty, BeatDataCharacteristics beatDataCharacteristics);
13 | void Initialize();
14 | bool IsRank(BeatmapLevel beatmapLevel, BeatmapDifficulty beatmapDifficulty, BeatDataCharacteristics beatDataCharacteristics);
15 | bool IsRank(string levelID, BeatmapDifficulty beatmapDifficulty, BeatDataCharacteristics beatDataCharacteristics);
16 | }
17 | }
--------------------------------------------------------------------------------
/HttpStatusExtention/Models/BeatSongData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 |
4 | namespace HttpStatusExtention.Models
5 | {
6 | public class BeatSongData
7 | {
8 | public float BPM { get; set; }
9 | public int DownloadCount { get; set; }
10 | public int Upvotes { get; set; }
11 | public int Downvotes { get; set; }
12 | public int SongDuration { get; set; }
13 | public int DiffOffset { get; set; }
14 | public int DiffCount { get; set; }
15 | public RankStatus RankedStatus { get; set; }
16 | public float Rating { get; set; }
17 | public DateTime UploadTime { get; set; }
18 | public string Key { get; set; }
19 | public string Hash { get; set; }
20 | public string SongName { get; set; }
21 | public string SongAuthorName { get; set; }
22 | public string LevelAuthorName { get; set; }
23 | public string CoverURL { get; set; }
24 | public string UploaderName { get; set; }
25 | public ConcurrentDictionary> Characteristics { get; set; }
26 | }
27 | }
--------------------------------------------------------------------------------
/HttpStatusExtention/Models/BeatSongDataDifficultyStats.cs:
--------------------------------------------------------------------------------
1 | namespace HttpStatusExtention.Models
2 | {
3 | public class BeatSongDataDifficultyStats
4 | {
5 | public float Star { get; set; }
6 | public float NJS { get; set; }
7 | public int Bombs { get; set; }
8 | public int Notes { get; set; }
9 | public int Obstacles { get; set; }
10 | public bool Ranked { get; set; }
11 | public BeatDataCharacteristics Characteristics { get; set; }
12 | public BeatMapDifficulty Difficulty { get; set; }
13 | public RecomendMod Mods { get; set; }
14 | public BeatSongData Song { get; set; }
15 | public float PP { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Models/CoroutineManager.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine.ResourceManagement.Util;
2 |
3 | namespace HttpStatusExtention.Models
4 | {
5 | public class CoroutineManager : ComponentSingleton
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Models/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 |
4 | namespace HttpStatusExtention.Models
5 | {
6 | public enum BeatDataCharacteristics
7 | {
8 | [Description("Custom")]
9 | Unkown,
10 | [Description("Standard")]
11 | Standard,
12 | [Description("OneSaber")]
13 | OneSaber,
14 | [Description("NoArrows")]
15 | NoArrows,
16 | [Description("Lightshow")]
17 | Lightshow,
18 | [Description("90Degree")]
19 | Degree90,
20 | [Description("360Degree")]
21 | Degree360,
22 | [Description("Lawless")]
23 | Lawless
24 | }
25 |
26 | public enum BeatMapDifficulty
27 | {
28 | Easy = 1,
29 | Normal,
30 | Hard,
31 | Expert,
32 | ExpertPlus
33 | }
34 | [Flags]
35 | public enum RecomendMod
36 | {
37 | NoodleExtensions = 1 << 1,
38 | MappingExtensions = 1 << 2,
39 | Chroma = 1 << 3,
40 | Cinema = 1 << 4
41 | }
42 |
43 | public enum RankStatus
44 | {
45 | Unranked,
46 | Ranked,
47 | Qualified,
48 | Queued
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Networks/WebClient.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Linq;
2 | using System;
3 | using System.IO;
4 | using System.Net;
5 | using System.Net.Http;
6 | using System.Net.Http.Headers;
7 | using System.Reflection;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | namespace HttpStatusExtention
13 | {
14 | public class WebResponse
15 | {
16 | public readonly HttpStatusCode StatusCode;
17 | public readonly string ReasonPhrase;
18 | public readonly HttpResponseHeaders Headers;
19 | public readonly HttpRequestMessage RequestMessage;
20 | public readonly bool IsSuccessStatusCode;
21 |
22 | private readonly byte[] _content;
23 |
24 | internal WebResponse(HttpResponseMessage resp, byte[] body)
25 | {
26 | this.StatusCode = resp.StatusCode;
27 | this.ReasonPhrase = resp.ReasonPhrase;
28 | this.Headers = resp.Headers;
29 | this.RequestMessage = resp.RequestMessage;
30 | this.IsSuccessStatusCode = resp.IsSuccessStatusCode;
31 |
32 | this._content = body;
33 | }
34 |
35 | public byte[] ContentToBytes()
36 | {
37 | return this._content;
38 | }
39 |
40 | public string ContentToString()
41 | {
42 | return Encoding.UTF8.GetString(this._content);
43 | }
44 |
45 | public JToken ConvertToJToken()
46 | {
47 | return JToken.Parse(this.ContentToString());
48 | }
49 | }
50 |
51 | internal static class WebClient
52 | {
53 | private static HttpClient _client;
54 | private static HttpClient Client
55 | {
56 | get
57 | {
58 | if (_client == null) {
59 | Connect();
60 | }
61 |
62 | return _client;
63 | }
64 | }
65 |
66 | private static readonly int RETRY_COUNT = 5;
67 |
68 | private static void Connect()
69 | {
70 | try {
71 | _client?.Dispose();
72 | }
73 | catch (Exception e) {
74 | Plugin.Log.Error(e);
75 | }
76 |
77 | _client = new HttpClient()
78 | {
79 | Timeout = new TimeSpan(0, 0, 15)
80 | };
81 | _ = _client.DefaultRequestHeaders.UserAgent.TryParseAdd($"{Assembly.GetExecutingAssembly().GetName().Name}/{Assembly.GetExecutingAssembly().GetName().Version}");
82 | }
83 |
84 | internal static async Task GetAsync(string url, CancellationToken token)
85 | {
86 | try {
87 | return await SendAsync(HttpMethod.Get, url, token);
88 | }
89 | catch (Exception e) {
90 | Plugin.Log.Error(e);
91 | return null;
92 | }
93 | }
94 |
95 | internal static async Task DownloadImage(string url, CancellationToken token)
96 | {
97 | try {
98 | var response = await SendAsync(HttpMethod.Get, url, token);
99 | return response?.IsSuccessStatusCode == true ? response.ContentToBytes() : null;
100 | }
101 | catch (Exception e) {
102 | Plugin.Log.Error(e);
103 | return null;
104 | }
105 | }
106 |
107 | ///
108 | /// たぶんもう使わない。
109 | ///
110 | ///
111 | ///
112 | ///
113 | ///
114 | internal static async Task DownloadSong(string hash, CancellationToken token, IProgress progress = null)
115 | {
116 | // check if beatsaver url needs to be pre-pended
117 | try {
118 | var response = await SendAsync(HttpMethod.Get, hash, token, progress: progress);
119 |
120 | return response?.IsSuccessStatusCode == true ? response.ContentToBytes() : null;
121 | }
122 | catch (Exception e) {
123 | Plugin.Log.Error(e);
124 | return null;
125 | }
126 | }
127 |
128 | internal static async Task SendAsync(HttpMethod methodType, string url, CancellationToken token, IProgress progress = null)
129 | {
130 | // send request
131 | try {
132 | HttpResponseMessage resp = null;
133 | var retryCount = 0;
134 | do {
135 | try {
136 | // create new request messsage
137 | var req = new HttpRequestMessage(methodType, url);
138 | if (retryCount != 0) {
139 | await Task.Delay(1000);
140 | }
141 | retryCount++;
142 | resp = await Client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
143 | Plugin.Log.Debug($"resp code : {resp.StatusCode}");
144 | }
145 | catch (Exception e) {
146 | Plugin.Log.Error($"resp code : {resp?.StatusCode}");
147 | Plugin.Log.Error(e);
148 | }
149 | } while (resp?.StatusCode != HttpStatusCode.NotFound && resp?.IsSuccessStatusCode != true && retryCount <= RETRY_COUNT);
150 |
151 | if (token.IsCancellationRequested) {
152 | throw new TaskCanceledException();
153 | }
154 |
155 | using (var memoryStream = new MemoryStream())
156 | using (var stream = await resp.Content.ReadAsStreamAsync().ConfigureAwait(false)) {
157 | var buffer = new byte[8192];
158 | var bytesRead = 0;
159 |
160 | var contentLength = resp?.Content.Headers.ContentLength;
161 | var totalRead = 0;
162 |
163 | // send report
164 | progress?.Report(0);
165 |
166 | while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0) {
167 | if (token.IsCancellationRequested) {
168 | throw new TaskCanceledException();
169 | }
170 |
171 | if (contentLength != null) {
172 | progress?.Report(totalRead / (double)contentLength);
173 | }
174 |
175 | await memoryStream.WriteAsync(buffer, 0, bytesRead).ConfigureAwait(false);
176 | totalRead += bytesRead;
177 | }
178 |
179 | progress?.Report(1);
180 | var bytes = memoryStream.ToArray();
181 |
182 | return new WebResponse(resp, bytes);
183 | }
184 | }
185 | catch (Exception e) {
186 | Plugin.Log.Error(e);
187 | throw;
188 | }
189 | }
190 | }
191 | }
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Calculators/AccSaberCalculator.cs:
--------------------------------------------------------------------------------
1 | using SiraUtil.Zenject;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Zenject;
6 |
7 | namespace HttpStatusExtention.PPCounters
8 | {
9 | public class AccSaberCalculator : IAsyncInitializable
10 | {
11 | [Inject] private readonly AccSaberData accSaberData;
12 |
13 | private List _curve;
14 | private float[] _slopes;
15 |
16 | private float _scale;
17 | private float _shift;
18 | private readonly PPData _pPData;
19 |
20 | public AccSaberCalculator(PPData pPData)
21 | {
22 | this._pPData = pPData;
23 | }
24 |
25 | public void SetCurve(AccSaber accSaber)
26 | {
27 | this._curve = accSaber.curve;
28 | this._scale = accSaber.scale;
29 | this._shift = accSaber.shift;
30 |
31 | this._slopes = CurveUtils.GetSlopes(this._curve);
32 | }
33 |
34 | public bool IsRanked(SongID songID)
35 | {
36 | return this.accSaberData.IsRanked(songID);
37 | }
38 |
39 | public float CalculateAP(SongID songID, float accuracy)
40 | {
41 | var complexity = this.accSaberData.GetComplexity(songID);
42 | return this.CalculateAP(complexity, accuracy);
43 | }
44 |
45 | public float CalculateAP(float complexity, float accuracy)
46 | {
47 | return CurveUtils.GetCurveMultiplier(this._curve, this._slopes, accuracy) * (complexity - this._shift) * this._scale;
48 | }
49 |
50 | public async Task InitializeAsync(CancellationToken token)
51 | {
52 | while (this._pPData?.CurveInit != true) {
53 | await Task.Delay(1);
54 | }
55 | this.SetCurve(this._pPData.Curves.AccSaber);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Calculators/BeatLeaderCalculator.cs:
--------------------------------------------------------------------------------
1 | using SiraUtil.Zenject;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using UnityEngine;
7 | using Zenject;
8 |
9 | namespace HttpStatusExtention.PPCounters
10 | {
11 | public class BeatLeaderCalculator : IAsyncInitializable
12 | {
13 | [Inject] private readonly BeatLeaderData beatLeaderData;
14 |
15 | private List _accCurve;
16 | private float[] _accSlopes;
17 | private float _accMultiplier;
18 |
19 | private float _passExponential;
20 | private float _passMultiplier;
21 | private float _passShift;
22 |
23 | private float _techExponentialMultiplier;
24 | private float _techMultiplier;
25 |
26 | private float _inflateExponential;
27 | private float _inflateMultiplier;
28 |
29 | private float _modifierMultiplier;
30 | private ModifiersMap _modifiersMap;
31 | private float _powerBottom;
32 | private BeatLeaderRating _rating;
33 | private float _passPP;
34 | private readonly GameplayModifiers gameplayModifiers;
35 | private readonly PPData _pPData;
36 | private BeatmapLevel _level;
37 | private BeatmapKey _key;
38 |
39 |
40 | [Inject]
41 | public BeatLeaderCalculator(PPData pPData, RelativeScoreAndImmediateRankCounter relativeScoreAndImmediateRankCounter, GameplayModifiers gameplayModifiers, GameplayCoreSceneSetupData gameplayCoreSceneSetupData)
42 | {
43 | this.gameplayModifiers = gameplayModifiers;
44 | this._pPData = pPData;
45 | _level = gameplayCoreSceneSetupData.beatmapLevel;
46 | _key = gameplayCoreSceneSetupData.beatmapKey;
47 | }
48 |
49 | public void SetCurve(BeatLeader beatLeader, SongID songID, GameplayModifiers modifiers)
50 | {
51 | this._accCurve = beatLeader.accCurve;
52 | this._accMultiplier = beatLeader.accMultiplier;
53 |
54 | this._passExponential = beatLeader.passExponential;
55 | this._passMultiplier = beatLeader.passMultiplier;
56 | this._passShift = beatLeader.passShift;
57 |
58 | this._techExponentialMultiplier = beatLeader.techExponentialMultiplier;
59 | this._techMultiplier = beatLeader.techMultiplier;
60 |
61 | this._inflateExponential = beatLeader.inflateExponential;
62 | this._inflateMultiplier = beatLeader.inflateMultiplier;
63 |
64 | this._modifiersMap = this.beatLeaderData.GetModifiersMap(songID);
65 |
66 | this.CalculateModifiersMultiplier(songID, modifiers);
67 |
68 | this._powerBottom = 0;
69 |
70 | this._rating = this.beatLeaderData.GetStars(songID);
71 | this._passPP = this.GetPassPP(this._rating.passRating * this._modifierMultiplier);
72 |
73 | this._accSlopes = CurveUtils.GetSlopes(this._accCurve);
74 | }
75 |
76 | public bool IsRanked(SongID songID)
77 | {
78 | return this.beatLeaderData.IsRanked(songID);
79 | }
80 |
81 | // hopefully this doesn't take too long to run...
82 | public float CalculatePP(SongID songID, float accuracy, bool failed = false)
83 | {
84 | var multiplier = this._modifierMultiplier + (failed ? this._modifiersMap.nf : 0);
85 |
86 | var passPP = this._passPP;
87 |
88 | // TODO: don't calculate this every time
89 | if (failed) {
90 | passPP = this.GetPassPP(this._rating.passRating * multiplier);
91 | }
92 |
93 | var accPP = this.GetAccPP(this._rating.accRating * multiplier, accuracy);
94 | var techPP = this.GetTechPP(this._rating.techRating * multiplier, accuracy);
95 |
96 | var rawPP = this.Inflate(passPP + accPP + techPP);
97 |
98 | if (float.IsInfinity(rawPP) || float.IsNaN(rawPP) || float.IsNegativeInfinity(rawPP)) {
99 | rawPP = 0;
100 | }
101 |
102 | return rawPP;
103 | }
104 |
105 | private float Inflate(float pp)
106 | {
107 | if (Mathf.Approximately(this._powerBottom, 0)) {
108 | this._powerBottom = Mathf.Pow(this._inflateMultiplier, this._inflateExponential);
109 | }
110 |
111 | return this._inflateMultiplier * Mathf.Pow(pp, this._inflateExponential) / this._powerBottom;
112 | }
113 |
114 | private float GetPassPP(float passRating)
115 | {
116 | var passPP = (this._passMultiplier * Mathf.Exp(Mathf.Pow(passRating, this._passExponential))) + this._passShift;
117 | if (float.IsInfinity(passPP) || float.IsNaN(passPP) || float.IsNegativeInfinity(passPP) || passPP < 0) {
118 | passPP = 0;
119 | }
120 |
121 | return passPP;
122 | }
123 |
124 | private float GetAccPP(float accRating, float accuracy)
125 | {
126 | return CurveUtils.GetCurveMultiplier(this._accCurve, this._accSlopes, accuracy) * accRating * this._accMultiplier;
127 | }
128 |
129 | private float GetTechPP(float techRating, float accuracy)
130 | {
131 | return (float)Math.Exp(this._techExponentialMultiplier * accuracy) * this._techMultiplier * techRating;
132 | }
133 |
134 | private void CalculateModifiersMultiplier(SongID songID, GameplayModifiers modifiers)
135 | {
136 | this._modifierMultiplier = 1;
137 |
138 | if (modifiers.disappearingArrows) {
139 | this._modifierMultiplier += this._modifiersMap.da;
140 | }
141 | if (modifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.Faster)) {
142 | this._modifierMultiplier += this._modifiersMap.fs;
143 | }
144 | else if (modifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.Slower)) {
145 | this._modifierMultiplier += this._modifiersMap.ss;
146 | }
147 | else if (modifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.SuperFast)) {
148 | this._modifierMultiplier += this._modifiersMap.sf;
149 | }
150 | if (modifiers.ghostNotes) {
151 | this._modifierMultiplier += this._modifiersMap.gn;
152 | }
153 | if (modifiers.noArrows) {
154 | this._modifierMultiplier += this._modifiersMap.na;
155 | }
156 | if (modifiers.noBombs) {
157 | this._modifierMultiplier += this._modifiersMap.nb;
158 | }
159 | if (modifiers.enabledObstacleType.Equals(GameplayModifiers.EnabledObstacleType.NoObstacles)) {
160 | this._modifierMultiplier += this._modifiersMap.no;
161 | }
162 | if (modifiers.proMode) {
163 | this._modifierMultiplier += this._modifiersMap.pm;
164 | }
165 | if (modifiers.smallCubes) {
166 | this._modifierMultiplier += this._modifiersMap.sc;
167 | }
168 | }
169 |
170 | public async Task InitializeAsync(CancellationToken token)
171 | {
172 | while (this._pPData?.CurveInit != true) {
173 | await Task.Delay(1);
174 | }
175 | var id = SongDataUtils.GetHash(this._level.levelID);
176 | this.SetCurve(this._pPData.Curves.BeatLeader, new SongID(id, this._key.difficulty), this.gameplayModifiers);
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Calculators/ScoreSaberCalculator.cs:
--------------------------------------------------------------------------------
1 | using SiraUtil.Zenject;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using Zenject;
7 |
8 | namespace HttpStatusExtention.PPCounters
9 | {
10 | public class ScoreSaberCalculator : IAsyncInitializable
11 | {
12 | [Inject] private readonly SSData ssData;
13 | private readonly RelativeScoreAndImmediateRankCounter relativeScoreAndImmediateRank;
14 | //private readonly IDifficultyBeatmap difficultyBeatmap;
15 | private BeatmapLevel _level;
16 | private BeatmapKey _key;
17 | private readonly GameplayModifiers gameplayModifiers;
18 | private readonly PPData ppData;
19 |
20 | private HashSet songsAllowingPositiveModifiers = new HashSet();
21 |
22 | private List _curve;
23 | private float[] _slopes;
24 | private float _multiplier;
25 |
26 | [Inject]
27 | public ScoreSaberCalculator(GameplayCoreSceneSetupData gameplayCoreSceneSetupData, RelativeScoreAndImmediateRankCounter relativeScoreAndImmediateRankCounter, GameplayModifiers gameplayModifiers, PPData pPData)
28 | {
29 | _level = gameplayCoreSceneSetupData.beatmapLevel;
30 | _key = gameplayCoreSceneSetupData.beatmapKey;
31 | this.gameplayModifiers = gameplayModifiers;
32 | this.relativeScoreAndImmediateRank = relativeScoreAndImmediateRankCounter;
33 | this.ppData = pPData;
34 | }
35 |
36 | public async Task InitializeAsync(CancellationToken token)
37 | {
38 | if (this.ppData?.CurveInit != true) {
39 | await Task.Delay(1);
40 | }
41 | var id = SongDataUtils.GetHash(this._level.levelID);
42 | this.SetCurve(this.ppData.Curves.ScoreSaber, new SongID(id, this._key.difficulty), this.relativeScoreAndImmediateRank._gameplayModifiersModel, this.gameplayModifiers);
43 | }
44 |
45 | public void SetCurve(ScoreSaber scoreSaber, SongID songID, GameplayModifiersModelSO gameplayModifiersModelSO, GameplayModifiers gameplayModifiers)
46 | {
47 | var allowedPositiveModifiers = this.AllowedPositiveModifiers(songID);
48 | var updatedModifiers = allowedPositiveModifiers ? GameplayModifierUtils.RemoveSuperFast(gameplayModifiers) : GameplayModifierUtils.RemovePositiveModifiers(gameplayModifiers);
49 |
50 | this._multiplier = CalculateMultiplier(gameplayModifiersModelSO, updatedModifiers, scoreSaber.modifiers);
51 |
52 | this.songsAllowingPositiveModifiers = scoreSaber.songsAllowingPositiveModifiers.ToHashSet();
53 |
54 | this._curve = allowedPositiveModifiers ? scoreSaber.modifierCurve : scoreSaber.standardCurve;
55 | this._slopes = CurveUtils.GetSlopes(this._curve);
56 | }
57 |
58 | public bool IsRanked(SongID songID)
59 | {
60 | return this.ssData.IsRanked(songID);
61 | }
62 |
63 | public bool AllowedPositiveModifiers(SongID songID)
64 | {
65 | return this.AllowedPositiveModifiers(songID.id);
66 | }
67 |
68 | public bool AllowedPositiveModifiers(string songID)
69 | {
70 | return this.songsAllowingPositiveModifiers.Contains(songID);
71 | }
72 |
73 | public float CalculatePP(SongID songID, float accuracy, bool failed = false)
74 | {
75 | var rawPP = this.ssData.GetPP(songID);
76 | return this.CalculatePP(rawPP, accuracy, failed);
77 | }
78 |
79 | public float CalculatePP(float rawPP, float accuracy, bool failed = false)
80 | {
81 | var multiplier = this._multiplier;
82 | if (failed) {
83 | multiplier -= 0.5f;
84 | }
85 |
86 | return rawPP * CurveUtils.GetCurveMultiplier(this._curve, this._slopes, accuracy * multiplier);
87 | }
88 |
89 | public static float CalculateMultiplier(GameplayModifiersModelSO gameplayModifiersModelSO, GameplayModifiers gameplayModifiers, ScoreSaberModifiers modifierMultipliers)
90 | {
91 | var modifierParams = gameplayModifiersModelSO.CreateModifierParamsList(gameplayModifiers);
92 | var multiplier = gameplayModifiersModelSO.GetTotalMultiplier(modifierParams, 1f);
93 |
94 | // ScoreSaber weights these multipliers differently
95 | if (gameplayModifiers.disappearingArrows) {
96 | multiplier += modifierMultipliers.da - GameplayModifierUtils.DA_ORIGINAL;
97 | }
98 |
99 | if (gameplayModifiers.ghostNotes) {
100 | multiplier += modifierMultipliers.gn - GameplayModifierUtils.GN_ORIGINAL;
101 | }
102 |
103 | if (gameplayModifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.Faster)) {
104 | multiplier += modifierMultipliers.fs - GameplayModifierUtils.FS_ORIGINAL;
105 | }
106 |
107 | return multiplier;
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/CurveUtils.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace HttpStatusExtention.PPCounters
5 | {
6 | internal static class CurveUtils
7 | {
8 | public static float[] GetSlopes(List curve)
9 | {
10 | var slopes = new float[curve.Count - 1];
11 | for (var i = 0; i < curve.Count - 1; i++) {
12 | var x1 = curve[i].x;
13 | var y1 = curve[i].y;
14 | var x2 = curve[i + 1].x;
15 | var y2 = curve[i + 1].y;
16 |
17 | var m = (y2 - y1) / (x2 - x1);
18 | slopes[i] = m;
19 | }
20 |
21 | return slopes;
22 | }
23 |
24 | public static float GetCurveMultiplier(List curve, float[] slopes, float accuracy)
25 | {
26 | if (accuracy >= curve.Last().x) {
27 | return curve.Last().y;
28 | }
29 |
30 | if (accuracy <= 0) {
31 | return 0f;
32 | }
33 |
34 | var i = -1;
35 |
36 | foreach (var point in curve) {
37 | if (point.x > accuracy) {
38 | break;
39 | }
40 |
41 | i++;
42 | }
43 |
44 | var lowerScore = curve[i].x;
45 | var lowerGiven = curve[i].y;
46 |
47 | return Lerp(slopes, lowerScore, lowerGiven, accuracy, i);
48 | }
49 |
50 | public static float Lerp(float[] slopes, float x1, float y1, float x3, int i)
51 | {
52 | var m = slopes[i];
53 |
54 | return (m * (x3 - x1)) + y1;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Data/AccSaberData.cs:
--------------------------------------------------------------------------------
1 | using SiraUtil.Zenject;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Zenject;
8 |
9 | namespace HttpStatusExtention.PPCounters
10 | {
11 | public class AccSaberData : IAsyncInitializable
12 | {
13 | private readonly bool _dataInitStart = false;
14 | public bool DataInit { get; private set; } = false;
15 | [Inject]
16 | private readonly PPDownloader _ppDownloader;
17 | private readonly Dictionary _rankedMaps = new Dictionary();
18 |
19 | private static readonly string ACCSABER_FILE_NAME = Path.Combine(Environment.CurrentDirectory, "UserData", "HttpStatusExtention", "accsaber.json");
20 |
21 | public async Task InitializeAsync(CancellationToken token)
22 | {
23 | while (this._ppDownloader?.Init != true) {
24 | await Task.Delay(1);
25 | }
26 | this.CreateRankedMapsDict(this._ppDownloader.AccSaberData);
27 | this.DataInit = true;
28 | }
29 |
30 | public float GetComplexity(SongID songID)
31 | {
32 | return !this.DataInit ? 0 : !this._rankedMaps.ContainsKey(songID) ? 0 : this._rankedMaps[songID];
33 | }
34 |
35 | public bool IsRanked(SongID songID)
36 | {
37 | return this._rankedMaps.ContainsKey(songID);
38 | }
39 |
40 | private void CreateRankedMapsDict(List rankedMaps)
41 | {
42 | foreach (var rankedMap in rankedMaps) {
43 | var id = rankedMap.songHash.ToUpper();
44 | var beatmapDifficulty = SongDataUtils.GetDifficulty(rankedMap.difficulty);
45 | var songID = new SongID(id, beatmapDifficulty);
46 | this._rankedMaps[songID] = rankedMap.complexity;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Data/BeatLeaderData.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using Zenject;
6 |
7 | namespace HttpStatusExtention.PPCounters
8 | {
9 | public class BeatLeaderData : IInitializable
10 | {
11 | private static readonly string BL_CACHE_FILE = Path.Combine(Environment.CurrentDirectory, "UserData", "BeatLeader", "LeaderboardsCache");
12 | public bool DataInit { get; private set; } = false;
13 |
14 | private readonly Dictionary _cache = new Dictionary();
15 |
16 | public void Initialize()
17 | {
18 | // TODO: support this better - won't work on first cache creation, or respect in-game cache updates.
19 | // Could use reflection to access BL cache, but may also want to load data myself so it doesn't rely on bl mod
20 | this.TryLoadCache();
21 | }
22 |
23 | public bool IsRanked(SongID songID)
24 | {
25 | return this._cache.ContainsKey(songID) && this._cache[songID].DifficultyInfo.stars > 0;
26 | }
27 |
28 | public BeatLeaderRating GetStars(SongID songID)
29 | {
30 | if (!this.DataInit) {
31 | return default;
32 | }
33 |
34 | if (!this._cache.ContainsKey(songID)) {
35 | return default;
36 | }
37 |
38 | var diffInfo = this._cache[songID].DifficultyInfo;
39 | return new BeatLeaderRating(diffInfo.accRating, diffInfo.passRating, diffInfo.techRating);
40 | }
41 |
42 | public ModifiersMap GetModifiersMap(SongID songID)
43 | {
44 | if (!this.DataInit) {
45 | return default;
46 | }
47 |
48 | if (!this._cache.ContainsKey(songID)) {
49 | return default;
50 | }
51 |
52 | var diffInfo = this._cache[songID].DifficultyInfo;
53 | return this._cache[songID].DifficultyInfo.modifierValues;
54 | }
55 |
56 | private void TryLoadCache()
57 | {
58 | if (File.Exists(BL_CACHE_FILE)) {
59 | try {
60 | var data = File.ReadAllText(BL_CACHE_FILE);
61 | var cacheFileData = JsonConvert.DeserializeObject(data);
62 | this.CreateCache(cacheFileData);
63 | this.DataInit = true;
64 | }
65 | catch (Exception) {
66 | }
67 | }
68 | }
69 |
70 | private void CreateCache(BeatLeaderCacheFileData cacheFileData)
71 | {
72 | foreach (var entry in cacheFileData.Entries) {
73 | var songID = new SongID(entry.SongInfo.hash.ToUpper(), SongDataUtils.GetDifficulty(entry.DifficultyInfo.difficultyName));
74 | this._cache[songID] = entry;
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Data/PPData.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SiraUtil.Zenject;
3 | using System;
4 | using System.IO;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Zenject;
8 |
9 | namespace HttpStatusExtention.PPCounters
10 | {
11 | public class PPData : IAsyncInitializable
12 | {
13 | public bool DataInit { get; private set; } = false;
14 | public bool CurveInit { get; private set; } = false;
15 | [Inject] private readonly PPDownloader _ppDownloader;
16 |
17 | public Leaderboards Curves { get; private set; } = new Leaderboards();
18 |
19 | private static readonly string CURVE_FILE_NAME = Path.Combine(Environment.CurrentDirectory, "UserData", "HttpStatusExtention", "curves.json");
20 |
21 | public async Task InitializeAsync(CancellationToken token)
22 | {
23 | this.LoadCurveFile();
24 | while (this._ppDownloader?.Init != true) {
25 | await Task.Delay(1);
26 | }
27 | lock (this.Curves) {
28 | this.Curves = this._ppDownloader.Curves;
29 | this.CurveInit = true;
30 | this.WriteCurveFile();
31 | }
32 | }
33 |
34 | private void LoadCurveFile()
35 | {
36 | if (File.Exists(CURVE_FILE_NAME)) {
37 | try {
38 |
39 | lock (this.Curves) {
40 | if (!this.CurveInit) {
41 | var jsonString = File.ReadAllText(CURVE_FILE_NAME);
42 | this.Curves = JsonConvert.DeserializeObject(jsonString);
43 | this.CurveInit = true;
44 | }
45 | }
46 | }
47 | catch (Exception) {
48 |
49 | }
50 | }
51 | }
52 |
53 | private void WriteCurveFile()
54 | {
55 | OSUtils.WriteFile(this.Curves, CURVE_FILE_NAME);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Data/SSData.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SiraUtil.Zenject;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Zenject;
9 |
10 | namespace HttpStatusExtention.PPCounters
11 | {
12 | public class SSData : IAsyncInitializable
13 | {
14 | public bool DataInit { get; private set; } = false;
15 | [Inject] private readonly PPDownloader _ppDownloader;
16 | private IReadOnlyDictionary _songData = new Dictionary();
17 |
18 | private static readonly string SS_PP_FILE_NAME = Path.Combine(Environment.CurrentDirectory, "UserData", "HttpStatusExtention", "pp.json");
19 |
20 | public async Task InitializeAsync(CancellationToken token)
21 | {
22 | this.LoadPPFile();
23 | while (this._ppDownloader?.Init != true) {
24 | await Task.Delay(1);
25 | }
26 | lock (this._songData) {
27 | this._songData = this._ppDownloader.RowPPs;
28 | this.DataInit = true;
29 | this.WritePPFile();
30 | }
31 | }
32 |
33 | public float GetPP(SongID songID)
34 | {
35 | if (!this.DataInit) {
36 | return 0f;
37 | }
38 |
39 | switch (songID.difficulty) {
40 | case BeatmapDifficulty.Easy:
41 | return this._songData[songID.id]._Easy_SoloStandard;
42 | case BeatmapDifficulty.Normal:
43 | return this._songData[songID.id]._Normal_SoloStandard;
44 | case BeatmapDifficulty.Hard:
45 | return this._songData[songID.id]._Hard_SoloStandard;
46 | case BeatmapDifficulty.Expert:
47 | return this._songData[songID.id]._Expert_SoloStandard;
48 | case BeatmapDifficulty.ExpertPlus:
49 | return this._songData[songID.id]._ExpertPlus_SoloStandard;
50 | default:
51 | return 0;
52 | }
53 | }
54 |
55 | public bool IsRanked(SongID songID)
56 | {
57 | return this._songData.ContainsKey(songID.id) && this.GetPP(songID) > 0;
58 | }
59 |
60 | private void LoadPPFile()
61 | {
62 | if (File.Exists(SS_PP_FILE_NAME)) {
63 | try {
64 | lock (this._songData) {
65 | if (!this.DataInit) {
66 | var json = JsonConvert.DeserializeObject>(File.ReadAllText(SS_PP_FILE_NAME));
67 | this._songData = json;
68 | this.DataInit = true;
69 | }
70 | }
71 | }
72 |
73 | catch (Exception) {
74 | }
75 | }
76 | }
77 |
78 | private void WritePPFile()
79 | {
80 | OSUtils.WriteFile(this._songData, SS_PP_FILE_NAME);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/GameplayModifierUtils.cs:
--------------------------------------------------------------------------------
1 | namespace HttpStatusExtention.PPCounters
2 | {
3 | public static class GameplayModifierUtils
4 | {
5 | public const float DA_ORIGINAL = 0.07f;
6 | public const float GN_ORIGINAL = 0.11f;
7 | public const float FS_ORIGINAL = 0.08f;
8 |
9 | public static GameplayModifiers RemovePositiveModifiers(GameplayModifiers modifiers)
10 | {
11 | return new GameplayModifiers(
12 | modifiers.energyType,
13 | modifiers.noFailOn0Energy,
14 | modifiers.instaFail,
15 | modifiers.failOnSaberClash,
16 | modifiers.enabledObstacleType,
17 | modifiers.noBombs,
18 | modifiers.fastNotes,
19 | modifiers.strictAngles,
20 | false, // DA
21 | (modifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.Faster) || modifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.SuperFast))
22 | ? GameplayModifiers.SongSpeed.Normal : modifiers.songSpeed,
23 | modifiers.noArrows,
24 | false, // GN
25 | modifiers.proMode,
26 | modifiers.zenMode,
27 | modifiers.smallCubes
28 | );
29 | }
30 |
31 | public static GameplayModifiers RemoveSuperFast(GameplayModifiers modifiers)
32 | {
33 | return new GameplayModifiers(
34 | modifiers.energyType,
35 | modifiers.noFailOn0Energy,
36 | modifiers.instaFail,
37 | modifiers.failOnSaberClash,
38 | modifiers.enabledObstacleType,
39 | modifiers.noBombs,
40 | modifiers.fastNotes,
41 | modifiers.strictAngles,
42 | modifiers.disappearingArrows,
43 | modifiers.songSpeed.Equals(GameplayModifiers.SongSpeed.SuperFast)
44 | ? GameplayModifiers.SongSpeed.Normal : modifiers.songSpeed,
45 | modifiers.noArrows,
46 | modifiers.ghostNotes,
47 | modifiers.proMode,
48 | modifiers.zenMode,
49 | modifiers.smallCubes
50 | );
51 | }
52 |
53 | public static GameplayModifierMask ToMask(this GameplayModifiers gameplayModifiers)
54 | {
55 | return ((gameplayModifiers.energyType == GameplayModifiers.EnergyType.Battery) ? GameplayModifierMask.BatteryEnergy : GameplayModifierMask.None) | (gameplayModifiers.noFailOn0Energy ? GameplayModifierMask.NoFail : GameplayModifierMask.None) | (gameplayModifiers.instaFail ? GameplayModifierMask.InstaFail : GameplayModifierMask.None) | ((gameplayModifiers.enabledObstacleType == GameplayModifiers.EnabledObstacleType.NoObstacles) ? GameplayModifierMask.NoObstacles : GameplayModifierMask.None) | (gameplayModifiers.noBombs ? GameplayModifierMask.NoBombs : GameplayModifierMask.None) | (gameplayModifiers.fastNotes ? GameplayModifierMask.FastNotes : GameplayModifierMask.None) | (gameplayModifiers.strictAngles ? GameplayModifierMask.StrictAngles : GameplayModifierMask.None) | (gameplayModifiers.disappearingArrows ? GameplayModifierMask.DisappearingArrows : GameplayModifierMask.None) | ((gameplayModifiers.songSpeed == GameplayModifiers.SongSpeed.Faster) ? GameplayModifierMask.FasterSong : GameplayModifierMask.None) | ((gameplayModifiers.songSpeed == GameplayModifiers.SongSpeed.Slower) ? GameplayModifierMask.SlowerSong : GameplayModifierMask.None) | ((gameplayModifiers.songSpeed == GameplayModifiers.SongSpeed.SuperFast) ? GameplayModifierMask.SuperFastSong : GameplayModifierMask.None) | (gameplayModifiers.noArrows ? GameplayModifierMask.NoArrows : GameplayModifierMask.None) | (gameplayModifiers.ghostNotes ? GameplayModifierMask.GhostNotes : GameplayModifierMask.None) | (gameplayModifiers.proMode ? GameplayModifierMask.ProMode : GameplayModifierMask.None) | (gameplayModifiers.zenMode ? GameplayModifierMask.ZenMode : GameplayModifierMask.None) | (gameplayModifiers.smallCubes ? GameplayModifierMask.SmallCubes : GameplayModifierMask.None);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/OSUtils.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System.IO;
3 |
4 | namespace HttpStatusExtention.PPCounters
5 | {
6 | public static class OSUtils
7 | {
8 | public static void WriteFile(T data, string fileName)
9 | {
10 | lock (data) {
11 | if (!File.Exists(fileName)) {
12 | new FileInfo(fileName).Directory.Create();
13 | }
14 | File.WriteAllText(fileName, JsonConvert.SerializeObject(data));
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/PPDownloader.cs:
--------------------------------------------------------------------------------
1 | using SiraUtil.Zenject;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace HttpStatusExtention.PPCounters
9 | {
10 | public class PPDownloader : IAsyncInitializable
11 | {
12 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
13 | #region // プロパティ
14 | public ReadOnlyDictionary RowPPs { get; private set; }
15 | public List AccSaberData { get; private set; }
16 | public Leaderboards Curves { get; private set; }
17 | public bool Init { get; private set; }
18 | #endregion
19 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
20 | #region // メンバ変数
21 | private const string URI_PREFIX = "https://cdn.pulselane.dev/";
22 | private const string ACCSABER_URL = "https://api.accsaber.com/";
23 | private const string PP_FILE_NAME = "raw_pp.json";
24 | private const string CURVE_FILE_NAME = "curves.json";
25 | private const string ACCSABER_RANKED_MAPS = "ranked-maps";
26 | #endregion
27 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
28 | #region // 構築・破棄
29 | public async Task InitializeAsync(CancellationToken token)
30 | {
31 | try {
32 | this.Init = false;
33 | var tasks = new List
34 | {
35 | this.StartDownloadingCurves(token),
36 | this.StartDownloadingSS(token),
37 | this.StartDownloadingAccSaber(token)
38 | };
39 | await Task.WhenAll(tasks.ToArray());
40 | this.Init = true;
41 | }
42 | catch (Exception) {
43 | }
44 | }
45 |
46 | public Task StartDownloadingCurves(CancellationToken token)
47 | {
48 | return this.GetCurves(token);
49 | }
50 |
51 | public Task StartDownloadingSS(CancellationToken token)
52 | {
53 | return this.GetRawSSPP(token);
54 | }
55 |
56 | public Task StartDownloadingAccSaber(CancellationToken token)
57 | {
58 | return this.GetAccSaberRankedMaps(token);
59 | }
60 | #endregion
61 | //゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*゚+。*゚+。。+゚*。+゚ ゚+。*゚+。。+゚*。+゚ ゚+。*
62 | #region // プライベートメソッド
63 | private async Task GetRawSSPP(CancellationToken token)
64 | {
65 | var uri = URI_PREFIX + PP_FILE_NAME;
66 | var result = await this.MakeWebRequest>(uri, token);
67 | this.RowPPs = new ReadOnlyDictionary(result);
68 | }
69 |
70 | private async Task GetAccSaberRankedMaps(CancellationToken token)
71 | {
72 | var uri = ACCSABER_URL + ACCSABER_RANKED_MAPS;
73 | var result = await this.MakeWebRequest>(uri, token);
74 | this.AccSaberData = result;
75 | }
76 |
77 | private async Task GetCurves(CancellationToken token)
78 | {
79 | var uri = URI_PREFIX + CURVE_FILE_NAME;
80 | var result = await this.MakeWebRequest(uri, token);
81 | this.Curves = result;
82 | }
83 |
84 | private async Task MakeWebRequest(string uri, CancellationToken token)
85 | {
86 | var result = await WebClient.GetAsync(uri, token);
87 | if (result == null || !result.IsSuccessStatusCode) {
88 | return default;
89 | }
90 | var jsonToken = result.ConvertToJToken();
91 | return jsonToken.ToObject();
92 | }
93 | #endregion
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/SongDataUtils.cs:
--------------------------------------------------------------------------------
1 | namespace HttpStatusExtention.PPCounters
2 | {
3 | public static class SongDataUtils
4 | {
5 | public static string GetHash(string levelId)
6 | {
7 | if (levelId.Contains("custom_level_")) {
8 | var splits = levelId.Split('_');
9 | return splits[2].ToUpper();
10 | }
11 | return levelId;
12 | }
13 |
14 | public static BeatmapDifficulty GetDifficulty(string difficulty)
15 | {
16 | switch (difficulty.ToLower()) {
17 | case "easy":
18 | return BeatmapDifficulty.Easy;
19 | case "normal":
20 | return BeatmapDifficulty.Normal;
21 | case "hard":
22 | return BeatmapDifficulty.Hard;
23 | case "expert":
24 | return BeatmapDifficulty.Expert;
25 | case "expertplus":
26 | return BeatmapDifficulty.ExpertPlus;
27 | default:
28 | throw new System.Exception("Unrecognized difficulty");
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/HttpStatusExtention/PPCounters/Structs/Struct.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 |
5 | namespace HttpStatusExtention.PPCounters
6 | {
7 | public class SongID : IEquatable
8 | {
9 | public string id;
10 | public BeatmapDifficulty difficulty;
11 |
12 | public SongID(string id, BeatmapDifficulty difficulty)
13 | {
14 | this.id = id;
15 | this.difficulty = difficulty;
16 | }
17 |
18 | public override bool Equals(object obj)
19 | {
20 | return this.Equals(obj as SongID);
21 | }
22 |
23 | public bool Equals(SongID other)
24 | {
25 | return !(other is null) &&
26 | this.id == other.id &&
27 | this.difficulty == other.difficulty;
28 | }
29 |
30 | public override int GetHashCode()
31 | {
32 | return HashCode.Combine(this.id, this.difficulty);
33 | }
34 |
35 | public static bool operator ==(SongID left, SongID right)
36 | {
37 | return EqualityComparer.Default.Equals(left, right);
38 | }
39 |
40 | public static bool operator !=(SongID left, SongID right)
41 | {
42 | return !(left == right);
43 | }
44 | }
45 |
46 | public class RawPPData
47 | {
48 | public float _Easy_SoloStandard { get; set; }
49 | public float _Normal_SoloStandard { get; set; }
50 | public float _Hard_SoloStandard { get; set; }
51 | public float _Expert_SoloStandard { get; set; }
52 | public float _ExpertPlus_SoloStandard { get; set; }
53 | }
54 |
55 | public class BeatLeaderCacheFileData
56 | {
57 | public List Entries;
58 | public long LastCheckTime;
59 | }
60 |
61 | public struct BeatLeaderLeaderboardCacheEntry
62 | {
63 | public string LeaderboardId;
64 | public BeatLeaderSongInfo SongInfo;
65 | public BeatLeaderDiffInfo DifficultyInfo;
66 | }
67 |
68 | public struct BeatLeaderSongInfo
69 | {
70 | public string id;
71 | public string hash;
72 | }
73 |
74 | public struct ModifiersMap
75 | {
76 | public int modifierId;
77 |
78 | public float da;
79 |
80 | public float fs;
81 |
82 | public float ss;
83 |
84 | public float sf;
85 |
86 | public float gn;
87 |
88 | public float na;
89 |
90 | public float nb;
91 |
92 | public float nf;
93 |
94 | public float no;
95 |
96 | public float pm;
97 |
98 | public float sc;
99 |
100 | public float GetModifierValueByModifierServerName(string name)
101 | {
102 | return (float)(typeof(ModifiersMap).GetField(name.ToLower(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(this) ?? -1f);
103 | }
104 |
105 | public void LoadFromGameModifiersParams(IEnumerable modifiersParams)
106 | {
107 | foreach (var modifiersParam in modifiersParams) {
108 | var text = ParseModifierLocalizationKeyToServerName(modifiersParam.modifierNameLocalizationKey);
109 | typeof(ModifiersMap).GetField(text.ToLower(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValueDirect(__makeref(this), modifiersParam.multiplier);
110 | }
111 | }
112 |
113 | public static string ParseModifierLocalizationKeyToServerName(string modifierLocalizationKey)
114 | {
115 | if (string.IsNullOrEmpty(modifierLocalizationKey)) {
116 | return modifierLocalizationKey;
117 | }
118 |
119 | var num = modifierLocalizationKey.IndexOf('_') + 1;
120 | var c = modifierLocalizationKey[num];
121 | var index = modifierLocalizationKey.IndexOf('_', num) + 1;
122 | var c2 = modifierLocalizationKey[index];
123 | return $"{char.ToUpper(c)}{char.ToUpper(c2)}";
124 | }
125 | }
126 |
127 | public struct BeatLeaderDiffInfo
128 | {
129 | public int id;
130 | public int value;
131 | public int mode;
132 | public int status;
133 | public string modeName;
134 | public string difficultyName;
135 | public float stars;
136 | public float accRating;
137 | public float passRating;
138 | public float techRating;
139 | public int type;
140 | public ModifiersMap modifierValues;
141 | }
142 |
143 | public struct BeatLeaderRating
144 | {
145 | public float accRating;
146 | public float passRating;
147 | public float techRating;
148 |
149 | public BeatLeaderRating(float accRating, float passRating, float techRating)
150 | {
151 | this.accRating = accRating;
152 | this.passRating = passRating;
153 | this.techRating = techRating;
154 | }
155 | }
156 |
157 | public class AccSaberRankedMap
158 | {
159 | public string difficulty;
160 | public string songHash;
161 | public float complexity;
162 | }
163 |
164 | public class Leaderboards
165 | {
166 | public ScoreSaber ScoreSaber { get; set; }
167 | public BeatLeader BeatLeader { get; set; }
168 | public AccSaber AccSaber { get; set; }
169 | }
170 |
171 | public class ScoreSaber
172 | {
173 | public List modifierCurve { get; set; }
174 | public List standardCurve { get; set; }
175 | public List songsAllowingPositiveModifiers { get; set; }
176 | public ScoreSaberModifiers modifiers { get; set; }
177 | }
178 |
179 | public class ScoreSaberModifiers
180 | {
181 | public float da;
182 | public float gn;
183 | public float fs;
184 | }
185 |
186 | public class BeatLeader
187 | {
188 | public List accCurve { get; set; }
189 | public float accMultiplier { get; set; }
190 | public float passExponential { get; set; }
191 | public float passMultiplier { get; set; }
192 | public float passShift { get; set; }
193 |
194 | public float techExponentialMultiplier { get; set; }
195 | public float techMultiplier { get; set; }
196 |
197 | public float inflateExponential { get; set; }
198 | public float inflateMultiplier { get; set; }
199 | }
200 |
201 | public class AccSaber
202 | {
203 | public List curve { get; set; }
204 | public float scale;
205 | public float shift;
206 | }
207 |
208 | public class Point
209 | {
210 | public float x { get; set; }
211 | public float y { get; set; }
212 | }
213 | }
--------------------------------------------------------------------------------
/HttpStatusExtention/Plugin.cs:
--------------------------------------------------------------------------------
1 | using HarmonyLib;
2 | using HttpStatusExtention.Installers;
3 | using IPA;
4 | using SiraUtil.Zenject;
5 | using System.Reflection;
6 | using IPALogger = IPA.Logging.Logger;
7 |
8 | namespace HttpStatusExtention
9 | {
10 | [Plugin(RuntimeOptions.SingleStartInit)]
11 | public class Plugin
12 | {
13 | internal static Plugin Instance { get; private set; }
14 | internal static IPALogger Log { get; private set; }
15 | private static Harmony s_harmony;
16 | public const string HARMONY_ID = "HttpStatusExtention.com.github.denpadokei";
17 |
18 | [Init]
19 | ///
20 | /// Called when the plugin is first loaded by IPA (either when the game starts or when the plugin is enabled if it starts disabled).
21 | /// [Init] methods that use a Constructor or called before regular methods like InitWithConfig.
22 | /// Only use [Init] with one Constructor.
23 | ///
24 | public void Init(IPALogger logger, Zenjector zenjector)
25 | {
26 | Instance = this;
27 | Log = logger;
28 | Log.Info("HttpStatusExtention initialized.");
29 | s_harmony = new Harmony(HARMONY_ID);
30 | zenjector.Install(Location.Player);
31 | zenjector.Install(Location.Menu | Location.Player);
32 | zenjector.Install(Location.App);
33 | }
34 |
35 | [OnStart]
36 | public void OnApplicationStart()
37 | {
38 | Log.Debug("OnApplicationStart");
39 | }
40 |
41 | [OnExit]
42 | public void OnApplicationQuit()
43 | {
44 | Log.Debug("OnApplicationQuit");
45 | }
46 |
47 | [OnEnable]
48 | public void OnEnable()
49 | {
50 | try {
51 | s_harmony?.PatchAll(Assembly.GetExecutingAssembly());
52 | }
53 | catch (System.Exception e) {
54 | Log.Error(e);
55 | }
56 | }
57 |
58 | [OnDisable]
59 | public void OnDisable()
60 | {
61 | try {
62 | s_harmony?.UnpatchSelf();
63 | }
64 | catch (System.Exception e) {
65 | Log.Error(e);
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/HttpStatusExtention/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("HttpStatusExtention")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("HttpStatusExtention")]
12 | [assembly: AssemblyCopyright("Copyright © 2021")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("bb330e07-49d6-4b83-ac8a-93f73d1f76da")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("10.0.0")]
35 | [assembly: AssemblyFileVersion("10.0.0")]
36 |
--------------------------------------------------------------------------------
/HttpStatusExtention/SRMQueueStatus.cs:
--------------------------------------------------------------------------------
1 | using HttpSiraStatus.Enums;
2 | using HttpSiraStatus.Interfaces;
3 | using HttpSiraStatus.Util;
4 | using HttpStatusExtention.HarmonyPathces;
5 | using IPA.Loader;
6 | using System;
7 | using System.Reflection;
8 | using Zenject;
9 |
10 | namespace HttpStatusExtention
11 | {
12 | public class SRMQueueStatus : IInitializable, IDisposable
13 | {
14 | private IStatusManager _statusManager;
15 | private bool _disposedValue;
16 |
17 | [Inject]
18 | public void Constractor(IStatusManager statusManager)
19 | {
20 | this._statusManager = statusManager;
21 | }
22 |
23 | public void Initialize()
24 | {
25 | if (PluginManager.GetPlugin("Song Request Manager V2") == null) {
26 | return;
27 | }
28 | try {
29 | SRMConigPatch.OnQueueStatusChanged += this.SRMConigPatch_OnQueueStatusChanged;
30 | var configType = Type.GetType("SongRequestManagerV2.Configuration.RequestBotConfig, SongRequestManagerV2");
31 | var instanceProperty = configType?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);
32 | var instance = instanceProperty?.GetValue(configType);
33 | var queueStatusProperty = configType?.GetProperty("RequestQueueOpen", BindingFlags.Instance | BindingFlags.Public);
34 | var queueStatus = (bool)queueStatusProperty?.GetValue(instance);
35 | this.SRMConigPatch_OnQueueStatusChanged(queueStatus);
36 | }
37 | catch (Exception e) {
38 | Plugin.Log.Error(e);
39 | }
40 | }
41 |
42 | private void SRMConigPatch_OnQueueStatusChanged(bool obj)
43 | {
44 | this._statusManager.OtherJSON["srm_queue_status"] = new JSONBool(obj);
45 | this._statusManager.EmitStatusUpdate(ChangedProperty.Other, BeatSaberEvent.Other);
46 | }
47 | protected virtual void Dispose(bool disposing)
48 | {
49 | if (!this._disposedValue) {
50 | if (disposing) {
51 | // TODO: マネージド状態を破棄します (マネージド オブジェクト)
52 | SRMConigPatch.OnQueueStatusChanged -= this.SRMConigPatch_OnQueueStatusChanged;
53 | }
54 | this._disposedValue = true;
55 | }
56 | }
57 | public void Dispose()
58 | {
59 | // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します
60 | this.Dispose(disposing: true);
61 | GC.SuppressFinalize(this);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/HttpStatusExtention/SongDetailsCaches/SongDetailsCacheUtility.cs:
--------------------------------------------------------------------------------
1 | using HttpStatusExtention.Converters;
2 | using HttpStatusExtention.Extentions;
3 | using HttpStatusExtention.Interfaces;
4 | using HttpStatusExtention.Models;
5 | using HttpStatusExtention.PPCounters;
6 | using SongDetailsCache;
7 | using System.Collections.Concurrent;
8 | using System.Linq;
9 | using Zenject;
10 |
11 | namespace HttpStatusExtention.SongDetailsCaches
12 | {
13 | ///
14 | /// 書き直すのめんどくさすぎる
15 | ///
16 | public class SongDetailsCacheUtility : ISongDataUtil, IInitializable
17 | {
18 | private SongDetails _songDetails = null;
19 | private PPDownloader _downloader = null;
20 | private volatile bool _init = false;
21 |
22 | public BeatSongData GetBeatStarSong(BeatmapLevel beatmapLevel)
23 | {
24 | if (!this._init) {
25 | return null;
26 | }
27 | var hash = beatmapLevel.GetHashOrLevelID();
28 | if (hash.Length != 40) {
29 | return null;
30 | }
31 | _ = this._songDetails.songs.FindByHash(hash, out var song);
32 | var result = new BeatSongData
33 | {
34 | Characteristics = new ConcurrentDictionary>()
35 | };
36 | foreach (var chara in song.difficulties.GroupBy(x => x.characteristic)) {
37 | var characteristics = SongDetailsConveter.ConvertToBeatDataCharacteristics(chara.Key);
38 | var dic = new ConcurrentDictionary();
39 | foreach (var diff in chara) {
40 | float calcPP()
41 | {
42 | return diff.stars <= 0.05f || (diff.song.rankedStates & SongDetailsCache.Structs.RankedStates.ScoresaberRanked) == 0
43 | ? 0f
44 | : diff.stars * 43.146f;
45 | }
46 | var pp = calcPP();
47 | var diffData = new BeatSongDataDifficultyStats
48 | {
49 | Difficulty = SongDetailsConveter.ConvertToBeatMapDifficulty(diff.difficulty),
50 | Star = diff.stars,
51 | NJS = diff.njs,
52 | Bombs = (int)diff.bombs,
53 | Notes = (int)diff.notes,
54 | Obstacles = (int)diff.obstacles,
55 | PP = pp,
56 | Mods = SongDetailsConveter.ConvertToRecomendMod(diff.mods),
57 | Ranked = (diff.song.rankedStates & SongDetailsCache.Structs.RankedStates.ScoresaberRanked) != 0,
58 | Song = result,
59 | Characteristics = characteristics
60 | };
61 | _ = dic.TryAdd(diffData.Difficulty, diffData);
62 | }
63 | _ = result.Characteristics.TryAdd(characteristics, dic);
64 | }
65 | result.Key = song.key;
66 | result.BPM = song.bpm;
67 | result.Rating = song.rating;
68 | result.DownloadCount = (int)song.downloadCount;
69 | result.Upvotes = (int)song.upvotes;
70 | result.Downvotes = (int)song.downvotes;
71 | result.SongDuration = (int)song.songDuration.TotalSeconds;
72 | result.DiffOffset = (int)song.diffOffset;
73 | result.DiffCount = song.diffCount;
74 | result.RankedStatus = SongDetailsConveter.ConvertToTRankStatus(song.rankedStates);
75 | result.UploadTime = song.uploadTime;
76 | result.Hash = song.hash;
77 | result.SongName = song.songName;
78 | result.SongAuthorName = song.songAuthorName;
79 | result.LevelAuthorName = song.levelAuthorName;
80 | result.CoverURL = song.coverURL;
81 | result.UploaderName = song.uploaderName;
82 | return result;
83 | }
84 |
85 | public BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatSongData song, BeatmapDifficulty difficulty)
86 | {
87 | return this.GetBeatStarSongDiffculityStats(song, difficulty, BeatDataCharacteristics.Standard);
88 | }
89 |
90 | public BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatmapLevel beatmapLevel, BeatmapDifficulty difficulty)
91 | {
92 | return this.GetBeatStarSongDiffculityStats(beatmapLevel, difficulty, BeatDataCharacteristics.Standard);
93 | }
94 |
95 | public BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatSongData song, BeatmapDifficulty difficulty, BeatDataCharacteristics beatDataCharacteristics)
96 | {
97 | return !song.Characteristics.TryGetValue(beatDataCharacteristics, out var dic)
98 | ? null
99 | : !dic.TryGetValue(BeatMapCoreConverter.ConvertToBeatMapDifficulity(difficulty), out var result) ? null : result;
100 | }
101 |
102 | public BeatSongDataDifficultyStats GetBeatStarSongDiffculityStats(BeatmapLevel beatmapLevel, BeatmapDifficulty difficulty, BeatDataCharacteristics beatDataCharacteristics)
103 | {
104 | var song = this.GetBeatStarSong(beatmapLevel);
105 | return song == null ? null : this.GetBeatStarSongDiffculityStats(song, difficulty, beatDataCharacteristics);
106 | }
107 |
108 | public double GetPP(BeatmapLevel beatmapLevel, BeatmapDifficulty difficulty, BeatDataCharacteristics beatDataCharacteristics)
109 | {
110 | if (beatDataCharacteristics == BeatDataCharacteristics.Standard && this._downloader.Init && this._downloader.RowPPs.TryGetValue(beatmapLevel.GetHashOrLevelID().ToUpper(), out var pp)) {
111 | // PP counterと同じ処理
112 | switch (difficulty) {
113 | case BeatmapDifficulty.Easy:
114 | return pp._Easy_SoloStandard;
115 | case BeatmapDifficulty.Normal:
116 | return pp._Normal_SoloStandard;
117 | case BeatmapDifficulty.Hard:
118 | return pp._Hard_SoloStandard;
119 | case BeatmapDifficulty.Expert:
120 | return pp._Expert_SoloStandard;
121 | case BeatmapDifficulty.ExpertPlus:
122 | return pp._ExpertPlus_SoloStandard;
123 | default:
124 | break;
125 | }
126 | }
127 | var song = this.GetBeatStarSongDiffculityStats(beatmapLevel, difficulty, beatDataCharacteristics);
128 | return song != null ? (double)song.PP : 0;
129 | }
130 |
131 | public void Initialize()
132 | {
133 | _ = SongDetails.Init().ContinueWith(async x =>
134 | {
135 | this._songDetails = await x;
136 | this._init = true;
137 | });
138 | }
139 |
140 | public bool IsRank(BeatmapLevel beatmapLevel, BeatmapDifficulty beatmapDifficulty, BeatDataCharacteristics beatDataCharacteristics)
141 | {
142 | var song = this.GetBeatStarSong(beatmapLevel);
143 | return this.GetBeatStarSongDiffculityStats(song, beatmapDifficulty, beatDataCharacteristics).Ranked;
144 | }
145 |
146 | public bool IsRank(string levelID, BeatmapDifficulty beatmapDifficulty, BeatDataCharacteristics beatDataCharacteristics)
147 | {
148 | var prevMap = SongCore.Loader.GetLevelById(levelID);
149 | return prevMap != null && prevMap is BeatmapLevel custom && this.IsRank(custom, beatmapDifficulty, beatDataCharacteristics);
150 | }
151 | [Inject]
152 | private void Constractor(PPDownloader downloader)
153 | {
154 | this._downloader = downloader;
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/HttpStatusExtention/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/bsmg/BSIPA-MetadataFileSchema/master/Schema.json",
3 | "id": "HttpStatusExtention",
4 | "name": "HttpStatusExtention",
5 | "author": "denpadokei",
6 | "version": "10.0.0",
7 | "description": "",
8 | "gameVersion": "1.39.0",
9 | "dependsOn": {
10 | "BSIPA": "^4.3.5",
11 | "SongDetailsCache": "^1.2.2",
12 | "SiraUtil": "^3.1.12",
13 | "SongCore": "^3.14.15",
14 | "HttpSiraStatus": "^13.0.0"
15 | }
16 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 denpadokei
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HttpStatusExtention
2 | HttpStatusでは表示できない情報を追加で送信します。
3 | 今のところ下記のオプションが追加されます。
4 | ## 依存MOD
5 | SiraUtil
6 | [HttpSiraStatus](https://github.com/denpadokei/beatsaber-http-status)
7 | SongDetailsCache
8 | SongCore
9 |
10 | # 注意
11 | BeatSaberHTTPStatusはデンパ時計が作ったものを使用してください。
12 |
13 | ### Status object
14 |
15 | ```js
16 | StatusObject = {
17 | "performance": null | {
18 | "current_pp": Double, // スコアに応じた取得可能PP
19 | },
20 | "beatmap": null | {
21 | "customLabel": 譜面の難易度につけられたカスタムラベルです。
22 | "pp": ランク譜面の時PPが入ります。
23 | "star": スコアセイバーの星
24 | "downloadCount": ダウンロード回数
25 | "upVotes": アップボーテ数
26 | "downVotes": ダウンボーテ数
27 | "rating": アップボーテとダウンボーテの比率みたいなもの
28 | }
29 | },
30 | OtherObject = {
31 | "srm_queue_status": Boolean // キューが開いてたらtrue.閉じてたらfalse.
32 | }
33 | ```
34 |
--------------------------------------------------------------------------------