Implement showShieldWhenSwordInHand option (#2417)

This commit is contained in:
LabyStudio 2021-04-05 18:53:57 +02:00 committed by GitHub
parent 26bbc92f94
commit e83686d6fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 317 additions and 11 deletions

View File

@ -79,10 +79,22 @@ public interface ViaVersionConfig {
/** /**
* Whether the player can block with the shield without a delay. * Whether the player can block with the shield without a delay.
* *
* This option requires {@link #isShowShieldWhenSwordInHand()} to be disabled
*
* @return {@code true} if non delayed shield blocking is enabled. * @return {@code true} if non delayed shield blocking is enabled.
*/ */
boolean isNoDelayShieldBlocking(); boolean isNoDelayShieldBlocking();
/**
* Puts the shield into the second hand when holding a sword.
* The shield will disappear when switching to another item.
*
* This option requires {@link #isShieldBlocking()} to be enabled
*
* @return {@code true} if the shield should appear when holding a sword
*/
boolean isShowShieldWhenSwordInHand();
/** /**
* Get if armor stand positions are fixed so holograms show up at the correct height in 1.9 & 1.10 * Get if armor stand positions are fixed so holograms show up at the correct height in 1.9 & 1.10
* *

View File

@ -33,6 +33,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
private boolean suppressMetadataErrors; private boolean suppressMetadataErrors;
private boolean shieldBlocking; private boolean shieldBlocking;
private boolean noDelayShieldBlocking; private boolean noDelayShieldBlocking;
private boolean showShieldWhenSwordInHand;
private boolean hologramPatch; private boolean hologramPatch;
private boolean pistonAnimationPatch; private boolean pistonAnimationPatch;
private boolean bossbarPatch; private boolean bossbarPatch;
@ -93,6 +94,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
suppressMetadataErrors = getBoolean("suppress-metadata-errors", false); suppressMetadataErrors = getBoolean("suppress-metadata-errors", false);
shieldBlocking = getBoolean("shield-blocking", true); shieldBlocking = getBoolean("shield-blocking", true);
noDelayShieldBlocking = getBoolean("no-delay-shield-blocking", false); noDelayShieldBlocking = getBoolean("no-delay-shield-blocking", false);
showShieldWhenSwordInHand = getBoolean("show-shield-when-sword-in-hand", false);
hologramPatch = getBoolean("hologram-patch", false); hologramPatch = getBoolean("hologram-patch", false);
pistonAnimationPatch = getBoolean("piston-animation-patch", false); pistonAnimationPatch = getBoolean("piston-animation-patch", false);
bossbarPatch = getBoolean("bossbar-patch", true); bossbarPatch = getBoolean("bossbar-patch", true);
@ -177,6 +179,11 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
return noDelayShieldBlocking; return noDelayShieldBlocking;
} }
@Override
public boolean isShowShieldWhenSwordInHand() {
return showShieldWhenSwordInHand;
}
@Override @Override
public boolean isHologramPatch() { public boolean isHologramPatch() {
return hologramPatch; return hologramPatch;

View File

@ -18,6 +18,7 @@
package us.myles.ViaVersion.protocols.protocol1_9to1_8.packets; package us.myles.ViaVersion.protocols.protocol1_9to1_8.packets;
import us.myles.ViaVersion.api.PacketWrapper; import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.minecraft.item.Item; import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.protocol.Protocol; import us.myles.ViaVersion.api.protocol.Protocol;
import us.myles.ViaVersion.api.remapper.PacketHandler; import us.myles.ViaVersion.api.remapper.PacketHandler;
@ -113,6 +114,25 @@ public class InventoryPackets {
@Override @Override
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
Item stack = wrapper.get(Type.ITEM, 0); Item stack = wrapper.get(Type.ITEM, 0);
boolean showShieldWhenSwordInHand = Via.getConfig().isShowShieldWhenSwordInHand()
&& Via.getConfig().isShieldBlocking();
// Check if it is the inventory of the player
if (showShieldWhenSwordInHand) {
InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class);
EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class);
short slotID = wrapper.get(Type.SHORT, 0);
short windowId = wrapper.get(Type.BYTE, 0);
// Store item in slot
inventoryTracker.setItemId(windowId, slotID, stack == null ? 0 : stack.getIdentifier());
// Sync shield item in offhand with main hand
entityTracker.syncShieldWithSword();
}
ItemRewriter.toClient(stack); ItemRewriter.toClient(stack);
} }
}); });
@ -145,8 +165,29 @@ public class InventoryPackets {
@Override @Override
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
Item[] stacks = wrapper.get(Type.ITEM_ARRAY, 0); Item[] stacks = wrapper.get(Type.ITEM_ARRAY, 0);
for (Item stack : stacks) Short windowId = wrapper.get(Type.UNSIGNED_BYTE, 0);
InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class);
EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class);
boolean showShieldWhenSwordInHand = Via.getConfig().isShowShieldWhenSwordInHand()
&& Via.getConfig().isShieldBlocking();
for (short i = 0; i < stacks.length; i++) {
Item stack = stacks[i];
// Store items in slots
if (showShieldWhenSwordInHand) {
inventoryTracker.setItemId(windowId, i, stack == null ? 0 : stack.getIdentifier());
}
ItemRewriter.toClient(stack); ItemRewriter.toClient(stack);
}
// Sync shield item in offhand with main hand
if (showShieldWhenSwordInHand) {
entityTracker.syncShieldWithSword();
}
} }
}); });
// Brewing Patch // Brewing Patch
@ -185,6 +226,7 @@ public class InventoryPackets {
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class); InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class);
inventoryTracker.setInventory(null); inventoryTracker.setInventory(null);
inventoryTracker.resetInventory(wrapper.get(Type.UNSIGNED_BYTE, 0));
} }
}); });
} }
@ -218,6 +260,22 @@ public class InventoryPackets {
@Override @Override
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
Item stack = wrapper.get(Type.ITEM, 0); Item stack = wrapper.get(Type.ITEM, 0);
boolean showShieldWhenSwordInHand = Via.getConfig().isShowShieldWhenSwordInHand()
&& Via.getConfig().isShieldBlocking();
if (showShieldWhenSwordInHand) {
InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class);
EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class);
short slotID = wrapper.get(Type.SHORT, 0);
// Update item in slot
inventoryTracker.setItemId((short) 0, slotID, stack == null ? 0 : stack.getIdentifier());
// Sync shield item in offhand with main hand
entityTracker.syncShieldWithSword();
}
ItemRewriter.toServer(stack); ItemRewriter.toServer(stack);
} }
}); });
@ -259,6 +317,18 @@ public class InventoryPackets {
@Override @Override
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
Item stack = wrapper.get(Type.ITEM, 0); Item stack = wrapper.get(Type.ITEM, 0);
if (Via.getConfig().isShowShieldWhenSwordInHand()) {
Short windowId = wrapper.get(Type.UNSIGNED_BYTE, 0);
byte mode = wrapper.get(Type.BYTE, 1);
short hoverSlot = wrapper.get(Type.SHORT, 0);
byte button = wrapper.get(Type.BYTE, 0);
// Move items in inventory to track the sword location
InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class);
inventoryTracker.handleWindowClick(windowId, mode, hoverSlot, button);
}
ItemRewriter.toServer(stack); ItemRewriter.toServer(stack);
} }
}); });
@ -305,12 +375,15 @@ public class InventoryPackets {
@Override @Override
public void registerMap() { public void registerMap() {
map(Type.UNSIGNED_BYTE); // 0 - Window ID
// Inventory tracking // Inventory tracking
handler(new PacketHandler() { handler(new PacketHandler() {
@Override @Override
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class); InventoryTracker inventoryTracker = wrapper.user().get(InventoryTracker.class);
inventoryTracker.setInventory(null); inventoryTracker.setInventory(null);
inventoryTracker.resetInventory(wrapper.get(Type.UNSIGNED_BYTE, 0));
} }
}); });
} }
@ -319,14 +392,30 @@ public class InventoryPackets {
protocol.registerIncoming(ServerboundPackets1_9.HELD_ITEM_CHANGE, new PacketRemapper() { protocol.registerIncoming(ServerboundPackets1_9.HELD_ITEM_CHANGE, new PacketRemapper() {
@Override @Override
public void registerMap() { public void registerMap() {
map(Type.SHORT); // 0 - Slot id
// Blocking patch // Blocking patch
handler(new PacketHandler() { handler(new PacketHandler() {
@Override @Override
public void handle(PacketWrapper wrapper) throws Exception { public void handle(PacketWrapper wrapper) throws Exception {
boolean showShieldWhenSwordInHand = Via.getConfig().isShowShieldWhenSwordInHand()
&& Via.getConfig().isShieldBlocking();
EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class); EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class);
if (entityTracker.isBlocking()) { if (entityTracker.isBlocking()) {
entityTracker.setBlocking(false); entityTracker.setBlocking(false);
entityTracker.setSecondHand(null);
if (!showShieldWhenSwordInHand) {
entityTracker.setSecondHand(null);
}
}
if (showShieldWhenSwordInHand) {
// Update current held item slot index
entityTracker.setHeldItemSlot(wrapper.get(Type.SHORT, 0));
// Sync shield item in offhand with main hand
entityTracker.syncShieldWithSword();
} }
} }
}); });

View File

@ -498,7 +498,9 @@ public class PlayerPackets {
// cancel any blocking >.> // cancel any blocking >.>
EntityTracker1_9 tracker = wrapper.user().get(EntityTracker1_9.class); EntityTracker1_9 tracker = wrapper.user().get(EntityTracker1_9.class);
if (tracker.isBlocking()) { if (tracker.isBlocking()) {
tracker.setSecondHand(null); if(!Via.getConfig().isShowShieldWhenSwordInHand()) {
tracker.setSecondHand(null);
}
tracker.setBlocking(false); tracker.setBlocking(false);
} }
} }

View File

@ -269,7 +269,9 @@ public class WorldPackets {
EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class); EntityTracker1_9 entityTracker = wrapper.user().get(EntityTracker1_9.class);
if (entityTracker.isBlocking()) { if (entityTracker.isBlocking()) {
entityTracker.setBlocking(false); entityTracker.setBlocking(false);
entityTracker.setSecondHand(null); if (!Via.getConfig().isShowShieldWhenSwordInHand()) {
entityTracker.setSecondHand(null);
}
} }
} }
} }
@ -296,23 +298,36 @@ public class WorldPackets {
if (Via.getConfig().isShieldBlocking()) { if (Via.getConfig().isShieldBlocking()) {
EntityTracker1_9 tracker = wrapper.user().get(EntityTracker1_9.class); EntityTracker1_9 tracker = wrapper.user().get(EntityTracker1_9.class);
// Check if the shield is already there or if we have to give it here
boolean showShieldWhenSwordInHand = Via.getConfig().isShowShieldWhenSwordInHand();
if (item != null && Protocol1_9To1_8.isSword(item.getIdentifier())) { if (item != null && Protocol1_9To1_8.isSword(item.getIdentifier())) {
if (hand == 0) { if (hand == 0) {
if (!tracker.isBlocking()) { if (!tracker.isBlocking()) {
tracker.setBlocking(true); tracker.setBlocking(true);
Item shield = new Item(442, (byte) 1, (short) 0, null);
tracker.setSecondHand(shield); // Check if the shield is already in the offhand
if (!showShieldWhenSwordInHand || tracker.getItemInSecondHand() == null) {
// Set shield in offhand when interacting with main hand
Item shield = new Item(442, (byte) 1, (short) 0, null);
tracker.setSecondHand(shield);
}
} }
} }
// Uses left or right hand to start blocking depending on the no delay setting // Use the main hand to trigger the blocking
boolean noDelayBlocking = Via.getConfig().isNoDelayShieldBlocking(); boolean blockUsingMainHand = Via.getConfig().isNoDelayShieldBlocking()
&& !showShieldWhenSwordInHand;
if (noDelayBlocking && hand == 1 || !noDelayBlocking && hand == 0) { if (blockUsingMainHand && hand == 1 || !blockUsingMainHand && hand == 0) {
wrapper.cancel(); wrapper.cancel();
} }
} else { } else {
tracker.setSecondHand(null); if (!showShieldWhenSwordInHand) {
// Remove the shield from the offhand
tracker.setSecondHand(null);
}
tracker.setBlocking(false); tracker.setBlocking(false);
} }
} }

View File

@ -66,6 +66,8 @@ public class EntityTracker1_9 extends EntityTracker {
private boolean teamExists = false; private boolean teamExists = false;
private GameMode gameMode; private GameMode gameMode;
private String currentTeam; private String currentTeam;
private int heldItemSlot;
private Item itemInSecondHand = null;
public EntityTracker1_9(UserConnection user) { public EntityTracker1_9(UserConnection user) {
super(user, EntityType.PLAYER); super(user, EntityType.PLAYER);
@ -89,7 +91,7 @@ public class EntityTracker1_9 extends EntityTracker {
PacketWrapper wrapper = new PacketWrapper(0x3C, null, getUser()); PacketWrapper wrapper = new PacketWrapper(0x3C, null, getUser());
wrapper.write(Type.VAR_INT, entityID); wrapper.write(Type.VAR_INT, entityID);
wrapper.write(Type.VAR_INT, 1); // slot wrapper.write(Type.VAR_INT, 1); // slot
wrapper.write(Type.ITEM, item); wrapper.write(Type.ITEM, this.itemInSecondHand = item);
try { try {
wrapper.send(Protocol1_9To1_8.class); wrapper.send(Protocol1_9To1_8.class);
} catch (Exception e) { } catch (Exception e) {
@ -97,6 +99,31 @@ public class EntityTracker1_9 extends EntityTracker {
} }
} }
public Item getItemInSecondHand() {
return itemInSecondHand;
}
/**
* It will set a shield to the offhand if a sword is in the main hand.
* The item in the offhand will be cleared if there is no sword in the main hand.
*/
public void syncShieldWithSword() {
InventoryTracker inventoryTracker = getUser().get(InventoryTracker.class);
// Get item in new selected slot
int inventorySlot = this.heldItemSlot + 36; // Hotbar slot index to inventory slot
int itemIdentifier = inventoryTracker.getItemId((short) 0, (short) inventorySlot);
boolean isSword = Protocol1_9To1_8.isSword(itemIdentifier);
// Update if the state changed
if (isSword == (this.itemInSecondHand == null)) {
// Update shield in off hand depending if a sword is in the main hand
setSecondHand(isSword ? new Item(442, (byte) 1, (short) 0, null) : null);
}
}
@Override @Override
public void removeEntity(int entityId) { public void removeEntity(int entityId) {
super.removeEntity(entityId); super.removeEntity(entityId);
@ -395,4 +422,8 @@ public class EntityTracker1_9 extends EntityTracker {
public void setCurrentTeam(String currentTeam) { public void setCurrentTeam(String currentTeam) {
this.currentTeam = currentTeam; this.currentTeam = currentTeam;
} }
public void setHeldItemSlot(int heldItemSlot) {
this.heldItemSlot = heldItemSlot;
}
} }

View File

@ -20,9 +20,16 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage;
import us.myles.ViaVersion.api.data.StoredObject; import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import java.util.HashMap;
import java.util.Map;
public class InventoryTracker extends StoredObject { public class InventoryTracker extends StoredObject {
private String inventory; private String inventory;
private final Map<Short, Map<Short, Integer>> windowItemCache = new HashMap<>();
private int itemIdInCursor = 0;
private boolean dragging = false;
public InventoryTracker(UserConnection user) { public InventoryTracker(UserConnection user) {
super(user); super(user);
} }
@ -34,4 +41,142 @@ public class InventoryTracker extends StoredObject {
public void setInventory(String inventory) { public void setInventory(String inventory) {
this.inventory = inventory; this.inventory = inventory;
} }
public void resetInventory(short windowId) {
// Reset the cursor state of the inventory
if (inventory == null) {
this.itemIdInCursor = 0;
this.dragging = false;
// Remove window from cache (Except players window)
if (windowId != 0) {
this.windowItemCache.remove(windowId);
}
}
}
public int getItemId(short windowId, short slot) {
Map<Short, Integer> itemMap = this.windowItemCache.get(windowId);
if (itemMap == null) {
return 0;
}
return itemMap.getOrDefault(slot, 0);
}
public void setItemId(short windowId, short slot, int itemId) {
if (windowId == -1 && slot == -1) {
// Set the cursor item
this.itemIdInCursor = itemId;
} else {
// Set item in normal inventory
this.windowItemCache.computeIfAbsent(windowId, k -> new HashMap<>()).put(slot, itemId);
}
}
/**
* Handle the window click to track the position of the sword
*
* @param windowId Id of the current inventory
* @param mode Inventory operation mode
* @param hoverSlot The slot number of the current mouse position
* @param button The button to use in the click
*/
public void handleWindowClick(short windowId, byte mode, short hoverSlot, byte button) {
EntityTracker1_9 entityTracker = getUser().get(EntityTracker1_9.class);
// Skip inventory background clicks
if (hoverSlot == -1) {
return;
}
// Interaction with the offhand slot
if (hoverSlot == 45) {
entityTracker.setSecondHand(null); // Remove it so we know that we can update it on ITEM_USE
return;
}
// It is not possible to put a sword into the armor or crafting result slot
boolean isArmorOrResultSlot = hoverSlot >= 5 && hoverSlot <= 8 || hoverSlot == 0;
switch (mode) {
case 0: // Click on slot
// The cursor is empty, so we can put an item to it
if (this.itemIdInCursor == 0) {
// Move item to cursor
this.itemIdInCursor = getItemId(windowId, hoverSlot);
// Remove item in slot
setItemId(windowId, hoverSlot, 0);
} else {
// Dropping item
if (hoverSlot == -999) {
this.itemIdInCursor = 0;
} else if (!isArmorOrResultSlot) {
int previousItem = getItemId(windowId, hoverSlot);
// Place item in inventory
setItemId(windowId, hoverSlot, this.itemIdInCursor);
// Pick up the other item
this.itemIdInCursor = previousItem;
}
}
break;
case 2: // Move item using number keys
if (!isArmorOrResultSlot) {
short hotkeySlot = (short) (button + 36);
// Get items to swap
int sourceItem = getItemId(windowId, hoverSlot);
int destinationItem = getItemId(windowId, hotkeySlot);
// Swap
setItemId(windowId, hotkeySlot, sourceItem);
setItemId(windowId, hoverSlot, destinationItem);
}
break;
case 4: // Drop item
int hoverItem = getItemId(windowId, hoverSlot);
if (hoverItem != 0) {
setItemId(windowId, hoverSlot, 0);
}
break;
case 5: // Mouse dragging
switch (button) {
case 0: // Start left dragging
case 4: // Start right dragging
this.dragging = true;
break;
case 1: // Place item during left dragging
case 5: // Place item during right dragging
// Check dragging mode and item on cursor
if (this.dragging && this.itemIdInCursor != 0 && !isArmorOrResultSlot) {
int previousItem = getItemId(windowId, hoverSlot);
// Place item on cursor in hovering slot
setItemId(windowId, hoverSlot, this.itemIdInCursor);
// Pick up the other item
this.itemIdInCursor = previousItem;
}
break;
case 2: // Stop left dragging
case 6: // Stop right dragging
this.dragging = false;
break;
}
break;
default:
break;
}
// Update shield state in offhand
entityTracker.syncShieldWithSword();
}
} }

View File

@ -169,7 +169,12 @@ suppress-metadata-errors: false
shield-blocking: true shield-blocking: true
# If this setting is active, the main hand is used instead of the off hand to trigger the blocking of the player. # If this setting is active, the main hand is used instead of the off hand to trigger the blocking of the player.
# With the main hand the blocking starts way faster. # With the main hand the blocking starts way faster.
# (Requires "show-shield-when-sword-in-hand" to be disabled)
no-delay-shield-blocking: false no-delay-shield-blocking: false
# If this setting is active, the shield will appear immediately for 1.9+ when you hold a sword in your main hand.
# The shield disappears when you switch to another item.
# (Requires "shield-blocking" to be enabled)
show-shield-when-sword-in-hand: false
# Enable player tick simulation, this fixes eating, drinking, nether portals. # Enable player tick simulation, this fixes eating, drinking, nether portals.
simulate-pt: true simulate-pt: true
# Should we use nms player to simulate packets, (may fix anti-cheat issues) # Should we use nms player to simulate packets, (may fix anti-cheat issues)