Jump to content

Recommended Posts

Posted

So i'm trying to add some extended player properties and makes so no unnecessary packets are sends through client/server. But since i've never worked with packets i did not really understand when i should send a packet to the client and when i should send it to the server. By now i send a packet only when something change (for example: the mana value has changed), but if i do this in a tick handler the mod crashes, giving a ClassCastException

java.lang.ClassCastException: net.minecraft.client.entity.EntityPlayerSP cannot be cast to net.minecraft.entity.player.EntityPlayerMP
at com.rpg.player.PlayerProperties.sendToClient(PlayerProperties.java:194)
at com.rpg.player.PlayerProperties.setMana(PlayerProperties.java:113)
at com.rpg.player.PlayerProperties.removeMana(PlayerProperties.java:121)
at com.rpg.player.PlayerProperties.tick(PlayerProperties.java:217)
at com.rpg.events.PlayerTick.onTick(PlayerTick.java:14)
at net.minecraftforge.fml.common.eventhandler.ASMEventHandler_12_PlayerTick_onTick_PlayerTickEvent.invoke(.dynamic)
at net.minecraftforge.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:49)
at net.minecraftforge.fml.common.eventhandler.EventBus.post(EventBus.java:140)
at net.minecraftforge.fml.common.FMLCommonHandler.onPlayerPostTick(FMLCommonHandler.java:357)
at net.minecraft.entity.player.EntityPlayer.onUpdate(EntityPlayer.java:402)
at net.minecraft.client.entity.EntityPlayerSP.onUpdate(EntityPlayerSP.java:163)
at net.minecraft.world.World.updateEntityWithOptionalForce(World.java:2011)
at net.minecraft.world.World.updateEntity(World.java:1976)
at net.minecraft.world.World.updateEntities(World.java:1805)
at net.minecraft.client.Minecraft.runTick(Minecraft.java:2176)
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1080)
at net.minecraft.client.Minecraft.run(Minecraft.java:380)
at net.minecraft.client.main.Main.main(Main.java:116)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)
at net.minecraft.launchwrapper.Launch.main(Launch.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97)
at GradleStart.main(GradleStart.java:26)

 

And this are all the classes involved:

 

PlayerProperties

package com.rpg.player;

import com.rpg.RPG;
import com.rpg.messages.EnergyRegenTimer;
import com.rpg.messages.EnergyValue;
import com.rpg.messages.GoldValue;
import com.rpg.messages.ManaRegenTimer;
import com.rpg.messages.ManaValue;
import com.rpg.messages.MaxEnergyValue;
import com.rpg.messages.MaxManaValue;
import com.rpg.messages.PlayerClass;

import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraftforge.common.IExtendedEntityProperties;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;

