499 lines
17 KiB
Java
499 lines
17 KiB
Java
package com.hypixel.hytale.protocol.packets.inventory;
|
|
|
|
import com.hypixel.hytale.protocol.InventorySection;
|
|
import com.hypixel.hytale.protocol.Packet;
|
|
import com.hypixel.hytale.protocol.SortType;
|
|
import com.hypixel.hytale.protocol.io.ValidationResult;
|
|
import io.netty.buffer.ByteBuf;
|
|
import java.util.Objects;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class UpdatePlayerInventory implements Packet {
|
|
public static final int PACKET_ID = 170;
|
|
public static final boolean IS_COMPRESSED = true;
|
|
public static final int NULLABLE_BIT_FIELD_SIZE = 1;
|
|
public static final int FIXED_BLOCK_SIZE = 2;
|
|
public static final int VARIABLE_FIELD_COUNT = 7;
|
|
public static final int VARIABLE_BLOCK_START = 30;
|
|
public static final int MAX_SIZE = 1677721600;
|
|
@Nullable
|
|
public InventorySection storage;
|
|
@Nullable
|
|
public InventorySection armor;
|
|
@Nullable
|
|
public InventorySection hotbar;
|
|
@Nullable
|
|
public InventorySection utility;
|
|
@Nullable
|
|
public InventorySection builderMaterial;
|
|
@Nullable
|
|
public InventorySection tools;
|
|
@Nullable
|
|
public InventorySection backpack;
|
|
@Nonnull
|
|
public SortType sortType = SortType.Name;
|
|
|
|
@Override
|
|
public int getId() {
|
|
return 170;
|
|
}
|
|
|
|
public UpdatePlayerInventory() {
|
|
}
|
|
|
|
public UpdatePlayerInventory(
|
|
@Nullable InventorySection storage,
|
|
@Nullable InventorySection armor,
|
|
@Nullable InventorySection hotbar,
|
|
@Nullable InventorySection utility,
|
|
@Nullable InventorySection builderMaterial,
|
|
@Nullable InventorySection tools,
|
|
@Nullable InventorySection backpack,
|
|
@Nonnull SortType sortType
|
|
) {
|
|
this.storage = storage;
|
|
this.armor = armor;
|
|
this.hotbar = hotbar;
|
|
this.utility = utility;
|
|
this.builderMaterial = builderMaterial;
|
|
this.tools = tools;
|
|
this.backpack = backpack;
|
|
this.sortType = sortType;
|
|
}
|
|
|
|
public UpdatePlayerInventory(@Nonnull UpdatePlayerInventory other) {
|
|
this.storage = other.storage;
|
|
this.armor = other.armor;
|
|
this.hotbar = other.hotbar;
|
|
this.utility = other.utility;
|
|
this.builderMaterial = other.builderMaterial;
|
|
this.tools = other.tools;
|
|
this.backpack = other.backpack;
|
|
this.sortType = other.sortType;
|
|
}
|
|
|
|
@Nonnull
|
|
public static UpdatePlayerInventory deserialize(@Nonnull ByteBuf buf, int offset) {
|
|
UpdatePlayerInventory obj = new UpdatePlayerInventory();
|
|
byte nullBits = buf.getByte(offset);
|
|
obj.sortType = SortType.fromValue(buf.getByte(offset + 1));
|
|
if ((nullBits & 1) != 0) {
|
|
int varPos0 = offset + 30 + buf.getIntLE(offset + 2);
|
|
obj.storage = InventorySection.deserialize(buf, varPos0);
|
|
}
|
|
|
|
if ((nullBits & 2) != 0) {
|
|
int varPos1 = offset + 30 + buf.getIntLE(offset + 6);
|
|
obj.armor = InventorySection.deserialize(buf, varPos1);
|
|
}
|
|
|
|
if ((nullBits & 4) != 0) {
|
|
int varPos2 = offset + 30 + buf.getIntLE(offset + 10);
|
|
obj.hotbar = InventorySection.deserialize(buf, varPos2);
|
|
}
|
|
|
|
if ((nullBits & 8) != 0) {
|
|
int varPos3 = offset + 30 + buf.getIntLE(offset + 14);
|
|
obj.utility = InventorySection.deserialize(buf, varPos3);
|
|
}
|
|
|
|
if ((nullBits & 16) != 0) {
|
|
int varPos4 = offset + 30 + buf.getIntLE(offset + 18);
|
|
obj.builderMaterial = InventorySection.deserialize(buf, varPos4);
|
|
}
|
|
|
|
if ((nullBits & 32) != 0) {
|
|
int varPos5 = offset + 30 + buf.getIntLE(offset + 22);
|
|
obj.tools = InventorySection.deserialize(buf, varPos5);
|
|
}
|
|
|
|
if ((nullBits & 64) != 0) {
|
|
int varPos6 = offset + 30 + buf.getIntLE(offset + 26);
|
|
obj.backpack = InventorySection.deserialize(buf, varPos6);
|
|
}
|
|
|
|
return obj;
|
|
}
|
|
|
|
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
|
|
byte nullBits = buf.getByte(offset);
|
|
int maxEnd = 30;
|
|
if ((nullBits & 1) != 0) {
|
|
int fieldOffset0 = buf.getIntLE(offset + 2);
|
|
int pos0 = offset + 30 + fieldOffset0;
|
|
pos0 += InventorySection.computeBytesConsumed(buf, pos0);
|
|
if (pos0 - offset > maxEnd) {
|
|
maxEnd = pos0 - offset;
|
|
}
|
|
}
|
|
|
|
if ((nullBits & 2) != 0) {
|
|
int fieldOffset1 = buf.getIntLE(offset + 6);
|
|
int pos1 = offset + 30 + fieldOffset1;
|
|
pos1 += InventorySection.computeBytesConsumed(buf, pos1);
|
|
if (pos1 - offset > maxEnd) {
|
|
maxEnd = pos1 - offset;
|
|
}
|
|
}
|
|
|
|
if ((nullBits & 4) != 0) {
|
|
int fieldOffset2 = buf.getIntLE(offset + 10);
|
|
int pos2 = offset + 30 + fieldOffset2;
|
|
pos2 += InventorySection.computeBytesConsumed(buf, pos2);
|
|
if (pos2 - offset > maxEnd) {
|
|
maxEnd = pos2 - offset;
|
|
}
|
|
}
|
|
|
|
if ((nullBits & 8) != 0) {
|
|
int fieldOffset3 = buf.getIntLE(offset + 14);
|
|
int pos3 = offset + 30 + fieldOffset3;
|
|
pos3 += InventorySection.computeBytesConsumed(buf, pos3);
|
|
if (pos3 - offset > maxEnd) {
|
|
maxEnd = pos3 - offset;
|
|
}
|
|
}
|
|
|
|
if ((nullBits & 16) != 0) {
|
|
int fieldOffset4 = buf.getIntLE(offset + 18);
|
|
int pos4 = offset + 30 + fieldOffset4;
|
|
pos4 += InventorySection.computeBytesConsumed(buf, pos4);
|
|
if (pos4 - offset > maxEnd) {
|
|
maxEnd = pos4 - offset;
|
|
}
|
|
}
|
|
|
|
if ((nullBits & 32) != 0) {
|
|
int fieldOffset5 = buf.getIntLE(offset + 22);
|
|
int pos5 = offset + 30 + fieldOffset5;
|
|
pos5 += InventorySection.computeBytesConsumed(buf, pos5);
|
|
if (pos5 - offset > maxEnd) {
|
|
maxEnd = pos5 - offset;
|
|
}
|
|
}
|
|
|
|
if ((nullBits & 64) != 0) {
|
|
int fieldOffset6 = buf.getIntLE(offset + 26);
|
|
int pos6 = offset + 30 + fieldOffset6;
|
|
pos6 += InventorySection.computeBytesConsumed(buf, pos6);
|
|
if (pos6 - offset > maxEnd) {
|
|
maxEnd = pos6 - offset;
|
|
}
|
|
}
|
|
|
|
return maxEnd;
|
|
}
|
|
|
|
@Override
|
|
public void serialize(@Nonnull ByteBuf buf) {
|
|
int startPos = buf.writerIndex();
|
|
byte nullBits = 0;
|
|
if (this.storage != null) {
|
|
nullBits = (byte)(nullBits | 1);
|
|
}
|
|
|
|
if (this.armor != null) {
|
|
nullBits = (byte)(nullBits | 2);
|
|
}
|
|
|
|
if (this.hotbar != null) {
|
|
nullBits = (byte)(nullBits | 4);
|
|
}
|
|
|
|
if (this.utility != null) {
|
|
nullBits = (byte)(nullBits | 8);
|
|
}
|
|
|
|
if (this.builderMaterial != null) {
|
|
nullBits = (byte)(nullBits | 16);
|
|
}
|
|
|
|
if (this.tools != null) {
|
|
nullBits = (byte)(nullBits | 32);
|
|
}
|
|
|
|
if (this.backpack != null) {
|
|
nullBits = (byte)(nullBits | 64);
|
|
}
|
|
|
|
buf.writeByte(nullBits);
|
|
buf.writeByte(this.sortType.getValue());
|
|
int storageOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int armorOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int hotbarOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int utilityOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int builderMaterialOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int toolsOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int backpackOffsetSlot = buf.writerIndex();
|
|
buf.writeIntLE(0);
|
|
int varBlockStart = buf.writerIndex();
|
|
if (this.storage != null) {
|
|
buf.setIntLE(storageOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.storage.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(storageOffsetSlot, -1);
|
|
}
|
|
|
|
if (this.armor != null) {
|
|
buf.setIntLE(armorOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.armor.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(armorOffsetSlot, -1);
|
|
}
|
|
|
|
if (this.hotbar != null) {
|
|
buf.setIntLE(hotbarOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.hotbar.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(hotbarOffsetSlot, -1);
|
|
}
|
|
|
|
if (this.utility != null) {
|
|
buf.setIntLE(utilityOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.utility.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(utilityOffsetSlot, -1);
|
|
}
|
|
|
|
if (this.builderMaterial != null) {
|
|
buf.setIntLE(builderMaterialOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.builderMaterial.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(builderMaterialOffsetSlot, -1);
|
|
}
|
|
|
|
if (this.tools != null) {
|
|
buf.setIntLE(toolsOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.tools.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(toolsOffsetSlot, -1);
|
|
}
|
|
|
|
if (this.backpack != null) {
|
|
buf.setIntLE(backpackOffsetSlot, buf.writerIndex() - varBlockStart);
|
|
this.backpack.serialize(buf);
|
|
} else {
|
|
buf.setIntLE(backpackOffsetSlot, -1);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int computeSize() {
|
|
int size = 30;
|
|
if (this.storage != null) {
|
|
size += this.storage.computeSize();
|
|
}
|
|
|
|
if (this.armor != null) {
|
|
size += this.armor.computeSize();
|
|
}
|
|
|
|
if (this.hotbar != null) {
|
|
size += this.hotbar.computeSize();
|
|
}
|
|
|
|
if (this.utility != null) {
|
|
size += this.utility.computeSize();
|
|
}
|
|
|
|
if (this.builderMaterial != null) {
|
|
size += this.builderMaterial.computeSize();
|
|
}
|
|
|
|
if (this.tools != null) {
|
|
size += this.tools.computeSize();
|
|
}
|
|
|
|
if (this.backpack != null) {
|
|
size += this.backpack.computeSize();
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
|
|
if (buffer.readableBytes() - offset < 30) {
|
|
return ValidationResult.error("Buffer too small: expected at least 30 bytes");
|
|
} else {
|
|
byte nullBits = buffer.getByte(offset);
|
|
if ((nullBits & 1) != 0) {
|
|
int storageOffset = buffer.getIntLE(offset + 2);
|
|
if (storageOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for Storage");
|
|
}
|
|
|
|
int pos = offset + 30 + storageOffset;
|
|
if (pos >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for Storage");
|
|
}
|
|
|
|
ValidationResult storageResult = InventorySection.validateStructure(buffer, pos);
|
|
if (!storageResult.isValid()) {
|
|
return ValidationResult.error("Invalid Storage: " + storageResult.error());
|
|
}
|
|
|
|
pos += InventorySection.computeBytesConsumed(buffer, pos);
|
|
}
|
|
|
|
if ((nullBits & 2) != 0) {
|
|
int armorOffset = buffer.getIntLE(offset + 6);
|
|
if (armorOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for Armor");
|
|
}
|
|
|
|
int posx = offset + 30 + armorOffset;
|
|
if (posx >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for Armor");
|
|
}
|
|
|
|
ValidationResult armorResult = InventorySection.validateStructure(buffer, posx);
|
|
if (!armorResult.isValid()) {
|
|
return ValidationResult.error("Invalid Armor: " + armorResult.error());
|
|
}
|
|
|
|
posx += InventorySection.computeBytesConsumed(buffer, posx);
|
|
}
|
|
|
|
if ((nullBits & 4) != 0) {
|
|
int hotbarOffset = buffer.getIntLE(offset + 10);
|
|
if (hotbarOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for Hotbar");
|
|
}
|
|
|
|
int posxx = offset + 30 + hotbarOffset;
|
|
if (posxx >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for Hotbar");
|
|
}
|
|
|
|
ValidationResult hotbarResult = InventorySection.validateStructure(buffer, posxx);
|
|
if (!hotbarResult.isValid()) {
|
|
return ValidationResult.error("Invalid Hotbar: " + hotbarResult.error());
|
|
}
|
|
|
|
posxx += InventorySection.computeBytesConsumed(buffer, posxx);
|
|
}
|
|
|
|
if ((nullBits & 8) != 0) {
|
|
int utilityOffset = buffer.getIntLE(offset + 14);
|
|
if (utilityOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for Utility");
|
|
}
|
|
|
|
int posxxx = offset + 30 + utilityOffset;
|
|
if (posxxx >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for Utility");
|
|
}
|
|
|
|
ValidationResult utilityResult = InventorySection.validateStructure(buffer, posxxx);
|
|
if (!utilityResult.isValid()) {
|
|
return ValidationResult.error("Invalid Utility: " + utilityResult.error());
|
|
}
|
|
|
|
posxxx += InventorySection.computeBytesConsumed(buffer, posxxx);
|
|
}
|
|
|
|
if ((nullBits & 16) != 0) {
|
|
int builderMaterialOffset = buffer.getIntLE(offset + 18);
|
|
if (builderMaterialOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for BuilderMaterial");
|
|
}
|
|
|
|
int posxxxx = offset + 30 + builderMaterialOffset;
|
|
if (posxxxx >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for BuilderMaterial");
|
|
}
|
|
|
|
ValidationResult builderMaterialResult = InventorySection.validateStructure(buffer, posxxxx);
|
|
if (!builderMaterialResult.isValid()) {
|
|
return ValidationResult.error("Invalid BuilderMaterial: " + builderMaterialResult.error());
|
|
}
|
|
|
|
posxxxx += InventorySection.computeBytesConsumed(buffer, posxxxx);
|
|
}
|
|
|
|
if ((nullBits & 32) != 0) {
|
|
int toolsOffset = buffer.getIntLE(offset + 22);
|
|
if (toolsOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for Tools");
|
|
}
|
|
|
|
int posxxxxx = offset + 30 + toolsOffset;
|
|
if (posxxxxx >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for Tools");
|
|
}
|
|
|
|
ValidationResult toolsResult = InventorySection.validateStructure(buffer, posxxxxx);
|
|
if (!toolsResult.isValid()) {
|
|
return ValidationResult.error("Invalid Tools: " + toolsResult.error());
|
|
}
|
|
|
|
posxxxxx += InventorySection.computeBytesConsumed(buffer, posxxxxx);
|
|
}
|
|
|
|
if ((nullBits & 64) != 0) {
|
|
int backpackOffset = buffer.getIntLE(offset + 26);
|
|
if (backpackOffset < 0) {
|
|
return ValidationResult.error("Invalid offset for Backpack");
|
|
}
|
|
|
|
int posxxxxxx = offset + 30 + backpackOffset;
|
|
if (posxxxxxx >= buffer.writerIndex()) {
|
|
return ValidationResult.error("Offset out of bounds for Backpack");
|
|
}
|
|
|
|
ValidationResult backpackResult = InventorySection.validateStructure(buffer, posxxxxxx);
|
|
if (!backpackResult.isValid()) {
|
|
return ValidationResult.error("Invalid Backpack: " + backpackResult.error());
|
|
}
|
|
|
|
posxxxxxx += InventorySection.computeBytesConsumed(buffer, posxxxxxx);
|
|
}
|
|
|
|
return ValidationResult.OK;
|
|
}
|
|
}
|
|
|
|
public UpdatePlayerInventory clone() {
|
|
UpdatePlayerInventory copy = new UpdatePlayerInventory();
|
|
copy.storage = this.storage != null ? this.storage.clone() : null;
|
|
copy.armor = this.armor != null ? this.armor.clone() : null;
|
|
copy.hotbar = this.hotbar != null ? this.hotbar.clone() : null;
|
|
copy.utility = this.utility != null ? this.utility.clone() : null;
|
|
copy.builderMaterial = this.builderMaterial != null ? this.builderMaterial.clone() : null;
|
|
copy.tools = this.tools != null ? this.tools.clone() : null;
|
|
copy.backpack = this.backpack != null ? this.backpack.clone() : null;
|
|
copy.sortType = this.sortType;
|
|
return copy;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) {
|
|
return true;
|
|
} else {
|
|
return !(obj instanceof UpdatePlayerInventory other)
|
|
? false
|
|
: Objects.equals(this.storage, other.storage)
|
|
&& Objects.equals(this.armor, other.armor)
|
|
&& Objects.equals(this.hotbar, other.hotbar)
|
|
&& Objects.equals(this.utility, other.utility)
|
|
&& Objects.equals(this.builderMaterial, other.builderMaterial)
|
|
&& Objects.equals(this.tools, other.tools)
|
|
&& Objects.equals(this.backpack, other.backpack)
|
|
&& Objects.equals(this.sortType, other.sortType);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return Objects.hash(this.storage, this.armor, this.hotbar, this.utility, this.builderMaterial, this.tools, this.backpack, this.sortType);
|
|
}
|
|
}
|