/*
* Copyright (C) 2013-2016 Gonçalo Baltazar <me@goncalomb.com>
*
* This file is part of NBTEditor.
*
* NBTEditor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NBTEditor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NBTEditor. If not, see <http://www.gnu.org/licenses/>.
*/
package com.goncalomb.bukkit.nbteditor.nbt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import com.goncalomb.bukkit.mylib.namemaps.EntityTypeMap;
import com.goncalomb.bukkit.mylib.reflect.NBTTagCompound;
import com.goncalomb.bukkit.mylib.reflect.NBTTagList;
import com.goncalomb.bukkit.mylib.reflect.NBTUtils;
import com.goncalomb.bukkit.nbteditor.nbt.variable.NBTGenericVariableContainer;
import com.goncalomb.bukkit.nbteditor.nbt.variable.NBTVariable;
import com.goncalomb.bukkit.nbteditor.nbt.variable.NBTVariableContainer;
import net.iharder.Base64;
abstract class EntityNBTBase {
private static HashMap<EntityType, Class<? extends EntityNBT>> _entityClasses;
private static HashMap<Class<? extends EntityNBT>, NBTGenericVariableContainer> _entityVariables;
private static HashMap<EntityType, NBTGenericVariableContainer> _entityVariablesByType;
static {
_entityClasses = new HashMap<EntityType, Class<? extends EntityNBT>>();
_entityVariables = new HashMap<Class<? extends EntityNBT>, NBTGenericVariableContainer>();
_entityVariablesByType = new HashMap<EntityType, NBTGenericVariableContainer>();
// Force static initialization of the EntityNBT class.
try {
Class.forName(EntityNBT.class.getName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
static void registerVariables(Class<? extends EntityNBT> entityClass, NBTGenericVariableContainer variables) {
_entityVariables.put(entityClass, variables);
}
static void registerVariables(EntityType entityType, NBTGenericVariableContainer variables) {
_entityVariablesByType.put(entityType, variables);
}
static void registerEntity(EntityType entityType, Class<? extends EntityNBT> entityClass) {
_entityClasses.put(entityType, entityClass);
}
public static boolean isValidType(EntityType entityType) {
return _entityClasses.containsKey(entityType);
}
public static Collection<EntityType> getValidEntityTypes() {
return _entityClasses.keySet();
}
private static EntityNBT newInstance(EntityType entityType, NBTTagCompound data) {
Class<? extends EntityNBT> entityClass = _entityClasses.get(entityType);
EntityNBTBase instance;
if (entityClass != null) {
try {
instance = entityClass.newInstance();
} catch (Exception e) {
throw new RuntimeException("Error when instantiating " + entityClass.getName() + ".", e);
}
instance._entityType = entityType;
} else {
instance = new EntityNBT(entityType);
}
if (data != null) {
instance._data = data;
}
instance._data.setString("id", EntityTypeMap.getName(entityType));
return (EntityNBT) instance;
}
public static EntityNBT fromEntityType(EntityType entityType) {
if (_entityClasses.containsKey(entityType)) {
return newInstance(entityType, null);
}
return null;
}
public static EntityNBT fromEntityData(NBTTagCompound data) {
EntityType entityType = EntityTypeMap.getByName(data.getString("id"));
if (entityType != null) {
return newInstance(entityType, data);
}
return null;
}
public static EntityNBT fromEntity(Entity entity) {
EntityNBT entityNbt = newInstance(entity.getType(), NBTUtils.getEntityNBTData(entity));
// When cloning, remove the UUID to force all entities to have a unique one.
entityNbt._data.remove("UUIDMost");
entityNbt._data.remove("UUIDLeast");
return entityNbt;
}
public static EntityNBT unserialize(String serializedData) {
try {
NBTTagCompound data = NBTTagCompound.unserialize(Base64.decode(serializedData));
// Backward compatibility with pre-1.9.
// On 1.9 the entities are stacked for bottom to top.
// This conversion needs to happen before instantiating any class, we cannot use onUnserialize.
while (data.hasKey("Riding")) {
NBTTagCompound riding = data.getCompound("Riding");
data.remove("Riding");
riding.setList("Passengers", new NBTTagList(data));
data = riding;
}
EntityNBT entityNBT = fromEntityData(data);
entityNBT.onUnserialize();
return entityNBT;
} catch (Throwable e) {
throw new RuntimeException("Error unserializing EntityNBT.", e);
}
}
private EntityType _entityType;
protected NBTTagCompound _data;
protected EntityNBTBase(EntityType entityType) {
_entityType = entityType;
_data = new NBTTagCompound();
if (entityType != null) {
_data.setString("id", EntityTypeMap.getName(entityType));
}
}
public EntityType getEntityType() {
return _entityType;
}
public NBTVariableContainer[] getAllVariables() {
NBTGenericVariableContainer aux;
ArrayList<NBTVariableContainer> list = new ArrayList<NBTVariableContainer>(3);
if ((aux = _entityVariablesByType.get(_entityType)) != null) {
list.add(aux.boundToData(_data));
}
for (Class<?> clazz = this.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
if ((aux = _entityVariables.get(clazz)) != null) {
list.add(aux.boundToData(_data));
}
}
return list.toArray(new NBTVariableContainer[0]);
}
public NBTVariable getVariable(String name) {
NBTGenericVariableContainer aux;
name = name.toLowerCase();
if ((aux = _entityVariablesByType.get(_entityType)) != null) {
if (aux.hasVariable(name)) {
return aux.getVariable(name, _data);
}
}
for (Class<?> clazz = this.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
if ((aux = _entityVariables.get(clazz)) != null) {
if (aux.hasVariable(name)) {
return aux.getVariable(name, _data);
}
}
}
return null;
}
public Entity spawn(Location location) {
return NBTUtils.spawnEntity(_data, location);
}
public String serialize() {
try {
return Base64.encodeBytes(_data.serialize(), Base64.GZIP);
} catch (Throwable e) {
throw new RuntimeException("Error serializing EntityNBT.", e);
}
}
void onUnserialize() { }
public EntityNBT clone() {
return fromEntityData(_data.clone());
}
public NBTTagCompound getData() {
return _data.clone();
}
public String getMetadataString() {
NBTTagCompound data = _data.clone();
data.remove("id");
return data.toString();
}
}