Skip to content

Commit

Permalink
v3.12 dev. progress
Browse files Browse the repository at this point in the history
Early prototype for quick-share GUI. Missing error handlers for now.
  • Loading branch information
TheCSDev committed Jul 1, 2024
1 parent 5c65345 commit f7da850
Show file tree
Hide file tree
Showing 16 changed files with 352 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

public class BetterStatsConfig extends AutoConfig
{
// ==================================================
private static @NonSerialized boolean FULL_VERSION = false;
// ==================================================
public static @NonSerialized boolean DEBUG_MODE = false;
// --------------------------------------------------
Expand All @@ -22,20 +20,5 @@ public class BetterStatsConfig extends AutoConfig
public @SerializedAs("server-sasConfig") SASConfig sasConfig = new SASConfig();
// ==================================================
public BetterStatsConfig(String name) { super(name); }
static
{
//check for the "full version" file's presence
try
{
final var s = BetterStats.class.getResourceAsStream("/betterstats.full.txt");
if(s != null) { s.close(); FULL_VERSION = true; }
}
catch(Exception e) { FULL_VERSION = true; }
}
// ==================================================
/**
* Returns {@code true} if all features should always be available.
*/
public final boolean isFullVersion() { return FULL_VERSION || this.forceFullVersion; }
// ==================================================
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.time.Instant;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
Expand All @@ -24,8 +25,10 @@

import io.github.thecsdev.betterstats.api.client.gui.screen.BetterStatsScreen;
import io.github.thecsdev.betterstats.api.util.io.RAMStatsProvider;
import io.github.thecsdev.betterstats.util.BST;
import io.github.thecsdev.betterstats.util.io.BetterStatsWebApiUtils;
import io.github.thecsdev.tcdcommons.api.util.TextUtils;
import io.github.thecsdev.tcdcommons.api.client.gui.other.TLabelElement;
import io.github.thecsdev.tcdcommons.api.util.enumerations.HorizontalAlignment;
import io.github.thecsdev.tcdcommons.api.util.io.HttpUtils.FetchOptions;
import io.github.thecsdev.tcdcommons.api.util.io.cache.CachedResource;
import io.github.thecsdev.tcdcommons.api.util.io.cache.CachedResourceManager;
Expand All @@ -39,16 +42,18 @@
public class QuickShareDownloadScreen extends QuickShareScreen
{
// ==================================================
private @Internal final String quickShareCode;
private @Nullable Screen bssParent;
private final String quickShareCode;
// --------------------------------------------------
private @Internal volatile boolean __started = false;
private @Internal volatile int __stage = 0;
private @Internal volatile Throwable __error = null;
// ==================================================
public QuickShareDownloadScreen(@Nullable Screen parent, String quickShareCode)
public QuickShareDownloadScreen(@Nullable Screen bssParent, @Nullable Screen parent, String quickShareCode)
throws NullPointerException
{
super(parent, TextUtils.translatable("betterstats.gui.qs_screen.download.title"));
super(parent, BST.gui_qsscreen_download_title());
this.bssParent = bssParent;
Objects.requireNonNull(quickShareCode);
quickShareCode = quickShareCode.toLowerCase(Locale.ENGLISH);
if(!quickShareCode.endsWith(QSC_SUFFIX)) quickShareCode += QSC_SUFFIX;
Expand All @@ -60,7 +65,22 @@ public QuickShareDownloadScreen(@Nullable Screen parent, String quickShareCode)
//start the operation
__start__stage1();

//FIXME - IMPLEMENT GUI
//the primary label
final var lbl = new TLabelElement(0, 0, getWidth(), getHeight());
lbl.setTextHorizontalAlignment(HorizontalAlignment.CENTER);
lbl.setTextColor(0xffffff00);
addChild(lbl, false);

//the primary label text
switch(this.__stage)
{
case 0: lbl.setText(BST.gui_qsscreen_download_stage0()); break;
case 1: lbl.setText(BST.gui_qsscreen_download_stage1()); break;
case 2: lbl.setText(BST.gui_qsscreen_download_stage2()); break;
case 3: lbl.setText(BST.gui_qsscreen_download_stage3()); break;
case 4: lbl.setText(BST.gui_qsscreen_download_stage4()); break;
default: break;
}
}
// ==================================================
private @Internal void __start_onError(@Nullable Exception exception)
Expand All @@ -82,12 +102,11 @@ public QuickShareDownloadScreen(@Nullable Screen parent, String quickShareCode)

//fetch the API links
BetterStatsWebApiUtils.fetchBssApiLinksAsync(MC_CLIENT,
json -> __start__stage2(json),
json -> __start__stage2and3(json),
error -> __start_onError(error));
}


private @Internal void __start__stage2(final JsonObject links)
private @Internal void __start__stage2and3(final JsonObject links)
{
//prepare
this.__stage = 2;
Expand All @@ -106,47 +125,79 @@ public QuickShareDownloadScreen(@Nullable Screen parent, String quickShareCode)
public final @Override CachedResource<byte[]> fetchResourceSync() throws Exception
{
//fetch the download link
@Nullable JsonObject downloadUrlData = null;
@Nullable CloseableHttpResponse response = null;
try
final AtomicReference<JsonObject> du_ready = new AtomicReference<JsonObject>();
final AtomicReference<Exception> du_error = new AtomicReference<Exception>();
CachedResourceManager.getResourceSync( //NOTE: MUST BE SYNCHRONOUS!
Identifier.of(
getModID(),
"quick_share/download_urls/" +
QuickShareDownloadScreen.this.quickShareCode + ".json"),
new IResourceFetchTask<JsonObject>()
{
//perform the http request
response = fetchSync(links.get("quickshare_gdu").getAsString(), new FetchOptions()
public final @Override ThreadExecutor<?> getMinecraftClientOrServer() { return MC_CLIENT; }
public final @Override Class<JsonObject> getResourceType() { return JsonObject.class; }
public final @Override CachedResource<JsonObject> fetchResourceSync() throws Exception
{
public final @Override String method() { return "POST"; }
public final @Override Object body()
//check if the API is available
if(!links.has("quickshare_gdu"))
throw new NullPointerException("Quick-share download API is unavailable.");

//perform the request
@Nullable CloseableHttpResponse response = null;
try
{
final var json = new JsonObject();
json.addProperty("file", QuickShareDownloadScreen.this.quickShareCode);
return json;
response = fetchSync(links.get("quickshare_gdu").getAsString(), new FetchOptions()
{
public final @Override String method() { return "POST"; }
public final @Override Object body()
{
final var json = new JsonObject();
json.addProperty("file", QuickShareDownloadScreen.this.quickShareCode);
return json;
}
});

//collect the response message
String responseMessage = "";
if(response.getEntity() != null)
responseMessage = EntityUtils.toString(response.getEntity());

//handle the response status code
final var statusCode = response.getStatusLine().getStatusCode();
final var statusMessage = response.getStatusLine().getReasonPhrase();
if(statusCode != 200)
throw new HttpException(
"BSS API server response message:\n----------\n" + responseMessage + "\n----------",
new HttpResponseException(statusCode, statusMessage));

//parse the response
final var json = GSON.fromJson(responseMessage, JsonObject.class);
@Nullable Instant expires = null;
try { expires = Instant.parse(json.get("expires").getAsString()); }
catch(Exception parseExc) { expires = Instant.now().plusSeconds(30); }

//return the response json
return new CachedResource<JsonObject>(json, responseMessage.length(), expires);
}
});

//collect the response message
String responseMessage = "";
if(response.getEntity() != null)
responseMessage = EntityUtils.toString(response.getEntity());

//handle the response status code
final var statusCode = response.getStatusLine().getStatusCode();
final var statusMessage = response.getStatusLine().getReasonPhrase();
if(statusCode != 200)
throw new HttpException(
"BSS API server response message:\n----------\n" + responseMessage + "\n----------",
new HttpResponseException(statusCode, statusMessage));

//parse the response json
downloadUrlData = GSON.fromJson(responseMessage, JsonObject.class);
}
finally { if(response != null) IOUtils.closeQuietly(response); }
finally { if(response != null) IOUtils.closeQuietly(response); }
}
public final @Override void onError(Exception error) { du_error.set(error); }
public final @Override void onReady(JsonObject result) { du_ready.set(result); }
});

//handle the download link fetch outcome
if(du_error.get() != null)
throw du_error.get();
else if(du_ready.get() == null)
throw new NullPointerException("Failed to obtain quick-share download URL.");

//prepare to download the MCBS file
QuickShareDownloadScreen.this.__stage = 3;
QuickShareDownloadScreen.this.refresh();

final var downloadUrlData = du_ready.get();
final var url = downloadUrlData.get("url").getAsString();
final var method = downloadUrlData.get("method").getAsString();
//final var expires = Instant.parse(downloadUrlData.get("expires").getAsString()); -- download url expiration is ignored for now
final var method = downloadUrlData.get("method").getAsString().toUpperCase(Locale.ENGLISH);
final var headers = downloadUrlData.get("headers").getAsJsonObject()
.entrySet().stream()
.map(entry -> new BasicHeader(entry.getKey(), entry.getValue().getAsString()))
Expand All @@ -158,6 +209,7 @@ public QuickShareDownloadScreen(@Nullable Screen parent, String quickShareCode)
" to download the quick-share file, but I only support HTTP GET.");

