Jump to content

[Solved]read/write Entity NBT for both sides?


gff1979

Recommended Posts

I'm writing a mod that creates a mob with visual attributes but I'm having a time figuring out how to save them for persistence with NBTTagCompounds. I think what is happening is that either the server or the client one is not saving nbt data but the other is. I'm also confused at to which is doing what and exactly what data is returned through isRemote. I thought that my server/client proxy class declarations might have been switched but I cannot find where they might be switched at. Do I try and capture the one Entity that's NBT value IS being saved and try to set the one that isn't to that value somehow? If so, what is the best way to do that?

 

package gff.companime.entity.companions;

import java.util.Random;

import gff.companime.Companime;
import net.minecraft.entity.EntityAgeable;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.ai.EntityAIAvoidEntity;
import net.minecraft.entity.ai.EntityAIFollowGolem;
import net.minecraft.entity.ai.EntityAILookAtTradePlayer;
import net.minecraft.entity.ai.EntityAIMoveIndoors;
import net.minecraft.entity.ai.EntityAIMoveTwardsRestriction;
import net.minecraft.entity.ai.EntityAIOpenDoor;
import net.minecraft.entity.ai.EntityAIPlay;
import net.minecraft.entity.ai.EntityAIRestrictOpenDoor;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAITempt;
import net.minecraft.entity.ai.EntityAITradePlayer;
import net.minecraft.entity.ai.EntityAIVillagerMate;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.ai.EntityAIWatchClosest2;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;

public class EntityGal extends EntityAgeable
{

public int haircolor;
public int eyecolor;

public EntityGal(World par1World)
    {
        super(par1World);
        this.texture = "/textures/mob/gal.png";
//        this.randomTickDivider = 0;
//        this.isMating = false;
//        this.isPlaying = false;
    
        this.moveSpeed = 0.5F;
        this.setSize(0.6F, 1.8F);
        this.getNavigator().setBreakDoors(true);
        this.getNavigator().setAvoidsWater(true);
        this.tasks.addTask(0, new EntityAISwimming(this));
        this.tasks.addTask(1, new EntityAITempt(this, 0.3F, Companime.braceletItem.itemID, false));
        this.tasks.addTask(2, new EntityAIMoveIndoors(this));        
        this.tasks.addTask(3, new EntityAIOpenDoor(this, true));
        this.tasks.addTask(4, new EntityAIMoveTwardsRestriction(this, 0.3F));
        this.tasks.addTask(5, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F));
        this.tasks.addTask(5, new EntityAIWatchClosest2(this, EntityGal.class, 5.0F, 0.02F));
        this.tasks.addTask(5, new EntityAIWander(this, 0.3F));
        this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityGal.class, 8.0F));
        
        genFeatures();
    }

public void genFeatures()
{
	Random rnd = new Random();
	this.haircolor = rnd.nextInt(0xFFFFFF);
	this.eyecolor = rnd.nextInt(0xFFFFFF);		
}

@Override
public EntityAgeable createChild(EntityAgeable entityageable) {
	// TODO Auto-generated method stub
	return null;
}

@Override
public int getMaxHealth() {
	// TODO Auto-generated method stub
	return 10;
}

public boolean interact(EntityPlayer par1EntityPlayer)
    {
        ItemStack itemstack = par1EntityPlayer.inventory.getCurrentItem();

        System.out.println(this);
        System.out.println("ID: "+this.entityId);
        System.out.println("isRemote = "+this.worldObj.isRemote);
        System.out.println("Hair color = "+this.haircolor);
                          
        return true;
    }

// NBT for persisting attributes for this instance of a Gal
    public void writeEntityToNBT(NBTTagCompound nbt)
    {
    	super.writeEntityToNBT(nbt);
    	
    	nbt.setInteger("haircolor", this.haircolor);    	    
    	
    	
    	
    }
    
    public void readEntityFromNBT(NBTTagCompound nbt)
    {
    	super.readEntityFromNBT(nbt);
    	
    	this.haircolor = nbt.getInteger("haircolor");
    	
    	    	    	
    	// call to check for non initialized persistant variables.    	
    }    
}


 

