Solved (De)serializing ItemStack as byte arrays goes wrong with MongoDB connection

tartur

New member
Jan 16, 2025
2
0
1
Hi,

I'm currently working on a Folia 1.21.4 project in which I work with a MongoDB connection that I use to store player's serialized inventories.
Here are the methods I'm using to convert ItemStack to MongoDB Document instances and vice-versa:

Java:
@Override
public Document save() {
    final Inventory inventory = this.player.getInventory();

    return new Document()
            .append("_id", this.player.getUniqueId().toString())
            .append("contents", Arrays.stream(inventory.getContents())
                    .filter(Objects::nonNull)
                    .map(item -> new Document("value", new String(item.serializeAsBytes())))
                    .toList());
}

@Override
public void load(Document document) {
    // this.items is of type List<ItemStack>
    this.items = document.getList("contents", Document.class).stream()
            .map(itemDoc -> ItemStack.deserializeBytes(itemDoc.getString("value").getBytes()))
            .toList();
}

I can perfectly save the player's inventory in the Mongo database. However, when I try to fetch the serialized data and deserializing it as follows:

Java:
try {
    final Publisher<Document> test = this.database.getCollection("inventories")
            .find(Filters.eq("_id", player.getUniqueId().toString()))
            .first();

    this.support.firePropertyChange("inventoryLoaded", player, Mono.from(test).doOnSuccess(document -> {
        if (document != null) {
            log.info("Inventory found!");
            inventory.load(document);
        }
    }).blockOptional().map(document -> inventory));
} catch (Exception exception) {
    log.error("Could not load inventory of player '{}' in database '{}': {}", player.getName(),
            this.databaseName, exception);

    this.support.firePropertyChange("inventoryLoaded", player, Optional.empty());
}

I get a ZipException, it seems that the received data is not in a correct format:

