From 8586781a81b11fe7ce7755cd6ff825d2a22b773b Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Wed, 19 Aug 2026 16:04:58 +0100 Subject: [PATCH] feat: initial start on basic instance file syncing --- apps/app-frontend/src/App.vue | 18 +- .../components/ui/modal/AppSettingsModal.vue | 8 +- .../instances/DefaultInstanceSettings.vue | 362 ----- .../instances/InstancesSyncedSettings.vue | 797 +++++++++++ .../src/components/ui/world/WorldItem.vue | 20 +- .../ui/world/modal/DesyncServerModal.vue | 67 + apps/app-frontend/src/helpers/instance.ts | 78 +- apps/app-frontend/src/helpers/types.d.ts | 3 + apps/app-frontend/src/helpers/worlds.ts | 14 + .../app-frontend/src/locales/de-CH/index.json | 3 - .../app-frontend/src/locales/de-DE/index.json | 3 - .../app-frontend/src/locales/en-US/index.json | 91 +- .../src/locales/es-419/index.json | 3 - .../app-frontend/src/locales/fr-FR/index.json | 3 - .../app-frontend/src/locales/hu-HU/index.json | 3 - .../app-frontend/src/locales/it-IT/index.json | 3 - .../app-frontend/src/locales/ja-JP/index.json | 3 - .../app-frontend/src/locales/ko-KR/index.json | 3 - .../app-frontend/src/locales/nl-NL/index.json | 3 - .../app-frontend/src/locales/pl-PL/index.json | 3 - .../app-frontend/src/locales/pt-BR/index.json | 3 - .../app-frontend/src/locales/ru-RU/index.json | 3 - .../app-frontend/src/locales/sv-SE/index.json | 3 - .../app-frontend/src/locales/uk-UA/index.json | 3 - .../app-frontend/src/locales/zh-CN/index.json | 3 - .../app-frontend/src/locales/zh-TW/index.json | 3 - .../settings-modal/hooks-settings.vue | 135 +- .../components/settings-modal/index.vue | 30 - .../settings-modal/java-settings.vue | 284 ++-- .../synced-options-settings.vue | 150 +- .../settings-modal/window-settings.vue | 109 +- .../src/pages/instance/worlds/index.vue | 12 + apps/app/build.rs | 11 + apps/app/src/api/instance.rs | 90 ++ apps/app/src/api/worlds.rs | 10 + ...acdd6586cfcea44c88152dc44d26f21332910.json | 12 + ...1b92e6abffdcad59050db59a0fcbace5b5f26.json | 20 + ...454c0cd8d49040bcb4a73c48cf044b3ecac89.json | 20 + ...ac1bafff2c46863e844a1f4ea5501eedb9ad4.json | 12 + ...0f4f6039574975f61747d3dd21bf57eda238a.json | 32 + ...7297c3e78428e58589586d65b62e917ee6ab3.json | 12 + ...68650ea8c5c8c45d267c3398d338e17cff239.json | 20 + ...175d493be079b599d5ac168988f0612ab563d.json | 12 + ...80f9bde067f3ac15aa40d15c19d217639b605.json | 12 + ...5a9eafcfbfdf7b98df1f838ba9af56695d7a4.json | 26 + ...f616424ecdb687b38fbbca4673ad5cdf24dd9.json | 12 + ...0a21ad800b41f69deda4ac6f4c6ca87428a14.json | 12 + ...74d295cfb808c03033e552928c3cf7347b6ad.json | 12 + ...da1f5c94fdf0db098072c4a138fe31932c370.json | 26 + ...55a6a7c6dbad8f7975973e2614441301bec23.json | 20 + ...f27e3ad2de43eebb2ee226497580d8ff69176.json | 20 + ...a4e314a4fc07eb8236f4bf35fd60db0605655.json | 12 + ...27d6e5e2c586da9cdcde53614a33320837dac.json | 20 + ...6ccd5b0ea889166672fedb26811ebb324c62f.json | 12 + ...60b48b538e41dca1a1e850a03f8b95ad9a34a.json | 44 + ...c7a0bd79be3d5b0836676073dc56355f87bc0.json | 12 + ...e461b77671619a65fb809bd9b8ed9ab71baaa.json | 20 + ...20260819120000_instance-synced-options.sql | 53 + packages/app-lib/src/api/instance.rs | 21 + .../app-lib/src/api/instance/lifecycle.rs | 13 +- .../src/api/instance/synced_options.rs | 1245 +++++++++++++++++ .../src/api/instance/synced_servers.rs | 1185 ++++++++++++++++ .../app-lib/src/api/pack/install_mrpack.rs | 21 + packages/app-lib/src/api/worlds.rs | 179 +-- packages/app-lib/src/state/dirs.rs | 7 + .../adapters/sqlite/instance_rows.rs | 105 +- .../instances/commands/create_instance.rs | 2 + .../src/state/instances/model/instance.rs | 22 +- .../app-lib/src/state/instances/watcher.rs | 28 +- packages/app-lib/src/state/mod.rs | 8 + packages/app-lib/src/state/process.rs | 14 + packages/ui/src/utils/ace-mode-mcfunction.ts | 106 ++ 72 files changed, 4836 insertions(+), 910 deletions(-) delete mode 100644 apps/app-frontend/src/components/ui/settings/instances/DefaultInstanceSettings.vue create mode 100644 apps/app-frontend/src/components/ui/settings/instances/InstancesSyncedSettings.vue create mode 100644 apps/app-frontend/src/components/ui/world/modal/DesyncServerModal.vue create mode 100644 packages/app-lib/.sqlx/query-025bdcd737cd08b4111946dc28cacdd6586cfcea44c88152dc44d26f21332910.json create mode 100644 packages/app-lib/.sqlx/query-0bb6b572586c47253e224af9bee1b92e6abffdcad59050db59a0fcbace5b5f26.json create mode 100644 packages/app-lib/.sqlx/query-0e12f60511e37652760cac595e7454c0cd8d49040bcb4a73c48cf044b3ecac89.json create mode 100644 packages/app-lib/.sqlx/query-1cdee99e388ed61a0d57a61ded6ac1bafff2c46863e844a1f4ea5501eedb9ad4.json create mode 100644 packages/app-lib/.sqlx/query-21463a2bec47047d05d9bdb2dac0f4f6039574975f61747d3dd21bf57eda238a.json create mode 100644 packages/app-lib/.sqlx/query-30694d61cfc967df2bc22341e127297c3e78428e58589586d65b62e917ee6ab3.json create mode 100644 packages/app-lib/.sqlx/query-42c6505cc670cca48934a9f3fe768650ea8c5c8c45d267c3398d338e17cff239.json create mode 100644 packages/app-lib/.sqlx/query-6f7d5d7df89066764e809f4d299175d493be079b599d5ac168988f0612ab563d.json create mode 100644 packages/app-lib/.sqlx/query-8f09e98c6b8e0f1d5444aabbf2a80f9bde067f3ac15aa40d15c19d217639b605.json create mode 100644 packages/app-lib/.sqlx/query-9d60938be8573be0f82cac164245a9eafcfbfdf7b98df1f838ba9af56695d7a4.json create mode 100644 packages/app-lib/.sqlx/query-a73b1547db507fc88dbeccb332af616424ecdb687b38fbbca4673ad5cdf24dd9.json create mode 100644 packages/app-lib/.sqlx/query-a90cef7f12500b65bb8a6ea070b0a21ad800b41f69deda4ac6f4c6ca87428a14.json create mode 100644 packages/app-lib/.sqlx/query-b9e21c3aa7ebb21db77c1b02a2274d295cfb808c03033e552928c3cf7347b6ad.json create mode 100644 packages/app-lib/.sqlx/query-c4f24e497f828d629498bc5c022da1f5c94fdf0db098072c4a138fe31932c370.json create mode 100644 packages/app-lib/.sqlx/query-d0620336091b153f01448417ca655a6a7c6dbad8f7975973e2614441301bec23.json create mode 100644 packages/app-lib/.sqlx/query-da27707b85df33ee2024216aa3df27e3ad2de43eebb2ee226497580d8ff69176.json create mode 100644 packages/app-lib/.sqlx/query-db7e6a3711ec75599be7b20ad31a4e314a4fc07eb8236f4bf35fd60db0605655.json create mode 100644 packages/app-lib/.sqlx/query-dc41f40aaad8fe8f379ca4b595127d6e5e2c586da9cdcde53614a33320837dac.json create mode 100644 packages/app-lib/.sqlx/query-e626908e7d94188472f78c82a286ccd5b0ea889166672fedb26811ebb324c62f.json create mode 100644 packages/app-lib/.sqlx/query-e6f5817e75f03035f327211cf7460b48b538e41dca1a1e850a03f8b95ad9a34a.json create mode 100644 packages/app-lib/.sqlx/query-fb55bb9d1e72cc4fa87ab6d851dc7a0bd79be3d5b0836676073dc56355f87bc0.json create mode 100644 packages/app-lib/.sqlx/query-fc91a68aaf7e6762d3bddbd4fe4e461b77671619a65fb809bd9b8ed9ab71baaa.json create mode 100644 packages/app-lib/src/api/instance/synced_options.rs create mode 100644 packages/app-lib/src/api/instance/synced_servers.rs create mode 100644 packages/ui/src/utils/ace-mode-mcfunction.ts diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index f6333d2a56..13737b1aa5 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -108,7 +108,12 @@ import { debugAnalytics, initAnalytics, trackEvent } from '@/helpers/analytics' import { check_reachable } from '@/helpers/auth.js' import { get_user, get_version } from '@/helpers/cache.js' import { install_create_modpack_instance, install_get_modpack_preview } from '@/helpers/install' -import { can_current_user_use_shared_instances, get as getInstance, run } from '@/helpers/instance' +import { + can_current_user_use_shared_instances, + get as getInstance, + get_global_synced_options, + run, +} from '@/helpers/instance' import { get as getCreds, login, logout } from '@/helpers/mr_auth.ts' import { mergeUrlQuery, parseModrinthLink } from '@/helpers/project-links.ts' import { get as getSettings, set as setSettings } from '@/helpers/settings.ts' @@ -419,6 +424,11 @@ const os = ref('') const isDevEnvironment = ref(false) const stateInitialized = ref(false) +const globalSyncedOptionsQuery = useQuery({ + queryKey: ['global-synced-options'], + queryFn: get_global_synced_options, + enabled: computed(() => stateInitialized.value), +}) const criticalErrorMessage = ref() @@ -1717,7 +1727,11 @@ provideAppUpdateDownloadProgress(appUpdateDownload) - + -import { - defineMessages, - injectNotificationManager, - Slider, - StyledInput, - Toggle, - useVIntl, -} from '@modrinth/ui' -import { ref, watch } from 'vue' - -import useMemorySlider from '@/composables/useMemorySlider' -import { get, parseEnvVars, serializeEnvVars, set } from '@/helpers/settings.ts' - -const { handleError } = injectNotificationManager() -const { formatMessage } = useVIntl() - -const messages = defineMessages({ - fullscreenTitle: { - id: 'app.settings.default-instance-options.fullscreen.title', - defaultMessage: 'Fullscreen', - }, - fullscreenDescription: { - id: 'app.settings.default-instance-options.fullscreen.description', - defaultMessage: 'Start instances in fullscreen by updating their options.txt file.', - }, - widthTitle: { - id: 'app.settings.default-instance-options.width.title', - defaultMessage: 'Width', - }, - widthDescription: { - id: 'app.settings.default-instance-options.width.description', - defaultMessage: 'The width of the game window when launched.', - }, - widthPlaceholder: { - id: 'app.settings.default-instance-options.width.placeholder', - defaultMessage: 'Enter width...', - }, - heightTitle: { - id: 'app.settings.default-instance-options.height.title', - defaultMessage: 'Height', - }, - heightDescription: { - id: 'app.settings.default-instance-options.height.description', - defaultMessage: 'The height of the game window when launched.', - }, - heightPlaceholder: { - id: 'app.settings.default-instance-options.height.placeholder', - defaultMessage: 'Enter height...', - }, - memoryAllocationTitle: { - id: 'app.settings.default-instance-options.memory-allocation.title', - defaultMessage: 'Memory allocation', - }, - memoryAllocationDescription: { - id: 'app.settings.default-instance-options.memory-allocation.description', - defaultMessage: 'Maximum memory available to each instance.', - }, - javaArgumentsTitle: { - id: 'app.settings.default-instance-options.java-arguments.title', - defaultMessage: 'Java arguments', - }, - javaArgumentsPlaceholder: { - id: 'app.settings.default-instance-options.java-arguments.placeholder', - defaultMessage: 'Enter Java arguments...', - }, - javaArgumentsDescription: { - id: 'app.settings.default-instance-options.java-arguments.description', - defaultMessage: 'Arguments passed to Java when launching an instance.', - }, - environmentVariablesTitle: { - id: 'app.settings.default-instance-options.environment-variables.title', - defaultMessage: 'Environment variables', - }, - environmentVariablesPlaceholder: { - id: 'app.settings.default-instance-options.environment-variables.placeholder', - defaultMessage: 'Enter environment variables...', - }, - environmentVariablesDescription: { - id: 'app.settings.default-instance-options.environment-variables.description', - defaultMessage: 'Environment variables set when launching an instance.', - }, - preLaunchHookTitle: { - id: 'app.settings.default-instance-options.pre-launch-hook.title', - defaultMessage: 'Pre-launch hook', - }, - preLaunchHookPlaceholder: { - id: 'app.settings.default-instance-options.pre-launch-hook.placeholder', - defaultMessage: 'Enter pre-launch command...', - }, - preLaunchHookDescription: { - id: 'app.settings.default-instance-options.pre-launch-hook.description', - defaultMessage: 'Runs before the instance starts.', - }, - wrapperHookTitle: { - id: 'app.settings.default-instance-options.wrapper-hook.title', - defaultMessage: 'Wrapper hook', - }, - wrapperHookPlaceholder: { - id: 'app.settings.default-instance-options.wrapper-hook.placeholder', - defaultMessage: 'Enter wrapper command...', - }, - wrapperHookDescription: { - id: 'app.settings.default-instance-options.wrapper-hook.description', - defaultMessage: 'Command used to wrap the Minecraft launch process.', - }, - postExitHookTitle: { - id: 'app.settings.default-instance-options.post-exit-hook.title', - defaultMessage: 'Post-exit hook', - }, - postExitHookPlaceholder: { - id: 'app.settings.default-instance-options.post-exit-hook.placeholder', - defaultMessage: 'Enter post-exit command...', - }, - postExitHookDescription: { - id: 'app.settings.default-instance-options.post-exit-hook.description', - defaultMessage: 'Runs after the game closes.', - }, - hookVariablesDescription: { - id: 'instance.settings.tabs.hooks.variables.description', - defaultMessage: - 'Hooks run in the working directory of the instance, with the following variables:', - }, - instanceNameDescription: { - id: 'instance.settings.tabs.hooks.variables.inst-name.description', - defaultMessage: '$INST_NAME: The name of the instance', - }, - instanceIdDescription: { - id: 'instance.settings.tabs.hooks.variables.inst-id.description', - defaultMessage: "$INST_ID: The name of the instance's folder", - }, - instanceDirDescription: { - id: 'instance.settings.tabs.hooks.variables.inst-dir.description', - defaultMessage: "$INST_DIR: The absolute path to the instance's folder", - }, - instanceMcDirDescription: { - id: 'instance.settings.tabs.hooks.variables.inst-mc-dir.description', - defaultMessage: '$INST_MC_DIR: An alias for $INST_DIR', - }, - instanceJavaDescription: { - id: 'instance.settings.tabs.hooks.variables.inst-java.description', - defaultMessage: '$INST_JAVA: The absolute path to the java binary', - }, - instanceJavaArgsDescription: { - id: 'instance.settings.tabs.hooks.variables.inst-java-args.description', - defaultMessage: '$INST_JAVA_ARGS: The JVM Arguments provided to the game', - }, -}) - -const fetchSettings = await get() -fetchSettings.launchArgs = fetchSettings.extra_launch_args.join(' ') -fetchSettings.envVars = serializeEnvVars(fetchSettings.custom_env_vars) - -const settings = ref(fetchSettings) - -const { maxMemory, snapPoints } = (await useMemorySlider().catch(handleError)) as unknown as { - maxMemory: number - snapPoints: number[] -} - -watch( - settings, - async () => { - const setSettings = JSON.parse(JSON.stringify(settings.value)) - - setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean) - setSettings.custom_env_vars = parseEnvVars(setSettings.envVars) - delete setSettings.launchArgs - delete setSettings.envVars - - if (!setSettings.custom_dir) { - setSettings.custom_dir = null - } - - await set(setSettings).catch(handleError) - }, - { deep: true }, -) - - - diff --git a/apps/app-frontend/src/components/ui/settings/instances/InstancesSyncedSettings.vue b/apps/app-frontend/src/components/ui/settings/instances/InstancesSyncedSettings.vue new file mode 100644 index 0000000000..3a5169b146 --- /dev/null +++ b/apps/app-frontend/src/components/ui/settings/instances/InstancesSyncedSettings.vue @@ -0,0 +1,797 @@ + + + + + diff --git a/apps/app-frontend/src/components/ui/world/WorldItem.vue b/apps/app-frontend/src/components/ui/world/WorldItem.vue index 8147d0bce0..b1e1ebac41 100644 --- a/apps/app-frontend/src/components/ui/world/WorldItem.vue +++ b/apps/app-frontend/src/components/ui/world/WorldItem.vue @@ -66,7 +66,7 @@ const router = useRouter() const { addNotification } = injectNotificationManager() const emit = defineEmits<{ - (e: 'play' | 'play-instance' | 'update' | 'stop' | 'refresh' | 'edit' | 'delete'): void + (e: 'play' | 'play-instance' | 'update' | 'stop' | 'refresh' | 'edit' | 'delete' | 'desync'): void (e: 'open-folder', world: SingleplayerWorld): void }>() @@ -217,6 +217,10 @@ const messages = defineMessages({ id: 'instance.worlds.copy_address', defaultMessage: 'Copy address', }, + desync: { + id: 'instance.worlds.desync_server', + defaultMessage: 'Desync', + }, viewInstance: { id: 'instance.worlds.view_instance', defaultMessage: 'View instance', @@ -500,6 +504,16 @@ const messages = defineMessages({ shown: world.type === 'server', action: () => copyToClipboard((world as ServerWorld).address), }, + { + id: 'desync', + label: formatMessage(messages.desync), + action: () => emit('desync'), + shown: + !instanceId && + world.type === 'server' && + (world as ServerWorld).source === 'user_synced' && + !!(world as ServerWorld).server_id, + }, { id: 'edit', label: formatMessage(commonMessages.editButton), @@ -577,6 +591,10 @@ const messages = defineMessages({