WorldSelectionList.java
net.minecraft.client.gui.screens.worldselection.WorldSelectionList
信息
- 全限定名:net.minecraft.client.gui.screens.worldselection.WorldSelectionList
- 类型:public class
- 包:net.minecraft.client.gui.screens.worldselection
- 源码路径:src/main/java/net/minecraft/client/gui/screens/worldselection/WorldSelectionList.java
- 起始行号:L76
- 继承:ObjectSelectionList<WorldSelectionList.Entry>
- 职责:
TODO
字段/常量
-
DATE_FORMAT- 类型:
DateTimeFormatter - 修饰符:
public static final - 源码定位:
L77 - 说明:
TODO
- 类型:
-
ERROR_HIGHLIGHTED_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L78 - 说明:
TODO
- 类型:
-
ERROR_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L79 - 说明:
TODO
- 类型:
-
MARKED_JOIN_HIGHLIGHTED_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L80 - 说明:
TODO
- 类型:
-
MARKED_JOIN_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L81 - 说明:
TODO
- 类型:
-
WARNING_HIGHLIGHTED_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L82 - 说明:
TODO
- 类型:
-
WARNING_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L83 - 说明:
TODO
- 类型:
-
JOIN_HIGHLIGHTED_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L84 - 说明:
TODO
- 类型:
-
JOIN_SPRITE- 类型:
Identifier - 修饰符:
private static final - 源码定位:
L85 - 说明:
TODO
- 类型:
-
LOGGER- 类型:
Logger - 修饰符:
private static final - 源码定位:
L86 - 说明:
TODO
- 类型:
-
FROM_NEWER_TOOLTIP_1- 类型:
Component - 修饰符:
private static final - 源码定位:
L87 - 说明:
TODO
- 类型:
-
FROM_NEWER_TOOLTIP_2- 类型:
Component - 修饰符:
private static final - 源码定位:
L88 - 说明:
TODO
- 类型:
-
SNAPSHOT_TOOLTIP_1- 类型:
Component - 修饰符:
private static final - 源码定位:
L89 - 说明:
TODO
- 类型:
-
SNAPSHOT_TOOLTIP_2- 类型:
Component - 修饰符:
private static final - 源码定位:
L90 - 说明:
TODO
- 类型:
-
WORLD_LOCKED_TOOLTIP- 类型:
Component - 修饰符:
private static final - 源码定位:
L91 - 说明:
TODO
- 类型:
-
WORLD_REQUIRES_CONVERSION- 类型:
Component - 修饰符:
private static final - 源码定位:
L92 - 说明:
TODO
- 类型:
-
INCOMPATIBLE_VERSION_TOOLTIP- 类型:
Component - 修饰符:
private static final - 源码定位:
L93 - 说明:
TODO
- 类型:
-
WORLD_EXPERIMENTAL- 类型:
Component - 修饰符:
private static final - 源码定位:
L94 - 说明:
TODO
- 类型:
-
screen- 类型:
Screen - 修饰符:
private final - 源码定位:
L95 - 说明:
TODO
- 类型:
-
pendingLevels- 类型:
CompletableFuture<List<LevelSummary>> - 修饰符:
private - 源码定位:
L96 - 说明:
TODO
- 类型:
-
currentlyDisplayedLevels- 类型:
List<LevelSummary> - 修饰符:
private - 源码定位:
L97 - 说明:
TODO
- 类型:
-
loadingHeader- 类型:
WorldSelectionList.LoadingHeader - 修饰符:
private final - 源码定位:
L98 - 说明:
TODO
- 类型:
-
entryType- 类型:
WorldSelectionList.EntryType - 修饰符:
private final - 源码定位:
L99 - 说明:
TODO
- 类型:
-
filter- 类型:
String - 修饰符:
private - 源码定位:
L100 - 说明:
TODO
- 类型:
-
hasPolled- 类型:
boolean - 修饰符:
private - 源码定位:
L101 - 说明:
TODO
- 类型:
-
onEntrySelect- 类型:
Consumer<LevelSummary> - 修饰符:
private final - 源码定位:
L102 - 说明:
TODO
- 类型:
-
onEntryInteract- 类型:
Consumer<WorldSelectionList.WorldListEntry> - 修饰符:
private final - 源码定位:
L103 - 说明:
TODO
- 类型:
内部类/嵌套类型
-
net.minecraft.client.gui.screens.worldselection.WorldSelectionList.Builder- 类型:
class - 修饰符:
public static - 源码定位:
L286 - 说明:
TODO
- 类型:
-
net.minecraft.client.gui.screens.worldselection.WorldSelectionList.Entry- 类型:
class - 修饰符:
public abstract static - 源码定位:
L345 - 说明:
TODO
- 类型:
-
net.minecraft.client.gui.screens.worldselection.WorldSelectionList.EntryType- 类型:
enum - 修饰符:
public static - 源码定位:
L356 - 说明:
TODO
- 类型:
-
net.minecraft.client.gui.screens.worldselection.WorldSelectionList.LoadingHeader- 类型:
class - 修饰符:
public static - 源码定位:
L362 - 说明:
TODO
- 类型:
-
net.minecraft.client.gui.screens.worldselection.WorldSelectionList.NoWorldsEntry- 类型:
class - 修饰符:
public static final - 源码定位:
L388 - 说明:
TODO
- 类型:
-
net.minecraft.client.gui.screens.worldselection.WorldSelectionList.WorldListEntry- 类型:
class - 修饰符:
public final - 源码定位:
L409 - 说明:
TODO
- 类型:
构造器
private WorldSelectionList(Screen screen, Minecraft minecraft, int width, int height, String filter, WorldSelectionList oldList, Consumer<LevelSummary> onEntrySelect, Consumer<WorldSelectionList.WorldListEntry> onEntryInteract, WorldSelectionList.EntryType entryType) @ L105
- 构造器名:WorldSelectionList
- 源码定位:L105
- 修饰符:private
参数:
- screen: Screen
- minecraft: Minecraft
- width: int
- height: int
- filter: String
- oldList: WorldSelectionList
- onEntrySelect: Consumer
- onEntryInteract: Consumer<WorldSelectionList.WorldListEntry>
- entryType: WorldSelectionList.EntryType
说明:
TODO
方法
下面的方法块按源码顺序生成。
protected void clearEntries() @ L133
- 方法名:clearEntries
- 源码定位:L133
- 返回类型:void
- 修饰符:protected
参数:
- 无
说明:
TODO
private List<LevelSummary> pollLevelsIgnoreErrors() @ L139
- 方法名:pollLevelsIgnoreErrors
- 源码定位:L139
- 返回类型:List
- 修饰符:private
参数:
- 无
说明:
TODO
public void reloadWorldList() @ L157
- 方法名:reloadWorldList
- 源码定位:L157
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) @ L161
- 方法名:extractWidgetRenderState
- 源码定位:L161
- 返回类型:void
- 修饰符:public
参数:
- graphics: GuiGraphicsExtractor
- mouseX: int
- mouseY: int
- a: float
说明:
TODO
private void handleNewLevels(List<LevelSummary> levels) @ L171
- 方法名:handleNewLevels
- 源码定位:L171
- 返回类型:void
- 修饰符:private
参数:
- levels: List
说明:
TODO
public void updateFilter(String newFilter) @ L189
- 方法名:updateFilter
- 源码定位:L189
- 返回类型:void
- 修饰符:public
参数:
- newFilter: String
说明:
TODO
private CompletableFuture<List<LevelSummary>> loadLevels() @ L197
- 方法名:loadLevels
- 源码定位:L197
- 返回类型:CompletableFuture<List
> - 修饰符:private
参数:
- 无
说明:
TODO
private void fillLevels(String filter, List<LevelSummary> levels) @ L213
- 方法名:fillLevels
- 源码定位:L213
- 返回类型:void
- 修饰符:private
参数:
- filter: String
- levels: List
说明:
TODO
private boolean filterAccepts(String filter, LevelSummary level) @ L237
- 方法名:filterAccepts
- 源码定位:L237
- 返回类型:boolean
- 修饰符:private
参数:
- filter: String
- level: LevelSummary
说明:
TODO
private void notifyListUpdated() @ L241
- 方法名:notifyListUpdated
- 源码定位:L241
- 返回类型:void
- 修饰符:private
参数:
- 无
说明:
TODO
private void handleLevelLoadFailure(Component message) @ L246
- 方法名:handleLevelLoadFailure
- 源码定位:L246
- 返回类型:void
- 修饰符:private
参数:
- message: Component
说明:
TODO
public int getRowWidth() @ L250
- 方法名:getRowWidth
- 源码定位:L250
- 返回类型:int
- 修饰符:public
参数:
- 无
说明:
TODO
public void setSelected(WorldSelectionList.Entry selected) @ L255
- 方法名:setSelected
- 源码定位:L255
- 返回类型:void
- 修饰符:public
参数:
- selected: WorldSelectionList.Entry
说明:
TODO
public Optional<WorldSelectionList.WorldListEntry> getSelectedOpt() @ L262
- 方法名:getSelectedOpt
- 源码定位:L262
- 返回类型:Optional<WorldSelectionList.WorldListEntry>
- 修饰符:public
参数:
- 无
说明:
TODO
public void returnToScreen() @ L267
- 方法名:returnToScreen
- 源码定位:L267
- 返回类型:void
- 修饰符:public
参数:
- 无
说明:
TODO
public Screen getScreen() @ L272
- 方法名:getScreen
- 源码定位:L272
- 返回类型:Screen
- 修饰符:public
参数:
- 无
说明:
TODO
public void updateWidgetNarration(NarrationElementOutput output) @ L276
- 方法名:updateWidgetNarration
- 源码定位:L276
- 返回类型:void
- 修饰符:public
参数:
- output: NarrationElementOutput
说明:
TODO
代码
@OnlyIn(Dist.CLIENT)
public class WorldSelectionList extends ObjectSelectionList<WorldSelectionList.Entry> {
public static final DateTimeFormatter DATE_FORMAT = Util.localizedDateFormatter(FormatStyle.SHORT);
private static final Identifier ERROR_HIGHLIGHTED_SPRITE = Identifier.withDefaultNamespace("world_list/error_highlighted");
private static final Identifier ERROR_SPRITE = Identifier.withDefaultNamespace("world_list/error");
private static final Identifier MARKED_JOIN_HIGHLIGHTED_SPRITE = Identifier.withDefaultNamespace("world_list/marked_join_highlighted");
private static final Identifier MARKED_JOIN_SPRITE = Identifier.withDefaultNamespace("world_list/marked_join");
private static final Identifier WARNING_HIGHLIGHTED_SPRITE = Identifier.withDefaultNamespace("world_list/warning_highlighted");
private static final Identifier WARNING_SPRITE = Identifier.withDefaultNamespace("world_list/warning");
private static final Identifier JOIN_HIGHLIGHTED_SPRITE = Identifier.withDefaultNamespace("world_list/join_highlighted");
private static final Identifier JOIN_SPRITE = Identifier.withDefaultNamespace("world_list/join");
private static final Logger LOGGER = LogUtils.getLogger();
private static final Component FROM_NEWER_TOOLTIP_1 = Component.translatable("selectWorld.tooltip.fromNewerVersion1").withStyle(ChatFormatting.RED);
private static final Component FROM_NEWER_TOOLTIP_2 = Component.translatable("selectWorld.tooltip.fromNewerVersion2").withStyle(ChatFormatting.RED);
private static final Component SNAPSHOT_TOOLTIP_1 = Component.translatable("selectWorld.tooltip.snapshot1").withStyle(ChatFormatting.GOLD);
private static final Component SNAPSHOT_TOOLTIP_2 = Component.translatable("selectWorld.tooltip.snapshot2").withStyle(ChatFormatting.GOLD);
private static final Component WORLD_LOCKED_TOOLTIP = Component.translatable("selectWorld.locked").withStyle(ChatFormatting.RED);
private static final Component WORLD_REQUIRES_CONVERSION = Component.translatable("selectWorld.conversion.tooltip").withStyle(ChatFormatting.RED);
private static final Component INCOMPATIBLE_VERSION_TOOLTIP = Component.translatable("selectWorld.incompatible.tooltip").withStyle(ChatFormatting.RED);
private static final Component WORLD_EXPERIMENTAL = Component.translatable("selectWorld.experimental");
private final Screen screen;
private CompletableFuture<List<LevelSummary>> pendingLevels;
private @Nullable List<LevelSummary> currentlyDisplayedLevels;
private final WorldSelectionList.LoadingHeader loadingHeader;
private final WorldSelectionList.EntryType entryType;
private String filter;
private boolean hasPolled;
private final @Nullable Consumer<LevelSummary> onEntrySelect;
private final @Nullable Consumer<WorldSelectionList.WorldListEntry> onEntryInteract;
private WorldSelectionList(
Screen screen,
Minecraft minecraft,
int width,
int height,
String filter,
@Nullable WorldSelectionList oldList,
@Nullable Consumer<LevelSummary> onEntrySelect,
@Nullable Consumer<WorldSelectionList.WorldListEntry> onEntryInteract,
WorldSelectionList.EntryType entryType
) {
super(minecraft, width, height, 0, 36);
this.screen = screen;
this.loadingHeader = new WorldSelectionList.LoadingHeader(minecraft);
this.filter = filter;
this.onEntrySelect = onEntrySelect;
this.onEntryInteract = onEntryInteract;
this.entryType = entryType;
if (oldList != null) {
this.pendingLevels = oldList.pendingLevels;
} else {
this.pendingLevels = this.loadLevels();
}
this.addEntry(this.loadingHeader);
this.handleNewLevels(this.pollLevelsIgnoreErrors());
}
@Override
protected void clearEntries() {
this.children().forEach(WorldSelectionList.Entry::close);
super.clearEntries();
}
private @Nullable List<LevelSummary> pollLevelsIgnoreErrors() {
try {
List<LevelSummary> completedLevels = this.pendingLevels.getNow(null);
if (this.entryType == WorldSelectionList.EntryType.UPLOAD_WORLD) {
if (completedLevels == null || this.hasPolled) {
return null;
}
this.hasPolled = true;
completedLevels = completedLevels.stream().filter(LevelSummary::canUpload).toList();
}
return completedLevels;
} catch (CancellationException | CompletionException var2) {
return null;
}
}
public void reloadWorldList() {
this.pendingLevels = this.loadLevels();
}
@Override
public void extractWidgetRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float a) {
List<LevelSummary> newLevels = this.pollLevelsIgnoreErrors();
if (newLevels != this.currentlyDisplayedLevels) {
this.handleNewLevels(newLevels);
}
super.extractWidgetRenderState(graphics, mouseX, mouseY, a);
}
private void handleNewLevels(@Nullable List<LevelSummary> levels) {
if (levels != null) {
if (levels.isEmpty()) {
switch (this.entryType) {
case SINGLEPLAYER:
CreateWorldScreen.openFresh(this.minecraft, () -> this.minecraft.setScreen(null));
break;
case UPLOAD_WORLD:
this.clearEntries();
this.addEntry(new WorldSelectionList.NoWorldsEntry(Component.translatable("mco.upload.select.world.none"), this.screen.getFont()));
}
} else {
this.fillLevels(this.filter, levels);
this.currentlyDisplayedLevels = levels;
}
}
}
public void updateFilter(String newFilter) {
if (this.currentlyDisplayedLevels != null && !newFilter.equals(this.filter)) {
this.fillLevels(newFilter, this.currentlyDisplayedLevels);
}
this.filter = newFilter;
}
private CompletableFuture<List<LevelSummary>> loadLevels() {
LevelStorageSource.LevelCandidates levelCandidates;
try {
levelCandidates = this.minecraft.getLevelSource().findLevelCandidates();
} catch (LevelStorageException var3) {
LOGGER.error("Couldn't load level list", (Throwable)var3);
this.handleLevelLoadFailure(var3.getMessageComponent());
return CompletableFuture.completedFuture(List.of());
}
return this.minecraft.getLevelSource().loadLevelSummaries(levelCandidates).exceptionally(throwable -> {
this.minecraft.delayCrash(CrashReport.forThrowable(throwable, "Couldn't load level list"));
return List.of();
});
}
private void fillLevels(String filter, List<LevelSummary> levels) {
List<WorldSelectionList.Entry> worldEntries = new ArrayList<>();
Optional<WorldSelectionList.WorldListEntry> selectedOpt = this.getSelectedOpt();
WorldSelectionList.WorldListEntry entryToSelect = null;
for (LevelSummary level : levels.stream().filter(levelx -> this.filterAccepts(filter.toLowerCase(Locale.ROOT), levelx)).toList()) {
WorldSelectionList.WorldListEntry worldListEntry = new WorldSelectionList.WorldListEntry(this, level);
if (selectedOpt.isPresent() && selectedOpt.get().getLevelSummary().getLevelId().equals(worldListEntry.getLevelSummary().getLevelId())) {
entryToSelect = worldListEntry;
}
worldEntries.add(worldListEntry);
}
this.removeEntries(this.children().stream().filter(entry -> !worldEntries.contains(entry)).toList());
worldEntries.forEach(entry -> {
if (!this.children().contains(entry)) {
this.addEntry(entry);
}
});
this.setSelected((WorldSelectionList.Entry)entryToSelect);
this.notifyListUpdated();
}
private boolean filterAccepts(String filter, LevelSummary level) {
return level.getLevelName().toLowerCase(Locale.ROOT).contains(filter) || level.getLevelId().toLowerCase(Locale.ROOT).contains(filter);
}
private void notifyListUpdated() {
this.refreshScrollAmount();
this.screen.triggerImmediateNarration(true);
}
private void handleLevelLoadFailure(Component message) {
this.minecraft.setScreen(new ErrorScreen(Component.translatable("selectWorld.unable_to_load"), message));
}
@Override
public int getRowWidth() {
return 270;
}
public void setSelected(WorldSelectionList.@Nullable Entry selected) {
super.setSelected(selected);
if (this.onEntrySelect != null) {
this.onEntrySelect.accept(selected instanceof WorldSelectionList.WorldListEntry entry ? entry.summary : null);
}
}
public Optional<WorldSelectionList.WorldListEntry> getSelectedOpt() {
WorldSelectionList.Entry selected = this.getSelected();
return selected instanceof WorldSelectionList.WorldListEntry worldEntry ? Optional.of(worldEntry) : Optional.empty();
}
public void returnToScreen() {
this.reloadWorldList();
this.minecraft.setScreen(this.screen);
}
public Screen getScreen() {
return this.screen;
}
@Override
public void updateWidgetNarration(NarrationElementOutput output) {
if (this.children().contains(this.loadingHeader)) {
this.loadingHeader.updateNarration(output);
} else {
super.updateWidgetNarration(output);
}
}
@OnlyIn(Dist.CLIENT)
public static class Builder {
private final Minecraft minecraft;
private final Screen screen;
private int width;
private int height;
private String filter = "";
private WorldSelectionList.EntryType type = WorldSelectionList.EntryType.SINGLEPLAYER;
private @Nullable WorldSelectionList oldList = null;
private @Nullable Consumer<LevelSummary> onEntrySelect = null;
private @Nullable Consumer<WorldSelectionList.WorldListEntry> onEntryInteract = null;
public Builder(Minecraft minecraft, Screen screen) {
this.minecraft = minecraft;
this.screen = screen;
}
public WorldSelectionList.Builder width(int width) {
this.width = width;
return this;
}
public WorldSelectionList.Builder height(int height) {
this.height = height;
return this;
}
public WorldSelectionList.Builder filter(String filter) {
this.filter = filter;
return this;
}
public WorldSelectionList.Builder oldList(@Nullable WorldSelectionList oldList) {
this.oldList = oldList;
return this;
}
public WorldSelectionList.Builder onEntrySelect(Consumer<LevelSummary> onEntrySelect) {
this.onEntrySelect = onEntrySelect;
return this;
}
public WorldSelectionList.Builder onEntryInteract(Consumer<WorldSelectionList.WorldListEntry> onEntryInteract) {
this.onEntryInteract = onEntryInteract;
return this;
}
public WorldSelectionList.Builder uploadWorld() {
this.type = WorldSelectionList.EntryType.UPLOAD_WORLD;
return this;
}
public WorldSelectionList build() {
return new WorldSelectionList(
this.screen, this.minecraft, this.width, this.height, this.filter, this.oldList, this.onEntrySelect, this.onEntryInteract, this.type
);
}
}
@OnlyIn(Dist.CLIENT)
public abstract static class Entry extends ObjectSelectionList.Entry<WorldSelectionList.Entry> implements AutoCloseable {
@Override
public void close() {
}
public @Nullable LevelSummary getLevelSummary() {
return null;
}
}
@OnlyIn(Dist.CLIENT)
public static enum EntryType {
SINGLEPLAYER,
UPLOAD_WORLD;
}
@OnlyIn(Dist.CLIENT)
public static class LoadingHeader extends WorldSelectionList.Entry {
private static final Component LOADING_LABEL = Component.translatable("selectWorld.loading_list");
private final Minecraft minecraft;
public LoadingHeader(Minecraft minecraft) {
this.minecraft = minecraft;
}
@Override
public void extractContent(GuiGraphicsExtractor graphics, int mouseX, int mouseY, boolean hovered, float a) {
int labelX = (this.minecraft.screen.width - this.minecraft.font.width(LOADING_LABEL)) / 2;
int labelY = this.getContentY() + (this.getContentHeight() - 9) / 2;
graphics.text(this.minecraft.font, LOADING_LABEL, labelX, labelY, -1);
String dots = LoadingDotsText.get(Util.getMillis());
int dotsX = (this.minecraft.screen.width - this.minecraft.font.width(dots)) / 2;
int dotsY = labelY + 9;
graphics.text(this.minecraft.font, dots, dotsX, dotsY, -8355712);
}
@Override
public Component getNarration() {
return LOADING_LABEL;
}
}
@OnlyIn(Dist.CLIENT)
public static final class NoWorldsEntry extends WorldSelectionList.Entry {
private final StringWidget stringWidget;
public NoWorldsEntry(Component component, Font font) {
this.stringWidget = new StringWidget(component, font);
}
@Override
public Component getNarration() {
return this.stringWidget.getMessage();
}
@Override
public void extractContent(GuiGraphicsExtractor graphics, int mouseX, int mouseY, boolean hovered, float a) {
this.stringWidget
.setPosition(this.getContentXMiddle() - this.stringWidget.getWidth() / 2, this.getContentYMiddle() - this.stringWidget.getHeight() / 2);
this.stringWidget.extractRenderState(graphics, mouseX, mouseY, a);
}
}
@OnlyIn(Dist.CLIENT)
public final class WorldListEntry extends WorldSelectionList.Entry implements SelectableEntry {
private static final int ICON_SIZE = 32;
private final WorldSelectionList list;
private final Minecraft minecraft;
private final Screen screen;
private final LevelSummary summary;
private final FaviconTexture icon;
private final StringWidget worldNameText;
private final StringWidget idAndLastPlayedText;
private final StringWidget infoText;
private @Nullable Path iconFile;
public WorldListEntry(WorldSelectionList list, LevelSummary summary) {
Objects.requireNonNull(WorldSelectionList.this);
super();
this.list = list;
this.minecraft = list.minecraft;
this.screen = list.getScreen();
this.summary = summary;
this.icon = FaviconTexture.forWorld(this.minecraft.getTextureManager(), summary.getLevelId());
this.iconFile = summary.getIcon();
int maxTextWidth = list.getRowWidth() - this.getTextX() - 2;
Component worldNameComponent = Component.literal(summary.getLevelName());
this.worldNameText = new StringWidget(worldNameComponent, this.minecraft.font);
this.worldNameText.setMaxWidth(maxTextWidth);
if (this.minecraft.font.width(worldNameComponent) > maxTextWidth) {
this.worldNameText.setTooltip(Tooltip.create(worldNameComponent));
}
String levelIdAndDate = summary.getLevelId();
long lastPlayed = summary.getLastPlayed();
if (lastPlayed != -1L) {
ZonedDateTime lastPlayedTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(lastPlayed), ZoneId.systemDefault());
levelIdAndDate = levelIdAndDate + " (" + WorldSelectionList.DATE_FORMAT.format(lastPlayedTime) + ")";
}
Component levelIdAndDateComponent = Component.literal(levelIdAndDate).withColor(-8355712);
this.idAndLastPlayedText = new StringWidget(levelIdAndDateComponent, this.minecraft.font);
this.idAndLastPlayedText.setMaxWidth(maxTextWidth);
if (this.minecraft.font.width(levelIdAndDate) > maxTextWidth) {
this.idAndLastPlayedText.setTooltip(Tooltip.create(levelIdAndDateComponent));
}
Component info = ComponentUtils.mergeStyles(summary.getInfo(), Style.EMPTY.withColor(-8355712));
this.infoText = new StringWidget(info, this.minecraft.font);
this.infoText.setMaxWidth(maxTextWidth);
if (this.minecraft.font.width(info) > maxTextWidth) {
this.infoText.setTooltip(Tooltip.create(info));
}
this.validateIconFile();
this.loadIcon();
}
private void validateIconFile() {
if (this.iconFile != null) {
try {
BasicFileAttributes attributes = Files.readAttributes(this.iconFile, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
if (attributes.isSymbolicLink()) {
List<ForbiddenSymlinkInfo> issues = this.minecraft.directoryValidator().validateSymlink(this.iconFile);
if (!issues.isEmpty()) {
WorldSelectionList.LOGGER.warn("{}", ContentValidationException.getMessage(this.iconFile, issues));
this.iconFile = null;
} else {
attributes = Files.readAttributes(this.iconFile, BasicFileAttributes.class);
}
}
if (!attributes.isRegularFile()) {
this.iconFile = null;
}
} catch (NoSuchFileException var3) {
this.iconFile = null;
} catch (IOException var4) {
WorldSelectionList.LOGGER.error("could not validate symlink", (Throwable)var4);
this.iconFile = null;
}
}
}
@Override
public Component getNarration() {
Component entryNarration = Component.translatable(
"narrator.select.world_info",
this.summary.getLevelName(),
Component.translationArg(new Date(this.summary.getLastPlayed())),
this.summary.getInfo()
);
if (this.summary.isLocked()) {
entryNarration = CommonComponents.joinForNarration(entryNarration, WorldSelectionList.WORLD_LOCKED_TOOLTIP);
}
if (this.summary.isExperimental()) {
entryNarration = CommonComponents.joinForNarration(entryNarration, WorldSelectionList.WORLD_EXPERIMENTAL);
}
return Component.translatable("narrator.select", entryNarration);
}
@Override
public void extractContent(GuiGraphicsExtractor graphics, int mouseX, int mouseY, boolean hovered, float a) {
int textX = this.getTextX();
this.worldNameText.setPosition(textX, this.getContentY() + 1);
this.worldNameText.extractRenderState(graphics, mouseX, mouseY, a);
this.idAndLastPlayedText.setPosition(textX, this.getContentY() + 9 + 3);
this.idAndLastPlayedText.extractRenderState(graphics, mouseX, mouseY, a);
this.infoText.setPosition(textX, this.getContentY() + 9 + 9 + 3);
this.infoText.extractRenderState(graphics, mouseX, mouseY, a);
graphics.blit(RenderPipelines.GUI_TEXTURED, this.icon.textureLocation(), this.getContentX(), this.getContentY(), 0.0F, 0.0F, 32, 32, 32, 32);
if (this.list.entryType == WorldSelectionList.EntryType.SINGLEPLAYER && (this.minecraft.options.touchscreen().get() || hovered)) {
graphics.fill(this.getContentX(), this.getContentY(), this.getContentX() + 32, this.getContentY() + 32, -1601138544);
int relX = mouseX - this.getContentX();
int relY = mouseY - this.getContentY();
boolean isOverIcon = this.mouseOverIcon(relX, relY, 32);
Identifier joinSprite = isOverIcon ? WorldSelectionList.JOIN_HIGHLIGHTED_SPRITE : WorldSelectionList.JOIN_SPRITE;
Identifier warningSprite = isOverIcon ? WorldSelectionList.WARNING_HIGHLIGHTED_SPRITE : WorldSelectionList.WARNING_SPRITE;
Identifier errorSprite = isOverIcon ? WorldSelectionList.ERROR_HIGHLIGHTED_SPRITE : WorldSelectionList.ERROR_SPRITE;
Identifier joinWithErrorSprite = isOverIcon ? WorldSelectionList.MARKED_JOIN_HIGHLIGHTED_SPRITE : WorldSelectionList.MARKED_JOIN_SPRITE;
if (this.summary instanceof LevelSummary.SymlinkLevelSummary || this.summary instanceof LevelSummary.CorruptedLevelSummary) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, errorSprite, this.getContentX(), this.getContentY(), 32, 32);
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, joinWithErrorSprite, this.getContentX(), this.getContentY(), 32, 32);
return;
}
if (this.summary.isLocked()) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, errorSprite, this.getContentX(), this.getContentY(), 32, 32);
if (isOverIcon) {
graphics.setTooltipForNextFrame(this.minecraft.font.split(WorldSelectionList.WORLD_LOCKED_TOOLTIP, 175), mouseX, mouseY);
}
} else if (this.summary.requiresManualConversion()) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, errorSprite, this.getContentX(), this.getContentY(), 32, 32);
if (isOverIcon) {
graphics.setTooltipForNextFrame(this.minecraft.font.split(WorldSelectionList.WORLD_REQUIRES_CONVERSION, 175), mouseX, mouseY);
}
} else if (!this.summary.isCompatible()) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, errorSprite, this.getContentX(), this.getContentY(), 32, 32);
if (isOverIcon) {
graphics.setTooltipForNextFrame(this.minecraft.font.split(WorldSelectionList.INCOMPATIBLE_VERSION_TOOLTIP, 175), mouseX, mouseY);
}
} else if (this.summary.shouldBackup()) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, joinWithErrorSprite, this.getContentX(), this.getContentY(), 32, 32);
if (this.summary.isDowngrade()) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, errorSprite, this.getContentX(), this.getContentY(), 32, 32);
if (isOverIcon) {
graphics.setTooltipForNextFrame(
ImmutableList.of(
WorldSelectionList.FROM_NEWER_TOOLTIP_1.getVisualOrderText(), WorldSelectionList.FROM_NEWER_TOOLTIP_2.getVisualOrderText()
),
mouseX,
mouseY
);
}
} else if (!SharedConstants.getCurrentVersion().stable()) {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, warningSprite, this.getContentX(), this.getContentY(), 32, 32);
if (isOverIcon) {
graphics.setTooltipForNextFrame(
ImmutableList.of(
WorldSelectionList.SNAPSHOT_TOOLTIP_1.getVisualOrderText(), WorldSelectionList.SNAPSHOT_TOOLTIP_2.getVisualOrderText()
),
mouseX,
mouseY
);
}
}
if (isOverIcon) {
WorldSelectionList.this.handleCursor(graphics);
}
} else {
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, joinSprite, this.getContentX(), this.getContentY(), 32, 32);
if (isOverIcon) {
WorldSelectionList.this.handleCursor(graphics);
}
}
}
}
private int getTextX() {
return this.getContentX() + 32 + 3;
}
@Override
public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) {
if (this.canInteract()) {
int relX = (int)event.x() - this.getContentX();
int relY = (int)event.y() - this.getContentY();
if (doubleClick || this.mouseOverIcon(relX, relY, 32) && this.list.entryType == WorldSelectionList.EntryType.SINGLEPLAYER) {
this.minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
Consumer<WorldSelectionList.WorldListEntry> onEntryInteract = this.list.onEntryInteract;
if (onEntryInteract != null) {
onEntryInteract.accept(this);
return true;
}
}
}
return super.mouseClicked(event, doubleClick);
}
@Override
public boolean keyPressed(KeyEvent event) {
if (event.isSelection() && this.canInteract()) {
this.minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
Consumer<WorldSelectionList.WorldListEntry> onEntryInteract = this.list.onEntryInteract;
if (onEntryInteract != null) {
onEntryInteract.accept(this);
return true;
}
}
return super.keyPressed(event);
}
public boolean canInteract() {
return this.summary.primaryActionActive() || this.list.entryType == WorldSelectionList.EntryType.UPLOAD_WORLD;
}
public void joinWorld() {
if (this.summary.primaryActionActive()) {
if (this.summary instanceof LevelSummary.SymlinkLevelSummary) {
this.minecraft.setScreen(NoticeWithLinkScreen.createWorldSymlinkWarningScreen(() -> this.minecraft.setScreen(this.screen)));
} else {
this.minecraft.createWorldOpenFlows().openWorld(this.summary.getLevelId(), this.list::returnToScreen);
}
}
}
public void deleteWorld() {
this.minecraft
.setScreen(
new ConfirmScreen(
result -> {
if (result) {
this.minecraft.setScreen(new ProgressScreen(true));
this.doDeleteWorld();
}
this.list.returnToScreen();
},
Component.translatable("selectWorld.deleteQuestion"),
Component.translatable("selectWorld.deleteWarning", this.summary.getLevelName()),
Component.translatable("selectWorld.deleteButton"),
CommonComponents.GUI_CANCEL
)
);
}
public void doDeleteWorld() {
LevelStorageSource levelSource = this.minecraft.getLevelSource();
String levelId = this.summary.getLevelId();
try (LevelStorageSource.LevelStorageAccess access = levelSource.createAccess(levelId)) {
access.deleteLevel();
} catch (IOException var8) {
SystemToast.onWorldDeleteFailure(this.minecraft, levelId);
WorldSelectionList.LOGGER.error("Failed to delete world {}", levelId, var8);
}
}
public void editWorld() {
this.queueLoadScreen();
String levelId = this.summary.getLevelId();
LevelStorageSource.LevelStorageAccess access;
try {
access = this.minecraft.getLevelSource().validateAndCreateAccess(levelId);
} catch (IOException var6) {
SystemToast.onWorldAccessFailure(this.minecraft, levelId);
WorldSelectionList.LOGGER.error("Failed to access level {}", levelId, var6);
this.list.reloadWorldList();
return;
} catch (ContentValidationException var7) {
WorldSelectionList.LOGGER.warn("{}", var7.getMessage());
this.minecraft.setScreen(NoticeWithLinkScreen.createWorldSymlinkWarningScreen(() -> this.minecraft.setScreen(this.screen)));
return;
}
EditWorldScreen editScreen;
try {
editScreen = EditWorldScreen.create(this.minecraft, access, result -> {
access.safeClose();
this.list.returnToScreen();
});
} catch (NbtException | ReportedNbtException | IOException var5) {
access.safeClose();
SystemToast.onWorldAccessFailure(this.minecraft, levelId);
WorldSelectionList.LOGGER.error("Failed to load world data {}", levelId, var5);
this.list.reloadWorldList();
return;
}
this.minecraft.setScreen(editScreen);
}
public void recreateWorld() {
this.queueLoadScreen();
try (LevelStorageSource.LevelStorageAccess access = this.minecraft.getLevelSource().validateAndCreateAccess(this.summary.getLevelId())) {
Pair<LevelSettings, WorldCreationContext> recreatedSettings = this.minecraft.createWorldOpenFlows().recreateWorldData(access);
LevelSettings levelSettings = recreatedSettings.getFirst();
WorldCreationContext creationContext = recreatedSettings.getSecond();
Path dataPackDir = CreateWorldScreen.createTempDataPackDirFromExistingWorld(access.getLevelPath(LevelResource.DATAPACK_DIR), this.minecraft);
creationContext.validate();
if (creationContext.options().isOldCustomizedWorld()) {
this.minecraft
.setScreen(
new ConfirmScreen(
result -> this.minecraft
.setScreen(
(Screen)(result
? CreateWorldScreen.createFromExisting(
this.minecraft, this.list::returnToScreen, levelSettings, creationContext, dataPackDir
)
: this.screen)
),
Component.translatable("selectWorld.recreate.customized.title"),
Component.translatable("selectWorld.recreate.customized.text"),
CommonComponents.GUI_PROCEED,
CommonComponents.GUI_CANCEL
)
);
} else {
this.minecraft
.setScreen(CreateWorldScreen.createFromExisting(this.minecraft, this.list::returnToScreen, levelSettings, creationContext, dataPackDir));
}
} catch (ContentValidationException var8) {
WorldSelectionList.LOGGER.warn("{}", var8.getMessage());
this.minecraft.setScreen(NoticeWithLinkScreen.createWorldSymlinkWarningScreen(() -> this.minecraft.setScreen(this.screen)));
} catch (Exception var9) {
WorldSelectionList.LOGGER.error("Unable to recreate world", (Throwable)var9);
this.minecraft
.setScreen(
new AlertScreen(
() -> this.minecraft.setScreen(this.screen),
Component.translatable("selectWorld.recreate.error.title"),
Component.translatable("selectWorld.recreate.error.text")
)
);
}
}
private void queueLoadScreen() {
this.minecraft.setScreenAndShow(new GenericMessageScreen(Component.translatable("selectWorld.data_read")));
}
private void loadIcon() {
boolean shouldHaveIcon = this.iconFile != null && Files.isRegularFile(this.iconFile);
if (shouldHaveIcon) {
try (InputStream stream = Files.newInputStream(this.iconFile)) {
this.icon.upload(NativeImage.read(stream));
} catch (Throwable var7) {
WorldSelectionList.LOGGER.error("Invalid icon for world {}", this.summary.getLevelId(), var7);
this.iconFile = null;
}
} else {
this.icon.clear();
}
}
@Override
public void close() {
if (!this.icon.isClosed()) {
this.icon.close();
}
}
public String getLevelName() {
return this.summary.getLevelName();
}
@Override
public LevelSummary getLevelSummary() {
return this.summary;
}
}
}引用的其他类
-
- 引用位置:
方法调用 - 关联成员:
NativeImage.read()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
CrashReport.forThrowable()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SharedConstants.getCurrentVersion()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
继承
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
StringWidget()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Tooltip.create()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SystemToast.onWorldAccessFailure(), SystemToast.onWorldDeleteFailure()
- 引用位置:
-
- 引用位置:
参数
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
AlertScreen()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ConfirmScreen()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ErrorScreen()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
FaviconTexture.forWorld()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
GenericMessageScreen()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
LoadingDotsText.get()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
NoticeWithLinkScreen.createWorldSymlinkWarningScreen()
- 引用位置:
-
- 引用位置:
构造调用 - 关联成员:
ProgressScreen()
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
CreateWorldScreen.createFromExisting(), CreateWorldScreen.createTempDataPackDirFromExistingWorld(), CreateWorldScreen.openFresh()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
EditWorldScreen.create()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
SimpleSoundInstance.forUI()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
CommonComponents.joinForNarration()
- 引用位置:
-
- 引用位置:
参数/字段/方法调用 - 关联成员:
Component.literal(), Component.translatable(), Component.translationArg()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ComponentUtils.mergeStyles()
- 引用位置:
-
- 引用位置:
字段/方法调用 - 关联成员:
Identifier.withDefaultNamespace()
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
Util.getMillis(), Util.localizedDateFormatter()
- 引用位置:
-
- 引用位置:
参数/字段/返回值
- 引用位置:
-
- 引用位置:
方法调用 - 关联成员:
ContentValidationException.getMessage()
- 引用位置: