Jump to content

Recommended Posts

Posted

Does breakConnection run smoothly or you get some weird behaviour? Also, in breakAllConnections getTarget returns an object with x, y, z of the pipe this pipe connects to, right? connections.contains might be a problem, because I believe it uses equals to check if they are the same object, and they might not be for some reason. Either overwrite equals for PipeDirection or check for the coordinates individually (I suggest last one).

Check out my blog!

http://www.whov.altervista.org

Posted

I haven't been able to test the single function yet, just the removal of a pipe with only one connection. As for the test, I overrode the equals function with a test that checks the results of each connection's toByte() method so I'm evaluating the direction rather than the object in memory.

As far as I can tell, the server and client entities desync, but the server side maintains the correct number while meaning that the pipes client side can behave in unintended ways since packets are transient and don't sync.

One solution will be to make packet handling server side only and sync the tile entities when the server ones receive a packet.

Still left with the visual artifacts client side though.

Posted

Hang on, if it's just a tile entity sync issue this should fix it (as long as Write and Read NBT are complete). Put these in your tile entity

 

   @Override
    public Packet getDescriptionPacket() {//sends packet
    	Packet packet = super.getDescriptionPacket();
    	NBTTagCompound tag = packet != null ? ((S35PacketUpdateTileEntity)packet).func_148857_g() : new NBTTagCompound();
        writeToNBT(tag);
        return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 1, tag);
    }

    @Override
    public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {//receives the packet
        super.onDataPacket(net, pkt);
        NBTTagCompound tag = pkt.func_148857_g();
        readFromNBT(tag);
    }

Check out my blog!

http://www.whov.altervista.org

Posted

Cheers for pointing out some of the points that I'd missed in the datapacket methods, I've also added in debug code to check the side that's desyncing and it is indeed the client-side that isn't updating it's connections list, server side it cleaning up just fine it would seem.

 

I've added in your changes in the datapacket methods, but the desync is still rearing it's ugly head I'm afraid.

 

Here's the TileEntity class as it stands currently:

 

package net.RPower.RPowermod.machines.power.cable;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import RPower.api.power.block.I_MFSink;
import RPower.api.power.block.cable.I_MFCable;
import RPower.api.power.block.cable.I_PipeDirection;
import RPower.api.power.core.E_MFPacketType;
import RPower.api.power.core.MFHelper;
import RPower.api.power.core.MFPacket;
import net.RPower.RPowermod.core.RPCore;
import net.minecraft.block.Block;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.util.ForgeDirection;

public class TileEntityFluxCable extends TileEntity implements I_MFCable {
//Connections are an array of [up, Down, North, East, West, South]
public List<I_PipeDirection> connections;

//whether or not the cable is lossy
public boolean insulatedCable;

//maximum limit the cable can carry
public double packetSizeLimit;

//Packets awaiting processing
public Queue<MFPacket> internalBuffer;

//automatically calculated.
public double percentageLoss;

//transfer mode, unbridged connections can only cross an intersection in straight lines (may be reserved for advanced cabling)
public boolean bridgeConnections;

private double distLimit;

public TileEntityFluxCable()
{
	this(32);
}

public TileEntityFluxCable(double packetSize)
{
	this(packetSize,false);
}

public TileEntityFluxCable(double packetSize, boolean insulated)
{
	this(packetSize,insulated,true);
}
public TileEntityFluxCable(double packetSize, boolean insulated,boolean bridged)
{
	packetSizeLimit= packetSize;
	insulatedCable=insulated;
	bridgeConnections=bridged;
	connections=new LinkedList<I_PipeDirection>();
	internalBuffer=new LinkedList<MFPacket>();
	checkLoss(insulated);
	distLimit = insulatedCable?(4*packetSize):packetSize;
}

private void checkLoss(boolean insulated) {
	if(!insulated)
	{
		percentageLoss=(packetSizeLimit/MFPacket.POWERLIMIT);
	}
}

@Override
public boolean takePacket(MFPacket packet)
{
	double excess=0;
	if(!insulatedCable)
	{

		excess+=(percentageLoss*packet.getBuffer());
		packet.setBuffer(packet.getBuffer()-excess);
	}
	if(packet.getBuffer()>packetSizeLimit)
	{
		excess += packet.getBuffer()-packetSizeLimit;
		packet.setBuffer(packetSizeLimit);
	}
	powerBleed(excess);

	boolean result=false;
	result=internalBuffer.add(packet);
	return result;
}

@Override
public void writeToNBT(NBTTagCompound nbtTag) {
	byte[] connectionsArr = new byte[connections.size()];
	int i =0;
	for (I_PipeDirection connection: connections) {
		connectionsArr[i] = connection.toByte();
		i++;
	}
	nbtTag.setByteArray("connections", connectionsArr);
	nbtTag.setBoolean("insulated", insulatedCable);
	nbtTag.setBoolean("bridged", bridgeConnections);

	nbtTag.setDouble("packetLimit", packetSizeLimit);

	super.writeToNBT(nbtTag);
};

@Override
public void readFromNBT(NBTTagCompound nbtTag) {
	byte[] connectionsArr = nbtTag.getByteArray("connections");
	for (byte b : connectionsArr) {
		I_PipeDirection temp = new PipeDirection(b);
		connections.add(temp);
	}

	insulatedCable=nbtTag.getBoolean("insulated");

	bridgeConnections=nbtTag.getBoolean("bridged");

	packetSizeLimit=nbtTag.getDouble("packetLimit");

	checkLoss(insulatedCable);

	super.readFromNBT(nbtTag);
}

//Fixed by Whov
@Override
public Packet getDescriptionPacket() {
	Packet packet = super.getDescriptionPacket();
    	NBTTagCompound nbtTag = packet != null ? ((S35PacketUpdateTileEntity)packet).func_148857_g() : new NBTTagCompound();
        writeToNBT(nbtTag);
	this.writeToNBT(nbtTag);
	//TODO: Get this damn working!
	return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 1, nbtTag);
}
//Fixed by Whov
@Override
public void onDataPacket(NetworkManager networkManager, S35PacketUpdateTileEntity packet) {
	super.onDataPacket(networkManager, packet);
	readFromNBT(packet.func_148857_g());
}