//perform the download
@Nullable CloseableHttpResponse response = null;
try
{
//fetch
Expand Down Expand Up @@ -193,24 +245,26 @@ public QuickShareDownloadScreen(@Nullable Screen parent, String quickShareCode)
//finally, conclude
@Nullable Instant expires_file = null;
try { expires_file = Instant.parse(downloadUrlData.get("expires_file").getAsString()); }
catch(Exception parseExc) { expires_file = Instant.now().plus(Duration.ofHours(48)); }
catch(Exception parseExc) { expires_file = Instant.now().plus(Duration.ofDays(1)); }

return new CachedResource<byte[]>(responseBody, responseBody.length, expires_file);
}
finally { if(response != null) IOUtils.closeQuietly(response); }
}
public final @Override void onError(Exception error) { __start_onError(error); }
public final @Override void onReady(byte[] result) { __start__stage3(result); }
public final @Override void onReady(byte[] result) { __start__stage4(result); }
});
}

private @Internal void __start__stage3(final byte[] mcbs)
private @Internal void __start__stage4(final byte[] mcbs)
{
this.__stage = 4;
refresh();
try
{
final var buffer = new PacketByteBuf(Unpooled.wrappedBuffer(mcbs));
final var stats = new RAMStatsProvider(buffer, true);
final var bss = new BetterStatsScreen(null, stats);
final var bss = new BetterStatsScreen(this.bssParent, stats);
MC_CLIENT.setScreen(bss.getAsScreen());
}
catch(Exception exc) { __start_onError(exc); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ public QuickShareScreen(@Nullable Screen parent, Text title)
}
// --------------------------------------------------
public final @Override Screen getParentScreen() { return this.parent; }
public @Virtual @Override void close() { MC_CLIENT.setScreen(getParentScreen()); }
// ==================================================
protected final void refresh() { MC_CLIENT.executeSync(() -> { if(!isOpen()) return; clearChildren(); init(); }); }
// --------------------------------------------------
public @Virtual @Override void renderBackground(TDrawContext pencil) { pencil.drawTFill(COLOR_BACKGROUND); }
public @Virtual @Override void renderBackground(TDrawContext pencil)
{
super.renderBackground(pencil);
pencil.drawTFill(COLOR_BACKGROUND);
}
// ==================================================
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.github.thecsdev.betterstats.client.gui.screen;

import static io.github.thecsdev.tcdcommons.api.util.TextUtils.translatable;
import static io.github.thecsdev.tcdcommons.api.util.TextUtils.literal;
import static io.github.thecsdev.betterstats.BetterStats.getModID;
import static io.github.thecsdev.betterstats.client.BetterStatsClient.MC_CLIENT;
import static io.github.thecsdev.betterstats.util.io.BetterStatsWebApiUtils.GSON;
Expand All @@ -12,6 +14,7 @@
import java.util.Objects;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.client.HttpResponseException;
Expand All @@ -28,8 +31,11 @@

import io.github.thecsdev.betterstats.api.util.io.IStatsProvider;
import io.github.thecsdev.betterstats.api.util.io.StatsProviderIO;
import io.github.thecsdev.betterstats.util.BST;
import io.github.thecsdev.betterstats.util.io.BetterStatsWebApiUtils;
import io.github.thecsdev.tcdcommons.api.util.TextUtils;
import io.github.thecsdev.tcdcommons.api.client.gui.other.TLabelElement;
import io.github.thecsdev.tcdcommons.api.client.gui.widget.TButtonWidget;
import io.github.thecsdev.tcdcommons.api.util.enumerations.HorizontalAlignment;
import io.github.thecsdev.tcdcommons.api.util.io.HttpUtils.FetchOptions;
import io.github.thecsdev.tcdcommons.api.util.io.cache.CachedResource;
import io.github.thecsdev.tcdcommons.api.util.io.cache.CachedResourceManager;
Expand All @@ -38,6 +44,7 @@
import io.netty.buffer.Unpooled;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.thread.ThreadExecutor;

Expand All @@ -56,7 +63,7 @@ public final class QuickShareUploadScreen extends QuickShareScreen
public QuickShareUploadScreen(@Nullable Screen parent, IStatsProvider stats)
throws NullPointerException
{
super(parent, TextUtils.translatable("betterstats.gui.qs_screen.upload.title"));
super(parent, BST.gui_qsscreen_upload_title());
this.stats = Objects.requireNonNull(stats);
}
// ==================================================
Expand All @@ -65,7 +72,34 @@ public QuickShareUploadScreen(@Nullable Screen parent, IStatsProvider stats)
//start the operation
__start__stage1();

//FIXME - IMPLEMENT GUI
//the primary label
final var lbl = new TLabelElement(0, 0, getWidth(), getHeight());
lbl.setTextHorizontalAlignment(HorizontalAlignment.CENTER);
lbl.setTextColor(0xffffff00);
addChild(lbl, false);

//the primary label text
switch(this.__stage)
{
case 0: lbl.setText(BST.gui_qsscreen_upload_stage0()); break;
case 1: lbl.setText(BST.gui_qsscreen_upload_stage1()); break;
case 2: lbl.setText(BST.gui_qsscreen_upload_stage2()); break;
case 3: lbl.setText(BST.gui_qsscreen_upload_stage3()); break;
case 4:
//set final stage label text
final var codeStr = StringUtils.removeEnd("" + this.__quickShareCode, QSC_SUFFIX)
.toUpperCase(Locale.ENGLISH);
final var codeTxt = literal(codeStr).formatted(Formatting.WHITE);
lbl.setText(BST.gui_qsscreen_upload_stage4(codeTxt));

//add a "Done" button
final var btn_done = new TButtonWidget((getWidth() / 2) - 75, getHeight() - 30, 150, 20);
btn_done.setText(translatable("gui.done"));
btn_done.setOnClick(__ -> close());
addChild(btn_done, false);
break;
default: break;
}
}
// ==================================================
private @Internal void __start_onError(@Nullable Exception exception)
Expand Down Expand Up @@ -98,7 +132,7 @@ public QuickShareUploadScreen(@Nullable Screen parent, IStatsProvider stats)

//fetch the upload link
CachedResourceManager.getResourceAsync(
Identifier.of(getModID(), "quick_share/upload_url.json"),
Identifier.of(getModID(), "quick_share/latest_upload_url.json"),
new IResourceFetchTask<JsonObject>()
{
public ThreadExecutor<?> getMinecraftClientOrServer() { return MC_CLIENT; }
Expand Down
Loading

0 comments on commit f7da850

Please sign in to comment.