Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd1fad2dd7 | ||
|
|
245f7e24e4 | ||
|
|
9d3db4cf97 | ||
|
|
b67ebecce8 | ||
|
|
d03207289e | ||
|
|
64497b305a | ||
|
|
f5b3799db9 | ||
|
|
00fedc7656 | ||
|
|
05aa05c5b4 | ||
|
|
5243c1800e | ||
|
|
77d702bc9b | ||
|
|
c098e24c4f | ||
|
|
3d083cf463 | ||
|
|
2ab62a8f4f | ||
|
|
296f42086f | ||
|
|
e9f547f7ba | ||
|
|
b7f388b9ee | ||
|
|
824ff375d4 | ||
|
|
8bec05ed38 | ||
|
|
b367caa4b2 | ||
|
|
1c281abf65 |
No files matched your search
@@ -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:
|
||||
|
||||

|
||||
@@ -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();
|
||||
}
|
||||
|
||||
+4
-1
@@ -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
|
||||
|
||||
+39
-6
@@ -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;
|
||||
|
||||
+26
-8
@@ -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));
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -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());
|
||||
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
|
||||
+85
-23
@@ -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);
|
||||
}
|
||||
}
|
||||
+28
@@ -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;
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+24
-12
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user