@Override
public void updateEntity() {
	if(!internalBuffer.isEmpty())
	{
		MFPacket packet = internalBuffer.remove();
		boolean result=false;
		byte  direction;
		for(int i=0; i<59;i++)
		{
			int posNeg = ((int)(Math.random()*%2==0)?1:-1;
			double randXvel=Math.random()*(2*posNeg);
			double randYvel=Math.random()*(2*posNeg);
			double randZvel=Math.random()*(2*posNeg);
			this.worldObj.spawnParticle("magicCrit", xCoord, yCoord, zCoord, randXvel, randYvel, randZvel);
		}

		switch(packet.getType())
		{
		case RESPOND:
			direction = packet.getOrigin().peek();
			result = (direction!=-1);
			result = pushPacket(packet);
			break;
		default:
			direction = packet.getOrigin().peek();
			packet.getOrigin().add(randDir(direction).toByte());
			result=pushPacket(packet);
			break;
		}
	}
	super.updateEntity();
}

private I_PipeDirection randDir(byte initDirection) {
	I_PipeDirection origin = new PipeDirection(initDirection);
	origin.invert();
	I_PipeDirection newDirection = origin;
	int attempt = 0;
	while (!connections.isEmpty()&&attempt<26&&newDirection.equals(origin))
	{
		attempt++;
		int randNum=(int)(Math.random()*connections.size());
		newDirection  = connections.get(randNum);
	}
	return newDirection;
}

@Override
public boolean checkConnections()
{
	boolean result=false;
	int x,y,z,i=0;
	for(y=-1;y<2;y++)
	{
		//System.out.println("Y Level: "+y);
		for(x=-1;x<2;x++)
		{
			//System.out.println("X Position: "+x);
			for(z=-1;z<2;z++)
			{
				//System.out.println("Z Position: "+z);
				i++;
				Block target = worldObj.getBlock(xCoord+x, yCoord+y, zCoord+z);
				//System.out.print("Test["+i+"] Checking block at ["+(xCoord+x)+","+(yCoord+y)+","+(zCoord+z)+"]");
				if(target.hasTileEntity(target.getDamageValue(worldObj, xCoord+x, yCoord+y, zCoord+z))&&MFHelper.checkConnectable(worldObj.getTileEntity(xCoord+x, yCoord+y, zCoord+z)))
				{
						//System.out.print(" - Valid!");
						boolean twoWay = (worldObj.getTileEntity(xCoord+x, yCoord+y, zCoord+z))instanceof I_MFCable;
						formConnection(twoWay, x,y,z);

				}
				//System.out.print('\n');
			}
		}
		//System.out.println("=================================================================");
	}


	return result;
}

@Override
public synchronized void formConnection(boolean twoWay, int x, int y, int z) {
	if(!(x==0&&y==0&&z==0))
	{
		connections.add(new PipeDirection(x, y, z));
		if (twoWay)
		{
			((I_MFCable)(worldObj.getTileEntity(xCoord+x, yCoord+y, zCoord+z))).formConnection(false, -x, -y, -z);

			worldObj.markBlockForUpdate(xCoord+x, yCoord+y, zCoord+z);
		}
		//System.out.println("connection formed");
		worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
	}
}

@Override
public synchronized void breakConnection(boolean twoWay, int x, int y, int z) {
	I_PipeDirection targetConnector = new PipeDirection(x,y,z);
	if(connections.contains(targetConnector))
	{
		System.out.println("Connector found...");
		System.out.println("Remove operation was "+(connections.remove(targetConnector)?"":"un")+"successful.");
	}
	if (twoWay&&(worldObj.getTileEntity(xCoord+x, yCoord+y, zCoord+z))instanceof I_MFCable)
	{
	I_MFCable targetTile = (I_MFCable)worldObj.getTileEntity(xCoord+x, yCoord+y, zCoord+z);
	targetTile.breakConnection(false,-x, -y, -z);
	worldObj.markBlockForUpdate(xCoord+x, yCoord+y, zCoord+z);
	}
	worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
}

@Override
public boolean pushPacket(MFPacket packet)
{
	boolean result= false;
	if(packet.getType()==E_MFPacketType.RESPOND&&packet.getBuffer()<=0)
	{
		packet=null;
	}
	if(packet!=null&&packet.getOrigin().size()<=distLimit);
	{

	byte direction = packet.getOrigin().peek();
	if(packet.getType()==E_MFPacketType.RESPOND)
		packet.getOrigin().pop();


	int[]origin = {xCoord,yCoord,zCoord};
	int[] target = new PipeDirection(direction).getTarget();
	for(int i=0;i<3;i++)
	{
		target[i]+=origin[i];
	}
	result = this.worldObj.getBlock(target[0], target[1], target[2]).hasTileEntity(0);
	if(result)
		result=this.worldObj.getTileEntity(target[0], target[1], target[2])instanceof I_MFSink;
	if(result)
		result=((I_MFSink)this.worldObj.getTileEntity(target[0], target[1], target[2])).takePacket(packet);
	if(!result)
		powerBleed(packet.getBuffer());
	}
	return result;
}

@Override
public boolean canUpdate()
{
	return true;
}

@Override
public double flowLimit() {
	return packetSizeLimit;
}

@Override
public void powerBleed(double excess) {
	//add power bleed to chunk atmosphere -> own effects + taint if Thaumcraft installed
	if(excess>0)
		System.err.println(""+excess+" MF bled off into atmosphere!\n");

}

@Override
public double getPacketLimit() {
	return packetSizeLimit;
}

@Override
public boolean isInsulated() {
	return insulatedCable;
}

@Override
public boolean isBridged() {
	return bridgeConnections;
}

@Override
public boolean canDeBridge() {
	return insulatedCable;
}

@Override
public List<I_PipeDirection> getConnections() {
	System.out.println("Getting "+(worldObj.isRemote?"Client":"Server")+" connections");
	return connections;
}

public String toJSON() {
	String result = "\n{";
	result+=("\n\t\"tileX\":\""+xCoord+"\",");
	result+=("\n\t\"tileY\":\""+yCoord+"\",");
	result+=("\n\t\"tileZ\":\""+zCoord+"\",");
	result+=("\n\t\"connections\":[\n");
	for (I_PipeDirection pipe : connections) {
		result+=(pipe.toJSON()+",\n");
	}
	result+="]";
	result+="\n}\n";

	return result;
}

@Override
public void breakAllConnections() {
	int conNum=connections.size();
	for (int i= 0; i<conNum;i++) {
		int[] target = connections.get(0).getTarget();
		breakConnection(true, target[0], target[1], target[2]);
	}

}

}

