diff --git a/patches/0001-Rename-the-app-to-Modrinth-Enhanced.patch b/patches/0001-Rename-the-app-to-Modrinth-Enhanced.patch index 4393421..71a57b7 100644 --- a/patches/0001-Rename-the-app-to-Modrinth-Enhanced.patch +++ b/patches/0001-Rename-the-app-to-Modrinth-Enhanced.patch @@ -36,7 +36,7 @@ index 50867a4..6367788 100644 diff --git a/apps/app-frontend/src/components/ui/modal/AppSettingsModal.vue b/apps/app-frontend/src/components/ui/modal/AppSettingsModal.vue -index dd55fe5..0f33d0e 100644 +index 5f863d5..32a86a3 100644 --- a/apps/app-frontend/src/components/ui/modal/AppSettingsModal.vue +++ b/apps/app-frontend/src/components/ui/modal/AppSettingsModal.vue @@ -312,7 +312,7 @@ const messages = defineMessages({ @@ -49,10 +49,10 @@ index dd55fe5..0f33d0e 100644 macos: { id: 'app.settings.operating-system.macos', diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json -index cc65180..faab078 100644 +index e81abe7..ba08ca7 100644 --- a/apps/app-frontend/src/locales/en-US/index.json +++ b/apps/app-frontend/src/locales/en-US/index.json -@@ -1548,7 +1548,7 @@ +@@ -1626,7 +1626,7 @@ "message": "Oldest" }, "app.settings.app-version": { diff --git a/patches/0002-Remove-advertising-and-Modrinth-upsells.patch b/patches/0002-Remove-advertising-and-Modrinth-upsells.patch index 2e1d726..aa96e6d 100644 --- a/patches/0002-Remove-advertising-and-Modrinth-upsells.patch +++ b/patches/0002-Remove-advertising-and-Modrinth-upsells.patch @@ -27,7 +27,7 @@ ads and these prompts were shown, and nothing more. 2 files changed, 29 insertions(+), 91 deletions(-) diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue -index a2ae0e6..6e7b27d 100644 +index 5faea7c..447f636 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -9,7 +9,6 @@ import { diff --git a/patches/0003-Remove-telemetry.patch b/patches/0003-Remove-telemetry.patch index 3d4a3b6..e1d786f 100644 --- a/patches/0003-Remove-telemetry.patch +++ b/patches/0003-Remove-telemetry.patch @@ -302,10 +302,10 @@ index bb20aa9..46a9749 100644 + void router } diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json -index faab078..233cac2 100644 +index ba08ca7..28d0366 100644 --- a/apps/app-frontend/src/locales/en-US/index.json +++ b/apps/app-frontend/src/locales/en-US/index.json -@@ -2667,7 +2667,7 @@ +@@ -2745,7 +2745,7 @@ "message": "Discord Rich Presence" }, "app.settings.privacy.telemetry.description": { @@ -335,7 +335,7 @@ index e69e6b7..e5bbabe 100644 } } diff --git a/packages/app-lib/src/api/instance/run.rs b/packages/app-lib/src/api/instance/run.rs -index 25626c1..d638128 100644 +index 415af27..4e46190 100644 --- a/packages/app-lib/src/api/instance/run.rs +++ b/packages/app-lib/src/api/instance/run.rs @@ -4,13 +4,10 @@ use crate::state::{ @@ -352,7 +352,7 @@ index 25626c1..d638128 100644 #[derive(Debug, Clone)] pub enum QuickPlayType { -@@ -232,54 +229,10 @@ async fn run_credentials( +@@ -242,54 +239,10 @@ async fn run_credentials( mc_set_options.push(("fullscreen".to_string(), "true".to_string())); } @@ -411,7 +411,7 @@ index 25626c1..d638128 100644 crate::minecraft_skins::flush_pending_skin_change().await?; crate::launcher::launch_minecraft( -@@ -297,21 +250,6 @@ async fn run_credentials( +@@ -307,21 +260,6 @@ async fn run_credentials( .await } @@ -433,7 +433,7 @@ index 25626c1..d638128 100644 pub async fn kill(instance_id: &str) -> crate::Result<()> { let state = State::get().await?; let processes = -@@ -373,13 +311,12 @@ pub async fn try_update_playtime_by_instance_id( +@@ -383,13 +321,12 @@ pub async fn try_update_playtime_by_instance_id( } } diff --git a/patches/0004-Add-offline-accounts.patch b/patches/0004-Add-offline-accounts.patch index e8feddb..3655ab4 100644 --- a/patches/0004-Add-offline-accounts.patch +++ b/patches/0004-Add-offline-accounts.patch @@ -51,10 +51,10 @@ which offered Microsoft and nothing else: create mode 100644 apps/app-frontend/src/components/ui/OfflineAccountModal.vue diff --git a/Cargo.lock b/Cargo.lock -index d40a959..95bc842 100644 +index bdc7307..9006b00 100644 --- a/Cargo.lock +++ b/Cargo.lock -@@ -11384,6 +11384,7 @@ dependencies = [ +@@ -11534,6 +11534,7 @@ dependencies = [ "indicatif", "itertools 0.14.0", "json5", @@ -63,7 +63,7 @@ index d40a959..95bc842 100644 "notify", "notify-debouncer-mini", diff --git a/Cargo.toml b/Cargo.toml -index a4a779c..a85f576 100644 +index b8b227e..8348c8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,6 +132,7 @@ lz4_flex = { version = "0.11.5", default-features = false, features = [ @@ -75,7 +75,7 @@ index a4a779c..a85f576 100644 modrinth-log = { path = "packages/modrinth-log" } modrinth-util = { path = "packages/modrinth-util" } diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue -index 6e7b27d..e5c6fd4 100644 +index 447f636..d750a5f 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -2467,15 +2467,17 @@ provideAppUpdateDownloadProgress(appUpdateDownload) @@ -439,7 +439,7 @@ index 94bd13e..cb7319a 100644 * Retrieves the default user * @return {Promise} diff --git a/apps/app/build.rs b/apps/app/build.rs -index c4becf9..f8de273 100644 +index 926cab4..68a02aa 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -14,6 +14,7 @@ fn main() { @@ -476,10 +476,10 @@ index 8227d94..f4eded6 100644 pub async fn remove_user(user: uuid::Uuid) -> Result<()> { Ok(minecraft_auth::remove_user(user).await?) diff --git a/packages/app-lib/Cargo.toml b/packages/app-lib/Cargo.toml -index 69545c2..7da46c9 100644 +index 15a1dc9..4fe7643 100644 --- a/packages/app-lib/Cargo.toml +++ b/packages/app-lib/Cargo.toml -@@ -48,6 +48,7 @@ image = { workspace = true, features = ["gif", "jpeg", "png", "webp"] } +@@ -52,6 +52,7 @@ image = { workspace = true, features = ["gif", "jpeg", "png", "webp"] } indicatif = { workspace = true, optional = true } itertools = { workspace = true } json5 = { workspace = true } diff --git a/patches/0005-Make-the-sidebars-foldable.patch b/patches/0005-Make-the-sidebars-foldable.patch index 512acff..a9793da 100644 --- a/patches/0005-Make-the-sidebars-foldable.patch +++ b/patches/0005-Make-the-sidebars-foldable.patch @@ -37,7 +37,7 @@ bar button - so nothing there promises anything. 4 files changed, 172 insertions(+), 15 deletions(-) diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue -index e5c6fd4..2f6cdce 100644 +index d750a5f..ac8308c 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -10,6 +10,8 @@ import { diff --git a/patches/0006-Add-Ely.by-accounts.patch b/patches/0006-Add-Ely.by-accounts.patch index a0dc17a..8f94348 100644 --- a/patches/0006-Add-Ely.by-accounts.patch +++ b/patches/0006-Add-Ely.by-accounts.patch @@ -402,7 +402,7 @@ index cb7319a..57580ff 100644 * Retrieves the default user * @return {Promise} diff --git a/apps/app/build.rs b/apps/app/build.rs -index f8de273..1fdb8e1 100644 +index 68a02aa..0f62dd8 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -15,6 +15,7 @@ fn main() { @@ -487,10 +487,10 @@ index a7fac4a..2d18da3 100644 pub async fn get_default_user() -> crate::Result> { let state = State::get().await?; diff --git a/packages/app-lib/src/launcher/mod.rs b/packages/app-lib/src/launcher/mod.rs -index b482547..660f63b 100644 +index 7910694..e623cab 100644 --- a/packages/app-lib/src/launcher/mod.rs +++ b/packages/app-lib/src/launcher/mod.rs -@@ -1056,6 +1056,22 @@ pub async fn launch_minecraft( +@@ -1059,6 +1059,22 @@ pub async fn launch_minecraft( command.arg("--add-opens=jdk.internal/jdk.internal.misc=ALL-UNNAMED"); } @@ -842,12 +842,12 @@ index 0000000..dc099f9 + Ok(jar) +} diff --git a/packages/app-lib/src/util/mod.rs b/packages/app-lib/src/util/mod.rs -index 7656b4a..d0e4d5d 100644 +index 1962d6c..395d7f1 100644 --- a/packages/app-lib/src/util/mod.rs +++ b/packages/app-lib/src/util/mod.rs @@ -1,4 +1,5 @@ //! Theseus utility functions +pub mod authlib_injector; + pub(crate) mod content_hash; pub mod fetch; pub mod io; - pub mod jre; diff --git a/patches/0007-Sign-in-to-Microsoft-in-the-player-s-own-browser.patch b/patches/0007-Sign-in-to-Microsoft-in-the-player-s-own-browser.patch index 5f51dc1..dad0a69 100644 --- a/patches/0007-Sign-in-to-Microsoft-in-the-player-s-own-browser.patch +++ b/patches/0007-Sign-in-to-Microsoft-in-the-player-s-own-browser.patch @@ -405,7 +405,7 @@ index 57580ff..c25af95 100644 * Adds an offline account with the given username and makes it the active one. * diff --git a/apps/app/build.rs b/apps/app/build.rs -index 1fdb8e1..5c797ab 100644 +index 0f62dd8..8562a68 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -14,6 +14,8 @@ fn main() { diff --git a/patches/0008-Round-the-window-corners-on-Linux.patch b/patches/0008-Round-the-window-corners-on-Linux.patch index 93a9e99..e84475d 100644 --- a/patches/0008-Round-the-window-corners-on-Linux.patch +++ b/patches/0008-Round-the-window-corners-on-Linux.patch @@ -11,7 +11,7 @@ Subject: [PATCH] Round the window corners on Linux 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue -index 2f6cdce..9412821 100644 +index ac8308c..d681a14 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -540,6 +540,16 @@ watch([os, isFullscreen], ([osName, fullscreen]) => { diff --git a/patches/0009-Scroll-with-the-middle-mouse-button.patch b/patches/0009-Scroll-with-the-middle-mouse-button.patch index d705815..fb073db 100644 --- a/patches/0009-Scroll-with-the-middle-mouse-button.patch +++ b/patches/0009-Scroll-with-the-middle-mouse-button.patch @@ -11,7 +11,7 @@ Subject: [PATCH] Scroll with the middle mouse button create mode 100644 apps/app-frontend/src/helpers/autoscroll.ts diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue -index 9412821..9f7f5e0 100644 +index d681a14..2102c4c 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -124,6 +124,7 @@ import { diff --git a/patches/0010-Manage-Ely.by-skins-from-the-skin-page.patch b/patches/0010-Manage-Ely.by-skins-from-the-skin-page.patch index c22e133..bff3016 100644 --- a/patches/0010-Manage-Ely.by-skins-from-the-skin-page.patch +++ b/patches/0010-Manage-Ely.by-skins-from-the-skin-page.patch @@ -844,7 +844,7 @@ index 8240b8b..bcd79ec 100644
PathBuf { -@@ -276,6 +284,7 @@ impl DirectoryInfo { - INSTANCES_FOLDER_NAME, - METADATA_FOLDER_NAME, - SYNCED_OPTIONS_FOLDER_NAME, -+ PLAYER_SKINS_FOLDER_NAME, - ]; +diff --git a/packages/app-lib/src/state/dirs/move_app_directory.rs b/packages/app-lib/src/state/dirs/move_app_directory.rs +index 7418675..a25a7fd 100644 +--- a/packages/app-lib/src/state/dirs/move_app_directory.rs ++++ b/packages/app-lib/src/state/dirs/move_app_directory.rs +@@ -11,13 +11,14 @@ use sqlx::SqlitePool; + use std::path::{Path, PathBuf}; + use tokio::fs; - struct MovePath { +-const MOVED_APP_DIRECTORIES: [&str; 6] = [ ++const MOVED_APP_DIRECTORIES: [&str; 7] = [ + "store", + "profiles", + "meta", + "caches", + "icons", + "synced-options", ++ "player_skins", + ]; + pub(crate) async fn relocate_tree(from: &Path, to: &Path) -> crate::Result<()> { + if !fs::try_exists(from).await? { diff --git a/packages/app-lib/src/state/settings.rs b/packages/app-lib/src/state/settings.rs index 2fd0d87..673dfaa 100644 --- a/packages/app-lib/src/state/settings.rs diff --git a/patches/0012-Launch-a-running-instance-again-on-another-account.patch b/patches/0012-Launch-a-running-instance-again-on-another-account.patch index ef0ab41..4ea09fd 100644 --- a/patches/0012-Launch-a-running-instance-again-on-another-account.patch +++ b/patches/0012-Launch-a-running-instance-again-on-another-account.patch @@ -206,7 +206,7 @@ index 977bf82..c390c4c 100644 + */ +process_uuid: string, } & ({ "type": "log4j" } & Log4jEvent | { "type": "legacy", message: string, }); diff --git a/apps/app-frontend/src/generated/app-events/postcard/index.d.ts b/apps/app-frontend/src/generated/app-events/postcard/index.d.ts -index 66144c0..42f013a 100644 +index e763358..149f9ef 100644 --- a/apps/app-frontend/src/generated/app-events/postcard/index.d.ts +++ b/apps/app-frontend/src/generated/app-events/postcard/index.d.ts @@ -50,7 +50,7 @@ export type FriendPayload = { tag: "friend_request", value: { from: string } } | @@ -219,7 +219,7 @@ index 66144c0..42f013a 100644 export type Log4jEvent = { timestamp_millis: i64 | undefined, logger_name: string | undefined, level: string | undefined, thread_name: string | undefined, message: string | undefined, throwable: string | undefined } export type ModLoader = { tag: "vanilla" } | { tag: "forge" } | { tag: "fabric" } | { tag: "quilt" } | { tag: "neoforge" } diff --git a/apps/app-frontend/src/generated/app-events/postcard/index.js b/apps/app-frontend/src/generated/app-events/postcard/index.js -index c8816ff..cd56865 100644 +index 2407d7d..1b343a0 100644 --- a/apps/app-frontend/src/generated/app-events/postcard/index.js +++ b/apps/app-frontend/src/generated/app-events/postcard/index.js @@ -475,6 +475,7 @@ function deserialize_LOG_EVENT(d) { @@ -440,7 +440,7 @@ index 638386d..3cccc8a 100644 function normalizeInstallProgress(value: unknown): WireObject { diff --git a/apps/app/build.rs b/apps/app/build.rs -index cc3611c..382ce22 100644 +index 02c30c0..fc78fe1 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -110,7 +110,9 @@ fn main() { @@ -520,10 +520,10 @@ index e938efa..5150b00 100644 + Ok(()) +} diff --git a/packages/app-lib/src/api/instance.rs b/packages/app-lib/src/api/instance.rs -index 0efd145..c5cfed2 100644 +index 8a0040f..46cc47a 100644 --- a/packages/app-lib/src/api/instance.rs +++ b/packages/app-lib/src/api/instance.rs -@@ -53,7 +53,8 @@ pub use self::projects::{ +@@ -59,7 +59,8 @@ pub use self::projects::{ update_managed_modrinth_version, update_project, }; pub use self::run::{ @@ -534,7 +534,7 @@ index 0efd145..c5cfed2 100644 pub use self::screenshot_groups::{ ScreenshotGroup, ScreenshotGroupImport, ScreenshotGroupMembershipUpdate, diff --git a/packages/app-lib/src/api/instance/run.rs b/packages/app-lib/src/api/instance/run.rs -index d638128..0394da0 100644 +index 4e46190..1d01c06 100644 --- a/packages/app-lib/src/api/instance/run.rs +++ b/packages/app-lib/src/api/instance/run.rs @@ -20,6 +20,25 @@ pub enum QuickPlayType { @@ -563,7 +563,7 @@ index d638128..0394da0 100644 ) -> crate::Result { let state = State::get().await?; if crate::state::instances::adapters::sqlite::instance_rows::is_instance_quarantined( -@@ -43,7 +62,8 @@ pub async fn run( +@@ -53,7 +72,8 @@ pub async fn run( .await? .ok_or_else(|| crate::ErrorKind::NoCredentialsError.as_error())?; @@ -573,7 +573,7 @@ index d638128..0394da0 100644 } #[tracing::instrument(skip(credentials))] -@@ -51,6 +71,7 @@ async fn run_credentials( +@@ -61,6 +81,7 @@ async fn run_credentials( instance_id: &str, credentials: &Credentials, quick_play_type: QuickPlayType, @@ -581,7 +581,7 @@ index d638128..0394da0 100644 ) -> crate::Result { let state = State::get().await?; let settings = Settings::get(&state.pool).await?; -@@ -246,6 +267,7 @@ async fn run_credentials( +@@ -256,6 +277,7 @@ async fn run_credentials( post_exit_hook, &context, quick_play_type, @@ -636,10 +636,10 @@ index 4ce0c57..3382b5b 100644 #[tracing::instrument] diff --git a/packages/app-lib/src/event/mod.rs b/packages/app-lib/src/event/mod.rs -index 059bfbf..a83f732 100644 +index 833349d..84b9a87 100644 --- a/packages/app-lib/src/event/mod.rs +++ b/packages/app-lib/src/event/mod.rs -@@ -558,6 +558,8 @@ mod log_types { +@@ -559,6 +559,8 @@ mod log_types { #[serde_binhum::serde_binhum] pub struct LogPayload { pub instance_id: String, @@ -649,10 +649,10 @@ index 059bfbf..a83f732 100644 #[cfg_attr(feature = "export-ts", ts(flatten))] pub event: LogEvent, diff --git a/packages/app-lib/src/launcher/mod.rs b/packages/app-lib/src/launcher/mod.rs -index b6055bb..55e1664 100644 +index e352716..6e7de28 100644 --- a/packages/app-lib/src/launcher/mod.rs +++ b/packages/app-lib/src/launcher/mod.rs -@@ -823,6 +823,9 @@ pub async fn launch_minecraft( +@@ -825,6 +825,9 @@ pub async fn launch_minecraft( post_exit_hook: Option, context: &InstanceLaunchContext, mut quick_play_type: QuickPlayType, @@ -662,7 +662,7 @@ index b6055bb..55e1664 100644 ) -> crate::Result { let instance = &context.instance; let content_set = &context.applied_content_set; -@@ -951,21 +954,27 @@ pub async fn launch_minecraft( +@@ -954,21 +957,27 @@ pub async fn launch_minecraft( let env_args = Vec::from(env_args); @@ -705,7 +705,7 @@ index b6055bb..55e1664 100644 } let natives_dir = state.directories.version_natives_dir(&version_jar); -@@ -1203,6 +1212,8 @@ pub async fn launch_minecraft( +@@ -1212,6 +1221,8 @@ pub async fn launch_minecraft( &instance.id, &instance.path, &instance.name, diff --git a/patches/0013-Explain-what-went-wrong-when-the-game-crashed.patch b/patches/0013-Explain-what-went-wrong-when-the-game-crashed.patch index 8779bd4..609dbd5 100644 --- a/patches/0013-Explain-what-went-wrong-when-the-game-crashed.patch +++ b/patches/0013-Explain-what-went-wrong-when-the-game-crashed.patch @@ -880,7 +880,7 @@ index e2a59fd..4fcddea 100644 }) diff --git a/apps/app/build.rs b/apps/app/build.rs -index 382ce22..fe38f0d 100644 +index fc78fe1..4553d48 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -44,6 +44,17 @@ fn main() { @@ -902,10 +902,10 @@ index 382ce22..fe38f0d 100644 "cache", InlinedPlugin::new() diff --git a/apps/app/capabilities/plugins.json b/apps/app/capabilities/plugins.json -index 15efb0c..dbc3220 100644 +index 1492b97..0a1560d 100644 --- a/apps/app/capabilities/plugins.json +++ b/apps/app/capabilities/plugins.json -@@ -112,6 +112,7 @@ +@@ -105,6 +105,7 @@ "ads:default", "friends:default", "worlds:default", @@ -970,10 +970,10 @@ index 15a5c1e..565a39f 100644 pub mod import; pub mod install; diff --git a/apps/app/src/main.rs b/apps/app/src/main.rs -index 098daa6..c404189 100644 +index 5e582b5..8429e4c 100644 --- a/apps/app/src/main.rs +++ b/apps/app/src/main.rs -@@ -249,6 +249,7 @@ fn main() { +@@ -251,6 +251,7 @@ fn main() { builder = builder .plugin(api::auth::init()) .plugin(api::ely_skins::init()) diff --git a/patches/0014-Browse-skins-from-Ely.by-NameMC-laby.net-and-crafty..patch b/patches/0014-Browse-skins-from-Ely.by-NameMC-laby.net-and-crafty..patch index fb3ae34..937fd74 100644 --- a/patches/0014-Browse-skins-from-Ely.by-NameMC-laby.net-and-crafty..patch +++ b/patches/0014-Browse-skins-from-Ely.by-NameMC-laby.net-and-crafty..patch @@ -976,7 +976,7 @@ index bcd79ec..9a10f0e 100644
diff --git a/apps/app/build.rs b/apps/app/build.rs -index fe38f0d..145ec31 100644 +index 4553d48..90e22ef 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -44,6 +44,19 @@ fn main() { @@ -1000,10 +1000,10 @@ index fe38f0d..145ec31 100644 "crash-analysis", InlinedPlugin::new() diff --git a/apps/app/capabilities/plugins.json b/apps/app/capabilities/plugins.json -index dbc3220..d22b900 100644 +index 0a1560d..c44fb00 100644 --- a/apps/app/capabilities/plugins.json +++ b/apps/app/capabilities/plugins.json -@@ -113,6 +113,7 @@ +@@ -106,6 +106,7 @@ "friends:default", "worlds:default", "ely-skins:default", @@ -1153,10 +1153,10 @@ index 0000000..cfd0d8c + .into() +} diff --git a/apps/app/src/main.rs b/apps/app/src/main.rs -index c404189..57a68e6 100644 +index 8429e4c..04eedbd 100644 --- a/apps/app/src/main.rs +++ b/apps/app/src/main.rs -@@ -250,6 +250,7 @@ fn main() { +@@ -252,6 +252,7 @@ fn main() { .plugin(api::auth::init()) .plugin(api::ely_skins::init()) .plugin(api::crash_analysis::init()) diff --git a/upstream.txt b/upstream.txt index 054567b..f198b15 100644 --- a/upstream.txt +++ b/upstream.txt @@ -1 +1 @@ -v0.20.5 +v0.21.2