Jump to content

[1.10.2] [SOLVED] Waila Support with Forge Energy Capabilities


Recommended Posts

Posted (edited)

Hi,

 

I'm currently having the problem that my solar panels show with Waila how many Forge Energy is stored in them, but they only update

the value when you right click on the solar panel and open the GUI.

 

@Optional.Interface(iface = "mcp.mobius.waila.api.IWailaDataProvider", modid = Constants.MODID_WAILA)
public class WailaCompat implements IWailaDataProvider {
    
    public static final WailaCompat INSTANCE = new WailaCompat();
    
    public static void load(IWailaRegistrar registrar) {
        registrar.registerBodyProvider(INSTANCE, TileEntityEnergy.class);
        registrar.registerNBTProvider(INSTANCE, TileEntityEnergy.class);
    }

    @Override
    @Nullable
    public ItemStack getWailaStack(IWailaDataAccessor accessor, IWailaConfigHandler config) {
        return null;
    }

    @Override
    @Nullable
    public List<String> getWailaHead(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
            IWailaConfigHandler config) {
        return currenttip;
    }

    @Override
    @Nullable
    public List<String> getWailaBody(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
            IWailaConfigHandler config) {
        
        TileEntity tile = accessor.getTileEntity();
        
        if(tile instanceof TileEntityEnergy) {
            TileEntityEnergy tileEnergy = (TileEntityEnergy) tile;
            
            if(tileEnergy.container != null) {
                if(currenttip.size() > 4)
                    currenttip.add("");
                
                int stored = tileEnergy.container.getEnergyStored();
                int max = tileEnergy.container.getMaxEnergyStored();
                
                currenttip.add(String.format("%s%s%s / %s%s%s %s", TextFormatting.WHITE, stored, TextFormatting.RESET,
                        TextFormatting.WHITE, max, TextFormatting.RESET, ModConfig.ENERGY_NAME));
            }
        }
        
        return currenttip;
    }

    @Override
    @Nullable
    public List<String> getWailaTail(ItemStack itemStack, List<String> currenttip, IWailaDataAccessor accessor,
            IWailaConfigHandler config) {
        return currenttip;
    }

    @Override
    @Nullable
    public NBTTagCompound getNBTData(EntityPlayerMP player, TileEntity te, NBTTagCompound tag, World world,
            BlockPos pos) {
        return tag;
    }
}

 

So, how can I fix this problem?

Just to note, the container of my TileEntityEnergy is the EnergyManager which stores all the energy and provides methods for extracting and receiving energy while also supporting other Energy systems like Tesla and handling the transition between them.

 

Thx in advance.

Bektor

Edited by Bektor

Developer of Primeval Forest.

Posted

If I were to guess I'd say it's a syncing issue. Are you by chance syncing up your server and client tileentity the moment you open the gui / right click the block? In any case I would like to see the block and tileentity classes (:

Posted
14 minutes ago, tommyte said:

If I were to guess I'd say it's a syncing issue. Are you by chance syncing up your server and client tileentity the moment you open the gui / right click the block? In any case I would like to see the block and tileentity classes (:

Spoiler

public class TileEntitySolarPanel extends TileEntityEnergy {
    
    private int inputRate;
    private boolean twoTick = false;
    
    public TileEntitySolarPanel() {
        super(250, 10);
        this.getEnergyManager().setTransferMode(EnergyTransfer.PRODUCER);
    }
    
    @Override
    public void update() {
        boolean flag = false;
        
        if(!this.hasWorld() || this.getWorld().isRemote)
            return;
        
        this.twoTick = !this.twoTick;
        this.inputRate = 10;
        
        this.container.receiveEnergy(this.inputRate, false);
        
        if(this.world.getTileEntity(this.getPos().down()) != null)
            EnergyUtils.sendEnergy(this, this.world.getTileEntity(this.getPos().down()), EnumFacing.DOWN, 10);
        
        if(flag)
            this.markDirty();
    }
    
    @Override
    public SPacketUpdateTileEntity getUpdatePacket() {
        return super.getUpdatePacket();
    }
    
    @Override
    public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
        super.onDataPacket(net, pkt);
        this.readFromNBT(pkt.getNbtCompound());
    }
    
    public int getField(int id) {
        if(id == 0) return this.container.getEnergyStored();
        else if(id == 1) return this.inputRate;
        else return 0;
    }
    
    public void setField(int id, int value) {
        if(id == 0) this.container.setEnergyStored(value);
        else if(id == 1) this.inputRate = value;
    }
    
    public boolean isUsableByPlayer(EntityPlayer player) {
        return this.getWorld().getTileEntity(this.getPos()) != this ? false : 
                        player.getDistanceSq((double)this.getPos().getX() + .5d, 
                        (double)this.getPos().getY() + .5d, 
                        (double)this.getPos().getZ() + .5d) <= 64.d;
    }
}

 

Thats the basic tile entity class, the block class does actually nothing, except for creating the tile entity.

 

And here are the main methods of the TileEntityEnergy. There are some other methods, but they are not important as they are

mostly getters etc.

Spoiler

    public final EnergyManager container;

    @Override
    public void readFromNBT(NBTTagCompound compound) {
        super.readFromNBT(compound);
        this.container.deserializeNBT(compound.getCompoundTag("Energy"));
    }
    
    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound compound) {
        compound.setTag("Energy", this.container.serializeNBT());
        return super.writeToNBT(compound);
    }
    
    @Override
    public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing) {
        T cap = this.container.getCapability(capability, facing);
        
        return cap != null ? cap : super.getCapability(capability, facing); 
    }
    
    @Override
    public boolean hasCapability(Capability<?> capability, @Nullable EnumFacing facing) {
        return this.container.hasCapability(capability, facing) || 
                super.hasCapability(capability, facing);
    }

 

 