These are the ouputs that my right click does on the Entity:

 

After right clicking the entities in a newly created world:

2013-06-26 02:41:15 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7406, l='MpServer', x=202.84, y=64.00, z=-22.14]

2013-06-26 02:41:15 [iNFO] [sTDOUT] ID: 7406

2013-06-26 02:41:15 [iNFO] [sTDOUT] isRemote = true

2013-06-26 02:41:15 [iNFO] [sTDOUT] Hair color = 15120629

2013-06-26 02:41:15 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7406, l='New World', x=202.74, y=64.00, z=-21.63]

2013-06-26 02:41:15 [iNFO] [sTDOUT] ID: 7406

2013-06-26 02:41:15 [iNFO] [sTDOUT] isRemote = false

2013-06-26 02:41:15 [iNFO] [sTDOUT] Hair color = 9323139

2013-06-26 02:41:16 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7409, l='MpServer', x=203.63, y=64.00, z=-23.59]

2013-06-26 02:41:16 [iNFO] [sTDOUT] ID: 7409

2013-06-26 02:41:16 [iNFO] [sTDOUT] isRemote = true

2013-06-26 02:41:16 [iNFO] [sTDOUT] Hair color = 4058533

2013-06-26 02:41:16 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7409, l='New World', x=203.62, y=64.00, z=-24.09]

2013-06-26 02:41:16 [iNFO] [sTDOUT] ID: 7409

2013-06-26 02:41:16 [iNFO] [sTDOUT] isRemote = false

2013-06-26 02:41:16 [iNFO] [sTDOUT] Hair color = 12925493

2013-06-26 02:41:18 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7411, l='MpServer', x=200.50, y=64.00, z=-25.50]

2013-06-26 02:41:18 [iNFO] [sTDOUT] ID: 7411

2013-06-26 02:41:18 [iNFO] [sTDOUT] isRemote = true

2013-06-26 02:41:18 [iNFO] [sTDOUT] Hair color = 15959885

2013-06-26 02:41:18 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7411, l='New World', x=200.50, y=64.00, z=-25.50]

2013-06-26 02:41:18 [iNFO] [sTDOUT] ID: 7411

2013-06-26 02:41:18 [iNFO] [sTDOUT] isRemote = false

2013-06-26 02:41:18 [iNFO] [sTDOUT] Hair color = 4745496

Second Click after exit to menu and restarting the same save:

2013-06-26 02:41:28 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7573, l='MpServer', x=207.23, y=63.00, z=-19.99]

2013-06-26 02:41:28 [iNFO] [sTDOUT] ID: 7573

2013-06-26 02:41:28 [iNFO] [sTDOUT] isRemote = true

2013-06-26 02:41:28 [iNFO] [sTDOUT] Hair color = 14664608

2013-06-26 02:41:28 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7573, l='New World', x=207.66, y=63.00, z=-20.27]

2013-06-26 02:41:28 [iNFO] [sTDOUT] ID: 7573

2013-06-26 02:41:28 [iNFO] [sTDOUT] isRemote = false

2013-06-26 02:41:28 [iNFO] [sTDOUT] Hair color = 9323139

2013-06-26 02:41:31 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7574, l='MpServer', x=205.88, y=64.00, z=-26.38]

2013-06-26 02:41:31 [iNFO] [sTDOUT] ID: 7574

2013-06-26 02:41:31 [iNFO] [sTDOUT] isRemote = true

2013-06-26 02:41:31 [iNFO] [sTDOUT] Hair color = 16074411

2013-06-26 02:41:31 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7574, l='New World', x=205.85, y=64.00, z=-26.36]

2013-06-26 02:41:31 [iNFO] [sTDOUT] ID: 7574

2013-06-26 02:41:31 [iNFO] [sTDOUT] isRemote = false

2013-06-26 02:41:31 [iNFO] [sTDOUT] Hair color = 12925493

