minecraft-src/net/minecraft/client/gui/screens/worldselection/CreateWorldScreen.java
2025-07-04 03:45:38 +03:00

864 lines
37 KiB
Java

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<DataLoadContext, WorldGenSettings> 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<DataLoadContext, WorldGenSettings> 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<DataLoadContext, WorldGenSettings> worldGenSettingsGetter,
WorldCreationContextMapper creationContextMapper,
ResourceKey<WorldPreset> 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<WorldCreationContext> 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<ResourceKey<WorldPreset>> 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<RegistryLayer> 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<RegistryLayer> registryAccess, PrimaryLevelData levelData) {
boolean bl = this.createWorldCallback.create(this, registryAccess, levelData, this.tempDataPackDir);
this.removeTempDataPackDir();
if (!bl) {
this.popScreen();
}
}
private boolean createNewWorld(LayeredRegistryAccess<RegistryLayer> registryAccess, WorldData worldData) {
String string = this.uiState.getTargetFolder();
WorldCreationContext worldCreationContext = this.uiState.getSettings();
queueLoadScreen(this.minecraft, PREPARING_WORLD_DATA);
Optional<LevelStorageSource.LevelStorageAccess> 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<Path, PackRepository> 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<Path, PackRepository> 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<WorldDataConfiguration> callback) {
List<String> list = ImmutableList.copyOf(packRepository.getSelectedIds());
List<String> list2 = (List<String>)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<WorldDataConfiguration> callback) {
this.minecraft.forceSetScreen(new GenericMessageScreen(Component.translatable("dataPack.validation.working")));
InitConfig initConfig = createDefaultLoadConfig(packRepository, worldDataConfiguration);
WorldLoader.<DataPackReloadCookie, WorldCreationContext>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<JsonElement> dynamicOps = worldCreationContext.worldgenLoadContext().createSerializationContext(JsonOps.INSTANCE);
DataResult<JsonElement> dataResult = WorldGenSettings.encode(dynamicOps, worldCreationContext.options(), worldCreationContext.selectedDimensions())
.setLifecycle(Lifecycle.stable());
DynamicOps<JsonElement> dynamicOps2 = dataLoadContext.datapackWorldgen().createSerializationContext(JsonOps.INSTANCE);
WorldGenSettings worldGenSettings = dataResult.<WorldGenSettings>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<Path> 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<LevelStorageSource.LevelStorageAccess> 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<Path> 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<Path> mutableObject = new MutableObject<>();
try {
Stream<Path> 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<Path, PackRepository> 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<SelectedGameMode> cycleButton = rowHelper.addChild(
CycleButton.<SelectedGameMode>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<Difficulty> cycleButton2 = rowHelper.addChild(
CycleButton.<Difficulty>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<Boolean> 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<WorldTypeEntry> cycleButton = rowHelper.addChild(
CycleButton.<WorldTypeEntry>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<WorldTypeEntry> createWorldTypeValueSupplier() {
return new 2(this);
}
private static MutableComponent createTypeButtonNarration(CycleButton<WorldTypeEntry> button) {
return button.getValue().isAmplified()
? CommonComponents.joinForNarration(button.createDefaultNarrationMessage(), AMPLIFIED_HELP_TEXT)
: button.createDefaultNarrationMessage();
}
}
}