Jump to content

[SOLVED] [1.15.2] Saving and Loading data per world


Roxane

Recommended Posts

Hi there,

 

For my mod I want an inventory like an Ender Chest that is shared between all players. Whereever anyone is accessing this inventory it is the same.

In previous versions I did this by saving a "per world inventory" NBT to the world save file. Aditionally I want to save all positions of these "chests", so I always know where they are.

 

With saving the data I have some Issues:

1) My onWorldLoaded and onWorldSaved events do not get called, even though I registered them in my mod's constructor. (The onClientSetupEvent gets called, so I assume the basic setup is ok).

2) How do I store my NBT data to the world save file? In previous versions I used a class extending WorldSavedData. I looked at https://mcforge.readthedocs.io/en/latest/datastorage/worldsaveddata/. But I can't figure out why

MapStorage storage = world.getMapStorage();

does not work. It seems neither MapStorage nor getMapStorage() exist...

3) May be related to Problem 1): The WorldEvent.Load::getWorld only returns an IWorld. My old MySavedData::forWorld required a World. Is that a problem at all? If yes, where do I get my World from?

 

This is my main mod class (For demonstration purposes I stripped all unnecessary stuff like Blocks and Items, they are working fine):

package de.tetopia.question;

import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.world.World;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

// The value here should match an entry in the META-INF/mods.toml file
@Mod(ModQuestion.MODID)
public class ModQuestion
{
    public static final String MODID = "questionmod";
	private static final Logger LOGGER = LogManager.getLogger();

    public ModQuestion() 
    {
    	FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetupEvent);
    	FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onWorldLoaded);
    	FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onWorldSaved);
    }
    
    private void onClientSetupEvent(final FMLClientSetupEvent event) 
    {
    	LOGGER.debug("Hello from the Client Setup!");
    }
    
	public void onWorldLoaded(WorldEvent.Load event)
	{
		LOGGER.debug("Hello from World Loading!");
		
		World world = null; //Where do I get my world from? event.getWorld() returns an IWorld
		if (!world.isRemote()) 
		{
			//Get the data from the world save file
			CompoundNBT tag = new CompoundNBT();
			
			CompoundNBT tagCompound = new CompoundNBT();
			MySavedData saver = MySavedData.forWorld(world);
			tag = saver.data;
			
			if(tagCompound.contains("MyTagName"))
			{
				//Do Stuff with the data
			}
		}
	}
	
	public void onWorldSaved(WorldEvent.Save event)
	{
		LOGGER.debug("Hello from World Saving!");
		
		World world = null; //Where do I get my world from? event.getWorld() returns an IWorld
		
		if (!world.isRemote())
		{
			CompoundNBT dataToSave = new CompoundNBT();
			
			//Store some data in dataToSave
			//dataToSave.put("MyTagName", ...);
					
			MySavedData saver = MySavedData.forWorld(world);
			saver.data = dataToSave;
			saver.markDirty();
		}
	}
}

 

And this is my custom WorldSavedData class:

package de.tetopia.question;

import net.minecraft.nbt.CompoundNBT;
import net.minecraft.world.World;
import net.minecraft.world.storage.WorldSavedData;

public class MySavedData extends WorldSavedData
{
	public CompoundNBT data = new CompoundNBT();
	
	public MySavedData()
	{
		super(ModQuestion.MODID);
	}
	
	public MySavedData(String name)
	{
		super(name);
	}

	@Override
	public void read(CompoundNBT nbt)
	{
		data = nbt.getCompound("MyTagName");
	}

	@Override
	public CompoundNBT write(CompoundNBT nbt)
	{
		nbt.put("MyTagName", data);
		return nbt;
	}
	
	public static MySavedData forWorld(World world)
	{
		MapStorage storage = world.getMapStorage(); //PROBLEM: What is the 1.15 way of doing this?
		MySavedData saver = (MySavedData) storage.getOrLoadData(MySavedData.class, ModQuestion.MODID);

		if (saver == null) 
		{
			saver = new MySavedData();
			storage.setData(ModQuestion.MODID, saver);
		}
		return saver;
	}
}

 

Edited by Roxane
Solved Issue
Link to comment
Share on other sites

7 minutes ago, Roxane said:

FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onWorldLoaded); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onWorldSaved);

The WorldEvent.Load and WorldEvent.Save do not fire on the mod event bus. They instead fire on the MinecraftForge.EVENT_BUS. The mod event bus is only for life-cycle events such as FMLCommonSetupEvent.

 

10 minutes ago, Roxane said:

But I can't figure out why

It seems to have been renamed to World::getMapData or ServerWorld::getSavedData()::getOrCreate

 

