Jump to content

Recommended Posts

Posted

So I am creating a mod, and I want an extra armor slot, however the tutorials out there are good until they get to packet handling, and its not just confusing for me but for others too. So I come here today to see if anyone would help write this for me. Anything to do with just adding one extra armor slot. PM for more info if you are truly interested because I understand that this is a lot to ask for, I am just done with trying to figure this out.

Here is my main mod file

 

package com.phantasyrealms.main;

 

import sun.net.NetworkServer;

import net.minecraftforge.common.MinecraftForge;

 

import com.phantasyrealms.entity.EntityBoomaMob;

import com.phantasyrealms.entity.PhantasyRealmsEntity;

import com.phantasyrealms.eventhandler.PhantasyEventHandler;

import com.phantasyrealms.item.PhantasyRealmsItems;

import com.phantasyrealms.lib.Strings;

 

import cpw.mods.fml.common.FMLCommonHandler;

import cpw.mods.fml.common.Mod;

import cpw.mods.fml.common.Mod.EventHandler;

import cpw.mods.fml.common.Mod.Instance;

import cpw.mods.fml.common.SidedProxy;

import cpw.mods.fml.common.event.FMLInitializationEvent;

import cpw.mods.fml.common.event.FMLPostInitializationEvent;

import cpw.mods.fml.common.event.FMLPreInitializationEvent;

import cpw.mods.fml.common.network.NetworkRegistry;

import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;

import cpw.mods.fml.relauncher.Side;

 

@Mod(modid = Strings.MODID, name = Strings.name, version = Strings.version)

 

public class MainRegistry {

 

@SidedProxy(clientSide = "com.phantasyrealms.main.ClientProxy", serverSide = "com.phantasyrealms.main.ServerProxy")

public static ServerProxy proxy;

 

 

@Instance(Strings.MODID)

public static MainRegistry modInstance;

public static Object instance;

 

 

 

public class RegisterKeyBindings

{

 

}

 

/**

* Loads before

* @param PostEvent

*/

 

@EventHandler

public static void PreLoad(FMLPreInitializationEvent PreEvent){

PhantasyRealmsEntity.mainRegistry();

proxy.registerRenderThings();

PhantasyRealmsItems.mainRegistry();

CraftingManager.mainRegistry();

}

 

 

 

/**

* Loads during

* @param event

*/

@EventHandler

public static void load(FMLInitializationEvent event){

 

 

}

 

/**

* Loads after

* @param PostEvent

*/

@EventHandler

public static void PostLoad(FMLPostInitializationEvent PostEvent){

 

}

 

}

 

Posted

I don't see how giving us Main mod file is gonna help.

Anyway - did you even have inventory made?

 

Howto:

 

Use IEntityExtendedProperties to store additional slots to player data.

Use IInventory to make inv.

Use IGuiHandler to open inv server/client-side.

Use Packets to make updates:

- When using IGuiHandler you open a container on server side and gui on client, hence you will need to send custom slot update packet to all players everytime something is edited in container. (on item move inside container (slots), do: sendToAll(new UpdatePacket(player, slotIndex)).

public UpdatePacket(EntityPlayer player, int slotIndex)
{
	this.playerId = player.getEntityId();
	this.slot = slot;
	this.stack = ExPlayer.get(player).getCustomInv().getStackInSlot(slot);
}

This will be send to all clients, and all clients will update client-side ExtendedPlayer data for given playerId.

 

To see how to make such packets you will need this:

http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2137055-1-7-2-customizing-packet-handling-with

This link also contains pretty much everything you need to dig into IEEP and containers.

 

Useful tools:

this.stack = pb.readItemStackFromBuffer();
pb.writeItemStackToBuffer(this.stack);

In handler: Use this to get player from snt id.
Entity p = world.getEntityByID(message.playerId);

Remember casting and null checks.

 

Note: This is pretty Intermidiate/Advanced stuff and will take some time to make, so make sure you know what you can/want.

1.7.10 is no longer supported by forge, you are on your own.

Posted

I don't see how giving us Main mod file is gonna help.

Anyway - did you even have inventory made?

 

Howto:

 

Use IEntityExtendedProperties to store additional slots to player data.

Use IInventory to make inv.

Use IGuiHandler to open inv server/client-side.

Use Packets to make updates:

- When using IGuiHandler you open a container on server side and gui on client, hence you will need to send custom slot update packet to all players everytime something is edited in container. (on item move inside container (slots), do: sendToAll(new UpdatePacket(player, slotIndex)).

public UpdatePacket(EntityPlayer player, int slotIndex)
{
	this.playerId = player.getEntityId();
	this.slot = slot;
	this.stack = ExPlayer.get(player).getCustomInv().getStackInSlot(slot);
}

This will be send to all clients, and all clients will update client-side ExtendedPlayer data for given playerId.

 

To see how to make such packets you will need this:

http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2137055-1-7-2-customizing-packet-handling-with

This link also contains pretty much everything you need to dig into IEEP and containers.

 

Useful tools:

this.stack = pb.readItemStackFromBuffer();
pb.writeItemStackToBuffer(this.stack);

In handler: Use this to get player from snt id.
Entity p = world.getEntityByID(message.playerId);

Remember casting and null checks.

 

Note: This is pretty Intermidiate/Advanced stuff and will take some time to make, so make sure you know what you can/want.

 

I have an Inventory made as I followed CoolAlias's Custom Inventory tutorial, however the trouble I run into is again, the Packethandler, his is for 1.6 and his tut for updating it is too vague and confusing to follow, and when trying to link his own version of a packet handler to the custom inventory tut I also get confused. I want one extra armor slot. As for putting my main class, I thought it would help as a reference to what to put.

Posted

His tutorials are probably best you'll find. I alredy pointed out:

"Note: This is pretty Intermidiate/Advanced stuff and will take some time to make, so make sure you know what you can/want."

 

Write something, post your code and error, we won't write whole mod for you. I wrote you everything you should learn to make this, now go and read some vanilla/forge code, it is not "making of block" and will need you to UNDERSTAND the code, not blindly follow method names and guessing. (use Syso and check caller methods to see how it's being processed).

 

If you'll need more accurate help then post error/code with more not-so-general question.

 

P.S: His tutorials are updated to 1.8/1.7.10 and if you spend at least few hours you could easily make it work following steps and then experimenting.

1.7.10 is no longer supported by forge, you are on your own.

Posted

His tutorials are probably best you'll find. I alredy pointed out:

"Note: This is pretty Intermidiate/Advanced stuff and will take some time to make, so make sure you know what you can/want."

 

Write something, post your code and error, we won't write whole mod for you. I wrote you everything you should learn to make this, now go and read some vanilla/forge code, it is not "making of block" and will need you to UNDERSTAND the code, not blindly follow method names and guessing. (use Syso and check caller methods to see how it's being processed).

 

