Minecraft Forge EntityJoinWorldEvent returns the wrong location! error

Using Forge 1.8.9 in Eclipse (Mars.1 release (4.5.1)) in the local development environment.

I try to set the playerโ€™s location every time they join or join the world. It always works the first time (for example, it starts and joins the world. See the first screenshot). First appearance in the world - works as expected

Having traveled around the world a little, leaving this world and returning back (to the same session without closing the MC), the world will not appear in the Console. Location - this is the same place as in "everything is OK." Plus there is a wrong location! Error.

Location is OK, but World is not displayed

Console Error:

[05:47:53] [Server thread/INFO]: Player992 joined the game [05:47:53] [Server thread/WARN]: Wrong location! (9, 9) should be (9, 6), EntityPlayerMP['Player992'/2371, l='world', x=145.00, y=73.00, z=145.00] [05:48:18] [Server thread/INFO]: Saving and pausing game... [05:48:18] [Server thread/INFO]: Saving chunks for level 'world'/Overworld [05:48:18] [Server thread/INFO]: Saving chunks for level 'world'/Nether [05:48:18] [Server thread/INFO]: Saving chunks for level 'world'/The End 

I tried several variations of this, including Minecraft Forge: using the correct listener for setLocationAndAngles , but not dice (different behavior).

Ignore all "imported" ones that are not relevant. These are artifacts of my many attempts.

 import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; //import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.client.event.RenderWorldEvent; import net.minecraftforge.event.world.WorldEvent; public class JoinGameLocation { @SubscribeEvent public void onEntityJoinWorld(EntityJoinWorldEvent event) { if (event.entity != null && event.entity instanceof EntityPlayer && !event.entity.worldObj.isRemote) { event.entity.setLocationAndAngles(145, 73, 145, 0, 0); } } } 

I read about the error of the wrong location, but something does not seem to be correct, given that I may appear at this place for the first time, so it is not as if I appear inside the block. I tried to create a slight delay (1-3 s), but the error still occurs.

+5
source share
1 answer

"Wrong location!" used when an object is added to a piece that it should not be in its coordinates.

Here where (in World.java ) the event is fired (well, in fact, there are several other places, but this is the one used by the players among other objects):

 /** * Called when an entity is spawned in the world. This includes players. */ public boolean spawnEntityInWorld(Entity p_72838_1_) { // do not drop any items while restoring blocksnapshots. Prevents dupes if (!this.isRemote && (p_72838_1_ == null || (p_72838_1_ instanceof net.minecraft.entity.item.EntityItem && this.restoringBlockSnapshots))) return false; int i = MathHelper.floor_double(p_72838_1_.posX / 16.0D); int j = MathHelper.floor_double(p_72838_1_.posZ / 16.0D); boolean flag = p_72838_1_.forceSpawn; if (p_72838_1_ instanceof EntityPlayer) { flag = true; } if (!flag && !this.isChunkLoaded(i, j, true)) { return false; } else { if (p_72838_1_ instanceof EntityPlayer) { EntityPlayer entityplayer = (EntityPlayer)p_72838_1_; this.playerEntities.add(entityplayer); this.updateAllPlayersSleepingFlag(); } if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityJoinWorldEvent(p_72838_1_, this)) && !flag) return false; this.getChunkFromChunkCoords(i, j).addEntity(p_72838_1_); this.loadedEntityList.add(p_72838_1_); this.onEntityAdded(p_72838_1_); return true; } } 

Please note that i and j (piece coordinates) do not change after updating the playerโ€™s location. Therefore, when Chunk.addEntity is Chunk.addEntity (see below), everything does not work:

 /** * Adds an entity to the chunk. Args: entity */ public void addEntity(Entity entityIn) { this.hasEntities = true; int i = MathHelper.floor_double(entityIn.posX / 16.0D); int j = MathHelper.floor_double(entityIn.posZ / 16.0D); if (i != this.xPosition || j != this.zPosition) { logger.warn("Wrong location! (" + i + ", " + j + ") should be (" + this.xPosition + ", " + this.zPosition + "), " + entityIn, new Object[] {entityIn}); entityIn.setDead(); } // ... rest of the method } 

It kills the player.


I'm not quite sure why it works for the first time. It will work whenever you enter the same block as the one to which you would be teleported, so if you log out when you are in the wrong place, you will log in the next time.

Before proceeding with the fix, a few more notes:

  • You do not need to do null validation with an instance - null will never pass an instance validation.
  • (At least according to CommandTeleport), you need to teleport EntityPlayerMP differently using EntityPlayerMP.playerNetServerHandler.setPlayerLocation .

To fix this, you need to postpone teleportation by 1 tick. I'm not quite sure what the canonical forge method is for this, but something like this should work:

 List<Entity> playersToTeleport = new ArrayList<Entity>(); @SubscribeEvent public void onEntityJoinWorld(EntityJoinWorldEvent event) { if (event.entity instanceof EntityPlayer && !event.entity.worldObj.isRemote) { playersToTeleport.add(event.entity); } } @SubscribeEvent public void teleportEntiesOnWorldTick(TickEvent.WorldTickEvent event) { // Make sure that this is the type of tick we want. if (event.phase == TickEvent.Phase.START && event.type == TickEvent.Type.WORLD) { for (Entity entity : playersToTeleport) { if (entity.worldObj == event.world) { if (entity instanceof EntityPlayerMP) { ((EntityPlayerMP) entity).playerNetServerHandler.setPlayerLocation(145, 73, 145, 0, 0); } else { entity.setLocationAndAngles(145, 73, 145, 0, 0); } } } playersToTeleport.clear(); } } 

If you need to change the position in which the player will, and will not always enter these specific coordinates, here is one way:

 @SubscribeEvent public void onEntityJoinWorld(EntityJoinWorldEvent event) { if (event.entity instanceof EntityPlayer && !event.entity.worldObj.isRemote) { queueTeleportNextTick(event.entity, Math.random() * 200 - 100, 73, Math.random() * 200 - 100, 0, 0); } } /** * List of teleports to perform next tick. */ private List<TeleportInfo> queuedTeleports = new ArrayList<TeleportInfo>(); /** * Stores information about a future teleport. */ private static class TeleportInfo { public TeleportInfo(Entity entity, double x, double y, double z, float yaw, float pitch) { this.entity = entity; this.x = x; this.y = y; this.z = z; this.yaw = yaw; this.pitch = pitch; } public final Entity entity; public final double x; public final double y; public final double z; public final float yaw; public final float pitch; } /** * Teleport the given entity to the given coordinates on the next game tick. */ public void queueTeleportNextTick(Entity entity, double x, double y, double z, float yaw, float pitch) { System.out.printf("Preparing to teleport %s to %f, %f, %f%n", entity, x, y, z); queuedTeleports.add(new TeleportInfo(entity, x, y, z, yaw, pitch)); } @SubscribeEvent public void teleportEntiesOnWorldTick(TickEvent.WorldTickEvent event) { // Make sure that this is the type of tick we want. if (event.phase == TickEvent.Phase.START && event.type == TickEvent.Type.WORLD) { // Perform each teleport Iterator<TeleportInfo> itr = queuedTeleports.iterator(); while (itr.hasNext()) { TeleportInfo info = itr.next(); if (info.entity.worldObj == event.world) { System.out.printf("Teleporting %s to %f, %f, %f%n", info.entity, info.x, info.y, info.z); if (info.entity instanceof EntityPlayerMP) { // EntityPlayerMPs are handled somewhat differently. ((EntityPlayerMP) info.entity).playerNetServerHandler .setPlayerLocation(info.x, info.y, info.z, info.pitch, info.yaw); } else { info.entity.setLocationAndAngles(info.x, info.y, info.z, info.pitch, info.yaw); } itr.remove(); } } } } 

Also note that to use TickEvent you need to register on a separate bus than to use with EntityJoinWorldEvent , so to fully register the events used here, you must do this:

 MinecraftForge.EVENT_BUS.register(this); FMLCommonHandler.instance().bus().register(this); 
0
source

Source: https://habr.com/ru/post/1243387/


All Articles