At the moment I'm not fully aware of how the WorldSavedData system has changed, however you can use a Capability that is attached to the World.

13 minutes ago, Roxane said:

If yes, where do I get my World from?

instanceof and cast.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

21 minutes ago, Animefan8888 said:

The WorldEvent.Load and WorldEvent.Save do not fire on the mod event bus. They instead fire on the MinecraftForge.EVENT_BUS. The mod event bus is only for life-cycle events such as FMLCommonSetupEvent.

Thanks! Calling "MinecraftForge.EVENT_BUS.addListener(this::onWorldLoaded);" did the trick ?

 

21 minutes ago, Animefan8888 said:

It seems to have been renamed to World::getMapData or ServerWorld::getSavedData()::getOrCreate

Hmm... I can't find the World::getMapData. And ServerWorld::getSavedData()::getOrCreate want's a Supplier <T> as parameter. Where do I get that from?

 

21 minutes ago, Animefan8888 said:

however you can use a Capability that is attached to the World.

Thanks for that tip, I'll look into that direction.

 

 

 

Edited by Roxane
Link to comment
Share on other sites

2 minutes ago, Roxane said:

Where do I get that from?

You create one. A Supplier is just a functional interface that returns an instance of type T. In this case I believe it would be an instance of your WorldSavedData class.

 

3 minutes ago, Roxane said:

Is it only the Capability you mentioned? Or is there something else?

It's the only forge provided way of doing it yes. There's three ways to do this. WorldSavedData(which has apparently changed a little bit), Capabilities(which to my knowledge are still the same as when they were first introduced), and of course you can write the file to disk yourself(not really the best option because the first two already exist).

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Thank you very much! With your Information I could get it to work ?

 

For anybody interested, this is my code now.

Mod class:

import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

// The value here should match an entry in the META-INF/mods.toml file
@Mod(ModQuestion.MODID)
public class ModQuestion
{
    public static final String MODID = "questionmod";
	private static final Logger LOGGER = LogManager.getLogger();

    public ModQuestion() 
    {
    	FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetupEvent);
    	MinecraftForge.EVENT_BUS.addListener(this::onWorldLoaded);
    	MinecraftForge.EVENT_BUS.addListener(this::onWorldSaved);
    }
    
    private void onClientSetupEvent(final FMLClientSetupEvent event) 
    {
    	//LOGGER.debug("Hello from the Client Setup!");
    }
    
	public void onWorldLoaded(WorldEvent.Load event)
	{
		if (!event.getWorld().isRemote() && event.getWorld() instanceof ServerWorld)
		{
			MySavedData saver = MySavedData.forWorld((ServerWorld) event.getWorld());
   			
			if(saver.data.contains("MyData"))
			{
				LOGGER.debug("Found my data: " + saver.data.get("MyData"));
				//Do whatever you want to do with the data
			}
		}
	}
	
	public void onWorldSaved(WorldEvent.Save event)
	{
		if (!event.getWorld().isRemote() && event.getWorld() instanceof ServerWorld)
		{
			MySavedData saver = MySavedData.forWorld((ServerWorld) event.getWorld());
			CompoundNBT myData = new CompoundNBT();
			myData.putInt("MyData", 0); //Put in whatever you want with myData.put
			saver.data = myData;
			saver.markDirty();
			LOGGER.debug("Put my data in!");
		}
	}
}

 

WorldSaverData class:

import java.util.function.Supplier;

import net.minecraft.nbt.CompoundNBT;
import net.minecraft.world.server.ServerWorld;
import net.minecraft.world.storage.DimensionSavedDataManager;
import net.minecraft.world.storage.WorldSavedData;

public class MySavedData extends WorldSavedData implements Supplier
{
	public CompoundNBT data = new CompoundNBT();
	
	public MySavedData()
	{
		super(ModQuestion.MODID);
	}
	
	public MySavedData(String name)
	{
		super(name);
	}

	@Override
	public void read(CompoundNBT nbt)
	{
		data = nbt.getCompound("MyCompound");
	}

	@Override
	public CompoundNBT write(CompoundNBT nbt)
	{
		nbt.put("MyCompound", data);
		return nbt;
	}
	
	public static MySavedData forWorld(ServerWorld world)
	{
		DimensionSavedDataManager storage = world.getSavedData();
		Supplier<MySavedData> sup = new MySavedData();
		MySavedData saver = (MySavedData) storage.getOrCreate(sup, ModQuestion.MODID);

		if (saver == null) 
		{
			saver = new MySavedData();
			storage.set(saver);
		}
		return saver;
	}
	
	@Override
	public Object get()
	{
		return this;
	}
}

 

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.