If you'll need more accurate help then post error/code with more not-so-general question.

 

P.S: His tutorials are updated to 1.8/1.7.10 and if you spend at least few hours you could easily make it work following steps and then experimenting.

 

Here is everything I have pertaining to custom inventory slot. I am not asking you to write a whole mod, this is just a small part of my mod, though one that makes that much more interesting.

Here is my MainRegistry

package com.phantasyrealms.main;

import sun.net.NetworkServer;
import net.minecraftforge.common.MinecraftForge;

import com.phantasyrealms.entity.EntityBoomaMob;
import com.phantasyrealms.entity.PhantasyRealmsEntity;
import com.phantasyrealms.eventhandler.PhantasyEventHandler;
import com.phantasyrealms.item.PhantasyRealmsItems;
import com.phantasyrealms.lib.Strings;

import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.relauncher.Side;

@Mod(modid = Strings.MODID, name = Strings.name, version = Strings.version)

public class MainRegistry {

@SidedProxy(clientSide = "com.phantasyrealms.main.ClientProxy", serverSide = "com.phantasyrealms.main.ServerProxy")
public static ServerProxy proxy;


@Instance(Strings.MODID)
public static MainRegistry modInstance;
public static Object instance;



public class RegisterKeyBindings
{

}

/**
 * Loads before
 * @param PostEvent
 */

@EventHandler
public static void PreLoad(FMLPreInitializationEvent PreEvent){
	PhantasyRealmsEntity.mainRegistry();
	proxy.registerRenderThings();
	PhantasyRealmsItems.mainRegistry();
	CraftingManager.mainRegistry();
}



/**
 * Loads during
 * @param event
 */
@EventHandler
public static void load(FMLInitializationEvent event){


}

/**
 * Loads after
 * @param PostEvent
 */
@EventHandler
public static void PostLoad(FMLPostInitializationEvent PostEvent){

}

}

Here is my PacketHandler

package com.phantasyrealms.packethandling;

import java.io.IOException;

import net.minecraft.entity.player.EntityPlayer;

import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.jcraft.jogg.Packet;

import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler;
import cpw.mods.fml.relauncher.Side;

public abstract class PacketHandler<T extends IMessage> implements IMessageHandler <T, IMessage> {

    public static final String CHANNEL = "myChannel";
    private static final BiMap<Integer, Class<? extends PacketHandler>> idMap;
    

            
    static {
            ImmutableBiMap.Builder<Integer, Class<? extends PacketHandler>> builder = ImmutableBiMap.builder();
            
            // we add all our packets here later
            
            idMap = builder.build();
    }
    
    public static PacketHandler constructPacket(int packetId) throws ProtocolException, ReflectiveOperationException {
        Class<? extends PacketHandler> clazz = idMap.get(Integer.valueOf(packetId));
        if (clazz == null) {
                throw new ProtocolException("Unknown Packet Id!");
        } else {
                return clazz.newInstance();
        }
}

public static class ProtocolException extends Exception {

        public ProtocolException() {
        }

        public ProtocolException(String message, Throwable cause) {
                super(message, cause);
        }

        public ProtocolException(String message) {
                super(message);
        }

        public ProtocolException(Throwable cause) {
                super(cause);
        }
}

public final int getPacketId() {
    if (idMap.inverse().containsKey(getClass())) {
            return idMap.inverse().get(getClass()).intValue();
    } else {
            throw new RuntimeException("Packet " + getClass().getSimpleName() + " is missing a mapping!");
    }
}

public final Packet makePacket() {
    ByteArrayDataOutput out = ByteStreams.newDataOutput();
    out.writeByte(getPacketId());
    write(out);
    return PacketDispatcher.getPacket(CHANNEL, out.toByteArray());
}
    
    
    public abstract void write(ByteArrayDataOutput out);
    
    public abstract void read(ByteArrayDataInput in);
    
    public abstract void execute(EntityPlayer player, Side side);
}

Here is my PacketDispatcher

package com.phantasyrealms.packethandling;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;

