the browser-facing portion of osu!
1<?php
2
3$profileScoresNotice = presence(env('USER_PROFILE_SCORES_NOTICE'));
4if ($profileScoresNotice !== null) {
5 $profileScoresNotice = markdown_plain($profileScoresNotice);
6}
7
8$clientTokenKeys = [];
9foreach (explode(',', env('CLIENT_TOKEN_KEYS') ?? '') as $entry) {
10 if ($entry !== '') {
11 [$platform, $encodedKey] = explode('=', $entry, 2);
12 $clientTokenKeys[$platform] = hex2bin($encodedKey);
13 }
14}
15
16// osu config~
17return [
18 'achievement' => [
19 'icon_prefix' => env('USER_ACHIEVEMENT_ICON_PREFIX', 'https://assets.ppy.sh/user-achievements/'),
20 ],
21
22 'api' => [
23 // changing the throttle rate doesn't reset any existing timers,
24 // changing the prefix key is the only way to invalidate them.
25 'throttle' => [
26 'global' => env('API_THROTTLE_GLOBAL', '1200,1,api'),
27 'scores_download' => env('API_THROTTLE_SCORES_DOWNLOAD', '10,1,api-scores-download'),
28 ],
29 ],
30
31 'avatar' => [
32 'cache_purge_prefix' => env('AVATAR_CACHE_PURGE_PREFIX'),
33 'cache_purge_method' => env('AVATAR_CACHE_PURGE_METHOD'),
34 'cache_purge_authorization_key' => env('AVATAR_CACHE_PURGE_AUTHORIZATION_KEY'),
35 'default' => env('DEFAULT_AVATAR', env('APP_URL', 'http://localhost').'/images/layout/avatar-guest@2x.png'),
36 'storage' => env('AVATAR_STORAGE', 'local-avatar'),
37 ],
38
39 'bbcode' => [
40 // this should be random or a config variable.
41 // ...who am I kidding, this shouldn't even exist at all.
42 'uid' => '1337',
43 ],
44 'beatmaps' => [
45 'max' => 50,
46 'max_scores' => 100,
47 'owners_max' => get_int(env('BEATMAPS_OWNERS_MAX')) ?? 10,
48
49 'difficulty_cache' => [
50 'server_url' => presence(env('BEATMAPS_DIFFICULTY_CACHE_SERVER_URL')) ?? 'http://localhost:5001',
51 ],
52 ],
53 'beatmap_processor' => [
54 'mirrors_to_use' => array_map('intval', explode(' ', env('BM_PROCESSOR_MIRRORS', '1'))),
55 'thumbnailer' => env('BM_PROCESSOR_THUMBNAILER', 'http://localhost:4001'),
56 'sentry' => env('BM_PROCESSOR_SENTRY'),
57 ],
58 'beatmapset' => [
59 'discussion_kudosu_per_user' => get_int(env('BEATMAPSET_DISCUSSION_KUDOSU_PER_USER')) ?? 10,
60 'discussion_review_max_blocks' => get_int(env('BEATMAPSET_DISCUSSION_REVIEW_MAXIMUM_BLOCKS', 10)),
61 'discussion_review_min_issues' => get_int(env('BEATMAPSET_DISCUSSION_REVIEW_MINIMUM_ISSUES', 1)),
62 'download_limit' => intval(env('BEATMAPSET_USER_DOWNLOAD_LIMIT_HOURLY', 10)),
63 'download_limit_supporter' => intval(env('BEATMAPSET_USER_DOWNLOAD_LIMIT_HOURLY_SUPPORTER', 20)),
64 'es_cache_duration' => 60 * (get_float(env('BEATMAPSET_ES_CACHE_DURATION')) ?? 1.0), // in minutes, converted to seconds
65 'favourite_limit' => intval(env('BEATMAPSET_USER_FAVOURITE_LIMIT', 100)),
66 'favourite_limit_supporter' => intval(env('BEATMAPSET_USER_FAVOURITE_LIMIT_SUPPORTER', 1000)),
67 'guest_advanced_search' => get_bool(env('BEATMAPSET_GUEST_ADVANCED_SEARCH')) ?? false,
68 'maximum_disqualified_rank_penalty_days' => get_int(env('BEATMAPSET_MAXIMUM_DISQUALIFIED_RANK_PENALTY_DAYS')) ?? 7,
69 'minimum_days_for_rank' => get_int(env('BEATMAPSET_MINIMUM_DAYS_FOR_RANK')) ?? 7,
70 'rank_per_day' => get_int(env('BEATMAPSET_RANK_PER_DAY')) ?? 8,
71 'rank_per_run' => get_int(env('BEATMAPSET_RANK_PER_RUN')) ?? 2,
72 'required_hype' => get_int(env('BEATMAPSET_REQUIRED_HYPE')) ?? 5,
73 'required_nominations' => get_int(env('BEATMAPSET_REQUIRED_NOMINATIONS')) ?? 2,
74 'upload_allowed' => get_int(env('BEATMAPSET_UPLOAD_ALLOWED')) ?? 4,
75 'upload_allowed_supporter' => get_int(env('BEATMAPSET_UPLOAD_ALLOWED_SUPPORTER')) ?? 8,
76 'upload_bonus_per_ranked' => get_int(env('BEATMAPSET_UPLOAD_BONUS_PER_RANKED')) ?? 1,
77 'upload_bonus_per_ranked_max' => get_int(env('BEATMAPSET_UPLOAD_BONUS_PER_RANKED_MAX')) ?? 2,
78 'upload_bonus_per_ranked_max_supporter' => get_int(env('BEATMAPSET_UPLOAD_BONUS_PER_RANKED_MAX_SUPPORTER')) ?? 12,
79 'upload_bonus_per_ranked_supporter' => get_int(env('BEATMAPSET_UPLOAD_BONUS_PER_RANKED_SUPPORTER')) ?? 1,
80 'user_daily_nominations' => get_int(env('BEATMAPSET_USER_DAILY_NOMINATIONS', 10)) ?? 10,
81 'user_weekly_hype' => get_int(env('BEATMAPSET_USER_WEEKLY_HYPE')) ?? 3,
82 ],
83 'camo' => [
84 'key' => presence(env('CAMO_KEY')),
85 'prefix' => env('CAMO_PREFIX', 'https://i.ppy.sh/'),
86 ],
87 'captcha' => [
88 'threshold' => get_int(env('CAPTCHA_THRESHOLD')) ?? 2,
89 ],
90 'chat' => [
91 'channel_limit' => get_int(env('CHAT_CHANNEL_LIMIT')) ?? 10000,
92 'message_length_limit' => get_int(env('CHAT_MESSAGE_LENGTH_LIMIT')) ?? 450,
93 'public_backlog_limit' => get_int(env('CHAT_PUBLIC_BACKLOG_LIMIT_HOURS')) ?? 24,
94 'rate_limits' => [
95 'public' => [
96 'limit' => get_int(env('CHAT_PUBLIC_LIMIT')) ?? 1,
97 'window' => get_int(env('CHAT_PUBLIC_WINDOW')) ?? 1,
98 ],
99 'private' => [
100 'limit' => get_int(env('CHAT_PRIVATE_LIMIT')) ?? 1,
101 'window' => get_int(env('CHAT_PRIVATE_WINDOW')) ?? 1,
102 ],
103 ],
104 ],
105 'client' => [
106 'check_version' => get_bool(env('CLIENT_CHECK_VERSION')) ?? true,
107 'default_build_id' => get_int(env('DEFAULT_BUILD_ID')) ?? 0,
108 'token_keys' => $clientTokenKeys,
109 'token_lifetime' => (get_float(env('CLIENT_TOKEN_LIFETIME_HOUR')) ?? 0.25) * 3600,
110 'token_queue' => env('CLIENT_TOKEN_QUEUE') ?? 'token-queue',
111 'user_agent' => env('CLIENT_USER_AGENT', 'osu!'),
112 ],
113 'elasticsearch' => [
114 'prefix' => env('ES_INDEX_PREFIX'),
115 'search_timeout' => env('ES_SEARCH_TIMEOUT', '5s'),
116 ],
117 'emails' => [
118 'account' => 'accounts@ppy.sh',
119 ],
120 'forum' => [
121 'admin_forum_id' => get_int(env('ADMIN_FORUM_ID')) ?? 28,
122 'double_post_allowed_forum_ids' => array_map('intval', explode(' ', env('DOUBLE_POST_ALLOWED_FORUM_IDS', '52 68 84 114'))),
123 'feature_forum_id' => get_int(env('FEATURE_FORUM_ID')) ?? 4,
124 'feature_topic_small_star_min' => get_int(env('FEATURE_TOPIC_SMALL_STAR_MIN')) ?? 1000,
125 'help_forum_id' => get_int(env('HELP_FORUM_ID')) ?? 5,
126 'initial_help_forum_ids' => array_map('intval', explode(' ', env('INITIAL_HELP_FORUM_IDS', '5 47 85'))),
127 'issue_forum_ids' => array_map('intval', explode(' ', env('ISSUE_FORUM_IDS', '4 5 29 30 101'))),
128 'max_post_length' => get_int(env('FORUM_POST_MAX_LENGTH')) ?? 60000,
129 'minimum_plays' => get_int(env('FORUM_POST_MINIMUM_PLAYS')) ?? 200,
130 'beatmap_description_forum_id' => get_int(env('BEATMAP_DESCRIPTION_FORUM_ID')) ?? 6,
131 'necropost_months' => 6,
132 'old_months' => 1,
133 'poll_edit_hours' => get_int(env('FORUM_POLL_EDIT_HOURS')) ?? 1,
134
135 'double_post_time' => [
136 'author' => 24,
137 'normal' => 72,
138 ],
139 ],
140 'git-sha' => presence(env('GIT_SHA'))
141 ?? (file_exists(__DIR__.'/../version') ? trim(file_get_contents(__DIR__.'/../version')) : null)
142 ?? 'unknown-version',
143 'github' => [
144 'client_id' => presence(env('GITHUB_CLIENT_ID')),
145 'client_secret' => presence(env('GITHUB_CLIENT_SECRET')),
146 ],
147 'is_development_deploy' => get_bool(env('IS_DEVELOPMENT_DEPLOY')) ?? true,
148 'landing' => [
149 'video_url' => env('LANDING_VIDEO_URL', 'https://assets.ppy.sh/media/landing.mp4'),
150 ],
151 'legacy' => [
152 'bancho_bot_user_id' => get_int(env('BANCHO_BOT_USER_ID')) ?? 3,
153 'shared_interop_secret' => env('SHARED_INTEROP_SECRET', ''),
154 ],
155 'multiplayer' => [
156 'max_attempts_limit' => get_int(env('MULTIPLAYER_MAX_ATTEMPTS_LIMIT')) ?? 128,
157 'room_close_grace_period_minutes' => get_int(env('MULTIPLAYER_ROOM_CLOSE_GRACE_PERIOD_MINUTES')) ?? 5,
158 ],
159 'notification' => [
160 'endpoint' => presence(env('NOTIFICATION_ENDPOINT'), '/home/notifications/feed'),
161 'queue_name' => presence(env('NOTIFICATION_QUEUE'), 'notification'),
162
163 'cleanup' => [
164 'keep_days' => get_int(env('NOTIFICATION_CLEANUP_KEEP_DAYS')) ?? 180,
165 'max_delete_per_run' => get_int(env('NOTIFICATION_CLEANUP_MAX_DELETE')) ?? 50000,
166 ],
167 ],
168 'oauth' => [
169 'retain_expired_tokens_days' => abs(get_int(env('OAUTH_RETAIN_EXPIRED_TOKENS_DAYS')) ?? 30),
170 'max_user_clients' => get_int(env('OAUTH_MAX_USER_CLIENTS')) ?? 1,
171 ],
172 'octane' => [
173 'local_cache_expire_second' => get_int(env('OCTANE_LOCAL_CACHE_EXPIRE_SECOND')) ?? 60,
174 'local_cache_reset_requests' => get_int(env('OCTANE_LOCAL_CACHE_RESET_REQUESTS')) ?? 100,
175 ],
176 'pagination' => [
177 'max_count' => get_int(env('PAGINATION_MAX_COUNT')) ?? 10000,
178 ],
179 'search' => [
180 'minimum_length' => get_int(env('SEARCH_MINIMUM_LENGTH', 2)),
181
182 'max' => [
183 'user' => 100,
184 ],
185 ],
186 'score_replays' => [
187 'storage' => env('SCORE_REPLAYS_STORAGE', 'local'),
188 ],
189 'scores' => [
190 'es_cache_duration' => 60 * (get_float(env('SCORES_ES_CACHE_DURATION')) ?? 0.5), // in minutes, converted to seconds
191 'experimental_rank_as_default' => get_bool(env('SCORES_EXPERIMENTAL_RANK_AS_DEFAULT')) ?? false,
192 'experimental_rank_as_extra' => get_bool(env('SCORES_EXPERIMENTAL_RANK_AS_EXTRA')) ?? false,
193 'index_max_id_distance' => get_int(env('SCORE_INDEX_MAX_ID_DISTANCE')) ?? 10_000_000,
194 'processing_queue' => presence(env('SCORES_PROCESSING_QUEUE')) ?? 'osu-queue:score-statistics',
195 'submission_enabled' => get_bool(env('SCORES_SUBMISSION_ENABLED')) ?? true,
196 ],
197
198 'seasonal' => [
199 'contest_id' => get_int(env('SEASONAL_CONTEST_ID')),
200 'ends_at' => env('SEASONAL_ENDS_AT'),
201 ],
202
203 'store' => [
204 'notice' => presence(str_replace('\n', "\n", env('STORE_NOTICE') ?? '')),
205 ],
206 'tags' => [
207 'tags_cache_duration' => 60 * (get_int(env('TAGS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds
208 'beatmap_tags_cache_duration' => 60 * (get_int(env('BEATMAP_TAGS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds
209 ],
210 'twitch_client_id' => presence(env('TWITCH_CLIENT_ID')),
211 'twitch_client_secret' => presence(env('TWITCH_CLIENT_SECRET')),
212 'urls' => [
213 'base' => 'https://osu.ppy.sh',
214 'bounty-form' => env('OS_BOUNTY_URL'),
215 'dev' => 'https://discord.gg/ppy',
216 'experimental_host' => presence(env('OSU_EXPERIMENTAL_HOST')),
217 'installer' => 'https://m1.ppy.sh/r/osu!install.exe',
218 'installer-mirror' => 'https://m2.ppy.sh/r/osu!install.exe',
219 'lazer_dl.android' => presence(env('OSU_URL_LAZER_ANDROID')) ?? 'https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk',
220 'lazer_dl.ios' => presence(env('OSU_URL_LAZER_IOS')) ?? '/home/testflight',
221 'lazer_dl.linux_x64' => presence(env('OSU_URL_LAZER_LINUX_X64')) ?? 'https://github.com/ppy/osu/releases/latest/download/osu.AppImage',
222 'lazer_dl.macos_as' => presence(env('OSU_URL_LAZER_MACOS_AS')) ?? 'https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip',
223 'lazer_dl.windows_x64' => presence(env('OSU_URL_LAZER_WINDOWS_X64')) ?? 'https://github.com/ppy/osu/releases/latest/download/install.exe',
224 'lazer_dl_other' => presence(env('OSU_URL_LAZER_OTHER')) ?? 'https://github.com/ppy/osu/#running-osu',
225 'lazer_info' => presence(env('OSU_URL_LAZER_INFO')),
226 'menu_content' => presence(env('OSU_URL_MENU_CONTENT_JSON')) ?? 'https://assets.ppy.sh/menu-content.json',
227 'osx' => 'https://osx.ppy.sh',
228 'server_status' => 'https://status.ppy.sh',
229 'smilies' => '/forum/images/smilies',
230 'social.twitter' => '/wiki/Twitter',
231 'source_code' => 'https://github.com/ppy',
232 'testflight.public' => env('TESTFLIGHT_LINK'),
233 'testflight.supporter' => env('TESTFLIGHT_LINK_SUPPORTER'),
234 'user.recover' => '/wiki/Help_centre/Account#sign-in',
235 'user.restriction' => presence(env('OSU_URL_USER_RESTRICTION')) ?? '/wiki/Help_centre/Account_restrictions',
236 'user.rules' => '/wiki/Osu!:Rules',
237 'youtube-tutorial-playlist' => 'PLmWVQsxi34bMYwAawZtzuptfMmszUa_tl',
238 ],
239 'user' => [
240 'allow_email_login' => get_bool(env('USER_ALLOW_EMAIL_LOGIN')) ?? true,
241 'allow_registration' => get_bool(env('ALLOW_REGISTRATION')) ?? true,
242 'allowed_rename_groups' => explode(' ', env('USER_ALLOWED_RENAME_GROUPS', 'default')),
243 'bypass_verification' => get_bool(env('USER_BYPASS_VERIFICATION')) ?? false,
244 'inactive_force_password_reset' => get_bool(env('USER_INACTIVE_FORCE_PASSWORD_RESET') ?? false),
245 'inactive_seconds_verification' => (get_int(env('USER_INACTIVE_DAYS_VERIFICATION')) ?? 180) * 86400,
246 'min_plays_for_posting' => get_int(env('USER_MIN_PLAYS_FOR_POSTING')) ?? 10,
247 'min_plays_allow_verified_bypass' => get_bool(env('USER_MIN_PLAYS_ALLOW_VERIFIED_BYPASS')) ?? true,
248 'post_action_verification' => get_bool(env('USER_POST_ACTION_VERIFICATION')) ?? true,
249 'profile_scores_notice' => $profileScoresNotice,
250 'user_page_forum_id' => intval(env('USER_PAGE_FORUM_ID', 70)),
251 'verification_key_length_hex' => 8,
252 'verification_key_tries_limit' => 8,
253 'max_follows' => get_int(env('USER_MAX_FOLLOWS')) ?? 5000,
254 'max_friends' => get_int(env('USER_MAX_FRIENDS')) ?? 250,
255 'max_friends_supporter' => get_int(env('USER_MAX_FRIENDS_SUPPORTER')) ?? 500,
256 'max_login_attempts' => get_int(env('USER_MAX_LOGIN_ATTEMPTS')) ?? 10,
257 'max_multiplayer_duration' => get_int(env('USER_MAX_MULTIPLAYER_DURATION')) ?? 14,
258 // see https://github.com/ppy/osu/pull/16024/files#diff-d5f8d0eb0eac5cfd6d2f486d34c4168e036b83b622c7a3c5bfce5205d67bf52bR327-R330
259 'max_multiplayer_duration_supporter' => get_int(env('USER_MAX_MULTIPLAYER_DURATION_SUPPORTER')) ?? 63,
260 'max_multiplayer_rooms' => get_int(env('USER_MAX_MULTIPLAYER_ROOMS')) ?? 1,
261 'max_multiplayer_rooms_supporter' => get_int(env('USER_MAX_MULTIPLAYER_ROOMS_SUPPORTER')) ?? 5,
262 'max_score_pins' => get_int(env('USER_MAX_SCORE_PINS')) ?? 10,
263 'max_score_pins_supporter' => get_int(env('USER_MAX_SCORE_PINS_SUPPORTER')) ?? 50,
264 'online_window' => intval(env('USER_ONLINE_WINDOW', 10)) * 60,
265 'password_reset' => [
266 'expires_hour' => 2,
267 'key_length' => 8,
268 'tries' => 8,
269 ],
270 'super_friendly' => array_map('intval', explode(' ', env('SUPER_FRIENDLY', '3'))),
271 'ban_persist_days' => get_int(env('BAN_PERSIST_DAYS')) ?? 28,
272
273 'country_change' => [
274 'max_mixed_months' => get_int(env('USER_COUNTRY_CHANGE_MAX_MIXED_MONTHS')) ?? 2,
275 'min_months' => get_int(env('USER_COUNTRY_CHANGE_MIN_MONTHS')) ?? 6,
276 ],
277
278 'registration_mode' => [
279 'client' => get_bool(env('REGISTRATION_MODE_CLIENT')) ?? true,
280 'web' => get_bool(env('REGISTRATION_MODE_WEB')) ?? false,
281 ],
282 ],
283 'user_report_notification' => [
284 'endpoint_cheating' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_CHEATING')),
285 'endpoint_moderation' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_MODERATION')),
286
287 'endpoint' => [
288 'beatmapset_discussion' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_BEATMAPSET_DISCUSSION')),
289 'beatmapset' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_BEATMAPSET')),
290 'chat' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_CHAT')),
291 'comment' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_COMMENT')),
292 'forum' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_FORUM')),
293 'user' => presence(env('USER_REPORT_NOTIFICATION_ENDPOINT_USER')),
294 ],
295 ],
296 'wiki' => [
297 'branch' => presence(env('WIKI_BRANCH'), 'master'),
298 'repository' => presence(env('WIKI_REPOSITORY'), 'osu-wiki'),
299 'user' => presence(env('WIKI_USER'), 'ppy'),
300 ],
301 'changelog' => [
302 'build_history_interval' => 60 * intval(env('CHANGELOG_BUILD_HISTORY_INTERVAL', 30)), // in minutes, converted to seconds
303 'chart_days' => intval(env('CHANGELOG_CHART_DAYS', 7)),
304 'featured_stream' => intval(env('FEATURED_UPDATE_STREAM', 5)),
305 'github_token' => env('CHANGELOG_GITHUB_TOKEN'),
306 'update_streams' => array_map('intval', explode(' ', env('UPDATE_STREAMS', '5 1'))),
307 ],
308 'rankings' => [
309 'country_performance_user_count' => intval(env('COUNTRY_PERFORMANCE_USER_COUNT', 1000)),
310 'country_performance_weighting_factor' => floatval(env('COUNTRY_PERFORMANCE_WEIGHTING_FACTOR', 0.99)),
311 ],
312];