Jump to content

How to call a blocks clientonly method from the tile entity's serveronly method?


Chronicide

Recommended Posts

Hey everyone,

 

I've made a new power supplying block. When I began, I made it behave like a button by overriding onBlockActivated and managing the power duration with a combination of the block metadata and an overridden updateTick.

 

It worked perfectly. I placed the new block, added some redstone wires next to it, right clicked, and the redstone wire lit up for the proper number of ticks.

 

So, I was happy with that. Now I needed to change the trigger from a right click to a chat event. The idea is to have a power supply that activates when a keyword is said within a specific range of it.

 

This sounds like a job for a tile entity. I try to develop incrementally (as I'm learning to mod as I go, and don't want to commit to some code until I know it works)

 

Eventually, I want a gui like the sign to set the password when the block is placed, but for now, I'm just hardcoding the password to see if I can get it to work. Here is my tile entity:

 

public class TileEntityPasswordBlock extends TileEntity
{
public String password = "Testing";

public void writeToNBT(NBTTagCompound par1NBTTagCompound)
{
	super.writeToNBT(par1NBTTagCompound);
	par1NBTTagCompound.setString("Password", this.password);
}

public void readFromNBT(NBTTagCompound par1NBTTagCompound)
{
	super.readFromNBT(par1NBTTagCompound);

	this.password = par1NBTTagCompound.getString("Password");

	if (this.password.length() > 15)
	{
		this.password = this.password.substring(0, 15);
        	}
}

public Packet getDescriptionPacket()
{
	NBTTagCompound  nbtTag = new NBTTagCompound();
	this.writeToNBT(nbtTag);
	return new Packet132TileEntityData(this.xCoord, this.yCoord, this.zCoord, 1, nbtTag);
}

public void onDataPacket(INetworkManager net, Packet132TileEntityData packet)
{
	readFromNBT(packet.customParam1);
}

public void checkChatForPassword(String chat)
{
	if (this.worldObj != null)
	{
		this.blockType = this.getBlockType();

		if (this.blockType != null && this.blockType instanceof PasswordBlock)
		{
			((PasswordBlock)this.blockType).passwordDetected(this.worldObj, this.xCoord, this.yCoord, this.zCoord);
		}
	}			
}	
}

 

Note that I'm not bothering to check that chat message for the password yet. For now, I just want it to light up when anything is said in chat.

 

My password block extends BlockContainer and overrides createNewTileEntity, and I've added a method to trigger the redstone that is called by the tile entity:

 

@Override
public TileEntity createNewTileEntity(World world) 
{
return new TileEntityPasswordBlock();
}

@SideOnly(Side.CLIENT)
public void passwordDetected(World world, int x, int y, int z)
{
int i = world.getBlockMetadata(x, y, z);

world.setBlockMetadataWithNotify(x, y, z, 10, 0);

if (i == 0)
{
	world.notifyBlocksOfNeighborChange(x, y, z, this.blockID);			
}		

world.scheduleBlockUpdate(x, y, z, this.blockID, 0);
}

 

Now I just need to call checkChatForPassword on every TileEntityPasswordBlock and pass in the chat.

 

I've already created a ServerChatEvent handler that intercepts chat, loops through players and delivers the chat to only those players within a given distance of the sender. I thought that this would be the ideal place to loop through my tile entities and and pass the current chat to their checkChatForPassword.

 

Unfortunately, 1) I could not find how to list the tile entites, and 2) the PasswordBlocks' passwordDetected is ClientOnly, and the ServerChatEvent works on the server thread. Even if I did hook it up, it wouldn't work because it need to be on the client thread.

 

How can I achieve this? I'm running low on ideas here, and would appreciate anything you guys could offer. Thanks!

 

- Scott

 

(This is my second try at posting this... the first doesn't look to have worked. If I notice two copies of this thread, I'll be sure to delete one.)

Link to comment
Share on other sites

You need a packet handler; the Forge tutorials have one such tutorial. Basically, from the "source" you need to send a 132TileEntityData packet, and your packetHandler needs to act accordingly. Here is how I do it (this is a GUI controlling a TE variable, but the principle is the same). Also note you need to make your mod "listen" to a channel at the top of your main mod class, like this:

