package com.mojang.realmsclient.gui.screens; import com.google.common.collect.Lists; import com.mojang.logging.LogUtils; import com.mojang.realmsclient.RealmsMainScreen; import com.mojang.realmsclient.client.RealmsClient; import com.mojang.realmsclient.dto.RealmsServer; import com.mojang.realmsclient.dto.RealmsWorldOptions; import com.mojang.realmsclient.dto.WorldTemplate; import com.mojang.realmsclient.dto.RealmsServer.WorldType; import com.mojang.realmsclient.dto.WorldTemplate.WorldTemplateType; import com.mojang.realmsclient.exception.RealmsServiceException; import com.mojang.realmsclient.gui.RealmsWorldSlotButton; import com.mojang.realmsclient.gui.RealmsWorldSlotButton.State; import com.mojang.realmsclient.util.task.CloseServerTask; import com.mojang.realmsclient.util.task.OpenServerTask; import com.mojang.realmsclient.util.task.SwitchMinigameTask; import com.mojang.realmsclient.util.task.SwitchSlotTask; import java.util.List; import java.util.Objects; import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.renderer.RenderType; 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.StringUtil; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public class RealmsConfigureWorldScreen extends RealmsScreen { private static final ResourceLocation EXPIRED_SPRITE = ResourceLocation.withDefaultNamespace("realm_status/expired"); private static final ResourceLocation EXPIRES_SOON_SPRITE = ResourceLocation.withDefaultNamespace("realm_status/expires_soon"); private static final ResourceLocation OPEN_SPRITE = ResourceLocation.withDefaultNamespace("realm_status/open"); private static final ResourceLocation CLOSED_SPRITE = ResourceLocation.withDefaultNamespace("realm_status/closed"); private static final Logger LOGGER = LogUtils.getLogger(); private static final Component WORLD_LIST_TITLE = Component.translatable("mco.configure.worlds.title"); private static final Component TITLE = Component.translatable("mco.configure.world.title"); private static final Component SERVER_EXPIRED_TOOLTIP = Component.translatable("mco.selectServer.expired"); private static final Component SERVER_EXPIRING_SOON_TOOLTIP = Component.translatable("mco.selectServer.expires.soon"); private static final Component SERVER_EXPIRING_IN_DAY_TOOLTIP = Component.translatable("mco.selectServer.expires.day"); private static final Component SERVER_OPEN_TOOLTIP = Component.translatable("mco.selectServer.open"); private static final Component SERVER_CLOSED_TOOLTIP = Component.translatable("mco.selectServer.closed"); private static final int DEFAULT_BUTTON_WIDTH = 80; private static final int DEFAULT_BUTTON_OFFSET = 5; @Nullable private Component toolTip; private final RealmsMainScreen lastScreen; @Nullable private RealmsServer serverData; private final long serverId; private int leftX; private int rightX; private Button playersButton; private Button settingsButton; private Button subscriptionButton; private Button optionsButton; private Button backupButton; private Button resetWorldButton; private Button switchMinigameButton; private boolean stateChanged; private final List slotButtonList = Lists.newArrayList(); public RealmsConfigureWorldScreen(RealmsMainScreen lastScreen, long serverId) { super(TITLE); this.lastScreen = lastScreen; this.serverId = serverId; } @Override public void init() { if (this.serverData == null) { this.fetchServerData(this.serverId); } this.leftX = this.width / 2 - 187; this.rightX = this.width / 2 + 190; this.playersButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.buttons.players"), button -> this.minecraft.setScreen(new RealmsPlayerScreen(this, this.serverData)) ) .bounds(this.centerButton(0, 3), row(0), 100, 20) .build() ); this.settingsButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.buttons.settings"), button -> this.minecraft.setScreen(new RealmsSettingsScreen(this, this.serverData.clone())) ) .bounds(this.centerButton(1, 3), row(0), 100, 20) .build() ); this.subscriptionButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.buttons.subscription"), button -> this.minecraft.setScreen(new RealmsSubscriptionInfoScreen(this, this.serverData.clone(), this.lastScreen)) ) .bounds(this.centerButton(2, 3), row(0), 100, 20) .build() ); this.slotButtonList.clear(); for (int i = 1; i < 5; i++) { this.slotButtonList.add(this.addSlotButton(i)); } this.switchMinigameButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.buttons.switchminigame"), button -> this.minecraft .setScreen( new RealmsSelectWorldTemplateScreen(Component.translatable("mco.template.title.minigame"), this::templateSelectionCallback, WorldType.MINIGAME) ) ) .bounds(this.leftButton(0), row(13) - 5, 100, 20) .build() ); this.optionsButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.buttons.options"), button -> this.minecraft .setScreen( new RealmsSlotOptionsScreen( this, ((RealmsWorldOptions)this.serverData.slots.get(this.serverData.activeSlot)).clone(), this.serverData.worldType, this.serverData.activeSlot ) ) ) .bounds(this.leftButton(0), row(13) - 5, 90, 20) .build() ); this.backupButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.backup"), button -> this.minecraft.setScreen(new RealmsBackupScreen(this, this.serverData.clone(), this.serverData.activeSlot)) ) .bounds(this.leftButton(1), row(13) - 5, 90, 20) .build() ); this.resetWorldButton = this.addRenderableWidget( Button.builder( Component.translatable("mco.configure.world.buttons.resetworld"), button -> this.minecraft .setScreen( RealmsResetWorldScreen.forResetSlot(this, this.serverData.clone(), () -> this.minecraft.execute(() -> this.minecraft.setScreen(this.getNewScreen()))) ) ) .bounds(this.leftButton(2), row(13) - 5, 90, 20) .build() ); this.addRenderableWidget(Button.builder(CommonComponents.GUI_BACK, button -> this.onClose()).bounds(this.rightX - 80 + 8, row(13) - 5, 70, 20).build()); this.backupButton.active = true; if (this.serverData == null) { this.hideMinigameButtons(); this.hideRegularButtons(); this.playersButton.active = false; this.settingsButton.active = false; this.subscriptionButton.active = false; } else { this.disableButtons(); if (this.isMinigame()) { this.hideRegularButtons(); } else { this.hideMinigameButtons(); } } } private RealmsWorldSlotButton addSlotButton(int index) { int i = this.frame(index); int j = row(5) + 5; RealmsWorldSlotButton realmsWorldSlotButton = new RealmsWorldSlotButton(i, j, 80, 80, index, button -> { State state = ((RealmsWorldSlotButton)button).getState(); if (state != null) { switch (state.action) { case NOTHING: break; case JOIN: this.joinRealm(this.serverData); break; case SWITCH_SLOT: if (state.minigame) { this.switchToMinigame(); } else if (state.empty) { this.switchToEmptySlot(index, this.serverData); } else { this.switchToFullSlot(index, this.serverData); } break; default: throw new IllegalStateException("Unknown action " + state.action); } } }); if (this.serverData != null) { realmsWorldSlotButton.setServerData(this.serverData); } return this.addRenderableWidget(realmsWorldSlotButton); } private int leftButton(int index) { return this.leftX + index * 95; } private int centerButton(int row, int column) { return this.width / 2 - (column * 105 - 5) / 2 + row * 105; } @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { super.render(guiGraphics, mouseX, mouseY, partialTick); this.toolTip = null; guiGraphics.drawCenteredString(this.font, WORLD_LIST_TITLE, this.width / 2, row(4), -1); if (this.serverData == null) { guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 17, -1); } else { String string = (String)Objects.requireNonNullElse(this.serverData.getName(), ""); int i = this.font.width(string); int j = this.serverData.state == com.mojang.realmsclient.dto.RealmsServer.State.CLOSED ? -6250336 : 8388479; int k = this.font.width(this.title); guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 12, -1); guiGraphics.drawCenteredString(this.font, string, this.width / 2, 24, j); int l = Math.min(this.centerButton(2, 3) + 80 - 11, this.width / 2 + i / 2 + k / 2 + 10); this.drawServerStatus(guiGraphics, l, 7, mouseX, mouseY); if (this.isMinigame()) { String string2 = this.serverData.getMinigameName(); if (string2 != null) { guiGraphics.drawString(this.font, Component.translatable("mco.configure.world.minigame", string2), this.leftX + 80 + 20 + 10, row(13), -1); } } } } private int frame(int index) { return this.leftX + (index - 1) * 98; } @Override public void onClose() { this.minecraft.setScreen(this.lastScreen); if (this.stateChanged) { this.lastScreen.resetScreen(); } } public void fetchServerData(long serverId) { new Thread(() -> { RealmsClient realmsClient = RealmsClient.getOrCreate(); try { RealmsServer realmsServer = realmsClient.getOwnRealm(serverId); this.minecraft.execute(() -> { this.serverData = realmsServer; this.disableButtons(); if (this.isMinigame()) { this.show(this.switchMinigameButton); } else { this.show(this.optionsButton); this.show(this.backupButton); this.show(this.resetWorldButton); } for (RealmsWorldSlotButton realmsWorldSlotButton : this.slotButtonList) { realmsWorldSlotButton.setServerData(realmsServer); } }); } catch (RealmsServiceException var5) { LOGGER.error("Couldn't get own world", (Throwable)var5); this.minecraft.execute(() -> this.minecraft.setScreen(new RealmsGenericErrorScreen(var5, this.lastScreen))); } }).start(); } private void disableButtons() { this.playersButton.active = !this.serverData.expired; this.settingsButton.active = !this.serverData.expired; this.subscriptionButton.active = true; this.switchMinigameButton.active = !this.serverData.expired; this.optionsButton.active = !this.serverData.expired; this.resetWorldButton.active = !this.serverData.expired; } private void joinRealm(RealmsServer server) { if (this.serverData.state == com.mojang.realmsclient.dto.RealmsServer.State.OPEN) { RealmsMainScreen.play(server, this); } else { this.openTheWorld(true); } } private void switchToMinigame() { RealmsSelectWorldTemplateScreen realmsSelectWorldTemplateScreen = new RealmsSelectWorldTemplateScreen( Component.translatable("mco.template.title.minigame"), this::templateSelectionCallback, WorldType.MINIGAME ); realmsSelectWorldTemplateScreen.setWarning(Component.translatable("mco.minigame.world.info.line1"), Component.translatable("mco.minigame.world.info.line2")); this.minecraft.setScreen(realmsSelectWorldTemplateScreen); } private void switchToFullSlot(int slot, RealmsServer server) { this.minecraft .setScreen( RealmsPopups.infoPopupScreen( this, Component.translatable("mco.configure.world.slot.switch.question.line1"), popupScreen -> { this.stateChanged(); this.minecraft .setScreen( new RealmsLongRunningMcoTaskScreen( this.lastScreen, new SwitchSlotTask(server.id, slot, () -> this.minecraft.execute(() -> this.minecraft.setScreen(this.getNewScreen()))) ) ); } ) ); } private void switchToEmptySlot(int slot, RealmsServer server) { this.minecraft .setScreen( RealmsPopups.infoPopupScreen( this, Component.translatable("mco.configure.world.slot.switch.question.line1"), popupScreen -> { this.stateChanged(); RealmsResetWorldScreen realmsResetWorldScreen = RealmsResetWorldScreen.forEmptySlot( this, slot, server, () -> this.minecraft.execute(() -> this.minecraft.setScreen(this.getNewScreen())) ); this.minecraft.setScreen(realmsResetWorldScreen); } ) ); } private void drawServerStatus(GuiGraphics guiGraphics, int x, int y, int mouseX, int mouseY) { if (this.serverData.expired) { this.drawRealmStatus(guiGraphics, x, y, mouseX, mouseY, EXPIRED_SPRITE, () -> SERVER_EXPIRED_TOOLTIP); } else if (this.serverData.state == com.mojang.realmsclient.dto.RealmsServer.State.CLOSED) { this.drawRealmStatus(guiGraphics, x, y, mouseX, mouseY, CLOSED_SPRITE, () -> SERVER_CLOSED_TOOLTIP); } else if (this.serverData.state == com.mojang.realmsclient.dto.RealmsServer.State.OPEN) { if (this.serverData.daysLeft < 7) { this.drawRealmStatus( guiGraphics, x, y, mouseX, mouseY, EXPIRES_SOON_SPRITE, () -> { if (this.serverData.daysLeft <= 0) { return SERVER_EXPIRING_SOON_TOOLTIP; } else { return (Component)(this.serverData.daysLeft == 1 ? SERVER_EXPIRING_IN_DAY_TOOLTIP : Component.translatable("mco.selectServer.expires.days", this.serverData.daysLeft)); } } ); } else { this.drawRealmStatus(guiGraphics, x, y, mouseX, mouseY, OPEN_SPRITE, () -> SERVER_OPEN_TOOLTIP); } } } private void drawRealmStatus(GuiGraphics guiGraphics, int x, int y, int mouseX, int mouseY, ResourceLocation sprite, Supplier tooltipSupplier) { guiGraphics.blitSprite(RenderType::guiTextured, sprite, x, y, 10, 28); if (mouseX >= x && mouseX <= x + 9 && mouseY >= y && mouseY <= y + 27) { this.setTooltipForNextRenderPass((Component)tooltipSupplier.get()); } } private boolean isMinigame() { return this.serverData != null && this.serverData.isMinigameActive(); } private void hideRegularButtons() { this.hide(this.optionsButton); this.hide(this.backupButton); this.hide(this.resetWorldButton); } private void hide(Button button) { button.visible = false; } private void show(Button button) { button.visible = true; } private void hideMinigameButtons() { this.hide(this.switchMinigameButton); } public void saveSlotSettings(RealmsWorldOptions worldOptions) { RealmsWorldOptions realmsWorldOptions = (RealmsWorldOptions)this.serverData.slots.get(this.serverData.activeSlot); worldOptions.templateId = realmsWorldOptions.templateId; worldOptions.templateImage = realmsWorldOptions.templateImage; RealmsClient realmsClient = RealmsClient.getOrCreate(); try { realmsClient.updateSlot(this.serverData.id, this.serverData.activeSlot, worldOptions); this.serverData.slots.put(this.serverData.activeSlot, worldOptions); if (realmsWorldOptions.gameMode != worldOptions.gameMode || realmsWorldOptions.hardcore != worldOptions.hardcore) { RealmsMainScreen.refreshServerList(); } } catch (RealmsServiceException var5) { LOGGER.error("Couldn't save slot settings", (Throwable)var5); this.minecraft.setScreen(new RealmsGenericErrorScreen(var5, this)); return; } this.minecraft.setScreen(this); } public void saveSettings(String key, String value) { String string = StringUtil.isBlank(value) ? "" : value; RealmsClient realmsClient = RealmsClient.getOrCreate(); try { realmsClient.update(this.serverData.id, key, string); this.serverData.setName(key); this.serverData.setDescription(string); this.stateChanged(); } catch (RealmsServiceException var6) { LOGGER.error("Couldn't save settings", (Throwable)var6); this.minecraft.setScreen(new RealmsGenericErrorScreen(var6, this)); return; } this.minecraft.setScreen(this); } public void openTheWorld(boolean join) { RealmsConfigureWorldScreen realmsConfigureWorldScreen = this.getNewScreen(); this.minecraft .setScreen( new RealmsLongRunningMcoTaskScreen(realmsConfigureWorldScreen, new OpenServerTask(this.serverData, realmsConfigureWorldScreen, join, this.minecraft)) ); } public void closeTheWorld() { RealmsConfigureWorldScreen realmsConfigureWorldScreen = this.getNewScreen(); this.minecraft.setScreen(new RealmsLongRunningMcoTaskScreen(realmsConfigureWorldScreen, new CloseServerTask(this.serverData, realmsConfigureWorldScreen))); } public void stateChanged() { this.stateChanged = true; } private void templateSelectionCallback(@Nullable WorldTemplate worldTemplate) { if (worldTemplate != null && WorldTemplateType.MINIGAME == worldTemplate.type) { this.stateChanged(); this.minecraft .setScreen(new RealmsLongRunningMcoTaskScreen(this.lastScreen, new SwitchMinigameTask(this.serverData.id, worldTemplate, this.getNewScreen()))); } else { this.minecraft.setScreen(this); } } public RealmsConfigureWorldScreen getNewScreen() { RealmsConfigureWorldScreen realmsConfigureWorldScreen = new RealmsConfigureWorldScreen(this.lastScreen, this.serverId); realmsConfigureWorldScreen.stateChanged = this.stateChanged; return realmsConfigureWorldScreen; } }