Problems using Set<UUID> data in custom data - dataview?

I have spent way, way too much time of my weekend struggling through a custom-data problem.
I have managed to examine different github sources for similarities and differences, and managed to make the FakeData example work on both players and particular block-tiles, and have stored other simple data-constructs (data objects with various strings, ints, booleans) successfully on said players and tile entities, with data persisting in the map data files as expected by using mostly the FakeData code and no additional deserializers/serializers and whatever that warning message in the startup log is about not extending an abstractdatabuilder not seeming to interfere…

For example, I can make a custom data with two booleans do exactly what I want, and persist across server shutdowns just fine. I can then add a string everywhere in the files necessary to add a new part, and it works fine. But, if I use a non-primative Set<UUID>, thats where the basic working system falls apart (and I sorta expected it would)

        System.out.println("Creating a default lock data");
        FramelockData fld = new FramelockData(ds, false, false);
        System.out.println("Offer=" + bs.getFinal().getLocation().get().offer(fld));

I create and then offer separately to isolate processes, and see that there is no problem with the data creation, and the offer is even returned back as a success, but then immediately the log errors:

> [04:45:00] [Server thread/ERROR] [FML/]: A TileEntity type net.minecraft.tileentity.TileEntityChest has throw an exception trying to write state. It will not persist. Report this to the mod author
> java.lang.IllegalArgumentException: Unable to translate object to NBTBase: f2657b0b-fcb6-4d74-af49-3eb629035666
>     at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1250]
>     at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1250]
>     at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1250]
>     at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1250]
>     at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1250]
>     at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1250]
>     at net.minecraft.tileentity.TileEntity.writeToNbt( ~[akw.class:?]
>     at net.minecraft.tileentity.TileEntity.handler$onWriteToNBT$0( ~[akw.class:?]
>     at net.minecraft.tileentity.TileEntity.func_145841_b( ~[akw.class:?]
>     at net.minecraft.tileentity.TileEntityLockable.func_145841_b( ~[alk.class:?]
>     at net.minecraft.tileentity.TileEntityChest.func_145841_b( ~[aky.class:?]
>     at [anj.class:?]
>     at [anj.class:?]
>     at [ld.class:?]
>     at [ld.class:?]
>     at [le.class:?]
>     at net.minecraft.server.MinecraftServer.func_71267_a( [MinecraftServer.class:?]
>     at net.minecraft.server.MinecraftServer.func_71217_p( [MinecraftServer.class:?]
>     at [MinecraftServer.class:?]
>     at Source) [?:1.8.0_74]
> [04:45:45] [Server thread/DEBUG] [FML/]: Gathering id map for writing to world save world 

I have deteremined from what I can find in General that it has something to do with the dataview , and the fact that a Set is of course not a primative object, but I have not found anything that addresses how to handle a set object going back or forth (No map chunk data nbt is modified so clearly its erroring when trying to save it, not just having problems decoding what a chunk of bytes means when reading)

Note that I am not married to the use of a Set<UUID> if there is some other type of collection/group object that is much easier for the custom data system to work with, I can try to change that, but I dont expect any grouping object to avoid this same problem since they are not primatives themselves, and a similar approach would be required to do whatever is necessary to convert a Set to an nbt friendly dataview/datacontainer system if it was a list, etc.

Can someone please point me in the right direction and with some sample-code to use as to what my system is missing that will let me pass that Set<UUID> back and forth in my data?

My mutable and immutable data, builder, datamanipulatorbuilder, keys and a query-constants files are shown in the pastebin link. Thank you in advance…

public void onPreInit(GamePreInitializationEvent event) {
    Sponge.getDataManager().register(FramelockData.class, ImmutableFramelockData.class, new FramelockDataManipulatorBuilder());

So your problem is that UUID is not serializable before this commit. Make sure that you’re using the latest SpongeAPI and latest SpongeForge/SpongeVanilla builds to run your plugin.

Basically the problem is that DataViews should only ever contain primitive types or Lists/Maps/Sets etc. If an object is added to the view that is not one of these types, it will attempt to serialize it first. The error you see is because the further conversion to NBT could not save the UUID objects as NBT tags.

While SpongeAPI types can extend DataSerializable and hence be serialized, there was no system up until recently for other objects. With the commit above, most data classes you’ll come across in the API will be serialized correctly.

okay, so part of the issue is that I’ve been using the shaded api jars, and the link doesn’t show any new APIs after March 15, so I was thinking most of the API pushes were into future-to-be-released versions a while now, which indeed includes that.

But, having gotten the updated jar from the - 4.1.0-SNAPSHOT link, and trying with a fresh new world folder on spongeforge (not latest but 2nd or 3rd latest)… … I still get a similar, but different error:

[06:13:21] [Server thread/FATAL]: Error executing task
java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Unable to translate object to NBTBase: f2657b0b-fcb6-4d74-af49-3eb629035666
        at Source) ~[?:1.8.0_74]
        at java.util.concurrent.FutureTask.get(Unknown Source) ~[?:1.8.0_74]
        at net.minecraft.util.Util.func_181617_a(SourceFile:45) [g.class:?]
        at net.minecraft.server.MinecraftServer.func_71190_q( [MinecraftServer.class:?]
        at net.minecraft.server.dedicated.DedicatedServer.func_71190_q( [ko.class:?]
        at net.minecraft.server.MinecraftServer.func_71217_p( [MinecraftServer.class:?]
        at [MinecraftServer.class:?]
        at Source) [?:1.8.0_74]
Caused by: java.lang.IllegalArgumentException: Unable to translate object to NBTBase: f2657b0b-fcb6-4d74-af49-3eb629035666
        at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[NbtTranslator.class:1.8.9-1808-4.1.0-BETA-1262]
        at net.minecraft.tileentity.TileEntity.writeToNbt( ~[akw.class:?]
        at net.minecraft.tileentity.TileEntity.handler$onWriteToNBT$0( ~[akw.class:?]
        at net.minecraft.tileentity.TileEntity.func_145841_b( ~[akw.class:?]
        at net.minecraft.tileentity.TileEntityLockable.func_145841_b( ~[alk.class:?]
        at net.minecraft.tileentity.TileEntityChest.func_145841_b( ~[aky.class:?]
        at ~[adm.class:?]
        at ~[Extent.class:1.8.9-1808-4.1.0-BETA-1262]
        at org.spongepowered.common.event.SpongeCommonEventFactory.callNotifyNeighborEvent( ~[SpongeCommonEventFactory.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[adm.class:?]
        at ~[adm.class:?]
        at ~[adm.class:?]
        at org.spongepowered.common.event.CauseTracker.markAndNotifyBlockPost( ~[CauseTracker.class:1.8.9-1808-4.1.0-BETA-1262]
        at org.spongepowered.common.event.CauseTracker.handleBlockCaptures( ~[CauseTracker.class:1.8.9-1808-4.1.0-BETA-1262]
        at org.spongepowered.common.event.CauseTracker.handlePostTickCaptures( ~[CauseTracker.class:1.8.9-1808-4.1.0-BETA-1262]
        at ~[PacketUtil.class:1.8.9-1808-4.1.0-BETA-1262]
        at$1.redirect$onProcessPacket$0(SourceFile:51) ~[fh$1.class:?]
        at$ ~[fh$1.class:?]
        at java.util.concurrent.Executors$ Source) ~[?:1.8.0_74]
        at Source) ~[?:1.8.0_74]
        at net.minecraft.util.Util.func_181617_a(SourceFile:44) ~[g.class:?]
        ... 5 more

From what you say, in theory it sounds like NOW the fakedata working example could be changed to a Set<String> or Set<boolean> and automatically work without additional special code to deal with Sets other than changing createValue to createSetValue [This is the real crux, not having seen an example involving a Set of any type let alone a ‘troublesome’ type that I tried to dive into this with]

Has my ‘further enhanced’ code beyond the FakeData example, by including a FramelockBuilder object messed up everything? Or are there new getSomething() methods added that i need to switch to in my code? Or am i casting something wrong. I learn from examples and modifying examples strategically, stepwise… and this was as far as I could get, with no Set<anything> examples

In toContainer(), replace

.set(KeysFramelock.UUIDLIST, this.csetUUIDsAllowed)



or something that does the equivalent

Thank you @simon816 for trying to dig me out here.

I’ve poked and prodded at this for a while now with the javadocs to find ‘the equivalent’ but I cant get anything compiler-friendly, and the closest I get to making sense of it wants to lead me down a long road of "change set to setmap, now change the key argument to a dataquery argument, which changes the KeysFramelock.UUIDLIST, throwing errors everywhere else that the key type was used…ugh {method set(DataQuery, Object) in the type MemoryDataContainer is not applicable for the arguments (Key<SetValue<UUID>>, Set<DataContainer>) }

Straight-up use of DataSerializer throws can not make static errors , but making a variable works, specifying type which makes sense (<Set> throws errors about being inappropriate for type)

this is as close as i can get without opening up a neverending chain of worms if I let eclipse fix either issue, but hopefully its ‘oh so close’ and i just need to modify something differently than either fix gives…

    public DataContainer toContainer() {
        DataSerializer<UUID> dataseruuid;
        return new MemoryDataContainer()
                .set(KeysFramelock.UUIDLIST, ) )
                .set(KeysFramelock.DENYROTATION, this.cboolDenyRotation)
                .set(KeysFramelock.OWNEDBYSERVER, this.cboolServerOwned);

Further guidance required

OK I think it’s because of the use of Key instead of DataQuery.
The generic signature is set(Key<? extends BaseValue<E>> key, E value)
But in this case, E can’t be used as it has to be converted from Set<UUID> to Set<DataContainer> (Because the DataContainer stores the uuid_high and uuid_low)
try this:


I appreciate the effort, but, I have no idea where you’re pulling things things from.

There is no “DataSerializers” that I can see - nothing comes up. There is DataSerializer, but with nothing except “.class” and “.this” available when used directly in the map() part.
I can make a DataSerializer variable, which wants a type, hence DataSerializer<UUID> dataseruuid; and that then gives the access to methods and use with ::serialize but there is no .UUID_DATA_SERIALIZER enum/key/whatevs? available in anything I can seem to play with.

Of course, the getQuery() does handle changing the set(,) to avoid that argument dislike, that part is okay I guess.
But of course, as I dig around and can only come up with making DataSerializer<UUID> dataseruuid; variable and the set() is now happier with dataseruuid::serialize, it does warn that the dataseruuid is not initialized. Okay, so auto-initialize makes the variable null, and then it gets mad, scratch that, lets make DataSerializer<UUID> dataseruuid =new DataSerializer<UUID> ();

and it hates this too - the set command is now perfectly fine not complaining, but this assignment says that it can not instantiate to type DataSerializer … (with or without the <UUID> in the =new part, and as expected, without complarins about missing the generic)

I am trying REALLY HARD to appreciate that you’re not able to whip out a quick line of code without having a whole damned shebang of custom Set <UUID> custom data files already prepared, and trying to go off of what in theory should be there if you recall correctly, and trying REALLY HARD to poke around based on this type of pointing-in-the-right-direction assistance to fiddle a bit and find something that correctly settles into position that I wouldn’t be able to pull out of the air due to not understanding what I’m trying to get to.

And after trying really hard, thats where I hit the wall again.

DataSerializers.UUID_DATA_SERIALIZER::serialize may as well be (PleasePleaseWork.HereGoesNothing::wishReallyHard) since both describe what we want done, but appear to be equally imaginary code :slight_smile:

I’ve provided my custom data templates, please do feel free to copy/paste them to have everything in the compiler to then actually try to make into a working solution instead of having to come up with a from scratch working example since you seem to have an idea of what we’re after – I feel like it is like describing what menu items to look under for an application you’ve never seen to gain access to a particular feature, where the exact wording and exact menu may not be right, but as the knower, you can scan every menu in 3 seconds to find the one that likely is the one with the entry to change a particular feature, then quickly tap on two options until you see the information on the screen that you recognize as being the one to change a particular setting, and while I’m not as rediculously inept as those folks who say “Er, all i see is Print Setting or Printing Settings, but I dont have a Printout Settings menu” to try to poke around for similar things, I do feel somewhat just as limited.

My grandmother might have been able to figure out that “Change Appearance” was what she’d want analogous to “Viewing Options” I’d pull out of the air, but she wouldn’t know that “Change Font” was the setting to change the color of her text due to not knowing that she wanted a property that was a font thing… but sure enough, once she figured that color setting out, she’d figure out that the “Change Font” was where to go to alter the way that her text looks and would experiment with other settings she saw there, confident that she wouldn’t break her internet connection by doing any of that. Thats me right now, feeling like my smart but confused grandmother in such a scenario, if she were still alive, capable of some extrapolation and interpolations, up to a point, trying to back-communicate what I do see because you’re in traffic on the phone trying to describe the most-likely thing that your memory offers and ‘represents’ what the right way to do it is, but the devil is in the details of the fact that you’re not near a computer to actually try and test to confirm

OK I see, my mistake. DataSerializers is part of SpongeCommon which explains why you’re not seeing it.
Try this:

DataSerializer<UUID> uuidSerializer = Sponge.getDataManager().getSerializer(UUID.class).get();
1 Like

Thanks again. The modification above makes sense, and would seem to be something able to apply to other things similarly. The changes do indeed compile as you have. I was still getting errors at first, but then realized that the toContainer() was present in both mutable and immutable data parts. After using the above code modifications in both places, we have a success! No errors after offereing the data and the readback has the right stuff. I would have thought that this only gave us one direction of translating things, and more similar code would be required to go the other direction…

But i dont know if this is right or not in the long run. In the template examples, the mutable data had
return super.toContainer() .set() .set() .set()
and the immutable had
return new MemoryDataContainer() .set() .set() .set()

For now, i have the memorydatacontainer version in both classes… but is there any important difference?

Realistically, should the immutable data and mutable data be damnnear the identical code, just using immutablevalues and no registered setters, allowing for rapid creation of highly parallel code, and thus duplicating the memorydatacontainer version is the way to go for simplicity and parallelism, or does it need something else for the longer run/properness?

Meanwhile, I’ve got some new eager coding to dive into this afternoon using this!! Thank you so much.

You should use super.toContainer()... so that super classes are able to serialize their data too.
And yes, the mutable and immutable variants should be very similar to each other. Make sure the toContainer() is the same for both.

1 Like

I’ve managed to make … some … progress. I refuse to acknowledge how many hours I have spent fighting and debugging this out with the changes above preferring to be in denial about the duration not impacting my lifetime. I have erased the map files over and over again to avoid cross-contaminating between changes, starting with a new fresh map each testing time unless by intention not, to observe persistance.

I am able to create the data which includes the uuid set and a boolean, I test by adding a playername and world uuid to the set so its not a set of 1 to start with, and offer the data. Offer is accepted, and the NBT view of the map data shows my tile entity (chest/furnace/hopper/etc) with the uuid-set and boolean, and the uuid set breaking into two entries of uuids stored as high/low pairs. And thats where the good news ends.

I have tried to retrieve the data numerous ways, and if the data is present, the toString() will look like this:

            Optional<ContainerLockData> optcd = event.getTargetBlock().getLocation().get().get(ContainerLockData.class);
            if (optcd.isPresent()) {
                ContainerLockData clockdata = optcd.get();
                System.out.println("CLockdata: " + clockdata.toString());

results in:
CLockdata: ContainerLockData{serverOwner=false, UUIDS=[f2657b0b-fcb6-4d74-af49-3eb629035666, 69ae57b9-7269-4ac2-9b51-a554d1fbbd9b]}

Which does show the expected information in the expected arrangement

However, I can not retrieve the components from this data.
Set<UUID> userset = clockdata.get(KeysContainerlock.UUIDLIST).get(); —>throws no such element exception, no value present.
Set<UUID> userset3 = event.getTargetBlock().getLocation().get().get(KeysContainerlock.UUIDLIST).get(); → throws the same exception and no value present complaint

            Set<UUID> userset2 = clockdata.userUUIDList().get();
**throws no error, accessing the method directly for that data BUT** 
            System.out.println("userset2 is " + userset2.size() + " contents: " + userset2.toString());
results in: 
userset2 is 0 contents: []
Then, if I shut down and reboot the server, the server sprays errors during startup
 >    [08:24:24] [Server thread/INFO] [STDERR/]: []: Could not deserialize com.prennet.boomod.boosautolock.containerlockdata.ContainerLockData! Don't worry though, we'll try to deserialize the rest of the data.
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.tileentity.TileEntity.readFromNbt(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.tileentity.TileEntity.handler$onReadFromNBT$0(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.tileentity.TileEntity.func_145839_a(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.tileentity.TileEntityLockable.func_145839_a(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.tileentity.TileEntityChest.func_145839_a(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.tileentity.TileEntity.func_145827_c(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraftforge.common.chunkio.ChunkIOProvider.callStage2(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraftforge.common.chunkio.ChunkIOProvider.callStage2(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraftforge.common.util.AsynchronousExecutor.skipQueue(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraftforge.common.util.AsynchronousExecutor.getSkipQueue(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraftforge.common.chunkio.ChunkIOExecutor.syncChunkLoad(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.server.MinecraftServer.prepareSpawnArea(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.server.MinecraftServer.func_71222_d(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.server.MinecraftServer.func_71247_a(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at net.minecraft.server.dedicated.DedicatedServer.func_71197_b(
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: []: 	at Source)
>     [08:24:24] [Server thread/INFO] [STDERR/]: [java.lang.Throwable:printStackTrace:-1]: Caused by: java.lang.ClassCastException: cannot be cast to java.util.Set
>     [08:24:24] [Server thread/INFO] [STDERR/]: [java.lang.Throwable:printStackTrace:-1]: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: [java.lang.Throwable:printStackTrace:-1]: 	at
>     [08:24:24] [Server thread/INFO] [STDERR/]: [java.lang.Throwable:printStackTrace:-1]: 	... 20 more

Clearly it does have a problem with going from the saved-data representation back into a dataobject, and I have hit the wall at this point where I know the error is as stated - regular immutable list can not be cast to java.util.set – but I am blind to the resolution of it: .createSetValue is for working with sets. this.csetUUIDsAllowed is a Set<UUID>. The DataContainer toContainer() is the return super.toContainer() version of what is above.

Line 34 follows:
    public ImmutableValue<Set<UUID>> userUUIDList() {
            return Sponge.getRegistry().getValueFactory().createSetValue(KeysContainerlock.UUIDLIST, new HashSet<UUID>(), this.csetUUIDsAllowed).asImmutable();

My gut points me to the ContainerLockBuilder (DataBuilder) specifically the container.get() for the set, but, I’ve thrown things at this and nothing I’ve tried sticks.

 public Optional<ContainerLockData> build(DataView container) throws InvalidDataException {
        if (container.contains(ContainerLockDataQueries.USERSET, ContainerLockDataQueries.SERVEROWNED)) {
            ContainerLockData framelock = new ContainerLockData(
                    (Set<UUID>) container.get(ContainerLockDataQueries.USERSET).get(), container.getBoolean(ContainerLockDataQueries.SERVEROWNED).get());
            return Optional.of(framelock);
        return Optional.empty();

Its so close to my grasp, but yet so far away…