channels={"RotaryCraftData"}, packetHandler = PacketHandler.class)

 

GUI:

    public void sendPacket(int a) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream(20); // 5 ints
        DataOutputStream outputStream = new DataOutputStream(bos);
        try {
        	//ModLoader.getMinecraftInstance().thePlayer.addChatMessage(String.valueOf(drops));
        	outputStream.writeInt(a);
        	if (a == 19)
        		outputStream.writeInt(omega);
        	if (a == 20)
        		outputStream.writeInt(torque);
        	outputStream.writeInt(coil.xCoord);
        	outputStream.writeInt(coil.yCoord);
        	outputStream.writeInt(coil.zCoord);

        }
        catch (Exception ex) {
                ex.printStackTrace();
        }

        Packet250CustomPayload packet = new Packet250CustomPayload();
        packet.channel = "RotaryCraftData";
        packet.data = bos.toByteArray();
        packet.length = bos.size();

        Side side = FMLCommonHandler.instance().getEffectiveSide();
        if (side == Side.SERVER) {
                // We are on the server side.
                EntityPlayerMP player2 = (EntityPlayerMP) player;
        } else if (side == Side.CLIENT) {
                // We are on the client side.
                EntityClientPlayerMP player2 = (EntityClientPlayerMP) player;
                PacketDispatcher.sendPacketToServer(packet);
        } else {
                // We are on the Bukkit server.
        }
    }

 

Corresponding sections of the packetHandler:

        [...]
private TileEntityAdvancedGear adv;
private TileEntityAdvancedGear clientadv;

@Override
public void onPacketData(INetworkManager manager, Packet250CustomPayload packet, Player player) {
	ep = (EntityPlayer) player;

	if (packet.channel.equals("RotaryCraftData")) {
		this.handleData(packet);
	}
}

private void handleData(Packet250CustomPayload packet) {
	DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(packet.data));
	int control;
	boolean drops;
	int len;
	int[] data;
	long longdata = 0;
	int x,y,z;
	boolean readinglong = false;
	//System.out.print(packet.length);
	try {
		control = inputStream.readInt();
		pack = EnumPackets.getEnum(control);
		len = pack.getNumberDataInts();
		data = new int[len];
		readinglong = pack.isLongPacket();
		if (!readinglong) {
			for (int i = 0; i < len; i++)
				data[i] = inputStream.readInt();
		}
		else
			longdata = inputStream.readLong();
		x = inputStream.readInt();
		y = inputStream.readInt();
		z = inputStream.readInt();
	} catch (IOException e) {
		e.printStackTrace();
		return;
	}

	World world = ModLoader.getMinecraftInstance().theWorld;
	Minecraft m = Minecraft.getMinecraft();
	WorldServer ws = m.getIntegratedServer().worldServerForDimension(m.thePlayer.dimension);
	switch (pack) {
                [...]
	case COIL:
		adv = (TileEntityAdvancedGear)ws.getBlockTileEntity(x, y, z);
		clientadv = (TileEntityAdvancedGear)world.getBlockTileEntity(x, y, z);
		if (control == 19) {
			adv.releaseOmega = data[0];
			clientadv.releaseOmega = data[0];
		}
		if (control == 20) {
			adv.releaseTorque = data[0];
			clientadv.releaseTorque = data[0];
		}
	break;
                [...]
           }
}

Link to comment
Share on other sites

Hey Reika,

 

Thanks for your help. Between your post and a tutorial I found about packets, I was able to create and send the packet I need. Everything is flowing properly now, but I still can't get the chat to trigger the redstone power.

 

I've managed to track it down to my packet not referencing the world that has my block's metadata. right now, I have two ways of triggering my block's power. I can right click it, or I can type a chat message. Right clicking it works, chatting doesn't. I printed the world.toString() for both, and on the one that works has a worldserver id, while the one that doesn't (from my chat) uses a world id.

 

So I need my packet to get a reference to the current world server that the tile is in... regardless of whether I'm playing in SSP or SMP. I've tried a ton of stuff, but nothing seems to work.

 

Any ideas?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

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