Minecraft Forge EntityJoinWorldEvent returns 错误的位置!错误
Minecraft Forge EntityJoinWorldEvent returns Wrong Location! Error
在本地开发环境中使用 Eclipse (Mars.1 Release (4.5.1)) 中的 Forge 1.8.9。
我试图在玩家每次加入或 re-join 世界时设置他们的位置。它总是第一次有效(例如 运行 并加入世界。请参阅第一个屏幕截图)。
在世界上移动了一点点,然后退出那个世界并返回(同一会话 w/out 关闭 MC),世界无法出现在控制台中。该位置与 "all OK" 登录中的位置相同。另外还有一个错误的位置!错误。
控制台的错误在这里:
[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
我已经尝试了一些变体,包括 Minecraft Forge: Using correct Join Game listener for setLocationAndAngles 但没有骰子(不同的行为)。
忽略所有不相关的 'imports'。它们是我多次尝试的成果。
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);
}
}
}
我已经阅读了一些关于错误位置错误的信息,但似乎有些不对,因为我可以第一次出现在那个位置,所以我并不是出现在一个街区内。我已经尝试创建一个短暂的延迟(1-3 秒),但错误仍然存在。
"Wrong location!" 当一个实体被添加到一个它不应该在其坐标中的区块时使用。
这是(在 World.java
中)触发事件的地方(好吧,实际上还有其他几个地方,但这是玩家和其他实体使用的地方):
/**
* 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;
}
}
请注意 i
和 j
(区块坐标)在更新玩家位置后不会改变。所以当 Chunk.addEntity
(见下文)被调用时,事情不起作用:
/**
* 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
}
这会杀死玩家。
我不完全确定它第一次运行的原因。每当你在与你被传送到的相同块中登录时它都会起作用,所以如果你在错误的位置后注销,你将在下一次成功登录。
在开始修复之前,还有一些其他事项需要注意:
- 您不需要使用 instanceof 进行空检查 -
null
永远不会通过 instanceof 测试。
- (至少根据 CommandTeleport),你需要以不同的方式传送
EntityPlayerMP
s,使用 EntityPlayerMP.playerNetServerHandler.setPlayerLocation
.
要修复它,您需要将传送延迟 1 个刻度。我不太确定规范的 forge 方法是什么,但像这样的方法应该有效:
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();
}
}
如果您需要能够改变玩家的位置而不是总是去那些特定的坐标,这是一种方法:
@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();
}
}
}
}
此外,请注意,要使用 TickEvent
,您需要注册到与使用 EntityJoinWorldEvent
不同的总线,因此要完全注册此处使用的事件,您需要这个:
MinecraftForge.EVENT_BUS.register(this);
FMLCommonHandler.instance().bus().register(this);
在本地开发环境中使用 Eclipse (Mars.1 Release (4.5.1)) 中的 Forge 1.8.9。
我试图在玩家每次加入或 re-join 世界时设置他们的位置。它总是第一次有效(例如 运行 并加入世界。请参阅第一个屏幕截图)。
在世界上移动了一点点,然后退出那个世界并返回(同一会话 w/out 关闭 MC),世界无法出现在控制台中。该位置与 "all OK" 登录中的位置相同。另外还有一个错误的位置!错误。
控制台的错误在这里:
[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
我已经尝试了一些变体,包括 Minecraft Forge: Using correct Join Game listener for setLocationAndAngles 但没有骰子(不同的行为)。
忽略所有不相关的 'imports'。它们是我多次尝试的成果。
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);
}
}
}
我已经阅读了一些关于错误位置错误的信息,但似乎有些不对,因为我可以第一次出现在那个位置,所以我并不是出现在一个街区内。我已经尝试创建一个短暂的延迟(1-3 秒),但错误仍然存在。
"Wrong location!" 当一个实体被添加到一个它不应该在其坐标中的区块时使用。
这是(在 World.java
中)触发事件的地方(好吧,实际上还有其他几个地方,但这是玩家和其他实体使用的地方):
/**
* 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;
}
}
请注意 i
和 j
(区块坐标)在更新玩家位置后不会改变。所以当 Chunk.addEntity
(见下文)被调用时,事情不起作用:
/**
* 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
}
这会杀死玩家。
我不完全确定它第一次运行的原因。每当你在与你被传送到的相同块中登录时它都会起作用,所以如果你在错误的位置后注销,你将在下一次成功登录。
在开始修复之前,还有一些其他事项需要注意:
- 您不需要使用 instanceof 进行空检查 -
null
永远不会通过 instanceof 测试。 - (至少根据 CommandTeleport),你需要以不同的方式传送
EntityPlayerMP
s,使用EntityPlayerMP.playerNetServerHandler.setPlayerLocation
.
要修复它,您需要将传送延迟 1 个刻度。我不太确定规范的 forge 方法是什么,但像这样的方法应该有效:
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();
}
}
如果您需要能够改变玩家的位置而不是总是去那些特定的坐标,这是一种方法:
@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();
}
}
}
}
此外,请注意,要使用 TickEvent
,您需要注册到与使用 EntityJoinWorldEvent
不同的总线,因此要完全注册此处使用的事件,您需要这个:
MinecraftForge.EVENT_BUS.register(this);
FMLCommonHandler.instance().bus().register(this);