package ganarchy.friendcode.client;
import net.minecraft.client.gui.screen.LoadingDisplay;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.ScreenTexts;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Util;
import org.apache.commons.codec.binary.Base32;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Locale;
public class WaitingForFriendCodeScreen extends Screen {
private final I2PSamPinger samPinger;
public WaitingForFriendCodeScreen(I2PSamPinger samPinger) {
super(Text.translatable("friendcode.opening"));
this.samPinger = samPinger;
}
@Override
protected void init() {
// cancel friend code.
this.addDrawableChild(new ButtonWidget(
this.width / 2 - 155,
this.height - 28,
310,
20,
ScreenTexts.CANCEL,
button -> this.close()
));
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices);
WaitingForFriendCodeScreen.drawCenteredText(matrices, this.textRenderer, this.title, this.width / 2, 50, 0xFFFFFF);
final String string = LoadingDisplay.get(Util.getMeasuringTimeMs());
this.client.textRenderer.draw(matrices, string, (float)(this.client.currentScreen.width / 2 - this.client.textRenderer.getWidth(string) / 2), (float)(this.client.currentScreen.height / 2 + this.client.textRenderer.fontHeight), 0x808080);
super.render(matrices, mouseX, mouseY, delta);
}
@Override
public void close() {
super.close();
this.samPinger.stopSam();
final MutableText text = Text.translatable("friendcode.publish.stopped");
this.client.inGameHud.getChatHud().addMessage(text);
}
@Override
public void tick() {
super.tick();
if (samPinger.status() != I2PSamPinger.Status.IDLE) {
this.client.setScreen(null);
switch (samPinger.status()) {
case CONNECTION_FAILED -> {
final MutableText text = Text.translatable("friendcode.publish.no_i2p");
this.client.inGameHud.getChatHud().addMessage(text);
break;
}
case SETUP_FAILED, CONNECTION_CLOSED -> {
final MutableText text = Text.translatable("friendcode.publish.failed");
this.client.inGameHud.getChatHud().addMessage(text);
break;
}
case RUNNING -> {
// convert from I2P base64 to URL-safe base64 so we don't have to write our own Base64 decoder
final String pubkey_b64 = samPinger.pubkey().replace('~', '_');
final byte[] pubkey = Base64.getUrlDecoder().decode(pubkey_b64.getBytes(StandardCharsets.UTF_8));
// a .b32.i2p is just a Base32-encoded SHA256
final String b32;
try {
b32 = new Base32().encodeAsString(MessageDigest.getInstance("SHA-256").digest(pubkey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
// but we wanna massage it a little
final String b32_i2p = b32.replaceFirst("=+$", "").toLowerCase(Locale.ROOT) + ".b32.i2p";
final MutableText text = Text.translatable("friendcode.publish.started", b32_i2p);
this.client.inGameHud.getChatHud().addMessage(text);
break;
}
}
}
}
}