package net.minecraft.server;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.koloboke.collect.map.hash.HashObjObjMaps;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
// CraftBukkit start
import java.util.UUID;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.map.CraftMapView;
// CraftBukkit end
public class WorldMap extends PersistentBase {
public int centerX;
public int centerZ;
public byte map;
public boolean track;
public boolean unlimitedTracking;
public byte scale;
public byte[] colors = new byte[16384];
public List<WorldMap.WorldMapHumanTracker> i = Lists.newArrayList();
public final Map<EntityHuman, WorldMap.WorldMapHumanTracker> k = HashObjObjMaps.newMutableMap(); // Spigot private -> public
public Map<UUID, MapIcon> decorations = Maps.newLinkedHashMap(); // Spigot
private org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper
// CraftBukkit start
public final CraftMapView mapView;
private CraftServer server;
private UUID uniqueId = null;
// CraftBukkit end
public WorldMap(String s) {
super(s);
// CraftBukkit start
mapView = new CraftMapView(this);
server = (CraftServer) org.bukkit.Bukkit.getServer();
vanillaRender.buffer = colors; // Paper
// CraftBukkit end
}
public void a(double d0, double d1, int i) {
int j = 128 * (1 << i);
int k = MathHelper.floor((d0 + 64.0D) / j);
int l = MathHelper.floor((d1 + 64.0D) / j);
this.centerX = k * j + j / 2 - 64;
this.centerZ = l * j + j / 2 - 64;
}
@Override
public void a(NBTTagCompound nbttagcompound) {
// CraftBukkit start
byte dimension = nbttagcompound.getByte("dimension");
if (dimension >= 10) {
long least = nbttagcompound.getLong("UUIDLeast");
long most = nbttagcompound.getLong("UUIDMost");
if (least != 0L && most != 0L) {
this.uniqueId = new UUID(most, least);
CraftWorld world = (CraftWorld) server.getWorld(this.uniqueId);
// Check if the stored world details are correct.
if (world == null) {
/* All Maps which do not have their valid world loaded are set to a dimension which hopefully won't be reached.
This is to prevent them being corrupted with the wrong map data. */
dimension = 127;
} else {
dimension = (byte) world.getHandle().dimension;
}
}
}
this.map = dimension;
// CraftBukkit end
this.centerX = nbttagcompound.getInt("xCenter");
this.centerZ = nbttagcompound.getInt("zCenter");
this.scale = nbttagcompound.getByte("scale");
this.scale = (byte) MathHelper.clamp(this.scale, 0, 4);
if (nbttagcompound.hasKeyOfType("trackingPosition", 1)) {
this.track = nbttagcompound.getBoolean("trackingPosition");
} else {
this.track = true;
}
this.unlimitedTracking = nbttagcompound.getBoolean("unlimitedTracking");
short short0 = nbttagcompound.getShort("width");
short short1 = nbttagcompound.getShort("height");
if (short0 == 128 && short1 == 128) {
this.colors = nbttagcompound.getByteArray("colors");
} else {
byte[] abyte = nbttagcompound.getByteArray("colors");
this.colors = new byte[16384];
int i = (128 - short0) / 2;
int j = (128 - short1) / 2;
for (int k = 0; k < short1; ++k) {
int l = k + j;
if (l >= 0 || l < 128) {
for (int i1 = 0; i1 < short0; ++i1) {
int j1 = i1 + i;
if (j1 >= 0 || j1 < 128) {
this.colors[j1 + l * 128] = abyte[i1 + k * short0];
}
}
}
}
}
vanillaRender.buffer = colors; // Paper
}
@Override
public NBTTagCompound b(NBTTagCompound nbttagcompound) {
// CraftBukkit start
if (this.map >= 10) {
if (this.uniqueId == null) {
for (org.bukkit.World world : server.getWorlds()) {
CraftWorld cWorld = (CraftWorld) world;
if (cWorld.getHandle().dimension == this.map) {
this.uniqueId = cWorld.getUID();
break;
}
}
}
/* Perform a second check to see if a matching world was found, this is a necessary
change incase Maps are forcefully unlinked from a World and lack a UID.*/
if (this.uniqueId != null) {
nbttagcompound.setLong("UUIDLeast", this.uniqueId.getLeastSignificantBits());
nbttagcompound.setLong("UUIDMost", this.uniqueId.getMostSignificantBits());
}
}
// CraftBukkit end
nbttagcompound.setByte("dimension", this.map);
nbttagcompound.setInt("xCenter", this.centerX);
nbttagcompound.setInt("zCenter", this.centerZ);
nbttagcompound.setByte("scale", this.scale);
nbttagcompound.setShort("width", (short) 128);
nbttagcompound.setShort("height", (short) 128);
nbttagcompound.setByteArray("colors", this.colors);
nbttagcompound.setBoolean("trackingPosition", this.track);
nbttagcompound.setBoolean("unlimitedTracking", this.unlimitedTracking);
return nbttagcompound;
}
public void updateSeenPlayers(EntityHuman entityhuman, ItemStack itemstack) { a(entityhuman, itemstack); } // Paper - OBFHELPER
public void a(EntityHuman entityhuman, ItemStack itemstack) {
if (!this.k.containsKey(entityhuman)) {
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = new WorldMap.WorldMapHumanTracker(entityhuman);
this.k.put(entityhuman, worldmap_worldmaphumantracker);
this.i.add(worldmap_worldmaphumantracker);
}
if (!entityhuman.inventory.f(itemstack)) {
this.decorations.remove(entityhuman.getUniqueID()); // Spigot
}
for (int i = 0; i < this.i.size(); ++i) {
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker1 = this.i.get(i);
if (!worldmap_worldmaphumantracker1.trackee.dead && (worldmap_worldmaphumantracker1.trackee.inventory.f(itemstack) || itemstack.z())) {
if (!itemstack.z() && worldmap_worldmaphumantracker1.trackee.dimension == this.map && this.track) {
this.a(MapIcon.Type.PLAYER, worldmap_worldmaphumantracker1.trackee.world, worldmap_worldmaphumantracker1.trackee.getUniqueID(), worldmap_worldmaphumantracker1.trackee.locX, worldmap_worldmaphumantracker1.trackee.locZ, worldmap_worldmaphumantracker1.trackee.yaw); // Spigot
}
} else {
this.k.remove(worldmap_worldmaphumantracker1.trackee);
this.i.remove(worldmap_worldmaphumantracker1);
}
}
if (itemstack.z() && this.track) {
EntityItemFrame entityitemframe = itemstack.A();
BlockPosition blockposition = entityitemframe.getBlockPosition();
this.a(MapIcon.Type.FRAME, entityhuman.world, UUID.nameUUIDFromBytes(("frame-" + entityitemframe.getId()).getBytes(Charsets.US_ASCII)), blockposition.getX(), blockposition.getZ(), entityitemframe.direction.get2DRotationValue() * 90); // Spigot
}
if (itemstack.hasTag() && itemstack.getTag().hasKeyOfType("Decorations", 9)) {
NBTTagList nbttaglist = itemstack.getTag().getList("Decorations", 10);
for (int j = 0; j < nbttaglist.size(); ++j) {
NBTTagCompound nbttagcompound = nbttaglist.get(j);
// Spigot - start
UUID uuid = UUID.nameUUIDFromBytes(nbttagcompound.getString("id").getBytes(Charsets.US_ASCII));
if (!this.decorations.containsKey(uuid)) {
// Paper start - protect against bad map icon indexes
byte iconId = nbttagcompound.getByte("type");
MapIcon.Type[] values = MapIcon.Type.values();
this.a(values.length > iconId ? values[iconId] : values[2], entityhuman.world, uuid, nbttagcompound.getDouble("x"), nbttagcompound.getDouble("z"), nbttagcompound.getDouble("rot"));
// Paper end
// Spigot - end
}
}
}
}
public static void a(ItemStack itemstack, BlockPosition blockposition, String s, MapIcon.Type mapicon_type) {
NBTTagList nbttaglist;
if (itemstack.hasTag() && itemstack.getTag().hasKeyOfType("Decorations", 9)) {
nbttaglist = itemstack.getTag().getList("Decorations", 10);
} else {
nbttaglist = new NBTTagList();
itemstack.a("Decorations", nbttaglist);
}
NBTTagCompound nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("type", mapicon_type.a());
nbttagcompound.setString("id", s);
nbttagcompound.setDouble("x", blockposition.getX());
nbttagcompound.setDouble("z", blockposition.getZ());
nbttagcompound.setDouble("rot", 180.0D);
nbttaglist.add(nbttagcompound);
if (mapicon_type.c()) {
NBTTagCompound nbttagcompound1 = itemstack.c("display");
nbttagcompound1.setInt("MapColor", mapicon_type.d());
}
}
private void a(MapIcon.Type mapicon_type, World world, UUID s, double d0, double d1, double d2) { // Spigot; string->uuid
int i = 1 << this.scale;
float f = (float) (d0 - this.centerX) / i;
float f1 = (float) (d1 - this.centerZ) / i;
byte b0 = (byte) ((int) (f * 2.0F + 0.5D));
byte b1 = (byte) ((int) (f1 * 2.0F + 0.5D));
boolean flag = true;
byte b2;
if (f >= -63.0F && f1 >= -63.0F && f <= 63.0F && f1 <= 63.0F) {
d2 += d2 < 0.0D ? -8.0D : 8.0D;
b2 = (byte) ((int) (d2 * 16.0D / 360.0D));
if (this.map < 0) {
int j = (int) (world.getWorldData().getDayTime() / 10L);
b2 = (byte) (j * j * 34187121 + j * 121 >> 15 & 15);
}
} else {
if (mapicon_type != MapIcon.Type.PLAYER) {
this.decorations.remove(s);
return;
}
boolean flag1 = true;
if (Math.abs(f) < 320.0F && Math.abs(f1) < 320.0F) {
mapicon_type = MapIcon.Type.PLAYER_OFF_MAP;
} else {
if (!this.unlimitedTracking) {
this.decorations.remove(s);
return;
}
mapicon_type = MapIcon.Type.PLAYER_OFF_LIMITS;
}
b2 = 0;
if (f <= -63.0F) {
b0 = -128;
}
if (f1 <= -63.0F) {
b1 = -128;
}
if (f >= 63.0F) {
b0 = 127;
}
if (f1 >= 63.0F) {
b1 = 127;
}
}
this.decorations.put(s, new MapIcon(mapicon_type, b0, b1, b2));
}
@Nullable
public Packet<?> a(ItemStack itemstack, World world, EntityHuman entityhuman) {
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = this.k.get(entityhuman);
return worldmap_worldmaphumantracker == null ? null : worldmap_worldmaphumantracker.a(itemstack);
}
public void flagDirty(int i, int j) {
super.c();
Iterator iterator = this.i.iterator();
while (iterator.hasNext()) {
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = (WorldMap.WorldMapHumanTracker) iterator.next();
worldmap_worldmaphumantracker.a(i, j);
}
}
public WorldMap.WorldMapHumanTracker a(EntityHuman entityhuman) {
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = this.k.get(entityhuman);
if (worldmap_worldmaphumantracker == null) {
worldmap_worldmaphumantracker = new WorldMap.WorldMapHumanTracker(entityhuman);
this.k.put(entityhuman, worldmap_worldmaphumantracker);
this.i.add(worldmap_worldmaphumantracker);
}
return worldmap_worldmaphumantracker;
}
public class WorldMapHumanTracker {
// Paper start
private void addSeenPlayers(java.util.Collection<MapIcon> icons) {
org.bukkit.entity.Player player = (org.bukkit.entity.Player) trackee.getBukkitEntity();
WorldMap.this.decorations.forEach((uuid, mapIcon) -> {
// If this cursor is for a player check visibility with vanish system
org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayer(uuid); // Spigot
if (other == null || player.canSee(other)) {
icons.add(mapIcon);
}
});
}
private boolean shouldUseVanillaMap() {
return mapView.getRenderers().size() == 1 && mapView.getRenderers().get(0).getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class;
}
// Paper stop
public final EntityHuman trackee;
private boolean d = true;
private int e;
private int f;
private int g = 127;
private int h = 127;
private int i;
public int b;
public WorldMapHumanTracker(EntityHuman entityhuman) {
this.trackee = entityhuman;
}
@Nullable
public Packet<?> a(ItemStack itemstack) {
// CraftBukkit start
if (!this.d && this.i % 5 != 0) { this.i++; return null; } // Paper - this won't end up sending, so don't render it!
boolean vanillaMaps = shouldUseVanillaMap(); // Paper
org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? WorldMap.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.trackee.getBukkitEntity()) : WorldMap.this.vanillaRender; // CraftBukkit // Paper
java.util.Collection<MapIcon> icons = new java.util.ArrayList<MapIcon>();
if (vanillaMaps) addSeenPlayers(icons); // Paper
for ( org.bukkit.map.MapCursor cursor : render.cursors) {
if (cursor.isVisible()) {
icons.add(new MapIcon(MapIcon.Type.a(cursor.getRawType()), cursor.getX(), cursor.getY(), cursor.getDirection()));
}
}
if (this.d) {
this.d = false;
// PAIL: this.e
return new PacketPlayOutMap(itemstack.getData(), WorldMap.this.scale, WorldMap.this.track, icons, render.buffer, this.e, this.f, this.g + 1 - this.e, this.h + 1 - this.f);
} else {
return this.i++ % 5 == 0 ? new PacketPlayOutMap(itemstack.getData(), WorldMap.this.scale, WorldMap.this.track, icons, render.buffer, 0, 0, 0, 0) : null;
}
// CraftBukkit end
}
public void a(int i, int j) {
if (this.d) {
this.e = Math.min(this.e, i);
this.f = Math.min(this.f, j);
this.g = Math.max(this.g, i);
this.h = Math.max(this.h, j);
} else {
this.d = true;
this.e = i;
this.f = j;
this.g = i;
this.h = j;
}
}
}
}