1
0
forked from Clones/Controlify
Files
Controlify/src/main/java/dev/isxander/controlify/gui/screen/SubmitUnknownControllerScreen.java
2023-09-26 23:12:55 +01:00

213 lines
8.0 KiB
Java

package dev.isxander.controlify.gui.screen;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import dev.isxander.controlify.Controlify;
import dev.isxander.controlify.controller.Controller;
import dev.isxander.controlify.controller.ControllerType;
import dev.isxander.controlify.hid.HIDDevice;
import dev.isxander.controlify.utils.ClientUtils;
import dev.isxander.controlify.utils.Log;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.*;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.regex.Pattern;
public class SubmitUnknownControllerScreen extends Screen implements DontInteruptScreen {
public static final String SUBMISSION_URL = "https://api-controlify.isxander.dev/api/v1/submit";
public static final Pattern NAME_PATTERN = Pattern.compile("^[\\w\\- ]{3,32}$");
private final Controller<?, ?> controller;
private Checkbox operationalCheckbox;
private final Screen lastScreen;
private boolean invalidName;
private Button submitButton;
private EditBox nameField;
public SubmitUnknownControllerScreen(Controller<?, ?> controller, Screen lastScreen) {
super(Component.translatable("controlify.controller_submission.title").withStyle(ChatFormatting.BOLD));
if (!canSubmit(controller))
throw new IllegalArgumentException("Controller ineligible for submission!");
this.controller = controller;
this.lastScreen = lastScreen;
}
@Override
protected void init() {
var content = this.addRenderableWidget(
new MultiLineTextWidget(
Component.translatable("controlify.controller_submission.message"),
font
)
);
content.setMaxWidth(this.width - 100);
content.setX(this.width / 2 - content.getWidth() / 2);
int titleBottomPadding = 11;
int checkboxPadding = 6;
int checkboxHeight = 20;
int buttonHeight = 20;
int nameFieldPaddingTop = 5;
int nameFieldHeight = 20;
int errorPadding = 4;
int allHeight = font.lineHeight + titleBottomPadding + content.getHeight() + checkboxPadding + checkboxHeight + checkboxPadding + buttonHeight + nameFieldPaddingTop + nameFieldHeight + errorPadding + font.lineHeight;
int y = this.height / 2 - allHeight / 2;
this.addRenderableWidget(ClientUtils.createStringWidget(this.getTitle(), font, 25, y));
y += font.lineHeight + titleBottomPadding;
content.setY(y);
y += content.getHeight() + checkboxPadding;
var operationalText = Component.translatable("controlify.controller_submission.operational_checkbox")
.withStyle(ChatFormatting.BOLD);
this.operationalCheckbox = this.addRenderableWidget(
new Checkbox(
this.width / 2 - font.width(operationalText) / 2 - 8,
y,
150,
checkboxHeight,
operationalText,
true
)
);
y += checkboxHeight + checkboxPadding;
this.submitButton = this.addRenderableWidget(
Button.builder(Component.translatable("controlify.controller_submission.submit"), this::onSubmitButton)
.pos(this.width / 2 - 155, y)
.width(150)
.build()
);
this.addRenderableWidget(
Button.builder(Component.translatable("controlify.controller_submission.skip"), btn -> onClose())
.pos(this.width / 2 + 5, y)
.width(150)
.build()
);
y += buttonHeight + nameFieldPaddingTop;
this.nameField = this.addRenderableWidget(
new EditBox(
font,
this.width / 2 - 155,
y,
310, 20,
Component.translatable("controlify.controller_submission.name_narration")
)
);
this.nameField.setHint(Component.translatable("controlify.controller_submission.name_hint"));
this.nameField.setValue(controller.name());
this.nameField.setFilter(s -> {
invalidName = !checkValidName(s);
submitButton.active = !invalidName;
return true;
});
}
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
renderBackground(graphics, mouseX, mouseY, delta);
super.render(graphics, mouseX, mouseY, delta);
if (invalidName) {
graphics.drawCenteredString(font, Component.translatable("controlify.controller_submission.invalid_name").withStyle(ChatFormatting.RED), this.width / 2, nameField.getRectangle().bottom() + 4, -1);
}
}
protected void onSubmitButton(Button button) {
if (submit()) {
dontShowAgain();
onClose();
} else {
// TODO: Show error message
dontShowAgain();
onClose();
}
}
protected boolean submit() {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(SUBMISSION_URL))
.POST(HttpRequest.BodyPublishers.ofString(this.generateRequestBody()))
.header("Content-Type", "application/json")
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() / 100 != 2) {
Log.LOGGER.error("Received non-2xx status code from '{}', got {} with body '{}'", SUBMISSION_URL, response.statusCode(), response.body());
return false;
}
Log.LOGGER.info("Successfully sent controller information to '{}'", SUBMISSION_URL);
return true;
} catch (Exception e) {
Log.LOGGER.error("Failed to submit controller to '%s'".formatted(SUBMISSION_URL), e);
return false;
}
}
private String generateRequestBody() {
HIDDevice hid = controller.hidInfo().orElseThrow().hidDevice().orElseThrow();
JsonObject object = new JsonObject();
object.addProperty("vendorID", hid.vendorID());
object.addProperty("productID", hid.productID());
object.addProperty("GUID", GLFW.glfwGetJoystickGUID(controller.joystickId()));
object.addProperty("reportedName", nameField.getValue());
object.addProperty("controlifyVersion", FabricLoader.getInstance().getModContainer("controlify").orElseThrow().getMetadata().getVersion().getFriendlyString());
object.addProperty("operational", operationalCheckbox.selected());
Gson gson = new Gson();
return gson.toJson(object);
}
private boolean checkValidName(String name) {
return NAME_PATTERN.matcher(name.trim()).matches();
}
private void dontShowAgain() {
controller.config().dontShowControllerSubmission = true;
Controlify.instance().config().setDirty();
}
@Override
public void onClose() {
dontShowAgain();
Controlify.instance().config().saveIfDirty();
minecraft.setScreen(lastScreen);
}
@Override
public boolean shouldCloseOnEsc() {
return false;
}
public static boolean canSubmit(Controller<?, ?> controller) {
return controller.type() == ControllerType.UNKNOWN
&& !controller.config().dontShowControllerSubmission
&& controller.hidInfo()
.map(info -> info.hidDevice().isPresent())
.orElse(false);
}
}