import com.jcraft.jogg.Packet;
import com.phantasyrealms.lib.Strings;
import com.phantasyrealms.main.MainRegistry;

import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.relauncher.Side;

/**
* 
* This class will house the SimpleNetworkWrapper instance, which I will name 'dispatcher',
* as well as give us a logical place from which to register our packets. These two things
* could be done anywhere, however, even in your Main class, but I will be adding other
* functionality (see below) that gives this class a bit more utility. 
* 
* While unnecessary, I'm going to turn this class into a 'wrapper' for SimpleNetworkWrapper
* so that instead of writing "PacketDispatcher.dispatcher.{method}" I can simply write
* "PacketDispatcher.{method}" All this does is make it quicker to type and slightly shorter;
* if you do not care about that, then make the 'dispatcher' field public instead of private,
* or, if you do not want to add a new class just for one field and one static method that
* you could put anywhere, feel free to put them wherever.
* 
* For further convenience, I have also added two extra sendToAllAround methods: one which
* takes an EntityPlayer and one which takes coordinates.
*
*/
public class PacketDispatcher
{
// a simple counter will allow us to get rid of 'magic' numbers used during packet registration
private static byte packetId = 0;

public static final SimpleNetworkWrapper dispatcher = NetworkRegistry.INSTANCE.newSimpleChannel(Strings.MODID);
/**
* Call this during pre-init or loading and register all of your packets (messages) here
*/
public static final void registerPackets() {
// Using an incrementing field instead of hard-coded numerals, I don't need to think
// about what number comes next or if I missed on should I ever rearrange the order
// of registration (for instance, if you wanted to alphabetize them... yeah...)
// It's even easier if you create a convenient 'registerMessage' method

// If you don't want to make a 'registerMessage' method, you can do it directly:
//PacketDispatcher.dispatcher.registerMessage(OpenGuiMessage.OpenGuiMessageHandler.class, OpenGuiMessage.class, packetId++, Side.SERVER);
//PacketDispatcher.dispatcher.registerMessage(SyncPlayerPropsMessage.SyncPlayerPropsMessageHandler.class, SyncPlayerPropsMessage.class, packetId++, Side.CLIENT);
}

/**
* Registers a message and message handler
*/
private static final void registerMessage(Class handlerClass, Class messageClass, Side side) {
PacketDispatcher.dispatcher.registerMessage(handlerClass, messageClass, packetId++, side);
}

//========================================================//
// The following methods are the 'wrapper' methods; again,
// this just makes sending a message slightly more compact
// and is purely a matter of stylistic preference
//========================================================//

/**
* Send this message to the specified player.
* See {@link SimpleNetworkWrapper#sendTo(IMessage, EntityPlayerMP)}
*/
public static final void sendTo(IMessage message, EntityPlayerMP player) {
PacketDispatcher.dispatcher.sendTo(message, player);
}

/**
* Send this message to everyone within a certain range of a point.
* See {@link SimpleNetworkWrapper#sendToDimension(IMessage, NetworkRegistry.TargetPoint)}
*/
public static final void sendToAllAround(IMessage message, NetworkRegistry.TargetPoint point) {
PacketDispatcher.dispatcher.sendToAllAround(message, point);

}

/**
* Sends a message to everyone within a certain range of the coordinates in the same dimension.
*/
public static final void sendToAllAround(IMessage message, int dimension, double x, double y, double z, 

double range) {
PacketDispatcher.sendToAllAround(message, new NetworkRegistry.TargetPoint(dimension, x, y, z, 

range));
}

/**
* Sends a message to everyone within a certain range of the player provided.
*/
public static final void sendToAllAround(IMessage message, EntityPlayer player, double range) {
PacketDispatcher.sendToAllAround(message, player.worldObj.provider.dimensionId, player.posX, 
player.posY, player.posZ, range);
}

/**
* Send this message to everyone within the supplied dimension.
* See {@link SimpleNetworkWrapper#sendToDimension(IMessage, int)}
*/
public static final void sendToDimension(IMessage message, int dimensionId) {
PacketDispatcher.dispatcher.sendToDimension(message, dimensionId);
}

/**
* Send this message to the server.
* See {@link SimpleNetworkWrapper#sendToServer(IMessage)}
*/
public static final void sendToServer(IMessage message) {
PacketDispatcher.dispatcher.sendToServer(message);
}

public static Packet getPacket(String channel, byte[] byteArray) {
// TODO Auto-generated method stub
return null;
}

}

Here is my ContainerCustomPlayer

package com.phantasyrealms.inventory;

import com.phantasyrealms.item.ItemMag;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemStack;

