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 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 noTemplatesMessage; public RealmsSelectWorldTemplateScreen(Component title, Consumer callback, WorldType worldType) { this(title, callback, worldType, null); } public RealmsSelectWorldTemplateScreen( Component title, Consumer 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.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 list = Lists.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 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 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 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 { 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 { public WorldTemplateList() { this(Collections.emptyList()); } public WorldTemplateList(final Iterable 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 getTemplates() { return (List)this.children().stream().map(entry -> entry.template).collect(Collectors.toList()); } } }