package hunternif.mc.atlas.marker;
import hunternif.mc.atlas.AntiqueAtlasMod;
import hunternif.mc.atlas.api.AtlasAPI;
import hunternif.mc.atlas.registry.MarkerTypes;
import hunternif.mc.atlas.util.DummyWorldAccess;
import hunternif.mc.atlas.util.Log;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Identifies when a player teleports in or out of the nether and puts a portal
* marker in the atlases he is carrying.
* @author Hunternif
*/
public class NetherPortalWatcher extends DummyWorldAccess {
private static final String[] inPortalFieldNames = {"inPortal", "field_71087_bX", "bX"};
/**
* When a player teleports, he is removed from the source dimension, where
* portal detection works well, and his ID is placed in this set.
* Then the player is spawned in the destination dimension, where portal
* detection doesn't work for some reason. But we can detect his arrival
* by checking if this set contains the player's ID!
*/
private final Set<Integer> teleportingPlayerIDs = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load event) {
if (!event.getWorld().isRemote) {
event.getWorld().addEventListener(this);
}
}
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload event) {
if (!event.getWorld().isRemote) {
event.getWorld().removeEventListener(this);
}
}
@Override
public void onEntityAdded(Entity entity) {
if (entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
if (teleportingPlayerIDs.remove(entity.getEntityId())) {
Log.info("Entering");
// player.dimension is the destination dimension
int dimension = player.dimension;
Log.info("Player %s teleported to the %s", player.getGameProfile().getName(),
dimension == 0 ? "Overworld" : "Nether");
addPortalMarkerIfNone(player, dimension);
}
}
}
@Override
public void onEntityRemoved(Entity entity) {
if (entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
if (isEntityInPortal(entity)) {
Log.info("Exiting");
// player.worldObj.provider.dimensionId is the dimension of origin
int dimension = player.getEntityWorld().provider.getDimension();
Log.info("Player %s left the %s", player.getGameProfile().getName(),
dimension == 0 ? "Overworld" : "Nether");
teleportingPlayerIDs.add(entity.getEntityId());
addPortalMarkerIfNone(player, dimension);
}
}
}
/** Put the Portal marker at the player's current coordinates into all
* atlases that he is carrying, if the same marker is not already there. */
private void addPortalMarkerIfNone(EntityPlayer player, int dimension) {
// Due to switching dimensions this player entity's worldObj is lagging.
// We need the very specific dimension each time.
World world = AntiqueAtlasMod.proxy.getServer().worldServerForDimension(dimension);
if (!AntiqueAtlasMod.settings.itemNeeded) {
addPortalMarkerIfNone(player, world, dimension, player.getUniqueID().hashCode());
return;
}
for (ItemStack stack : player.inventory.mainInventory) {
if (stack == null || stack.getItem() != AntiqueAtlasMod.itemAtlas) continue;
addPortalMarkerIfNone(player, world, dimension, stack.getItemDamage());
}
}
private void addPortalMarkerIfNone(EntityPlayer player, World world, int dimension, int atlasID) {
// Can't use entity.dimension here, because its value has already been updated!
DimensionMarkersData data = AntiqueAtlasMod.markersData.getMarkersData(atlasID, world)
.getMarkersDataInDimension(dimension);
int x = (int)player.posX;
int z = (int)player.posZ;
// Check if the marker already exists:
List<Marker> markers = data.getMarkersAtChunk((x >> 4) / MarkersData.CHUNK_STEP, (z >> 4) / MarkersData.CHUNK_STEP);
if (markers != null) {
for (Marker marker : markers) {
if (marker.getType().equals(MarkerTypes.NETHER_PORTAL)) {
// Found the marker.
return;
}
}
}
// Marker not found, place new one:
AtlasAPI.markers.putMarker(world, false, atlasID, MarkerTypes.NETHER_PORTAL, "gui.antiqueatlas.marker.netherPortal", x, z);
}
private static boolean isEntityInPortal(Entity entity) {
return ObfuscationReflectionHelper.getPrivateValue(Entity.class, entity, inPortalFieldNames);
}
}