public class ContainerCustomPlayer extends Container
{
/** Avoid magic numbers! This will greatly reduce the chance of you making errors in 'transferStackInSlot' method */
private static final int ARMOR_START = InventoryCustomPlayer.INV_SIZE, ARMOR_END = ARMOR_START+3,
INV_START = ARMOR_END+1, INV_END = INV_START+26, HOTBAR_START = INV_END+1,
HOTBAR_END = HOTBAR_START+8;

public ContainerCustomPlayer(EntityPlayer player, InventoryPlayer inventoryPlayer, InventoryCustomPlayer inventoryCustom)
{
int i;

//Add CUSTOM slots - we'll just add two for now, both of the same type.
//Make a new Slot class for each different item type you want to add
this.addSlotToContainer(new Slot(inventoryCustom, 0, 80, );
this.addSlotToContainer(new Slot(inventoryCustom, 1, 80, 26));

//Add ARMOR slots; note you need to make a public version of SlotArmor
//just copy and paste the vanilla code into a new class and change what you need
for (i = 0; i < 4; ++i)
{
this.addSlotToContainer(new SlotMag(player, inventoryPlayer, inventoryPlayer.getSizeInventory() - 1 - i, 8, 8 + i * 18, i));
}

//Add vanilla PLAYER INVENTORY - just copied/pasted from vanilla classes
for (i = 0; i < 3; ++i)
{
for (int j = 0; j < 9; ++j)
{
this.addSlotToContainer(new Slot(inventoryPlayer, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
}
}

//Add ACTION BAR - just copied/pasted from vanilla classes
for (i = 0; i < 9; ++i)
{
this.addSlotToContainer(new Slot(inventoryPlayer, i, 8 + i * 18, 142));
}
}

/**
* This should always return true, since custom inventory can be accessed from anywhere
*/
@Override
public boolean canInteractWith(EntityPlayer player)
{
return true;
}

/**
* Called when a player shift-clicks on a slot. You must override this or you will crash when someone does that.
* Basically the same as every other container I make, since I define the same constant indices for all of them
*/
public ItemStack transferStackInSlot(EntityPlayer player, int par2)
{
ItemStack itemstack = null;
Slot slot = (Slot) this.inventorySlots.get(par2);

if (slot != null && slot.getHasStack())
{
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();

//Either armor slot or custom item slot was clicked
if (par2 < INV_START)
{
//try to place in player inventory / action bar
if (!this.mergeItemStack(itemstack1, INV_START, HOTBAR_END + 1, true))
{
return null;
}

slot.onSlotChange(itemstack1, itemstack);
}
//Item is in inventory / hotbar, try to place either in custom or armor slots
else
{
//if item is our custom item
if (itemstack1.getItem() instanceof ItemMag)
{
if (!this.mergeItemStack(itemstack1, 0, InventoryCustomPlayer.INV_SIZE, false))
{
return null;
}
}
//if item is armor
else if (itemstack1.getItem() instanceof ItemArmor)
{
int type = ((ItemArmor) itemstack1.getItem()).armorType;
if (!this.mergeItemStack(itemstack1, ARMOR_START + type, ARMOR_START + type + 1, false))
{
return null;
}
}
//item in player's inventory, but not in action bar
else if (par2 >= INV_START && par2 < HOTBAR_START)
{
//place in action bar
if (!this.mergeItemStack(itemstack1, HOTBAR_START, HOTBAR_START + 1, false))
{
return null;
}
}
//item in action bar - place in player inventory
else if (par2 >= HOTBAR_START && par2 < HOTBAR_END + 1)
{
if (!this.mergeItemStack(itemstack1, INV_START, INV_END + 1, false))
{
return null;
}
}
}

if (itemstack1.stackSize == 0)
{
slot.putStack((ItemStack) null);
}
else
{
slot.onSlotChanged();
}

if (itemstack1.stackSize == itemstack.stackSize)
{
return null;
}

slot.onPickupFromSlot(player, itemstack1);
}

return itemstack;
}
}

 

Here is my ExtendedPlayer

package com.phantasyrealms.inventory;

import java.io.DataOutputStream;

import ibxm.Player;

import org.apache.commons.io.output.ByteArrayOutputStream;
import com.phantasyrealms.main.MainRegistry;
import com.phantasyrealms.main.ServerProxy;
import com.phantasyrealms.packethandling.PacketHandler;

import cpw.mods.fml.common.FMLCommonHandler;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraftforge.common.IExtendedEntityProperties;

public class ExtendedPlayer implements IExtendedEntityProperties
{
/*
Here I create a constant EXT_PROP_NAME for this class of properties
You need a unique name for every instance of IExtendedEntityProperties
you make, and doing it at the top of each class as a constant makes
it very easy to organize and avoid typos. It's easiest to keep the same
constant name in every class, as it will be distinguished by the class
name: ExtendedPlayer.EXT_PROP_NAME vs. ExtendedEntity.EXT_PROP_NAME

Note that a single entity can have multiple extended properties, so each
property should have a unique name. Try to come up with something more
unique than the tutorial example.
*/
public final static String EXT_PROP_NAME = "ExtendedPlayer";
public final InventoryCustomPlayer inventory = new InventoryCustomPlayer();



/**
 * Used to register these extended properties for the player during EntityConstructing event
 * This method is for convenience only; it will make your code look nicer
 */
public static final void register(EntityPlayer player)
{
	player.registerExtendedProperties(ExtendedPlayer.EXT_PROP_NAME, new ExtendedPlayer());
}

/**
 * Returns ExtendedPlayer properties for player
 * This method is for convenience only; it will make your code look nicer
 */
public static final ExtendedPlayer get(EntityPlayer player)
{
	return (ExtendedPlayer) player.getExtendedProperties(EXT_PROP_NAME);
}

// Save any custom data that needs saving here
@Override
public void saveNBTData(NBTTagCompound compound)
{
	// We need to create a new tag compound that will save everything for our Extended Properties
	NBTTagCompound properties = new NBTTagCompound();
	// Write custom inventory to NBT
	this.inventory.writeToNBT(properties);

	compound.setTag(EXT_PROP_NAME, properties);

}

// Load whatever data you saved
@Override
public void loadNBTData(NBTTagCompound compound)
{
	NBTTagCompound properties = (NBTTagCompound) compound.getTag(EXT_PROP_NAME);
	this.inventory.readFromNBT(properties);
}

private static final String getSaveKey(EntityPlayer player) {
	// no longer a username field, so use the command sender name instead:
	return player.getCommandSenderName() + ":" + EXT_PROP_NAME;
	}

	public static final void loadProxyData(EntityPlayer player) {
	ExtendedPlayer playerData = ExtendedPlayer.get(player);
	NBTTagCompound savedData = ServerProxy.getEntityData(getSaveKey(player));
	if (savedData != null) { playerData.loadNBTData(savedData); }

	}

/*
I personally have yet to find a use for this method. If you know of any,
please let me know and I'll add it in! 
*/
@Override
public void init(Entity entity, World world)
{
}

public void sync() {
	// TODO Auto-generated method stub

}






}

 

Here is my GuiCustomPlayerInventory

package com.phantasyrealms.inventory;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;



import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.util.ResourceLocation;

public class GuiCustomPlayerInventory extends GuiContainer
{
/** x size of the inventory window in pixels. Defined as float, passed as int */
private float xSize_lo;

/** y size of the inventory window in pixels. Defined as float, passed as int. */
private float ySize_lo;

/** Normally I use '(ModInfo.MOD_ID, "textures/...")', but it can be done this way as well */
private static final ResourceLocation iconLocation = new ResourceLocation("tutorial:textures/gui/custom_inventory.png");

/** Could use IInventory type to be more generic, but this way will save an import... */
private final InventoryCustomPlayer inventory;

public GuiCustomPlayerInventory(EntityPlayer player, InventoryPlayer inventoryPlayer, InventoryCustomPlayer inventoryCustom)
{
super(new ContainerCustomPlayer(player, inventoryPlayer, inventoryCustom));
this.inventory = inventoryCustom;
//if you need the player for something later on, store it in a local variable here as well
}

/**
* Draws the screen and all the components in it.
*/
public void drawScreen(int par1, int par2, float par3)
{
super.drawScreen(par1, par2, par3);
this.xSize_lo = (float)par1;
this.ySize_lo = (float)par2;
}

/**
* Draw the foreground layer for the GuiContainer (everything in front of the items)
*/
protected void drawGuiContainerForegroundLayer(int par1, int par2)
{
//This method will simply draw inventory names on the screen - you could do without it entirely
//if that's not important to you, since we are overriding the default inventory rather than
//creating a specific type of inventory

String s = this.inventory.hasCustomInventoryName() ? this.inventory.getInventoryName() : I18n.format(this.inventory.getInventoryName());
//with the name "Custom Inventory", the 'Cu' will be drawn in the first slot
this.fontRendererObj.drawString(s, this.xSize - this.fontRendererObj.getStringWidth(s), 12, 4210752);
//this just adds "Inventory" above the player's inventory below
this.fontRendererObj.drawString(I18n.format("container.inventory"), 80, this.ySize - 96, 4210752);
}

/**
* Draw the background layer for the GuiContainer (everything behind the items)
*/
protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3)
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(iconLocation);
int k = (this.width - this.xSize) / 2;
int l = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize);
int i1;
drawPlayerModel(k + 51, l + 75, 30, (float)(k + 51) - this.xSize_lo, (float)(l + 75 - 50) - this.ySize_lo, this.mc.thePlayer);
}

/**
* This renders the player model in standard inventory position;
* copied straight from vanilla code but with renamed method parameters
*/
public static void drawPlayerModel(int x, int y, int scale, float yaw, float pitch, EntityLivingBase entity) {
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glPushMatrix();
GL11.glTranslatef(x, y, 50.0F);
GL11.glScalef(-scale, scale, scale);
GL11.glRotatef(180.0F, 0.0F, 0.0F, 1.0F);
float f2 = entity.renderYawOffset;
float f3 = entity.rotationYaw;
float f4 = entity.rotationPitch;
float f5 = entity.prevRotationYawHead;
float f6 = entity.rotationYawHead;
GL11.glRotatef(135.0F, 0.0F, 1.0F, 0.0F);
RenderHelper.enableStandardItemLighting();
GL11.glRotatef(-135.0F, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(-((float) Math.atan(pitch / 40.0F)) * 20.0F, 1.0F, 0.0F, 0.0F);
entity.renderYawOffset = (float) Math.atan(yaw / 40.0F) * 20.0F;
entity.rotationYaw = (float) Math.atan(yaw / 40.0F) * 40.0F;
entity.rotationPitch = -((float) Math.atan(pitch / 40.0F)) * 20.0F;
entity.rotationYawHead = entity.rotationYaw;
entity.prevRotationYawHead = entity.rotationYaw;
GL11.glTranslatef(0.0F, entity.yOffset, 0.0F);
RenderManager.instance.playerViewY = 180.0F;
RenderManager.instance.renderEntityWithPosYaw(entity, 0.0D, 0.0D, 0.0D, 0.0F, 1.0F);
entity.renderYawOffset = f2;
entity.rotationYaw = f3;
entity.rotationPitch = f4;
entity.prevRotationYawHead = f5;
entity.rotationYawHead = f6;
GL11.glPopMatrix();
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
}
}

Here is my InventoryCustomPlayer

package com.phantasyrealms.inventory;

import com.phantasyrealms.item.PhantasyRealmsItems;
import com.phantasyrealms.item.Saber;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;

public class InventoryCustomPlayer implements IInventory
{
/** The name your custom inventory will display in the GUI, possibly just "Inventory" */
private final String name = "Custom Inventory";

/** The key used to store and retrieve the inventory from NBT */
private final String tagName = "CustomInvTag";

/** Define the inventory size here for easy reference */
// This is also the place to define which slot is which if you have different types,
// for example SLOT_SHIELD = 0, SLOT_AMULET = 1;
public static final int INV_SIZE = 2;

/** Inventory's size must be same as number of slots you add to the Container class */
private ItemStack[] inventory = new ItemStack[iNV_SIZE];

public InventoryCustomPlayer()
{
	// don't need anything here!
}

@Override
public int getSizeInventory()
{
	return inventory.length;
}

@Override
public ItemStack getStackInSlot(int slot)
{
	return inventory[slot];
}

@Override
public ItemStack decrStackSize(int slot, int amount)
{
	ItemStack stack = getStackInSlot(slot);
	if (stack != null)
	{
		if (stack.stackSize > amount)
		{
			stack = stack.splitStack(amount);
			this.markDirty();
		}
		else
		{
			setInventorySlotContents(slot, null);
		}
	}
	return stack;
}

@Override
public ItemStack getStackInSlotOnClosing(int slot)
{
	ItemStack stack = getStackInSlot(slot);
	setInventorySlotContents(slot, null);
	return stack;
}

@Override
public void setInventorySlotContents(int slot, ItemStack itemstack)
{
	this.inventory[slot] = itemstack;

	if (itemstack != null && itemstack.stackSize > this.getInventoryStackLimit())
	{
		itemstack.stackSize = this.getInventoryStackLimit();
	}

	this.markDirty();
}

@Override
public String getInventoryName()
{
	return name;
}

@Override
public boolean hasCustomInventoryName()
{
	return name.length() > 0;
}

/**
 * Our custom slots are similar to armor - only one item per slot
 */
@Override
public int getInventoryStackLimit()
{
	return 1;
}

@Override
public void markDirty()
{
	for (int i = 0; i < getSizeInventory(); ++i)
	{
		if (getStackInSlot(i) != null && getStackInSlot(i).stackSize == 0) {
			inventory[i] = null;
		}
	}
}

@Override
public boolean isUseableByPlayer(EntityPlayer entityplayer)
{
	return true;
}

@Override
public void openInventory() {}

@Override
public void closeInventory() {}

/**
 * This method doesn't seem to do what it claims to do, as
 * items can still be left-clicked and placed in the inventory
 * even when this returns false
 */
@Override
public boolean isItemValidForSlot(int slot, ItemStack itemstack)
{
	// If you have different kinds of slots, then check them here:
	// if (slot == SLOT_SHIELD && itemstack.getItem() instanceof ItemShield) return true;

	// For now, only ItemUseMana items can be stored in these slots
	return itemstack.getItem() instanceof Saber;
}

public void writeToNBT(NBTTagCompound compound)
{
	NBTTagList items = new NBTTagList();

	for (int i = 0; i < getSizeInventory(); ++i)
	{
		if (getStackInSlot(i) != null)
		{
			NBTTagCompound item = new NBTTagCompound();
			item.setByte("Slot", (byte) i);
			getStackInSlot(i).writeToNBT(item);
			items.appendTag(item);
		}
	}

	// We're storing our items in a custom tag list using our 'tagName' from above
	// to prevent potential conflicts
	compound.setTag(tagName, items);
}

public void readFromNBT(NBTTagCompound compound) {
	// now you must include the NBTBase type ID when getting the list; NBTTagCompound's ID is 10
	NBTTagList items = compound.getTagList(tagName, compound.getId());
	for (int i = 0; i < items.tagCount(); ++i) {
		// tagAt(int) has changed to getCompoundTagAt(int)
		NBTTagCompound item = items.getCompoundTagAt(i);
		byte slot = item.getByte("Slot");
		if (slot >= 0 && slot < getSizeInventory()) {
			inventory[slot] = ItemStack.loadItemStackFromNBT(item);
		}
	}
}






}

 

Here is my KeyHandler

package com.phantasyrealms.inventory;



import org.lwjgl.input.Keyboard;

import com.phantasyrealms.main.MainRegistry;

import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent.KeyInputEvent;
import net.minecraft.client.gui.GuiChat;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.player.EntityPlayer;

public class KeyHandler
{
/** Key index for easy handling */
public static final int CUSTOM_INV = 0;

/** Key descriptions; use a language file to localize the description later */
private static final String[] desc = {"Mag"};

/** Default key values  these can be changed using the in-game menu */
private static final int[] keyValues = {Keyboard.KEY_P};

private final KeyBinding[] keys;

public KeyHandler() {
	// the advantage of doing it with the above static arrays is now we can just loop through
	// creating and registering all of our keybindings automatically
	keys = new KeyBinding[desc.length];
	for (int i = 0; i < desc.length; ++i) {
		// create the new KeyBinding:
		keys[i] = new KeyBinding(desc[i], keyValues[i], "Mag");
		// and be sure to register it to the ClientRegistry:
		ClientRegistry.registerKeyBinding(keys[i]);
	}
}

// rather than the old KeyHandler class doing it for us
// now we must subscribe to the KeyInputEvent ourselves
@SubscribeEvent
public void onKeyInput(KeyInputEvent event) {
	// first check that the player is not using the chat menu
	// you can use this method from before:
	// if (FMLClientHandler.instance().getClient().inGameHasFocus) {
	// or you can use this new one that is available, doesn't really matter
	if (!FMLClientHandler.instance().isGUIOpen(GuiChat.class)) {
		// you can get the key code of the key pressed using the Keyboard class:
		int kb = Keyboard.getEventKey();
		// similarly, you can get the key state, but this will always be true when the event is fired:
		boolean isDown = Keyboard.getEventKeyState();

		// same as before, chain if-else if statements to find which of your custom keys
		// was pressed and act accordingly:
		if (kb == keys[CUSTOM_INV].getKeyCode()) {
			EntityPlayer player = FMLClientHandler.instance().getClient().thePlayer;
			// if (player.openContainer instanceof ContainerCustomPlayer) {
			// before you could close the screen from here, but that no longer seems to be
			// possible instead, you need to do so from within the GUI itself
			// so we will just send a packet to open the GUI:

		}
	}
}

public static void init() {
	// TODO Auto-generated method stub

}
}

Here is my CustomSlot

package com.phantasyrealms.inventory;


import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;

public class SlotMag extends Slot
{
/** The armor type that can be placed on that slot, it uses the same values of armorType field on ItemArmor. */
final int armorType;

/** The parent class of this slot, ContainerPlayer, SlotArmor is a Anon inner class. */
final EntityPlayer player;

public SlotMag(EntityPlayer player, IInventory inventory, int slotIndex, int x, int y, int armorType)
{
	super(inventory, slotIndex, x, y);
	this.player = player;
	this.armorType = armorType;
}

/**
 * Returns the maximum stack size for a given slot (usually the same as getInventoryStackLimit(), but 1 in the case
 * of armor slots)
 */
public int getSlotStackLimit()
{
	return 1;
}

/**
 * Check if the stack is a valid item for this slot. Always true beside for the armor slots.
 */
public boolean isItemValid(ItemStack itemstack)
{
	Item item = (itemstack == null ? null : itemstack.getItem());
	return item != null && item.isValidArmor(itemstack, armorType, player);
}

/**
 * Returns the icon index on items.png that is used as background image of the slot.
 */
@SideOnly(Side.CLIENT)
public IIcon getBackgroundIconIndex()
{
	return ItemArmor.func_94602_b(this.armorType);
}
}

 

Now what all do I need to make this work?

Posted

width=460 height=259http://d3dsacqprgcsqh.cloudfront.net/photo/aoZmAww_460sa_v1.gif[/img]

 

That ain't how you make Packets in MC.

Again, look up the link I provided: http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2137055-1-7-x-1-8-customizing-packet-handling-with

 

It has literally EVERYTHING - look it up, it's not smart for me to just rewrite whole tutorial.

Note from tutorial author: "Also, for those of you venturing into 1.8, the network code has not changed so you can use the exact same code*! Pretty awesome, no?"

 

For sake of everything:

/**
* @author Ernio
*/

//OVERALL COMMENT: IMessage is responsible for creating message on sender side - in this case on SERVER
//IMessageHandler is the one who handler messago on receiver, in this case it's AbstractClientMessageHandler - this class is a abstraction made to ensure handler is CLIENT side (implements IMessageHandler and checks client stuff)
public class PacketSynchronizeInventory implements IMessage // This packet travels from Server to Client and should be invoked by Inventory on slot change.
{
private int playerId; // the id you will use later on client
private int slot; //index of slot in you custom inv
private ItemStack stack = null; //stack to send

public PacketSynchronizeInventory() {} //Empty constructor is NEEDED (required by implementation of interface)

public PacketSynchronizeInventory(EntityPlayer player, int slot) //Actual constructor of packet
{
	// Init of packet data
	this.playerId = player.getEntityId();
	this.slot = slot;
	this.stack = ExPlayer.get(player).getCustomInventory().getStackInSlot(slot);
	// Note: this all happens on server side inside you inventory class
}

@Override
public void toBytes(ByteBuf buffer) //encoding
{
	buffer.writeInt(this.playerId);
	buffer.writeByte(this.slot);
	PacketBuffer pb = new PacketBuffer(buffer);
	try
	{
		pb.writeItemStackToBuffer(this.stack); //vanilla tool for encoding
	}
	catch (IOException e)
	{
	}
}

@Override
public void fromBytes(ByteBuf buffer) //decoding
{
	this.playerId = buffer.readInt();
	this.slot = buffer.readByte();
	PacketBuffer pb = new PacketBuffer(buffer);
	try
	{
		this.stack = pb.readItemStackFromBuffer();
	}
	catch (IOException e)
	{
	}
}

//Statci handler (receiver class) working on CLIENT
public static class PacketSynchronizeInventoryHandler extends AbstractClientMessageHandler<PacketSynchronizeInventory>
{
	@Override
	public IMessage handleClientMessage(EntityPlayer player, PacketSynchronizeInventory message, MessageContext ctx)
	{
//message contains all sent data
		*World world = Main.proxy.getClientWorld();
		*if (world == null) return null;
		*Entity p = world.getEntityByID(message.playerId);
		*if (p != null && p instanceof EntityPlayer)
//Those 4 lines with * are total bullshit, I used that before I jumped on abstract layer and forgot to remove it, now I can simply use player from method's 1st parameter, jut noticed that lol
		{
			ExPlayer.get((EntityPlayer) p).getCustomInventory().stackList[message.slot] = message.stack; //pretty obvious
		}
		return null; //You can return client answer to this packet
	}
}
}

 

Registering"

registerMessage(PacketSynchronizeInventoryHandler.class, PacketSynchronizeInventory.class, Side.CLIENT); // handler, sender, handling-side - from SERVER to CLIENT so handling side in CLIENT obviously.

 

Invoking packet:

Inside you IInventory you have to call this syncing on:

setInventorySlotContents
decrStackSize
dropItems

Whenever slot has changed.

 

How to send: //This is what is invoked by 3 methods above.

PacketDispatcher.sendToAll(new PacketSynchronizeInventory(player.get(), slot));

 

Remember to make sure it's only called server side.

 

Pretty much everything. Also - Cheers to coolAlias!

 

P.S - I only glympsed on your code, I assumed your GuiHandler is working properly and both Container and Gui are not crashing (you gave no error log) :)