2013-06-26 02:41:32 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7575, l='MpServer', x=200.50, y=64.00, z=-25.50]

2013-06-26 02:41:32 [iNFO] [sTDOUT] ID: 7575

2013-06-26 02:41:32 [iNFO] [sTDOUT] isRemote = true

2013-06-26 02:41:32 [iNFO] [sTDOUT] Hair color = 6780648

2013-06-26 02:41:32 [iNFO] [sTDOUT] EntityGal['entity.Gal.name'/7575, l='New World', x=200.50, y=64.00, z=-25.50]

2013-06-26 02:41:32 [iNFO] [sTDOUT] ID: 7575

2013-06-26 02:41:32 [iNFO] [sTDOUT] isRemote = false

2013-06-26 02:41:32 [iNFO] [sTDOUT] Hair color = 4745496

 

As shown, the second of each entity is saving the data properly and it states that it's the client side world or "New World". The Entities world that isn't saving is MpServer.

 

I'm a bit confused as to what is going on. My assumption is that there are two instances of the entity one for client and one for server. As I said I'm not sure what exactly isRemote returns. Is it true if the entity is on MpServer? Because the documentation in my IDE states that it should return false, yet my entity's println(this) would state otherwise.

 

 

My Entity Render: (this renders hair color based on the entity.haircolor value, but doesn't seem to use the version that gets saved...)

package gff.companime.client.renderer.entity;

import java.awt.Color;

import org.lwjgl.opengl.GL11;

import gff.companime.entity.companions.EntityGal;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.passive.EntitySheep;

@SideOnly(Side.CLIENT)
public class RenderGal extends RenderLiving 
{

public RenderGal(ModelBase modelGal, ModelBase modelHair, float par2) 
{
	super(modelGal, par2);
	this.setRenderPassModel(modelHair);
}

protected int setHairColor(EntityGal gal, int par2, float par3)
    {
        this.loadTexture("/textures/mob/longhair.png");
        
        Color c = new Color(gal.haircolor);
        byte red = (byte) c.getRed();
        byte green = (byte) c.getGreen();
        byte blue = (byte) c.getBlue();
        
        GL11.glColor3b(red,green,blue);
        return 1;     
    }
    /**
     * Queries whether should render the specified pass or not.
     */
    protected int shouldRenderPass(EntityLiving par1EntityLiving, int par2, float par3)
    {
        return this.setHairColor((EntityGal)par1EntityLiving, par2, par3);
    }

}

 

My mod class:

package gff.companime; //The package your mod is in

import java.util.logging.Logger;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.Configuration;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.Init;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.Mod.PreInit;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkMod;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import cpw.mods.fml.common.network.NetworkMod.SidedPacketHandler;
import cpw.mods.fml.common.SidedProxy;
import gff.companime.*;
import gff.companime.block.PetrifiedWood;
import gff.companime.entity.*;
import gff.companime.entity.companions.EntityGal;
import gff.companime.entity.companions.EntityGalBase;
import gff.companime.item.Bracelet;
import gff.companime.server.CommonProxy;

@NetworkMod(clientSideRequired=true,serverSideRequired=false, //Whether client side and server side are needed
clientPacketHandlerSpec = @SidedPacketHandler(channels = {"NitroMod" }, packetHandler = ClientPacketHandler.class), //For clientside packet handling
serverPacketHandlerSpec = @SidedPacketHandler(channels = {"NitroMod" }, packetHandler = ServerPacketHandler.class)) //For serverside packet handling

@Mod(modid=Companime.modid,name="Companime",version="0.0.1")

public class Companime // Mod class
{ 

public static final String modid = "gff_Companime"; // I think this is proper use but I don't fully grok

// * Create instances for use in Registration *

// Tiles:

// blocks:
public final static Block petrifiedWood = new PetrifiedWood(500, Material.rock);

// Items:
public final static Item braceletItem = new Bracelet(500);

// Entities:
// public final static EntityGal gal = new EntityGal(null);	

@Instance( modid ) //The instance, this is very important later on
public static Companime instance = new Companime();

@SidedProxy(clientSide = "gff.companime.client.ClientProxy", serverSide = "gff.companime.server.CommonProxy")
public static CommonProxy proxy;

public static Logger logger;

// * Methods *
@PreInit
public void preInit(FMLPreInitializationEvent event) 
{
	logger = Logger.getLogger(modid);
	logger.setParent(FMLLog.getLogger());

	Configuration config = new Configuration(
			event.getSuggestedConfigurationFile());
	//Configuration.load(config);
}

@Init
//	public void InitCobaltCraft(FMLInitializationEvent event){ //Your main initialization method
public void load(FMLInitializationEvent event)
{ //Your main initialization method									

	NetworkRegistry.instance().registerGuiHandler(this, proxy); //Registers the class that deals with GUI data

	proxy.registerRenderInformation();

	proxy.registerTiles();

	proxy.registerBlocks();

	proxy.addNames();

	proxy.addRecipes();

	proxy.addEntities();

	// Register entities? proxy.addEntities(); ?
//		 EntityRegistry.registerModEntity(EntityGal.class, "Gal", 2, this, 80, 3, true);
//	     EntityRegistry.addSpawn(EntityGal.class, 5, 2, 6, EnumCreatureType.creature, BiomeGenBase.beach);
//	     LanguageRegistry.instance().addStringLocalization("name.gal.entity", "Gal");

}

}

 

ClientProxy: (currently just registers the entity and spawn egg data)

 

package gff.companime.client;

import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.registry.EntityRegistry;
import gff.companime.*;
import gff.companime.client.model.*;
import gff.companime.client.renderer.entity.RenderGal;
import gff.companime.client.renderer.entity.RenderGalBase;
import gff.companime.entity.*;
import gff.companime.entity.companions.EntityGal;
import gff.companime.entity.companions.EntityGalBase;
import gff.companime.server.CommonProxy;
import net.minecraftforge.client.MinecraftForgeClient;

public class ClientProxy extends CommonProxy {

@Override
public void registerRenderInformation()
{		
//		// Register Models
//		// Test Gal
//		RenderingRegistry.registerEntityRenderingHandler(EntityGalBase.class, new RenderGalBase(new ModelGalBase(), new ModelGalBase(), 0.5F));				
//		EntityRegistry.registerGlobalEntityID(EntityGalBase.class, "TestGal", EntityRegistry.findGlobalUniqueEntityId() , 3515848, 0xFF8020);
	// Real Gal
	RenderingRegistry.registerEntityRenderingHandler(EntityGal.class, new RenderGal(new ModelGal(), new ModelLongHair(), 0.5F));				
	EntityRegistry.registerGlobalEntityID(EntityGal.class, "Gal", EntityRegistry.findGlobalUniqueEntityId() , 0xd4a98a, 0xd4a98a);
}

}

 

CommonProxy:

package gff.companime.server;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.FurnaceRecipes;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.network.IGuiHandler;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
import gff.companime.*;
import gff.companime.item.*;
import gff.companime.block.*;
import gff.companime.entity.companions.*;

public class CommonProxy implements IGuiHandler //THIS IS IMPORTANT, CANNOT BE A PROXY/GUI HANDLER WITHOUT THIS!!
{ 	

// Methods
public void registerRenderInformation() //Client side texture registering
{

}

@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) 
{ //For GUI's
return null;
}

@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) 
{ //For GUI's
	return null;
}

