package net.minecraft.client.gui.screens.worldselection; import com.google.common.collect.ImmutableList; import com.google.gson.JsonElement; import com.mojang.datafixers.util.Pair; import com.mojang.logging.LogUtils; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Stream; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.FileUtil; import net.minecraft.SharedConstants; 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.CycleButton; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.components.CycleButton.ValueListSupplier; import net.minecraft.client.gui.components.tabs.GridLayoutTab; import net.minecraft.client.gui.components.tabs.TabManager; import net.minecraft.client.gui.components.tabs.TabNavigationBar; import net.minecraft.client.gui.components.toasts.SystemToast; import net.minecraft.client.gui.layouts.CommonLayouts; import net.minecraft.client.gui.layouts.HeaderAndFooterLayout; import net.minecraft.client.gui.layouts.LayoutSettings; import net.minecraft.client.gui.layouts.LinearLayout; import net.minecraft.client.gui.layouts.GridLayout.RowHelper; import net.minecraft.client.gui.navigation.ScreenRectangle; import net.minecraft.client.gui.screens.ConfirmScreen; import net.minecraft.client.gui.screens.GenericMessageScreen; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.packs.PackSelectionScreen; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen.WorldTab.1; import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen.WorldTab.2; import net.minecraft.client.gui.screens.worldselection.SwitchGrid.Builder; import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState.SelectedGameMode; import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState.WorldTypeEntry; import net.minecraft.client.renderer.RenderType; import net.minecraft.commands.Commands; import net.minecraft.core.LayeredRegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.RegistryLayer; import net.minecraft.server.WorldLoader; import net.minecraft.server.WorldLoader.DataLoadContext; import net.minecraft.server.WorldLoader.DataLoadOutput; import net.minecraft.server.WorldLoader.InitConfig; import net.minecraft.server.WorldLoader.PackConfig; import net.minecraft.server.packs.repository.PackRepository; import net.minecraft.server.packs.repository.ServerPacksSource; import net.minecraft.world.Difficulty; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.level.DataPackConfig; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.LevelSettings; import net.minecraft.world.level.WorldDataConfiguration; import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.WorldDimensions.Complete; import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorPresets; import net.minecraft.world.level.levelgen.presets.WorldPreset; import net.minecraft.world.level.levelgen.presets.WorldPresets; import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; import net.minecraft.world.level.storage.WorldData; import net.minecraft.world.level.validation.DirectoryValidator; import org.apache.commons.lang3.mutable.MutableObject; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public class CreateWorldScreen extends Screen { private static final int GROUP_BOTTOM = 1; private static final int TAB_COLUMN_WIDTH = 210; private static final Logger LOGGER = LogUtils.getLogger(); private static final String TEMP_WORLD_PREFIX = "mcworld-"; static final Component GAME_MODEL_LABEL = Component.translatable("selectWorld.gameMode"); static final Component NAME_LABEL = Component.translatable("selectWorld.enterName"); static final Component EXPERIMENTS_LABEL = Component.translatable("selectWorld.experiments"); static final Component ALLOW_COMMANDS_INFO = Component.translatable("selectWorld.allowCommands.info"); private static final Component PREPARING_WORLD_DATA = Component.translatable("createWorld.preparing"); private static final int HORIZONTAL_BUTTON_SPACING = 10; private static final int VERTICAL_BUTTON_SPACING = 8; public static final ResourceLocation TAB_HEADER_BACKGROUND = ResourceLocation.withDefaultNamespace("textures/gui/tab_header_background.png"); private final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this); final WorldCreationUiState uiState; private final TabManager tabManager = new TabManager(guiEventListener -> { AbstractWidget var10000 = this.addRenderableWidget(guiEventListener); }, guiEventListener -> this.removeWidget(guiEventListener)); private boolean recreated; private final DirectoryValidator packValidator; private final CreateWorldCallback createWorldCallback; @Nullable private final Screen lastScreen; @Nullable private Path tempDataPackDir; @Nullable private PackRepository tempDataPackRepository; @Nullable private TabNavigationBar tabNavigationBar; public static void openFresh(Minecraft minecraft, @Nullable Screen lastScreen) { openFresh( minecraft, lastScreen, (createWorldScreen, layeredRegistryAccess, primaryLevelData, path) -> createWorldScreen.createNewWorld(layeredRegistryAccess, primaryLevelData) ); } public static void openFresh(Minecraft minecraft, @Nullable Screen lastScreen, CreateWorldCallback callback) { WorldCreationContextMapper worldCreationContextMapper = (reloadableServerResources, layeredRegistryAccess, dataPackReloadCookie) -> new WorldCreationContext( dataPackReloadCookie.worldGenSettings(), layeredRegistryAccess, reloadableServerResources, dataPackReloadCookie.dataConfiguration() ); Function function = dataLoadContext -> new WorldGenSettings( WorldOptions.defaultWithRandomSeed(), WorldPresets.createNormalWorldDimensions(dataLoadContext.datapackWorldgen()) ); openCreateWorldScreen(minecraft, lastScreen, function, worldCreationContextMapper, WorldPresets.NORMAL, callback); } public static void testWorld(Minecraft minecraft, @Nullable Screen lastScreen) { WorldCreationContextMapper worldCreationContextMapper = (reloadableServerResources, layeredRegistryAccess, dataPackReloadCookie) -> new WorldCreationContext( dataPackReloadCookie.worldGenSettings().options(), dataPackReloadCookie.worldGenSettings().dimensions(), layeredRegistryAccess, reloadableServerResources, dataPackReloadCookie.dataConfiguration(), new InitialWorldCreationOptions( SelectedGameMode.CREATIVE, Set.of(GameRules.RULE_DAYLIGHT, GameRules.RULE_WEATHER_CYCLE, GameRules.RULE_DOMOBSPAWNING), FlatLevelGeneratorPresets.REDSTONE_READY ) ); Function function = dataLoadContext -> new WorldGenSettings( WorldOptions.testWorldWithRandomSeed(), WorldPresets.createFlatWorldDimensions(dataLoadContext.datapackWorldgen()) ); openCreateWorldScreen( minecraft, lastScreen, function, worldCreationContextMapper, WorldPresets.FLAT, (createWorldScreen, layeredRegistryAccess, primaryLevelData, path) -> createWorldScreen.createNewWorld(layeredRegistryAccess, primaryLevelData) ); } private static void openCreateWorldScreen( Minecraft minecraft, @Nullable Screen lastScreen, Function worldGenSettingsGetter, WorldCreationContextMapper creationContextMapper, ResourceKey preset, CreateWorldCallback createWorldCallback ) { queueLoadScreen(minecraft, PREPARING_WORLD_DATA); PackRepository packRepository = new PackRepository(new ServerPacksSource(minecraft.directoryValidator())); WorldDataConfiguration worldDataConfiguration = SharedConstants.IS_RUNNING_IN_IDE ? new WorldDataConfiguration(new DataPackConfig(List.of("vanilla", "tests"), List.of()), FeatureFlags.DEFAULT_FLAGS) : WorldDataConfiguration.DEFAULT; InitConfig initConfig = createDefaultLoadConfig(packRepository, worldDataConfiguration); CompletableFuture completableFuture = WorldLoader.load( initConfig, dataLoadContext -> new DataLoadOutput<>( new DataPackReloadCookie((WorldGenSettings)worldGenSettingsGetter.apply(dataLoadContext), dataLoadContext.dataConfiguration()), dataLoadContext.datapackDimensions() ), (closeableResourceManager, reloadableServerResources, layeredRegistryAccess, dataPackReloadCookie) -> { closeableResourceManager.close(); return creationContextMapper.apply(reloadableServerResources, layeredRegistryAccess, dataPackReloadCookie); }, Util.backgroundExecutor(), minecraft ); minecraft.managedBlock(completableFuture::isDone); minecraft.setScreen( new CreateWorldScreen(minecraft, lastScreen, (WorldCreationContext)completableFuture.join(), Optional.of(preset), OptionalLong.empty(), createWorldCallback) ); } public static CreateWorldScreen createFromExisting( Minecraft minecraft, @Nullable Screen lastScreen, LevelSettings levelSettings, WorldCreationContext context, @Nullable Path tempDataPackDir ) { CreateWorldScreen createWorldScreen = new CreateWorldScreen( minecraft, lastScreen, context, WorldPresets.fromSettings(context.selectedDimensions()), OptionalLong.of(context.options().seed()), (createWorldScreenx, layeredRegistryAccess, primaryLevelData, path) -> createWorldScreenx.createNewWorld(layeredRegistryAccess, primaryLevelData) ); createWorldScreen.recreated = true; createWorldScreen.uiState.setName(levelSettings.levelName()); createWorldScreen.uiState.setAllowCommands(levelSettings.allowCommands()); createWorldScreen.uiState.setDifficulty(levelSettings.difficulty()); createWorldScreen.uiState.getGameRules().assignFrom(levelSettings.gameRules(), null); if (levelSettings.hardcore()) { createWorldScreen.uiState.setGameMode(SelectedGameMode.HARDCORE); } else if (levelSettings.gameType().isSurvival()) { createWorldScreen.uiState.setGameMode(SelectedGameMode.SURVIVAL); } else if (levelSettings.gameType().isCreative()) { createWorldScreen.uiState.setGameMode(SelectedGameMode.CREATIVE); } createWorldScreen.tempDataPackDir = tempDataPackDir; return createWorldScreen; } private CreateWorldScreen( Minecraft minecraft, @Nullable Screen lastScreen, WorldCreationContext context, Optional> preset, OptionalLong seed, CreateWorldCallback createWorldCallback ) { super(Component.translatable("selectWorld.create")); this.lastScreen = lastScreen; this.packValidator = minecraft.directoryValidator(); this.createWorldCallback = createWorldCallback; this.uiState = new WorldCreationUiState(minecraft.getLevelSource().getBaseDir(), context, preset, seed); } public WorldCreationUiState getUiState() { return this.uiState; } @Override protected void init() { this.tabNavigationBar = TabNavigationBar.builder(this.tabManager, this.width) .addTabs(new CreateWorldScreen.GameTab(), new CreateWorldScreen.WorldTab(), new CreateWorldScreen.MoreTab()) .build(); this.addRenderableWidget(this.tabNavigationBar); LinearLayout linearLayout = this.layout.addToFooter(LinearLayout.horizontal().spacing(8)); linearLayout.addChild(Button.builder(Component.translatable("selectWorld.create"), button -> this.onCreate()).build()); linearLayout.addChild(Button.builder(CommonComponents.GUI_CANCEL, button -> this.popScreen()).build()); this.layout.visitWidgets(abstractWidget -> { abstractWidget.setTabOrderGroup(1); this.addRenderableWidget(abstractWidget); }); this.tabNavigationBar.selectTab(0, false); this.uiState.onChanged(); this.repositionElements(); } @Override protected void setInitialFocus() { } @Override public void repositionElements() { if (this.tabNavigationBar != null) { this.tabNavigationBar.setWidth(this.width); this.tabNavigationBar.arrangeElements(); int i = this.tabNavigationBar.getRectangle().bottom(); ScreenRectangle screenRectangle = new ScreenRectangle(0, i, this.width, this.height - this.layout.getFooterHeight() - i); this.tabManager.setTabArea(screenRectangle); this.layout.setHeaderHeight(i); this.layout.arrangeElements(); } } private static void queueLoadScreen(Minecraft minecraft, Component title) { minecraft.forceSetScreen(new GenericMessageScreen(title)); } private void onCreate() { WorldCreationContext worldCreationContext = this.uiState.getSettings(); Complete complete = worldCreationContext.selectedDimensions().bake(worldCreationContext.datapackDimensions()); LayeredRegistryAccess layeredRegistryAccess = worldCreationContext.worldgenRegistries() .replaceFrom(RegistryLayer.DIMENSIONS, complete.dimensionsRegistryAccess()); Lifecycle lifecycle = FeatureFlags.isExperimental(worldCreationContext.dataConfiguration().enabledFeatures()) ? Lifecycle.experimental() : Lifecycle.stable(); Lifecycle lifecycle2 = layeredRegistryAccess.compositeAccess().allRegistriesLifecycle(); Lifecycle lifecycle3 = lifecycle2.add(lifecycle); boolean bl = !this.recreated && lifecycle2 == Lifecycle.stable(); LevelSettings levelSettings = this.createLevelSettings(complete.specialWorldProperty() == PrimaryLevelData.SpecialWorldProperty.DEBUG); PrimaryLevelData primaryLevelData = new PrimaryLevelData(levelSettings, this.uiState.getSettings().options(), complete.specialWorldProperty(), lifecycle3); WorldOpenFlows.confirmWorldCreation(this.minecraft, this, lifecycle3, () -> this.createWorldAndCleanup(layeredRegistryAccess, primaryLevelData), bl); } private void createWorldAndCleanup(LayeredRegistryAccess registryAccess, PrimaryLevelData levelData) { boolean bl = this.createWorldCallback.create(this, registryAccess, levelData, this.tempDataPackDir); this.removeTempDataPackDir(); if (!bl) { this.popScreen(); } } private boolean createNewWorld(LayeredRegistryAccess registryAccess, WorldData worldData) { String string = this.uiState.getTargetFolder(); WorldCreationContext worldCreationContext = this.uiState.getSettings(); queueLoadScreen(this.minecraft, PREPARING_WORLD_DATA); Optional optional = createNewWorldDirectory(this.minecraft, string, this.tempDataPackDir); if (optional.isEmpty()) { SystemToast.onPackCopyFailure(this.minecraft, string); return false; } else { this.minecraft .createWorldOpenFlows() .createLevelFromExistingSettings((LevelStorageSource.LevelStorageAccess)optional.get(), worldCreationContext.dataPackResources(), registryAccess, worldData); return true; } } private LevelSettings createLevelSettings(boolean debug) { String string = this.uiState.getName().trim(); if (debug) { GameRules gameRules = new GameRules(WorldDataConfiguration.DEFAULT.enabledFeatures()); gameRules.getRule(GameRules.RULE_DAYLIGHT).set(false, null); return new LevelSettings(string, GameType.SPECTATOR, false, Difficulty.PEACEFUL, true, gameRules, WorldDataConfiguration.DEFAULT); } else { return new LevelSettings( string, this.uiState.getGameMode().gameType, this.uiState.isHardcore(), this.uiState.getDifficulty(), this.uiState.isAllowCommands(), this.uiState.getGameRules(), this.uiState.getSettings().dataConfiguration() ); } } @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (this.tabNavigationBar.keyPressed(keyCode)) { return true; } else if (super.keyPressed(keyCode, scanCode, modifiers)) { return true; } else if (keyCode != 257 && keyCode != 335) { return false; } else { this.onCreate(); return true; } } @Override public void onClose() { this.popScreen(); } public void popScreen() { this.minecraft.setScreen(this.lastScreen); this.removeTempDataPackDir(); } @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { super.render(guiGraphics, mouseX, mouseY, partialTick); guiGraphics.blit(RenderType::guiTextured, Screen.FOOTER_SEPARATOR, 0, this.height - this.layout.getFooterHeight() - 2, 0.0F, 0.0F, this.width, 2, 32, 2); } @Override protected void renderMenuBackground(GuiGraphics partialTick) { partialTick.blit(RenderType::guiTextured, TAB_HEADER_BACKGROUND, 0, 0, 0.0F, 0.0F, this.width, this.layout.getHeaderHeight(), 16, 16); this.renderMenuBackground(partialTick, 0, this.layout.getHeaderHeight(), this.width, this.height); } @Nullable private Path getOrCreateTempDataPackDir() { if (this.tempDataPackDir == null) { try { this.tempDataPackDir = Files.createTempDirectory("mcworld-"); } catch (IOException var2) { LOGGER.warn("Failed to create temporary dir", (Throwable)var2); SystemToast.onPackCopyFailure(this.minecraft, this.uiState.getTargetFolder()); this.popScreen(); } } return this.tempDataPackDir; } void openExperimentsScreen(WorldDataConfiguration worldDataConfiguration) { Pair pair = this.getDataPackSelectionSettings(worldDataConfiguration); if (pair != null) { this.minecraft .setScreen(new ExperimentsScreen(this, pair.getSecond(), packRepository -> this.tryApplyNewDataPacks(packRepository, false, this::openExperimentsScreen))); } } void openDataPackSelectionScreen(WorldDataConfiguration worldDataConfiguration) { Pair pair = this.getDataPackSelectionSettings(worldDataConfiguration); if (pair != null) { this.minecraft .setScreen( new PackSelectionScreen( pair.getSecond(), packRepository -> this.tryApplyNewDataPacks(packRepository, true, this::openDataPackSelectionScreen), pair.getFirst(), Component.translatable("dataPack.title") ) ); } } private void tryApplyNewDataPacks(PackRepository packRepository, boolean shouldConfirm, Consumer callback) { List list = ImmutableList.copyOf(packRepository.getSelectedIds()); List list2 = (List)packRepository.getAvailableIds() .stream() .filter(string -> !list.contains(string)) .collect(ImmutableList.toImmutableList()); WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration( new DataPackConfig(list, list2), this.uiState.getSettings().dataConfiguration().enabledFeatures() ); if (this.uiState.tryUpdateDataConfiguration(worldDataConfiguration)) { this.minecraft.setScreen(this); } else { FeatureFlagSet featureFlagSet = packRepository.getRequestedFeatureFlags(); if (FeatureFlags.isExperimental(featureFlagSet) && shouldConfirm) { this.minecraft.setScreen(new ConfirmExperimentalFeaturesScreen(packRepository.getSelectedPacks(), bl -> { if (bl) { this.applyNewPackConfig(packRepository, worldDataConfiguration, callback); } else { callback.accept(this.uiState.getSettings().dataConfiguration()); } })); } else { this.applyNewPackConfig(packRepository, worldDataConfiguration, callback); } } } private void applyNewPackConfig(PackRepository packRepository, WorldDataConfiguration worldDataConfiguration, Consumer callback) { this.minecraft.forceSetScreen(new GenericMessageScreen(Component.translatable("dataPack.validation.working"))); InitConfig initConfig = createDefaultLoadConfig(packRepository, worldDataConfiguration); WorldLoader.load( initConfig, dataLoadContext -> { if (dataLoadContext.datapackWorldgen().lookupOrThrow(Registries.WORLD_PRESET).listElements().findAny().isEmpty()) { throw new IllegalStateException("Needs at least one world preset to continue"); } else if (dataLoadContext.datapackWorldgen().lookupOrThrow(Registries.BIOME).listElements().findAny().isEmpty()) { throw new IllegalStateException("Needs at least one biome continue"); } else { WorldCreationContext worldCreationContext = this.uiState.getSettings(); DynamicOps dynamicOps = worldCreationContext.worldgenLoadContext().createSerializationContext(JsonOps.INSTANCE); DataResult dataResult = WorldGenSettings.encode(dynamicOps, worldCreationContext.options(), worldCreationContext.selectedDimensions()) .setLifecycle(Lifecycle.stable()); DynamicOps dynamicOps2 = dataLoadContext.datapackWorldgen().createSerializationContext(JsonOps.INSTANCE); WorldGenSettings worldGenSettings = dataResult.flatMap(jsonElement -> WorldGenSettings.CODEC.parse(dynamicOps2, jsonElement)) .getOrThrow(string -> new IllegalStateException("Error parsing worldgen settings after loading data packs: " + string)); return new DataLoadOutput<>(new DataPackReloadCookie(worldGenSettings, dataLoadContext.dataConfiguration()), dataLoadContext.datapackDimensions()); } }, (closeableResourceManager, reloadableServerResources, layeredRegistryAccess, dataPackReloadCookie) -> { closeableResourceManager.close(); return new WorldCreationContext( dataPackReloadCookie.worldGenSettings(), layeredRegistryAccess, reloadableServerResources, dataPackReloadCookie.dataConfiguration() ); }, Util.backgroundExecutor(), this.minecraft ) .thenApply(worldCreationContext -> { worldCreationContext.validate(); return worldCreationContext; }) .thenAcceptAsync(this.uiState::setSettings, this.minecraft) .handleAsync( (void_, throwable) -> { if (throwable != null) { LOGGER.warn("Failed to validate datapack", throwable); this.minecraft .setScreen( new ConfirmScreen( bl -> { if (bl) { callback.accept(this.uiState.getSettings().dataConfiguration()); } else { callback.accept(WorldDataConfiguration.DEFAULT); } }, Component.translatable("dataPack.validation.failed"), CommonComponents.EMPTY, Component.translatable("dataPack.validation.back"), Component.translatable("dataPack.validation.reset") ) ); } else { this.minecraft.setScreen(this); } return null; }, this.minecraft ); } private static InitConfig createDefaultLoadConfig(PackRepository packRepository, WorldDataConfiguration initialDataConfig) { PackConfig packConfig = new PackConfig(packRepository, initialDataConfig, false, true); return new InitConfig(packConfig, Commands.CommandSelection.INTEGRATED, 2); } private void removeTempDataPackDir() { if (this.tempDataPackDir != null && Files.exists(this.tempDataPackDir, new LinkOption[0])) { try { Stream stream = Files.walk(this.tempDataPackDir); try { stream.sorted(Comparator.reverseOrder()).forEach(path -> { try { Files.delete(path); } catch (IOException var2) { LOGGER.warn("Failed to remove temporary file {}", path, var2); } }); } catch (Throwable var5) { if (stream != null) { try { stream.close(); } catch (Throwable var4) { var5.addSuppressed(var4); } } throw var5; } if (stream != null) { stream.close(); } } catch (IOException var6) { LOGGER.warn("Failed to list temporary dir {}", this.tempDataPackDir); } } this.tempDataPackDir = null; } private static void copyBetweenDirs(Path fromDir, Path toDir, Path filePath) { try { Util.copyBetweenDirs(fromDir, toDir, filePath); } catch (IOException var4) { LOGGER.warn("Failed to copy datapack file from {} to {}", filePath, toDir); throw new UncheckedIOException(var4); } } private static Optional createNewWorldDirectory(Minecraft minecraft, String saveName, @Nullable Path tempDataPackDir) { try { LevelStorageSource.LevelStorageAccess levelStorageAccess = minecraft.getLevelSource().createAccess(saveName); if (tempDataPackDir == null) { return Optional.of(levelStorageAccess); } try { Stream stream = Files.walk(tempDataPackDir); Optional var6; try { Path path = levelStorageAccess.getLevelPath(LevelResource.DATAPACK_DIR); FileUtil.createDirectoriesSafe(path); stream.filter(path2 -> !path2.equals(tempDataPackDir)).forEach(path3 -> copyBetweenDirs(tempDataPackDir, path, path3)); var6 = Optional.of(levelStorageAccess); } catch (Throwable var8) { if (stream != null) { try { stream.close(); } catch (Throwable var7) { var8.addSuppressed(var7); } } throw var8; } if (stream != null) { stream.close(); } return var6; } catch (UncheckedIOException | IOException var9) { LOGGER.warn("Failed to copy datapacks to world {}", saveName, var9); levelStorageAccess.close(); } } catch (UncheckedIOException | IOException var10) { LOGGER.warn("Failed to create access for {}", saveName, var10); } return Optional.empty(); } @Nullable public static Path createTempDataPackDirFromExistingWorld(Path datapackDir, Minecraft minecraft) { MutableObject mutableObject = new MutableObject<>(); try { Stream stream = Files.walk(datapackDir); try { stream.filter(path2 -> !path2.equals(datapackDir)).forEach(path2 -> { Path path3 = mutableObject.getValue(); if (path3 == null) { try { path3 = Files.createTempDirectory("mcworld-"); } catch (IOException var5) { LOGGER.warn("Failed to create temporary dir"); throw new UncheckedIOException(var5); } mutableObject.setValue(path3); } copyBetweenDirs(datapackDir, path3, path2); }); } catch (Throwable var7) { if (stream != null) { try { stream.close(); } catch (Throwable var6) { var7.addSuppressed(var6); } } throw var7; } if (stream != null) { stream.close(); } } catch (UncheckedIOException | IOException var8) { LOGGER.warn("Failed to copy datapacks from world {}", datapackDir, var8); SystemToast.onPackCopyFailure(minecraft, datapackDir.toString()); return null; } return mutableObject.getValue(); } @Nullable private Pair getDataPackSelectionSettings(WorldDataConfiguration worldDataConfiguration) { Path path = this.getOrCreateTempDataPackDir(); if (path != null) { if (this.tempDataPackRepository == null) { this.tempDataPackRepository = ServerPacksSource.createPackRepository(path, this.packValidator); this.tempDataPackRepository.reload(); } this.tempDataPackRepository.setSelected(worldDataConfiguration.dataPacks().getEnabled()); return Pair.of(path, this.tempDataPackRepository); } else { return null; } } @Environment(EnvType.CLIENT) class GameTab extends GridLayoutTab { private static final Component TITLE = Component.translatable("createWorld.tab.game.title"); private static final Component ALLOW_COMMANDS = Component.translatable("selectWorld.allowCommands"); private final EditBox nameEdit; GameTab() { super(TITLE); RowHelper rowHelper = this.layout.rowSpacing(8).createRowHelper(1); LayoutSettings layoutSettings = rowHelper.newCellSettings(); this.nameEdit = new EditBox(CreateWorldScreen.this.font, 208, 20, Component.translatable("selectWorld.enterName")); this.nameEdit.setValue(CreateWorldScreen.this.uiState.getName()); this.nameEdit.setResponder(CreateWorldScreen.this.uiState::setName); CreateWorldScreen.this.uiState .addListener( worldCreationUiState -> this.nameEdit .setTooltip( Tooltip.create( Component.translatable("selectWorld.targetFolder", Component.literal(worldCreationUiState.getTargetFolder()).withStyle(ChatFormatting.ITALIC)) ) ) ); CreateWorldScreen.this.setInitialFocus(this.nameEdit); rowHelper.addChild( CommonLayouts.labeledElement(CreateWorldScreen.this.font, this.nameEdit, CreateWorldScreen.NAME_LABEL), rowHelper.newCellSettings().alignHorizontallyCenter() ); CycleButton cycleButton = rowHelper.addChild( CycleButton.builder(selectedGameMode -> selectedGameMode.displayName) .withValues(SelectedGameMode.SURVIVAL, SelectedGameMode.HARDCORE, SelectedGameMode.CREATIVE) .create( 0, 0, 210, 20, CreateWorldScreen.GAME_MODEL_LABEL, (cycleButtonx, selectedGameMode) -> CreateWorldScreen.this.uiState.setGameMode(selectedGameMode) ), layoutSettings ); CreateWorldScreen.this.uiState.addListener(worldCreationUiState -> { cycleButton.setValue(worldCreationUiState.getGameMode()); cycleButton.active = !worldCreationUiState.isDebug(); cycleButton.setTooltip(Tooltip.create(worldCreationUiState.getGameMode().getInfo())); }); CycleButton cycleButton2 = rowHelper.addChild( CycleButton.builder(Difficulty::getDisplayName) .withValues(Difficulty.values()) .create( 0, 0, 210, 20, Component.translatable("options.difficulty"), (cycleButtonx, difficulty) -> CreateWorldScreen.this.uiState.setDifficulty(difficulty) ), layoutSettings ); CreateWorldScreen.this.uiState.addListener(worldCreationUiState -> { cycleButton2.setValue(CreateWorldScreen.this.uiState.getDifficulty()); cycleButton2.active = !CreateWorldScreen.this.uiState.isHardcore(); cycleButton2.setTooltip(Tooltip.create(CreateWorldScreen.this.uiState.getDifficulty().getInfo())); }); CycleButton cycleButton3 = rowHelper.addChild( CycleButton.onOffBuilder() .withTooltip(boolean_ -> Tooltip.create(CreateWorldScreen.ALLOW_COMMANDS_INFO)) .create(0, 0, 210, 20, ALLOW_COMMANDS, (cycleButtonx, boolean_) -> CreateWorldScreen.this.uiState.setAllowCommands(boolean_)) ); CreateWorldScreen.this.uiState.addListener(worldCreationUiState -> { cycleButton3.setValue(CreateWorldScreen.this.uiState.isAllowCommands()); cycleButton3.active = !CreateWorldScreen.this.uiState.isDebug() && !CreateWorldScreen.this.uiState.isHardcore(); }); if (!SharedConstants.getCurrentVersion().isStable()) { rowHelper.addChild( Button.builder( CreateWorldScreen.EXPERIMENTS_LABEL, button -> CreateWorldScreen.this.openExperimentsScreen(CreateWorldScreen.this.uiState.getSettings().dataConfiguration()) ) .width(210) .build() ); } } } @Environment(EnvType.CLIENT) class MoreTab extends GridLayoutTab { private static final Component TITLE = Component.translatable("createWorld.tab.more.title"); private static final Component GAME_RULES_LABEL = Component.translatable("selectWorld.gameRules"); private static final Component DATA_PACKS_LABEL = Component.translatable("selectWorld.dataPacks"); MoreTab() { super(TITLE); RowHelper rowHelper = this.layout.rowSpacing(8).createRowHelper(1); rowHelper.addChild(Button.builder(GAME_RULES_LABEL, button -> this.openGameRulesScreen()).width(210).build()); rowHelper.addChild( Button.builder( CreateWorldScreen.EXPERIMENTS_LABEL, button -> CreateWorldScreen.this.openExperimentsScreen(CreateWorldScreen.this.uiState.getSettings().dataConfiguration()) ) .width(210) .build() ); rowHelper.addChild( Button.builder( DATA_PACKS_LABEL, button -> CreateWorldScreen.this.openDataPackSelectionScreen(CreateWorldScreen.this.uiState.getSettings().dataConfiguration()) ) .width(210) .build() ); } private void openGameRulesScreen() { CreateWorldScreen.this.minecraft .setScreen( new EditGameRulesScreen( CreateWorldScreen.this.uiState.getGameRules().copy(CreateWorldScreen.this.uiState.getSettings().dataConfiguration().enabledFeatures()), optional -> { CreateWorldScreen.this.minecraft.setScreen(CreateWorldScreen.this); optional.ifPresent(CreateWorldScreen.this.uiState::setGameRules); } ) ); } } @Environment(EnvType.CLIENT) class WorldTab extends GridLayoutTab { private static final Component TITLE = Component.translatable("createWorld.tab.world.title"); private static final Component AMPLIFIED_HELP_TEXT = Component.translatable("generator.minecraft.amplified.info"); private static final Component GENERATE_STRUCTURES = Component.translatable("selectWorld.mapFeatures"); private static final Component GENERATE_STRUCTURES_INFO = Component.translatable("selectWorld.mapFeatures.info"); private static final Component BONUS_CHEST = Component.translatable("selectWorld.bonusItems"); private static final Component SEED_LABEL = Component.translatable("selectWorld.enterSeed"); static final Component SEED_EMPTY_HINT = Component.translatable("selectWorld.seedInfo").withStyle(ChatFormatting.DARK_GRAY); private static final int WORLD_TAB_WIDTH = 310; private final EditBox seedEdit; private final Button customizeTypeButton; WorldTab() { super(TITLE); RowHelper rowHelper = this.layout.columnSpacing(10).rowSpacing(8).createRowHelper(2); CycleButton cycleButton = rowHelper.addChild( CycleButton.builder(WorldTypeEntry::describePreset) .withValues(this.createWorldTypeValueSupplier()) .withCustomNarration(CreateWorldScreen.WorldTab::createTypeButtonNarration) .create( 0, 0, 150, 20, Component.translatable("selectWorld.mapType"), (cycleButtonx, worldTypeEntry) -> CreateWorldScreen.this.uiState.setWorldType(worldTypeEntry) ) ); cycleButton.setValue(CreateWorldScreen.this.uiState.getWorldType()); CreateWorldScreen.this.uiState.addListener(worldCreationUiState -> { WorldTypeEntry worldTypeEntry = worldCreationUiState.getWorldType(); cycleButton.setValue(worldTypeEntry); if (worldTypeEntry.isAmplified()) { cycleButton.setTooltip(Tooltip.create(AMPLIFIED_HELP_TEXT)); } else { cycleButton.setTooltip(null); } cycleButton.active = CreateWorldScreen.this.uiState.getWorldType().preset() != null; }); this.customizeTypeButton = rowHelper.addChild(Button.builder(Component.translatable("selectWorld.customizeType"), button -> this.openPresetEditor()).build()); CreateWorldScreen.this.uiState .addListener(worldCreationUiState -> this.customizeTypeButton.active = !worldCreationUiState.isDebug() && worldCreationUiState.getPresetEditor() != null); this.seedEdit = new 1(this, CreateWorldScreen.this.font, 308, 20, Component.translatable("selectWorld.enterSeed"), CreateWorldScreen.this); this.seedEdit.setHint(SEED_EMPTY_HINT); this.seedEdit.setValue(CreateWorldScreen.this.uiState.getSeed()); this.seedEdit.setResponder(string -> CreateWorldScreen.this.uiState.setSeed(this.seedEdit.getValue())); rowHelper.addChild(CommonLayouts.labeledElement(CreateWorldScreen.this.font, this.seedEdit, SEED_LABEL), 2); Builder builder = SwitchGrid.builder(310); builder.addSwitch(GENERATE_STRUCTURES, CreateWorldScreen.this.uiState::isGenerateStructures, CreateWorldScreen.this.uiState::setGenerateStructures) .withIsActiveCondition(() -> !CreateWorldScreen.this.uiState.isDebug()) .withInfo(GENERATE_STRUCTURES_INFO); builder.addSwitch(BONUS_CHEST, CreateWorldScreen.this.uiState::isBonusChest, CreateWorldScreen.this.uiState::setBonusChest) .withIsActiveCondition(() -> !CreateWorldScreen.this.uiState.isHardcore() && !CreateWorldScreen.this.uiState.isDebug()); SwitchGrid switchGrid = builder.build(); rowHelper.addChild(switchGrid.layout(), 2); CreateWorldScreen.this.uiState.addListener(worldCreationUiState -> switchGrid.refreshStates()); } private void openPresetEditor() { PresetEditor presetEditor = CreateWorldScreen.this.uiState.getPresetEditor(); if (presetEditor != null) { CreateWorldScreen.this.minecraft.setScreen(presetEditor.createEditScreen(CreateWorldScreen.this, CreateWorldScreen.this.uiState.getSettings())); } } private ValueListSupplier createWorldTypeValueSupplier() { return new 2(this); } private static MutableComponent createTypeButtonNarration(CycleButton button) { return button.getValue().isAmplified() ? CommonComponents.joinForNarration(button.createDefaultNarrationMessage(), AMPLIFIED_HELP_TEXT) : button.createDefaultNarrationMessage(); } } }