1.7.10 is no longer supported by forge, you are on your own.

Posted

width=460 height=259http://d3dsacqprgcsqh.cloudfront.net/photo/aoZmAww_460sa_v1.gif[/img]

 

That ain't how you make Packets in MC.

Again, look up the link I provided: http://www.minecraftforum.net/forums/mapping-and-modding/mapping-and-modding-tutorials/2137055-1-7-x-1-8-customizing-packet-handling-with

 

It has literally EVERYTHING - look it up, it's not smart for me to just rewrite whole tutorial.

Note from tutorial author: "Also, for those of you venturing into 1.8, the network code has not changed so you can use the exact same code*! Pretty awesome, no?"

 

For sake of everything:

/**
* @author Ernio
*/

//OVERALL COMMENT: IMessage is responsible for creating message on sender side - in this case on SERVER
//IMessageHandler is the one who handler messago on receiver, in this case it's AbstractClientMessageHandler - this class is a abstraction made to ensure handler is CLIENT side (implements IMessageHandler and checks client stuff)
public class PacketSynchronizeInventory implements IMessage // This packet travels from Server to Client and should be invoked by Inventory on slot change.
{
private int playerId; // the id you will use later on client
private int slot; //index of slot in you custom inv
private ItemStack stack = null; //stack to send

public PacketSynchronizeInventory() {} //Empty constructor is NEEDED (required by implementation of interface)

public PacketSynchronizeInventory(EntityPlayer player, int slot) //Actual constructor of packet
{
	// Init of packet data
	this.playerId = player.getEntityId();
	this.slot = slot;
	this.stack = ExPlayer.get(player).getCustomInventory().getStackInSlot(slot);
	// Note: this all happens on server side inside you inventory class
}

@Override
public void toBytes(ByteBuf buffer) //encoding
{
	buffer.writeInt(this.playerId);
	buffer.writeByte(this.slot);
	PacketBuffer pb = new PacketBuffer(buffer);
	try
	{
		pb.writeItemStackToBuffer(this.stack); //vanilla tool for encoding
	}
	catch (IOException e)
	{
	}
}

@Override
public void fromBytes(ByteBuf buffer) //decoding
{
	this.playerId = buffer.readInt();
	this.slot = buffer.readByte();
	PacketBuffer pb = new PacketBuffer(buffer);
	try
	{
		this.stack = pb.readItemStackFromBuffer();
	}
	catch (IOException e)
	{
	}
}

//Statci handler (receiver class) working on CLIENT
public static class PacketSynchronizeInventoryHandler extends AbstractClientMessageHandler<PacketSynchronizeInventory>
{
	@Override
	public IMessage handleClientMessage(EntityPlayer player, PacketSynchronizeInventory message, MessageContext ctx)
	{
//message contains all sent data
		*World world = Main.proxy.getClientWorld();
		*if (world == null) return null;
		*Entity p = world.getEntityByID(message.playerId);
		*if (p != null && p instanceof EntityPlayer)
//Those 4 lines with * are total bullshit, I used that before I jumped on abstract layer and forgot to remove it, now I can simply use player from method's 1st parameter, jut noticed that lol
		{
			ExPlayer.get((EntityPlayer) p).getCustomInventory().stackList[message.slot] = message.stack; //pretty obvious
		}
		return null; //You can return client answer to this packet
	}
}
}

 

