homunculus/src/main/java/com/mondecitronne/homunculus/EntityHomunculus.java

package com.mondecitronne.homunculus;

import com.mojang.authlib.GameProfile;
import com.mondecitronne.homunculus.skin.DefaultSkin;
import com.mondecitronne.homunculus.skin.FallbackSkin;
import com.mondecitronne.homunculus.skin.HTTPSkin;
import com.mondecitronne.homunculus.skin.PlayerSkin;
import com.mondecitronne.homunculus.skin.Skin;
import io.netty.util.internal.StringUtil;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.world.World;

public class EntityHomunculus extends EntityCreature {
	private static final DataParameter<NBTTagCompound> SKIN_SOURCE = EntityDataManager.createKey(EntityHomunculus.class,
			DataSerializers.COMPOUND_TAG);

	private final Skin defaultSkin;
	private static final Skin FALLBACK_SKIN = new FallbackSkin();
	private Skin skin;

	private boolean masochism;

	public EntityHomunculus(World world) {
		super(world);
		getDataManager().register(SKIN_SOURCE, new NBTTagCompound());
		defaultSkin = new DefaultSkin(getUniqueID());
	}

	@Override
	protected void entityInit() {
		super.entityInit();
		if (getRNG().nextInt(70) == 0) {
			masochism = true;
		}
	}

	@Override
	protected void initEntityAI() {
		tasks.addTask(0, new EntityAIPanicFire(this, 0.7));
		tasks.addTask(1, new EntityAISwimming(this));
		tasks.addTask(2, new EntityAIWatchLeashHolder(this));
		tasks.addTask(3, new EntityAIMasochism(this, 0.5, 15.0F));
	}

	private boolean isPlayerProfileUpdated(GameProfile a, GameProfile b) {
		if (b == null || b.getName() == null) {
			return a != null;
		} else {
			return !a.getName().toLowerCase().equals(b.getName().toLowerCase())
					|| (b.getId() != null && a.getId() != null && !b.getId().equals(a.getId()));
		}
	}

	private void dispatchFetchSkin() {
		if (this.getEntityWorld().isRemote && skin != null) {
			skin.dispatchFetch();
		}
	}

	private void updateSkin() {
		NBTTagCompound sourceNBT = getDataManager().get(SKIN_SOURCE);
		if (sourceNBT.hasKey("Type")) {
			switch (sourceNBT.getString("Type")) {
			case "player":
				GameProfile sourceProfile = NBTUtil.readGameProfileFromNBT(sourceNBT);
				if (sourceProfile == null || StringUtil.isNullOrEmpty(sourceProfile.getName())) {
					skin = null;
					break;
				}
				if (!(skin instanceof PlayerSkin)
						|| isPlayerProfileUpdated(((PlayerSkin) skin).getPlayerProfile(), sourceProfile)) {
					skin = new PlayerSkin(sourceProfile, getEntityWorld().isRemote);
					dispatchFetchSkin();
				}
				break;
			case "http":
				String url = sourceNBT.getString("URL");
				String modelType = sourceNBT.getString("Model");
				if (!StringUtil.isNullOrEmpty(url)) {
					if (modelType == null || !modelType.equals("default") && !modelType.equals("slim")) {
						modelType = "default";
					}
					skin = new HTTPSkin(url, modelType);
					dispatchFetchSkin();
				} else {
					skin = null;
				}
				break;
			default:
				skin = null;
				break;
			}
		} else {
			skin = null;
		}
	}

	public Skin getSkin() {
		updateSkin();
		if (skin != null) {
			if (skin.isLoaded()) {
				return skin;
			} else {
				return FALLBACK_SKIN;
			}
		} else {
			return defaultSkin;
		}
	}

	@Override
	public void readFromNBT(NBTTagCompound compound) {
		super.readFromNBT(compound);
		if (compound.hasKey("SkinSource")) {
			NBTTagCompound sourceCompound = compound.getCompoundTag("SkinSource");
			this.getDataManager().set(SKIN_SOURCE, sourceCompound);
		} else if (compound.hasKey("SkinOwner")) {
			// backwards compatibility with old NBT
			NBTTagCompound ownerCompound = compound.getCompoundTag("SkinOwner").copy();
			if (ownerCompound.hasKey("Name")) {
				ownerCompound.setString("Type", "player");
				this.getDataManager().set(SKIN_SOURCE, ownerCompound);
			}
		}
		masochism = compound.getBoolean("Masochism");
	}

	@Override
	public NBTTagCompound writeToNBT(NBTTagCompound compound) {
		super.writeToNBT(compound);
		updateSkin();
		if (skin instanceof PlayerSkin) {
			NBTTagCompound profileCompound = new NBTTagCompound();
			NBTUtil.writeGameProfile(profileCompound, ((PlayerSkin) skin).getPlayerProfile());
			NBTTagCompound sourceCompound = new NBTTagCompound();
			sourceCompound.setTag("Name", profileCompound.getTag("Name"));
			if (profileCompound.hasKey("Id")) {
				// save ID so that if a player changes their name, the skin is invalidated
				// rather than pulling the skin of whoever takes the name
				sourceCompound.setTag("Id", profileCompound.getTag("Id"));
			}
			sourceCompound.setString("Type", "player");
			compound.setTag("SkinSource", sourceCompound);
		} else if (skin instanceof HTTPSkin) {
			NBTTagCompound sourceCompound = new NBTTagCompound();
			sourceCompound.setString("URL", ((HTTPSkin) skin).getUrl());
			sourceCompound.setString("Model", skin.getModelType());
			sourceCompound.setString("Type", "http");
			compound.setTag("SkinSource", sourceCompound);
		} else if (compound.hasKey("SkinSource")) {
			compound.removeTag("SkinSource");
		}
		if (masochism) {
			compound.setBoolean("Masochism", masochism);
		}
		return compound;
	}

	public boolean isMasochist() {
		return masochism;
	}
}