3
0
Mirror von https://github.com/ViaVersion/ViaBackwards.git synchronisiert 2024-12-26 16:12:43 +01:00

Add StructuredEnchantmentRewriter, update translation mappings

Dieser Commit ist enthalten in:
Nassim Jahnke 2024-04-03 21:09:52 +02:00
Ursprung 1507f788ec
Commit f7dad9b5d7
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: EF6771C01F6EF02F
9 geänderte Dateien mit 465 neuen und 38 gelöschten Zeilen

Datei anzeigen

@ -11,3 +11,6 @@ ij_java_names_count_to_use_import_on_demand = 999999
ij_java_imports_layout = *,|,$*
ij_java_generate_final_locals = true
ij_java_generate_final_parameters = true
[*.json]
indent_size = 2

Datei anzeigen

@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.data.Mappings;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectArrayMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag;
@ -35,6 +36,7 @@ import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.util.Key;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -44,6 +46,7 @@ public class BackwardsMappings extends MappingDataBase {
protected Int2ObjectMap<MappedItem> backwardsItemMappings;
private Map<String, String> backwardsSoundMappings;
private Map<String, String> entityNames;
private Int2ObjectMap<String> enchantmentNames;
public BackwardsMappings(final String unmappedVersion, final String mappedVersion) {
this(unmappedVersion, mappedVersion, null);
@ -77,23 +80,37 @@ public class BackwardsMappings extends MappingDataBase {
}
}
final CompoundTag entityNames = data.getCompoundTag("entitynames");
if (entityNames != null) {
this.entityNames = new HashMap<>(entityNames.size());
for (final Map.Entry<String, Tag> entry : entityNames.entrySet()) {
final StringTag mappedTag = (StringTag) entry.getValue();
this.entityNames.put(entry.getKey(), mappedTag.getValue());
}
this.entityNames = loadNameByStringMappings(data, "entitynames");
this.enchantmentNames = loadNameByIdMappings(data, "enchantmentnames");
this.backwardsSoundMappings = loadNameByStringMappings(data, "soundnames");
}
private @Nullable Map<String, String> loadNameByStringMappings(final CompoundTag data, final String key) {
final CompoundTag nameMappings = data.getCompoundTag(key);
if (nameMappings == null) {
return null;
}
final CompoundTag soundNames = data.getCompoundTag("soundnames");
if (soundNames != null) {
backwardsSoundMappings = new HashMap<>(soundNames.size());
for (final Map.Entry<String, Tag> entry : soundNames.entrySet()) {
final StringTag mappedTag = (StringTag) entry.getValue();
backwardsSoundMappings.put(entry.getKey(), mappedTag.getValue());
}
final Map<String, String> map = new HashMap<>(nameMappings.size());
for (final Map.Entry<String, Tag> entry : nameMappings.entrySet()) {
final StringTag mappedTag = (StringTag) entry.getValue();
map.put(entry.getKey(), mappedTag.getValue());
}
return map;
}
private @Nullable Int2ObjectMap<String> loadNameByIdMappings(final CompoundTag data, final String key) {
final CompoundTag nameMappings = data.getCompoundTag(key);
if (nameMappings == null) {
return null;
}
final Int2ObjectMap<String> map = new Int2ObjectArrayMap<>(nameMappings.size());
for (final Map.Entry<String, Tag> entry : nameMappings.entrySet()) {
final StringTag mappedTag = (StringTag) entry.getValue();
map.put(Integer.parseInt(entry.getKey()), mappedTag.getValue());
}
return map;
}
@Override
@ -142,13 +159,20 @@ public class BackwardsMappings extends MappingDataBase {
public @Nullable String mappedEntityName(final String entityName) {
if (entityNames == null) {
ViaBackwards.getPlatform().getLogger().severe("No entity mappings found when requesting them for " + entityName);
new Exception().printStackTrace();
ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, "No entity mappings found when requesting them for " + entityName, new RuntimeException());
return null;
}
return entityNames.get(entityName);
}
public @Nullable String mappedEnchantmentName(final int enchantmentId) {
if (enchantmentNames == null) {
ViaBackwards.getPlatform().getLogger().log(Level.SEVERE, "No enchantment name mappings found when requesting " + enchantmentId, new RuntimeException());
return null;
}
return enchantmentNames.get(enchantmentId);
}
public @Nullable Int2ObjectMap<MappedItem> getBackwardsItemMappings() {
return backwardsItemMappings;
}

Datei anzeigen

@ -27,6 +27,8 @@ import com.viaversion.viaversion.libs.opennbt.tag.builtin.ListTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.StringTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.rewriter.ItemRewriter;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
public abstract class BackwardsItemRewriterBase<C extends ClientboundPacketType, S extends ServerboundPacketType,
@ -52,26 +54,49 @@ public abstract class BackwardsItemRewriterBase<C extends ClientboundPacketType,
return item;
}
protected boolean hasBackupTag(CompoundTag displayTag, String tagName) {
return displayTag.contains(nbtTagName("o" + tagName));
protected boolean hasBackupTag(CompoundTag tag, String tagName) {
return tag.contains(nbtTagName(tagName));
}
protected void saveStringTag(CompoundTag displayTag, StringTag original, String name) {
protected void saveStringTag(CompoundTag tag, StringTag original, String name) {
// Multiple places might try to backup data
String backupName = nbtTagName("o" + name);
if (!displayTag.contains(backupName)) {
displayTag.putString(backupName, original.getValue());
String backupName = nbtTagName(name);
if (!tag.contains(backupName)) {
tag.putString(backupName, original.getValue());
}
}
protected void saveListTag(CompoundTag displayTag, ListTag<?> original, String name) {
protected void saveListTag(CompoundTag tag, ListTag<?> original, String name) {
// Multiple places might try to backup data
String backupName = nbtTagName("o" + name);
if (!displayTag.contains(backupName)) {
displayTag.put(backupName, original.copy());
String backupName = nbtTagName(name);
if (!tag.contains(backupName)) {
tag.put(backupName, original.copy());
}
}
protected void saveGenericTagList(CompoundTag tag, List<Tag> original, String name) {
// List tags cannot contain tags of different types, so we have to store them a bit more awkwardly as an indexed compound tag
String backupName = getNbtTagName() + "|" + name;
if (!tag.contains(backupName)) {
CompoundTag output = new CompoundTag();
for (int i = 0; i < original.size(); i++) {
output.put(Integer.toString(i), original.get(i));
}
tag.put(backupName, output);
}
}
protected List<Tag> removeGenericTagList(CompoundTag tag, String name) {
String backupName = getNbtTagName() + "|" + name;
CompoundTag data = tag.getCompoundTag(backupName);
if (data == null) {
return null;
}
tag.remove(backupName);
return new ArrayList<>(data.values());
}
protected void restoreDisplayTag(Item item) {
if (item.tag() == null) return;
@ -90,19 +115,30 @@ public abstract class BackwardsItemRewriterBase<C extends ClientboundPacketType,
}
protected void restoreStringTag(CompoundTag tag, String tagName) {
Tag original = tag.remove(nbtTagName("o" + tagName));
Tag original = tag.remove(nbtTagName(tagName));
if (original instanceof StringTag) {
tag.putString(tagName, ((StringTag) original).getValue());
}
}
protected void restoreListTag(CompoundTag tag, String tagName) {
Tag original = tag.remove(nbtTagName("o" + tagName));
Tag original = tag.remove(nbtTagName(tagName));
if (original instanceof ListTag) {
tag.put(tagName, ((ListTag<?>) original).copy());
}
}
public <T extends Tag> @Nullable ListTag<T> removeListTag(CompoundTag tag, String tagName, Class<T> tagType) {
String backupName = nbtTagName(tagName);
ListTag<T> data = tag.getListTag(backupName, tagType);
if (data == null) {
return null;
}
tag.remove(backupName);
return data;
}
@Override
public String nbtTagName() {
return "VB|" + protocol.getClass().getSimpleName();

Datei anzeigen

@ -36,6 +36,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType,
T extends BackwardsProtocol<C, ?, ?, S>> extends BackwardsItemRewriter<C, S, T> {
protected final StructuredEnchantmentRewriter enchantmentRewriter = new StructuredEnchantmentRewriter(this);
public BackwardsStructuredItemRewriter(final T protocol, final Type<Item> itemType, final Type<Item[]> itemArrayType) {
super(protocol, itemType, itemArrayType);
}
@ -53,6 +55,10 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
final StructuredDataContainer data = item.structuredData();
data.setIdLookup(protocol, true);
if (protocol.getMappingData().getEnchantmentMappings() != null) {
enchantmentRewriter.handleToClient(item);
}
if (protocol.getTranslatableRewriter() != null) {
// Handle name and lore components
final StructuredData<Tag> customNameData = data.getNonEmpty(StructuredDataKey.CUSTOM_NAME);
@ -114,6 +120,10 @@ public class BackwardsStructuredItemRewriter<C extends ClientboundPacketType, S
final StructuredDataContainer data = item.structuredData();
data.setIdLookup(protocol, false);
if (protocol.getMappingData().getEnchantmentMappings() != null) {
enchantmentRewriter.handleToServer(item);
}
final CompoundTag tag = customTag(item);
if (tag != null) {
final Tag originalId = tag.remove(nbtTagName("id"));

Datei anzeigen

@ -23,6 +23,7 @@ import com.viaversion.viaversion.libs.opennbt.tag.builtin.ListTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.NumberTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.StringTag;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.Key;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@ -34,8 +35,8 @@ import java.util.Map;
*/
public class EnchantmentRewriter {
private final Map<String, String> enchantmentMappings = new HashMap<>();
private final BackwardsItemRewriter<?, ?, ?> itemRewriter;
protected final Map<String, String> enchantmentMappings = new HashMap<>();
protected final BackwardsItemRewriter<?, ?, ?> itemRewriter;
private final boolean jsonFormat;
public EnchantmentRewriter(BackwardsItemRewriter<?, ?, ?> itemRewriter, boolean jsonFormat) {
@ -48,7 +49,7 @@ public class EnchantmentRewriter {
}
public void registerEnchantment(String key, String replacementLore) {
enchantmentMappings.put(key, replacementLore);
enchantmentMappings.put(Key.stripMinecraftNamespace(key), replacementLore);
}
public void handleToClient(Item item) {
@ -89,7 +90,7 @@ public class EnchantmentRewriter {
continue;
}
String enchantmentId = idTag.getValue();
String enchantmentId = Key.stripMinecraftNamespace(idTag.getValue());
String remappedName = enchantmentMappings.get(enchantmentId);
if (remappedName != null) {
if (!changed) {

Datei anzeigen

@ -0,0 +1,189 @@
/*
* This file is part of ViaBackwards - https://github.com/ViaVersion/ViaBackwards
* Copyright (C) 2016-2024 ViaVersion and contributors
*
* 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.viaversion.viabackwards.api.rewriters;
import com.viaversion.viabackwards.api.data.BackwardsMappings;
import com.viaversion.viaversion.api.data.Mappings;
import com.viaversion.viaversion.api.minecraft.data.StructuredData;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer;
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.item.data.Enchantments;
import com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;
import com.viaversion.viaversion.libs.fastutil.ints.IntIntPair;
import com.viaversion.viaversion.libs.fastutil.objects.ObjectIterator;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.ByteTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.ListTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.NumberTag;
import com.viaversion.viaversion.libs.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.util.ComponentUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StructuredEnchantmentRewriter {
protected final BackwardsItemRewriter<?, ?, ?> itemRewriter;
private boolean rewriteIds = true;
public StructuredEnchantmentRewriter(final BackwardsItemRewriter<?, ?, ?> itemRewriter) {
this.itemRewriter = itemRewriter;
}
public void handleToClient(final Item item) {
final StructuredDataContainer data = item.structuredData();
rewriteEnchantmentsToClient(data, StructuredDataKey.ENCHANTMENTS, false);
rewriteEnchantmentsToClient(data, StructuredDataKey.STORED_ENCHANTMENTS, true);
}
public void handleToServer(final Item item) {
final StructuredDataContainer data = item.structuredData();
final StructuredData<CompoundTag> customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA);
if (customData == null) {
return;
}
final CompoundTag tag = customData.value();
if (tag.contains(itemRewriter.getNbtTagName() + "|enchantments")) {
rewriteEnchantmentsToServer(data, tag, StructuredDataKey.ENCHANTMENTS, false);
}
if (tag.contains(itemRewriter.getNbtTagName() + "|stored_enchantments")) {
rewriteEnchantmentsToServer(data, tag, StructuredDataKey.STORED_ENCHANTMENTS, true);
}
}
public void rewriteEnchantmentsToClient(final StructuredDataContainer data, final StructuredDataKey<Enchantments> key, final boolean storedEnchant) {
final StructuredData<Enchantments> enchantmentsData = data.getNonEmpty(key);
if (enchantmentsData == null) {
return;
}
final CompoundTag tag = data.computeIfAbsent(StructuredDataKey.CUSTOM_DATA, $ -> new CompoundTag()).value();
final Enchantments enchantments = enchantmentsData.value();
final List<Tag> loreToAdd = new ArrayList<>();
boolean changed = false;
final ObjectIterator<Int2IntMap.Entry> iterator = enchantments.enchantments().int2IntEntrySet().iterator();
final List<IntIntPair> updatedIds = new ArrayList<>();
while (iterator.hasNext()) {
final Int2IntMap.Entry entry = iterator.next();
final BackwardsMappings mappingData = itemRewriter.protocol().getMappingData();
final Mappings mappings = mappingData.getEnchantmentMappings();
final int mappedId = mappings.getNewId(entry.getIntKey());
if (mappedId != -1) {
if (rewriteIds) {
// Update the map after to iteration to preserve the current ids before possibly saving the original, avoid CME
updatedIds.add(IntIntPair.of(entry.getIntKey(), mappedId));
}
continue;
}
final String remappedName = mappingData.mappedEnchantmentName(entry.getIntKey());
if (remappedName != null) {
if (!changed) {
// Backup original before doing modifications
itemRewriter.saveListTag(tag, asTag(enchantments), key.identifier());
changed = true;
}
final int level = entry.getIntValue();
loreToAdd.add(ComponentUtil.jsonStringToTag(ComponentUtil.legacyToJsonString("§7" + remappedName + " " + EnchantmentRewriter.getRomanNumber(level), true)));
iterator.remove();
}
}
for (final IntIntPair pair : updatedIds) {
enchantments.add(pair.firstInt(), pair.secondInt());
}
if (loreToAdd.isEmpty()) {
// No removed enchantments
return;
}
// Add glint override if there are no enchantments left
if (!storedEnchant && enchantments.size() == 0) {
final StructuredData<Boolean> glintOverride = data.getNonEmpty(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE);
if (glintOverride != null) {
tag.putBoolean(itemRewriter.getNbtTagName() + "|glint", glintOverride.value());
} else {
tag.putBoolean(itemRewriter.getNbtTagName() + "|noglint", true);
}
data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, true);
}
// Save original lore
final StructuredData<Tag[]> loreData = data.getNonEmpty(StructuredDataKey.LORE);
if (loreData != null) {
final List<Tag> loreList = Arrays.asList(loreData.value());
itemRewriter.saveGenericTagList(tag, loreList, "lore");
loreToAdd.addAll(loreList);
} else {
tag.putBoolean(itemRewriter.getNbtTagName() + "|nolore", true);
}
if (enchantments.showInTooltip()) {
tag.putBoolean(itemRewriter.getNbtTagName() + "|show_" + key.identifier(), true);
}
data.set(StructuredDataKey.LORE, loreToAdd.toArray(new Tag[0]));
}
private ListTag<CompoundTag> asTag(final Enchantments enchantments) {
final ListTag<CompoundTag> listTag = new ListTag<>(CompoundTag.class);
for (final Int2IntMap.Entry entry : enchantments.enchantments().int2IntEntrySet()) {
final CompoundTag enchantment = new CompoundTag();
enchantment.putInt("id", entry.getIntKey());
enchantment.putInt("lvl", entry.getIntValue());
listTag.add(enchantment);
}
return listTag;
}
public void rewriteEnchantmentsToServer(final StructuredDataContainer data, final CompoundTag tag, final StructuredDataKey<Enchantments> key, final boolean storedEnchant) {
final ListTag<CompoundTag> enchantmentsTag = itemRewriter.removeListTag(tag, key.identifier(), CompoundTag.class);
if (enchantmentsTag == null) {
return;
}
final Tag glintTag = tag.remove(itemRewriter.getNbtTagName() + "|glint");
if (glintTag instanceof ByteTag) {
data.set(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE, ((NumberTag) glintTag).asBoolean());
} else if (tag.remove(itemRewriter.getNbtTagName() + "|noglint") != null) {
data.remove(StructuredDataKey.ENCHANTMENT_GLINT_OVERRIDE);
}
final List<Tag> lore = itemRewriter.removeGenericTagList(tag, "lore");
if (lore != null) {
data.set(StructuredDataKey.LORE, lore.toArray(new Tag[0]));
} else if (tag.remove(itemRewriter.getNbtTagName() + "|nolore") != null) {
data.remove(StructuredDataKey.LORE);
}
final Enchantments enchantments = new Enchantments(tag.remove(itemRewriter.getNbtTagName() + "|show_" + key.identifier()) != null);
for (final CompoundTag enchantment : enchantmentsTag) {
enchantments.add(enchantment.getInt("id"), enchantment.getInt("lvl"));
}
data.set(key, enchantments);
}
public void setRewriteIds(final boolean rewriteIds) {
this.rewriteIds = rewriteIds;
}
}

Datei anzeigen

@ -18,6 +18,8 @@
package com.viaversion.viabackwards.protocol.protocol1_20_3to1_20_5.rewriter;
import com.viaversion.viabackwards.api.rewriters.BackwardsStructuredItemRewriter;
import com.viaversion.viabackwards.api.rewriters.EnchantmentRewriter;
import com.viaversion.viabackwards.api.rewriters.StructuredEnchantmentRewriter;
import com.viaversion.viabackwards.protocol.protocol1_20_3to1_20_5.Protocol1_20_3To1_20_5;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.Particle;
@ -42,6 +44,7 @@ public final class BlockItemPacketRewriter1_20_5 extends BackwardsStructuredItem
public BlockItemPacketRewriter1_20_5(final Protocol1_20_3To1_20_5 protocol) {
super(protocol, Types1_20_5.ITEM, Types1_20_5.ITEM_ARRAY, Type.ITEM1_20_2, Type.ITEM1_20_2_ARRAY);
enchantmentRewriter.setRewriteIds(false); // Let VV handle it
}
@Override

Datei anzeigen

@ -1,28 +1,189 @@
{
"1.20.5": {
"advancements.adventure.blowback.description": "Kill a Breeze with a deflected Breeze-shot Wind Charge",
"advancements.adventure.blowback.title": "Blowback",
"advancements.adventure.crafters_crafting_crafters.description": "Be near a Crafter when it crafts a Crafter",
"advancements.adventure.crafters_crafting_crafters.title": "Crafters Crafting Crafters",
"advancements.adventure.lighten_up.description": "Scrape a Copper Bulb with an Axe to make it brighter",
"advancements.adventure.lighten_up.title": "Lighten Up",
"advancements.adventure.minecraft_trials_edition.description": "Step foot in a Trial Chamber",
"advancements.adventure.minecraft_trials_edition.title": "Minecraft: Trial(s) Edition",
"advancements.adventure.overoverkill.description": "Deal 50 hearts of damage in a single hit using the Mace",
"advancements.adventure.overoverkill.title": "Over-Overkill",
"advancements.adventure.under_lock_and_key.description": "Unlock a Vault with a Trial Key",
"advancements.adventure.under_lock_and_key.title": "Under Lock and Key",
"advancements.adventure.who_needs_rockets.description": "Use a Wind Charge to launch yourself upward 8 blocks",
"advancements.adventure.who_needs_rockets.title": "Who Needs Rockets?",
"argument.resource_or_id.failed_to_parse": "Failed to parse structure: %s",
"argument.resource_or_id.invalid": "Invalid id or tag",
"arguments.item.component.expected": "Expected item component",
"arguments.item.component.malformed": "Malformed '%s' component: '%s'",
"arguments.item.component.repeated": "Item component '%s' was repeated, but only one value can be specified",
"arguments.item.component.unknown": "Unknown item component '%s'",
"arguments.item.predicate.malformed": "Malformed '%s' predicate: '%s'",
"arguments.item.predicate.unknown": "Unknown item predicate '%s''",
"attribute.name.generic.block_interaction_range": "Block Interaction Range",
"attribute.name.generic.entity_interaction_range": "Entity Interaction Range",
"attribute.name.generic.fall_damage_multiplier": "Fall Damage Multiplier",
"attribute.name.generic.gravity": "Gravity",
"attribute.name.generic.jump_strength": "Jump Strength",
"attribute.name.generic.safe_fall_distance": "Safe Fall Distance",
"attribute.name.generic.scale": "Scale",
"attribute.name.generic.step_height": "Step Height",
"attribute.name.player.block_break_speed": "Block Break Speed",
"attribute.name.player.block_interaction_range": "Block Interaction Range",
"attribute.name.player.entity_interaction_range": "Entity Interaction Range",
"block.minecraft.heavy_core": "Heavy Core",
"block.minecraft.vault": "Vault",
"chat.disabled.invalid_command_signature": "Command had unexpected or missing command argument signatures.",
"chat.disabled.invalid_signature": "Chat had an invalid signature. Please try reconnecting.",
"chat.disabled.out_of_order_chat": "Chat received out-of-order. Did your system time change?",
"chunk.toast.checkLog": "See log for more details",
"chunk.toast.loadFailure": "Failed to load chunk at %s",
"chunk.toast.lowDiskSpace": "Low disk space!",
"chunk.toast.lowDiskSpace.description": "Might not be able to save the world.",
"chunk.toast.saveFailure": "Failed to save chunk at %s",
"commands.datapack.disable.failed.feature": "Pack '%s' cannot be disabled, since it is part of an enabled flag!",
"commands.setworldspawn.failure.not_overworld": "Can only set the world spawn for overworld",
"commands.transfer.error.no_players": "Must specify at least one player to transfer",
"commands.transfer.success.multiple": "Transferring %s players to %s:%s",
"commands.transfer.success.single": "Transferring %s to %s:%s",
"connect.failed.transfer": "Connection failed while transferring to the server",
"connect.transferring": "Transferring to new server...",
"disconnect.transfer": "Transferred to another server",
"effect.minecraft.infested": "Infested",
"effect.minecraft.oozing": "Oozing",
"effect.minecraft.raid_omen": "Raid Omen",
"effect.minecraft.trial_omen": "Trial Omen",
"effect.minecraft.weaving": "Weaving",
"effect.minecraft.wind_charged": "Wind Charged",
"enchantment.minecraft.breach": "Breach",
"enchantment.minecraft.density": "Density",
"enchantment.minecraft.sweeping_edge": "Sweeping Edge",
"enchantment.minecraft.wind_burst": "Wind Burst",
"entity.minecraft.armadillo": "Armadillo",
"entity.minecraft.bogged": "Bogged",
"entity.minecraft.breeze_wind_charge": "Wind Charge",
"entity.minecraft.ominous_item_spawner": "Ominous Item Spawner",
"filled_map.trial_chambers": "Trial Chambers Map",
"gamerule.spawnChunkRadius": "Spawn chunk radius",
"gamerule.spawnChunkRadius.description": "Amount of chunks that stay loaded around the overworld spawn position.",
"gamerule.spawnRadius.description": "Controls the size of the area around the spawn point that players can spawn in.",
"item.canUse.unknown": "Unknown",
"item.components": "%s component(s)",
"item.minecraft.armadillo_scute": "Armadillo Scute",
"item.minecraft.armadillo_spawn_egg": "Armadillo Spawn Egg",
"item.minecraft.bogged_spawn_egg": "Bogged Spawn Egg",
"item.minecraft.bolt_armor_trim_smithing_template": "Smithing Template",
"item.minecraft.breeze_rod": "Breeze Rod",
"item.minecraft.flow_armor_trim_smithing_template": "Smithing Template",
"item.minecraft.flow_banner_pattern": "Banner Pattern",
"item.minecraft.flow_banner_pattern.desc": "Flow",
"item.minecraft.flow_pottery_sherd": "Flow Pottery Sherd",
"item.minecraft.guster_banner_pattern": "Banner Pattern",
"item.minecraft.guster_banner_pattern.desc": "Guster",
"item.minecraft.guster_pottery_sherd": "Guster Pottery Sherd",
"item.minecraft.lingering_potion.effect.infested": "Lingering Potion of Infestation",
"item.minecraft.lingering_potion.effect.oozing": "Lingering Potion of Oozing",
"item.minecraft.lingering_potion.effect.weaving": "Lingering Potion of Weaving",
"item.minecraft.lingering_potion.effect.wind_charged": "Lingering Potion of Wind Charging",
"item.minecraft.mace": "Mace",
"item.minecraft.ominous_bottle": "Ominous Bottle",
"item.minecraft.ominous_trial_key": "Ominous Trial Key",
"item.minecraft.potion.effect.infested": "Potion of Infestation",
"item.minecraft.potion.effect.oozing": "Potion of Oozing",
"item.minecraft.potion.effect.weaving": "Potion of Weaving",
"item.minecraft.potion.effect.wind_charged": "Potion of Wind Charging",
"item.minecraft.scrape_pottery_sherd": "Scrape Pottery Sherd",
"item.minecraft.splash_potion.effect.infested": "Splash Potion of Infestation",
"item.minecraft.splash_potion.effect.oozing": "Splash Potion of Oozing",
"item.minecraft.splash_potion.effect.weaving": "Splash Potion of Weaving",
"item.minecraft.splash_potion.effect.wind_charged": "Splash Potion of Wind Charging",
"item.minecraft.tipped_arrow.effect.infested": "Arrow of Infestation",
"item.minecraft.tipped_arrow.effect.oozing": "Arrow of Oozing",
"item.minecraft.tipped_arrow.effect.weaving": "Arrow of Weaving",
"item.minecraft.tipped_arrow.effect.wind_charged": "Arrow of Wind Charging",
"item.minecraft.turtle_scute": "Turtle Scute",
"item.minecraft.wind_charge": "Wind Charge",
"item.minecraft.wolf_armor": "Wolf Armor",
"item.modifiers.body": "When equipped:",
"mco.backup.narration": "Backup from %s",
"mco.client.outdated.stable.version": "Your client version (%s) is not compatible with Realms.\n\nPlease use the most recent version of Minecraft.",
"mco.client.unsupported.snapshot.version": "Your client version (%s) is not compatible with Realms.\n\nRealms is not available for this snapshot version.",
"mco.invited.player.narration": "Invited player %s",
"mco.upload.entry.commands": "%1$s, %2$s",
"multiplayer.disconnect.transfers_disabled": "Server does not accept transfers",
"optimizeWorld.stage.finished.chunks": "Finishing up upgrading chunks...",
"optimizeWorld.stage.finished.entities": "Finishing up upgrading entities...",
"optimizeWorld.stage.finished.poi": "Finishing up upgrading points of interest...",
"optimizeWorld.stage.upgrading.chunks": "Upgrading all chunks...",
"optimizeWorld.stage.upgrading.entities": "Upgrading all entities...",
"optimizeWorld.stage.upgrading.poi": "Upgrading all points of interest...",
"options.accessibility.menu_background_blurriness": "Menu Background Blur",
"options.accessibility.menu_background_blurriness.tooltip": "Changes the blurriness of menu backgrounds",
"options.font": "Font Settings...",
"options.font.title": "Font Settings",
"options.japaneseGlyphVariants": "Japanese Glyph Variants",
"options.japaneseGlyphVariants.tooltip": "Uses Japanese variants of CJK characters in the default font",
"options.telemetry.disabled": "Telemetry is disabled.",
"selectWorld.allowCommands.new": "Allow Commands",
"selectWorld.commands": "Commands",
"selectWorld.warning.lowDiskSpace.description": "There is not much space left on your device.\nRunning out of disk space while in game can lead to your world being damaged.",
"selectWorld.warning.lowDiskSpace.title": "Warning! Low disk space!",
"slot.only_single_allowed": "Only single slots allowed, got '%s'",
"subtitles.block.candle.extinguish": "Candle extinguishes",
"subtitles.block.trial_spawner.about_to_spawn_item": "Ominous item prepares",
"subtitles.block.trial_spawner.ambient_charged": "Ominous Trial Spawner crackles",
"subtitles.block.trial_spawner.charge_activate": "Omen engulfs Trial Spawner",
"subtitles.block.trial_spawner.spawn_item": "Ominous item drops",
"subtitles.block.trial_spawner.spawn_item_begin": "Ominous item appears",
"subtitles.block.vault.activate": "Vault ignites",
"subtitles.block.vault.ambient": "Vault crackles",
"subtitles.block.vault.close_shutter": "Vault closes",
"subtitles.block.vault.deactivate": "Vault extinguishes",
"subtitles.block.vault.eject_item": "Vault ejects item",
"subtitles.block.vault.insert_item": "Vault unlocks",
"subtitles.block.vault.insert_item_fail": "Vault fails unlocking",
"subtitles.block.vault.open_shutter": "Vault opens",
"subtitles.block.wet_sponge.dries": "Sponge dries",
"subtitles.entity.armadillo.ambient": "Armadillo grunts",
"subtitles.entity.armadillo.brush": "Armadillo brushes",
"subtitles.entity.armadillo.brush": "Scute is brushed off",
"subtitles.entity.armadillo.death": "Armadillo dies",
"subtitles.entity.armadillo.eat": "Armadillo eats",
"subtitles.entity.armadillo.hurt": "Armadillo hurts",
"subtitles.entity.armadillo.hurt_reduced": "Armadillo shields itself",
"subtitles.entity.armadillo.land": "Armadillo lands",
"subtitles.entity.armadillo.peek": "Armadillo peeks",
"subtitles.entity.armadillo.roll": "Armadillo rolls up",
"subtitles.entity.armadillo.scute_drop": "Scute drops",
"subtitles.entity.armadillo.unroll": "Armadillo unrolls",
"subtitles.entity.armadillo.scute_drop": "Armadillo sheds scute",
"subtitles.entity.armadillo.unroll_finish": "Armadillo unrolls",
"subtitles.entity.armadillo.unroll_start": "Armadillo peeks",
"subtitles.entity.bogged.ambient": "Bogged rattles",
"subtitles.entity.bogged.death": "Bogged dies",
"subtitles.entity.bogged.hurt": "Bogged hurts",
"subtitles.entity.breeze.charge": "Breeze charges",
"subtitles.entity.breeze.deflect": "Breeze deflects",
"subtitles.entity.breeze.whirl": "Breeze whirls",
"subtitles.item.armor.equip_wolf": "Wolf armor straps",
"subtitles.item.armor.unequip_wolf": "Wolf armor slips off"
"subtitles.entity.breeze.wind_burst": "Wind Charge bursts",
"subtitles.entity.donkey.jump": "Donkey jumps",
"subtitles.entity.mule.jump": "Mule jumps",
"subtitles.entity.parrot.imitate.bogged": "Parrot rattles",
"subtitles.entity.wind_charge.throw": "Wind Charge flies",
"subtitles.entity.wind_charge.wind_burst": "Wind Charge bursts",
"subtitles.event.mob_effect.bad_omen": "Omen takes hold",
"subtitles.event.mob_effect.raid_omen": "Raid looms nearby",
"subtitles.event.mob_effect.trial_omen": "Ominous trial looms nearby",
"subtitles.item.armor.equip_wolf": "Wolf Armor is fastened",
"subtitles.item.armor.unequip_wolf": "Wolf Armor snips away",
"subtitles.item.mace.smash_air": "Mace smashes",
"subtitles.item.mace.smash_ground": "Mace smashes",
"subtitles.item.ominous_bottle.dispose": "Bottle breaks",
"subtitles.item.wolf_armor.break": "Wolf Armor breaks",
"subtitles.item.wolf_armor.crack": "Wolf Armor cracks",
"subtitles.item.wolf_armor.damage": "Wolf Armor takes damage",
"subtitles.item.wolf_armor.repair": "Wolf Armor is repaired",
"trim_pattern.minecraft.bolt": "Bolt Armor Trim",
"trim_pattern.minecraft.flow": "Flow Armor Trim"
},
"1.20.3": {
"accessibility.onboarding.accessibility.button": "Accessibility Settings...",