Registering"

registerMessage(PacketSynchronizeInventoryHandler.class, PacketSynchronizeInventory.class, Side.CLIENT); // handler, sender, handling-side - from SERVER to CLIENT so handling side in CLIENT obviously.

 

Invoking packet:

Inside you IInventory you have to call this syncing on:

setInventorySlotContents
decrStackSize
dropItems

Whenever slot has changed.

 

How to send: //This is what is invoked by 3 methods above.

PacketDispatcher.sendToAll(new PacketSynchronizeInventory(player.get(), slot));

 

Remember to make sure it's only called server side.

 

Pretty much everything. Also - Cheers to coolAlias!

 

P.S - I only glympsed on your code, I assumed your GuiHandler is working properly and both Container and Gui are not crashing (you gave no error log) :)

 

Yes everything else as shown in my examples work fine when I just run minecraft except for that they arent setup right actually work. Anyways I added the Synchronizing event handler, so how exactly do I call these

setInventorySlotContents
decrStackSize
dropItems

to sync? In a format such as this?

public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }

    public synchronized void decrement() {
        c--;
    }

    public synchronized int value() {
        return c;
    }
}

 

and where should I place the

PacketDispatcher.sendToAll(new PacketSynchronizeInventory(player.get(), slot));

because anywhere I try placing in my PacketDispatcher errors just appear.

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

×
×
  • Create New...

Important Information

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