Posted

In your getDescriptionPacket method, your writing twice to NBT. I don't know if that causes the issue, but you might want to fix it.

Don't PM me with questions. They will be ignored! Make a thread on the appropriate board for support.

 

1.12 -> 1.13 primer by williewillus.

 

1.7.10 and older versions of Minecraft are no longer supported due to it's age! Update to the latest version for support.

 

http://www.howoldisminecraft1710.today/

Posted

Cheers for pointing that out Lars, I've cleaned that up and I'm wondering if I can force the client side to resync in any other way...

I'll do some more research over the weekend. Cheers again to everyone for all the help and suggestions so far though! (these forums actually help keep me motivated and ambitious)

Posted

Actually, I got it wrong. The server overrides the client. Always. Unless, before that happens, you send that packet using those 2 methods to update the server before it can override the client. If the pipes are wrong in the client, they are either wrong in the server too or the custom renderer is doing something wrong (maybe using old data saved in a field instead of grabbing new one each tick? That would not be really good practice anyway, at least if it's just a intArray).

Check out my blog!

http://www.whov.altervista.org

Posted

Currently the server is keeping the correct connections data and it's the client that's out of sync, I'm shunting all the connection making/breaking/checking code out of the tile entities now and into static methods in the MFHelper api class. Partly to centralize control so that one fix works for all, partly so that I'm not duplicating whole methods for each TE. I'll keep you guys posted if I manage to solve it or not. Cheers again to everyone for the suggestions, help and support!

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

    • So me and a couple of friends are playing with a shitpost mod pack and one of the mods in the pack is corail tombstone and for some reason there is a problem with it, where on death to fire the player will get kicked out of the server and the tombstone will not spawn basically deleting an entire inventory, it doesn't matter what type of fire it is, whether it's from vanilla fire/lava, or from modded fire like ice&fire/lycanites and it's common enough to where everyone on the server has experienced at least once or twice and it doesn't give any crash log. a solution to this would be much appreciated thank you!
    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • 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() ); } }  
  • Topics

×
×
  • Create New...

Important Information

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