diff --git a/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpack.java b/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpack.java index a4dda36..fd11ede 100644 --- a/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpack.java +++ b/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpack.java @@ -1,9 +1,16 @@ package net.imprex.zip.api; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import java.util.UUID; + import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import net.imprex.zip.api.ZIPBackpackHistory.HistoryConsumer; + public interface ZIPBackpack { void save(); @@ -20,6 +27,22 @@ public interface ZIPBackpack { boolean giveUnsueableContent(Player player); + List getHistroy(); + + void computeHistory(UUID uuid, OffsetDateTime dateTime, HistoryConsumer consumer); + + default void computeHistory(UUID uuid, HistoryConsumer consumer) { + this.computeHistory(uuid, OffsetDateTime.now(), consumer); + } + + void addHistory(UUID uuid, OffsetDateTime dateTime, Map items); + + default void addHistory(UUID uuid, Map items) { + this.addHistory(uuid, OffsetDateTime.now(), items); + } + + void clearHistory(); + boolean isValid(); Inventory getInventory(); diff --git a/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpackHistory.java b/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpackHistory.java new file mode 100644 index 0000000..20ad276 --- /dev/null +++ b/zip-api/src/main/java/net/imprex/zip/api/ZIPBackpackHistory.java @@ -0,0 +1,21 @@ +package net.imprex.zip.api; + +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.inventory.ItemStack; + +public interface ZIPBackpackHistory { + + UUID player(); + + OffsetDateTime dateTime(); + + Map items(); + + public static interface HistoryConsumer { + + void consume(); + } +} \ No newline at end of file diff --git a/zip-common/src/main/java/net/imprex/zip/common/Ingrim4Buffer.java b/zip-common/src/main/java/net/imprex/zip/common/Ingrim4Buffer.java index 6ffb81c..d2d099e 100644 --- a/zip-common/src/main/java/net/imprex/zip/common/Ingrim4Buffer.java +++ b/zip-common/src/main/java/net/imprex/zip/common/Ingrim4Buffer.java @@ -10,6 +10,10 @@ import java.nio.channels.ScatteringByteChannel; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.UUID; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; @@ -99,6 +103,23 @@ public void writeString(String value) { this.buf.writeBytes(bytes); } + public UUID readUUID() { + return new UUID(this.buf.readLong(), this.buf.readLong()); + } + + public void writeUUID(UUID uuid) { + this.buf.writeLong(uuid.getMostSignificantBits()); + this.buf.writeLong(uuid.getLeastSignificantBits()); + } + + public OffsetDateTime readDateTime() { + return OffsetDateTime.ofInstant(Instant.ofEpochSecond(this.buf.readLong()), ZoneOffset.UTC); + } + + public void writeDateTime(OffsetDateTime dateTime) { + this.buf.writeLong(dateTime.toEpochSecond()); + } + /** * Reads a byte array which got write byte {@link Ingrim4Buffer#writeByteArray(byte[])} * @return byte[] diff --git a/zip-common/src/main/java/net/imprex/zip/common/UniqueId.java b/zip-common/src/main/java/net/imprex/zip/common/UniqueId.java index a843d96..9b20ee5 100644 --- a/zip-common/src/main/java/net/imprex/zip/common/UniqueId.java +++ b/zip-common/src/main/java/net/imprex/zip/common/UniqueId.java @@ -1,3 +1,20 @@ +/* + * Copyright © 2014 - 2021 Leipzig University (Database Research Group) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @see https://github.com/dbs-leipzig/gradoop/blob/develop/gradoop-common/src/main/java/org/gradoop/common/model/impl/id/GradoopId.java + */ package net.imprex.zip.common; import java.net.NetworkInterface; @@ -11,11 +28,6 @@ import net.imprex.zip.api.ZIPUniqueId; -/** - * - * @author dbs-leipzig - * @see https://github.com/dbs-leipzig/gradoop/blob/develop/gradoop-common/src/main/java/org/gradoop/common/model/impl/id/ScriptedItemId.java - */ public class UniqueId implements Comparable, ZIPUniqueId { /** diff --git a/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java index 7bcaa4e..f7921f4 100644 --- a/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java +++ b/zip-nms/zip-nms-api/src/main/java/net/imprex/zip/nms/api/NmsManager.java @@ -8,9 +8,13 @@ public interface NmsManager { - byte[] itemstackToBinary(ItemStack[] items); + byte[] itemstackArrayToBinary(ItemStack[] items); - List binaryToItemStack(byte[] binary); + List binaryToItemStackArray(byte[] binary); + + byte[] itemstackToBinary(ItemStack item); + + ItemStack binaryToItemStack(byte[] binary); void setSkullProfile(SkullMeta meta, String texture); diff --git a/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java b/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java index 2e1ebd5..71a7445 100644 --- a/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_19_R1/src/main/java/net/imprex/zip/nms/v1_19_R1/ZipNmsManager.java @@ -48,7 +48,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -60,7 +60,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -74,6 +74,19 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = craftItem.save(new CompoundTag()); + return nbtToBinary(tag); + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + return CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(nbt)); + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java b/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java index 4b80fb1..97d64c7 100644 --- a/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_19_R2/src/main/java/net/imprex/zip/nms/v1_19_R2/ZipNmsManager.java @@ -48,7 +48,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -60,7 +60,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -74,6 +74,19 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = craftItem.save(new CompoundTag()); + return nbtToBinary(tag); + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + return CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(nbt)); + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java b/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java index 33b2dd1..b7bb358 100644 --- a/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_19_R3/src/main/java/net/imprex/zip/nms/v1_19_R3/ZipNmsManager.java @@ -48,7 +48,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -60,7 +60,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -74,6 +74,19 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = craftItem.save(new CompoundTag()); + return nbtToBinary(tag); + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + return CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(nbt)); + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java index ecc1f92..1dacc5d 100644 --- a/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_20_R1/src/main/java/net/imprex/zip/nms/v1_20_R1/ZipNmsManager.java @@ -48,7 +48,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -60,7 +60,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -74,6 +74,19 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = craftItem.save(new CompoundTag()); + return nbtToBinary(tag); + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + return CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(nbt)); + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java index aa53f74..8825caa 100644 --- a/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_20_R2/src/main/java/net/imprex/zip/nms/v1_20_R2/ZipNmsManager.java @@ -48,7 +48,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -60,7 +60,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -74,6 +74,19 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = craftItem.save(new CompoundTag()); + return nbtToBinary(tag); + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + return CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(nbt)); + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java index 3cab8ee..4792c48 100644 --- a/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_20_R3/src/main/java/net/imprex/zip/nms/v1_20_R3/ZipNmsManager.java @@ -49,7 +49,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -61,7 +61,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -75,6 +75,19 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = craftItem.save(new CompoundTag()); + return nbtToBinary(tag); + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + return CraftItemStack.asBukkitCopy(net.minecraft.world.item.ItemStack.of(nbt)); + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java b/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java index 73b2f24..33b62ed 100644 --- a/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_20_R4/src/main/java/net/imprex/zip/nms/v1_20_R4/ZipNmsManager.java @@ -60,7 +60,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -77,7 +77,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -98,6 +98,33 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return nbtToBinary(NBT_EMPTY_ITEMSTACK); + } else { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = (CompoundTag) craftItem.save(DEFAULT_REGISTRY); + return nbtToBinary(tag); + } + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + + if (nbt.getString("id").equals("minecraft:air")) { + return new ItemStack(Material.AIR); + } else { + Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, nbt); + if (optional.isPresent()) { + return CraftItemStack.asBukkitCopy(optional.get()); + } + } + + return null; + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java index 36248a6..03df67f 100644 --- a/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_21_R1/src/main/java/net/imprex/zip/nms/v1_21_R1/ZipNmsManager.java @@ -93,7 +93,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -110,7 +110,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -131,6 +131,33 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return nbtToBinary(NBT_EMPTY_ITEMSTACK); + } else { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = (CompoundTag) craftItem.save(DEFAULT_REGISTRY); + return nbtToBinary(tag); + } + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + + if (nbt.getString("id").equals("minecraft:air")) { + return new ItemStack(Material.AIR); + } else { + Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, nbt); + if (optional.isPresent()) { + return CraftItemStack.asBukkitCopy(optional.get()); + } + } + + return null; + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java index bd63f72..0dacc76 100644 --- a/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_21_R2/src/main/java/net/imprex/zip/nms/v1_21_R2/ZipNmsManager.java @@ -93,7 +93,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -110,7 +110,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -131,6 +131,33 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return nbtToBinary(NBT_EMPTY_ITEMSTACK); + } else { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = (CompoundTag) craftItem.save(DEFAULT_REGISTRY); + return nbtToBinary(tag); + } + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + + if (nbt.getString("id").equals("minecraft:air")) { + return new ItemStack(Material.AIR); + } else { + Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, nbt); + if (optional.isPresent()) { + return CraftItemStack.asBukkitCopy(optional.get()); + } + } + + return null; + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java b/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java index 7c2a710..d2a0bab 100644 --- a/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java +++ b/zip-nms/zip-nms-v1_21_R3/src/main/java/net/imprex/zip/nms/v1_21_R3/ZipNmsManager.java @@ -93,7 +93,7 @@ public CompoundTag binaryToNBT(byte[] binary) { } @Override - public byte[] itemstackToBinary(ItemStack[] items) { + public byte[] itemstackArrayToBinary(ItemStack[] items) { CompoundTag inventory = new CompoundTag(); ListTag list = new ListTag(); for (ItemStack itemStack : items) { @@ -110,7 +110,7 @@ public byte[] itemstackToBinary(ItemStack[] items) { } @Override - public List binaryToItemStack(byte[] binary) { + public List binaryToItemStackArray(byte[] binary) { CompoundTag nbt = binaryToNBT(binary); List items = new ArrayList<>(); if (nbt.contains("i", 9)) { @@ -131,6 +131,33 @@ public List binaryToItemStack(byte[] binary) { return items; } + @Override + public byte[] itemstackToBinary(ItemStack item) { + if (item == null || item.getType() == Material.AIR) { + return nbtToBinary(NBT_EMPTY_ITEMSTACK); + } else { + net.minecraft.world.item.ItemStack craftItem = CraftItemStack.asNMSCopy(item); + CompoundTag tag = (CompoundTag) craftItem.save(DEFAULT_REGISTRY); + return nbtToBinary(tag); + } + } + + @Override + public ItemStack binaryToItemStack(byte[] binary) { + CompoundTag nbt = binaryToNBT(binary); + + if (nbt.getString("id").equals("minecraft:air")) { + return new ItemStack(Material.AIR); + } else { + Optional optional = net.minecraft.world.item.ItemStack.parse(DEFAULT_REGISTRY, nbt); + if (optional.isPresent()) { + return CraftItemStack.asBukkitCopy(optional.get()); + } + } + + return null; + } + @Override public void setSkullProfile(SkullMeta meta, String texture) { try { diff --git a/zip-plugin/src/main/java/net/imprex/zip/Backpack.java b/zip-plugin/src/main/java/net/imprex/zip/Backpack.java index 4a2c20f..995016b 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/Backpack.java +++ b/zip-plugin/src/main/java/net/imprex/zip/Backpack.java @@ -1,7 +1,12 @@ package net.imprex.zip; +import java.time.OffsetDateTime; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.UUID; +import java.util.WeakHashMap; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -13,14 +18,22 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; +import com.google.common.collect.Lists; + import net.imprex.zip.api.ZIPBackpack; +import net.imprex.zip.api.ZIPBackpackHistory; +import net.imprex.zip.api.ZIPBackpackHistory.HistoryConsumer; import net.imprex.zip.common.Ingrim4Buffer; import net.imprex.zip.common.UniqueId; import net.imprex.zip.config.MessageConfig; import net.imprex.zip.config.MessageKey; +import net.imprex.zip.migration.BackpackMigrator; +import net.imprex.zip.util.FixedSizeQueue; public class Backpack implements ZIPBackpack { + public static final int VERSION = 100; + private final BackpackHandler backpackHandler; private final MessageConfig messageConfig; @@ -32,9 +45,13 @@ public class Backpack implements ZIPBackpack { private final String typeRaw; private final BackpackType type; + private final FixedSizeQueue history; + private ItemStack[] content; private Inventory inventory; + private final Map transfer = new WeakHashMap<>(); + public Backpack(BackpackPlugin plugin, BackpackType type, UniqueId id) { this.backpackHandler = plugin.getBackpackHandler(); this.messageConfig = plugin.getBackpackConfig().message(); @@ -50,6 +67,8 @@ public Backpack(BackpackPlugin plugin, BackpackType type, UniqueId id) { this.inventory = Bukkit.createInventory(null, 9 * rows, displayName); this.content = this.inventory.getContents(); + this.history = new FixedSizeQueue<>(plugin.getBackpackConfig().general().historySize); + this.backpackHandler.registerBackpack(this); this.save(); @@ -61,19 +80,33 @@ public Backpack(BackpackPlugin plugin, UniqueId id, Ingrim4Buffer buffer) { this.identifierKey = plugin.getBackpackIdentifierKey(); this.storageKey = plugin.getBackpackStorageKey(); - /* - * Load backpack id from buffer but don't use it! - * Just for later migration to SQL - */ - buffer.readByteArray(); - this.id = id; + try { + // pooled buffer + buffer = BackpackMigrator.migrate(plugin, buffer); - this.typeRaw = buffer.readString(); - this.type = plugin.getBackpackRegistry().getTypeByName(this.typeRaw); + // read id + this.id = UniqueId.fromByteArray(buffer.readByteArray()); - byte[] contentAsByteArray = buffer.readByteArray(); - ItemStack[] content = NmsInstance.binaryToItemStack(contentAsByteArray).toArray(ItemStack[]::new); - this.content = content; + // read type + this.typeRaw = buffer.readString(); + this.type = plugin.getBackpackRegistry().getTypeByName(this.typeRaw); + + // read content + byte[] contentAsByteArray = buffer.readByteArray(); + ItemStack[] content = NmsInstance.binaryToItemStackArray(contentAsByteArray).toArray(ItemStack[]::new); + this.content = content; + + // read history + this.history = new FixedSizeQueue<>(plugin.getBackpackConfig().general().historySize); + int historySize = buffer.readInt(); + for (int i = 0; i < historySize; i++) { + this.history.add(BackpackHistory.read(buffer)); + } + } finally { + // release buffer + buffer.release(); + buffer = null; + } if (this.type != null) { String displayName = this.type.getDisplayName(); @@ -107,9 +140,17 @@ public void save(Ingrim4Buffer buffer) { throw new NullPointerException("content can not be null"); } + // write latest version + buffer.writeInt(VERSION); + + // write id, type and content buffer.writeByteArray(this.id.toByteArray()); buffer.writeString(this.typeRaw); - buffer.writeByteArray(NmsInstance.itemstackToBinary(this.content)); + buffer.writeByteArray(NmsInstance.itemstackArrayToBinary(this.content)); + + // write history + buffer.writeInt(this.history.size()); + this.history.forEach(history -> history.write(buffer)); } @Override @@ -203,9 +244,59 @@ public boolean giveUnsueableContent(Player player) { this.content[i] = null; } } + + BackpackHistory.create(player.getUniqueId(), content, content); + return empty; } + @Override + public List getHistroy() { + return Collections.unmodifiableList(Lists.newArrayList(this.history.collection())); + } + + @Override + public void computeHistory(UUID uuid, OffsetDateTime dateTime, HistoryConsumer consumer) { + ItemStack[] previous = this.content.clone(); + consumer.consume(); + + Map difference = BackpackHistory.difference(previous, this.content); + if (!difference.isEmpty()) { + this.history.add(new BackpackHistory(uuid, dateTime, difference)); + } + } + + @Override + public void addHistory(UUID uuid, OffsetDateTime dateTime, Map items) { + this.history.add(new BackpackHistory(uuid, dateTime, items)); + } + + @Override + public void clearHistory() { + this.history.clear(); + } + + public void addToTransfer(Player player) { + BackpackTransferPlayer previous = this.transfer.put(player, new BackpackTransferPlayer()); + if (!(previous == null || previous.isEmpty())) { + // close inventory was not triggered + this.addHistory(player.getUniqueId(), previous.getCreated(), previous.getItems()); + } + } + + public BackpackTransferPlayer getTransfer(Player player) { + return this.transfer.computeIfAbsent(player, target -> new BackpackTransferPlayer()); + } + + public void closeTransfer(Player player) { + BackpackTransferPlayer transfer = this.transfer.remove(player); + if (transfer == null || transfer.isEmpty()) { + return; + } + + this.addHistory(player.getUniqueId(), transfer.getItems()); + } + @Override public boolean isValid() { return this.inventory != null && this.type != null && this.content != null; diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java index 5dd7c2d..698e7a5 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java +++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackHandler.java @@ -19,6 +19,7 @@ import com.google.common.io.ByteStreams; +import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.Unpooled; import net.imprex.zip.api.ZIPBackpack; import net.imprex.zip.api.ZIPBackpackType; @@ -94,8 +95,8 @@ public void save(ZIPBackpack backpack) { } Path file = this.folderPath.resolve(backpack.getId().toString()); + Ingrim4Buffer buffer = new Ingrim4Buffer(PooledByteBufAllocator.DEFAULT.buffer()); try (FileOutputStream outputStream = new FileOutputStream(file.toFile())) { - Ingrim4Buffer buffer = new Ingrim4Buffer(Unpooled.buffer()); ((Backpack) backpack).save(buffer); byte[] bytes = new byte[buffer.readableBytes()]; @@ -103,6 +104,8 @@ public void save(ZIPBackpack backpack) { outputStream.write(bytes); } catch (IOException e) { e.printStackTrace(); + } finally { + buffer.release(); } } @@ -149,9 +152,7 @@ public Backpack getBackpack(ItemStack item) { uniqueId = UniqueId.fromByteArray(storageKey); Backpack backpack = this.getBackpack(uniqueId); - if (backpack != null) { - return backpack; - } + return backpack; } if (dataContainer.has(this.backpackIdentifierKey, PersistentDataType.STRING)) { diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackHistory.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackHistory.java new file mode 100644 index 0000000..1f4ebe9 --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackHistory.java @@ -0,0 +1,102 @@ +package net.imprex.zip; + +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import org.bukkit.inventory.ItemStack; + +import net.imprex.zip.api.ZIPBackpackHistory; +import net.imprex.zip.common.Ingrim4Buffer; + +public record BackpackHistory(UUID player, OffsetDateTime dateTime, Map items) implements ZIPBackpackHistory { + + public static BackpackHistory read(Ingrim4Buffer buffer) { + UUID player = buffer.readUUID(); + OffsetDateTime dateTime = buffer.readDateTime(); + + Map content = new HashMap<>(); + for (int i = 0; i < buffer.readInt(); i++) { + content.put( + NmsInstance.binaryToItemStack(buffer.readByteArray()), + buffer.readInt()); + } + + return new BackpackHistory(player, dateTime, content); + } + + public void write(Ingrim4Buffer buffer) { + buffer.writeUUID(this.player); + buffer.writeDateTime(this.dateTime); + + buffer.writeInt(this.items.size()); + for (Entry entry : this.items.entrySet()) { + buffer.writeByteArray(NmsInstance.itemstackToBinary(entry.getKey())); + buffer.writeInt(entry.getValue()); + } + } + + public static ZIPBackpackHistory create(UUID player, ItemStack[] previous, ItemStack[] newest) { + return new BackpackHistory(player, OffsetDateTime.now(), difference(previous, newest)); + } + + public static Map difference(ItemStack[] originalStack, ItemStack[] compareStack) { + Map originalList = merge(originalStack); + Map compareList = merge(compareStack); + Map diffList = new HashMap<>(); + + // check if amount changed or items were removed + check: for (Entry entry : originalList.entrySet()) { + ItemStack itemStack = entry.getKey(); + int originalAmount = entry.getValue(); + + for (Iterator> iterator = compareList.entrySet().iterator(); iterator.hasNext();) { + Entry compareEntry = iterator.next(); + // ignore not equals items + if (!itemStack.isSimilar(compareEntry.getKey())) { + continue; + } + + // we already handled this item, so we don't need to check it again + iterator.remove(); + + int compareAmount = compareEntry.getValue(); + // item amount is the same + if (originalAmount == compareAmount) { + continue check; + } + + // calculate item amount difference + int diffAmount = compareAmount - originalAmount; + diffList.put(itemStack, diffAmount); + continue check; + } + + // item was removed + diffList.put(itemStack, -originalAmount); + } + + // new added items + // we already check the existing items and compared them so only non previous existing items are remaining + diffList.putAll(compareList); + return diffList; + } + + public static Map merge(ItemStack[] itemStack) { + Map newStack = new HashMap<>(); + check: for (ItemStack checkStack : itemStack) { + for (Entry diffStack : newStack.entrySet()) { + if (diffStack.getKey().isSimilar(checkStack)) { + diffStack.setValue(diffStack.getValue() + checkStack.getAmount()); + continue check; + } + } + + newStack.put(checkStack.clone(), checkStack.getAmount()); + } + return newStack; + } +} \ No newline at end of file diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java index da489c0..b447006 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java +++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackListener.java @@ -3,6 +3,7 @@ import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockPlaceEvent; @@ -47,7 +48,7 @@ public void onPlayerJoin(PlayerJoinEvent event) { } @EventHandler(ignoreCancelled = false) - public void onInventoryClick(InventoryClickEvent event) { + public void onInventoryClickBackpack(InventoryClickEvent event) { ItemStack currentItem = event.getCurrentItem(); if (event.getClick() == ClickType.NUMBER_KEY) { int hotbarSlot = event.getHotbarButton(); @@ -55,7 +56,7 @@ public void onInventoryClick(InventoryClickEvent event) { currentItem = event.getWhoClicked().getInventory().getItem(hotbarSlot); } } - + if (!this.backpackHandler.isBackpack(currentItem)) { return; } @@ -71,12 +72,67 @@ public void onInventoryClick(InventoryClickEvent event) { } } + /** + * Handle priority events first and only log to history if not cancelled + * + * @param event + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onInventoryClickHistory(InventoryClickEvent event) { + Backpack backpack = this.backpackHandler.getBackpack(event.getClickedInventory()); + if (backpack == null) { + return; + } + + Player player = (Player) event.getWhoClicked(); + BackpackTransferPlayer transfer = backpack.getTransfer(player); + ItemStack currentItem = event.getCurrentItem(); + + switch (event.getAction()) { + case SWAP_WITH_CURSOR: + transfer.track(event.getCursor(), event.getCursor().getAmount()); + transfer.track(currentItem, -currentItem.getAmount()); + break; + + case HOTBAR_SWAP: + int hotbar = event.getHotbarButton(); + if (hotbar == -1) { + return; + } + + ItemStack hotbarItem = player.getInventory().getItem(hotbar); + transfer.track(hotbarItem, hotbarItem.getAmount()); + transfer.track(currentItem, -currentItem.getAmount()); + break; + + case PLACE_ALL: + case PLACE_SOME: + case PLACE_ONE: + transfer.track(event.getCursor(), event.getCursor().getAmount()); + break; + + case PICKUP_ALL: + case PICKUP_HALF: + case PICKUP_ONE: + case PICKUP_SOME: + case COLLECT_TO_CURSOR: + case DROP_ALL_SLOT: + case DROP_ONE_SLOT: + transfer.track(currentItem, -currentItem.getAmount()); + break; + + default: + return; + } + } + @EventHandler(ignoreCancelled = false) public void onInventoryClose(InventoryCloseEvent event) { Inventory topInventory = event.getPlayer().getOpenInventory().getTopInventory(); if (topInventory != null) { Backpack backpack = this.backpackHandler.getBackpack(topInventory); if (backpack != null) { + backpack.closeTransfer((Player) event.getPlayer()); backpack.save(); } } diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java index 2d4d5a2..2af9e3a 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java +++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackPlugin.java @@ -1,5 +1,9 @@ package net.imprex.zip; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; @@ -18,6 +22,7 @@ import net.imprex.zip.api.ZIPService; import net.imprex.zip.command.BackpackCommand; import net.imprex.zip.config.BackpackConfig; +import net.imprex.zip.config.GeneralConfig; import net.imprex.zip.util.ZIPLogger; public class BackpackPlugin extends JavaPlugin implements Listener, ZIPService { @@ -31,6 +36,9 @@ public class BackpackPlugin extends JavaPlugin implements Listener, ZIPService { private UpdateSystem updateSystem; + public ZoneId dateZone; + public DateTimeFormatter dateFormatter; + @Override public void onLoad() { this.backpackIdentifierKey = this.createNamespacedKey("backpack.type"); @@ -48,6 +56,10 @@ public void onEnable() { this.backpackConfig.deserialize(); + GeneralConfig generalConfig = this.backpackConfig.general(); + this.dateZone = generalConfig.dateZoneId; + this.dateFormatter = DateTimeFormatter.ofPattern(generalConfig.dateFormat, generalConfig.dateLocale); + this.backpackRegistry.register(); this.updateSystem = new UpdateSystem(this); @@ -110,6 +122,10 @@ public void handleItemOnError(Cancellable event, Player player, ItemStack item) } } + public String formatDateTime(OffsetDateTime dateTime) { + return this.dateFormatter.format(dateTime.atZoneSameInstant(this.dateZone)); + } + public NamespacedKey createNamespacedKey(String key) { return new NamespacedKey(this, key); } diff --git a/zip-plugin/src/main/java/net/imprex/zip/BackpackTransferPlayer.java b/zip-plugin/src/main/java/net/imprex/zip/BackpackTransferPlayer.java new file mode 100644 index 0000000..d0cb38a --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/BackpackTransferPlayer.java @@ -0,0 +1,43 @@ +package net.imprex.zip; + +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +public class BackpackTransferPlayer { + + private final OffsetDateTime created = OffsetDateTime.now(); + + private final Map items = new HashMap<>(); + + public void track(ItemStack item, int amount) { + if (item == null || item.getType() == Material.AIR) { + return; + } + + for (Entry entry : this.items.entrySet()) { + if (entry.getKey().isSimilar(item)) { + entry.setValue(entry.getValue() + amount); + return; + } + } + + this.items.put(item, amount); + } + + public boolean isEmpty() { + return this.items.isEmpty(); + } + + public Map getItems() { + return this.items; + } + + public OffsetDateTime getCreated() { + return this.created; + } +} diff --git a/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java b/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java index d4afe3e..01de11c 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java +++ b/zip-plugin/src/main/java/net/imprex/zip/NmsInstance.java @@ -38,11 +38,19 @@ public static void initialize() { ZIPLogger.info("NMS adapter for server version \"" + nmsVersion + "\" found!"); } - public static byte[] itemstackToBinary(ItemStack[] items) { + public static byte[] itemstackArrayToBinary(ItemStack[] items) { + return instance.itemstackArrayToBinary(items); + } + + public static List binaryToItemStackArray(byte[] binary) { + return instance.binaryToItemStackArray(binary); + } + + public static byte[] itemstackToBinary(ItemStack items) { return instance.itemstackToBinary(items); } - public static List binaryToItemStack(byte[] binary) { + public static ItemStack binaryToItemStack(byte[] binary) { return instance.binaryToItemStack(binary); } diff --git a/zip-plugin/src/main/java/net/imprex/zip/command/BackpackCommand.java b/zip-plugin/src/main/java/net/imprex/zip/command/BackpackCommand.java index 5a7b2d6..1982ca7 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/command/BackpackCommand.java +++ b/zip-plugin/src/main/java/net/imprex/zip/command/BackpackCommand.java @@ -34,6 +34,8 @@ public BackpackCommand(BackpackPlugin plugin) { this.registerSubCommand(new PickupCommand(plugin)); this.registerSubCommand(new TypeCommand(plugin)); this.registerSubCommand(new LoreCommand(plugin)); + this.registerSubCommand(new HistoryCommand(plugin)); + this.registerSubCommand(new LookupCommand(plugin)); this.buildHelpMessage(); } diff --git a/zip-plugin/src/main/java/net/imprex/zip/command/HistoryCommand.java b/zip-plugin/src/main/java/net/imprex/zip/command/HistoryCommand.java new file mode 100644 index 0000000..810b40f --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/command/HistoryCommand.java @@ -0,0 +1,85 @@ +package net.imprex.zip.command; + +import java.util.List; +import java.util.Map.Entry; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import net.imprex.zip.Backpack; +import net.imprex.zip.BackpackPlugin; +import net.imprex.zip.api.ZIPBackpackHistory; +import net.imprex.zip.config.MessageKey; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.HoverEvent.Action; +import net.md_5.bungee.api.chat.ItemTag; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.chat.hover.content.Item; + +public class HistoryCommand extends BackpackSubCommand { + + public HistoryCommand(BackpackPlugin plugin) { + super(plugin, MessageKey.CommandHelpPickup, "zeroinventoryproblems.history", "history"); + } + + @Override + public void onCommand(CommandSender sender, String[] args) { + Player player = this.isPlayer(sender); + if (player == null) { + return; + } + + Backpack backpack = this.checkIfHoldingBackpack(player); + if (backpack == null) { + this.messageConfig.send(player, MessageKey.YouNeedToHoldABackpackInYourHand); + return; + } + + List historyList = backpack.getHistroy(); + if (historyList.isEmpty()) { + this.messageConfig.send(player, MessageKey.HistoryIsEmpty); + return; + } + + TextComponent component = new TextComponent(); + component.addExtra(new TextComponent(this.messageConfig.getWithoutPrefix(MessageKey.CommandHistorySpacer))); + component.addExtra("\n"); + + for (ZIPBackpackHistory history : backpack.getHistroy()) { + TextComponent header = new TextComponent(this.messageConfig.getWithoutPrefix( + MessageKey.CommandHistoryEntryHeader, + this.plugin.formatDateTime(history.dateTime()), + Bukkit.getOfflinePlayer(history.player()).getName())); + component.addExtra(header); + component.addExtra("\n"); + + for (Entry entry : history.items().entrySet()) { + ItemStack item = entry.getKey(); + boolean itemAdded = entry.getValue() > 0; + + TextComponent itemComponent = new TextComponent(this.messageConfig.getWithoutPrefix( + itemAdded + ? MessageKey.CommandHistoryEntryAdded + : MessageKey.CommandHistoryEntryRemoved, + entry.getValue(), + entry.getKey().getType().toString())); + + itemComponent.setHoverEvent(new HoverEvent(Action.SHOW_ITEM, new Item( + item.getType().getKey().toString(), + entry.getValue(), + ItemTag.ofNbt(item.getItemMeta().getAsString())))); + + component.addExtra(itemComponent); + component.addExtra("\n"); + } + } + + component.addExtra(new TextComponent(this.messageConfig.getWithoutPrefix(MessageKey.CommandHistorySpacer))); + } + + @Override + public void onTabComplete(CommandSender sender, String[] args, List result) { + } +} \ No newline at end of file diff --git a/zip-plugin/src/main/java/net/imprex/zip/command/LookupCommand.java b/zip-plugin/src/main/java/net/imprex/zip/command/LookupCommand.java new file mode 100644 index 0000000..a0b1db3 --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/command/LookupCommand.java @@ -0,0 +1,35 @@ +package net.imprex.zip.command; + +import java.util.List; + +import org.bukkit.command.CommandSender; + +import net.imprex.zip.BackpackPlugin; +import net.imprex.zip.config.MessageKey; + +public class LookupCommand extends BackpackSubCommand { + + /* + * TODO + * + * Find a way to handle big amount of files + * (maybe as search request and queued in background?) + * + * zip lookup pack <- info when click actions + * zip lookup pack open + * zip lookup pack give + * zip lookup pack history + * zip lookup user history <- search all ids (when in history) + */ + public LookupCommand(BackpackPlugin plugin) { + super(plugin, MessageKey.CommandHelpType, "zeroinventoryproblems.lookup", "lookup"); + } + + @Override + public void onTabComplete(CommandSender sender, String[] args, List result) { + } + + @Override + public void onCommand(CommandSender sender, String[] args) { + } +} \ No newline at end of file diff --git a/zip-plugin/src/main/java/net/imprex/zip/config/GeneralConfig.java b/zip-plugin/src/main/java/net/imprex/zip/config/GeneralConfig.java index 28bb450..9852355 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/config/GeneralConfig.java +++ b/zip-plugin/src/main/java/net/imprex/zip/config/GeneralConfig.java @@ -1,5 +1,9 @@ package net.imprex.zip.config; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + import org.bukkit.configuration.ConfigurationSection; public class GeneralConfig { @@ -9,9 +13,14 @@ public class GeneralConfig { public boolean verbose; public String locale; + public Locale dateLocale; + public ZoneId dateZoneId; + public String dateFormat; public int maxLoreCount; + public int historySize; + public GeneralConfig(ConfigurationSection config) { if (config == null) { throw new IllegalArgumentException("Config section general was not found"); @@ -31,6 +40,13 @@ public GeneralConfig(ConfigurationSection config) { if (config.contains("locale")) { this.locale = config.getString("locale"); + + try { + this.dateLocale = Locale.forLanguageTag(this.locale); + } catch (Exception e) { + e.printStackTrace(); + this.dateLocale = Locale.getDefault(); + } } else { throw new IllegalArgumentException("Config section general is missing locale value"); } @@ -40,5 +56,28 @@ public GeneralConfig(ConfigurationSection config) { } else { this.maxLoreCount = 10; } + + if (config.contains("historySize") && config.isInt("historySize")) { + this.historySize = config.getInt("historySize"); + } else { + this.historySize = 5; + } + + try { + this.dateZoneId = ZoneId.of(config.getString("dateZoneId")); + } catch (Exception e) { + // TODO log missing config value + e.printStackTrace(); + this.dateZoneId = ZoneId.systemDefault(); + } + + try { + this.dateFormat = config.getString("dateFormat"); + DateTimeFormatter.ofPattern(this.dateFormat, this.dateLocale); + } catch (Exception e) { + // TODO log missing config value + e.printStackTrace(); + this.dateFormat = "MM.dd.yyyy HH:mm"; + } } } \ No newline at end of file diff --git a/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java b/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java index 50e99e5..3014021 100644 --- a/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java +++ b/zip-plugin/src/main/java/net/imprex/zip/config/MessageKey.java @@ -60,7 +60,12 @@ public enum MessageKey { LoreLineChange("loreLineChange", "The lore line {0} was changed"), LoreLineDelete("loreLineDelete", "The lore line {0} was deleted"), MaxLoreCountReached("maxLoreCountReached", "You have reached the max lore count of {0}"), - UnableToLoadBackpack("unableToLoadBackpack", "Backpack can't be loaded!"); + UnableToLoadBackpack("unableToLoadBackpack", "Backpack can't be loaded!"), + HistoryIsEmpty("historyIsEmpty", "Backpack history is empty!"), + CommandHistorySpacer("commandHistorySpacer", "§8[]§7========== §eZeroInventoryProblems History §7==========§8[]"), + CommandHistoryEntryHeader("commandHistoryHeader", "{0}: {1}"), + CommandHistoryEntryAdded("commandHistoryAdded", " | §a+ x{0} {1}"), + CommandHistoryEntryRemoved("commandHistoryRemoved", " | §c- x{0} {1}"); public static MessageKey findByKey(String key) { for (MessageKey messageKey : values()) { diff --git a/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigration.java b/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigration.java new file mode 100644 index 0000000..7678818 --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigration.java @@ -0,0 +1,15 @@ +package net.imprex.zip.migration; + +import net.imprex.zip.BackpackPlugin; +import net.imprex.zip.common.Ingrim4Buffer; + +public interface BackpackMigration { + + int sourceVersion(); + + default int targetVersion() { + return this.sourceVersion() + 1; + } + + Ingrim4Buffer migrate(BackpackPlugin plugin, Ingrim4Buffer sourceBuffer, Ingrim4Buffer targetBuffer); +} diff --git a/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigrationV1.java b/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigrationV1.java new file mode 100644 index 0000000..8a7d5eb --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigrationV1.java @@ -0,0 +1,56 @@ +package net.imprex.zip.migration; + +import org.bukkit.inventory.ItemStack; + +import net.imprex.zip.BackpackPlugin; +import net.imprex.zip.NmsInstance; +import net.imprex.zip.common.Ingrim4Buffer; +import net.imprex.zip.common.UniqueId; + +public class BackpackMigrationV1 implements BackpackMigration { + + private UniqueId id; + + private String typeRaw; + + private ItemStack[] content; + + @Override + public int sourceVersion() { + return 1; + } + + @Override + public int targetVersion() { + return 100; + } + + @Override + public Ingrim4Buffer migrate(BackpackPlugin plugin, Ingrim4Buffer sourceBuffer, Ingrim4Buffer targetBuffer) { + this.read(plugin, sourceBuffer); + + targetBuffer.writeByteArray(this.id.toByteArray()); + targetBuffer.writeString(this.typeRaw); + targetBuffer.writeByteArray(NmsInstance.itemstackArrayToBinary(this.content)); + + // version 2 + // write history size + targetBuffer.writeInt(0); + + return targetBuffer; + } + + public void read(BackpackPlugin plugin, Ingrim4Buffer buffer) { + /* + * Load backpack id from buffer but don't use it! Just for later migration to + * SQL + */ + this.id = UniqueId.fromByteArray(buffer.readByteArray()); + + this.typeRaw = buffer.readString(); + + byte[] contentAsByteArray = buffer.readByteArray(); + ItemStack[] content = NmsInstance.binaryToItemStackArray(contentAsByteArray).toArray(ItemStack[]::new); + this.content = content; + } +} diff --git a/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigrator.java b/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigrator.java new file mode 100644 index 0000000..77a94d2 --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/migration/BackpackMigrator.java @@ -0,0 +1,62 @@ +package net.imprex.zip.migration; + +import java.util.HashMap; +import java.util.Map; + +import io.netty.buffer.PooledByteBufAllocator; +import net.imprex.zip.Backpack; +import net.imprex.zip.BackpackPlugin; +import net.imprex.zip.common.Ingrim4Buffer; + +public class BackpackMigrator { + + private static final Map MIGRATIONS = new HashMap<>(); + + static { + register(new BackpackMigrationV1()); + } + + private static void register(BackpackMigration migration) { + MIGRATIONS.put(migration.sourceVersion(), migration); + } + + public static Ingrim4Buffer migrate(BackpackPlugin plugin, Ingrim4Buffer sourceBuffer) { + while (true) { + int version; + try { + /* + * Forgot the version code so we need to check if we + * first write a byte array starting with a varInt... + */ + version = sourceBuffer.readVarInt(); + sourceBuffer.resetReaderIndex(); + + if (version == 12) { + version = 1; + } else { + version = sourceBuffer.readInt(); + } + } catch (Exception e) { + sourceBuffer.resetReaderIndex(); + version = sourceBuffer.readInt(); + } + + // no migration needed because were on the latest version + if (version == Backpack.VERSION) { + return sourceBuffer; + } + + BackpackMigration migration = MIGRATIONS.get(version); + if (migration == null) { + throw new IllegalArgumentException("Missing backpack migration step for source version '" + version + "'"); + } + + Ingrim4Buffer targetBuffer = new Ingrim4Buffer(PooledByteBufAllocator.DEFAULT.buffer()); + targetBuffer.writeInt(migration.targetVersion()); // write version + migration.migrate(plugin, sourceBuffer, targetBuffer); + + sourceBuffer.release(); + sourceBuffer = targetBuffer; + } + } +} \ No newline at end of file diff --git a/zip-plugin/src/main/java/net/imprex/zip/util/FixedSizeQueue.java b/zip-plugin/src/main/java/net/imprex/zip/util/FixedSizeQueue.java new file mode 100644 index 0000000..70ac659 --- /dev/null +++ b/zip-plugin/src/main/java/net/imprex/zip/util/FixedSizeQueue.java @@ -0,0 +1,43 @@ +package net.imprex.zip.util; + +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.LinkedBlockingQueue; + +public class FixedSizeQueue implements Iterable { + + private final LinkedBlockingQueue queue; + + public FixedSizeQueue(int capacity) { + this.queue = new LinkedBlockingQueue<>(capacity); + } + + public void add(T item) { + if (this.queue.remainingCapacity() == 0) { + this.queue.poll(); + } + + this.queue.offer(item); + } + + public T poll() { + return this.queue.poll(); + } + + public int size() { + return this.queue.size(); + } + + public void clear() { + this.queue.clear(); + } + + @Override + public Iterator iterator() { + return this.queue.iterator(); + } + + public Collection collection() { + return this.queue; + } +} diff --git a/zip-plugin/src/main/resources/config/config-1.19.yml b/zip-plugin/src/main/resources/config/config-1.19.yml index 53d8bee..6a1f7e5 100644 --- a/zip-plugin/src/main/resources/config/config-1.19.yml +++ b/zip-plugin/src/main/resources/config/config-1.19.yml @@ -2,6 +2,9 @@ general: checkForUpdates: true verbose: false locale: en_US + historySize: 10 + dateZoneId: America/New_York + dateFormat: MM.dd.yyyy HH:mm type: big: uniqueName: 'big' diff --git a/zip-plugin/src/main/resources/config/config-1.20.yml b/zip-plugin/src/main/resources/config/config-1.20.yml index 53d8bee..6a1f7e5 100644 --- a/zip-plugin/src/main/resources/config/config-1.20.yml +++ b/zip-plugin/src/main/resources/config/config-1.20.yml @@ -2,6 +2,9 @@ general: checkForUpdates: true verbose: false locale: en_US + historySize: 10 + dateZoneId: America/New_York + dateFormat: MM.dd.yyyy HH:mm type: big: uniqueName: 'big' diff --git a/zip-plugin/src/main/resources/config/config-1.21.yml b/zip-plugin/src/main/resources/config/config-1.21.yml index 53d8bee..6a1f7e5 100644 --- a/zip-plugin/src/main/resources/config/config-1.21.yml +++ b/zip-plugin/src/main/resources/config/config-1.21.yml @@ -2,6 +2,9 @@ general: checkForUpdates: true verbose: false locale: en_US + historySize: 10 + dateZoneId: America/New_York + dateFormat: MM.dd.yyyy HH:mm type: big: uniqueName: 'big'