public void registerTiles()
{ //For registering TileEntities

}

public void registerBlocks()
{ //For registering Blocks

	// Petrified Wood Block

	LanguageRegistry.addName(Companime.petrifiedWood, "Petrified Wood");
	MinecraftForge.setBlockHarvestLevel(Companime.petrifiedWood, "axe", 0);
}

public void addNames() // Change to addItems and handle more than just names soon...
{ //For adding Item's in game names

	// Friendship bracelet item

	LanguageRegistry.addName(Companime.braceletItem, "Friendship Bracelet");
}

public void addRecipes()
{ //For adding your Item's recipes

	// Friendship Bracelet Recipes
	ItemStack silkString = new ItemStack(Item.silk);		

	GameRegistry.addRecipe( new ItemStack(Companime.braceletItem) ,
	"xxx", 
	"x x",
	"xxx",
	Character.valueOf('x') , silkString);
}

public void addEntities()
{

	// Gal		
	EntityRegistry.registerModEntity(EntityGal.class, "Gal", 2, Companime.instance, 30, 3, true);
	EntityRegistry.addSpawn(EntityGal.class, 5, 2, 6, EnumCreatureType.creature, BiomeGenBase.beach);		
	LanguageRegistry.instance().addStringLocalization("name.gal.entity", "Gal");
}

}

 

