diff --git a/build.gradle b/build.gradle index 5b2cf8021..c70b40348 100644 --- a/build.gradle +++ b/build.gradle @@ -258,6 +258,10 @@ allprojects { testImplementation "net.fabricmc:fabric-loader-junit:${project.loader_version}" testImplementation sourceSets.testmod.output testImplementation 'org.mockito:mockito-core:5.4.0' + + //Needed for FFAPI to compile even! + compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")) + implementation(include("io.github.llamalad7:mixinextras-forge:0.4.1")) } test { diff --git a/fabric-block-view-api-v2/src/main/resources/fabric-block-view-api-v2.mixins.json b/fabric-block-view-api-v2/src/main/resources/fabric-block-view-api-v2.mixins.json index bbd819000..19534443e 100644 --- a/fabric-block-view-api-v2/src/main/resources/fabric-block-view-api-v2.mixins.json +++ b/fabric-block-view-api-v2/src/main/resources/fabric-block-view-api-v2.mixins.json @@ -4,7 +4,8 @@ "compatibilityLevel": "JAVA_17", "mixins": [ "BlockEntityMixin", - "BlockViewMixin" + "BlockViewMixin", + "WorldViewMixin" ], "injectors": { "defaultRequire": 1 diff --git a/fabric-events-interaction-v0/build.gradle b/fabric-events-interaction-v0/build.gradle index a2e684128..2a025c9e1 100644 --- a/fabric-events-interaction-v0/build.gradle +++ b/fabric-events-interaction-v0/build.gradle @@ -1,4 +1,4 @@ archivesBaseName = "fabric-events-interaction-v0" version = getSubprojectVersion(project) -moduleDependencies(project, ['fabric-api-base']) +moduleDependencies(project, ['fabric-api-base', 'fabric-networking-api-v1']) diff --git a/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/impl/event/interaction/FakePlayerNetworkHandler.java b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/impl/event/interaction/FakePlayerNetworkHandler.java new file mode 100644 index 000000000..edcb79d37 --- /dev/null +++ b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/impl/event/interaction/FakePlayerNetworkHandler.java @@ -0,0 +1,39 @@ +/* + * 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.impl.event.interaction; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.network.ClientConnection; +import net.minecraft.network.NetworkSide; +import net.minecraft.network.PacketCallbacks; +import net.minecraft.network.packet.Packet; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; + +import net.fabricmc.fabric.impl.networking.UntrackedNetworkHandler; + +public final class FakePlayerNetworkHandler extends ServerPlayNetworkHandler implements UntrackedNetworkHandler { + private static final ClientConnection FAKE_CONNECTION = new ClientConnection(NetworkSide.CLIENTBOUND); + + public FakePlayerNetworkHandler(ServerPlayerEntity player) { + super(player.getServer(), FAKE_CONNECTION, player); + } + + @Override + public void sendPacket(Packet packet, @Nullable PacketCallbacks callbacks) { } +} diff --git a/fabric-events-interaction-v0/src/main/resources/fabric.mod.json b/fabric-events-interaction-v0/src/main/resources/fabric.mod.json index cc6234340..e86ca4915 100644 --- a/fabric-events-interaction-v0/src/main/resources/fabric.mod.json +++ b/fabric-events-interaction-v0/src/main/resources/fabric.mod.json @@ -18,6 +18,7 @@ "depends": { "fabricloader": ">=0.4.0", "fabric-api-base": "*", + "fabric-networking-api-v1": "*", "minecraft": ">=1.15-alpha.19.37.a" }, "entrypoints": { diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/UntrackedNetworkHandler.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/UntrackedNetworkHandler.java new file mode 100644 index 000000000..19f7be7f5 --- /dev/null +++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/impl/networking/UntrackedNetworkHandler.java @@ -0,0 +1,21 @@ +/* + * 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.impl.networking; + +// An internal marker interface used by the fake player API to prevent the network addon from being tracked. +public interface UntrackedNetworkHandler { +} diff --git a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java index 675ede32b..82275334b 100644 --- a/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java +++ b/fabric-networking-api-v1/src/main/java/net/fabricmc/fabric/mixin/networking/ServerPlayNetworkHandlerMixin.java @@ -34,6 +34,7 @@ import net.fabricmc.fabric.impl.networking.DisconnectPacketSource; import net.fabricmc.fabric.impl.networking.NetworkHandlerExtensions; +import net.fabricmc.fabric.impl.networking.UntrackedNetworkHandler; import net.fabricmc.fabric.impl.networking.server.ServerPlayNetworkAddon; // We want to apply a bit earlier than other mods which may not use us in order to prevent refCount issues @@ -52,8 +53,10 @@ abstract class ServerPlayNetworkHandlerMixin implements NetworkHandlerExtensions @Inject(method = "", at = @At("RETURN")) private void initAddon(CallbackInfo ci) { this.addon = new ServerPlayNetworkAddon((ServerPlayNetworkHandler) (Object) this, this.server); - // A bit of a hack but it allows the field above to be set in case someone registers handlers during INIT event which refers to said field - this.addon.lateInit(); + if (!(this instanceof UntrackedNetworkHandler)) { + // A bit of a hack but it allows the field above to be set in case someone registers handlers during INIT event which refers to said field + this.addon.lateInit(); + } } @Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true) diff --git a/fabric-object-builder-api-v1/build.gradle b/fabric-object-builder-api-v1/build.gradle index 7b009c1e0..9d9976e47 100644 --- a/fabric-object-builder-api-v1/build.gradle +++ b/fabric-object-builder-api-v1/build.gradle @@ -3,7 +3,7 @@ version = getSubprojectVersion(project) moduleDependencies(project, [ 'fabric-api-base', -// 'fabric-resource-loader-v0' + 'fabric-resource-loader-v0' ]) testDependencies(project, [':fabric-command-api-v2']) diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/TradeOffersTypeAwareBuyForOneEmeraldFactoryMixin.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/TradeOffersTypeAwareBuyForOneEmeraldFactoryMixin.java index df6856937..60dc07672 100644 --- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/TradeOffersTypeAwareBuyForOneEmeraldFactoryMixin.java +++ b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/TradeOffersTypeAwareBuyForOneEmeraldFactoryMixin.java @@ -18,16 +18,13 @@ import java.util.stream.Stream; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Cancellable; 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.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import net.minecraft.entity.Entity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.random.Random; import net.minecraft.registry.DefaultedRegistry; import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOffers; @@ -47,12 +44,18 @@ private Stream disableVanillaCheck(DefaultedRegistry instan } /** - * To prevent "item" -> "air" trades, if the result of a type aware trade is air, make sure no offer is created. + * To prevent crashes due to passing a {@code null} item to a {@link TradeOffer}, return a {@code null} trade offer + * early before {@code null} is passed to the constructor. */ - @Inject(method = "create", at = @At(value = "NEW", target = "net/minecraft/village/TradeOffer"), locals = LocalCapture.CAPTURE_FAILEXCEPTION, cancellable = true, require = 0) - private void failOnNullItem(Entity entity, Random random, CallbackInfoReturnable cir, ItemStack buyingItem) { - if (buyingItem.isEmpty()) { // Will return true for an "empty" item stack that had null passed in the ctor - cir.setReturnValue(null); // Return null to prevent creation of empty trades + @ModifyExpressionValue( + method = "create", + at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;") + ) + private Object failOnNullItem(Object item, @Cancellable CallbackInfoReturnable cir) { + if (item == null) { + cir.setReturnValue(null); } + + return item; } } diff --git a/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json b/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json index 156455342..470e19b4a 100644 --- a/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json +++ b/fabric-object-builder-api-v1/src/main/resources/fabric.mod.json @@ -16,7 +16,7 @@ "FabricMC" ], "depends": { - "fabricloader": ">=0.8.2", + "fabricloader": ">=0.16.10", "fabric-api-base": "*" }, "description": "Builders for objects vanilla has locked down.", diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java index 20da7ef6a..fa56fe02e 100644 --- a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ModNioResourcePack.java @@ -16,34 +16,55 @@ package net.fabricmc.fabric.impl.resource.loader; +import java.io.IOException; import java.io.InputStream; +import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.spi.FileSystemProvider; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; -import cpw.mods.niofs.union.UnionFileSystemProvider; -import net.minecraftforge.resource.PathPackResources; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import net.minecraft.resource.AbstractFileResourcePack; import net.minecraft.resource.InputSupplier; +import net.minecraft.resource.ResourcePack; import net.minecraft.resource.ResourceType; +import net.minecraft.resource.metadata.ResourceMetadataReader; +import net.minecraft.util.Identifier; +import net.minecraft.util.PathUtil; import net.fabricmc.fabric.api.resource.ModResourcePack; import net.fabricmc.fabric.api.resource.ResourcePackActivationType; import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; -public class ModNioResourcePack extends PathPackResources implements ModResourcePack { +public class ModNioResourcePack implements ResourcePack, ModResourcePack { + private static final Logger LOGGER = LoggerFactory.getLogger(ModNioResourcePack.class); + private static final Pattern RESOURCE_PACK_PATH = Pattern.compile("[a-z0-9-_.]+"); private static final FileSystem DEFAULT_FS = FileSystems.getDefault(); - private static final UnionFileSystemProvider UFSP = (UnionFileSystemProvider) FileSystemProvider.installedProviders().stream().filter(fsp -> fsp.getScheme().equals("union")).findFirst().orElseThrow(() -> new IllegalStateException("Couldn't find UnionFileSystemProvider")); + private final String id; private final ModMetadata modInfo; - private final ResourcePackActivationType activationType; + private final List basePaths; private final ResourceType type; + private final AutoCloseable closer; + private final ResourcePackActivationType activationType; + private final Map> namespaces; public static ModNioResourcePack create(String id, ModContainer mod, String subPath, ResourceType type, ResourcePackActivationType activationType) { List rootPaths = mod.getRootPaths(); @@ -68,47 +89,204 @@ public static ModNioResourcePack create(String id, ModContainer mod, String subP if (paths.isEmpty()) return null; - Path union = paths.size() == 1 ? paths.get(0) : UFSP.newFileSystem((a, b) -> true, paths.toArray(Path[]::new)).getRoot(); - ModNioResourcePack ret = new ModNioResourcePack(id, mod.getMetadata(), union, type, activationType); + ModNioResourcePack ret = new ModNioResourcePack(id, mod.getMetadata(), paths, type, null, activationType); return ret.getNamespaces(type).isEmpty() ? null : ret; } - private ModNioResourcePack(String id, ModMetadata modInfo, Path path, ResourceType type, ResourcePackActivationType activationType) { - super(id, false, path); + private ModNioResourcePack(String id, ModMetadata modInfo, List paths, ResourceType type, AutoCloseable closer, ResourcePackActivationType activationType) { + this.id = id; this.modInfo = modInfo; + this.basePaths = paths; this.type = type; + this.closer = closer; this.activationType = activationType; + this.namespaces = readNamespaces(paths, modInfo.getId()); } - @Override - public ModMetadata getFabricModMetadata() { - return modInfo; + static Map> readNamespaces(List paths, String modId) { + Map> ret = new EnumMap<>(ResourceType.class); + + for (ResourceType type : ResourceType.values()) { + Set namespaces = null; + + for (Path path : paths) { + Path dir = path.resolve(type.getDirectory()); + if (!Files.isDirectory(dir)) continue; + + String separator = path.getFileSystem().getSeparator(); + + try (DirectoryStream ds = Files.newDirectoryStream(dir)) { + for (Path p : ds) { + if (!Files.isDirectory(p)) continue; + + String s = p.getFileName().toString(); + // s may contain trailing slashes, remove them + s = s.replace(separator, ""); + + if (!RESOURCE_PACK_PATH.matcher(s).matches()) { + LOGGER.warn("Fabric NioResourcePack: ignored invalid namespace: {} in mod ID {}", s, modId); + continue; + } + + if (namespaces == null) namespaces = new HashSet<>(); + + namespaces.add(s); + } + } catch (IOException e) { + LOGGER.warn("getNamespaces in mod " + modId + " failed!", e); + } + } + + ret.put(type, namespaces != null ? namespaces : Collections.emptySet()); + } + + return ret; } - public ResourcePackActivationType getActivationType() { - return this.activationType; + private Path getPath(String filename) { + if (hasAbsentNs(filename)) return null; + + for (Path basePath : basePaths) { + Path childPath = basePath.resolve(filename.replace("/", basePath.getFileSystem().getSeparator())).toAbsolutePath().normalize(); + + if (childPath.startsWith(basePath) && exists(childPath)) { + return childPath; + } + } + + return null; + } + + private static final String resPrefix = ResourceType.CLIENT_RESOURCES.getDirectory() + "/"; + private static final String dataPrefix = ResourceType.SERVER_DATA.getDirectory() + "/"; + + private boolean hasAbsentNs(String filename) { + int prefixLen; + ResourceType type; + + if (filename.startsWith(resPrefix)) { + prefixLen = resPrefix.length(); + type = ResourceType.CLIENT_RESOURCES; + } else if (filename.startsWith(dataPrefix)) { + prefixLen = dataPrefix.length(); + type = ResourceType.SERVER_DATA; + } else { + return false; + } + + int nsEnd = filename.indexOf('/', prefixLen); + if (nsEnd < 0) return false; + + return !namespaces.get(type).contains(filename.substring(prefixLen, nsEnd)); } private InputSupplier openFile(String filename) { - final Path path = resolve(filename); - if (Files.exists(path)) { - return InputSupplier.create(path); - } + Path path = getPath(filename); + + if (path != null && Files.isRegularFile(path)) { + return () -> Files.newInputStream(path); + } + if (ModResourcePackUtil.containsDefault(this.modInfo, filename)) { return () -> ModResourcePackUtil.openDefault(this.modInfo, this.type, filename); } + return null; } @Nullable @Override public InputSupplier openRoot(String... pathSegments) { - return openFile(String.join("/", pathSegments)); + PathUtil.validatePath(pathSegments); + + return this.openFile(String.join("/", pathSegments)); + } + + @Override + @Nullable + public InputSupplier open(ResourceType type, Identifier id) { + final Path path = getPath(getFilename(type, id)); + return path == null ? null : InputSupplier.create(path); + } + + @Override + public void findResources(ResourceType type, String namespace, String path, ResultConsumer visitor) { + if (!namespaces.getOrDefault(type, Collections.emptySet()).contains(namespace)) { + return; + } + + for (Path basePath : basePaths) { + String separator = basePath.getFileSystem().getSeparator(); + Path nsPath = basePath.resolve(type.getDirectory()).resolve(namespace); + Path searchPath = nsPath.resolve(path.replace("/", separator)).normalize(); + if (!exists(searchPath)) continue; + + try { + Files.walkFileTree(searchPath, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + String filename = nsPath.relativize(file).toString().replace(separator, "/"); + Identifier identifier = Identifier.of(namespace, filename); + + if (identifier == null) { + LOGGER.error("Invalid path in mod resource-pack {}: {}:{}, ignoring", id, namespace, filename); + } else { + visitor.accept(identifier, InputSupplier.create(file)); + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + LOGGER.warn("findResources at " + path + " in namespace " + namespace + ", mod " + modInfo.getId() + " failed!", e); + } + } + } + + @Override + public Set getNamespaces(ResourceType type) { + return namespaces.getOrDefault(type, Collections.emptySet()); + } + + @Override + public T parseMetadata(ResourceMetadataReader metaReader) throws IOException { + try (InputStream is = openFile("pack.mcmeta").get()) { + return AbstractFileResourcePack.parseMetadata(metaReader, is); + } + } + + @Override + public void close() { + if (closer != null) { + try { + closer.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public ModMetadata getFabricModMetadata() { + return modInfo; + } + + public ResourcePackActivationType getActivationType() { + return this.activationType; + } + + @Override + public String getName() { + return id; } private static boolean exists(Path path) { // NIO Files.exists is notoriously slow when checking the file system return path.getFileSystem() == DEFAULT_FS ? path.toFile().exists() : Files.exists(path); } + + private static String getFilename(ResourceType type, Identifier id) { + return String.format(Locale.ROOT, "%s/%s/%s", type.getDirectory(), id.getNamespace(), id.getPath()); + } } diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ServerLanguageUtil.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ServerLanguageUtil.java new file mode 100644 index 000000000..a38a8efbe --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/impl/resource/loader/ServerLanguageUtil.java @@ -0,0 +1,56 @@ +/* + * 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.impl.resource.loader; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import net.minecraft.resource.ResourceType; +import net.minecraft.util.Language; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +public final class ServerLanguageUtil { + private static final String ASSETS_PREFIX = ResourceType.CLIENT_RESOURCES.getDirectory() + '/'; + + private ServerLanguageUtil() { + } + + public static Collection getModLanguageFiles() { + Set paths = new LinkedHashSet<>(); + + for (ModContainer mod : FabricLoader.getInstance().getAllMods()) { + if (mod.getMetadata().getType().equals("builtin")) continue; + + final Map> map = ModNioResourcePack.readNamespaces(mod.getRootPaths(), mod.getMetadata().getId()); + + for (String ns : map.get(ResourceType.CLIENT_RESOURCES)) { + mod.findPath(ASSETS_PREFIX + ns + "/lang/" + Language.DEFAULT_LANGUAGE + ".json") + .filter(Files::isRegularFile) + .ifPresent(paths::add); + } + } + + return Collections.unmodifiableCollection(paths); + } +} diff --git a/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/server/LanguageMixin.java b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/server/LanguageMixin.java new file mode 100644 index 000000000..1ec0d82cf --- /dev/null +++ b/fabric-resource-loader-v0/src/main/java/net/fabricmc/fabric/mixin/resource/loader/server/LanguageMixin.java @@ -0,0 +1,83 @@ +/* + * 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.loader.server; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonParseException; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Final; +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.Redirect; + +import net.minecraft.util.Language; + +import net.fabricmc.fabric.impl.resource.loader.ServerLanguageUtil; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +@Mixin(Language.class) +class LanguageMixin { + @Shadow + @Final + private static Logger LOGGER; + + @Redirect(method = "create", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableMap$Builder;build()Lcom/google/common/collect/ImmutableMap;", remap = false)) + private static ImmutableMap create(ImmutableMap.Builder cir) { + Map map = new HashMap<>(cir.buildOrThrow()); + + for (Path path : ServerLanguageUtil.getModLanguageFiles()) { + loadFromPath(path, map::put); + } + + return ImmutableMap.copyOf(map); + } + + @Redirect(method = "load(Ljava/util/function/BiConsumer;Ljava/lang/String;)V", at = @At(value = "INVOKE", target = "Ljava/lang/Class;getResourceAsStream(Ljava/lang/String;)Ljava/io/InputStream;")) + private static InputStream readCorrectVanillaResource(Class instance, String path) throws IOException { + ModContainer mod = FabricLoader.getInstance().getModContainer("minecraft").orElseThrow(); + Path langPath = mod.findPath(path).orElse(null); + + if (langPath == null) { + throw new IOException("Could not read %s from minecraft ModContainer".formatted(path)); + } else { + return Files.newInputStream(langPath); + } + } + + private static void loadFromPath(Path path, BiConsumer entryConsumer) { + try (InputStream stream = Files.newInputStream(path)) { + LOGGER.debug("Loading translations from {}", path); + load(stream, entryConsumer); + } catch (JsonParseException | IOException e) { + LOGGER.error("Couldn't read strings from {}", path, e); + } + } + + @Shadow + public static void load(InputStream inputStream, BiConsumer entryConsumer) { + } +} diff --git a/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json b/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json index 461e1102d..287d7a9ff 100644 --- a/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json +++ b/fabric-resource-loader-v0/src/main/resources/fabric-resource-loader-v0.mixins.json @@ -10,6 +10,9 @@ "SimpleResourceReloadMixin", "MinecraftServerMixin" ], + "server": [ + "server.LanguageMixin" + ], "injectors": { "defaultRequire": 1 }, diff --git a/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/LanguageTestMod.java b/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/LanguageTestMod.java index 92ff0799d..2d9252728 100644 --- a/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/LanguageTestMod.java +++ b/fabric-resource-loader-v0/src/testmod/java/net/fabricmc/fabric/test/resource/loader/LanguageTestMod.java @@ -27,6 +27,8 @@ public static void onInitializeServer(ServerStartingEvent event) { } private static void testTranslationLoaded() { + testTranslationLoaded("item.minecraft.potato", "Potato"); // Test that vanilla translation loads + testTranslationLoaded("text.fabric-resource-loader-v0-testmod.server.lang.override", "Vanilla override test"); testTranslationLoaded("pack.source.fabricmod", "Fabric mod"); testTranslationLoaded("text.fabric-resource-loader-v0-testmod.server.lang.test0", "Test from fabric-resource-loader-v0-testmod"); testTranslationLoaded("text.fabric-resource-loader-v0-testmod.server.lang.test1", "Test from fabric-resource-loader-v0-testmod-test1"); diff --git a/fabric-resource-loader-v0/src/testmod/resources/assets/minecraft/lang/en_us.json b/fabric-resource-loader-v0/src/testmod/resources/assets/minecraft/lang/en_us.json new file mode 100644 index 000000000..3943934aa --- /dev/null +++ b/fabric-resource-loader-v0/src/testmod/resources/assets/minecraft/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "text.fabric-resource-loader-v0-testmod.server.lang.override": "Vanilla override test" +} diff --git a/gradle.properties b/gradle.properties index 56b3bc56a..c1d4f8c2b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,72 +2,72 @@ org.gradle.jvmargs=-Xmx3500M org.gradle.parallel=true fabric.loom.multiProjectOptimisation=true -version=0.92.2 +version=0.92.5 minecraft_version=1.20.1 yarn_version=+build.1 -loader_version=0.14.21 -installer_version=0.11.1 +loader_version=0.16.10 +installer_version=0.16.10 prerelease=false # Do not manually update, use the bumpversions task: -fabric-api-base-version=0.4.31 -fabric-api-lookup-api-v1-version=1.6.36 -fabric-biome-api-v1-version=13.0.13 -fabric-block-api-v1-version=1.0.11 -fabric-block-view-api-v2-version=1.0.1 -fabric-blockrenderlayer-v1-version=1.1.41 -fabric-command-api-v1-version=1.2.34 -fabric-command-api-v2-version=2.2.13 -fabric-commands-v0-version=0.2.51 -fabric-containers-v0-version=0.1.64 -fabric-content-registries-v0-version=4.0.11 -fabric-crash-report-info-v1-version=0.2.19 -fabric-data-attachment-api-v1-version=1.0.0 -fabric-data-generation-api-v1-version=12.3.4 -fabric-dimensions-v1-version=2.1.54 -fabric-entity-events-v1-version=1.6.0 -fabric-events-interaction-v0-version=0.6.2 -fabric-events-lifecycle-v0-version=0.2.63 -fabric-game-rule-api-v1-version=1.0.40 -fabric-gametest-api-v1-version=1.2.13 -fabric-item-api-v1-version=2.1.28 -fabric-item-group-api-v1-version=4.0.12 -fabric-key-binding-api-v1-version=1.0.37 -fabric-keybindings-v0-version=0.2.35 -fabric-lifecycle-events-v1-version=2.2.22 -fabric-loot-api-v2-version=1.2.1 -fabric-loot-tables-v1-version=1.1.45 -fabric-message-api-v1-version=5.1.9 -fabric-mining-level-api-v1-version=2.1.50 -fabric-model-loading-api-v1-version=1.0.3 -fabric-models-v0-version=0.4.2 -fabric-networking-api-v1-version=1.3.11 -fabric-networking-v0-version=0.3.51 -fabric-object-builder-api-v1-version=11.1.3 -fabric-particles-v1-version=1.1.2 -fabric-recipe-api-v1-version=1.0.21 -fabric-registry-sync-v0-version=2.3.3 -fabric-renderer-api-v1-version=3.2.1 -fabric-renderer-indigo-version=1.5.2 -fabric-renderer-registries-v1-version=3.2.46 -fabric-rendering-data-attachment-v1-version=0.3.37 -fabric-rendering-fluids-v1-version=3.0.28 -fabric-rendering-v0-version=1.1.49 -fabric-rendering-v1-version=3.0.8 -fabric-resource-conditions-api-v1-version=2.3.8 -fabric-resource-loader-v0-version=0.11.10 -fabric-screen-api-v1-version=2.0.8 -fabric-screen-handler-api-v1-version=1.3.30 -fabric-sound-api-v1-version=1.0.13 -fabric-transfer-api-v1-version=3.3.5 -fabric-transitive-access-wideners-v1-version=4.3.1 -fabric-convention-tags-v1-version=1.5.5 -fabric-client-tags-api-v1-version=1.1.2 +fabric-api-base-version=0.4.32 +fabric-api-lookup-api-v1-version=1.6.37 +fabric-biome-api-v1-version=13.0.14 +fabric-block-api-v1-version=1.0.12 +fabric-block-view-api-v2-version=1.0.2 +fabric-blockrenderlayer-v1-version=1.1.42 +fabric-command-api-v1-version=1.2.35 +fabric-command-api-v2-version=2.2.14 +fabric-commands-v0-version=0.2.52 +fabric-containers-v0-version=0.1.66 +fabric-content-registries-v0-version=4.0.13 +fabric-crash-report-info-v1-version=0.2.20 +fabric-data-attachment-api-v1-version=1.0.2 +fabric-data-generation-api-v1-version=12.3.6 +fabric-dimensions-v1-version=2.1.55 +fabric-entity-events-v1-version=1.6.1 +fabric-events-interaction-v0-version=0.6.4 +fabric-events-lifecycle-v0-version=0.2.64 +fabric-game-rule-api-v1-version=1.0.41 +fabric-gametest-api-v1-version=1.2.15 +fabric-item-api-v1-version=2.1.29 +fabric-item-group-api-v1-version=4.0.14 +fabric-key-binding-api-v1-version=1.0.38 +fabric-keybindings-v0-version=0.2.36 +fabric-lifecycle-events-v1-version=2.2.23 +fabric-loot-api-v2-version=1.2.3 +fabric-loot-tables-v1-version=1.1.47 +fabric-message-api-v1-version=5.1.10 +fabric-mining-level-api-v1-version=2.1.52 +fabric-model-loading-api-v1-version=1.0.4 +fabric-models-v0-version=0.4.3 +fabric-networking-api-v1-version=1.3.13 +fabric-networking-v0-version=0.3.53 +fabric-object-builder-api-v1-version=11.1.5 +fabric-particles-v1-version=1.1.3 +fabric-recipe-api-v1-version=1.0.23 +fabric-registry-sync-v0-version=2.3.5 +fabric-renderer-api-v1-version=3.2.2 +fabric-renderer-indigo-version=1.5.3 +fabric-renderer-registries-v1-version=3.2.47 +fabric-rendering-data-attachment-v1-version=0.3.38 +fabric-rendering-fluids-v1-version=3.0.29 +fabric-rendering-v0-version=1.1.50 +fabric-rendering-v1-version=3.0.9 +fabric-resource-conditions-api-v1-version=2.3.9 +fabric-resource-loader-v0-version=0.11.12 +fabric-screen-api-v1-version=2.0.9 +fabric-screen-handler-api-v1-version=1.3.32 +fabric-sound-api-v1-version=1.0.14 +fabric-transfer-api-v1-version=3.3.6 +fabric-transitive-access-wideners-v1-version=4.3.2 +fabric-convention-tags-v1-version=1.5.6 +fabric-client-tags-api-v1-version=1.1.3 # FFAPI Properties loom.platform=forge -forge_version=1.20.1-47.3.27 +forge_version=1.20.1-47.4.0 pack_format=15 -forgified_version=1.11.12 -forge_fabric_loader_version=2.6.0+0.15.0+1.20.1 +forgified_version=1.12.0 +forge_fabric_loader_version=2.7.11+0.16.5+1.20.1