New demo mode and local mode reimplementation (#6819)

* feat: demo mode when using local account

* refactor[demo]: do not allow local account download the game and switch game dir when is demo, use DateUtils instead creating another date checker

* fix[demo]: require activity from optifine installer fragment to minecraft downloader verify if is a local account
This commit is contained in:
TarikBR 2025-04-11 17:33:01 -03:00 committed by GitHub
parent 2387817f4f
commit a6f3fc0606
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 116 additions and 33 deletions

View File

@ -43,11 +43,13 @@ import net.kdt.pojavlaunch.services.ProgressServiceKeeper;
import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader; import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader;
import net.kdt.pojavlaunch.tasks.AsyncVersionList; import net.kdt.pojavlaunch.tasks.AsyncVersionList;
import net.kdt.pojavlaunch.tasks.MinecraftDownloader; import net.kdt.pojavlaunch.tasks.MinecraftDownloader;
import net.kdt.pojavlaunch.utils.DateUtils;
import net.kdt.pojavlaunch.utils.NotificationUtils; import net.kdt.pojavlaunch.utils.NotificationUtils;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles; import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile; import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.text.ParseException;
public class LauncherActivity extends BaseActivity { public class LauncherActivity extends BaseActivity {
public static final String SETTING_FRAGMENT_TAG = "SETTINGS_FRAGMENT"; public static final String SETTING_FRAGMENT_TAG = "SETTINGS_FRAGMENT";
@ -125,6 +127,23 @@ public class LauncherActivity extends BaseActivity {
} }
String normalizedVersionId = AsyncMinecraftDownloader.normalizeVersionId(prof.lastVersionId); String normalizedVersionId = AsyncMinecraftDownloader.normalizeVersionId(prof.lastVersionId);
JMinecraftVersionList.Version mcVersion = AsyncMinecraftDownloader.getListedVersion(normalizedVersionId); JMinecraftVersionList.Version mcVersion = AsyncMinecraftDownloader.getListedVersion(normalizedVersionId);
// Do not load when is a modded version or older than minecraft 1.3 on demo account
if (mAccountSpinner.getSelectedAccount().isDemo()) {
boolean isOlderThan13 = true;
if (mcVersion != null) {
try {
isOlderThan13 = DateUtils.dateBefore(DateUtils.parseReleaseDate(mcVersion.releaseTime), 2012, 6, 22);
} catch (ParseException ignored) {}
}
if (isOlderThan13) {
Toast.makeText(this, R.string.toast_not_available_demo, Toast.LENGTH_LONG).show();
return false;
}
}
new MinecraftDownloader().start( new MinecraftDownloader().start(
this, this,
mcVersion, mcVersion,

View File

@ -118,6 +118,7 @@ public final class Tools {
public static String DIR_ACCOUNT_NEW; public static String DIR_ACCOUNT_NEW;
public static String DIR_GAME_HOME = Environment.getExternalStorageDirectory().getAbsolutePath() + "/games/PojavLauncher"; public static String DIR_GAME_HOME = Environment.getExternalStorageDirectory().getAbsolutePath() + "/games/PojavLauncher";
public static String DIR_GAME_NEW; public static String DIR_GAME_NEW;
public static String GAME_PROFILES_FILE;
// New since 3.0.0 // New since 3.0.0
public static String DIRNAME_HOME_JRE = "lib"; public static String DIRNAME_HOME_JRE = "lib";
@ -200,6 +201,8 @@ public final class Tools {
OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources"; OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources";
CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap"; CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap";
CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json"; CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json";
GAME_PROFILES_FILE = Tools.DIR_GAME_NEW + "/launcher_profiles.json";
switchDemo(isDemoProfile(ctx));
} }
/** /**
@ -461,7 +464,7 @@ public final class Tools {
} }
public static String[] getMinecraftClientArgs(MinecraftAccount profile, JMinecraftVersionList.Version versionInfo, File gameDir) { public static String[] getMinecraftClientArgs(MinecraftAccount profile, JMinecraftVersionList.Version versionInfo, File gameDir) {
String username = profile.username; String username = profile.username.replace("Demo.", "");
String versionName = versionInfo.id; String versionName = versionInfo.id;
if (versionInfo.inheritsFrom != null) { if (versionInfo.inheritsFrom != null) {
versionName = versionInfo.inheritsFrom; versionName = versionInfo.inheritsFrom;
@ -506,13 +509,13 @@ public final class Tools {
} }
} }
return JSONUtils.insertJSONValueList( String mcArguments = versionInfo.minecraftArguments == null ?
splitAndFilterEmpty(
versionInfo.minecraftArguments == null ?
fromStringArray(minecraftArgs.toArray(new String[0])): fromStringArray(minecraftArgs.toArray(new String[0])):
versionInfo.minecraftArguments versionInfo.minecraftArguments;
), varArgMap
); if(profile.isDemo()) mcArguments += " --demo";
return JSONUtils.insertJSONValueList(splitAndFilterEmpty(mcArguments), varArgMap);
} }
public static String fromStringArray(String[] strArr) { public static String fromStringArray(String[] strArr) {
@ -1413,4 +1416,26 @@ public final class Tools {
} }
}).show(); }).show();
} }
public static void switchDemo(boolean isDemo){
if(isDemo) {
DIR_GAME_NEW = DIR_DATA + "/demo/.minecraft";
} else {
DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft";
}
DIR_HOME_VERSION = DIR_GAME_NEW + "/versions";
DIR_HOME_LIBRARY = DIR_GAME_NEW + "/libraries";
ASSETS_PATH = DIR_GAME_NEW + "/assets";
OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources";
}
public static boolean isDemoProfile(Context ctx){
MinecraftAccount currentProfile = PojavProfile.getCurrentProfileContent(ctx, null);
return currentProfile != null && currentProfile.isDemo();
}
public static boolean isLocalProfile(Context ctx){
MinecraftAccount currentProfile = PojavProfile.getCurrentProfileContent(ctx, null);
return currentProfile == null || currentProfile.isLocal();
}
} }

View File

@ -87,7 +87,6 @@ public class MicrosoftBackgroundLogin {
MinecraftAccount acc = MinecraftAccount.load(mcName); MinecraftAccount acc = MinecraftAccount.load(mcName);
if(acc == null) acc = new MinecraftAccount(); if(acc == null) acc = new MinecraftAccount();
if (doesOwnGame) {
acc.xuid = xsts[0]; acc.xuid = xsts[0];
acc.clientToken = "0"; /* FIXME */ acc.clientToken = "0"; /* FIXME */
acc.accessToken = mcToken; acc.accessToken = mcToken;
@ -97,7 +96,6 @@ public class MicrosoftBackgroundLogin {
acc.msaRefreshToken = msRefreshToken; acc.msaRefreshToken = msRefreshToken;
acc.expiresAt = expiresAt; acc.expiresAt = expiresAt;
acc.updateSkinFace(); acc.updateSkinFace();
}
acc.save(); acc.save();
if(doneListener != null) { if(doneListener != null) {
@ -298,7 +296,9 @@ public class MicrosoftBackgroundLogin {
}else{ }else{
Log.i("MicrosoftLogin","It seems that this Microsoft Account does not own the game."); Log.i("MicrosoftLogin","It seems that this Microsoft Account does not own the game.");
doesOwnGame = false; doesOwnGame = false;
throw new PresentedException(new RuntimeException(conn.getResponseMessage()), R.string.minecraft_not_owned); mcName = "Demo.Player";
mcUuid = "00000000-0000-0000-0000-000000000000";
//throw new PresentedException(new RuntimeException(conn.getResponseMessage()), R.string.minecraft_not_owned);
//throwResponseError(conn); //throwResponseError(conn);
} }
} }

View File

@ -89,6 +89,12 @@ public class MainMenuFragment extends Fragment {
} }
private void runInstallerWithConfirmation(boolean isCustomArgs) { private void runInstallerWithConfirmation(boolean isCustomArgs) {
// avoid using custom installers to install a version
if(Tools.isLocalProfile(requireContext()) || Tools.isDemoProfile(requireContext())){
Toast.makeText(requireContext(), R.string.toast_not_available_demo, Toast.LENGTH_LONG).show();
return;
}
if (ProgressKeeper.getTaskCount() == 0) if (ProgressKeeper.getTaskCount() == 0)
Tools.installMod(requireActivity(), isCustomArgs); Tools.installMod(requireActivity(), isCustomArgs);
else else

View File

@ -41,7 +41,7 @@ public class OptiFineInstallFragment extends ModVersionListFragment<OptiFineUtil
@Override @Override
public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) { public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) {
return new OptiFineDownloadTask((OptiFineUtils.OptiFineVersion) selectedVersion, listenerProxy); return new OptiFineDownloadTask((OptiFineUtils.OptiFineVersion) selectedVersion, listenerProxy, requireActivity());
} }
@Override @Override

View File

@ -2,11 +2,13 @@ package net.kdt.pojavlaunch.fragments;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import net.kdt.pojavlaunch.PojavProfile;
import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.Tools;
@ -25,17 +27,25 @@ public class ProfileTypeSelectFragment extends Fragment {
// NOTE: Special care needed! If you wll decide to add these to the back stack, please read // NOTE: Special care needed! If you wll decide to add these to the back stack, please read
// the comment in FabricInstallFragment.onDownloadFinished() and amend the code // the comment in FabricInstallFragment.onDownloadFinished() and amend the code
// in FabricInstallFragment.onDownloadFinished() and ModVersionListFragment.onDownloadFinished() // in FabricInstallFragment.onDownloadFinished() and ModVersionListFragment.onDownloadFinished()
view.findViewById(R.id.optifine_profile).setOnClickListener(v -> Tools.swapFragment(requireActivity(), OptiFineInstallFragment.class, view.findViewById(R.id.optifine_profile).setOnClickListener(v ->
OptiFineInstallFragment.TAG, null)); tryInstall(OptiFineInstallFragment.class, OptiFineInstallFragment.TAG));
view.findViewById(R.id.modded_profile_fabric).setOnClickListener((v)-> view.findViewById(R.id.modded_profile_fabric).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), FabricInstallFragment.class, FabricInstallFragment.TAG, null)); tryInstall(FabricInstallFragment.class, FabricInstallFragment.TAG));
view.findViewById(R.id.modded_profile_forge).setOnClickListener((v)-> view.findViewById(R.id.modded_profile_forge).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), ForgeInstallFragment.class, ForgeInstallFragment.TAG, null)); tryInstall(ForgeInstallFragment.class, ForgeInstallFragment.TAG));
view.findViewById(R.id.modded_profile_modpack).setOnClickListener((v)-> view.findViewById(R.id.modded_profile_modpack).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), SearchModFragment.class, SearchModFragment.TAG, null)); tryInstall(SearchModFragment.class, SearchModFragment.TAG));
view.findViewById(R.id.modded_profile_quilt).setOnClickListener((v)-> view.findViewById(R.id.modded_profile_quilt).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), QuiltInstallFragment.class, QuiltInstallFragment.TAG, null)); tryInstall(QuiltInstallFragment.class, QuiltInstallFragment.TAG));
view.findViewById(R.id.modded_profile_bta).setOnClickListener((v)-> view.findViewById(R.id.modded_profile_bta).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), BTAInstallFragment.class, BTAInstallFragment.TAG, null)); tryInstall(BTAInstallFragment.class, BTAInstallFragment.TAG));
}
private void tryInstall(Class<? extends Fragment> fragmentClass, String tag){
if(Tools.isLocalProfile(requireContext()) || Tools.isDemoProfile(requireContext())){
Toast.makeText(requireContext(), R.string.toast_not_available_demo, Toast.LENGTH_LONG).show();
} else {
Tools.swapFragment(requireActivity(), fragmentClass, tag, null);
}
} }
} }