Any help would be appreciated even if it's something simple or relates to something else in the code as I just started learning MCForge this weekend...

Link to comment
Share on other sites

"isRemote" returns true if the world is client-side. what's actually saving correctly in your post is the server side. so, when you exit and return, the server loads what's saved on the nbt, but the client side generates a new random hair color. i'm not too familiar with mc's entity stuff, but i've played around with it a bit and i think you can use the data watcher to fix that. if not, then maybe you can send packets from the server to the client containing the entity id and hair color

Link to comment
Share on other sites

Thanks for the help. I have a working implementation and just to help anyone here's the simplified version of my EntityGal implementation.

package gff.companime.entity.companions;

import java.util.Random;

import gff.companime.Companime;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.DataWatcher;
import net.minecraft.entity.EntityAgeable;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.ai.EntityAIAvoidEntity;
import net.minecraft.entity.ai.EntityAIFollowGolem;
import net.minecraft.entity.ai.EntityAILookAtTradePlayer;
import net.minecraft.entity.ai.EntityAIMoveIndoors;
import net.minecraft.entity.ai.EntityAIMoveTwardsRestriction;
import net.minecraft.entity.ai.EntityAIOpenDoor;
import net.minecraft.entity.ai.EntityAIPlay;
import net.minecraft.entity.ai.EntityAIRestrictOpenDoor;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAITempt;
import net.minecraft.entity.ai.EntityAITradePlayer;
import net.minecraft.entity.ai.EntityAIVillagerMate;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.ai.EntityAIWatchClosest2;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;

public class EntityGal extends EntityAgeable
{
public int haircolor;

public EntityGal(World par1World)
    {
        super(par1World);
        this.texture = "/textures/mob/gal/gal.png";
        this.moveSpeed = 0.5F;
        this.setSize(0.6F, 1.8F);
        this.getNavigator().setBreakDoors(true);
        this.getNavigator().setAvoidsWater(true);
        // AI tasks for the entity to perform
        this.tasks.addTask(0, new EntityAISwimming(this));
        this.tasks.addTask(1, new EntityAITempt(this, 0.3F, Companime.braceletItem.itemID, false));
        this.tasks.addTask(2, new EntityAIMoveIndoors(this));        
        this.tasks.addTask(3, new EntityAIOpenDoor(this, true));
        this.tasks.addTask(4, new EntityAIMoveTwardsRestriction(this, 0.3F));
        this.tasks.addTask(5, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F));
        this.tasks.addTask(5, new EntityAIWatchClosest2(this, EntityGal.class, 5.0F, 0.02F));
        this.tasks.addTask(5, new EntityAIWander(this, 0.3F));
        this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityGal.class, 8.0F));
        
        // Generate instance specific data
        Random rnd = new Random();
        this.setHairColor(rnd.nextInt(0xffffff));
        this.haircolor = this.getHairColor();

    }

    public void entityInit()
    {
        super.entityInit();
        // Create a data watcher for hair color
        this.dataWatcher.addObject(20, Integer.valueOf(0));
        
    }

// writes persistent values that is saved to the world server side
    public void writeEntityToNBT(NBTTagCompound nbt)
    {
    	super.writeEntityToNBT(nbt);
    	
    	nbt.setInteger("haircolor", this.getHairColor());  	        	    	
    }
    
