diff --git a/build.gradle b/build.gradle index 15a12e46db..7ad6dece4d 100644 --- a/build.gradle +++ b/build.gradle @@ -272,7 +272,7 @@ allprojects { spotless { java { - licenseHeaderFile(rootProject.file("HEADER")) + licenseHeaderFile(rootProject.file("HEADER")).named('default') removeUnusedImports() importOrder('java', 'javax', '', 'net.minecraft', 'net.fabricmc') indentWithTabs() diff --git a/fabric-data-fixer-api-v1/HEADER-PORT b/fabric-data-fixer-api-v1/HEADER-PORT new file mode 100644 index 0000000000..bc0e19e4ac --- /dev/null +++ b/fabric-data-fixer-api-v1/HEADER-PORT @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + diff --git a/fabric-data-fixer-api-v1/README.md b/fabric-data-fixer-api-v1/README.md new file mode 100644 index 0000000000..a457d825a5 --- /dev/null +++ b/fabric-data-fixer-api-v1/README.md @@ -0,0 +1,22 @@ +# Fabric Data Fixer API (v1) +For user-facing documentation, please check the Javadoc. + +This is a port of data fixer API from QSL, with some internal changes and new methods: + +- Unlike QSL, this API saves the data version in a NBT compound `_FabricDataVersions`. +- Current data version can be specified in the `fabric.mod.json` file. +- `FabricDataFixerBuilder#build()` overload that uses bootstrap executor. + +Files with ported code are marked with `// From QSL.` comment and a special license header. + +## Running the tests +The repository contains three files: `level.dat` and two region files (with chunks trimmed). You can run `gradlew runTestmodServer` to test the updating. If this does not error, the tests passed! + +To generate the file (without running the tests), run `gradlew runGenOldSave`, then optionally trim the chunks with NBT editing tools. + +The test files contain: + +- A chest block with a modded item, at `0, 10, 0` in the overworld +- A modded chest block with a modded item, at `0, 9, 0` in the overworld +- A modded block at `0, 8, 0` in the overworld +- A modded biome at chunk `0, 0` in the End diff --git a/fabric-data-fixer-api-v1/build.gradle b/fabric-data-fixer-api-v1/build.gradle new file mode 100644 index 0000000000..79d9582a16 --- /dev/null +++ b/fabric-data-fixer-api-v1/build.gradle @@ -0,0 +1,38 @@ +archivesBaseName = "fabric-data-fixer-api-v1" +version = getSubprojectVersion(project) + +moduleDependencies(project, [ + 'fabric-api-base', + 'fabric-lifecycle-events-v1' +]) + +testDependencies(project, [ + ':fabric-biome-api-v1', + ':fabric-item-api-v1', + ':fabric-object-builder-api-v1' +]) + +loom { + accessWidenerPath = file('src/main/resources/fabric-data-fixer-api-v1.accesswidener') + + runs { + genOldSave { + inherit testmodServer + name "Generate old save" + vmArg "-Dfabric.dataFixerTestMod.genMode=true" + + ideConfigGenerated = true + } + } +} + +spotless { + java { + /** + * This module contains a code from QSL, and they need a separate license header. + * Code ported from QSL should be marked as "From QSL" in a comment, + * inside the "source" (i.e. not inside header comment). + */ + licenseHeaderFile(project.file("HEADER-PORT")).named('port').onlyIfContentMatches('.*From QSL.*') + } +} diff --git a/fabric-data-fixer-api-v1/src/client/java/net/fabricmc/fabric/impl/client/datafixer/v1/ClientFreezer.java b/fabric-data-fixer-api-v1/src/client/java/net/fabricmc/fabric/impl/client/datafixer/v1/ClientFreezer.java new file mode 100644 index 0000000000..89a11c3145 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/client/java/net/fabricmc/fabric/impl/client/datafixer/v1/ClientFreezer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.impl.client.datafixer.v1; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; + +public final class ClientFreezer implements ClientModInitializer { + // From QSL. + @Override + public void onInitializeClient() { + ClientLifecycleEvents.CLIENT_STARTED.register((client) -> FabricDataFixesInternals.get().freeze()); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/DataFixerEntrypoint.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/DataFixerEntrypoint.java new file mode 100644 index 0000000000..d370d8c585 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/DataFixerEntrypoint.java @@ -0,0 +1,25 @@ +/* + * 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.api.datafixer.v1; + +import com.mojang.datafixers.schemas.Schema; + +public interface DataFixerEntrypoint { + void onRegisterBlockEntities(SchemaRegistry registry, Schema schema); + + void onRegisterEntities(SchemaRegistry registry, Schema schema); +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/EmptySchema.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/EmptySchema.java new file mode 100644 index 0000000000..fac347c665 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/EmptySchema.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.api.datafixer.v1; + +import java.util.Map; +import java.util.function.Supplier; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TypeTemplate; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import org.jetbrains.annotations.Range; + +/** + * Represents an empty {@link Schema}, having no parent and containing no type definitions. + */ +public final class EmptySchema extends FirstSchema { + // From QSL. + + /** + * Constructs an empty schema. + * + * @param versionKey the data version key + */ + public EmptySchema(@Range(from = 0, to = Integer.MAX_VALUE) int versionKey) { + super(versionKey); + } + + // Ensure the schema stays empty. + @Override + public void registerType(boolean recursive, DSL.TypeReference type, Supplier template) { + throw new UnsupportedOperationException(); + } + + @Override + protected Map> buildTypes() { + return Object2ObjectMaps.emptyMap(); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FabricDataFixerBuilder.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FabricDataFixerBuilder.java new file mode 100644 index 0000000000..f354b2331f --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FabricDataFixerBuilder.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.api.datafixer.v1; + +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.DataFixerUpper; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Range; + +import net.minecraft.util.Util; + +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; +import net.fabricmc.loader.api.ModContainer; + +/** + * An extended variant of the {@link DataFixerBuilder} class, which provides an extra method. + */ +public class FabricDataFixerBuilder extends DataFixerBuilder { + // From QSL. + protected final int dataVersion; + + /** + * Creates a new {@code FabricDataFixerBuilder}. + * + * @param dataVersion the current data version + */ + public FabricDataFixerBuilder(@Range(from = 0, to = Integer.MAX_VALUE) int dataVersion) { + super(dataVersion); + this.dataVersion = dataVersion; + } + + /** + * Creates a new {@code FabricDataFixerBuilder}. This method gets the current version from + * the {@code fabric-data-fixer-api-v1:version} field in the {@code custom} object of + * the {@code fabric.mod.json} file of {@code mod}. To specify the version + * manually, use the other overload. + * + * @param mod the mod container + * @return the data fixer builder + * @throws RuntimeException if the version field does not exist or is not a number + */ + public static FabricDataFixerBuilder create(ModContainer mod) { + Objects.requireNonNull(mod, "mod cannot be null"); + int dataVersion = FabricDataFixesInternals.getDataVersionFromMetadata(mod); + return new FabricDataFixerBuilder(dataVersion); + } + + /** + * @return the current data version + */ + @Range(from = 0, to = Integer.MAX_VALUE) + public int getDataVersion() { + return this.dataVersion; + } + + /** + * Builds the final {@code DataFixer}. + * + *

This will build either an {@linkplain #build() unoptimized fixer} or an + * optimized fixer, depending on the vanilla game's settings. + * + * @param types the set of required {@link com.mojang.datafixers.DSL.TypeReference}s, only used if the game is using optimized data fixers + * @param executorGetter the executor supplier, only invoked if the game is using optimized data fixers + * @return the newly built data fixer + */ + @Contract(value = "_, _ -> new") + public DataFixerUpper build(Set types, Supplier executorGetter) { + Objects.requireNonNull(executorGetter, "executorGetter cannot be null"); + return (DataFixerUpper) (types.isEmpty() ? this.build().fixer() : Util.make(() -> { + Result result = this.build(); + result.optimize(types, executorGetter.get()).join(); + return result.fixer(); + })); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FabricDataFixes.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FabricDataFixes.java new file mode 100644 index 0000000000..7f23baaf64 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FabricDataFixes.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.api.datafixer.v1; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.function.BiFunction; +import java.util.function.Supplier; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.DataFixerUpper; +import com.mojang.datafixers.schemas.Schema; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import net.minecraft.datafixer.DataFixTypes; +import net.minecraft.nbt.NbtCompound; + +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; +import net.fabricmc.loader.api.ModContainer; + +/** + * Provides methods to register custom {@link DataFixer}s. + */ +public final class FabricDataFixes { + // From QSL. + private FabricDataFixes() { + throw new RuntimeException("FabricDataFixes only contains static declarations."); + } + + /** + * A "base" version {@code 0} schema, for use by all mods. + * + *

This schema must be the first one added! + * + * @see DataFixerBuilder#addSchema(int, BiFunction) + */ + public static BiFunction getBaseSchema() { + return (version, parent) -> { + Preconditions.checkArgument(version == 0, "version must be 0"); + Preconditions.checkArgument(parent == null, "parent must be null"); + return FabricDataFixesInternals.get().getBaseSchema(); + }; + } + + /** + * Registers a new data fixer. + * + * @param modId the mod ID + * @param currentVersion the current version of the mod's data + * @param dataFixer the data fixer + */ + public static void registerFixer(String modId, + @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + DataFixerUpper dataFixer) { + registerFixer(modId, currentVersion, null, dataFixer); + } + + /** + * Registers a new data fixer. + * + * @param modId the mod ID + * @param currentVersion the current version of the mod's data + * @param key the optional key of the saved current version + * @param dataFixer the data fixer + */ + public static void registerFixer(String modId, + @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + @Nullable String key, DataFixerUpper dataFixer) { + Objects.requireNonNull(modId, "modId cannot be null"); + //noinspection ConstantConditions + Preconditions.checkArgument(currentVersion >= 0, "currentVersion must be positive"); + Objects.requireNonNull(dataFixer, "dataFixer cannot be null"); + + if (isFrozen()) { + throw new IllegalStateException("Can't register data fixer after registry is frozen"); + } + + FabricDataFixesInternals.get().registerFixer(modId, currentVersion, key, dataFixer); + } + + /** + * Registers a new data fixer. + * + * @param mod the mod container + * @param currentVersion the current version of the mod's data + * @param dataFixer the data fixer + */ + public static void registerFixer(ModContainer mod, + @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + DataFixerUpper dataFixer) { + registerFixer(mod, currentVersion, null, dataFixer); + } + + /** + * Registers a new data fixer. + * + * @param mod the mod container + * @param currentVersion the current version of the mod's data + * @param key the optional key of the saved current version + * @param dataFixer the data fixer + */ + public static void registerFixer(ModContainer mod, + @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + @Nullable String key, + DataFixerUpper dataFixer) { + Objects.requireNonNull(mod, "mod cannot be null"); + + registerFixer(mod.getMetadata().getId(), currentVersion, key, dataFixer); + } + + /** + * Registers a new data fixer. This method gets the current version from + * the {@code fabric-data-fixer-api-v1:version} field in the {@code custom} object of + * the {@code fabric.mod.json} file of {@code mod}. To specify the version + * manually, use the other overloads. + * + * @param mod the mod container + * @param dataFixer the data fixer + * @throws RuntimeException if the version field does not exist or is not a number + */ + public static void registerFixer(ModContainer mod, DataFixerUpper dataFixer) { + Objects.requireNonNull(mod, "mod cannot be null"); + registerFixer(mod.getMetadata().getId(), FabricDataFixesInternals.getDataVersionFromMetadata(mod), FabricDataFixesInternals.getKeyFromMetadata(mod), dataFixer); + } + + /** + * Builds and registers a new data fixer. + * + * @param mod the mod container + * @param builder the data fixer builder + */ + public static void buildAndRegisterFixer(ModContainer mod, + FabricDataFixerBuilder builder) { + buildAndRegisterFixer(mod, null, builder); + } + + /** + * Builds and registers a new data fixer. + * + * @param mod the mod container + * @param builder the data fixer builder + */ + public static void buildAndRegisterFixer(ModContainer mod, + @Nullable String key, FabricDataFixerBuilder builder) { + Objects.requireNonNull(mod, "mod cannot be null"); + Objects.requireNonNull(builder, "data fixer builder cannot be null"); + + Supplier executor = () -> Executors.newSingleThreadExecutor( + new ThreadFactoryBuilder().setNameFormat("Fabric DataFixer Bootstrap").setDaemon(true).setPriority(1).build() + ); + + registerFixer(mod.getMetadata().getId(), builder.getDataVersion(), + key, builder.build(DataFixTypes.REQUIRED_TYPES, executor)); + } + + /** + * Gets a mod's data fixers. + * + * @param modId the mod ID + * @return the mod's data fixers, or empty optional if the mod hasn't registered any + */ + public static Optional>> getFixers(String modId) { + Objects.requireNonNull(modId, "modId cannot be null"); + + List> fixers = new ArrayList<>(); + + List entries = FabricDataFixesInternals.get().getFixerEntries(modId); + + if (entries == null) { + return Optional.empty(); + } + + for (FabricDataFixesInternals.DataFixerEntry entry : entries) { + if (entry == null) { + fixers.add(Optional.empty()); + } else { + fixers.add(Optional.of(entry.dataFixer())); + } + } + + return Optional.of(fixers); + } + + /** + * Gets a mod's data version from a {@link NbtCompound}. + * + * @param nbt the NBT compound + * @param modId the mod ID + * @return the mod's data version, or {@code 0} if the NBT has no data for that mod + */ + @Contract(pure = true) + @Range(from = 0, to = Integer.MAX_VALUE) + public static int getModDataVersion(NbtCompound nbt, String modId) { + return getModDataVersion(nbt, modId, null); + } + + /** + * Gets a mod's data version from a {@link NbtCompound}. + * + * @param nbt the NBT compound + * @param modId the mod ID + * @return the mod's data version, or {@code 0} if the NBT has no data for that mod + */ + @Contract(pure = true) + @Range(from = 0, to = Integer.MAX_VALUE) + public static int getModDataVersion(NbtCompound nbt, String modId, @Nullable String key) { + Objects.requireNonNull(nbt, "compound cannot be null"); + Objects.requireNonNull(modId, "modId cannot be null"); + + return FabricDataFixesInternals.getModDataVersion(nbt, modId, key); + } + + /** + * Checks if the data fixer registry is frozen. Data fixers cannot be registered + * after the registry gets frozen. + * + * @return {@code true} if frozen, or {@code false} otherwise. + */ + @Contract(pure = true) + public static boolean isFrozen() { + return FabricDataFixesInternals.get().isFrozen(); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FirstSchema.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FirstSchema.java new file mode 100644 index 0000000000..9b12488186 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/FirstSchema.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.api.datafixer.v1; + +import java.util.Map; +import java.util.function.Supplier; + +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import org.jetbrains.annotations.Range; + +/** + * Represents a {@link Schema} that has no parent. + */ +public class FirstSchema extends Schema { + // From QSL. + + /** + * Creates a schema. + * + * @param versionKey the data version key + */ + public FirstSchema(@Range(from = 0, to = Integer.MAX_VALUE) int versionKey) { + super(versionKey, null); + } + + // all of these methods refer to this.parent without checking if its null + @Override + public void registerTypes(Schema schema, Map> entityTypes, + Map> blockEntityTypes) { + } + + @Override + public Map> registerEntities(Schema schema) { + return Map.of(); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + return Map.of(); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/SchemaRegistry.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/SchemaRegistry.java new file mode 100644 index 0000000000..09a9f84133 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/SchemaRegistry.java @@ -0,0 +1,47 @@ +/* + * 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.api.datafixer.v1; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.datafixers.util.Either; + +import net.minecraft.util.Identifier; + +public interface SchemaRegistry { + void register(Identifier id, Supplier template); + + void register(Identifier id, Function template); + + void addSchema(BiFunction factory); + + Supplier remove(Identifier id); + + ImmutableMap, Function>> get(); + + ImmutableList getKeys(); + + ImmutableList, Function>> getValues(); + + ImmutableList> getFutureSchemas(); +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/SimpleFixes.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/SimpleFixes.java new file mode 100644 index 0000000000..766d3d1eec --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/SimpleFixes.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.api.datafixer.v1; + +import static java.util.Objects.requireNonNull; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; + +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.fix.BlockNameFix; +import net.minecraft.datafixer.fix.EntityRenameFix; +import net.minecraft.datafixer.fix.GameEventRenamesFix; +import net.minecraft.datafixer.fix.ItemNameFix; +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; +import net.minecraft.util.Identifier; + +/** + * Provides methods to add common {@link DataFix}es to {@link DataFixerBuilder}s. + */ +public final class SimpleFixes { + // From QSL. + private SimpleFixes() { + throw new RuntimeException("SimpleFixes contains only static declarations."); + } + + /** + * Adds a block rename fix to the builder, in case a block's identifier is changed. + * + * @param builder the builder + * @param name the fix's name + * @param oldId the block's old identifier + * @param newId the block's new identifier + * @param schema the schema this fixer should be a part of + * @see BlockNameFix + */ + public static void addBlockRenameFix(DataFixerBuilder builder, String name, + Identifier oldId, Identifier newId, + Schema schema) { + Objects.requireNonNull(builder, "DataFixerBuilder cannot be null"); + Objects.requireNonNull(name, "Fix name cannot be null"); + Objects.requireNonNull(oldId, "Old identifier cannot be null"); + Objects.requireNonNull(newId, "New identifier cannot be null"); + Objects.requireNonNull(schema, "Schema cannot be null"); + + final String oldIdStr = oldId.toString(), newIdStr = newId.toString(); + builder.addFixer(BlockNameFix.create(schema, name, (inputName) -> + Objects.equals(IdentifierNormalizingSchema.normalize(inputName), oldIdStr) ? newIdStr : inputName)); + } + + /** + * Adds an entity rename fix to the builder, in case an entity's identifier is changed. + * + * @param builder the builder + * @param name the fix's name + * @param oldId the entity's old identifier + * @param newId the entity's new identifier + * @param schema the schema this fix should be a part of + * @see EntityRenameFix + */ + public static void addEntityRenameFix(DataFixerBuilder builder, String name, + Identifier oldId, Identifier newId, + Schema schema) { + requireNonNull(builder, "DataFixerBuilder cannot be null"); + requireNonNull(name, "Fix name cannot be null"); + requireNonNull(oldId, "Old identifier cannot be null"); + requireNonNull(newId, "New identifier cannot be null"); + requireNonNull(schema, "Schema cannot be null"); + + final String oldIdStr = oldId.toString(), newIdStr = newId.toString(); + builder.addFixer(new EntityRenameFix(name, schema, false) { + @Override + protected String rename(String inputName) { + return Objects.equals(IdentifierNormalizingSchema.normalize(inputName), oldIdStr) ? newIdStr : inputName; + } + }); + } + + /** + * Adds an item rename fix to the builder, in case an item's identifier is changed. + * + * @param builder the builder + * @param name the fix's name + * @param oldId the item's old identifier + * @param newId the item's new identifier + * @param schema the schema this fix should be a part of + * @see ItemNameFix + */ + public static void addItemRenameFix(DataFixerBuilder builder, String name, + Identifier oldId, Identifier newId, + Schema schema) { + Objects.requireNonNull(builder, "DataFixerBuilder cannot be null"); + Objects.requireNonNull(name, "Fix name cannot be null"); + Objects.requireNonNull(oldId, "Old identifier cannot be null"); + Objects.requireNonNull(newId, "New identifier cannot be null"); + Objects.requireNonNull(schema, "Schema cannot be null"); + + final String oldIdStr = oldId.toString(), newIdStr = newId.toString(); + builder.addFixer(ItemNameFix.create(schema, name, (inputName) -> + Objects.equals(IdentifierNormalizingSchema.normalize(inputName), oldIdStr) ? newIdStr : inputName)); + } + + /** + * Adds a blockstate rename fix to the builder, in case a blockstate's name is changed. + * + * @param builder the builder + * @param name the fix's name + * @param blockId the block's identifier + * @param oldState the blockstate's old name + * @param defaultValue the blockstate's default value + * @param newState the blockstates's new name + * @param schema the schema this fixer should be a part of + * @see BlockStateRenameFix + */ + public static void addBlockStateRenameFix(DataFixerBuilder builder, String name, Identifier blockId, String oldState, + String defaultValue, String newState, + Schema schema) { + requireNonNull(builder, "DataFixerBuilder cannot be null"); + requireNonNull(name, "Fix name cannot be null"); + requireNonNull(blockId, "Block Id cannot be null"); + requireNonNull(oldState, "Old BlockState cannot be null"); + requireNonNull(defaultValue, "Default value cannot be null"); + requireNonNull(newState, "New BlockState cannot be null"); + requireNonNull(schema, "Schema cannot be null"); + + final String blockIdStr = blockId.toString(); + builder.addFixer(new BlockStateRenameFix(schema, name, blockIdStr, oldState, defaultValue, newState)); + } + + /** + * Adds a biome rename fix to the builder, in case biome identifiers are changed. + * + * @param builder the builder + * @param name the fix's name + * @param changes a map of old biome identifiers to new biome identifiers + * @param schema the schema this fixer should be a part of + * @see GameEventRenamesFix + */ + public static void addBiomeRenameFix(DataFixerBuilder builder, String name, + Map changes, + Schema schema) { + Objects.requireNonNull(builder, "DataFixerBuilder cannot be null"); + Objects.requireNonNull(name, "Fix name cannot be null"); + Objects.requireNonNull(changes, "Changes cannot be null"); + Objects.requireNonNull(schema, "Schema cannot be null"); + + ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); + + for (Map.Entry entry : changes.entrySet()) { + mapBuilder.put(entry.getKey().toString(), entry.getValue().toString()); + } + + builder.addFixer(new GameEventRenamesFix(schema, name, TypeReferences.BIOME, Schemas.replacing(mapBuilder.build()))); + } + + public static class BlockStateRenameFix extends DataFix { + private final String name; + private final String blockId; + private final String oldState; + private final String defaultState; + private final String newState; + + public BlockStateRenameFix(Schema outputSchema, String name, String blockId, String oldState, String defaultState, String newState) { + super(outputSchema, false); + this.name = name; + this.blockId = blockId; + this.oldState = oldState; + this.defaultState = defaultState; + this.newState = newState; + } + + private Dynamic fix(Dynamic dynamic) { + Optional optional = dynamic.get("Name").asString().result(); + return optional.equals(Optional.of(this.blockId)) ? dynamic.update("Properties", dynamic1 -> { + String string = dynamic1.get(this.oldState).asString(this.defaultState); + return dynamic1.remove(this.oldState).set(this.newState, dynamic1.createString(string)); + }) : dynamic; + } + + @Override + protected TypeRewriteRule makeRule() { + return this.fixTypeEverywhereTyped(this.name, this.getInputSchema().getType(TypeReferences.BLOCK_STATE), + typed -> typed.update(DSL.remainderFinder(), this::fix) + ); + } + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/package-info.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/package-info.java new file mode 100644 index 0000000000..d926a4e037 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/api/datafixer/v1/package-info.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +/** + *

Custom DataFixerUpper API

+ * + *

This API lets you register a {@code DataFixer} for your own mod, allowing mods to make + * changes affecting world save without breaking compatibility! + * + *

Here is an example simple use of this API: + *

{@code
+ * // the latest version of the mod's data
+ * // this should match the version of the last schema added!
+ * // note that the default data version is 0, meaning that you can upgrade
+ * //  from a version that did not have a fixer
+ * //  (by registering a schema for upgrading from version 0 to version 1)
+ * public static final int CURRENT_DATA_VERSION = 1;
+ *
+ * public static void initialize(ModContainer mod) {
+ *     // create a builder
+ *     FabricDataFixerBuilder builder = new FabricDataFixerBuilder(CURRENT_DATA_VERSION);
+ *     // add the "base" version 0 schema
+ *     builder.addSchema(0, FabricDataFixes.BASE_SCHEMA);
+ *     // add a schema for upgrading from version 0 to version 1
+ *     Schema schemaV1 = builder.addSchema(1, IdentifierNormalizingSchema::new)
+ *     // add fixes to the schema - for example, an item rename (identifier change)
+ *     // multiple fixes can share the same schema
+ *     SimpleFixes.addItemRenameFix(builder, "Rename cool_item to awesome_item",
+ *         new Identifier("mymod", "cool_item"),
+ *         new Identifier("mymod", "awesome_item"),
+ *         schemaV1);
+ *
+ *     // register the fixer!
+ *     // this will create either an unoptimized fixer or an optimized fixer,
+ *     //  depending on the game configuration
+ *     FabricDataFixes.buildAndRegisterFixer(mod, builder);
+ * }
+ * }
+ * + *

Data version

+ * + *

A data fixer needs an integer "data version" to function. This is different from the + * mod version given in {@code version} key of the {@code fabric.mod.json} file. + * + *

The default data version, used for versions without datafixers, is {@code 0}. + * To add a data fixer, you must assign a positive integer as the current data version. + * This is increased every time a new data fixer is added, unless the version is + * already increased in the same release. Multiple mod versions can share the same + * data version if no data fixes are needed between those. + * + *

Data versions do not have to be consecutive (incremented one by one). However, + * it should never decrease. When making a significant change (such as forking or + * updating to a new Minecraft release), it is usually recommended to have a "gap" + * between the two data versions, to be used for data fixers before the significant + * change. + * + *

There are three ways to specify the current data version: + *

+ * + *

The data version for a specific data fix is assigned when creating a schema using + * the {@code addSchema} method. Multiple data fixes can share the same schema, if they + * all provide fixes for the same data version. + * + * @see net.fabricmc.fabric.api.datafixer.v1.FabricDataFixes + * @see net.fabricmc.fabric.api.datafixer.v1.SimpleFixes + * @see net.fabricmc.fabric.api.datafixer.v1.FabricDataFixerBuilder + */ + +// From QSL. +package net.fabricmc.fabric.api.datafixer.v1; diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/DataFixerUpperExtension.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/DataFixerUpperExtension.java new file mode 100644 index 0000000000..12b4ce7502 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/DataFixerUpperExtension.java @@ -0,0 +1,24 @@ +/* + * 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.datafixer.v1; + +import com.mojang.datafixers.schemas.Schema; +import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; + +public interface DataFixerUpperExtension { + Int2ObjectSortedMap fabric_getSchemas(); +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricDataFixesInternals.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricDataFixesInternals.java new file mode 100644 index 0000000000..5fac866e8a --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricDataFixesInternals.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.impl.datafixer.v1; + +import java.util.List; + +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.DataFixerUpper; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; +import org.slf4j.Logger; + +import net.minecraft.SharedConstants; +import net.minecraft.datafixer.DataFixTypes; +import net.minecraft.datafixer.Schemas; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; + +import net.fabricmc.fabric.api.datafixer.v1.DataFixerEntrypoint; +import net.fabricmc.fabric.api.datafixer.v1.SchemaRegistry; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.fabricmc.loader.api.entrypoint.EntrypointContainer; +import net.fabricmc.loader.api.metadata.CustomValue; + +public abstract class FabricDataFixesInternals { + // From QSL. + private static final Logger LOGGER = LogUtils.getLogger(); + protected static final String DATA_VERSIONS_KEY = "_FabricDataVersions"; + public static final String METADATA_VERSION_KEY = "fabric-data-fixer-api-v1:version"; + public static final String METADATA_KEY_KEY = "fabric-data-fixer-api-v1:key"; + private static final String ENTRYPOINT_KEY = "fabric-data-fixer"; + + public record DataFixerEntry(DataFixerUpper dataFixer, int currentVersion, @Nullable String key) { + } + + @Range(from = 0, to = Integer.MAX_VALUE) + public static int getDataVersionFromMetadata(ModContainer mod) { + CustomValue version = mod.getMetadata().getCustomValue(METADATA_VERSION_KEY); + + if (version == null || version.getType() != CustomValue.CvType.NUMBER) { + throw new RuntimeException("Data version is not set in the fabric.mod.json file; set it or pass explicitly"); + } + + return version.getAsNumber().intValue(); + } + + @Nullable + public static String getKeyFromMetadata(ModContainer mod) { + CustomValue key = mod.getMetadata().getCustomValue(METADATA_KEY_KEY); + + if (key == null) { + return null; + } + + if (key.getType() != CustomValue.CvType.STRING) { + throw new RuntimeException("Key is not a string in the fabric.mod.json file"); + } + + return key.getAsString(); + } + + @Contract(pure = true) + @Range(from = 0, to = Integer.MAX_VALUE) + public static int getModDataVersion(NbtCompound nbt, String modId, @Nullable String key) { + String nbtKey = modId; + + if (key != null) { + nbtKey += ('_' + key); + } + + NbtCompound dataVersions = nbt.getCompound(DATA_VERSIONS_KEY); + return dataVersions.getInt(nbtKey); + } + + private static List getEntrypoints() { + List> dataFixerEntrypoints = FabricLoader.getInstance() + .getEntrypointContainers(ENTRYPOINT_KEY, DataFixerEntrypoint.class); + return dataFixerEntrypoints.stream().map(EntrypointContainer::getEntrypoint).toList(); + } + + public static void registerBlockEntities(SchemaRegistry registry, Schema schema) { + List entrypoints = getEntrypoints(); + + for (DataFixerEntrypoint entrypoint : entrypoints) { + entrypoint.onRegisterBlockEntities(registry, schema); + } + } + + public static void registerEntities(SchemaRegistry registry, Schema schema) { + List entrypoints = getEntrypoints(); + + for (DataFixerEntrypoint entrypoint : entrypoints) { + entrypoint.onRegisterEntities(registry, schema); + } + } + + private static FabricDataFixesInternals instance; + + public static FabricDataFixesInternals get() { + if (instance == null) { + Schema latestVanillaSchema; + + try { + latestVanillaSchema = Schemas.getFixer() + .getSchema(DataFixUtils.makeKey(SharedConstants.getGameVersion().getSaveVersion().getId())); + } catch (Throwable e) { + latestVanillaSchema = null; + } + + if (latestVanillaSchema == null) { + LOGGER.warn("[Fabric DFU API] Failed to initialize! Either someone stopped DFU from initializing,"); + LOGGER.warn("[Fabric DFU API] or this Minecraft build is hosed."); + LOGGER.warn("[Fabric DFU API] Using no-op implementation."); + instance = new NoOpFabricDataFixesInternals(); + } else { + instance = new FabricDataFixesInternalsImpl(latestVanillaSchema); + } + } + + return instance; + } + + public abstract void registerFixer(String modId, @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, @Nullable String key, DataFixerUpper dataFixer); + + public abstract boolean isEmpty(); + + public abstract @Nullable List getFixerEntries(String modId); + + @Contract(value = "-> new", pure = true) + public abstract Schema getBaseSchema(); + + public abstract Dynamic updateWithAllFixers(DataFixTypes dataFixTypes, Dynamic element); + + public abstract NbtCompound addModDataVersions(NbtCompound nbt); + + public abstract void freeze(); + + @Contract(pure = true) + public abstract boolean isFrozen(); +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricDataFixesInternalsImpl.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricDataFixesInternalsImpl.java new file mode 100644 index 0000000000..867074dd86 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricDataFixesInternalsImpl.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.impl.datafixer.v1; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mojang.datafixers.DataFixerUpper; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import net.minecraft.datafixer.DataFixTypes; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; + +public final class FabricDataFixesInternalsImpl extends FabricDataFixesInternals { + // From QSL. + private Schema latestSchema; + + private Map> modDataFixers; + private boolean frozen; + + public FabricDataFixesInternalsImpl(Schema latestVanillaSchema) { + this.latestSchema = latestVanillaSchema; + this.modDataFixers = new Object2ReferenceOpenHashMap<>(); + this.frozen = false; + } + + @Override + public void registerFixer(String modId, @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + @Nullable String key, DataFixerUpper dataFixer) { + Int2ObjectSortedMap schemas = ((DataFixerUpperExtension) dataFixer).fabric_getSchemas(); + Schema lastSchema = schemas.getOrDefault(schemas.lastIntKey(), null); + + if (lastSchema != null) { + this.latestSchema = lastSchema; + } + + this.modDataFixers.computeIfAbsent(modId, modIdx -> new ObjectArrayList<>()) + .add(new DataFixerEntry(dataFixer, currentVersion, key)); + } + + @Override + public boolean isEmpty() { + return this.modDataFixers.isEmpty(); + } + + @Override + public @Nullable List getFixerEntries(String modId) { + return modDataFixers.get(modId); + } + + @Override + public Schema getBaseSchema() { + return new Schema(0, this.latestSchema); + } + + @Override + public Dynamic updateWithAllFixers(DataFixTypes dataFixTypes, Dynamic current) { + NbtCompound compound = (NbtCompound) current.getValue(); + + Map fixers = new HashMap<>(); + + for (Map.Entry> entry : this.modDataFixers.entrySet()) { + List dataFixerEntries = entry.getValue(); + + for (DataFixerEntry dataFixerEntry : dataFixerEntries) { + int modDataVersion = FabricDataFixesInternals.getModDataVersion(compound, entry.getKey(), dataFixerEntry.key()); + + current = dataFixerEntry.dataFixer() + .update(dataFixTypes.typeReference, + current, + modDataVersion, dataFixerEntry.currentVersion()); + } + } + + return current; + } + + @Override + public NbtCompound addModDataVersions(NbtCompound nbt) { + NbtCompound dataVersions = nbt.getCompound(DATA_VERSIONS_KEY); + + for (Map.Entry> entries : this.modDataFixers.entrySet()) { + for (DataFixerEntry entry : entries.getValue()) { + String finalEntryKey = entries.getKey(); + String entryKey = entry.key(); + + if (entryKey != null) { + finalEntryKey += ('_' + entryKey); + } + + dataVersions.putInt(finalEntryKey, entry.currentVersion()); + } + } + + nbt.put(DATA_VERSIONS_KEY, dataVersions); + return nbt; + } + + @Override + public void freeze() { + if (!this.frozen) { + modDataFixers = Collections.unmodifiableMap(this.modDataFixers); + } + + this.frozen = true; + } + + @Override + public boolean isFrozen() { + return this.frozen; + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricSubSchema.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricSubSchema.java new file mode 100644 index 0000000000..530c54b4b5 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/FabricSubSchema.java @@ -0,0 +1,79 @@ +/* + * 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.datafixer.v1; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.datafixers.util.Either; + +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; + +import net.fabricmc.fabric.api.datafixer.v1.SchemaRegistry; + +public class FabricSubSchema extends IdentifierNormalizingSchema { + public SchemaRegistry registeredBlockEntities; + public SchemaRegistry registeredEntities; + + public FabricSubSchema(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + Map> map = super.registerBlockEntities(schema); + + this.registeredBlockEntities = new SchemaRegistryImpl(); + FabricDataFixesInternals.registerBlockEntities(this.registeredBlockEntities, schema); + + ImmutableMap, Function>> modRegistry = this.registeredBlockEntities.get(); + + for (Map.Entry, Function>> entry : modRegistry.entrySet()) { + Either, Function> value = entry.getValue(); + + value.ifLeft(supplier -> schema.register(map, entry.getKey(), supplier)); + + value.ifRight(function -> schema.register(map, entry.getKey(), function)); + } + + return map; + } + + @Override + public Map> registerEntities(Schema schema) { + Map> map = super.registerEntities(schema); + + this.registeredEntities = new SchemaRegistryImpl(); + FabricDataFixesInternals.registerEntities(this.registeredEntities, schema); + + ImmutableMap, Function>> modRegistry = this.registeredEntities.get(); + + for (Map.Entry, Function>> entry : modRegistry.entrySet()) { + Either, Function> value = entry.getValue(); + + value.ifLeft(supplier -> schema.register(map, entry.getKey(), supplier)); + + value.ifRight(function -> schema.register(map, entry.getKey(), function)); + } + + return map; + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/NoOpFabricDataFixesInternals.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/NoOpFabricDataFixesInternals.java new file mode 100644 index 0000000000..6e2e593be6 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/NoOpFabricDataFixesInternals.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.impl.datafixer.v1; + +import java.util.List; + +import com.mojang.datafixers.DataFixerUpper; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import net.minecraft.datafixer.DataFixTypes; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; + +import net.fabricmc.fabric.api.datafixer.v1.EmptySchema; + +public final class NoOpFabricDataFixesInternals extends FabricDataFixesInternals { + // From QSL. + private final Schema schema = new EmptySchema(0); + + private boolean frozen = false; + + public NoOpFabricDataFixesInternals() { + } + + @Override + public void registerFixer(String modId, @Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, @Nullable String key, DataFixerUpper dataFixer) { + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public @Nullable List getFixerEntries(String modId) { + return null; + } + + @Override + public Schema getBaseSchema() { + return this.schema; + } + + @Override + public Dynamic updateWithAllFixers(DataFixTypes dataFixTypes, Dynamic dynamic) { + return new Dynamic<>(dynamic.getOps(), dynamic.getValue().copy()); + } + + @Override + public NbtCompound addModDataVersions(NbtCompound nbt) { + return nbt; + } + + @Override + public void freeze() { + this.frozen = true; + } + + @Override + public boolean isFrozen() { + return this.frozen; + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/SchemaRegistryImpl.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/SchemaRegistryImpl.java new file mode 100644 index 0000000000..893730fbe6 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/SchemaRegistryImpl.java @@ -0,0 +1,88 @@ +/* + * 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.datafixer.v1; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import com.mojang.datafixers.util.Either; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.datafixer.v1.SchemaRegistry; + +public class SchemaRegistryImpl implements SchemaRegistry { + private final Map, Function>> registry = new Object2ReferenceOpenHashMap<>(); + private final List> subVersionSchemas = new ObjectArrayList<>(); + + @Override + public void register(Identifier id, Supplier template) { + this.registry.put(id.toString(), Either.left(template)); + } + + @Override + public void register(Identifier id, Function template) { + this.registry.put(id.toString(), Either.right(template)); + } + + @Override + public void addSchema(BiFunction factory) { + this.subVersionSchemas.add(factory); + } + + @Override + public Supplier remove(Identifier id) { + Either, Function> found = this.registry.get(id.toString()); + AtomicReference> supplier = new AtomicReference<>(); + + found.ifLeft(supplier::set); + found.ifRight(function -> supplier.set(() -> function.apply(id.toString()))); + + this.registry.remove(id.toString()); + return supplier.get(); + } + + @Override + public ImmutableMap, Function>> get() { + return ImmutableMap.copyOf(this.registry); + } + + @Override + public ImmutableList getKeys() { + return ImmutableList.copyOf(this.registry.keySet()); + } + + @Override + public ImmutableList, Function>> getValues() { + return ImmutableList.copyOf(this.registry.values()); + } + + @Override + public ImmutableList> getFutureSchemas() { + return ImmutableList.copyOf(this.subVersionSchemas); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/server/ServerFreezer.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/server/ServerFreezer.java new file mode 100644 index 0000000000..d1c9a757cc --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/impl/datafixer/v1/server/ServerFreezer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.impl.datafixer.v1.server; + +import net.fabricmc.api.DedicatedServerModInitializer; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; + +public final class ServerFreezer implements DedicatedServerModInitializer { + // From QSL. + @Override + public void onInitializeServer() { + ServerLifecycleEvents.SERVER_STARTING.register((server) -> FabricDataFixesInternals.get().freeze()); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/DataFixTypesMixin.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/DataFixTypesMixin.java new file mode 100644 index 0000000000..6ae8f2901b --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/DataFixTypesMixin.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-2022 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. + * + * This file is a modified version of Quilt Standard Libraries, + * authored by QuiltMC. + */ + +package net.fabricmc.fabric.mixin.datafixer.v1; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.mojang.datafixers.DataFixer; +import com.mojang.serialization.Dynamic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.datafixer.DataFixTypes; +import net.minecraft.nbt.NbtElement; + +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; + +@Mixin(DataFixTypes.class) +public class DataFixTypesMixin { + // From QSL. + @SuppressWarnings({"rawtypes", "unchecked"}) + @ModifyReturnValue( + method = "update(Lcom/mojang/datafixers/DataFixer;Lcom/mojang/serialization/Dynamic;II)Lcom/mojang/serialization/Dynamic;", + at = @At("RETURN") + ) + private Dynamic updateDataWithFixers(Dynamic original, DataFixer fixer, Dynamic dynamic, int oldVersion, int targetVersion) { + DataFixTypes type = DataFixTypes.class.cast(this); + Object value = original.getValue(); + + if (type != DataFixTypes.WORLD_GEN_SETTINGS && value instanceof NbtElement) { + return FabricDataFixesInternals.get().updateWithAllFixers(type, (Dynamic) original); + } + + return original; + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/DataFixerUpperMixin.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/DataFixerUpperMixin.java new file mode 100644 index 0000000000..6f55e95e5d --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/DataFixerUpperMixin.java @@ -0,0 +1,38 @@ +/* + * 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.datafixer.v1; + +import com.mojang.datafixers.DataFixerUpper; +import com.mojang.datafixers.schemas.Schema; +import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.fabricmc.fabric.impl.datafixer.v1.DataFixerUpperExtension; + +@Mixin(DataFixerUpper.class) +public class DataFixerUpperMixin implements DataFixerUpperExtension { + @Shadow + @Final + private Int2ObjectSortedMap schemas; + + @Override + public Int2ObjectSortedMap fabric_getSchemas() { + return this.schemas; + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/NbtHelperMixin.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/NbtHelperMixin.java new file mode 100644 index 0000000000..decfd65a9a --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/NbtHelperMixin.java @@ -0,0 +1,34 @@ +/* + * 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.datafixer.v1; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtHelper; + +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; + +@Mixin(NbtHelper.class) +public class NbtHelperMixin { + @ModifyReturnValue(method = "putDataVersion(Lnet/minecraft/nbt/NbtCompound;)Lnet/minecraft/nbt/NbtCompound;", at = @At("RETURN")) + private static NbtCompound addModDataVersions(NbtCompound original) { + return FabricDataFixesInternals.get().addModDataVersions(original); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/SchemasMixin.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/SchemasMixin.java new file mode 100644 index 0000000000..f3e383f40b --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/SchemasMixin.java @@ -0,0 +1,62 @@ +/* + * 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.datafixer.v1; + +import com.mojang.datafixers.DataFixerBuilder; +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.Slice; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.fix.ChoiceTypesFix; + +import net.fabricmc.fabric.impl.datafixer.v1.FabricSubSchema; + +@Mixin(Schemas.class) +public class SchemasMixin { + @Inject( + method = "build", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/datafixers/DataFixerBuilder;addSchema(ILjava/util/function/BiFunction;)Lcom/mojang/datafixers/schemas/Schema;", + ordinal = 0 + ), + slice = @Slice( + from = @At( + value = "CONSTANT", + args = "intValue=1803" + ) + ) + ) + private static void addFabricFixers(DataFixerBuilder builder, CallbackInfo ci) { + FabricSubSchema schema = (FabricSubSchema) builder.addSchema(1903, FabricSubSchema::new); + + if (!schema.registeredBlockEntities.getKeys().isEmpty()) { + builder.addFixer(new ChoiceTypesFix(schema, "Add Fabric block entities.", TypeReferences.BLOCK_ENTITY)); + } + + if (!schema.registeredEntities.getKeys().isEmpty()) { + builder.addFixer(new ChoiceTypesFix(schema, "Add Fabric entities.", TypeReferences.ENTITY)); + } + + schema.registeredBlockEntities = null; + schema.registeredEntities = null; + } +} diff --git a/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/VersionedChunkStorageMixin.java b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/VersionedChunkStorageMixin.java new file mode 100644 index 0000000000..16f2b28c50 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/java/net/fabricmc/fabric/mixin/datafixer/v1/VersionedChunkStorageMixin.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.mixin.datafixer.v1; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.SaveVersion; +import net.minecraft.world.storage.VersionedChunkStorage; + +import net.fabricmc.fabric.impl.datafixer.v1.FabricDataFixesInternals; + +@Mixin(VersionedChunkStorage.class) +public class VersionedChunkStorageMixin { + @WrapOperation(method = "updateChunkNbt", at = @At(value = "INVOKE", target = "Lnet/minecraft/SaveVersion;getId()I")) + private int bypassCheck(SaveVersion instance, Operation original) { + if (!FabricDataFixesInternals.get().isEmpty()) { + return -1; + } + + return original.call(instance); + } +} diff --git a/fabric-data-fixer-api-v1/src/main/resources/assets/fabric-data-fixer-api-v1/icon.png b/fabric-data-fixer-api-v1/src/main/resources/assets/fabric-data-fixer-api-v1/icon.png new file mode 100644 index 0000000000..2931efbf61 Binary files /dev/null and b/fabric-data-fixer-api-v1/src/main/resources/assets/fabric-data-fixer-api-v1/icon.png differ diff --git a/fabric-data-fixer-api-v1/src/main/resources/fabric-data-fixer-api-v1.accesswidener b/fabric-data-fixer-api-v1/src/main/resources/fabric-data-fixer-api-v1.accesswidener new file mode 100644 index 0000000000..d84b4e5cdf --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/resources/fabric-data-fixer-api-v1.accesswidener @@ -0,0 +1,12 @@ +accessWidener v2 named + +transitive-accessible field net/minecraft/datafixer/DataFixTypes typeReference Lcom/mojang/datafixers/DSL$TypeReference; + +# methods for creating fixes +transitive-accessible method net/minecraft/datafixer/Schemas replacingRaw (Ljava/util/Map;)Ljava/util/function/UnaryOperator; +transitive-accessible method net/minecraft/datafixer/Schemas replacing (Ljava/util/Map;)Ljava/util/function/UnaryOperator; +transitive-accessible method net/minecraft/datafixer/Schemas replacing (Ljava/lang/String;Ljava/lang/String;)Ljava/util/function/UnaryOperator; +transitive-accessible method net/minecraft/datafixer/schema/Schema100 targetItems (Lcom/mojang/datafixers/schemas/Schema;)Lcom/mojang/datafixers/types/templates/TypeTemplate; + +accessible class net/minecraft/registry/RegistryLoader$RegistryLoadable +accessible class net/minecraft/registry/RegistryLoader$Loader diff --git a/fabric-data-fixer-api-v1/src/main/resources/fabric-data-fixer-api-v1.mixins.json b/fabric-data-fixer-api-v1/src/main/resources/fabric-data-fixer-api-v1.mixins.json new file mode 100644 index 0000000000..a7b28f5f59 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/resources/fabric-data-fixer-api-v1.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.mixin.datafixer.v1", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "DataFixerUpperMixin", + "DataFixTypesMixin", + "NbtHelperMixin", + "SchemasMixin", + "VersionedChunkStorageMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/fabric-data-fixer-api-v1/src/main/resources/fabric.mod.json b/fabric-data-fixer-api-v1/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..15ddf7fedf --- /dev/null +++ b/fabric-data-fixer-api-v1/src/main/resources/fabric.mod.json @@ -0,0 +1,38 @@ +{ + "schemaVersion": 1, + "id": "fabric-data-fixer-api-v1", + "name": "Fabric Data Fixer API (v1)", + "version": "${version}", + "environment": "*", + "license": "Apache-2.0", + "icon": "assets/fabric-data-fixer-api-v1/icon.png", + "contact": { + "homepage": "https://fabricmc.net", + "irc": "irc://irc.esper.net:6667/fabric", + "issues": "https://github.com/FabricMC/fabric/issues", + "sources": "https://github.com/FabricMC/fabric" + }, + "authors": [ + "FabricMC", + "QuiltMC" + ], + "depends": { + "fabricloader": ">=0.15.11" + }, + "description": "Provides support for modded data fixers.", + "accessWidener": "fabric-data-fixer-api-v1.accesswidener", + "entrypoints": { + "server": [ + "net.fabricmc.fabric.impl.datafixer.v1.server.ServerFreezer" + ], + "client": [ + "net.fabricmc.fabric.impl.client.datafixer.v1.ClientFreezer" + ] + }, + "mixins": [ + "fabric-data-fixer-api-v1.mixins.json" + ], + "custom": { + "fabric-api:module-lifecycle": "experimental" + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ChestSchema1.java b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ChestSchema1.java new file mode 100644 index 0000000000..f3536de3ea --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ChestSchema1.java @@ -0,0 +1,40 @@ +/* + * 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.test.datafixer.v1; + +import java.util.Map; +import java.util.function.Supplier; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; + +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; + +public class ChestSchema1 extends IdentifierNormalizingSchema { + public ChestSchema1(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + Map> map = super.registerBlockEntities(schema); + schema.register(map, DataFixerTest.OLD_CHEST_ID.toString(), (() -> DSL.optionalFields("Items", DSL.list(TypeReferences.ITEM_STACK.in(schema))))); + return map; + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ChestSchema2.java b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ChestSchema2.java new file mode 100644 index 0000000000..8dcc837855 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ChestSchema2.java @@ -0,0 +1,38 @@ +/* + * 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.test.datafixer.v1; + +import java.util.Map; +import java.util.function.Supplier; + +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; + +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; + +public class ChestSchema2 extends IdentifierNormalizingSchema { + public ChestSchema2(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + Map> map = super.registerBlockEntities(schema); + map.put(DataFixerTest.NEW_CHEST_ID.toString(), map.remove(DataFixerTest.OLD_CHEST_ID.toString())); + return map; + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/DataFixerTest.java b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/DataFixerTest.java new file mode 100644 index 0000000000..814d1ec2b9 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/DataFixerTest.java @@ -0,0 +1,217 @@ +/* + * 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.test.datafixer.v1; + +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +import com.mojang.datafixers.schemas.Schema; +import com.mojang.logging.LogUtils; +import org.slf4j.Logger; + +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.datafixer.Schemas; +import net.minecraft.datafixer.TypeReferences; +import net.minecraft.datafixer.fix.ChoiceTypesFix; +import net.minecraft.datafixer.fix.RenameBlockEntityFix; +import net.minecraft.datafixer.schema.IdentifierNormalizingSchema; +import net.minecraft.item.Item; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.structure.StructureTemplate; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.biome.Biome; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.biome.v1.TheEndBiomes; +import net.fabricmc.fabric.api.datafixer.v1.FabricDataFixerBuilder; +import net.fabricmc.fabric.api.datafixer.v1.FabricDataFixes; +import net.fabricmc.fabric.api.datafixer.v1.SimpleFixes; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; + +public class DataFixerTest implements ModInitializer, ServerLifecycleEvents.ServerStarted { + private static final Logger LOGGER = LogUtils.getLogger(); + static final String MOD_ID = "fabric-data-fixer-api-v1-testmod"; + /** + * Note: must be equal to the one in {@code fabric.mod.json} (used for verifying). + */ + private static final int CURRENT_DATA_VERSION = 4; + /** + * If {@code true}, generates a "base" save data for running a data fixer. If + * {@code false} (default), runs the data fixer. + */ + public static final boolean GENERATE_MODE = Boolean.getBoolean("fabric.dataFixerTestMod.genMode"); + + public static final Identifier OLD_ITEM_ID = Identifier.of(MOD_ID, "old_item"); + public static final Identifier NEW_ITEM_ID = Identifier.of(MOD_ID, "new_item"); + public static final Item ITEM = new Item(new Item.Settings()); + + public static final Identifier OLD_BLOCK_ID = Identifier.of(MOD_ID, "old_block"); + public static final Identifier NEW_BLOCK_ID = Identifier.of(MOD_ID, "new_block"); + public static final Block BLOCK = new Block(AbstractBlock.Settings.create()); + + public static final Identifier OLD_CHEST_ID = Identifier.of(MOD_ID, "old_chest"); + public static final Identifier NEW_CHEST_ID = Identifier.of(MOD_ID, "new_chest"); + public static final Block CHEST = new Block(AbstractBlock.Settings.copy(Blocks.CHEST)); + public static final BlockEntityType CHEST_BLOCK_ENTITY = FabricBlockEntityTypeBuilder.create(ModdedChestBlockEntity::new, BLOCK).build(); + + public static final Identifier OLD_BIOME_ID = Identifier.of(MOD_ID, "old_biome"); + public static final Identifier NEW_BIOME_ID = Identifier.of(MOD_ID, "new_biome"); + public static final RegistryKey BIOME_KEY = RegistryKey.of( + RegistryKeys.BIOME, GENERATE_MODE ? OLD_BIOME_ID : NEW_BIOME_ID); + + @Override + public void onInitialize() { + Registry.register(Registries.ITEM, GENERATE_MODE ? OLD_ITEM_ID : NEW_ITEM_ID, ITEM); + Registry.register(Registries.BLOCK, GENERATE_MODE ? OLD_BLOCK_ID : NEW_BLOCK_ID, BLOCK); + Registry.register(Registries.BLOCK, GENERATE_MODE ? OLD_CHEST_ID : NEW_CHEST_ID, CHEST); + Registry.register(Registries.BLOCK_ENTITY_TYPE, GENERATE_MODE ? OLD_CHEST_ID : NEW_CHEST_ID, CHEST_BLOCK_ENTITY); + TheEndBiomes.addMainIslandBiome(BIOME_KEY, 10); + + ServerLifecycleEvents.SERVER_STARTED.register(this); + + if (!GENERATE_MODE) { + // Not generate mode - run the data fixer and tests. + initDataFixer(); + initChestDataFixer(); // Used to test isolation issues + testNbt(); + } + } + + private void initDataFixer() { + ModContainer mod = FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(); + FabricDataFixerBuilder builder = FabricDataFixerBuilder.create(mod); + + if (builder.getDataVersion() != CURRENT_DATA_VERSION) { + throw new AssertionError(String.format(Locale.ROOT, "Expected data version %d, got %d", CURRENT_DATA_VERSION, builder.getDataVersion())); + } + + builder.addSchema(0, FabricDataFixes.getBaseSchema()); + + Schema schema1 = builder.addSchema(1, IdentifierNormalizingSchema::new); + SimpleFixes.addItemRenameFix(builder, "Rename old_item to new_item", OLD_ITEM_ID, NEW_ITEM_ID, schema1); + + Schema schema2 = builder.addSchema(2, IdentifierNormalizingSchema::new); + SimpleFixes.addBlockRenameFix(builder, "Rename old_block to new_block", OLD_BLOCK_ID, NEW_BLOCK_ID, schema2); + + Schema schema3 = builder.addSchema(3, IdentifierNormalizingSchema::new); + SimpleFixes.addBiomeRenameFix(builder, "Rename old_biome to new_biome", Map.of(OLD_BIOME_ID, NEW_BIOME_ID), schema3); + + Schema schema4 = builder.addSchema(4, IdentifierNormalizingSchema::new); + SimpleFixes.addBlockRenameFix(builder, "Rename old_chest to new_chest", OLD_CHEST_ID, NEW_CHEST_ID, schema4); + + FabricDataFixes.buildAndRegisterFixer(mod, builder); + + // Test that the data fixer exists + FabricDataFixes.getFixers(MOD_ID).orElseThrow(() -> new AssertionError("Data fixer is not be registered")); + } + + private void initChestDataFixer() { + ModContainer mod = FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow(); + FabricDataFixerBuilder builder = new FabricDataFixerBuilder(GENERATE_MODE ? 1 : 2); + + builder.addSchema(0, FabricDataFixes.getBaseSchema()); + + Schema schema1 = builder.addSchema(1, ChestSchema1::new); + builder.addFixer(new ChoiceTypesFix(schema1, "Add modded chest", TypeReferences.BLOCK_ENTITY)); + + if (!GENERATE_MODE) { + Schema schema2 = builder.addSchema(2, ChestSchema2::new); + builder.addFixer(RenameBlockEntityFix.create(schema2, "Rename modded chest", Schemas.replacing(OLD_CHEST_ID.toString(), NEW_CHEST_ID.toString()))); + } + + FabricDataFixes.buildAndRegisterFixer(mod, "chest", builder); + } + + /** + * Tests parsing of various data version-containing NBTs. + */ + private void testNbt() { + NbtCompound structureNbt = new NbtCompound(); + new StructureTemplate().writeNbt(structureNbt); + int structureDataVersion = FabricDataFixes.getModDataVersion(structureNbt, MOD_ID); + + if (structureDataVersion != CURRENT_DATA_VERSION) { + throw new AssertionError( + String.format(Locale.ROOT, "Expected structure data version %d, got %d", CURRENT_DATA_VERSION, structureDataVersion) + ); + } + + int unknownModDataVersion = FabricDataFixes.getModDataVersion(structureNbt, "tater"); + + if (unknownModDataVersion != 0) { + throw new AssertionError( + String.format(Locale.ROOT, "Expected unknown mod data version %d, got %d", 0, unknownModDataVersion) + ); + } + + int emptyNbtDataVersion = FabricDataFixes.getModDataVersion(new NbtCompound(), MOD_ID); + + if (emptyNbtDataVersion != 0) { + throw new AssertionError( + String.format(Locale.ROOT, "Expected empty NBT data version %d, got %d", 0, emptyNbtDataVersion) + ); + } + } + + @Override + public void onServerStarted(MinecraftServer server) { + ServerWorld world = server.getOverworld(); + ServerWorld end = Objects.requireNonNull(server.getWorld(World.END)); + LOGGER.info("Loading the End..."); + end.getChunk(0, 0); // Load chunks to generate modded biome/test upgrading + + // In non-generate mode, we only have to load the End, nothing else needed + if (!GENERATE_MODE) return; + + LOGGER.info("Preparing world for data fixer testing..."); + + BlockPos chestPos = new BlockPos(0, 10, 0); + world.setBlockState(chestPos, Blocks.CHEST.getDefaultState()); + + if (world.getBlockEntity(chestPos) instanceof ChestBlockEntity chestBlockEntity) { + chestBlockEntity.setStack(0, ITEM.getDefaultStack()); + } + + BlockPos moddedChestPos = chestPos.down(); + world.setBlockState(moddedChestPos, CHEST.getDefaultState()); + + if (world.getBlockEntity(moddedChestPos) instanceof ModdedChestBlockEntity chestBlockEntity) { + chestBlockEntity.setStack(0, ITEM.getDefaultStack()); + } + + world.setBlockState(moddedChestPos.down(), BLOCK.getDefaultState()); + + LOGGER.info("Generation finished, stopping server..."); + server.stop(false); + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ModdedChestBlockEntity.java b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ModdedChestBlockEntity.java new file mode 100644 index 0000000000..fa3ca3a217 --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/ModdedChestBlockEntity.java @@ -0,0 +1,32 @@ +/* + * 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.test.datafixer.v1; + +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.util.math.BlockPos; + +public class ModdedChestBlockEntity extends ChestBlockEntity { + protected ModdedChestBlockEntity(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + protected ModdedChestBlockEntity(BlockPos pos, BlockState state) { + super(pos, state); + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/mixin/RegistryLoaderMixin.java b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/mixin/RegistryLoaderMixin.java new file mode 100644 index 0000000000..f716aa0f2d --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/java/net/fabricmc/fabric/test/datafixer/v1/mixin/RegistryLoaderMixin.java @@ -0,0 +1,73 @@ +/* + * 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.test.datafixer.v1.mixin; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import com.llamalad7.mixinextras.injector.ModifyReceiver; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.registry.MutableRegistry; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.RegistryLoader; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.biome.TheEndBiomeCreator; +import net.minecraft.world.gen.carver.ConfiguredCarver; +import net.minecraft.world.gen.feature.PlacedFeature; + +import net.fabricmc.fabric.test.datafixer.v1.DataFixerTest; + +@Mixin(RegistryLoader.class) +public class RegistryLoaderMixin { + @SuppressWarnings("unchecked") + @ModifyReceiver( + method = "load", + at = @At( + value = "INVOKE", + target = "Ljava/util/List;forEach(Ljava/util/function/Consumer;)V", + ordinal = 1 + ) + ) + private static List> registerTestmodBiome( + List> instance, + Consumer> consumer + ) { + Stream> registries = instance.stream().map(RegistryLoader.Loader::registry); + Map, MutableRegistry> registryMap = new Object2ObjectOpenHashMap<>(); + registries.forEach(registry -> registryMap.put(registry.getKey(), registry)); + + MutableRegistry biomeRegistry = (MutableRegistry) registryMap.get(RegistryKeys.BIOME); + MutableRegistry placedFeatureRegistry = (MutableRegistry) registryMap.get(RegistryKeys.PLACED_FEATURE); + MutableRegistry> configuredCarverRegistry = (MutableRegistry>) registryMap.get(RegistryKeys.CONFIGURED_CARVER); + + if (biomeRegistry == null || placedFeatureRegistry == null || configuredCarverRegistry == null || biomeRegistry.contains(DataFixerTest.BIOME_KEY)) { + return instance; + } + + Biome customBiome = TheEndBiomeCreator.createEndHighlands(placedFeatureRegistry.createMutableEntryLookup(), configuredCarverRegistry.createMutableEntryLookup()); + Registry.register(biomeRegistry, DataFixerTest.BIOME_KEY, customBiome); + + return instance; + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/resources/fabric-data-fixer-api-v1-testmod.mixins.json b/fabric-data-fixer-api-v1/src/testmod/resources/fabric-data-fixer-api-v1-testmod.mixins.json new file mode 100644 index 0000000000..d1f9cc696a --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/resources/fabric-data-fixer-api-v1-testmod.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.test.datafixer.v1.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "RegistryLoaderMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/fabric-data-fixer-api-v1/src/testmod/resources/fabric.mod.json b/fabric-data-fixer-api-v1/src/testmod/resources/fabric.mod.json new file mode 100644 index 0000000000..acf5740e6a --- /dev/null +++ b/fabric-data-fixer-api-v1/src/testmod/resources/fabric.mod.json @@ -0,0 +1,25 @@ +{ + "schemaVersion": 1, + "id": "fabric-data-fixer-api-v1-testmod", + "name": "Fabric Data Fixer API (Test Mod)", + "version": "1.0.0", + "environment": "*", + "license": "Apache-2.0", + "depends": { + "fabric-biome-api-v1": "*", + "fabric-data-fixer-api-v1": "*", + "fabric-item-api-v1": "*", + "fabric-object-builder-api-v1": "*" + }, + "entrypoints": { + "main": [ + "net.fabricmc.fabric.test.datafixer.v1.DataFixerTest" + ] + }, + "mixins": [ + "fabric-data-fixer-api-v1-testmod.mixins.json" + ], + "custom": { + "fabric-data-fixer-api-v1:version": 4 + } +} diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/Registration.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/Registration.java index 9463c25af3..5b30dcd9ed 100644 --- a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/Registration.java +++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/Registration.java @@ -48,7 +48,7 @@ public final class Registration { public static final Item OCTAGONAL_COLUMN_ITEM = register("octagonal_column", new BlockItem(OCTAGONAL_COLUMN_BLOCK, new Item.Settings())); public static final Item RIVERSTONE_ITEM = register("riverstone", new BlockItem(RIVERSTONE_BLOCK, new Item.Settings())); - public static final BlockEntityType FRAME_BLOCK_ENTITY_TYPE = register("frame", FabricBlockEntityTypeBuilder.create(FrameBlockEntity::new, FRAME_BLOCKS).build(null)); + public static final BlockEntityType FRAME_BLOCK_ENTITY_TYPE = register("frame", FabricBlockEntityTypeBuilder.create(FrameBlockEntity::new, FRAME_BLOCKS).build()); private static T register(String path, T block) { return Registry.register(Registries.BLOCK, RendererTest.id(path), block); diff --git a/gradle.properties b/gradle.properties index 9134ef38af..59c8d05fe5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,6 +24,7 @@ fabric-commands-v0-version=0.2.66 fabric-content-registries-v0-version=8.0.14 fabric-crash-report-info-v1-version=0.2.29 fabric-data-attachment-api-v1-version=1.1.24 +fabric-data-fixer-api-v1-version=1.0.0 fabric-data-generation-api-v1-version=20.2.11 fabric-dimensions-v1-version=4.0.0 fabric-entity-events-v1-version=1.6.12 diff --git a/settings.gradle b/settings.gradle index 30f7ff72cb..b798b0ac2d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -27,6 +27,7 @@ include 'fabric-content-registries-v0' include 'fabric-convention-tags-v2' include 'fabric-crash-report-info-v1' include 'fabric-data-attachment-api-v1' +include 'fabric-data-fixer-api-v1' include 'fabric-data-generation-api-v1' include 'fabric-dimensions-v1' include 'fabric-entity-events-v1'