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.AsyncVersionList;
import net.kdt.pojavlaunch.tasks.MinecraftDownloader;
import net.kdt.pojavlaunch.utils.DateUtils;
import net.kdt.pojavlaunch.utils.NotificationUtils;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
import java.lang.ref.WeakReference;
import java.text.ParseException;
public class LauncherActivity extends BaseActivity {
public static final String SETTING_FRAGMENT_TAG = "SETTINGS_FRAGMENT";
@ -125,6 +127,23 @@ public class LauncherActivity extends BaseActivity {
}
String normalizedVersionId = AsyncMinecraftDownloader.normalizeVersionId(prof.lastVersionId);
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(
this,
mcVersion,

View File

@ -118,6 +118,7 @@ public final class Tools {
public static String DIR_ACCOUNT_NEW;
public static String DIR_GAME_HOME = Environment.getExternalStorageDirectory().getAbsolutePath() + "/games/PojavLauncher";
public static String DIR_GAME_NEW;
public static String GAME_PROFILES_FILE;
// New since 3.0.0
public static String DIRNAME_HOME_JRE = "lib";
@ -200,6 +201,8 @@ public final class Tools {
OBSOLETE_RESOURCES_PATH = DIR_GAME_NEW + "/resources";
CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap";
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) {
String username = profile.username;
String username = profile.username.replace("Demo.", "");
String versionName = versionInfo.id;
if (versionInfo.inheritsFrom != null) {
versionName = versionInfo.inheritsFrom;
@ -506,13 +509,13 @@ public final class Tools {
}
}
return JSONUtils.insertJSONValueList(
splitAndFilterEmpty(
versionInfo.minecraftArguments == null ?
fromStringArray(minecraftArgs.toArray(new String[0])):
versionInfo.minecraftArguments
), varArgMap
);
String mcArguments = versionInfo.minecraftArguments == null ?
fromStringArray(minecraftArgs.toArray(new String[0])):
versionInfo.minecraftArguments;
if(profile.isDemo()) mcArguments += " --demo";
return JSONUtils.insertJSONValueList(splitAndFilterEmpty(mcArguments), varArgMap);
}
public static String fromStringArray(String[] strArr) {
@ -1413,4 +1416,26 @@ public final class Tools {
}
}).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,17 +87,15 @@ public class MicrosoftBackgroundLogin {
MinecraftAccount acc = MinecraftAccount.load(mcName);
if(acc == null) acc = new MinecraftAccount();
if (doesOwnGame) {
acc.xuid = xsts[0];
acc.clientToken = "0"; /* FIXME */
acc.accessToken = mcToken;
acc.username = mcName;
acc.profileId = mcUuid;
acc.isMicrosoft = true;
acc.msaRefreshToken = msRefreshToken;
acc.expiresAt = expiresAt;
acc.updateSkinFace();
}
acc.xuid = xsts[0];
acc.clientToken = "0"; /* FIXME */
acc.accessToken = mcToken;
acc.username = mcName;
acc.profileId = mcUuid;
acc.isMicrosoft = true;
acc.msaRefreshToken = msRefreshToken;
acc.expiresAt = expiresAt;
acc.updateSkinFace();
acc.save();
if(doneListener != null) {
@ -298,7 +296,9 @@ public class MicrosoftBackgroundLogin {
}else{
Log.i("MicrosoftLogin","It seems that this Microsoft Account does not own the game.");
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);
}
}

View File

@ -89,6 +89,12 @@ public class MainMenuFragment extends Fragment {
}
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)
Tools.installMod(requireActivity(), isCustomArgs);
else

View File

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

View File

@ -2,11 +2,13 @@ package net.kdt.pojavlaunch.fragments;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import net.kdt.pojavlaunch.PojavProfile;
import net.kdt.pojavlaunch.R;
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
// the comment in FabricInstallFragment.onDownloadFinished() and amend the code
// in FabricInstallFragment.onDownloadFinished() and ModVersionListFragment.onDownloadFinished()
view.findViewById(R.id.optifine_profile).setOnClickListener(v -> Tools.swapFragment(requireActivity(), OptiFineInstallFragment.class,
OptiFineInstallFragment.TAG, null));
view.findViewById(R.id.optifine_profile).setOnClickListener(v ->
tryInstall(OptiFineInstallFragment.class, OptiFineInstallFragment.TAG));
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)->
Tools.swapFragment(requireActivity(), ForgeInstallFragment.class, ForgeInstallFragment.TAG, null));
tryInstall(ForgeInstallFragment.class, ForgeInstallFragment.TAG));
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)->
Tools.swapFragment(requireActivity(), QuiltInstallFragment.class, QuiltInstallFragment.TAG, null));
tryInstall(QuiltInstallFragment.class, QuiltInstallFragment.TAG));
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;
import android.app.Activity;
import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.JMinecraftVersionList;
@ -22,11 +24,13 @@ public class OptiFineDownloadTask implements Runnable, Tools.DownloaderFeedback,
private final ModloaderDownloadListener mListener;
private final Object mMinecraftDownloadLock = new Object();
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.mDestinationFile = new File(Tools.DIR_CACHE, "optifine-installer.jar");
this.mListener = mListener;
this.activity = activity;
}
@Override
@ -89,7 +93,7 @@ public class OptiFineDownloadTask implements Runnable, Tools.DownloaderFeedback,
if(minecraftJsonVersion == null) return false;
try {
synchronized (mMinecraftDownloadLock) {
new MinecraftDownloader().start(null, minecraftJsonVersion, minecraftVersion, this);
new MinecraftDownloader().start(activity, minecraftJsonVersion, minecraftVersion, this);
mMinecraftDownloadLock.wait();
}
}catch (InterruptedException e) {

View File

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

View File

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

View File

@ -16,7 +16,7 @@ import java.util.UUID;
public class LauncherProfiles {
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 */
public static void load(){

View File

@ -252,7 +252,7 @@
<string name="main_add_account">Add account</string>
<string name="tasks_ongoing">Tasks are in progress, please wait</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_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>
@ -430,4 +430,5 @@
<string name="bta_installer_untested_versions">Untested 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="toast_not_available_demo">This is not available in demo mode</string>
</resources>