Skip to content

Commit

Permalink
Feat[launcher]: add support for the "Better than Adventure!" mod
Browse files Browse the repository at this point in the history
  • Loading branch information
artdeell committed Feb 4, 2025
1 parent 3d256a2 commit ee3435d
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public static void launchMinecraft(final AppCompatActivity activity, MinecraftAc
}
javaArgList.addAll(Arrays.asList(getMinecraftJVMArgs(versionId, gamedir)));
javaArgList.add("-cp");
javaArgList.add(getLWJGL3ClassPath() + ":" + launchClassPath);
javaArgList.add(launchClassPath + ":" + getLWJGL3ClassPath());

javaArgList.add(versionInfo.mainClass);
javaArgList.addAll(Arrays.asList(launchArgs));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package net.kdt.pojavlaunch.fragments;

import android.content.Context;
import android.view.LayoutInflater;
import android.widget.ExpandableListAdapter;

import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.modloaders.BTADownloadTask;
import net.kdt.pojavlaunch.modloaders.BTAUtils;
import net.kdt.pojavlaunch.modloaders.BTAVersionListAdapter;
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;

import java.io.File;
import java.io.IOException;

public class BTAInstallFragment extends ModVersionListFragment<BTAUtils.BTAVersionList> {
private static ModloaderListenerProxy sTaskProxy;
@Override
public int getTitleText() {
return R.string.select_bta_version;
}

@Override
public int getNoDataMsg() {
return R.string.modloader_dl_failed_to_load_list;
}

@Override
public ModloaderListenerProxy getTaskProxy() {
return sTaskProxy;
}

@Override
public BTAUtils.BTAVersionList loadVersionList() throws IOException {
return BTAUtils.downloadVersionList();
}

@Override
public void setTaskProxy(ModloaderListenerProxy proxy) {
sTaskProxy = proxy;
}

@Override
public ExpandableListAdapter createAdapter(BTAUtils.BTAVersionList versionList, LayoutInflater layoutInflater) {
return new BTAVersionListAdapter(versionList, layoutInflater);
}

@Override
public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) {
return new BTADownloadTask(listenerProxy, (BTAUtils.BTAVersion) selectedVersion);
}

