我使用 this 教程为我的 mod 添加功能,尽管当我更改它们以适合我的 mod 时,它在我离开世界时停止保存。我很确定问题与将数据写入播放器有关,因为当我将 readNBT 函数内部的代码更改为仅使用 set 函数内部的数字运行而不是从 nbt 读取时,它仍然没有什么都改变不了。我知道该函数仍在运行,因为如果我将 System.out.println 放入其中,它仍会输出一些内容。
无论如何,这是我的功能相关文件中的代码:
public class StatusStorage implements Capability.IStorage<IStatus>
{
@Override
public NBTBase writeNBT(Capability<IStatus> capability, IStatus instance, EnumFacing side)
{
NBTTagCompound status = new NBTTagCompound();
status.setInteger("hasFalna", instance.get(0));
status.setInteger("strength", instance.get(1));
status.setInteger("edurance", instance.get(2));
status.setInteger("dexterity", instance.get(3));
status.setInteger("agility", instance.get(4));
status.setInteger("magic", instance.get(5));
status.setInteger("level", instance.get(6));
status.setString("familia", instance.getFamilia());
return status;
}
@Override
public void readNBT(Capability<IStatus> capability, IStatus instance, EnumFacing side, NBTBase nbt)
{
if(nbt instanceof NBTTagCompound)
{
NBTTagCompound tag = (NBTTagCompound)nbt;
instance.set(tag.getInteger("hasFalna"), 0);
instance.set(tag.getInteger("strength"), 1);
instance.set(tag.getInteger("endurance"), 2);
instance.set(tag.getInteger("dexterity"), 3);
instance.set(tag.getInteger("agility"), 4);
instance.set(tag.getInteger("magic"), 5);
instance.set(tag.getInteger("level"), 6);
instance.setFamilia(tag.getString("familia"));
System.out.println("code ran");
}
}
}
public class StatusProvider implements ICapabilitySerializable<NBTBase>
{
@CapabilityInject(IStatus.class)
public static final Capability<IStatus> STATUS_CAP = null;
private IStatus instance = STATUS_CAP.getDefaultInstance();
@Override
public boolean hasCapability(Capability<?> capability, EnumFacing facing)
{
return capability == STATUS_CAP;
}
@Override
public <T> T getCapability(Capability<T> capability, EnumFacing facing)
{
return capability == STATUS_CAP ? STATUS_CAP.<T> cast(this.instance) : null;
}
@Override
public NBTBase serializeNBT()
{
return STATUS_CAP.getStorage().writeNBT(STATUS_CAP, this.instance, null);
}
@Override
public void deserializeNBT(NBTBase nbt)
{
STATUS_CAP.getStorage().readNBT(STATUS_CAP, this.instance, null, nbt);
}
}
public class Status implements IStatus
{
private int hasFalna = 0;
private int strength = 0;
private int endurance = 0;
private int dexterity = 0;
private int agility = 0;
private int magic = 0;
private int level = 1;
private String familia = "";
@Override
public void decrease(int points)
{
this.strength -= points;
if (this.strength < 0.0F) this.strength = 0;
}
@Override
public void increase(int points)
{
this.strength += points;
}
@Override
public void set(int stat, int id)
{
switch(id)
{
case 0:
this.hasFalna = stat;
break;
case 1:
this.strength = stat;
break;
case 2:
this.endurance = stat;
break;
case 3:
this.dexterity = stat;
break;
case 4:
this.agility = stat;
break;
case 5:
this.magic = stat;
break;
case 6:
this.level = stat;
break;
}
}
@Override
public int get(int id)
{
switch(id)
{
case 0:
return this.hasFalna;
case 1:
return this.strength;
case 2:
return this.endurance;
case 3:
return this.dexterity;
case 4:
return this.agility;
case 5:
return this.magic;
case 6:
return this.level;
}
return 0;
}
@Override
public void giveFalna()
{
hasFalna = 1;
}
@Override
public boolean getFalna()
{
return (hasFalna == 1);
}
@Override
public String getFamilia()
{
return familia;
}
@Override
public void setFamilia(String familia)
{
this.familia = familia;
}
}
public interface IStatus
{
public void giveFalna();
public boolean getFalna();
public String getFamilia();
public void setFamilia(String familiaName);
public void increase(int points);
public void decrease(int points);
public void set(int falna, int id);
public int get(int id);
}
public class CapabilityHandler
{
public static final ResourceLocation STATUS_CAP = new ResourceLocation(Reference.MODID, "status");
@SubscribeEvent
public void attachCapability(AttachCapabilitiesEvent<Entity> event)
{
if(!(event.getObject() instanceof EntityPlayer)) return;
event.addCapability(STATUS_CAP, new StatusProvider());
}
}
public class EventHandler
{
@SubscribeEvent
public void onPlayerAttack(AttackEntityEvent event)
{
Entity entity = event.getEntity();
if(!(entity instanceof EntityPlayer)) return;
EntityPlayer player = (EntityPlayer)entity;
IStatus status = player.getCapability(StatusProvider.STATUS_CAP, null);
if(!status.getFalna()) return;
status.increase(1);
}
}
public class CommonProxy
{
public void init()
{
CapabilityManager.INSTANCE.register(IStatus.class, new StatusStorage(), Status::new);
MinecraftForge.EVENT_BUS.register(new CapabilityHandler());
MinecraftForge.EVENT_BUS.register(new EventHandler());
}
}
答案 0 :(得分:0)
我在块功能方面遇到了类似的问题。原因是对能力的 EnumFacing
方面的无知。如果你想保存/加载能力,那么你需要将能力绑定到任何一侧。
检查 EnumFacing
、IStorage
、ICapabilitySerializable#hasCapability
中的 ICapabilitySerializable#getCapability
参数。
在 EnumFacing
、ICapabilitySerializable#serializeNBT
和 ICapabilitySerializable#deserializeNBT
在 player.getCapability
传递有效的 EventHandler#onPlayerAttack
。
//somewhere global variable
public static final EnumFacing capaSide = EnumFacing.UP;
public class StatusStorage implements Capability.IStorage<IStatus>
{
@Override
public NBTBase writeNBT(Capability<IStatus> capability, IStatus instance, EnumFacing side)
{
if(side == capaSide){
//regular logic
} else
return new NBTTagCompound(); //otherwise empty, null is not supported
}
@Override
public void readNBT(Capability<IStatus> capability, IStatus instance, EnumFacing side, NBTBase nbt)
{
if(side == capaSide){
//regular logic
} //otherwise ignore
}
}
public class StatusProvider implements ICapabilitySerializable<NBTBase>
{
@CapabilityInject(IStatus.class)
public static final Capability<IStatus> STATUS_CAP = null;
private IStatus instance = STATUS_CAP.getDefaultInstance();
@Override
public boolean hasCapability(Capability<?> capability, EnumFacing facing)
{
return capability == STATUS_CAP && facing == capaSide; //check side
}
@Override
public <T> T getCapability(Capability<T> capability, EnumFacing facing)
{ //check side
return hasCapability(capability, facing) ? STATUS_CAP.<T> cast(this.instance) : null;
}
@Override
public NBTBase serializeNBT()
{ //pass valid side
return STATUS_CAP.getStorage().writeNBT(STATUS_CAP, this.instance, capaSide);
}
@Override
public void deserializeNBT(NBTBase nbt)
{ //pass valid side
STATUS_CAP.getStorage().readNBT(STATUS_CAP, this.instance, capaSide, nbt);
}
}
public class EventHandler
{
@SubscribeEvent
public void onPlayerAttack(AttackEntityEvent event)
{
... //pass valid side
IStatus status = player.getCapability(StatusProvider.STATUS_CAP, capaSide);
...
}
}
答案 1 :(得分:0)
我不久前问过这个问题,所以我记不太清了,但是如果有人偶然发现这个问题,问题是我没有任何数据包,这就是同步客户端和服务器上的功能的原因.一个很好的指南是 https://forge.gemwire.uk/wiki/Networking,就我而言,我所做的是创建一个消息库,
public abstract class MessageBase<REQ extends IMessage> implements IMessage, IMessageHandler<REQ, REQ>{
@Override
public REQ onMessage(REQ message, MessageContext ctx){
if(ctx.side == Side.SERVER)
{
handleServerSide(message, ctx.getServerHandler().player);
} else
{
EntityPlayer player = ClientThings.getPlayer();
if(player != null)
{
handleClientSide(message, player);
}
}
return null;
}
public abstract void handleClientSide(REQ message, EntityPlayer player);
public abstract void handleServerSide(REQ message, EntityPlayer player);
}
这将被另一个类扩展,这将是您的消息。
对这段代码的一些解释是 onMessage 函数运行,好吧,无论何时发送消息,其中的代码只是检查它是在客户端还是服务器上发送的。 ClientThings.getPlayer();
只是执行 Minecraft.getMinecraft().player
但在一个只在客户端上运行的单独的类中,这样你的 mod 就不会在服务器上崩溃,因为 Minecraft.getMinecraft() 是一个客户端唯一的方法并且会返回一个方法仅运行服务器时未在任何服务器端代码中发现异常。
在创建 MessageBase 之后,您将创建实际的消息,它看起来像这样
public class MessageCap extends MessageBase<MessageCap>
{
private IExampleCapability capability = new Capability();
private EntityPlayer player;
@Override
public void handleClientSide(MessageCap message, EntityPlayer player)
{
IExampleCapability capability = message.cap;
if (player != null)
{
IExampleCapability old = player.getCapability(CapabilityProvider.CAPABILITY_CAP, Capability.capSide);
old.setSomeInt(capability.getSomeInt(0));
}
}
@Override
public void handleServerSide(MessageCap message, EntityPlayer player)
{
EntityPlayerMP playerMP = (EntityPlayerMP)player;
IExampleCapability capibility = message.cap;
IExampleCapability old = playerMP.getCapability(CapabilityProvider.CAPABILITY_CAP, Capability.capSide);
old.setSomeInt(capability.getSomeInt());
}
@Override
public void fromBytes(ByteBuf buf)
{
if(buf.isReadable())
{
cap.setSomeInt = buf.readInt();
}
}
public MessageCap(IExampleCapability cap, EntityPlayer player)
{
this.cap = cap;
this.player = player;
}
public MessageCap()
{
}
}
在 fromBytes 和 toBytes 函数内部,您需要将能力和字节相互转换,而在 handleServerSide 和 handleClientSide 中,您只需使用该函数为您提供的能力更新当前的能力。
最后,您将需要一个功能处理程序,以便您可以发送数据包,
public class NetworkHandler
{
private static SimpleNetworkWrapper INSTANCE;
private static SimpleNetworkWrapper INSTANCE_2;
public static void init()
{
INSTANCE = NetworkRegistry.INSTANCE.newSimpleChannel(Reference.MODID);
INSTANCE.registerMessage(MessageCap.class, MessageCap.class, 0, Side.SERVER);
INSTANCE_2 = NetworkRegistry.INSTANCE.newSimpleChannel(Reference.MODID + "_client");
INSTANCE_2.registerMessage(MessageCap.class, MessageCap.class, 0, Side.CLIENT);
}
public static void sendToServer(IMessage message)
{
INSTANCE.sendToServer(message);
}
public static void refreshThing(IMessage message, EntityPlayerMP player)
{
INSTANCE_2.sendTo(message, player);
}
}
这不是一个很好的实现,因为我知道你应该只需要一个网络包装器的实例,但我的只有当客户端和服务器都有一个时才有效。
现在您终于掌握了所有这些,每当您更改服务器上的功能时,您都需要调用 NetworkHandler.refreshThing(new MessageCap(capability, player), player);
以在客户端上刷新它,并且当您在客户端上更改某些内容时,您会想要致电NetworkHandler.sendToServer(new MessageStatus(capability, player));