mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-05 11:39:57 -04:00
Fix tags_populated resource conditions in 1.18.2 (#2099)
* Fix tags_populated resource conditions * move gametest entrypoint further up before resource manager getting loaded * Add maxShiftBy Co-authored-by: deirn <deirn@bai.lol> Co-authored-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
parent
7b0f312611
commit
91b7aa665b
9 changed files with 163 additions and 11 deletions
fabric-gametest-api-v1/src/main
java/net/fabricmc/fabric/mixin/gametest/server
resources
fabric-resource-conditions-api-v1/src/main
java/net/fabricmc/fabric
api/resource/conditions/v1
impl/resource/conditions
mixin/resource/conditions
resources
|
@ -34,12 +34,9 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
|||
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.server.Main;
|
||||
import net.minecraft.server.SaveLoader;
|
||||
import net.minecraft.server.dedicated.EulaReader;
|
||||
import net.minecraft.server.dedicated.ServerPropertiesLoader;
|
||||
import net.minecraft.util.UserCache;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.world.SaveProperties;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
import net.minecraft.world.level.storage.LevelSummary;
|
||||
|
||||
|
@ -52,8 +49,9 @@ public class MainMixin {
|
|||
return FabricGameTestHelper.ENABLED || reader.isEulaAgreedTo();
|
||||
}
|
||||
|
||||
@Inject(method = "main", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;startServer(Ljava/util/function/Function;)Lnet/minecraft/server/MinecraftServer;"))
|
||||
private static void main(String[] args, CallbackInfo info, OptionParser optionParser, OptionSpec optionSpec, OptionSpec optionSpec2, OptionSpec optionSpec3, OptionSpec optionSpec4, OptionSpec optionSpec5, OptionSpec optionSpec6, OptionSpec optionSpec7, OptionSpec optionSpec8, OptionSpec optionSpec9, OptionSpec optionSpec10, OptionSpec optionSpec11, OptionSpec optionSpec12, OptionSpec optionSpec13, OptionSpec optionSpec14, OptionSpec optionSpec15, OptionSet optionSet, Path path, ServerPropertiesLoader serverPropertiesLoader, Path path2, EulaReader eulaReader, File file, YggdrasilAuthenticationService yggdrasilAuthenticationService, MinecraftSessionService minecraftSessionService, GameProfileRepository gameProfileRepository, UserCache userCache, String string, LevelStorage levelStorage, LevelStorage.Session session, LevelSummary levelSummary, boolean bl, ResourcePackManager resourcePackManager, SaveLoader lv2, DynamicRegistryManager.Immutable lv3, SaveProperties saveProperties) {
|
||||
// Inject after resourcePackManager is stored
|
||||
@Inject(method = "main", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", shift = At.Shift.BY, by = 2, target = "Lnet/minecraft/resource/ResourcePackManager;<init>(Lnet/minecraft/resource/ResourceType;[Lnet/minecraft/resource/ResourcePackProvider;)V"))
|
||||
private static void main(String[] args, CallbackInfo info, OptionParser optionParser, OptionSpec optionSpec, OptionSpec optionSpec2, OptionSpec optionSpec3, OptionSpec optionSpec4, OptionSpec optionSpec5, OptionSpec optionSpec6, OptionSpec optionSpec7, OptionSpec optionSpec8, OptionSpec optionSpec9, OptionSpec optionSpec10, OptionSpec optionSpec11, OptionSpec optionSpec12, OptionSpec optionSpec13, OptionSpec optionSpec14, OptionSpec optionSpec15, OptionSet optionSet, Path path, ServerPropertiesLoader serverPropertiesLoader, Path path2, EulaReader eulaReader, File file, YggdrasilAuthenticationService yggdrasilAuthenticationService, MinecraftSessionService minecraftSessionService, GameProfileRepository gameProfileRepository, UserCache userCache, String string, LevelStorage levelStorage, LevelStorage.Session session, LevelSummary levelSummary, boolean bl, ResourcePackManager resourcePackManager) {
|
||||
if (FabricGameTestHelper.ENABLED) {
|
||||
FabricGameTestHelper.runHeadlessServer(session, resourcePackManager);
|
||||
info.cancel(); // Do not progress in starting the normal dedicated server
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"server.MainMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
"defaultRequire": 1,
|
||||
"maxShiftBy": 2
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,10 @@ import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
|||
* A resource condition is an identified {@code Predicate<JsonObject>} that can decide whether a resource should be loaded or not.
|
||||
* <ul>
|
||||
* <li>A JSON object that may contain a condition can be parsed with {@link #objectMatchesConditions}.
|
||||
* This is the preferred way of implementing conditional objects, as it handles the details of the format (see below) and catches and logs thrown exceptions.</li>
|
||||
* This is the preferred way of implementing conditional objects, as it handles the details of the format (see below) and catches and logs thrown exceptions.
|
||||
* This function should only be called from the "apply" phase of a {@link net.minecraft.resource.ResourceReloader},
|
||||
* otherwise some conditions might behave in unexpected ways.
|
||||
* </li>
|
||||
* <li>The lower-level {@link #conditionsMatch} and {@link #conditionMatches} may be useful when implementing conditions.</li>
|
||||
* <li>Conditions are registered with {@link #register} and queried with {@link #get}.</li>
|
||||
* </ul>
|
||||
|
@ -113,6 +116,9 @@ public final class ResourceConditions {
|
|||
* Check if the passed JSON object either has no {@code fabric:conditions} tag, or all of its conditions match.
|
||||
* This should be called for objects that may contain a conditions entry.
|
||||
*
|
||||
* <p>This function should only be called from the "apply" phase of a {@link net.minecraft.resource.ResourceReloader},
|
||||
* otherwise some conditions might behave in unexpected ways.
|
||||
*
|
||||
* <p>If an exception is thrown during condition testing, it will be caught and logged, and false will be returned.
|
||||
*/
|
||||
public static boolean objectMatchesConditions(JsonObject object) {
|
||||
|
|
|
@ -16,19 +16,27 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.resource.conditions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.tag.Tag;
|
||||
import net.minecraft.tag.TagKey;
|
||||
import net.minecraft.tag.TagManagerLoader;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryEntry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
|
@ -124,16 +132,54 @@ public class ResourceConditionsImpl {
|
|||
return and;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the tags deserialized by {@link TagManagerLoader} before they are bound, to use them in the tags_populated conditions.
|
||||
* The tags are set at the end of the "apply" phase in {@link TagManagerLoader}, and cleared in {@link net.minecraft.server.DataPackContents#refresh}.
|
||||
* If the resource reload fails, the thread local is not cleared and:
|
||||
* - the map will remain in memory until the next reload;
|
||||
* - any call to {@link #tagsPopulatedMatch} will check the tags from the failed reload instead of failing directly.
|
||||
* This is probably acceptable.
|
||||
*/
|
||||
public static final ThreadLocal<Map<RegistryKey<?>, Map<Identifier, Tag<RegistryEntry<?>>>>> LOADED_TAGS = new ThreadLocal<>();
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static void setTags(List<TagManagerLoader.RegistryTags<?>> tags) {
|
||||
Map<RegistryKey<?>, Map<Identifier, Tag<RegistryEntry<?>>>> tagMap = new HashMap<>();
|
||||
|
||||
for (TagManagerLoader.RegistryTags<?> registryTags : tags) {
|
||||
tagMap.put(registryTags.key(), (Map) registryTags.tags());
|
||||
}
|
||||
|
||||
LOADED_TAGS.set(tagMap);
|
||||
}
|
||||
|
||||
public static void clearTags() {
|
||||
LOADED_TAGS.remove();
|
||||
}
|
||||
|
||||
public static <T> boolean tagsPopulatedMatch(JsonObject object, RegistryKey<? extends Registry<T>> registryKey) {
|
||||
JsonArray array = JsonHelper.getArray(object, "values");
|
||||
@Nullable
|
||||
Map<RegistryKey<?>, Map<Identifier, Tag<RegistryEntry<?>>>> allTags = LOADED_TAGS.get();
|
||||
|
||||
if (allTags == null) {
|
||||
LOGGER.warn("Can't retrieve deserialized tags. Failing tags_populated resource condition check.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Identifier, Tag<RegistryEntry<?>>> registryTags = allTags.get(registryKey);
|
||||
|
||||
if (registryTags == null) {
|
||||
// No tag for this registry
|
||||
return array.isEmpty();
|
||||
}
|
||||
|
||||
for (JsonElement element : array) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
Identifier id = new Identifier(element.getAsString());
|
||||
TagKey<T> tag = TagKey.of(registryKey, id);
|
||||
Registry<T> registry = (Registry<T>) Registry.REGISTRIES.get(registryKey.getValue());
|
||||
Tag<RegistryEntry<?>> tag = registryTags.get(id);
|
||||
|
||||
if (!registry.containsTag(tag)) {
|
||||
if (tag == null || tag.values().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* 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 net.fabricmc.fabric.mixin.resource.conditions;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.server.DataPackContents;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
/**
|
||||
* Clear the tags captured by {@link DataPackContentsMixin}.
|
||||
* This must happen after the resource reload is complete, to ensure that the tags remain available throughout the entire "apply" phase.
|
||||
*/
|
||||
@Mixin(DataPackContents.class)
|
||||
public class DataPackContentsMixin {
|
||||
@Inject(
|
||||
method = "refresh",
|
||||
at = @At("HEAD")
|
||||
)
|
||||
public void hookRefresh(DynamicRegistryManager dynamicRegistryManager, CallbackInfo ci) {
|
||||
ResourceConditionsImpl.clearTags();
|
||||
}
|
||||
}
|
|
@ -33,6 +33,9 @@ import net.minecraft.util.profiler.Profiler;
|
|||
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceConditions;
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
/**
|
||||
* Using {@link SinglePreparationResourceReloaderMixin}, apply resource conditions at the very beginning of the "apply" phase.
|
||||
*/
|
||||
@Mixin(JsonDataLoader.class)
|
||||
public class JsonDataLoaderMixin extends SinglePreparationResourceReloaderMixin {
|
||||
@Shadow
|
||||
|
|
|
@ -25,6 +25,10 @@ import net.minecraft.resource.ResourceManager;
|
|||
import net.minecraft.resource.SinglePreparationResourceReloader;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
/**
|
||||
* This mixin allows us to inject arbitrary logic at the beginning of the "apply" phase.
|
||||
* Used by the subclass {@link JsonDataLoaderMixin}.
|
||||
*/
|
||||
@Mixin(SinglePreparationResourceReloader.class)
|
||||
public class SinglePreparationResourceReloaderMixin {
|
||||
// thenAcceptAsync in reload
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* 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 net.fabricmc.fabric.mixin.resource.conditions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Dynamic;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import net.minecraft.tag.TagManagerLoader;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
/**
|
||||
* Capture deserialized tags, right at the end of the "apply" phase of the tag loader, for use by the xxx_tags_populated condition.
|
||||
* This gives access to these tags during the rest of the "apply" phase, when resource conditions are applied.
|
||||
*/
|
||||
@Mixin(TagManagerLoader.class)
|
||||
public class TagManagerLoaderMixin {
|
||||
@Shadow
|
||||
private List<TagManagerLoader.RegistryTags<?>> registryTags;
|
||||
|
||||
// lambda body inside thenAcceptAsync, in the reload method
|
||||
@Dynamic
|
||||
@Inject(
|
||||
method = "method_40098(Ljava/util/List;Ljava/lang/Void;)V",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void hookApply(List<?> list, Void void_, CallbackInfo ci) {
|
||||
ResourceConditionsImpl.setTags(registryTags);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
"package": "net.fabricmc.fabric.mixin.resource.conditions",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"DataPackContentsMixin",
|
||||
"JsonDataLoaderMixin",
|
||||
"SinglePreparationResourceReloaderMixin"
|
||||
"SinglePreparationResourceReloaderMixin",
|
||||
"TagManagerLoaderMixin"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue