Skip to content
Merged
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@
package com.viaversion.viabackwards.protocol.v26_3to26_2.rewriter;

import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.rewriters.BackwardsRegistryRewriter;
import com.viaversion.viabackwards.protocol.v26_3to26_2.Protocol26_3To26_2;
import com.viaversion.viaversion.util.Key;
import java.util.List;
import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.Nullable;

public final class RegistryDataRewriter26_3 extends BackwardsRegistryRewriter {

Expand All @@ -33,9 +38,20 @@ public RegistryDataRewriter26_3(final Protocol26_3To26_2 protocol) {

@Override
public void updateEnchantmentTerm(final CompoundTag term) {
final String type = term.getString("type");
if (type != null) {
term.putString("condition", type);
// 26.3 uses "type" instead of "condition" - rename it back for 26.2
final StringTag typeTag = term.removeUnchecked("type");
if (typeTag != null) {
term.put("condition", typeTag);
}
final String type = typeTag != null ? typeTag.getValue() : null;

// In 26.3, the "terms" field of all_of/any_of can be an inline term (CompoundTag)
// instead of always being a ListTag<CompoundTag>. Convert it to a list for 26.2.
final Tag termsTag = term.get("terms");
if (termsTag instanceof CompoundTag inlineTerm) {
final ListTag<CompoundTag> termsList = new ListTag<>(CompoundTag.class);
termsList.add(inlineTerm);
term.put("terms", termsList);
}

if (Key.equals(type, "damage_source_properties")) {
Expand All @@ -49,6 +65,20 @@ public void updateEnchantmentTerm(final CompoundTag term) {
} else if (Key.equals(type, "match_block")) {
// Has to run before super, which would otherwise replace the term by a dummy effect
updateMatchBlock(term);
} else if (Key.equals(type, "int_value_check") || Key.equals(type, "float_value_check")) {
term.putString("condition", "minecraft:value_check");
if (!convert(term, "value", "value", RegistryDataRewriter26_3::toNumberProvider)
|| !convert(term, "test", "range", RegistryDataRewriter26_3::toIntRange)) {
replaceWithAlwaysTrue(term);
}
} else if (Key.equals(type, "time_check")) {
if (!convert(term, "value", "value", RegistryDataRewriter26_3::toIntRange)) {
replaceWithAlwaysTrue(term);
}
} else if (Key.equals(type, "random_chance")) {
if (!convert(term, "chance", "chance", RegistryDataRewriter26_3::toNumberProvider)) {
replaceWithAlwaysTrue(term);
}
}

super.updateEnchantmentTerm(term);
Expand Down Expand Up @@ -127,12 +157,105 @@ private void updateMatchBlock(final CompoundTag term) {
return;
}

// Block tags and lists have no equivalent, replace the term by an always true condition
// Block tags and lists have no equivalent
replaceWithAlwaysTrue(term);
}

private static void replaceWithAlwaysTrue(final CompoundTag term) {
term.clear();
term.putString("condition", "minecraft:all_of");
term.put("terms", new ListTag<>(CompoundTag.class));
}

// Int and float providers have been merged into a single number provider type
private static @Nullable Tag toNumberProvider(final Tag tag) {
if (tag instanceof NumberTag) {
return tag;
}
if (!(tag instanceof CompoundTag provider)) {
return null; // References to provider registries
}

return switch (Key.stripMinecraftNamespace(provider.getString("type", ""))) {
case "constant", "enchantment_level", "environment_attribute" -> provider;
case "storage", "score" -> {
provider.remove("fallback");
yield provider;
}
case "uniform" -> convert(provider, "min", "min", RegistryDataRewriter26_3::toNumberProvider)
&& convert(provider, "max", "max", RegistryDataRewriter26_3::toNumberProvider) ? provider : null;
case "binomial" -> convert(provider, "n", "n", RegistryDataRewriter26_3::toNumberProvider)
&& convert(provider, "p", "p", RegistryDataRewriter26_3::toNumberProvider) ? provider : null;
case "add" -> {
provider.putString("type", "minecraft:sum");
yield convert(provider, "inputs", "summands", RegistryDataRewriter26_3::toNumberProviderList) ? provider : null;
}
// Old int conversion rounds instead of truncating
case "from_int", "from_float", "round" -> {
final Tag input = provider.get("input");
yield input != null ? toNumberProvider(input) : null;
}
default -> null;
};
}

private static @Nullable Tag toNumberProviderList(final Tag tag) {
final ListTag<CompoundTag> list = new ListTag<>(CompoundTag.class);
for (final Tag element : tag instanceof ListTag<?> listTag ? listTag : List.of(tag)) {
final Tag provider = toNumberProvider(element);
if (provider == null) {
return null;
}
if (provider instanceof CompoundTag compoundTag) {
list.add(compoundTag);
} else {
final CompoundTag constant = new CompoundTag();
constant.putString("type", "minecraft:constant");
constant.put("value", provider);
list.add(constant);
}
}
return list;
}

private static @Nullable Tag toIntRange(final Tag tag) {
if (tag instanceof NumberTag numberTag) {
return tag instanceof IntTag ? tag : new IntTag(Math.round(numberTag.asFloat()));
}
if (!(tag instanceof CompoundTag range)) {
return null;
}

if (range.contains("type")) {
// Single provider, convert to an exact range
final Tag value = toNumberProvider(range);
if (value == null) {
return null;
}

final CompoundTag exactRange = new CompoundTag();
exactRange.put("min", value);
exactRange.put("max", value.copy());
return exactRange;
}
return convert(range, "min", "min", RegistryDataRewriter26_3::toNumberProvider)
&& convert(range, "max", "max", RegistryDataRewriter26_3::toNumberProvider) ? range : null;
}

private static boolean convert(final CompoundTag tag, final String key, final String newKey, final Function<Tag, @Nullable Tag> converter) {
final Tag value = tag.remove(key);
if (value == null) {
return true;
}

final Tag converted = converter.apply(value);
if (converted == null) {
return false;
}
tag.put(newKey, converted);
return true;
}

private void updateTagKey(final ListTag<CompoundTag> tags) {
for (final CompoundTag tag : tags) {
final Tag idTag = tag.get("id");
Expand Down