minecraft-src/com/mojang/realmsclient/gui/screens/RealmsSelectWorldTemplateScreen.java
2025-07-04 03:45:38 +03:00

388 lines
16 KiB
Java

package com.mojang.realmsclient.gui.screens;
import com.google.common.collect.Lists;
import com.mojang.datafixers.util.Either;
import com.mojang.logging.LogUtils;
import com.mojang.realmsclient.client.RealmsClient;
import com.mojang.realmsclient.dto.WorldTemplate;
import com.mojang.realmsclient.dto.WorldTemplatePaginatedList;
import com.mojang.realmsclient.dto.RealmsServer.WorldType;
import com.mojang.realmsclient.exception.RealmsServiceException;
import com.mojang.realmsclient.util.RealmsTextureManager;
import com.mojang.realmsclient.util.TextRenderingUtils;
import com.mojang.realmsclient.util.TextRenderingUtils.Line;
import com.mojang.realmsclient.util.TextRenderingUtils.LineSegment;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.components.ObjectSelectionList;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.components.WidgetSprites;
import net.minecraft.client.gui.layouts.HeaderAndFooterLayout;
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.screens.ConfirmLinkScreen;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.resources.language.I18n;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.realms.RealmsScreen;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.CommonLinks;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class RealmsSelectWorldTemplateScreen extends RealmsScreen {
static final Logger LOGGER = LogUtils.getLogger();
static final ResourceLocation SLOT_FRAME_SPRITE = ResourceLocation.withDefaultNamespace("widget/slot_frame");
private static final Component SELECT_BUTTON_NAME = Component.translatable("mco.template.button.select");
private static final Component TRAILER_BUTTON_NAME = Component.translatable("mco.template.button.trailer");
private static final Component PUBLISHER_BUTTON_NAME = Component.translatable("mco.template.button.publisher");
private static final int BUTTON_WIDTH = 100;
private static final int BUTTON_SPACING = 10;
private final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this);
final Consumer<WorldTemplate> callback;
RealmsSelectWorldTemplateScreen.WorldTemplateList worldTemplateList;
private final WorldType worldType;
private Button selectButton;
private Button trailerButton;
private Button publisherButton;
@Nullable
WorldTemplate selectedTemplate = null;
@Nullable
String currentLink;
@Nullable
private Component[] warning;
@Nullable
List<Line> noTemplatesMessage;
public RealmsSelectWorldTemplateScreen(Component title, Consumer<WorldTemplate> callback, WorldType worldType) {
this(title, callback, worldType, null);
}
public RealmsSelectWorldTemplateScreen(
Component title, Consumer<WorldTemplate> callback, WorldType worldType, @Nullable WorldTemplatePaginatedList worldTemplatePaginatedList
) {
super(title);
this.callback = callback;
this.worldType = worldType;
if (worldTemplatePaginatedList == null) {
this.worldTemplateList = new RealmsSelectWorldTemplateScreen.WorldTemplateList();
this.fetchTemplatesAsync(new WorldTemplatePaginatedList(10));
} else {
this.worldTemplateList = new RealmsSelectWorldTemplateScreen.WorldTemplateList(Lists.<WorldTemplate>newArrayList(worldTemplatePaginatedList.templates));
this.fetchTemplatesAsync(worldTemplatePaginatedList);
}
}
public void setWarning(Component... warning) {
this.warning = warning;
}
@Override
public void init() {
this.layout.addTitleHeader(this.title, this.font);
this.worldTemplateList = this.layout.addToContents(new RealmsSelectWorldTemplateScreen.WorldTemplateList(this.worldTemplateList.getTemplates()));
LinearLayout linearLayout = this.layout.addToFooter(LinearLayout.horizontal().spacing(10));
linearLayout.defaultCellSetting().alignHorizontallyCenter();
this.trailerButton = linearLayout.addChild(Button.builder(TRAILER_BUTTON_NAME, button -> this.onTrailer()).width(100).build());
this.selectButton = linearLayout.addChild(Button.builder(SELECT_BUTTON_NAME, button -> this.selectTemplate()).width(100).build());
linearLayout.addChild(Button.builder(CommonComponents.GUI_CANCEL, button -> this.onClose()).width(100).build());
this.publisherButton = linearLayout.addChild(Button.builder(PUBLISHER_BUTTON_NAME, button -> this.onPublish()).width(100).build());
this.updateButtonStates();
this.layout.visitWidgets(guiEventListener -> {
AbstractWidget var10000 = this.addRenderableWidget(guiEventListener);
});
this.repositionElements();
}
@Override
protected void repositionElements() {
this.worldTemplateList.setSize(this.width, this.height - this.layout.getFooterHeight() - this.getHeaderHeight());
this.layout.arrangeElements();
}
@Override
public Component getNarrationMessage() {
List<Component> list = Lists.<Component>newArrayListWithCapacity(2);
list.add(this.title);
if (this.warning != null) {
list.addAll(Arrays.asList(this.warning));
}
return CommonComponents.joinLines(list);
}
void updateButtonStates() {
this.publisherButton.visible = this.selectedTemplate != null && !this.selectedTemplate.link.isEmpty();
this.trailerButton.visible = this.selectedTemplate != null && !this.selectedTemplate.trailer.isEmpty();
this.selectButton.active = this.selectedTemplate != null;
}
@Override
public void onClose() {
this.callback.accept(null);
}
private void selectTemplate() {
if (this.selectedTemplate != null) {
this.callback.accept(this.selectedTemplate);
}
}
private void onTrailer() {
if (this.selectedTemplate != null && !this.selectedTemplate.trailer.isBlank()) {
ConfirmLinkScreen.confirmLinkNow(this, this.selectedTemplate.trailer);
}
}
private void onPublish() {
if (this.selectedTemplate != null && !this.selectedTemplate.link.isBlank()) {
ConfirmLinkScreen.confirmLinkNow(this, this.selectedTemplate.link);
}
}
private void fetchTemplatesAsync(WorldTemplatePaginatedList output) {
(new Thread("realms-template-fetcher") {
public void run() {
WorldTemplatePaginatedList worldTemplatePaginatedList = output;
RealmsClient realmsClient = RealmsClient.getOrCreate();
while (worldTemplatePaginatedList != null) {
Either<WorldTemplatePaginatedList, Exception> either = RealmsSelectWorldTemplateScreen.this.fetchTemplates(worldTemplatePaginatedList, realmsClient);
worldTemplatePaginatedList = (WorldTemplatePaginatedList)RealmsSelectWorldTemplateScreen.this.minecraft.submit(() -> {
if (either.right().isPresent()) {
RealmsSelectWorldTemplateScreen.LOGGER.error("Couldn't fetch templates", (Throwable)either.right().get());
if (RealmsSelectWorldTemplateScreen.this.worldTemplateList.isEmpty()) {
RealmsSelectWorldTemplateScreen.this.noTemplatesMessage = TextRenderingUtils.decompose(I18n.get("mco.template.select.failure"));
}
return null;
} else {
WorldTemplatePaginatedList worldTemplatePaginatedListx = (WorldTemplatePaginatedList)either.left().get();
for (WorldTemplate worldTemplate : worldTemplatePaginatedListx.templates) {
RealmsSelectWorldTemplateScreen.this.worldTemplateList.addEntry(worldTemplate);
}
if (worldTemplatePaginatedListx.templates.isEmpty()) {
if (RealmsSelectWorldTemplateScreen.this.worldTemplateList.isEmpty()) {
String string = I18n.get("mco.template.select.none", "%link");
LineSegment lineSegment = LineSegment.link(I18n.get("mco.template.select.none.linkTitle"), CommonLinks.REALMS_CONTENT_CREATION.toString());
RealmsSelectWorldTemplateScreen.this.noTemplatesMessage = TextRenderingUtils.decompose(string, lineSegment);
}
return null;
} else {
return worldTemplatePaginatedListx;
}
}
}).join();
}
}
}).start();
}
Either<WorldTemplatePaginatedList, Exception> fetchTemplates(WorldTemplatePaginatedList templates, RealmsClient realmsClient) {
try {
return Either.left(realmsClient.fetchWorldTemplates(templates.page + 1, templates.size, this.worldType));
} catch (RealmsServiceException var4) {
return Either.right(var4);
}
}
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
super.render(guiGraphics, mouseX, mouseY, partialTick);
this.currentLink = null;
if (this.noTemplatesMessage != null) {
this.renderMultilineMessage(guiGraphics, mouseX, mouseY, this.noTemplatesMessage);
}
if (this.warning != null) {
for (int i = 0; i < this.warning.length; i++) {
Component component = this.warning[i];
guiGraphics.drawCenteredString(this.font, component, this.width / 2, row(-1 + i), -6250336);
}
}
}
private void renderMultilineMessage(GuiGraphics guiGraphics, int x, int y, List<Line> lines) {
for (int i = 0; i < lines.size(); i++) {
Line line = (Line)lines.get(i);
int j = row(4 + i);
int k = line.segments.stream().mapToInt(lineSegmentx -> this.font.width(lineSegmentx.renderedText())).sum();
int l = this.width / 2 - k / 2;
for (LineSegment lineSegment : line.segments) {
int m = lineSegment.isLink() ? 3368635 : -1;
int n = guiGraphics.drawString(this.font, lineSegment.renderedText(), l, j, m);
if (lineSegment.isLink() && x > l && x < n && y > j - 3 && y < j + 8) {
this.setTooltipForNextRenderPass(Component.literal(lineSegment.getLinkUrl()));
this.currentLink = lineSegment.getLinkUrl();
}
l = n;
}
}
}
int getHeaderHeight() {
return this.warning != null ? row(1) : 33;
}
@Environment(EnvType.CLIENT)
class Entry extends ObjectSelectionList.Entry<RealmsSelectWorldTemplateScreen.Entry> {
private static final WidgetSprites WEBSITE_LINK_SPRITES = new WidgetSprites(
ResourceLocation.withDefaultNamespace("icon/link"), ResourceLocation.withDefaultNamespace("icon/link_highlighted")
);
private static final WidgetSprites TRAILER_LINK_SPRITES = new WidgetSprites(
ResourceLocation.withDefaultNamespace("icon/video_link"), ResourceLocation.withDefaultNamespace("icon/video_link_highlighted")
);
private static final Component PUBLISHER_LINK_TOOLTIP = Component.translatable("mco.template.info.tooltip");
private static final Component TRAILER_LINK_TOOLTIP = Component.translatable("mco.template.trailer.tooltip");
public final WorldTemplate template;
private long lastClickTime;
@Nullable
private ImageButton websiteButton;
@Nullable
private ImageButton trailerButton;
public Entry(final WorldTemplate template) {
this.template = template;
if (!template.link.isBlank()) {
this.websiteButton = new ImageButton(
15, 15, WEBSITE_LINK_SPRITES, ConfirmLinkScreen.confirmLink(RealmsSelectWorldTemplateScreen.this, template.link), PUBLISHER_LINK_TOOLTIP
);
this.websiteButton.setTooltip(Tooltip.create(PUBLISHER_LINK_TOOLTIP));
}
if (!template.trailer.isBlank()) {
this.trailerButton = new ImageButton(
15, 15, TRAILER_LINK_SPRITES, ConfirmLinkScreen.confirmLink(RealmsSelectWorldTemplateScreen.this, template.trailer), TRAILER_LINK_TOOLTIP
);
this.trailerButton.setTooltip(Tooltip.create(TRAILER_LINK_TOOLTIP));
}
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
RealmsSelectWorldTemplateScreen.this.selectedTemplate = this.template;
RealmsSelectWorldTemplateScreen.this.updateButtonStates();
if (Util.getMillis() - this.lastClickTime < 250L && this.isFocused()) {
RealmsSelectWorldTemplateScreen.this.callback.accept(this.template);
}
this.lastClickTime = Util.getMillis();
if (this.websiteButton != null) {
this.websiteButton.mouseClicked(mouseX, mouseY, button);
}
if (this.trailerButton != null) {
this.trailerButton.mouseClicked(mouseX, mouseY, button);
}
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public void render(GuiGraphics guiGraphics, int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean hovering, float partialTick) {
guiGraphics.blit(
RenderType::guiTextured, RealmsTextureManager.worldTemplate(this.template.id, this.template.image), left + 1, top + 1 + 1, 0.0F, 0.0F, 38, 38, 38, 38
);
guiGraphics.blitSprite(RenderType::guiTextured, RealmsSelectWorldTemplateScreen.SLOT_FRAME_SPRITE, left, top + 1, 40, 40);
int i = 5;
int j = RealmsSelectWorldTemplateScreen.this.font.width(this.template.version);
if (this.websiteButton != null) {
this.websiteButton.setPosition(left + width - j - this.websiteButton.getWidth() - 10, top);
this.websiteButton.render(guiGraphics, mouseX, mouseY, partialTick);
}
if (this.trailerButton != null) {
this.trailerButton.setPosition(left + width - j - this.trailerButton.getWidth() * 2 - 15, top);
this.trailerButton.render(guiGraphics, mouseX, mouseY, partialTick);
}
int k = left + 45 + 20;
int l = top + 5;
guiGraphics.drawString(RealmsSelectWorldTemplateScreen.this.font, this.template.name, k, l, -1);
guiGraphics.drawString(RealmsSelectWorldTemplateScreen.this.font, this.template.version, left + width - j - 5, l, 7105644);
guiGraphics.drawString(RealmsSelectWorldTemplateScreen.this.font, this.template.author, k, l + 9 + 5, -6250336);
if (!this.template.recommendedPlayers.isBlank()) {
guiGraphics.drawString(RealmsSelectWorldTemplateScreen.this.font, this.template.recommendedPlayers, k, top + height - 9 / 2 - 5, 5000268);
}
}
@Override
public Component getNarration() {
Component component = CommonComponents.joinLines(
Component.literal(this.template.name),
Component.translatable("mco.template.select.narrate.authors", this.template.author),
Component.literal(this.template.recommendedPlayers),
Component.translatable("mco.template.select.narrate.version", this.template.version)
);
return Component.translatable("narrator.select", component);
}
}
@Environment(EnvType.CLIENT)
class WorldTemplateList extends ObjectSelectionList<RealmsSelectWorldTemplateScreen.Entry> {
public WorldTemplateList() {
this(Collections.emptyList());
}
public WorldTemplateList(final Iterable<WorldTemplate> templates) {
super(
Minecraft.getInstance(),
RealmsSelectWorldTemplateScreen.this.width,
RealmsSelectWorldTemplateScreen.this.height - 33 - RealmsSelectWorldTemplateScreen.this.getHeaderHeight(),
RealmsSelectWorldTemplateScreen.this.getHeaderHeight(),
46
);
templates.forEach(this::addEntry);
}
public void addEntry(WorldTemplate template) {
this.addEntry(RealmsSelectWorldTemplateScreen.this.new Entry(template));
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
if (RealmsSelectWorldTemplateScreen.this.currentLink != null) {
ConfirmLinkScreen.confirmLinkNow(RealmsSelectWorldTemplateScreen.this, RealmsSelectWorldTemplateScreen.this.currentLink);
return true;
} else {
return super.mouseClicked(mouseX, mouseY, button);
}
}
public void setSelected(@Nullable RealmsSelectWorldTemplateScreen.Entry entry) {
super.setSelected(entry);
RealmsSelectWorldTemplateScreen.this.selectedTemplate = entry == null ? null : entry.template;
RealmsSelectWorldTemplateScreen.this.updateButtonStates();
}
@Override
public int getRowWidth() {
return 300;
}
public boolean isEmpty() {
return this.getItemCount() == 0;
}
public List<WorldTemplate> getTemplates() {
return (List<WorldTemplate>)this.children().stream().map(entry -> entry.template).collect(Collectors.toList());
}
}
}