Code:
java.lang.RuntimeException: java.util.zip.ZipException: Not in GZIP format
        at org.bukkit.craftbukkit.util.CraftMagicNumbers.deserializeNbtFromBytes(CraftMagicNumbers.java:600) ~[bloomexcalia-1.21.1.jar:1.21.1-30-d38a6b0]
        at org.bukkit.craftbukkit.util.CraftMagicNumbers.deserializeItem(CraftMagicNumbers.java:514) ~[bloomexcalia-1.21.1.jar:1.21.1-30-d38a6b0]
        at org.bukkit.inventory.ItemStack.deserializeBytes(ItemStack.java:737) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.inventory.ItemStack.deserializeItemsFromBytes(ItemStack.java:829) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/fr.tartur.inventorykeeper.database.MongoInventory.load(MongoInventory.java:38) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/fr.tartur.inventorykeeper.database.MongoResource.lambda$loadInventory$1(MongoResource.java:98) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:171) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:176) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.reactivestreams.client.internal.BatchCursor.lambda$next$1(BatchCursor.java:48) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.function.AsyncCallbackSupplier.lambda$whenComplete$1(AsyncCallbackSupplier.java:97) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.operation.AsyncCommandBatchCursor.lambda$next$0(AsyncCommandBatchCursor.java:115) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.function.AsyncCallbackSupplier.lambda$whenComplete$2(AsyncCallbackSupplier.java:101) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.operation.AsyncCommandBatchCursor$ResourceManager.execute(AsyncCommandBatchCursor.java:253) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.operation.AsyncCommandBatchCursor.next(AsyncCommandBatchCursor.java:105) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.reactivestreams.client.internal.BatchCursor.lambda$next$2(BatchCursor.java:42) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.MonoCreate$DefaultMonoSink.success(MonoCreate.java:176) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.reactivestreams.client.internal.MongoOperationPublisher.lambda$sinkToCallback$37(MongoOperationPublisher.java:590) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.reactivestreams.client.internal.OperationExecutorImpl.lambda$execute$1(OperationExecutorImpl.java:96) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.function.AsyncCallbackSupplier.lambda$whenComplete$1(AsyncCallbackSupplier.java:97) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.function.RetryingAsyncCallbackSupplier$RetryingCallback.onResult(RetryingAsyncCallbackSupplier.java:126) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.function.AsyncCallbackSupplier.lambda$whenComplete$1(AsyncCallbackSupplier.java:97) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.function.AsyncCallbackSupplier.lambda$whenComplete$1(AsyncCallbackSupplier.java:97) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.operation.FindOperation.lambda$exceptionTransformingCallback$6(FindOperation.java:349) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.operation.AsyncOperationHelper.lambda$transformingReadCallback$21(AsyncOperationHelper.java:471) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.DefaultServer$DefaultServerProtocolExecutor.lambda$executeAsync$0(DefaultServer.java:248) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.CommandProtocolImpl.lambda$executeAsync$0(CommandProtocolImpl.java:79) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.UsageTrackingInternalConnection.lambda$sendAndReceiveAsync$1(UsageTrackingInternalConnection.java:139) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.ErrorHandlingResultCallback.onResult(ErrorHandlingResultCallback.java:47) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.AsyncSupplier.lambda$finish$0(AsyncSupplier.java:73) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.SingleResultCallback.complete(SingleResultCallback.java:67) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.AsyncSupplier.lambda$onErrorIf$5(AsyncSupplier.java:130) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.AsyncSupplier.lambda$finish$0(AsyncSupplier.java:73) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.AsyncSupplier.lambda$finish$0(AsyncSupplier.java:73) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.async.AsyncSupplier.lambda$finish$0(AsyncSupplier.java:73) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.InternalStreamConnection.lambda$sendCommandMessageAsync$9(InternalStreamConnection.java:635) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:946) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.InternalStreamConnection$MessageHeaderCallback$MessageCallback.onResult(InternalStreamConnection.java:909) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.InternalStreamConnection$2.completed(InternalStreamConnection.java:743) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.InternalStreamConnection$2.completed(InternalStreamConnection.java:740) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:240) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.AsynchronousChannelStream$BasicCompletionHandler.completed(AsynchronousChannelStream.java:220) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at InventoryKeeper-1.0.0-alpha-all.jar/com.mongodb.internal.connection.tlschannel.async.AsynchronousTlsChannel.lambda$read$4(AsynchronousTlsChannel.java:122) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[?:?]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[?:?]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
        Suppressed: java.lang.Exception: #block terminated with an error
                at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.BlockingOptionalMonoSubscriber.blockingGet(BlockingOptionalMonoSubscriber.java:129) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
                at InventoryKeeper-1.0.0-alpha-all.jar/reactor.core.publisher.Mono.blockOptional(Mono.java:1831) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
                at InventoryKeeper-1.0.0-alpha-all.jar/fr.tartur.inventorykeeper.database.MongoResource.lambda$loadInventory$3(MongoResource.java:100) ~[InventoryKeeper-1.0.0-alpha-all.jar:?]
                at io.papermc.paper.threadedregions.scheduler.FoliaAsyncScheduler$AsyncScheduledTask.run(FoliaAsyncScheduler.java:219) ~[bloomexcalia-1.21.1.jar:?]
                at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
                at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
                at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
Caused by: java.util.zip.ZipException: Not in GZIP format
        at java.base/java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:176) ~[?:?]
        at java.base/java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:79) ~[?:?]
        at java.base/java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:91) ~[?:?]
        at net.minecraft.nbt.NbtIo.createDecompressorStream(NbtIo.java:74) ~[bloomexcalia-1.21.1.jar:1.21.1-30-d38a6b0]
        at net.minecraft.nbt.NbtIo.readCompressed(NbtIo.java:82) ~[bloomexcalia-1.21.1.jar:1.21.1-30-d38a6b0]
        at org.bukkit.craftbukkit.util.CraftMagicNumbers.deserializeNbtFromBytes(CraftMagicNumbers.java:596) ~[bloomexcalia-1.21.1.jar:1.21.1-30-d38a6b0]
        ... 64 more

Can someone help me please? I will respond as fast as possible and add any missing resources if so.
 

tartur

New member
Jan 16, 2025
2
0
1
I saved this problem using Base64 encoding as follows:

Java:
@Override
public Document save() {
    final Inventory inventory = this.player.getInventory();

    return new Document()
            .append("_id", this.player.getUniqueId().toString())
            .append("contents", Arrays.stream(inventory.getContents())
                    .filter(Objects::nonNull)
                    .map(item -> new Document("value", Base64.getEncoder().encodeToString(item.serializeAsBytes())))
                    .toList());
}

@Override
public void load(Document document) {
    this.items = document.getList("contents", Document.class).stream()
            .map(itemDoc -> ItemStack.deserializeBytes(
                    Base64.getDecoder().decode(itemDoc.getString("value"))))
            .toList();
}