diff --git a/electron/index.js b/electron/index.js
index 264aa581..8ec625bc 100644
--- a/electron/index.js
+++ b/electron/index.js
@@ -14,8 +14,6 @@ app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling");
const isDev = app.commandLine.hasSwitch("dev");
const isLocal = app.commandLine.hasSwitch("local");
-const safeMode = app.commandLine.hasSwitch("safe-mode");
-const externalMod = app.commandLine.getSwitchValue("load-mod");
const roamingFolder =
process.env.APPDATA ||
@@ -60,7 +58,7 @@ function createWindow() {
useContentSize: false,
minWidth: 800,
minHeight: 600,
- title: "shapez.io Standalone",
+ title: "shapez.io Demo",
transparent: false,
icon: path.join(__dirname, "favicon" + faviconExtension),
// fullscreen: true,
@@ -341,49 +339,8 @@ ipcMain.handle("fs-job", async (event, job) => {
}
});
-ipcMain.handle("open-mods-folder", async () => {
- shell.openPath(modsPath);
-});
-
-console.log("Loading mods ...");
-
-function loadMods() {
- if (safeMode) {
- console.log("Safe Mode enabled for mods, skipping mod search");
- }
- console.log("Loading mods from", modsPath);
- let modFiles = safeMode
- ? []
- : fs
- .readdirSync(modsPath)
- .filter(filename => filename.endsWith(".js"))
- .map(filename => path.join(modsPath, filename));
-
- if (externalMod) {
- console.log("Adding external mod source:", externalMod);
- const externalModPaths = externalMod.split(",");
- modFiles = modFiles.concat(externalModPaths);
- }
-
- return modFiles.map(filename => fs.readFileSync(filename, "utf8"));
-}
-
-let mods = [];
-try {
- mods = loadMods();
- console.log("Loaded", mods.length, "mods");
-} catch (ex) {
- console.error("Failed to load mods");
- dialog.showErrorBox("Failed to load mods:", ex);
-}
-
ipcMain.handle("get-mods", async () => {
- return mods;
+ return [];
});
steam.init(isDev);
-
-// Only allow achievements and puzzle DLC if no mods are loaded
-if (mods.length === 0) {
- steam.listen();
-}
diff --git a/gulp/standalone.js b/gulp/standalone.js
index 75f0b7a8..529b405c 100644
--- a/gulp/standalone.js
+++ b/gulp/standalone.js
@@ -151,10 +151,10 @@ function gulptasksStandalone($, gulp) {
asar: asar,
executableName: "shapezio",
icon: path.join(electronBaseDir, "favicon"),
- name: "shapez.io-standalone" + suffix,
+ name: "shapez.io-demo" + suffix,
out: tempDestDir,
overwrite: true,
- appBundleId: "io.shapez.standalone",
+ appBundleId: "io.shapez.demo",
appCategoryType: "public.app-category.games",
}).then(
appPaths => {
diff --git a/res/logo.png b/res/logo.png
index 75dd3a35..c83d2d59 100644
Binary files a/res/logo.png and b/res/logo.png differ
diff --git a/res/logo_wegame.png b/res/logo_wegame.png
deleted file mode 100644
index ac9092b3..00000000
Binary files a/res/logo_wegame.png and /dev/null differ
diff --git a/src/js/core/restriction_manager.js b/src/js/core/restriction_manager.js
index 3ca30597..2a9819d6 100644
--- a/src/js/core/restriction_manager.js
+++ b/src/js/core/restriction_manager.js
@@ -63,7 +63,6 @@ export class RestrictionManager extends ReadWriteProxy {
onHasLegacySavegamesChanged(has119Savegames = false) {
if (has119Savegames && !this.currentData.savegameV1119Imported) {
this.currentData.savegameV1119Imported = true;
- console.warn("Current user now has access to all levels due to 1119 savegame");
return this.writeAsync();
}
return Promise.resolve();
@@ -74,25 +73,6 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
isLimitedVersion() {
- if (IS_MAC) {
- // On mac, the full version is always active
- return false;
- }
-
- if (G_IS_STANDALONE) {
- // Standalone is never limited
- return false;
- }
-
- if (queryParamOptions.embedProvider === "gamedistribution") {
- // also full version on gamedistribution
- return false;
- }
-
- if (G_IS_DEV) {
- return typeof window !== "undefined" && window.location.search.indexOf("demo") >= 0;
- }
-
return true;
}
@@ -101,7 +81,7 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
getIsStandaloneMarketingActive() {
- return this.isLimitedVersion();
+ return true;
}
/**
@@ -109,7 +89,7 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
getIsExportingScreenshotsPossible() {
- return !this.isLimitedVersion();
+ return false;
}
/**
@@ -117,7 +97,7 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {number}
*/
getMaximumWaypoints() {
- return this.isLimitedVersion() ? 2 : 1e20;
+ return 2;
}
/**
@@ -125,7 +105,7 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
getHasUnlimitedSavegames() {
- return !this.isLimitedVersion();
+ return false;
}
/**
@@ -133,7 +113,7 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
getHasExtendedSettings() {
- return !this.isLimitedVersion();
+ return false;
}
/**
@@ -141,7 +121,7 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
getHasExtendedUpgrades() {
- return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
+ return false;
}
/**
@@ -149,6 +129,6 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
getHasExtendedLevelsAndFreeplay() {
- return !this.isLimitedVersion() || this.currentData.savegameV1119Imported;
+ return false;
}
}
diff --git a/src/js/mods/modloader.js b/src/js/mods/modloader.js
index 3d0985d5..7a2964f0 100644
--- a/src/js/mods/modloader.js
+++ b/src/js/mods/modloader.js
@@ -1,18 +1,11 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
-import { globalConfig } from "../core/config";
import { createLogger } from "../core/logging";
-import { StorageImplBrowserIndexedDB } from "../platform/browser/storage_indexed_db";
-import { StorageImplElectron } from "../platform/electron/storage";
-import { FILE_NOT_FOUND } from "../platform/storage";
import { Mod } from "./mod";
import { ModInterface } from "./mod_interface";
import { MOD_SIGNALS } from "./mod_signals";
-import semverValidRange from "semver/ranges/valid";
-import semverSatisifies from "semver/functions/satisfies";
-
const LOG = createLogger("mods");
/**
@@ -104,165 +97,9 @@ export class ModLoader {
};
}
- exposeExports() {
- if (G_IS_DEV || G_IS_STANDALONE) {
- let exports = {};
- const modules = require.context("../", true, /\.js$/);
- Array.from(modules.keys()).forEach(key => {
- // @ts-ignore
- const module = modules(key);
- for (const member in module) {
- if (member === "default" || member === "__$S__") {
- // Setter
- continue;
- }
- if (exports[member]) {
- throw new Error("Duplicate export of " + member);
- }
-
- Object.defineProperty(exports, member, {
- get() {
- return module[member];
- },
- set(v) {
- module.__$S__(member, v);
- },
- });
- }
- });
-
- window.shapez = exports;
- }
- }
+ exposeExports() {}
async initMods() {
- if (!G_IS_STANDALONE && !G_IS_DEV) {
- this.initialized = true;
- return;
- }
-
- // Create a storage for reading mod settings
- const storage = G_IS_STANDALONE
- ? new StorageImplElectron(this.app)
- : new StorageImplBrowserIndexedDB(this.app);
- await storage.initialize();
-
- LOG.log("hook:init", this.app, this.app.storage);
- this.exposeExports();
-
- let mods = [];
- if (G_IS_STANDALONE) {
- mods = await ipcRenderer.invoke("get-mods");
- }
- if (G_IS_DEV && globalConfig.debug.externalModUrl) {
- const modURLs = Array.isArray(globalConfig.debug.externalModUrl)
- ? globalConfig.debug.externalModUrl
- : [globalConfig.debug.externalModUrl];
-
- for (let i = 0; i < modURLs.length; i++) {
- const response = await fetch(modURLs[i], {
- method: "GET",
- });
- if (response.status !== 200) {
- throw new Error(
- "Failed to load " + modURLs[i] + ": " + response.status + " " + response.statusText
- );
- }
- mods.push(await response.text());
- }
- }
-
- window.$shapez_registerMod = (modClass, meta) => {
- if (this.initialized) {
- throw new Error("Can't register mod after modloader is initialized");
- }
- if (this.modLoadQueue.some(entry => entry.meta.id === meta.id)) {
- console.warn("Not registering mod", meta, "since a mod with the same id is already loaded");
- return;
- }
- this.modLoadQueue.push({
- modClass,
- meta,
- });
- };
-
- mods.forEach(modCode => {
- modCode += `
- if (typeof Mod !== 'undefined') {
- if (typeof METADATA !== 'object') {
- throw new Error("No METADATA variable found");
- }
- window.$shapez_registerMod(Mod, METADATA);
- }
- `;
- try {
- const func = new Function(modCode);
- func();
- } catch (ex) {
- console.error(ex);
- alert("Failed to parse mod (launch with --dev for more info): \n\n" + ex);
- }
- });
-
- delete window.$shapez_registerMod;
-
- for (let i = 0; i < this.modLoadQueue.length; i++) {
- const { modClass, meta } = this.modLoadQueue[i];
- const modDataFile = "modsettings_" + meta.id + "__" + meta.version + ".json";
-
- if (meta.minimumGameVersion) {
- const minimumGameVersion = meta.minimumGameVersion;
- if (!semverValidRange(minimumGameVersion)) {
- alert("Mod " + meta.id + " has invalid minimumGameVersion: " + minimumGameVersion);
- continue;
- }
- if (!semverSatisifies(G_BUILD_VERSION, minimumGameVersion)) {
- alert(
- "Mod '" +
- meta.id +
- "' is incompatible with this version of the game: \n\n" +
- "Mod requires version " +
- minimumGameVersion +
- " but this game has version " +
- G_BUILD_VERSION
- );
- continue;
- }
- }
-
- let settings = meta.settings;
-
- if (meta.settings) {
- try {
- const storedSettings = await storage.readFileAsync(modDataFile);
- settings = JSON.parse(storedSettings);
- } catch (ex) {
- if (ex === FILE_NOT_FOUND) {
- // Write default data
- await storage.writeFileAsync(modDataFile, JSON.stringify(meta.settings));
- } else {
- alert("Failed to load settings for " + meta.id + ", will use defaults:\n\n" + ex);
- }
- }
- }
-
- try {
- const mod = new modClass({
- app: this.app,
- modLoader: this,
- meta,
- settings,
- saveSettings: () => storage.writeFileAsync(modDataFile, JSON.stringify(mod.settings)),
- });
- mod.init();
- this.mods.push(mod);
- } catch (ex) {
- console.error(ex);
- alert("Failed to initialize mods (launch with --dev for more info): \n\n" + ex);
- }
- }
-
- this.modLoadQueue = [];
this.initialized = true;
}
}
diff --git a/src/js/platform/browser/game_analytics.js b/src/js/platform/browser/game_analytics.js
index aa0ac796..da00eab4 100644
--- a/src/js/platform/browser/game_analytics.js
+++ b/src/js/platform/browser/game_analytics.js
@@ -18,23 +18,7 @@ const analyticsLocalFile = "shapez_token_123.bin";
export class ShapezGameAnalytics extends GameAnalyticsInterface {
get environment() {
- if (G_IS_DEV) {
- return "dev";
- }
-
- if (G_IS_STANDALONE) {
- return "steam";
- }
-
- if (G_IS_RELEASE) {
- return "prod";
- }
-
- if (window.location.host.indexOf("alpha") >= 0) {
- return "alpha";
- } else {
- return "beta";
- }
+ return "steam-demo";
}
/**
diff --git a/src/js/platform/sound.js b/src/js/platform/sound.js
index d43c76c2..3198b1b9 100644
--- a/src/js/platform/sound.js
+++ b/src/js/platform/sound.js
@@ -31,14 +31,10 @@ export const SOUNDS = {
export const MUSIC = {
// The theme always depends on the standalone only, even if running the full
// version in the browser
- theme: G_IS_STANDALONE ? "theme-full" : "theme-short",
+ theme: "theme-short",
menu: "menu",
};
-if (G_IS_STANDALONE || G_IS_DEV) {
- MUSIC.puzzle = "puzzle-full";
-}
-
export class SoundInstanceInterface {
constructor(key, url) {
this.key = key;
diff --git a/src/js/profile/application_settings.js b/src/js/profile/application_settings.js
index 03f4fdcf..47965279 100644
--- a/src/js/profile/application_settings.js
+++ b/src/js/profile/application_settings.js
@@ -186,10 +186,7 @@ function initializeSettings() {
if (app.platformWrapper.getSupportsFullscreen()) {
app.platformWrapper.setFullscreen(value);
}
- },
- /**
- * @param {Application} app
- */ app => app.restrictionMgr.getHasExtendedSettings()
+ }
),
new BoolSetting(
diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js
index 10e280b9..b316e8a4 100644
--- a/src/js/states/main_menu.js
+++ b/src/js/states/main_menu.js
@@ -38,16 +38,15 @@ export class MainMenuState extends GameState {
getInnerHTML() {
const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION;
const showExitAppButton = G_IS_STANDALONE;
- const showUpdateLabel = !G_WEGAME_VERSION;
const showBrowserWarning = !G_IS_STANDALONE && !isSupportedBrowser();
- const showPuzzleDLC = !G_WEGAME_VERSION && (G_IS_STANDALONE || G_IS_DEV);
+ const showPuzzleDLC = false;
const showWegameFooter = G_WEGAME_VERSION;
- const hasMods = MODS.anyModsActive();
+ const hasMods = false;
let showExternalLinks = true;
if (G_IS_STANDALONE) {
- if (G_WEGAME_VERSION || G_CHINA_VERSION) {
+ if (G_WEGAME_VERSION) {
showExternalLinks = false;
}
} else {
@@ -58,19 +57,12 @@ export class MainMenuState extends GameState {
}
let showDiscordLink = showExternalLinks;
- if (G_CHINA_VERSION) {
- showDiscordLink = true;
- }
- const showCrosspromo = !G_IS_STANDALONE && showExternalLinks;
+ const showCrosspromo = false;
const showDemoAdvertisement =
showExternalLinks && this.app.restrictionMgr.getIsStandaloneMarketingActive();
- const ownsPuzzleDLC =
- G_IS_DEV ||
- (G_IS_STANDALONE &&
- /** @type { PlatformWrapperImplElectron}*/ (this.app.platformWrapper).dlcs.puzzle);
-
+ const ownsPuzzleDLC = false;
const bannerHtml = `
${T.demoBanners.title}
${T.demoBanners.intro}
@@ -96,7 +88,6 @@ export class MainMenuState extends GameState {
- ${showUpdateLabel ? `
MODS UPDATE!` : ""}
diff --git a/src/js/states/mods.js b/src/js/states/mods.js
index 1e0fe5f1..a59058f8 100644
--- a/src/js/states/mods.js
+++ b/src/js/states/mods.js
@@ -1,6 +1,5 @@
import { THIRDPARTY_URLS } from "../core/config";
import { TextualGameState } from "../core/textual_game_state";
-import { MODS } from "../mods/modloader";
import { T } from "../translations";
export class ModsState extends TextualGameState {
@@ -14,35 +13,29 @@ export class ModsState extends TextualGameState {
internalGetFullHtml() {
let headerHtml = `
- `;
return `
- ${headerHtml}
-
- ${this.getInnerHTML()}
-
- `;
+ ${headerHtml}
+
+ ${this.getInnerHTML()}
+
+ `;
}
getMainContentHTML() {
- if (!G_IS_STANDALONE && !G_IS_DEV) {
- return `
+ return `
${T.mods.noModSupport}
@@ -52,49 +45,6 @@ export class ModsState extends TextualGameState {
`;
- }
-
- if (MODS.mods.length === 0) {
- return `
-
-
- ${T.mods.modsInfo}
-
-
-
-
- `;
- }
-
- let modsHtml = ``;
-
- MODS.mods.forEach(mod => {
- modsHtml += `
-
-
-
${mod.metadata.name}
-
${mod.metadata.description}
-
${T.mods.modWebsite}
-
-
${T.mods.version}${mod.metadata.version}
-
${T.mods.author}${mod.metadata.author}
-
-
-
-
-
- `;
- });
- return `
-
-
- ${T.mods.modsInfo}
-
-
-
- ${modsHtml}
-
- `;
}
onEnter() {
diff --git a/translations/base-zh-CN.yaml b/translations/base-zh-CN.yaml
index 6e70b501..edceae6a 100644
--- a/translations/base-zh-CN.yaml
+++ b/translations/base-zh-CN.yaml
@@ -74,7 +74,7 @@ mainMenu:
puzzleDlcWishlist: 添加愿望单!
puzzleDlcViewNow: 查看DLC
mods:
- title: 激活游戏模组(Mods)
+ title: 激活游戏模组
warningPuzzleDLC: 无法在任何游戏模组(Mods)下进行“谜题挑战者”DLC,请关闭所有游戏模组(Mods)。
dialogs:
buttons:
@@ -139,7 +139,8 @@ dialogs:
desc: 您还没有解锁蓝图功能!通过第12关的挑战后可解锁蓝图。
keybindingsIntroduction:
title: 实用快捷键
- desc: 这个游戏有很多有用的快捷键设定。以下是其中的一些介绍,记得在
按键设置中查看其他按键设定!
+ desc:
+ 这个游戏有很多有用的快捷键设定。以下是其中的一些介绍,记得在
按键设置中查看其他按键设定!
CTRL键
+ 拖动:选择区域以复制或删除。
SHIFT键
: 按住以放置多个同一种设施。
ALT键
:反向放置传送带。
@@ -318,15 +319,18 @@ ingame:
hints:
1_1_extractor: 在
圆形上放置一个
开采器来获取圆形!
提示:
按下鼠标左键选中
开采器
1_2_conveyor: 用
传送带将您的开采器连接到中心基地上!
提示:选中
传送带后
按下鼠标左键可拖动布置传送带!
- 1_3_expand: 您可以放置更多的
开采器和
传送带来更有效率地完成关卡目标。
+ 1_3_expand:
+ 您可以放置更多的
开采器和
传送带来更有效率地完成关卡目标。
提示:按住
SHIFT
键可放置多个
开采器,注意用
R
键可旋转
开采器的出口方向,确保开采的图形可以顺利传送。
2_1_place_cutter: 现在放置一个
切割器,这个设施可把
圆形切成两半!
注意:无论如何放置,切割机总是
从上到下切割。
- 2_2_place_trash: 使用切割机后产生的废弃图形会导致
堵塞。
注意使用
垃圾桶清除当前
+ 2_2_place_trash:
+ 使用切割机后产生的废弃图形会导致
堵塞。
注意使用
垃圾桶清除当前
(!) 不需要的废物。
2_3_more_cutters: 干的好!现在放置
2个以上的切割机来加快当前缓慢的过程!
提示:用
快捷键0-9可以快速选择各项设施!
- 3_1_rectangles: 现在让我们开采一些矩形!找到
矩形地带并
放置4个开采器并将它们用
传送带连接到中心基地。
+ 3_1_rectangles:
+ 现在让我们开采一些矩形!找到
矩形地带并
放置4个开采器并将它们用
传送带连接到中心基地。
提示:选中
传送带后按住
SHIFT键可快速准确地规划
传送带路线!
21_1_place_quad_painter: 放置
四口上色器并且获取一些
圆形,
白色和
红色!
21_2_switch_to_wires: 按
E 键选择
电线层!
@@ -620,7 +624,8 @@ storyRewards:
desc: 恭喜!您解锁了
旋转机。它会顺时针将输入的
图形旋转90度。
reward_painter:
title: 上色
- desc: 恭喜!您解锁了
上色器。开采一些颜色(就像您开采图形一样),将其在上色器中与图形结合来将图形上色!
+ desc:
+ 恭喜!您解锁了
上色器。开采一些颜色(就像您开采图形一样),将其在上色器中与图形结合来将图形上色!
注意:如果您不幸患有色盲,可以在设置中启用
色盲模式
reward_mixer:
title: 混合颜色
@@ -637,11 +642,13 @@ storyRewards:
desc: 恭喜!您解锁了
隧道。它可放置在
传送带或
设施下方以运送物品。
reward_rotater_ccw:
title: 逆时针旋转
- desc: 恭喜!您解锁了
旋转机的
逆时针变体。它可以逆时针旋转
图形。
+ desc:
+ 恭喜!您解锁了
旋转机的
逆时针变体。它可以逆时针旋转
图形。
选择
旋转机然后按"T"键来选取这个变体。
reward_miner_chainable:
title: 链式开采器
- desc: 您已经解锁了
链式开采器!它能
转发资源给其他的开采器,这样您就能更有效率的开采各类资源了!
+ desc:
+ 您已经解锁了
链式开采器!它能
转发资源给其他的开采器,这样您就能更有效率的开采各类资源了!
注意:新的开采器已替换了工具栏里旧的开采器!
reward_underground_belt_tier_2:
title: 二级隧道
@@ -658,12 +665,14 @@ storyRewards:
它
优先从左边输出,这样您就可以用它做一个
溢流门了!
reward_freeplay:
title: 自由模式
- desc: 成功了!您解锁了
自由模式!挑战升级!这意味着现在将
随机生成图形!
+ desc:
+ 成功了!您解锁了
自由模式!挑战升级!这意味着现在将
随机生成图形!
从现在起,中心基地最为需要的是
产量,我强烈建议您去制造一台能够自动交付所需图形的机器!
基地会在
电线层输出需要的图形,您需要去分析图形并在此基础上自动配置您的工厂。
reward_blueprints:
title: 蓝图
- desc: 您现在可以
复制粘贴您的工厂的一部分了!按住 CTRL键并拖动鼠标来选择一块区域,然后按C键复制。
+ desc:
+ 您现在可以
复制粘贴您的工厂的一部分了!按住 CTRL键并拖动鼠标来选择一块区域,然后按C键复制。
粘贴并
不是免费的,您需要制造
蓝图图形来负担。蓝图图形是您刚刚交付的图形。
no_reward:
title: 下一关
@@ -691,7 +700,8 @@ storyRewards:
注意:您注意到
传送读取器和
存储器输出的他们最后读取的物品了吗?试着在显示屏上展示一下!
reward_constant_signal:
title: 恒定信号
- desc: 恭喜!您解锁了生成于电线层之上的
恒定信号,把它连接到
过滤器时非常有用。
+ desc:
+ 恭喜!您解锁了生成于电线层之上的
恒定信号,把它连接到
过滤器时非常有用。
比如,它能发出图形、颜色、开关值(1 / 0)的固定信号。
reward_logic_gates:
title: 逻辑门
@@ -710,7 +720,8 @@ storyRewards:
提示:可在设置中打开电线层教程!
reward_filter:
title: 物品过滤器
- desc: 恭喜!您解锁了
物品过滤器!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。
+ desc:
+ 恭喜!您解锁了
物品过滤器!它会根据在电线层上输入的信号决定是从上面还是右边输出物品。
您也可以输入开关值(1 / 0)信号来激活或者禁用它。
reward_demo_end:
title: 试玩结束
@@ -1093,7 +1104,7 @@ backendErrors:
too-many-likes-already: 您的谜题已经得到了许多玩家的赞赏。如果您仍然希望删除它,请联系support@shapez.io!
no-permission: 您没有执行此操作的权限。
mods:
- title: 游戏模组(Mods)
+ title: 游戏模组
author: 作者
version: 版本
openFolder: 打开游戏模组(Mods)文件夹
@@ -1103,6 +1114,7 @@ mods:
noModSupport: 您需要在Steam平台获得完整版才可以安装游戏模组(Mods)。
togglingComingSoon:
title: 即将开放
- description: 当前只能通过将游戏模组(Mods)文件复制到mods文件夹或从mods文件夹移除来启用或禁用游戏模组(Mods)。
+ description:
+ 当前只能通过将游戏模组(Mods)文件复制到mods文件夹或从mods文件夹移除来启用或禁用游戏模组(Mods)。
但是,可以切换游戏模组(Mods)已经计划在之后的更新中实现!
modWebsite: Website