View File

@ -1,5 +1,7 @@
package net.kdt.pojavlaunch.modloaders; package net.kdt.pojavlaunch.modloaders;
import android.app.Activity;
import com.kdt.mcgui.ProgressLayout; import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.JMinecraftVersionList; import net.kdt.pojavlaunch.JMinecraftVersionList;
@ -22,11 +24,13 @@ public class OptiFineDownloadTask implements Runnable, Tools.DownloaderFeedback,
private final ModloaderDownloadListener mListener; private final ModloaderDownloadListener mListener;
private final Object mMinecraftDownloadLock = new Object(); private final Object mMinecraftDownloadLock = new Object();
private Throwable mDownloaderThrowable; private Throwable mDownloaderThrowable;
private final Activity activity;
public OptiFineDownloadTask(OptiFineUtils.OptiFineVersion mOptiFineVersion, ModloaderDownloadListener mListener) { public OptiFineDownloadTask(OptiFineUtils.OptiFineVersion mOptiFineVersion, ModloaderDownloadListener mListener, Activity activity) {
this.mOptiFineVersion = mOptiFineVersion; this.mOptiFineVersion = mOptiFineVersion;
this.mDestinationFile = new File(Tools.DIR_CACHE, "optifine-installer.jar"); this.mDestinationFile = new File(Tools.DIR_CACHE, "optifine-installer.jar");
this.mListener = mListener; this.mListener = mListener;
this.activity = activity;
} }
@Override @Override
@ -89,7 +93,7 @@ public class OptiFineDownloadTask implements Runnable, Tools.DownloaderFeedback,
if(minecraftJsonVersion == null) return false; if(minecraftJsonVersion == null) return false;
try { try {
synchronized (mMinecraftDownloadLock) { synchronized (mMinecraftDownloadLock) {
new MinecraftDownloader().start(null, minecraftJsonVersion, minecraftVersion, this); new MinecraftDownloader().start(activity, minecraftJsonVersion, minecraftVersion, this);
mMinecraftDownloadLock.wait(); mMinecraftDownloadLock.wait();
} }
}catch (InterruptedException e) { }catch (InterruptedException e) {

View File

@ -54,6 +54,8 @@ public class MinecraftDownloader {
private static final ThreadLocal<byte[]> sThreadLocalDownloadBuffer = new ThreadLocal<>(); private static final ThreadLocal<byte[]> sThreadLocalDownloadBuffer = new ThreadLocal<>();
private boolean isLocalProfile = false;
/** /**
* Start the game version download process on the global executor service. * Start the game version download process on the global executor service.
* @param activity Activity, used for automatic installation of JRE 17 if needed * @param activity Activity, used for automatic installation of JRE 17 if needed
@ -64,6 +66,14 @@ public class MinecraftDownloader {
public void start(@Nullable Activity activity, @Nullable JMinecraftVersionList.Version version, public void start(@Nullable Activity activity, @Nullable JMinecraftVersionList.Version version,
@NonNull String realVersion, // this was there for a reason @NonNull String realVersion, // this was there for a reason
@NonNull AsyncMinecraftDownloader.DoneListener listener) { @NonNull AsyncMinecraftDownloader.DoneListener listener) {
if(activity != null){
isLocalProfile = Tools.isLocalProfile(activity);
Tools.switchDemo(Tools.isDemoProfile(activity));
} else {
isLocalProfile = true;
Tools.switchDemo(true);
}
sExecutorService.execute(() -> { sExecutorService.execute(() -> {
try { try {
downloadGame(activity, version, realVersion); downloadGame(activity, version, realVersion);
@ -506,6 +516,10 @@ public class MinecraftDownloader {
} }
private void downloadFile() throws Exception { private void downloadFile() throws Exception {
if(isLocalProfile){
throw new RuntimeException("Download failed. Please make sure you are logged in with a Microsoft Account.");
}
try { try {
DownloadUtils.ensureSha1(mTargetPath, mTargetSha1, () -> { DownloadUtils.ensureSha1(mTargetPath, mTargetSha1, () -> {
DownloadMirror.downloadFileMirrored(mDownloadClass, mTargetUrl, mTargetPath, DownloadMirror.downloadFileMirrored(mDownloadClass, mTargetUrl, mTargetPath,

View File

@ -45,7 +45,11 @@ public class MinecraftAccount {
} }
public boolean isLocal(){ public boolean isLocal(){
return accessToken.equals("0"); return accessToken.equals("0") && !username.startsWith("Demo.");
}
public boolean isDemo(){
return username.startsWith("Demo.");
} }
public void updateSkinFace() { public void updateSkinFace() {

View File

@ -16,7 +16,7 @@ import java.util.UUID;
public class LauncherProfiles { public class LauncherProfiles {
public static MinecraftLauncherProfiles mainProfileJson; public static MinecraftLauncherProfiles mainProfileJson;
private static final File launcherProfilesFile = new File(Tools.DIR_GAME_NEW, "launcher_profiles.json"); private static final File launcherProfilesFile = new File(Tools.GAME_PROFILES_FILE);
/** Reload the profile from the file, creating a default one if necessary */ /** Reload the profile from the file, creating a default one if necessary */
public static void load(){ public static void load(){

View File

@ -252,7 +252,7 @@
<string name="main_add_account">Add account</string> <string name="main_add_account">Add account</string>
<string name="tasks_ongoing">Tasks are in progress, please wait</string> <string name="tasks_ongoing">Tasks are in progress, please wait</string>
<string name="no_saved_accounts">No saved accounts</string> <string name="no_saved_accounts">No saved accounts</string>
<string name="mc_download_failed">Failed to download Minecraft! This could be due to bad network connection, incorrectly placed files, etc..</string> <string name="mc_download_failed">Failed to download Minecraft! This could be due to bad network connection, incorrectly placed files, trying to download Minecraft from a Local account, etc..</string>
<string name="xerr_unknown">Unknown Xbox Live API error %d</string> <string name="xerr_unknown">Unknown Xbox Live API error %d</string>
<string name="xerr_no_account">You don\'t seem to have an Xbox Live account. Please log in once on https://minecraft.net/ and try again.</string> <string name="xerr_no_account">You don\'t seem to have an Xbox Live account. Please log in once on https://minecraft.net/ and try again.</string>
<string name="xerr_adult_verification">An adult needs to verify your account.</string> <string name="xerr_adult_verification">An adult needs to verify your account.</string>
@ -430,4 +430,5 @@
<string name="bta_installer_untested_versions">Untested BTA versions</string> <string name="bta_installer_untested_versions">Untested BTA versions</string>
<string name="bta_installer_nightly_versions">Nightly BTA versions</string> <string name="bta_installer_nightly_versions">Nightly BTA versions</string>
<string name="newdl_extracting_native_libraries">Extracting native libraries (%d/%d)</string> <string name="newdl_extracting_native_libraries">Extracting native libraries (%d/%d)</string>
<string name="toast_not_available_demo">This is not available in demo mode</string>
</resources> </resources>