public class PlayerProperties implements IExtendedEntityProperties {

public final static String PLAYER_PROPERTIES = "PlayerProperties";
private EntityPlayer player;

private int mana;
private int maxMana;
private int energy;
private int maxEnergy;
private int gold;
private String playerClass;
private boolean firstJoin;
private int manaRegenTimer;
private int energyRegenTimer;

private int ticks = 0;

public PlayerProperties(EntityPlayer player) {
	this.player = player;
	this.mana = this.maxMana = 100;
	this.energy = this.maxEnergy = 100;
	this.firstJoin = true;
	this.playerClass = "";
	this.manaRegenTimer = 10;
	this.energyRegenTimer = 10;
	this.gold = 0;
}

public static final void register(EntityPlayer player) {
	player.registerExtendedProperties(PLAYER_PROPERTIES, new PlayerProperties(player));
}

public static final PlayerProperties get(EntityPlayer player) {
	return (PlayerProperties) player.getExtendedProperties(PLAYER_PROPERTIES);
}

@Override
public void saveNBTData(NBTTagCompound compound) {
	NBTTagCompound nbt = new NBTTagCompound();

	nbt.setInteger("mana", this.mana);
	nbt.setInteger("maxMana", this.maxMana);
	nbt.setInteger("energy", this.energy);
	nbt.setInteger("maxEnergy", this.maxEnergy);
	nbt.setInteger("gold", this.gold);
	nbt.setString("playerClass", this.playerClass);
	nbt.setBoolean("firstJoin", this.firstJoin);
	nbt.setInteger("manaRegenTimer", this.manaRegenTimer);
	nbt.setInteger("energyRegenTimer", this.energyRegenTimer);

	compound.setTag(PLAYER_PROPERTIES, nbt);
}

@Override
public void loadNBTData(NBTTagCompound compound) {
	NBTTagCompound nbt = (NBTTagCompound) compound.getTag(PLAYER_PROPERTIES);

	this.mana = nbt.getInteger("mana");
	this.maxMana = nbt.getInteger("maxMana");
	this.energy = nbt.getInteger("energy");
	this.maxEnergy = nbt.getInteger("maxEnergy");
	this.gold = nbt.getInteger("gold");
	this.playerClass = nbt.getString("playerClass");
	this.firstJoin = nbt.getBoolean("firstJoin");
	this.manaRegenTimer = nbt.getInteger("manaRegenTimer");
	this.energyRegenTimer = nbt.getInteger("energyRegenTimer");
}

@Override
public void init(Entity entity, World world) {
}

public boolean isFirstJoin() {
	return this.firstJoin;
}

public void setJoined() {
	this.firstJoin = false;
}

public int getMana() {
	return this.mana;
}

public void setMana(int amount) {
	this.mana = amount;
	if (this.mana > this.maxMana)
		this.mana = this.maxMana;
	if (this.mana < 0)
		this.mana = 0;
	this.sendToClient(new ManaValue(this.mana));
}

public void addMana(int amount) {
	this.setMana(this.mana + amount);
}

public void removeMana(int amount) {
	this.setMana(this.mana - amount);
}

public int getMaxMana() {
	return this.maxMana;
}

public void setMaxMana(int amount) {
	this.maxMana = amount;
	this.sendToClient(new MaxManaValue(this.maxMana));
}

public int getEnergy() {
	return this.energy;
}

public void setEnergy(int amount) {
	this.energy = amount;
	if (this.energy > this.maxEnergy)
		this.energy = this.maxEnergy;
	if (this.energy < 0)
		this.energy = 0;
	this.sendToClient(new EnergyValue(this.energy));
}

public int getMaxEnergy() {
	return this.maxEnergy;
}

public void setMaxEnergy(int amount) {
	this.maxEnergy = amount;
	this.sendToClient(new MaxEnergyValue(this.maxEnergy));
}

public int getGold() {
	return this.gold;
}

public void setGold(int amount) {
	this.gold = amount;
	if (this.gold < 0)
		this.gold = 0;
	this.sendToClient(new GoldValue(this.gold));
}

public String getPlayerClass() {
	return this.playerClass;
}

public void setPlayerClass(String playerClass) {
	this.playerClass = playerClass;
	this.sendToClient(new PlayerClass(this.playerClass.charAt(0)));
}

public int getManaRegenTimer() {
	return 20 * this.manaRegenTimer;
}

public void setManaRegenTimer(int amount) {
	this.manaRegenTimer = amount;
	this.sendToClient(new ManaRegenTimer(this.manaRegenTimer));
}

public int getEnergyRegenTimer() {
	return 20 * this.energyRegenTimer;
}

public void setEnergyRegenTimer(int amount) {
	this.energyRegenTimer = amount;
	this.sendToClient(new EnergyRegenTimer(this.energyRegenTimer));
}

private void sendToClient(IMessage message) {
	RPG.network.sendTo(message, (EntityPlayerMP) this.player);
}

private void sendToServer(IMessage message) {
	RPG.network.sendToServer(message);
}

public void copy(PlayerProperties props) {
	this.mana = props.getMana();
	this.maxMana = props.getMaxMana();
	this.energy = props.getEnergy();
	this.maxEnergy = props.getMaxEnergy();
	this.gold = props.getGold();
	this.playerClass = props.getPlayerClass();
	this.firstJoin = props.isFirstJoin();
	this.manaRegenTimer = props.getManaRegenTimer();
	this.energyRegenTimer = props.getEnergyRegenTimer();
}

public void tick() {
	this.ticks++;
	if (this.ticks == this.getManaRegenTimer() && this.mana < this.maxMana) {
		this.ticks = 0;
		this.addMana(5);
	}
}

}

 

ManaValue

package com.rpg.messages;

import io.netty.buffer.ByteBuf;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;

public class ManaValue implements IMessage{

private int value;

public ManaValue() {
	this.value = 0;
}

public ManaValue(int amount) {
	this.value = amount;
}

@Override
public void fromBytes(ByteBuf buf) {
	this.value = buf.readInt();
}

@Override
public void toBytes(ByteBuf buf) {
	buf.writeInt(this.value);
}

public void setValue(int amount) {
	this.value = amount;
}

public int getValue() {
	return this.value;
}

}

 

ManaHandler

package com.rpg.handler;

import com.rpg.RPG;
import com.rpg.messages.ManaValue;
import com.rpg.player.PlayerProperties;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.IThreadListener;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;

public class ManaHandler implements IMessageHandler<ManaValue, IMessage>{

public ManaHandler() {

}

@Override
public IMessage onMessage(final ManaValue message, MessageContext ctx) {

	IThreadListener thread = RPG.proxy.getListener(ctx);
	EntityPlayer player = RPG.proxy.getPlayer(ctx);
	if(player != null) {
		final PlayerProperties props = PlayerProperties.get(player);
		if(props != null) {
			thread.addScheduledTask(new Runnable() {
				@Override
				public void run() {
					props.setMana(message.getValue());
				}
			});
		}
	}
	return null;
}

}

 

TickHandler

package com.rpg.events;

import com.rpg.player.PlayerProperties;

import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.PlayerTickEvent;

public class PlayerTick {

@SubscribeEvent
public void onTick(PlayerTickEvent event) {
	PlayerProperties props = PlayerProperties.get(event.player);
	if(props != null && event.player.worldObj.isRemote)
		props.tick();
}
}

 

ClientProxy

package com.rpg.proxy;

import com.rpg.RPG;
import com.rpg.events.DimensionChange;
import com.rpg.events.EntityCreation;
import com.rpg.events.PlayerClone;
import com.rpg.events.PlayerJoin;
import com.rpg.events.PlayerRespawn;
import com.rpg.events.PlayerTick;
import com.rpg.gui.GuiOverlay;
import com.rpg.handler.EnergyHandler;
import com.rpg.handler.EnergyRegenHandler;
import com.rpg.handler.GoldHandler;
import com.rpg.handler.ManaHandler;
import com.rpg.handler.ManaRegenHandler;
import com.rpg.handler.MaxEnergyHandler;
import com.rpg.handler.MaxManaHandler;
import com.rpg.handler.PlayerClassHandler;
import com.rpg.messages.EnergyRegenTimer;
import com.rpg.messages.EnergyValue;
import com.rpg.messages.GoldValue;
import com.rpg.messages.ManaRegenTimer;
import com.rpg.messages.ManaValue;
import com.rpg.messages.MaxEnergyValue;
import com.rpg.messages.MaxManaValue;
import com.rpg.messages.PlayerClass;

import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.IThreadListener;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import net.minecraftforge.fml.relauncher.Side;

public class ClientProxy extends CommonProxy{

public void preInit() {
	RPG.network = NetworkRegistry.INSTANCE.newSimpleChannel(RPG.MODID);
	RPG.network.registerMessage(ManaHandler.class, ManaValue.class, 0, Side.CLIENT);
}

public void init() {
	MinecraftForge.EVENT_BUS.register(new GuiOverlay(Minecraft.getMinecraft()));
}

public IThreadListener getListener(MessageContext ctx) {
	return Minecraft.getMinecraft();
}

public EntityPlayer getPlayer(MessageContext ctx) {
	return Minecraft.getMinecraft().thePlayer;
}

public void registerEvents() {
	MinecraftForge.EVENT_BUS.register(new PlayerTick());
}
}

 

CommonProxy

package com.rpg.proxy;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.IThreadListener;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;

public class CommonProxy {

public void preInit() {}
public void init() {}

public IThreadListener getListener(MessageContext ctx) {
	return (WorldServer) ctx.getServerHandler().playerEntity.worldObj;
}

public EntityPlayer getPlayer(MessageContext ctx) {
	return ctx.getServerHandler().playerEntity;
}

public void registerEvents() {}
}

 

Main Mod File

package com.rpg;

import com.rpg.proxy.CommonProxy;

import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventHandler;
import net.minecraftforge.fml.common.Mod.Instance;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;

@Mod(modid = RPG.MODID, version = RPG.VERSION, name = RPG.NAME)
public class RPG
{
@SidedProxy(clientSide = "com.rpg.proxy.ClientProxy", serverSide = "com.rpg.proxy.CommonProxy")
public static CommonProxy proxy;
@Instance("rpg")
public static RPG instance;
public static final String NAME = "RPG Mod";
    public static final String MODID = "rpg";
    public static final String VERSION = "1.0";
    
    public static SimpleNetworkWrapper network;
    
    @EventHandler
public void preInit(FMLPreInitializationEvent event) {
    	proxy.preInit();
    }
    
    @EventHandler
    public void init(FMLInitializationEvent event)
    {
    	proxy.init();
    	proxy.registerEvents();
    }
}

 

Let me know if other classes are needed

So i want to understand how can this work? When i should send a packet to the server and when to the client?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

You're only calling

PlayerProperties#tick

on the client side (when

World#isRemote

is

true

). You need to call it on the server instead (when

World#isRemote

is

false

).

 

The server should control all data and send packets to the relevant clients when they need the data for rendering purposes. The client should receive player input (clicks and key presses) and send packets to the server when these should trigger some action.

 

You should convert your

IExtendedEntityProperties

to a capability,

IEEP

has been removed in 1.9+.

 

You should also annotate override methods with

@Override

so you get a compilation error if they don't actually override a super method.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

I've changed the check in the tick handler, but now this happens: the server sets the value to 100 and the bar in game goes to 100, but this error is fired as soon as it does

[11:56:22] [Client thread/FATAL]: Error executing task
java.util.concurrent.ExecutionException: java.lang.ClassCastException: net.minecraft.client.entity.EntityPlayerSP cannot be cast to net.minecraft.entity.player.EntityPlayerMP
at java.util.concurrent.FutureTask.report(Unknown Source) ~[?:1.8.0_92]
at java.util.concurrent.FutureTask.get(Unknown Source) ~[?:1.8.0_92]
at net.minecraft.util.Util.runTask(Util.java:23) [util.class:?]
at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:1070) [Minecraft.class:?]
at net.minecraft.client.Minecraft.run(Minecraft.java:380) [Minecraft.class:?]
at net.minecraft.client.main.Main.main(Main.java:116) [Main.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_92]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_92]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_92]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_92]
at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) [launchwrapper-1.12.jar:?]
at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.12.jar:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_92]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_92]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_92]
at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_92]
at net.minecraftforge.gradle.GradleStartCommon.launch(GradleStartCommon.java:97) [start/:?]
at GradleStart.main(GradleStart.java:26) [start/:?]
Caused by: java.lang.ClassCastException: net.minecraft.client.entity.EntityPlayerSP cannot be cast to net.minecraft.entity.player.EntityPlayerMP
at com.rpg.player.PlayerProperties.sendToClient(PlayerProperties.java:208) ~[PlayerProperties.class:?]
at com.rpg.player.PlayerProperties.setMana(PlayerProperties.java:118) ~[PlayerProperties.class:?]
at com.rpg.handler.ManaHandler$1.run(ManaHandler.java:30) ~[ManaHandler$1.class:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_92]
at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_92]
at net.minecraft.util.Util.runTask(Util.java:22) ~[util.class:?]
... 15 more

 

