Compare commits

...
21 Commits
Author SHA1 Message Date
Nassim Jahnke dd1fad2dd7 5.0.1 Release 2024-06-22 15:53:57 +02:00
EnZaXD 245f7e24e4 Fix DummyInitializer unit test to bootstrap without errors (#3965) 2024-06-22 11:04:11 +02:00
EnZaXD 9d3db4cf97 Improve performance in ProtocolVersionRange#contains (#3964) 2024-06-22 11:02:16 +02:00
EnZaXD b67ebecce8 [ci skip] Advertise ViaLoader in README file (#3966) 2024-06-22 11:01:55 +02:00
EnZaXD d03207289e Backup original book pages for creative mode clients in 1.8->1.9 (#3952) 2024-06-21 14:38:00 +02:00
Nassim Jahnke 64497b305a Fix enchantment order 2024-06-20 17:42:27 +02:00
Nassim Jahnke f5b3799db9 Use default dimension data for defaulted registry entry 2024-06-20 17:32:19 +02:00
Nassim Jahnke 00fedc7656 Also handle aqua affinity 2024-06-20 16:08:46 +02:00
Nassim Jahnke 05aa05c5b4 Disable soul speed check for now 2024-06-20 15:31:18 +02:00
Nassim Jahnke 5243c1800e entity effect magic
Fixes #3927
2024-06-20 12:51:35 +02:00
Nassim Jahnke 77d702bc9b Also handle movement related enchantments
Fixes #3960
2024-06-20 12:12:29 +02:00
RK_01 c098e24c4f Return generic Item array in StructuredItem#emptyArray (#3955) 2024-06-17 20:08:34 +02:00
EnZaXD 3d083cf463 Add handleMenuType function to ItemRewriter (#3953) 2024-06-17 16:39:01 +02:00
Nassim Jahnke 2ab62a8f4f Protected 2024-06-17 12:48:52 +02:00
Nassim Jahnke 296f42086f Also handle item_name item data for component rewriting 2024-06-17 12:43:18 +02:00
Nassim Jahnke e9f547f7ba Add ArrayUtil 2024-06-17 11:48:21 +02:00
CamotoyandAJ Ferguson b7f388b9ee 1.20.5 -> 1.20.3: nullify empty tags (#3949)
Co-authored-by: AJ Ferguson <AJ-Ferguson@users.noreply.github.com>
2024-06-17 09:42:09 +02:00
EnZaXD 824ff375d4 Fix GameMode#getById behaviour in 1.8->1.9 (#3947)
Vanilla fallbacks to SURVIVAL and doesn't throw any errors,
also NOT_SET was missing previously.
2024-06-16 22:28:50 +02:00
Leon 8bec05ed38 Fixup hardcore handling on join in 1.8->1.9 (#3946)
* Fix players crashing when logging into a 1.8 server on hardcore mode, Fixes #3944

* Requested change to #3944 fix
2024-06-16 22:06:39 +02:00
Nassim Jahnke b367caa4b2 Add Charged item tag if there's charged projectiles
Closes ViaVersion/ViaBackwards#787
2024-06-16 21:58:07 +02:00
Nassim Jahnke 1c281abf65 Bump version to 5.0.1-SNAPSHOT 2024-06-16 21:38:32 +02:00
22 changed files with 390 additions and 73 deletions

No files matched your search

+5
View File
@@ -11,6 +11,9 @@ The base ViaVersion jar runs on Paper and Velocity. We also have projects integr
on Fabric, Forge, Bungee, Sponge, or as a standalone proxy to join from basically any client version on
any server version from the past decade. **See [HERE](https://github.com/ViaVersion) for an overview of the different Via\* projects.**
Note that ViaVersion will be able to **run best on either Paper servers or Fabric clients** due to having
direct access to client/server state and more extensive API.
Supported Versions:
![Table (https://i.imgur.com/sTrVnC2.png)](https://i.imgur.com/sTrVnC2.png)
@@ -67,6 +70,8 @@ dependencies {
If you need access to the existing protocol or platform implementations, use the parent artifact `viaversion`.
Please note the [differences in licensing](#license).
Note: If you want to make your own platform implementation of ViaVersion (and additional addons),
you can use the [ViaLoader](https://github.com/ViaVersion/ViaLoader) project.
Building
--------
@@ -24,6 +24,7 @@ package com.viaversion.viaversion.api.minecraft;
public enum GameMode {
NOT_SET(""),
SURVIVAL("Survival Mode"),
CREATIVE("Creative Mode"),
ADVENTURE("Adventure Mode"),
@@ -41,11 +42,11 @@ public enum GameMode {
public static GameMode getById(int id) {
return switch (id) {
case 0 -> SURVIVAL;
case -1 -> NOT_SET;
case 1 -> CREATIVE;
case 2 -> ADVENTURE;
case 3 -> SPECTATOR;
default -> throw new IllegalArgumentException("Unknown gamemode id: " + id);
default /*0*/ -> SURVIVAL;
};
}
}
@@ -25,6 +25,12 @@ package com.viaversion.viaversion.api.minecraft;
import com.viaversion.nbt.tag.Tag;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents an entry in a registry.
*
* @param key key of the registry entry
* @param tag data of the registry entry, or null if the client should use its default
*/
public record RegistryEntry(String key, @Nullable Tag tag) {
public RegistryEntry withKey(final String key) {
@@ -46,8 +46,8 @@ public class StructuredItem implements Item {
return new StructuredItem(0, 0);
}
public static StructuredItem[] emptyArray(final int size) {
final StructuredItem[] items = new StructuredItem[size];
public static Item[] emptyArray(final int size) {
final Item[] items = new Item[size];
for (int i = 0; i < items.length; i++) {
items[i] = empty();
}
@@ -104,7 +104,10 @@ public class ProtocolVersionRange {
*/
public boolean contains(final ProtocolVersion version) {
if (this.ranges == null) return true;
return this.ranges.stream().anyMatch(range -> range.contains(version));
for (Range<ProtocolVersion> range : this.ranges) {
if (range.contains(version)) return true;
}
return false;
}
@Override
@@ -31,6 +31,7 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
@@ -40,6 +41,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public final class PlayerChangeItemListener extends ViaBukkitListener {
private final Enchantment efficiency = Enchantment.getByKey(NamespacedKey.minecraft("efficiency"));
private final Enchantment aquaAffinity = Enchantment.getByKey(NamespacedKey.minecraft("aqua_affinity"));
private final Enchantment depthStrider = Enchantment.getByKey(NamespacedKey.minecraft("depth_strider"));
private final Enchantment soulSpeed = Enchantment.getByKey(NamespacedKey.minecraft("soul_speed"));
private final Enchantment swiftSneak = Enchantment.getByKey(NamespacedKey.minecraft("swift_sneak"));
public PlayerChangeItemListener(final ViaVersionPlugin plugin) {
super(plugin, Protocol1_20_5To1_21.class);
@@ -49,8 +54,16 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
public void onPlayerInventorySlotChangedEvent(final PlayerInventorySlotChangeEvent event) {
final Player player = event.getPlayer();
final ItemStack item = event.getNewItemStack();
if (event.getSlot() == player.getInventory().getHeldItemSlot()) {
sendAttributeUpdate(player, item);
final PlayerInventory inventory = player.getInventory();
final int slot = event.getSlot();
if (slot == inventory.getHeldItemSlot()) {
sendAttributeUpdate(player, item, Slot.HAND);
} else if (slot == 36) {
sendAttributeUpdate(player, item, Slot.BOOTS);
} else if (slot == 37) {
sendAttributeUpdate(player, item, Slot.LEGGINGS);
} else if (slot == 39) {
sendAttributeUpdate(player, item, Slot.HELMET);
}
}
@@ -58,10 +71,10 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
public void onPlayerItemHeld(final PlayerItemHeldEvent event) {
final Player player = event.getPlayer();
final ItemStack item = player.getInventory().getItem(event.getNewSlot());
sendAttributeUpdate(player, item);
sendAttributeUpdate(player, item, Slot.HAND);
}
private void sendAttributeUpdate(final Player player, @Nullable final ItemStack item) {
private void sendAttributeUpdate(final Player player, @Nullable final ItemStack item, final Slot slot) {
final UserConnection connection = Via.getAPI().getConnection(player.getUniqueId());
if (connection == null || !isOnPipe(player)) {
return;
@@ -72,7 +85,27 @@ public final class PlayerChangeItemListener extends ViaBukkitListener {
return;
}
final int efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
storage.setEfficiencyLevel(new EfficiencyAttributeStorage.StoredEfficiency(player.getEntityId(), efficiencyLevel), connection);
final EfficiencyAttributeStorage.ActiveEnchants activeEnchants = storage.activeEnchants();
int efficiencyLevel = activeEnchants.efficiency().level();
int aquaAffinityLevel = activeEnchants.aquaAffinity().level();
int soulSpeedLevel = activeEnchants.soulSpeed().level();
int swiftSneakLevel = activeEnchants.swiftSneak().level();
int depthStriderLevel = activeEnchants.depthStrider().level();
switch (slot) {
case HAND -> efficiencyLevel = item != null ? item.getEnchantmentLevel(efficiency) : 0;
case HELMET -> aquaAffinityLevel = item != null ? item.getEnchantmentLevel(aquaAffinity) : 0;
case LEGGINGS -> swiftSneakLevel = item != null && swiftSneak != null ? item.getEnchantmentLevel(swiftSneak) : 0;
case BOOTS -> {
depthStriderLevel = item != null && depthStrider != null ? item.getEnchantmentLevel(depthStrider) : 0;
// TODO This needs continuous ticking for the supporting block as a conditional effect
// and is even more prone to desync from high ping than the other attributes
//soulSpeedLevel = item != null && soulSpeed != null ? item.getEnchantmentLevel(soulSpeed) : 0;
}
}
storage.setEnchants(player.getEntityId(), connection, efficiencyLevel, soulSpeedLevel, swiftSneakLevel, aquaAffinityLevel, depthStriderLevel);
}
private enum Slot {
HAND, BOOTS, LEGGINGS, HELMET
}
}
@@ -48,6 +48,14 @@ public final class DimensionDataImpl implements DimensionData {
this.minY = minY.asInt();
}
public static DimensionData withDefaultsFor(final String key, final int id) {
return switch (key) {
case "overworld", "overworld_caves" -> new DimensionDataImpl(id, -64, 384);
case "the_nether", "the_end" -> new DimensionDataImpl(id, 0, 256);
default -> throw new IllegalArgumentException("Missing registry data for unknown dimension: " + key);
};
}
@Override
public int id() {
return id;
@@ -225,18 +225,32 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
wrapper.passthrough(Types.DOUBLE); // X
wrapper.passthrough(Types.DOUBLE); // Y
wrapper.passthrough(Types.DOUBLE); // Z
wrapper.passthrough(Types.FLOAT); // Offset X
wrapper.passthrough(Types.FLOAT); // Offset Y
wrapper.passthrough(Types.FLOAT); // Offset Z
final float offX = wrapper.passthrough(Types.FLOAT);
final float offY = wrapper.passthrough(Types.FLOAT);
final float offZ = wrapper.passthrough(Types.FLOAT);
final float data = wrapper.passthrough(Types.FLOAT);
wrapper.passthrough(Types.INT); // Particle Count
final int count = wrapper.passthrough(Types.INT);
// Read data and add it to Particle
final ParticleMappings mappings = protocol.getMappingData().getParticleMappings();
final int mappedId = mappings.getNewId(particleId);
final Particle particle = new Particle(mappedId);
if (mappedId == mappings.mappedId("entity_effect")) {
particle.add(Types.INT, data != 0 ? ThreadLocalRandom.current().nextInt() : 0); // rgb
final int color;
if (data == 0) {
// Black
color = 0;
} else if (count != 0) {
// Randomized color
color = ThreadLocalRandom.current().nextInt();
} else {
// From offset
final int red = Math.round(offX * 255);
final int green = Math.round(offY * 255);
final int blue = Math.round(offZ * 255);
color = (red << 16) | (green << 8) | blue;
}
particle.add(Types.INT, EntityPacketRewriter1_20_5.withAlpha(color));
} else if (particleId == mappings.id("dust_color_transition")) {
for (int i = 0; i < 7; i++) {
particle.add(Types.FLOAT, wrapper.read(Types.FLOAT));
@@ -408,6 +422,10 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
dataConverter.writeToTag(connection, structuredData, tag);
}
if (tag.isEmpty()) {
dataItem.setTag(null);
}
return dataItem;
}
@@ -1071,13 +1089,13 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter<Clientboun
data.set(StructuredDataKey.SUSPICIOUS_STEW_EFFECTS, suspiciousStewEffects);
}
private void updateLodestoneTracker(final boolean tracked, final CompoundTag lodestonePosTag, final String lodestoneDimensionTag, final StructuredDataContainer data) {
private void updateLodestoneTracker(final boolean tracked, final CompoundTag lodestonePosTag, final String lodestoneDimension, final StructuredDataContainer data) {
GlobalBlockPosition position = null;
if (lodestonePosTag != null && lodestoneDimensionTag != null) {
if (lodestonePosTag != null && lodestoneDimension != null) {
final int x = lodestonePosTag.getInt("X");
final int y = lodestonePosTag.getInt("Y");
final int z = lodestonePosTag.getInt("Z");
position = new GlobalBlockPosition(lodestoneDimensionTag, x, y, z);
position = new GlobalBlockPosition(lodestoneDimension, x, y, z);
}
data.set(StructuredDataKey.LODESTONE_TRACKER, new LodestoneTracker(position, tracked));
}
@@ -387,7 +387,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
}
}
private int withAlpha(final int rgb) {
static int withAlpha(final int rgb) {
return 255 << 24 | rgb & 0xffffff;
}
@@ -218,7 +218,12 @@ public final class StructuredDataConverter {
}
});
register(StructuredDataKey.BASE_COLOR, (data, tag) -> getBlockEntityTag(tag).putInt("Base", data));
register(StructuredDataKey.CHARGED_PROJECTILES1_20_5, (connection, data, tag) -> convertItemList(connection, data, tag, "ChargedProjectiles"));
register(StructuredDataKey.CHARGED_PROJECTILES1_20_5, (connection, data, tag) -> {
convertItemList(connection, data, tag, "ChargedProjectiles");
if (data.length != 0) {
tag.putBoolean("Charged", true);
}
});
register(StructuredDataKey.BUNDLE_CONTENTS1_20_5, (connection, data, tag) -> convertItemList(connection, data, tag, "Items"));
register(StructuredDataKey.LODESTONE_TRACKER, (data, tag) -> {
tag.putBoolean("LodestoneTracked", data.tracked());
@@ -37,6 +37,7 @@ import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.data.Paintings1_20_5;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.storage.EfficiencyAttributeStorage;
import com.viaversion.viaversion.rewriter.EntityRewriter;
import com.viaversion.viaversion.util.ArrayUtil;
import com.viaversion.viaversion.util.Key;
public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPacket1_20_5, Protocol1_20_5To1_21> {
@@ -60,7 +61,7 @@ public final class EntityPacketRewriter1_21 extends EntityRewriter<ClientboundPa
campfireDamageType.putString("scaling", "when_caused_by_living_non_player");
campfireDamageType.putString("message_id", "inFire");
campfireDamageType.putFloat("exhaustion", 0.1F);
wrapper.set(Types.REGISTRY_ENTRY_ARRAY, 0, addRegistryEntries(entries, new RegistryEntry("minecraft:campfire", campfireDamageType)));
wrapper.set(Types.REGISTRY_ENTRY_ARRAY, 0, ArrayUtil.add(entries, new RegistryEntry("minecraft:campfire", campfireDamageType)));
} else {
handleRegistryData1_20_5(wrapper.user(), type, entries);
}
@@ -23,57 +23,119 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21;
import com.viaversion.viaversion.protocols.v1_20_5to1_21.packet.ClientboundPackets1_21;
import java.util.List;
public final class EfficiencyAttributeStorage implements StorableObject {
private static final int MINING_EFFICIENCY_ID = 19;
private final Object lock = new Object(); // Slightly sloppy locking, but should be good enough
private static final EnchantAttributeModifier EFFICIENCY = new EnchantAttributeModifier("minecraft:enchantment.efficiency/mainhand", 19, 0, level -> (level * level) + 1);
private static final EnchantAttributeModifier SOUL_SPEED = new EnchantAttributeModifier("minecraft:enchantment.soul_speed", 21, 0.1, level -> 0.04D + ((level - 1) * 0.01D));
private static final EnchantAttributeModifier SWIFT_SNEAK = new EnchantAttributeModifier("minecraft:enchantment.swift_sneak", 25, 0.3, level -> level * 0.15D);
private static final EnchantAttributeModifier AQUA_AFFINITY = new EnchantAttributeModifier("minecraft:enchantment.aqua_affinity", 28, 0.2, level -> level * 4, (byte) 2);
private static final EnchantAttributeModifier DEPTH_STRIDER = new EnchantAttributeModifier("minecraft:enchantment.depth_strider", 30, 0, level -> level / 3D);
private static final ActiveEnchants DEFAULT = new ActiveEnchants(-1,
new ActiveEnchant(EFFICIENCY, 0),
new ActiveEnchant(SOUL_SPEED, 0),
new ActiveEnchant(SWIFT_SNEAK, 0),
new ActiveEnchant(AQUA_AFFINITY, 0),
new ActiveEnchant(DEPTH_STRIDER, 0)
);
private final Object lock = new Object();
private volatile boolean attributesSent = true;
private volatile boolean loginSent;
private volatile StoredEfficiency efficiencyLevel;
private ActiveEnchants activeEnchants = DEFAULT;
public void setEfficiencyLevel(final StoredEfficiency efficiencyLevel, final UserConnection connection) {
this.efficiencyLevel = efficiencyLevel;
public void setEnchants(final int entityId, final UserConnection connection, final int efficiency, final int soulSpeed,
final int swiftSneak, final int aquaAffinity, final int depthStrider) {
// Always called from the main thread
if (efficiency == activeEnchants.efficiency.level
&& soulSpeed == activeEnchants.soulSpeed.level
&& swiftSneak == activeEnchants.swiftSneak.level
&& aquaAffinity == activeEnchants.aquaAffinity.level
&& depthStrider == activeEnchants.depthStrider.level) {
return;
}
synchronized (lock) {
this.activeEnchants = new ActiveEnchants(entityId,
new ActiveEnchant(EFFICIENCY, efficiency),
new ActiveEnchant(SOUL_SPEED, soulSpeed),
new ActiveEnchant(SWIFT_SNEAK, swiftSneak),
new ActiveEnchant(AQUA_AFFINITY, aquaAffinity),
new ActiveEnchant(DEPTH_STRIDER, depthStrider)
);
this.attributesSent = false;
}
sendAttributesPacket(connection);
}
public ActiveEnchants activeEnchants() {
return activeEnchants;
}
public void onLoginSent(final UserConnection connection) {
// Always called from the netty thread
this.loginSent = true;
sendAttributesPacket(connection);
}
private void sendAttributesPacket(final UserConnection connection) {
final StoredEfficiency efficiency;
final ActiveEnchants enchants;
synchronized (lock) {
// Older servers (and often Bungee) will send world state packets before sending the login packet
if (!loginSent || efficiencyLevel == null) {
if (!loginSent || attributesSent) {
return;
}
efficiency = efficiencyLevel;
efficiencyLevel = null;
enchants = this.activeEnchants;
attributesSent = true;
}
final PacketWrapper attributesPacket = PacketWrapper.create(ClientboundPackets1_21.UPDATE_ATTRIBUTES, connection);
attributesPacket.write(Types.VAR_INT, efficiency.entityId());
attributesPacket.write(Types.VAR_INT, enchants.entityId());
attributesPacket.write(Types.VAR_INT, 1); // Size
attributesPacket.write(Types.VAR_INT, MINING_EFFICIENCY_ID); // Attribute ID
attributesPacket.write(Types.DOUBLE, 0D); // Base
final List<ActiveEnchant> list = List.of(
enchants.efficiency(),
enchants.soulSpeed(),
enchants.swiftSneak(),
enchants.aquaAffinity(),
enchants.depthStrider()
);
attributesPacket.write(Types.VAR_INT, list.size());
for (final ActiveEnchant enchant : list) {
final EnchantAttributeModifier modifier = enchant.modifier;
attributesPacket.write(Types.VAR_INT, modifier.attributeId);
attributesPacket.write(Types.DOUBLE, modifier.baseValue);
final int level = efficiency.level;
if (level > 0) {
final double modifierAmount = (level * level) + 1D;
attributesPacket.write(Types.VAR_INT, 1); // Modifiers
attributesPacket.write(Types.STRING, "minecraft:enchantment.efficiency/mainhand"); // Id
attributesPacket.write(Types.DOUBLE, modifierAmount);
attributesPacket.write(Types.BYTE, (byte) 0); // 'Add' operation
} else {
attributesPacket.write(Types.VAR_INT, 0); // Modifiers
if (enchant.level > 0) {
attributesPacket.write(Types.VAR_INT, 1); // Modifiers
attributesPacket.write(Types.STRING, modifier.key);
attributesPacket.write(Types.DOUBLE, enchant.modifier.modifierFunction.get(enchant.level));
attributesPacket.write(Types.BYTE, modifier.operation);
} else {
attributesPacket.write(Types.VAR_INT, 0); // Modifiers
}
}
attributesPacket.scheduleSend(Protocol1_20_5To1_21.class);
}
public record StoredEfficiency(int entityId, int level) {
public record ActiveEnchants(int entityId, ActiveEnchant efficiency, ActiveEnchant soulSpeed,
ActiveEnchant swiftSneak, ActiveEnchant aquaAffinity, ActiveEnchant depthStrider) {
}
public record ActiveEnchant(EnchantAttributeModifier modifier, int level) {
}
public record EnchantAttributeModifier(String key, int attributeId, double baseValue, LevelToModifier modifierFunction, byte operation) {
private EnchantAttributeModifier(String key, int attributeId, double baseValue, LevelToModifier modifierFunction) {
this(key, attributeId, baseValue, modifierFunction, (byte) 0);
}
}
@FunctionalInterface
private interface LevelToModifier {
double get(int level);
}
}
@@ -418,6 +418,8 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
}
ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
tag.put(nbtTagName("pages"), pages == null ? new ListTag<>(StringTag.class) : pages.copy());
if (pages == null) {
pages = new ListTag<>(Collections.singletonList(new StringTag(ComponentUtil.emptyJsonComponent().toString())));
tag.put("pages", pages);
@@ -481,6 +483,32 @@ public class ItemPacketRewriter1_9 extends ItemRewriter<ClientboundPackets1_8, S
item.setTag(tag);
item.setData((short) data);
}
if (item.identifier() == 387) { // WRITTEN_BOOK
CompoundTag tag = item.tag();
if (tag != null) {
// Prefer saved pages since they are more likely to be accurate
ListTag<StringTag> backup = tag.removeUnchecked(nbtTagName("pages"));
if (backup != null) {
if (!backup.isEmpty()) {
tag.put("pages", backup);
} else {
tag.remove("pages");
if (tag.isEmpty()) {
item.setTag(null);
}
}
} else {
// Fallback to normal pages tag
ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
if (pages != null) {
for (int i = 0; i < pages.size(); i++) {
final StringTag page = pages.get(i);
page.setValue(ComponentUtil.convertJsonOrEmpty(page.getValue(), SerializerVersion.V1_9, SerializerVersion.V1_8).toString());
}
}
}
}
}
boolean newItem = item.identifier() >= 198 && item.identifier() <= 212;
newItem |= item.identifier() == 397 && item.data() == 5;
@@ -196,7 +196,9 @@ public class PlayerPacketRewriter1_9 {
handler(wrapper -> {
EntityTracker1_9 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_9.class);
tracker.setGameMode(GameMode.getById(wrapper.get(Types.UNSIGNED_BYTE, 0))); //Set player gamemode
short gamemodeId = wrapper.get(Types.UNSIGNED_BYTE, 0);
gamemodeId &= -9; // remove the hardcore mode flag
tracker.setGameMode(GameMode.getById(gamemodeId)); //Set player gamemode
});
// Track player's dimension
@@ -527,7 +527,11 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
final Map<String, DimensionData> dimensionDataMap = new HashMap<>(entries.length);
for (int i = 0; i < entries.length; i++) {
final RegistryEntry entry = entries[i];
dimensionDataMap.put(Key.stripMinecraftNamespace(entry.key()), new DimensionDataImpl(i, (CompoundTag) entry.tag()));
final String key = Key.stripMinecraftNamespace(entry.key());
final DimensionData dimensionData = entry.tag() != null
? new DimensionDataImpl(i, (CompoundTag) entry.tag())
: DimensionDataImpl.withDefaultsFor(key, i);
dimensionDataMap.put(key, dimensionData);
}
tracker(connection).setDimensions(dimensionDataMap);
}
@@ -112,20 +112,22 @@ public class ItemRewriter<C extends ClientboundPacketType, S extends Serverbound
@Override
public void register() {
map(Types.VAR_INT); // Container id
handler(wrapper -> {
final int windowType = wrapper.read(Types.VAR_INT);
final int mappedId = protocol.getMappingData().getMenuMappings().getNewId(windowType);
if (mappedId == -1) {
wrapper.cancel();
return;
}
wrapper.write(Types.VAR_INT, mappedId);
});
handler(wrapper -> handleMenuType(wrapper));
}
});
}
public void handleMenuType(final PacketWrapper wrapper) {
final int windowType = wrapper.read(Types.VAR_INT);
final int mappedId = protocol.getMappingData().getMenuMappings().getNewId(windowType);
if (mappedId == -1) {
wrapper.cancel();
return;
}
wrapper.write(Types.VAR_INT, mappedId);
}
public void registerSetSlot(C packetType) {
protocol.registerClientbound(packetType, new PacketHandlers() {
@Override
@@ -69,14 +69,8 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
final ComponentRewriter componentRewriter = protocol.getComponentRewriter();
if (componentRewriter != null) {
// Handle name and lore components
final StructuredData<Tag> customNameData = dataContainer.getNonEmpty(StructuredDataKey.CUSTOM_NAME);
if (customNameData != null) {
final Tag originalName = customNameData.value().copy();
componentRewriter.processTag(connection, customNameData.value());
if (!customNameData.value().equals(originalName)) {
saveTag(createCustomTag(item), originalName, "Name");
}
}
updateComponent(connection, item, StructuredDataKey.ITEM_NAME, "item_name");
updateComponent(connection, item, StructuredDataKey.CUSTOM_NAME, "custom_name");
final StructuredData<Tag[]> loreData = dataContainer.getNonEmpty(StructuredDataKey.LORE);
if (loreData != null) {
@@ -164,6 +158,19 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
}
}
protected void updateComponent(final UserConnection connection, final Item item, final StructuredDataKey<Tag> key, final String backupKey) {
final StructuredData<Tag> name = item.dataContainer().getNonEmpty(key);
if (name == null) {
return;
}
final Tag originalName = name.value().copy();
protocol.getComponentRewriter().processTag(connection, name.value());
if (!name.value().equals(originalName)) {
saveTag(createCustomTag(item), originalName, backupKey);
}
}
protected void restoreTextComponents(final Item item) {
final StructuredDataContainer data = item.dataContainer();
final StructuredData<CompoundTag> customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA);
@@ -172,12 +179,17 @@ public class StructuredItemRewriter<C extends ClientboundPacketType, S extends S
}
// Remove custom name
if (customData.value().remove(nbtTagName("customName")) != null) {
if (customData.value().remove(nbtTagName("added_custom_name")) != null) {
data.remove(StructuredDataKey.CUSTOM_NAME);
} else {
final Tag name = removeBackupTag(customData.value(), "Name");
if (name != null) {
data.set(StructuredDataKey.CUSTOM_NAME, name);
final Tag customName = removeBackupTag(customData.value(), "custom_name");
if (customName != null) {
data.set(StructuredDataKey.CUSTOM_NAME, customName);
}
final Tag itemName = removeBackupTag(customData.value(), "item_name");
if (itemName != null) {
data.set(StructuredDataKey.ITEM_NAME, itemName);
}
}
}
@@ -0,0 +1,50 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* 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.viaversion.util;
import java.util.Arrays;
/**
* For type safety and effort reasons, buffer types use arrays instead of lists.
* <p>
* This class includes simple methods to work with these arrays in case they need to be modified
* (obviously being more expensive due to the required array copies for every modification).
*/
public final class ArrayUtil {
public static <T> T[] add(final T[] array, final T element) {
final int length = array.length;
final T[] newArray = Arrays.copyOf(array, length + 1);
newArray[length] = element;
return newArray;
}
@SafeVarargs
public static <T> T[] add(final T[] array, final T... elements) {
final int length = array.length;
final T[] newArray = Arrays.copyOf(array, length + elements.length);
System.arraycopy(elements, 0, newArray, length, elements.length);
return newArray;
}
public static <T> T[] remove(final T[] array, final int index) {
final T[] newArray = Arrays.copyOf(array, array.length - 1);
System.arraycopy(array, index + 1, newArray, index, newArray.length - index);
return newArray;
}
}
@@ -19,7 +19,6 @@ package com.viaversion.viaversion.common.dummy;
import com.viaversion.viaversion.ViaManagerImpl;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
public final class DummyInitializer {
@@ -31,7 +30,10 @@ public final class DummyInitializer {
}
initialized = true;
Via.init(new ViaManagerImpl(new TestPlatform(), null, null, null));
((ProtocolManagerImpl) Via.getManager().getProtocolManager()).registerProtocols();
Via.init(new ViaManagerImpl(new TestPlatform(), new TestInjector(), null, new TestLoader()));
final ViaManagerImpl manager = (ViaManagerImpl) Via.getManager();
manager.init();
manager.onServerLoaded();
}
}
@@ -0,0 +1,44 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* 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.viaversion.common.dummy;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.platform.ViaInjector;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
public class TestInjector implements ViaInjector {
@Override
public void inject() {
}
@Override
public void uninject() {
}
@Override
public ProtocolVersion getServerProtocolVersion() {
return ProtocolVersion.v1_15_2;
}
@Override
public JsonObject getDump() {
return null;
}
}
@@ -0,0 +1,31 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* 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.viaversion.common.dummy;
import com.viaversion.viaversion.api.platform.ViaPlatformLoader;
public class TestLoader implements ViaPlatformLoader {
@Override
public void load() {
}
@Override
public void unload() {
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=5.0.0
projectVersion=5.0.1
# Smile emoji
mcVersions=1.21,1.20.6,1.20.5,1.20.4, 1.20.3, 1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9