// reads a persistant value from the server's world
    public void readEntityFromNBT(NBTTagCompound nbt)
    {
    	super.readEntityFromNBT(nbt);
    	
    	this.setHairColor(nbt.getInteger("haircolor"));        	    	       
    }   
    
    // This function will set an RGB color
    // It is called later with EntityGal.getHairColor() in the renderer
    public void setHairColor(int color)
    {
        this.dataWatcher.updateObject(20, Integer.valueOf(color));
    }
    
    // Returns the value set by setHairColor as "haircolor" in the Entities NBTTagCompound
    public int getHairColor()
    {
        return this.dataWatcher.getWatchableObjectInt(20);
    }
    
@Override
public EntityAgeable createChild(EntityAgeable entityageable) {
	// TODO Auto-generated method stub
	return null;
}

@Override
public int getMaxHealth() {
	// TODO Auto-generated method stub
	return 10;
}

public boolean interact(EntityPlayer par1EntityPlayer)
    {
        ItemStack itemstack = par1EntityPlayer.inventory.getCurrentItem();
        
        System.out.println(this.worldObj);
        System.out.println("ID: "+this.entityId);
        System.out.println("isRemote = "+this.worldObj.isRemote);
        System.out.println(this.getHairColor()); // Example of how to get this entity haircolor
        return true;
    }

}

 

I learned that a DataWatcher instance can only have IDs from 0 to 31 and that if your child class is using the parents instance of datawatcher it shouldn't use any ids from the parent. These throw exceptions. Also, don't write to them from the Client or if isRemote is true. They are made to store from client then update from server so they throw an exception when written/updated clientside.

 

