This commit is contained in:
KennyTV 2020-04-22 23:25:57 +02:00
parent 4d701b1c4e
commit 2b0745dc2c
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
9 changed files with 207 additions and 51 deletions

View File

@ -29,17 +29,24 @@ import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* Entity rewriter base class.
*
* @see EntityRewriter
* @see LegacyEntityRewriter
*/
public abstract class EntityRewriterBase<T extends BackwardsProtocol> extends Rewriter<T> {
private final Map<EntityType, EntityData> entityTypes = new HashMap<>();
private final List<MetaHandlerSettings> metaHandlers = new ArrayList<>();
private final MetaType displayNameMetaType;
private final int displayNameIndex;
private Map<Integer, Integer> typeMapping;
protected EntityRewriterBase(T protocol) {
EntityRewriterBase(T protocol) {
this(protocol, MetaType1_9.String, 2);
}
protected EntityRewriterBase(T protocol, MetaType displayNameMetaType, int displayNameIndex) {
EntityRewriterBase(T protocol, MetaType displayNameMetaType, int displayNameIndex) {
super(protocol);
this.displayNameMetaType = displayNameMetaType;
this.displayNameIndex = displayNameIndex;
@ -61,15 +68,63 @@ public abstract class EntityRewriterBase<T extends BackwardsProtocol> extends Re
return entityTypes.get(type);
}
/**
* @param oldType old type of the higher version
* @param replacement new type of the higher version
* @return created entity data
* @see #mapEntityDirect(EntityType, EntityType) for id only rewriting
*/
protected EntityData mapEntity(EntityType oldType, EntityType replacement) {
Preconditions.checkArgument(oldType.getClass() == replacement.getClass());
// Already rewrite the id here
EntityData data = new EntityData(oldType.getId(), getOldEntityId(replacement.getId()));
int mappedReplacementId = getOldEntityId(replacement.getId());
EntityData data = new EntityData(oldType.getId(), mappedReplacementId);
mapEntityDirect(oldType.getId(), mappedReplacementId);
entityTypes.put(oldType, data);
return data;
}
/**
* Maps entity ids based on the enum constant's names.
*
* @param oldTypes entity types of the higher version
* @param newTypeClass entity types enum class of the lower version
* @param <T> new type class
*/
public <T extends Enum<T> & EntityType> void mapTypes(EntityType[] oldTypes, Class<T> newTypeClass) {
if (typeMapping == null) typeMapping = new HashMap<>(oldTypes.length);
for (EntityType oldType : oldTypes) {
try {
T newType = Enum.valueOf(newTypeClass, oldType.name());
typeMapping.put(oldType.getId(), newType.getId());
} catch (IllegalArgumentException e) {
// Missing ones should be mapped BEFORE using this method
if (!typeMapping.containsKey(oldType.getId())) {
ViaBackwards.getPlatform().getLogger().warning("Could not find new entity type for " + oldType + "! " +
"Old type: " + oldType.getClass().getSimpleName() + " New type: " + newTypeClass.getSimpleName());
}
}
}
}
/**
* Directly maps the entity without any other rewriting.
*
* @param oldType type of the higher version
* @param newType type of the lower version
* @see #mapEntity(EntityType, EntityType) for mapping with data
*/
public void mapEntityDirect(EntityType oldType, EntityType newType) {
Preconditions.checkArgument(oldType.getClass() != newType.getClass());
mapEntityDirect(oldType.getId(), newType.getId());
}
private void mapEntityDirect(int oldType, int newType) {
if (typeMapping == null) typeMapping = new HashMap<>();
typeMapping.put(oldType, newType);
}
public MetaHandlerSettings registerMetaHandler() {
MetaHandlerSettings settings = new MetaHandlerSettings();
metaHandlers.add(settings);
@ -241,7 +296,7 @@ public abstract class EntityRewriterBase<T extends BackwardsProtocol> extends Re
protected abstract EntityType getTypeFromId(int typeId);
protected int getOldEntityId(int newId) {
return newId;
public int getOldEntityId(int newId) {
return typeMapping != null ? typeMapping.getOrDefault(newId, newId) : newId;
}
}

View File

@ -430,7 +430,7 @@ public class EntityPackets1_13 extends LegacyEntityRewriter<Protocol1_12_2To1_13
}
@Override
protected int getOldEntityId(final int newId) {
public int getOldEntityId(final int newId) {
return EntityTypeMapping.getOldId(newId).orElse(newId);
}
}

View File

@ -564,7 +564,7 @@ public class EntityPackets1_14 extends LegacyEntityRewriter<Protocol1_13_2To1_14
}
@Override
protected int getOldEntityId(final int newId) {
public int getOldEntityId(final int newId) {
Integer oldId = EntityTypeMapping.getOldId(newId);
return oldId != null ? oldId : newId;
}

View File

@ -253,7 +253,7 @@ public class EntityPackets1_15 extends EntityRewriter<Protocol1_14_4To1_15> {
}
@Override
protected int getOldEntityId(final int newId) {
public int getOldEntityId(final int newId) {
return EntityTypeMapping.getOldEntityId(newId);
}
}

View File

@ -8,7 +8,6 @@ import nl.matsv.viabackwards.api.rewriters.TranslatableRewriter;
import nl.matsv.viabackwards.protocol.protocol1_15_2to1_16.data.BackwardsMappings;
import nl.matsv.viabackwards.protocol.protocol1_15_2to1_16.packets.BlockItemPackets1_16;
import nl.matsv.viabackwards.protocol.protocol1_15_2to1_16.packets.EntityPackets1_16;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.rewriters.TagRewriter;
@ -17,6 +16,10 @@ import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.Protocol1_16To1_15_2;
import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import us.myles.ViaVersion.util.GsonUtil;
import us.myles.viaversion.libs.gson.JsonElement;
import us.myles.viaversion.libs.gson.JsonObject;
import us.myles.viaversion.libs.gson.JsonPrimitive;
import java.util.UUID;
@ -29,9 +32,35 @@ public class Protocol1_15_2To1_16 extends BackwardsProtocol {
executeAsyncAfterLoaded(Protocol1_16To1_15_2.class, BackwardsMappings::init);
(blockItemPackets = new BlockItemPackets1_16(this)).register();
new EntityPackets1_16(this).register();
EntityPackets1_16 entityPackets = new EntityPackets1_16(this);
entityPackets.register();
TranslatableRewriter translatableRewriter = new TranslatableRewriter(this);
TranslatableRewriter translatableRewriter = new TranslatableRewriter(this) {
@Override
public String processTranslate(String value) {
JsonObject object = GsonUtil.getGson().fromJson(value, JsonObject.class);
JsonElement with = object.get("with");
if (with == null) {
return super.processTranslate(value);
}
for (JsonElement element : with.getAsJsonArray()) {
if (!element.isJsonObject()) continue;
JsonElement hoverEventElement = element.getAsJsonObject().get("hoverEvent");
if (hoverEventElement == null) continue;
JsonObject hoverEvent = hoverEventElement.getAsJsonObject();
JsonElement contentsElement = hoverEvent.remove("contents");
if (contentsElement != null) {
JsonObject values = new JsonObject();
values.add("text", new JsonPrimitive(contentsElement.toString()));
hoverEvent.add("value", values);
}
}
return super.processTranslate(object.toString());
}
};
translatableRewriter.registerBossBar(0x0D, 0x0D);
translatableRewriter.registerChatMessage(0x0F, 0x0F);
translatableRewriter.registerCombatEvent(0x33, 0x33);
@ -100,7 +129,7 @@ public class Protocol1_15_2To1_16 extends BackwardsProtocol {
new TagRewriter(this, id -> BackwardsMappings.blockMappings.getNewId(id), id -> {
Integer oldId = MappingData.oldToNewItems.inverse().get(id);
return oldId != null ? oldId : -1;
}, Protocol1_15_2To1_16::getNewEntityId).register(0x5C, 0x5C);
}, entityPackets::getOldEntityId).register(0x5C, 0x5C);
registerOutgoing(State.PLAY, 0x43, 0x4E);
registerOutgoing(State.PLAY, 0x44, 0x43);
@ -148,17 +177,6 @@ public class Protocol1_15_2To1_16 extends BackwardsProtocol {
registerIncoming(State.PLAY, 0x2D, 0x2C);
}
public static int getNewEntityId(final int oldId) {
if (oldId == 95) {
return 57;
} else if (oldId > 56 && oldId < 95) {
return oldId + 1;
} else if (oldId > 103) {
return oldId - 1;
}
return oldId;
}
public static int getNewBlockStateId(int id) {
int newId = BackwardsMappings.blockStateMappings.getNewId(id);
if (newId == -1) {
@ -168,7 +186,6 @@ public class Protocol1_15_2To1_16 extends BackwardsProtocol {
return newId;
}
public static int getNewBlockId(int id) {
int newId = BackwardsMappings.blockMappings.getNewId(id);
if (newId == -1) {

View File

@ -15,9 +15,12 @@ import us.myles.ViaVersion.api.type.types.UUIDIntArrayType;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_15to1_14_4.types.Chunk1_15Type;
import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData;
import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.types.Chunk1_16Type;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.storage.ClientWorld;
import us.myles.ViaVersion.util.CompactArrayUtil;
import us.myles.viaversion.libs.opennbt.tag.builtin.CompoundTag;
import us.myles.viaversion.libs.opennbt.tag.builtin.IntArrayTag;
import us.myles.viaversion.libs.opennbt.tag.builtin.LongArrayTag;
import us.myles.viaversion.libs.opennbt.tag.builtin.StringTag;
import us.myles.viaversion.libs.opennbt.tag.builtin.Tag;
@ -118,7 +121,9 @@ public class BlockItemPackets1_16 extends nl.matsv.viabackwards.api.rewriters.It
public void registerMap() {
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
Chunk chunk = wrapper.passthrough(new Chunk1_15Type(clientWorld));
Chunk chunk = wrapper.read(new Chunk1_16Type(clientWorld));
wrapper.write(new Chunk1_15Type(clientWorld), chunk);
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
@ -128,6 +133,14 @@ public class BlockItemPackets1_16 extends nl.matsv.viabackwards.api.rewriters.It
}
}
CompoundTag heightMaps = chunk.getHeightMap();
for (Tag heightMapTag : heightMaps) {
LongArrayTag heightMap = (LongArrayTag) heightMapTag;
int[] heightMapData = new int[256];
CompactArrayUtil.iterateCompactArrayWithPadding(9, heightMapData.length, heightMap.getValue(), (i, v) -> heightMapData[i] = v);
heightMap.setValue(CompactArrayUtil.createCompactArray(9, heightMapData.length, i -> heightMapData[i]));
}
if (chunk.isBiomeData()) {
for (int i = 0; i < 1024; i++) {
int biome = chunk.getBiomeData()[i];

View File

@ -96,15 +96,19 @@ public class EntityPackets1_16 extends EntityRewriter<Protocol1_15_2To1_16> {
return meta;
});
mapEntityDirect(Entity1_16Types.EntityType.ZOMBIFIED_PIGLIN, Entity1_15Types.EntityType.ZOMBIE_PIGMAN);
mapEntity(Entity1_16Types.EntityType.HOGLIN, Entity1_16Types.EntityType.COW).jsonName("Hoglin");
mapEntity(Entity1_16Types.EntityType.ZOGLIN, Entity1_16Types.EntityType.COW).jsonName("Zoglin");
mapEntity(Entity1_16Types.EntityType.PIGLIN, Entity1_16Types.EntityType.ZOMBIFIED_PIGLIN).jsonName("Piglin");
mapEntity(Entity1_16Types.EntityType.STRIDER, Entity1_16Types.EntityType.MAGMA_CUBE).jsonName("Strider");
mapTypes(Entity1_16Types.EntityType.values(), Entity1_15Types.EntityType.class);
registerMetaHandler().filter(Entity1_16Types.EntityType.ZOGLIN, 16).removed();
registerMetaHandler().filter(Entity1_16Types.EntityType.HOGLIN, 15).removed();
registerMetaHandler().filter(Entity1_16Types.EntityType.PIGLIN, 16).removed(); // charging crossbow
registerMetaHandler().filter(Entity1_16Types.EntityType.PIGLIN, 16).removed();
registerMetaHandler().filter(Entity1_16Types.EntityType.PIGLIN, 17).removed();
registerMetaHandler().filter(Entity1_16Types.EntityType.STRIDER, 15).handle(meta -> {
@ -132,16 +136,4 @@ public class EntityPackets1_16 extends EntityRewriter<Protocol1_15_2To1_16> {
protected EntityType getTypeFromId(int typeId) {
return Entity1_16Types.getTypeFromId(typeId);
}
@Override
protected int getOldEntityId(int newId) {
if (newId == Entity1_16Types.EntityType.HOGLIN.getId() || newId == Entity1_16Types.EntityType.ZOGLIN.getId()) {
return Entity1_15Types.EntityType.COW.getId();
} else if (newId == Entity1_16Types.EntityType.PIGLIN.getId()) {
return Entity1_15Types.EntityType.ZOMBIE_PIGMAN.getId();
} else if (newId == Entity1_16Types.EntityType.STRIDER.getId()) {
return Entity1_15Types.EntityType.MAGMA_CUBE.getId();
}
return Protocol1_15_2To1_16.getNewEntityId(newId);
}
}

View File

@ -5,8 +5,8 @@
"minecraft:basalt[axis=x]": "minecraft:gray_glazed_terracotta[facing=north]",
"minecraft:basalt[axis=y]": "minecraft:gray_glazed_terracotta[facing=north]",
"minecraft:basalt[axis=z]": "minecraft:gray_glazed_terracotta[facing=north]",
"minecraft:soul_fire_torch": "minecraft:torch",
"minecraft:soul_fire_wall_torch": "minecraft:wall_torch[",
"minecraft:soul_torch": "minecraft:torch",
"minecraft:soul_wall_torch": "minecraft:wall_torch[",
"minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=none]": "minecraft:cobblestone_wall[east=false,north=false,south=false,up=true,waterlogged=true,west=false]",
"minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=low]": "minecraft:cobblestone_wall[east=false,north=false,south=false,up=true,waterlogged=true,west=true]",
"minecraft:cobblestone_wall[east=none,north=none,south=none,up=true,waterlogged=true,west=tall]": "minecraft:cobblestone_wall[east=false,north=false,south=false,up=true,waterlogged=true,west=true]",
@ -5515,8 +5515,8 @@
"minecraft:polished_blackstone_wall[east=tall,north=tall,south=tall,up=false,waterlogged=false,west=none]": "minecraft:red_nether_brick_wall[east=true,north=true,south=true,up=false,waterlogged=false,west=false]",
"minecraft:polished_blackstone_wall[east=tall,north=tall,south=tall,up=false,waterlogged=false,west=low]": "minecraft:red_nether_brick_wall[east=true,north=true,south=true,up=false,waterlogged=false,west=true]",
"minecraft:polished_blackstone_wall[east=tall,north=tall,south=tall,up=false,waterlogged=false,west=tall]": "minecraft:red_nether_brick_wall[east=true,north=true,south=true,up=false,waterlogged=false,west=true]",
"minecraft:soul_fire_lantern[hanging=true]": "minecraft:lantern[hanging=true]",
"minecraft:soul_fire_lantern[hanging=false]": "minecraft:lantern[hanging=false]",
"minecraft:soul_lantern[hanging=true]": "minecraft:lantern[hanging=true]",
"minecraft:soul_lantern[hanging=false]": "minecraft:lantern[hanging=false]",
"minecraft:warped_stem": "minecraft:dark_oak_log[",
"minecraft:stripped_warped_stem": "minecraft:stripped_dark_oak_log[",
"minecraft:warped_nylium": "minecraft:mycelium[snowy=false]",
@ -5766,7 +5766,7 @@
"id": "minecraft:gray_glazed_terracotta",
"name": "1.16 Basalt"
},
"minecraft:soul_fire_torch": {
"minecraft:soul_torch": {
"id": "minecraft:torch",
"name": "1.16 Soul Fire Torch"
},
@ -5838,7 +5838,7 @@
"id": "minecraft:salmon_spawn_egg",
"name": "1.16 Strider Spawn Egg"
},
"minecraft:soul_fire_lantern": {
"minecraft:soul_lantern": {
"id": "minecraft:lantern",
"name": "1.16 Soul Fire Lantern"
},
@ -6206,6 +6206,21 @@
"music.nether.crimson_forest": "music.nether",
"music.nether.warped_forest": "music.nether",
"entity.strider.saddle": "entity.horse.saddle",
"music_disc.pigstep": ""
"music_disc.pigstep": "",
"block.chain.break": "block.metal.break",
"block.chain.fall": "block.metal.fall",
"block.chain.hit": "block.metal.hit",
"block.chain.place": "block.metal.place",
"block.chain.step": "block.metal.step",
"block.gilded_blackstone.break": "block.metal.break",
"block.gilded_blackstone.fall": "block.metal.fall",
"block.gilded_blackstone.hit": "block.metal.hit",
"block.gilded_blackstone.place": "block.metal.place",
"block.gilded_blackstone.step": "block.metal.step",
"block.nether_gold_ore.break": "block.metal.break",
"block.nether_gold_ore.fall": "block.metal.fall",
"block.nether_gold_ore.hit": "block.metal.hit",
"block.nether_gold_ore.place": "block.metal.place",
"block.nether_gold_ore.step": "block.metal.step"
}
}

View File

@ -3,13 +3,19 @@
"selectWorld.locked": "Locked by another running instance of Minecraft",
"selectWorld.access_failure": "Failed to access level",
"selectWorld.delete_failure": "Failed to delete level",
"editGamerule.title": "Edit game rules",
"editGamerule.default": "Default: %s",
"selectWorld.gameRules": "Game rules",
"multiplayer.status.ping": "%s ms",
"chat.queue": "[+%s pending lines]",
"options.entityDistanceScaling": "Entity Distance",
"options.entityDistancePercent": "%s%%",
"options.chat.line_spacing": "Line Spacing",
"options.chat.delay_none": "Chat Delay: None",
"options.chat.delay": "Chat Delay: %s seconds",
"block.minecraft.nether_gold_ore": "Nether Gold Ore",
"block.minecraft.soul_fire_torch": "Soul Fire Torch",
"block.minecraft.soul_fire_wall_torch": "Soul Fire Wall Torch",
"block.minecraft.soul_torch": "Soul Torch",
"block.minecraft.soul_wall_torch": "Soul Wall Torch",
"block.minecraft.respawn_anchor": "Respawn Anchor",
"block.minecraft.spawn.not_valid": "You have no home bed or respawn anchor, or it was obstructed",
"block.minecraft.set_spawn": "Respawn point set",
@ -65,7 +71,7 @@
"block.minecraft.potted_crimson_roots": "Potted Crimson Roots",
"block.minecraft.potted_warped_roots": "Potted Warped Roots",
"block.minecraft.target": "Target",
"block.minecraft.soul_fire_lantern": "Soul Fire Lantern",
"block.minecraft.soul_lantern": "Soul Lantern",
"block.minecraft.soul_campfire": "Soul Campfire",
"block.minecraft.lodestone": "Lodestone",
"block.minecraft.netherite_block": "Block of Netherite",
@ -114,11 +120,11 @@
"item.minecraft.netherite_shovel": "Netherite Shovel",
"item.minecraft.netherite_sword": "Netherite Sword",
"item.minecraft.warped_fungus_on_a_stick": "Warped Fungus on a Stick",
"container.upgrade": "Upgrade",
"container.upgrade": "Upgrade gear",
"jigsaw_block.pool": "Target pool:",
"jigsaw_block.name": "Name:",
"jigsaw_block.target": "Target name:",
"jigsaw_block.levels": "Levels: ",
"jigsaw_block.levels": "Levels: %s",
"jigsaw_block.generate": "Generate",
"jigsaw_block.joint_label": "Joint type:",
"jigsaw_block.joint.rollable": "Rollable",
@ -136,8 +142,10 @@
"death.attack.badRespawnPoint.message": "%1$s was killed by %2$s",
"death.attack.badRespawnPoint.link": "Intentional Game Design",
"enchantment.minecraft.soul_speed": "Soul Speed",
"gui.entity_tooltip.type": "Type: %s",
"stat.minecraft.target_hit": "Targets Hit",
"stat.minecraft.interact_with_smithing_table": "Interactions with Smithing Table",
"attribute.unknown": "Unknown attribute",
"attribute.name.horse.jump_strength": "Horse Jump Strength",
"attribute.name.zombie.spawn_reinforcements": "Zombie Reinforcements",
"attribute.name.generic.max_health": "Max Health",
@ -235,6 +243,17 @@
"subtitles.particle.soul_escape": "Soul escapes",
"advancements.adventure.bullseye.title": "Bullseye",
"advancements.adventure.bullseye.description": "Hit the bullseye of a Target block with an arrow",
"argument.uuid.invalid": "Invalid UUID",
"commands.attribute.failed.entity": "%s is not a valid entity for this command",
"commands.attribute.failed.no_attribute": "Entity %s has no attribute %s",
"commands.attribute.failed.no_modifier": "Attribute %s for entity %s has no modifier %s",
"commands.attribute.failed.modifier_already_present": "Modifier %s is already present on attribute %s for entity %s",
"commands.attribute.value.get.success": "Value of attribute %s for entity %s is %s",
"commands.attribute.base_value.get.success": "Base value of attribute %s for entity %s is %s",
"commands.attribute.base_value.set.success": "Base value for attribute %s for entity %s set to %s",
"commands.attribute.modifier.add.success": "Added modifier %s to attribute %s for entity %s",
"commands.attribute.modifier.remove.success": "Removed modifier %s from attribute %s for entity %s",
"commands.attribute.modifier.value.get.success": "Value of modifier %s on attribute %s for entity %s is %s",
"commands.locatebiome.success": "The nearest %s is at %s (%s blocks away)",
"commands.teleport.invalidPosition": "Invalid position for teleport",
"commands.locatebiome.notFound": "Could not find a %s within reasonable distance",
@ -244,7 +263,52 @@
"biome.minecraft.soul_sand_valley": "Soul Sand Valley",
"biome.minecraft.warped_forest": "Warped Forest",
"biome.minecraft.crimson_forest": "Crimson Forest",
"biome.minecraft.basalt_deltas": "Basalt Deltas"
"biome.minecraft.basalt_deltas": "Basalt Deltas",
"gamerule.announceAdvancements": "Announce advancements",
"gamerule.commandBlockOutput": "Broadcast command block output",
"gamerule.disableElytraMovementCheck": "Disable Elytra movement check",
"gamerule.disableRaids": "Disable raids",
"gamerule.doDaylightCycle": "Advance in-game time",
"gamerule.doEntityDrops": "Drop entity equipment",
"gamerule.doEntityDrops.description": "Controls drops from minecarts (including inventories), item frames, boats, etc.",
"gamerule.doFireTick": "Update fire",
"gamerule.doImmediateRespawn": "Respawn immediately",
"gamerule.doInsomnia": "Spawn phantoms",
"gamerule.doLimitedCrafting": "Require recipe for crafting",
"gamerule.doLimitedCrafting.description": "If enabled, players will be able to craft only unlocked recipes",
"gamerule.doMobLoot": "Drop mob loot",
"gamerule.doMobLoot.description": "Controls resource drops, including experience orbs",
"gamerule.doMobSpawning": "Spawn mobs",
"gamerule.doMobSpawning.description": "Some entities might have separate rules",
"gamerule.doPatrolSpawning": "Spawn pillager patrols",
"gamerule.doTileDrops": "Drop blocks",
"gamerule.doTileDrops.description": "Controls resource drops, including experience orbs",
"gamerule.doTraderSpawning": "Spawn wandering traders",
"gamerule.doWeatherCycle": "Update weather",
"gamerule.drowningDamage": "Deal drowning damage",
"gamerule.fallDamage": "Deal fall damage",
"gamerule.fireDamage": "Deal fire damage",
"gamerule.keepInventory": "Keep inventory after death",
"gamerule.logAdminCommands": "Broadcast admin commands",
"gamerule.maxCommandChainLength": "Command chain size limit",
"gamerule.maxCommandChainLength.description": "Applies to command block chains and functions",
"gamerule.maxEntityCramming": "Entity cramming threshold",
"gamerule.mobGriefing": "Allow destructive mob actions",
"gamerule.naturalRegeneration": "Regenerate health",
"gamerule.randomTickSpeed": "Random tick speed rate",
"gamerule.reducedDebugInfo": "Reduce debug info",
"gamerule.reducedDebugInfo.description": "Limits contents of debug screen",
"gamerule.sendCommandFeedback": "Send command feedback",
"gamerule.showDeathMessages": "Show death messages",
"gamerule.spawnRadius": "Respawn location radius",
"gamerule.spectatorsGenerateChunks": "Allow spectators to generate terrain",
"gamerule.category.chat": "Chat",
"gamerule.category.spawning": "Spawning",
"gamerule.category.updates": "World updates",
"gamerule.category.drops": "Drops",
"gamerule.category.mobs": "Mobs",
"gamerule.category.player": "Player",
"gamerule.category.misc": "Miscellaneous"
},
"1.15": {
"narration.suggestion.tooltip": "Selected suggestion %d out of %d: %s (%s)",