and also when relogging the bar is resetted :/

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

ManaHandler

calls

PlayerProperties#setMana

, which calls

sendToClient

regardless of which side it was called from. Only call

sendToClient

on the server.

 

Your

ClientProxy#getPlayer

and

getListener

methods are incorrect because they don't account for the logical side (client thread or server thread) that they're being called from and only return the client player and listener. The

MessageContext#side

field tells you which logical side the message was received on.

 

This page explains sides in more detail.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Ok, so i've changed the proxy methods to this

public IThreadListener getListener(MessageContext ctx) {
	return ctx.side == Side.CLIENT ? Minecraft.getMinecraft() : super.getListener(ctx);
}

public EntityPlayer getPlayer(MessageContext ctx) {
	return ctx.side == Side.CLIENT ? Minecraft.getMinecraft().thePlayer : super.getPlayer(ctx);
}

 

but i did not udnerstand where i should call the sendToClient method. I think that this is incorrect and it will loop, so where should i call that method?

package com.rpg.handler;

import com.rpg.RPG;
import com.rpg.messages.ManaValue;
import com.rpg.player.PlayerProperties;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.IThreadListener;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;

public class ManaHandler implements IMessageHandler<ManaValue, IMessage>{

public ManaHandler() {

}

@Override
public IMessage onMessage(final ManaValue message, MessageContext ctx) {

	IThreadListener thread = RPG.proxy.getListener(ctx);
	EntityPlayer player = RPG.proxy.getPlayer(ctx);
	if(player != null) {
		final PlayerProperties props = PlayerProperties.get(player);
		if(props != null) {
			thread.addScheduledTask(new Runnable() {
				@Override
				public void run() {
					props.setMana(message.getValue());
					props.sendToClient(new ManaValue(props.getMana()));
				}
			});
		}
	}
	return null;
}

}

 

EDIT: i've change the addMana function to this

public void addMana(int amount) {
	this.setMana(this.mana + amount);
	this.sendToClient(new ManaValue(this.getMana()));
}

 

now the bar actually updates but it still reset once the player log out

 

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

ManaHandler

is called when the client receives a

ManaValue

message from the server.

PlayerProperties#sendToClient

sends a

ManaValue

message from the server to the client.

 

PlayerProperties#sendToClient

must be called from

PlayerProperties#setMana

, but only on the server side (when

World#isRemote

is

false

).

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

I've done the class to sendToClient in the tick method (since this is called only from the tickHandler when world.isRemote is false).

public void tick() {
	this.ticks++;
	if (this.ticks == this.getManaRegenTimer() && this.mana < this.maxMana) {
		this.ticks = 0;
		this.addMana(5);
		this.sendToClient(new ManaValue(this.getMana()));
	}
}

 

In game the bar updates correctly but still when logs out and then re-login the bar goes to default value (95/100)

 

EDIT: in the PlayerJoinEvent i've printed the mana value and it is 100, while the bar displays 95

 

@SubscribeEvent
public void onPlayerJoin(PlayerLoggedInEvent event) {
	PlayerProperties props = PlayerProperties.get(event.player);
	if(props != null) {
		System.out.println(props.getMana());
		if(props.isFirstJoin()) {
			props.setJoined();
		}
	}
}

 

This event is registered as well

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

PlayerLoggedInEvent

is only fired on the server.

 

The client doesn't save any data, so you need to send the player's current mana value to their client when they log in.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Tried doing this

package com.rpg.events;

import com.rpg.messages.ManaValue;
import com.rpg.player.PlayerProperties;

import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;