It's a bit of code to have to write a get/set for each persistent parameter if you have more than a handful of them but it works. If I'm missing something or anyone has more advice it would be appreciated.

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.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • rp.crazyheal.xyz mods  
    • I'm developing a dimension, but it's kinda resource intensive so some times during player teleporting it lags behind making the player phase down into the void, so im trying to implement some kind of pregeneration to force the game loading a small set of chunks in the are the player will teleport to. Some of the things i've tried like using ServerLevel and ServerChunkCache methods like getChunk() dont actually trigger chunk generation if the chunk isn't already on persistent storage (already generated) or placing tickets, but that doesn't work either. Ideally i should be able to check when the task has ended too. I've peeked around some pregen engines, but they're too complex for my current understanding of the system of which I have just a basic understanding (how ServerLevel ,ServerChunkCache  and ChunkMap work) of. Any tips or other classes I should be looking into to understand how to do this correctly?
    • https://mclo.gs/4UC49Ao
    • Way back in the Forge 1.17 days, work started for adding JPMS (Java Platform Module Support) to ModLauncher and ForgeModLoader. This has been used internally by Forge and some libraries for a while now, but mods (those with mods.toml specifically) have not been able to take advantage of it. As of Forge 1.21.1 and 1.21.3, this is now possible!   What is JPMS and what does it mean for modders? JPMS is the Java Platform Module System, introduced in Java 9. It allows you to define modules, which are collections of packages and resources that can be exported or hidden from other modules. This allows for much more fine-tuned control over visibility, cleaner syntax for service declarations and support for sealed types across packages. For example, you might have a mod with a module called `com.example.mod` that exports `com.example.mod.api` and `com.example.mod.impl` to other mods, but hides `com.example.mod.internal` from them. This would allow you to have a clean API for other mods to use, while keeping your internal implementation details hidden from IDE hints, helping prevent accidental usage of internals that might break without prior notice. This is particularly useful if you'd like to use public records with module-private constructors or partially module-private record components, as you can create a sealed interface that only your record implements, having the interface be exported and the record hidden. It's also nice for declaring and using services, as you'll get compile-time errors from the Java compiler for typos and the like, rather than deferring to runtime errors. In more advanced cases, you can also have public methods that are only accessible to specific other modules -- handy if you want internal interactions between multiple of your own mods.   How do I bypass it? We understand there may be drama in implementing a system that prevents mods from accessing each other's internals when necessary (like when a mod is abandoned or you need to fix a compat issue) -- after all, we are already modding a game that doesn't have explicit support for Java mods yet. We have already thought of this and are offering APIs from day one to selectively bypass module restrictions. Let me be clear: Forge mods are not required to use JPMS. If you don't want to use it, you don't have to. The default behaviour is to have fully open, fully exported automatic modules. In Java, you can use the `Add-Opens` and `Add-Exports` manifest attributes to selectively bypass module restrictions of other mods at launch time, and we've added explicit support for these when loading your Forge mods. At compile-time, you can use existing solutions such as the extra-java-module-info Gradle plugin to deal with non-modular dependencies and add extra opens and exports to other modules. Here's an example on how to make the internal package `com.example.examplemod.internal` open to your mod in your build.gradle: tasks.named('jar', Jar) { manifest { attributes([ 'Add-Opens' : 'com.example.examplemod/com.example.examplemod.internal' 'Specification-Title' : mod_id, 'Specification-Vendor' : mod_authors // (...) ]) } } With the above in your mod's jar manifest, you can now reflectively access the classes inside that internal package. Multiple entries are separated with a space, as per Java's official spec. You can also use Add-Exports to directly call without reflection, however you'd need to use the Gradle plugin mentioned earlier to be able to compile. The syntax for Add-Exports is the same as Add-Opens, and instructions for the compile-time step with the Gradle plugin are detailed later in this post. Remember to prefer the opens and exports keywords inside module-info.java for sources you control. The Add-Opens/Add-Exports attributes are only intended for forcing open other mods.   What else is new with module support? Previously, the runtime module name was always forced to the first mod ID in your `mods.toml` file and all packages were forced fully open and exported. Module names are now distinguished from mod IDs, meaning the module name in your module-info.java can be different from the mod ID in your `mods.toml`. This allows you to have a more descriptive module name that doesn't have to be the same as your mod ID, however we strongly recommend including your mod ID as part of your module name to aid troubleshooting. The `Automatic-Module-Name` manifest attribute is now also honoured, allowing you to specify a module name for your mod without needing to create a `module-info.java` file. This is particularly useful for mods that don't care about JPMS features but want to have a more descriptive module name and easier integration with other mods that do use JPMS.   How do I use it? The first step is to create a `module-info.java` file in your mod's source directory. This file should be in the same package as your main mod class, and should look something like this: open module com.example.examplemod { requires net.minecraftforge.eventbus; requires net.minecraftforge.fmlcore; requires net.minecraftforge.forge; requires net.minecraftforge.javafmlmod; requires net.minecraftforge.mergetool.api; requires org.slf4j; requires logging; } For now, we're leaving the whole module open to reflection, which is a good starting point. When we know we want to close something off, we can remove the open modifier from the module and open or export individual packages instead. Remember that you need to be open to Forge (module name net.minecraftforge.forge), otherwise it can't call your mod's constructor. Next is fixing modules in Gradle. While Forge and Java support modules properly, Gradle does not put automatic modules on the module path by default, meaning that the logging module (from com.mojang:logging) is not found. To fix this, add the Gradle plugin and add a compile-time module definition for that Mojang library: plugins { // (...) id 'org.gradlex.extra-java-module-info' version "1.9" } // (...) extraJavaModuleInfo { failOnMissingModuleInfo = false automaticModule("com.mojang:logging", "logging") } The automatic module override specified in your build.gradle should match the runtime one to avoid errors. You can do the same for any library or mod dependency that is missing either a module-info or explicit Automatic-Module-Name, however be aware that you may need to update your mod once said library adds one. That's all you need to get started with module support in your mods. You can learn more about modules and how to use them at dev.java.
    • Faire la mise à jour grâce à ce lien m'a aider personnellement, merci à @Paint_Ninja. https://www.amd.com/en/support 
  • Topics

×
×
  • Create New...

Important Information

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