summary refs log tree commit diff stats
path: root/src/main/java/ganarchy/friendcode/client/WaitingForFriendCodeScreen.java
blob: 79c12f7dd9fc17fb167bf696dc85c822d8ede955 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package ganarchy.friendcode.client;

import ganarchy.friendcode.FriendCode;
import ganarchy.friendcode.util.KeyUtil;
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 net.minecraft.util.WorldSavePath;
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;
    private final CodeType codeType;

    public WaitingForFriendCodeScreen(CodeType codeType, I2PSamPinger samPinger) {
        super(Text.translatable("friendcode.opening"));
        this.codeType = codeType;
        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 -> {
                    if (codeType == CodeType.WORLD) {
                        var worldDir = client.getServer().submit(() -> client.getServer().getSavePath(WorldSavePath.ROOT)).join();
                        var keyFile = worldDir.resolve("friendcode.key");
                        if (!KeyUtil.writeKeyFile(keyFile, samPinger.privateKey())) {
                            FriendCode.LOGGER.error("Couldn't save world code. It will be regenerated next time.");
                        }
                    }
                    // convert from I2P base64 to URL-safe base64 so we don't have to write our own Base64 decoder
                    final String pubkey_b64 = samPinger.publicKey().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;
                }
            }
        }
    }
}