/**
* Copyright (c) Lambda Innovation, 2013-2015
* 本作品版权由Lambda Innovation所有。
* http://www.li-dev.cn/
*
* This project is open-source, and it is distributed under
* the terms of GNU General Public License. You can modify
* and distribute freely as long as you follow the license.
* 本项目是一个开源项目,且遵循GNU通用公共授权协议。
* 在遵照该协议的情况下,您可以自由传播和修改。
* http://www.gnu.org/licenses/gpl.html
*/
package cn.liutils.util.helper;
import java.util.Map.Entry;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import cn.annoreg.core.Registrant;
import cn.annoreg.mc.RegEventHandler;
import cn.annoreg.mc.SideHelper;
import cn.annoreg.mc.network.RegNetworkCall;
import cn.annoreg.mc.s11n.InstanceSerializer;
import cn.annoreg.mc.s11n.RegSerializable;
import cn.annoreg.mc.s11n.StorageOption;
import cn.annoreg.mc.s11n.StorageOption.Data;
import cn.annoreg.mc.s11n.StorageOption.Instance;
import cn.annoreg.mc.s11n.StorageOption.RangedTarget;
import cn.liutils.core.LIUtils;
import cn.liutils.util.client.ClientUtils;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.Phase;
import cpw.mods.fml.common.gameevent.TickEvent.PlayerTickEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagIntArray;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;
import net.minecraftforge.common.IExtendedEntityProperties;
import net.minecraftforge.event.entity.player.PlayerEvent;
/**
* The environment provider and handler of DataPart. <br/>
* It is recommended not to access this class explicitly, but create
* some static get method in each DataPart class.
* @author WeAthFolD
*/
@Registrant
@RegSerializable(instance = PlayerData.Serializer.class)
public abstract class PlayerData implements IExtendedEntityProperties {
private static String IDENTIFIER = "liu_playerData";
static BiMap<String, Class<? extends DataPart> > staticParts = HashBiMap.create();
BiMap<Class<?> , DataPart> constructed = HashBiMap.create();
public static void register(String name, Class<? extends DataPart> clazz) {
staticParts.put(name, clazz);
}
/**
* Do NOT modify this field!
*/
public EntityPlayer player;
PlayerData(EntityPlayer player) {
this.player = player;
try {
constructData();
} catch(Exception e) {
LIUtils.log.error("Error constructing DataPart");
e.printStackTrace();
}
}
private void constructData() throws InstantiationException, IllegalAccessException {
for(Entry< String, Class<? extends DataPart> > s : staticParts.entrySet()) {
String name = s.getKey();
Class<? extends DataPart> clazz = s.getValue();
DataPart dp = clazz.newInstance();
dp.data = this;
constructed.put(clazz, dp);
}
}
protected void tick() {
for(DataPart p : constructed.values()) {
p.tick();
}
}
@Override
public void init(Entity entity, World world) {
player = (EntityPlayer) entity;
}
public String getName(DataPart part) {
return staticParts.inverse().get(part.getClass());
}
public <T extends DataPart> T getPart(String name) {
return (T) constructed.get(staticParts.get(name));
}
public <T extends DataPart> T getPart(Class<T> clazz) {
return (T) constructed.get(clazz);
}
public static PlayerData get(EntityPlayer player) {
PlayerData data = (PlayerData) player.getExtendedProperties(IDENTIFIER);
if(data == null) {
if(player.worldObj.isRemote) {
data = new PlayerData.Client(player);
} else {
data = new PlayerData.Server(player);
data.loadNBTDataCustom(player.getEntityData());
}
player.registerExtendedProperties(IDENTIFIER, data);
}
return data;
}
public static PlayerData getNonCreate(EntityPlayer player) {
return (PlayerData) player.getExtendedProperties(IDENTIFIER);
}
void loadNBTDataCustom(NBTTagCompound tag) {
for(DataPart p : constructed.values()) {
String name = getName(p);
NBTTagCompound t = (NBTTagCompound) tag.getTag(name);
if(t != null) {
p.fromNBT(t);
}
p.dirty = false;
}
}
abstract void saveNBTDataCustom(NBTTagCompound tag);
@Override
public void loadNBTData(NBTTagCompound tag) {
// HACKHACK
loadNBTDataCustom(tag.getCompoundTag("ForgeData"));
}
@Override
public void saveNBTData(NBTTagCompound tag2) {
NBTTagCompound tag = tag2.getCompoundTag("ForgeData");
saveNBTDataCustom(tag);
tag2.setTag("ForgeData", tag);
}
public static class Client extends PlayerData {
public Client(EntityPlayer player) {
super(player);
}
@Override
protected void tick() {
for(DataPart p : constructed.values()) {
if(p.dirty) {
if(p.tickUntilQuery-- == 0) {
p.tickUntilQuery = 20;
query(getName(p));
}
}
}
super.tick();
}
@Override
public void saveNBTDataCustom(NBTTagCompound tag) {}
}
public static class Server extends PlayerData {
public Server(EntityPlayer player) {
super(player);
}
@Override
public void loadNBTData(NBTTagCompound tag) {
super.loadNBTData(tag);
}
@Override
public void saveNBTDataCustom(NBTTagCompound tag) {
for(DataPart p : constructed.values()) {
if(p.isSynced()) {
NBTTagCompound ret = p.toNBT();
if(ret != null)
tag.setTag(getName(p), ret);
} else {
LIUtils.log.warn("Ignored saving of " + p.getName());
}
}
}
}
@RegNetworkCall(side = Side.SERVER, thisStorage = StorageOption.Option.INSTANCE)
protected void query(@Data String pname) {
DataPart part = getPart(pname);
if(part != null) // FIX for client-only DataParts.
synced(player, player, pname, getPart(pname).toNBT());
}
@RegNetworkCall(side = Side.CLIENT, thisStorage = StorageOption.Option.INSTANCE)
protected void synced(
@RangedTarget(range = 10) EntityPlayer _player,
@Instance EntityPlayer player, @Data String pname, @Data NBTTagCompound tag) {
DataPart part = getPart(pname);
part.fromNBT(tag);
part.dirty = false;
}
@RegEventHandler
public static class Events {
@SubscribeEvent
public void onPlayerTick(PlayerTickEvent event) {
if(event.phase == Phase.END)
return;
PlayerData data = PlayerData.get(event.player);
if(data != null) {
data.player = event.player;
data.tick();
}
}
@SubscribeEvent
public void onPlayerClone(PlayerEvent.Clone event) {
EntityPlayer player = event.entityPlayer;
PlayerData data = PlayerData.getNonCreate(event.original);
if(data != null) {
data.player = player;
player.registerExtendedProperties(IDENTIFIER, data);
}
}
}
public static class Serializer implements InstanceSerializer<PlayerData> {
@Override
public PlayerData readInstance(NBTBase nbt) throws Exception {
int[] ids = ((NBTTagIntArray) nbt).func_150302_c();
World world = SideHelper.getWorld(ids[0]);
if (world != null) {
Entity ent = world.getEntityByID(ids[1]);
if(ent instanceof EntityPlayer) {
return PlayerData.get((EntityPlayer) ent);
}
}
return null;
}
@Override
public NBTBase writeInstance(PlayerData obj) throws Exception {
EntityPlayer ent = obj.player;
return new NBTTagIntArray(new int[] { ent.dimension, ent.getEntityId() });
}
}
}