forked from QuiltMC/quilt-standard-libraries
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port FabricMC/fabric#2856 and attempt to add a confirmation screen.
- Loading branch information
1 parent
a781ebc
commit b821808
Showing
11 changed files
with
435 additions
and
12 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
library/core/resource_loader/src/main/resources/assets/quilt_resource_loader/lang/en_us.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"quilt.error.id.malformed": "The identifier \"%s\" is malformed.", | ||
"quilt.error.registry.get_element_failure": "Could not find element \"%s\" in the registry." | ||
} |
4 changes: 4 additions & 0 deletions
4
library/core/resource_loader/src/main/resources/assets/quilt_resource_loader/lang/fr_fr.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"quilt.error.id.malformed": "L'identifiant \"%s\" est malformé.", | ||
"quilt.error.registry.get_element_failure": "L'élément \"%s\" n'a pas pu être trouvé dans le registre." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
...ain/java/org/quiltmc/qsl/worldgen/dimension/impl/DimensionDeserializationFailHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright 2023 QuiltMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.quiltmc.qsl.worldgen.dimension.impl; | ||
|
||
import java.util.List; | ||
|
||
import org.jetbrains.annotations.ApiStatus; | ||
|
||
@ApiStatus.Internal | ||
public interface DimensionDeserializationFailHandler { | ||
boolean shouldContinueLoad(List<FailSoftMapCodec.DecodeError> errors); | ||
} |
186 changes: 186 additions & 0 deletions
186
...gen/dimension/src/main/java/org/quiltmc/qsl/worldgen/dimension/impl/FailSoftMapCodec.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
/* | ||
* Copyright 2016, 2017, 2018, 2019 FabricMC | ||
* Copyright 2023 QuiltMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.quiltmc.qsl.worldgen.dimension.impl; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import com.mojang.datafixers.util.Pair; | ||
import com.mojang.serialization.Codec; | ||
import com.mojang.serialization.DataResult; | ||
import com.mojang.serialization.DynamicOps; | ||
import com.mojang.serialization.Lifecycle; | ||
import com.mojang.serialization.MapLike; | ||
import com.mojang.serialization.codecs.BaseMapCodec; | ||
import com.mojang.serialization.codecs.UnboundedMapCodec; | ||
import org.jetbrains.annotations.ApiStatus; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import net.minecraft.text.HoverEvent; | ||
import net.minecraft.text.Text; | ||
|
||
/** | ||
* Has the same functionality as {@link UnboundedMapCodec} but it will fail-soft when an entry cannot be deserialized. | ||
*/ | ||
@ApiStatus.Internal | ||
public class FailSoftMapCodec<K, V> implements BaseMapCodec<K, V>, Codec<Map<K, V>> { | ||
public static final Logger LOGGER = LoggerFactory.getLogger("FailSoftMapCodec"); | ||
|
||
private final Codec<K> keyCodec; | ||
private final Codec<V> elementCodec; | ||
private final List<DecodeError> errors = new ArrayList<>(); | ||
|
||
public FailSoftMapCodec(final Codec<K> keyCodec, final Codec<V> elementCodec) { | ||
this.keyCodec = keyCodec; | ||
this.elementCodec = elementCodec; | ||
} | ||
|
||
@Override | ||
public Codec<K> keyCodec() { | ||
return this.keyCodec; | ||
} | ||
|
||
@Override | ||
public Codec<V> elementCodec() { | ||
return this.elementCodec; | ||
} | ||
|
||
public List<DecodeError> getErrors() { | ||
return this.errors; | ||
} | ||
|
||
@Override | ||
public <T> DataResult<Pair<Map<K, V>, T>> decode(final DynamicOps<T> ops, final T input) { | ||
return ops.getMap(input).setLifecycle(Lifecycle.stable()).flatMap(map -> decode(ops, map)).map(r -> Pair.of(r, input)); | ||
} | ||
|
||
@Override | ||
public <T> DataResult<T> encode(final Map<K, V> input, final DynamicOps<T> ops, final T prefix) { | ||
return this.encode(input, ops, ops.mapBuilder()).build(prefix); | ||
} | ||
|
||
/** | ||
* In {@link BaseMapCodec#decode(DynamicOps, MapLike)}, the whole deserialization will fail if one element fails. | ||
* {@code apply2stable} will return fail when any of the two elements is failed. | ||
* In this implementation, if one deserialization fails, it will log and ignore. | ||
*/ | ||
@Override | ||
public <T> DataResult<Map<K, V>> decode(final DynamicOps<T> ops, final MapLike<T> input) { | ||
final ImmutableMap.Builder<K, V> builder = ImmutableMap.builder(); | ||
this.errors.clear(); | ||
|
||
input.entries().forEach(pair -> { | ||
try { | ||
final DataResult<K> k = this.keyCodec().parse(ops, pair.getFirst()); | ||
final DataResult<V> v = this.elementCodec().parse(ops, pair.getSecond()); | ||
|
||
k.get().ifRight(partial -> { | ||
LOGGER.error("Failed to decode key {} from {} {}", k, pair, partial); | ||
|
||
this.errors.add(new DecodeError(Kind.KEY, partial.message())); | ||
}); | ||
v.get().ifRight(partial -> { | ||
LOGGER.error("Failed to decode value {} from {} {}", v, pair, partial); | ||
|
||
this.errors.add(new DecodeError(Kind.VALUE, partial.message())); | ||
}); | ||
|
||
if (k.get().left().isPresent() && v.get().left().isPresent()) { | ||
builder.put(k.get().left().get(), v.get().left().get()); | ||
} // ignore failure | ||
} catch (Throwable e) { | ||
LOGGER.error("Decoding {}", pair, e); | ||
} | ||
}); | ||
|
||
final Map<K, V> elements = builder.build(); | ||
|
||
return DataResult.success(elements); | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
|
||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
|
||
final var that = (FailSoftMapCodec<?, ?>) o; | ||
return Objects.equals(this.keyCodec, that.keyCodec) && Objects.equals(this.elementCodec, that.elementCodec); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(this.keyCodec, this.elementCodec); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "FailSoftMapCodec[" + this.keyCodec + " -> " + this.elementCodec + ']'; | ||
} | ||
|
||
public enum Kind { | ||
KEY { | ||
private static final String NOT_A_VALID_ID = "Not a valid resource location: "; | ||
|
||
@Override | ||
Text getFancyMessage(String originalMessage) { | ||
if (originalMessage.startsWith(NOT_A_VALID_ID)) { | ||
var substr = originalMessage.substring(NOT_A_VALID_ID.length()); | ||
|
||
return Text.translatable("quilt.error.id.malformed", | ||
substr.substring(0, substr.indexOf(" ")) | ||
) | ||
.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(originalMessage)))); | ||
} | ||
|
||
return Text.literal(originalMessage); | ||
} | ||
}, | ||
VALUE { | ||
private static final String FAILED_TO_GET_ELEMENT = "Failed to get element "; | ||
|
||
@Override | ||
Text getFancyMessage(String originalMessage) { | ||
if (originalMessage.startsWith(FAILED_TO_GET_ELEMENT)) { | ||
return Text.translatable("quilt.error.registry.get_element_failure", | ||
originalMessage.substring(FAILED_TO_GET_ELEMENT.length()) | ||
) | ||
.styled(style -> style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(originalMessage)))); | ||
} | ||
|
||
return Text.literal(originalMessage); | ||
} | ||
}; | ||
|
||
abstract Text getFancyMessage(String originalMessage); | ||
} | ||
|
||
public record DecodeError(Kind kind, String message) { | ||
public Text getFancyMessage() { | ||
return this.kind.getFancyMessage(this.message); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
...rc/main/java/org/quiltmc/qsl/worldgen/dimension/impl/client/ClientQuiltDimensionsMod.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright 2023 QuiltMC | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.quiltmc.qsl.worldgen.dimension.impl.client; | ||
|
||
import org.jetbrains.annotations.ApiStatus; | ||
|
||
import net.minecraft.client.MinecraftClient; | ||
import net.minecraft.client.gui.screen.ConfirmScreen; | ||
import net.minecraft.text.Text; | ||
|
||
import org.quiltmc.loader.api.ModContainer; | ||
import org.quiltmc.qsl.base.api.entrypoint.client.ClientModInitializer; | ||
import org.quiltmc.qsl.worldgen.dimension.impl.FailSoftMapCodec; | ||
import org.quiltmc.qsl.worldgen.dimension.impl.QuiltDimensionsImpl; | ||
import org.quiltmc.qsl.worldgen.dimension.mixin.client.MinecraftClientAccessor; | ||
|
||
@ApiStatus.Internal | ||
public class ClientQuiltDimensionsMod implements ClientModInitializer { | ||
@Override | ||
public void onInitializeClient(ModContainer mod) { | ||
QuiltDimensionsImpl.setDimensionFailHandler(errors -> { | ||
if (QuiltDimensionsImpl.IGNORE_FAIL) return true; | ||
|
||
MinecraftClient client = MinecraftClient.getInstance(); | ||
var oldScreen = client.currentScreen; | ||
client.currentScreen = null; | ||
final var result = new boolean[2]; | ||
result[1] = true; | ||
|
||
var errorMessage = errors.stream() | ||
.map(FailSoftMapCodec.DecodeError::getFancyMessage) | ||
.reduce(Text.literal("Errors:\n"), (mutableText, text) -> mutableText.copy().append("\n - ").append(text)); | ||
|
||
var confirmScreen = new ConfirmScreen(res -> { | ||
result[0] = res; | ||
result[1] = false; | ||
}, Text.translatable("quilt.error.dimension.deserialization"), errorMessage); | ||
|
||
client.setScreen(confirmScreen); | ||
|
||
// This is quite bad but there's not much choice since this will run on the render thread. | ||
while (result[1]) { | ||
((MinecraftClientAccessor) client).invokeRender(true); | ||
} | ||
|
||
client.setScreen(oldScreen); | ||
|
||
return result[0]; | ||
}); | ||
} | ||
} |
Oops, something went wrong.
b821808
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note to whoever sees this: this is WIP.