Developer of Primeval Forest.

Posted

I can't find any networking here. I'd really like to see where you are syncing up your client side with your server side. 

 

4 minutes ago, Bektor said:

if(!this.hasWorld() || this.getWorld().isRemote) return;

 

You are, logically, only changing the amount of energy stored on server side, so the client side, what waila is displaying (I think), doesn't know of any changes in the amount of energy stored until you send a packet there ;)

Posted
32 minutes ago, tommyte said:

I can't find any networking here. I'd really like to see where you are syncing up your client side with your server side. 

 

 

You are, logically, only changing the amount of energy stored on server side, so the client side, what waila is displaying (I think), doesn't know of any changes in the amount of energy stored until you send a packet there ;)

The only other stuff there is is the Container for the GUI:

public class ContainerSolarPanel extends Container {
	
	private int energyStored;
	private int productionAmount;
	private TileEntitySolarPanel panel;
	
	public ContainerSolarPanel(InventoryPlayer playerInv, TileEntitySolarPanel panel) {
		this.panel = panel;
		
		for(int y = 0; y < 3; ++y)
			for(int x = 0; x < 9; ++x)
				this.addSlotToContainer(new Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 84 + y * 18));
		
		for(int x = 0; x < 9; ++x)
			this.addSlotToContainer(new Slot(playerInv, x, 8 + x * 18, 142));
	}
	
	@Override
	public void detectAndSendChanges() {
		super.detectAndSendChanges();
		
		for(int i = 0; i < this.listeners.size(); i++) {
			IContainerListener listener = (IContainerListener) this.listeners.get(i);
			
			if(this.energyStored != this.panel.getField(0))
				listener.sendProgressBarUpdate(this, 0, this.panel.getField(0));
			if(this.productionAmount != this.panel.getField(1))
				listener.sendProgressBarUpdate(this, 1, this.panel.getField(1));
		}
		
		this.energyStored = this.panel.getField(0);
		this.productionAmount = this.panel.getField(1);
	}
	
	@Override
	@SideOnly(Side.CLIENT)
	public void updateProgressBar(int id, int data) {
		super.updateProgressBar(id, data);
		this.panel.setField(id, data);
	}
	
    @Override
    public boolean canInteractWith(EntityPlayer playerIn) {
        return this.panel.isUsableByPlayer(playerIn);
    }
}

 

Developer of Primeval Forest.

Posted

WAILA doesn't load the container, so the container's synchronization code doesn't work for displaying information to WAILA, ergo, you are not syncing the information except when the player opens the gui by right clicking the block. 

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted
2 minutes ago, Bektor said:

@Override public void detectAndSendChanges()

{

super.detectAndSendChanges();

for(int i = 0; i < this.listeners.size(); i++)

{

IContainerListener listener = (IContainerListener)

this.listeners.get(i);

if(this.energyStored != this.panel.getField(0)) listener.sendProgressBarUpdate(this, 0, this.panel.getField(0));

if(this.productionAmount != this.panel.getField(1)) listener.sendProgressBarUpdate(this, 1, this.panel.getField(1));

}

this.energyStored = this.panel.getField(0);

this.productionAmount = this.panel.getField(1);

}

There you go :) This method, as the name implies detects and sends changes (on server side) and sends them to the client. This then only occurs when the container is activated, aka when the player open the gui. You should set up your own networking to sync properly between server and client. Here is an article about in on the docs: https://mcforge.readthedocs.io/en/latest/networking/

 

Read all the articles there about networking and then you should be good to go ;)

Posted (edited)

Ok, I just got now my network message done.

But how can I access in the message handler the tile entity to change the energy value on the client?

Just to note, my message only sends a basic integer value.

 

I'm also wondering to which players I should send the message for the best performance.

I also guess I should just send the update every time when I call markDirty() in the tile entity itself, am I right?

Edited by Bektor

Developer of Primeval Forest.

Posted

I usually do it like this: 

data.setIntArray("ints", new int[]{pos.getX(), pos.getY(), pos.getZ(), amount});

Here I set an array of integers with the three coords of the tile entity and the number i'd like to transfer. 

 

int[] values = message.data.getIntArray("ints");
final String name = message.data.getString("string");
final BlockPos pos = new BlockPos(values[0], values[1], values[2]);
final int amount = values[3];

Then in the messagehandler I read these values like this.

TileEntity entity = theplayer.getEntityWorld().getTileEntity(pos);
				
if(entity != null && entity.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, null))
{
//here do something like entity.getCapability(etc., etc.).setEnergy(amount);
}

You'd put something like this into your run method.

Posted (edited)

Hm, how can I get the client world and the client player entitiy from my MessageContext#getClientHandler() as the message gets send to the client from the server.?

Edited by Bektor

Developer of Primeval Forest.

Posted
1 minute ago, Bektor said:

Hm, how can I get the client world and the client player entitiy from my MessageContext#getClientHandler() as the message gets send to the client from the server.?

You can get the singleplayer player by calling Minecraft.getMinecraft().player. 

Posted
Just now, tommyte said:

You can get the singleplayer player by calling Minecraft.getMinecraft().player. 

And how do I get the world?

Also, how can I send the new data only to all players currently looking at the block?

Developer of Primeval Forest.

Posted

Seriously?

 

Minecraft.getMinecraft().player.getWorld() was too hard?

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted
  • Like 1

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted
1 minute ago, Bektor said:

And how do I get the world?

player.getEntityWorld(); (or something like that)

 

1 minute ago, Bektor said:

Also, how can I send the new data only to all players currently looking at the block?

you can use this:

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

Just set the range to a value that would make sure no one outside it can hover over the block.

Posted
2 minutes ago, Draco18s said:

wow thank you! I actually didn't know this myself (:

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

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

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