Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dc8e116
Implement DataFixerUpper
screret May 21, 2026
7c1cc76
start adding GT fixers
screret May 21, 2026
44f0e4f
Move `GT_DATA_VERSION` field to GTCEu.java
screret May 23, 2026
b445014
Formatting changes
screret May 23, 2026
9b20cd0
Add the remaining 150 or so GT machines to V0 schema
screret May 23, 2026
b8a26d7
Convert (almost) all existing "fixers" into proper data fixers
screret May 23, 2026
7c41454
Add TODO
screret May 23, 2026
0886cd9
Exclude OPTIONS fixer type from being processed by us
screret May 23, 2026
827cb71
saner GT data version & explanatory comment
screret May 23, 2026
2eadd02
Lazy
screret May 23, 2026
ba1ed0e
Fix namespaced IDs
screret May 23, 2026
f8f8982
Rename `getModDataVersion` to `getGTDataVersion` and `addModDataVersi…
screret May 27, 2026
fd60466
Delete DataFixerEntry
screret May 27, 2026
05bdc3d
Make vanilla schema initialization lazy
screret May 27, 2026
1f9615a
Add extra utility functions for updating from specific source data ve…
screret May 27, 2026
d09c87f
Inject fixers to Forge registry ID maps
screret May 27, 2026
c2a3319
Simplify datafixer handling; we don't need handling for multiple mods…
screret May 27, 2026
194ae3d
Remove `TagPrefix.invertedName`; that code was for updating to **1.0.…
screret May 27, 2026
6610614
change schema variable names and some strings
screret May 27, 2026
a6ad070
remove unnecessary hook() from fluid stack type
screret May 27, 2026
d8db52f
Merge branch '1.20.1' into sc/datafixerupper-1.20
screret May 27, 2026
1e77cbb
Remove defined fields that don't use named types (like items/fluids)
screret May 27, 2026
3b232e3
add javadoc describing what, why & how
screret May 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions src/main/java/com/gregtechceu/gtceu/GTCEu.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ public class GTCEu {

public static final Path GTCEU_FOLDER = getGameDir().resolve("gtceu");

/**
* Only used for datafixers. Bump whenever a block changes id, save data layout changes, etc.<br>
* Should be bumped up to the next multiple of 10 the first time it is bumped after a release, then by 1 for each
* subsequent change.
*
* <p>
* Example versions:
* <ul>
* <li>0: 7.5.3</li>
* <li>10: 8.0.0-SNAPSHOT+HASH1</li> // TODO set the correct hash here
* <li>11: 8.0.0-SNAPSHOT+HASH2</li>
* <li>20: 8.0.1-SNAPSHOT+HASH2</li>
* </ul>
*/
public static final int GT_DATA_VERSION = 10;

public GTCEu() {
GTCEu.init();
GTCEuAPI.instance = this;
Expand Down Expand Up @@ -96,7 +112,7 @@ public static boolean isDataGen() {

/**
* A friendly reminder that the server instance is populated on the server side only, so null/side check it!
*
*
* @return the current minecraft server instance
*/
public static MinecraftServer getMinecraftServer() {
Expand All @@ -113,7 +129,7 @@ public static boolean isModLoaded(String modId) {

/**
* For async stuff use this, otherwise use {@link GTCEu isClientSide}
*
*
* @return if the current thread is the client thread
*/
public static boolean isClientThread() {
Expand All @@ -123,7 +139,7 @@ public static boolean isClientThread() {
/**
* @return if the game is the <strong>PHYSICAL</strong> client, e.g. not a dedicated server.
* @apiNote Do not use this to check if you're currently on the server thread for side-specific actions!
* It does <strong>NOT</strong> work for that. Use {@link #isClientThread()} instead.
* It does <strong>NOT</strong> work for that. Use {@link #isClientThread()} instead.
* @see #isClientThread()
*/
public static boolean isClientSide() {
Expand All @@ -132,9 +148,9 @@ public static boolean isClientSide() {

/**
* This check isn't the same for client and server!
*
*
* @return if it's safe to access the current instance {@link net.minecraft.world.level.Level Level} on client or if
* it's safe to access any level on server.
* it's safe to access any level on server.
*/
public static boolean canGetServerLevel() {
if (isClientSide()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public boolean isEmpty() {
.requiresCorrectToolForDrops().strength(4.5F, 9.0F),
new ResourceLocation("block/end_stone"), true, false, true);

public static final TagPrefix rawOre = new TagPrefix("raw", true)
public static final TagPrefix rawOre = new TagPrefix("raw")
.idPattern("raw_%s")
.defaultTagPath("raw_materials/%s")
.unformattedTagPath("raw_materials")
Expand Down Expand Up @@ -983,8 +983,6 @@ public record BlockProperties(Supplier<Supplier<RenderType>> renderType,
@Getter
@Setter
private String idPattern;
@Getter
public final boolean invertedName;

protected final List<TagType> tags = new ArrayList<>();
@Setter
Expand Down Expand Up @@ -1050,13 +1048,8 @@ public record BlockProperties(Supplier<Supplier<RenderType>> renderType,
protected final Set<TagKey<Block>> miningToolTag = new HashSet<>();

public TagPrefix(String name) {
this(name, false);
}

public TagPrefix(String name, boolean invertedName) {
this.name = name;
this.idPattern = "%s_" + getLowerCaseName();
this.invertedName = invertedName;
this.langValue = "%s " + FormattingUtil.toEnglishName(getLowerCaseName());
PREFIXES.put(name, this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.gregtechceu.gtceu.api.datafixer;

import com.gregtechceu.gtceu.GTCEu;
import com.gregtechceu.gtceu.common.data.datafixer.GTDataFixers;

import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixUtils;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.serialization.Dynamic;

import net.minecraft.SharedConstants;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.util.datafix.DataFixers;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.*;

@ApiStatus.Internal
public abstract class DataFixHelper {

public static final String GT_DATA_VERSION_TAG = "GTCEu_DataVersion";
public static final Logger LOGGER = LogManager.getLogger("GTCEu/DFU");

// region updating

public static <T> Dynamic<T> updateToCurrentVersion(DSL.TypeReference type, Dynamic<T> dynamic) {
return update(type, dynamic, getGTDataVersion(dynamic), GTCEu.GT_DATA_VERSION);
}

public static <T> Dynamic<T> update(DSL.TypeReference type, Dynamic<T> dynamic, int version, int newVersion) {
if (GTDataFixers.getDataFixer() == null) {
return dynamic;
}
return GTDataFixers.getDataFixer().update(type, dynamic, version, newVersion);
}

public static CompoundTag updateToCurrentVersion(DSL.TypeReference dataFixTypes, CompoundTag tag) {
return update(dataFixTypes, tag, getGTDataVersion(tag), GTCEu.GT_DATA_VERSION);
}

public static CompoundTag update(DSL.TypeReference dataFixTypes, CompoundTag tag, int version, int newVersion) {
return (CompoundTag) update(dataFixTypes, new Dynamic<>(NbtOps.INSTANCE, tag), version, newVersion).getValue();
}

// endregion

// region data version handling

public static CompoundTag addGTDataVersion(CompoundTag tag) {
if (GTDataFixers.getDataFixer() != null) {
tag.putInt(GT_DATA_VERSION_TAG, GTCEu.GT_DATA_VERSION);
}

return tag;
}

@Contract(pure = true)
@Range(from = 0, to = Integer.MAX_VALUE)
public static int getGTDataVersion(Dynamic<?> dynamic) {
return getGTDataVersion(dynamic, -1);
}

@Contract(pure = true)
@Range(from = 0, to = Integer.MAX_VALUE)
public static int getGTDataVersion(Dynamic<?> dynamic, int defaultValue) {
return dynamic.get(GT_DATA_VERSION_TAG).asInt(defaultValue);
}

@Contract(pure = true)
@Range(from = 0, to = Integer.MAX_VALUE)
public static int getGTDataVersion(CompoundTag tag) {
return getGTDataVersion(tag, -1);
}

@Contract(pure = true)
@Range(from = 0, to = Integer.MAX_VALUE)
public static int getGTDataVersion(CompoundTag tag, int defaultValue) {
return tag.contains(GT_DATA_VERSION_TAG, CompoundTag.TAG_ANY_NUMERIC) ? tag.getInt(GT_DATA_VERSION_TAG) :
defaultValue;
}

// endregion

public static Schema getLatestVanillaSchema() {
int vanillaDataVersion = SharedConstants.getCurrentVersion().getDataVersion().getVersion();
return DataFixers.getDataFixer().getSchema(DataFixUtils.makeKey(vanillaDataVersion));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.gregtechceu.gtceu.api.datafixer;

import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.datafixers.types.Type;
import com.mojang.datafixers.types.templates.TypeTemplate;
import com.mojang.serialization.Dynamic;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import org.jetbrains.annotations.Range;

import java.util.Map;
import java.util.function.Supplier;

class EmptyDataFixer implements DataFixer {

static final EmptyDataFixer INSTANCE = new EmptyDataFixer();

private final Schema schema = new EmptySchema(0);

private EmptyDataFixer() {}

@Override
public <T> Dynamic<T> update(DSL.TypeReference type, Dynamic<T> input, int version, int newVersion) {
return input;
}

@Override
public Schema getSchema(int key) {
return this.schema;
}

/*
* Copyright 2022 QuiltMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Represents an empty {@link Schema}, having no parent and containing no type definitions.
*/
static final class EmptySchema extends Schema {

/**
* Constructs an empty schema.
*
* @param versionKey the data version key
*/
EmptySchema(@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<String, Supplier<TypeTemplate>> entityTypes,
Map<String, Supplier<TypeTemplate>> blockEntityTypes) {}

@Override
public Map<String, Supplier<TypeTemplate>> registerEntities(Schema schema) {
return Map.of();
}

@Override
public Map<String, Supplier<TypeTemplate>> registerBlockEntities(Schema schema) {
return Map.of();
}

// Ensure the schema stays empty.
@Override
public void registerType(boolean recursive, DSL.TypeReference type, Supplier<TypeTemplate> template) {
throw new UnsupportedOperationException();
}

@Override
protected Map<String, Type<?>> buildTypes() {
return Object2ObjectMaps.emptyMap();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 embeddedt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.gregtechceu.gtceu.api.datafixer;

import com.gregtechceu.gtceu.GTCEu;

import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.serialization.Dynamic;
import org.jetbrains.annotations.Nullable;

import java.util.function.Supplier;

/**
* Original from
* <a href=
* "https://github.com/embeddedt/ModernFix/blob/c561d818f398f9b9f6b826db34b705a97c187f74/src/main/java/org/embeddedt/modernfix/dfu/LazyDataFixer.java">ModernFix</a>
*/
public class LazyDataFixer implements DataFixer {
private @Nullable DataFixer backingDataFixer;
private final Supplier<@Nullable DataFixer> dfuSupplier;

public LazyDataFixer(Supplier<@Nullable DataFixer> dfuSupplier) {
this.backingDataFixer = null;
this.dfuSupplier = dfuSupplier;
}

private DataFixer getDataFixer() {
synchronized(this) {
if (this.backingDataFixer == null) {
GTCEu.LOGGER.info("Instantiating GTCEu Data Fixers");
this.backingDataFixer = this.dfuSupplier.get();
}
if (this.backingDataFixer == null) {
this.backingDataFixer = EmptyDataFixer.INSTANCE;
}
}

return this.backingDataFixer;
}

public <T> Dynamic<T> update(DSL.TypeReference type, Dynamic<T> input, int version, int newVersion) {
return version >= newVersion ? input : this.getDataFixer().update(type, input, version, newVersion);
}

public Schema getSchema(int key) {
return this.getDataFixer().getSchema(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.gregtechceu.gtceu.api.datafixer.fixes;

import com.mojang.datafixers.schemas.Schema;
import com.mojang.serialization.Dynamic;
import net.minecraft.util.datafix.fixes.ItemStackTagFix;

public abstract class ToolBehaviorFix extends ItemStackTagFix {

private final String behaviorId;
private final String newBehaviorId;

public ToolBehaviorFix(Schema outputSchema, String name, String behaviorId) {
this(outputSchema, name, behaviorId, behaviorId);
}

public ToolBehaviorFix(Schema outputSchema, String name, String behaviorId, String newBehaviorId) {
super(outputSchema, name, itemId -> true);
this.behaviorId = behaviorId;
this.newBehaviorId = newBehaviorId;
}

@Override
protected final <T> Dynamic<T> fixItemStackTag(Dynamic<T> tag) {
Dynamic<T> behavior = tag.remove(behaviorId);
behavior = fixBehavior(behavior);
return tag.set(newBehaviorId, behavior);
}

protected abstract <T> Dynamic<T> fixBehavior(Dynamic<T> tag);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NotNullByDefault
package com.gregtechceu.gtceu.api.datafixer.fixes;

import org.jetbrains.annotations.NotNullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NotNullByDefault
package com.gregtechceu.gtceu.api.datafixer;

import org.jetbrains.annotations.NotNullByDefault;
Loading
Loading