package choonster.testmod3.network.capability;
import choonster.testmod3.TestMod3;
import choonster.testmod3.util.CapabilityUtils;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import javax.annotation.Nullable;
/**
* Base class for messages that update capability data for a single slot of a {@link Container}.
*
* @param <HANDLER> The capability handler type
* @param <DATA> The data type written to and read from the buffer
* @author Choonster
*/
public abstract class MessageUpdateContainerCapability<HANDLER, DATA> implements IMessage {
/**
* The {@link Capability} instance to update.
*/
protected final Capability<HANDLER> capability;
/**
* The {@link EnumFacing} to get the capability handler from.
*/
@Nullable
EnumFacing facing;
/**
* The window ID of the {@link Container}.
*/
int windowID;
/**
* The slot's index in the {@link Container}.
*/
int slotNumber;
/**
* The capability data instance.
*/
DATA capabilityData;
protected MessageUpdateContainerCapability(final Capability<HANDLER> capability) {
this.capability = capability;
}
protected MessageUpdateContainerCapability(final Capability<HANDLER> capability, @Nullable final EnumFacing facing, final int windowID, final int slotNumber, final HANDLER handler) {
this.capability = capability;
this.facing = facing;
this.windowID = windowID;
this.slotNumber = slotNumber;
this.capabilityData = convertCapabilityToData(handler);
}
/**
* Convert from the supplied buffer into your specific message type
*
* @param buf The buffer
*/
@Override
public final void fromBytes(final ByteBuf buf) {
windowID = buf.readInt();
slotNumber = buf.readInt();
final int facingIndex = buf.readByte();
if (facingIndex >= 0) {
facing = EnumFacing.getFront(facingIndex);
} else {
facing = null;
}
final boolean hasData = buf.readBoolean();
if (hasData) {
capabilityData = readCapabilityData(buf);
}
}
/**
* Deconstruct your message into the supplied byte buffer
*
* @param buf The buffer
*/
@Override
public final void toBytes(final ByteBuf buf) {
buf.writeInt(windowID);
buf.writeInt(slotNumber);
if (facing != null) {
buf.writeByte(facing.getIndex());
} else {
buf.writeByte(-1);
}
buf.writeBoolean(hasData());
writeCapabilityData(buf, capabilityData);
}
/**
* Is there any capability data to sync?
*
* @return Is there any capability data to sync?
*/
public final boolean hasData() {
return capabilityData != null;
}
/**
* Convert the capability handler instance to a data instance.
*
* @param handler The handler
* @return The data instance
*/
@Nullable
protected abstract DATA convertCapabilityToData(HANDLER handler);
/**
* Read the capability data from the buffer.
*
* @param buf The buffer
* @return The data instance
*/
protected abstract DATA readCapabilityData(final ByteBuf buf);
/**
* Write the capability data to the buffer.
*
* @param buf The buffer
* @param data The data instance
*/
protected abstract void writeCapabilityData(final ByteBuf buf, DATA data);
public abstract static class Handler<HANDLER, DATA, MESSAGE extends MessageUpdateContainerCapability<HANDLER, DATA>> implements IMessageHandler<MESSAGE, IMessage> {
/**
* Called when a message is received of the appropriate type. You can optionally return a reply message, or null if no reply
* is needed.
*
* @param message The message
* @param ctx The message context
* @return an optional return message
*/
@Nullable
@Override
public final IMessage onMessage(final MESSAGE message, final MessageContext ctx) {
if (!message.hasData()) return null; // Don't do anything if no data was sent
TestMod3.proxy.getThreadListener(ctx).addScheduledTask(() -> {
final EntityPlayer player = TestMod3.proxy.getPlayer(ctx);
final Container container;
if (message.windowID == 0) {
container = player.inventoryContainer;
} else if (message.windowID == player.openContainer.windowId) {
container = player.openContainer;
} else {
return;
}
final ItemStack originalStack = container.getSlot(message.slotNumber).getStack();
final HANDLER originalHandler = CapabilityUtils.getCapability(originalStack, message.capability, message.facing);
if (originalHandler != null) {
final ItemStack newStack = originalStack.copy();
final HANDLER newHandler = CapabilityUtils.getCapability(newStack, message.capability, message.facing);
assert newHandler != null;
applyCapabilityData(newHandler, message.capabilityData);
if (!originalHandler.equals(newHandler)) {
container.putStackInSlot(message.slotNumber, newStack);
}
}
});
return null;
}
/**
* Apply the capability data from the data instance to the capability handler instance.
*
* @param handler The capability handler instance
* @param data The data
*/
protected abstract void applyCapabilityData(final HANDLER handler, final DATA data);
}
}