|
43 | 43 | import com.viaversion.viaversion.api.minecraft.data.StructuredDataContainer; |
44 | 44 | import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey; |
45 | 45 | import com.viaversion.viaversion.api.minecraft.item.Item; |
| 46 | +import com.viaversion.viaversion.api.minecraft.item.StructuredItem; |
46 | 47 | import com.viaversion.viaversion.api.minecraft.item.data.Consumable1_21_2; |
47 | 48 | import com.viaversion.viaversion.api.minecraft.item.data.DeathProtection; |
48 | 49 | import com.viaversion.viaversion.api.minecraft.item.data.Enchantable; |
@@ -256,6 +257,33 @@ public void registerPackets() { |
256 | 257 | } |
257 | 258 | wrapper.write(itemType(), handleItemToServer(wrapper.user(), wrapper.read(mappedItemType()))); |
258 | 259 | }); |
| 260 | + protocol.replaceServerbound(ServerboundPackets1_20_5.SET_CREATIVE_MODE_SLOT, wrapper -> { |
| 261 | + if (protocol.getEntityRewriter() != null && !protocol.getEntityRewriter().tracker(wrapper.user()).canInstaBuild()) { |
| 262 | + wrapper.cancel(); |
| 263 | + return; |
| 264 | + } |
| 265 | + |
| 266 | + final short slot = wrapper.passthrough(Types.SHORT); |
| 267 | + final Item item = wrapper.read(mappedItemType()); |
| 268 | + |
| 269 | + // Clearing the inventory via the destroy item slot is the only creative action old clients don't |
| 270 | + // apply locally, but 1.21.2+ servers do not send back the empty contents. |
| 271 | + // Detect the action by checking for linear slot clears and leave other actions alone. |
| 272 | + final InventoryStateIdStorage storage = wrapper.user().get(InventoryStateIdStorage.class); |
| 273 | + final boolean empty = Item.isEmpty(item); |
| 274 | + if (empty && slot == storage.nextClearedSlot()) { |
| 275 | + if (slot == 45) { |
| 276 | + storage.setNextClearedSlot(0); |
| 277 | + sendEmptyInventorySlots(wrapper.user()); |
| 278 | + } else { |
| 279 | + storage.setNextClearedSlot(slot + 1); |
| 280 | + } |
| 281 | + } else { |
| 282 | + storage.setNextClearedSlot(empty && slot == 0 ? 1 : 0); |
| 283 | + } |
| 284 | + |
| 285 | + wrapper.write(itemType(), handleItemToServer(wrapper.user(), item)); |
| 286 | + }); |
259 | 287 |
|
260 | 288 | protocol.registerServerbound(ServerboundPackets1_20_5.USE_ITEM_ON, wrapper -> { |
261 | 289 | wrapper.passthrough(Types.VAR_INT); // Hand |
@@ -367,6 +395,18 @@ public void registerPackets() { |
367 | 395 | }); |
368 | 396 | } |
369 | 397 |
|
| 398 | + private void sendEmptyInventorySlots(final UserConnection connection) { |
| 399 | + final int stateId = connection.get(InventoryStateIdStorage.class).stateId(); |
| 400 | + for (int slot = 1; slot <= 45; slot++) { |
| 401 | + final PacketWrapper setSlotPacket = PacketWrapper.create(ClientboundPackets1_21.CONTAINER_SET_SLOT, connection); |
| 402 | + setSlotPacket.write(Types.BYTE, (byte) 0); // Player inventory |
| 403 | + setSlotPacket.write(Types.VAR_INT, stateId); // State id; re-use the last known one |
| 404 | + setSlotPacket.write(Types.SHORT, (short) slot); |
| 405 | + setSlotPacket.write(mappedItemType(), StructuredItem.empty()); |
| 406 | + setSlotPacket.send(Protocol1_21_2To1_21.class); |
| 407 | + } |
| 408 | + } |
| 409 | + |
370 | 410 | private void varIntToUnsignedByte(final PacketWrapper wrapper) { |
371 | 411 | final int containerId = wrapper.read(Types.VAR_INT); |
372 | 412 | wrapper.write(Types.UNSIGNED_BYTE, (short) containerId); |
|
0 commit comments