@Override
public void onDownloadFinished(Context context, File downloadedFile) {
// We don't have to do anything after the BTADownloadTask ends, so this is a stub
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
Tools.swapFragment(requireActivity(), SearchModFragment.class, SearchModFragment.TAG, null));
view.findViewById(R.id.modded_profile_quilt).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), QuiltInstallFragment.class, QuiltInstallFragment.TAG, null));
view.findViewById(R.id.modded_profile_bta).setOnClickListener((v)->
Tools.swapFragment(requireActivity(), BTAInstallFragment.class, BTAInstallFragment.TAG, null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package net.kdt.pojavlaunch.modloaders;

import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.Log;

import com.kdt.mcgui.ProgressLayout;

import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import net.kdt.pojavlaunch.utils.FileUtils;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class BTADownloadTask implements Runnable {
private static final String BASE_JSON = "{\"inheritsFrom\":\"b1.7.3\",\"mainClass\":\"net.minecraft.client.Minecraft\",\"libraries\":[{\"name\":\"bta-client:bta-client:%1$s\",\"downloads\":{\"artifact\":{\"path\":\"bta-client/bta-client-%1$s.jar\",\"url\":\"%2$s\"}}}],\"id\":\"%3$s\"}";
private final ModloaderDownloadListener mListener;
private final BTAUtils.BTAVersion mBtaVersion;

public BTADownloadTask(ModloaderDownloadListener mListener, BTAUtils.BTAVersion mBtaVersion) {
this.mListener = mListener;
this.mBtaVersion = mBtaVersion;
}

@Override
public void run() {
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.fabric_dl_progress, "BTA");
try {
runCatching() ;
mListener.onDownloadFinished(null);
}catch (IOException e) {
mListener.onDownloadError(e);
}
ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK);
}

private String tryDownloadIcon() {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (Base64OutputStream base64OutputStream = new Base64OutputStream(byteArrayOutputStream, Base64.DEFAULT)){
// Instead of appending and wasting memory with a StringBuilder, just write the prefix
// to the stream before the base64 icon data.
byteArrayOutputStream.write("data:image/png;base64,".getBytes(StandardCharsets.US_ASCII));
DownloadUtils.download(mBtaVersion.iconUrl, base64OutputStream);
return new String(byteArrayOutputStream.toByteArray(), StandardCharsets.US_ASCII);
}catch (IOException e) {
Log.w("BTADownloadTask", "Failed to download base64 icon", e);
}finally {
try {
byteArrayOutputStream.close();
} catch (IOException e) {
Log.wtf("BTADownloadTask", "Failed to close a byte array stream??", e);
}
}
return null;
}

private void createJson(String btaVersionId) throws IOException {
String btaJson = String.format(BASE_JSON, mBtaVersion.versionName, mBtaVersion.downloadUrl, btaVersionId);
File jsonDir = new File(Tools.DIR_HOME_VERSION, btaVersionId);
File jsonFile = new File(jsonDir, btaVersionId+".json");
FileUtils.ensureDirectory(jsonDir);
Tools.write(jsonFile.getAbsolutePath(), btaJson);
}

// BTA doesn't have SHA1 checksums in its repositories, so the user may try to reinstall it
// if it didn't work due to a broken download. So, for reinstalls like that to work,
// we need to delete the old client jar to force the download of a new one.
private void removeOldClient() throws IOException{
File btaClientPath = new File(Tools.DIR_HOME_LIBRARY, String.format("bta-client/bta-client-%1$s.jar", mBtaVersion.versionName));
if(btaClientPath.exists() && !btaClientPath.delete())
throw new IOException("Failed to delete old client jar");
}

private void createProfile(String btaVersionId) throws IOException {
LauncherProfiles.load();
MinecraftProfile btaProfile = new MinecraftProfile();
btaProfile.lastVersionId = btaVersionId;
btaProfile.name = "Better than Adventure!";
btaProfile.icon = tryDownloadIcon();
LauncherProfiles.insertMinecraftProfile(btaProfile);
LauncherProfiles.write();
}

public void runCatching() throws IOException {
removeOldClient();
String btaVersionId = "bta-"+mBtaVersion.versionName;
createJson(btaVersionId);
createProfile(btaVersionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package net.kdt.pojavlaunch.modloaders;

import android.util.Log;

import androidx.annotation.Keep;

import com.google.gson.JsonParseException;
import com.google.gson.annotations.SerializedName;

import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.utils.DownloadUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class BTAUtils {
private static final String BTA_CLIENT_URL = "https://downloads.betterthanadventure.net/bta-client/release/%s/client.jar";
private static final String BTA_ICON_URL = "https://downloads.betterthanadventure.net/bta-client/release/%s/auto/%s.png";
private static final List<String> BTA_TESTED_VERSIONS = new ArrayList<>();
static {
BTA_TESTED_VERSIONS.add("v7.3");
BTA_TESTED_VERSIONS.add("v7.2_01");
BTA_TESTED_VERSIONS.add("v7.2");
BTA_TESTED_VERSIONS.add("v7.1_01");
BTA_TESTED_VERSIONS.add("v7.1");
}

private static String getIconUrl(String version) {
String versionUnderscore = version.replace('.','_');
return String.format(BTA_ICON_URL, version, versionUnderscore);
}

private static List<BTAVersion> createVersionList(List<String> versionStrings) {
ListIterator<String> iterator = versionStrings.listIterator(versionStrings.size());
ArrayList<BTAVersion> btaVersions = new ArrayList<>(versionStrings.size());
while(iterator.hasPrevious()) {
String version = iterator.previous();
btaVersions.add(new BTAVersion(
version,
String.format(BTA_CLIENT_URL, version),
getIconUrl(version)
));
}
btaVersions.trimToSize();
return btaVersions;
}

private static BTAVersionList processReleasesJson(String releasesInfo) throws JsonParseException {
BTAVersionsManifest manifest = Tools.GLOBAL_GSON.fromJson(releasesInfo, BTAVersionsManifest.class);
List<String> stringVersions = manifest.versions;
List<String> testedVersions = new ArrayList<>();
List<String> untestedVersions = new ArrayList<>();
for(String version : stringVersions) {
if(version == null) break;
if(BTA_TESTED_VERSIONS.contains(version)) {
testedVersions.add(version);
}else {
untestedVersions.add(version);
}
}

return new BTAVersionList(
createVersionList(testedVersions),
createVersionList(untestedVersions)
);
}

public static BTAVersionList downloadVersionList() throws IOException {
try {
return DownloadUtils.downloadStringCached(
"https://downloads.betterthanadventure.net/bta-client/release/versions.json",
"bta_releases", BTAUtils::processReleasesJson);
}catch (DownloadUtils.ParseException e) {
Log.e("BTAUtils", "Failed to process json", e);
return null;
}
}

private static class BTAVersionsManifest {
@Keep
public List<String> versions;
@Keep
@SerializedName("default")
public String defaultVersion;
}

public static class BTAVersion {
public final String versionName;
public final String downloadUrl;
public final String iconUrl;

public BTAVersion(String versionName, String downloadUrl, String iconUrl) {
this.versionName = versionName;
this.downloadUrl = downloadUrl;
this.iconUrl = iconUrl;
}
}
public static class BTAVersionList {
public final List<BTAVersion> testedVersions;
public final List<BTAVersion> untestedVersions;

public BTAVersionList(List<BTAVersion> mTestedVersions, List<BTAVersion> mUntestedVersions) {
this.testedVersions = mTestedVersions;
this.untestedVersions = mUntestedVersions;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package net.kdt.pojavlaunch.modloaders;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.TextView;

import net.kdt.pojavlaunch.R;

import java.util.ArrayList;
import java.util.List;

public class BTAVersionListAdapter extends BaseExpandableListAdapter implements ExpandableListAdapter{
private final LayoutInflater mLayoutInflater;
private final ArrayList<String> mGroupNames;
private final ArrayList<List<BTAUtils.BTAVersion>> mGroups;

public BTAVersionListAdapter(BTAUtils.BTAVersionList versionList, LayoutInflater mLayoutInflater) {
this.mLayoutInflater = mLayoutInflater;
Context context = mLayoutInflater.getContext();
mGroupNames = new ArrayList<>(2);
mGroups = new ArrayList<>(2);
if(!versionList.testedVersions.isEmpty()) {
mGroupNames.add(context.getString(R.string.bta_installer_available_versions));
mGroups.add(versionList.testedVersions);
}
if(!versionList.untestedVersions.isEmpty()) {
mGroupNames.add(context.getString(R.string.bta_installer_untested_versions));
mGroups.add(versionList.untestedVersions);
}
mGroupNames.trimToSize();
mGroups.trimToSize();
}

@Override
public int getGroupCount() {
return mGroups.size();
}

@Override
public int getChildrenCount(int i) {
return mGroups.get(i).size();
}

@Override
public Object getGroup(int i) {
return mGroupNames.get(i);
}

@Override
public Object getChild(int i, int i1) {
return mGroups.get(i).get(i1);
}

@Override
public long getGroupId(int i) {
return i;
}

@Override
public long getChildId(int i, int i1) {
return i1;
}

@Override
public boolean hasStableIds() {
return true;
}

@Override
public View getGroupView(int i, boolean b, View convertView, ViewGroup viewGroup) {
if(convertView == null)
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);

((TextView) convertView).setText((String)getGroup(i));

return convertView;
}

@Override
public View getChildView(int i, int i1, boolean b, View convertView, ViewGroup viewGroup) {
if(convertView == null)
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
((TextView) convertView).setText(((BTAUtils.BTAVersion)getChild(i,i1)).versionName);
return convertView;
}

@Override
public boolean isChildSelectable(int i, int i1) {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@
android:layout_marginTop="@dimen/padding_large"
android:text="@string/modpack_install_button" />

<com.kdt.mcgui.MineButton
android:id="@+id/modded_profile_bta"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/padding_large"
android:layout_marginTop="@dimen/padding_large"
android:text="@string/create_bta_profile" />

<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
Expand Down
Loading

0 comments on commit ee3435d

Please sign in to comment.