public class PlayerJoin {

@SubscribeEvent
public void onPlayerJoin(PlayerLoggedInEvent event) {
	PlayerProperties props = PlayerProperties.get(event.player);
	if(props != null) {
		System.out.println(props.getMana());
		props.sendToClient(new ManaValue(props.getMana()));
		if(props.isFirstJoin()) {
			props.setJoined();
		}
	}
}
}

 

The print return 100 but the bar is still at 95

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

Set a breakpoint in

ManaHandler

, does the client player exist when message sent from the

PlayerLoggedInEvent

handler is received?

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Yes, the player exist in ManaHandler (i put a breakpoint at the start of the onMessage method. If the player does not exist then the null check will be true and nothing will happen). So the onMessage method is running but looking at the properties that this method will get i see that the mana value is 95 (while in the event is printed as 100). So i guess that client and server handles different values, wich is not good. How can i sync them?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

The function to change the mana value is called only in the ManaHandler. What i do is this

public int getMana() {
	return this.mana;
}

public void setMana(int amount) {
	this.mana = amount;
	if (this.mana > this.maxMana)
		this.mana = this.maxMana;
	if (this.mana < 0)
		this.mana = 0;
}

public void addMana(int amount) {
	this.sendToServer(new ManaValue(this.mana + amount));
}

public void removeMana(int amount) {
	this.sendToServer(new ManaValue(this.mana - amount));
}

 

Every time i want to change the value (by adding or subtracting something) i send a packet on the server with the new amount of mana, then in the ManaHandler the setMana function is called to actually set the new value. What i miss is from server to client what should i send, because if i send the same message i think it will always call the setMana function (as it will return constantly in the handler) :/

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

No, that's completely backwards. NEVER send values to the server - the server is the one that should be sending values to the client. Why are you controlling mana values from the client?

 

Also, it'd be simpler to send the packet from #setMana which I assume is the centralized method for actually modifying mana values.

 

Finally, your #addMana and #removeMana methods are very weird - you don't actually modify the mana value, you just send a packet?

Posted

Yes, i send only a packet holding the new value, than in the handler the setMana function is called, setting the mana to the new value by doing this

props.setMana(message.getValue());

 

But i assume this is wrong due to you response. So i should only send in the message the amount of mana to add/subtract, then in the handler calcualte the new mana amount (with controls and stuff) and only then call the setMana function to set the mana to the new value and send a packet to the client? Basically should i do this in the PlayerProperties

public void setMana(int amount) {
	this.mana = amount;
}

public void addMana(int amount) {
	this.sendToServer(new ManaValue(amount));
}

public void removeMana(int amount) {
	this.sendToServer(new ManaValue(-1 * amount));
}

 

and this in the handler?

@Override
public IMessage onMessage(final ManaValue message, final MessageContext ctx) {

	IThreadListener thread = RPG.proxy.getListener(ctx);
	EntityPlayer player = RPG.proxy.getPlayer(ctx);
	if(player != null) {
		final PlayerProperties props = PlayerProperties.get(player);
		if(props != null) {
			thread.addScheduledTask(new Runnable() {
				@Override
				public void run() {
					int mana = props.getMana() + message.getValue();
					if (mana > props.getMaxMana())
						mana = props.getMaxMana();
					if (mana < 0)
						mana = 0;
					props.setMana(mana);
					//packet
				}
			});
		}
	}
	return null;
}

Don't blame me if i always ask for your help. I just want to learn to be better :)

Posted

No, you shouldn't be sending ANY data at all to the server... the server should already know about needing to add or subtract mana as the server is the only place that should ever happen.

 

Basically all the client should ever do is display the current value of the mana. If you have a key press responsible for activating magic spells or whatever, the answer is not to have the client change the mana value but to send a packet to the server saying 'hey player A wants to activate magic spell B, is that okay?' and the server goes through all the logic of determining if the player can do that, which involves checking and then modifying the current mana value and ultimately sending a packet back to the client with the new current mana value.

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

×
×
  • Create New...

Important Information

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